source: mainline/uspace/drv/usbhid/kbddev.c@ 48d2765

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

Doxygen comments, part 1

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