diff options
-rw-r--r-- | hw/slavio_serial.c | 110 |
1 files changed, 88 insertions, 22 deletions
diff --git a/hw/slavio_serial.c b/hw/slavio_serial.c index 7112bec840..404138d4b0 100644 --- a/hw/slavio_serial.c +++ b/hw/slavio_serial.c @@ -74,6 +74,8 @@ typedef enum { chn_a, chn_b, } chn_id_t; +#define CHN_C(s) ((s)->chn == chn_b? 'b' : 'a') + typedef enum { ser, kbd, mouse, } chn_type_t; @@ -113,7 +115,7 @@ static void put_queue(void *opaque, int b) ChannelState *s = opaque; SERIOQueue *q = &s->queue; - SER_DPRINTF("put: 0x%02x\n", b); + SER_DPRINTF("channel %c put: 0x%02x\n", CHN_C(s), b); if (q->count >= SERIO_QUEUE_SIZE) return; q->data[q->wptr] = b; @@ -137,7 +139,7 @@ static uint32_t get_queue(void *opaque) q->rptr = 0; q->count--; } - KBD_DPRINTF("get 0x%02x\n", val); + KBD_DPRINTF("channel %c get 0x%02x\n", CHN_C(s), val); if (q->count > 0) serial_receive_byte(s, 0); return val; @@ -198,11 +200,10 @@ static inline void clr_rxint(ChannelState *s) { s->rxint = 0; s->rxint_under_svc = 0; - if (s->chn == 0) + if (s->chn == chn_a) s->rregs[3] &= ~0x20; - else { + else s->otherchn->rregs[3] &= ~4; - } if (s->txint) set_txint(s); else @@ -215,11 +216,10 @@ static inline void set_rxint(ChannelState *s) s->rxint = 1; if (!s->txint_under_svc) { s->rxint_under_svc = 1; - if (s->chn == 0) + if (s->chn == chn_a) s->rregs[3] |= 0x20; - else { + else s->otherchn->rregs[3] |= 4; - } s->rregs[2] = 4; slavio_serial_update_irq(s); } @@ -229,11 +229,10 @@ static inline void clr_txint(ChannelState *s) { s->txint = 0; s->txint_under_svc = 0; - if (s->chn == 0) + if (s->chn == chn_a) s->rregs[3] &= ~0x10; - else { + else s->otherchn->rregs[3] &= ~2; - } if (s->rxint) set_rxint(s); else @@ -246,16 +245,74 @@ static inline void set_txint(ChannelState *s) s->txint = 1; if (!s->rxint_under_svc) { s->txint_under_svc = 1; - if (s->chn == 0) + if (s->chn == chn_a) s->rregs[3] |= 0x10; - else { + else s->otherchn->rregs[3] |= 2; - } s->rregs[2] = 0; slavio_serial_update_irq(s); } } +static void slavio_serial_update_parameters(ChannelState *s) +{ + int speed, parity, data_bits, stop_bits; + QEMUSerialSetParams ssp; + + if (!s->chr || s->type != ser) + return; + + if (s->wregs[4] & 1) { + if (s->wregs[4] & 2) + parity = 'E'; + else + parity = 'O'; + } else { + parity = 'N'; + } + if ((s->wregs[4] & 0x0c) == 0x0c) + stop_bits = 2; + else + stop_bits = 1; + switch (s->wregs[5] & 0x60) { + case 0x00: + data_bits = 5; + break; + case 0x20: + data_bits = 7; + break; + case 0x40: + data_bits = 6; + break; + default: + case 0x60: + data_bits = 8; + break; + } + speed = 2457600 / ((s->wregs[12] | (s->wregs[13] << 8)) + 2); + switch (s->wregs[4] & 0xc0) { + case 0x00: + break; + case 0x40: + speed /= 16; + break; + case 0x80: + speed /= 32; + break; + default: + case 0xc0: + speed /= 64; + break; + } + ssp.speed = speed; + ssp.parity = parity; + ssp.data_bits = data_bits; + ssp.stop_bits = stop_bits; + SER_DPRINTF("channel %c: speed=%d parity=%c data=%d stop=%d\n", CHN_C(s), + speed, parity, data_bits, stop_bits); + qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); +} + static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) { SerialState *ser = opaque; @@ -269,7 +326,7 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr, uint s = &ser->chn[channel]; switch (saddr) { case 0: - SER_DPRINTF("Write channel %c, reg[%d] = %2.2x\n", channel? 'b' : 'a', s->reg, val & 0xff); + SER_DPRINTF("Write channel %c, reg[%d] = %2.2x\n", CHN_C(s), s->reg, val & 0xff); newreg = 0; switch (s->reg) { case 0: @@ -292,9 +349,18 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr, uint break; } break; - case 1 ... 8: - case 10 ... 15: + case 1 ... 3: + case 6 ... 8: + case 10 ... 11: + case 14 ... 15: + s->wregs[s->reg] = val; + break; + case 4: + case 5: + case 12: + case 13: s->wregs[s->reg] = val; + slavio_serial_update_parameters(s); break; case 9: switch (val & 0xc0) { @@ -321,7 +387,7 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr, uint s->reg = 0; break; case 1: - SER_DPRINTF("Write channel %c, ch %d\n", channel? 'b' : 'a', val); + SER_DPRINTF("Write channel %c, ch %d\n", CHN_C(s), val); if (s->wregs[5] & 8) { // tx enabled s->tx = val; if (s->chr) @@ -352,7 +418,7 @@ static uint32_t slavio_serial_mem_readb(void *opaque, target_phys_addr_t addr) s = &ser->chn[channel]; switch (saddr) { case 0: - SER_DPRINTF("Read channel %c, reg[%d] = %2.2x\n", channel? 'b' : 'a', s->reg, s->rregs[s->reg]); + SER_DPRINTF("Read channel %c, reg[%d] = %2.2x\n", CHN_C(s), s->reg, s->rregs[s->reg]); ret = s->rregs[s->reg]; s->reg = 0; return ret; @@ -363,7 +429,7 @@ static uint32_t slavio_serial_mem_readb(void *opaque, target_phys_addr_t addr) ret = get_queue(s); else ret = s->rx; - SER_DPRINTF("Read channel %c, ch %d\n", channel? 'b' : 'a', ret); + SER_DPRINTF("Read channel %c, ch %d\n", CHN_C(s), ret); return ret; default: break; @@ -381,13 +447,13 @@ static int serial_can_receive(void *opaque) ret = 0; else ret = 1; - SER_DPRINTF("can receive %d\n", ret); + //SER_DPRINTF("channel %c can receive %d\n", CHN_C(s), ret); return ret; } static void serial_receive_byte(ChannelState *s, int ch) { - SER_DPRINTF("put ch %d\n", ch); + SER_DPRINTF("channel %c put ch %d\n", CHN_C(s), ch); s->rregs[0] |= 1; s->rx = ch; set_rxint(s); |