VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DrvHostSerial.cpp@ 28345

Last change on this file since 28345 was 28258, checked in by vboxsync, 15 years ago

PDM critsects for drivers. Fixed critsect cleanup in failure path. Started on new transmit locking scheme (required for intnet buffer serialization).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.2 KB
Line 
1/* $Id: DrvHostSerial.cpp 28258 2010-04-13 14:51:16Z vboxsync $ */
2/** @file
3 * VBox stream I/O devices: Host serial driver
4 */
5
6/*
7 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_HOST_SERIAL
28#include <VBox/pdm.h>
29#include <VBox/err.h>
30
31#include <VBox/log.h>
32#include <iprt/asm.h>
33#include <iprt/assert.h>
34#include <iprt/file.h>
35#include <iprt/mem.h>
36#include <iprt/semaphore.h>
37#include <iprt/uuid.h>
38
39#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
40# include <errno.h>
41# ifdef RT_OS_SOLARIS
42# include <sys/termios.h>
43# else
44# include <termios.h>
45# endif
46# include <sys/types.h>
47# include <fcntl.h>
48# include <string.h>
49# include <unistd.h>
50# ifdef RT_OS_DARWIN
51# include <sys/select.h>
52# else
53# include <sys/poll.h>
54# endif
55# include <sys/ioctl.h>
56# include <pthread.h>
57
58# ifdef RT_OS_LINUX
59/*
60 * TIOCM_LOOP is not defined in the above header files for some reason but in asm/termios.h.
61 * But inclusion of this file however leads to compilation errors because of redefinition of some
62 * structs. Thatswhy it is defined here until a better solution is found.
63 */
64# ifndef TIOCM_LOOP
65# define TIOCM_LOOP 0x8000
66# endif
67/* For linux custom baudrate code we also need serial_struct */
68# include <linux/serial.h>
69# endif /* linux */
70
71#elif defined(RT_OS_WINDOWS)
72# include <Windows.h>
73#endif
74
75#include "../Builtins.h"
76
77
78/** Size of the send fifo queue (in bytes) */
79#ifdef RT_OS_DARWIN
80 /** @todo This is really desperate, but it seriously looks like
81 * the data is arriving way too fast for us to push over. 9600
82 * baud and zoc reports sending at 12000+ chars/sec... */
83# define CHAR_MAX_SEND_QUEUE 0x400
84# define CHAR_MAX_SEND_QUEUE_MASK 0x3ff
85#else
86# define CHAR_MAX_SEND_QUEUE 0x80
87# define CHAR_MAX_SEND_QUEUE_MASK 0x7f
88#endif
89
90/*******************************************************************************
91* Structures and Typedefs *
92*******************************************************************************/
93
94/**
95 * Char driver instance data.
96 *
97 * @implements PDMICHARCONNECTOR
98 */
99typedef struct DRVHOSTSERIAL
100{
101 /** Pointer to the driver instance structure. */
102 PPDMDRVINS pDrvIns;
103 /** Pointer to the char port interface of the driver/device above us. */
104 PPDMICHARPORT pDrvCharPort;
105 /** Our char interface. */
106 PDMICHARCONNECTOR ICharConnector;
107 /** Receive thread. */
108 PPDMTHREAD pRecvThread;
109 /** Send thread. */
110 PPDMTHREAD pSendThread;
111 /** Status lines monitor thread. */
112 PPDMTHREAD pMonitorThread;
113 /** Send event semephore */
114 RTSEMEVENT SendSem;
115
116 /** the device path */
117 char *pszDevicePath;
118
119#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
120 /** the device handle */
121 RTFILE DeviceFile;
122# ifdef RT_OS_DARWIN
123 /** The device handle used for reading.
124 * Used to prevent the read selecto from blocking the writes. */
125 RTFILE DeviceFileR;
126# endif
127 /** The read end of the control pipe */
128 RTFILE WakeupPipeR;
129 /** The write end of the control pipe */
130 RTFILE WakeupPipeW;
131# ifndef RT_OS_LINUX
132 /** The current line status.
133 * Used by the polling version of drvHostSerialMonitorThread. */
134 int fStatusLines;
135# endif
136#elif defined(RT_OS_WINDOWS)
137 /** the device handle */
138 HANDLE hDeviceFile;
139 /** The event semaphore for waking up the receive thread */
140 HANDLE hHaltEventSem;
141 /** The event semaphore for overlapped receiving */
142 HANDLE hEventRecv;
143 /** For overlapped receiving */
144 OVERLAPPED overlappedRecv;
145 /** The event semaphore for overlapped sending */
146 HANDLE hEventSend;
147 /** For overlapped sending */
148 OVERLAPPED overlappedSend;
149#endif
150
151 /** Internal send FIFO queue */
152 uint8_t volatile aSendQueue[CHAR_MAX_SEND_QUEUE];
153 uint32_t volatile iSendQueueHead;
154 uint32_t volatile iSendQueueTail;
155
156 /** Read/write statistics */
157 STAMCOUNTER StatBytesRead;
158 STAMCOUNTER StatBytesWritten;
159#ifdef RT_OS_DARWIN
160 /** The number of bytes we've dropped because the send queue
161 * was full. */
162 STAMCOUNTER StatSendOverflows;
163#endif
164} DRVHOSTSERIAL, *PDRVHOSTSERIAL;
165
166
167/** Converts a pointer to DRVCHAR::ICharConnector to a PDRVHOSTSERIAL. */
168#define PDMICHAR_2_DRVHOSTSERIAL(pInterface) RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ICharConnector)
169
170
171/* -=-=-=-=- IBase -=-=-=-=- */
172
173/**
174 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
175 */
176static DECLCALLBACK(void *) drvHostSerialQueryInterface(PPDMIBASE pInterface, const char *pszIID)
177{
178 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
179 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
180
181 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
182 PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARCONNECTOR, &pThis->ICharConnector);
183 return NULL;
184}
185
186
187/* -=-=-=-=- ICharConnector -=-=-=-=- */
188
189/** @copydoc PDMICHARCONNECTOR::pfnWrite */
190static DECLCALLBACK(int) drvHostSerialWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
191{
192 PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
193 const uint8_t *pbBuffer = (const uint8_t *)pvBuf;
194
195 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
196
197 for (uint32_t i = 0; i < cbWrite; i++)
198 {
199#ifdef RT_OS_DARWIN /* don't wanna break the others here. */
200 uint32_t iHead = ASMAtomicUoReadU32(&pThis->iSendQueueHead);
201 uint32_t iTail = ASMAtomicUoReadU32(&pThis->iSendQueueTail);
202 int32_t cbAvail = iTail > iHead
203 ? iTail - iHead - 1
204 : CHAR_MAX_SEND_QUEUE - (iHead - iTail) - 1;
205 if (cbAvail <= 0)
206 {
207# ifdef DEBUG
208 uint64_t volatile u64Now = RTTimeNanoTS(); NOREF(u64Now);
209# endif
210 Log(("%s: dropping %d chars (cbAvail=%d iHead=%d iTail=%d)\n", __FUNCTION__, cbWrite - i , cbAvail, iHead, iTail));
211 STAM_COUNTER_INC(&pThis->StatSendOverflows);
212 break;
213 }
214
215 pThis->aSendQueue[iHead] = pbBuffer[i];
216 STAM_COUNTER_INC(&pThis->StatBytesWritten);
217 ASMAtomicWriteU32(&pThis->iSendQueueHead, (iHead + 1) & CHAR_MAX_SEND_QUEUE_MASK);
218 if (cbAvail < CHAR_MAX_SEND_QUEUE / 4)
219 {
220 RTSemEventSignal(pThis->SendSem);
221 RTThreadYield();
222 }
223#else /* old code */
224 uint32_t idx = ASMAtomicUoReadU32(&pThis->iSendQueueHead);
225
226 pThis->aSendQueue[idx] = pbBuffer[i];
227 idx = (idx + 1) & CHAR_MAX_SEND_QUEUE_MASK;
228
229 STAM_COUNTER_INC(&pThis->StatBytesWritten);
230 ASMAtomicWriteU32(&pThis->iSendQueueHead, idx);
231#endif /* old code */
232 }
233 RTSemEventSignal(pThis->SendSem);
234 return VINF_SUCCESS;
235}
236
237static DECLCALLBACK(int) drvHostSerialSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
238{
239 PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
240#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
241 struct termios *termiosSetup;
242 int baud_rate;
243#elif defined(RT_OS_WINDOWS)
244 LPDCB comSetup;
245#endif
246
247 LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
248
249#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
250 termiosSetup = (struct termios *)RTMemTmpAllocZ(sizeof(struct termios));
251
252 /* Enable receiver */
253 termiosSetup->c_cflag |= (CLOCAL | CREAD);
254
255 switch (Bps) {
256 case 50:
257 baud_rate = B50;
258 break;
259 case 75:
260 baud_rate = B75;
261 break;
262 case 110:
263 baud_rate = B110;
264 break;
265 case 134:
266 baud_rate = B134;
267 break;
268 case 150:
269 baud_rate = B150;
270 break;
271 case 200:
272 baud_rate = B200;
273 break;
274 case 300:
275 baud_rate = B300;
276 break;
277 case 600:
278 baud_rate = B600;
279 break;
280 case 1200:
281 baud_rate = B1200;
282 break;
283 case 1800:
284 baud_rate = B1800;
285 break;
286 case 2400:
287 baud_rate = B2400;
288 break;
289 case 4800:
290 baud_rate = B4800;
291 break;
292 case 9600:
293 baud_rate = B9600;
294 break;
295 case 19200:
296 baud_rate = B19200;
297 break;
298 case 38400:
299 baud_rate = B38400;
300 break;
301 case 57600:
302 baud_rate = B57600;
303 break;
304 case 115200:
305 baud_rate = B115200;
306 break;
307 default:
308#ifdef RT_OS_LINUX
309 struct serial_struct serialStruct;
310 if (ioctl(pThis->DeviceFile, TIOCGSERIAL, &serialStruct) != -1)
311 {
312 serialStruct.custom_divisor = serialStruct.baud_base / Bps;
313 if (!serialStruct.custom_divisor)
314 serialStruct.custom_divisor = 1;
315 serialStruct.flags &= ~ASYNC_SPD_MASK;
316 serialStruct.flags |= ASYNC_SPD_CUST;
317 ioctl(pThis->DeviceFile, TIOCSSERIAL, &serialStruct);
318 baud_rate = B38400;
319 }
320 else
321 baud_rate = B9600;
322#else /* !RT_OS_LINUX */
323 baud_rate = B9600;
324#endif /* !RT_OS_LINUX */
325 }
326
327 cfsetispeed(termiosSetup, baud_rate);
328 cfsetospeed(termiosSetup, baud_rate);
329
330 switch (chParity) {
331 case 'E':
332 termiosSetup->c_cflag |= PARENB;
333 break;
334 case 'O':
335 termiosSetup->c_cflag |= (PARENB | PARODD);
336 break;
337 case 'N':
338 break;
339 default:
340 break;
341 }
342
343 switch (cDataBits) {
344 case 5:
345 termiosSetup->c_cflag |= CS5;
346 break;
347 case 6:
348 termiosSetup->c_cflag |= CS6;
349 break;
350 case 7:
351 termiosSetup->c_cflag |= CS7;
352 break;
353 case 8:
354 termiosSetup->c_cflag |= CS8;
355 break;
356 default:
357 break;
358 }
359
360 switch (cStopBits) {
361 case 2:
362 termiosSetup->c_cflag |= CSTOPB;
363 default:
364 break;
365 }
366
367 /* set serial port to raw input */
368 termiosSetup->c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ECHOK | ISIG | IEXTEN);
369
370 tcsetattr(pThis->DeviceFile, TCSANOW, termiosSetup);
371 RTMemTmpFree(termiosSetup);
372#elif defined(RT_OS_WINDOWS)
373 comSetup = (LPDCB)RTMemTmpAllocZ(sizeof(DCB));
374
375 comSetup->DCBlength = sizeof(DCB);
376
377 switch (Bps) {
378 case 110:
379 comSetup->BaudRate = CBR_110;
380 break;
381 case 300:
382 comSetup->BaudRate = CBR_300;
383 break;
384 case 600:
385 comSetup->BaudRate = CBR_600;
386 break;
387 case 1200:
388 comSetup->BaudRate = CBR_1200;
389 break;
390 case 2400:
391 comSetup->BaudRate = CBR_2400;
392 break;
393 case 4800:
394 comSetup->BaudRate = CBR_4800;
395 break;
396 case 9600:
397 comSetup->BaudRate = CBR_9600;
398 break;
399 case 14400:
400 comSetup->BaudRate = CBR_14400;
401 break;
402 case 19200:
403 comSetup->BaudRate = CBR_19200;
404 break;
405 case 38400:
406 comSetup->BaudRate = CBR_38400;
407 break;
408 case 57600:
409 comSetup->BaudRate = CBR_57600;
410 break;
411 case 115200:
412 comSetup->BaudRate = CBR_115200;
413 break;
414 default:
415 comSetup->BaudRate = CBR_9600;
416 }
417
418 comSetup->fBinary = TRUE;
419 comSetup->fOutxCtsFlow = FALSE;
420 comSetup->fOutxDsrFlow = FALSE;
421 comSetup->fDtrControl = DTR_CONTROL_DISABLE;
422 comSetup->fDsrSensitivity = FALSE;
423 comSetup->fTXContinueOnXoff = TRUE;
424 comSetup->fOutX = FALSE;
425 comSetup->fInX = FALSE;
426 comSetup->fErrorChar = FALSE;
427 comSetup->fNull = FALSE;
428 comSetup->fRtsControl = RTS_CONTROL_DISABLE;
429 comSetup->fAbortOnError = FALSE;
430 comSetup->wReserved = 0;
431 comSetup->XonLim = 5;
432 comSetup->XoffLim = 5;
433 comSetup->ByteSize = cDataBits;
434
435 switch (chParity) {
436 case 'E':
437 comSetup->Parity = EVENPARITY;
438 break;
439 case 'O':
440 comSetup->Parity = ODDPARITY;
441 break;
442 case 'N':
443 comSetup->Parity = NOPARITY;
444 break;
445 default:
446 break;
447 }
448
449 switch (cStopBits) {
450 case 1:
451 comSetup->StopBits = ONESTOPBIT;
452 break;
453 case 2:
454 comSetup->StopBits = TWOSTOPBITS;
455 break;
456 default:
457 break;
458 }
459
460 comSetup->XonChar = 0;
461 comSetup->XoffChar = 0;
462 comSetup->ErrorChar = 0;
463 comSetup->EofChar = 0;
464 comSetup->EvtChar = 0;
465
466 SetCommState(pThis->hDeviceFile, comSetup);
467 RTMemTmpFree(comSetup);
468#endif /* RT_OS_WINDOWS */
469
470 return VINF_SUCCESS;
471}
472
473/* -=-=-=-=- receive thread -=-=-=-=- */
474
475/**
476 * Send thread loop.
477 *
478 * @returns VINF_SUCCESS.
479 * @param ThreadSelf Thread handle to this thread.
480 * @param pvUser User argument.
481 */
482static DECLCALLBACK(int) drvHostSerialSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
483{
484 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
485
486 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
487 return VINF_SUCCESS;
488
489#ifdef RT_OS_WINDOWS
490 /* Make sure that the halt event semaphore is reset. */
491 DWORD dwRet = WaitForSingleObject(pThis->hHaltEventSem, 0);
492
493 HANDLE haWait[2];
494 haWait[0] = pThis->hEventSend;
495 haWait[1] = pThis->hHaltEventSem;
496#endif
497
498 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
499 {
500 int rc = RTSemEventWait(pThis->SendSem, RT_INDEFINITE_WAIT);
501 if (RT_FAILURE(rc))
502 break;
503
504 /*
505 * Write the character to the host device.
506 */
507#ifdef RT_OS_DARWIN
508 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
509 {
510 /* copy the send queue so we get a linear buffer with the maximal size. */
511 uint8_t abBuf[sizeof(pThis->aSendQueue)];
512 uint32_t cb = 0;
513 uint32_t iTail = ASMAtomicUoReadU32(&pThis->iSendQueueTail);
514 uint32_t iHead = ASMAtomicUoReadU32(&pThis->iSendQueueHead);
515 if (iTail == iHead)
516 break;
517 do
518 {
519 abBuf[cb++] = pThis->aSendQueue[iTail];
520 iTail = (iTail + 1) & CHAR_MAX_SEND_QUEUE_MASK;
521 } while (iTail != iHead);
522
523 ASMAtomicWriteU32(&pThis->iSendQueueTail, iTail);
524
525 /* write it. */
526# ifdef DEBUG
527 uint64_t volatile u64Now = RTTimeNanoTS(); NOREF(u64Now);
528# endif
529# if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
530
531 size_t cbWritten;
532 rc = RTFileWrite(pThis->DeviceFile, abBuf, cb, &cbWritten);
533 if (rc == VERR_TRY_AGAIN)
534 cbWritten = 0;
535 if (cbWritten < cb && (RT_SUCCESS(rc) || rc == VERR_TRY_AGAIN))
536 {
537 /* ok, block till the device is ready for more (O_NONBLOCK) effect. */
538 rc = VINF_SUCCESS;
539 uint8_t const *pbSrc = &abBuf[0];
540 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
541 {
542 /* advance */
543 cb -= cbWritten;
544 pbSrc += cbWritten;
545
546 /* wait */
547 fd_set WrSet;
548 FD_ZERO(&WrSet);
549 FD_SET(pThis->DeviceFile, &WrSet);
550 fd_set XcptSet;
551 FD_ZERO(&XcptSet);
552 FD_SET(pThis->DeviceFile, &XcptSet);
553 rc = select(pThis->DeviceFile + 1, NULL, &WrSet, &XcptSet, NULL);
554 /** @todo check rc? */
555
556 /* try write more */
557 rc = RTFileWrite(pThis->DeviceFile, pbSrc, cb, &cbWritten);
558 if (rc == VERR_TRY_AGAIN)
559 cbWritten = 0;
560 else if (RT_FAILURE(rc))
561 break;
562 else if (cbWritten >= cb)
563 break;
564 rc = VINF_SUCCESS;
565 } /* wait/write loop */
566 }
567
568# elif defined(RT_OS_WINDOWS)
569 /* perform an overlapped write operation. */
570 DWORD cbWritten;
571 memset(&pThis->overlappedSend, 0, sizeof(pThis->overlappedSend));
572 pThis->overlappedSend.hEvent = pThis->hEventSend;
573 if (!WriteFile(pThis->hDeviceFile, abBuf, cb, &cbWritten, &pThis->overlappedSend))
574 {
575 dwRet = GetLastError();
576 if (dwRet == ERROR_IO_PENDING)
577 {
578 /*
579 * write blocked, wait for completion or wakeup...
580 */
581 dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE);
582 if (dwRet != WAIT_OBJECT_0)
583 {
584 AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event sempahore is set but the thread is still in running state\n"));
585 break;
586 }
587 }
588 else
589 rc = RTErrConvertFromWin32(dwRet);
590 }
591
592# endif /* RT_OS_WINDOWS */
593 if (RT_FAILURE(rc))
594 {
595 LogRel(("HostSerial#%d: Serial Write failed with %Rrc; terminating send thread\n", pDrvIns->iInstance, rc));
596 return rc;
597 }
598 } /* write loop */
599
600#else /* old code */
601 uint32_t iTail;
602 while ( pThread->enmState == PDMTHREADSTATE_RUNNING
603 && (iTail = ASMAtomicUoReadU32(&pThis->iSendQueueTail)) != ASMAtomicUoReadU32(&pThis->iSendQueueHead))
604 {
605 /** @todo process more than one byte? */
606 unsigned cbProcessed = 1;
607 uint8_t abBuf[1];
608 abBuf[0] = pThis->aSendQueue[iTail];
609
610# if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
611
612 rc = RTFileWrite(pThis->DeviceFile, abBuf, cbProcessed, NULL);
613
614# elif defined(RT_OS_WINDOWS)
615
616 DWORD cbBytesWritten;
617 memset(&pThis->overlappedSend, 0, sizeof(pThis->overlappedSend));
618 pThis->overlappedSend.hEvent = pThis->hEventSend;
619
620 if (!WriteFile(pThis->hDeviceFile, abBuf, cbProcessed, &cbBytesWritten, &pThis->overlappedSend))
621 {
622 dwRet = GetLastError();
623 if (dwRet == ERROR_IO_PENDING)
624 {
625 /*
626 * write blocked, wait ...
627 */
628 dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE);
629 if (dwRet != WAIT_OBJECT_0)
630 {
631 AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event sempahore is set but the thread is still in running state\n"));
632 break;
633 }
634 }
635 else
636 rc = RTErrConvertFromWin32(dwRet);
637 }
638
639# endif
640
641 if (RT_SUCCESS(rc))
642 {
643 Assert(cbProcessed == 1);
644 ASMAtomicWriteU32(&pThis->iSendQueueTail, (iTail + 1) & CHAR_MAX_SEND_QUEUE_MASK);
645 }
646 else if (RT_FAILURE(rc))
647 {
648 LogRel(("HostSerial#%d: Serial Write failed with %Rrc; terminating send thread\n", pDrvIns->iInstance, rc));
649 return rc;
650 }
651 }
652#endif /* old code */
653 }
654
655 return VINF_SUCCESS;
656}
657
658/**
659 * Unblock the send thread so it can respond to a state change.
660 *
661 * @returns a VBox status code.
662 * @param pDrvIns The driver instance.
663 * @param pThread The send thread.
664 */
665static DECLCALLBACK(int) drvHostSerialWakeupSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
666{
667 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
668 int rc;
669
670 rc = RTSemEventSignal(pThis->SendSem);
671 if (RT_FAILURE(rc))
672 return rc;
673
674#ifdef RT_OS_WINDOWS
675 if (!SetEvent(pThis->hHaltEventSem))
676 return RTErrConvertFromWin32(GetLastError());
677#endif
678
679 return VINF_SUCCESS;
680}
681
682/* -=-=-=-=- receive thread -=-=-=-=- */
683
684/**
685 * Receive thread loop.
686 *
687 * This thread pushes data from the host serial device up the driver
688 * chain toward the serial device.
689 *
690 * @returns VINF_SUCCESS.
691 * @param ThreadSelf Thread handle to this thread.
692 * @param pvUser User argument.
693 */
694static DECLCALLBACK(int) drvHostSerialRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
695{
696 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
697 uint8_t abBuffer[256];
698 uint8_t *pbBuffer = NULL;
699 size_t cbRemaining = 0; /* start by reading host data */
700 int rc = VINF_SUCCESS;
701 int rcThread = VINF_SUCCESS;
702
703 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
704 return VINF_SUCCESS;
705
706#ifdef RT_OS_WINDOWS
707 /* Make sure that the halt event semaphore is reset. */
708 DWORD dwRet = WaitForSingleObject(pThis->hHaltEventSem, 0);
709
710 HANDLE ahWait[2];
711 ahWait[0] = pThis->hEventRecv;
712 ahWait[1] = pThis->hHaltEventSem;
713#endif
714
715 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
716 {
717 if (!cbRemaining)
718 {
719 /* Get a block of data from the host serial device. */
720
721#if defined(RT_OS_DARWIN) /* poll is broken on x86 darwin, returns POLLNVAL. */
722 fd_set RdSet;
723 FD_ZERO(&RdSet);
724 FD_SET(pThis->DeviceFileR, &RdSet);
725 FD_SET(pThis->WakeupPipeR, &RdSet);
726 fd_set XcptSet;
727 FD_ZERO(&XcptSet);
728 FD_SET(pThis->DeviceFileR, &XcptSet);
729 FD_SET(pThis->WakeupPipeR, &XcptSet);
730# if 1 /* it seems like this select is blocking the write... */
731 rc = select(RT_MAX(pThis->WakeupPipeR, pThis->DeviceFileR) + 1, &RdSet, NULL, &XcptSet, NULL);
732# else
733 struct timeval tv = { 0, 1000 };
734 rc = select(RT_MAX(pThis->WakeupPipeR, pThis->DeviceFileR) + 1, &RdSet, NULL, &XcptSet, &tv);
735# endif
736 if (rc == -1)
737 {
738 int err = errno;
739 rcThread = RTErrConvertFromErrno(err);
740 LogRel(("HostSerial#%d: select failed with errno=%d / %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, err, rcThread));
741 break;
742 }
743
744 /* this might have changed in the meantime */
745 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
746 break;
747 if (rc == 0)
748 continue;
749
750 /* drain the wakeup pipe */
751 size_t cbRead;
752 if ( FD_ISSET(pThis->WakeupPipeR, &RdSet)
753 || FD_ISSET(pThis->WakeupPipeR, &XcptSet))
754 {
755 rc = RTFileRead(pThis->WakeupPipeR, abBuffer, 1, &cbRead);
756 if (RT_FAILURE(rc))
757 {
758 LogRel(("HostSerial#%d: draining the wakekup pipe failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
759 rcThread = rc;
760 break;
761 }
762 continue;
763 }
764
765 /* read data from the serial port. */
766 rc = RTFileRead(pThis->DeviceFileR, abBuffer, sizeof(abBuffer), &cbRead);
767 if (RT_FAILURE(rc))
768 {
769 LogRel(("HostSerial#%d: (1) Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
770 rcThread = rc;
771 break;
772 }
773 cbRemaining = cbRead;
774
775#elif defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
776
777 size_t cbRead;
778 struct pollfd aFDs[2];
779 aFDs[0].fd = pThis->DeviceFile;
780 aFDs[0].events = POLLIN;
781 aFDs[0].revents = 0;
782 aFDs[1].fd = pThis->WakeupPipeR;
783 aFDs[1].events = POLLIN | POLLERR | POLLHUP;
784 aFDs[1].revents = 0;
785 rc = poll(aFDs, RT_ELEMENTS(aFDs), -1);
786 if (rc < 0)
787 {
788 int err = errno;
789 rcThread = RTErrConvertFromErrno(err);
790 LogRel(("HostSerial#%d: poll failed with errno=%d / %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, err, rcThread));
791 break;
792 }
793 /* this might have changed in the meantime */
794 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
795 break;
796 if (rc > 0 && aFDs[1].revents)
797 {
798 if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
799 break;
800 /* notification to terminate -- drain the pipe */
801 RTFileRead(pThis->WakeupPipeR, &abBuffer, 1, &cbRead);
802 continue;
803 }
804 rc = RTFileRead(pThis->DeviceFile, abBuffer, sizeof(abBuffer), &cbRead);
805 if (RT_FAILURE(rc))
806 {
807 /* don't terminate worker thread when data unavailable */
808 if (rc == VERR_TRY_AGAIN)
809 continue;
810
811 LogRel(("HostSerial#%d: (2) Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
812 rcThread = rc;
813 break;
814 }
815 cbRemaining = cbRead;
816
817#elif defined(RT_OS_WINDOWS)
818
819 DWORD dwEventMask = 0;
820 DWORD dwNumberOfBytesTransferred;
821
822 memset(&pThis->overlappedRecv, 0, sizeof(pThis->overlappedRecv));
823 pThis->overlappedRecv.hEvent = pThis->hEventRecv;
824
825 if (!WaitCommEvent(pThis->hDeviceFile, &dwEventMask, &pThis->overlappedRecv))
826 {
827 dwRet = GetLastError();
828 if (dwRet == ERROR_IO_PENDING)
829 {
830 dwRet = WaitForMultipleObjects(2, ahWait, FALSE, INFINITE);
831 if (dwRet != WAIT_OBJECT_0)
832 {
833 /* notification to terminate */
834 AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event sempahore is set but the thread is still in running state\n"));
835 break;
836 }
837 }
838 else
839 {
840 rcThread = RTErrConvertFromWin32(dwRet);
841 LogRel(("HostSerial#%d: Wait failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread));
842 break;
843 }
844 }
845 /* this might have changed in the meantime */
846 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
847 break;
848
849 /* Check the event */
850 if (dwEventMask & EV_RXCHAR)
851 {
852 if (!ReadFile(pThis->hDeviceFile, abBuffer, sizeof(abBuffer), &dwNumberOfBytesTransferred, &pThis->overlappedRecv))
853 {
854 rcThread = RTErrConvertFromWin32(GetLastError());
855 LogRel(("HostSerial#%d: Read failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread));
856 break;
857 }
858 cbRemaining = dwNumberOfBytesTransferred;
859 }
860 else if (dwEventMask & EV_BREAK)
861 {
862 Log(("HostSerial#%d: Detected break\n"));
863 rc = pThis->pDrvCharPort->pfnNotifyBreak(pThis->pDrvCharPort);
864 }
865 else
866 {
867 /* The status lines have changed. Notify the device. */
868 DWORD dwNewStatusLinesState = 0;
869 uint32_t uNewStatusLinesState = 0;
870
871 /* Get the new state */
872 if (GetCommModemStatus(pThis->hDeviceFile, &dwNewStatusLinesState))
873 {
874 if (dwNewStatusLinesState & MS_RLSD_ON)
875 uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_DCD;
876 if (dwNewStatusLinesState & MS_RING_ON)
877 uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_RI;
878 if (dwNewStatusLinesState & MS_DSR_ON)
879 uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_DSR;
880 if (dwNewStatusLinesState & MS_CTS_ON)
881 uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_CTS;
882 rc = pThis->pDrvCharPort->pfnNotifyStatusLinesChanged(pThis->pDrvCharPort, uNewStatusLinesState);
883 if (RT_FAILURE(rc))
884 {
885 /* Notifying device failed, continue but log it */
886 LogRel(("HostSerial#%d: Notifying device failed with error %Rrc; continuing.\n", pDrvIns->iInstance, rc));
887 }
888 }
889 else
890 {
891 /* Getting new state failed, continue but log it */
892 LogRel(("HostSerial#%d: Getting status lines state failed with error %Rrc; continuing.\n", pDrvIns->iInstance, RTErrConvertFromWin32(GetLastError())));
893 }
894 }
895#endif
896
897 Log(("Read %d bytes.\n", cbRemaining));
898 pbBuffer = abBuffer;
899 }
900 else
901 {
902 /* Send data to the guest. */
903 size_t cbProcessed = cbRemaining;
904 rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pbBuffer, &cbProcessed);
905 if (RT_SUCCESS(rc))
906 {
907 Assert(cbProcessed); Assert(cbProcessed <= cbRemaining);
908 pbBuffer += cbProcessed;
909 cbRemaining -= cbProcessed;
910 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
911 }
912 else if (rc == VERR_TIMEOUT)
913 {
914 /* Normal case, just means that the guest didn't accept a new
915 * character before the timeout elapsed. Just retry. */
916 rc = VINF_SUCCESS;
917 }
918 else
919 {
920 LogRel(("HostSerial#%d: NotifyRead failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
921 rcThread = rc;
922 break;
923 }
924 }
925 }
926
927 return rcThread;
928}
929
930/**
931 * Unblock the send thread so it can respond to a state change.
932 *
933 * @returns a VBox status code.
934 * @param pDrvIns The driver instance.
935 * @param pThread The send thread.
936 */
937static DECLCALLBACK(int) drvHostSerialWakeupRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
938{
939 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
940#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
941 return RTFileWrite(pThis->WakeupPipeW, "", 1, NULL);
942#elif defined(RT_OS_WINDOWS)
943 if (!SetEvent(pThis->hHaltEventSem))
944 return RTErrConvertFromWin32(GetLastError());
945 return VINF_SUCCESS;
946#else
947# error adapt me!
948#endif
949}
950
951#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
952/* -=-=-=-=- Monitor thread -=-=-=-=- */
953
954/**
955 * Monitor thread loop.
956 *
957 * This thread monitors the status lines and notifies the device
958 * if they change.
959 *
960 * @returns VINF_SUCCESS.
961 * @param ThreadSelf Thread handle to this thread.
962 * @param pvUser User argument.
963 */
964static DECLCALLBACK(int) drvHostSerialMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
965{
966 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
967 int rc = VINF_SUCCESS;
968 unsigned uStatusLinesToCheck = 0;
969
970 uStatusLinesToCheck = TIOCM_CAR | TIOCM_RNG | TIOCM_LE | TIOCM_CTS;
971
972 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
973 return VINF_SUCCESS;
974
975 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
976 {
977 uint32_t newStatusLine = 0;
978 unsigned int statusLines;
979
980# ifdef RT_OS_LINUX
981 /*
982 * Wait for status line change.
983 */
984 rc = ioctl(pThis->DeviceFile, TIOCMIWAIT, uStatusLinesToCheck);
985 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
986 break;
987 if (rc < 0)
988 {
989ioctl_error:
990 PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail",
991 N_("Ioctl failed for serial host device '%s' (%Rrc). The device will not work properly"),
992 pThis->pszDevicePath, RTErrConvertFromErrno(errno));
993 break;
994 }
995
996 rc = ioctl(pThis->DeviceFile, TIOCMGET, &statusLines);
997 if (rc < 0)
998 goto ioctl_error;
999# else /* !RT_OS_LINUX */
1000 /*
1001 * Poll for the status line change.
1002 */
1003 rc = ioctl(pThis->DeviceFile, TIOCMGET, &statusLines);
1004 if (rc < 0)
1005 {
1006 PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail",
1007 N_("Ioctl failed for serial host device '%s' (%Rrc). The device will not work properly"),
1008 pThis->pszDevicePath, RTErrConvertFromErrno(errno));
1009 break;
1010 }
1011 if (!((statusLines ^ pThis->fStatusLines) & uStatusLinesToCheck))
1012 {
1013 PDMR3ThreadSleep(pThread, 500); /* 0.5 sec */
1014 continue;
1015 }
1016 pThis->fStatusLines = statusLines;
1017# endif /* !RT_OS_LINUX */
1018
1019 if (statusLines & TIOCM_CAR)
1020 newStatusLine |= PDMICHARPORT_STATUS_LINES_DCD;
1021 if (statusLines & TIOCM_RNG)
1022 newStatusLine |= PDMICHARPORT_STATUS_LINES_RI;
1023 if (statusLines & TIOCM_LE)
1024 newStatusLine |= PDMICHARPORT_STATUS_LINES_DSR;
1025 if (statusLines & TIOCM_CTS)
1026 newStatusLine |= PDMICHARPORT_STATUS_LINES_CTS;
1027 rc = pThis->pDrvCharPort->pfnNotifyStatusLinesChanged(pThis->pDrvCharPort, newStatusLine);
1028 }
1029
1030 return VINF_SUCCESS;
1031}
1032
1033/**
1034 * Unblock the monitor thread so it can respond to a state change.
1035 * We need to execute this code exactly once during initialization.
1036 * But we don't want to block --- therefore this dedicated thread.
1037 *
1038 * @returns a VBox status code.
1039 * @param pDrvIns The driver instance.
1040 * @param pThread The send thread.
1041 */
1042static DECLCALLBACK(int) drvHostSerialWakeupMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
1043{
1044# ifdef RT_OS_LINUX
1045 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1046 int rc = VINF_SUCCESS;
1047# if 0
1048 unsigned int uSerialLineFlags;
1049 unsigned int uSerialLineStatus;
1050 unsigned int uIoctl;
1051# endif
1052
1053 /*
1054 * Linux is a bit difficult as the thread is sleeping in an ioctl call.
1055 * So there is no way to have a wakeup pipe.
1056 *
1057 * 1. Thatswhy we set the serial device into loopback mode and change one of the
1058 * modem control bits.
1059 * This should make the ioctl call return.
1060 *
1061 * 2. We still got reports about long shutdown times. It may bepossible
1062 * that the loopback mode is not implemented on all devices.
1063 * The next possible solution is to close the device file to make the ioctl
1064 * return with EBADF and be able to suspend the thread.
1065 *
1066 * 3. The second approach doesn't work too, the ioctl doesn't return.
1067 * But it seems that the ioctl is interruptible (return code in errno is EINTR).
1068 */
1069
1070# if 0 /* Disabled because it does not work for all. */
1071 /* Get current status of control lines. */
1072 rc = ioctl(pThis->DeviceFile, TIOCMGET, &uSerialLineStatus);
1073 if (rc < 0)
1074 goto ioctl_error;
1075
1076 uSerialLineFlags = TIOCM_LOOP;
1077 rc = ioctl(pThis->DeviceFile, TIOCMBIS, &uSerialLineFlags);
1078 if (rc < 0)
1079 goto ioctl_error;
1080
1081 /*
1082 * Change current level on the RTS pin to make the ioctl call return in the
1083 * monitor thread.
1084 */
1085 uIoctl = (uSerialLineStatus & TIOCM_CTS) ? TIOCMBIC : TIOCMBIS;
1086 uSerialLineFlags = TIOCM_RTS;
1087
1088 rc = ioctl(pThis->DeviceFile, uIoctl, &uSerialLineFlags);
1089 if (rc < 0)
1090 goto ioctl_error;
1091
1092 /* Change RTS back to the previous level. */
1093 uIoctl = (uIoctl == TIOCMBIC) ? TIOCMBIS : TIOCMBIC;
1094
1095 rc = ioctl(pThis->DeviceFile, uIoctl, &uSerialLineFlags);
1096 if (rc < 0)
1097 goto ioctl_error;
1098
1099 /*
1100 * Set serial device into normal state.
1101 */
1102 uSerialLineFlags = TIOCM_LOOP;
1103 rc = ioctl(pThis->DeviceFile, TIOCMBIC, &uSerialLineFlags);
1104 if (rc >= 0)
1105 return VINF_SUCCESS;
1106
1107ioctl_error:
1108 PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail",
1109 N_("Ioctl failed for serial host device '%s' (%Rrc). The device will not work properly"),
1110 pThis->pszDevicePath, RTErrConvertFromErrno(errno));
1111# endif
1112
1113# if 0
1114 /* Close file to make ioctl return. */
1115 RTFileClose(pData->DeviceFile);
1116 /* Open again to make use after suspend possible again. */
1117 rc = RTFileOpen(&pData->DeviceFile, pData->pszDevicePath, RTFILE_O_OPEN | RTFILE_O_READWRITE);
1118 AssertMsgRC(rc, ("Opening device file again failed rc=%Rrc\n", rc));
1119
1120 if (RT_FAILURE(rc))
1121 PDMDrvHlpVMSetRuntimeError(pDrvIns, false, "DrvHostSerialFail",
1122 N_("Opening failed for serial host device '%s' (%Rrc). The device will not work"),
1123 pData->pszDevicePath, rc);
1124# endif
1125
1126 rc = RTThreadPoke(pThread->Thread);
1127 if (RT_FAILURE(rc))
1128 PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail",
1129 N_("Suspending serial monitor thread failed for serial device '%s' (%Rrc). The shutdown may take longer than expected"),
1130 pThis->pszDevicePath, RTErrConvertFromErrno(rc));
1131
1132# else /* !RT_OS_LINUX*/
1133
1134 /* In polling mode there is nobody to wake up (PDMThread will cancel the sleep). */
1135 NOREF(pDrvIns);
1136 NOREF(pThread);
1137
1138# endif /* RT_OS_LINUX */
1139
1140 return VINF_SUCCESS;
1141}
1142#endif /* RT_OS_LINUX || RT_OS_DARWIN || RT_OS_SOLARIS */
1143
1144/**
1145 * Set the modem lines.
1146 *
1147 * @returns VBox status code
1148 * @param pInterface Pointer to the interface structure.
1149 * @param RequestToSend Set to true if this control line should be made active.
1150 * @param DataTerminalReady Set to true if this control line should be made active.
1151 */
1152static DECLCALLBACK(int) drvHostSerialSetModemLines(PPDMICHARCONNECTOR pInterface, bool RequestToSend, bool DataTerminalReady)
1153{
1154 PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
1155
1156#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1157 int modemStateSet = 0;
1158 int modemStateClear = 0;
1159
1160 if (RequestToSend)
1161 modemStateSet |= TIOCM_RTS;
1162 else
1163 modemStateClear |= TIOCM_RTS;
1164
1165 if (DataTerminalReady)
1166 modemStateSet |= TIOCM_DTR;
1167 else
1168 modemStateClear |= TIOCM_DTR;
1169
1170 if (modemStateSet)
1171 ioctl(pThis->DeviceFile, TIOCMBIS, &modemStateSet);
1172
1173 if (modemStateClear)
1174 ioctl(pThis->DeviceFile, TIOCMBIC, &modemStateClear);
1175#elif defined(RT_OS_WINDOWS)
1176 if (RequestToSend)
1177 EscapeCommFunction(pThis->hDeviceFile, SETRTS);
1178 else
1179 EscapeCommFunction(pThis->hDeviceFile, CLRRTS);
1180
1181 if (DataTerminalReady)
1182 EscapeCommFunction(pThis->hDeviceFile, SETDTR);
1183 else
1184 EscapeCommFunction(pThis->hDeviceFile, CLRDTR);
1185#endif
1186
1187 return VINF_SUCCESS;
1188}
1189
1190/**
1191 * Sets the TD line into break condition.
1192 *
1193 * @returns VBox status code.
1194 * @param pInterface Pointer to the interface structure containing the called function pointer.
1195 * @param fBreak Set to true to let the device send a break false to put into normal operation.
1196 * @thread Any thread.
1197 */
1198static DECLCALLBACK(int) drvHostSerialSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak)
1199{
1200 PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
1201
1202#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1203 if (fBreak)
1204 ioctl(pThis->DeviceFile, TIOCSBRK);
1205 else
1206 ioctl(pThis->DeviceFile, TIOCCBRK);
1207
1208#elif defined(RT_OS_WINDOWS)
1209 if (fBreak)
1210 SetCommBreak(pThis->hDeviceFile);
1211 else
1212 ClearCommBreak(pThis->hDeviceFile);
1213#endif
1214
1215 return VINF_SUCCESS;
1216}
1217
1218/* -=-=-=-=- driver interface -=-=-=-=- */
1219
1220/**
1221 * Destruct a char driver instance.
1222 *
1223 * Most VM resources are freed by the VM. This callback is provided so that
1224 * any non-VM resources can be freed correctly.
1225 *
1226 * @param pDrvIns The driver instance data.
1227 */
1228static DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns)
1229{
1230 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1231 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
1232 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1233
1234 /* Empty the send queue */
1235 pThis->iSendQueueTail = pThis->iSendQueueHead = 0;
1236
1237 RTSemEventDestroy(pThis->SendSem);
1238 pThis->SendSem = NIL_RTSEMEVENT;
1239
1240#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1241
1242 if (pThis->WakeupPipeW != NIL_RTFILE)
1243 {
1244 int rc = RTFileClose(pThis->WakeupPipeW);
1245 AssertRC(rc);
1246 pThis->WakeupPipeW = NIL_RTFILE;
1247 }
1248 if (pThis->WakeupPipeR != NIL_RTFILE)
1249 {
1250 int rc = RTFileClose(pThis->WakeupPipeR);
1251 AssertRC(rc);
1252 pThis->WakeupPipeR = NIL_RTFILE;
1253 }
1254# if defined(RT_OS_DARWIN)
1255 if (pThis->DeviceFileR != NIL_RTFILE)
1256 {
1257 if (pThis->DeviceFileR != pThis->DeviceFile)
1258 {
1259 int rc = RTFileClose(pThis->DeviceFileR);
1260 AssertRC(rc);
1261 }
1262 pThis->DeviceFileR = NIL_RTFILE;
1263 }
1264# endif
1265 if (pThis->DeviceFile != NIL_RTFILE)
1266 {
1267 int rc = RTFileClose(pThis->DeviceFile);
1268 AssertRC(rc);
1269 pThis->DeviceFile = NIL_RTFILE;
1270 }
1271
1272#elif defined(RT_OS_WINDOWS)
1273
1274 CloseHandle(pThis->hEventRecv);
1275 CloseHandle(pThis->hEventSend);
1276 CancelIo(pThis->hDeviceFile);
1277 CloseHandle(pThis->hDeviceFile);
1278
1279#endif
1280}
1281
1282/**
1283 * Construct a char driver instance.
1284 *
1285 * @copydoc FNPDMDRVCONSTRUCT
1286 */
1287static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1288{
1289 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1290 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
1291 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1292
1293 /*
1294 * Init basic data members and interfaces.
1295 */
1296#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1297 pThis->DeviceFile = NIL_RTFILE;
1298# ifdef RT_OS_DARWIN
1299 pThis->DeviceFileR = NIL_RTFILE;
1300# endif
1301 pThis->WakeupPipeR = NIL_RTFILE;
1302 pThis->WakeupPipeW = NIL_RTFILE;
1303#endif
1304 /* IBase. */
1305 pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface;
1306 /* ICharConnector. */
1307 pThis->ICharConnector.pfnWrite = drvHostSerialWrite;
1308 pThis->ICharConnector.pfnSetParameters = drvHostSerialSetParameters;
1309 pThis->ICharConnector.pfnSetModemLines = drvHostSerialSetModemLines;
1310 pThis->ICharConnector.pfnSetBreak = drvHostSerialSetBreak;
1311
1312/** @todo Initialize all members with NIL values!! The destructor is ALWAYS called. */
1313
1314 /*
1315 * Query configuration.
1316 */
1317 /* Device */
1318 int rc = CFGMR3QueryStringAlloc(pCfg, "DevicePath", &pThis->pszDevicePath);
1319 if (RT_FAILURE(rc))
1320 {
1321 AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc));
1322 return rc;
1323 }
1324
1325 /*
1326 * Open the device
1327 */
1328#ifdef RT_OS_WINDOWS
1329
1330 pThis->hHaltEventSem = CreateEvent(NULL, FALSE, FALSE, NULL);
1331 AssertReturn(pThis->hHaltEventSem != NULL, VERR_NO_MEMORY);
1332
1333 pThis->hEventRecv = CreateEvent(NULL, FALSE, FALSE, NULL);
1334 AssertReturn(pThis->hEventRecv != NULL, VERR_NO_MEMORY);
1335
1336 pThis->hEventSend = CreateEvent(NULL, FALSE, FALSE, NULL);
1337 AssertReturn(pThis->hEventSend != NULL, VERR_NO_MEMORY);
1338
1339 HANDLE hFile = CreateFile(pThis->pszDevicePath,
1340 GENERIC_READ | GENERIC_WRITE,
1341 0, // must be opened with exclusive access
1342 NULL, // no SECURITY_ATTRIBUTES structure
1343 OPEN_EXISTING, // must use OPEN_EXISTING
1344 FILE_FLAG_OVERLAPPED, // overlapped I/O
1345 NULL); // no template file
1346 if (hFile == INVALID_HANDLE_VALUE)
1347 rc = RTErrConvertFromWin32(GetLastError());
1348 else
1349 {
1350 pThis->hDeviceFile = hFile;
1351 /* for overlapped read */
1352 if (!SetCommMask(hFile, EV_RXCHAR | EV_CTS | EV_DSR | EV_RING | EV_RLSD))
1353 {
1354 LogRel(("HostSerial#%d: SetCommMask failed with error %d.\n", pDrvIns->iInstance, GetLastError()));
1355 return VERR_FILE_IO_ERROR;
1356 }
1357 rc = VINF_SUCCESS;
1358 }
1359
1360#else
1361
1362 rc = RTFileOpen(&pThis->DeviceFile, pThis->pszDevicePath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
1363# ifdef RT_OS_DARWIN
1364 if (RT_SUCCESS(rc))
1365 rc = RTFileOpen(&pThis->DeviceFileR, pThis->pszDevicePath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
1366# endif
1367
1368
1369#endif
1370
1371 if (RT_FAILURE(rc))
1372 {
1373 AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pThis->pszDevicePath, rc));
1374 switch (rc)
1375 {
1376 case VERR_ACCESS_DENIED:
1377 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1378#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1379 N_("Cannot open host device '%s' for read/write access. Check the permissions "
1380 "of that device ('/bin/ls -l %s'): Most probably you need to be member "
1381 "of the device group. Make sure that you logout/login after changing "
1382 "the group settings of the current user"),
1383#else
1384 N_("Cannot open host device '%s' for read/write access. Check the permissions "
1385 "of that device"),
1386#endif
1387 pThis->pszDevicePath, pThis->pszDevicePath);
1388 default:
1389 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1390 N_("Failed to open host device '%s'"),
1391 pThis->pszDevicePath);
1392 }
1393 }
1394
1395 /* Set to non blocking I/O */
1396#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1397
1398 fcntl(pThis->DeviceFile, F_SETFL, O_NONBLOCK);
1399# ifdef RT_OS_DARWIN
1400 fcntl(pThis->DeviceFileR, F_SETFL, O_NONBLOCK);
1401# endif
1402 int aFDs[2];
1403 if (pipe(aFDs) != 0)
1404 {
1405 rc = RTErrConvertFromErrno(errno);
1406 AssertRC(rc);
1407 return rc;
1408 }
1409 pThis->WakeupPipeR = aFDs[0];
1410 pThis->WakeupPipeW = aFDs[1];
1411
1412#elif defined(RT_OS_WINDOWS)
1413
1414 /* Set the COMMTIMEOUTS to get non blocking I/O */
1415 COMMTIMEOUTS comTimeout;
1416
1417 comTimeout.ReadIntervalTimeout = MAXDWORD;
1418 comTimeout.ReadTotalTimeoutMultiplier = 0;
1419 comTimeout.ReadTotalTimeoutConstant = 0;
1420 comTimeout.WriteTotalTimeoutMultiplier = 0;
1421 comTimeout.WriteTotalTimeoutConstant = 0;
1422
1423 SetCommTimeouts(pThis->hDeviceFile, &comTimeout);
1424
1425#endif
1426
1427 /*
1428 * Get the ICharPort interface of the above driver/device.
1429 */
1430 pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT);
1431 if (!pThis->pDrvCharPort)
1432 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no char port interface above"), pDrvIns->iInstance);
1433
1434 /*
1435 * Create the receive, send and monitor threads pluss the related send semaphore.
1436 */
1437 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvHostSerialRecvThread, drvHostSerialWakeupRecvThread, 0, RTTHREADTYPE_IO, "SerRecv");
1438 if (RT_FAILURE(rc))
1439 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create receive thread"), pDrvIns->iInstance);
1440
1441 rc = RTSemEventCreate(&pThis->SendSem);
1442 AssertRC(rc);
1443
1444 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSendThread, pThis, drvHostSerialSendThread, drvHostSerialWakeupSendThread, 0, RTTHREADTYPE_IO, "SerSend");
1445 if (RT_FAILURE(rc))
1446 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create send thread"), pDrvIns->iInstance);
1447
1448#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1449 /* Linux & darwin needs a separate thread which monitors the status lines. */
1450# ifndef RT_OS_LINUX
1451 ioctl(pThis->DeviceFile, TIOCMGET, &pThis->fStatusLines);
1452# endif
1453 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pMonitorThread, pThis, drvHostSerialMonitorThread, drvHostSerialWakeupMonitorThread, 0, RTTHREADTYPE_IO, "SerMon");
1454 if (RT_FAILURE(rc))
1455 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create monitor thread"), pDrvIns->iInstance);
1456#endif
1457
1458 /*
1459 * Register release statistics.
1460 */
1461 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance);
1462 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance);
1463#ifdef RT_OS_DARWIN /* new Write code, not darwin specific. */
1464 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatSendOverflows, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes overflowed", "/Devices/HostSerial%d/SendOverflow", pDrvIns->iInstance);
1465#endif
1466
1467 return VINF_SUCCESS;
1468}
1469
1470/**
1471 * Char driver registration record.
1472 */
1473const PDMDRVREG g_DrvHostSerial =
1474{
1475 /* u32Version */
1476 PDM_DRVREG_VERSION,
1477 /* szName */
1478 "Host Serial",
1479 /* szRCMod */
1480 "",
1481 /* szR0Mod */
1482 "",
1483/* pszDescription */
1484 "Host serial driver.",
1485 /* fFlags */
1486 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1487 /* fClass. */
1488 PDM_DRVREG_CLASS_CHAR,
1489 /* cMaxInstances */
1490 ~0,
1491 /* cbInstance */
1492 sizeof(DRVHOSTSERIAL),
1493 /* pfnConstruct */
1494 drvHostSerialConstruct,
1495 /* pfnDestruct */
1496 drvHostSerialDestruct,
1497 /* pfnRelocate */
1498 NULL,
1499 /* pfnIOCtl */
1500 NULL,
1501 /* pfnPowerOn */
1502 NULL,
1503 /* pfnReset */
1504 NULL,
1505 /* pfnSuspend */
1506 NULL,
1507 /* pfnResume */
1508 NULL,
1509 /* pfnAttach */
1510 NULL,
1511 /* pfnDetach */
1512 NULL,
1513 /* pfnPowerOff */
1514 NULL,
1515 /* pfnSoftReset */
1516 NULL,
1517 /* u32EndVersion */
1518 PDM_DRVREG_VERSION
1519};
1520
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette