VirtualBox

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

Last change on this file since 12381 was 12381, checked in by vboxsync, 16 years ago

DrvHostSerial: Open a new descriptor for reading the device to get around issue that the read select seems to block writing (fd lock?). Increase the buffer (darwin only) to make it possible to handle medium sized transfers (DevSerial *is* sending us data at the wrong rate).

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