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

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

Auto-repeat of keys.

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