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
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/*----------------------------------------------------------------------------*/
[17ada7a]62/** Default modifiers when the keyboard is initialized. */
[2391aaf]63static const unsigned DEFAULT_ACTIVE_MODS = KM_NUM_LOCK;
[17ada7a]64
65/** Boot protocol report size (key part). */
[2391aaf]66static const size_t BOOTP_REPORT_SIZE = 6;
[17ada7a]67
68/** Boot protocol total report size. */
[2391aaf]69static const size_t BOOTP_BUFFER_SIZE = 8;
[17ada7a]70
71/** Boot protocol output report size. */
[2391aaf]72static const size_t BOOTP_BUFFER_OUT_SIZE = 1;
[17ada7a]73
74/** Boot protocol error key code. */
[d477734]75static const uint8_t BOOTP_ERROR_ROLLOVER = 1;
[17ada7a]76
77/** Default idle rate for keyboards. */
[6bb456c]78static const uint8_t IDLE_RATE = 0;
[17ada7a]79
80/** Delay before a pressed key starts auto-repeating. */
[dfe53af]81static const unsigned int DEFAULT_DELAY_BEFORE_FIRST_REPEAT = 500 * 1000;
[17ada7a]82
83/** Delay between two repeats of a pressed key when auto-repeating. */
[dfe53af]84static const unsigned int DEFAULT_REPEAT_DELAY = 50 * 1000;
[2391aaf]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
[17ada7a]102/** Keyboard layout map. */
[2391aaf]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/*----------------------------------------------------------------------------*/
[17ada7a]114/** Mapping of USB modifier key codes to generic modifier key codes. */
[2391aaf]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
[17ada7a]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.
[2391aaf]141 *
[17ada7a]142 * @param fun Device function handling the call.
[2391aaf]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;
[27270db]152 assert(kbd_dev != NULL);
[2391aaf]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 }
[27270db]166
[2391aaf]167 async_answer_0(icallid, EINVAL);
168}
169
170/*----------------------------------------------------------------------------*/
171/* Key processing functions */
172/*----------------------------------------------------------------------------*/
[17ada7a]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 */
[2391aaf]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
[6bb456c]216 usb_log_debug("Output report buffer: %s\n",
217 usb_debug_str_buffer(buffer, BOOTP_BUFFER_OUT_SIZE, 0));
[2391aaf]218
219 assert(kbd_dev->hid_dev != NULL);
220 assert(kbd_dev->hid_dev->initialized);
[35f0899]221 usbhid_req_set_report(kbd_dev->hid_dev, USB_HID_REPORT_TYPE_OUTPUT,
222 buffer, BOOTP_BUFFER_OUT_SIZE);
[2391aaf]223}
224
225/*----------------------------------------------------------------------------*/
[17ada7a]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 */
[dfe53af]242void usbhid_kbd_push_ev(usbhid_kbd_t *kbd_dev, int type, unsigned int key)
[2391aaf]243{
244 console_event_t ev;
245 unsigned mod_mask;
246
[48d2765]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 */
[2391aaf]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 */
[dfe53af]284 unsigned int locks_old = kbd_dev->lock_keys;
285
[2391aaf]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. */
[dfe53af]291 if (kbd_dev->lock_keys != locks_old) {
292 usbhid_kbd_set_led(kbd_dev);
293 }
[2391aaf]294 } else {
295 kbd_dev->lock_keys = kbd_dev->lock_keys & ~mod_mask;
296 }
297 }
298
[ac8285d]299 if (key == KC_CAPS_LOCK || key == KC_NUM_LOCK || key == KC_SCROLL_LOCK) {
[1c6c4092]300 // do not send anything to the console, this is our business
301 return;
302 }
303
[2391aaf]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);
[7a2f8ea0]329 if (kbd_dev->console_phone < 0) {
330 usb_log_warning(
331 "Connection to console not ready, key discarded.\n");
332 return;
333 }
[2391aaf]334
335 async_msg_4(kbd_dev->console_phone, KBD_EVENT, ev.type, ev.key,
336 ev.mods, ev.c);
337}
338
339/*----------------------------------------------------------------------------*/
[17ada7a]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 */
[2391aaf]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/*----------------------------------------------------------------------------*/
[17ada7a]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 */
[2391aaf]398static void usbhid_kbd_check_key_changes(usbhid_kbd_t *kbd_dev,
[17ada7a]399 const uint8_t *key_codes, size_t count)
[2391aaf]400{
401 unsigned int key;
402 unsigned int i, j;
403
[d477734]404 /*
405 * First of all, check if the kbd have reported phantom state.
406 */
407 i = 0;
408 // all fields should report Error Rollover
[17ada7a]409 while (i < count &&
[d477734]410 key_codes[i] == BOOTP_ERROR_ROLLOVER) {
411 ++i;
412 }
[17ada7a]413 if (i == count) {
[d477734]414 usb_log_debug("Phantom state occured.\n");
415 // phantom state, do nothing
416 return;
417 }
418
[17ada7a]419 /* TODO: quite dummy right now, think of better implementation */
420 assert(count == kbd_dev->key_count);
[2391aaf]421
422 /*
423 * 1) Key releases
424 */
[17ada7a]425 for (j = 0; j < count; ++j) {
[2391aaf]426 // try to find the old key in the new key list
427 i = 0;
[48d2765]428 while (i < kbd_dev->key_count
429 && key_codes[i] != kbd_dev->keys[j]) {
[2391aaf]430 ++i;
431 }
432
[17ada7a]433 if (i == count) {
[2391aaf]434 // not found, i.e. the key was released
[48d2765]435 key = usbhid_parse_scancode(kbd_dev->keys[j]);
[dfe53af]436 usbhid_kbd_repeat_stop(kbd_dev, key);
[2391aaf]437 usbhid_kbd_push_ev(kbd_dev, KEY_RELEASE, key);
[d477734]438 usb_log_debug2("Key released: %d\n", key);
[2391aaf]439 } else {
440 // found, nothing happens
441 }
442 }
443
444 /*
445 * 1) Key presses
446 */
[48d2765]447 for (i = 0; i < kbd_dev->key_count; ++i) {
[2391aaf]448 // try to find the new key in the old key list
449 j = 0;
[17ada7a]450 while (j < count && kbd_dev->keys[j] != key_codes[i]) {
[2391aaf]451 ++j;
452 }
453
[17ada7a]454 if (j == count) {
[2391aaf]455 // not found, i.e. new key pressed
456 key = usbhid_parse_scancode(key_codes[i]);
[d477734]457 usb_log_debug2("Key pressed: %d (keycode: %d)\n", key,
[2391aaf]458 key_codes[i]);
459 usbhid_kbd_push_ev(kbd_dev, KEY_PRESS, key);
[dfe53af]460 usbhid_kbd_repeat_start(kbd_dev, key);
[b05e2fe]461 } else {
[2391aaf]462 // found, nothing happens
463 }
464 }
465
[17ada7a]466 memcpy(kbd_dev->keys, key_codes, count);
[6bb456c]467
468 usb_log_debug("New stored keycodes: %s\n",
[48d2765]469 usb_debug_str_buffer(kbd_dev->keys, kbd_dev->key_count, 0));
[2391aaf]470}
471
472/*----------------------------------------------------------------------------*/
473/* Callbacks for parser */
474/*----------------------------------------------------------------------------*/
[17ada7a]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).
[7309799]486 * @param arg User-specified argument. Expects pointer to the keyboard device
487 * structure representing the keyboard.
[17ada7a]488 *
489 * @sa usbhid_kbd_check_key_changes(), usbhid_kbd_check_modifier_changes()
490 */
[2391aaf]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);
[7ac73a4]502
[6bb456c]503 usb_log_debug("Got keys from parser: %s\n",
[48d2765]504 usb_debug_str_buffer(key_codes, kbd_dev->key_count, 0));
[2391aaf]505
[48d2765]506 if (count != kbd_dev->key_count) {
[2391aaf]507 usb_log_warning("Number of received keycodes (%d) differs from"
[48d2765]508 " expected number (%d).\n", count, kbd_dev->key_count);
[2391aaf]509 return;
510 }
511
512 usbhid_kbd_check_modifier_changes(kbd_dev, modifiers);
[17ada7a]513 usbhid_kbd_check_key_changes(kbd_dev, key_codes, count);
[2391aaf]514}
515
516/*----------------------------------------------------------------------------*/
517/* General kbd functions */
518/*----------------------------------------------------------------------------*/
[48d2765]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 */
[2391aaf]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
[6bb456c]543 usb_log_debug("Calling usb_hid_boot_keyboard_input_report() with "
544 "buffer %s\n", usb_debug_str_buffer(buffer, actual_size, 0));
[2391aaf]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/*----------------------------------------------------------------------------*/
[48d2765]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 */
[1c6c4092]567static usbhid_kbd_t *usbhid_kbd_new(void)
[2391aaf]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
[27270db]585 kbd_dev->console_phone = -1;
[2391aaf]586 kbd_dev->initialized = 0;
587
588 return kbd_dev;
589}
590
591/*----------------------------------------------------------------------------*/
[48d2765]592/**
593 * Properly destroys the USB/HID keyboard structure.
594 *
595 * @param kbd_dev Pointer to the structure to be destroyed.
596 */
[1c6c4092]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
[dfe53af]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
[1c6c4092]617 free(*kbd_dev);
618 *kbd_dev = NULL;
619}
620
621/*----------------------------------------------------------------------------*/
[48d2765]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 */
[2391aaf]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)
[48d2765]676 kbd_dev->key_count = BOOTP_REPORT_SIZE;
677 kbd_dev->keys = (uint8_t *)calloc(
678 kbd_dev->key_count, sizeof(uint8_t));
[2391aaf]679
[48d2765]680 if (kbd_dev->keys == NULL) {
[2391aaf]681 usb_log_fatal("No memory!\n");
[48d2765]682 return ENOMEM;
[2391aaf]683 }
684
685 kbd_dev->modifiers = 0;
686 kbd_dev->mods = DEFAULT_ACTIVE_MODS;
687 kbd_dev->lock_keys = 0;
688
[dfe53af]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
[2391aaf]704 /*
705 * Set boot protocol.
706 * Set LEDs according to initial setup.
[7ac73a4]707 * Set Idle rate
[2391aaf]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
[7ac73a4]715 usbhid_req_set_idle(kbd_dev->hid_dev, IDLE_RATE);
716
[2391aaf]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/*----------------------------------------------------------------------------*/
[48d2765]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 */
[2391aaf]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));
[e8c1fb0]761 break;
[2391aaf]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));
[e8c1fb0]773 break;
[2391aaf]774 }
775
776 if (sess_rc != EOK) {
777 usb_log_warning("Error closing session: %s.\n",
778 str_error(sess_rc));
[e8c1fb0]779 break;
[2391aaf]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);
[24d943f]796
[6bb456c]797 // disabled for now, no reason to sleep
798 //async_usleep(kbd_dev->hid_dev->poll_interval);
[2391aaf]799 }
800}
801
802/*----------------------------------------------------------------------------*/
[48d2765]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 */
[2391aaf]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);
[1c6c4092]830
831 // at the end, properly destroy the KBD structure
832 usbhid_kbd_free(&kbd_dev);
833 assert(kbd_dev == NULL);
[2391aaf]834
835 return EOK;
836}
837
838/*----------------------------------------------------------------------------*/
839/* API functions */
840/*----------------------------------------------------------------------------*/
[48d2765]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
[17ada7a]845 * and starts new fibril for polling the keyboard for events and another one for
846 * handling auto-repeat of keys.
[48d2765]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 *
[17ada7a]864 * @sa usbhid_kbd_fibril(), usbhid_kbd_repeat_fibril()
[48d2765]865 */
[2391aaf]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 */
[1c6c4092]880 usb_log_info("Initializing USB/HID KBD device...\n");
[2391aaf]881
882 usbhid_kbd_t *kbd_dev = usbhid_kbd_new();
883 if (kbd_dev == NULL) {
[1c6c4092]884 usb_log_error("Error while creating USB/HID KBD device "
[2391aaf]885 "structure.\n");
886 ddf_fun_destroy(kbd_fun);
[48d2765]887 return ENOMEM; // TODO: some other code??
[2391aaf]888 }
889
890 int rc = usbhid_kbd_init(kbd_dev, dev);
891
892 if (rc != EOK) {
[1c6c4092]893 usb_log_error("Failed to initialize USB/HID KBD device.\n");
[2391aaf]894 ddf_fun_destroy(kbd_fun);
[1c6c4092]895 usbhid_kbd_free(&kbd_dev);
[2391aaf]896 return rc;
897 }
898
[1c6c4092]899 usb_log_info("USB/HID KBD device structure initialized.\n");
[2391aaf]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");
[1c6c4092]911 // TODO: Can / should I destroy the DDF function?
912 ddf_fun_destroy(kbd_fun);
913 usbhid_kbd_free(&kbd_dev);
[2391aaf]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");
[1c6c4092]921 // TODO: Can / should I destroy the DDF function?
922 ddf_fun_destroy(kbd_fun);
923 usbhid_kbd_free(&kbd_dev);
[2391aaf]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) {
[1c6c4092]932 usb_log_error("Failed to start fibril for KBD device\n");
[2391aaf]933 return ENOMEM;
934 }
935 fibril_add_ready(fid);
[dfe53af]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);
[2391aaf]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.