source: mainline/uspace/drv/usbhid/usbhid.c@ 32aef25b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 32aef25b was 4125b7d, checked in by Vojtech Horky <vojtechhorky@…>, 15 years ago

usb_log_printf() checks for printf correctness

It is surprising how many printf warnings simple check could
produce ;-).

Next time, it won't compile. Bad, huh?

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