Commit 8be1f5c889fa39571f9dad766497b7bdd56e9d6d
1 parent
d827220b
keyboard support (Blue Swirl)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1346 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
166 additions
and
25 deletions
hw/slavio_serial.c
| 1 | /* | 1 | /* |
| 2 | * QEMU Sparc SLAVIO serial port emulation | 2 | * QEMU Sparc SLAVIO serial port emulation |
| 3 | * | 3 | * |
| 4 | - * Copyright (c) 2003-2004 Fabrice Bellard | 4 | + * Copyright (c) 2003-2005 Fabrice Bellard |
| 5 | * | 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to deal | 7 | * of this software and associated documentation files (the "Software"), to deal |
| @@ -22,13 +22,13 @@ | @@ -22,13 +22,13 @@ | ||
| 22 | * THE SOFTWARE. | 22 | * THE SOFTWARE. |
| 23 | */ | 23 | */ |
| 24 | #include "vl.h" | 24 | #include "vl.h" |
| 25 | - | 25 | +/* debug serial */ |
| 26 | //#define DEBUG_SERIAL | 26 | //#define DEBUG_SERIAL |
| 27 | 27 | ||
| 28 | /* debug keyboard */ | 28 | /* debug keyboard */ |
| 29 | //#define DEBUG_KBD | 29 | //#define DEBUG_KBD |
| 30 | 30 | ||
| 31 | -/* debug keyboard : only mouse */ | 31 | +/* debug mouse */ |
| 32 | //#define DEBUG_MOUSE | 32 | //#define DEBUG_MOUSE |
| 33 | 33 | ||
| 34 | /* | 34 | /* |
| @@ -42,11 +42,49 @@ | @@ -42,11 +42,49 @@ | ||
| 42 | * | 42 | * |
| 43 | */ | 43 | */ |
| 44 | 44 | ||
| 45 | +#ifdef DEBUG_SERIAL | ||
| 46 | +#define SER_DPRINTF(fmt, args...) \ | ||
| 47 | +do { printf("SER: " fmt , ##args); } while (0) | ||
| 48 | +#else | ||
| 49 | +#define SER_DPRINTF(fmt, args...) | ||
| 50 | +#endif | ||
| 51 | +#ifdef DEBUG_KBD | ||
| 52 | +#define KBD_DPRINTF(fmt, args...) \ | ||
| 53 | +do { printf("KBD: " fmt , ##args); } while (0) | ||
| 54 | +#else | ||
| 55 | +#define KBD_DPRINTF(fmt, args...) | ||
| 56 | +#endif | ||
| 57 | +#ifdef DEBUG_MOUSE | ||
| 58 | +#define MS_DPRINTF(fmt, args...) \ | ||
| 59 | +do { printf("SER: " fmt , ##args); } while (0) | ||
| 60 | +#else | ||
| 61 | +#define MS_DPRINTF(fmt, args...) | ||
| 62 | +#endif | ||
| 63 | + | ||
| 64 | +typedef enum { | ||
| 65 | + chn_a, chn_b, | ||
| 66 | +} chn_id_t; | ||
| 67 | + | ||
| 68 | +typedef enum { | ||
| 69 | + ser, kbd, mouse, | ||
| 70 | +} chn_type_t; | ||
| 71 | + | ||
| 72 | +#define KBD_QUEUE_SIZE 256 | ||
| 73 | + | ||
| 74 | +typedef struct { | ||
| 75 | + uint8_t data[KBD_QUEUE_SIZE]; | ||
| 76 | + int rptr, wptr, count; | ||
| 77 | +} KBDQueue; | ||
| 78 | + | ||
| 45 | typedef struct ChannelState { | 79 | typedef struct ChannelState { |
| 46 | int irq; | 80 | int irq; |
| 47 | int reg; | 81 | int reg; |
| 48 | int rxint, txint; | 82 | int rxint, txint; |
| 83 | + chn_id_t chn; // this channel, A (base+4) or B (base+0) | ||
| 84 | + chn_type_t type; | ||
| 85 | + struct ChannelState *otherchn; | ||
| 49 | uint8_t rx, tx, wregs[16], rregs[16]; | 86 | uint8_t rx, tx, wregs[16], rregs[16]; |
| 87 | + KBDQueue queue; | ||
| 50 | CharDriverState *chr; | 88 | CharDriverState *chr; |
| 51 | } ChannelState; | 89 | } ChannelState; |
| 52 | 90 | ||
| @@ -56,6 +94,45 @@ struct SerialState { | @@ -56,6 +94,45 @@ struct SerialState { | ||
| 56 | 94 | ||
| 57 | #define SERIAL_MAXADDR 7 | 95 | #define SERIAL_MAXADDR 7 |
| 58 | 96 | ||
| 97 | +static void handle_kbd_command(ChannelState *s, int val); | ||
| 98 | +static int serial_can_receive(void *opaque); | ||
| 99 | +static void serial_receive_byte(ChannelState *s, int ch); | ||
| 100 | + | ||
| 101 | +static void put_queue(void *opaque, int b) | ||
| 102 | +{ | ||
| 103 | + ChannelState *s = opaque; | ||
| 104 | + KBDQueue *q = &s->queue; | ||
| 105 | + | ||
| 106 | + KBD_DPRINTF("put: 0x%02x\n", b); | ||
| 107 | + if (q->count >= KBD_QUEUE_SIZE) | ||
| 108 | + return; | ||
| 109 | + q->data[q->wptr] = b; | ||
| 110 | + if (++q->wptr == KBD_QUEUE_SIZE) | ||
| 111 | + q->wptr = 0; | ||
| 112 | + q->count++; | ||
| 113 | + serial_receive_byte(s, 0); | ||
| 114 | +} | ||
| 115 | + | ||
| 116 | +static uint32_t get_queue(void *opaque) | ||
| 117 | +{ | ||
| 118 | + ChannelState *s = opaque; | ||
| 119 | + KBDQueue *q = &s->queue; | ||
| 120 | + int val; | ||
| 121 | + | ||
| 122 | + if (q->count == 0) { | ||
| 123 | + return 0; | ||
| 124 | + } else { | ||
| 125 | + val = q->data[q->rptr]; | ||
| 126 | + if (++q->rptr == KBD_QUEUE_SIZE) | ||
| 127 | + q->rptr = 0; | ||
| 128 | + q->count--; | ||
| 129 | + } | ||
| 130 | + KBD_DPRINTF("get 0x%02x\n", val); | ||
| 131 | + if (q->count > 0) | ||
| 132 | + serial_receive_byte(s, 0); | ||
| 133 | + return val; | ||
| 134 | +} | ||
| 135 | + | ||
| 59 | static void slavio_serial_update_irq(ChannelState *s) | 136 | static void slavio_serial_update_irq(ChannelState *s) |
| 60 | { | 137 | { |
| 61 | if ((s->wregs[1] & 1) && // interrupts enabled | 138 | 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 | @@ -110,6 +187,7 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr, uint | ||
| 110 | s = &ser->chn[channel]; | 187 | s = &ser->chn[channel]; |
| 111 | switch (saddr) { | 188 | switch (saddr) { |
| 112 | case 0: | 189 | case 0: |
| 190 | + SER_DPRINTF("Write channel %c, reg[%d] = %2.2x\n", channel? 'b' : 'a', s->reg, val & 0xff); | ||
| 113 | newreg = 0; | 191 | newreg = 0; |
| 114 | switch (s->reg) { | 192 | switch (s->reg) { |
| 115 | case 0: | 193 | case 0: |
| @@ -158,11 +236,23 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr, uint | @@ -158,11 +236,23 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr, uint | ||
| 158 | s->reg = 0; | 236 | s->reg = 0; |
| 159 | break; | 237 | break; |
| 160 | case 1: | 238 | case 1: |
| 239 | + SER_DPRINTF("Write channel %c, ch %d\n", channel? 'b' : 'a', val); | ||
| 161 | if (s->wregs[5] & 8) { // tx enabled | 240 | if (s->wregs[5] & 8) { // tx enabled |
| 162 | s->tx = val; | 241 | s->tx = val; |
| 163 | if (s->chr) | 242 | if (s->chr) |
| 164 | qemu_chr_write(s->chr, &s->tx, 1); | 243 | qemu_chr_write(s->chr, &s->tx, 1); |
| 244 | + else if (s->type == kbd) { | ||
| 245 | + handle_kbd_command(s, val); | ||
| 246 | + } | ||
| 165 | s->txint = 1; | 247 | s->txint = 1; |
| 248 | + s->rregs[0] |= 4; | ||
| 249 | + // Interrupts reported only on channel A | ||
| 250 | + if (s->chn == 0) | ||
| 251 | + s->rregs[3] |= 0x10; | ||
| 252 | + else { | ||
| 253 | + s->otherchn->rregs[3] |= 2; | ||
| 254 | + } | ||
| 255 | + slavio_serial_update_irq(s); | ||
| 166 | } | 256 | } |
| 167 | break; | 257 | break; |
| 168 | default: | 258 | default: |
| @@ -183,12 +273,18 @@ static uint32_t slavio_serial_mem_readb(void *opaque, target_phys_addr_t addr) | @@ -183,12 +273,18 @@ static uint32_t slavio_serial_mem_readb(void *opaque, target_phys_addr_t addr) | ||
| 183 | s = &ser->chn[channel]; | 273 | s = &ser->chn[channel]; |
| 184 | switch (saddr) { | 274 | switch (saddr) { |
| 185 | case 0: | 275 | case 0: |
| 276 | + SER_DPRINTF("Read channel %c, reg[%d] = %2.2x\n", channel? 'b' : 'a', s->reg, s->rregs[s->reg]); | ||
| 186 | ret = s->rregs[s->reg]; | 277 | ret = s->rregs[s->reg]; |
| 187 | s->reg = 0; | 278 | s->reg = 0; |
| 188 | return ret; | 279 | return ret; |
| 189 | case 1: | 280 | case 1: |
| 281 | + SER_DPRINTF("Read channel %c, ch %d\n", channel? 'b' : 'a', s->rx); | ||
| 190 | s->rregs[0] &= ~1; | 282 | s->rregs[0] &= ~1; |
| 191 | - return s->rx; | 283 | + if (s->type == kbd) |
| 284 | + ret = get_queue(s); | ||
| 285 | + else | ||
| 286 | + ret = s->rx; | ||
| 287 | + return ret; | ||
| 192 | default: | 288 | default: |
| 193 | break; | 289 | break; |
| 194 | } | 290 | } |
| @@ -208,6 +304,12 @@ static int serial_can_receive(void *opaque) | @@ -208,6 +304,12 @@ static int serial_can_receive(void *opaque) | ||
| 208 | static void serial_receive_byte(ChannelState *s, int ch) | 304 | static void serial_receive_byte(ChannelState *s, int ch) |
| 209 | { | 305 | { |
| 210 | s->rregs[0] |= 1; | 306 | s->rregs[0] |= 1; |
| 307 | + // Interrupts reported only on channel A | ||
| 308 | + if (s->chn == 0) | ||
| 309 | + s->rregs[3] |= 0x20; | ||
| 310 | + else { | ||
| 311 | + s->otherchn->rregs[3] |= 4; | ||
| 312 | + } | ||
| 211 | s->rx = ch; | 313 | s->rx = ch; |
| 212 | s->rxint = 1; | 314 | s->rxint = 1; |
| 213 | slavio_serial_update_irq(s); | 315 | slavio_serial_update_irq(s); |
| @@ -295,39 +397,73 @@ static int slavio_serial_load(QEMUFile *f, void *opaque, int version_id) | @@ -295,39 +397,73 @@ static int slavio_serial_load(QEMUFile *f, void *opaque, int version_id) | ||
| 295 | 397 | ||
| 296 | SerialState *slavio_serial_init(int base, int irq, CharDriverState *chr1, CharDriverState *chr2) | 398 | SerialState *slavio_serial_init(int base, int irq, CharDriverState *chr1, CharDriverState *chr2) |
| 297 | { | 399 | { |
| 298 | - int slavio_serial_io_memory; | 400 | + int slavio_serial_io_memory, i; |
| 299 | SerialState *s; | 401 | SerialState *s; |
| 300 | 402 | ||
| 301 | s = qemu_mallocz(sizeof(SerialState)); | 403 | s = qemu_mallocz(sizeof(SerialState)); |
| 302 | if (!s) | 404 | if (!s) |
| 303 | return NULL; | 405 | return NULL; |
| 304 | - s->chn[0].irq = irq; | ||
| 305 | - s->chn[1].irq = irq; | ||
| 306 | - s->chn[0].chr = chr1; | ||
| 307 | - s->chn[1].chr = chr2; | ||
| 308 | 406 | ||
| 309 | slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read, slavio_serial_mem_write, s); | 407 | slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read, slavio_serial_mem_write, s); |
| 310 | cpu_register_physical_memory(base, SERIAL_MAXADDR, slavio_serial_io_memory); | 408 | cpu_register_physical_memory(base, SERIAL_MAXADDR, slavio_serial_io_memory); |
| 311 | 409 | ||
| 312 | - if (chr1) { | ||
| 313 | - qemu_chr_add_read_handler(chr1, serial_can_receive, serial_receive1, &s->chn[0]); | ||
| 314 | - qemu_chr_add_event_handler(chr1, serial_event); | ||
| 315 | - } | ||
| 316 | - if (chr2) { | ||
| 317 | - qemu_chr_add_read_handler(chr2, serial_can_receive, serial_receive1, &s->chn[1]); | ||
| 318 | - qemu_chr_add_event_handler(chr2, serial_event); | 410 | + s->chn[0].chr = chr1; |
| 411 | + s->chn[1].chr = chr2; | ||
| 412 | + | ||
| 413 | + for (i = 0; i < 2; i++) { | ||
| 414 | + s->chn[i].irq = irq; | ||
| 415 | + s->chn[i].chn = 1 - i; | ||
| 416 | + s->chn[i].type = ser; | ||
| 417 | + if (s->chn[i].chr) { | ||
| 418 | + qemu_chr_add_read_handler(s->chn[i].chr, serial_can_receive, serial_receive1, &s->chn[i]); | ||
| 419 | + qemu_chr_add_event_handler(s->chn[i].chr, serial_event); | ||
| 420 | + } | ||
| 319 | } | 421 | } |
| 422 | + s->chn[0].otherchn = &s->chn[1]; | ||
| 423 | + s->chn[1].otherchn = &s->chn[0]; | ||
| 320 | register_savevm("slavio_serial", base, 1, slavio_serial_save, slavio_serial_load, s); | 424 | register_savevm("slavio_serial", base, 1, slavio_serial_save, slavio_serial_load, s); |
| 321 | qemu_register_reset(slavio_serial_reset, s); | 425 | qemu_register_reset(slavio_serial_reset, s); |
| 322 | slavio_serial_reset(s); | 426 | slavio_serial_reset(s); |
| 323 | return s; | 427 | return s; |
| 324 | } | 428 | } |
| 325 | 429 | ||
| 430 | +static const uint8_t keycodes[128] = { | ||
| 431 | + 127, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 53, | ||
| 432 | + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 89, 76, 77, 78, | ||
| 433 | + 79, 80, 81, 82, 83, 84, 85, 86, 87, 42, 99, 88, 100, 101, 102, 103, | ||
| 434 | + 104, 105, 106, 107, 108, 109, 110, 47, 19, 121, 119, 5, 6, 8, 10, 12, | ||
| 435 | + 14, 16, 17, 18, 7, 98, 23, 68, 69, 70, 71, 91, 92, 93, 125, 112, | ||
| 436 | + 113, 114, 94, 50, 0, 0, 124, 9, 11, 0, 0, 0, 0, 0, 0, 0, | ||
| 437 | + 90, 0, 46, 22, 13, 111, 52, 20, 96, 24, 28, 74, 27, 123, 44, 66, | ||
| 438 | + 0, 45, 2, 4, 48, 0, 0, 21, 0, 0, 0, 0, 0, 120, 122, 67, | ||
| 439 | +}; | ||
| 440 | + | ||
| 326 | static void sunkbd_event(void *opaque, int ch) | 441 | static void sunkbd_event(void *opaque, int ch) |
| 327 | { | 442 | { |
| 328 | ChannelState *s = opaque; | 443 | ChannelState *s = opaque; |
| 329 | - // XXX: PC -> Sun Type 5 translation? | ||
| 330 | - serial_receive_byte(s, ch); | 444 | + int release = ch & 0x80; |
| 445 | + | ||
| 446 | + ch = keycodes[ch & 0x7f]; | ||
| 447 | + KBD_DPRINTF("Keycode %d (%s)\n", ch, release? "release" : "press"); | ||
| 448 | + put_queue(s, ch | release); | ||
| 449 | +} | ||
| 450 | + | ||
| 451 | +static void handle_kbd_command(ChannelState *s, int val) | ||
| 452 | +{ | ||
| 453 | + KBD_DPRINTF("Command %d\n", val); | ||
| 454 | + switch (val) { | ||
| 455 | + case 1: // Reset, return type code | ||
| 456 | + put_queue(s, 0xff); | ||
| 457 | + put_queue(s, 0xff); | ||
| 458 | + put_queue(s, 5); // Type 5 | ||
| 459 | + break; | ||
| 460 | + case 7: // Query layout | ||
| 461 | + put_queue(s, 0xfe); | ||
| 462 | + put_queue(s, 0x20); // XXX, layout? | ||
| 463 | + break; | ||
| 464 | + default: | ||
| 465 | + break; | ||
| 466 | + } | ||
| 331 | } | 467 | } |
| 332 | 468 | ||
| 333 | static void sunmouse_event(void *opaque, | 469 | static void sunmouse_event(void *opaque, |
| @@ -343,22 +479,27 @@ static void sunmouse_event(void *opaque, | @@ -343,22 +479,27 @@ static void sunmouse_event(void *opaque, | ||
| 343 | 479 | ||
| 344 | void slavio_serial_ms_kbd_init(int base, int irq) | 480 | void slavio_serial_ms_kbd_init(int base, int irq) |
| 345 | { | 481 | { |
| 346 | - int slavio_serial_io_memory; | 482 | + int slavio_serial_io_memory, i; |
| 347 | SerialState *s; | 483 | SerialState *s; |
| 348 | 484 | ||
| 349 | s = qemu_mallocz(sizeof(SerialState)); | 485 | s = qemu_mallocz(sizeof(SerialState)); |
| 350 | if (!s) | 486 | if (!s) |
| 351 | return; | 487 | return; |
| 352 | - s->chn[0].irq = irq; | ||
| 353 | - s->chn[1].irq = irq; | ||
| 354 | - s->chn[0].chr = NULL; | ||
| 355 | - s->chn[1].chr = NULL; | 488 | + for (i = 0; i < 2; i++) { |
| 489 | + s->chn[i].irq = irq; | ||
| 490 | + s->chn[i].chn = 1 - i; | ||
| 491 | + s->chn[i].chr = NULL; | ||
| 492 | + } | ||
| 493 | + s->chn[0].otherchn = &s->chn[1]; | ||
| 494 | + s->chn[1].otherchn = &s->chn[0]; | ||
| 495 | + s->chn[0].type = mouse; | ||
| 496 | + s->chn[1].type = kbd; | ||
| 356 | 497 | ||
| 357 | slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read, slavio_serial_mem_write, s); | 498 | slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read, slavio_serial_mem_write, s); |
| 358 | cpu_register_physical_memory(base, SERIAL_MAXADDR, slavio_serial_io_memory); | 499 | cpu_register_physical_memory(base, SERIAL_MAXADDR, slavio_serial_io_memory); |
| 359 | 500 | ||
| 360 | - qemu_add_kbd_event_handler(sunkbd_event, &s->chn[0]); | ||
| 361 | - qemu_add_mouse_event_handler(sunmouse_event, &s->chn[1]); | 501 | + qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0]); |
| 502 | + qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1]); | ||
| 362 | qemu_register_reset(slavio_serial_reset, s); | 503 | qemu_register_reset(slavio_serial_reset, s); |
| 363 | slavio_serial_reset(s); | 504 | slavio_serial_reset(s); |
| 364 | } | 505 | } |