master
c 346 lines 8.81 KB
Raw
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/core/sysbus.h"
33 #include "hw/m68k/next-cube.h"
34 #include "standard-headers/linux/input-event-codes.h"
35 #include "ui/console.h"
36 #include "migration/vmstate.h"
37 #include "qom/object.h"
38
39 OBJECT_DECLARE_SIMPLE_TYPE(NextKBDState, NEXTKBD)
40
41 /* following definitions from next68k netbsd */
42 #define CSR_INT 0x00800000
43 #define CSR_DATA 0x00400000
44
45 #define KD_KEYMASK 0x007f
46 #define KD_DIRECTION 0x0080 /* pressed or released */
47 #define KD_CNTL 0x0100
48 #define KD_LSHIFT 0x0200
49 #define KD_RSHIFT 0x0400
50 #define KD_LCOMM 0x0800
51 #define KD_RCOMM 0x1000
52 #define KD_LALT 0x2000
53 #define KD_RALT 0x4000
54 #define KD_VALID 0x8000 /* only set for scancode keys ? */
55 #define KD_MODS 0x4f00
56
57 #define KBD_QUEUE_SIZE 256
58
59 typedef struct {
60 uint8_t data[KBD_QUEUE_SIZE];
61 int rptr, wptr, count;
62 } KBDQueue;
63
64
65 struct NextKBDState {
66 SysBusDevice sbd;
67 MemoryRegion mr;
68 QemuInputHandlerState *hs;
69 KBDQueue queue;
70 uint16_t shift;
71 };
72
73
74 /* lots of magic numbers here */
75 static uint32_t kbd_read_byte(void *opaque, hwaddr addr)
76 {
77 switch (addr & 0x3) {
78 case 0x0: /* 0xe000 */
79 return 0x80 | 0x20;
80
81 case 0x1: /* 0xe001 */
82 return 0x80 | 0x40 | 0x20 | 0x10;
83
84 case 0x2: /* 0xe002 */
85 /* returning 0x40 caused mach to hang */
86 return 0x10 | 0x2 | 0x1;
87
88 default:
89 qemu_log_mask(LOG_UNIMP, "NeXT kbd read byte %"HWADDR_PRIx"\n", addr);
90 }
91
92 return 0;
93 }
94
95 static uint32_t kbd_read_word(void *opaque, hwaddr addr)
96 {
97 qemu_log_mask(LOG_UNIMP, "NeXT kbd read word %"HWADDR_PRIx"\n", addr);
98 return 0;
99 }
100
101 /* even more magic numbers */
102 static uint32_t kbd_read_long(void *opaque, hwaddr addr)
103 {
104 int key = 0;
105 NextKBDState *s = NEXTKBD(opaque);
106 KBDQueue *q = &s->queue;
107
108 switch (addr & 0xf) {
109 case 0x0: /* 0xe000 */
110 return 0xA0F09300;
111
112 case 0x8: /* 0xe008 */
113 /* get keycode from buffer */
114 if (q->count > 0) {
115 key = q->data[q->rptr];
116 if (++q->rptr == KBD_QUEUE_SIZE) {
117 q->rptr = 0;
118 }
119
120 q->count--;
121
122 if (s->shift) {
123 key |= s->shift;
124 }
125
126 if (key & 0x80) {
127 return 0;
128 } else {
129 return 0x10000000 | KD_VALID | key;
130 }
131 } else {
132 return 0;
133 }
134
135 default:
136 qemu_log_mask(LOG_UNIMP, "NeXT kbd read long %"HWADDR_PRIx"\n", addr);
137 return 0;
138 }
139 }
140
141 static uint64_t kbd_readfn(void *opaque, hwaddr addr, unsigned size)
142 {
143 switch (size) {
144 case 1:
145 return kbd_read_byte(opaque, addr);
146 case 2:
147 return kbd_read_word(opaque, addr);
148 case 4:
149 return kbd_read_long(opaque, addr);
150 default:
151 g_assert_not_reached();
152 }
153 }
154
155 static void kbd_writefn(void *opaque, hwaddr addr, uint64_t value,
156 unsigned size)
157 {
158 qemu_log_mask(LOG_UNIMP, "NeXT kbd write: size=%u addr=0x%"HWADDR_PRIx
159 "val=0x%"PRIx64"\n", size, addr, value);
160 }
161
162 static const MemoryRegionOps kbd_ops = {
163 .read = kbd_readfn,
164 .write = kbd_writefn,
165 .valid.min_access_size = 1,
166 .valid.max_access_size = 4,
167 .endianness = DEVICE_BIG_ENDIAN,
168 };
169
170 static const int linux_to_nextkbd_keycode[] = {
171 [KEY_ESC] = 0x49,
172 [KEY_1] = 0x4a,
173 [KEY_2] = 0x4b,
174 [KEY_3] = 0x4c,
175 [KEY_4] = 0x4d,
176 [KEY_5] = 0x50,
177 [KEY_6] = 0x4f,
178 [KEY_7] = 0x4e,
179 [KEY_8] = 0x1e,
180 [KEY_9] = 0x1f,
181 [KEY_0] = 0x20,
182 [KEY_MINUS] = 0x1d,
183 [KEY_EQUAL] = 0x1c,
184 [KEY_BACKSPACE] = 0x1b,
185
186 [KEY_Q] = 0x42,
187 [KEY_W] = 0x43,
188 [KEY_E] = 0x44,
189 [KEY_R] = 0x45,
190 [KEY_T] = 0x48,
191 [KEY_Y] = 0x47,
192 [KEY_U] = 0x46,
193 [KEY_I] = 0x06,
194 [KEY_O] = 0x07,
195 [KEY_P] = 0x08,
196 [KEY_ENTER] = 0x2a,
197 [KEY_A] = 0x39,
198 [KEY_S] = 0x3a,
199
200 [KEY_D] = 0x3b,
201 [KEY_F] = 0x3c,
202 [KEY_G] = 0x3d,
203 [KEY_H] = 0x40,
204 [KEY_J] = 0x3f,
205 [KEY_K] = 0x3e,
206 [KEY_L] = 0x2d,
207 [KEY_SEMICOLON] = 0x2c,
208 [KEY_APOSTROPHE] = 0x2b,
209 [KEY_GRAVE] = 0x26,
210 [KEY_Z] = 0x31,
211 [KEY_X] = 0x32,
212 [KEY_C] = 0x33,
213 [KEY_V] = 0x34,
214
215 [KEY_B] = 0x35,
216 [KEY_N] = 0x37,
217 [KEY_M] = 0x36,
218 [KEY_COMMA] = 0x2e,
219 [KEY_DOT] = 0x2f,
220 [KEY_SLASH] = 0x30,
221
222 [KEY_SPACE] = 0x38,
223 };
224
225 static void nextkbd_put_keycode(NextKBDState *s, int keycode)
226 {
227 KBDQueue *q = &s->queue;
228
229 if (q->count >= KBD_QUEUE_SIZE) {
230 return;
231 }
232
233 q->data[q->wptr] = keycode;
234 if (++q->wptr == KBD_QUEUE_SIZE) {
235 q->wptr = 0;
236 }
237
238 q->count++;
239
240 /*
241 * might need to actually trigger the NeXT irq, but as the keyboard works
242 * at the moment, I'll worry about it later
243 */
244 /* s->update_irq(s->update_arg, 1); */
245 }
246
247 static void nextkbd_event(DeviceState *dev, QemuConsole *src,
248 QemuInputEvent *evt)
249 {
250 NextKBDState *s = NEXTKBD(dev);
251 int keycode;
252
253 if (evt->key.key >= ARRAY_SIZE(linux_to_nextkbd_keycode)) {
254 return;
255 }
256
257 /* Shift key currently has no keycode, so handle separately */
258 if (evt->key.key == KEY_LEFTSHIFT) {
259 if (evt->key.down) {
260 s->shift |= KD_LSHIFT;
261 } else {
262 s->shift &= ~KD_LSHIFT;
263 }
264 }
265
266 if (evt->key.key == KEY_RIGHTSHIFT) {
267 if (evt->key.down) {
268 s->shift |= KD_RSHIFT;
269 } else {
270 s->shift &= ~KD_RSHIFT;
271 }
272 }
273
274 keycode = linux_to_nextkbd_keycode[evt->key.key];
275 if (!keycode) {
276 return;
277 }
278
279 /* If key release event, create keyboard break code */
280 if (!evt->key.down) {
281 keycode |= 0x80;
282 }
283
284 nextkbd_put_keycode(s, keycode);
285 }
286
287 static const QemuInputHandler nextkbd_handler = {
288 .name = "QEMU NeXT Keyboard",
289 .mask = INPUT_EVENT_MASK_KEY,
290 .event = nextkbd_event,
291 };
292
293 static void nextkbd_reset(DeviceState *dev)
294 {
295 NextKBDState *nks = NEXTKBD(dev);
296
297 memset(&nks->queue, 0, sizeof(KBDQueue));
298 nks->shift = 0;
299 }
300
301 static void nextkbd_realize(DeviceState *dev, Error **errp)
302 {
303 NextKBDState *s = NEXTKBD(dev);
304
305 memory_region_init_io(&s->mr, OBJECT(dev), &kbd_ops, s, "next.kbd", 0x1000);
306 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
307
308 s->hs = qemu_input_handler_register(dev, &nextkbd_handler);
309 }
310
311 static void nextkbd_unrealize(DeviceState *dev)
312 {
313 NextKBDState *s = NEXTKBD(dev);
314
315 g_clear_pointer(&s->hs, qemu_input_handler_unregister);
316 }
317
318 static const VMStateDescription nextkbd_vmstate = {
319 .name = TYPE_NEXTKBD,
320 .unmigratable = 1, /* TODO: Implement this when m68k CPU is migratable */
321 };
322
323 static void nextkbd_class_init(ObjectClass *oc, const void *data)
324 {
325 DeviceClass *dc = DEVICE_CLASS(oc);
326
327 set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
328 dc->vmsd = &nextkbd_vmstate;
329 dc->realize = nextkbd_realize;
330 dc->unrealize = nextkbd_unrealize;
331 device_class_set_legacy_reset(dc, nextkbd_reset);
332 }
333
334 static const TypeInfo nextkbd_info = {
335 .name = TYPE_NEXTKBD,
336 .parent = TYPE_SYS_BUS_DEVICE,
337 .instance_size = sizeof(NextKBDState),
338 .class_init = nextkbd_class_init,
339 };
340
341 static void nextkbd_register_types(void)
342 {
343 type_register_static(&nextkbd_info);
344 }
345
346 type_init(nextkbd_register_types)