DevSerial.cpp revision c14f98bb1a1d803f3ca09ed07fec34bba4a8b010
/** @file
*
* VBox serial device:
* Serial communication port driver
*/
/*
* Copyright (C) 2006-2007 innotek GmbH
*
* 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.
*/
/*
* This code is based on:
*
* QEMU 16450 UART emulation
*
* Copyright (c) 2003-2004 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DEV_SERIAL
#include <iprt/semaphore.h>
#include <iprt/critsect.h>
#include "Builtins.h"
#undef VBOX_SERIAL_PCI /* The PCI variant has lots of problems: wrong IRQ line and wrong IO base assigned. */
#ifdef VBOX_SERIAL_PCI
#endif /* VBOX_SERIAL_PCI */
#define SERIAL_SAVED_STATE_VERSION 3
/*
* These are the definitions for the Modem Control Register
*/
/*
* These are the definitions for the Modem Status Register
*/
struct SerialState
{
/** Access critical section. */
/** Pointer to the device instance. */
/** Pointer to the device instance. */
#endif
/** The base interface. */
/** The character port interface. */
/** Pointer to the attached base driver. */
/** Pointer to the attached character driver. */
/* NOTE: this hidden state is necessary for tx irq generation as
it can be reset while reading iir */
int thr_ipending;
int irq;
bool msr_changed;
bool fGCEnabled;
bool fR0Enabled;
bool afAlignment[6];
int last_break_enable;
#ifdef VBOX_SERIAL_PCI
#endif /* VBOX_SERIAL_PCI */
};
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
#ifdef VBOX_SERIAL_PCI
#define PCIDEV_2_SERIALSTATE(pPciDev) ( (SerialState *)((uintptr_t)(pPciDev) - RT_OFFSETOF(SerialState, dev)) )
#endif /* VBOX_SERIAL_PCI */
#define PDMIBASE_2_SERIALSTATE(pInstance) ( (SerialState *)((uintptr_t)(pInterface) - RT_OFFSETOF(SerialState, IBase)) )
#define PDMICHARPORT_2_SERIALSTATE(pInstance) ( (SerialState *)((uintptr_t)(pInterface) - RT_OFFSETOF(SerialState, ICharPort)) )
PDMBOTHCBDECL(int) serialIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
PDMBOTHCBDECL(int) serialIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
#ifdef IN_RING3
static void serial_update_irq(SerialState *s)
{
s->iir = UART_IIR_RDI;
s->iir = UART_IIR_THRI;
s->iir = UART_IIR_RLSI;
} else {
s->iir = UART_IIR_NO_INT;
}
if (s->iir != UART_IIR_NO_INT) {
#ifdef VBOX_SERIAL_PCI
#else /* !VBOX_SERIAL_PCI */
#endif /* !VBOX_SERIAL_PCI */
} else {
#ifdef VBOX_SERIAL_PCI
#else /* !VBOX_SERIAL_PCI */
#endif /* !VBOX_SERIAL_PCI */
}
}
static void serial_update_parameters(SerialState *s)
{
if (s->lcr & 0x08) {
if (s->lcr & 0x10)
parity = 'E';
else
parity = 'O';
} else {
parity = 'N';
}
if (s->lcr & 0x04)
stop_bits = 2;
else
stop_bits = 1;
if (s->divider == 0)
return;
}
#endif
{
unsigned char ch;
addr &= 7;
#ifndef IN_RING3
NOREF(s);
return VINF_IOM_HC_IOPORT_WRITE;
#else
switch(addr) {
default:
case 0:
if (s->lcr & UART_LCR_DLAB) {
} else {
s->thr_ipending = 0;
s->lsr &= ~UART_LSR_THRE;
{
}
s->thr_ipending = 1;
s->lsr |= UART_LSR_THRE;
s->lsr |= UART_LSR_TEMT;
}
break;
case 1:
if (s->lcr & UART_LCR_DLAB) {
} else {
if (s->lsr & UART_LSR_THRE) {
s->thr_ipending = 1;
}
}
break;
case 2:
break;
case 3:
{
int break_enable;
{
}
if (break_enable != s->last_break_enable) {
}
}
break;
case 4:
{
int rc = s->pDrvChar->pfnSetModemLines(s->pDrvChar, !!(s->mcr & UART_MCR_RTS), !!(s->mcr & UART_MCR_DTR));
}
break;
case 5:
break;
case 6:
break;
case 7:
break;
}
return VINF_SUCCESS;
#endif
}
{
*pRC = VINF_SUCCESS;
addr &= 7;
switch(addr) {
default:
case 0:
if (s->lcr & UART_LCR_DLAB) {
} else {
#ifndef IN_RING3
#else
{
}
#endif
}
break;
case 1:
if (s->lcr & UART_LCR_DLAB) {
} else {
}
break;
case 2:
#ifndef IN_RING3
#else
/* reset THR pending bit */
s->thr_ipending = 0;
#endif
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
if (s->mcr & UART_MCR_LOOP) {
/* in loopback, the modem output pins are connected to the
inputs */
} else {
#ifndef IN_RING3
#else
/* Reset delta bits. */
s->msr &= ~UART_MSR_ANY_DELTA;
s->msr_changed = false;
#endif
}
break;
case 7:
break;
}
return ret;
}
#ifdef IN_RING3
static DECLCALLBACK(int) serialNotifyRead(PPDMICHARPORT pInterface, const void *pvBuf, size_t *pcbRead)
{
int rc;
{
/* If a character is still in the read queue, then wait for it to be emptied. */
if (VBOX_FAILURE(rc))
return rc;
}
{
*pcbRead = 1;
rc = VINF_SUCCESS;
}
else
rc = VERR_TIMEOUT;
return rc;
}
static DECLCALLBACK(int) serialNotifyStatusLinesChanged(PPDMICHARPORT pInterface, uint32_t newStatusLines)
{
/* Set new states. */
newMsr |= UART_MSR_DCD;
newMsr |= UART_MSR_RI;
newMsr |= UART_MSR_DSR;
newMsr |= UART_MSR_CTS;
/* Compare the old and the new states and set the delta bits accordingly. */
newMsr |= UART_MSR_DDCD;
newMsr |= UART_MSR_TERI;
newMsr |= UART_MSR_DDSR;
newMsr |= UART_MSR_DCTS;
pData->msr_changed = true;
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 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;
}
#ifdef IN_RING3
/**
* Saves a state of the serial port device.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to save the state to.
*/
{
}
/**
* Loads a saved serial port device state.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to the saved state.
* @param u32Version The data unit version number.
*/
{
int rc;
if (u32Version != SERIAL_SAVED_STATE_VERSION)
{
}
if (VBOX_FAILURE(rc))
return rc;
if (u32 != ~0U)
{
}
/* Be careful with pointers in the structure; they are not preserved
* in the saved state. */
{
}
return VINF_SUCCESS;
}
/**
* @copydoc FNPDMDEVRELOCATE
*/
{
}
#ifdef VBOX_SERIAL_PCI
static DECLCALLBACK(int) serialIOPortRegionMap(PPCIDEVICE pPciDev, /* unsigned */ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
{
int rc = VINF_SUCCESS;
AssertMsg(RT_ALIGN(GCPhysAddress, 8) == GCPhysAddress, ("Expected 8 byte alignment. GCPhysAddress=%#x\n", GCPhysAddress));
/*
* Register our port IO handlers.
*/
return rc;
}
#endif /* VBOX_SERIAL_PCI */
/** @copyfrom PIBASE::pfnqueryInterface */
{
switch (enmInterface)
{
case PDMINTERFACE_BASE:
case PDMINTERFACE_CHAR_PORT:
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;
/*
* Validate configuration.
*/
{
AssertMsgFailed(("serialConstruct Invalid configuration values\n"));
}
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
pData->fGCEnabled = true;
else if (VBOX_FAILURE(rc))
N_("Configuration error: Failed to get the \"GCEnabled\" value"));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
pData->fR0Enabled = true;
else if (VBOX_FAILURE(rc))
N_("Configuration error: Failed to get the \"R0Enabled\" value"));
/* IBase */
/* ICharPort */
/*
* Initialize critical section.
* This must of course be done before attaching drivers or anything else which can call us back..
*/
char szName[24];
if (VBOX_FAILURE(rc))
return rc;
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
{
/* Provide sensible defaults. */
if (iInstance == 0)
irq_lvl = 4;
else if (iInstance == 1)
irq_lvl = 3;
}
else if (VBOX_FAILURE(rc))
N_("Configuration error: Failed to get the \"IRQ\" value"));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
{
if (iInstance == 0)
io_base = 0x3f8;
else if (iInstance == 1)
io_base = 0x2f8;
}
else if (VBOX_FAILURE(rc))
N_("Configuration error: Failed to get the \"IOBase\" value"));
#ifdef VBOX_SERIAL_PCI
if (VBOX_FAILURE(rc))
return rc;
/*
* Register the PCI I/O ports.
*/
if (VBOX_FAILURE(rc))
return rc;
#else /* !VBOX_SERIAL_PCI */
if (VBOX_FAILURE (rc))
return rc;
if (pData->fGCEnabled)
if (pData->fR0Enabled)
#endif /* !VBOX_SERIAL_PCI */
/* Attach the char driver and get the interfaces. For now no run-time
* changes are supported. */
if (VBOX_SUCCESS(rc))
{
pData->pDrvChar = (PDMICHAR *)pData->pDrvBase->pfnQueryInterface(pData->pDrvBase, PDMINTERFACE_CHAR);
{
return VERR_PDM_MISSING_INTERFACE;
}
/** @todo provide read notification interface!!!! */
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
{
}
else
{
}
pDevIns, /* pDevIns */
iInstance, /* u32Instance */
SERIAL_SAVED_STATE_VERSION, /* u32Version */
sizeof (*pData), /* cbGuess */
NULL, /* pfnSavePrep */
serialSaveExec, /* pfnSaveExec */
NULL, /* pfnSaveDone */
NULL, /* pfnLoadPrep */
serialLoadExec, /* pfnLoadExec */
NULL /* pfnLoadDone */
);
if (VBOX_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceSerialPort =
{
/* u32Version */
/* szDeviceName */
"serial",
/* szGCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
"Serial Communication Port",
/* fFlags */
PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
/* fClass */
/* cMaxInstances */
1,
/* cbInstance */
sizeof(SerialState),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface. */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */