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

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

Quick fix to enable compilation ;-)

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