VirtualBox

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

Last change on this file since 69397 was 69397, checked in by vboxsync, 7 years ago

Serial: Don't switch to polling mode if the TIOCMIWAIT ioctl was interrupted which is a valid case (when tcsetattr gets called) and broke certain serial devices

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