qemu/hw/input/lasips2.c
<<
>>
Prefs
   1/*
   2 * QEMU HP Lasi PS/2 interface emulation
   3 *
   4 * Copyright (c) 2019 Sven Schnelle
   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#include "qemu/osdep.h"
  25#include "qemu/log.h"
  26#include "hw/qdev-properties.h"
  27#include "hw/hw.h"
  28#include "hw/input/ps2.h"
  29#include "hw/input/lasips2.h"
  30#include "hw/sysbus.h"
  31#include "exec/hwaddr.h"
  32#include "sysemu/sysemu.h"
  33#include "trace.h"
  34#include "exec/address-spaces.h"
  35#include "migration/vmstate.h"
  36#include "hw/irq.h"
  37
  38
  39struct LASIPS2State;
  40typedef struct LASIPS2Port {
  41    struct LASIPS2State *parent;
  42    MemoryRegion reg;
  43    void *dev;
  44    uint8_t id;
  45    uint8_t control;
  46    uint8_t buf;
  47    bool loopback_rbne;
  48    bool irq;
  49} LASIPS2Port;
  50
  51typedef struct LASIPS2State {
  52    LASIPS2Port kbd;
  53    LASIPS2Port mouse;
  54    qemu_irq irq;
  55} LASIPS2State;
  56
  57static const VMStateDescription vmstate_lasips2 = {
  58    .name = "lasips2",
  59    .version_id = 0,
  60    .minimum_version_id = 0,
  61    .fields = (VMStateField[]) {
  62        VMSTATE_UINT8(kbd.control, LASIPS2State),
  63        VMSTATE_UINT8(kbd.id, LASIPS2State),
  64        VMSTATE_BOOL(kbd.irq, LASIPS2State),
  65        VMSTATE_UINT8(mouse.control, LASIPS2State),
  66        VMSTATE_UINT8(mouse.id, LASIPS2State),
  67        VMSTATE_BOOL(mouse.irq, LASIPS2State),
  68        VMSTATE_END_OF_LIST()
  69    }
  70};
  71
  72typedef enum {
  73    REG_PS2_ID = 0,
  74    REG_PS2_RCVDATA = 4,
  75    REG_PS2_CONTROL = 8,
  76    REG_PS2_STATUS = 12,
  77} lasips2_read_reg_t;
  78
  79typedef enum {
  80    REG_PS2_RESET = 0,
  81    REG_PS2_XMTDATA = 4,
  82} lasips2_write_reg_t;
  83
  84typedef enum {
  85    LASIPS2_CONTROL_ENABLE = 0x01,
  86    LASIPS2_CONTROL_LOOPBACK = 0x02,
  87    LASIPS2_CONTROL_DIAG = 0x20,
  88    LASIPS2_CONTROL_DATDIR = 0x40,
  89    LASIPS2_CONTROL_CLKDIR = 0x80,
  90} lasips2_control_reg_t;
  91
  92typedef enum {
  93    LASIPS2_STATUS_RBNE = 0x01,
  94    LASIPS2_STATUS_TBNE = 0x02,
  95    LASIPS2_STATUS_TERR = 0x04,
  96    LASIPS2_STATUS_PERR = 0x08,
  97    LASIPS2_STATUS_CMPINTR = 0x10,
  98    LASIPS2_STATUS_DATSHD = 0x40,
  99    LASIPS2_STATUS_CLKSHD = 0x80,
 100} lasips2_status_reg_t;
 101
 102static const char *artist_read_reg_name(uint64_t addr)
 103{
 104    switch (addr & 0xc) {
 105    case REG_PS2_ID:
 106        return " PS2_ID";
 107
 108    case REG_PS2_RCVDATA:
 109        return " PS2_RCVDATA";
 110
 111    case REG_PS2_CONTROL:
 112        return " PS2_CONTROL";
 113
 114    case REG_PS2_STATUS:
 115        return " PS2_STATUS";
 116
 117    default:
 118        return "";
 119    }
 120}
 121
 122static const char *artist_write_reg_name(uint64_t addr)
 123{
 124    switch (addr & 0x0c) {
 125    case REG_PS2_RESET:
 126        return " PS2_RESET";
 127
 128    case REG_PS2_XMTDATA:
 129        return " PS2_XMTDATA";
 130
 131    case REG_PS2_CONTROL:
 132        return " PS2_CONTROL";
 133
 134    default:
 135        return "";
 136    }
 137}
 138
 139static void lasips2_update_irq(LASIPS2State *s)
 140{
 141    trace_lasips2_intr(s->kbd.irq | s->mouse.irq);
 142    qemu_set_irq(s->irq, s->kbd.irq | s->mouse.irq);
 143}
 144
 145static void lasips2_reg_write(void *opaque, hwaddr addr, uint64_t val,
 146                              unsigned size)
 147{
 148    LASIPS2Port *port = opaque;
 149
 150    trace_lasips2_reg_write(size, port->id, addr,
 151                            artist_write_reg_name(addr), val);
 152
 153    switch (addr & 0xc) {
 154    case REG_PS2_CONTROL:
 155        port->control = val;
 156        break;
 157
 158    case REG_PS2_XMTDATA:
 159        if (port->control & LASIPS2_CONTROL_LOOPBACK) {
 160            port->buf = val;
 161            port->irq = true;
 162            port->loopback_rbne = true;
 163            lasips2_update_irq(port->parent);
 164            break;
 165        }
 166
 167        if (port->id) {
 168            ps2_write_mouse(port->dev, val);
 169        } else {
 170            ps2_write_keyboard(port->dev, val);
 171        }
 172        break;
 173
 174    case REG_PS2_RESET:
 175        break;
 176
 177    default:
 178        qemu_log_mask(LOG_UNIMP, "%s: unknown register 0x%02" HWADDR_PRIx "\n",
 179                      __func__, addr);
 180        break;
 181    }
 182}
 183
 184static uint64_t lasips2_reg_read(void *opaque, hwaddr addr, unsigned size)
 185{
 186    LASIPS2Port *port = opaque;
 187    uint64_t ret = 0;
 188
 189    switch (addr & 0xc) {
 190    case REG_PS2_ID:
 191        ret = port->id;
 192        break;
 193
 194    case REG_PS2_RCVDATA:
 195        if (port->control & LASIPS2_CONTROL_LOOPBACK) {
 196            port->irq = false;
 197            port->loopback_rbne = false;
 198            lasips2_update_irq(port->parent);
 199            ret = port->buf;
 200            break;
 201        }
 202
 203        ret = ps2_read_data(port->dev);
 204        break;
 205
 206    case REG_PS2_CONTROL:
 207        ret = port->control;
 208        break;
 209
 210    case REG_PS2_STATUS:
 211
 212        ret = LASIPS2_STATUS_DATSHD | LASIPS2_STATUS_CLKSHD;
 213
 214        if (port->control & LASIPS2_CONTROL_DIAG) {
 215            if (!(port->control & LASIPS2_CONTROL_DATDIR)) {
 216                ret &= ~LASIPS2_STATUS_DATSHD;
 217            }
 218
 219            if (!(port->control & LASIPS2_CONTROL_CLKDIR)) {
 220                ret &= ~LASIPS2_STATUS_CLKSHD;
 221            }
 222        }
 223
 224        if (port->control & LASIPS2_CONTROL_LOOPBACK) {
 225            if (port->loopback_rbne) {
 226                ret |= LASIPS2_STATUS_RBNE;
 227            }
 228        } else {
 229            if (!ps2_queue_empty(port->dev)) {
 230                ret |= LASIPS2_STATUS_RBNE;
 231            }
 232        }
 233
 234        if (port->parent->kbd.irq || port->parent->mouse.irq) {
 235            ret |= LASIPS2_STATUS_CMPINTR;
 236        }
 237        break;
 238
 239    default:
 240        qemu_log_mask(LOG_UNIMP, "%s: unknown register 0x%02" HWADDR_PRIx "\n",
 241                      __func__, addr);
 242        break;
 243    }
 244    trace_lasips2_reg_read(size, port->id, addr,
 245                           artist_read_reg_name(addr), ret);
 246
 247    return ret;
 248}
 249
 250static const MemoryRegionOps lasips2_reg_ops = {
 251    .read = lasips2_reg_read,
 252    .write = lasips2_reg_write,
 253    .impl = {
 254        .min_access_size = 1,
 255        .max_access_size = 4,
 256    },
 257    .endianness = DEVICE_NATIVE_ENDIAN,
 258};
 259
 260static void ps2dev_update_irq(void *opaque, int level)
 261{
 262    LASIPS2Port *port = opaque;
 263    port->irq = level;
 264    lasips2_update_irq(port->parent);
 265}
 266
 267void lasips2_init(MemoryRegion *address_space,
 268                  hwaddr base, qemu_irq irq)
 269{
 270    LASIPS2State *s;
 271
 272    s = g_malloc0(sizeof(LASIPS2State));
 273
 274    s->irq = irq;
 275    s->mouse.id = 1;
 276    s->kbd.parent = s;
 277    s->mouse.parent = s;
 278
 279    vmstate_register(NULL, base, &vmstate_lasips2, s);
 280
 281    s->kbd.dev = ps2_kbd_init(ps2dev_update_irq, &s->kbd);
 282    s->mouse.dev = ps2_mouse_init(ps2dev_update_irq, &s->mouse);
 283
 284    memory_region_init_io(&s->kbd.reg, NULL, &lasips2_reg_ops, &s->kbd,
 285                          "lasips2-kbd", 0x100);
 286    memory_region_add_subregion(address_space, base, &s->kbd.reg);
 287
 288    memory_region_init_io(&s->mouse.reg, NULL, &lasips2_reg_ops, &s->mouse,
 289                          "lasips2-mouse", 0x100);
 290    memory_region_add_subregion(address_space, base + 0x100, &s->mouse.reg);
 291}
 292