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
RevLine 
[2391aaf]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>
[7ac73a4]39#include <stdio.h>
[2391aaf]40
41#include <io/keycode.h>
42#include <ipc/kbd.h>
[1c6c4092]43#include <async.h>
[dfe53af]44#include <fibril.h>
45#include <fibril_synch.h>
[2391aaf]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>
[fc5ed5d]53#include <usb/classes/hidut.h>
[2391aaf]54
55#include "kbddev.h"
56#include "hiddev.h"
57#include "hidreq.h"
58#include "layout.h"
59#include "conv.h"
[dfe53af]60#include "kbdrepeat.h"
[2391aaf]61
62/*----------------------------------------------------------------------------*/
[17ada7a]63/** Default modifiers when the keyboard is initialized. */
[2391aaf]64static const unsigned DEFAULT_ACTIVE_MODS = KM_NUM_LOCK;
[17ada7a]65
66/** Boot protocol report size (key part). */
[2391aaf]67static const size_t BOOTP_REPORT_SIZE = 6;
[17ada7a]68
69/** Boot protocol total report size. */
[2391aaf]70static const size_t BOOTP_BUFFER_SIZE = 8;
[17ada7a]71
72/** Boot protocol output report size. */
[2391aaf]73static const size_t BOOTP_BUFFER_OUT_SIZE = 1;
[17ada7a]74
75/** Boot protocol error key code. */
[d477734]76static const uint8_t BOOTP_ERROR_ROLLOVER = 1;
[17ada7a]77
78/** Default idle rate for keyboards. */
[6bb456c]79static const uint8_t IDLE_RATE = 0;
[2391aaf]80
[17ada7a]81/** Delay before a pressed key starts auto-repeating. */
[dfe53af]82static const unsigned int DEFAULT_DELAY_BEFORE_FIRST_REPEAT = 500 * 1000;
[17ada7a]83
84/** Delay between two repeats of a pressed key when auto-repeating. */
[dfe53af]85static const unsigned int DEFAULT_REPEAT_DELAY = 50 * 1000;
[2391aaf]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
[00b13408]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
[2391aaf]103/*----------------------------------------------------------------------------*/
104/* Keyboard layouts */
105/*----------------------------------------------------------------------------*/
106
107#define NUM_LAYOUTS 3
108
[17ada7a]109/** Keyboard layout map. */
[2391aaf]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/*----------------------------------------------------------------------------*/
[17ada7a]121/** Mapping of USB modifier key codes to generic modifier key codes. */
[2391aaf]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
[fc5ed5d]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
[2391aaf]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
[17ada7a]155/**
156 * Default handler for IPC methods not handled by DDF.
[2391aaf]157 *
[17ada7a]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.
[2391aaf]161 *
[17ada7a]162 * @param fun Device function handling the call.
[2391aaf]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;
[27270db]172 assert(kbd_dev != NULL);
[2391aaf]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 }
[27270db]186
[2391aaf]187 async_answer_0(icallid, EINVAL);
188}
189
190/*----------------------------------------------------------------------------*/
191/* Key processing functions */
192/*----------------------------------------------------------------------------*/
[17ada7a]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 */
[2391aaf]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
[6bb456c]236 usb_log_debug("Output report buffer: %s\n",
237 usb_debug_str_buffer(buffer, BOOTP_BUFFER_OUT_SIZE, 0));
[2391aaf]238
239 assert(kbd_dev->hid_dev != NULL);
[00b13408]240 assert(kbd_dev->hid_dev->initialized == USBHID_KBD_STATUS_INITIALIZED);
[35f0899]241 usbhid_req_set_report(kbd_dev->hid_dev, USB_HID_REPORT_TYPE_OUTPUT,
242 buffer, BOOTP_BUFFER_OUT_SIZE);
[2391aaf]243}
244
245/*----------------------------------------------------------------------------*/
[17ada7a]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 */
[dfe53af]262void usbhid_kbd_push_ev(usbhid_kbd_t *kbd_dev, int type, unsigned int key)
[2391aaf]263{
264 console_event_t ev;
265 unsigned mod_mask;
266
[48d2765]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 */
[2391aaf]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 */
[dfe53af]304 unsigned int locks_old = kbd_dev->lock_keys;
305
[2391aaf]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. */
[dfe53af]311 if (kbd_dev->lock_keys != locks_old) {
312 usbhid_kbd_set_led(kbd_dev);
313 }
[2391aaf]314 } else {
315 kbd_dev->lock_keys = kbd_dev->lock_keys & ~mod_mask;
316 }
317 }
318
[ac8285d]319 if (key == KC_CAPS_LOCK || key == KC_NUM_LOCK || key == KC_SCROLL_LOCK) {
[1c6c4092]320 // do not send anything to the console, this is our business
321 return;
322 }
323
[2391aaf]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);
[7a2f8ea0]349 if (kbd_dev->console_phone < 0) {
350 usb_log_warning(
351 "Connection to console not ready, key discarded.\n");
352 return;
353 }
[2391aaf]354
355 async_msg_4(kbd_dev->console_phone, KBD_EVENT, ev.type, ev.key,
356 ev.mods, ev.c);
357}
358
359/*----------------------------------------------------------------------------*/
[17ada7a]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 */
[fc5ed5d]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)
[2391aaf]405{
[fc5ed5d]406 return (key_code == KC_NUM_LOCK
407 || key_code == KC_SCROLL_LOCK
408 || key_code == KC_CAPS_LOCK);
[2391aaf]409}
410
411/*----------------------------------------------------------------------------*/
[17ada7a]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 */
[2391aaf]427static void usbhid_kbd_check_key_changes(usbhid_kbd_t *kbd_dev,
[17ada7a]428 const uint8_t *key_codes, size_t count)
[2391aaf]429{
430 unsigned int key;
431 unsigned int i, j;
432
[d477734]433 /*
434 * First of all, check if the kbd have reported phantom state.
[fc5ed5d]435 *
436 * TODO: this must be changed as we don't know which keys are modifiers
437 * and which are regular keys.
[d477734]438 */
439 i = 0;
440 // all fields should report Error Rollover
[17ada7a]441 while (i < count &&
[d477734]442 key_codes[i] == BOOTP_ERROR_ROLLOVER) {
443 ++i;
444 }
[17ada7a]445 if (i == count) {
[d477734]446 usb_log_debug("Phantom state occured.\n");
447 // phantom state, do nothing
448 return;
449 }
450
[17ada7a]451 /* TODO: quite dummy right now, think of better implementation */
452 assert(count == kbd_dev->key_count);
[2391aaf]453
454 /*
455 * 1) Key releases
456 */
[17ada7a]457 for (j = 0; j < count; ++j) {
[2391aaf]458 // try to find the old key in the new key list
459 i = 0;
[48d2765]460 while (i < kbd_dev->key_count
461 && key_codes[i] != kbd_dev->keys[j]) {
[2391aaf]462 ++i;
463 }
464
[17ada7a]465 if (i == count) {
[2391aaf]466 // not found, i.e. the key was released
[48d2765]467 key = usbhid_parse_scancode(kbd_dev->keys[j]);
[fc5ed5d]468 if (!usbhid_kbd_is_lock(key)) {
469 usbhid_kbd_repeat_stop(kbd_dev, key);
470 }
[2391aaf]471 usbhid_kbd_push_ev(kbd_dev, KEY_RELEASE, key);
[d477734]472 usb_log_debug2("Key released: %d\n", key);
[2391aaf]473 } else {
474 // found, nothing happens
475 }
476 }
477
478 /*
479 * 1) Key presses
480 */
[48d2765]481 for (i = 0; i < kbd_dev->key_count; ++i) {
[2391aaf]482 // try to find the new key in the old key list
483 j = 0;
[17ada7a]484 while (j < count && kbd_dev->keys[j] != key_codes[i]) {
[2391aaf]485 ++j;
486 }
487
[17ada7a]488 if (j == count) {
[2391aaf]489 // not found, i.e. new key pressed
490 key = usbhid_parse_scancode(key_codes[i]);
[d477734]491 usb_log_debug2("Key pressed: %d (keycode: %d)\n", key,
[2391aaf]492 key_codes[i]);
493 usbhid_kbd_push_ev(kbd_dev, KEY_PRESS, key);
[fc5ed5d]494 if (!usbhid_kbd_is_lock(key)) {
495 usbhid_kbd_repeat_start(kbd_dev, key);
496 }
[a8def7d]497 } else {
[2391aaf]498 // found, nothing happens
499 }
500 }
501
[17ada7a]502 memcpy(kbd_dev->keys, key_codes, count);
[6bb456c]503
504 usb_log_debug("New stored keycodes: %s\n",
[48d2765]505 usb_debug_str_buffer(kbd_dev->keys, kbd_dev->key_count, 0));
[2391aaf]506}
507
508/*----------------------------------------------------------------------------*/
509/* Callbacks for parser */
510/*----------------------------------------------------------------------------*/
[17ada7a]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).
[7309799]522 * @param arg User-specified argument. Expects pointer to the keyboard device
523 * structure representing the keyboard.
[17ada7a]524 *
525 * @sa usbhid_kbd_check_key_changes(), usbhid_kbd_check_modifier_changes()
526 */
[2391aaf]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);
[7ac73a4]538
[6bb456c]539 usb_log_debug("Got keys from parser: %s\n",
[a8def7d]540 usb_debug_str_buffer(key_codes, count, 0));
[2391aaf]541
[48d2765]542 if (count != kbd_dev->key_count) {
[2391aaf]543 usb_log_warning("Number of received keycodes (%d) differs from"
[48d2765]544 " expected number (%d).\n", count, kbd_dev->key_count);
[2391aaf]545 return;
546 }
547
[fc5ed5d]548 ///usbhid_kbd_check_modifier_changes(kbd_dev, key_codes, count);
[17ada7a]549 usbhid_kbd_check_key_changes(kbd_dev, key_codes, count);
[2391aaf]550}
551
552/*----------------------------------------------------------------------------*/
553/* General kbd functions */
554/*----------------------------------------------------------------------------*/
[48d2765]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 */
[2391aaf]570static void usbhid_kbd_process_data(usbhid_kbd_t *kbd_dev,
571 uint8_t *buffer, size_t actual_size)
572{
[00b13408]573 assert(kbd_dev->initialized == USBHID_KBD_STATUS_INITIALIZED);
[a8def7d]574 assert(kbd_dev->hid_dev->parser != NULL);
575
[2391aaf]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
[2f593872]582 usb_log_debug("Calling usb_hid_parse_report() with "
[6bb456c]583 "buffer %s\n", usb_debug_str_buffer(buffer, actual_size, 0));
[2391aaf]584
[2f593872]585// int rc = usb_hid_boot_keyboard_input_report(buffer, actual_size,
586// callbacks, kbd_dev);
[b53d3b7]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
[2f593872]590 int rc = usb_hid_parse_report(kbd_dev->hid_dev->parser, buffer,
[b53d3b7]591 actual_size, path, USB_HID_PATH_COMPARE_STRICT, callbacks, kbd_dev);
592
593 usb_hid_report_path_free (path);
[2391aaf]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/*----------------------------------------------------------------------------*/
[48d2765]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 */
[1c6c4092]613static usbhid_kbd_t *usbhid_kbd_new(void)
[2391aaf]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
[27270db]631 kbd_dev->console_phone = -1;
[00b13408]632 kbd_dev->initialized = USBHID_KBD_STATUS_UNINITIALIZED;
[2391aaf]633
634 return kbd_dev;
635}
636
637/*----------------------------------------------------------------------------*/
[e69f10b]638
[00b13408]639static void usbhid_kbd_mark_unusable(usbhid_kbd_t *kbd_dev)
640{
641 kbd_dev->initialized = USBHID_KBD_STATUS_TO_DESTROY;
[1c6c4092]642}
643
644/*----------------------------------------------------------------------------*/
[48d2765]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 */
[2391aaf]665static int usbhid_kbd_init(usbhid_kbd_t *kbd_dev, ddf_dev_t *dev)
666{
667 int rc;
668
[fbefd0e]669 usb_log_debug("Initializing HID/KBD structure...\n");
[2391aaf]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
[00b13408]683 if (kbd_dev->initialized == USBHID_KBD_STATUS_INITIALIZED) {
[2391aaf]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
[00b13408]696 assert(kbd_dev->hid_dev->initialized == USBHID_KBD_STATUS_INITIALIZED);
[2391aaf]697
698 // save the size of the report (boot protocol report by default)
[fc5ed5d]699// kbd_dev->key_count = BOOTP_REPORT_SIZE;
700
[b53d3b7]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);
[fc5ed5d]704 kbd_dev->key_count = usb_hid_report_input_length(
[b53d3b7]705 kbd_dev->hid_dev->parser, path, USB_HID_PATH_COMPARE_STRICT);
706 usb_hid_report_path_free (path);
[fc5ed5d]707
708 usb_log_debug("Size of the input report: %zu\n", kbd_dev->key_count);
709
[48d2765]710 kbd_dev->keys = (uint8_t *)calloc(
711 kbd_dev->key_count, sizeof(uint8_t));
[2391aaf]712
[48d2765]713 if (kbd_dev->keys == NULL) {
[2391aaf]714 usb_log_fatal("No memory!\n");
[48d2765]715 return ENOMEM;
[2391aaf]716 }
717
718 kbd_dev->modifiers = 0;
719 kbd_dev->mods = DEFAULT_ACTIVE_MODS;
720 kbd_dev->lock_keys = 0;
721
[dfe53af]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
[2391aaf]737 /*
738 * Set boot protocol.
739 * Set LEDs according to initial setup.
[7ac73a4]740 * Set Idle rate
[2391aaf]741 */
742 assert(kbd_dev->hid_dev != NULL);
743 assert(kbd_dev->hid_dev->initialized);
[2f593872]744 //usbhid_req_set_protocol(kbd_dev->hid_dev, USB_HID_PROTOCOL_BOOT);
[2391aaf]745
746 usbhid_kbd_set_led(kbd_dev);
747
[7ac73a4]748 usbhid_req_set_idle(kbd_dev->hid_dev, IDLE_RATE);
749
[00b13408]750 kbd_dev->initialized = USBHID_KBD_STATUS_INITIALIZED;
[fbefd0e]751 usb_log_debug("HID/KBD device structure initialized.\n");
[2391aaf]752
753 return EOK;
754}
755
756/*----------------------------------------------------------------------------*/
757/* HID/KBD polling */
758/*----------------------------------------------------------------------------*/
[48d2765]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 */
[2391aaf]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
[fbefd0e]778 usb_log_debug("Polling keyboard...\n");
[2391aaf]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) {
[3954a63b]789 sess_rc = usb_pipe_start_session(
[2391aaf]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));
[e8c1fb0]794 break;
[2391aaf]795 }
796
[3954a63b]797 rc = usb_pipe_read(&kbd_dev->hid_dev->poll_pipe,
[2391aaf]798 buffer, BOOTP_BUFFER_SIZE, &actual_size);
799
[3954a63b]800 sess_rc = usb_pipe_end_session(
[2391aaf]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));
[e8c1fb0]806 break;
[2391aaf]807 }
808
809 if (sess_rc != EOK) {
810 usb_log_warning("Error closing session: %s.\n",
811 str_error(sess_rc));
[e8c1fb0]812 break;
[2391aaf]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);
[24d943f]829
[6bb456c]830 // disabled for now, no reason to sleep
831 //async_usleep(kbd_dev->hid_dev->poll_interval);
[2391aaf]832 }
833}
834
835/*----------------------------------------------------------------------------*/
[48d2765]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 */
[2391aaf]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);
[1c6c4092]863
[00b13408]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);
[1c6c4092]867 // at the end, properly destroy the KBD structure
[00b13408]868// usbhid_kbd_free(&kbd_dev);
869// assert(kbd_dev == NULL);
[2391aaf]870
871 return EOK;
872}
873
874/*----------------------------------------------------------------------------*/
875/* API functions */
876/*----------------------------------------------------------------------------*/
[48d2765]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
[17ada7a]881 * and starts new fibril for polling the keyboard for events and another one for
882 * handling auto-repeat of keys.
[48d2765]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 *
[17ada7a]900 * @sa usbhid_kbd_fibril(), usbhid_kbd_repeat_fibril()
[48d2765]901 */
[2391aaf]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 */
[fbefd0e]916 usb_log_debug("Initializing USB/HID KBD device...\n");
[2391aaf]917
918 usbhid_kbd_t *kbd_dev = usbhid_kbd_new();
919 if (kbd_dev == NULL) {
[1c6c4092]920 usb_log_error("Error while creating USB/HID KBD device "
[2391aaf]921 "structure.\n");
922 ddf_fun_destroy(kbd_fun);
[48d2765]923 return ENOMEM; // TODO: some other code??
[2391aaf]924 }
925
926 int rc = usbhid_kbd_init(kbd_dev, dev);
927
928 if (rc != EOK) {
[1c6c4092]929 usb_log_error("Failed to initialize USB/HID KBD device.\n");
[2391aaf]930 ddf_fun_destroy(kbd_fun);
[1c6c4092]931 usbhid_kbd_free(&kbd_dev);
[2391aaf]932 return rc;
933 }
934
[fbefd0e]935 usb_log_debug("USB/HID KBD device structure initialized.\n");
[2391aaf]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) {
[fbefd0e]946 usb_log_error("Could not bind DDF function: %s.\n",
947 str_error(rc));
[1c6c4092]948 // TODO: Can / should I destroy the DDF function?
949 ddf_fun_destroy(kbd_fun);
950 usbhid_kbd_free(&kbd_dev);
[2391aaf]951 return rc;
952 }
953
954 rc = ddf_fun_add_to_class(kbd_fun, "keyboard");
955 if (rc != EOK) {
[fbefd0e]956 usb_log_error(
957 "Could not add DDF function to class 'keyboard': %s.\n",
958 str_error(rc));
[1c6c4092]959 // TODO: Can / should I destroy the DDF function?
960 ddf_fun_destroy(kbd_fun);
961 usbhid_kbd_free(&kbd_dev);
[2391aaf]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) {
[fbefd0e]970 usb_log_error("Failed to start fibril for `%s' device.\n",
971 dev->name);
[2391aaf]972 return ENOMEM;
973 }
974 fibril_add_ready(fid);
[dfe53af]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);
[2391aaf]985
986 (void)keyboard_ops;
987
988 /*
989 * Hurrah, device is initialized.
990 */
991 return EOK;
992}
993
[00b13408]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
[2391aaf]1031/**
1032 * @}
1033 */
Note: See TracBrowser for help on using the repository browser.