VirtualBox

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

Last change on this file since 37286 was 35769, checked in by vboxsync, 14 years ago

DrvHostSerial: better fix for timing problems over host serial

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