DrvChar.cpp revision 1c94c0a63ba68be1a7b2c640e70d7a06464e4fca
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/** @file
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync *
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * VBox stream I/O devices:
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Generic char driver
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/*
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync *
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
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.
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync *
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * additional information or have any questions.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/*******************************************************************************
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync* Header Files *
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync*******************************************************************************/
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync#define LOG_GROUP LOG_GROUP_DRV_CHAR
da957c069c2a3c582fe265ff88170ce4c42b499dvboxsync#include <VBox/pdmdrv.h>
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync#include <iprt/asm.h>
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync#include <iprt/assert.h>
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync#include <iprt/stream.h>
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync#include <iprt/semaphore.h>
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync#include "Builtins.h"
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync/** Size of the send fifo queue (in bytes) */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync#define CHAR_MAX_SEND_QUEUE 0x80
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync#define CHAR_MAX_SEND_QUEUE_MASK 0x7f
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/*******************************************************************************
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync* Structures and Typedefs *
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync*******************************************************************************/
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/**
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Char driver instance data.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsynctypedef struct DRVCHAR
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync{
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the driver instance structure. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PPDMDRVINS pDrvIns;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the char port interface of the driver/device above us. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PPDMICHARPORT pDrvCharPort;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the stream interface of the driver below us. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PPDMISTREAM pDrvStream;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Our char interface. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDMICHAR IChar;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Flag to notify the receive thread it should terminate. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync volatile bool fShutdown;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Receive thread ID. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync RTTHREAD ReceiveThread;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** Send thread ID. */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync RTTHREAD SendThread;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** Send event semephore */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync RTSEMEVENT SendSem;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** Internal send FIFO queue */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync uint8_t aSendQueue[CHAR_MAX_SEND_QUEUE];
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync uint32_t iSendQueueHead;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync uint32_t iSendQueueTail;
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsync
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsync /** Read/write statistics */
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsync STAMCOUNTER StatBytesRead;
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsync STAMCOUNTER StatBytesWritten;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync} DRVCHAR, *PDRVCHAR;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
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
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* -=-=-=-=- IBase -=-=-=-=- */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/**
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Queries an interface to the driver.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync *
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.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync{
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDRVCHAR pData = PDMINS2DATA(pDrvIns, PDRVCHAR);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync switch (enmInterface)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync {
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync case PDMINTERFACE_BASE:
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return &pDrvIns->IBase;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync case PDMINTERFACE_CHAR:
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return &pData->IChar;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync default:
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return NULL;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync}
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* -=-=-=-=- IChar -=-=-=-=- */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/** @copydoc PDMICHAR::pfnWrite */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic DECLCALLBACK(int) drvCharWrite(PPDMICHAR pInterface, const void *pvBuf, size_t cbWrite)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync{
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDRVCHAR pData = PDMICHAR_2_DRVCHAR(pInterface);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync const char *pBuffer = (const char *)pvBuf;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync for (uint32_t i=0;i<cbWrite;i++)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync {
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync uint32_t idx = pData->iSendQueueHead;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync pData->aSendQueue[idx] = pBuffer[i];
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync idx = (idx + 1) & CHAR_MAX_SEND_QUEUE_MASK;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsync STAM_COUNTER_INC(&pData->StatBytesWritten);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync ASMAtomicXchgU32(&pData->iSendQueueHead, idx);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync }
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync RTSemEventSignal(pData->SendSem);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync return VINF_SUCCESS;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync}
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
aa32d4906f2f685992091893d5abdf27a2352a85vboxsync/** @copydoc PDMICHAR::pfnSetParameters */
e3f5c51715cbf77ae2d2e9d05bafd00d69b1bec9vboxsyncstatic DECLCALLBACK(int) drvCharSetParameters(PPDMICHAR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
aa32d4906f2f685992091893d5abdf27a2352a85vboxsync{
e3f5c51715cbf77ae2d2e9d05bafd00d69b1bec9vboxsync /*PDRVCHAR pData = PDMICHAR_2_DRVCHAR(pInterface); - unused*/
aa32d4906f2f685992091893d5abdf27a2352a85vboxsync
e3f5c51715cbf77ae2d2e9d05bafd00d69b1bec9vboxsync LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
aa32d4906f2f685992091893d5abdf27a2352a85vboxsync return VINF_SUCCESS;
aa32d4906f2f685992091893d5abdf27a2352a85vboxsync}
aa32d4906f2f685992091893d5abdf27a2352a85vboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync/* -=-=-=-=- receive thread -=-=-=-=- */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync/**
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * Send thread loop.
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync *
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * @returns 0 on success.
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * @param ThreadSelf Thread handle to this thread.
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * @param pvUser User argument.
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsyncstatic DECLCALLBACK(int) drvCharSendLoop(RTTHREAD ThreadSelf, void *pvUser)
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync{
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync PDRVCHAR pData = (PDRVCHAR)pvUser;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync for(;;)
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync {
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync int rc = RTSemEventWait(pData->SendSem, RT_INDEFINITE_WAIT);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync if (VBOX_FAILURE(rc))
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync break;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /*
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * Write the character to the attached stream (if present).
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync */
e3f5c51715cbf77ae2d2e9d05bafd00d69b1bec9vboxsync if ( !pData->fShutdown
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync && pData->pDrvStream)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync {
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync while (pData->iSendQueueTail != pData->iSendQueueHead)
b514c03a427443a7ad18c1202d2ee7acc47cf9afvboxsync {
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync size_t cbProcessed = 1;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync rc = pData->pDrvStream->pfnWrite(pData->pDrvStream, &pData->aSendQueue[pData->iSendQueueTail], &cbProcessed);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync if (VBOX_SUCCESS(rc))
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync {
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync Assert(cbProcessed);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync pData->iSendQueueTail++;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync pData->iSendQueueTail &= CHAR_MAX_SEND_QUEUE_MASK;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync }
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync else if (rc == VERR_TIMEOUT)
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync {
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /* Normal case, just means that the stream didn't accept a new
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * character before the timeout elapsed. Just retry. */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync rc = VINF_SUCCESS;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync }
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync else
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync {
6a84b239fa9107dd2fd98a0baafca598a05a2c15vboxsync LogFlow(("Write failed with %Vrc; skipping\n", rc));
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync break;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync }
b514c03a427443a7ad18c1202d2ee7acc47cf9afvboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync }
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync else
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync break;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync pData->SendThread = NIL_RTTHREAD;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync return VINF_SUCCESS;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync}
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* -=-=-=-=- receive thread -=-=-=-=- */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/**
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Receive thread loop.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync *
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @returns 0 on success.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param ThreadSelf Thread handle to this thread.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param pvUser User argument.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync{
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDRVCHAR pData = (PDRVCHAR)pvUser;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync char aBuffer[256], *pBuffer;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync size_t cbRemaining, cbProcessed;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync int rc;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync cbRemaining = 0;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pBuffer = aBuffer;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync while (!pData->fShutdown)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync {
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync if (!cbRemaining)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync {
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* Get block of data from stream driver. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync if (pData->pDrvStream)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync {
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync cbRemaining = sizeof(aBuffer);
bd96e81081052c309669f3f9b183cfec4fffbefbvboxsync rc = pData->pDrvStream->pfnRead(pData->pDrvStream, aBuffer, &cbRemaining);
c6958b923ed12aadcf58ebbdbc80aadebbd9493evboxsync if (VBOX_FAILURE(rc))
c6958b923ed12aadcf58ebbdbc80aadebbd9493evboxsync {
c6958b923ed12aadcf58ebbdbc80aadebbd9493evboxsync LogFlow(("Read failed with %Vrc\n", rc));
c6958b923ed12aadcf58ebbdbc80aadebbd9493evboxsync break;
c6958b923ed12aadcf58ebbdbc80aadebbd9493evboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync else
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync {
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync cbRemaining = 0;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync RTThreadSleep(100);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pBuffer = aBuffer;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync else
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync {
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* Send data to guest. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync cbProcessed = cbRemaining;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync rc = pData->pDrvCharPort->pfnNotifyRead(pData->pDrvCharPort, pBuffer, &cbProcessed);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync if (VBOX_SUCCESS(rc))
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync {
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync Assert(cbProcessed);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pBuffer += cbProcessed;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync cbRemaining -= cbProcessed;
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsync STAM_COUNTER_ADD(&pData->StatBytesRead, cbProcessed);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync else if (rc == VERR_TIMEOUT)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync {
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* Normal case, just means that the guest didn't accept a new
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * character before the timeout elapsed. Just retry. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync rc = VINF_SUCCESS;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync else
b1cc88518a7578ee20491f3d97b9792c24c6428dvboxsync {
c6958b923ed12aadcf58ebbdbc80aadebbd9493evboxsync LogFlow(("NotifyRead failed with %Vrc\n", rc));
b1cc88518a7578ee20491f3d97b9792c24c6428dvboxsync break;
b1cc88518a7578ee20491f3d97b9792c24c6428dvboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pData->ReceiveThread = NIL_RTTHREAD;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return VINF_SUCCESS;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync}
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
362838d79d234a41380be42aae9118850cc3c929vboxsync/**
362838d79d234a41380be42aae9118850cc3c929vboxsync * Set the modem lines.
362838d79d234a41380be42aae9118850cc3c929vboxsync *
362838d79d234a41380be42aae9118850cc3c929vboxsync * @returns VBox status code
362838d79d234a41380be42aae9118850cc3c929vboxsync * @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.
362838d79d234a41380be42aae9118850cc3c929vboxsync */
bc36547e8dd3d35e5f756643a267bbe01e2c1d4cvboxsyncstatic DECLCALLBACK(int) drvCharSetModemLines(PPDMICHAR pInterface, bool RequestToSend, bool DataTerminalReady)
362838d79d234a41380be42aae9118850cc3c929vboxsync{
362838d79d234a41380be42aae9118850cc3c929vboxsync /* Nothing to do here. */
362838d79d234a41380be42aae9118850cc3c929vboxsync return VINF_SUCCESS;
362838d79d234a41380be42aae9118850cc3c929vboxsync}
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* -=-=-=-=- driver interface -=-=-=-=- */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/**
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Construct a char driver instance.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync *
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.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync{
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDRVCHAR pData = PDMINS2DATA(pDrvIns, PDRVCHAR);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /*
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Init basic data members and interfaces.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pData->ReceiveThread = NIL_RTTHREAD;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pData->fShutdown = false;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* IBase. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* IChar. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pData->IChar.pfnWrite = drvCharWrite;
aa32d4906f2f685992091893d5abdf27a2352a85vboxsync pData->IChar.pfnSetParameters = drvCharSetParameters;
362838d79d234a41380be42aae9118850cc3c929vboxsync pData->IChar.pfnSetModemLines = drvCharSetModemLines;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /*
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Get the ICharPort interface of the above driver/device.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pData->pDrvCharPort = (PPDMICHARPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_CHAR_PORT);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync if (!pData->pDrvCharPort)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /*
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Attach driver below and query its stream interface.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PPDMIBASE pBase;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync if (VBOX_FAILURE(rc))
30adc6dd25ed9fef4d800a6d9f1ab7e765b4c340vboxsync return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pData->pDrvStream = (PPDMISTREAM)pBase->pfnQueryInterface(pBase, PDMINTERFACE_STREAM);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync if (!pData->pDrvStream)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
c18940aaee473ca75acc33491c1dc77b3e8094e0vboxsync rc = RTThreadCreate(&pData->ReceiveThread, drvCharReceiveLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharRecv");
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync if (VBOX_FAILURE(rc))
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync rc = RTSemEventCreate(&pData->SendSem);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync AssertRC(rc);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
c18940aaee473ca75acc33491c1dc77b3e8094e0vboxsync rc = RTThreadCreate(&pData->SendThread, drvCharSendLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharSend");
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync if (VBOX_FAILURE(rc))
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsync
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsync PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
5a2abb913df18ed4e588e179043708667f7bc1a1vboxsync PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
e3f5c51715cbf77ae2d2e9d05bafd00d69b1bec9vboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return VINF_SUCCESS;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync}
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/**
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Destruct a char driver instance.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync *
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 *
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param pDrvIns The driver instance data.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync{
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDRVCHAR pData = PDMINS2DATA(pDrvIns, PDRVCHAR);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pData->fShutdown = true;
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync if (pData->ReceiveThread)
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync {
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync RTThreadWait(pData->ReceiveThread, 1000, NULL);
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync if (pData->ReceiveThread != NIL_RTTHREAD)
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync LogRel(("Char%d: receive thread did not terminate\n", pDrvIns->iInstance));
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync }
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /* Empty the send queue */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync pData->iSendQueueTail = pData->iSendQueueHead = 0;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
574bc38df445ce8595342c92f4341545b5277048vboxsync RTSemEventSignal(pData->SendSem);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync RTSemEventDestroy(pData->SendSem);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync pData->SendSem = NIL_RTSEMEVENT;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync if (pData->SendThread)
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync {
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync RTThreadWait(pData->SendThread, 1000, NULL);
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync if (pData->SendThread != NIL_RTTHREAD)
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync LogRel(("Char%d: send thread did not terminate\n", pDrvIns->iInstance));
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync }
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync}
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/**
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Char driver registration record.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncconst PDMDRVREG g_DrvChar =
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync{
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* u32Version */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDM_DRVREG_VERSION,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* szDriverName */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync "Char",
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pszDescription */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync "Generic char driver.",
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* fFlags */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* fClass. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDM_DRVREG_CLASS_CHAR,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* cMaxInstances */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync ~0,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* cbInstance */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync sizeof(DRVCHAR),
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnConstruct */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync drvCharConstruct,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnDestruct */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync drvCharDestruct,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnIOCtl */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync NULL,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnPowerOn */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync NULL,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnReset */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync NULL,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnSuspend */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync NULL,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnResume */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync NULL,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* pfnDetach */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync NULL,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** pfnPowerOff */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync NULL
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync};
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync