DrvHostParallel.cpp revision bb56e9b73a923e975bc18bea69639b4c07e32f60
/* $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
/** @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)
#else
# error "Not ported"
#endif
#if defined(VBOX_WITH_WIN_PARPORT_SUP) && defined(IN_RING0)
# include <iprt/asm-amd64-x86.h>
#endif
#if defined(VBOX_WITH_WIN_PARPORT_SUP) && defined(IN_RING3)
# include <Windows.h>
# include <setupapi.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. */
/** Device Path */
char *pszDevicePath;
/** Device Handle */
#ifndef VBOX_WITH_WIN_PARPORT_SUP
/** Thread waiting for interrupts. */
/** Wakeup pipe read end. */
/** Wakeup pipe write end. */
/** Current mode the parallel port is in. */
#endif
#ifdef VBOX_WITH_WIN_PARPORT_SUP
/** Data register. */
/** Status register. */
/** Control register. */
/** Data read buffer. */
/** Control read buffer. */
/** Status read buffer. */
/** Parallel port name */
char szParportName[6];
/** Whether the parallel port is available or not. */
bool fParportAvail;
/** Device Handle */
#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))) )
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#define CTRL_REG_OFFSET 2
#define STATUS_REG_OFFSET 1
#define LPT_CONTROL_ENABLE_BIDIRECT 0x20
#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.
*
*/
{
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;
/* I have included break after each case. Need to work on this. */
switch ((DRVHOSTPARALLELR0OP)uOperation)
{
case DRVHOSTPARALLELR0OP_READ:
break;
break;
break;
break;
break;
break;
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.
*/
{
short wHeaderSize;
uint32_t u32ParportAddr = 0;
wHeaderSize = sizeof(IO_DES);
if (cmRet != CR_SUCCESS)
{
if (cmRet != CR_SUCCESS)
return 0;
}
if (cmRet != CR_SUCCESS)
{
return 0;
}
/* This loop is based on the fact that only one resourece is assigned to
* the LPT port. If multiple resources (address range) are assigned to
* to LPT port, it will pick and return the last one
*/
for (;;)
{
u32Size = 0;
if (cmRet != CR_SUCCESS)
{
LogFlowFunc(("Failed to get Size \n"));
break;
}
if (!pBuf)
{
break;
}
if (cmRet != CR_SUCCESS)
{
LogFlowFunc(("Failed to get Des Data \n"));
if (pBuf)
break;
}
LogFlowFunc(("call GetIOResource\n"));
if (pBuf)
rdPrevResDes = 0;
2,
0L,
0L);
if (pBuf)
if (cmRet != CR_SUCCESS)
break;
}
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. */
{
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
if (pBuf)
}
else
{
/* No need to bother about this error (in most cases its errno=13,
* INVALID_DATA . Just break from here and proceed to next device
* enumerated item
*/
break;
}
}
{
if (u32ParportAddr)
{
/* Find parallel port name and update the shared data struncture */
/* check for the confirmation for the availability of parallel port */
{
LogFlowFunc(("Parallel port Not Found. \n"));
return VERR_NOT_FOUND;
}
LogFlowFunc(("Parallel port string not properly formatted.\n"));
return VERR_NOT_FOUND;
}
/* check for the confirmation for the availability of parallel port */
{
LogFlowFunc(("Parallel Port Not Found.\n"));
return VERR_NOT_FOUND;
}
/* checking again to make sure that we have got a valid name and in valid format too. */
LogFlowFunc(("Parallel Port name \"LPT\" Not Found.\n"));
return VERR_NOT_FOUND;
}
{
LogFlowFunc(("Printer Port Name Not Found.\n"));
return VERR_NOT_FOUND;
}
pThis->fParportAvail = true;
}
else
LogFlowFunc(("u32Parport Addr No Available \n"));
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 */
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 */
if (pThis->fParportAvail)
{
{
LogFlowFunc(("calling R0 to read from parallel port\n"));
}
}
# 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
{
/*
* 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.
*/
{
int rc;
#ifndef VBOX_WITH_WIN_PARPORT_SUP
{
}
{
}
{
}
if (pThis->pszDevicePath)
{
}
#else /* VBOX_WITH_WIN_PARPORT_SUP */
{
}
#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
#else
#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 = 0;
pThis->u32LptAddrControl = 0;
pThis->u32LptAddrStatus = 0;
/* If we have the char port availabe use it , else I am not getting exclusive access to parallel port.
Read and write will be done only if addresses are available
*/
if (pThis->szParportName)
{
}
#endif
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*/