source: mainline/uspace/drv/bus/usb/usbhid/usbhid.c@ 1ecc5de

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1ecc5de was 5203e256, checked in by Martin Decky <martin@…>, 14 years ago

keep the drivers source tree tidy by using logical subdirectories

  • Property mode set to 100644
File size: 18.2 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 driver API.
35 */
36
37#include <usb/debug.h>
38#include <usb/classes/classes.h>
39#include <usb/hid/hid.h>
40#include <usb/hid/hidparser.h>
41#include <usb/hid/hidreport.h>
42#include <usb/hid/request.h>
43#include <errno.h>
44#include <str_error.h>
45
46#include "usbhid.h"
47
48#include "kbd/kbddev.h"
49#include "generic/hiddev.h"
50#include "mouse/mousedev.h"
51#include "subdrivers.h"
52
53/*----------------------------------------------------------------------------*/
54
55/* Array of endpoints expected on the device, NULL terminated. */
56usb_endpoint_description_t *usb_hid_endpoints[USB_HID_POLL_EP_COUNT + 1] = {
57 &usb_hid_kbd_poll_endpoint_description,
58 &usb_hid_mouse_poll_endpoint_description,
59 &usb_hid_generic_poll_endpoint_description,
60 NULL
61};
62
63static const int USB_HID_MAX_SUBDRIVERS = 10;
64
65/*----------------------------------------------------------------------------*/
66
67static int usb_hid_set_boot_kbd_subdriver(usb_hid_dev_t *hid_dev)
68{
69 assert(hid_dev != NULL && hid_dev->subdriver_count == 0);
70
71 hid_dev->subdrivers = (usb_hid_subdriver_t *)malloc(
72 sizeof(usb_hid_subdriver_t));
73 if (hid_dev->subdrivers == NULL) {
74 return ENOMEM;
75 }
76
77 assert(hid_dev->subdriver_count >= 0);
78
79 // set the init callback
80 hid_dev->subdrivers[hid_dev->subdriver_count].init = usb_kbd_init;
81
82 // set the polling callback
83 hid_dev->subdrivers[hid_dev->subdriver_count].poll =
84 usb_kbd_polling_callback;
85
86 // set the polling ended callback
87 hid_dev->subdrivers[hid_dev->subdriver_count].poll_end = NULL;
88
89 // set the deinit callback
90 hid_dev->subdrivers[hid_dev->subdriver_count].deinit = usb_kbd_deinit;
91
92 // set subdriver count
93 ++hid_dev->subdriver_count;
94
95 return EOK;
96}
97
98/*----------------------------------------------------------------------------*/
99
100static int usb_hid_set_boot_mouse_subdriver(usb_hid_dev_t *hid_dev)
101{
102 assert(hid_dev != NULL && hid_dev->subdriver_count == 0);
103
104 hid_dev->subdrivers = (usb_hid_subdriver_t *)malloc(
105 sizeof(usb_hid_subdriver_t));
106 if (hid_dev->subdrivers == NULL) {
107 return ENOMEM;
108 }
109
110 assert(hid_dev->subdriver_count >= 0);
111
112 // set the init callback
113 hid_dev->subdrivers[hid_dev->subdriver_count].init = usb_mouse_init;
114
115 // set the polling callback
116 hid_dev->subdrivers[hid_dev->subdriver_count].poll =
117 usb_mouse_polling_callback;
118
119 // set the polling ended callback
120 hid_dev->subdrivers[hid_dev->subdriver_count].poll_end = NULL;
121
122 // set the deinit callback
123 hid_dev->subdrivers[hid_dev->subdriver_count].deinit = usb_mouse_deinit;
124
125 // set subdriver count
126 ++hid_dev->subdriver_count;
127
128 return EOK;
129}
130
131/*----------------------------------------------------------------------------*/
132
133static int usb_hid_set_generic_hid_subdriver(usb_hid_dev_t *hid_dev)
134{
135 assert(hid_dev != NULL && hid_dev->subdriver_count == 0);
136
137 hid_dev->subdrivers = (usb_hid_subdriver_t *)malloc(
138 sizeof(usb_hid_subdriver_t));
139 if (hid_dev->subdrivers == NULL) {
140 return ENOMEM;
141 }
142
143 assert(hid_dev->subdriver_count >= 0);
144
145 // set the init callback
146 hid_dev->subdrivers[hid_dev->subdriver_count].init =
147 usb_generic_hid_init;
148
149 // set the polling callback
150 hid_dev->subdrivers[hid_dev->subdriver_count].poll =
151 usb_generic_hid_polling_callback;
152
153 // set the polling ended callback
154 hid_dev->subdrivers[hid_dev->subdriver_count].poll_end = NULL;
155
156 // set the deinit callback
157 hid_dev->subdrivers[hid_dev->subdriver_count].deinit = NULL;
158
159 // set subdriver count
160 ++hid_dev->subdriver_count;
161
162 return EOK;
163}
164
165/*----------------------------------------------------------------------------*/
166
167static bool usb_hid_ids_match(usb_hid_dev_t *hid_dev,
168 const usb_hid_subdriver_mapping_t *mapping)
169{
170 assert(hid_dev != NULL);
171 assert(hid_dev->usb_dev != NULL);
172
173 return (hid_dev->usb_dev->descriptors.device.vendor_id
174 == mapping->vendor_id
175 && hid_dev->usb_dev->descriptors.device.product_id
176 == mapping->product_id);
177}
178
179/*----------------------------------------------------------------------------*/
180
181static bool usb_hid_path_matches(usb_hid_dev_t *hid_dev,
182 const usb_hid_subdriver_mapping_t *mapping)
183{
184 assert(hid_dev != NULL);
185 assert(mapping != NULL);
186
187 usb_hid_report_path_t *usage_path = usb_hid_report_path();
188 if (usage_path == NULL) {
189 usb_log_debug("Failed to create usage path.\n");
190 return false;
191 }
192 int i = 0;
193 while (mapping->usage_path[i].usage != 0
194 || mapping->usage_path[i].usage_page != 0) {
195 if (usb_hid_report_path_append_item(usage_path,
196 mapping->usage_path[i].usage_page,
197 mapping->usage_path[i].usage) != EOK) {
198 usb_log_debug("Failed to append to usage path.\n");
199 usb_hid_report_path_free(usage_path);
200 return false;
201 }
202 ++i;
203 }
204
205 assert(hid_dev->report != NULL);
206
207 usb_log_debug("Compare flags: %d\n", mapping->compare);
208
209 bool matches = false;
210 uint8_t report_id = mapping->report_id;
211
212 do {
213 usb_log_debug("Trying report id %u\n", report_id);
214
215 if (report_id != 0) {
216 usb_hid_report_path_set_report_id(usage_path,
217 report_id);
218 }
219
220 usb_hid_report_field_t *field = usb_hid_report_get_sibling(
221 hid_dev->report,
222 NULL, usage_path, mapping->compare,
223 USB_HID_REPORT_TYPE_INPUT);
224
225 usb_log_debug("Field: %p\n", field);
226
227 if (field != NULL) {
228 matches = true;
229 break;
230 }
231
232 report_id = usb_hid_get_next_report_id(
233 hid_dev->report, report_id,
234 USB_HID_REPORT_TYPE_INPUT);
235 } while (!matches && report_id != 0);
236
237 usb_hid_report_path_free(usage_path);
238
239 return matches;
240}
241
242/*----------------------------------------------------------------------------*/
243
244static int usb_hid_save_subdrivers(usb_hid_dev_t *hid_dev,
245 const usb_hid_subdriver_t **subdrivers, int count)
246{
247 int i;
248
249 if (count <= 0) {
250 hid_dev->subdriver_count = 0;
251 hid_dev->subdrivers = NULL;
252 return EOK;
253 }
254
255 // add one generic HID subdriver per device
256
257 hid_dev->subdrivers = (usb_hid_subdriver_t *)malloc((count + 1) *
258 sizeof(usb_hid_subdriver_t));
259 if (hid_dev->subdrivers == NULL) {
260 return ENOMEM;
261 }
262
263 for (i = 0; i < count; ++i) {
264 hid_dev->subdrivers[i].init = subdrivers[i]->init;
265 hid_dev->subdrivers[i].deinit = subdrivers[i]->deinit;
266 hid_dev->subdrivers[i].poll = subdrivers[i]->poll;
267 hid_dev->subdrivers[i].poll_end = subdrivers[i]->poll_end;
268 }
269
270 hid_dev->subdrivers[count].init = usb_generic_hid_init;
271 hid_dev->subdrivers[count].poll = usb_generic_hid_polling_callback;
272 hid_dev->subdrivers[count].deinit = NULL;
273 hid_dev->subdrivers[count].poll_end = NULL;
274
275 hid_dev->subdriver_count = count + 1;
276
277 return EOK;
278}
279
280/*----------------------------------------------------------------------------*/
281
282static int usb_hid_find_subdrivers(usb_hid_dev_t *hid_dev)
283{
284 assert(hid_dev != NULL);
285
286 const usb_hid_subdriver_t *subdrivers[USB_HID_MAX_SUBDRIVERS];
287
288 int i = 0, count = 0;
289 const usb_hid_subdriver_mapping_t *mapping = &usb_hid_subdrivers[i];
290
291 bool ids_matched;
292 bool matched;
293
294 while (count < USB_HID_MAX_SUBDRIVERS &&
295 (mapping->usage_path != NULL
296 || mapping->vendor_id >= 0 || mapping->product_id >= 0)) {
297 // check the vendor & product ID
298 if (mapping->vendor_id >= 0 && mapping->product_id < 0) {
299 usb_log_warning("Missing Product ID for Vendor ID %d\n",
300 mapping->vendor_id);
301 return EINVAL;
302 }
303 if (mapping->product_id >= 0 && mapping->vendor_id < 0) {
304 usb_log_warning("Missing Vendor ID for Product ID %d\n",
305 mapping->product_id);
306 return EINVAL;
307 }
308
309 ids_matched = false;
310 matched = false;
311
312 if (mapping->vendor_id >= 0) {
313 assert(mapping->product_id >= 0);
314 usb_log_debug("Comparing device against vendor ID %u"
315 " and product ID %u.\n", mapping->vendor_id,
316 mapping->product_id);
317 if (usb_hid_ids_match(hid_dev, mapping)) {
318 usb_log_debug("IDs matched.\n");
319 ids_matched = true;
320 }
321 }
322
323 if (mapping->usage_path != NULL) {
324 usb_log_debug("Comparing device against usage path.\n");
325 if (usb_hid_path_matches(hid_dev, mapping)) {
326 // does not matter if IDs were matched
327 matched = true;
328 }
329 } else {
330 // matched only if IDs were matched and there is no path
331 matched = ids_matched;
332 }
333
334 if (matched) {
335 usb_log_debug("Subdriver matched.\n");
336 subdrivers[count++] = &mapping->subdriver;
337 }
338
339 mapping = &usb_hid_subdrivers[++i];
340 }
341
342 // we have all subdrivers determined, save them into the hid device
343 return usb_hid_save_subdrivers(hid_dev, subdrivers, count);
344}
345
346/*----------------------------------------------------------------------------*/
347
348static int usb_hid_check_pipes(usb_hid_dev_t *hid_dev, usb_device_t *dev)
349{
350 assert(hid_dev != NULL && dev != NULL);
351
352 int rc = EOK;
353
354 if (dev->pipes[USB_HID_KBD_POLL_EP_NO].present) {
355 usb_log_debug("Found keyboard endpoint.\n");
356 // save the pipe index
357 hid_dev->poll_pipe_index = USB_HID_KBD_POLL_EP_NO;
358 } else if (dev->pipes[USB_HID_MOUSE_POLL_EP_NO].present) {
359 usb_log_debug("Found mouse endpoint.\n");
360 // save the pipe index
361 hid_dev->poll_pipe_index = USB_HID_MOUSE_POLL_EP_NO;
362 } else if (dev->pipes[USB_HID_GENERIC_POLL_EP_NO].present) {
363 usb_log_debug("Found generic HID endpoint.\n");
364 // save the pipe index
365 hid_dev->poll_pipe_index = USB_HID_GENERIC_POLL_EP_NO;
366 } else {
367 usb_log_error("None of supported endpoints found - probably"
368 " not a supported device.\n");
369 rc = ENOTSUP;
370 }
371
372 return rc;
373}
374
375/*----------------------------------------------------------------------------*/
376
377static int usb_hid_init_report(usb_hid_dev_t *hid_dev)
378{
379 assert(hid_dev != NULL && hid_dev->report != NULL);
380
381 uint8_t report_id = 0;
382 size_t size;
383
384 size_t max_size = 0;
385
386 do {
387 usb_log_debug("Getting size of the report.\n");
388 size = usb_hid_report_byte_size(hid_dev->report, report_id,
389 USB_HID_REPORT_TYPE_INPUT);
390 usb_log_debug("Report ID: %u, size: %zu\n", report_id, size);
391 max_size = (size > max_size) ? size : max_size;
392 usb_log_debug("Getting next report ID\n");
393 report_id = usb_hid_get_next_report_id(hid_dev->report,
394 report_id, USB_HID_REPORT_TYPE_INPUT);
395 } while (report_id != 0);
396
397 usb_log_debug("Max size of input report: %zu\n", max_size);
398
399 hid_dev->max_input_report_size = max_size;
400 assert(hid_dev->input_report == NULL);
401
402 hid_dev->input_report = malloc(max_size);
403 if (hid_dev->input_report == NULL) {
404 return ENOMEM;
405 }
406 memset(hid_dev->input_report, 0, max_size);
407
408 return EOK;
409}
410
411/*----------------------------------------------------------------------------*/
412
413usb_hid_dev_t *usb_hid_new(void)
414{
415 usb_hid_dev_t *hid_dev = (usb_hid_dev_t *)calloc(1,
416 sizeof(usb_hid_dev_t));
417
418 if (hid_dev == NULL) {
419 usb_log_fatal("No memory!\n");
420 return NULL;
421 }
422
423 hid_dev->report = (usb_hid_report_t *)(malloc(sizeof(
424 usb_hid_report_t)));
425 if (hid_dev->report == NULL) {
426 usb_log_fatal("No memory!\n");
427 free(hid_dev);
428 return NULL;
429 }
430
431 hid_dev->poll_pipe_index = -1;
432
433 return hid_dev;
434}
435
436/*----------------------------------------------------------------------------*/
437
438int usb_hid_init(usb_hid_dev_t *hid_dev, usb_device_t *dev)
439{
440 int rc, i;
441
442 usb_log_debug("Initializing HID structure...\n");
443
444 if (hid_dev == NULL) {
445 usb_log_error("Failed to init HID structure: no structure given"
446 ".\n");
447 return EINVAL;
448 }
449
450 if (dev == NULL) {
451 usb_log_error("Failed to init HID structure: no USB device"
452 " given.\n");
453 return EINVAL;
454 }
455
456 /* The USB device should already be initialized, save it in structure */
457 hid_dev->usb_dev = dev;
458
459 rc = usb_hid_check_pipes(hid_dev, dev);
460 if (rc != EOK) {
461 return rc;
462 }
463
464 /* Get the report descriptor and parse it. */
465 rc = usb_hid_process_report_descriptor(hid_dev->usb_dev,
466 hid_dev->report, &hid_dev->report_desc, &hid_dev->report_desc_size);
467
468 bool fallback = false;
469
470 if (rc == EOK) {
471 // try to find subdrivers that may want to handle this device
472 rc = usb_hid_find_subdrivers(hid_dev);
473 if (rc != EOK || hid_dev->subdriver_count == 0) {
474 // try to fall back to the boot protocol if available
475 usb_log_info("No subdrivers found to handle this"
476 " device.\n");
477 fallback = true;
478 assert(hid_dev->subdrivers == NULL);
479 assert(hid_dev->subdriver_count == 0);
480 }
481 } else {
482 usb_log_error("Failed to parse Report descriptor.\n");
483 // try to fall back to the boot protocol if available
484 fallback = true;
485 }
486
487 if (fallback) {
488 // fall back to boot protocol
489 switch (hid_dev->poll_pipe_index) {
490 case USB_HID_KBD_POLL_EP_NO:
491 usb_log_info("Falling back to kbd boot protocol.\n");
492 rc = usb_kbd_set_boot_protocol(hid_dev);
493 if (rc == EOK) {
494 rc = usb_hid_set_boot_kbd_subdriver(hid_dev);
495 }
496 break;
497 case USB_HID_MOUSE_POLL_EP_NO:
498 usb_log_info("Falling back to mouse boot protocol.\n");
499 rc = usb_mouse_set_boot_protocol(hid_dev);
500 if (rc == EOK) {
501 rc = usb_hid_set_boot_mouse_subdriver(hid_dev);
502 }
503 break;
504 default:
505 assert(hid_dev->poll_pipe_index
506 == USB_HID_GENERIC_POLL_EP_NO);
507
508 usb_log_info("Falling back to generic HID driver.\n");
509 rc = usb_hid_set_generic_hid_subdriver(hid_dev);
510 }
511 }
512
513 if (rc != EOK) {
514 usb_log_error("No subdriver for handling this device could be"
515 " initialized: %s.\n", str_error(rc));
516 usb_log_debug("Subdriver count: %d\n",
517 hid_dev->subdriver_count);
518
519 } else {
520 bool ok = false;
521
522 usb_log_debug("Subdriver count: %d\n",
523 hid_dev->subdriver_count);
524
525 for (i = 0; i < hid_dev->subdriver_count; ++i) {
526 if (hid_dev->subdrivers[i].init != NULL) {
527 usb_log_debug("Initializing subdriver %d.\n",i);
528 rc = hid_dev->subdrivers[i].init(hid_dev,
529 &hid_dev->subdrivers[i].data);
530 if (rc != EOK) {
531 usb_log_warning("Failed to initialize"
532 " HID subdriver structure.\n");
533 } else {
534 // at least one subdriver initialized
535 ok = true;
536 }
537 } else {
538 ok = true;
539 }
540 }
541
542 rc = (ok) ? EOK : -1; // what error to report
543 }
544
545
546 if (rc == EOK) {
547 // save max input report size and allocate space for the report
548 rc = usb_hid_init_report(hid_dev);
549 if (rc != EOK) {
550 usb_log_error("Failed to initialize input report buffer"
551 ".\n");
552 }
553 }
554
555
556 return rc;
557}
558
559/*----------------------------------------------------------------------------*/
560
561bool usb_hid_polling_callback(usb_device_t *dev, uint8_t *buffer,
562 size_t buffer_size, void *arg)
563{
564 int i;
565
566 if (dev == NULL || arg == NULL || buffer == NULL) {
567 usb_log_error("Missing arguments to polling callback.\n");
568 return false;
569 }
570
571 usb_hid_dev_t *hid_dev = (usb_hid_dev_t *)arg;
572
573 assert(hid_dev->input_report != NULL);
574 usb_log_debug("New data [%zu/%zu]: %s\n", buffer_size,
575 hid_dev->max_input_report_size,
576 usb_debug_str_buffer(buffer, buffer_size, 0));
577
578 if (hid_dev->max_input_report_size >= buffer_size) {
579 /*! @todo This should probably be atomic. */
580 memcpy(hid_dev->input_report, buffer, buffer_size);
581 hid_dev->input_report_size = buffer_size;
582 usb_hid_new_report(hid_dev);
583 }
584
585 // parse the input report
586
587 int rc = usb_hid_parse_report(hid_dev->report, buffer, buffer_size,
588 &hid_dev->report_id);
589
590 if (rc != EOK) {
591 usb_log_warning("Error in usb_hid_parse_report():"
592 "%s\n", str_error(rc));
593 }
594
595 bool cont = false;
596
597 // continue if at least one of the subdrivers want to continue
598 for (i = 0; i < hid_dev->subdriver_count; ++i) {
599 if (hid_dev->subdrivers[i].poll != NULL
600 && hid_dev->subdrivers[i].poll(hid_dev,
601 hid_dev->subdrivers[i].data)) {
602 cont = true;
603 }
604 }
605
606 return cont;
607}
608
609/*----------------------------------------------------------------------------*/
610
611void usb_hid_polling_ended_callback(usb_device_t *dev, bool reason,
612 void *arg)
613{
614 int i;
615
616 if (dev == NULL || arg == NULL) {
617 return;
618 }
619
620 usb_hid_dev_t *hid_dev = (usb_hid_dev_t *)arg;
621
622 for (i = 0; i < hid_dev->subdriver_count; ++i) {
623 if (hid_dev->subdrivers[i].poll_end != NULL) {
624 hid_dev->subdrivers[i].poll_end(hid_dev,
625 hid_dev->subdrivers[i].data, reason);
626 }
627 }
628
629 usb_hid_free(&hid_dev);
630}
631
632/*----------------------------------------------------------------------------*/
633
634void usb_hid_new_report(usb_hid_dev_t *hid_dev)
635{
636 ++hid_dev->report_nr;
637}
638
639/*----------------------------------------------------------------------------*/
640
641int usb_hid_report_number(usb_hid_dev_t *hid_dev)
642{
643 return hid_dev->report_nr;
644}
645
646/*----------------------------------------------------------------------------*/
647
648void usb_hid_free(usb_hid_dev_t **hid_dev)
649{
650 int i;
651
652 if (hid_dev == NULL || *hid_dev == NULL) {
653 return;
654 }
655
656 usb_log_debug("Subdrivers: %p, subdriver count: %d\n",
657 (*hid_dev)->subdrivers, (*hid_dev)->subdriver_count);
658
659 assert((*hid_dev)->subdrivers != NULL
660 || (*hid_dev)->subdriver_count == 0);
661
662 for (i = 0; i < (*hid_dev)->subdriver_count; ++i) {
663 if ((*hid_dev)->subdrivers[i].deinit != NULL) {
664 (*hid_dev)->subdrivers[i].deinit(*hid_dev,
665 (*hid_dev)->subdrivers[i].data);
666 }
667 }
668
669 // free the subdrivers info
670 if ((*hid_dev)->subdrivers != NULL) {
671 free((*hid_dev)->subdrivers);
672 }
673
674 // destroy the parser
675 if ((*hid_dev)->report != NULL) {
676 usb_hid_free_report((*hid_dev)->report);
677 }
678
679 if ((*hid_dev)->report_desc != NULL) {
680 free((*hid_dev)->report_desc);
681 }
682
683 free(*hid_dev);
684 *hid_dev = NULL;
685}
686
687/**
688 * @}
689 */
Note: See TracBrowser for help on using the repository browser.