DevParallel.cpp revision d59a43b735abea2db17caa9b5661d2f5118f0e02
/* $Id$ */
/** @file
* DevParallel - Parallel (Port) Device Emulation.
*
* Contributed by: Alexander Eichner
* Based on DevSerial.cpp
*/
/*
* Copyright (C) 2006-2007 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_DEV_PARALLEL
#include <iprt/semaphore.h>
#include <iprt/critsect.h>
#include "../Builtins.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#define PARALLEL_SAVED_STATE_VERSION 1
/* defines for accessing the register bits */
#define LPT_STATUS_BUSY 0x80
#define LPT_STATUS_ACK 0x40
#define LPT_STATUS_PAPER_OUT 0x20
#define LPT_STATUS_SELECT_IN 0x10
#define LPT_STATUS_ERROR 0x08
#define LPT_STATUS_IRQ 0x04
#define LPT_STATUS_EPP_TIMEOUT 0x01
#define LPT_CONTROL_ENABLE_BIDIRECT 0x20
#define LPT_CONTROL_ENABLE_IRQ_VIA_ACK 0x10
#define LPT_CONTROL_SELECT_PRINTER 0x08
#define LPT_CONTROL_RESET 0x04
#define LPT_CONTROL_AUTO_LINEFEED 0x02
#define LPT_CONTROL_STROBE 0x01
/** mode defines for the extended control register */
#define LPT_ECP_ECR_CHIPMODE_MASK 0xe0
#define LPT_ECP_ECR_CHIPMODE_CONFIGURATION 0x07
#define LPT_ECP_ECR_CHIPMODE_FIFO_TEST 0x06
#define LPT_ECP_ECR_CHIPMODE_RESERVED 0x05
#define LPT_ECP_ECR_CHIPMODE_EPP 0x04
#define LPT_ECP_ECR_CHIPMODE_ECP_FIFO 0x03
#define LPT_ECP_ECR_CHIPMODE_PP_FIFO 0x02
#define LPT_ECP_ECR_CHIPMODE_BYTE 0x01
#define LPT_ECP_ECR_CHIPMODE_COMPAT 0x00
/** FIFO status bits in extended control register */
#define LPT_ECP_ECR_FIFO_MASK 0x03
#define LPT_ECP_ECR_FIFO_SOME_DATA 0x00
#define LPT_ECP_ECR_FIFO_FULL 0x02
#define LPT_ECP_ECR_FIFO_EMPTY 0x01
#define LPT_ECP_CONFIGA_FIFO_WITDH_MASK 0x70
#define LPT_ECP_CONFIGA_FIFO_WIDTH_16 0x00
#define LPT_ECP_CONFIGA_FIFO_WIDTH_32 0x20
#define LPT_ECP_CONFIGA_FIFO_WIDTH_8 0x10
#define LPT_ECP_FIFO_DEPTH 2
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef struct ParallelState
{
/** Access critical section. */
/** Pointer to the device instance - R3 Ptr */
/** Pointer to the device instance - R0 Ptr */
/** Pointer to the device instance - RC Ptr */
/** The base interface. */
/** The host device port interface. */
/** Pointer to the attached base driver. */
/** Pointer to the attached host device. */
/** Unused event semaphore... */
/** The ECP FIFO implementation*/
int act_fifo_pos_write;
int act_fifo_pos_read;
int irq;
bool fGCEnabled;
bool fR0Enabled;
bool afAlignment[1];
typedef DEVPARALLELSTATE ParallelState;
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
#define PDMIHOSTPARALLELPORT_2_PARALLELSTATE(pInstance) ( (ParallelState *)((uintptr_t)(pInterface) - RT_OFFSETOF(ParallelState, IHostParallelPort)) )
#define PDMIHOSTDEVICEPORT_2_PARALLELSTATE(pInstance) ( (ParallelState *)((uintptr_t)(pInterface) - RT_OFFSETOF(ParallelState, IHostDevicePort)) )
#define PDMIBASE_2_PARALLELSTATE(pInstance) ( (ParallelState *)((uintptr_t)(pInterface) - RT_OFFSETOF(ParallelState, IBase)) )
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
PDMBOTHCBDECL(int) parallelIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
PDMBOTHCBDECL(int) parallelIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
#if 0
PDMBOTHCBDECL(int) parallelIOPortReadECP(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
PDMBOTHCBDECL(int) parallelIOPortWriteECP(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
#endif
#ifdef IN_RING3
static void parallel_set_irq(ParallelState *s)
{
if (s->reg_control & LPT_CONTROL_ENABLE_IRQ_VIA_ACK)
{
}
}
static void parallel_clear_irq(ParallelState *s)
{
}
#endif
{
unsigned char ch;
addr &= 7;
switch(addr) {
default:
case 0:
#ifndef IN_RING3
return VINF_IOM_HC_IOPORT_WRITE;
#else
if (RT_LIKELY(s->pDrvHostParallelConnector))
{
}
#endif
break;
case 1:
break;
case 2:
/* Set the reserved bits to one */
if (ch != s->reg_control) {
#ifndef IN_RING3
return VINF_IOM_HC_IOPORT_WRITE;
#else
s->reg_control = val;
#endif
}
break;
case 3:
s->reg_epp_addr = val;
break;
case 4:
s->reg_epp_data = val;
break;
case 5:
break;
case 6:
break;
case 7:
break;
}
return VINF_SUCCESS;
}
{
*pRC = VINF_SUCCESS;
addr &= 7;
switch(addr) {
default:
case 0:
if (!(s->reg_control & LPT_CONTROL_ENABLE_BIDIRECT))
else
{
#ifndef IN_RING3
#else
if (RT_LIKELY(s->pDrvHostParallelConnector))
{
int rc = s->pDrvHostParallelConnector->pfnRead(s->pDrvHostParallelConnector, &s->reg_data, &cbRead);
}
#endif
}
break;
case 1:
#ifndef IN_RING3
#else
if (RT_LIKELY(s->pDrvHostParallelConnector))
{
}
ret = s->reg_status;
#endif
break;
case 2:
ret = s->reg_control;
break;
case 3:
ret = s->reg_epp_addr;
break;
case 4:
ret = s->reg_epp_data;
break;
case 5:
break;
case 6:
break;
case 7:
break;
}
return ret;
}
#if 0
{
unsigned char ch;
addr &= 7;
switch(addr) {
default:
case 0:
s->act_fifo_pos_write++;
if (s->act_fifo_pos_write < LPT_ECP_FIFO_DEPTH) {
/* FIFO has some data (clear both FIFO bits) */
} else {
/* FIFO is full */
/* Clear FIFO empty bit */
s->reg_ecp_ecr &= ~LPT_ECP_ECR_FIFO_EMPTY;
/* Set FIFO full bit */
s->reg_ecp_ecr |= LPT_ECP_ECR_FIFO_FULL;
s->act_fifo_pos_write = 0;
}
} else {
s->reg_ecp_base_plus_400h = ch;
}
break;
case 1:
s->reg_ecp_config_b = ch;
break;
case 2:
/* If we change the mode clear FIFO */
/* reset the fifo */
s->act_fifo_pos_write = 0;
s->act_fifo_pos_read = 0;
/* Set FIFO empty bit */
/* Clear FIFO full bit */
s->reg_ecp_ecr &= ~LPT_ECP_ECR_FIFO_FULL;
}
/* Set new mode */
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
}
return VINF_SUCCESS;
}
{
*pRC = VINF_SUCCESS;
addr &= 7;
switch(addr) {
default:
case 0:
s->act_fifo_pos_read++;
if (s->act_fifo_pos_read == LPT_ECP_FIFO_DEPTH)
s->act_fifo_pos_read = 0; /* end of FIFO, start at beginning */
if (s->act_fifo_pos_read == s->act_fifo_pos_write) {
/* FIFO is empty */
/* Set FIFO empty bit */
/* Clear FIFO full bit */
s->reg_ecp_ecr &= ~LPT_ECP_ECR_FIFO_FULL;
} else {
/* FIFO has some data (clear all FIFO bits) */
}
} else {
ret = s->reg_ecp_base_plus_400h;
}
break;
case 1:
ret = s->reg_ecp_config_b;
break;
case 2:
ret = s->reg_ecp_ecr;
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
}
return ret;
}
#endif
#ifdef IN_RING3
{
return VINF_SUCCESS;
}
#endif /* IN_RING3 */
/**
* Port I/O Handler for OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param Port Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
{
int rc = VINF_SUCCESS;
if (cb == 1)
{
if (rc == VINF_SUCCESS)
{
}
}
else
return rc;
}
/**
* Port I/O Handler for IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param Port Port number used for the IN operation.
* @param pu32 Where to return the read value.
* @param cb The value size in bytes.
*/
{
int rc = VINF_SUCCESS;
if (cb == 1)
{
if (rc == VINF_SUCCESS)
{
}
}
else
return rc;
}
#if 0
/**
* Port I/O Handler for OUT operations on ECP registers.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param Port Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
{
int rc = VINF_SUCCESS;
if (cb == 1)
{
if (rc == VINF_SUCCESS)
{
}
}
else
return rc;
}
/**
* Port I/O Handler for IN operations on ECP registers.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument.
* @param Port Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
{
int rc = VINF_SUCCESS;
if (cb == 1)
{
if (rc == VINF_SUCCESS)
{
}
}
else
return rc;
}
#endif
#ifdef IN_RING3
/**
* @copydoc FNSSMDEVLIVEEXEC
*/
{
return VINF_SSM_DONT_CALL_AGAIN;
}
/**
* @copydoc FNSSMDEVSAVEEXEC
*/
{
return VINF_SUCCESS;
}
/**
* @copydoc FNSSMDEVLOADEXEC
*/
{
AssertMsgReturn(uVersion == PARALLEL_SAVED_STATE_VERSION, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
if (uPass == SSM_PASS_FINAL)
{
}
/* the config */
if (RT_FAILURE(rc))
return rc;
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("IRQ changed: config=%#x state=%#x"), pThis->irq, iIrq);
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("IOBase changed: config=%#x state=%#x"), pThis->base, uIoBase);
/* not necessary... but it doesn't harm. */
return VINF_SUCCESS;
}
/**
* @copydoc FNPDMDEVRELOCATE
*/
{
}
/** @copydoc PIBASE::pfnqueryInterface */
{
switch (enmInterface)
{
case PDMINTERFACE_BASE:
return &pThis->IHostParallelPort;
default:
return NULL;
}
}
/**
* Destruct a device instance.
*
* Most VM resources are freed by the VM. This callback is provided so that any non-VM
* resources can be freed correctly.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
{
return VINF_SUCCESS;
}
/**
* Construct a device instance for a VM.
*
* @returns VBox status.
* @param pDevIns The device instance data.
* If the registration structure is needed, pDevIns->pDevReg points to it.
* @param iInstance Instance number. Use this to figure out which registers and such to use.
* The device number is also found in pDevIns->iInstance, but since it's
* likely to be freqently used PDM passes it as parameter.
* @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
* of the device instance. It's also found in pDevIns->pCfgHandle, but like
* iInstance it's expected to be used a bit in this function.
*/
int iInstance,
{
int rc;
/*
* Init the data so parallelDestruct doesn't choke.
*/
/* IBase */
/* IHostParallelPort */
/* Init parallel state */
pThis->act_fifo_pos_read = 0;
pThis->act_fifo_pos_write = 0;
/*
* Validate and read the configuration.
*/
N_("Configuration error: Unknown config key"));
if (RT_FAILURE(rc))
N_("Configuration error: Failed to get the \"GCEnabled\" value"));
if (RT_FAILURE(rc))
N_("Configuration error: Failed to get the \"R0Enabled\" value"));
if (RT_FAILURE(rc))
N_("Configuration error: Failed to get the \"IRQ\" value"));
if (RT_FAILURE(rc))
N_("Configuration error: Failed to get the \"IOBase\" value"));
/*
* Initialize critical section and event semaphore.
* This must of course be done before attaching drivers or anything else which can call us back..
*/
char szName[24];
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
/*
* Register the I/O ports and saved state.
*/
if (RT_FAILURE(rc))
return rc;
#if 0
/* register ecp registers */
if (RT_FAILURE(rc))
return rc;
#endif
if (pThis->fGCEnabled)
{
if (RT_FAILURE(rc))
return rc;
#if 0
if (RT_FAILURE(rc))
return rc;
#endif
}
if (pThis->fR0Enabled)
{
if (RT_FAILURE(rc))
return rc;
#if 0
if (RT_FAILURE(rc))
return rc;
#endif
}
if (RT_FAILURE(rc))
return rc;
/*
* Attach the parallel port driver and get the interfaces.
* For now no run-time changes are supported.
*/
if (RT_SUCCESS(rc))
{
pThis->pDrvHostParallelConnector = (PDMIHOSTPARALLELCONNECTOR *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase,
if (!pThis->pDrvHostParallelConnector)
{
return VERR_PDM_MISSING_INTERFACE;
}
/** @todo provide read notification interface!!!! */
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
{
}
else
{
}
/* Set compatibility mode */
pThis->pDrvHostParallelConnector->pfnSetMode(pThis->pDrvHostParallelConnector, PDM_PARALLEL_PORT_MODE_COMPAT);
/* Get status of control register */
pThis->pDrvHostParallelConnector->pfnReadControl(pThis->pDrvHostParallelConnector, &pThis->reg_control);
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceParallelPort =
{
/* u32Version */
/* szDeviceName */
"parallel",
/* szRCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
"Parallel Communication Port",
/* fFlags */
/* fClass */
/* cMaxInstances */
1,
/* cbInstance */
sizeof(ParallelState),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */