2 * This file is part of the libpayload project.
4 * Copyright (C) 2008-2010 coresystems GmbH
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 enum { hid_subclass_none = 0, hid_subclass_boot = 1 };
36 typedef enum { hid_proto_boot = 0, hid_proto_report = 1 } hid_proto;
37 enum { hid_boot_proto_none = 0, hid_boot_proto_keyboard =
38 1, hid_boot_proto_mouse = 2
41 static const char *boot_protos[3] = { "(none)", "keyboard", "mouse" };
43 enum { GET_REPORT = 0x1, GET_IDLE = 0x2, GET_PROTOCOL = 0x3, SET_REPORT =
44 0x9, SET_IDLE = 0xa, SET_PROTOCOL = 0xb
48 usb_hid_destroy (usbdev_t *dev)
55 hid_descriptor_t *descriptor;
58 #define HID_INST(dev) ((usbhid_inst_t*)(dev)->data)
60 /* keybuffer is global to all USB keyboards */
62 #define KEYBOARD_BUFFER_SIZE 16
63 static short keybuffer[KEYBOARD_BUFFER_SIZE];
65 const char *countries[36][2] = {
66 { "not supported", "us" },
69 { "Canadian-Bilingual", "ca" },
70 { "Canadian-French", "ca" },
71 { "Czech Republic", "cz" },
79 { "International (ISO)", "iso" },
81 { "Japan (Katakana)", "jp" },
83 { "Latin American", "us" },
84 { "Netherlands/Dutch", "nl" },
85 { "Norwegian", "no" },
86 { "Persian (Farsi)", "ir" },
88 { "Portuguese", "pt" },
93 { "Swiss/French", "ch" },
94 { "Swiss/German", "ch" },
95 { "Switzerland", "ch" },
97 { "Turkish-Q", "tr" },
100 { "Yugoslavia", "yu" },
101 { "Turkish-F", "tr" },
102 /* 36 - 255: Reserved */
109 const short map[4][0x80];
112 static const struct layout_maps *map;
114 static const struct layout_maps keyboard_layouts[] = {
115 // #ifdef CONFIG_PC_KEYBOARD_LAYOUT_US
116 { .country = "us", .map = {
118 -1, -1, -1, -1, 'a', 'b', 'c', 'd',
119 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
121 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
122 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
124 '3', '4', '5', '6', '7', '8', '9', '0',
125 '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
127 ']', '\\', -1, ';', '\'', '`', ',', '.',
128 '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
130 KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
131 KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
133 KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
134 KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
136 KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
137 -1, -1, -1, -1, -1, -1, -1, -1,
139 -1, -1, -1, -1, -1, -1, -1, -1,
140 -1, -1, -1, -1, -1, -1, -1, -1,
142 { /* Shift modifier */
143 -1, -1, -1, -1, 'A', 'B', 'C', 'D',
144 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
146 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
147 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
149 '#', '$', '%', '^', '&', '*', '(', ')',
150 '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
152 ']', '\\', -1, ':', '\'', '`', ',', '.',
153 '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
155 KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
156 KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
158 KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
159 KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
161 KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
162 -1, -1, -1, -1, -1, -1, -1, -1,
164 -1, -1, -1, -1, -1, -1, -1, -1,
165 -1, -1, -1, -1, -1, -1, -1, -1,
168 -1, -1, -1, -1, 'a', 'b', 'c', 'd',
169 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
171 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
172 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
174 '3', '4', '5', '6', '7', '8', '9', '0',
175 '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
177 ']', '\\', -1, ';', '\'', '`', ',', '.',
178 '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
180 KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
181 KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
183 KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
184 KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
186 KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
187 -1, -1, -1, -1, -1, -1, -1, -1,
189 -1, -1, -1, -1, -1, -1, -1, -1,
190 -1, -1, -1, -1, -1, -1, -1, -1,
192 { /* Shift+Alt modifier */
193 -1, -1, -1, -1, 'A', 'B', 'C', 'D',
194 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
196 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
197 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
199 '#', '$', '%', '^', '&', '*', '(', ')',
200 '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
202 ']', '\\', -1, ':', '\'', '`', ',', '.',
203 '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
205 KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
206 KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
208 KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
209 KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
211 KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
212 -1, -1, -1, -1, -1, -1, -1, -1,
214 -1, -1, -1, -1, -1, -1, -1, -1,
215 -1, -1, -1, -1, -1, -1, -1, -1,
221 #define MOD_SHIFT (1 << 0)
222 #define MOD_ALT (1 << 1)
223 #define MOD_CTRL (1 << 2)
225 static void usb_hid_keyboard_queue(int ch) {
226 /* ignore key presses if buffer full */
227 if (keycount < KEYBOARD_BUFFER_SIZE)
228 keybuffer[keycount++] = ch;
238 } usb_hid_keyboard_event_t;
240 #define KEYBOARD_REPEAT_MS 30
241 #define INITIAL_REPEAT_DELAY 10
242 #define REPEAT_DELAY 2
245 usb_hid_process_keyboard_event(usb_hid_keyboard_event_t *current,
246 usb_hid_keyboard_event_t *previous)
248 int i, keypress = 0, modifiers = 0;
249 static int lastkeypress = 0, repeat_delay = INITIAL_REPEAT_DELAY;
251 if (current->modifiers & 0x01) /* Left-Ctrl */ modifiers |= MOD_CTRL;
252 if (current->modifiers & 0x02) /* Left-Shift */ modifiers |= MOD_SHIFT;
253 if (current->modifiers & 0x04) /* Left-Alt */ modifiers |= MOD_ALT;
254 if (current->modifiers & 0x08) /* Left-GUI */ ;
255 if (current->modifiers & 0x10) /* Right-Ctrl */ modifiers |= MOD_CTRL;
256 if (current->modifiers & 0x20) /* Right-Shift */ modifiers |= MOD_SHIFT;
257 if (current->modifiers & 0x40) /* Right-AltGr */ modifiers |= MOD_ALT;
258 if (current->modifiers & 0x80) /* Right-GUI */ ;
260 if ((current->modifiers & 0x05) && ((current->keys[0] == 0x4c) ||
261 (current->keys[0]==0x63))) {
262 /* vulcan nerve pinch */
267 /* Did the event change at all? */
268 if (lastkeypress && !memcmp(current, previous, sizeof(usb_hid_keyboard_event_t))) {
269 /* No. Then it's a key repeat event. */
273 usb_hid_keyboard_queue(lastkeypress);
274 repeat_delay = REPEAT_DELAY;
282 for (i=0; i<6; i++) {
285 // No more keys? skip
286 if (current->keys[i] == 0)
289 for (j=0; j<6; j++) {
290 if (current->keys[i] == previous->keys[j]) {
299 /* Mask off MOD_CTRL */
300 keypress = map->map[modifiers & 0x03][current->keys[i]];
302 if (modifiers & MOD_CTRL) {
312 if (keypress == -1) {
313 /* Debug: Print unknown keys */
314 debug ("usbhid: <%x> %x [ %x %x %x %x %x %x ] %d\n",
315 current->modifiers, current->repeats,
316 current->keys[0], current->keys[1],
317 current->keys[2], current->keys[3],
318 current->keys[4], current->keys[5], i);
320 /* Unknown key? Try next one in the queue */
324 usb_hid_keyboard_queue(keypress);
326 /* Remember for authentic key repeat */
327 lastkeypress = keypress;
328 repeat_delay = INITIAL_REPEAT_DELAY;
333 usb_hid_poll (usbdev_t *dev)
335 usb_hid_keyboard_event_t current;
336 static usb_hid_keyboard_event_t previous = {
337 .buffer = { 0, 0, 0, 0, 0, 0, 0, 0}
340 while ((buf=dev->controller->poll_intr_queue (HID_INST(dev)->queue))) {
341 memcpy(¤t.buffer, buf, 8);
342 usb_hid_process_keyboard_event(¤t, &previous);
348 usb_hid_set_idle (usbdev_t *dev, interface_descriptor_t *interface, u16 duration)
351 dr.data_dir = host_to_device;
352 dr.req_type = class_type;
353 dr.req_recp = iface_recp;
354 dr.bRequest = SET_IDLE;
355 dr.wValue = (duration >> 2) << 8;
356 dr.wIndex = interface->bInterfaceNumber;
358 dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0);
362 usb_hid_set_protocol (usbdev_t *dev, interface_descriptor_t *interface, hid_proto proto)
365 dr.data_dir = host_to_device;
366 dr.req_type = class_type;
367 dr.req_recp = iface_recp;
368 dr.bRequest = SET_PROTOCOL;
370 dr.wIndex = interface->bInterfaceNumber;
372 dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0);
375 static struct console_input_driver cons = {
376 .havekey = usbhid_havechar,
377 .getchar = usbhid_getchar
381 static int usb_hid_set_layout (const char *country)
383 /* FIXME should be per keyboard */
386 for (i=0; i<ARRAY_SIZE(keyboard_layouts); i++) {
387 if (strncmp(keyboard_layouts[i].country, country,
388 strlen(keyboard_layouts[i].country)))
391 /* Found, changing keyboard layout */
392 map = &keyboard_layouts[i];
393 printf(" Keyboard layout '%s'\n", map->country);
397 printf("Keyboard layout '%s' not found, using '%s'\n",
398 country, map->country);
400 /* Nothing found, not changed */
405 usb_hid_init (usbdev_t *dev)
408 static int installed = 0;
411 console_add_input_driver (&cons);
414 configuration_descriptor_t *cd = (configuration_descriptor_t*)dev->configuration;
415 interface_descriptor_t *interface = (interface_descriptor_t*)(((char *) cd) + cd->bLength);
417 if (interface->bInterfaceSubClass == hid_subclass_boot) {
419 debug (" supports boot interface..\n");
420 debug (" it's a %s\n",
421 boot_protos[interface->bInterfaceProtocol]);
422 switch (interface->bInterfaceProtocol) {
423 case hid_boot_proto_keyboard:
424 dev->data = malloc (sizeof (usbhid_inst_t));
426 usb_fatal("Not enough memory for USB HID device.\n");
427 debug (" configuring...\n");
428 usb_hid_set_protocol(dev, interface, hid_proto_boot);
429 usb_hid_set_idle(dev, interface, KEYBOARD_REPEAT_MS);
430 debug (" activating...\n");
432 HID_INST (dev)->descriptor =
434 get_descriptor(dev, gen_bmRequestType
435 (device_to_host, standard_type, iface_recp),
437 countrycode = HID_INST(dev)->descriptor->bCountryCode;
438 /* 35 countries defined: */
439 if (countrycode > 35)
441 printf (" Keyboard has %s layout (country code %02x)\n",
442 countries[countrycode][0], countrycode);
444 /* Set keyboard layout accordingly */
445 usb_hid_set_layout(countries[countrycode][1]);
447 // only add here, because we only support boot-keyboard HID devices
448 dev->destroy = usb_hid_destroy;
449 dev->poll = usb_hid_poll;
451 for (i = 0; i <= dev->num_endp; i++) {
452 if (dev->endpoints[i].endpoint == 0)
454 if (dev->endpoints[i].type != INTERRUPT)
456 if (dev->endpoints[i].direction != IN)
460 debug (" found endpoint %x for interrupt-in\n", i);
461 /* 20 buffers of 8 bytes, for every 10 msecs */
462 HID_INST(dev)->queue = dev->controller->create_intr_queue (&dev->endpoints[i], 8, 20, 10);
464 debug (" configuration done.\n");
466 case hid_boot_proto_mouse:
467 printf("NOTICE: USB mice are not supported.\n");
473 int usbhid_havechar (void)
475 return (keycount != 0);
478 int usbhid_getchar (void)
485 memmove(keybuffer, keybuffer + 1, --keycount);