diff options
author | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2005-04-06 20:42:35 +0000 |
---|---|---|
committer | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2005-04-06 20:42:35 +0000 |
commit | 8be1f5c889fa39571f9dad766497b7bdd56e9d6d (patch) | |
tree | d0ba78cac37eeb18d8b960a416ef4ed5c4258958 /hw | |
parent | d827220bbf2b4d56af6161753e9f88db400f6b63 (diff) |
keyboard support (Blue Swirl)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1346 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw')
-rw-r--r-- | hw/slavio_serial.c | 191 |
1 files changed, 166 insertions, 25 deletions
diff --git a/hw/slavio_serial.c b/hw/slavio_serial.c index 3dfac0af59..4b02d29f6b 100644 --- a/hw/slavio_serial.c +++ b/hw/slavio_serial.c @@ -1,7 +1,7 @@ /* * QEMU Sparc SLAVIO serial port emulation * - * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2003-2005 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 @@ -22,13 +22,13 @@ * THE SOFTWARE. */ #include "vl.h" - +/* debug serial */ //#define DEBUG_SERIAL /* debug keyboard */ //#define DEBUG_KBD -/* debug keyboard : only mouse */ +/* debug mouse */ //#define DEBUG_MOUSE /* @@ -42,11 +42,49 @@ * */ +#ifdef DEBUG_SERIAL +#define SER_DPRINTF(fmt, args...) \ +do { printf("SER: " fmt , ##args); } while (0) +#else +#define SER_DPRINTF(fmt, args...) +#endif +#ifdef DEBUG_KBD +#define KBD_DPRINTF(fmt, args...) \ +do { printf("KBD: " fmt , ##args); } while (0) +#else +#define KBD_DPRINTF(fmt, args...) +#endif +#ifdef DEBUG_MOUSE +#define MS_DPRINTF(fmt, args...) \ +do { printf("SER: " fmt , ##args); } while (0) +#else +#define MS_DPRINTF(fmt, args...) +#endif + +typedef enum { + chn_a, chn_b, +} chn_id_t; + +typedef enum { + ser, kbd, mouse, +} chn_type_t; + +#define KBD_QUEUE_SIZE 256 + +typedef struct { + uint8_t data[KBD_QUEUE_SIZE]; + int rptr, wptr, count; +} KBDQueue; + typedef struct ChannelState { int irq; int reg; int rxint, txint; + chn_id_t chn; // this channel, A (base+4) or B (base+0) + chn_type_t type; + struct ChannelState *otherchn; uint8_t rx, tx, wregs[16], rregs[16]; + KBDQueue queue; CharDriverState *chr; } ChannelState; @@ -56,6 +94,45 @@ struct SerialState { #define SERIAL_MAXADDR 7 +static void handle_kbd_command(ChannelState *s, int val); +static int serial_can_receive(void *opaque); +static void serial_receive_byte(ChannelState *s, int ch); + +static void put_queue(void *opaque, int b) +{ + ChannelState *s = opaque; + KBDQueue *q = &s->queue; + + KBD_DPRINTF("put: 0x%02x\n", b); + if (q->count >= KBD_QUEUE_SIZE) + return; + q->data[q->wptr] = b; + if (++q->wptr == KBD_QUEUE_SIZE) + q->wptr = 0; + q->count++; + serial_receive_byte(s, 0); +} + +static uint32_t get_queue(void *opaque) +{ + ChannelState *s = opaque; + KBDQueue *q = &s->queue; + int val; + + if (q->count == 0) { + return 0; + } else { + val = q->data[q->rptr]; + if (++q->rptr == KBD_QUEUE_SIZE) + q->rptr = 0; + q->count--; + } + KBD_DPRINTF("get 0x%02x\n", val); + if (q->count > 0) + serial_receive_byte(s, 0); + return val; +} + static void slavio_serial_update_irq(ChannelState *s) { if ((s->wregs[1] & 1) && // interrupts enabled @@ -110,6 +187,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); newreg = 0; switch (s->reg) { case 0: @@ -158,11 +236,23 @@ 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); if (s->wregs[5] & 8) { // tx enabled s->tx = val; if (s->chr) qemu_chr_write(s->chr, &s->tx, 1); + else if (s->type == kbd) { + handle_kbd_command(s, val); + } s->txint = 1; + s->rregs[0] |= 4; + // Interrupts reported only on channel A + if (s->chn == 0) + s->rregs[3] |= 0x10; + else { + s->otherchn->rregs[3] |= 2; + } + slavio_serial_update_irq(s); } break; default: @@ -183,12 +273,18 @@ 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]); ret = s->rregs[s->reg]; s->reg = 0; return ret; case 1: + SER_DPRINTF("Read channel %c, ch %d\n", channel? 'b' : 'a', s->rx); s->rregs[0] &= ~1; - return s->rx; + if (s->type == kbd) + ret = get_queue(s); + else + ret = s->rx; + return ret; default: break; } @@ -208,6 +304,12 @@ static int serial_can_receive(void *opaque) static void serial_receive_byte(ChannelState *s, int ch) { s->rregs[0] |= 1; + // Interrupts reported only on channel A + if (s->chn == 0) + s->rregs[3] |= 0x20; + else { + s->otherchn->rregs[3] |= 4; + } s->rx = ch; s->rxint = 1; slavio_serial_update_irq(s); @@ -295,39 +397,73 @@ static int slavio_serial_load(QEMUFile *f, void *opaque, int version_id) SerialState *slavio_serial_init(int base, int irq, CharDriverState *chr1, CharDriverState *chr2) { - int slavio_serial_io_memory; + int slavio_serial_io_memory, i; SerialState *s; s = qemu_mallocz(sizeof(SerialState)); if (!s) return NULL; - s->chn[0].irq = irq; - s->chn[1].irq = irq; - s->chn[0].chr = chr1; - s->chn[1].chr = chr2; slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read, slavio_serial_mem_write, s); cpu_register_physical_memory(base, SERIAL_MAXADDR, slavio_serial_io_memory); - if (chr1) { - qemu_chr_add_read_handler(chr1, serial_can_receive, serial_receive1, &s->chn[0]); - qemu_chr_add_event_handler(chr1, serial_event); - } - if (chr2) { - qemu_chr_add_read_handler(chr2, serial_can_receive, serial_receive1, &s->chn[1]); - qemu_chr_add_event_handler(chr2, serial_event); + s->chn[0].chr = chr1; + s->chn[1].chr = chr2; + + for (i = 0; i < 2; i++) { + s->chn[i].irq = irq; + s->chn[i].chn = 1 - i; + s->chn[i].type = ser; + if (s->chn[i].chr) { + qemu_chr_add_read_handler(s->chn[i].chr, serial_can_receive, serial_receive1, &s->chn[i]); + qemu_chr_add_event_handler(s->chn[i].chr, serial_event); + } } + s->chn[0].otherchn = &s->chn[1]; + s->chn[1].otherchn = &s->chn[0]; register_savevm("slavio_serial", base, 1, slavio_serial_save, slavio_serial_load, s); qemu_register_reset(slavio_serial_reset, s); slavio_serial_reset(s); return s; } +static const uint8_t keycodes[128] = { + 127, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 89, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 42, 99, 88, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 47, 19, 121, 119, 5, 6, 8, 10, 12, + 14, 16, 17, 18, 7, 98, 23, 68, 69, 70, 71, 91, 92, 93, 125, 112, + 113, 114, 94, 50, 0, 0, 124, 9, 11, 0, 0, 0, 0, 0, 0, 0, + 90, 0, 46, 22, 13, 111, 52, 20, 96, 24, 28, 74, 27, 123, 44, 66, + 0, 45, 2, 4, 48, 0, 0, 21, 0, 0, 0, 0, 0, 120, 122, 67, +}; + static void sunkbd_event(void *opaque, int ch) { ChannelState *s = opaque; - // XXX: PC -> Sun Type 5 translation? - serial_receive_byte(s, ch); + int release = ch & 0x80; + + ch = keycodes[ch & 0x7f]; + KBD_DPRINTF("Keycode %d (%s)\n", ch, release? "release" : "press"); + put_queue(s, ch | release); +} + +static void handle_kbd_command(ChannelState *s, int val) +{ + KBD_DPRINTF("Command %d\n", val); + switch (val) { + case 1: // Reset, return type code + put_queue(s, 0xff); + put_queue(s, 0xff); + put_queue(s, 5); // Type 5 + break; + case 7: // Query layout + put_queue(s, 0xfe); + put_queue(s, 0x20); // XXX, layout? + break; + default: + break; + } } static void sunmouse_event(void *opaque, @@ -343,22 +479,27 @@ static void sunmouse_event(void *opaque, void slavio_serial_ms_kbd_init(int base, int irq) { - int slavio_serial_io_memory; + int slavio_serial_io_memory, i; SerialState *s; s = qemu_mallocz(sizeof(SerialState)); if (!s) return; - s->chn[0].irq = irq; - s->chn[1].irq = irq; - s->chn[0].chr = NULL; - s->chn[1].chr = NULL; + for (i = 0; i < 2; i++) { + s->chn[i].irq = irq; + s->chn[i].chn = 1 - i; + s->chn[i].chr = NULL; + } + s->chn[0].otherchn = &s->chn[1]; + s->chn[1].otherchn = &s->chn[0]; + s->chn[0].type = mouse; + s->chn[1].type = kbd; slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read, slavio_serial_mem_write, s); cpu_register_physical_memory(base, SERIAL_MAXADDR, slavio_serial_io_memory); - qemu_add_kbd_event_handler(sunkbd_event, &s->chn[0]); - qemu_add_mouse_event_handler(sunmouse_event, &s->chn[1]); + qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0]); + qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1]); qemu_register_reset(slavio_serial_reset, s); slavio_serial_reset(s); } |