DrvChar.cpp revision bd96e81081052c309669f3f9b183cfec4fffbefb
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * VBox stream I/O devices:
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Generic char driver
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Copyright (C) 2006 InnoTek Systemberatung GmbH
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * available from http://www.virtualbox.org. This file is free software;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * you can redistribute it and/or modify it under the terms of the GNU
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * General Public License as published by the Free Software Foundation,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * distribution. VirtualBox OSE is distributed in the hope that it will
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * be useful, but WITHOUT ANY WARRANTY of any kind.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * If you received this file as part of a commercial VirtualBox
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * distribution, then only the terms of your commercial VirtualBox
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * license agreement apply instead of the previous paragraph.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/*******************************************************************************
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync* Header Files *
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync*******************************************************************************/
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/*******************************************************************************
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync* Structures and Typedefs *
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync*******************************************************************************/
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Char driver instance data.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsynctypedef struct DRVCHAR
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the driver instance structure. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the char port interface of the driver/device above us. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the stream interface of the driver below us. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Our char interface. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Flag to notify the receive thread it should terminate. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync volatile bool fShutdown;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Receive thread ID. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/** Converts a pointer to DRVCHAR::IChar to a PDRVCHAR. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync#define PDMICHAR_2_DRVCHAR(pInterface) ( (PDRVCHAR)((uintptr_t)pInterface - RT_OFFSETOF(DRVCHAR, IChar)) )
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* -=-=-=-=- IBase -=-=-=-=- */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Queries an interface to the driver.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @returns Pointer to interface.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @returns NULL if the interface was not supported by the driver.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param pInterface Pointer to this interface structure.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param enmInterface The requested interface identification.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* -=-=-=-=- IChar -=-=-=-=- */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/** @copydoc PDMICHAR::pfnWrite */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic DECLCALLBACK(int) drvCharWrite(PPDMICHAR pInterface, const void *pvBuf, size_t cbWrite)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Write the character to the attached stream (if present).
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync rc = pData->pDrvStream->pfnWrite(pData->pDrvStream, pBuffer, &cbProcessed);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* Normal case, just means that the stream didn't accept a new
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * character before the timeout elapsed. Just retry. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync LogFlow(("%s: returns rc=%Vrc\n", __FUNCTION__, rc));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* -=-=-=-=- receive thread -=-=-=-=- */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Receive thread loop.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @returns 0 on success.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param ThreadSelf Thread handle to this thread.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param pvUser User argument.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* Get block of data from stream driver. */
bd96e81081052c309669f3f9b183cfec4fffbefbvboxsync rc = pData->pDrvStream->pfnRead(pData->pDrvStream, aBuffer, &cbRemaining);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* Send data to guest. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync rc = pData->pDrvCharPort->pfnNotifyRead(pData->pDrvCharPort, pBuffer, &cbProcessed);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* Normal case, just means that the guest didn't accept a new
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * character before the timeout elapsed. Just retry. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* -=-=-=-=- driver interface -=-=-=-=- */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Construct a char driver instance.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @returns VBox status.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param pDrvIns The driver instance data.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * If the registration structure is needed,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * pDrvIns->pDrvReg points to it.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param pCfgHandle Configuration node handle for the driver. Use this to
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * obtain the configuration of the driver instance. It's
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * also found in pDrvIns->pCfgHandle as it's expected to
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * be used frequently in this function.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Init basic data members and interfaces.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* IBase. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* IChar. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Get the ICharPort interface of the above driver/device.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pData->pDrvCharPort = (PPDMICHARPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_CHAR_PORT);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Attach driver below and query its stream interface.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d failed to attach driver below"), pDrvIns->iInstance);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pData->pDrvStream = (PPDMISTREAM)pBase->pfnQueryInterface(pBase, PDMINTERFACE_STREAM);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
323346579ad67963aacbbb0a988d26d3791f3844vboxsync rc = RTThreadCreate(&pData->ReceiveThread, drvCharReceiveLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "Char");
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Destruct a char driver instance.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Most VM resources are freed by the VM. This callback is provided so that
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * any non-VM resources can be freed correctly.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param pDrvIns The driver instance data.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** @todo r=bird: use RTThreadWait() and the RTTHREADFLAGS_WAITABLE instead of active waiting like this.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * (the api is relatively new, which is why it's not used in all the places it should.) */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync LogRel(("Char%d: receive thread did not terminate\n", pDrvIns->iInstance));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Char driver registration record.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* u32Version */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* szDriverName */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pszDescription */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync "Generic char driver.",
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* fFlags */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* fClass. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* cMaxInstances */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* cbInstance */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnConstruct */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnDestruct */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnIOCtl */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnPowerOn */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnReset */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnSuspend */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnResume */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnDetach */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** pfnPowerOff */