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 "exec/address-spaces.h"
  33#include "hw/hw.h"
  34#include "hw/sysbus.h"
  35#include "hw/m68k/next-cube.h"
  36#include "ui/console.h"
  37#include "sysemu/sysemu.h"
  38#include "migration/vmstate.h"
  39
  40#define NEXTKBD(obj) OBJECT_CHECK(NextKBDState, (obj), TYPE_NEXTKBD)
  41
  42/* following defintions from next68k netbsd */
  43#define CSR_INT 0x00800000
  44#define CSR_DATA 0x00400000
  45
  46#define KD_KEYMASK    0x007f
  47#define KD_DIRECTION  0x0080 /* pressed or released */
  48#define KD_CNTL       0x0100
  49#define KD_LSHIFT     0x0200
  50#define KD_RSHIFT     0x0400
  51#define KD_LCOMM      0x0800
  52#define KD_RCOMM      0x1000
  53#define KD_LALT       0x2000
  54#define KD_RALT       0x4000
  55#define KD_VALID      0x8000 /* only set for scancode keys ? */
  56#define KD_MODS       0x4f00
  57
  58#define KBD_QUEUE_SIZE 256
  59
  60typedef struct {
  61    uint8_t data[KBD_QUEUE_SIZE];
  62    int rptr, wptr, count;
  63} KBDQueue;
  64
  65
  66typedef struct NextKBDState {
  67    SysBusDevice sbd;
  68    MemoryRegion mr;
  69    KBDQueue queue;
  70    uint16_t shift;
  71} NextKBDState;
  72
  73static void queue_code(void *opaque, int code);
  74
  75/* lots of magic numbers here */
  76static uint32_t kbd_read_byte(void *opaque, hwaddr addr)
  77{
  78    switch (addr & 0x3) {
  79    case 0x0:   /* 0xe000 */
  80        return 0x80 | 0x20;
  81
  82    case 0x1:   /* 0xe001 */
  83        return 0x80 | 0x40 | 0x20 | 0x10;
  84
  85    case 0x2:   /* 0xe002 */
  86        /* returning 0x40 caused mach to hang */
  87        return 0x10 | 0x2 | 0x1;
  88
  89    default:
  90        qemu_log_mask(LOG_UNIMP, "NeXT kbd read byte %"HWADDR_PRIx"\n", addr);
  91    }
  92
  93    return 0;
  94}
  95
  96static uint32_t kbd_read_word(void *opaque, hwaddr addr)
  97{
  98    qemu_log_mask(LOG_UNIMP, "NeXT kbd read word %"HWADDR_PRIx"\n", addr);
  99    return 0;
 100}
 101
 102/* even more magic numbers */
 103static uint32_t kbd_read_long(void *opaque, hwaddr addr)
 104{
 105    int key = 0;
 106    NextKBDState *s = NEXTKBD(opaque);
 107    KBDQueue *q = &s->queue;
 108
 109    switch (addr & 0xf) {
 110    case 0x0:   /* 0xe000 */
 111        return 0xA0F09300;
 112
 113    case 0x8:   /* 0xe008 */
 114        /* get keycode from buffer */
 115        if (q->count > 0) {
 116            key = q->data[q->rptr];
 117            if (++q->rptr == KBD_QUEUE_SIZE) {
 118                q->rptr = 0;
 119            }
 120
 121            q->count--;
 122
 123            if (s->shift) {
 124                key |= s->shift;
 125            }
 126
 127            if (key & 0x80) {
 128                return 0;
 129            } else {
 130                return 0x10000000 | KD_VALID | key;
 131            }
 132        } else {
 133            return 0;
 134        }
 135
 136    default:
 137        qemu_log_mask(LOG_UNIMP, "NeXT kbd read long %"HWADDR_PRIx"\n", addr);
 138        return 0;
 139    }
 140}
 141
 142static uint64_t kbd_readfn(void *opaque, hwaddr addr, unsigned size)
 143{
 144    switch (size) {
 145    case 1:
 146        return kbd_read_byte(opaque, addr);
 147    case 2:
 148        return kbd_read_word(opaque, addr);
 149    case 4:
 150        return kbd_read_long(opaque, addr);
 151    default:
 152        g_assert_not_reached();
 153    }
 154}
 155
 156static void kbd_writefn(void *opaque, hwaddr addr, uint64_t value,
 157                        unsigned size)
 158{
 159    qemu_log_mask(LOG_UNIMP, "NeXT kbd write: size=%u addr=0x%"HWADDR_PRIx
 160                  "val=0x%"PRIx64"\n", size, addr, value);
 161}
 162
 163static const MemoryRegionOps kbd_ops = {
 164    .read = kbd_readfn,
 165    .write = kbd_writefn,
 166    .valid.min_access_size = 1,
 167    .valid.max_access_size = 4,
 168    .endianness = DEVICE_NATIVE_ENDIAN,
 169};
 170
 171static void nextkbd_event(void *opaque, int ch)
 172{
 173    /*
 174     * Will want to set vars for caps/num lock
 175     * if (ch & 0x80) -> key release
 176     * there's also e0 escaped scancodes that might need to be handled
 177     */
 178    queue_code(opaque, ch);
 179}
 180
 181static const unsigned char next_keycodes[128] = {
 182    0x00, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x50, 0x4F,
 183    0x4E, 0x1E, 0x1F, 0x20, 0x1D, 0x1C, 0x1B, 0x00,
 184    0x42, 0x43, 0x44, 0x45, 0x48, 0x47, 0x46, 0x06,
 185    0x07, 0x08, 0x00, 0x00, 0x2A, 0x00, 0x39, 0x3A,
 186    0x3B, 0x3C, 0x3D, 0x40, 0x3F, 0x3E, 0x2D, 0x2C,
 187    0x2B, 0x26, 0x00, 0x00, 0x31, 0x32, 0x33, 0x34,
 188    0x35, 0x37, 0x36, 0x2e, 0x2f, 0x30, 0x00, 0x00,
 189    0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 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    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 193    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 194};
 195
 196static void queue_code(void *opaque, int code)
 197{
 198    NextKBDState *s = NEXTKBD(opaque);
 199    KBDQueue *q = &s->queue;
 200    int key = code & KD_KEYMASK;
 201    int release = code & 0x80;
 202    static int ext;
 203
 204    if (code == 0xE0) {
 205        ext = 1;
 206    }
 207
 208    if (code == 0x2A || code == 0x1D || code == 0x36) {
 209        if (code == 0x2A) {
 210            s->shift = KD_LSHIFT;
 211        } else if (code == 0x36) {
 212            s->shift = KD_RSHIFT;
 213            ext = 0;
 214        } else if (code == 0x1D && !ext) {
 215            s->shift = KD_LCOMM;
 216        } else if (code == 0x1D && ext) {
 217            ext = 0;
 218            s->shift = KD_RCOMM;
 219        }
 220        return;
 221    } else if (code == (0x2A | 0x80) || code == (0x1D | 0x80) ||
 222               code == (0x36 | 0x80)) {
 223        s->shift = 0;
 224        return;
 225    }
 226
 227    if (q->count >= KBD_QUEUE_SIZE) {
 228        return;
 229    }
 230
 231    q->data[q->wptr] = next_keycodes[key] | release;
 232
 233    if (++q->wptr == KBD_QUEUE_SIZE) {
 234        q->wptr = 0;
 235    }
 236
 237    q->count++;
 238
 239    /*
 240     * might need to actually trigger the NeXT irq, but as the keyboard works
 241     * at the moment, I'll worry about it later
 242     */
 243    /* s->update_irq(s->update_arg, 1); */
 244}
 245
 246static void nextkbd_reset(DeviceState *dev)
 247{
 248    NextKBDState *nks = NEXTKBD(dev);
 249
 250    memset(&nks->queue, 0, sizeof(KBDQueue));
 251    nks->shift = 0;
 252}
 253
 254static void nextkbd_realize(DeviceState *dev, Error **errp)
 255{
 256    NextKBDState *s = NEXTKBD(dev);
 257
 258    memory_region_init_io(&s->mr, OBJECT(dev), &kbd_ops, s, "next.kbd", 0x1000);
 259    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
 260
 261    qemu_add_kbd_event_handler(nextkbd_event, s);
 262}
 263
 264static const VMStateDescription nextkbd_vmstate = {
 265    .name = TYPE_NEXTKBD,
 266    .unmigratable = 1,    /* TODO: Implement this when m68k CPU is migratable */
 267};
 268
 269static void nextkbd_class_init(ObjectClass *oc, void *data)
 270{
 271    DeviceClass *dc = DEVICE_CLASS(oc);
 272
 273    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 274    dc->vmsd = &nextkbd_vmstate;
 275    dc->realize = nextkbd_realize;
 276    dc->reset = nextkbd_reset;
 277}
 278
 279static const TypeInfo nextkbd_info = {
 280    .name          = TYPE_NEXTKBD,
 281    .parent        = TYPE_SYS_BUS_DEVICE,
 282    .instance_size = sizeof(NextKBDState),
 283    .class_init    = nextkbd_class_init,
 284};
 285
 286static void nextkbd_register_types(void)
 287{
 288    type_register_static(&nextkbd_info);
 289}
 290
 291type_init(nextkbd_register_types)
 292