VirtualBox

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

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

DrvHostSerial: build fixes.

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