source: mainline/uspace/drv/usbhid/usbhid.c@ 8148ee3a

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

Fixed freeing of subdrivers + some debug output.

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