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 */ modifiers
|= KB_MOD_CTRL
;
269 if (current
->modifiers
& 0x02) /* Left-Shift */ modifiers
|= KB_MOD_SHIFT
;
270 if (current
->modifiers
& 0x04) /* Left-Alt */ modifiers
|= KB_MOD_ALT
;
271 if (current
->modifiers
& 0x08) /* Left-GUI */;
272 if (current
->modifiers
& 0x10) /* Right-Ctrl */ modifiers
|= KB_MOD_CTRL
;
273 if (current
->modifiers
& 0x20) /* Right-Shift */ modifiers
|= KB_MOD_SHIFT
;
274 if (current
->modifiers
& 0x40) /* Right-AltGr */ modifiers
|= KB_MOD_ALT
;
275 if (current
->modifiers
& 0x80) /* Right-GUI */;
277 if ((current
->modifiers
& 0x05) && ((current
->keys
[0] == 0x4c) ||
278 (current
->keys
[0] == 0x63))) {
279 /* vulcan nerve pinch */
284 /* Did the event change at all? */
285 if (inst
->lastkeypress
&&
286 !memcmp(current
, previous
, sizeof(*current
))) {
287 /* No. Then it's a key repeat event. */
288 if (inst
->repeat_delay
) {
289 inst
->repeat_delay
--;
291 usb_hid_keyboard_queue(inst
->lastkeypress
);
292 inst
->repeat_delay
= REPEAT_DELAY
;
298 inst
->lastkeypress
= 0;
300 for (i
= 0; i
< 6; i
++) {
303 // No more keys? skip
304 if (current
->keys
[i
] == 0)
307 for (j
= 0; j
< 6; j
++) {
308 if (current
->keys
[i
] == previous
->keys
[j
]) {
316 /* Mask off KB_MOD_CTRL */
317 keypress
= map
->map
[modifiers
& 0x03][current
->keys
[i
]];
319 if (modifiers
& KB_MOD_CTRL
) {
329 if (keypress
== -1) {
330 /* Debug: Print unknown keys */
331 usb_debug("usbhid: <%x> %x [ %x %x %x %x %x %x ] %d\n",
332 current
->modifiers
, current
->repeats
,
333 current
->keys
[0], current
->keys
[1],
334 current
->keys
[2], current
->keys
[3],
335 current
->keys
[4], current
->keys
[5], i
);
337 /* Unknown key? Try next one in the queue */
341 usb_hid_keyboard_queue(keypress
);
343 /* Remember for authentic key repeat */
344 inst
->lastkeypress
= keypress
;
345 inst
->repeat_delay
= INITIAL_REPEAT_DELAY
;
350 usb_hid_poll(usbdev_t
*dev
)
352 usb_hid_keyboard_event_t current
;
355 while ((buf
= dev
->controller
->poll_intr_queue (HID_INST(dev
)->queue
))) {
356 memcpy(¤t
.buffer
, buf
, 8);
357 usb_hid_process_keyboard_event(HID_INST(dev
), ¤t
);
358 HID_INST(dev
)->previous
= current
;
363 usb_hid_set_idle(usbdev_t
*dev
, interface_descriptor_t
*interface
, u16 duration
)
366 dr
.data_dir
= host_to_device
;
367 dr
.req_type
= class_type
;
368 dr
.req_recp
= iface_recp
;
369 dr
.bRequest
= SET_IDLE
;
370 dr
.wValue
= (duration
>> 2) << 8;
371 dr
.wIndex
= interface
->bInterfaceNumber
;
373 dev
->controller
->control(dev
, OUT
, sizeof(dev_req_t
), &dr
, 0, 0);
377 usb_hid_set_protocol(usbdev_t
*dev
, interface_descriptor_t
*interface
, hid_proto proto
)
380 dr
.data_dir
= host_to_device
;
381 dr
.req_type
= class_type
;
382 dr
.req_recp
= iface_recp
;
383 dr
.bRequest
= SET_PROTOCOL
;
385 dr
.wIndex
= interface
->bInterfaceNumber
;
387 dev
->controller
->control(dev
, OUT
, sizeof(dev_req_t
), &dr
, 0, 0);
390 static struct console_input_driver cons
= {
391 .havekey
= usbhid_havechar
,
392 .getchar
= usbhid_getchar
,
393 .input_type
= CONSOLE_INPUT_TYPE_USB
,
396 static int usb_hid_set_layout(const char *country
)
398 /* FIXME should be per keyboard */
401 for (i
= 0; i
< ARRAY_SIZE(keyboard_layouts
); i
++) {
402 if (strncmp(keyboard_layouts
[i
].country
, country
,
403 strlen(keyboard_layouts
[i
].country
)))
406 /* Found, changing keyboard layout */
407 map
= &keyboard_layouts
[i
];
408 usb_debug(" Keyboard layout '%s'\n", map
->country
);
412 usb_debug(" Keyboard layout '%s' not found, using '%s'\n",
413 country
, map
->country
);
415 /* Nothing found, not changed */
420 usb_hid_init(usbdev_t
*dev
)
423 static int installed
= 0;
426 console_add_input_driver(&cons
);
429 configuration_descriptor_t
*cd
= (configuration_descriptor_t
*)dev
->configuration
;
430 interface_descriptor_t
*interface
= (interface_descriptor_t
*)(((char *) cd
) + cd
->bLength
);
432 if (interface
->bInterfaceSubClass
== hid_subclass_boot
) {
434 usb_debug(" supports boot interface..\n");
435 usb_debug(" it's a %s\n",
436 boot_protos
[interface
->bInterfaceProtocol
]);
437 switch (interface
->bInterfaceProtocol
) {
438 case hid_boot_proto_keyboard
:
439 dev
->data
= xzalloc(sizeof(usbhid_inst_t
));
440 usb_debug(" configuring...\n");
441 usb_hid_set_protocol(dev
, interface
, hid_proto_boot
);
442 usb_hid_set_idle(dev
, interface
, KEYBOARD_REPEAT_MS
);
443 usb_debug(" activating...\n");
445 hid_descriptor_t
*desc
= malloc(sizeof(hid_descriptor_t
));
446 if (!desc
|| get_descriptor(dev
, gen_bmRequestType(
447 device_to_host
, standard_type
, iface_recp
),
448 0x21, 0, desc
, sizeof(*desc
)) != sizeof(*desc
)) {
449 usb_debug("get_descriptor(HID) failed\n");
450 usb_detach_device(dev
->controller
, dev
->address
);
453 HID_INST(dev
)->descriptor
= desc
;
454 countrycode
= desc
->bCountryCode
;
455 /* 35 countries defined: */
456 if (countrycode
>= ARRAY_SIZE(countries
))
458 usb_debug(" Keyboard has %s layout (country code %02x)\n",
459 countries
[countrycode
][0], countrycode
);
461 /* Set keyboard layout accordingly */
462 usb_hid_set_layout(countries
[countrycode
][1]);
464 // only add here, because we only support boot-keyboard HID devices
465 dev
->destroy
= usb_hid_destroy
;
466 dev
->poll
= usb_hid_poll
;
468 for (i
= 1; i
< dev
->num_endp
; i
++) {
469 if (dev
->endpoints
[i
].type
!= INTERRUPT
)
471 if (dev
->endpoints
[i
].direction
!= IN
)
475 if (i
>= dev
->num_endp
) {
476 usb_debug("Could not find HID endpoint\n");
477 usb_detach_device(dev
->controller
, dev
->address
);
480 usb_debug(" found endpoint %x for interrupt-in\n", i
);
481 /* 20 buffers of 8 bytes, for every 10 msecs */
482 HID_INST(dev
)->queue
= dev
->controller
->create_intr_queue(&dev
->endpoints
[i
], 8, 20, 10);
484 usb_debug(" configuration done.\n");
486 case hid_boot_proto_mouse
:
487 usb_debug("NOTICE: USB mice are not supported.\n");
493 int usbhid_havechar (void)
495 return (keycount
!= 0);
498 int usbhid_getchar (void)
505 memmove(keybuffer
, keybuffer
+ 1, --keycount
);
510 int usbhid_getmodifiers(void)