summaryrefslogtreecommitdiff
path: root/src/VBox/Devices/Serial/DevSerial.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Serial/DevSerial.cpp')
-rw-r--r--src/VBox/Devices/Serial/DevSerial.cpp692
1 files changed, 511 insertions, 181 deletions
diff --git a/src/VBox/Devices/Serial/DevSerial.cpp b/src/VBox/Devices/Serial/DevSerial.cpp
index 1ee327def..e35b8e90c 100644
--- a/src/VBox/Devices/Serial/DevSerial.cpp
+++ b/src/VBox/Devices/Serial/DevSerial.cpp
@@ -1,10 +1,11 @@
-/* $Id: DevSerial.cpp 28800 2010-04-27 08:22:32Z vboxsync $ */
+/* $Id: DevSerial.cpp 29892 2010-05-31 10:33:53Z vboxsync $ */
/** @file
- * DevSerial - 16450 UART emulation.
+ * DevSerial - 16550A UART emulation.
+ * (taken from hw/serial.c 2010/05/15 with modifications)
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -18,9 +19,10 @@
/*
* This code is based on:
*
- * QEMU 16450 UART emulation
+ * QEMU 16550A UART emulation
*
* Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -39,7 +41,6 @@
* 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.
- *
*/
/*******************************************************************************
@@ -65,57 +66,92 @@
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
-#define SERIAL_SAVED_STATE_VERSION 3
+#define SERIAL_SAVED_STATE_VERSION_16450 3
+#define SERIAL_SAVED_STATE_VERSION 4
+
+#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
-#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
+#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */
+#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */
+#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */
+#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */
-#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */
-#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */
-#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */
-#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */
+#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
+#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
-#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
-#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
+#define UART_IIR_MSI 0x00 /* Modem status interrupt */
+#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */
+#define UART_IIR_RDI 0x04 /* Receiver data interrupt */
+#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */
+#define UART_IIR_CTI 0x0C /* Character Timeout Indication */
-#define UART_IIR_MSI 0x00 /* Modem status interrupt */
-#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */
-#define UART_IIR_RDI 0x04 /* Receiver data interrupt */
-#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */
+#define UART_IIR_FENF 0x80 /* Fifo enabled, but not functionning */
+#define UART_IIR_FE 0xC0 /* Fifo enabled */
/*
* These are the definitions for the Modem Control Register
*/
-#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
-#define UART_MCR_OUT2 0x08 /* Out2 complement */
-#define UART_MCR_OUT1 0x04 /* Out1 complement */
-#define UART_MCR_RTS 0x02 /* RTS complement */
-#define UART_MCR_DTR 0x01 /* DTR complement */
+#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
+#define UART_MCR_OUT2 0x08 /* Out2 complement */
+#define UART_MCR_OUT1 0x04 /* Out1 complement */
+#define UART_MCR_RTS 0x02 /* RTS complement */
+#define UART_MCR_DTR 0x01 /* DTR complement */
/*
* These are the definitions for the Modem Status Register
*/
-#define UART_MSR_DCD 0x80 /* Data Carrier Detect */
-#define UART_MSR_RI 0x40 /* Ring Indicator */
-#define UART_MSR_DSR 0x20 /* Data Set Ready */
-#define UART_MSR_CTS 0x10 /* Clear to Send */
-#define UART_MSR_DDCD 0x08 /* Delta DCD */
-#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */
-#define UART_MSR_DDSR 0x02 /* Delta DSR */
-#define UART_MSR_DCTS 0x01 /* Delta CTS */
-#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */
-
-#define UART_LSR_TEMT 0x40 /* Transmitter empty */
-#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
-#define UART_LSR_BI 0x10 /* Break interrupt indicator */
-#define UART_LSR_FE 0x08 /* Frame error indicator */
-#define UART_LSR_PE 0x04 /* Parity error indicator */
-#define UART_LSR_OE 0x02 /* Overrun error indicator */
-#define UART_LSR_DR 0x01 /* Receiver data ready */
+#define UART_MSR_DCD 0x80 /* Data Carrier Detect */
+#define UART_MSR_RI 0x40 /* Ring Indicator */
+#define UART_MSR_DSR 0x20 /* Data Set Ready */
+#define UART_MSR_CTS 0x10 /* Clear to Send */
+#define UART_MSR_DDCD 0x08 /* Delta DCD */
+#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */
+#define UART_MSR_DDSR 0x02 /* Delta DSR */
+#define UART_MSR_DCTS 0x01 /* Delta CTS */
+#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */
+
+#define UART_LSR_TEMT 0x40 /* Transmitter empty */
+#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
+#define UART_LSR_BI 0x10 /* Break interrupt indicator */
+#define UART_LSR_FE 0x08 /* Frame error indicator */
+#define UART_LSR_PE 0x04 /* Parity error indicator */
+#define UART_LSR_OE 0x02 /* Overrun error indicator */
+#define UART_LSR_DR 0x01 /* Receiver data ready */
+#define UART_LSR_INT_ANY 0x1E /* Any of the lsr-interrupt-triggering status bits */
+
+/*
+ * Interrupt trigger levels.
+ * The byte-counts are for 16550A - in newer UARTs the byte-count for each ITL is higher.
+ */
+#define UART_FCR_ITL_1 0x00 /* 1 byte ITL */
+#define UART_FCR_ITL_2 0x40 /* 4 bytes ITL */
+#define UART_FCR_ITL_3 0x80 /* 8 bytes ITL */
+#define UART_FCR_ITL_4 0xC0 /* 14 bytes ITL */
+
+#define UART_FCR_DMS 0x08 /* DMA Mode Select */
+#define UART_FCR_XFR 0x04 /* XMIT Fifo Reset */
+#define UART_FCR_RFR 0x02 /* RCVR Fifo Reset */
+#define UART_FCR_FE 0x01 /* FIFO Enable */
+
+#define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */
+#define XMIT_FIFO 0
+#define RECV_FIFO 1
+#define MAX_XMIT_RETRY 8
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
+
+struct SerialFifo
+{
+ uint8_t data[UART_FIFO_LENGTH];
+ uint8_t count;
+ uint8_t itl;
+ uint8_t tail;
+ uint8_t head;
+};
+
/**
* Serial device.
*
@@ -126,14 +162,14 @@ struct SerialState
{
/** Access critical section. */
PDMCRITSECT CritSect;
-
/** Pointer to the device instance - R3 Ptr. */
PPDMDEVINSR3 pDevInsR3;
/** Pointer to the device instance - R0 Ptr. */
PPDMDEVINSR0 pDevInsR0;
/** Pointer to the device instance - RC Ptr. */
PPDMDEVINSRC pDevInsRC;
- RTRCPTR Alignment0; /**< Alignment. */
+ /** Alignment. */
+ RTRCPTR Alignment0;
/** LUN\#0: The base interface. */
PDMIBASE IBase;
/** LUN\#0: The character port interface. */
@@ -141,32 +177,49 @@ struct SerialState
/** Pointer to the attached base driver. */
R3PTRTYPE(PPDMIBASE) pDrvBase;
/** Pointer to the attached character driver. */
- R3PTRTYPE(PPDMICHARCONNECTOR) pDrvChar;
+ R3PTRTYPE(PPDMICHARCONNECTOR) pDrvChar;
+ RTSEMEVENT ReceiveSem;
+ PTMTIMERR3 fifo_timeout_timer;
+ PTMTIMERR3 transmit_timerR3;
+ PTMTIMERR0 transmit_timerR0; /* currently not used */
+ PTMTIMERRC transmit_timerRC; /* currently not used */
+ RTRCPTR Alignment1;
+ SerialFifo recv_fifo;
+ SerialFifo xmit_fifo;
+
+ uint32_t base;
uint16_t divider;
- uint16_t auAlignment[3];
- uint8_t rbr; /* receive register */
- uint8_t ier;
- uint8_t iir; /* read only */
- uint8_t lcr;
- uint8_t mcr;
- uint8_t lsr; /* read only */
- uint8_t msr; /* read only */
- uint8_t scr;
+ uint16_t Alignment2[1];
+ uint8_t rbr; /**< receive register */
+ uint8_t thr; /**< transmit holding register */
+ uint8_t tsr; /**< transmit shift register */
+ uint8_t ier; /**< interrupt enable register */
+ uint8_t iir; /**< interrupt itentification register, R/O */
+ uint8_t lcr; /**< line control register */
+ uint8_t mcr; /**< modem control register */
+ uint8_t lsr; /**< line status register, R/O */
+ uint8_t msr; /**< modem status register, R/O */
+ uint8_t scr; /**< scratch register */
+ uint8_t fcr; /**< fifo control register */
+ uint8_t fcr_vmstate;
/* NOTE: this hidden state is necessary for tx irq generation as
it can be reset while reading iir */
int thr_ipending;
+ int timeout_ipending;
int irq;
+ int last_break_enable;
+ /** Counter for retrying xmit */
+ int tsr_retry;
bool msr_changed;
-
bool fGCEnabled;
bool fR0Enabled;
bool fYieldOnLSRRead;
- bool afAlignment[4];
-
- RTSEMEVENT ReceiveSem;
- int last_break_enable;
- uint32_t base;
+ bool volatile fRecvWaiting;
+ bool f16550AEnabled;
+ bool Alignment3[2];
+ /** Time it takes to transmit a character */
+ uint64_t char_transmit_time;
#ifdef VBOX_SERIAL_PCI
PCIDEVICE dev;
@@ -193,20 +246,82 @@ RT_C_DECLS_END
#ifdef IN_RING3
+static int serial_can_receive(SerialState *s);
+static void serial_receive(void *opaque, const uint8_t *buf, int size);
+
+static void fifo_clear(SerialState *s, int fifo)
+{
+ SerialFifo *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
+ memset(f->data, 0, UART_FIFO_LENGTH);
+ f->count = 0;
+ f->head = 0;
+ f->tail = 0;
+}
+
+static int fifo_put(SerialState *s, int fifo, uint8_t chr)
+{
+ SerialFifo *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
+
+ /* Receive overruns do not overwrite FIFO contents. */
+ if (fifo == XMIT_FIFO || f->count < UART_FIFO_LENGTH)
+ {
+ f->data[f->head++] = chr;
+ if (f->head == UART_FIFO_LENGTH)
+ f->head = 0;
+ }
+
+ if (f->count < UART_FIFO_LENGTH)
+ f->count++;
+ else if (fifo == RECV_FIFO)
+ s->lsr |= UART_LSR_OE;
+
+ return 1;
+}
+
+static uint8_t fifo_get(SerialState *s, int fifo)
+{
+ SerialFifo *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
+ uint8_t c;
+
+ if (f->count == 0)
+ return 0;
+
+ c = f->data[f->tail++];
+ if (f->tail == UART_FIFO_LENGTH)
+ f->tail = 0;
+ f->count--;
+
+ return c;
+}
+
static void serial_update_irq(SerialState *s)
{
- if ((s->lsr & UART_LSR_DR) && (s->ier & UART_IER_RDI)) {
- s->iir = UART_IIR_RDI;
- } else if (s->thr_ipending && (s->ier & UART_IER_THRI)) {
- s->iir = UART_IIR_THRI;
- } else if (s->msr_changed && (s->ier & UART_IER_RLSI)) {
- s->iir = UART_IIR_RLSI;
- } else if (s->lsr & UART_LSR_BI) {
- s->iir = 0; /* No special status bit */
- } else {
- s->iir = UART_IIR_NO_INT;
+ uint8_t tmp_iir = UART_IIR_NO_INT;
+
+ if ( (s->ier & UART_IER_RLSI)
+ && (s->lsr & UART_LSR_INT_ANY)) {
+ tmp_iir = UART_IIR_RLSI;
+ } else if ((s->ier & UART_IER_RDI) && s->timeout_ipending) {
+ /* Note that(s->ier & UART_IER_RDI) can mask this interrupt,
+ * this is not in the specification but is observed on existing
+ * hardware. */
+ tmp_iir = UART_IIR_CTI;
+ } else if ( (s->ier & UART_IER_RDI)
+ && (s->lsr & UART_LSR_DR)
+ && ( !(s->fcr & UART_FCR_FE)
+ || s->recv_fifo.count >= s->recv_fifo.itl)) {
+ tmp_iir = UART_IIR_RDI;
+ } else if ( (s->ier & UART_IER_THRI)
+ && s->thr_ipending) {
+ tmp_iir = UART_IIR_THRI;
+ } else if ( (s->ier & UART_IER_MSI)
+ && (s->msr & UART_MSR_ANY_DELTA)) {
+ tmp_iir = UART_IIR_MSI;
}
- if (s->iir != UART_IIR_NO_INT) {
+ s->iir = tmp_iir | (s->iir & 0xF0);
+
+ /** XXX only call the SetIrq function if the state really changes! */
+ if (tmp_iir != UART_IIR_NO_INT) {
Log(("serial_update_irq %d 1\n", s->irq));
# ifdef VBOX_SERIAL_PCI
PDMDevHlpPCISetIrqNoWait(s->CTX_SUFF(pDevIns), 0, 1);
@@ -225,9 +340,14 @@ static void serial_update_irq(SerialState *s)
static void serial_update_parameters(SerialState *s)
{
- int speed, parity, data_bits, stop_bits;
+ int speed, parity, data_bits, stop_bits, frame_size;
+
+ if (s->divider == 0)
+ return;
+ frame_size = 1;
if (s->lcr & 0x08) {
+ frame_size++;
if (s->lcr & 0x10)
parity = 'E';
else
@@ -239,27 +359,69 @@ static void serial_update_parameters(SerialState *s)
stop_bits = 2;
else
stop_bits = 1;
+
data_bits = (s->lcr & 0x03) + 5;
- if (s->divider == 0)
- return;
+ frame_size += data_bits + stop_bits;
speed = 115200 / s->divider;
+ s->char_transmit_time = (TMTimerGetFreq(CTX_SUFF(s->transmit_timer)) / speed) * frame_size;
Log(("speed=%d parity=%c data=%d stop=%d\n", speed, parity, data_bits, stop_bits));
+
if (RT_LIKELY(s->pDrvChar))
s->pDrvChar->pfnSetParameters(s->pDrvChar, speed, parity, data_bits, stop_bits);
}
+static void serial_xmit(void *opaque)
+{
+ SerialState *s = (SerialState*)opaque;
+ uint64_t new_xmit_ts = TMTimerGet(CTX_SUFF(s->transmit_timer));
+
+ if (s->tsr_retry <= 0) {
+ if (s->fcr & UART_FCR_FE) {
+ s->tsr = fifo_get(s, XMIT_FIFO);
+ if (!s->xmit_fifo.count)
+ s->lsr |= UART_LSR_THRE;
+ } else {
+ s->tsr = s->thr;
+ s->lsr |= UART_LSR_THRE;
+ }
+ }
+
+ if (s->mcr & UART_MCR_LOOP) {
+ /* in loopback mode, say that we just received a char */
+ serial_receive(s, &s->tsr, 1);
+ } else if ( RT_LIKELY(s->pDrvChar)
+ && RT_FAILURE(s->pDrvChar->pfnWrite(s->pDrvChar, &s->tsr, 1))) {
+ if ((s->tsr_retry >= 0) && (s->tsr_retry <= MAX_XMIT_RETRY)) {
+ s->tsr_retry++;
+ TMTimerSet(CTX_SUFF(s->transmit_timer), new_xmit_ts + s->char_transmit_time);
+ return;
+ } else {
+ /* drop this character. */
+ s->tsr_retry = 0;
+ }
+ }
+ else {
+ s->tsr_retry = 0;
+ }
+
+ if (!(s->lsr & UART_LSR_THRE))
+ TMTimerSet(CTX_SUFF(s->transmit_timer),
+ TMTimerGet(CTX_SUFF(s->transmit_timer)) + s->char_transmit_time);
+
+ if (s->lsr & UART_LSR_THRE) {
+ s->lsr |= UART_LSR_TEMT;
+ s->thr_ipending = 1;
+ serial_update_irq(s);
+ }
+}
+
#endif /* IN_RING3 */
-static int serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+static int serial_ioport_write(SerialState *s, uint32_t addr, uint32_t val)
{
- SerialState *s = (SerialState *)opaque;
- unsigned char ch;
-
addr &= 7;
- LogFlow(("serial: write addr=0x%02x val=0x%02x\n", addr, val));
#ifndef IN_RING3
- NOREF(ch);
NOREF(s);
return VINF_IOM_HC_IOPORT_WRITE;
#else
@@ -269,38 +431,21 @@ static int serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
if (s->lcr & UART_LCR_DLAB) {
s->divider = (s->divider & 0xff00) | val;
serial_update_parameters(s);
-#if 0 /* disabled because this causes regressions */
- } else if (s->lsr & UART_LSR_THRE) {
- s->thr_ipending = 0;
- ch = val;
- if (RT_LIKELY(s->pDrvChar))
- {
- Log(("serial_ioport_write: write 0x%X\n", ch));
- int rc = s->pDrvChar->pfnWrite(s->pDrvChar, &ch, 1);
- AssertRC(rc);
- }
- s->thr_ipending = 1;
- serial_update_irq(s);
- } else
- Log(("serial: THR not EMPTY!\n"));
-#else
} else {
- s->thr_ipending = 0;
- s->lsr &= ~UART_LSR_THRE;
- serial_update_irq(s);
- ch = val;
- if (RT_LIKELY(s->pDrvChar))
- {
- Log(("serial_ioport_write: write 0x%X\n", ch));
- int rc = s->pDrvChar->pfnWrite(s->pDrvChar, &ch, 1);
- AssertRC(rc);
+ s->thr = (uint8_t) val;
+ if (s->fcr & UART_FCR_FE) {
+ fifo_put(s, XMIT_FIFO, s->thr);
+ s->thr_ipending = 0;
+ s->lsr &= ~UART_LSR_TEMT;
+ s->lsr &= ~UART_LSR_THRE;
+ serial_update_irq(s);
+ } else {
+ s->thr_ipending = 0;
+ s->lsr &= ~UART_LSR_THRE;
+ serial_update_irq(s);
}
- s->thr_ipending = 1;
- s->lsr |= UART_LSR_THRE;
- s->lsr |= UART_LSR_TEMT;
- serial_update_irq(s);
+ serial_xmit(s);
}
-#endif
break;
case 1:
if (s->lcr & UART_LCR_DLAB) {
@@ -310,20 +455,62 @@ static int serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
s->ier = val & 0x0f;
if (s->lsr & UART_LSR_THRE) {
s->thr_ipending = 1;
+ serial_update_irq(s);
}
- serial_update_irq(s);
}
break;
case 2:
+ if (!s->f16550AEnabled)
+ break;
+
+ val = val & 0xFF;
+
+ if (s->fcr == val)
+ break;
+
+ /* Did the enable/disable flag change? If so, make sure FIFOs get flushed */
+ if ((val ^ s->fcr) & UART_FCR_FE)
+ val |= UART_FCR_XFR | UART_FCR_RFR;
+
+ /* FIFO clear */
+ if (val & UART_FCR_RFR) {
+ TMTimerStop(s->fifo_timeout_timer);
+ s->timeout_ipending = 0;
+ fifo_clear(s, RECV_FIFO);
+ }
+ if (val & UART_FCR_XFR) {
+ fifo_clear(s, XMIT_FIFO);
+ }
+
+ if (val & UART_FCR_FE) {
+ s->iir |= UART_IIR_FE;
+ /* Set RECV_FIFO trigger Level */
+ switch (val & 0xC0) {
+ case UART_FCR_ITL_1:
+ s->recv_fifo.itl = 1;
+ break;
+ case UART_FCR_ITL_2:
+ s->recv_fifo.itl = 4;
+ break;
+ case UART_FCR_ITL_3:
+ s->recv_fifo.itl = 8;
+ break;
+ case UART_FCR_ITL_4:
+ s->recv_fifo.itl = 14;
+ break;
+ }
+ } else
+ s->iir &= ~UART_IIR_FE;
+
+ /* Set fcr - or at least the bits in it that are supposed to "stick" */
+ s->fcr = val & 0xC9;
+ serial_update_irq(s);
break;
case 3:
{
int break_enable;
- if (s->lcr != val)
- {
- s->lcr = val;
- serial_update_parameters(s);
- }
+ s->lcr = val;
+ serial_update_parameters(s);
break_enable = (val >> 6) & 1;
if (break_enable != s->last_break_enable) {
s->last_break_enable = break_enable;
@@ -340,7 +527,9 @@ static int serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
s->mcr = val & 0x1f;
if (RT_LIKELY(s->pDrvChar))
{
- int rc = s->pDrvChar->pfnSetModemLines(s->pDrvChar, !!(s->mcr & UART_MCR_RTS), !!(s->mcr & UART_MCR_DTR));
+ int rc = s->pDrvChar->pfnSetModemLines(s->pDrvChar,
+ !!(s->mcr & UART_MCR_RTS),
+ !!(s->mcr & UART_MCR_DTR));
AssertRC(rc);
}
break;
@@ -368,16 +557,29 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr, int *pRC)
default:
case 0:
if (s->lcr & UART_LCR_DLAB) {
+ /* DLAB == 1: divisor latch (LS) */
ret = s->divider & 0xff;
} else {
#ifndef IN_RING3
*pRC = VINF_IOM_HC_IOPORT_READ;
#else
- Log(("serial_io_port_read: read 0x%X\n", s->rbr));
- ret = s->rbr;
- s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
+ if (s->fcr & UART_FCR_FE) {
+ ret = fifo_get(s, RECV_FIFO);
+ if (s->recv_fifo.count == 0)
+ s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
+ else
+ TMTimerSet(s->fifo_timeout_timer,
+ TMTimerGet(s->fifo_timeout_timer) + s->char_transmit_time * 4);
+ s->timeout_ipending = 0;
+ } else {
+ Log(("serial_io_port_read: read 0x%X\n", s->rbr));
+ ret = s->rbr;
+ s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
+ }
serial_update_irq(s);
+ if (s->fRecvWaiting)
{
+ s->fRecvWaiting = false;
int rc = RTSemEventSignal(s->ReceiveSem);
AssertRC(rc);
}
@@ -386,6 +588,7 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr, int *pRC)
break;
case 1:
if (s->lcr & UART_LCR_DLAB) {
+ /* DLAB == 1: divisor latch (MS) */
ret = (s->divider >> 8) & 0xff;
} else {
ret = s->ier;
@@ -396,12 +599,12 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr, int *pRC)
*pRC = VINF_IOM_HC_IOPORT_READ;
#else
ret = s->iir;
- /* reset THR pending bit */
- if ((ret & 0x7) == UART_IIR_THRI)
+ if ((ret & UART_IIR_ID) == UART_IIR_THRI) {
s->thr_ipending = 0;
+ serial_update_irq(s);
+ }
/* reset msr changed bit */
s->msr_changed = false;
- serial_update_irq(s);
#endif
break;
case 3:
@@ -422,6 +625,15 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr, int *pRC)
#endif
}
ret = s->lsr;
+ /* Clear break and overrun interrupts */
+ if (s->lsr & (UART_LSR_BI|UART_LSR_OE)) {
+#ifndef IN_RING3
+ *pRC = VINF_IOM_HC_IOPORT_READ;
+#else
+ s->lsr &= ~(UART_LSR_BI|UART_LSR_OE);
+ serial_update_irq(s);
+#endif
+ }
break;
case 6:
if (s->mcr & UART_MCR_LOOP) {
@@ -432,55 +644,86 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr, int *pRC)
ret |= (s->mcr & 0x01) << 5;
} else {
ret = s->msr;
- /* Reset delta bits. */
- s->msr &= ~UART_MSR_ANY_DELTA;
+ /* Clear delta bits & msr int after read, if they were set */
+ if (s->msr & UART_MSR_ANY_DELTA) {
+#ifndef IN_RING3
+ *pRC = VINF_IOM_HC_IOPORT_READ;
+#else
+ s->msr &= 0xF0;
+ serial_update_irq(s);
+#endif
+ }
}
break;
case 7:
ret = s->scr;
break;
}
- LogFlow(("serial: read addr=0x%02x val=0x%02x\n", addr, ret));
return ret;
}
#ifdef IN_RING3
+static int serial_can_receive(SerialState *s)
+{
+ if (s->fcr & UART_FCR_FE) {
+ if (s->recv_fifo.count < UART_FIFO_LENGTH)
+ return (s->recv_fifo.count <= s->recv_fifo.itl)
+ ? s->recv_fifo.itl - s->recv_fifo.count : 1;
+ else
+ return 0;
+ } else {
+ return !(s->lsr & UART_LSR_DR);
+ }
+}
+
+static void serial_receive(void *opaque, const uint8_t *buf, int size)
+{
+ SerialState *s = (SerialState*)opaque;
+ if (s->fcr & UART_FCR_FE) {
+ int i;
+ for (i = 0; i < size; i++) {
+ fifo_put(s, RECV_FIFO, buf[i]);
+ }
+ s->lsr |= UART_LSR_DR;
+ /* call the timeout receive callback in 4 char transmit time */
+ TMTimerSet(s->fifo_timeout_timer, TMTimerGet(s->fifo_timeout_timer) + s->char_transmit_time * 4);
+ } else {
+ if (s->lsr & UART_LSR_DR)
+ s->lsr |= UART_LSR_OE;
+ s->rbr = buf[0];
+ s->lsr |= UART_LSR_DR;
+ }
+ serial_update_irq(s);
+}
+
+/** @copydoc PDMICHARPORT::pfnNotifyRead */
static DECLCALLBACK(int) serialNotifyRead(PPDMICHARPORT pInterface, const void *pvBuf, size_t *pcbRead)
{
SerialState *pThis = PDMICHARPORT_2_SERIALSTATE(pInterface);
- int rc;
-
- Assert(*pcbRead != 0);
+ const uint8_t *pu8Buf = (const uint8_t*)pvBuf;
+ size_t cbRead = *pcbRead;
PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
- if (pThis->lsr & UART_LSR_DR)
+ for (; cbRead > 0; cbRead--, pu8Buf++)
{
- /* If a character is still in the read queue, then wait for it to be emptied. */
- PDMCritSectLeave(&pThis->CritSect);
- rc = RTSemEventWait(pThis->ReceiveSem, 250);
- if (RT_FAILURE(rc))
- return rc;
-
- PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
- }
-
- if (!(pThis->lsr & UART_LSR_DR))
- {
- pThis->rbr = *(const char *)pvBuf;
- pThis->lsr |= UART_LSR_DR;
- serial_update_irq(pThis);
- *pcbRead = 1;
- rc = VINF_SUCCESS;
+ if (!serial_can_receive(pThis))
+ {
+ /* If we cannot receive then wait for not more than 250ms. If we still
+ * cannot receive then the new character will either overwrite rbr
+ * or it will be dropped at fifo_put(). */
+ pThis->fRecvWaiting = true;
+ PDMCritSectLeave(&pThis->CritSect);
+ int rc = RTSemEventWait(pThis->ReceiveSem, 250);
+ PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
+ }
+ serial_receive(pThis, &pu8Buf[0], 1);
}
- else
- rc = VERR_TIMEOUT;
-
PDMCritSectLeave(&pThis->CritSect);
-
- return rc;
+ return VINF_SUCCESS;
}
+/** @copydoc PDMICHARPORT::pfnNotifyStatusLinesChanged */
static DECLCALLBACK(int) serialNotifyStatusLinesChanged(PPDMICHARPORT pInterface, uint32_t newStatusLines)
{
SerialState *pThis = PDMICHARPORT_2_SERIALSTATE(pInterface);
@@ -519,27 +762,13 @@ static DECLCALLBACK(int) serialNotifyStatusLinesChanged(PPDMICHARPORT pInterface
return VINF_SUCCESS;
}
+/** @copydoc PDMICHARPORT::pfnNotifyBufferFull */
static DECLCALLBACK(int) serialNotifyBufferFull(PPDMICHARPORT pInterface, bool fFull)
{
-#if 0
- SerialState *pThis = PDMICHARPORT_2_SERIALSTATE(pInterface);
- PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED);
- if (fFull)
- {
- pThis->lsr &= ~UART_LSR_THRE;
- }
- else
- {
- pThis->thr_ipending = 1;
- pThis->lsr |= UART_LSR_THRE;
- pThis->lsr |= UART_LSR_TEMT;
- }
- serial_update_irq(pThis);
- PDMCritSectLeave(&pThis->CritSect);
-#endif
return VINF_SUCCESS;
}
+/** @copydoc PDMICHARPORT::pfnNotifyBreak */
static DECLCALLBACK(int) serialNotifyBreak(PPDMICHARPORT pInterface)
{
SerialState *pThis = PDMICHARPORT_2_SERIALSTATE(pInterface);
@@ -556,6 +785,67 @@ static DECLCALLBACK(int) serialNotifyBreak(PPDMICHARPORT pInterface)
return VINF_SUCCESS;
}
+/**
+ * Fifo timer functions.
+ */
+static DECLCALLBACK(void) serialFifoTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+ SerialState *s = (SerialState*)pvUser;
+ if (s->recv_fifo.count)
+ {
+ s->timeout_ipending = 1;
+ serial_update_irq(s);
+ }
+}
+
+/**
+ * Transmit timer function.
+ * Just retry to transmit a character.
+ *
+ * @param pTimer The timer handle.
+ * @param pDevIns The device instance.
+ * @param pvUser The user pointer.
+ */
+static DECLCALLBACK(void) serialTransmitTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+ SerialState *s = (SerialState*)pvUser;
+ serial_xmit(s);
+}
+
+/**
+ * Reset the serial device.
+ *
+ * @param pDevIns The device instance.
+ */
+static DECLCALLBACK(void) serialReset(PPDMDEVINS pDevIns)
+{
+ SerialState *s = PDMINS_2_DATA(pDevIns, SerialState *);
+
+ s->rbr = 0;
+ s->ier = 0;
+ s->iir = UART_IIR_NO_INT;
+ s->lcr = 0;
+ s->lsr = UART_LSR_TEMT | UART_LSR_THRE;
+ s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
+ /* Default to 9600 baud, 1 start bit, 8 data bits, 1 stop bit, no parity. */
+ s->divider = 0x0C;
+ s->mcr = UART_MCR_OUT2;
+ s->scr = 0;
+ s->tsr_retry = 0;
+ s->char_transmit_time = (TMTimerGetFreq(CTX_SUFF(s->transmit_timer)) / 9600) * 10;
+
+ fifo_clear(s, RECV_FIFO);
+ fifo_clear(s, XMIT_FIFO);
+
+ s->thr_ipending = 0;
+ s->last_break_enable = 0;
+# ifdef VBOX_SERIAL_PCI
+ PDMDevHlpPCISetIrqNoWait(s->CTX_SUFF(pDevIns), 0, 0);
+# else /* !VBOX_SERIAL_PCI */
+ PDMDevHlpISASetIrqNoWait(s->CTX_SUFF(pDevIns), s->irq, 0);
+# endif /* !VBOX_SERIAL_PCI */
+}
+
#endif /* IN_RING3 */
/**
@@ -655,11 +945,18 @@ static DECLCALLBACK(int) serialSaveExec(PPDMDEVINS pDevIns,
SSMR3PutU8(pSSM, pThis->lsr);
SSMR3PutU8(pSSM, pThis->msr);
SSMR3PutU8(pSSM, pThis->scr);
+ SSMR3PutU8(pSSM, pThis->fcr); /* 16550A */
SSMR3PutS32(pSSM, pThis->thr_ipending);
SSMR3PutS32(pSSM, pThis->irq);
SSMR3PutS32(pSSM, pThis->last_break_enable);
SSMR3PutU32(pSSM, pThis->base);
SSMR3PutBool(pSSM, pThis->msr_changed);
+
+ /* Don't store:
+ * - the content of the FIFO
+ * - tsr_retry
+ */
+
return SSMR3PutU32(pSSM, ~0); /* sanity/terminator */
}
@@ -673,7 +970,13 @@ static DECLCALLBACK(int) serialLoadExec(PPDMDEVINS pDevIns,
{
SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
- AssertMsgReturn(uVersion == SERIAL_SAVED_STATE_VERSION, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
+ if (uVersion == SERIAL_SAVED_STATE_VERSION_16450)
+ {
+ pThis->f16550AEnabled = false;
+ LogRel(("Serial#%d: falling back to 16450 mode from load state\n", pDevIns->iInstance));
+ }
+ else
+ AssertMsgReturn(uVersion == SERIAL_SAVED_STATE_VERSION, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
if (uPass == SSM_PASS_FINAL)
{
@@ -685,6 +988,10 @@ static DECLCALLBACK(int) serialLoadExec(PPDMDEVINS pDevIns,
SSMR3GetU8(pSSM, &pThis->lsr);
SSMR3GetU8(pSSM, &pThis->msr);
SSMR3GetU8(pSSM, &pThis->scr);
+ if (uVersion > SERIAL_SAVED_STATE_VERSION_16450)
+ {
+ SSMR3GetU8(pSSM, &pThis->fcr);
+ }
SSMR3GetS32(pSSM, &pThis->thr_ipending);
}
@@ -700,7 +1007,8 @@ static DECLCALLBACK(int) serialLoadExec(PPDMDEVINS pDevIns,
if ( pThis->irq != iIrq
|| pThis->base != IOBase)
- return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - saved irq=%#x iobase=%#x; configured irq=%#x iobase=%#x"),
+ return SSMR3SetCfgError(pSSM, RT_SRC_POS,
+ N_("Config mismatch - saved irq=%#x iobase=%#x; configured irq=%#x iobase=%#x"),
iIrq, IOBase, pThis->irq, pThis->base);
if (uPass == SSM_PASS_FINAL)
@@ -713,8 +1021,10 @@ static DECLCALLBACK(int) serialLoadExec(PPDMDEVINS pDevIns,
return rc;
AssertMsgReturn(u32 == ~0U, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
- if (pThis->lsr & UART_LSR_DR)
+ if ( (pThis->lsr & UART_LSR_DR)
+ || pThis->fRecvWaiting)
{
+ pThis->fRecvWaiting = false;
rc = RTSemEventSignal(pThis->ReceiveSem);
AssertRC(rc);
}
@@ -734,8 +1044,9 @@ static DECLCALLBACK(int) serialLoadExec(PPDMDEVINS pDevIns,
*/
static DECLCALLBACK(void) serialRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
{
- SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
- pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState *);
+ pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pThis->transmit_timerRC = TMTimerRCPtr(pThis->transmit_timerR3);
}
#ifdef VBOX_SERIAL_PCI
@@ -765,7 +1076,7 @@ static DECLCALLBACK(int) serialIOPortRegionMap(PPCIDEVICE pPciDev, /* unsigned *
#endif /* VBOX_SERIAL_PCI */
/**
- * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ * @interface_method_impl{PDMIBASE, pfnQueryInterface}
*/
static DECLCALLBACK(void *) serialQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
@@ -798,11 +1109,9 @@ static DECLCALLBACK(int) serialDestruct(PPDMDEVINS pDevIns)
/**
- * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ * @interface_method_impl{PDMDEVREG, pfnConstruct}
*/
-static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns,
- int iInstance,
- PCFGMNODE pCfg)
+static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
{
int rc;
SerialState *pThis = PDMINS_2_DATA(pDevIns, SerialState*);
@@ -820,10 +1129,6 @@ static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns,
pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
- pThis->lsr = UART_LSR_TEMT | UART_LSR_THRE;
- pThis->iir = UART_IIR_NO_INT;
- pThis->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
-
/* IBase */
pThis->IBase.pfnQueryInterface = serialQueryInterface;
@@ -851,7 +1156,13 @@ static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns,
/*
* Validate and read the configuration.
*/
- if (!CFGMR3AreValuesValid(pCfg, "IRQ\0" "IOBase\0" "GCEnabled\0" "R0Enabled\0" "YieldOnLSRRead\0"))
+ if (!CFGMR3AreValuesValid(pCfg, "IRQ\0"
+ "IOBase\0"
+ "GCEnabled\0"
+ "R0Enabled\0"
+ "YieldOnLSRRead\0"
+ "Enable16550A\0"
+ ))
{
AssertMsgFailed(("serialConstruct Invalid configuration values\n"));
return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
@@ -903,6 +1214,11 @@ static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns,
Log(("DevSerial: instance %d iobase=%04x irq=%d\n", iInstance, io_base, irq_lvl));
+ rc = CFGMR3QueryBoolDef(pCfg, "Enable16550A", &pThis->f16550AEnabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"Enable16550A\" value"));
+
pThis->irq = irq_lvl;
#ifdef VBOX_SERIAL_PCI
pThis->base = -1;
@@ -910,6 +1226,8 @@ static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns,
pThis->base = io_base;
#endif
+ LogRel(("Serial#%d: emulating %s\n", pDevIns->iInstance, pThis->f16550AEnabled ? "16550A" : "16450"));
+
/*
* Initialize critical section and the semaphore.
* This must of course be done before attaching drivers or anything else which can call us back..
@@ -921,6 +1239,20 @@ static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns,
rc = RTSemEventCreate(&pThis->ReceiveSem);
AssertRC(rc);
+ rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, serialFifoTimer, pThis,
+ TMTIMER_FLAGS_NO_CRIT_SECT, "Serial Fifo Timer",
+ &pThis->fifo_timeout_timer);
+ AssertRC(rc);
+
+ rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, serialTransmitTimer, pThis,
+ TMTIMER_FLAGS_NO_CRIT_SECT, "Serial Transmit Timer",
+ &pThis->transmit_timerR3);
+ AssertRC(rc);
+ pThis->transmit_timerR0 = TMTimerR0Ptr(pThis->transmit_timerR3);
+ pThis->transmit_timerRC = TMTimerRCPtr(pThis->transmit_timerR3);
+
+ serialReset(pDevIns);
+
#ifdef VBOX_SERIAL_PCI
/*
* Register the PCI Device and region.
@@ -951,7 +1283,6 @@ static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns,
return rc;
}
-
if (pThis->fR0Enabled)
{
rc = PDMDevHlpIOPortRegisterR0(pDevIns, io_base, 8, 0, "serialIOPortWrite",
@@ -1035,7 +1366,7 @@ const PDMDEVREG g_DeviceSerialPort =
/* pfnPowerOn */
NULL,
/* pfnReset */
- NULL,
+ serialReset,
/* pfnSuspend */
NULL,
/* pfnResume */
@@ -1057,5 +1388,4 @@ const PDMDEVREG g_DeviceSerialPort =
};
#endif /* IN_RING3 */
-
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */