xref: /openbmc/qemu/hw/m68k/next-kbd.c (revision 8063396bf3459a810d24e3efd6110b8480f0de5b)
1c8e8bc85SThomas Huth /*
2c8e8bc85SThomas Huth  * QEMU NeXT Keyboard/Mouse emulation
3c8e8bc85SThomas Huth  *
4c8e8bc85SThomas Huth  * Copyright (c) 2011 Bryce Lanham
5c8e8bc85SThomas Huth  *
6c8e8bc85SThomas Huth  * Permission is hereby granted, free of charge, to any person obtaining a copy
7c8e8bc85SThomas Huth  * of this software and associated documentation files (the "Software"), to deal
8c8e8bc85SThomas Huth  * in the Software without restriction, including without limitation the rights
9c8e8bc85SThomas Huth  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10c8e8bc85SThomas Huth  * copies of the Software, and to permit persons to whom the Software is
11c8e8bc85SThomas Huth  * furnished to do so, subject to the following conditions:
12c8e8bc85SThomas Huth  *
13c8e8bc85SThomas Huth  * The above copyright notice and this permission notice shall be included in
14c8e8bc85SThomas Huth  * all copies or substantial portions of the Software.
15c8e8bc85SThomas Huth  *
16c8e8bc85SThomas Huth  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17c8e8bc85SThomas Huth  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18c8e8bc85SThomas Huth  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19c8e8bc85SThomas Huth  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20c8e8bc85SThomas Huth  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21c8e8bc85SThomas Huth  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22c8e8bc85SThomas Huth  * THE SOFTWARE.
23c8e8bc85SThomas Huth  */
24c8e8bc85SThomas Huth 
25c8e8bc85SThomas Huth /*
26c8e8bc85SThomas Huth  * This is admittedly hackish, but works well enough for basic input. Mouse
27c8e8bc85SThomas Huth  * support will be added once we can boot something that needs the mouse.
28c8e8bc85SThomas Huth  */
29c8e8bc85SThomas Huth 
30c8e8bc85SThomas Huth #include "qemu/osdep.h"
31c8e8bc85SThomas Huth #include "qemu/log.h"
32c8e8bc85SThomas Huth #include "exec/address-spaces.h"
33c8e8bc85SThomas Huth #include "hw/hw.h"
34c8e8bc85SThomas Huth #include "hw/sysbus.h"
35c8e8bc85SThomas Huth #include "hw/m68k/next-cube.h"
36c8e8bc85SThomas Huth #include "ui/console.h"
37c8e8bc85SThomas Huth #include "sysemu/sysemu.h"
38c8e8bc85SThomas Huth #include "migration/vmstate.h"
39db1015e9SEduardo Habkost #include "qom/object.h"
40c8e8bc85SThomas Huth 
41*8063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(NextKBDState, NEXTKBD)
42c8e8bc85SThomas Huth 
43c8e8bc85SThomas Huth /* following defintions from next68k netbsd */
44c8e8bc85SThomas Huth #define CSR_INT 0x00800000
45c8e8bc85SThomas Huth #define CSR_DATA 0x00400000
46c8e8bc85SThomas Huth 
47c8e8bc85SThomas Huth #define KD_KEYMASK    0x007f
48c8e8bc85SThomas Huth #define KD_DIRECTION  0x0080 /* pressed or released */
49c8e8bc85SThomas Huth #define KD_CNTL       0x0100
50c8e8bc85SThomas Huth #define KD_LSHIFT     0x0200
51c8e8bc85SThomas Huth #define KD_RSHIFT     0x0400
52c8e8bc85SThomas Huth #define KD_LCOMM      0x0800
53c8e8bc85SThomas Huth #define KD_RCOMM      0x1000
54c8e8bc85SThomas Huth #define KD_LALT       0x2000
55c8e8bc85SThomas Huth #define KD_RALT       0x4000
56c8e8bc85SThomas Huth #define KD_VALID      0x8000 /* only set for scancode keys ? */
57c8e8bc85SThomas Huth #define KD_MODS       0x4f00
58c8e8bc85SThomas Huth 
59c8e8bc85SThomas Huth #define KBD_QUEUE_SIZE 256
60c8e8bc85SThomas Huth 
61c8e8bc85SThomas Huth typedef struct {
62c8e8bc85SThomas Huth     uint8_t data[KBD_QUEUE_SIZE];
63c8e8bc85SThomas Huth     int rptr, wptr, count;
64c8e8bc85SThomas Huth } KBDQueue;
65c8e8bc85SThomas Huth 
66c8e8bc85SThomas Huth 
67db1015e9SEduardo Habkost struct NextKBDState {
68c8e8bc85SThomas Huth     SysBusDevice sbd;
69c8e8bc85SThomas Huth     MemoryRegion mr;
70c8e8bc85SThomas Huth     KBDQueue queue;
71c8e8bc85SThomas Huth     uint16_t shift;
72db1015e9SEduardo Habkost };
73c8e8bc85SThomas Huth 
74c8e8bc85SThomas Huth static void queue_code(void *opaque, int code);
75c8e8bc85SThomas Huth 
76c8e8bc85SThomas Huth /* lots of magic numbers here */
77c8e8bc85SThomas Huth static uint32_t kbd_read_byte(void *opaque, hwaddr addr)
78c8e8bc85SThomas Huth {
79c8e8bc85SThomas Huth     switch (addr & 0x3) {
80c8e8bc85SThomas Huth     case 0x0:   /* 0xe000 */
81c8e8bc85SThomas Huth         return 0x80 | 0x20;
82c8e8bc85SThomas Huth 
83c8e8bc85SThomas Huth     case 0x1:   /* 0xe001 */
84c8e8bc85SThomas Huth         return 0x80 | 0x40 | 0x20 | 0x10;
85c8e8bc85SThomas Huth 
86c8e8bc85SThomas Huth     case 0x2:   /* 0xe002 */
87c8e8bc85SThomas Huth         /* returning 0x40 caused mach to hang */
88c8e8bc85SThomas Huth         return 0x10 | 0x2 | 0x1;
89c8e8bc85SThomas Huth 
90c8e8bc85SThomas Huth     default:
91c8e8bc85SThomas Huth         qemu_log_mask(LOG_UNIMP, "NeXT kbd read byte %"HWADDR_PRIx"\n", addr);
92c8e8bc85SThomas Huth     }
93c8e8bc85SThomas Huth 
94c8e8bc85SThomas Huth     return 0;
95c8e8bc85SThomas Huth }
96c8e8bc85SThomas Huth 
97c8e8bc85SThomas Huth static uint32_t kbd_read_word(void *opaque, hwaddr addr)
98c8e8bc85SThomas Huth {
99c8e8bc85SThomas Huth     qemu_log_mask(LOG_UNIMP, "NeXT kbd read word %"HWADDR_PRIx"\n", addr);
100c8e8bc85SThomas Huth     return 0;
101c8e8bc85SThomas Huth }
102c8e8bc85SThomas Huth 
103c8e8bc85SThomas Huth /* even more magic numbers */
104c8e8bc85SThomas Huth static uint32_t kbd_read_long(void *opaque, hwaddr addr)
105c8e8bc85SThomas Huth {
106c8e8bc85SThomas Huth     int key = 0;
107c8e8bc85SThomas Huth     NextKBDState *s = NEXTKBD(opaque);
108c8e8bc85SThomas Huth     KBDQueue *q = &s->queue;
109c8e8bc85SThomas Huth 
110c8e8bc85SThomas Huth     switch (addr & 0xf) {
111c8e8bc85SThomas Huth     case 0x0:   /* 0xe000 */
112c8e8bc85SThomas Huth         return 0xA0F09300;
113c8e8bc85SThomas Huth 
114c8e8bc85SThomas Huth     case 0x8:   /* 0xe008 */
115c8e8bc85SThomas Huth         /* get keycode from buffer */
116c8e8bc85SThomas Huth         if (q->count > 0) {
117c8e8bc85SThomas Huth             key = q->data[q->rptr];
118c8e8bc85SThomas Huth             if (++q->rptr == KBD_QUEUE_SIZE) {
119c8e8bc85SThomas Huth                 q->rptr = 0;
120c8e8bc85SThomas Huth             }
121c8e8bc85SThomas Huth 
122c8e8bc85SThomas Huth             q->count--;
123c8e8bc85SThomas Huth 
124c8e8bc85SThomas Huth             if (s->shift) {
125c8e8bc85SThomas Huth                 key |= s->shift;
126c8e8bc85SThomas Huth             }
127c8e8bc85SThomas Huth 
128c8e8bc85SThomas Huth             if (key & 0x80) {
129c8e8bc85SThomas Huth                 return 0;
130c8e8bc85SThomas Huth             } else {
131c8e8bc85SThomas Huth                 return 0x10000000 | KD_VALID | key;
132c8e8bc85SThomas Huth             }
133c8e8bc85SThomas Huth         } else {
134c8e8bc85SThomas Huth             return 0;
135c8e8bc85SThomas Huth         }
136c8e8bc85SThomas Huth 
137c8e8bc85SThomas Huth     default:
138c8e8bc85SThomas Huth         qemu_log_mask(LOG_UNIMP, "NeXT kbd read long %"HWADDR_PRIx"\n", addr);
139c8e8bc85SThomas Huth         return 0;
140c8e8bc85SThomas Huth     }
141c8e8bc85SThomas Huth }
142c8e8bc85SThomas Huth 
143c8e8bc85SThomas Huth static uint64_t kbd_readfn(void *opaque, hwaddr addr, unsigned size)
144c8e8bc85SThomas Huth {
145c8e8bc85SThomas Huth     switch (size) {
146c8e8bc85SThomas Huth     case 1:
147c8e8bc85SThomas Huth         return kbd_read_byte(opaque, addr);
148c8e8bc85SThomas Huth     case 2:
149c8e8bc85SThomas Huth         return kbd_read_word(opaque, addr);
150c8e8bc85SThomas Huth     case 4:
151c8e8bc85SThomas Huth         return kbd_read_long(opaque, addr);
152c8e8bc85SThomas Huth     default:
153c8e8bc85SThomas Huth         g_assert_not_reached();
154c8e8bc85SThomas Huth     }
155c8e8bc85SThomas Huth }
156c8e8bc85SThomas Huth 
157c8e8bc85SThomas Huth static void kbd_writefn(void *opaque, hwaddr addr, uint64_t value,
158c8e8bc85SThomas Huth                         unsigned size)
159c8e8bc85SThomas Huth {
160c8e8bc85SThomas Huth     qemu_log_mask(LOG_UNIMP, "NeXT kbd write: size=%u addr=0x%"HWADDR_PRIx
161c8e8bc85SThomas Huth                   "val=0x%"PRIx64"\n", size, addr, value);
162c8e8bc85SThomas Huth }
163c8e8bc85SThomas Huth 
164c8e8bc85SThomas Huth static const MemoryRegionOps kbd_ops = {
165c8e8bc85SThomas Huth     .read = kbd_readfn,
166c8e8bc85SThomas Huth     .write = kbd_writefn,
167c8e8bc85SThomas Huth     .valid.min_access_size = 1,
168c8e8bc85SThomas Huth     .valid.max_access_size = 4,
169c8e8bc85SThomas Huth     .endianness = DEVICE_NATIVE_ENDIAN,
170c8e8bc85SThomas Huth };
171c8e8bc85SThomas Huth 
172c8e8bc85SThomas Huth static void nextkbd_event(void *opaque, int ch)
173c8e8bc85SThomas Huth {
174c8e8bc85SThomas Huth     /*
175c8e8bc85SThomas Huth      * Will want to set vars for caps/num lock
176c8e8bc85SThomas Huth      * if (ch & 0x80) -> key release
177c8e8bc85SThomas Huth      * there's also e0 escaped scancodes that might need to be handled
178c8e8bc85SThomas Huth      */
179c8e8bc85SThomas Huth     queue_code(opaque, ch);
180c8e8bc85SThomas Huth }
181c8e8bc85SThomas Huth 
182c8e8bc85SThomas Huth static const unsigned char next_keycodes[128] = {
183c8e8bc85SThomas Huth     0x00, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x50, 0x4F,
184c8e8bc85SThomas Huth     0x4E, 0x1E, 0x1F, 0x20, 0x1D, 0x1C, 0x1B, 0x00,
185c8e8bc85SThomas Huth     0x42, 0x43, 0x44, 0x45, 0x48, 0x47, 0x46, 0x06,
186c8e8bc85SThomas Huth     0x07, 0x08, 0x00, 0x00, 0x2A, 0x00, 0x39, 0x3A,
187c8e8bc85SThomas Huth     0x3B, 0x3C, 0x3D, 0x40, 0x3F, 0x3E, 0x2D, 0x2C,
188c8e8bc85SThomas Huth     0x2B, 0x26, 0x00, 0x00, 0x31, 0x32, 0x33, 0x34,
189c8e8bc85SThomas Huth     0x35, 0x37, 0x36, 0x2e, 0x2f, 0x30, 0x00, 0x00,
190c8e8bc85SThomas Huth     0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
191c8e8bc85SThomas Huth     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
192c8e8bc85SThomas Huth     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
193c8e8bc85SThomas Huth     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
194c8e8bc85SThomas Huth     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
195c8e8bc85SThomas Huth };
196c8e8bc85SThomas Huth 
197c8e8bc85SThomas Huth static void queue_code(void *opaque, int code)
198c8e8bc85SThomas Huth {
199c8e8bc85SThomas Huth     NextKBDState *s = NEXTKBD(opaque);
200c8e8bc85SThomas Huth     KBDQueue *q = &s->queue;
201c8e8bc85SThomas Huth     int key = code & KD_KEYMASK;
202c8e8bc85SThomas Huth     int release = code & 0x80;
203c8e8bc85SThomas Huth     static int ext;
204c8e8bc85SThomas Huth 
205c8e8bc85SThomas Huth     if (code == 0xE0) {
206c8e8bc85SThomas Huth         ext = 1;
207c8e8bc85SThomas Huth     }
208c8e8bc85SThomas Huth 
209c8e8bc85SThomas Huth     if (code == 0x2A || code == 0x1D || code == 0x36) {
210c8e8bc85SThomas Huth         if (code == 0x2A) {
211c8e8bc85SThomas Huth             s->shift = KD_LSHIFT;
212c8e8bc85SThomas Huth         } else if (code == 0x36) {
213c8e8bc85SThomas Huth             s->shift = KD_RSHIFT;
214c8e8bc85SThomas Huth             ext = 0;
215c8e8bc85SThomas Huth         } else if (code == 0x1D && !ext) {
216c8e8bc85SThomas Huth             s->shift = KD_LCOMM;
217c8e8bc85SThomas Huth         } else if (code == 0x1D && ext) {
218c8e8bc85SThomas Huth             ext = 0;
219c8e8bc85SThomas Huth             s->shift = KD_RCOMM;
220c8e8bc85SThomas Huth         }
221c8e8bc85SThomas Huth         return;
222c8e8bc85SThomas Huth     } else if (code == (0x2A | 0x80) || code == (0x1D | 0x80) ||
223c8e8bc85SThomas Huth                code == (0x36 | 0x80)) {
224c8e8bc85SThomas Huth         s->shift = 0;
225c8e8bc85SThomas Huth         return;
226c8e8bc85SThomas Huth     }
227c8e8bc85SThomas Huth 
228c8e8bc85SThomas Huth     if (q->count >= KBD_QUEUE_SIZE) {
229c8e8bc85SThomas Huth         return;
230c8e8bc85SThomas Huth     }
231c8e8bc85SThomas Huth 
232c8e8bc85SThomas Huth     q->data[q->wptr] = next_keycodes[key] | release;
233c8e8bc85SThomas Huth 
234c8e8bc85SThomas Huth     if (++q->wptr == KBD_QUEUE_SIZE) {
235c8e8bc85SThomas Huth         q->wptr = 0;
236c8e8bc85SThomas Huth     }
237c8e8bc85SThomas Huth 
238c8e8bc85SThomas Huth     q->count++;
239c8e8bc85SThomas Huth 
240c8e8bc85SThomas Huth     /*
241c8e8bc85SThomas Huth      * might need to actually trigger the NeXT irq, but as the keyboard works
242c8e8bc85SThomas Huth      * at the moment, I'll worry about it later
243c8e8bc85SThomas Huth      */
244c8e8bc85SThomas Huth     /* s->update_irq(s->update_arg, 1); */
245c8e8bc85SThomas Huth }
246c8e8bc85SThomas Huth 
247c8e8bc85SThomas Huth static void nextkbd_reset(DeviceState *dev)
248c8e8bc85SThomas Huth {
249c8e8bc85SThomas Huth     NextKBDState *nks = NEXTKBD(dev);
250c8e8bc85SThomas Huth 
251c8e8bc85SThomas Huth     memset(&nks->queue, 0, sizeof(KBDQueue));
252c8e8bc85SThomas Huth     nks->shift = 0;
253c8e8bc85SThomas Huth }
254c8e8bc85SThomas Huth 
255c8e8bc85SThomas Huth static void nextkbd_realize(DeviceState *dev, Error **errp)
256c8e8bc85SThomas Huth {
257c8e8bc85SThomas Huth     NextKBDState *s = NEXTKBD(dev);
258c8e8bc85SThomas Huth 
259c8e8bc85SThomas Huth     memory_region_init_io(&s->mr, OBJECT(dev), &kbd_ops, s, "next.kbd", 0x1000);
260c8e8bc85SThomas Huth     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
261c8e8bc85SThomas Huth 
262c8e8bc85SThomas Huth     qemu_add_kbd_event_handler(nextkbd_event, s);
263c8e8bc85SThomas Huth }
264c8e8bc85SThomas Huth 
265c8e8bc85SThomas Huth static const VMStateDescription nextkbd_vmstate = {
266c8e8bc85SThomas Huth     .name = TYPE_NEXTKBD,
267c8e8bc85SThomas Huth     .unmigratable = 1,    /* TODO: Implement this when m68k CPU is migratable */
268c8e8bc85SThomas Huth };
269c8e8bc85SThomas Huth 
270c8e8bc85SThomas Huth static void nextkbd_class_init(ObjectClass *oc, void *data)
271c8e8bc85SThomas Huth {
272c8e8bc85SThomas Huth     DeviceClass *dc = DEVICE_CLASS(oc);
273c8e8bc85SThomas Huth 
274c8e8bc85SThomas Huth     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
275c8e8bc85SThomas Huth     dc->vmsd = &nextkbd_vmstate;
276c8e8bc85SThomas Huth     dc->realize = nextkbd_realize;
277c8e8bc85SThomas Huth     dc->reset = nextkbd_reset;
278c8e8bc85SThomas Huth }
279c8e8bc85SThomas Huth 
280c8e8bc85SThomas Huth static const TypeInfo nextkbd_info = {
281c8e8bc85SThomas Huth     .name          = TYPE_NEXTKBD,
282c8e8bc85SThomas Huth     .parent        = TYPE_SYS_BUS_DEVICE,
283c8e8bc85SThomas Huth     .instance_size = sizeof(NextKBDState),
284c8e8bc85SThomas Huth     .class_init    = nextkbd_class_init,
285c8e8bc85SThomas Huth };
286c8e8bc85SThomas Huth 
287c8e8bc85SThomas Huth static void nextkbd_register_types(void)
288c8e8bc85SThomas Huth {
289c8e8bc85SThomas Huth     type_register_static(&nextkbd_info);
290c8e8bc85SThomas Huth }
291c8e8bc85SThomas Huth 
292c8e8bc85SThomas Huth type_init(nextkbd_register_types)
293