From 8825ec58285957b8586e9439a84bbd5eca773fbb Mon Sep 17 00:00:00 2001
From: Anthony Liguori <aliguori@us.ibm.com>
Date: Wed, 29 May 2013 13:44:35 -0500
Subject: [PATCH] Demonstrate separating register decode from get/set
---
hw/char/serial.c | 253 ++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 167 insertions(+), 86 deletions(-)
@@ -299,6 +299,113 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
return FALSE;
}
+enum {
+ UART_INVALID = 0,
+ UART_DIVIDER,
+ UART_RBR,
+ UART_THR,
+ UART_IER,
+ UART_IIR,
+ UART_FCR,
+ UART_LCR,
+ UART_MCR,
+ UART_LSR,
+ UART_MSR,
+ UART_SCR,
+};
+
+static int serial_decode(void *opaque, hwaddr addr, unsigned size, bool is_write)
+{
+ SerialState *s = opaque;
+
+ switch (addr) {
+ case 0:
+ if (s->lcr & UART_LCR_DLAB) {
+ return UART_DIVIDER;
+ }
+ if (is_write) {
+ return UART_THR;
+ }
+ return UART_RBR;
+ case 1:
+ if (s->lcr & UART_LCR_DLAB) {
+ return UART_DIVIDER;
+ }
+ return UART_IER;
+ case 2:
+ if (is_write) {
+ return UART_FCR;
+ }
+ return UART_IIR;
+ case 3:
+ return UART_LCR;
+ case 4:
+ return UART_MCR;
+ case 5:
+ if (is_write) {
+ return UART_INVALID;
+ }
+ return UART_LSR;
+ case 6:
+ if (is_write) {
+ return UART_INVALID;
+ }
+ return UART_MSR;
+ case 7:
+ return UART_SCR;
+ default:
+ return UART_INVALID;
+ }
+}
+
+static int serial_load(void *opaque, int regno, unsigned size, uint64_t *ret)
+{
+ SerialState *s = opaque;
+
+ switch (regno) {
+ case UART_IER:
+ *ret = s->ier;
+ break;
+ case UART_IIR:
+ *ret = s->iir;
+ break;
+ case UART_LCR:
+ *ret = s->lcr;
+ break;
+ case UART_MCR:
+ *ret = s->mcr;
+ break;
+ case UART_LSR:
+ *ret = s->lsr;
+ break;
+ case UART_SCR:
+ *ret = s->scr;
+ break;
+ default:
+ break;
+ }
+
+ return regno;
+}
+
+static int serial_store(void *opaque, int regno, unsigned size, uint64_t val)
+{
+ SerialState *s = opaque;
+
+ switch (regno) {
+ case UART_THR:
+ s->thr = val;
+ break;
+ case UART_IER:
+ s->ier = val & 0x0f;
+ break;
+ case UART_LCR:
+ s->lcr = val;
+ break;
+ }
+
+ return regno;
+}
static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
@@ -307,52 +414,48 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
addr &= 7;
DPRINTF("write addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 "\n", addr, val);
- switch(addr) {
+ switch(serial_store(s, serial_decode(s, addr, size, true), size, val)) {
default:
- case 0:
- if (s->lcr & UART_LCR_DLAB) {
+ case UART_DIVIDER:
+ if (addr == 0) {
s->divider = (s->divider & 0xff00) | val;
- serial_update_parameters(s);
} else {
- 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);
- }
- serial_xmit(NULL, G_IO_OUT, s);
+ s->divider = (s->divider & 0x00ff) | (val << 8);
}
+ serial_update_parameters(s);
break;
- case 1:
- if (s->lcr & UART_LCR_DLAB) {
- s->divider = (s->divider & 0x00ff) | (val << 8);
- serial_update_parameters(s);
+ case UART_THR:
+ 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->ier = val & 0x0f;
- /* If the backend device is a real serial port, turn polling of the modem
- status lines on physical port on or off depending on UART_IER_MSI state */
- if (s->poll_msl >= 0) {
- if (s->ier & UART_IER_MSI) {
- s->poll_msl = 1;
- serial_update_msl(s);
- } else {
- qemu_del_timer(s->modem_status_poll);
- s->poll_msl = 0;
- }
- }
- if (s->lsr & UART_LSR_THRE) {
- s->thr_ipending = 1;
- serial_update_irq(s);
+ s->thr_ipending = 0;
+ s->lsr &= ~UART_LSR_THRE;
+ serial_update_irq(s);
+ }
+ serial_xmit(NULL, G_IO_OUT, s);
+ break;
+ case UART_IER:
+ /* If the backend device is a real serial port, turn polling of the modem
+ status lines on physical port on or off depending on UART_IER_MSI state */
+ if (s->poll_msl >= 0) {
+ if (s->ier & UART_IER_MSI) {
+ s->poll_msl = 1;
+ serial_update_msl(s);
+ } else {
+ qemu_del_timer(s->modem_status_poll);
+ s->poll_msl = 0;
}
}
+ if (s->lsr & UART_LSR_THRE) {
+ s->thr_ipending = 1;
+ serial_update_irq(s);
+ }
break;
- case 2:
+ case UART_FCR:
val = val & 0xFF;
if (s->fcr == val)
@@ -398,10 +501,9 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
s->fcr = val & 0xC9;
serial_update_irq(s);
break;
- case 3:
+ case UART_LCR:
{
int break_enable;
- s->lcr = val;
serial_update_parameters(s);
break_enable = (val >> 6) & 1;
if (break_enable != s->last_break_enable) {
@@ -411,7 +513,7 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
}
}
break;
- case 4:
+ case UART_MCR:
{
int flags;
int old_mcr = s->mcr;
@@ -437,75 +539,57 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
}
}
break;
- case 5:
- break;
- case 6:
- break;
- case 7:
- s->scr = val;
- break;
}
}
static uint64_t serial_ioport_read(void *opaque, hwaddr addr, unsigned size)
{
SerialState *s = opaque;
- uint32_t ret;
+ uint64_t ret = 0xFF;
addr &= 7;
- switch(addr) {
+ switch(serial_load(s, serial_decode(s, addr, size, false), size, &ret)) {
default:
- case 0:
- if (s->lcr & UART_LCR_DLAB) {
+ break;
+ case UART_DIVIDER:
+ if (addr == 0) {
ret = s->divider & 0xff;
} else {
- 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
- qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock_ns (vm_clock) + s->char_transmit_time * 4);
- s->timeout_ipending = 0;
- } else {
- ret = s->rbr;
- s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
- }
- serial_update_irq(s);
- if (!(s->mcr & UART_MCR_LOOP)) {
- /* in loopback mode, don't receive any data */
- qemu_chr_accept_input(s->chr);
- }
+ ret = (s->divider >> 8) & 0xff;
}
break;
- case 1:
- if (s->lcr & UART_LCR_DLAB) {
- ret = (s->divider >> 8) & 0xff;
+ case UART_RBR:
+ 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
+ qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock_ns (vm_clock) + s->char_transmit_time * 4);
+ s->timeout_ipending = 0;
} else {
- ret = s->ier;
+ ret = s->rbr;
+ s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
+ }
+ serial_update_irq(s);
+ if (!(s->mcr & UART_MCR_LOOP)) {
+ /* in loopback mode, don't receive any data */
+ qemu_chr_accept_input(s->chr);
}
break;
- case 2:
- ret = s->iir;
- if ((ret & UART_IIR_ID) == UART_IIR_THRI) {
+ case UART_IIR:
+ if ((s->iir & UART_IIR_ID) == UART_IIR_THRI) {
s->thr_ipending = 0;
serial_update_irq(s);
}
break;
- case 3:
- ret = s->lcr;
- break;
- case 4:
- ret = s->mcr;
- break;
- case 5:
- ret = s->lsr;
+ case UART_LSR:
/* Clear break and overrun interrupts */
if (s->lsr & (UART_LSR_BI|UART_LSR_OE)) {
s->lsr &= ~(UART_LSR_BI|UART_LSR_OE);
serial_update_irq(s);
}
break;
- case 6:
+ case UART_MSR:
if (s->mcr & UART_MCR_LOOP) {
/* in loopback, the modem output pins are connected to the
inputs */
@@ -523,9 +607,6 @@ static uint64_t serial_ioport_read(void *opaque, hwaddr addr, unsigned size)
}
}
break;
- case 7:
- ret = s->scr;
- break;
}
DPRINTF("read addr=0x%" HWADDR_PRIx " val=0x%02x\n", addr, ret);
return ret;
--
1.8.0