Merge tag 'v9.0.0-rc3'
[qemu/ar7.git] / hw / arm / syborg_keyboard.c
blob2bd153d2c2e26b1bc9db88b110d996b9ee0d1542
1 /*
2 * Syborg keyboard controller.
4 * Copyright (c) 2008 CodeSourcery
5 * Copyright (c) 2010, 2013 Stefan Weil
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
26 #include "qemu/osdep.h"
27 #include "hw/sysbus.h"
28 #include "ui/console.h"
29 #include "syborg.h"
31 //#define DEBUG_SYBORG_KEYBOARD
33 #ifdef DEBUG_SYBORG_KEYBOARD
34 #define DPRINTF(fmt, ...) \
35 do { printf("syborg_keyboard: " fmt , ##args); } while (0)
36 #define BADF(fmt, ...) \
37 do { fprintf(stderr, "syborg_keyboard: error: " fmt , ## __VA_ARGS__); \
38 exit(1);} while (0)
39 #else
40 #define DPRINTF(fmt, ...) do {} while(0)
41 #define BADF(fmt, ...) \
42 do { fprintf(stderr, "syborg_keyboard: error: " fmt , ## __VA_ARGS__); \
43 } while (0)
44 #endif
46 enum {
47 KBD_ID = 0,
48 KBD_DATA = 1,
49 KBD_FIFO_COUNT = 2,
50 KBD_INT_ENABLE = 3,
51 KBD_FIFO_SIZE = 4
54 typedef struct {
55 SysBusDevice busdev;
56 MemoryRegion iomem;
57 uint32_t int_enabled;
58 int extension_bit;
59 uint32_t fifo_size;
60 uint32_t *key_fifo;
61 uint32_t read_pos, read_count;
62 qemu_irq irq;
63 } SyborgKeyboardState;
65 static void syborg_keyboard_update(SyborgKeyboardState *s)
67 int level = s->read_count && s->int_enabled;
68 DPRINTF("Update IRQ %d\n", level);
69 qemu_set_irq(s->irq, level);
72 static uint64_t syborg_keyboard_read(void *opaque, hwaddr offset,
73 unsigned size)
75 SyborgKeyboardState *s = (SyborgKeyboardState *)opaque;
76 int c;
78 DPRINTF("reg read %d\n", (int)offset);
79 offset &= 0xfff;
80 switch (offset >> 2) {
81 case KBD_ID:
82 return SYBORG_ID_KEYBOARD;
83 case KBD_FIFO_COUNT:
84 return s->read_count;
85 case KBD_DATA:
86 if (s->read_count == 0) {
87 c = -1;
88 DPRINTF("FIFO underflow\n");
89 } else {
90 c = s->key_fifo[s->read_pos];
91 DPRINTF("FIFO read 0x%x\n", c);
92 s->read_count--;
93 s->read_pos++;
94 if (s->read_pos == s->fifo_size)
95 s->read_pos = 0;
97 syborg_keyboard_update(s);
98 return c;
99 case KBD_INT_ENABLE:
100 return s->int_enabled;
101 case KBD_FIFO_SIZE:
102 return s->fifo_size;
103 default:
104 cpu_abort(cpu_single_env, "syborg_keyboard_read: Bad offset %x\n",
105 (int)offset);
106 return 0;
110 static void syborg_keyboard_write(void *opaque, hwaddr offset,
111 uint64_t value, unsigned size)
113 SyborgKeyboardState *s = (SyborgKeyboardState *)opaque;
115 DPRINTF("reg write %d\n", (int)offset);
116 offset &= 0xfff;
117 switch (offset >> 2) {
118 case KBD_INT_ENABLE:
119 s->int_enabled = value;
120 syborg_keyboard_update(s);
121 break;
122 default:
123 cpu_abort(cpu_single_env, "syborg_keyboard_write: Bad offset %x\n",
124 (int)offset);
128 static const MemoryRegionOps syborg_keyboard_ops = {
129 .read = syborg_keyboard_read,
130 .write = syborg_keyboard_write,
131 .endianness = DEVICE_NATIVE_ENDIAN,
134 static void syborg_keyboard_event(void *opaque, int keycode)
136 SyborgKeyboardState *s = (SyborgKeyboardState *)opaque;
137 int slot;
138 uint32_t val;
140 /* Strip off 0xe0 prefixes and reconstruct the full scancode. */
141 if (keycode == 0xe0 && !s->extension_bit) {
142 DPRINTF("Extension bit\n");
143 s->extension_bit = 0x80;
144 return;
146 val = (keycode & 0x7f) | s->extension_bit;
147 if (keycode & 0x80)
148 val |= 0x80000000u;
149 s->extension_bit = 0;
151 DPRINTF("FIFO push 0x%x\n", val);
152 slot = s->read_pos + s->read_count;
153 if (slot >= s->fifo_size)
154 slot -= s->fifo_size;
156 if (s->read_count < s->fifo_size) {
157 s->read_count++;
158 s->key_fifo[slot] = val;
159 } else {
160 fprintf(stderr, "syborg_keyboard error! FIFO overflow\n");
163 syborg_keyboard_update(s);
166 static const VMStateDescription vmstate_syborg_keyboard = {
167 .name = "syborg_keyboard",
168 .version_id = 1,
169 .minimum_version_id = 1,
170 .minimum_version_id_old = 1,
171 .fields = (VMStateField[]) {
172 VMSTATE_UINT32_EQUAL(fifo_size, SyborgKeyboardState),
173 VMSTATE_UINT32(int_enabled, SyborgKeyboardState),
174 VMSTATE_UINT32(read_pos, SyborgKeyboardState),
175 VMSTATE_UINT32(read_count, SyborgKeyboardState),
176 VMSTATE_VARRAY_UINT32(key_fifo, SyborgKeyboardState, fifo_size, 1,
177 vmstate_info_uint32, uint32),
178 VMSTATE_END_OF_LIST()
182 static int syborg_keyboard_init(SysBusDevice *sbd)
184 DeviceState *dev = DEVICE(sbd);
185 SyborgKeyboardState *s = SYBORG_KEYBOARD(dev);
187 sysbus_init_irq(dev, &s->irq);
188 memory_region_init_io(&s->iomem, &syborg_keyboard_ops, s,
189 "keyboard", 0x1000);
190 sysbus_init_mmio(sbd, &s->iomem);
191 if (s->fifo_size <= 0) {
192 fprintf(stderr, "syborg_keyboard: fifo too small\n");
193 s->fifo_size = 16;
195 s->key_fifo = g_malloc0(s->fifo_size * sizeof(s->key_fifo[0]));
197 qemu_add_kbd_event_handler(syborg_keyboard_event, s);
199 vmstate_register(&dev->qdev, -1, &vmstate_syborg_keyboard, s);
200 return 0;
203 static Property syborg_keyboard_properties[] = {
204 DEFINE_PROP_UINT32("fifo-size", SyborgKeyboardState, fifo_size, 16),
205 DEFINE_PROP_END_OF_LIST()
208 static void syborg_keyboard_class_init(ObjectClass *klass, void *data)
210 DeviceClass *dc = DEVICE_CLASS(klass);
211 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
212 dc->props = syborg_keyboard_properties;
213 k->init = syborg_keyboard_init;
216 static const TypeInfo syborg_keyboard_info = {
217 .name = "syborg,keyboard",
218 .parent = TYPE_SYS_BUS_DEVICE,
219 .instance_size = sizeof(SyborgKeyboardState),
220 .class_init = syborg_keyboard_class_init
223 static void syborg_keyboard_register_types(void)
225 type_register_static(&syborg_keyboard_info);
228 type_init(syborg_keyboard_register_types)