DrvChar.cpp revision 85a8039cb441b4ab1462507454334d8d85969194
/* $Id$ */
/** @file
* Driver that adapts PDMISTREAM into PDMICHARCONNECTOR / PDMICHARPORT.
*
* Converts synchronous calls (PDMICHARCONNECTOR::pfnWrite, PDMISTREAM::pfnRead)
* into asynchronous ones.
*/
/*
* Copyright (C) 2006-2010 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DRV_CHAR
#include <iprt/semaphore.h>
#include "Builtins.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Size of the send fifo queue (in bytes) */
#define CHAR_MAX_SEND_QUEUE 0x80
#define CHAR_MAX_SEND_QUEUE_MASK 0x7f
/** Converts a pointer to DRVCHAR::ICharConnector to a PDRVCHAR. */
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Char driver instance data.
*
* @implements PDMICHARCONNECTOR
*/
typedef struct DRVCHAR
{
/** Pointer to the driver instance structure. */
/** Pointer to the stream interface of the driver below us. */
/** Our char interface. */
/** Flag to notify the receive thread it should terminate. */
volatile bool fShutdown;
/** Receive thread ID. */
/** Send thread ID. */
/** Send event semephore */
/** Internal send FIFO queue */
uint32_t volatile iSendQueueHead;
/* -=-=-=-=- IBase -=-=-=-=- */
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
return NULL;
}
/* -=-=-=-=- ICharConnector -=-=-=-=- */
/** @copydoc PDMICHARCONNECTOR::pfnWrite */
static DECLCALLBACK(int) drvCharWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
{
{
}
return VINF_SUCCESS;
}
/** @copydoc PDMICHARCONNECTOR::pfnSetParameters */
static DECLCALLBACK(int) drvCharSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
{
/*PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface); - unused*/
LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
return VINF_SUCCESS;
}
/* -=-=-=-=- receive thread -=-=-=-=- */
/**
* Send thread loop - pushes data down thru the driver chain.
*
* @returns 0 on success.
* @param ThreadSelf Thread handle to this thread.
* @param pvUser User argument.
*/
{
{
if (RT_FAILURE(rc))
break;
/*
* Write the character to the attached stream (if present).
*/
|| !pThis->pDrvStream)
break;
{
rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &pThis->aSendQueue[pThis->iSendQueueTail], &cbProcessed);
if (RT_SUCCESS(rc))
{
pThis->iSendQueueTail++;
}
else if (rc == VERR_TIMEOUT)
{
/* Normal case, just means that the stream didn't accept a new
* character before the timeout elapsed. Just retry. */
rc = VINF_SUCCESS;
}
else
{
break;
}
}
}
return VINF_SUCCESS;
}
/* -=-=-=-=- receive thread -=-=-=-=- */
/**
* Receive thread loop.
*
* @returns 0 on success.
* @param ThreadSelf Thread handle to this thread.
* @param pvUser User argument.
*/
{
int rc;
cbRemaining = 0;
{
if (!cbRemaining)
{
/* Get block of data from stream driver. */
if (pThis->pDrvStream)
{
cbRemaining = sizeof(aBuffer);
if (RT_FAILURE(rc))
{
break;
}
}
else
{
cbRemaining = 0;
RTThreadSleep(100);
}
}
else
{
/* Send data to guest. */
if (RT_SUCCESS(rc))
{
pBuffer += cbProcessed;
}
else if (rc == VERR_TIMEOUT)
{
/* Normal case, just means that the guest didn't accept a new
* character before the timeout elapsed. Just retry. */
rc = VINF_SUCCESS;
}
else
{
break;
}
}
}
return VINF_SUCCESS;
}
/**
* Set the modem lines.
*
* @returns VBox status code
* @param pInterface Pointer to the interface structure.
* @param RequestToSend Set to true if this control line should be made active.
* @param DataTerminalReady Set to true if this control line should be made active.
*/
static DECLCALLBACK(int) drvCharSetModemLines(PPDMICHARCONNECTOR pInterface, bool RequestToSend, bool DataTerminalReady)
{
/* Nothing to do here. */
return VINF_SUCCESS;
}
/**
* Sets the TD line into break condition.
*
* @returns VBox status code.
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @param fBreak Set to true to let the device send a break false to put into normal operation.
* @thread Any thread.
*/
{
/* Nothing to do here. */
return VINF_SUCCESS;
}
/* -=-=-=-=- driver interface -=-=-=-=- */
/**
* Destruct a char driver instance.
*
* Most VM resources are freed by the VM. This callback is provided so that
* any non-VM resources can be freed correctly.
*
* @param pDrvIns The driver instance data.
*/
{
/*
* Tell the threads to shut down.
*/
{
}
/*
* Wait for the threads.
* ASSUMES that PDM destroys the driver chain from the the bottom and up.
*/
{
if (RT_SUCCESS(rc))
else
}
{
if (RT_SUCCESS(rc))
else
}
}
/**
* Construct a char driver instance.
*
* @copydoc FNPDMDRVCONSTRUCT
*/
{
/*
* Init basic data members and interfaces.
*/
/* IBase. */
/* ICharConnector. */
/*
*/
if (!pThis->pDrvCharPort)
return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
/*
* Attach driver below and query its stream interface.
*/
if (RT_FAILURE(rc))
return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
if (!pThis->pDrvStream)
return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
/*
* Don't start the receive thread if the driver doesn't support reading
*/
{
if (RT_FAILURE(rc))
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
}
if (RT_FAILURE(rc))
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
return VINF_SUCCESS;
}
/**
* Char driver registration record.
*/
{
/* u32Version */
/* szName */
"Char",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"Generic char driver.",
/* fFlags */
/* fClass. */
/* cMaxInstances */
~0,
/* cbInstance */
sizeof(DRVCHAR),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32EndVersion */
};