source: mainline/uspace/drv/hid/usbhid/usbhid.c@ f40237f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f40237f was e0a5d4c, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usb: update copyrights

The data was generated by a script, guided manually. If you feel your
name is missing somewhere, please add it!

The semi-automated process was roughly:

1) Changes per file and author (limited to our team) were counted
2) Trivial numbers were thrown away
3) Authors were sorted by lines added to file
4) All previous copyrights were replaced by the newly generated one
5) Hunks changing only year were discarded

It seems that a lot of my copyrights were added. It is due to me being
both sticking my nose everywhere and lazy to update the copyright right
away :)

  • Property mode set to 100644
File size: 16.2 KB
Line 
1/*
2 * Copyright (c) 2011 Lubos Slovak
3 * Copyright (c) 2018 Petr Manek, Ondrej Hlavaty
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup drvusbhid
31 * @{
32 */
33/**
34 * @file
35 * USB HID driver API.
36 */
37
38#include <usb/debug.h>
39#include <usb/classes/classes.h>
40#include <usb/hid/hid.h>
41#include <usb/hid/hidparser.h>
42#include <usb/hid/hidreport.h>
43#include <usb/hid/request.h>
44
45#include <errno.h>
46#include <macros.h>
47#include <str_error.h>
48
49#include "usbhid.h"
50
51#include "kbd/kbddev.h"
52#include "generic/hiddev.h"
53#include "mouse/mousedev.h"
54#include "subdrivers.h"
55
56/* Array of endpoints expected on the device, NULL terminated. */
57const usb_endpoint_description_t *usb_hid_endpoints[] = {
58 &usb_hid_kbd_poll_endpoint_description,
59 &usb_hid_mouse_poll_endpoint_description,
60 &usb_hid_generic_poll_endpoint_description,
61 NULL
62};
63
64static errno_t usb_hid_set_boot_kbd_subdriver(usb_hid_dev_t *hid_dev)
65{
66 assert(hid_dev != NULL);
67 assert(hid_dev->subdriver_count == 0);
68
69 hid_dev->subdrivers = malloc(sizeof(usb_hid_subdriver_t));
70 if (hid_dev->subdrivers == NULL) {
71 return ENOMEM;
72 }
73 hid_dev->subdriver_count = 1;
74 // TODO 0 should be keyboard, but find a better way
75 hid_dev->subdrivers[0] = usb_hid_subdrivers[0].subdriver;
76
77 return EOK;
78}
79
80static errno_t usb_hid_set_boot_mouse_subdriver(usb_hid_dev_t *hid_dev)
81{
82 assert(hid_dev != NULL);
83 assert(hid_dev->subdriver_count == 0);
84
85 hid_dev->subdrivers = malloc(sizeof(usb_hid_subdriver_t));
86 if (hid_dev->subdrivers == NULL) {
87 return ENOMEM;
88 }
89 hid_dev->subdriver_count = 1;
90 // TODO 2 should be mouse, but find a better way
91 hid_dev->subdrivers[0] = usb_hid_subdrivers[2].subdriver;
92
93 return EOK;
94}
95
96static errno_t usb_hid_set_generic_hid_subdriver(usb_hid_dev_t *hid_dev)
97{
98 assert(hid_dev != NULL);
99 assert(hid_dev->subdriver_count == 0);
100
101 hid_dev->subdrivers = malloc(sizeof(usb_hid_subdriver_t));
102 if (hid_dev->subdrivers == NULL) {
103 return ENOMEM;
104 }
105 hid_dev->subdriver_count = 1;
106
107 /* Set generic hid subdriver routines */
108 hid_dev->subdrivers[0].init = usb_generic_hid_init;
109 hid_dev->subdrivers[0].poll = usb_generic_hid_polling_callback;
110 hid_dev->subdrivers[0].poll_end = NULL;
111 hid_dev->subdrivers[0].deinit = usb_generic_hid_deinit;
112
113 return EOK;
114}
115
116static bool usb_hid_ids_match(const usb_hid_dev_t *hid_dev,
117 const usb_hid_subdriver_mapping_t *mapping)
118{
119 assert(hid_dev);
120 assert(hid_dev->usb_dev);
121 assert(mapping);
122 const usb_standard_device_descriptor_t *d =
123 &usb_device_descriptors(hid_dev->usb_dev)->device;
124
125 return (d->vendor_id == mapping->vendor_id)
126 && (d->product_id == mapping->product_id);
127}
128
129static bool usb_hid_path_matches(usb_hid_dev_t *hid_dev,
130 const usb_hid_subdriver_mapping_t *mapping)
131{
132 assert(hid_dev != NULL);
133 assert(mapping != NULL);
134
135 usb_hid_report_path_t *usage_path = usb_hid_report_path();
136 if (usage_path == NULL) {
137 usb_log_debug("Failed to create usage path.");
138 return false;
139 }
140
141 for (int i = 0; mapping->usage_path[i].usage != 0
142 || mapping->usage_path[i].usage_page != 0; ++i) {
143 if (usb_hid_report_path_append_item(usage_path,
144 mapping->usage_path[i].usage_page,
145 mapping->usage_path[i].usage) != EOK) {
146 usb_log_debug("Failed to append to usage path.");
147 usb_hid_report_path_free(usage_path);
148 return false;
149 }
150 }
151
152 usb_log_debug("Compare flags: %d", mapping->compare);
153
154 bool matches = false;
155 uint8_t report_id = mapping->report_id;
156
157 do {
158 usb_log_debug("Trying report id %u", report_id);
159 if (report_id != 0) {
160 usb_hid_report_path_set_report_id(usage_path,
161 report_id);
162 }
163
164 const usb_hid_report_field_t *field =
165 usb_hid_report_get_sibling(
166 &hid_dev->report, NULL, usage_path, mapping->compare,
167 USB_HID_REPORT_TYPE_INPUT);
168
169 usb_log_debug("Field: %p", field);
170
171 if (field != NULL) {
172 matches = true;
173 break;
174 }
175
176 report_id = usb_hid_get_next_report_id(
177 &hid_dev->report, report_id, USB_HID_REPORT_TYPE_INPUT);
178 } while (!matches && report_id != 0);
179
180 usb_hid_report_path_free(usage_path);
181
182 return matches;
183}
184
185static errno_t usb_hid_save_subdrivers(usb_hid_dev_t *hid_dev,
186 const usb_hid_subdriver_t **subdrivers, unsigned count)
187{
188 assert(hid_dev);
189 assert(subdrivers);
190
191 if (count == 0) {
192 hid_dev->subdriver_count = 0;
193 hid_dev->subdrivers = NULL;
194 return EOK;
195 }
196
197 /* +1 for generic hid subdriver */
198 hid_dev->subdrivers = calloc((count + 1), sizeof(usb_hid_subdriver_t));
199 if (hid_dev->subdrivers == NULL) {
200 return ENOMEM;
201 }
202
203 for (unsigned i = 0; i < count; ++i) {
204 hid_dev->subdrivers[i] = *subdrivers[i];
205 }
206
207 /* Add one generic HID subdriver per device */
208 hid_dev->subdrivers[count].init = usb_generic_hid_init;
209 hid_dev->subdrivers[count].poll = usb_generic_hid_polling_callback;
210 hid_dev->subdrivers[count].deinit = usb_generic_hid_deinit;
211 hid_dev->subdrivers[count].poll_end = NULL;
212
213 hid_dev->subdriver_count = count + 1;
214
215 return EOK;
216}
217
218static errno_t usb_hid_find_subdrivers(usb_hid_dev_t *hid_dev)
219{
220 assert(hid_dev != NULL);
221
222 const usb_hid_subdriver_t *subdrivers[USB_HID_MAX_SUBDRIVERS];
223 unsigned count = 0;
224
225 for (unsigned i = 0; i < USB_HID_MAX_SUBDRIVERS; ++i) {
226 const usb_hid_subdriver_mapping_t *mapping =
227 &usb_hid_subdrivers[i];
228 /* Check the vendor & product ID. */
229 if (mapping->vendor_id >= 0 && mapping->product_id < 0) {
230 usb_log_warning("Mapping[%d]: Missing Product ID for "
231 "Vendor ID %d\n", i, mapping->vendor_id);
232 }
233 if (mapping->product_id >= 0 && mapping->vendor_id < 0) {
234 usb_log_warning("Mapping[%d]: Missing Vendor ID for "
235 "Product ID %d\n", i, mapping->product_id);
236 }
237
238 bool matched = false;
239
240 /* Check ID match. */
241 if (mapping->vendor_id >= 0 && mapping->product_id >= 0) {
242 usb_log_debug("Comparing device against vendor ID %u"
243 " and product ID %u.\n", mapping->vendor_id,
244 mapping->product_id);
245 if (usb_hid_ids_match(hid_dev, mapping)) {
246 usb_log_debug("IDs matched.");
247 matched = true;
248 }
249 }
250
251 /* Check usage match. */
252 if (mapping->usage_path != NULL) {
253 usb_log_debug("Comparing device against usage path.");
254 if (usb_hid_path_matches(hid_dev, mapping)) {
255 /* Does not matter if IDs were matched. */
256 matched = true;
257 }
258 }
259
260 if (matched) {
261 usb_log_debug("Subdriver matched.");
262 subdrivers[count++] = &mapping->subdriver;
263 }
264 }
265
266 /* We have all subdrivers determined, save them into the hid device */
267 return usb_hid_save_subdrivers(hid_dev, subdrivers, count);
268}
269
270static errno_t usb_hid_check_pipes(usb_hid_dev_t *hid_dev, usb_device_t *dev)
271{
272 assert(hid_dev);
273 assert(dev);
274
275 static const struct {
276 const usb_endpoint_description_t *desc;
277 const char* description;
278 } endpoints[] = {
279 {&usb_hid_kbd_poll_endpoint_description, "Keyboard endpoint"},
280 {&usb_hid_mouse_poll_endpoint_description, "Mouse endpoint"},
281 {&usb_hid_generic_poll_endpoint_description, "Generic HID endpoint"},
282 };
283
284 for (unsigned i = 0; i < ARRAY_SIZE(endpoints); ++i) {
285 usb_endpoint_mapping_t *epm =
286 usb_device_get_mapped_ep_desc(dev, endpoints[i].desc);
287 if (epm && epm->present) {
288 usb_log_debug("Found: %s.", endpoints[i].description);
289 hid_dev->poll_pipe_mapping = epm;
290 return EOK;
291 }
292 }
293 return ENOTSUP;
294}
295
296static errno_t usb_hid_init_report(usb_hid_dev_t *hid_dev)
297{
298 assert(hid_dev != NULL);
299
300 uint8_t report_id = 0;
301 size_t max_size = 0;
302
303 do {
304 usb_log_debug("Getting size of the report.");
305 const size_t size =
306 usb_hid_report_byte_size(&hid_dev->report, report_id,
307 USB_HID_REPORT_TYPE_INPUT);
308 usb_log_debug("Report ID: %u, size: %zu", report_id, size);
309 max_size = (size > max_size) ? size : max_size;
310 usb_log_debug("Getting next report ID");
311 report_id = usb_hid_get_next_report_id(&hid_dev->report,
312 report_id, USB_HID_REPORT_TYPE_INPUT);
313 } while (report_id != 0);
314
315 usb_log_debug("Max size of input report: %zu", max_size);
316
317 assert(hid_dev->input_report == NULL);
318
319 hid_dev->input_report = calloc(1, max_size);
320 if (hid_dev->input_report == NULL) {
321 return ENOMEM;
322 }
323 hid_dev->max_input_report_size = max_size;
324
325 return EOK;
326}
327
328static bool usb_hid_polling_callback(usb_device_t *dev, uint8_t *buffer,
329 size_t buffer_size, void *arg)
330{
331 if (dev == NULL || arg == NULL || buffer == NULL) {
332 usb_log_error("Missing arguments to polling callback.");
333 return false;
334 }
335 usb_hid_dev_t *hid_dev = arg;
336
337 assert(hid_dev->input_report != NULL);
338
339 usb_log_debug("New data [%zu/%zu]: %s", buffer_size,
340 hid_dev->max_input_report_size,
341 usb_debug_str_buffer(buffer, buffer_size, 0));
342
343 if (hid_dev->max_input_report_size >= buffer_size) {
344 /*! @todo This should probably be atomic. */
345 memcpy(hid_dev->input_report, buffer, buffer_size);
346 hid_dev->input_report_size = buffer_size;
347 usb_hid_new_report(hid_dev);
348 }
349
350 /* Parse the input report */
351 const errno_t rc = usb_hid_parse_report(
352 &hid_dev->report, buffer, buffer_size, &hid_dev->report_id);
353 if (rc != EOK) {
354 usb_log_warning("Failure in usb_hid_parse_report():"
355 "%s\n", str_error(rc));
356 }
357
358 bool cont = false;
359 /* Continue if at least one of the subdrivers want to continue */
360 for (unsigned i = 0; i < hid_dev->subdriver_count; ++i) {
361 if (hid_dev->subdrivers[i].poll != NULL) {
362 cont = cont || hid_dev->subdrivers[i].poll(
363 hid_dev, hid_dev->subdrivers[i].data);
364 }
365 }
366
367 return cont;
368}
369
370static bool usb_hid_polling_error_callback(usb_device_t *dev, errno_t err_code, void *arg)
371{
372 assert(dev);
373 assert(arg);
374 usb_hid_dev_t *hid_dev = arg;
375
376 usb_log_error("Device %s polling error: %s", usb_device_get_name(dev),
377 str_error(err_code));
378
379 /* Continue polling until the device is about to be removed. */
380 return hid_dev->running;
381}
382
383static void usb_hid_polling_ended_callback(usb_device_t *dev, bool reason, void *arg)
384{
385 assert(dev);
386 assert(arg);
387
388 usb_hid_dev_t *hid_dev = arg;
389
390 for (unsigned i = 0; i < hid_dev->subdriver_count; ++i) {
391 if (hid_dev->subdrivers[i].poll_end != NULL) {
392 hid_dev->subdrivers[i].poll_end(
393 hid_dev, hid_dev->subdrivers[i].data, reason);
394 }
395 }
396
397 hid_dev->running = false;
398}
399
400/*
401 * This functions initializes required structures from the device's descriptors
402 * and starts new fibril for polling the keyboard for events and another one for
403 * handling auto-repeat of keys.
404 *
405 * During initialization, the keyboard is switched into boot protocol, the idle
406 * rate is set to 0 (infinity), resulting in the keyboard only reporting event
407 * when a key is pressed or released. Finally, the LED lights are turned on
408 * according to the default setup of lock keys.
409 *
410 * @note By default, the keyboards is initialized with Num Lock turned on and
411 * other locks turned off.
412 *
413 * @param hid_dev Device to initialize, non-NULL.
414 * @param dev USB device, non-NULL.
415 * @return Error code.
416 */
417errno_t usb_hid_init(usb_hid_dev_t *hid_dev, usb_device_t *dev)
418{
419 assert(hid_dev);
420 assert(dev);
421
422 usb_log_debug("Initializing HID structure...");
423
424 usb_hid_report_init(&hid_dev->report);
425
426 /* The USB device should already be initialized, save it in structure */
427 hid_dev->usb_dev = dev;
428 hid_dev->poll_pipe_mapping = NULL;
429
430 errno_t rc = usb_hid_check_pipes(hid_dev, dev);
431 if (rc != EOK) {
432 return rc;
433 }
434
435 /* Get the report descriptor and parse it. */
436 rc = usb_hid_process_report_descriptor(
437 hid_dev->usb_dev, &hid_dev->report, &hid_dev->report_desc,
438 &hid_dev->report_desc_size);
439
440 /* If report parsing went well, find subdrivers. */
441 if (rc == EOK) {
442 usb_hid_find_subdrivers(hid_dev);
443 } else {
444 usb_log_error("Failed to parse report descriptor: fallback.");
445 hid_dev->subdrivers = NULL;
446 hid_dev->subdriver_count = 0;
447 }
448
449 usb_log_debug("Subdriver count(before trying boot protocol): %d",
450 hid_dev->subdriver_count);
451
452 /* No subdrivers, fall back to the boot protocol if available. */
453 if (hid_dev->subdriver_count == 0) {
454 assert(hid_dev->subdrivers == NULL);
455 usb_log_info("No subdrivers found to handle device, trying "
456 "boot protocol.\n");
457
458 switch (hid_dev->poll_pipe_mapping->interface->interface_protocol) {
459 case USB_HID_PROTOCOL_KEYBOARD:
460 usb_log_info("Falling back to kbd boot protocol.");
461 rc = usb_kbd_set_boot_protocol(hid_dev);
462 if (rc == EOK) {
463 usb_hid_set_boot_kbd_subdriver(hid_dev);
464 }
465 break;
466 case USB_HID_PROTOCOL_MOUSE:
467 usb_log_info("Falling back to mouse boot protocol.");
468 rc = usb_mouse_set_boot_protocol(hid_dev);
469 if (rc == EOK) {
470 usb_hid_set_boot_mouse_subdriver(hid_dev);
471 }
472 break;
473 default:
474 usb_log_info("Falling back to generic HID driver.");
475 usb_hid_set_generic_hid_subdriver(hid_dev);
476 }
477 }
478
479 usb_log_debug("Subdriver count(after trying boot protocol): %d",
480 hid_dev->subdriver_count);
481
482 /* Still no subdrivers? */
483 if (hid_dev->subdriver_count == 0) {
484 assert(hid_dev->subdrivers == NULL);
485 usb_log_error(
486 "No subdriver for handling this device could be found.\n");
487 return ENOTSUP;
488 }
489
490 /* Initialize subdrivers */
491 bool ok = false;
492 for (unsigned i = 0; i < hid_dev->subdriver_count; ++i) {
493 if (hid_dev->subdrivers[i].init != NULL) {
494 usb_log_debug("Initializing subdriver %d.",i);
495 const errno_t pret = hid_dev->subdrivers[i].init(hid_dev,
496 &hid_dev->subdrivers[i].data);
497 if (pret != EOK) {
498 usb_log_warning("Failed to initialize"
499 " HID subdriver structure: %s.\n",
500 str_error(pret));
501 rc = pret;
502 } else {
503 /* At least one subdriver initialized. */
504 ok = true;
505 }
506 } else {
507 /* Does not need initialization. */
508 ok = true;
509 }
510 }
511
512 if (ok) {
513 /* Save max input report size and
514 * allocate space for the report */
515 rc = usb_hid_init_report(hid_dev);
516 if (rc != EOK) {
517 usb_log_error("Failed to initialize input report buffer: %s", str_error(rc));
518 // FIXME: What happens now?
519 }
520
521 usb_polling_t *polling = &hid_dev->polling;
522 if ((rc = usb_polling_init(polling))) {
523 usb_log_error("Failed to initialize polling: %s", str_error(rc));
524 // FIXME: What happens now?
525 }
526
527 polling->device = hid_dev->usb_dev;
528 polling->ep_mapping = hid_dev->poll_pipe_mapping;
529 polling->request_size = hid_dev->poll_pipe_mapping->pipe.desc.max_transfer_size;
530 polling->buffer = malloc(polling->request_size);
531 polling->on_data = usb_hid_polling_callback;
532 polling->on_polling_end = usb_hid_polling_ended_callback;
533 polling->on_error = usb_hid_polling_error_callback;
534 polling->arg = hid_dev;
535 }
536
537 return rc;
538}
539
540void usb_hid_new_report(usb_hid_dev_t *hid_dev)
541{
542 ++hid_dev->report_nr;
543}
544
545int usb_hid_report_number(const usb_hid_dev_t *hid_dev)
546{
547 return hid_dev->report_nr;
548}
549
550void usb_hid_deinit(usb_hid_dev_t *hid_dev)
551{
552 assert(hid_dev);
553 assert(hid_dev->subdrivers != NULL || hid_dev->subdriver_count == 0);
554
555 free(hid_dev->polling.buffer);
556 usb_polling_fini(&hid_dev->polling);
557
558 usb_log_debug("Subdrivers: %p, subdriver count: %d",
559 hid_dev->subdrivers, hid_dev->subdriver_count);
560
561 for (unsigned i = 0; i < hid_dev->subdriver_count; ++i) {
562 if (hid_dev->subdrivers[i].deinit != NULL) {
563 hid_dev->subdrivers[i].deinit(hid_dev,
564 hid_dev->subdrivers[i].data);
565 }
566 }
567
568 /* Free allocated structures */
569 free(hid_dev->subdrivers);
570 free(hid_dev->report_desc);
571
572 /* Destroy the parser */
573 usb_hid_report_deinit(&hid_dev->report);
574}
575
576/**
577 * @}
578 */
Note: See TracBrowser for help on using the repository browser.