2 * Driver for HID devices ported from CoreBoot
4 * Copyright (C) 2014 BALATON Zoltan
6 * This file was part of the libpayload project.
8 * Copyright (C) 2008-2010 coresystems GmbH
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include "libopenbios/bindings.h"
36 #include <libc/string.h>
37 #include "libc/byteorder.h"
38 #include "libc/vsprintf.h"
39 #include "drivers/usb.h"
42 DECLARE_UNNAMED_NODE(usb_kbd, INSTALL_OPEN, sizeof(int));
45 keyboard_open(int *idx)
51 keyboard_close(int *idx)
55 static void keyboard_read(void);
57 NODE_METHODS( usb_kbd ) = {
58 { "open", keyboard_open },
59 { "close", keyboard_close },
60 { "read", keyboard_read },
63 #ifdef CONFIG_USB_DEBUG
64 static const char *boot_protos[3] = { "(none)", "keyboard", "mouse" };
66 typedef enum { hid_proto_boot = 0, hid_proto_report = 1 } hid_proto;
67 enum { GET_REPORT = 0x1, GET_IDLE = 0x2, GET_PROTOCOL = 0x3, SET_REPORT =
68 0x9, SET_IDLE = 0xa, SET_PROTOCOL = 0xb
78 } usb_hid_keyboard_event_t;
82 hid_descriptor_t *descriptor;
84 usb_hid_keyboard_event_t previous;
89 #define HID_INST(dev) ((usbhid_inst_t*)(dev)->data)
92 usb_hid_destroy (usbdev_t *dev)
94 if (HID_INST(dev)->queue) {
96 for (i = 0; i <= dev->num_endp; i++) {
97 if (dev->endpoints[i].endpoint == 0)
99 if (dev->endpoints[i].type != INTERRUPT)
101 if (dev->endpoints[i].direction != IN)
105 dev->controller->destroy_intr_queue(
106 &dev->endpoints[i], HID_INST(dev)->queue);
107 HID_INST(dev)->queue = NULL;
112 /* keybuffer is global to all USB keyboards */
114 #define KEYBOARD_BUFFER_SIZE 16
115 static short keybuffer[KEYBOARD_BUFFER_SIZE];
117 const char *countries[36][2] = {
121 { "Canadian-Bilingual", "ca" },
122 { "Canadian-French", "ca" },
123 { "Czech Republic", "cz" },
131 { "International (ISO)", "iso" },
133 { "Japan (Katakana)", "jp" },
135 { "Latin American", "us" },
136 { "Netherlands/Dutch", "nl" },
137 { "Norwegian", "no" },
138 { "Persian (Farsi)", "ir" },
140 { "Portuguese", "pt" },
142 { "Slovakia", "sl" },
145 { "Swiss/French", "ch" },
146 { "Swiss/German", "ch" },
147 { "Switzerland", "ch" },
149 { "Turkish-Q", "tr" },
152 { "Yugoslavia", "yu" },
153 { "Turkish-F", "tr" },
154 /* 36 - 255: Reserved */
161 const short map[4][0x80];
164 static const struct layout_maps *map;
166 #define KEY_BREAK 0x101 /* Not on PC KBD */
167 #define KEY_DOWN 0x102 /* Down arrow key */
168 #define KEY_UP 0x103 /* Up arrow key */
169 #define KEY_LEFT 0x104 /* Left arrow key */
170 #define KEY_RIGHT 0x105 /* Right arrow key */
171 #define KEY_HOME 0x106 /* home key */
172 #define KEY_BACKSPACE 0x107 /* not on pc */
173 #define KEY_F0 0x108 /* function keys; 64 reserved */
174 #define KEY_F(n) (KEY_F0 + (n))
176 #define KEY_DC 0x14a /* delete character */
177 #define KEY_IC 0x14b /* insert char or enter ins mode */
179 #define KEY_NPAGE 0x152 /* next page */
180 #define KEY_PPAGE 0x153 /* previous page */
182 #define KEY_ENTER 0x157 /* enter or send (unreliable) */
184 #define KEY_PRINT 0x15a /* print/copy */
186 #define KEY_END 0x166 /* end key */
188 static const struct layout_maps keyboard_layouts[] = {
189 // #ifdef CONFIG_PC_KEYBOARD_LAYOUT_US
190 { .country = "us", .map = {
192 -1, -1, -1, -1, 'a', 'b', 'c', 'd',
193 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
195 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
196 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
198 '3', '4', '5', '6', '7', '8', '9', '0',
199 '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
201 ']', '\\', -1, ';', '\'', '`', ',', '.',
202 '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
204 KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
205 KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
207 KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
208 KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
210 KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
211 -1, -1, -1, -1, -1, -1, -1, -1,
213 -1, -1, -1, -1, -1, -1, -1, -1,
214 -1, -1, -1, -1, -1, -1, -1, -1,
216 { /* Shift modifier */
217 -1, -1, -1, -1, 'A', 'B', 'C', 'D',
218 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
220 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
221 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
223 '#', '$', '%', '^', '&', '*', '(', ')',
224 '\n', '\e', '\b', '\t', ' ', '_', '+', '{',
226 '}', '|', -1, ':', '"', '~', '<', '>',
227 '?', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
229 KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
230 KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
232 KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
233 KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
235 KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
236 -1, -1, -1, -1, -1, -1, -1, -1,
238 -1, -1, -1, -1, -1, -1, -1, -1,
239 -1, -1, -1, -1, -1, -1, -1, -1,
242 -1, -1, -1, -1, 'a', 'b', 'c', 'd',
243 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
245 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
246 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
248 '3', '4', '5', '6', '7', '8', '9', '0',
249 '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
251 ']', '\\', -1, ';', '\'', '`', ',', '.',
252 '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
254 KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
255 KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
257 KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
258 KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
260 KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
261 -1, -1, -1, -1, -1, -1, -1, -1,
263 -1, -1, -1, -1, -1, -1, -1, -1,
264 -1, -1, -1, -1, -1, -1, -1, -1,
266 { /* Shift+Alt modifier */
267 -1, -1, -1, -1, 'A', 'B', 'C', 'D',
268 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
270 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
271 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
273 '#', '$', '%', '^', '&', '*', '(', ')',
274 '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
276 ']', '\\', -1, ':', '\'', '`', ',', '.',
277 '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
279 KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
280 KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
282 KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
283 KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
285 KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
286 -1, -1, -1, -1, -1, -1, -1, -1,
288 -1, -1, -1, -1, -1, -1, -1, -1,
289 -1, -1, -1, -1, -1, -1, -1, -1,
295 #define MOD_SHIFT (1 << 0)
296 #define MOD_ALT (1 << 1)
297 #define MOD_CTRL (1 << 2)
299 static void usb_hid_keyboard_queue(int ch) {
300 /* ignore key presses if buffer full */
301 if (keycount < KEYBOARD_BUFFER_SIZE)
302 keybuffer[keycount++] = ch;
305 #define KEYBOARD_REPEAT_MS 30
306 #define INITIAL_REPEAT_DELAY 10
307 #define REPEAT_DELAY 2
310 usb_hid_process_keyboard_event(usbhid_inst_t *const inst,
311 const usb_hid_keyboard_event_t *const current)
313 const usb_hid_keyboard_event_t *const previous = &inst->previous;
315 int i, keypress = 0, modifiers = 0;
317 if (current->modifiers & 0x01) /* Left-Ctrl */ modifiers |= MOD_CTRL;
318 if (current->modifiers & 0x02) /* Left-Shift */ modifiers |= MOD_SHIFT;
319 if (current->modifiers & 0x04) /* Left-Alt */ modifiers |= MOD_ALT;
320 if (current->modifiers & 0x08) /* Left-GUI */ ;
321 if (current->modifiers & 0x10) /* Right-Ctrl */ modifiers |= MOD_CTRL;
322 if (current->modifiers & 0x20) /* Right-Shift */ modifiers |= MOD_SHIFT;
323 if (current->modifiers & 0x40) /* Right-AltGr */ modifiers |= MOD_ALT;
324 if (current->modifiers & 0x80) /* Right-GUI */ ;
326 /* Did the event change at all? */
327 if (inst->lastkeypress &&
328 !memcmp(current, previous, sizeof(*current))) {
329 /* No. Then it's a key repeat event. */
330 if (inst->repeat_delay) {
331 inst->repeat_delay--;
333 usb_hid_keyboard_queue(inst->lastkeypress);
334 inst->repeat_delay = REPEAT_DELAY;
340 inst->lastkeypress = 0;
342 for (i=0; i<6; i++) {
345 // No more keys? skip
346 if (current->keys[i] == 0)
349 for (j=0; j<6; j++) {
350 if (current->keys[i] == previous->keys[j]) {
359 /* Mask off MOD_CTRL */
360 keypress = map->map[modifiers & 0x03][current->keys[i]];
362 if (modifiers & MOD_CTRL) {
372 if (keypress == -1) {
373 /* Debug: Print unknown keys */
374 usb_debug ("usbhid: <%x> %x [ %x %x %x %x %x %x ] %d\n",
375 current->modifiers, current->repeats,
376 current->keys[0], current->keys[1],
377 current->keys[2], current->keys[3],
378 current->keys[4], current->keys[5], i);
380 /* Unknown key? Try next one in the queue */
384 usb_hid_keyboard_queue(keypress);
386 /* Remember for authentic key repeat */
387 inst->lastkeypress = keypress;
388 inst->repeat_delay = INITIAL_REPEAT_DELAY;
393 usb_hid_poll (usbdev_t *dev)
395 usb_hid_keyboard_event_t current;
398 while ((buf=dev->controller->poll_intr_queue (HID_INST(dev)->queue))) {
399 memcpy(¤t.buffer, buf, 8);
400 usb_hid_process_keyboard_event(HID_INST(dev), ¤t);
401 HID_INST(dev)->previous = current;
406 usb_hid_set_idle (usbdev_t *dev, interface_descriptor_t *interface, u16 duration)
409 dr.data_dir = host_to_device;
410 dr.req_type = class_type;
411 dr.req_recp = iface_recp;
412 dr.bRequest = SET_IDLE;
413 dr.wValue = __cpu_to_le16((duration >> 2) << 8);
414 dr.wIndex = __cpu_to_le16(interface->bInterfaceNumber);
416 dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0);
420 usb_hid_set_protocol (usbdev_t *dev, interface_descriptor_t *interface, hid_proto proto)
423 dr.data_dir = host_to_device;
424 dr.req_type = class_type;
425 dr.req_recp = iface_recp;
426 dr.bRequest = SET_PROTOCOL;
427 dr.wValue = __cpu_to_le16(proto);
428 dr.wIndex = __cpu_to_le16(interface->bInterfaceNumber);
430 dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0);
433 static int usb_hid_set_layout (const char *country)
435 /* FIXME should be per keyboard */
438 for (i=0; i<sizeof(keyboard_layouts)/sizeof(keyboard_layouts[0]); i++) {
439 if (strncmp(keyboard_layouts[i].country, country,
440 strlen(keyboard_layouts[i].country)))
443 /* Found, changing keyboard layout */
444 map = &keyboard_layouts[i];
445 usb_debug(" Keyboard layout '%s'\n", map->country);
449 usb_debug(" Keyboard layout '%s' not found, using '%s'\n",
450 country, map->country);
452 /* Nothing found, not changed */
457 usb_hid_init (usbdev_t *dev)
459 configuration_descriptor_t *cd = (configuration_descriptor_t*)dev->configuration;
460 interface_descriptor_t *interface = (interface_descriptor_t*)(((char *) cd) + cd->bLength);
462 if (interface->bInterfaceSubClass == hid_subclass_boot) {
464 usb_debug (" supports boot interface..\n");
465 usb_debug (" it's a %s\n",
466 boot_protos[interface->bInterfaceProtocol]);
467 switch (interface->bInterfaceProtocol) {
468 case hid_boot_proto_keyboard:
469 dev->data = malloc (sizeof (usbhid_inst_t));
471 printk("Not enough memory for USB HID device.\n");
474 memset(&HID_INST(dev)->previous, 0x00,
475 sizeof(HID_INST(dev)->previous));
476 usb_debug (" configuring...\n");
477 usb_hid_set_protocol(dev, interface, hid_proto_boot);
478 usb_hid_set_idle(dev, interface, KEYBOARD_REPEAT_MS);
479 usb_debug (" activating...\n");
481 HID_INST (dev)->descriptor =
483 get_descriptor(dev, gen_bmRequestType
484 (device_to_host, standard_type, iface_recp),
486 countrycode = HID_INST(dev)->descriptor->bCountryCode;
488 /* 35 countries defined: */
489 if (countrycode > 35)
491 usb_debug (" Keyboard has %s layout (country code %02x)\n",
492 countries[countrycode][0], countrycode);
494 /* Set keyboard layout accordingly */
495 usb_hid_set_layout(countries[countrycode][1]);
497 // only add here, because we only support boot-keyboard HID devices
498 dev->destroy = usb_hid_destroy;
499 dev->poll = usb_hid_poll;
501 for (i = 0; i <= dev->num_endp; i++) {
502 if (dev->endpoints[i].endpoint == 0)
504 if (dev->endpoints[i].type != INTERRUPT)
506 if (dev->endpoints[i].direction != IN)
510 usb_debug (" found endpoint %x for interrupt-in\n", i);
511 /* 20 buffers of 8 bytes, for every 10 msecs */
512 HID_INST(dev)->queue = dev->controller->create_intr_queue (&dev->endpoints[i], 8, 20, 10);
514 usb_debug (" configuration done.\n");
517 usb_debug("NOTICE: HID interface protocol %d%s not supported.\n",
518 interface->bInterfaceProtocol,
519 (interface->bInterfaceProtocol == hid_boot_proto_mouse ?
520 " (USB mouse)" : ""));
526 static int usbhid_havechar (void)
528 return (keycount != 0);
531 static int usbhid_getchar (void)
538 memmove(keybuffer, keybuffer + 1, --keycount);
543 /* ( addr len -- actual ) */
544 static void keyboard_read(void)
551 addr=(char *)cell2pointer(POP());
553 for (i = 0; i < len; i++) {
554 if (!usbhid_havechar())
556 key = usbhid_getchar();
562 void ob_usb_hid_add_keyboard(const char *path)
567 snprintf(name, sizeof(name), "%s/keyboard", path);
568 usb_debug("Found keyboard at %s\n", name);
569 REGISTER_NAMED_NODE(usb_kbd, name);
572 fword("find-device");
574 push_str("keyboard");
575 fword("device-type");
577 aliases = find_dev("/aliases");
578 set_property(aliases, "adb-keyboard", name, strlen(name) + 1);