DrvHostSerial.cpp revision 4a61dadcdd5fbace94426335d7a985ff31936a2c
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * VBox stream I/O devices: Host serial driver
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Contributed by: Alexander Eichner
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Copyright (C) 2006-2007 innotek GmbH
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*******************************************************************************/
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
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * structs. Thatswhy it is defined here until a better solution is found.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/** Size of the send fifo queue (in bytes) */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/*******************************************************************************
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync* Structures and Typedefs *
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync*******************************************************************************/
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Char driver instance data.
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. */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /** Send event semephore */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /** the device path */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /** the device handle */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /** The read end of the control pipe */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /** The write end of the control pipe */
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 */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /** Read/write statistics */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/** Converts a pointer to DRVCHAR::IChar to a PDRVHOSTSERIAL. */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync#define PDMICHAR_2_DRVHOSTSERIAL(pInterface) ( (PDRVHOSTSERIAL)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTSERIAL, IChar)) )
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/* -=-=-=-=- IBase -=-=-=-=- */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Queries an interface to the driver.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @returns Pointer to interface.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @returns NULL if the interface was not supported by the driver.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @param pInterface Pointer to this interface structure.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @param enmInterface The requested interface identification.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsyncstatic DECLCALLBACK(void *) drvHostSerialQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/* -=-=-=-=- IChar -=-=-=-=- */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/** @copydoc PDMICHAR::pfnWrite */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsyncstatic DECLCALLBACK(int) drvHostSerialWrite(PPDMICHAR pInterface, const void *pvBuf, size_t cbWrite)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync PDRVHOSTSERIAL pData = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsyncstatic DECLCALLBACK(int) drvHostSerialSetParameters(PPDMICHAR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync PDRVHOSTSERIAL pData = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync termiosSetup = (struct termios *)RTMemTmpAllocZ(sizeof(struct termios));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Enable receiver */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 19200:
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 38400:
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 57600:
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync case 115200:
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* set serial port to raw input */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync termiosSetup->c_lflag = ~(ICANON | ECHO | ECHOE | ISIG);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync tcsetattr(pData->DeviceFile, TCSANOW, termiosSetup);
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)
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync while (pThread->enmState == PDMTHREADSTATE_RUNNING)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync int rc = RTSemEventWait(pData->SendSem, RT_INDEFINITE_WAIT);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Write the character to the host device.
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync while ( pThread->enmState == PDMTHREADSTATE_RUNNING
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync rc = RTFileWrite(pData->DeviceFile, &pData->aSendQueue[pData->iSendQueueTail], cbProcessed, NULL);
9f9f83ee5948916d644046a79836873db40bfc88vboxsync memset(&pData->overlappedSend, 0, sizeof(pData->overlappedSend));
9f9f83ee5948916d644046a79836873db40bfc88vboxsync if (!WriteFile(pData->hDeviceFile, &pData->aSendQueue[pData->iSendQueueTail], cbProcessed, &cbBytesWritten, &pData->overlappedSend))
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync * write blocked, wait ...
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync LogRel(("HostSerial#%d: Serial Write failed with %Vrc; terminating send 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.
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsyncstatic DECLCALLBACK(int) drvHostSerialWakeupSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync PDRVHOSTSERIAL pData = PDMINS2DATA(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)
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync size_t cbRemaining = 0; /* start by reading host data */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync while (pThread->enmState == PDMTHREADSTATE_RUNNING)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Get a block of data from the host serial device. */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /* poll failed for whatever reason */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /* this might have changed in the meantime */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /* notification to terminate -- drain the pipe */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync rc = RTFileRead(pData->DeviceFile, abBuffer, sizeof(abBuffer), &cbRead);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync LogRel(("HostSerial#%d: Read failed with %Vrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
9f9f83ee5948916d644046a79836873db40bfc88vboxsync memset(&pData->overlappedRecv, 0, sizeof(pData->overlappedRecv));
9f9f83ee5948916d644046a79836873db40bfc88vboxsync if (!WaitCommEvent(pData->hDeviceFile, &dwEventMask, &pData->overlappedRecv))
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /* notification to terminate */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync LogRel(("HostSerial#%d: Wait failed with error %Vrc; terminating the worker thread.\n", pDrvIns->iInstance, RTErrConvertFromWin32(dwRet)));
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /* this might have changed in the meantime */
9f9f83ee5948916d644046a79836873db40bfc88vboxsync if (!ReadFile(pData->hDeviceFile, abBuffer, sizeof(abBuffer), &dwNumberOfBytesTransferred, &pData->overlappedRecv))
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync LogRel(("HostSerial#%d: Read failed with error %Vrc; terminating the worker thread.\n", pDrvIns->iInstance, RTErrConvertFromWin32(GetLastError())));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Send data to the guest. */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync rc = pData->pDrvCharPort->pfnNotifyRead(pData->pDrvCharPort, pbBuffer, &cbProcessed);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync Assert(cbProcessed); Assert(cbProcessed <= cbRemaining);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync STAM_COUNTER_ADD(&pData->StatBytesRead, cbProcessed);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Normal case, just means that the guest didn't accept a new
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * character before the timeout elapsed. Just retry. */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync LogRel(("HostSerial#%d: NotifyRead failed with %Vrc, 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.
9f9f83ee5948916d644046a79836873db40bfc88vboxsyncstatic DECLCALLBACK(int) drvHostSerialWakeupRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync return RTFileWrite(pData->WakeupPipeW, "", 1, NULL);
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)
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync uStatusLinesToCheck = TIOCM_CAR | TIOCM_RNG | TIOCM_LE | TIOCM_CTS;
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync while (pThread->enmState == PDMTHREADSTATE_RUNNING)
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync unsigned int statusLines;
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * Wait for status line change.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync rc = ioctl(pData->DeviceFile, TIOCMIWAIT, &uStatusLinesToCheck);
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync rc = pData->pDrvCharPort->pfnNotifyStatusLinesChanged(pData->pDrvCharPort, newStatusLine);
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * Unblock the monitor thread so it can respond to a state change.
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)
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * Linux is a bit difficult as the thread is sleeping in an ioctl call.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * So there is no way to have a wakeup pipe.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * Thatswhy we set the serial device into loopback mode and change one of the
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * modem control bits.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * This should make the ioctl call return.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync rc = ioctl(pData->DeviceFile, TIOCMBIS, TIOCM_LOOP);
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync AssertMsgFailed(("%s: Setting device into loopback mode failed. Cannot wake up the thread!!\n", __FUNCTION__));
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync rc = ioctl(pData->DeviceFile, TIOCMBIS, TIOCM_RTS);
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync AssertMsgFailed(("%s: Setting bit failed. Cannot wake up thread!!\n", __FUNCTION__));
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * Set serial device into normal state.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync rc = ioctl(pData->DeviceFile, TIOCMBIC, TIOCM_LOOP);
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync AssertMsgFailed(("%s: Setting device into normal mode failed. Device is not in a working state!!\n", __FUNCTION__));
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync /** @todo */
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * Set the modem lines.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * @returns VBox status code
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * @param pInterface Pointer to the interface structure.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * @param RequestToSend Set to 1 if this control line should be made active.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync * @param DataTerminalReady Set to 1 if this control line should be made active.
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsyncstatic DECLCALLBACK(int) drvHostSerialSetModemLines(PPDMICHAR pInterface, unsigned RequestToSend, unsigned DataTerminalReady)
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync PDRVHOSTSERIAL pData = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync/* -=-=-=-=- driver interface -=-=-=-=- */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Construct a char driver instance.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @returns VBox status.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @param pDrvIns The driver instance data.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * If the registration structure is needed,
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * pDrvIns->pDrvReg points to it.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @param pCfgHandle Configuration node handle for the driver. Use this to
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * obtain the configuration of the driver instance. It's
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * also found in pDrvIns->pCfgHandle as it's expected to
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * be used frequently in this function.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsyncstatic DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Init basic data members and interfaces.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* IBase. */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface;
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* IChar. */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync pData->IChar.pfnSetParameters = drvHostSerialSetParameters;
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync pData->IChar.pfnSetModemLines = drvHostSerialSetModemLines;
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Query configuration.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Device */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync int rc = CFGMR3QueryStringAlloc(pCfgHandle, "DevicePath", &pData->pszDevicePath);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Vra.\n", rc));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Open the device
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync pData->hHaltEventSem = CreateEvent(NULL, FALSE, FALSE, NULL);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync AssertReturn(pData->hHaltEventSem != NULL, VERR_NO_MEMORY);
9f9f83ee5948916d644046a79836873db40bfc88vboxsync pData->hEventRecv = CreateEvent(NULL, FALSE, FALSE, NULL);
9f9f83ee5948916d644046a79836873db40bfc88vboxsync AssertReturn(pData->hEventRecv != NULL, VERR_NO_MEMORY);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync pData->hEventSend = CreateEvent(NULL, FALSE, FALSE, NULL);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync AssertReturn(pData->hEventSend != NULL, VERR_NO_MEMORY);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync 0, // must be opened with exclusive access
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync /* for overlapped read */
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync LogRel(("HostSerial#%d: SetCommMask failed with error %d.\n", pDrvIns->iInstance, GetLastError()));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync rc = RTFileOpen(&pData->DeviceFile, pData->pszDevicePath, RTFILE_O_OPEN | RTFILE_O_READWRITE);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync AssertMsgFailed(("Could not open host device %s, rc=%Vrc\n", pData->pszDevicePath, rc));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
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 */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Set the COMMTIMEOUTS to get non blocking I/O */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Get the ICharPort interface of the above driver/device.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync pData->pDrvCharPort = (PPDMICHARPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_CHAR_PORT);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no char port interface above"), pDrvIns->iInstance);
9f9f83ee5948916d644046a79836873db40bfc88vboxsync rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pData->pRecvThread, pData, drvHostSerialRecvThread, drvHostSerialWakeupRecvThread, 0, RTTHREADTYPE_IO, "Serial Receive");
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create receive thread"), pDrvIns->iInstance);
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pData->pSendThread, pData, drvHostSerialSendThread, drvHostSerialWakeupSendThread, 0, RTTHREADTYPE_IO, "Serial Send");
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create send thread"), pDrvIns->iInstance);
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pData->pMonitorThread, pData, drvHostSerialMonitorThread, drvHostSerialWakeupMonitorThread, 0, RTTHREADTYPE_IO, "Serial Monitor");
4a61dadcdd5fbace94426335d7a985ff31936a2cvboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create monitor thread"), pDrvIns->iInstance);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Destruct a char driver instance.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Most VM resources are freed by the VM. This callback is provided so that
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * any non-VM resources can be freed correctly.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * @param pDrvIns The driver instance data.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsyncstatic DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns)
e554e8c589d84d4df49504e096f81dfa48c2a06evboxsync PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* Empty the send queue */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync * Char driver registration record.
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* u32Version */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* szDriverName */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync "Host Serial",
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pszDescription */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync "Host serial driver.",
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* fFlags */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* fClass. */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* cMaxInstances */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* cbInstance */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnConstruct */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnDestruct */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnIOCtl */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnPowerOn */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnReset */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnSuspend */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnResume */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /* pfnDetach */
a2e6357e0295f680b36aaf31bedd6409a5336165vboxsync /** pfnPowerOff */