VirtualBox

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

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

typo

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