3 * Copyright (C) 2008-2010 coresystems GmbH
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 enum { hid_subclass_none
= 0, hid_subclass_boot
= 1 };
35 typedef enum { hid_proto_boot
= 0, hid_proto_report
= 1 } hid_proto
;
36 enum { hid_boot_proto_none
= 0, hid_boot_proto_keyboard
=
37 1, hid_boot_proto_mouse
= 2
39 static const char *boot_protos
[3] = { "(none)", "keyboard", "mouse" };
40 enum { GET_REPORT
= 0x1, GET_IDLE
= 0x2, GET_PROTOCOL
= 0x3, SET_REPORT
=
41 0x9, SET_IDLE
= 0xa, SET_PROTOCOL
= 0xb
51 } usb_hid_keyboard_event_t
;
55 hid_descriptor_t
*descriptor
;
57 usb_hid_keyboard_event_t previous
;
62 #define HID_INST(dev) ((usbhid_inst_t*)(dev)->data)
65 usb_hid_destroy(usbdev_t
*dev
)
67 if (HID_INST(dev
)->queue
) {
69 for (i
= 0; i
<= dev
->num_endp
; i
++) {
70 if (dev
->endpoints
[i
].endpoint
== 0)
72 if (dev
->endpoints
[i
].type
!= INTERRUPT
)
74 if (dev
->endpoints
[i
].direction
!= IN
)
78 dev
->controller
->destroy_intr_queue(
79 &dev
->endpoints
[i
], HID_INST(dev
)->queue
);
80 HID_INST(dev
)->queue
= NULL
;
82 free(HID_INST(dev
)->descriptor
);
83 HID_INST(dev
)->descriptor
= NULL
;
88 /* keybuffer is global to all USB keyboards */
90 #define KEYBOARD_BUFFER_SIZE 16
91 static short keybuffer
[KEYBOARD_BUFFER_SIZE
];
94 const char *countries
[36][2] = {
95 { "not supported", "us" },
98 { "Canadian-Bilingual", "ca" },
99 { "Canadian-French", "ca" },
100 { "Czech Republic", "cz" },
108 { "International (ISO)", "iso" },
110 { "Japan (Katakana)", "jp" },
112 { "Latin American", "us" },
113 { "Netherlands/Dutch", "nl" },
114 { "Norwegian", "no" },
115 { "Persian (Farsi)", "ir" },
117 { "Portuguese", "pt" },
119 { "Slovakia", "sl" },
122 { "Swiss/French", "ch" },
123 { "Swiss/German", "ch" },
124 { "Switzerland", "ch" },
126 { "Turkish-Q", "tr" },
129 { "Yugoslavia", "yu" },
130 { "Turkish-F", "tr" },
131 /* 36 - 255: Reserved */
136 const short map
[4][0x80];
139 static const struct layout_maps
*map
;
141 static const struct layout_maps keyboard_layouts
[] = {
142 // #if CONFIG(LP_PC_KEYBOARD_LAYOUT_US)
143 { .country
= "us", .map
= {
145 -1, -1, -1, -1, 'a', 'b', 'c', 'd',
146 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
148 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
149 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
151 '3', '4', '5', '6', '7', '8', '9', '0',
152 '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
154 ']', '\\', -1, ';', '\'', '`', ',', '.',
155 '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
157 KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT
, -1 /* ScrLk */,
158 KEY_BREAK
, KEY_IC
, KEY_HOME
, KEY_PPAGE
, KEY_DC
, KEY_END
, KEY_NPAGE
, KEY_RIGHT
,
160 KEY_LEFT
, KEY_DOWN
, KEY_UP
, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
161 KEY_ENTER
, KEY_END
, KEY_DOWN
, KEY_NPAGE
, KEY_LEFT
, -1, KEY_RIGHT
, KEY_HOME
,
163 KEY_UP
, KEY_PPAGE
, -1, KEY_DC
, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
164 -1, -1, -1, -1, -1, -1, -1, -1,
166 -1, -1, -1, -1, -1, -1, -1, -1,
167 -1, -1, -1, -1, -1, -1, -1, -1,
169 { /* Shift modifier */
170 -1, -1, -1, -1, 'A', 'B', 'C', 'D',
171 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
173 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
174 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
176 '#', '$', '%', '^', '&', '*', '(', ')',
177 '\n', '\e', '\b', '\t', ' ', '_', '+', '[',
179 ']', '\\', -1, ':', '\'', '`', ',', '.',
180 '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
182 KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT
, -1 /* ScrLk */,
183 KEY_BREAK
, KEY_IC
, KEY_HOME
, KEY_PPAGE
, KEY_DC
, KEY_END
, KEY_NPAGE
, KEY_RIGHT
,
185 KEY_LEFT
, KEY_DOWN
, KEY_UP
, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
186 KEY_ENTER
, KEY_END
, KEY_DOWN
, KEY_NPAGE
, KEY_LEFT
, -1, KEY_RIGHT
, KEY_HOME
,
188 KEY_UP
, KEY_PPAGE
, -1, KEY_DC
, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
189 -1, -1, -1, -1, -1, -1, -1, -1,
191 -1, -1, -1, -1, -1, -1, -1, -1,
192 -1, -1, -1, -1, -1, -1, -1, -1,
195 -1, -1, -1, -1, 'a', 'b', 'c', 'd',
196 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
198 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
199 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
201 '3', '4', '5', '6', '7', '8', '9', '0',
202 '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
204 ']', '\\', -1, ';', '\'', '`', ',', '.',
205 '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
207 KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT
, -1 /* ScrLk */,
208 KEY_BREAK
, KEY_IC
, KEY_HOME
, KEY_PPAGE
, KEY_DC
, KEY_END
, KEY_NPAGE
, KEY_RIGHT
,
210 KEY_LEFT
, KEY_DOWN
, KEY_UP
, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
211 KEY_ENTER
, KEY_END
, KEY_DOWN
, KEY_NPAGE
, KEY_LEFT
, -1, KEY_RIGHT
, KEY_HOME
,
213 KEY_UP
, KEY_PPAGE
, -1, KEY_DC
, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
214 -1, -1, -1, -1, -1, -1, -1, -1,
216 -1, -1, -1, -1, -1, -1, -1, -1,
217 -1, -1, -1, -1, -1, -1, -1, -1,
219 { /* Shift+Alt modifier */
220 -1, -1, -1, -1, 'A', 'B', 'C', 'D',
221 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
223 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
224 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
226 '#', '$', '%', '^', '&', '*', '(', ')',
227 '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
229 ']', '\\', -1, ':', '\'', '`', ',', '.',
230 '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
232 KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT
, -1 /* ScrLk */,
233 KEY_BREAK
, KEY_IC
, KEY_HOME
, KEY_PPAGE
, KEY_DC
, KEY_END
, KEY_NPAGE
, KEY_RIGHT
,
235 KEY_LEFT
, KEY_DOWN
, KEY_UP
, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
236 KEY_ENTER
, KEY_END
, KEY_DOWN
, KEY_NPAGE
, KEY_LEFT
, -1, KEY_RIGHT
, KEY_HOME
,
238 KEY_UP
, KEY_PPAGE
, -1, KEY_DC
, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
239 -1, -1, -1, -1, -1, -1, -1, -1,
241 -1, -1, -1, -1, -1, -1, -1, -1,
242 -1, -1, -1, -1, -1, -1, -1, -1,
248 static void usb_hid_keyboard_queue(int ch
) {
249 /* ignore key presses if buffer full */
250 if (keycount
< KEYBOARD_BUFFER_SIZE
)
251 keybuffer
[keycount
++] = ch
;
254 #define KEYBOARD_REPEAT_MS 30
255 #define INITIAL_REPEAT_DELAY 10
256 #define REPEAT_DELAY 2
259 usb_hid_process_keyboard_event(usbhid_inst_t
*const inst
,
260 const usb_hid_keyboard_event_t
*const current
)
262 const usb_hid_keyboard_event_t
*const previous
= &inst
->previous
;
268 if (current
->modifiers
& 0x01) /* Left-Ctrl */
269 modifiers
|= KB_MOD_CTRL
;
270 if (current
->modifiers
& 0x02) /* Left-Shift */
271 modifiers
|= KB_MOD_SHIFT
;
272 if (current
->modifiers
& 0x04) /* Left-Alt */
273 modifiers
|= KB_MOD_ALT
;
274 if (current
->modifiers
& 0x08) /* Left-GUI */
276 if (current
->modifiers
& 0x10) /* Right-Ctrl */
277 modifiers
|= KB_MOD_CTRL
;
278 if (current
->modifiers
& 0x20) /* Right-Shift */
279 modifiers
|= KB_MOD_SHIFT
;
280 if (current
->modifiers
& 0x40) /* Right-AltGr */
281 modifiers
|= KB_MOD_ALT
;
282 if (current
->modifiers
& 0x80) /* Right-GUI */
285 if ((current
->modifiers
& 0x05) && ((current
->keys
[0] == 0x4c) ||
286 (current
->keys
[0] == 0x63))) {
287 /* vulcan nerve pinch */
292 /* Did the event change at all? */
293 if (inst
->lastkeypress
&&
294 !memcmp(current
, previous
, sizeof(*current
))) {
295 /* No. Then it's a key repeat event. */
296 if (inst
->repeat_delay
) {
297 inst
->repeat_delay
--;
299 usb_hid_keyboard_queue(inst
->lastkeypress
);
300 inst
->repeat_delay
= REPEAT_DELAY
;
306 inst
->lastkeypress
= 0;
308 for (i
= 0; i
< 6; i
++) {
311 // No more keys? skip
312 if (current
->keys
[i
] == 0)
315 for (j
= 0; j
< 6; j
++) {
316 if (current
->keys
[i
] == previous
->keys
[j
]) {
324 /* Mask off KB_MOD_CTRL */
325 keypress
= map
->map
[modifiers
& 0x03][current
->keys
[i
]];
327 if (modifiers
& KB_MOD_CTRL
) {
337 if (keypress
== -1) {
338 /* Debug: Print unknown keys */
339 usb_debug("usbhid: <%x> %x [ %x %x %x %x %x %x ] %d\n",
340 current
->modifiers
, current
->repeats
,
341 current
->keys
[0], current
->keys
[1],
342 current
->keys
[2], current
->keys
[3],
343 current
->keys
[4], current
->keys
[5], i
);
345 /* Unknown key? Try next one in the queue */
349 usb_hid_keyboard_queue(keypress
);
351 /* Remember for authentic key repeat */
352 inst
->lastkeypress
= keypress
;
353 inst
->repeat_delay
= INITIAL_REPEAT_DELAY
;
358 usb_hid_poll(usbdev_t
*dev
)
360 usb_hid_keyboard_event_t current
;
363 while ((buf
= dev
->controller
->poll_intr_queue (HID_INST(dev
)->queue
))) {
364 memcpy(¤t
.buffer
, buf
, 8);
365 usb_hid_process_keyboard_event(HID_INST(dev
), ¤t
);
366 HID_INST(dev
)->previous
= current
;
371 usb_hid_set_idle(usbdev_t
*dev
, interface_descriptor_t
*interface
, u16 duration
)
374 dr
.data_dir
= host_to_device
;
375 dr
.req_type
= class_type
;
376 dr
.req_recp
= iface_recp
;
377 dr
.bRequest
= SET_IDLE
;
378 dr
.wValue
= (duration
>> 2) << 8;
379 dr
.wIndex
= interface
->bInterfaceNumber
;
381 dev
->controller
->control(dev
, OUT
, sizeof(dev_req_t
), &dr
, 0, 0);
385 usb_hid_set_protocol(usbdev_t
*dev
, interface_descriptor_t
*interface
, hid_proto proto
)
388 dr
.data_dir
= host_to_device
;
389 dr
.req_type
= class_type
;
390 dr
.req_recp
= iface_recp
;
391 dr
.bRequest
= SET_PROTOCOL
;
393 dr
.wIndex
= interface
->bInterfaceNumber
;
395 dev
->controller
->control(dev
, OUT
, sizeof(dev_req_t
), &dr
, 0, 0);
398 static struct console_input_driver cons
= {
399 .havekey
= usbhid_havechar
,
400 .getchar
= usbhid_getchar
,
401 .input_type
= CONSOLE_INPUT_TYPE_USB
,
404 static int usb_hid_set_layout(const char *country
)
406 /* FIXME should be per keyboard */
409 for (i
= 0; i
< ARRAY_SIZE(keyboard_layouts
); i
++) {
410 if (strncmp(keyboard_layouts
[i
].country
, country
,
411 strlen(keyboard_layouts
[i
].country
)))
414 /* Found, changing keyboard layout */
415 map
= &keyboard_layouts
[i
];
416 usb_debug(" Keyboard layout '%s'\n", map
->country
);
420 usb_debug(" Keyboard layout '%s' not found, using '%s'\n",
421 country
, map
->country
);
423 /* Nothing found, not changed */
428 usb_hid_init(usbdev_t
*dev
)
431 static int installed
= 0;
434 console_add_input_driver(&cons
);
437 configuration_descriptor_t
*cd
= (configuration_descriptor_t
*)dev
->configuration
;
438 interface_descriptor_t
*interface
= (interface_descriptor_t
*)(((char *) cd
) + cd
->bLength
);
440 if (interface
->bInterfaceSubClass
== hid_subclass_boot
) {
442 usb_debug(" supports boot interface..\n");
443 usb_debug(" it's a %s\n",
444 boot_protos
[interface
->bInterfaceProtocol
]);
445 switch (interface
->bInterfaceProtocol
) {
446 case hid_boot_proto_keyboard
:
447 dev
->data
= xzalloc(sizeof(usbhid_inst_t
));
448 usb_debug(" configuring...\n");
449 usb_hid_set_protocol(dev
, interface
, hid_proto_boot
);
450 usb_hid_set_idle(dev
, interface
, KEYBOARD_REPEAT_MS
);
451 usb_debug(" activating...\n");
453 hid_descriptor_t
*desc
= malloc(sizeof(hid_descriptor_t
));
454 if (!desc
|| get_descriptor(dev
, gen_bmRequestType(
455 device_to_host
, standard_type
, iface_recp
),
456 0x21, 0, desc
, sizeof(*desc
)) != sizeof(*desc
)) {
457 usb_debug("get_descriptor(HID) failed\n");
458 usb_detach_device(dev
->controller
, dev
->address
);
461 HID_INST(dev
)->descriptor
= desc
;
462 countrycode
= desc
->bCountryCode
;
463 /* 35 countries defined: */
464 if (countrycode
>= ARRAY_SIZE(countries
))
466 usb_debug(" Keyboard has %s layout (country code %02x)\n",
467 countries
[countrycode
][0], countrycode
);
469 /* Set keyboard layout accordingly */
470 usb_hid_set_layout(countries
[countrycode
][1]);
472 // only add here, because we only support boot-keyboard HID devices
473 dev
->destroy
= usb_hid_destroy
;
474 dev
->poll
= usb_hid_poll
;
476 for (i
= 1; i
< dev
->num_endp
; i
++) {
477 if (dev
->endpoints
[i
].type
!= INTERRUPT
)
479 if (dev
->endpoints
[i
].direction
!= IN
)
483 if (i
>= dev
->num_endp
) {
484 usb_debug("Could not find HID endpoint\n");
485 usb_detach_device(dev
->controller
, dev
->address
);
488 usb_debug(" found endpoint %x for interrupt-in\n", i
);
489 /* 20 buffers of 8 bytes, for every 10 msecs */
490 HID_INST(dev
)->queue
= dev
->controller
->create_intr_queue(&dev
->endpoints
[i
], 8, 20, 10);
492 usb_debug(" configuration done.\n");
494 case hid_boot_proto_mouse
:
495 usb_debug("NOTICE: USB mice are not supported.\n");
501 int usbhid_havechar (void)
503 return (keycount
!= 0);
506 int usbhid_getchar (void)
513 memmove(keybuffer
, keybuffer
+ 1, --keycount
);
518 int usbhid_getmodifiers(void)