VirtualBox

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

Last change on this file since 34714 was 34215, checked in by vboxsync, 14 years ago

Serial/host: removed debug output

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