VirtualBox

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

Last change on this file since 40754 was 40438, checked in by vboxsync, 12 years ago

Serial: don't leave the loop on EINTR. We have a similar handling in DrvTAP.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use