source: mainline/uspace/drv/usbhid/kbd/kbddev.c@ 4125b7d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4125b7d was 4125b7d, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

usb_log_printf() checks for printf correctness

It is surprising how many printf warnings simple check could
produce ;-).

Next time, it won't compile. Bad, huh?

  • Property mode set to 100644
File size: 29.1 KB
Line 
1/*
2 * Copyright (c) 2011 Lubos Slovak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - 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 * - 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 ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup drvusbhid
30 * @{
31 */
32/**
33 * @file
34 * USB HID keyboard device structure and API.
35 */
36
37#include <errno.h>
38#include <str_error.h>
39#include <stdio.h>
40
41#include <io/keycode.h>
42#include <ipc/kbd.h>
43#include <async.h>
44#include <fibril.h>
45#include <fibril_synch.h>
46
47#include <usb/usb.h>
48#include <usb/dp.h>
49#include <usb/request.h>
50#include <usb/classes/hid.h>
51#include <usb/pipes.h>
52#include <usb/debug.h>
53#include <usb/classes/hidparser.h>
54#include <usb/classes/classes.h>
55#include <usb/classes/hidut.h>
56#include <usb/classes/hidreq.h>
57#include <usb/classes/hidreport.h>
58#include <usb/classes/hid/utled.h>
59
60#include <usb/devdrv.h>
61
62#include "kbddev.h"
63
64#include "layout.h"
65#include "conv.h"
66#include "kbdrepeat.h"
67
68#include "../usbhid.h"
69
70/*----------------------------------------------------------------------------*/
71/** Default modifiers when the keyboard is initialized. */
72static const unsigned DEFAULT_ACTIVE_MODS = KM_NUM_LOCK;
73
74///** Boot protocol report size (key part). */
75//static const size_t BOOTP_REPORT_SIZE = 6;
76
77///** Boot protocol total report size. */
78//static const size_t BOOTP_BUFFER_SIZE = 8;
79
80///** Boot protocol output report size. */
81//static const size_t BOOTP_BUFFER_OUT_SIZE = 1;
82
83///** Boot protocol error key code. */
84//static const uint8_t BOOTP_ERROR_ROLLOVER = 1;
85static const uint8_t ERROR_ROLLOVER = 1;
86
87/** Default idle rate for keyboards. */
88static const uint8_t IDLE_RATE = 0;
89
90/** Delay before a pressed key starts auto-repeating. */
91static const unsigned int DEFAULT_DELAY_BEFORE_FIRST_REPEAT = 500 * 1000;
92
93/** Delay between two repeats of a pressed key when auto-repeating. */
94static const unsigned int DEFAULT_REPEAT_DELAY = 50 * 1000;
95
96/*----------------------------------------------------------------------------*/
97
98/** Keyboard polling endpoint description for boot protocol class. */
99usb_endpoint_description_t usb_hid_kbd_poll_endpoint_description = {
100 .transfer_type = USB_TRANSFER_INTERRUPT,
101 .direction = USB_DIRECTION_IN,
102 .interface_class = USB_CLASS_HID,
103 .interface_subclass = USB_HID_SUBCLASS_BOOT,
104 .interface_protocol = USB_HID_PROTOCOL_KEYBOARD,
105 .flags = 0
106};
107
108//static usb_endpoint_description_t hid_poll_endpoint_description = {
109// .transfer_type = USB_TRANSFER_INTERRUPT,
110// .direction = USB_DIRECTION_IN,
111// .interface_class = USB_CLASS_HID,
112// .flags = 0
113//};
114
115///* Array of endpoints expected on the device, NULL terminated. */
116//usb_endpoint_description_t
117// *usb_kbd_endpoints[USB_KBD_POLL_EP_COUNT + 1] = {
118// &boot_poll_endpoint_description,
119// &hid_poll_endpoint_description,
120// NULL
121//};
122
123const char *HID_KBD_FUN_NAME = "keyboard";
124const char *HID_KBD_CLASS_NAME = "keyboard";
125
126/*----------------------------------------------------------------------------*/
127
128enum {
129 USB_KBD_BOOT_REPORT_DESCRIPTOR_SIZE = 63
130};
131
132static const uint8_t USB_KBD_BOOT_REPORT_DESCRIPTOR[
133 USB_KBD_BOOT_REPORT_DESCRIPTOR_SIZE] = {
134 0x05, 0x01, // Usage Page (Generic Desktop),
135 0x09, 0x06, // Usage (Keyboard),
136 0xA1, 0x01, // Collection (Application),
137 0x75, 0x01, // Report Size (1),
138 0x95, 0x08, // Report Count (8),
139 0x05, 0x07, // Usage Page (Key Codes);
140 0x19, 0xE0, // Usage Minimum (224),
141 0x29, 0xE7, // Usage Maximum (231),
142 0x15, 0x00, // Logical Minimum (0),
143 0x25, 0x01, // Logical Maximum (1),
144 0x81, 0x02, // Input (Data, Variable, Absolute), ; Modifier byte
145 0x95, 0x01, // Report Count (1),
146 0x75, 0x08, // Report Size (8),
147 0x81, 0x01, // Input (Constant), ; Reserved byte
148 0x95, 0x05, // Report Count (5),
149 0x75, 0x01, // Report Size (1),
150 0x05, 0x08, // Usage Page (Page# for LEDs),
151 0x19, 0x01, // Usage Minimum (1),
152 0x29, 0x05, // Usage Maxmimum (5),
153 0x91, 0x02, // Output (Data, Variable, Absolute), ; LED report
154 0x95, 0x01, // Report Count (1),
155 0x75, 0x03, // Report Size (3),
156 0x91, 0x01, // Output (Constant), ; LED report padding
157 0x95, 0x06, // Report Count (6),
158 0x75, 0x08, // Report Size (8),
159 0x15, 0x00, // Logical Minimum (0),
160 0x25, 0xff, // Logical Maximum (255),
161 0x05, 0x07, // Usage Page (Key Codes),
162 0x19, 0x00, // Usage Minimum (0),
163 0x29, 0xff, // Usage Maximum (255),
164 0x81, 0x00, // Input (Data, Array), ; Key arrays (6 bytes)
165 0xC0 // End Collection
166
167};
168
169/*----------------------------------------------------------------------------*/
170
171typedef enum usb_kbd_flags {
172 USB_KBD_STATUS_UNINITIALIZED = 0,
173 USB_KBD_STATUS_INITIALIZED = 1,
174 USB_KBD_STATUS_TO_DESTROY = -1
175} usb_kbd_flags;
176
177/*----------------------------------------------------------------------------*/
178
179static void usb_kbd_process_keycodes(const uint8_t *key_codes, size_t count,
180 uint8_t report_id, void *arg);
181
182static const usb_hid_report_in_callbacks_t usb_kbd_parser_callbacks = {
183 .keyboard = usb_kbd_process_keycodes
184};
185
186/*----------------------------------------------------------------------------*/
187/* Keyboard layouts */
188/*----------------------------------------------------------------------------*/
189
190#define NUM_LAYOUTS 3
191
192/** Keyboard layout map. */
193static layout_op_t *layout[NUM_LAYOUTS] = {
194 &us_qwerty_op,
195 &us_dvorak_op,
196 &cz_op
197};
198
199static int active_layout = 0;
200
201/*----------------------------------------------------------------------------*/
202/* Modifier constants */
203/*----------------------------------------------------------------------------*/
204/** Mapping of USB modifier key codes to generic modifier key codes. */
205static const keycode_t usbhid_modifiers_keycodes[USB_HID_MOD_COUNT] = {
206 KC_LCTRL, /* USB_HID_MOD_LCTRL */
207 KC_LSHIFT, /* USB_HID_MOD_LSHIFT */
208 KC_LALT, /* USB_HID_MOD_LALT */
209 0, /* USB_HID_MOD_LGUI */
210 KC_RCTRL, /* USB_HID_MOD_RCTRL */
211 KC_RSHIFT, /* USB_HID_MOD_RSHIFT */
212 KC_RALT, /* USB_HID_MOD_RALT */
213 0, /* USB_HID_MOD_RGUI */
214};
215
216typedef enum usbhid_lock_code {
217 USB_KBD_LOCK_NUM = 0x53,
218 USB_KBD_LOCK_CAPS = 0x39,
219 USB_KBD_LOCK_SCROLL = 0x47,
220 USB_KBD_LOCK_COUNT = 3
221} usbhid_lock_code;
222
223static const usbhid_lock_code usbhid_lock_codes[USB_KBD_LOCK_COUNT] = {
224 USB_KBD_LOCK_NUM,
225 USB_KBD_LOCK_CAPS,
226 USB_KBD_LOCK_SCROLL
227};
228
229/*----------------------------------------------------------------------------*/
230/* IPC method handler */
231/*----------------------------------------------------------------------------*/
232
233static void default_connection_handler(ddf_fun_t *, ipc_callid_t, ipc_call_t *);
234//ddf_dev_ops_t keyboard_ops = {
235// .default_handler = default_connection_handler
236//};
237
238/**
239 * Default handler for IPC methods not handled by DDF.
240 *
241 * Currently recognizes only one method (IPC_M_CONNECT_TO_ME), in which case it
242 * assumes the caller is the console and thus it stores IPC phone to it for
243 * later use by the driver to notify about key events.
244 *
245 * @param fun Device function handling the call.
246 * @param icallid Call id.
247 * @param icall Call data.
248 */
249static void default_connection_handler(ddf_fun_t *fun,
250 ipc_callid_t icallid, ipc_call_t *icall)
251{
252 sysarg_t method = IPC_GET_IMETHOD(*icall);
253
254 usb_hid_dev_t *hid_dev = (usb_hid_dev_t *)fun->driver_data;
255
256 if (hid_dev == NULL || hid_dev->data == NULL) {
257 async_answer_0(icallid, EINVAL);
258 return;
259 }
260
261 assert(hid_dev != NULL);
262 assert(hid_dev->data != NULL);
263 usb_kbd_t *kbd_dev = (usb_kbd_t *)hid_dev->data;
264
265 if (method == IPC_M_CONNECT_TO_ME) {
266 int callback = IPC_GET_ARG5(*icall);
267
268 if (kbd_dev->console_phone != -1) {
269 async_answer_0(icallid, ELIMIT);
270 return;
271 }
272
273 kbd_dev->console_phone = callback;
274 async_answer_0(icallid, EOK);
275 return;
276 }
277
278 async_answer_0(icallid, EINVAL);
279}
280
281/*----------------------------------------------------------------------------*/
282/* Key processing functions */
283/*----------------------------------------------------------------------------*/
284/**
285 * Handles turning of LED lights on and off.
286 *
287 * In case of USB keyboards, the LEDs are handled in the driver, not in the
288 * device. When there should be a change (lock key was pressed), the driver
289 * uses a Set_Report request sent to the device to set the state of the LEDs.
290 *
291 * This functions sets the LED lights according to current settings of modifiers
292 * kept in the keyboard device structure.
293 *
294 * @param kbd_dev Keyboard device structure.
295 */
296static void usb_kbd_set_led(usb_hid_dev_t *hid_dev, usb_kbd_t *kbd_dev)
297{
298 if (kbd_dev->output_size == 0) {
299 return;
300 }
301
302 unsigned i = 0;
303
304 /* Reset the LED data. */
305 memset(kbd_dev->led_data, 0, kbd_dev->led_output_size * sizeof(int32_t));
306
307 if ((kbd_dev->mods & KM_NUM_LOCK) && (i < kbd_dev->led_output_size)) {
308 kbd_dev->led_data[i++] = USB_HID_LED_NUM_LOCK;
309 }
310
311 if ((kbd_dev->mods & KM_CAPS_LOCK) && (i < kbd_dev->led_output_size)) {
312 kbd_dev->led_data[i++] = USB_HID_LED_CAPS_LOCK;
313 }
314
315 if ((kbd_dev->mods & KM_SCROLL_LOCK)
316 && (i < kbd_dev->led_output_size)) {
317 kbd_dev->led_data[i++] = USB_HID_LED_SCROLL_LOCK;
318 }
319
320 // TODO: COMPOSE and KANA
321
322 usb_log_debug("Creating output report.\n");
323
324 int rc = usb_hid_report_output_translate(hid_dev->parser,
325 kbd_dev->led_path,
326 USB_HID_PATH_COMPARE_END | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
327 kbd_dev->output_buffer,
328 kbd_dev->output_size, kbd_dev->led_data, kbd_dev->led_output_size);
329
330 if (rc != EOK) {
331 usb_log_warning("Error translating LED output to output report"
332 ".\n");
333 return;
334 }
335
336 usb_log_debug("Output report buffer: %s\n",
337 usb_debug_str_buffer(kbd_dev->output_buffer, kbd_dev->output_size,
338 0));
339
340 usbhid_req_set_report(&hid_dev->usb_dev->ctrl_pipe,
341 hid_dev->usb_dev->interface_no, USB_HID_REPORT_TYPE_OUTPUT,
342 kbd_dev->output_buffer, kbd_dev->output_size);
343}
344
345/*----------------------------------------------------------------------------*/
346/**
347 * Processes key events.
348 *
349 * @note This function was copied from AT keyboard driver and modified to suit
350 * USB keyboard.
351 *
352 * @note Lock keys are not sent to the console, as they are completely handled
353 * in the driver. It may, however, be required later that the driver
354 * sends also these keys to application (otherwise it cannot use those
355 * keys at all).
356 *
357 * @param kbd_dev Keyboard device structure.
358 * @param type Type of the event (press / release). Recognized values:
359 * KEY_PRESS, KEY_RELEASE
360 * @param key Key code of the key according to HID Usage Tables.
361 */
362void usb_kbd_push_ev(usb_hid_dev_t *hid_dev, usb_kbd_t *kbd_dev, int type,
363 unsigned int key)
364{
365 console_event_t ev;
366 unsigned mod_mask;
367
368 /*
369 * These parts are copy-pasted from the AT keyboard driver.
370 *
371 * They definitely require some refactoring, but will keep it for later
372 * when the console and keyboard system is changed in HelenOS.
373 */
374 switch (key) {
375 case KC_LCTRL: mod_mask = KM_LCTRL; break;
376 case KC_RCTRL: mod_mask = KM_RCTRL; break;
377 case KC_LSHIFT: mod_mask = KM_LSHIFT; break;
378 case KC_RSHIFT: mod_mask = KM_RSHIFT; break;
379 case KC_LALT: mod_mask = KM_LALT; break;
380 case KC_RALT: mod_mask = KM_RALT; break;
381 default: mod_mask = 0; break;
382 }
383
384 if (mod_mask != 0) {
385 if (type == KEY_PRESS)
386 kbd_dev->mods = kbd_dev->mods | mod_mask;
387 else
388 kbd_dev->mods = kbd_dev->mods & ~mod_mask;
389 }
390
391 switch (key) {
392 case KC_CAPS_LOCK: mod_mask = KM_CAPS_LOCK; break;
393 case KC_NUM_LOCK: mod_mask = KM_NUM_LOCK; break;
394 case KC_SCROLL_LOCK: mod_mask = KM_SCROLL_LOCK; break;
395 default: mod_mask = 0; break;
396 }
397
398 if (mod_mask != 0) {
399 if (type == KEY_PRESS) {
400 /*
401 * Only change lock state on transition from released
402 * to pressed. This prevents autorepeat from messing
403 * up the lock state.
404 */
405 unsigned int locks_old = kbd_dev->lock_keys;
406
407 kbd_dev->mods =
408 kbd_dev->mods ^ (mod_mask & ~kbd_dev->lock_keys);
409 kbd_dev->lock_keys = kbd_dev->lock_keys | mod_mask;
410
411 /* Update keyboard lock indicator lights. */
412 if (kbd_dev->lock_keys != locks_old
413 && hid_dev != NULL) { // ugly hack
414 usb_kbd_set_led(hid_dev, kbd_dev);
415 }
416 } else {
417 kbd_dev->lock_keys = kbd_dev->lock_keys & ~mod_mask;
418 }
419 }
420
421 if (key == KC_CAPS_LOCK || key == KC_NUM_LOCK || key == KC_SCROLL_LOCK) {
422 // do not send anything to the console, this is our business
423 return;
424 }
425
426 if (type == KEY_PRESS && (kbd_dev->mods & KM_LCTRL) && key == KC_F1) {
427 active_layout = 0;
428 layout[active_layout]->reset();
429 return;
430 }
431
432 if (type == KEY_PRESS && (kbd_dev->mods & KM_LCTRL) && key == KC_F2) {
433 active_layout = 1;
434 layout[active_layout]->reset();
435 return;
436 }
437
438 if (type == KEY_PRESS && (kbd_dev->mods & KM_LCTRL) && key == KC_F3) {
439 active_layout = 2;
440 layout[active_layout]->reset();
441 return;
442 }
443
444 ev.type = type;
445 ev.key = key;
446 ev.mods = kbd_dev->mods;
447
448 ev.c = layout[active_layout]->parse_ev(&ev);
449
450 usb_log_debug2("Sending key %d to the console\n", ev.key);
451 if (kbd_dev->console_phone < 0) {
452 usb_log_warning(
453 "Connection to console not ready, key discarded.\n");
454 return;
455 }
456
457 async_msg_4(kbd_dev->console_phone, KBD_EVENT, ev.type, ev.key,
458 ev.mods, ev.c);
459}
460
461/*----------------------------------------------------------------------------*/
462
463static inline int usb_kbd_is_lock(unsigned int key_code)
464{
465 return (key_code == KC_NUM_LOCK
466 || key_code == KC_SCROLL_LOCK
467 || key_code == KC_CAPS_LOCK);
468}
469
470/*----------------------------------------------------------------------------*/
471/**
472 * Checks if some keys were pressed or released and generates key events.
473 *
474 * An event is created only when key is pressed or released. Besides handling
475 * the events (usb_kbd_push_ev()), the auto-repeat fibril is notified about
476 * key presses and releases (see usb_kbd_repeat_start() and
477 * usb_kbd_repeat_stop()).
478 *
479 * @param kbd_dev Keyboard device structure.
480 * @param key_codes Parsed keyboard report - codes of currently pressed keys
481 * according to HID Usage Tables.
482 * @param count Number of key codes in report (size of the report).
483 *
484 * @sa usb_kbd_push_ev(), usb_kbd_repeat_start(), usb_kbd_repeat_stop()
485 */
486static void usb_kbd_check_key_changes(usb_hid_dev_t *hid_dev,
487 usb_kbd_t *kbd_dev, const uint8_t *key_codes, size_t count)
488{
489 unsigned int key;
490 unsigned int i, j;
491
492 /*
493 * First of all, check if the kbd have reported phantom state.
494 *
495 * As there is no way to distinguish keys from modifiers, we do not have
496 * a way to check that 'all keys report Error Rollover'. We thus check
497 * if there is at least one such error and in such case we ignore the
498 * whole input report.
499 */
500 i = 0;
501 while (i < count && key_codes[i] != ERROR_ROLLOVER) {
502 ++i;
503 }
504 if (i != count) {
505 usb_log_debug("Phantom state occured.\n");
506 // phantom state, do nothing
507 return;
508 }
509
510 /* TODO: quite dummy right now, think of better implementation */
511 assert(count == kbd_dev->key_count);
512
513 /*
514 * 1) Key releases
515 */
516 for (j = 0; j < count; ++j) {
517 // try to find the old key in the new key list
518 i = 0;
519 while (i < kbd_dev->key_count
520 && key_codes[i] != kbd_dev->keys[j]) {
521 ++i;
522 }
523
524 if (i == count) {
525 // not found, i.e. the key was released
526 key = usbhid_parse_scancode(kbd_dev->keys[j]);
527 if (!usb_kbd_is_lock(key)) {
528 usb_kbd_repeat_stop(kbd_dev, key);
529 }
530 usb_kbd_push_ev(hid_dev, kbd_dev, KEY_RELEASE, key);
531 usb_log_debug2("Key released: %d\n", key);
532 } else {
533 // found, nothing happens
534 }
535 }
536
537 /*
538 * 1) Key presses
539 */
540 for (i = 0; i < kbd_dev->key_count; ++i) {
541 // try to find the new key in the old key list
542 j = 0;
543 while (j < count && kbd_dev->keys[j] != key_codes[i]) {
544 ++j;
545 }
546
547 if (j == count) {
548 // not found, i.e. new key pressed
549 key = usbhid_parse_scancode(key_codes[i]);
550 usb_log_debug2("Key pressed: %d (keycode: %d)\n", key,
551 key_codes[i]);
552 usb_kbd_push_ev(hid_dev, kbd_dev, KEY_PRESS,
553 key);
554 if (!usb_kbd_is_lock(key)) {
555 usb_kbd_repeat_start(kbd_dev, key);
556 }
557 } else {
558 // found, nothing happens
559 }
560 }
561
562 memcpy(kbd_dev->keys, key_codes, count);
563
564 usb_log_debug("New stored keycodes: %s\n",
565 usb_debug_str_buffer(kbd_dev->keys, kbd_dev->key_count, 0));
566}
567
568/*----------------------------------------------------------------------------*/
569/* Callbacks for parser */
570/*----------------------------------------------------------------------------*/
571/**
572 * Callback function for the HID report parser.
573 *
574 * This function is called by the HID report parser with the parsed report.
575 * The parsed report is used to check if any events occured (key was pressed or
576 * released, modifier was pressed or released).
577 *
578 * @param key_codes Parsed keyboard report - codes of currently pressed keys
579 * according to HID Usage Tables.
580 * @param count Number of key codes in report (size of the report).
581 * @param report_id
582 * @param arg User-specified argument. Expects pointer to the keyboard device
583 * structure representing the keyboard.
584 *
585 * @sa usb_kbd_check_key_changes(), usb_kbd_check_modifier_changes()
586 */
587static void usb_kbd_process_keycodes(const uint8_t *key_codes, size_t count,
588 uint8_t report_id, void *arg)
589{
590 if (arg == NULL) {
591 usb_log_warning("Missing argument in callback "
592 "usbhid_process_keycodes().\n");
593 return;
594 }
595
596 usb_hid_dev_t *hid_dev = (usb_hid_dev_t *)arg;
597
598 if (hid_dev->data == NULL) {
599 usb_log_warning("Missing KBD device structure in callback.\n");
600 return;
601 }
602
603 usb_kbd_t *kbd_dev = (usb_kbd_t *)hid_dev->data;
604
605 usb_log_debug("Got keys from parser (report id: %u): %s\n",
606 report_id, usb_debug_str_buffer(key_codes, count, 0));
607
608 if (count != kbd_dev->key_count) {
609 usb_log_warning("Number of received keycodes (%zu) differs from"
610 " expected (%zu).\n", count, kbd_dev->key_count);
611 return;
612 }
613
614 ///usb_kbd_check_modifier_changes(kbd_dev, key_codes, count);
615 usb_kbd_check_key_changes(hid_dev, kbd_dev, key_codes, count);
616}
617
618/*----------------------------------------------------------------------------*/
619/* General kbd functions */
620/*----------------------------------------------------------------------------*/
621/**
622 * Processes data received from the device in form of report.
623 *
624 * This function uses the HID report parser to translate the data received from
625 * the device into generic USB HID key codes and into generic modifiers bitmap.
626 * The parser then calls the given callback (usb_kbd_process_keycodes()).
627 *
628 * @note Currently, only the boot protocol is supported.
629 *
630 * @param kbd_dev Keyboard device structure (must be initialized).
631 * @param buffer Data from the keyboard (i.e. the report).
632 * @param actual_size Size of the data from keyboard (report size) in bytes.
633 *
634 * @sa usb_kbd_process_keycodes(), usb_hid_boot_keyboard_input_report(),
635 * usb_hid_parse_report().
636 */
637static void usb_kbd_process_data(usb_hid_dev_t *hid_dev,
638 uint8_t *buffer, size_t actual_size)
639{
640 assert(hid_dev->parser != NULL);
641
642 usb_log_debug("Calling usb_hid_parse_report() with "
643 "buffer %s\n", usb_debug_str_buffer(buffer, actual_size, 0));
644
645// int rc = usb_hid_boot_keyboard_input_report(buffer, actual_size,
646// callbacks, kbd_dev);
647 usb_hid_report_path_t *path = usb_hid_report_path();
648 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_KEYBOARD, 0);
649 //usb_hid_report_path_set_report_id(path, 0);
650
651 int rc = usb_hid_parse_report(hid_dev->parser, buffer,
652 actual_size, path,
653 USB_HID_PATH_COMPARE_END | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
654 &usb_kbd_parser_callbacks, hid_dev);
655
656 usb_hid_report_path_free(path);
657
658 if (rc != EOK) {
659 usb_log_warning("Error in usb_hid_boot_keyboard_input_report():"
660 "%s\n", str_error(rc));
661 }
662}
663
664/*----------------------------------------------------------------------------*/
665/* HID/KBD structure manipulation */
666/*----------------------------------------------------------------------------*/
667
668static void usb_kbd_mark_unusable(usb_kbd_t *kbd_dev)
669{
670 kbd_dev->initialized = USB_KBD_STATUS_TO_DESTROY;
671}
672
673/*----------------------------------------------------------------------------*/
674
675/**
676 * Creates a new USB/HID keyboard structure.
677 *
678 * The structure returned by this function is not initialized. Use
679 * usb_kbd_init() to initialize it prior to polling.
680 *
681 * @return New uninitialized structure for representing a USB/HID keyboard or
682 * NULL if not successful (memory error).
683 */
684static usb_kbd_t *usb_kbd_new(void)
685{
686 usb_kbd_t *kbd_dev =
687 (usb_kbd_t *)calloc(1, sizeof(usb_kbd_t));
688
689 if (kbd_dev == NULL) {
690 usb_log_fatal("No memory!\n");
691 return NULL;
692 }
693
694 kbd_dev->console_phone = -1;
695 kbd_dev->initialized = USB_KBD_STATUS_UNINITIALIZED;
696
697 return kbd_dev;
698}
699
700/*----------------------------------------------------------------------------*/
701/* API functions */
702/*----------------------------------------------------------------------------*/
703/**
704 * Initialization of the USB/HID keyboard structure.
705 *
706 * This functions initializes required structures from the device's descriptors.
707 *
708 * During initialization, the keyboard is switched into boot protocol, the idle
709 * rate is set to 0 (infinity), resulting in the keyboard only reporting event
710 * when a key is pressed or released. Finally, the LED lights are turned on
711 * according to the default setup of lock keys.
712 *
713 * @note By default, the keyboards is initialized with Num Lock turned on and
714 * other locks turned off.
715 *
716 * @param kbd_dev Keyboard device structure to be initialized.
717 * @param dev DDF device structure of the keyboard.
718 *
719 * @retval EOK if successful.
720 * @retval EINVAL if some parameter is not given.
721 * @return Other value inherited from function usbhid_dev_init().
722 */
723int usb_kbd_init(usb_hid_dev_t *hid_dev)
724{
725 usb_log_debug("Initializing HID/KBD structure...\n");
726
727 if (hid_dev == NULL) {
728 usb_log_error("Failed to init keyboard structure: no structure"
729 " given.\n");
730 return EINVAL;
731 }
732
733 usb_kbd_t *kbd_dev = usb_kbd_new();
734 if (kbd_dev == NULL) {
735 usb_log_error("Error while creating USB/HID KBD device "
736 "structure.\n");
737 return ENOMEM; // TODO: some other code??
738 }
739
740 /*
741 * TODO: make more general
742 */
743 usb_hid_report_path_t *path = usb_hid_report_path();
744 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_KEYBOARD, 0);
745
746 usb_hid_report_path_set_report_id(path, 0);
747
748 kbd_dev->key_count = usb_hid_report_input_length(
749 hid_dev->parser, path,
750 USB_HID_PATH_COMPARE_END | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY);
751 usb_hid_report_path_free(path);
752
753 usb_log_debug("Size of the input report: %zu\n", kbd_dev->key_count);
754
755 kbd_dev->keys = (uint8_t *)calloc(kbd_dev->key_count, sizeof(uint8_t));
756
757 if (kbd_dev->keys == NULL) {
758 usb_log_fatal("No memory!\n");
759 free(kbd_dev);
760 return ENOMEM;
761 }
762
763 /*
764 * Output report
765 */
766 kbd_dev->output_size = 0;
767 kbd_dev->output_buffer = usb_hid_report_output(hid_dev->parser,
768 &kbd_dev->output_size);
769 if (kbd_dev->output_buffer == NULL && kbd_dev->output_size != 0) {
770 usb_log_warning("Error creating output report buffer.\n");
771 free(kbd_dev->keys);
772 free(kbd_dev);
773 return ENOMEM;
774 }
775
776 usb_log_debug("Output buffer size: %zu\n", kbd_dev->output_size);
777
778 kbd_dev->led_path = usb_hid_report_path();
779 usb_hid_report_path_append_item(
780 kbd_dev->led_path, USB_HIDUT_PAGE_LED, 0);
781
782 kbd_dev->led_output_size = usb_hid_report_output_size(hid_dev->parser,
783 kbd_dev->led_path,
784 USB_HID_PATH_COMPARE_END | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY);
785
786 usb_log_debug("Output report size (in items): %zu\n",
787 kbd_dev->led_output_size);
788
789 kbd_dev->led_data = (int32_t *)calloc(
790 kbd_dev->led_output_size, sizeof(int32_t));
791
792 if (kbd_dev->led_data == NULL) {
793 usb_log_warning("Error creating buffer for LED output report."
794 "\n");
795 free(kbd_dev->keys);
796 usb_hid_report_output_free(kbd_dev->output_buffer);
797 free(kbd_dev);
798 return ENOMEM;
799 }
800
801 /*
802 * Modifiers and locks
803 */
804 kbd_dev->modifiers = 0;
805 kbd_dev->mods = DEFAULT_ACTIVE_MODS;
806 kbd_dev->lock_keys = 0;
807
808 /*
809 * Autorepeat
810 */
811 kbd_dev->repeat.key_new = 0;
812 kbd_dev->repeat.key_repeated = 0;
813 kbd_dev->repeat.delay_before = DEFAULT_DELAY_BEFORE_FIRST_REPEAT;
814 kbd_dev->repeat.delay_between = DEFAULT_REPEAT_DELAY;
815
816 kbd_dev->repeat_mtx = (fibril_mutex_t *)(
817 malloc(sizeof(fibril_mutex_t)));
818 if (kbd_dev->repeat_mtx == NULL) {
819 usb_log_fatal("No memory!\n");
820 free(kbd_dev->keys);
821 usb_hid_report_output_free(kbd_dev->output_buffer);
822 free(kbd_dev);
823 return ENOMEM;
824 }
825
826 fibril_mutex_initialize(kbd_dev->repeat_mtx);
827
828 // save the KBD device structure into the HID device structure
829 hid_dev->data = kbd_dev;
830
831 // set handler for incoming calls
832 hid_dev->ops.default_handler = default_connection_handler;
833
834 /*
835 * Set LEDs according to initial setup.
836 * Set Idle rate
837 */
838 usb_kbd_set_led(hid_dev, kbd_dev);
839
840 usbhid_req_set_idle(&hid_dev->usb_dev->ctrl_pipe,
841 hid_dev->usb_dev->interface_no, IDLE_RATE);
842
843 /*
844 * Create new fibril for auto-repeat
845 */
846 fid_t fid = fibril_create(usb_kbd_repeat_fibril, kbd_dev);
847 if (fid == 0) {
848 usb_log_error("Failed to start fibril for KBD auto-repeat");
849 return ENOMEM;
850 }
851 fibril_add_ready(fid);
852
853 kbd_dev->initialized = USB_KBD_STATUS_INITIALIZED;
854 usb_log_debug("HID/KBD device structure initialized.\n");
855
856 return EOK;
857}
858
859/*----------------------------------------------------------------------------*/
860
861bool usb_kbd_polling_callback(usb_hid_dev_t *hid_dev, uint8_t *buffer,
862 size_t buffer_size)
863{
864 if (hid_dev == NULL || buffer == NULL) {
865 // do not continue polling (???)
866 return false;
867 }
868
869 // TODO: add return value from this function
870 usb_kbd_process_data(hid_dev, buffer, buffer_size);
871
872 return true;
873}
874
875/*----------------------------------------------------------------------------*/
876
877int usb_kbd_is_initialized(const usb_kbd_t *kbd_dev)
878{
879 return (kbd_dev->initialized == USB_KBD_STATUS_INITIALIZED);
880}
881
882/*----------------------------------------------------------------------------*/
883
884int usb_kbd_is_ready_to_destroy(const usb_kbd_t *kbd_dev)
885{
886 return (kbd_dev->initialized == USB_KBD_STATUS_TO_DESTROY);
887}
888
889/*----------------------------------------------------------------------------*/
890/**
891 * Properly destroys the USB/HID keyboard structure.
892 *
893 * @param kbd_dev Pointer to the structure to be destroyed.
894 */
895void usb_kbd_free(usb_kbd_t **kbd_dev)
896{
897 if (kbd_dev == NULL || *kbd_dev == NULL) {
898 return;
899 }
900
901 // hangup phone to the console
902 async_hangup((*kbd_dev)->console_phone);
903
904 if ((*kbd_dev)->repeat_mtx != NULL) {
905 /* TODO: replace by some check and wait */
906 assert(!fibril_mutex_is_locked((*kbd_dev)->repeat_mtx));
907 free((*kbd_dev)->repeat_mtx);
908 }
909
910 // free the output buffer
911 usb_hid_report_output_free((*kbd_dev)->output_buffer);
912
913 free(*kbd_dev);
914 *kbd_dev = NULL;
915}
916
917/*----------------------------------------------------------------------------*/
918
919void usb_kbd_deinit(usb_hid_dev_t *hid_dev)
920{
921 if (hid_dev == NULL) {
922 return;
923 }
924
925 if (hid_dev->data != NULL) {
926 usb_kbd_t *kbd_dev = (usb_kbd_t *)hid_dev->data;
927 if (usb_kbd_is_initialized(kbd_dev)) {
928 usb_kbd_mark_unusable(kbd_dev);
929 } else {
930 usb_kbd_free(&kbd_dev);
931 }
932 }
933}
934
935/*----------------------------------------------------------------------------*/
936
937int usb_kbd_set_boot_protocol(usb_hid_dev_t *hid_dev)
938{
939 int rc = usb_hid_parse_report_descriptor(hid_dev->parser,
940 USB_KBD_BOOT_REPORT_DESCRIPTOR,
941 USB_KBD_BOOT_REPORT_DESCRIPTOR_SIZE);
942
943 if (rc != EOK) {
944 usb_log_error("Failed to parse boot report descriptor: %s\n",
945 str_error(rc));
946 return rc;
947 }
948
949 rc = usbhid_req_set_protocol(&hid_dev->usb_dev->ctrl_pipe,
950 hid_dev->usb_dev->interface_no, USB_HID_PROTOCOL_BOOT);
951
952 if (rc != EOK) {
953 usb_log_warning("Failed to set boot protocol to the device: "
954 "%s\n", str_error(rc));
955 return rc;
956 }
957
958 return EOK;
959}
960
961/**
962 * @}
963 */
Note: See TracBrowser for help on using the repository browser.