qemu/hw/m68k/next-kbd.c
<<
>>
Prefs
   1/*
   2 * QEMU NeXT Keyboard/Mouse emulation
   3 *
   4 * Copyright (c) 2011 Bryce Lanham
   5 *
   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
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25/*
  26 * This is admittedly hackish, but works well enough for basic input. Mouse
  27 * support will be added once we can boot something that needs the mouse.
  28 */
  29
  30#include "qemu/osdep.h"
  31#include "qemu/log.h"
  32#include "hw/sysbus.h"
  33#include "hw/m68k/next-cube.h"
  34#include "ui/console.h"
  35#include "migration/vmstate.h"
  36#include "qom/object.h"
  37
  38OBJECT_DECLARE_SIMPLE_TYPE(NextKBDState, NEXTKBD)
  39
  40/* following defintions from next68k netbsd */
  41#define CSR_INT 0x00800000
  42#define CSR_DATA 0x00400000
  43
  44#define KD_KEYMASK    0x007f
  45#define KD_DIRECTION  0x0080 /* pressed or released */
  46#define KD_CNTL       0x0100
  47#define KD_LSHIFT     0x0200
  48#define KD_RSHIFT     0x0400
  49#define KD_LCOMM      0x0800
  50#define KD_RCOMM      0x1000
  51#define KD_LALT       0x2000
  52#define KD_RALT       0x4000
  53#define KD_VALID      0x8000 /* only set for scancode keys ? */
  54#define KD_MODS       0x4f00
  55
  56#define KBD_QUEUE_SIZE 256
  57
  58typedef struct {
  59    uint8_t data[KBD_QUEUE_SIZE];
  60    int rptr, wptr, count;
  61} KBDQueue;
  62
  63
  64struct NextKBDState {
  65    SysBusDevice sbd;
  66    MemoryRegion mr;
  67    KBDQueue queue;
  68    uint16_t shift;
  69};
  70
  71static void queue_code(void *opaque, int code);
  72
  73/* lots of magic numbers here */
  74static uint32_t kbd_read_byte(void *opaque, hwaddr addr)
  75{
  76    switch (addr & 0x3) {
  77    case 0x0:   /* 0xe000 */
  78        return 0x80 | 0x20;
  79
  80    case 0x1:   /* 0xe001 */
  81        return 0x80 | 0x40 | 0x20 | 0x10;
  82
  83    case 0x2:   /* 0xe002 */
  84        /* returning 0x40 caused mach to hang */
  85        return 0x10 | 0x2 | 0x1;
  86
  87    default:
  88        qemu_log_mask(LOG_UNIMP, "NeXT kbd read byte %"HWADDR_PRIx"\n", addr);
  89    }
  90
  91    return 0;
  92}
  93
  94static uint32_t kbd_read_word(void *opaque, hwaddr addr)
  95{
  96    qemu_log_mask(LOG_UNIMP, "NeXT kbd read word %"HWADDR_PRIx"\n", addr);
  97    return 0;
  98}
  99
 100/* even more magic numbers */
 101static uint32_t kbd_read_long(void *opaque, hwaddr addr)
 102{
 103    int key = 0;
 104    NextKBDState *s = NEXTKBD(opaque);
 105    KBDQueue *q = &s->queue;
 106
 107    switch (addr & 0xf) {
 108    case 0x0:   /* 0xe000 */
 109        return 0xA0F09300;
 110
 111    case 0x8:   /* 0xe008 */
 112        /* get keycode from buffer */
 113        if (q->count > 0) {
 114            key = q->data[q->rptr];
 115            if (++q->rptr == KBD_QUEUE_SIZE) {
 116                q->rptr = 0;
 117            }
 118
 119            q->count--;
 120
 121            if (s->shift) {
 122                key |= s->shift;
 123            }
 124
 125            if (key & 0x80) {
 126                return 0;
 127            } else {
 128                return 0x10000000 | KD_VALID | key;
 129            }
 130        } else {
 131            return 0;
 132        }
 133
 134    default:
 135        qemu_log_mask(LOG_UNIMP, "NeXT kbd read long %"HWADDR_PRIx"\n", addr);
 136        return 0;
 137    }
 138}
 139
 140static uint64_t kbd_readfn(void *opaque, hwaddr addr, unsigned size)
 141{
 142    switch (size) {
 143    case 1:
 144        return kbd_read_byte(opaque, addr);
 145    case 2:
 146        return kbd_read_word(opaque, addr);
 147    case 4:
 148        return kbd_read_long(opaque, addr);
 149    default:
 150        g_assert_not_reached();
 151    }
 152}
 153
 154static void kbd_writefn(void *opaque, hwaddr addr, uint64_t value,
 155                        unsigned size)
 156{
 157    qemu_log_mask(LOG_UNIMP, "NeXT kbd write: size=%u addr=0x%"HWADDR_PRIx
 158                  "val=0x%"PRIx64"\n", size, addr, value);
 159}
 160
 161static const MemoryRegionOps kbd_ops = {
 162    .read = kbd_readfn,
 163    .write = kbd_writefn,
 164    .valid.min_access_size = 1,
 165    .valid.max_access_size = 4,
 166    .endianness = DEVICE_NATIVE_ENDIAN,
 167};
 168
 169static void nextkbd_event(void *opaque, int ch)
 170{
 171    /*
 172     * Will want to set vars for caps/num lock
 173     * if (ch & 0x80) -> key release
 174     * there's also e0 escaped scancodes that might need to be handled
 175     */
 176    queue_code(opaque, ch);
 177}
 178
 179static const unsigned char next_keycodes[128] = {
 180    0x00, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x50, 0x4F,
 181    0x4E, 0x1E, 0x1F, 0x20, 0x1D, 0x1C, 0x1B, 0x00,
 182    0x42, 0x43, 0x44, 0x45, 0x48, 0x47, 0x46, 0x06,
 183    0x07, 0x08, 0x00, 0x00, 0x2A, 0x00, 0x39, 0x3A,
 184    0x3B, 0x3C, 0x3D, 0x40, 0x3F, 0x3E, 0x2D, 0x2C,
 185    0x2B, 0x26, 0x00, 0x00, 0x31, 0x32, 0x33, 0x34,
 186    0x35, 0x37, 0x36, 0x2e, 0x2f, 0x30, 0x00, 0x00,
 187    0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 188    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 189    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 190    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 191    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 192};
 193
 194static void queue_code(void *opaque, int code)
 195{
 196    NextKBDState *s = NEXTKBD(opaque);
 197    KBDQueue *q = &s->queue;
 198    int key = code & KD_KEYMASK;
 199    int release = code & 0x80;
 200    static int ext;
 201
 202    if (code == 0xE0) {
 203        ext = 1;
 204    }
 205
 206    if (code == 0x2A || code == 0x1D || code == 0x36) {
 207        if (code == 0x2A) {
 208            s->shift = KD_LSHIFT;
 209        } else if (code == 0x36) {
 210            s->shift = KD_RSHIFT;
 211            ext = 0;
 212        } else if (code == 0x1D && !ext) {
 213            s->shift = KD_LCOMM;
 214        } else if (code == 0x1D && ext) {
 215            ext = 0;
 216            s->shift = KD_RCOMM;
 217        }
 218        return;
 219    } else if (code == (0x2A | 0x80) || code == (0x1D | 0x80) ||
 220               code == (0x36 | 0x80)) {
 221        s->shift = 0;
 222        return;
 223    }
 224
 225    if (q->count >= KBD_QUEUE_SIZE) {
 226        return;
 227    }
 228
 229    q->data[q->wptr] = next_keycodes[key] | release;
 230
 231    if (++q->wptr == KBD_QUEUE_SIZE) {
 232        q->wptr = 0;
 233    }
 234
 235    q->count++;
 236
 237    /*
 238     * might need to actually trigger the NeXT irq, but as the keyboard works
 239     * at the moment, I'll worry about it later
 240     */
 241    /* s->update_irq(s->update_arg, 1); */
 242}
 243
 244static void nextkbd_reset(DeviceState *dev)
 245{
 246    NextKBDState *nks = NEXTKBD(dev);
 247
 248    memset(&nks->queue, 0, sizeof(KBDQueue));
 249    nks->shift = 0;
 250}
 251
 252static void nextkbd_realize(DeviceState *dev, Error **errp)
 253{
 254    NextKBDState *s = NEXTKBD(dev);
 255
 256    memory_region_init_io(&s->mr, OBJECT(dev), &kbd_ops, s, "next.kbd", 0x1000);
 257    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
 258
 259    qemu_add_kbd_event_handler(nextkbd_event, s);
 260}
 261
 262static const VMStateDescription nextkbd_vmstate = {
 263    .name = TYPE_NEXTKBD,
 264    .unmigratable = 1,    /* TODO: Implement this when m68k CPU is migratable */
 265};
 266
 267static void nextkbd_class_init(ObjectClass *oc, void *data)
 268{
 269    DeviceClass *dc = DEVICE_CLASS(oc);
 270
 271    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 272    dc->vmsd = &nextkbd_vmstate;
 273    dc->realize = nextkbd_realize;
 274    dc->reset = nextkbd_reset;
 275}
 276
 277static const TypeInfo nextkbd_info = {
 278    .name          = TYPE_NEXTKBD,
 279    .parent        = TYPE_SYS_BUS_DEVICE,
 280    .instance_size = sizeof(NextKBDState),
 281    .class_init    = nextkbd_class_init,
 282};
 283
 284static void nextkbd_register_types(void)
 285{
 286    type_register_static(&nextkbd_info);
 287}
 288
 289type_init(nextkbd_register_types)
 290