VirtualBox

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

Last change on this file since 12334 was 12334, checked in by vboxsync, 17 years ago

DrvHostSerial: darwin fix.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette