1
/* $Id: DrvHostSerial.cpp $ */
3
* VBox stream I/O devices: Host serial driver
7
* Copyright (C) 2006-2012 Oracle Corporation
9
* This file is part of VirtualBox Open Source Edition (OSE), as
10
* available from http://www.virtualbox.org. This file is free software;
11
* you can redistribute it and/or modify it under the terms of the GNU
12
* General Public License (GPL) as published by the Free Software
13
* Foundation, in version 2 as it comes in the "COPYING" file of the
14
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
20
/*******************************************************************************
22
*******************************************************************************/
23
#define LOG_GROUP LOG_GROUP_DRV_HOST_SERIAL
24
#include <VBox/vmm/pdm.h>
29
#include <iprt/assert.h>
30
#include <iprt/file.h>
32
#include <iprt/pipe.h>
33
#include <iprt/semaphore.h>
34
#include <iprt/uuid.h>
36
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
39
# include <sys/termios.h>
43
# include <sys/types.h>
48
# include <sys/select.h>
50
# include <sys/poll.h>
52
# include <sys/ioctl.h>
57
* TIOCM_LOOP is not defined in the above header files for some reason but in asm/termios.h.
58
* But inclusion of this file however leads to compilation errors because of redefinition of some
59
* structs. That's why it is defined here until a better solution is found.
62
# define TIOCM_LOOP 0x8000
64
/* For linux custom baudrate code we also need serial_struct */
65
# include <linux/serial.h>
68
#elif defined(RT_OS_WINDOWS)
75
/*******************************************************************************
76
* Structures and Typedefs *
77
*******************************************************************************/
80
* Char driver instance data.
82
* @implements PDMICHARCONNECTOR
84
typedef struct DRVHOSTSERIAL
86
/** Pointer to the driver instance structure. */
88
/** Pointer to the char port interface of the driver/device above us. */
89
PPDMICHARPORT pDrvCharPort;
90
/** Our char interface. */
91
PDMICHARCONNECTOR ICharConnector;
92
/** Receive thread. */
93
PPDMTHREAD pRecvThread;
95
PPDMTHREAD pSendThread;
96
/** Status lines monitor thread. */
97
PPDMTHREAD pMonitorThread;
98
/** Send event semaphore */
101
/** the device path */
104
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
105
/** the device handle */
108
/** The device handle used for reading.
109
* Used to prevent the read select from blocking the writes. */
112
/** The read end of the control pipe */
114
/** The write end of the control pipe */
117
/** The current line status.
118
* Used by the polling version of drvHostSerialMonitorThread. */
121
#elif defined(RT_OS_WINDOWS)
122
/** the device handle */
124
/** The event semaphore for waking up the receive thread */
125
HANDLE hHaltEventSem;
126
/** The event semaphore for overlapped receiving */
128
/** For overlapped receiving */
129
OVERLAPPED overlappedRecv;
130
/** The event semaphore for overlapped sending */
132
/** For overlapped sending */
133
OVERLAPPED overlappedSend;
136
/** Internal send FIFO queue */
137
uint8_t volatile u8SendByte;
138
bool volatile fSending;
139
uint8_t Alignment[2];
141
/** Read/write statistics */
142
STAMCOUNTER StatBytesRead;
143
STAMCOUNTER StatBytesWritten;
145
/** The number of bytes we've dropped because the send queue
147
STAMCOUNTER StatSendOverflows;
149
} DRVHOSTSERIAL, *PDRVHOSTSERIAL;
152
/** Converts a pointer to DRVCHAR::ICharConnector to a PDRVHOSTSERIAL. */
153
#define PDMICHAR_2_DRVHOSTSERIAL(pInterface) RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ICharConnector)
156
/* -=-=-=-=- IBase -=-=-=-=- */
159
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
161
static DECLCALLBACK(void *) drvHostSerialQueryInterface(PPDMIBASE pInterface, const char *pszIID)
163
PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
164
PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
166
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
167
PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARCONNECTOR, &pThis->ICharConnector);
172
/* -=-=-=-=- ICharConnector -=-=-=-=- */
174
/** @copydoc PDMICHARCONNECTOR::pfnWrite */
175
static DECLCALLBACK(int) drvHostSerialWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
177
PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
178
const uint8_t *pbBuffer = (const uint8_t *)pvBuf;
180
LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
182
for (uint32_t i = 0; i < cbWrite; i++)
184
if (ASMAtomicXchgBool(&pThis->fSending, true))
185
return VERR_BUFFER_OVERFLOW;
187
pThis->u8SendByte = pbBuffer[i];
188
RTSemEventSignal(pThis->SendSem);
189
STAM_COUNTER_INC(&pThis->StatBytesWritten);
194
static DECLCALLBACK(int) drvHostSerialSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
196
PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
197
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
198
struct termios *termiosSetup;
200
#elif defined(RT_OS_WINDOWS)
204
LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
206
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
207
termiosSetup = (struct termios *)RTMemTmpAllocZ(sizeof(struct termios));
209
/* Enable receiver */
210
termiosSetup->c_cflag |= (CLOCAL | CREAD);
267
struct serial_struct serialStruct;
268
if (ioctl(RTFileToNative(pThis->hDeviceFile), TIOCGSERIAL, &serialStruct) != -1)
270
serialStruct.custom_divisor = serialStruct.baud_base / Bps;
271
if (!serialStruct.custom_divisor)
272
serialStruct.custom_divisor = 1;
273
serialStruct.flags &= ~ASYNC_SPD_MASK;
274
serialStruct.flags |= ASYNC_SPD_CUST;
275
ioctl(RTFileToNative(pThis->hDeviceFile), TIOCSSERIAL, &serialStruct);
280
#else /* !RT_OS_LINUX */
282
#endif /* !RT_OS_LINUX */
285
cfsetispeed(termiosSetup, baud_rate);
286
cfsetospeed(termiosSetup, baud_rate);
291
termiosSetup->c_cflag |= PARENB;
294
termiosSetup->c_cflag |= (PARENB | PARODD);
305
termiosSetup->c_cflag |= CS5;
308
termiosSetup->c_cflag |= CS6;
311
termiosSetup->c_cflag |= CS7;
314
termiosSetup->c_cflag |= CS8;
323
termiosSetup->c_cflag |= CSTOPB;
328
/* set serial port to raw input */
329
termiosSetup->c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ECHOK | ISIG | IEXTEN);
331
tcsetattr(RTFileToNative(pThis->hDeviceFile), TCSANOW, termiosSetup);
332
RTMemTmpFree(termiosSetup);
336
* XXX In Linux, if a thread calls tcsetattr while the monitor thread is
337
* waiting in ioctl for a modem status change then 8250.c wrongly disables
338
* modem irqs and so the monitor thread never gets released. The workaround
339
* is to send a signal after each tcsetattr.
341
RTThreadPoke(pThis->pMonitorThread->Thread);
344
#elif defined(RT_OS_WINDOWS)
345
comSetup = (LPDCB)RTMemTmpAllocZ(sizeof(DCB));
347
comSetup->DCBlength = sizeof(DCB);
352
comSetup->BaudRate = CBR_110;
355
comSetup->BaudRate = CBR_300;
358
comSetup->BaudRate = CBR_600;
361
comSetup->BaudRate = CBR_1200;
364
comSetup->BaudRate = CBR_2400;
367
comSetup->BaudRate = CBR_4800;
370
comSetup->BaudRate = CBR_9600;
373
comSetup->BaudRate = CBR_14400;
376
comSetup->BaudRate = CBR_19200;
379
comSetup->BaudRate = CBR_38400;
382
comSetup->BaudRate = CBR_57600;
385
comSetup->BaudRate = CBR_115200;
388
comSetup->BaudRate = CBR_9600;
391
comSetup->fBinary = TRUE;
392
comSetup->fOutxCtsFlow = FALSE;
393
comSetup->fOutxDsrFlow = FALSE;
394
comSetup->fDtrControl = DTR_CONTROL_DISABLE;
395
comSetup->fDsrSensitivity = FALSE;
396
comSetup->fTXContinueOnXoff = TRUE;
397
comSetup->fOutX = FALSE;
398
comSetup->fInX = FALSE;
399
comSetup->fErrorChar = FALSE;
400
comSetup->fNull = FALSE;
401
comSetup->fRtsControl = RTS_CONTROL_DISABLE;
402
comSetup->fAbortOnError = FALSE;
403
comSetup->wReserved = 0;
404
comSetup->XonLim = 5;
405
comSetup->XoffLim = 5;
406
comSetup->ByteSize = cDataBits;
411
comSetup->Parity = EVENPARITY;
414
comSetup->Parity = ODDPARITY;
417
comSetup->Parity = NOPARITY;
426
comSetup->StopBits = ONESTOPBIT;
429
comSetup->StopBits = TWOSTOPBITS;
435
comSetup->XonChar = 0;
436
comSetup->XoffChar = 0;
437
comSetup->ErrorChar = 0;
438
comSetup->EofChar = 0;
439
comSetup->EvtChar = 0;
441
SetCommState(pThis->hDeviceFile, comSetup);
442
RTMemTmpFree(comSetup);
443
#endif /* RT_OS_WINDOWS */
448
/* -=-=-=-=- receive thread -=-=-=-=- */
453
* @returns VINF_SUCCESS.
454
* @param ThreadSelf Thread handle to this thread.
455
* @param pvUser User argument.
457
static DECLCALLBACK(int) drvHostSerialSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
459
PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
461
if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
465
/* Make sure that the halt event semaphore is reset. */
466
DWORD dwRet = WaitForSingleObject(pThis->hHaltEventSem, 0);
469
haWait[0] = pThis->hEventSend;
470
haWait[1] = pThis->hHaltEventSem;
473
while (pThread->enmState == PDMTHREADSTATE_RUNNING)
475
int rc = RTSemEventWait(pThis->SendSem, RT_INDEFINITE_WAIT);
480
* Write the character to the host device.
482
while (pThread->enmState == PDMTHREADSTATE_RUNNING)
484
/* copy the send queue so we get a linear buffer with the maximal size. */
485
uint8_t ch = pThis->u8SendByte;
486
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
489
rc = RTFileWrite(pThis->hDeviceFile, &ch, 1, &cbWritten);
490
if (rc == VERR_TRY_AGAIN)
492
if (cbWritten < 1 && (RT_SUCCESS(rc) || rc == VERR_TRY_AGAIN))
494
/* ok, block till the device is ready for more (O_NONBLOCK) effect. */
496
while (pThread->enmState == PDMTHREADSTATE_RUNNING)
501
FD_SET(RTFileToNative(pThis->hDeviceFile), &WrSet);
504
FD_SET(RTFileToNative(pThis->hDeviceFile), &XcptSet);
506
uint64_t u64Now = RTTimeMilliTS();
508
rc = select(RTFileToNative(pThis->hDeviceFile) + 1, NULL, &WrSet, &XcptSet, NULL);
509
/** @todo check rc? */
512
Log2(("select wait for %dms\n", RTTimeMilliTS() - u64Now));
515
rc = RTFileWrite(pThis->hDeviceFile, &ch, 1, &cbWritten);
516
if (rc == VERR_TRY_AGAIN)
518
else if (RT_FAILURE(rc))
520
else if (cbWritten >= 1)
523
} /* wait/write loop */
526
#elif defined(RT_OS_WINDOWS)
527
/* perform an overlapped write operation. */
529
memset(&pThis->overlappedSend, 0, sizeof(pThis->overlappedSend));
530
pThis->overlappedSend.hEvent = pThis->hEventSend;
531
if (!WriteFile(pThis->hDeviceFile, &ch, 1, &cbWritten, &pThis->overlappedSend))
533
dwRet = GetLastError();
534
if (dwRet == ERROR_IO_PENDING)
537
* write blocked, wait for completion or wakeup...
539
dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE);
540
if (dwRet != WAIT_OBJECT_0)
542
AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event semaphore is set but the thread is still in running state\n"));
547
rc = RTErrConvertFromWin32(dwRet);
550
#endif /* RT_OS_WINDOWS */
553
LogRel(("HostSerial#%d: Serial Write failed with %Rrc; terminating send thread\n", pDrvIns->iInstance, rc));
556
ASMAtomicXchgBool(&pThis->fSending, false);
565
* Unblock the send thread so it can respond to a state change.
567
* @returns a VBox status code.
568
* @param pDrvIns The driver instance.
569
* @param pThread The send thread.
571
static DECLCALLBACK(int) drvHostSerialWakeupSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD /*pThread*/)
573
PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
576
rc = RTSemEventSignal(pThis->SendSem);
581
if (!SetEvent(pThis->hHaltEventSem))
582
return RTErrConvertFromWin32(GetLastError());
588
/* -=-=-=-=- receive thread -=-=-=-=- */
591
* Receive thread loop.
593
* This thread pushes data from the host serial device up the driver
594
* chain toward the serial device.
596
* @returns VINF_SUCCESS.
597
* @param ThreadSelf Thread handle to this thread.
598
* @param pvUser User argument.
600
static DECLCALLBACK(int) drvHostSerialRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
602
PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
603
uint8_t abBuffer[256];
604
uint8_t *pbBuffer = NULL;
605
size_t cbRemaining = 0; /* start by reading host data */
606
int rc = VINF_SUCCESS;
607
int rcThread = VINF_SUCCESS;
609
if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
613
/* Make sure that the halt event semaphore is reset. */
614
DWORD dwRet = WaitForSingleObject(pThis->hHaltEventSem, 0);
617
ahWait[0] = pThis->hEventRecv;
618
ahWait[1] = pThis->hHaltEventSem;
621
while (pThread->enmState == PDMTHREADSTATE_RUNNING)
625
/* Get a block of data from the host serial device. */
627
#if defined(RT_OS_DARWIN) /* poll is broken on x86 darwin, returns POLLNVAL. */
630
FD_SET(RTFileToNative(pThis->hDeviceFileR), &RdSet);
631
FD_SET(RTPipeToNative(pThis->hWakeupPipeR), &RdSet);
634
FD_SET(RTFileToNative(pThis->hDeviceFile), &XcptSet);
635
FD_SET(RTPipeToNative(pThis->hWakeupPipeR), &XcptSet);
636
# if 1 /* it seems like this select is blocking the write... */
637
rc = select(RT_MAX(RTPipeToNative(pThis->hWakeupPipeR), RTFileToNative(pThis->hDeviceFileR)) + 1,
638
&RdSet, NULL, &XcptSet, NULL);
640
struct timeval tv = { 0, 1000 };
641
rc = select(RTPipeToNative(pThis->hWakeupPipeR), RTFileToNative(pThis->hDeviceFileR) + 1,
642
&RdSet, NULL, &XcptSet, &tv);
647
rcThread = RTErrConvertFromErrno(err);
648
LogRel(("HostSerial#%d: select failed with errno=%d / %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, err, rcThread));
652
/* this might have changed in the meantime */
653
if (pThread->enmState != PDMTHREADSTATE_RUNNING)
658
/* drain the wakeup pipe */
660
if ( FD_ISSET(RTPipeToNative(pThis->hWakeupPipeR), &RdSet)
661
|| FD_ISSET(RTPipeToNative(pThis->hWakeupPipeR), &XcptSet))
663
rc = RTPipeRead(pThis->hWakeupPipeR, abBuffer, 1, &cbRead);
666
LogRel(("HostSerial#%d: draining the wakeup pipe failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
673
/* read data from the serial port. */
674
rc = RTFileRead(pThis->hDeviceFileR, abBuffer, sizeof(abBuffer), &cbRead);
677
LogRel(("HostSerial#%d: (1) Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
681
cbRemaining = cbRead;
683
#elif defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
686
struct pollfd aFDs[2];
687
aFDs[0].fd = RTFileToNative(pThis->hDeviceFile);
688
aFDs[0].events = POLLIN;
690
aFDs[1].fd = RTPipeToNative(pThis->hWakeupPipeR);
691
aFDs[1].events = POLLIN | POLLERR | POLLHUP;
693
rc = poll(aFDs, RT_ELEMENTS(aFDs), -1);
700
* EINTR errors should be harmless, even if they are not supposed to occur in our setup.
702
Log(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, err, strerror(err)));
707
rcThread = RTErrConvertFromErrno(err);
708
LogRel(("HostSerial#%d: poll failed with errno=%d / %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, err, rcThread));
711
/* this might have changed in the meantime */
712
if (pThread->enmState != PDMTHREADSTATE_RUNNING)
714
if (rc > 0 && aFDs[1].revents)
716
if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
718
/* notification to terminate -- drain the pipe */
719
RTPipeRead(pThis->hWakeupPipeR, &abBuffer, 1, &cbRead);
722
rc = RTFileRead(pThis->hDeviceFile, abBuffer, sizeof(abBuffer), &cbRead);
725
/* don't terminate worker thread when data unavailable */
726
if (rc == VERR_TRY_AGAIN)
729
LogRel(("HostSerial#%d: (2) Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
733
cbRemaining = cbRead;
735
#elif defined(RT_OS_WINDOWS)
737
DWORD dwEventMask = 0;
738
DWORD dwNumberOfBytesTransferred;
740
memset(&pThis->overlappedRecv, 0, sizeof(pThis->overlappedRecv));
741
pThis->overlappedRecv.hEvent = pThis->hEventRecv;
743
if (!WaitCommEvent(pThis->hDeviceFile, &dwEventMask, &pThis->overlappedRecv))
745
dwRet = GetLastError();
746
if (dwRet == ERROR_IO_PENDING)
748
dwRet = WaitForMultipleObjects(2, ahWait, FALSE, INFINITE);
749
if (dwRet != WAIT_OBJECT_0)
751
/* notification to terminate */
752
AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event semaphore is set but the thread is still in running state\n"));
758
rcThread = RTErrConvertFromWin32(dwRet);
759
LogRel(("HostSerial#%d: Wait failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread));
763
/* this might have changed in the meantime */
764
if (pThread->enmState != PDMTHREADSTATE_RUNNING)
767
/* Check the event */
768
if (dwEventMask & EV_RXCHAR)
770
if (!ReadFile(pThis->hDeviceFile, abBuffer, sizeof(abBuffer), &dwNumberOfBytesTransferred, &pThis->overlappedRecv))
772
rcThread = RTErrConvertFromWin32(GetLastError());
773
LogRel(("HostSerial#%d: Read failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread));
776
cbRemaining = dwNumberOfBytesTransferred;
778
else if (dwEventMask & EV_BREAK)
780
Log(("HostSerial#%d: Detected break\n"));
781
rc = pThis->pDrvCharPort->pfnNotifyBreak(pThis->pDrvCharPort);
785
/* The status lines have changed. Notify the device. */
786
DWORD dwNewStatusLinesState = 0;
787
uint32_t uNewStatusLinesState = 0;
789
/* Get the new state */
790
if (GetCommModemStatus(pThis->hDeviceFile, &dwNewStatusLinesState))
792
if (dwNewStatusLinesState & MS_RLSD_ON)
793
uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_DCD;
794
if (dwNewStatusLinesState & MS_RING_ON)
795
uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_RI;
796
if (dwNewStatusLinesState & MS_DSR_ON)
797
uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_DSR;
798
if (dwNewStatusLinesState & MS_CTS_ON)
799
uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_CTS;
800
rc = pThis->pDrvCharPort->pfnNotifyStatusLinesChanged(pThis->pDrvCharPort, uNewStatusLinesState);
803
/* Notifying device failed, continue but log it */
804
LogRel(("HostSerial#%d: Notifying device failed with error %Rrc; continuing.\n", pDrvIns->iInstance, rc));
809
/* Getting new state failed, continue but log it */
810
LogRel(("HostSerial#%d: Getting status lines state failed with error %Rrc; continuing.\n", pDrvIns->iInstance, RTErrConvertFromWin32(GetLastError())));
815
Log(("Read %d bytes.\n", cbRemaining));
820
/* Send data to the guest. */
821
size_t cbProcessed = cbRemaining;
822
rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pbBuffer, &cbProcessed);
825
Assert(cbProcessed); Assert(cbProcessed <= cbRemaining);
826
pbBuffer += cbProcessed;
827
cbRemaining -= cbProcessed;
828
STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
830
else if (rc == VERR_TIMEOUT)
832
/* Normal case, just means that the guest didn't accept a new
833
* character before the timeout elapsed. Just retry. */
838
LogRel(("HostSerial#%d: NotifyRead failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
849
* Unblock the send thread so it can respond to a state change.
851
* @returns a VBox status code.
852
* @param pDrvIns The driver instance.
853
* @param pThread The send thread.
855
static DECLCALLBACK(int) drvHostSerialWakeupRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD /*pThread*/)
857
PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
858
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
860
return RTPipeWrite(pThis->hWakeupPipeW, "", 1, &cbIgnored);
862
#elif defined(RT_OS_WINDOWS)
863
if (!SetEvent(pThis->hHaltEventSem))
864
return RTErrConvertFromWin32(GetLastError());
871
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
872
/* -=-=-=-=- Monitor thread -=-=-=-=- */
875
* Monitor thread loop.
877
* This thread monitors the status lines and notifies the device
880
* @returns VINF_SUCCESS.
881
* @param ThreadSelf Thread handle to this thread.
882
* @param pvUser User argument.
884
static DECLCALLBACK(int) drvHostSerialMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
886
PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
887
int rc = VINF_SUCCESS;
888
unsigned long const uStatusLinesToCheck = TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS;
890
if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
895
unsigned int statusLines;
898
* Get the status line state.
900
rc = ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMGET, &statusLines);
903
PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail",
904
N_("Ioctl failed for serial host device '%s' (%Rrc). The device will not work properly"),
905
pThis->pszDevicePath, RTErrConvertFromErrno(errno));
909
uint32_t newStatusLine = 0;
911
if (statusLines & TIOCM_CAR)
912
newStatusLine |= PDMICHARPORT_STATUS_LINES_DCD;
913
if (statusLines & TIOCM_RNG)
914
newStatusLine |= PDMICHARPORT_STATUS_LINES_RI;
915
if (statusLines & TIOCM_DSR)
916
newStatusLine |= PDMICHARPORT_STATUS_LINES_DSR;
917
if (statusLines & TIOCM_CTS)
918
newStatusLine |= PDMICHARPORT_STATUS_LINES_CTS;
919
pThis->pDrvCharPort->pfnNotifyStatusLinesChanged(pThis->pDrvCharPort, newStatusLine);
921
if (PDMTHREADSTATE_RUNNING != pThread->enmState)
926
* Wait for status line change.
928
* XXX In Linux, if a thread calls tcsetattr while the monitor thread is
929
* waiting in ioctl for a modem status change then 8250.c wrongly disables
930
* modem irqs and so the monitor thread never gets released. The workaround
931
* is to send a signal after each tcsetattr.
933
ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMIWAIT, uStatusLinesToCheck);
935
/* Poll for status line change. */
936
if (!((statusLines ^ pThis->fStatusLines) & uStatusLinesToCheck))
937
PDMR3ThreadSleep(pThread, 500); /* 0.5 sec */
938
pThis->fStatusLines = statusLines;
941
while (PDMTHREADSTATE_RUNNING == pThread->enmState);
947
* Unblock the monitor thread so it can respond to a state change.
948
* We need to execute this code exactly once during initialization.
949
* But we don't want to block --- therefore this dedicated thread.
951
* @returns a VBox status code.
952
* @param pDrvIns The driver instance.
953
* @param pThread The send thread.
955
static DECLCALLBACK(int) drvHostSerialWakeupMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
958
PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
959
int rc = VINF_SUCCESS;
961
rc = RTThreadPoke(pThread->Thread);
963
PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail",
964
N_("Suspending serial monitor thread failed for serial device '%s' (%Rrc). The shutdown may take longer than expected"),
965
pThis->pszDevicePath, RTErrConvertFromErrno(rc));
967
# else /* !RT_OS_LINUX*/
969
/* In polling mode there is nobody to wake up (PDMThread will cancel the sleep). */
973
# endif /* RT_OS_LINUX */
977
#endif /* RT_OS_LINUX || RT_OS_DARWIN || RT_OS_SOLARIS */
980
* Set the modem lines.
982
* @returns VBox status code
983
* @param pInterface Pointer to the interface structure.
984
* @param RequestToSend Set to true if this control line should be made active.
985
* @param DataTerminalReady Set to true if this control line should be made active.
987
static DECLCALLBACK(int) drvHostSerialSetModemLines(PPDMICHARCONNECTOR pInterface, bool RequestToSend, bool DataTerminalReady)
989
PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
991
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
992
int modemStateSet = 0;
993
int modemStateClear = 0;
996
modemStateSet |= TIOCM_RTS;
998
modemStateClear |= TIOCM_RTS;
1000
if (DataTerminalReady)
1001
modemStateSet |= TIOCM_DTR;
1003
modemStateClear |= TIOCM_DTR;
1006
ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMBIS, &modemStateSet);
1008
if (modemStateClear)
1009
ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMBIC, &modemStateClear);
1011
#elif defined(RT_OS_WINDOWS)
1013
EscapeCommFunction(pThis->hDeviceFile, SETRTS);
1015
EscapeCommFunction(pThis->hDeviceFile, CLRRTS);
1017
if (DataTerminalReady)
1018
EscapeCommFunction(pThis->hDeviceFile, SETDTR);
1020
EscapeCommFunction(pThis->hDeviceFile, CLRDTR);
1024
return VINF_SUCCESS;
1028
* Sets the TD line into break condition.
1030
* @returns VBox status code.
1031
* @param pInterface Pointer to the interface structure containing the called function pointer.
1032
* @param fBreak Set to true to let the device send a break false to put into normal operation.
1033
* @thread Any thread.
1035
static DECLCALLBACK(int) drvHostSerialSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak)
1037
PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
1039
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1041
ioctl(RTFileToNative(pThis->hDeviceFile), TIOCSBRK);
1043
ioctl(RTFileToNative(pThis->hDeviceFile), TIOCCBRK);
1045
#elif defined(RT_OS_WINDOWS)
1047
SetCommBreak(pThis->hDeviceFile);
1049
ClearCommBreak(pThis->hDeviceFile);
1052
return VINF_SUCCESS;
1055
/* -=-=-=-=- driver interface -=-=-=-=- */
1058
* Destruct a char driver instance.
1060
* Most VM resources are freed by the VM. This callback is provided so that
1061
* any non-VM resources can be freed correctly.
1063
* @param pDrvIns The driver instance data.
1065
static DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns)
1067
PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1068
LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
1069
PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1071
/* Empty the send queue */
1072
if (pThis->SendSem != NIL_RTSEMEVENT)
1074
RTSemEventDestroy(pThis->SendSem);
1075
pThis->SendSem = NIL_RTSEMEVENT;
1078
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1080
int rc = RTPipeClose(pThis->hWakeupPipeW); AssertRC(rc);
1081
pThis->hWakeupPipeW = NIL_RTPIPE;
1082
rc = RTPipeClose(pThis->hWakeupPipeR); AssertRC(rc);
1083
pThis->hWakeupPipeR = NIL_RTPIPE;
1085
# if defined(RT_OS_DARWIN)
1086
if (pThis->hDeviceFileR != NIL_RTFILE)
1088
if (pThis->hDeviceFileR != pThis->hDeviceFile)
1090
rc = RTFileClose(pThis->hDeviceFileR);
1093
pThis->hDeviceFileR = NIL_RTFILE;
1096
if (pThis->hDeviceFile != NIL_RTFILE)
1098
rc = RTFileClose(pThis->hDeviceFile); AssertRC(rc);
1099
pThis->hDeviceFile = NIL_RTFILE;
1102
#elif defined(RT_OS_WINDOWS)
1103
CloseHandle(pThis->hEventRecv);
1104
CloseHandle(pThis->hEventSend);
1105
CancelIo(pThis->hDeviceFile);
1106
CloseHandle(pThis->hDeviceFile);
1110
if (pThis->pszDevicePath)
1112
MMR3HeapFree(pThis->pszDevicePath);
1113
pThis->pszDevicePath = NULL;
1118
* Construct a char driver instance.
1120
* @copydoc FNPDMDRVCONSTRUCT
1122
static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t /*fFlags*/)
1124
PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1125
LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
1126
PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1129
* Init basic data members and interfaces.
1131
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1132
pThis->hDeviceFile = NIL_RTFILE;
1133
# ifdef RT_OS_DARWIN
1134
pThis->hDeviceFileR = NIL_RTFILE;
1136
pThis->hWakeupPipeR = NIL_RTPIPE;
1137
pThis->hWakeupPipeW = NIL_RTPIPE;
1138
#elif defined(RT_OS_WINDOWS)
1139
pThis->hEventRecv = INVALID_HANDLE_VALUE;
1140
pThis->hEventSend = INVALID_HANDLE_VALUE;
1141
pThis->hDeviceFile = INVALID_HANDLE_VALUE;
1143
pThis->SendSem = NIL_RTSEMEVENT;
1145
pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface;
1146
/* ICharConnector. */
1147
pThis->ICharConnector.pfnWrite = drvHostSerialWrite;
1148
pThis->ICharConnector.pfnSetParameters = drvHostSerialSetParameters;
1149
pThis->ICharConnector.pfnSetModemLines = drvHostSerialSetModemLines;
1150
pThis->ICharConnector.pfnSetBreak = drvHostSerialSetBreak;
1153
* Query configuration.
1156
int rc = CFGMR3QueryStringAlloc(pCfg, "DevicePath", &pThis->pszDevicePath);
1159
AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc));
1166
#ifdef RT_OS_WINDOWS
1168
pThis->hHaltEventSem = CreateEvent(NULL, FALSE, FALSE, NULL);
1169
AssertReturn(pThis->hHaltEventSem != NULL, VERR_NO_MEMORY);
1171
pThis->hEventRecv = CreateEvent(NULL, FALSE, FALSE, NULL);
1172
AssertReturn(pThis->hEventRecv != NULL, VERR_NO_MEMORY);
1174
pThis->hEventSend = CreateEvent(NULL, FALSE, FALSE, NULL);
1175
AssertReturn(pThis->hEventSend != NULL, VERR_NO_MEMORY);
1177
HANDLE hFile = CreateFile(pThis->pszDevicePath,
1178
GENERIC_READ | GENERIC_WRITE,
1179
0, // must be opened with exclusive access
1180
NULL, // no SECURITY_ATTRIBUTES structure
1181
OPEN_EXISTING, // must use OPEN_EXISTING
1182
FILE_FLAG_OVERLAPPED, // overlapped I/O
1183
NULL); // no template file
1184
if (hFile == INVALID_HANDLE_VALUE)
1185
rc = RTErrConvertFromWin32(GetLastError());
1188
pThis->hDeviceFile = hFile;
1189
/* for overlapped read */
1190
if (!SetCommMask(hFile, EV_RXCHAR | EV_CTS | EV_DSR | EV_RING | EV_RLSD))
1192
LogRel(("HostSerial#%d: SetCommMask failed with error %d.\n", pDrvIns->iInstance, GetLastError()));
1193
return VERR_FILE_IO_ERROR;
1198
#else /* !RT_OS_WINDOWS */
1200
uint32_t fOpen = RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;
1202
/* This seems to be necessary on some Linux hosts, otherwise we hang here forever. */
1203
fOpen |= RTFILE_O_NON_BLOCK;
1205
rc = RTFileOpen(&pThis->hDeviceFile, pThis->pszDevicePath, fOpen);
1207
/* RTFILE_O_NON_BLOCK not supported? */
1208
if (rc == VERR_INVALID_PARAMETER)
1209
rc = RTFileOpen(&pThis->hDeviceFile, pThis->pszDevicePath, fOpen & ~RTFILE_O_NON_BLOCK);
1211
# ifdef RT_OS_DARWIN
1213
rc = RTFileOpen(&pThis->hDeviceFileR, pThis->pszDevicePath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
1217
#endif /* !RT_OS_WINDOWS */
1221
AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pThis->pszDevicePath, rc));
1224
case VERR_ACCESS_DENIED:
1225
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1226
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1227
N_("Cannot open host device '%s' for read/write access. Check the permissions "
1228
"of that device ('/bin/ls -l %s'): Most probably you need to be member "
1229
"of the device group. Make sure that you logout/login after changing "
1230
"the group settings of the current user"),
1232
N_("Cannot open host device '%s' for read/write access. Check the permissions "
1235
pThis->pszDevicePath, pThis->pszDevicePath);
1237
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1238
N_("Failed to open host device '%s'"),
1239
pThis->pszDevicePath);
1243
/* Set to non blocking I/O */
1244
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1246
fcntl(RTFileToNative(pThis->hDeviceFile), F_SETFL, O_NONBLOCK);
1247
# ifdef RT_OS_DARWIN
1248
fcntl(RTFileToNative(pThis->hDeviceFileR), F_SETFL, O_NONBLOCK);
1250
rc = RTPipeCreate(&pThis->hWakeupPipeR, &pThis->hWakeupPipeW, 0 /*fFlags*/);
1251
AssertRCReturn(rc, rc);
1253
#elif defined(RT_OS_WINDOWS)
1255
/* Set the COMMTIMEOUTS to get non blocking I/O */
1256
COMMTIMEOUTS comTimeout;
1258
comTimeout.ReadIntervalTimeout = MAXDWORD;
1259
comTimeout.ReadTotalTimeoutMultiplier = 0;
1260
comTimeout.ReadTotalTimeoutConstant = 0;
1261
comTimeout.WriteTotalTimeoutMultiplier = 0;
1262
comTimeout.WriteTotalTimeoutConstant = 0;
1264
SetCommTimeouts(pThis->hDeviceFile, &comTimeout);
1269
* Get the ICharPort interface of the above driver/device.
1271
pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT);
1272
if (!pThis->pDrvCharPort)
1273
return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no char port interface above"), pDrvIns->iInstance);
1276
* Create the receive, send and monitor threads plus the related send semaphore.
1278
rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvHostSerialRecvThread, drvHostSerialWakeupRecvThread, 0, RTTHREADTYPE_IO, "SerRecv");
1280
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create receive thread"), pDrvIns->iInstance);
1282
rc = RTSemEventCreate(&pThis->SendSem);
1285
rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSendThread, pThis, drvHostSerialSendThread, drvHostSerialWakeupSendThread, 0, RTTHREADTYPE_IO, "SerSend");
1287
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create send thread"), pDrvIns->iInstance);
1289
#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
1290
/* Linux & darwin needs a separate thread which monitors the status lines. */
1291
# ifndef RT_OS_LINUX
1292
ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMGET, &pThis->fStatusLines);
1294
rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pMonitorThread, pThis, drvHostSerialMonitorThread, drvHostSerialWakeupMonitorThread, 0, RTTHREADTYPE_IO, "SerMon");
1296
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create monitor thread"), pDrvIns->iInstance);
1300
* Register release statistics.
1302
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance);
1303
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance);
1304
#ifdef RT_OS_DARWIN /* new Write code, not darwin specific. */
1305
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatSendOverflows, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes overflowed", "/Devices/HostSerial%d/SendOverflow", pDrvIns->iInstance);
1308
return VINF_SUCCESS;
1312
* Char driver registration record.
1314
const PDMDRVREG g_DrvHostSerial =
1324
/* pszDescription */
1325
"Host serial driver.",
1327
PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1329
PDM_DRVREG_CLASS_CHAR,
1333
sizeof(DRVHOSTSERIAL),
1335
drvHostSerialConstruct,
1337
drvHostSerialDestruct,