commit cce15a1ed6aac51f8b7e05eba5c93466c3d96df7 Author: Anthony Liguori Date: Wed May 29 13:44:35 2013 -0500 Demonstrate separating register decode from get/set diff --git a/hw/char/serial.c b/hw/char/serial.c index 66b6348..34c8a2e 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -299,6 +299,89 @@ 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, +}; + +#define REG_READ 1 +#define REG_WRITE 2 +#define REG_RW (REG_READ | REG_WRITE) + +typedef struct RegisterDecodeEntry +{ + uint64_t addr; + unsigned access_size; + int regno; + int flags; +} RegisterDecodeEntry; + +static RegisterDecodeEntry decoder[] = { + /* addresses 0-1 must be open decoded due to latching */ + { .addr = 2, .regno = UART_FCR, .flags = REG_WRITE }, + { .addr = 2, .regno = UART_IIR, .flags = REG_READ }, + { .addr = 3, .regno = UART_LCR, .flags = REG_RW }, + { .addr = 4, .regno = UART_MCR, .flags = REG_RW }, + { .addr = 5, .regno = UART_LSR, .flags = REG_READ }, + { .addr = 6, .regno = UART_MSR, .flags = REG_READ }, + { .addr = 7, .regno = UART_SCR, .flags = REG_RW }, +}; + +typedef struct RegisterMapEntry +{ + size_t offset; + int regno; + RegisterType type; + uint64_t mask; + int rshift; + int flags; +} RegisterMapEntry; + +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; + default: + return register_decode(decoder, + ARRAY_SIZE(decoder), + addr, size, is_write); + } +} + +static RegisterMapEntry regmap[] = { + DEFINE_REG_U8(SerialState, ier, UART_IER), + DEFINE_REG_U8(SerialState, iir, UART_IIR), + DEFINE_REG_U8(SerialState, lcr, UART_LCR), + DEFINE_REG_U8(SerialState, mcr, UART_MCR), + DEFINE_REG_U8(SerialState, lsr, UART_LSR), + DEFINE_REG_U8(SerialState, scr, UART_SCR), + DEFINE_REG_U8(SerialState, thr, UART_THR), +}; static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) @@ -307,52 +390,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(register_store(regmap, 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 +477,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 +489,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 +515,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(register_load(regmap, 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 +583,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;