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