DrvHostSerial.cpp revision bb56e9b73a923e975bc18bea69639b4c07e32f60
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * VBox stream I/O devices: Host serial driver
c58f1213e628a545081c70e26c6b67a841cff880vboxsync * Copyright (C) 2006-2012 Oracle Corporation
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * available from http://www.virtualbox.org. This file is free software;
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * you can redistribute it and/or modify it under the terms of the GNU
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * General Public License (GPL) as published by the Free Software
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/*******************************************************************************
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync* Header Files *
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync*******************************************************************************/
5dcae142d05d0d6307e097f026b53a048b8ef949vboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * TIOCM_LOOP is not defined in the above header files for some reason but in asm/termios.h.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * But inclusion of this file however leads to compilation errors because of redefinition of some
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync * structs. That's why it is defined here until a better solution is found.
36ea08d3a9cad2208ca17245ac69fc0e57b0a7d1vboxsync/* For linux custom baudrate code we also need serial_struct */
6a713fdf2e42488a1179d8c5a9d9cc62e484b1f0vboxsync# endif /* linux */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/*******************************************************************************
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync* Structures and Typedefs *
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync*******************************************************************************/
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Char driver instance data.
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync * @implements PDMICHARCONNECTOR
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /** Pointer to the driver instance structure. */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /** Pointer to the char port interface of the driver/device above us. */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /** Our char interface. */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /** Receive thread. */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /** Send thread. */
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync /** Status lines monitor thread. */
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync /** Send event semaphore */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /** the device path */
5dcae142d05d0d6307e097f026b53a048b8ef949vboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /** the device handle */
a9e040e11af94d3457b824f2942d11375f16f598vboxsync /** The device handle used for reading.
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync * Used to prevent the read select from blocking the writes. */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /** The read end of the control pipe */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /** The write end of the control pipe */
6a713fdf2e42488a1179d8c5a9d9cc62e484b1f0vboxsync /** The current line status.
6a713fdf2e42488a1179d8c5a9d9cc62e484b1f0vboxsync * Used by the polling version of drvHostSerialMonitorThread. */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /** the device handle */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /** The event semaphore for waking up the receive thread */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /** The event semaphore for overlapped receiving */
9f9f83ee5948916d644046a79836873db40bfc88vboxsync /** For overlapped receiving */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /** The event semaphore for overlapped sending */
9f9f83ee5948916d644046a79836873db40bfc88vboxsync /** For overlapped sending */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /** Internal send FIFO queue */
eeec2f0aba1a5a7304026483450d55b52d85e3dcvboxsync bool volatile fSending;
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /** Read/write statistics */
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync /** The number of bytes we've dropped because the send queue
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync * was full. */
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync/** Converts a pointer to DRVCHAR::ICharConnector to a PDRVHOSTSERIAL. */
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync#define PDMICHAR_2_DRVHOSTSERIAL(pInterface) RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ICharConnector)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/* -=-=-=-=- IBase -=-=-=-=- */
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * @interface_method_impl{PDMIBASE,pfnQueryInterface}
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsyncstatic DECLCALLBACK(void *) drvHostSerialQueryInterface(PPDMIBASE pInterface, const char *pszIID)
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
a39ea3668b7019c23a68936259545f9b71bce1aavboxsync PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
a39ea3668b7019c23a68936259545f9b71bce1aavboxsync PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARCONNECTOR, &pThis->ICharConnector);
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync/* -=-=-=-=- ICharConnector -=-=-=-=- */
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync/** @copydoc PDMICHARCONNECTOR::pfnWrite */
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsyncstatic DECLCALLBACK(int) drvHostSerialWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsyncstatic DECLCALLBACK(int) drvHostSerialSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
5dcae142d05d0d6307e097f026b53a048b8ef949vboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
0deaf9b7b14fd7b44a999419acd224f002a2b13bvboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync termiosSetup = (struct termios *)RTMemTmpAllocZ(sizeof(struct termios));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Enable receiver */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 19200:
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 38400:
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 57600:
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 115200:
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync if (ioctl(RTFileToNative(pThis->hDeviceFile), TIOCGSERIAL, &serialStruct) != -1)
36ea08d3a9cad2208ca17245ac69fc0e57b0a7d1vboxsync serialStruct.custom_divisor = serialStruct.baud_base / Bps;
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync ioctl(RTFileToNative(pThis->hDeviceFile), TIOCSSERIAL, &serialStruct);
36ea08d3a9cad2208ca17245ac69fc0e57b0a7d1vboxsync#else /* !RT_OS_LINUX */
36ea08d3a9cad2208ca17245ac69fc0e57b0a7d1vboxsync#endif /* !RT_OS_LINUX */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* set serial port to raw input */
d3ee346250c69f7a5640458e7b6f17705e5ccf79vboxsync termiosSetup->c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ECHOK | ISIG | IEXTEN);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync tcsetattr(RTFileToNative(pThis->hDeviceFile), TCSANOW, termiosSetup);
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync * XXX In Linux, if a thread calls tcsetattr while the monitor thread is
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync * waiting in ioctl for a modem status change then 8250.c wrongly disables
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync * modem irqs and so the monitor thread never gets released. The workaround
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync * is to send a signal after each tcsetattr.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 14400:
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 19200:
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 38400:
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 57600:
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 115200:
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync#endif /* RT_OS_WINDOWS */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/* -=-=-=-=- receive thread -=-=-=-=- */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Send thread loop.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @returns VINF_SUCCESS.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @param ThreadSelf Thread handle to this thread.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @param pvUser User argument.
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsyncstatic DECLCALLBACK(int) drvHostSerialSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
a9e040e11af94d3457b824f2942d11375f16f598vboxsync /* Make sure that the halt event semaphore is reset. */
fdc1cae8a7a45e9299077dd0270c3736b02e4e97vboxsync DWORD dwRet = WaitForSingleObject(pThis->hHaltEventSem, 0);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync while (pThread->enmState == PDMTHREADSTATE_RUNNING)
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync int rc = RTSemEventWait(pThis->SendSem, RT_INDEFINITE_WAIT);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Write the character to the host device.
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync while (pThread->enmState == PDMTHREADSTATE_RUNNING)
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync /* copy the send queue so we get a linear buffer with the maximal size. */
f2f744fb6b4e8cd5db2d98026156d350d4c5b99fvboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync rc = RTFileWrite(pThis->hDeviceFile, &ch, 1, &cbWritten);
eeec2f0aba1a5a7304026483450d55b52d85e3dcvboxsync if (cbWritten < 1 && (RT_SUCCESS(rc) || rc == VERR_TRY_AGAIN))
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync /* ok, block till the device is ready for more (O_NONBLOCK) effect. */
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync while (pThread->enmState == PDMTHREADSTATE_RUNNING)
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync FD_SET(RTFileToNative(pThis->hDeviceFile), &WrSet);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync FD_SET(RTFileToNative(pThis->hDeviceFile), &XcptSet);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync rc = select(RTFileToNative(pThis->hDeviceFile) + 1, NULL, &WrSet, &XcptSet, NULL);
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync /** @todo check rc? */
eeec2f0aba1a5a7304026483450d55b52d85e3dcvboxsync Log2(("select wait for %dms\n", RTTimeMilliTS() - u64Now));
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync /* try write more */
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync rc = RTFileWrite(pThis->hDeviceFile, &ch, 1, &cbWritten);
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync } /* wait/write loop */
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync /* perform an overlapped write operation. */
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync memset(&pThis->overlappedSend, 0, sizeof(pThis->overlappedSend));
eeec2f0aba1a5a7304026483450d55b52d85e3dcvboxsync if (!WriteFile(pThis->hDeviceFile, &ch, 1, &cbWritten, &pThis->overlappedSend))
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync * write blocked, wait for completion or wakeup...
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE);
d72aa6b0dab3e9b60aa78bfca99c767c48a406b0vboxsync AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event semaphore is set but the thread is still in running state\n"));
f2f744fb6b4e8cd5db2d98026156d350d4c5b99fvboxsync#endif /* RT_OS_WINDOWS */
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync LogRel(("HostSerial#%d: Serial Write failed with %Rrc; terminating send thread\n", pDrvIns->iInstance, rc));
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync } /* write loop */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync * Unblock the send thread so it can respond to a state change.
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync * @returns a VBox status code.
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync * @param pDrvIns The driver instance.
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync * @param pThread The send thread.
eeec2f0aba1a5a7304026483450d55b52d85e3dcvboxsyncstatic DECLCALLBACK(int) drvHostSerialWakeupSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD /*pThread*/)
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/* -=-=-=-=- receive thread -=-=-=-=- */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Receive thread loop.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * This thread pushes data from the host serial device up the driver
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * chain toward the serial device.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @returns VINF_SUCCESS.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @param ThreadSelf Thread handle to this thread.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @param pvUser User argument.
9f9f83ee5948916d644046a79836873db40bfc88vboxsyncstatic DECLCALLBACK(int) drvHostSerialRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync size_t cbRemaining = 0; /* start by reading host data */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
a9e040e11af94d3457b824f2942d11375f16f598vboxsync /* Make sure that the halt event semaphore is reset. */
b5523a351ce3a0829df2421672ef4bf1481970bcvboxsync DWORD dwRet = WaitForSingleObject(pThis->hHaltEventSem, 0);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync while (pThread->enmState == PDMTHREADSTATE_RUNNING)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Get a block of data from the host serial device. */
ccc1001951ecd639b15b3034260c6012423349b3vboxsync#if defined(RT_OS_DARWIN) /* poll is broken on x86 darwin, returns POLLNVAL. */
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync FD_SET(RTFileToNative(pThis->hDeviceFileR), &RdSet);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync FD_SET(RTPipeToNative(pThis->hWakeupPipeR), &RdSet);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync FD_SET(RTFileToNative(pThis->hDeviceFile), &XcptSet);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync FD_SET(RTPipeToNative(pThis->hWakeupPipeR), &XcptSet);
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync# if 1 /* it seems like this select is blocking the write... */
cae3b8810c39392e70dfb12b951431018a80fcbavboxsync rc = select(RT_MAX(RTPipeToNative(pThis->hWakeupPipeR), RTFileToNative(pThis->hDeviceFileR)) + 1,
cae3b8810c39392e70dfb12b951431018a80fcbavboxsync rc = select(RTPipeToNative(pThis->hWakeupPipeR), RTFileToNative(pThis->hDeviceFileR) + 1,
ccc1001951ecd639b15b3034260c6012423349b3vboxsync LogRel(("HostSerial#%d: select failed with errno=%d / %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, err, rcThread));
ccc1001951ecd639b15b3034260c6012423349b3vboxsync /* this might have changed in the meantime */
ccc1001951ecd639b15b3034260c6012423349b3vboxsync /* drain the wakeup pipe */
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync if ( FD_ISSET(RTPipeToNative(pThis->hWakeupPipeR), &RdSet)
cae3b8810c39392e70dfb12b951431018a80fcbavboxsync || FD_ISSET(RTPipeToNative(pThis->hWakeupPipeR), &XcptSet))
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync rc = RTPipeRead(pThis->hWakeupPipeR, abBuffer, 1, &cbRead);
d72aa6b0dab3e9b60aa78bfca99c767c48a406b0vboxsync LogRel(("HostSerial#%d: draining the wakeup pipe failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
ccc1001951ecd639b15b3034260c6012423349b3vboxsync /* read data from the serial port. */
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync rc = RTFileRead(pThis->hDeviceFileR, abBuffer, sizeof(abBuffer), &cbRead);
2690c73e870741ed425524c68c11159c1e53d314vboxsync LogRel(("HostSerial#%d: (1) Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
5dcae142d05d0d6307e097f026b53a048b8ef949vboxsync#elif defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
afb320af7168b3ee668e08ea03594f9c4b814a70vboxsync * EINTR errors should be harmless, even if they are not supposed to occur in our setup.
afb320af7168b3ee668e08ea03594f9c4b814a70vboxsync Log(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, err, strerror(err)));
ccc1001951ecd639b15b3034260c6012423349b3vboxsync LogRel(("HostSerial#%d: poll failed with errno=%d / %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, err, rcThread));
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /* this might have changed in the meantime */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /* notification to terminate -- drain the pipe */
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync RTPipeRead(pThis->hWakeupPipeR, &abBuffer, 1, &cbRead);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync rc = RTFileRead(pThis->hDeviceFile, abBuffer, sizeof(abBuffer), &cbRead);
2690c73e870741ed425524c68c11159c1e53d314vboxsync /* don't terminate worker thread when data unavailable */
2690c73e870741ed425524c68c11159c1e53d314vboxsync LogRel(("HostSerial#%d: (2) Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync memset(&pThis->overlappedRecv, 0, sizeof(pThis->overlappedRecv));
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync if (!WaitCommEvent(pThis->hDeviceFile, &dwEventMask, &pThis->overlappedRecv))
ccc1001951ecd639b15b3034260c6012423349b3vboxsync dwRet = WaitForMultipleObjects(2, ahWait, FALSE, INFINITE);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /* notification to terminate */
d72aa6b0dab3e9b60aa78bfca99c767c48a406b0vboxsync AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event semaphore is set but the thread is still in running state\n"));
ccc1001951ecd639b15b3034260c6012423349b3vboxsync LogRel(("HostSerial#%d: Wait failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread));
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /* this might have changed in the meantime */
b8eec6346e7fdb58d20a6943f968494715412c81vboxsync /* Check the event */
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync if (!ReadFile(pThis->hDeviceFile, abBuffer, sizeof(abBuffer), &dwNumberOfBytesTransferred, &pThis->overlappedRecv))
ccc1001951ecd639b15b3034260c6012423349b3vboxsync LogRel(("HostSerial#%d: Read failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread));
22bdb1ce26b2d5a41d1b071c16f1078e5348bb0dvboxsync rc = pThis->pDrvCharPort->pfnNotifyBreak(pThis->pDrvCharPort);
b8eec6346e7fdb58d20a6943f968494715412c81vboxsync /* The status lines have changed. Notify the device. */
b8eec6346e7fdb58d20a6943f968494715412c81vboxsync /* Get the new state */
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync if (GetCommModemStatus(pThis->hDeviceFile, &dwNewStatusLinesState))
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_DCD;
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_RI;
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_DSR;
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_CTS;
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync rc = pThis->pDrvCharPort->pfnNotifyStatusLinesChanged(pThis->pDrvCharPort, uNewStatusLinesState);
b8eec6346e7fdb58d20a6943f968494715412c81vboxsync /* Notifying device failed, continue but log it */
fe813b3594039ba864493438e78ee0e7132bc445vboxsync LogRel(("HostSerial#%d: Notifying device failed with error %Rrc; continuing.\n", pDrvIns->iInstance, rc));
b8eec6346e7fdb58d20a6943f968494715412c81vboxsync /* Getting new state failed, continue but log it */
fe813b3594039ba864493438e78ee0e7132bc445vboxsync LogRel(("HostSerial#%d: Getting status lines state failed with error %Rrc; continuing.\n", pDrvIns->iInstance, RTErrConvertFromWin32(GetLastError())));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Send data to the guest. */
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pbBuffer, &cbProcessed);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync Assert(cbProcessed); Assert(cbProcessed <= cbRemaining);
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Normal case, just means that the guest didn't accept a new
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * character before the timeout elapsed. Just retry. */
fe813b3594039ba864493438e78ee0e7132bc445vboxsync LogRel(("HostSerial#%d: NotifyRead failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync * Unblock the send thread so it can respond to a state change.
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync * @returns a VBox status code.
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync * @param pDrvIns The driver instance.
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync * @param pThread The send thread.
eeec2f0aba1a5a7304026483450d55b52d85e3dcvboxsyncstatic DECLCALLBACK(int) drvHostSerialWakeupRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD /*pThread*/)
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
5dcae142d05d0d6307e097f026b53a048b8ef949vboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync return RTPipeWrite(pThis->hWakeupPipeW, "", 1, &cbIgnored);
5dcae142d05d0d6307e097f026b53a048b8ef949vboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync/* -=-=-=-=- Monitor thread -=-=-=-=- */
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * Monitor thread loop.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * This thread monitors the status lines and notifies the device
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * if they change.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * @returns VINF_SUCCESS.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * @param ThreadSelf Thread handle to this thread.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * @param pvUser User argument.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsyncstatic DECLCALLBACK(int) drvHostSerialMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync unsigned long const uStatusLinesToCheck = TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS;
bc36547e8dd3d35e5f756643a267bbe01e2c1d4cvboxsync if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync unsigned int statusLines;
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync * Get the status line state.
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync rc = ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMGET, &statusLines);
940dbfa4936f2e3966e9e874c4886709f0c75b44vboxsync PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail",
6a713fdf2e42488a1179d8c5a9d9cc62e484b1f0vboxsync N_("Ioctl failed for serial host device '%s' (%Rrc). The device will not work properly"),
6a713fdf2e42488a1179d8c5a9d9cc62e484b1f0vboxsync pThis->pszDevicePath, RTErrConvertFromErrno(errno));
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync pThis->pDrvCharPort->pfnNotifyStatusLinesChanged(pThis->pDrvCharPort, newStatusLine);
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync * Wait for status line change.
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync * XXX In Linux, if a thread calls tcsetattr while the monitor thread is
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync * waiting in ioctl for a modem status change then 8250.c wrongly disables
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync * modem irqs and so the monitor thread never gets released. The workaround
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync * is to send a signal after each tcsetattr.
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMIWAIT, uStatusLinesToCheck);
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync /* Poll for status line change. */
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync if (!((statusLines ^ pThis->fStatusLines) & uStatusLinesToCheck))
1914c6bfdb6ec56deb381bdca7da860677d165eavboxsync while (PDMTHREADSTATE_RUNNING == pThread->enmState);
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * Unblock the monitor thread so it can respond to a state change.
db3cb946a1e459bf2a3f3292f481093eb526ebc0vboxsync * We need to execute this code exactly once during initialization.
db3cb946a1e459bf2a3f3292f481093eb526ebc0vboxsync * But we don't want to block --- therefore this dedicated thread.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * @returns a VBox status code.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * @param pDrvIns The driver instance.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * @param pThread The send thread.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsyncstatic DECLCALLBACK(int) drvHostSerialWakeupMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
940dbfa4936f2e3966e9e874c4886709f0c75b44vboxsync PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail",
0ff7152ccdcc57edca12c5f17b9699c66eeff975vboxsync N_("Suspending serial monitor thread failed for serial device '%s' (%Rrc). The shutdown may take longer than expected"),
6a713fdf2e42488a1179d8c5a9d9cc62e484b1f0vboxsync# else /* !RT_OS_LINUX*/
6a713fdf2e42488a1179d8c5a9d9cc62e484b1f0vboxsync /* In polling mode there is nobody to wake up (PDMThread will cancel the sleep). */
6a713fdf2e42488a1179d8c5a9d9cc62e484b1f0vboxsync# endif /* RT_OS_LINUX */
0deaf9b7b14fd7b44a999419acd224f002a2b13bvboxsync#endif /* RT_OS_LINUX || RT_OS_DARWIN || RT_OS_SOLARIS */
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * Set the modem lines.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * @returns VBox status code
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * @param pInterface Pointer to the interface structure.
bc36547e8dd3d35e5f756643a267bbe01e2c1d4cvboxsync * @param RequestToSend Set to true if this control line should be made active.
bc36547e8dd3d35e5f756643a267bbe01e2c1d4cvboxsync * @param DataTerminalReady Set to true if this control line should be made active.
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsyncstatic DECLCALLBACK(int) drvHostSerialSetModemLines(PPDMICHARCONNECTOR pInterface, bool RequestToSend, bool DataTerminalReady)
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
5dcae142d05d0d6307e097f026b53a048b8ef949vboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMBIS, &modemStateSet);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMBIC, &modemStateClear);
22bdb1ce26b2d5a41d1b071c16f1078e5348bb0dvboxsync * Sets the TD line into break condition.
22bdb1ce26b2d5a41d1b071c16f1078e5348bb0dvboxsync * @returns VBox status code.
22bdb1ce26b2d5a41d1b071c16f1078e5348bb0dvboxsync * @param pInterface Pointer to the interface structure containing the called function pointer.
22bdb1ce26b2d5a41d1b071c16f1078e5348bb0dvboxsync * @param fBreak Set to true to let the device send a break false to put into normal operation.
22bdb1ce26b2d5a41d1b071c16f1078e5348bb0dvboxsync * @thread Any thread.
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsyncstatic DECLCALLBACK(int) drvHostSerialSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak)
22bdb1ce26b2d5a41d1b071c16f1078e5348bb0dvboxsync PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
22bdb1ce26b2d5a41d1b071c16f1078e5348bb0dvboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync ioctl(RTFileToNative(pThis->hDeviceFile), TIOCSBRK);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync ioctl(RTFileToNative(pThis->hDeviceFile), TIOCCBRK);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/* -=-=-=-=- driver interface -=-=-=-=- */
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * Destruct a char driver instance.
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * Most VM resources are freed by the VM. This callback is provided so that
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * any non-VM resources can be freed correctly.
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync * @param pDrvIns The driver instance data.
e74eef731a813e4e06680c587a6759b9974b29c9vboxsyncstatic DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns)
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync /* Empty the send queue */
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
bb56e9b73a923e975bc18bea69639b4c07e32f60vboxsync int rc = RTPipeClose(pThis->hWakeupPipeW); AssertRC(rc);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync rc = RTPipeClose(pThis->hWakeupPipeR); AssertRC(rc);
1e1273b11e17928ec3c3a8fff45121aa7a169413vboxsync rc = RTFileClose(pThis->hDeviceFile); AssertRC(rc);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Construct a char driver instance.
cba6719bd64ec749967bbe931230452664109857vboxsync * @copydoc FNPDMDRVCONSTRUCT
eeec2f0aba1a5a7304026483450d55b52d85e3dcvboxsyncstatic DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t /*fFlags*/)
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Init basic data members and interfaces.
5dcae142d05d0d6307e097f026b53a048b8ef949vboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* IBase. */
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface;
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync /* ICharConnector. */
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync pThis->ICharConnector.pfnWrite = drvHostSerialWrite;
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync pThis->ICharConnector.pfnSetParameters = drvHostSerialSetParameters;
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync pThis->ICharConnector.pfnSetModemLines = drvHostSerialSetModemLines;
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync pThis->ICharConnector.pfnSetBreak = drvHostSerialSetBreak;
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Query configuration.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Device */
c28fa006ba669ad8f26ae31d00a338379c04ea1bvboxsync int rc = CFGMR3QueryStringAlloc(pCfg, "DevicePath", &pThis->pszDevicePath);
9d61cc96134bcb140ed445a8a9eb9fda6ffcb1fbvboxsync AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Open the device
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync pThis->hHaltEventSem = CreateEvent(NULL, FALSE, FALSE, NULL);
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync AssertReturn(pThis->hHaltEventSem != NULL, VERR_NO_MEMORY);
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync pThis->hEventRecv = CreateEvent(NULL, FALSE, FALSE, NULL);
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync AssertReturn(pThis->hEventRecv != NULL, VERR_NO_MEMORY);
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync pThis->hEventSend = CreateEvent(NULL, FALSE, FALSE, NULL);
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync AssertReturn(pThis->hEventSend != NULL, VERR_NO_MEMORY);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync 0, // must be opened with exclusive access
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /* for overlapped read */
b8eec6346e7fdb58d20a6943f968494715412c81vboxsync if (!SetCommMask(hFile, EV_RXCHAR | EV_CTS | EV_DSR | EV_RING | EV_RLSD))
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync LogRel(("HostSerial#%d: SetCommMask failed with error %d.\n", pDrvIns->iInstance, GetLastError()));
795333b05cd74f8d4dcc72848a16da2973ba4568vboxsync#else /* !RT_OS_WINDOWS */
795333b05cd74f8d4dcc72848a16da2973ba4568vboxsync uint32_t fOpen = RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;
d9bbd32b2ca16437c0ac753cdf2b7bde3a888c4cvboxsync /* This seems to be necessary on some Linux hosts, otherwise we hang here forever. */
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync rc = RTFileOpen(&pThis->hDeviceFile, pThis->pszDevicePath, fOpen);
d9bbd32b2ca16437c0ac753cdf2b7bde3a888c4cvboxsync /* RTFILE_O_NON_BLOCK not supported? */
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync rc = RTFileOpen(&pThis->hDeviceFile, pThis->pszDevicePath, fOpen & ~RTFILE_O_NON_BLOCK);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync rc = RTFileOpen(&pThis->hDeviceFileR, pThis->pszDevicePath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
795333b05cd74f8d4dcc72848a16da2973ba4568vboxsync#endif /* !RT_OS_WINDOWS */
fe813b3594039ba864493438e78ee0e7132bc445vboxsync AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pThis->pszDevicePath, rc));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
5dcae142d05d0d6307e097f026b53a048b8ef949vboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync N_("Cannot open host device '%s' for read/write access. Check the permissions "
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync "of that device ('/bin/ls -l %s'): Most probably you need to be member "
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync "of the device group. Make sure that you logout/login after changing "
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync "the group settings of the current user"),
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync N_("Cannot open host device '%s' for read/write access. Check the permissions "
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync "of that device"),
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Set to non blocking I/O */
5dcae142d05d0d6307e097f026b53a048b8ef949vboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync fcntl(RTFileToNative(pThis->hDeviceFile), F_SETFL, O_NONBLOCK);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync fcntl(RTFileToNative(pThis->hDeviceFileR), F_SETFL, O_NONBLOCK);
dc0a54940789f994c84390cb4a9f03da0b492285vboxsync rc = RTPipeCreate(&pThis->hWakeupPipeR, &pThis->hWakeupPipeW, 0 /*fFlags*/);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Set the COMMTIMEOUTS to get non blocking I/O */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Get the ICharPort interface of the above driver/device.
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no char port interface above"), pDrvIns->iInstance);
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync * Create the receive, send and monitor threads plus the related send semaphore.
fbf08fabb4c4b383d6aa2830c2bd5b943a26f10cvboxsync rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvHostSerialRecvThread, drvHostSerialWakeupRecvThread, 0, RTTHREADTYPE_IO, "SerRecv");
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create receive thread"), pDrvIns->iInstance);
fbf08fabb4c4b383d6aa2830c2bd5b943a26f10cvboxsync rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSendThread, pThis, drvHostSerialSendThread, drvHostSerialWakeupSendThread, 0, RTTHREADTYPE_IO, "SerSend");
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create send thread"), pDrvIns->iInstance);
5dcae142d05d0d6307e097f026b53a048b8ef949vboxsync#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
6a713fdf2e42488a1179d8c5a9d9cc62e484b1f0vboxsync /* Linux & darwin needs a separate thread which monitors the status lines. */
cae3b8810c39392e70dfb12b951431018a80fcbavboxsync ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMGET, &pThis->fStatusLines);
fbf08fabb4c4b383d6aa2830c2bd5b943a26f10cvboxsync rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pMonitorThread, pThis, drvHostSerialMonitorThread, drvHostSerialWakeupMonitorThread, 0, RTTHREADTYPE_IO, "SerMon");
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create monitor thread"), pDrvIns->iInstance);
6a713fdf2e42488a1179d8c5a9d9cc62e484b1f0vboxsync * Register release statistics.
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance);
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance);
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync#ifdef RT_OS_DARWIN /* new Write code, not darwin specific. */
1f72a47e266d9b7498b6a06aacf53a23ff874bc2vboxsync PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatSendOverflows, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes overflowed", "/Devices/HostSerial%d/SendOverflow", pDrvIns->iInstance);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Char driver registration record.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* u32Version */
5b465a7c1237993faf8bb50120d247f3f0319adavboxsync /* szName */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync "Host Serial",
ad77e3ec3cde24263bc7537575f5cae442bee3b1vboxsync /* szRCMod */
ad77e3ec3cde24263bc7537575f5cae442bee3b1vboxsync /* szR0Mod */
ad77e3ec3cde24263bc7537575f5cae442bee3b1vboxsync/* pszDescription */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync "Host serial driver.",
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* fFlags */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* fClass. */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* cMaxInstances */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* cbInstance */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnConstruct */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnDestruct */
ad77e3ec3cde24263bc7537575f5cae442bee3b1vboxsync /* pfnRelocate */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnIOCtl */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnPowerOn */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnReset */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnSuspend */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnResume */
cba6719bd64ec749967bbe931230452664109857vboxsync /* pfnAttach */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnDetach */
cba6719bd64ec749967bbe931230452664109857vboxsync /* pfnPowerOff */
cba6719bd64ec749967bbe931230452664109857vboxsync /* pfnSoftReset */
cba6719bd64ec749967bbe931230452664109857vboxsync /* u32EndVersion */