VirtualBox

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

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

DrvHostSerial: review.

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