DrvHostParallel.cpp revision 4a1d0d5d3004efb053f61cc5098c7dbd6be96b01
/* $Id$ */
/** @file
* VirtualBox Host Parallel Port Driver.
*
* Initial Linux-only code contributed by: Alexander Eichner
*/
/*
* Copyright (C) 2006-2012 Oracle Corporation
*
* 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DRV_HOST_PARALLEL
#include <iprt/semaphore.h>
#ifdef RT_OS_LINUX
# include <fcntl.h>
# include <unistd.h>
# include <errno.h>
#endif
/** @todo r=bird: The following indicator is neccessary to select the right
* code. */
/** @def VBOX_WITH_WIN_PARPORT_SUP *
* Indicates whether to use the generic direct hardware access or host specific
* code to access the parallel port.
*/
#if defined(RT_OS_LINUX)
#elif defined(RT_OS_WINDOWS)
//# define VBOX_WITH_WIN_PARPORT_SUP
#else
# error "Not ported"
#endif
#if defined(VBOX_WITH_WIN_PARPORT_SUP) && defined(IN_RING0)
# include <Wdm.h>
# include <parallel.h>
# include <iprt/asm-amd64-x86.h>
#endif
#if defined(VBOX_WITH_WIN_PARPORT_SUP) && defined(IN_RING3)
# include <stdio.h>
# include <windows.h>
# include <devguid.h>
# include <setupapi.h>
# include <regstr.h>
# include <string.h>
# include <cfgmgr32.h>
#endif
#include "VBoxDD.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Host parallel port driver instance data.
* @implements PDMIHOSTPARALLELCONNECTOR
*/
typedef struct DRVHOSTPARALLEL
{
/** Pointer to the driver instance structure. */
/** Pointer to the driver instance. */
/** Our host device interface. */
/** Our host device interface. */
/** Ring-3 base interface for the ring-0 context. */
/** Device Path */
char *pszDevicePath;
/** Device Handle */
#ifndef VBOX_WITH_WIN_PARPORT_SUP /** @todo r=bird: Eliminate thing not needed, it rules out things you might have missed. */
/** Thread waiting for interrupts. */
/** Wakeup pipe read end. */
/** Wakeup pipe write end. */
#endif
/** Current mode the parallel port is in. */
#ifdef VBOX_WITH_WIN_PARPORT_SUP
/** Data register. */
/** Status register. */
/** Control register. */
/** Data read buffer. */
/** Control read buffer. */
/** Status read buffer. */
/** Whether the parallel port is available or not. */
bool fParportAvail;
# ifdef IN_RING0 /** @todo r=bird: This isn't needed and will not work as you always need to declare all structure members in all contexts. (Size will differer between ring-3 and ring-0 now, meaning you'll corrupt heap because ring-3 does the alloc.) */
typedef struct DEVICE_EXTENSION
{
# endif
#endif /* VBOX_WITH_WIN_PARPORT_SUP */
/**
* Ring-0 operations.
*/
typedef enum DRVHOSTPARALLELR0OP
{
/** Invalid zero value. */
/** Perform R0 initialization. */
/** Read data. */
/** Read status register. */
/** Read control register. */
/** Write data. */
/** Write control register. */
/** Set port direction. */
/** Converts a pointer to DRVHOSTPARALLEL::IHostDeviceConnector to a PDRHOSTPARALLEL. */
#define PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface) ( (PDRVHOSTPARALLEL)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector))) )
#ifdef VBOX_WITH_WIN_PARPORT_SUP
#ifdef IN_RING0
/**
* R0 mode function to write byte value to data port.
* @returns VBox status code.
* @param pDrvIns Driver instance.
* @param u64Arg Data to be written to data register.
*
*/
static int drvR0HostParallelReqWrite(PPDMDRVINS pDrvIns, uint64_t u64Arg) /** @todo r=bird: PDMBOTHCBDECL is only required for entry points. Everything which doesn't need to be globally visible shall be static! */
{
return VINF_SUCCESS;
}
/**
* R0 mode function to write byte value to parallel port control
* register.
* @returns VBox status code.
* @param pDrvIns Driver instance.
* @param u64Arg Data to be written to control register.
*/
{
return VINF_SUCCESS;
}
/**
* R0 mode function to ready byte value from the parallel port
* data register
* @returns VBox status code.
* @param pDrvIns Driver instance.
* @param u64Arg Not used.
*/
{
return VINF_SUCCESS;
}
/**
* R0 mode function to ready byte value from the parallel port
* control register.
* @returns VBox status code.
* @param pDrvIns Driver instance.
* @param u64Arg Not used.
*/
{
return VINF_SUCCESS;
}
/**
* R0 mode function to ready byte value from the parallel port
* status register.
* @returns VBox status code.
* @param pDrvIns Driver instance.
* @param u64Arg Not used.
*/
{
return VINF_SUCCESS;
}
/**
* R0 mode function to set the direction of parallel port -
* operate in bidirectional mode or single direction.
* @returns VBox status code.
* @param pDrvIns Driver instance.
* @param u64Arg Mode.
*/
{
if (u64Arg)
{
}
else
{
}
return VINF_SUCCESS;
}
/**
* @interface_method_impl{FNPDMDRVREQHANDLERR0}
*/
PDMBOTHCBDECL(int) drvR0HostParallelReqHandler(PPDMDRVINS pDrvIns, uint32_t uOperation, uint64_t u64Arg)
{
int rc;
switch ((DRVHOSTPARALLELR0OP)uOperation)
{
case DRVHOSTPARALLELR0OP_READ:
default: /* not supported */
}
return rc;
}
#endif /* IN_RING0 */
#endif /* VBOX_WITH_WIN_PARPORT_SUP */
#ifdef IN_RING3
# ifdef VBOX_WITH_WIN_PARPORT_SUP
/**
* Find IO port range for the parallel port and return the lower address.
*
* @returns parallel port IO address.
* @param DevInst Device Instance for parallel port.
*/
static uint32_t drvHostWinFindIORangeResource(const DEVINST DevInst) /** @todo r=bird: Prefix methods like in the rest of the file, since windows specific, throw in a 'Win'. */
{
short wHeaderSize;
wHeaderSize = sizeof(IO_DES);
if (cmRet != CR_SUCCESS)
{
if (cmRet != CR_SUCCESS)
return 0;
}
if (cmRet != CR_SUCCESS)
{
return 0;
}
for (;;)
{
u32Size = 0;
cmRet = CM_Get_Res_Des_Data_Size((PULONG)(&u32Size), nextLogConf, 0L); /** @todo r=bird: (PULONG)(&u32Size) - generally a bad idea, but not really a problem here though... Why don't you use ULONG for the size variable? Like 'ULONG cbBuf;'? */
if (cmRet != CR_SUCCESS)
{
CM_Free_Res_Des_Handle(nextLogConf); /** @todo r=bird: Why are you doing this twice in this code path? */
break;
}
if (!pBuf)
{
break;
}
if (cmRet != CR_SUCCESS)
{
break;
}
LogFlowFunc(("call GetIOResource\n"));
rdPrevResDes = 0;
2,
0L,
0L);
if (cmRet != CR_SUCCESS)
break;
/** @todo r=bird: No need to else soemthing when the 'then' clause
* returned or broke ouf of the loop. */
}
return u32ParportAddr;
}
/**
* Get Parallel port address and update the shared data
* structure.
* @returns VBox status code.
* @param pThis The host parallel port instance data.
*/
{
int rc = VINF_SUCCESS;
if (hDevInfo == INVALID_HANDLE_VALUE)
return VERR_INVALID_HANDLE;
/* Enumerate through all devices in Set. */
{
uint32_t u32BufSize = 0;
while (!SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_FRIENDLYNAME, (PDWORD)&u32DataType, (uint8_t *)pBuf,
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
if (pBuf)
/* Max size will never be more than 2048 bytes */
/** @todo r=bird: Too many parentheses and casts: (uint8_t *)((char*)RTMemAlloc(u32BufSize * 2)) */
}
else
break;
}
if (pBuf) /** @todo r=bird: You're not checking errors here. */
{
if (strstr((char*)pBuf, "LPT")) /** @todo Use IPRT equivalent? r=klaus: make check much more precise, must be LPT + number (>= 1), without anything else before or after. */
{
LogFlowFunc(("found parallel port\n"));
if (u32ParportAddr)
{
pThis->fParportAvail = true;
}
if (pThis->fParportAvail)
break;
}
}
if (pBuf)
if (pThis->fParportAvail)
{
/* Parallel port address has been found. No need to iterate further. */
break;
}
}
return rc;
}
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
/**
* Changes the current mode of the host parallel port.
*
* @returns VBox status code.
* @param pThis The host parallel port instance data.
* @param enmMode The mode to change the port to.
*/
{
int iMode = 0;
int rc = VINF_SUCCESS;
# ifndef VBOX_WITH_WIN_PARPORT_SUP
int rcLnx;
{
switch (enmMode)
{
break;
break;
break;
default:
return VERR_NOT_SUPPORTED;
}
if (RT_UNLIKELY(rcLnx < 0))
else
}
return rc;
# else /* VBOX_WITH_WIN_PARPORT_SUP */
return VINF_SUCCESS;
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
}
/* -=-=-=-=- IBase -=-=-=-=- */
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTPARALLELCONNECTOR, &pThis->CTX_SUFF(IHostParallelConnector));
return NULL;
}
/* -=-=-=-=- IHostDeviceConnector -=-=-=-=- */
/** @copydoc PDMICHARCONNECTOR::pfnWrite */
static DECLCALLBACK(int) drvHostParallelWrite(PPDMIHOSTPARALLELCONNECTOR pInterface, const void *pvBuf, size_t cbWrite, PDMPARALLELPORTMODE enmMode)
{
//PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
PDRVHOSTPARALLEL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
int rc = VINF_SUCCESS;
int rcLnx = 0;
if (RT_FAILURE(rc))
return rc;
# ifndef VBOX_WITH_WIN_PARPORT_SUP
if (enmMode == PDM_PARALLEL_PORT_MODE_SPP)
{
/* Set the data lines directly. */
}
else
{
/* Use write interface. */
}
if (RT_UNLIKELY(rcLnx < 0))
# else /* VBOX_WITH_WIN_PARPORT_SUP */
/** @todo r=klaus this code assumes cbWrite==1, which may not be guaranteed forever */
if (pThis->fParportAvail)
{
}
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
return rc;
}
/**
* @interface_method_impl{PDMIBASE,pfnRead}
*/
static DECLCALLBACK(int) drvHostParallelRead(PPDMIHOSTPARALLELCONNECTOR pInterface, void *pvBuf, size_t cbRead, PDMPARALLELPORTMODE enmMode)
{
PDRVHOSTPARALLEL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
int rc = VINF_SUCCESS;
# ifndef VBOX_WITH_WIN_PARPORT_SUP
int rcLnx = 0;
if (RT_FAILURE(rc))
return rc;
if (enmMode == PDM_PARALLEL_PORT_MODE_SPP)
{
/* Set the data lines directly. */
}
else
{
/* Use write interface. */
}
if (RT_UNLIKELY(rcLnx < 0))
# else /* VBOX_WITH_WIN_PARPORT_SUP */
/** @todo r=klaus this code assumes cbRead==1, which may not be guaranteed forever */
LogFlowFunc(("calling R0 to read from parallel port\n"));
if (pThis->fParportAvail)
{
*(uint8_t *)pvBuf = (uint8_t)pThis->u8ReadIn; /** r=bird: *((uint8_t *)(pvBuf)) is too many parentheses. Heaping them up make the code harder to read. Please check C++ operator precedence rules. */
}
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
return rc;
}
static DECLCALLBACK(int) drvHostParallelSetPortDirection(PPDMIHOSTPARALLELCONNECTOR pInterface, bool fForward)
{
PDRVHOSTPARALLEL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
int rc = VINF_SUCCESS;
int iMode = 0;
if (!fForward)
iMode = 1;
# ifndef VBOX_WITH_WIN_PARPORT_SUP
if (RT_UNLIKELY(rcLnx < 0))
# else /* VBOX_WITH_WIN_PARPORT_SUP */
if (pThis->fParportAvail)
{
}
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
return rc;
}
/**
* @interface_method_impl{PDMIBASE,pfnWriteControl}
*/
static DECLCALLBACK(int) drvHostParallelWriteControl(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t fReg)
{
PDRVHOSTPARALLEL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
int rc = VINF_SUCCESS;
int rcLnx = 0;
# ifndef VBOX_WITH_WIN_PARPORT_SUP
if (RT_UNLIKELY(rcLnx < 0))
# else /* VBOX_WITH_WIN_PARPORT_SUP */
if (pThis->fParportAvail)
{
}
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
return rc;
}
/**
* @interface_method_impl{PDMIBASE,pfnReadControl}
*/
static DECLCALLBACK(int) drvHostParallelReadControl(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t *pfReg)
{
PDRVHOSTPARALLEL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
int rc = VINF_SUCCESS;
int rcLnx = 0;
# ifndef VBOX_WITH_WIN_PARPORT_SUP
if (RT_UNLIKELY(rcLnx < 0))
else
{
}
# else /* VBOX_WITH_WIN_PARPORT_SUP */
*pfReg = 0; /* Initialize the buffer*/
if (pThis->fParportAvail)
{
LogFlowFunc(("calling R0 to read control from parallel port\n"));
}
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
return rc;
}
/**
* @interface_method_impl{PDMIBASE,pfnReadStatus}
*/
static DECLCALLBACK(int) drvHostParallelReadStatus(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t *pfReg)
{
PDRVHOSTPARALLEL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
int rc = VINF_SUCCESS;
int rcLnx = 0;
# ifndef VBOX_WITH_WIN_PARPORT_SUP
if (RT_UNLIKELY(rcLnx < 0))
else
{
}
# else /* VBOX_WITH_WIN_PARPORT_SUP */
*pfReg = 0; /* Intialize the buffer. */
if (pThis->fParportAvail)
{
LogFlowFunc(("calling R0 to read status from parallel port\n"));
}
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
return rc;
}
# ifndef VBOX_WITH_WIN_PARPORT_SUP /** @todo r=bird: This whole function is unused in the direct access path. */
{
/*
* We can wait for interrupts using poll on linux hosts.
*/
{
int rc;
if (rc < 0)
{
return RTErrConvertFromErrno(errno);
}
break;
{
break;
/* notification to terminate -- drain the pipe */
char ch;
continue;
}
/* Interrupt occurred. */
}
return VINF_SUCCESS;
}
/**
* Unblock the monitor thread so it can respond to a state change.
*
* @returns a VBox status code.
* @param pDrvIns The driver instance.
* @param pThread The send thread.
*/
{
}
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
/**
* Destruct a host parallel 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.
*/
{
#ifndef VBOX_WITH_WIN_PARPORT_SUP
int rc;
rc = RTFileClose(pThis->hFileDevice); AssertRC(rc); /** @todo r=bird: Why aren't this closed on Windows? */
if (pThis->pszDevicePath)
{
}
#endif /* VBOX_WITH_WIN_PARPORT_SUP */
}
/**
* Construct a host parallel driver instance.
*
* @copydoc FNPDMDRVCONSTRUCT
*/
static DECLCALLBACK(int) drvHostParallelConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
/*
* Init basic data members and interfaces.
*
* Must be done before returning any failure because we've got a destructor.
*/
#ifndef VBOX_WITH_WIN_PARPORT_SUP
#endif
#ifdef VBOX_WITH_DRVINTNET_IN_R0
#endif
/* IBase. */
/* IHostParallelConnector. */
/*
* Validate the config.
*/
N_("Unknown host parallel configuration option, only supports DevicePath"));
/*
* Query configuration.
*/
/* Device */
if (RT_FAILURE(rc))
{
return rc;
}
/*
* Open the device
*/
rc = RTFileOpen(&pThis->hFileDevice, pThis->pszDevicePath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
if (RT_FAILURE(rc))
#ifndef VBOX_WITH_WIN_PARPORT_SUP
/*
* Try to get exclusive access to parallel port
*/
if (rc < 0)
N_("Parallel#%d could not get exclusive access for parallel port '%s'"
"Be sure that no other process or driver accesses this port"),
/*
* Claim the parallel port
*/
if (rc < 0)
N_("Parallel#%d could not claim parallel port '%s'"
"Be sure that no other process or driver accesses this port"),
/*
*/
if (!pThis->pDrvHostParallelPort)
return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Parallel#%d has no parallel port interface above"),
/*
* Create wakeup pipe.
*/
/*
* Start in SPP mode.
*/
if (RT_FAILURE(rc))
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostParallel#%d cannot change mode of parallel mode to SPP"), pDrvIns->iInstance);
/*
* Start waiting for interrupts.
*/
rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pMonitorThread, pThis, drvHostParallelMonitorThread, drvHostParallelWakeupMonitorThread, 0,
RTTHREADTYPE_IO, "ParMon");
if (RT_FAILURE(rc))
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostParallel#%d cannot create monitor thread"), pDrvIns->iInstance);
#else /* VBOX_WITH_WIN_PARPORT_SUP */
pThis->fParportAvail = false;
pThis->u32LptAddr = 0L;
pThis->u32LptAddrControl = 0L;
pThis->u32LptAddrStatus = 0L;
return rc;
/**@todo r=bird: Just remove or #if 0 this code.*/
/** @todo r=klaus convert to IPRT */
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); /** @todo r=bird: Continuation indent under start parentheses like the rest of the file. */
if (hPort == INVALID_HANDLE_VALUE) /** @todo r=bird: Please, variable == constant, not the other way around. Just learn to not make accidental assignments in conditionals! */
{
/** @todo r=klaus probably worth a nicely formatted release log entry,
* pointing to the device name etc etc. */
LogFlowFunc(("failed to get exclusive access to parallel port\n"));
/** @todo r=klaus wrong kind of error code, must be IPRT error code */
return GetLastError();
}
#endif /* VBOX_WITH_WIN_PARPORT_SUP */
return VINF_SUCCESS;
}
/**
* Char driver registration record.
*/
const PDMDRVREG g_DrvHostParallel =
{
/* u32Version */
/* szName */
"HostParallel",
/* szRCMod */
"",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
"Parallel host driver.",
/* fFlags */
#if defined(VBOX_WITH_WIN_PARPORT_SUP)
#else
#endif
/* fClass. */
/* cMaxInstances */
~0U,
/* cbInstance */
sizeof(DRVHOSTPARALLEL),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32EndVersion */
};
#endif /*IN_RING3*/