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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a372663 was 00b13408, checked in by Lubos Slovak <lubos.slovak@…>, 14 years ago

Fixed destroying of KBD structure when polling ended.

The structure must be destroyed in the autorepeat fibril, as there is
no nice way to stop the fibril on demand.

  • Property mode set to 100644
File size: 30.4 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 int rc = usb_hid_parse_report(kbd_dev->hid_dev->parser, buffer,
588 actual_size, callbacks, kbd_dev);
589
590 if (rc != EOK) {
591 usb_log_warning("Error in usb_hid_boot_keyboard_input_report():"
592 "%s\n", str_error(rc));
593 }
594}
595
596/*----------------------------------------------------------------------------*/
597/* HID/KBD structure manipulation */
598/*----------------------------------------------------------------------------*/
599/**
600 * Creates a new USB/HID keyboard structure.
601 *
602 * The structure returned by this function is not initialized. Use
603 * usbhid_kbd_init() to initialize it prior to polling.
604 *
605 * @return New uninitialized structure for representing a USB/HID keyboard or
606 * NULL if not successful (memory error).
607 */
608static usbhid_kbd_t *usbhid_kbd_new(void)
609{
610 usbhid_kbd_t *kbd_dev =
611 (usbhid_kbd_t *)malloc(sizeof(usbhid_kbd_t));
612
613 if (kbd_dev == NULL) {
614 usb_log_fatal("No memory!\n");
615 return NULL;
616 }
617
618 memset(kbd_dev, 0, sizeof(usbhid_kbd_t));
619
620 kbd_dev->hid_dev = usbhid_dev_new();
621 if (kbd_dev->hid_dev == NULL) {
622 usb_log_fatal("Could not create HID device structure.\n");
623 return NULL;
624 }
625
626 kbd_dev->console_phone = -1;
627 kbd_dev->initialized = USBHID_KBD_STATUS_UNINITIALIZED;
628
629 return kbd_dev;
630}
631
632/*----------------------------------------------------------------------------*/
633
634static void usbhid_kbd_mark_unusable(usbhid_kbd_t *kbd_dev)
635{
636 kbd_dev->initialized = USBHID_KBD_STATUS_TO_DESTROY;
637}
638
639/*----------------------------------------------------------------------------*/
640/**
641 * Initialization of the USB/HID keyboard structure.
642 *
643 * This functions initializes required structures from the device's descriptors.
644 *
645 * During initialization, the keyboard is switched into boot protocol, the idle
646 * rate is set to 0 (infinity), resulting in the keyboard only reporting event
647 * when a key is pressed or released. Finally, the LED lights are turned on
648 * according to the default setup of lock keys.
649 *
650 * @note By default, the keyboards is initialized with Num Lock turned on and
651 * other locks turned off.
652 *
653 * @param kbd_dev Keyboard device structure to be initialized.
654 * @param dev DDF device structure of the keyboard.
655 *
656 * @retval EOK if successful.
657 * @retval EINVAL if some parameter is not given.
658 * @return Other value inherited from function usbhid_dev_init().
659 */
660static int usbhid_kbd_init(usbhid_kbd_t *kbd_dev, ddf_dev_t *dev)
661{
662 int rc;
663
664 usb_log_info("Initializing HID/KBD structure...\n");
665
666 if (kbd_dev == NULL) {
667 usb_log_error("Failed to init keyboard structure: no structure"
668 " given.\n");
669 return EINVAL;
670 }
671
672 if (dev == NULL) {
673 usb_log_error("Failed to init keyboard structure: no device"
674 " given.\n");
675 return EINVAL;
676 }
677
678 if (kbd_dev->initialized == USBHID_KBD_STATUS_INITIALIZED) {
679 usb_log_warning("Keyboard structure already initialized.\n");
680 return EINVAL;
681 }
682
683 rc = usbhid_dev_init(kbd_dev->hid_dev, dev, &poll_endpoint_description);
684
685 if (rc != EOK) {
686 usb_log_error("Failed to initialize HID device structure: %s\n",
687 str_error(rc));
688 return rc;
689 }
690
691 assert(kbd_dev->hid_dev->initialized == USBHID_KBD_STATUS_INITIALIZED);
692
693 // save the size of the report (boot protocol report by default)
694// kbd_dev->key_count = BOOTP_REPORT_SIZE;
695
696 usb_hid_report_path_t path;
697 path.usage_page = USB_HIDUT_PAGE_KEYBOARD;
698 kbd_dev->key_count = usb_hid_report_input_length(
699 kbd_dev->hid_dev->parser, &path);
700
701 usb_log_debug("Size of the input report: %zu\n", kbd_dev->key_count);
702
703 kbd_dev->keys = (uint8_t *)calloc(
704 kbd_dev->key_count, sizeof(uint8_t));
705
706 if (kbd_dev->keys == NULL) {
707 usb_log_fatal("No memory!\n");
708 return ENOMEM;
709 }
710
711 kbd_dev->modifiers = 0;
712 kbd_dev->mods = DEFAULT_ACTIVE_MODS;
713 kbd_dev->lock_keys = 0;
714
715 kbd_dev->repeat.key_new = 0;
716 kbd_dev->repeat.key_repeated = 0;
717 kbd_dev->repeat.delay_before = DEFAULT_DELAY_BEFORE_FIRST_REPEAT;
718 kbd_dev->repeat.delay_between = DEFAULT_REPEAT_DELAY;
719
720 kbd_dev->repeat_mtx = (fibril_mutex_t *)(
721 malloc(sizeof(fibril_mutex_t)));
722 if (kbd_dev->repeat_mtx == NULL) {
723 usb_log_fatal("No memory!\n");
724 free(kbd_dev->keys);
725 return ENOMEM;
726 }
727
728 fibril_mutex_initialize(kbd_dev->repeat_mtx);
729
730 /*
731 * Set boot protocol.
732 * Set LEDs according to initial setup.
733 * Set Idle rate
734 */
735 assert(kbd_dev->hid_dev != NULL);
736 assert(kbd_dev->hid_dev->initialized);
737 //usbhid_req_set_protocol(kbd_dev->hid_dev, USB_HID_PROTOCOL_BOOT);
738
739 usbhid_kbd_set_led(kbd_dev);
740
741 usbhid_req_set_idle(kbd_dev->hid_dev, IDLE_RATE);
742
743 kbd_dev->initialized = USBHID_KBD_STATUS_INITIALIZED;
744 usb_log_info("HID/KBD device structure initialized.\n");
745
746 return EOK;
747}
748
749/*----------------------------------------------------------------------------*/
750/* HID/KBD polling */
751/*----------------------------------------------------------------------------*/
752/**
753 * Main keyboard polling function.
754 *
755 * This function uses the Interrupt In pipe of the keyboard to poll for events.
756 * The keyboard is initialized in a way that it reports only when a key is
757 * pressed or released, so there is no actual need for any sleeping between
758 * polls (see usbhid_kbd_try_add_device() or usbhid_kbd_init()).
759 *
760 * @param kbd_dev Initialized keyboard structure representing the device to
761 * poll.
762 *
763 * @sa usbhid_kbd_process_data()
764 */
765static void usbhid_kbd_poll(usbhid_kbd_t *kbd_dev)
766{
767 int rc, sess_rc;
768 uint8_t buffer[BOOTP_BUFFER_SIZE];
769 size_t actual_size;
770
771 usb_log_info("Polling keyboard...\n");
772
773 if (!kbd_dev->initialized) {
774 usb_log_error("HID/KBD device not initialized!\n");
775 return;
776 }
777
778 assert(kbd_dev->hid_dev != NULL);
779 assert(kbd_dev->hid_dev->initialized);
780
781 while (true) {
782 sess_rc = usb_endpoint_pipe_start_session(
783 &kbd_dev->hid_dev->poll_pipe);
784 if (sess_rc != EOK) {
785 usb_log_warning("Failed to start a session: %s.\n",
786 str_error(sess_rc));
787 break;
788 }
789
790 rc = usb_endpoint_pipe_read(&kbd_dev->hid_dev->poll_pipe,
791 buffer, BOOTP_BUFFER_SIZE, &actual_size);
792
793 sess_rc = usb_endpoint_pipe_end_session(
794 &kbd_dev->hid_dev->poll_pipe);
795
796 if (rc != EOK) {
797 usb_log_warning("Error polling the keyboard: %s.\n",
798 str_error(rc));
799 break;
800 }
801
802 if (sess_rc != EOK) {
803 usb_log_warning("Error closing session: %s.\n",
804 str_error(sess_rc));
805 break;
806 }
807
808 /*
809 * If the keyboard answered with NAK, it returned no data.
810 * This implies that no change happened since last query.
811 */
812 if (actual_size == 0) {
813 usb_log_debug("Keyboard returned NAK\n");
814 continue;
815 }
816
817 /*
818 * TODO: Process pressed keys.
819 */
820 usb_log_debug("Calling usbhid_kbd_process_data()\n");
821 usbhid_kbd_process_data(kbd_dev, buffer, actual_size);
822
823 // disabled for now, no reason to sleep
824 //async_usleep(kbd_dev->hid_dev->poll_interval);
825 }
826}
827
828/*----------------------------------------------------------------------------*/
829/**
830 * Function executed by the main driver fibril.
831 *
832 * Just starts polling the keyboard for events.
833 *
834 * @param arg Initialized keyboard device structure (of type usbhid_kbd_t)
835 * representing the device.
836 *
837 * @retval EOK if the fibril finished polling the device.
838 * @retval EINVAL if no device was given in the argument.
839 *
840 * @sa usbhid_kbd_poll()
841 *
842 * @todo Change return value - only case when the fibril finishes is in case
843 * of some error, so the error should probably be propagated from function
844 * usbhid_kbd_poll() to here and up.
845 */
846static int usbhid_kbd_fibril(void *arg)
847{
848 if (arg == NULL) {
849 usb_log_error("No device!\n");
850 return EINVAL;
851 }
852
853 usbhid_kbd_t *kbd_dev = (usbhid_kbd_t *)arg;
854
855 usbhid_kbd_poll(kbd_dev);
856
857 // as there is another fibril using this device, so we must leave the
858 // structure to it, but mark it for destroying.
859 usbhid_kbd_mark_unusable(kbd_dev);
860 // at the end, properly destroy the KBD structure
861// usbhid_kbd_free(&kbd_dev);
862// assert(kbd_dev == NULL);
863
864 return EOK;
865}
866
867/*----------------------------------------------------------------------------*/
868/* API functions */
869/*----------------------------------------------------------------------------*/
870/**
871 * Function for adding a new device of type USB/HID/keyboard.
872 *
873 * This functions initializes required structures from the device's descriptors
874 * and starts new fibril for polling the keyboard for events and another one for
875 * handling auto-repeat of keys.
876 *
877 * During initialization, the keyboard is switched into boot protocol, the idle
878 * rate is set to 0 (infinity), resulting in the keyboard only reporting event
879 * when a key is pressed or released. Finally, the LED lights are turned on
880 * according to the default setup of lock keys.
881 *
882 * @note By default, the keyboards is initialized with Num Lock turned on and
883 * other locks turned off.
884 * @note Currently supports only boot-protocol keyboards.
885 *
886 * @param dev Device to add.
887 *
888 * @retval EOK if successful.
889 * @retval ENOMEM if there
890 * @return Other error code inherited from one of functions usbhid_kbd_init(),
891 * ddf_fun_bind() and ddf_fun_add_to_class().
892 *
893 * @sa usbhid_kbd_fibril(), usbhid_kbd_repeat_fibril()
894 */
895int usbhid_kbd_try_add_device(ddf_dev_t *dev)
896{
897 /*
898 * Create default function.
899 */
900 ddf_fun_t *kbd_fun = ddf_fun_create(dev, fun_exposed, "keyboard");
901 if (kbd_fun == NULL) {
902 usb_log_error("Could not create DDF function node.\n");
903 return ENOMEM;
904 }
905
906 /*
907 * Initialize device (get and process descriptors, get address, etc.)
908 */
909 usb_log_info("Initializing USB/HID KBD device...\n");
910
911 usbhid_kbd_t *kbd_dev = usbhid_kbd_new();
912 if (kbd_dev == NULL) {
913 usb_log_error("Error while creating USB/HID KBD device "
914 "structure.\n");
915 ddf_fun_destroy(kbd_fun);
916 return ENOMEM; // TODO: some other code??
917 }
918
919 int rc = usbhid_kbd_init(kbd_dev, dev);
920
921 if (rc != EOK) {
922 usb_log_error("Failed to initialize USB/HID KBD device.\n");
923 ddf_fun_destroy(kbd_fun);
924 usbhid_kbd_free(&kbd_dev);
925 return rc;
926 }
927
928 usb_log_info("USB/HID KBD device structure initialized.\n");
929
930 /*
931 * Store the initialized keyboard device and keyboard ops
932 * to the DDF function.
933 */
934 kbd_fun->driver_data = kbd_dev;
935 kbd_fun->ops = &keyboard_ops;
936
937 rc = ddf_fun_bind(kbd_fun);
938 if (rc != EOK) {
939 usb_log_error("Could not bind DDF function.\n");
940 // TODO: Can / should I destroy the DDF function?
941 ddf_fun_destroy(kbd_fun);
942 usbhid_kbd_free(&kbd_dev);
943 return rc;
944 }
945
946 rc = ddf_fun_add_to_class(kbd_fun, "keyboard");
947 if (rc != EOK) {
948 usb_log_error("Could not add DDF function to class 'keyboard'"
949 "\n");
950 // TODO: Can / should I destroy the DDF function?
951 ddf_fun_destroy(kbd_fun);
952 usbhid_kbd_free(&kbd_dev);
953 return rc;
954 }
955
956 /*
957 * Create new fibril for handling this keyboard
958 */
959 fid_t fid = fibril_create(usbhid_kbd_fibril, kbd_dev);
960 if (fid == 0) {
961 usb_log_error("Failed to start fibril for KBD device\n");
962 return ENOMEM;
963 }
964 fibril_add_ready(fid);
965
966 /*
967 * Create new fibril for auto-repeat
968 */
969 fid = fibril_create(usbhid_kbd_repeat_fibril, kbd_dev);
970 if (fid == 0) {
971 usb_log_error("Failed to start fibril for KBD auto-repeat");
972 return ENOMEM;
973 }
974 fibril_add_ready(fid);
975
976 (void)keyboard_ops;
977
978 /*
979 * Hurrah, device is initialized.
980 */
981 return EOK;
982}
983
984/*----------------------------------------------------------------------------*/
985
986int usbhid_kbd_is_usable(const usbhid_kbd_t *kbd_dev)
987{
988 return (kbd_dev->initialized == USBHID_KBD_STATUS_INITIALIZED);
989}
990
991/*----------------------------------------------------------------------------*/
992/**
993 * Properly destroys the USB/HID keyboard structure.
994 *
995 * @param kbd_dev Pointer to the structure to be destroyed.
996 */
997void usbhid_kbd_free(usbhid_kbd_t **kbd_dev)
998{
999 if (kbd_dev == NULL || *kbd_dev == NULL) {
1000 return;
1001 }
1002
1003 // hangup phone to the console
1004 async_hangup((*kbd_dev)->console_phone);
1005
1006 if ((*kbd_dev)->hid_dev != NULL) {
1007 usbhid_dev_free(&(*kbd_dev)->hid_dev);
1008 assert((*kbd_dev)->hid_dev == NULL);
1009 }
1010
1011 if ((*kbd_dev)->repeat_mtx != NULL) {
1012 /* TODO: replace by some check and wait */
1013 assert(!fibril_mutex_is_locked((*kbd_dev)->repeat_mtx));
1014 free((*kbd_dev)->repeat_mtx);
1015 }
1016
1017 free(*kbd_dev);
1018 *kbd_dev = NULL;
1019}
1020
1021/**
1022 * @}
1023 */
Note: See TracBrowser for help on using the repository browser.