VirtualBox

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

Last change on this file since 46727 was 45061, checked in by vboxsync, 12 years ago

Review of PDM driver destructors making sure that variables they use are correctly initialized in the constructor. Found several RTFileClose(0) cases.

  • 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 45061 2013-03-18 14:09:03Z vboxsync $ */
2/** @file
3 * VBox stream I/O devices: Host serial driver
4 */
5
6/*
7 * Copyright (C) 2006-2012 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 if (pThis->SendSem != NIL_RTSEMEVENT)
1073 {
1074 RTSemEventDestroy(pThis->SendSem);
1075 pThis->SendSem = NIL_RTSEMEVENT;
1076 }
1077
1078 int rc;
1079#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1080
1081 rc = RTPipeClose(pThis->hWakeupPipeW); AssertRC(rc);
1082 pThis->hWakeupPipeW = NIL_RTPIPE;
1083 rc = RTPipeClose(pThis->hWakeupPipeR); AssertRC(rc);
1084 pThis->hWakeupPipeR = NIL_RTPIPE;
1085
1086# if defined(RT_OS_DARWIN)
1087 if (pThis->hDeviceFileR != NIL_RTFILE)
1088 {
1089 if (pThis->hDeviceFileR != pThis->hDeviceFile)
1090 {
1091 rc = RTFileClose(pThis->hDeviceFileR);
1092 AssertRC(rc);
1093 }
1094 pThis->hDeviceFileR = NIL_RTFILE;
1095 }
1096# endif
1097 if (pThis->hDeviceFile != NIL_RTFILE)
1098 {
1099 rc = RTFileClose(pThis->hDeviceFile); AssertRC(rc);
1100 pThis->hDeviceFile = NIL_RTFILE;
1101 }
1102
1103#elif defined(RT_OS_WINDOWS)
1104 CloseHandle(pThis->hEventRecv);
1105 CloseHandle(pThis->hEventSend);
1106 CancelIo(pThis->hDeviceFile);
1107 CloseHandle(pThis->hDeviceFile);
1108
1109#endif
1110
1111 if (pThis->pszDevicePath)
1112 {
1113 MMR3HeapFree(pThis->pszDevicePath);
1114 pThis->pszDevicePath = NULL;
1115 }
1116}
1117
1118/**
1119 * Construct a char driver instance.
1120 *
1121 * @copydoc FNPDMDRVCONSTRUCT
1122 */
1123static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t /*fFlags*/)
1124{
1125 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1126 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
1127 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1128
1129 /*
1130 * Init basic data members and interfaces.
1131 */
1132#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1133 pThis->hDeviceFile = NIL_RTFILE;
1134# ifdef RT_OS_DARWIN
1135 pThis->hDeviceFileR = NIL_RTFILE;
1136# endif
1137 pThis->hWakeupPipeR = NIL_RTPIPE;
1138 pThis->hWakeupPipeW = NIL_RTPIPE;
1139#elif defined(RT_OS_WINDOWS)
1140 pThis->hEventRecv = INVALID_HANDLE_VALUE;
1141 pThis->hEventSend = INVALID_HANDLE_VALUE;
1142 pThis->hDeviceFile = INVALID_HANDLE_VALUE;
1143#endif
1144 pThis->SendSem = NIL_RTSEMEVENT;
1145 /* IBase. */
1146 pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface;
1147 /* ICharConnector. */
1148 pThis->ICharConnector.pfnWrite = drvHostSerialWrite;
1149 pThis->ICharConnector.pfnSetParameters = drvHostSerialSetParameters;
1150 pThis->ICharConnector.pfnSetModemLines = drvHostSerialSetModemLines;
1151 pThis->ICharConnector.pfnSetBreak = drvHostSerialSetBreak;
1152
1153 /*
1154 * Query configuration.
1155 */
1156 /* Device */
1157 int rc = CFGMR3QueryStringAlloc(pCfg, "DevicePath", &pThis->pszDevicePath);
1158 if (RT_FAILURE(rc))
1159 {
1160 AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc));
1161 return rc;
1162 }
1163
1164 /*
1165 * Open the device
1166 */
1167#ifdef RT_OS_WINDOWS
1168
1169 pThis->hHaltEventSem = CreateEvent(NULL, FALSE, FALSE, NULL);
1170 AssertReturn(pThis->hHaltEventSem != NULL, VERR_NO_MEMORY);
1171
1172 pThis->hEventRecv = CreateEvent(NULL, FALSE, FALSE, NULL);
1173 AssertReturn(pThis->hEventRecv != NULL, VERR_NO_MEMORY);
1174
1175 pThis->hEventSend = CreateEvent(NULL, FALSE, FALSE, NULL);
1176 AssertReturn(pThis->hEventSend != NULL, VERR_NO_MEMORY);
1177
1178 HANDLE hFile = CreateFile(pThis->pszDevicePath,
1179 GENERIC_READ | GENERIC_WRITE,
1180 0, // must be opened with exclusive access
1181 NULL, // no SECURITY_ATTRIBUTES structure
1182 OPEN_EXISTING, // must use OPEN_EXISTING
1183 FILE_FLAG_OVERLAPPED, // overlapped I/O
1184 NULL); // no template file
1185 if (hFile == INVALID_HANDLE_VALUE)
1186 rc = RTErrConvertFromWin32(GetLastError());
1187 else
1188 {
1189 pThis->hDeviceFile = hFile;
1190 /* for overlapped read */
1191 if (!SetCommMask(hFile, EV_RXCHAR | EV_CTS | EV_DSR | EV_RING | EV_RLSD))
1192 {
1193 LogRel(("HostSerial#%d: SetCommMask failed with error %d.\n", pDrvIns->iInstance, GetLastError()));
1194 return VERR_FILE_IO_ERROR;
1195 }
1196 rc = VINF_SUCCESS;
1197 }
1198
1199#else /* !RT_OS_WINDOWS */
1200
1201 uint32_t fOpen = RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;
1202# ifdef RT_OS_LINUX
1203 /* This seems to be necessary on some Linux hosts, otherwise we hang here forever. */
1204 fOpen |= RTFILE_O_NON_BLOCK;
1205# endif
1206 rc = RTFileOpen(&pThis->hDeviceFile, pThis->pszDevicePath, fOpen);
1207# ifdef RT_OS_LINUX
1208 /* RTFILE_O_NON_BLOCK not supported? */
1209 if (rc == VERR_INVALID_PARAMETER)
1210 rc = RTFileOpen(&pThis->hDeviceFile, pThis->pszDevicePath, fOpen & ~RTFILE_O_NON_BLOCK);
1211# endif
1212# ifdef RT_OS_DARWIN
1213 if (RT_SUCCESS(rc))
1214 rc = RTFileOpen(&pThis->hDeviceFileR, pThis->pszDevicePath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
1215# endif
1216
1217
1218#endif /* !RT_OS_WINDOWS */
1219
1220 if (RT_FAILURE(rc))
1221 {
1222 AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pThis->pszDevicePath, rc));
1223 switch (rc)
1224 {
1225 case VERR_ACCESS_DENIED:
1226 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1227#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1228 N_("Cannot open host device '%s' for read/write access. Check the permissions "
1229 "of that device ('/bin/ls -l %s'): Most probably you need to be member "
1230 "of the device group. Make sure that you logout/login after changing "
1231 "the group settings of the current user"),
1232#else
1233 N_("Cannot open host device '%s' for read/write access. Check the permissions "
1234 "of that device"),
1235#endif
1236 pThis->pszDevicePath, pThis->pszDevicePath);
1237 default:
1238 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1239 N_("Failed to open host device '%s'"),
1240 pThis->pszDevicePath);
1241 }
1242 }
1243
1244 /* Set to non blocking I/O */
1245#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1246
1247 fcntl(RTFileToNative(pThis->hDeviceFile), F_SETFL, O_NONBLOCK);
1248# ifdef RT_OS_DARWIN
1249 fcntl(RTFileToNative(pThis->hDeviceFileR), F_SETFL, O_NONBLOCK);
1250# endif
1251 rc = RTPipeCreate(&pThis->hWakeupPipeR, &pThis->hWakeupPipeW, 0 /*fFlags*/);
1252 AssertRCReturn(rc, rc);
1253
1254#elif defined(RT_OS_WINDOWS)
1255
1256 /* Set the COMMTIMEOUTS to get non blocking I/O */
1257 COMMTIMEOUTS comTimeout;
1258
1259 comTimeout.ReadIntervalTimeout = MAXDWORD;
1260 comTimeout.ReadTotalTimeoutMultiplier = 0;
1261 comTimeout.ReadTotalTimeoutConstant = 0;
1262 comTimeout.WriteTotalTimeoutMultiplier = 0;
1263 comTimeout.WriteTotalTimeoutConstant = 0;
1264
1265 SetCommTimeouts(pThis->hDeviceFile, &comTimeout);
1266
1267#endif
1268
1269 /*
1270 * Get the ICharPort interface of the above driver/device.
1271 */
1272 pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT);
1273 if (!pThis->pDrvCharPort)
1274 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no char port interface above"), pDrvIns->iInstance);
1275
1276 /*
1277 * Create the receive, send and monitor threads plus the related send semaphore.
1278 */
1279 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvHostSerialRecvThread, drvHostSerialWakeupRecvThread, 0, RTTHREADTYPE_IO, "SerRecv");
1280 if (RT_FAILURE(rc))
1281 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create receive thread"), pDrvIns->iInstance);
1282
1283 rc = RTSemEventCreate(&pThis->SendSem);
1284 AssertRC(rc);
1285
1286 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSendThread, pThis, drvHostSerialSendThread, drvHostSerialWakeupSendThread, 0, RTTHREADTYPE_IO, "SerSend");
1287 if (RT_FAILURE(rc))
1288 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create send thread"), pDrvIns->iInstance);
1289
1290#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1291 /* Linux & darwin needs a separate thread which monitors the status lines. */
1292# ifndef RT_OS_LINUX
1293 ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMGET, &pThis->fStatusLines);
1294# endif
1295 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pMonitorThread, pThis, drvHostSerialMonitorThread, drvHostSerialWakeupMonitorThread, 0, RTTHREADTYPE_IO, "SerMon");
1296 if (RT_FAILURE(rc))
1297 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create monitor thread"), pDrvIns->iInstance);
1298#endif
1299
1300 /*
1301 * Register release statistics.
1302 */
1303 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance);
1304 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance);
1305#ifdef RT_OS_DARWIN /* new Write code, not darwin specific. */
1306 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatSendOverflows, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes overflowed", "/Devices/HostSerial%d/SendOverflow", pDrvIns->iInstance);
1307#endif
1308
1309 return VINF_SUCCESS;
1310}
1311
1312/**
1313 * Char driver registration record.
1314 */
1315const PDMDRVREG g_DrvHostSerial =
1316{
1317 /* u32Version */
1318 PDM_DRVREG_VERSION,
1319 /* szName */
1320 "Host Serial",
1321 /* szRCMod */
1322 "",
1323 /* szR0Mod */
1324 "",
1325/* pszDescription */
1326 "Host serial driver.",
1327 /* fFlags */
1328 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1329 /* fClass. */
1330 PDM_DRVREG_CLASS_CHAR,
1331 /* cMaxInstances */
1332 ~0U,
1333 /* cbInstance */
1334 sizeof(DRVHOSTSERIAL),
1335 /* pfnConstruct */
1336 drvHostSerialConstruct,
1337 /* pfnDestruct */
1338 drvHostSerialDestruct,
1339 /* pfnRelocate */
1340 NULL,
1341 /* pfnIOCtl */
1342 NULL,
1343 /* pfnPowerOn */
1344 NULL,
1345 /* pfnReset */
1346 NULL,
1347 /* pfnSuspend */
1348 NULL,
1349 /* pfnResume */
1350 NULL,
1351 /* pfnAttach */
1352 NULL,
1353 /* pfnDetach */
1354 NULL,
1355 /* pfnPowerOff */
1356 NULL,
1357 /* pfnSoftReset */
1358 NULL,
1359 /* u32EndVersion */
1360 PDM_DRVREG_VERSION
1361};
1362
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