Reduce warnings/errors in libpayload when using picky compiler options
[coreboot.git] / payloads / libpayload / drivers / usb / usbhid.c
1 /*
2  * This file is part of the libpayload project.
3  *
4  * Copyright (C) 2008-2010 coresystems GmbH
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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.
16  *
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
27  * SUCH DAMAGE.
28  */
29
30 // #define USB_DEBUG
31
32 #include <usb/usb.h>
33 #include <curses.h>
34
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
39 };
40 #ifdef USB_DEBUG
41 static const char *boot_protos[3] = { "(none)", "keyboard", "mouse" };
42 #endif
43 enum { GET_REPORT = 0x1, GET_IDLE = 0x2, GET_PROTOCOL = 0x3, SET_REPORT =
44                 0x9, SET_IDLE = 0xa, SET_PROTOCOL = 0xb
45 };
46
47 static void
48 usb_hid_destroy (usbdev_t *dev)
49 {
50         free (dev->data);
51 }
52
53 typedef struct {
54         void* queue;
55         hid_descriptor_t *descriptor;
56 } usbhid_inst_t;
57
58 #define HID_INST(dev) ((usbhid_inst_t*)(dev)->data)
59
60 /* keybuffer is global to all USB keyboards */
61 static int keycount;
62 #define KEYBOARD_BUFFER_SIZE 16
63 static short keybuffer[KEYBOARD_BUFFER_SIZE];
64
65 const char *countries[36][2] = {
66         { "not supported", "us" },
67         { "Arabic", "ae" },
68         { "Belgian", "be" },
69         { "Canadian-Bilingual", "ca" },
70         { "Canadian-French", "ca" },
71         { "Czech Republic", "cz" },
72         { "Danish", "dk" },
73         { "Finnish", "fi" },
74         { "French", "fr" },
75         { "German", "de" },
76         { "Greek", "gr" },
77         { "Hebrew", "il" },
78         { "Hungary", "hu" },
79         { "International (ISO)", "iso" },
80         { "Italian", "it" },
81         { "Japan (Katakana)", "jp" },
82         { "Korean", "us" },
83         { "Latin American", "us" },
84         { "Netherlands/Dutch", "nl" },
85         { "Norwegian", "no" },
86         { "Persian (Farsi)", "ir" },
87         { "Poland", "pl" },
88         { "Portuguese", "pt" },
89         { "Russia", "ru" },
90         { "Slovakia", "sl" },
91         { "Spanish", "es" },
92         { "Swedish", "se" },
93         { "Swiss/French", "ch" },
94         { "Swiss/German", "ch" },
95         { "Switzerland", "ch" },
96         { "Taiwan", "tw" },
97         { "Turkish-Q", "tr" },
98         { "UK", "uk" },
99         { "US", "us" },
100         { "Yugoslavia", "yu" },
101         { "Turkish-F", "tr" },
102         /* 36 - 255: Reserved */
103 };
104
105
106
107 struct layout_maps {
108         const char *country;
109         const short map[4][0x80];
110 };
111
112 static const struct layout_maps *map;
113
114 static const struct layout_maps keyboard_layouts[] = {
115 // #ifdef CONFIG_PC_KEYBOARD_LAYOUT_US
116 { .country = "us", .map = {
117         { /* No modifier */
118         -1, -1, -1, -1, 'a', 'b', 'c', 'd',
119         'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
120         /* 0x10 */
121         'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
122         'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
123         /* 0x20 */
124         '3', '4', '5', '6', '7', '8', '9', '0',
125         '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
126         /* 0x30 */
127         ']', '\\', -1, ';', '\'', '`', ',', '.',
128         '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
129         /* 0x40 */
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,
132         /* 50 */
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,
135         /* 60 */
136         KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
137         -1, -1, -1, -1, -1, -1, -1, -1,
138         /* 70 */
139         -1, -1, -1, -1, -1, -1, -1, -1,
140         -1, -1, -1, -1, -1, -1, -1, -1,
141          },
142         { /* Shift modifier */
143         -1, -1, -1, -1, 'A', 'B', 'C', 'D',
144         'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
145         /* 0x10 */
146         'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
147         'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
148         /* 0x20 */
149         '#', '$', '%', '^', '&', '*', '(', ')',
150         '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
151         /* 0x30 */
152         ']', '\\', -1, ':', '\'', '`', ',', '.',
153         '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
154         /* 0x40 */
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,
157         /* 50 */
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,
160         /* 60 */
161         KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
162         -1, -1, -1, -1, -1, -1, -1, -1,
163         /* 70 */
164         -1, -1, -1, -1, -1, -1, -1, -1,
165         -1, -1, -1, -1, -1, -1, -1, -1,
166          },
167         { /* Alt */
168         -1, -1, -1, -1, 'a', 'b', 'c', 'd',
169         'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
170         /* 0x10 */
171         'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
172         'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
173         /* 0x20 */
174         '3', '4', '5', '6', '7', '8', '9', '0',
175         '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
176         /* 0x30 */
177         ']', '\\', -1, ';', '\'', '`', ',', '.',
178         '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
179         /* 0x40 */
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,
182         /* 50 */
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,
185         /* 60 */
186         KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
187         -1, -1, -1, -1, -1, -1, -1, -1,
188         /* 70 */
189         -1, -1, -1, -1, -1, -1, -1, -1,
190         -1, -1, -1, -1, -1, -1, -1, -1,
191          },
192         { /* Shift+Alt modifier */
193         -1, -1, -1, -1, 'A', 'B', 'C', 'D',
194         'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
195         /* 0x10 */
196         'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
197         'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
198         /* 0x20 */
199         '#', '$', '%', '^', '&', '*', '(', ')',
200         '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
201         /* 0x30 */
202         ']', '\\', -1, ':', '\'', '`', ',', '.',
203         '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
204         /* 0x40 */
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,
207         /* 50 */
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,
210         /* 60 */
211         KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
212         -1, -1, -1, -1, -1, -1, -1, -1,
213         /* 70 */
214         -1, -1, -1, -1, -1, -1, -1, -1,
215         -1, -1, -1, -1, -1, -1, -1, -1,
216          }
217 }},
218 //#endif
219 };
220
221 #define MOD_SHIFT    (1 << 0)
222 #define MOD_ALT      (1 << 1)
223 #define MOD_CTRL     (1 << 2)
224
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;
229 }
230
231 typedef union {
232         struct {
233                 u8 modifiers;
234                 u8 repeats;
235                 u8 keys[6];
236         };
237         u8 buffer[8];
238 } usb_hid_keyboard_event_t;
239
240 #define KEYBOARD_REPEAT_MS      30
241 #define INITIAL_REPEAT_DELAY    10
242 #define REPEAT_DELAY             2
243
244 static void
245 usb_hid_process_keyboard_event(usb_hid_keyboard_event_t *current,
246                 usb_hid_keyboard_event_t *previous)
247 {
248         int i, keypress = 0, modifiers = 0;
249         static int lastkeypress = 0, repeat_delay = INITIAL_REPEAT_DELAY;
250
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 */   ;
259
260         if ((current->modifiers & 0x05) && ((current->keys[0] == 0x4c) ||
261                                 (current->keys[0]==0x63))) {
262                 /* vulcan nerve pinch */
263                 if (reset_handler)
264                         reset_handler();
265         }
266
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. */
270                 if (repeat_delay) {
271                         repeat_delay--;
272                 } else {
273                         usb_hid_keyboard_queue(lastkeypress);
274                         repeat_delay = REPEAT_DELAY;
275                 }
276
277                 return;
278         }
279
280         lastkeypress = 0;
281
282         for (i=0; i<6; i++) {
283                 int j;
284                 int skip = 0;
285                 // No more keys? skip
286                 if (current->keys[i] == 0)
287                         return;
288
289                 for (j=0; j<6; j++) {
290                         if (current->keys[i] == previous->keys[j]) {
291                                 skip = 1;
292                                 break;
293                         }
294                 }
295                 if (skip)
296                         continue;
297
298
299                 /* Mask off MOD_CTRL */
300                 keypress = map->map[modifiers & 0x03][current->keys[i]];
301
302                 if (modifiers & MOD_CTRL) {
303                         switch (keypress) {
304                         case 'a' ... 'z':
305                                 keypress &= 0x1f;
306                                 break;
307                         default:
308                                 continue;
309                         }
310                 }
311
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);
319
320                         /* Unknown key? Try next one in the queue */
321                         continue;
322                 }
323
324                 usb_hid_keyboard_queue(keypress);
325
326                 /* Remember for authentic key repeat */
327                 lastkeypress = keypress;
328                 repeat_delay = INITIAL_REPEAT_DELAY;
329         }
330 }
331
332 static void
333 usb_hid_poll (usbdev_t *dev)
334 {
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}
338         };
339         u8* buf;
340         while ((buf=dev->controller->poll_intr_queue (HID_INST(dev)->queue))) {
341                 memcpy(&current.buffer, buf, 8);
342                 usb_hid_process_keyboard_event(&current, &previous);
343                 previous = current;
344         }
345 }
346
347 static void
348 usb_hid_set_idle (usbdev_t *dev, interface_descriptor_t *interface, u16 duration)
349 {
350         dev_req_t dr;
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;
357         dr.wLength = 0;
358         dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0);
359 }
360
361 static void
362 usb_hid_set_protocol (usbdev_t *dev, interface_descriptor_t *interface, hid_proto proto)
363 {
364         dev_req_t dr;
365         dr.data_dir = host_to_device;
366         dr.req_type = class_type;
367         dr.req_recp = iface_recp;
368         dr.bRequest = SET_PROTOCOL;
369         dr.wValue = proto;
370         dr.wIndex = interface->bInterfaceNumber;
371         dr.wLength = 0;
372         dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0);
373 }
374
375 static struct console_input_driver cons = {
376         .havekey = usbhid_havechar,
377         .getchar = usbhid_getchar
378 };
379
380
381 static int usb_hid_set_layout (const char *country)
382 {
383         /* FIXME should be per keyboard */
384         int i;
385
386         for (i=0; i<ARRAY_SIZE(keyboard_layouts); i++) {
387                 if (strncmp(keyboard_layouts[i].country, country,
388                                         strlen(keyboard_layouts[i].country)))
389                         continue;
390
391                 /* Found, changing keyboard layout */
392                 map = &keyboard_layouts[i];
393                 printf("  Keyboard layout '%s'\n", map->country);
394                 return 0;
395         }
396
397         printf("Keyboard layout '%s' not found, using '%s'\n",
398                         country, map->country);
399
400         /* Nothing found, not changed */
401         return -1;
402 }
403
404 void
405 usb_hid_init (usbdev_t *dev)
406 {
407
408         static int installed = 0;
409         if (!installed) {
410                 installed = 1;
411                 console_add_input_driver (&cons);
412         }
413
414         configuration_descriptor_t *cd = (configuration_descriptor_t*)dev->configuration;
415         interface_descriptor_t *interface = (interface_descriptor_t*)(((char *) cd) + cd->bLength);
416
417         if (interface->bInterfaceSubClass == hid_subclass_boot) {
418                 u8 countrycode;
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));
425                         if (!dev->data)
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");
431
432                         HID_INST (dev)->descriptor =
433                                 (hid_descriptor_t *)
434                                         get_descriptor(dev, gen_bmRequestType
435                                         (device_to_host, standard_type, iface_recp),
436                                         0x21, 0, 0);
437                         countrycode = HID_INST(dev)->descriptor->bCountryCode;
438                         /* 35 countries defined: */
439                         if (countrycode > 35)
440                                 countrycode = 0;
441                         printf ("  Keyboard has %s layout (country code %02x)\n",
442                                         countries[countrycode][0], countrycode);
443
444                         /* Set keyboard layout accordingly */
445                         usb_hid_set_layout(countries[countrycode][1]);
446
447                         // only add here, because we only support boot-keyboard HID devices
448                         dev->destroy = usb_hid_destroy;
449                         dev->poll = usb_hid_poll;
450                         int i;
451                         for (i = 0; i <= dev->num_endp; i++) {
452                                 if (dev->endpoints[i].endpoint == 0)
453                                         continue;
454                                 if (dev->endpoints[i].type != INTERRUPT)
455                                         continue;
456                                 if (dev->endpoints[i].direction != IN)
457                                         continue;
458                                 break;
459                         }
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);
463                         keycount = 0;
464                         debug ("  configuration done.\n");
465                         break;
466                 case hid_boot_proto_mouse:
467                         printf("NOTICE: USB mice are not supported.\n");
468                         break;
469                 }
470         }
471 }
472
473 int usbhid_havechar (void)
474 {
475         return (keycount != 0);
476 }
477
478 int usbhid_getchar (void)
479 {
480         short ret;
481
482         if (keycount == 0)
483                 return 0;
484         ret = keybuffer[0];
485         memmove(keybuffer, keybuffer + 1, --keycount);
486
487         return (int)ret;
488 }
489