source: mainline/uspace/drv/usbhid/kbddev.c@ b53d3b7

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b53d3b7 was b53d3b7, checked in by Matej Klonfar <maklf@…>, 14 years ago

bitmap data parsing bug fix
parser initialization check bug fix
querying report data by usage path

  • Property mode set to 100644
File size: 30.8 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/classes/hid.h>
49#include <usb/pipes.h>
50#include <usb/debug.h>
51#include <usb/classes/hidparser.h>
52#include <usb/classes/classes.h>
53#include <usb/classes/hidut.h>
54
55#include "kbddev.h"
56#include "hiddev.h"
57#include "hidreq.h"
58#include "layout.h"
59#include "conv.h"
60#include "kbdrepeat.h"
61
62/*----------------------------------------------------------------------------*/
63/** Default modifiers when the keyboard is initialized. */
64static const unsigned DEFAULT_ACTIVE_MODS = KM_NUM_LOCK;
65
66/** Boot protocol report size (key part). */
67static const size_t BOOTP_REPORT_SIZE = 6;
68
69/** Boot protocol total report size. */
70static const size_t BOOTP_BUFFER_SIZE = 8;
71
72/** Boot protocol output report size. */
73static const size_t BOOTP_BUFFER_OUT_SIZE = 1;
74
75/** Boot protocol error key code. */
76static const uint8_t BOOTP_ERROR_ROLLOVER = 1;
77
78/** Default idle rate for keyboards. */
79static const uint8_t IDLE_RATE = 0;
80
81/** Delay before a pressed key starts auto-repeating. */
82static const unsigned int DEFAULT_DELAY_BEFORE_FIRST_REPEAT = 500 * 1000;
83
84/** Delay between two repeats of a pressed key when auto-repeating. */
85static const unsigned int DEFAULT_REPEAT_DELAY = 50 * 1000;
86
87/** Keyboard polling endpoint description for boot protocol class. */
88static usb_endpoint_description_t poll_endpoint_description = {
89 .transfer_type = USB_TRANSFER_INTERRUPT,
90 .direction = USB_DIRECTION_IN,
91 .interface_class = USB_CLASS_HID,
92 .interface_subclass = USB_HID_SUBCLASS_BOOT,
93 .interface_protocol = USB_HID_PROTOCOL_KEYBOARD,
94 .flags = 0
95};
96
97typedef enum usbhid_kbd_flags {
98 USBHID_KBD_STATUS_UNINITIALIZED = 0,
99 USBHID_KBD_STATUS_INITIALIZED = 1,
100 USBHID_KBD_STATUS_TO_DESTROY = -1
101} usbhid_kbd_flags;
102
103/*----------------------------------------------------------------------------*/
104/* Keyboard layouts */
105/*----------------------------------------------------------------------------*/
106
107#define NUM_LAYOUTS 3
108
109/** Keyboard layout map. */
110static layout_op_t *layout[NUM_LAYOUTS] = {
111 &us_qwerty_op,
112 &us_dvorak_op,
113 &cz_op
114};
115
116static int active_layout = 0;
117
118/*----------------------------------------------------------------------------*/
119/* Modifier constants */
120/*----------------------------------------------------------------------------*/
121/** Mapping of USB modifier key codes to generic modifier key codes. */
122static const keycode_t usbhid_modifiers_keycodes[USB_HID_MOD_COUNT] = {
123 KC_LCTRL, /* USB_HID_MOD_LCTRL */
124 KC_LSHIFT, /* USB_HID_MOD_LSHIFT */
125 KC_LALT, /* USB_HID_MOD_LALT */
126 0, /* USB_HID_MOD_LGUI */
127 KC_RCTRL, /* USB_HID_MOD_RCTRL */
128 KC_RSHIFT, /* USB_HID_MOD_RSHIFT */
129 KC_RALT, /* USB_HID_MOD_RALT */
130 0, /* USB_HID_MOD_RGUI */
131};
132
133typedef enum usbhid_lock_code {
134 USBHID_LOCK_NUM = 0x53,
135 USBHID_LOCK_CAPS = 0x39,
136 USBHID_LOCK_SCROLL = 0x47,
137 USBHID_LOCK_COUNT = 3
138} usbhid_lock_code;
139
140static const usbhid_lock_code usbhid_lock_codes[USBHID_LOCK_COUNT] = {
141 USBHID_LOCK_NUM,
142 USBHID_LOCK_CAPS,
143 USBHID_LOCK_SCROLL
144};
145
146/*----------------------------------------------------------------------------*/
147/* IPC method handler */
148/*----------------------------------------------------------------------------*/
149
150static void default_connection_handler(ddf_fun_t *, ipc_callid_t, ipc_call_t *);
151static ddf_dev_ops_t keyboard_ops = {
152 .default_handler = default_connection_handler
153};
154
155/**
156 * Default handler for IPC methods not handled by DDF.
157 *
158 * Currently recognizes only one method (IPC_M_CONNECT_TO_ME), in which case it
159 * assumes the caller is the console and thus it stores IPC phone to it for
160 * later use by the driver to notify about key events.
161 *
162 * @param fun Device function handling the call.
163 * @param icallid Call id.
164 * @param icall Call data.
165 */
166void default_connection_handler(ddf_fun_t *fun,
167 ipc_callid_t icallid, ipc_call_t *icall)
168{
169 sysarg_t method = IPC_GET_IMETHOD(*icall);
170
171 usbhid_kbd_t *kbd_dev = (usbhid_kbd_t *)fun->driver_data;
172 assert(kbd_dev != NULL);
173
174 if (method == IPC_M_CONNECT_TO_ME) {
175 int callback = IPC_GET_ARG5(*icall);
176
177 if (kbd_dev->console_phone != -1) {
178 async_answer_0(icallid, ELIMIT);
179 return;
180 }
181
182 kbd_dev->console_phone = callback;
183 async_answer_0(icallid, EOK);
184 return;
185 }
186
187 async_answer_0(icallid, EINVAL);
188}
189
190/*----------------------------------------------------------------------------*/
191/* Key processing functions */
192/*----------------------------------------------------------------------------*/
193/**
194 * Handles turning of LED lights on and off.
195 *
196 * In case of USB keyboards, the LEDs are handled in the driver, not in the
197 * device. When there should be a change (lock key was pressed), the driver
198 * uses a Set_Report request sent to the device to set the state of the LEDs.
199 *
200 * This functions sets the LED lights according to current settings of modifiers
201 * kept in the keyboard device structure.
202 *
203 * @param kbd_dev Keyboard device structure.
204 */
205static void usbhid_kbd_set_led(usbhid_kbd_t *kbd_dev)
206{
207 uint8_t buffer[BOOTP_BUFFER_OUT_SIZE];
208 int rc= 0;
209
210 memset(buffer, 0, BOOTP_BUFFER_OUT_SIZE);
211 uint8_t leds = 0;
212
213 if (kbd_dev->mods & KM_NUM_LOCK) {
214 leds |= USB_HID_LED_NUM_LOCK;
215 }
216
217 if (kbd_dev->mods & KM_CAPS_LOCK) {
218 leds |= USB_HID_LED_CAPS_LOCK;
219 }
220
221 if (kbd_dev->mods & KM_SCROLL_LOCK) {
222 leds |= USB_HID_LED_SCROLL_LOCK;
223 }
224
225 // TODO: COMPOSE and KANA
226
227 usb_log_debug("Creating output report.\n");
228 usb_log_debug("Leds: 0x%x\n", leds);
229 if ((rc = usb_hid_boot_keyboard_output_report(
230 leds, buffer, BOOTP_BUFFER_OUT_SIZE)) != EOK) {
231 usb_log_warning("Error composing output report to the keyboard:"
232 "%s.\n", str_error(rc));
233 return;
234 }
235
236 usb_log_debug("Output report buffer: %s\n",
237 usb_debug_str_buffer(buffer, BOOTP_BUFFER_OUT_SIZE, 0));
238
239 assert(kbd_dev->hid_dev != NULL);
240 assert(kbd_dev->hid_dev->initialized == USBHID_KBD_STATUS_INITIALIZED);
241 usbhid_req_set_report(kbd_dev->hid_dev, USB_HID_REPORT_TYPE_OUTPUT,
242 buffer, BOOTP_BUFFER_OUT_SIZE);
243}
244
245/*----------------------------------------------------------------------------*/
246/**
247 * Processes key events.
248 *
249 * @note This function was copied from AT keyboard driver and modified to suit
250 * USB keyboard.
251 *
252 * @note Lock keys are not sent to the console, as they are completely handled
253 * in the driver. It may, however, be required later that the driver
254 * sends also these keys to application (otherwise it cannot use those
255 * keys at all).
256 *
257 * @param kbd_dev Keyboard device structure.
258 * @param type Type of the event (press / release). Recognized values:
259 * KEY_PRESS, KEY_RELEASE
260 * @param key Key code of the key according to HID Usage Tables.
261 */
262void usbhid_kbd_push_ev(usbhid_kbd_t *kbd_dev, int type, unsigned int key)
263{
264 console_event_t ev;
265 unsigned mod_mask;
266
267 /*
268 * These parts are copy-pasted from the AT keyboard driver.
269 *
270 * They definitely require some refactoring, but will keep it for later
271 * when the console and keyboard system is changed in HelenOS.
272 */
273 switch (key) {
274 case KC_LCTRL: mod_mask = KM_LCTRL; break;
275 case KC_RCTRL: mod_mask = KM_RCTRL; break;
276 case KC_LSHIFT: mod_mask = KM_LSHIFT; break;
277 case KC_RSHIFT: mod_mask = KM_RSHIFT; break;
278 case KC_LALT: mod_mask = KM_LALT; break;
279 case KC_RALT: mod_mask = KM_RALT; break;
280 default: mod_mask = 0; break;
281 }
282
283 if (mod_mask != 0) {
284 if (type == KEY_PRESS)
285 kbd_dev->mods = kbd_dev->mods | mod_mask;
286 else
287 kbd_dev->mods = kbd_dev->mods & ~mod_mask;
288 }
289
290 switch (key) {
291 case KC_CAPS_LOCK: mod_mask = KM_CAPS_LOCK; break;
292 case KC_NUM_LOCK: mod_mask = KM_NUM_LOCK; break;
293 case KC_SCROLL_LOCK: mod_mask = KM_SCROLL_LOCK; break;
294 default: mod_mask = 0; break;
295 }
296
297 if (mod_mask != 0) {
298 if (type == KEY_PRESS) {
299 /*
300 * Only change lock state on transition from released
301 * to pressed. This prevents autorepeat from messing
302 * up the lock state.
303 */
304 unsigned int locks_old = kbd_dev->lock_keys;
305
306 kbd_dev->mods =
307 kbd_dev->mods ^ (mod_mask & ~kbd_dev->lock_keys);
308 kbd_dev->lock_keys = kbd_dev->lock_keys | mod_mask;
309
310 /* Update keyboard lock indicator lights. */
311 if (kbd_dev->lock_keys != locks_old) {
312 usbhid_kbd_set_led(kbd_dev);
313 }
314 } else {
315 kbd_dev->lock_keys = kbd_dev->lock_keys & ~mod_mask;
316 }
317 }
318
319 if (key == KC_CAPS_LOCK || key == KC_NUM_LOCK || key == KC_SCROLL_LOCK) {
320 // do not send anything to the console, this is our business
321 return;
322 }
323
324 if (type == KEY_PRESS && (kbd_dev->mods & KM_LCTRL) && key == KC_F1) {
325 active_layout = 0;
326 layout[active_layout]->reset();
327 return;
328 }
329
330 if (type == KEY_PRESS && (kbd_dev->mods & KM_LCTRL) && key == KC_F2) {
331 active_layout = 1;
332 layout[active_layout]->reset();
333 return;
334 }
335
336 if (type == KEY_PRESS && (kbd_dev->mods & KM_LCTRL) && key == KC_F3) {
337 active_layout = 2;
338 layout[active_layout]->reset();
339 return;
340 }
341
342 ev.type = type;
343 ev.key = key;
344 ev.mods = kbd_dev->mods;
345
346 ev.c = layout[active_layout]->parse_ev(&ev);
347
348 usb_log_debug2("Sending key %d to the console\n", ev.key);
349 if (kbd_dev->console_phone < 0) {
350 usb_log_warning(
351 "Connection to console not ready, key discarded.\n");
352 return;
353 }
354
355 async_msg_4(kbd_dev->console_phone, KBD_EVENT, ev.type, ev.key,
356 ev.mods, ev.c);
357}
358
359/*----------------------------------------------------------------------------*/
360/**
361 * Checks if modifiers were pressed or released and generates key events.
362 *
363 * @param kbd_dev Keyboard device structure.
364 * @param modifiers Bitmap of modifiers.
365 *
366 * @sa usbhid_kbd_push_ev()
367 */
368//static void usbhid_kbd_check_modifier_changes(usbhid_kbd_t *kbd_dev,
369// const uint8_t *key_codes, size_t count)
370//{
371// /*
372// * TODO: why the USB keyboard has NUM_, SCROLL_ and CAPS_LOCK
373// * both as modifiers and as keyUSB_HID_LOCK_COUNTs with their own scancodes???
374// *
375// * modifiers should be sent as normal keys to usbhid_parse_scancode()!!
376// * so maybe it would be better if I received it from report parser in
377// * that way
378// */
379
380// int i;
381// for (i = 0; i < count; ++i) {
382// if ((modifiers & usb_hid_modifiers_consts[i]) &&
383// !(kbd_dev->modifiers & usb_hid_modifiers_consts[i])) {
384// // modifier pressed
385// if (usbhid_modifiers_keycodes[i] != 0) {
386// usbhid_kbd_push_ev(kbd_dev, KEY_PRESS,
387// usbhid_modifiers_keycodes[i]);
388// }
389// } else if (!(modifiers & usb_hid_modifiers_consts[i]) &&
390// (kbd_dev->modifiers & usb_hid_modifiers_consts[i])) {
391// // modifier released
392// if (usbhid_modifiers_keycodes[i] != 0) {
393// usbhid_kbd_push_ev(kbd_dev, KEY_RELEASE,
394// usbhid_modifiers_keycodes[i]);
395// }
396// } // no change
397// }
398
399// kbd_dev->modifiers = modifiers;
400//}
401
402/*----------------------------------------------------------------------------*/
403
404static inline int usbhid_kbd_is_lock(unsigned int key_code)
405{
406 return (key_code == KC_NUM_LOCK
407 || key_code == KC_SCROLL_LOCK
408 || key_code == KC_CAPS_LOCK);
409}
410
411/*----------------------------------------------------------------------------*/
412/**
413 * Checks if some keys were pressed or released and generates key events.
414 *
415 * An event is created only when key is pressed or released. Besides handling
416 * the events (usbhid_kbd_push_ev()), the auto-repeat fibril is notified about
417 * key presses and releases (see usbhid_kbd_repeat_start() and
418 * usbhid_kbd_repeat_stop()).
419 *
420 * @param kbd_dev Keyboard device structure.
421 * @param key_codes Parsed keyboard report - codes of currently pressed keys
422 * according to HID Usage Tables.
423 * @param count Number of key codes in report (size of the report).
424 *
425 * @sa usbhid_kbd_push_ev(), usbhid_kbd_repeat_start(), usbhid_kbd_repeat_stop()
426 */
427static void usbhid_kbd_check_key_changes(usbhid_kbd_t *kbd_dev,
428 const uint8_t *key_codes, size_t count)
429{
430 unsigned int key;
431 unsigned int i, j;
432
433 /*
434 * First of all, check if the kbd have reported phantom state.
435 *
436 * TODO: this must be changed as we don't know which keys are modifiers
437 * and which are regular keys.
438 */
439 i = 0;
440 // all fields should report Error Rollover
441 while (i < count &&
442 key_codes[i] == BOOTP_ERROR_ROLLOVER) {
443 ++i;
444 }
445 if (i == count) {
446 usb_log_debug("Phantom state occured.\n");
447 // phantom state, do nothing
448 return;
449 }
450
451 /* TODO: quite dummy right now, think of better implementation */
452 assert(count == kbd_dev->key_count);
453
454 /*
455 * 1) Key releases
456 */
457 for (j = 0; j < count; ++j) {
458 // try to find the old key in the new key list
459 i = 0;
460 while (i < kbd_dev->key_count
461 && key_codes[i] != kbd_dev->keys[j]) {
462 ++i;
463 }
464
465 if (i == count) {
466 // not found, i.e. the key was released
467 key = usbhid_parse_scancode(kbd_dev->keys[j]);
468 if (!usbhid_kbd_is_lock(key)) {
469 usbhid_kbd_repeat_stop(kbd_dev, key);
470 }
471 usbhid_kbd_push_ev(kbd_dev, KEY_RELEASE, key);
472 usb_log_debug2("Key released: %d\n", key);
473 } else {
474 // found, nothing happens
475 }
476 }
477
478 /*
479 * 1) Key presses
480 */
481 for (i = 0; i < kbd_dev->key_count; ++i) {
482 // try to find the new key in the old key list
483 j = 0;
484 while (j < count && kbd_dev->keys[j] != key_codes[i]) {
485 ++j;
486 }
487
488 if (j == count) {
489 // not found, i.e. new key pressed
490 key = usbhid_parse_scancode(key_codes[i]);
491 usb_log_debug2("Key pressed: %d (keycode: %d)\n", key,
492 key_codes[i]);
493 usbhid_kbd_push_ev(kbd_dev, KEY_PRESS, key);
494 if (!usbhid_kbd_is_lock(key)) {
495 usbhid_kbd_repeat_start(kbd_dev, key);
496 }
497 } else {
498 // found, nothing happens
499 }
500 }
501
502 memcpy(kbd_dev->keys, key_codes, count);
503
504 usb_log_debug("New stored keycodes: %s\n",
505 usb_debug_str_buffer(kbd_dev->keys, kbd_dev->key_count, 0));
506}
507
508/*----------------------------------------------------------------------------*/
509/* Callbacks for parser */
510/*----------------------------------------------------------------------------*/
511/**
512 * Callback function for the HID report parser.
513 *
514 * This function is called by the HID report parser with the parsed report.
515 * The parsed report is used to check if any events occured (key was pressed or
516 * released, modifier was pressed or released).
517 *
518 * @param key_codes Parsed keyboard report - codes of currently pressed keys
519 * according to HID Usage Tables.
520 * @param count Number of key codes in report (size of the report).
521 * @param modifiers Bitmap of modifiers (Ctrl, Alt, Shift, GUI).
522 * @param arg User-specified argument. Expects pointer to the keyboard device
523 * structure representing the keyboard.
524 *
525 * @sa usbhid_kbd_check_key_changes(), usbhid_kbd_check_modifier_changes()
526 */
527static void usbhid_kbd_process_keycodes(const uint8_t *key_codes, size_t count,
528 uint8_t modifiers, void *arg)
529{
530 if (arg == NULL) {
531 usb_log_warning("Missing argument in callback "
532 "usbhid_process_keycodes().\n");
533 return;
534 }
535
536 usbhid_kbd_t *kbd_dev = (usbhid_kbd_t *)arg;
537 assert(kbd_dev != NULL);
538
539 usb_log_debug("Got keys from parser: %s\n",
540 usb_debug_str_buffer(key_codes, count, 0));
541
542 if (count != kbd_dev->key_count) {
543 usb_log_warning("Number of received keycodes (%d) differs from"
544 " expected number (%d).\n", count, kbd_dev->key_count);
545 return;
546 }
547
548 ///usbhid_kbd_check_modifier_changes(kbd_dev, key_codes, count);
549 usbhid_kbd_check_key_changes(kbd_dev, key_codes, count);
550}
551
552/*----------------------------------------------------------------------------*/
553/* General kbd functions */
554/*----------------------------------------------------------------------------*/
555/**
556 * Processes data received from the device in form of report.
557 *
558 * This function uses the HID report parser to translate the data received from
559 * the device into generic USB HID key codes and into generic modifiers bitmap.
560 * The parser then calls the given callback (usbhid_kbd_process_keycodes()).
561 *
562 * @note Currently, only the boot protocol is supported.
563 *
564 * @param kbd_dev Keyboard device structure (must be initialized).
565 * @param buffer Data from the keyboard (i.e. the report).
566 * @param actual_size Size of the data from keyboard (report size) in bytes.
567 *
568 * @sa usbhid_kbd_process_keycodes(), usb_hid_boot_keyboard_input_report().
569 */
570static void usbhid_kbd_process_data(usbhid_kbd_t *kbd_dev,
571 uint8_t *buffer, size_t actual_size)
572{
573 assert(kbd_dev->initialized == USBHID_KBD_STATUS_INITIALIZED);
574 assert(kbd_dev->hid_dev->parser != NULL);
575
576 usb_hid_report_in_callbacks_t *callbacks =
577 (usb_hid_report_in_callbacks_t *)malloc(
578 sizeof(usb_hid_report_in_callbacks_t));
579
580 callbacks->keyboard = usbhid_kbd_process_keycodes;
581
582 usb_log_debug("Calling usb_hid_parse_report() with "
583 "buffer %s\n", usb_debug_str_buffer(buffer, actual_size, 0));
584
585// int rc = usb_hid_boot_keyboard_input_report(buffer, actual_size,
586// callbacks, kbd_dev);
587 usb_hid_report_path_t *path = usb_hid_report_path();
588 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_KEYBOARD, 0);
589
590 int rc = usb_hid_parse_report(kbd_dev->hid_dev->parser, buffer,
591 actual_size, path, USB_HID_PATH_COMPARE_STRICT, callbacks, kbd_dev);
592
593 usb_hid_report_path_free (path);
594
595 if (rc != EOK) {
596 usb_log_warning("Error in usb_hid_boot_keyboard_input_report():"
597 "%s\n", str_error(rc));
598 }
599}
600
601/*----------------------------------------------------------------------------*/
602/* HID/KBD structure manipulation */
603/*----------------------------------------------------------------------------*/
604/**
605 * Creates a new USB/HID keyboard structure.
606 *
607 * The structure returned by this function is not initialized. Use
608 * usbhid_kbd_init() to initialize it prior to polling.
609 *
610 * @return New uninitialized structure for representing a USB/HID keyboard or
611 * NULL if not successful (memory error).
612 */
613static usbhid_kbd_t *usbhid_kbd_new(void)
614{
615 usbhid_kbd_t *kbd_dev =
616 (usbhid_kbd_t *)malloc(sizeof(usbhid_kbd_t));
617
618 if (kbd_dev == NULL) {
619 usb_log_fatal("No memory!\n");
620 return NULL;
621 }
622
623 memset(kbd_dev, 0, sizeof(usbhid_kbd_t));
624
625 kbd_dev->hid_dev = usbhid_dev_new();
626 if (kbd_dev->hid_dev == NULL) {
627 usb_log_fatal("Could not create HID device structure.\n");
628 return NULL;
629 }
630
631 kbd_dev->console_phone = -1;
632 kbd_dev->initialized = USBHID_KBD_STATUS_UNINITIALIZED;
633
634 return kbd_dev;
635}
636
637/*----------------------------------------------------------------------------*/
638
639static void usbhid_kbd_mark_unusable(usbhid_kbd_t *kbd_dev)
640{
641 kbd_dev->initialized = USBHID_KBD_STATUS_TO_DESTROY;
642}
643
644/*----------------------------------------------------------------------------*/
645/**
646 * Initialization of the USB/HID keyboard structure.
647 *
648 * This functions initializes required structures from the device's descriptors.
649 *
650 * During initialization, the keyboard is switched into boot protocol, the idle
651 * rate is set to 0 (infinity), resulting in the keyboard only reporting event
652 * when a key is pressed or released. Finally, the LED lights are turned on
653 * according to the default setup of lock keys.
654 *
655 * @note By default, the keyboards is initialized with Num Lock turned on and
656 * other locks turned off.
657 *
658 * @param kbd_dev Keyboard device structure to be initialized.
659 * @param dev DDF device structure of the keyboard.
660 *
661 * @retval EOK if successful.
662 * @retval EINVAL if some parameter is not given.
663 * @return Other value inherited from function usbhid_dev_init().
664 */
665static int usbhid_kbd_init(usbhid_kbd_t *kbd_dev, ddf_dev_t *dev)
666{
667 int rc;
668
669 usb_log_debug("Initializing HID/KBD structure...\n");
670
671 if (kbd_dev == NULL) {
672 usb_log_error("Failed to init keyboard structure: no structure"
673 " given.\n");
674 return EINVAL;
675 }
676
677 if (dev == NULL) {
678 usb_log_error("Failed to init keyboard structure: no device"
679 " given.\n");
680 return EINVAL;
681 }
682
683 if (kbd_dev->initialized == USBHID_KBD_STATUS_INITIALIZED) {
684 usb_log_warning("Keyboard structure already initialized.\n");
685 return EINVAL;
686 }
687
688 rc = usbhid_dev_init(kbd_dev->hid_dev, dev, &poll_endpoint_description);
689
690 if (rc != EOK) {
691 usb_log_error("Failed to initialize HID device structure: %s\n",
692 str_error(rc));
693 return rc;
694 }
695
696 assert(kbd_dev->hid_dev->initialized == USBHID_KBD_STATUS_INITIALIZED);
697
698 // save the size of the report (boot protocol report by default)
699// kbd_dev->key_count = BOOTP_REPORT_SIZE;
700
701 usb_hid_report_path_t *path;
702 path = usb_hid_report_path();
703 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_KEYBOARD, 0);
704 kbd_dev->key_count = usb_hid_report_input_length(
705 kbd_dev->hid_dev->parser, path, USB_HID_PATH_COMPARE_STRICT);
706 usb_hid_report_path_free (path);
707
708 usb_log_debug("Size of the input report: %zu\n", kbd_dev->key_count);
709
710 kbd_dev->keys = (uint8_t *)calloc(
711 kbd_dev->key_count, sizeof(uint8_t));
712
713 if (kbd_dev->keys == NULL) {
714 usb_log_fatal("No memory!\n");
715 return ENOMEM;
716 }
717
718 kbd_dev->modifiers = 0;
719 kbd_dev->mods = DEFAULT_ACTIVE_MODS;
720 kbd_dev->lock_keys = 0;
721
722 kbd_dev->repeat.key_new = 0;
723 kbd_dev->repeat.key_repeated = 0;
724 kbd_dev->repeat.delay_before = DEFAULT_DELAY_BEFORE_FIRST_REPEAT;
725 kbd_dev->repeat.delay_between = DEFAULT_REPEAT_DELAY;
726
727 kbd_dev->repeat_mtx = (fibril_mutex_t *)(
728 malloc(sizeof(fibril_mutex_t)));
729 if (kbd_dev->repeat_mtx == NULL) {
730 usb_log_fatal("No memory!\n");
731 free(kbd_dev->keys);
732 return ENOMEM;
733 }
734
735 fibril_mutex_initialize(kbd_dev->repeat_mtx);
736
737 /*
738 * Set boot protocol.
739 * Set LEDs according to initial setup.
740 * Set Idle rate
741 */
742 assert(kbd_dev->hid_dev != NULL);
743 assert(kbd_dev->hid_dev->initialized);
744 //usbhid_req_set_protocol(kbd_dev->hid_dev, USB_HID_PROTOCOL_BOOT);
745
746 usbhid_kbd_set_led(kbd_dev);
747
748 usbhid_req_set_idle(kbd_dev->hid_dev, IDLE_RATE);
749
750 kbd_dev->initialized = USBHID_KBD_STATUS_INITIALIZED;
751 usb_log_debug("HID/KBD device structure initialized.\n");
752
753 return EOK;
754}
755
756/*----------------------------------------------------------------------------*/
757/* HID/KBD polling */
758/*----------------------------------------------------------------------------*/
759/**
760 * Main keyboard polling function.
761 *
762 * This function uses the Interrupt In pipe of the keyboard to poll for events.
763 * The keyboard is initialized in a way that it reports only when a key is
764 * pressed or released, so there is no actual need for any sleeping between
765 * polls (see usbhid_kbd_try_add_device() or usbhid_kbd_init()).
766 *
767 * @param kbd_dev Initialized keyboard structure representing the device to
768 * poll.
769 *
770 * @sa usbhid_kbd_process_data()
771 */
772static void usbhid_kbd_poll(usbhid_kbd_t *kbd_dev)
773{
774 int rc, sess_rc;
775 uint8_t buffer[BOOTP_BUFFER_SIZE];
776 size_t actual_size;
777
778 usb_log_debug("Polling keyboard...\n");
779
780 if (!kbd_dev->initialized) {
781 usb_log_error("HID/KBD device not initialized!\n");
782 return;
783 }
784
785 assert(kbd_dev->hid_dev != NULL);
786 assert(kbd_dev->hid_dev->initialized);
787
788 while (true) {
789 sess_rc = usb_pipe_start_session(
790 &kbd_dev->hid_dev->poll_pipe);
791 if (sess_rc != EOK) {
792 usb_log_warning("Failed to start a session: %s.\n",
793 str_error(sess_rc));
794 break;
795 }
796
797 rc = usb_pipe_read(&kbd_dev->hid_dev->poll_pipe,
798 buffer, BOOTP_BUFFER_SIZE, &actual_size);
799
800 sess_rc = usb_pipe_end_session(
801 &kbd_dev->hid_dev->poll_pipe);
802
803 if (rc != EOK) {
804 usb_log_warning("Error polling the keyboard: %s.\n",
805 str_error(rc));
806 break;
807 }
808
809 if (sess_rc != EOK) {
810 usb_log_warning("Error closing session: %s.\n",
811 str_error(sess_rc));
812 break;
813 }
814
815 /*
816 * If the keyboard answered with NAK, it returned no data.
817 * This implies that no change happened since last query.
818 */
819 if (actual_size == 0) {
820 usb_log_debug("Keyboard returned NAK\n");
821 continue;
822 }
823
824 /*
825 * TODO: Process pressed keys.
826 */
827 usb_log_debug("Calling usbhid_kbd_process_data()\n");
828 usbhid_kbd_process_data(kbd_dev, buffer, actual_size);
829
830 // disabled for now, no reason to sleep
831 //async_usleep(kbd_dev->hid_dev->poll_interval);
832 }
833}
834
835/*----------------------------------------------------------------------------*/
836/**
837 * Function executed by the main driver fibril.
838 *
839 * Just starts polling the keyboard for events.
840 *
841 * @param arg Initialized keyboard device structure (of type usbhid_kbd_t)
842 * representing the device.
843 *
844 * @retval EOK if the fibril finished polling the device.
845 * @retval EINVAL if no device was given in the argument.
846 *
847 * @sa usbhid_kbd_poll()
848 *
849 * @todo Change return value - only case when the fibril finishes is in case
850 * of some error, so the error should probably be propagated from function
851 * usbhid_kbd_poll() to here and up.
852 */
853static int usbhid_kbd_fibril(void *arg)
854{
855 if (arg == NULL) {
856 usb_log_error("No device!\n");
857 return EINVAL;
858 }
859
860 usbhid_kbd_t *kbd_dev = (usbhid_kbd_t *)arg;
861
862 usbhid_kbd_poll(kbd_dev);
863
864 // as there is another fibril using this device, so we must leave the
865 // structure to it, but mark it for destroying.
866 usbhid_kbd_mark_unusable(kbd_dev);
867 // at the end, properly destroy the KBD structure
868// usbhid_kbd_free(&kbd_dev);
869// assert(kbd_dev == NULL);
870
871 return EOK;
872}
873
874/*----------------------------------------------------------------------------*/
875/* API functions */
876/*----------------------------------------------------------------------------*/
877/**
878 * Function for adding a new device of type USB/HID/keyboard.
879 *
880 * This functions initializes required structures from the device's descriptors
881 * and starts new fibril for polling the keyboard for events and another one for
882 * handling auto-repeat of keys.
883 *
884 * During initialization, the keyboard is switched into boot protocol, the idle
885 * rate is set to 0 (infinity), resulting in the keyboard only reporting event
886 * when a key is pressed or released. Finally, the LED lights are turned on
887 * according to the default setup of lock keys.
888 *
889 * @note By default, the keyboards is initialized with Num Lock turned on and
890 * other locks turned off.
891 * @note Currently supports only boot-protocol keyboards.
892 *
893 * @param dev Device to add.
894 *
895 * @retval EOK if successful.
896 * @retval ENOMEM if there
897 * @return Other error code inherited from one of functions usbhid_kbd_init(),
898 * ddf_fun_bind() and ddf_fun_add_to_class().
899 *
900 * @sa usbhid_kbd_fibril(), usbhid_kbd_repeat_fibril()
901 */
902int usbhid_kbd_try_add_device(ddf_dev_t *dev)
903{
904 /*
905 * Create default function.
906 */
907 ddf_fun_t *kbd_fun = ddf_fun_create(dev, fun_exposed, "keyboard");
908 if (kbd_fun == NULL) {
909 usb_log_error("Could not create DDF function node.\n");
910 return ENOMEM;
911 }
912
913 /*
914 * Initialize device (get and process descriptors, get address, etc.)
915 */
916 usb_log_debug("Initializing USB/HID KBD device...\n");
917
918 usbhid_kbd_t *kbd_dev = usbhid_kbd_new();
919 if (kbd_dev == NULL) {
920 usb_log_error("Error while creating USB/HID KBD device "
921 "structure.\n");
922 ddf_fun_destroy(kbd_fun);
923 return ENOMEM; // TODO: some other code??
924 }
925
926 int rc = usbhid_kbd_init(kbd_dev, dev);
927
928 if (rc != EOK) {
929 usb_log_error("Failed to initialize USB/HID KBD device.\n");
930 ddf_fun_destroy(kbd_fun);
931 usbhid_kbd_free(&kbd_dev);
932 return rc;
933 }
934
935 usb_log_debug("USB/HID KBD device structure initialized.\n");
936
937 /*
938 * Store the initialized keyboard device and keyboard ops
939 * to the DDF function.
940 */
941 kbd_fun->driver_data = kbd_dev;
942 kbd_fun->ops = &keyboard_ops;
943
944 rc = ddf_fun_bind(kbd_fun);
945 if (rc != EOK) {
946 usb_log_error("Could not bind DDF function: %s.\n",
947 str_error(rc));
948 // TODO: Can / should I destroy the DDF function?
949 ddf_fun_destroy(kbd_fun);
950 usbhid_kbd_free(&kbd_dev);
951 return rc;
952 }
953
954 rc = ddf_fun_add_to_class(kbd_fun, "keyboard");
955 if (rc != EOK) {
956 usb_log_error(
957 "Could not add DDF function to class 'keyboard': %s.\n",
958 str_error(rc));
959 // TODO: Can / should I destroy the DDF function?
960 ddf_fun_destroy(kbd_fun);
961 usbhid_kbd_free(&kbd_dev);
962 return rc;
963 }
964
965 /*
966 * Create new fibril for handling this keyboard
967 */
968 fid_t fid = fibril_create(usbhid_kbd_fibril, kbd_dev);
969 if (fid == 0) {
970 usb_log_error("Failed to start fibril for `%s' device.\n",
971 dev->name);
972 return ENOMEM;
973 }
974 fibril_add_ready(fid);
975
976 /*
977 * Create new fibril for auto-repeat
978 */
979 fid = fibril_create(usbhid_kbd_repeat_fibril, kbd_dev);
980 if (fid == 0) {
981 usb_log_error("Failed to start fibril for KBD auto-repeat");
982 return ENOMEM;
983 }
984 fibril_add_ready(fid);
985
986 (void)keyboard_ops;
987
988 /*
989 * Hurrah, device is initialized.
990 */
991 return EOK;
992}
993
994/*----------------------------------------------------------------------------*/
995
996int usbhid_kbd_is_usable(const usbhid_kbd_t *kbd_dev)
997{
998 return (kbd_dev->initialized == USBHID_KBD_STATUS_INITIALIZED);
999}
1000
1001/*----------------------------------------------------------------------------*/
1002/**
1003 * Properly destroys the USB/HID keyboard structure.
1004 *
1005 * @param kbd_dev Pointer to the structure to be destroyed.
1006 */
1007void usbhid_kbd_free(usbhid_kbd_t **kbd_dev)
1008{
1009 if (kbd_dev == NULL || *kbd_dev == NULL) {
1010 return;
1011 }
1012
1013 // hangup phone to the console
1014 async_hangup((*kbd_dev)->console_phone);
1015
1016 if ((*kbd_dev)->hid_dev != NULL) {
1017 usbhid_dev_free(&(*kbd_dev)->hid_dev);
1018 assert((*kbd_dev)->hid_dev == NULL);
1019 }
1020
1021 if ((*kbd_dev)->repeat_mtx != NULL) {
1022 /* TODO: replace by some check and wait */
1023 assert(!fibril_mutex_is_locked((*kbd_dev)->repeat_mtx));
1024 free((*kbd_dev)->repeat_mtx);
1025 }
1026
1027 free(*kbd_dev);
1028 *kbd_dev = NULL;
1029}
1030
1031/**
1032 * @}
1033 */
Note: See TracBrowser for help on using the repository browser.