source: mainline/uspace/drv/usbhid/usbhid.c@ 252cf2a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 252cf2a was 252cf2a, checked in by Matej Klonfar <maklf@…>, 14 years ago

Development changes merge
Correct parsing of usages in case of array items

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