VirtualBox

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

Last change on this file since 33939 was 33755, checked in by vboxsync, 14 years ago

DrvHostSerial: cosmetical fixes

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