source: mainline/uspace/lib/usbhid/src/hidparser.c@ d5955a5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d5955a5 was 1e371cf, checked in by Jiri Svoboda <jiri@…>, 12 years ago

Fix USB HID regression.

  • Property mode set to 100644
File size: 15.6 KB
Line 
1/*
2 * Copyright (c) 2011 Matej Klonfar
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 libusbhid
30 * @{
31 */
32/** @file
33 * USB HID report data parser implementation.
34 */
35#include <usb/hid/hidparser.h>
36#include <errno.h>
37#include <stdio.h>
38#include <malloc.h>
39#include <mem.h>
40#include <usb/debug.h>
41#include <assert.h>
42
43
44/*
45 * Data translation private functions
46 */
47uint32_t usb_hid_report_tag_data_uint32(const uint8_t *data, size_t size);
48
49int usb_hid_translate_data(usb_hid_report_field_t *item, const uint8_t *data);
50
51uint32_t usb_hid_translate_data_reverse(usb_hid_report_field_t *item,
52 int32_t value);
53
54
55
56static int usb_pow(int a, int b)
57{
58 switch (b) {
59 case 0:
60 return 1;
61 break;
62 case 1:
63 return a;
64 break;
65 default:
66 return a * usb_pow(a, b - 1);
67 break;
68 }
69}
70
71
72/** Returns size of report of specified report id and type in items
73 *
74 * @param parser Opaque report parser structure
75 * @param report_id
76 * @param type
77 * @return Number of items in specified report
78 */
79size_t usb_hid_report_size(usb_hid_report_t *report, uint8_t report_id,
80 usb_hid_report_type_t type)
81{
82 usb_hid_report_description_t *report_des;
83
84 if (report == NULL) {
85 return 0;
86 }
87
88 report_des = usb_hid_report_find_description(report, report_id, type);
89 if (report_des == NULL) {
90 return 0;
91 } else {
92 return report_des->item_length;
93 }
94}
95
96/** Returns size of report of specified report id and type in bytes
97 *
98 * @param parser Opaque report parser structure
99 * @param report_id
100 * @param type
101 * @return Number of items in specified report
102 */
103size_t usb_hid_report_byte_size(usb_hid_report_t *report, uint8_t report_id,
104 usb_hid_report_type_t type)
105{
106 usb_hid_report_description_t *report_des;
107
108 if (report == NULL) {
109 return 0;
110 }
111
112 report_des = usb_hid_report_find_description(report, report_id, type);
113 if (report_des == NULL) {
114 return 0;
115 } else {
116 return ((report_des->bit_length + 7) / 8) ;
117 }
118}
119
120
121/** Parse and act upon a HID report.
122 *
123 * @see usb_hid_parse_report_descriptor
124 *
125 * @param parser Opaque HID report parser structure.
126 * @param data Data for the report.
127 * @return Error code.
128 */
129int usb_hid_parse_report(const usb_hid_report_t *report, const uint8_t *data,
130 size_t size, uint8_t *report_id)
131{
132 usb_hid_report_description_t *report_des;
133 usb_hid_report_type_t type = USB_HID_REPORT_TYPE_INPUT;
134
135 if (report == NULL) {
136 return EINVAL;
137 }
138
139 if (report->use_report_ids != 0) {
140 *report_id = data[0];
141 } else {
142 *report_id = 0;
143 }
144
145 report_des = usb_hid_report_find_description(report, *report_id,
146 type);
147
148 if (report_des == NULL) {
149 return EINVAL;
150 }
151
152 /* read data */
153 list_foreach(report_des->report_items, ritems_link,
154 usb_hid_report_field_t, item) {
155
156 if (USB_HID_ITEM_FLAG_CONSTANT(item->item_flags) == 0) {
157
158 if (USB_HID_ITEM_FLAG_VARIABLE(item->item_flags) == 0) {
159 /* array */
160 item->value =
161 usb_hid_translate_data(item, data);
162
163 item->usage = USB_HID_EXTENDED_USAGE(
164 item->usages[item->value -
165 item->physical_minimum]);
166
167 item->usage_page =
168 USB_HID_EXTENDED_USAGE_PAGE(
169 item->usages[item->value -
170 item->physical_minimum]);
171
172 usb_hid_report_set_last_item(
173 item->collection_path,
174 USB_HID_TAG_CLASS_GLOBAL,
175 item->usage_page);
176
177 usb_hid_report_set_last_item(
178 item->collection_path,
179 USB_HID_TAG_CLASS_LOCAL, item->usage);
180 } else {
181 /* variable item */
182 item->value = usb_hid_translate_data(item,
183 data);
184 }
185 }
186 }
187
188 return EOK;
189}
190
191
192/**
193 * Translate data from the report as specified in report descriptor item
194 *
195 * @param item Report descriptor item with definition of translation
196 * @param data Data to translate
197 * @return Translated data
198 */
199int usb_hid_translate_data(usb_hid_report_field_t *item, const uint8_t *data)
200{
201 int resolution;
202 int offset;
203 int part_size;
204
205 int32_t value = 0;
206 int32_t mask = 0;
207 const uint8_t *foo = 0;
208
209 /* now only short tags are allowed */
210 if (item->size > 32) {
211 return 0;
212 }
213
214 if ((item->physical_minimum == 0) && (item->physical_maximum == 0)) {
215 item->physical_minimum = item->logical_minimum;
216 item->physical_maximum = item->logical_maximum;
217 }
218
219
220 if (item->physical_maximum == item->physical_minimum) {
221 resolution = 1;
222 } else {
223 resolution = (item->logical_maximum - item->logical_minimum) /
224 ((item->physical_maximum - item->physical_minimum) *
225 (usb_pow(10, (item->unit_exponent))));
226 }
227
228 offset = item->offset;
229 // FIXME
230 if ((size_t) (offset / 8) != (size_t) ((offset+item->size - 1) / 8)) {
231
232 part_size = 0;
233
234 size_t i = 0;
235 for (i = (size_t) (offset / 8);
236 i <= (size_t) (offset + item->size - 1) / 8; i++) {
237 if (i == (size_t) (offset / 8)) {
238 /* the higher one */
239 part_size = 8 - (offset % 8);
240 foo = data + i;
241 mask = ((1 << (item->size - part_size)) - 1);
242 value = (*foo & mask);
243 } else if (i == ((offset + item->size - 1) / 8)) {
244 /* the lower one */
245 foo = data + i;
246 mask = ((1 << (item->size - part_size)) - 1) <<
247 (8 - (item->size - part_size));
248
249 value = (((*foo & mask) >> (8 -
250 (item->size - part_size))) << part_size) +
251 value;
252 } else {
253 value = (*(data + 1) << (part_size + 8)) +
254 value;
255 part_size += 8;
256 }
257 }
258 } else {
259 foo = data + (offset / 8);
260 mask = ((1 << item->size) - 1) <<
261 (8 - ((offset % 8) + item->size));
262 value = (*foo & mask) >> (8 - ((offset % 8) + item->size));
263 }
264
265 if ((item->logical_minimum < 0) || (item->logical_maximum < 0)) {
266 value = USB_HID_UINT32_TO_INT32(value, item->size);
267 }
268
269 return (int) (((value - item->logical_minimum) / resolution) +
270 item->physical_minimum);
271}
272
273
274/* OUTPUT API */
275
276/**
277 * Allocates output report buffer for output report
278 *
279 * @param parser Report parsed structure
280 * @param size Size of returned buffer
281 * @param report_id Report id of created output report
282 * @return Returns allocated output buffer for specified output
283 */
284uint8_t *usb_hid_report_output(usb_hid_report_t *report, size_t *size,
285 uint8_t report_id)
286{
287 if (report == NULL) {
288 *size = 0;
289 return NULL;
290 }
291
292 usb_hid_report_description_t *report_des = NULL;
293
294 list_foreach(report->reports, reports_link,
295 usb_hid_report_description_t, rdes) {
296 if ((rdes->report_id == report_id) &&
297 (rdes->type == USB_HID_REPORT_TYPE_OUTPUT)) {
298 report_des = rdes;
299 break;
300 }
301 }
302
303 if (report_des == NULL) {
304 *size = 0;
305 return NULL;
306 } else {
307 *size = (report_des->bit_length + (8 - 1)) / 8;
308 uint8_t *ret = malloc((*size) * sizeof(uint8_t));
309 memset(ret, 0, (*size) * sizeof(uint8_t));
310 return ret;
311 }
312}
313
314
315/** Frees output report buffer
316 *
317 * @param output Output report buffer
318 * @return void
319 */
320void usb_hid_report_output_free(uint8_t *output)
321{
322 if (output != NULL) {
323 free(output);
324 }
325}
326
327/** Makes the output report buffer for data given in the report structure
328 *
329 * @param parser Opaque report parser structure
330 * @param path Usage path specifing which parts of output will be set
331 * @param flags Usage path structure comparison flags
332 * @param buffer Output buffer
333 * @param size Size of output buffer
334 * @return Error code
335 */
336int usb_hid_report_output_translate(usb_hid_report_t *report,
337 uint8_t report_id, uint8_t *buffer, size_t size)
338{
339 int32_t value = 0;
340 int offset;
341 int length;
342 int32_t tmp_value;
343
344 if (report == NULL) {
345 return EINVAL;
346 }
347
348 if (report->use_report_ids != 0) {
349 buffer[0] = report_id;
350 }
351
352 usb_hid_report_description_t *report_des;
353 report_des = usb_hid_report_find_description(report, report_id,
354 USB_HID_REPORT_TYPE_OUTPUT);
355
356 if (report_des == NULL) {
357 return EINVAL;
358 }
359
360 list_foreach(report_des->report_items, ritems_link,
361 usb_hid_report_field_t, report_item) {
362 value = usb_hid_translate_data_reverse(report_item,
363 report_item->value);
364
365 offset = report_des->bit_length - report_item->offset - 1;
366 length = report_item->size;
367
368 usb_log_debug("\ttranslated value: %x\n", value);
369
370 if ((offset / 8) == ((offset + length - 1) / 8)) {
371 if (((size_t) (offset / 8) >= size) ||
372 ((size_t) (offset + length - 1) / 8) >= size) {
373 break; // TODO ErrorCode
374 }
375 size_t shift = 8 - offset % 8 - length;
376 value = value << shift;
377 value = value & (((1 << length) - 1) << shift);
378
379 uint8_t mask = 0;
380 mask = 0xff - (((1 << length) - 1) << shift);
381 buffer[offset / 8] = (buffer[offset / 8] & mask) |
382 value;
383 } else {
384 int i = 0;
385 uint8_t mask = 0;
386 for (i = (offset / 8);
387 i <= ((offset + length - 1) / 8); i++) {
388 if (i == (offset / 8)) {
389 tmp_value = value;
390 tmp_value = tmp_value &
391 ((1 << (8 - (offset % 8))) - 1);
392
393 tmp_value = tmp_value << (offset % 8);
394
395 mask = ~(((1 << (8 - (offset % 8))) - 1)
396 << (offset % 8));
397
398 buffer[i] = (buffer[i] & mask) |
399 tmp_value;
400 } else if (i == ((offset + length - 1) / 8)) {
401
402 value = value >> (length -
403 ((offset + length) % 8));
404
405 value = value & ((1 << (length -
406 ((offset + length) % 8))) - 1);
407
408 mask = (1 << (length -
409 ((offset + length) % 8))) - 1;
410
411 buffer[i] = (buffer[i] & mask) | value;
412 } else {
413 buffer[i] = value & (0xff << i);
414 }
415 }
416 }
417
418 /* reset value */
419 report_item->value = 0;
420 }
421
422 return EOK;
423}
424
425
426/**
427 * Translate given data for putting them into the outoput report
428 * @param item Report item structure
429 * @param value Value to translate
430 * @return ranslated value
431 */
432uint32_t usb_hid_translate_data_reverse(usb_hid_report_field_t *item,
433 int value)
434{
435 int ret = 0;
436 int resolution;
437
438 if (USB_HID_ITEM_FLAG_CONSTANT(item->item_flags)) {
439 ret = item->logical_minimum;
440 }
441
442 if ((item->physical_minimum == 0) && (item->physical_maximum == 0)) {
443 item->physical_minimum = item->logical_minimum;
444 item->physical_maximum = item->logical_maximum;
445 }
446
447 /* variable item */
448 if (item->physical_maximum == item->physical_minimum) {
449 resolution = 1;
450 } else {
451 resolution = (item->logical_maximum - item->logical_minimum) /
452 ((item->physical_maximum - item->physical_minimum) *
453 (usb_pow(10, (item->unit_exponent))));
454 }
455
456 ret = ((value - item->physical_minimum) * resolution) +
457 item->logical_minimum;
458
459 usb_log_debug("\tvalue(%x), resolution(%x), phymin(%x) logmin(%x), "
460 "ret(%x)\n", value, resolution, item->physical_minimum,
461 item->logical_minimum, ret);
462
463 if ((item->logical_minimum < 0) || (item->logical_maximum < 0)) {
464 return USB_HID_INT32_TO_UINT32(ret, item->size);
465 }
466
467 return (int32_t) 0 + ret;
468}
469
470
471/**
472 * Clones given state table
473 *
474 * @param item State table to clone
475 * @return Pointer to the cloned item
476 */
477usb_hid_report_item_t *usb_hid_report_item_clone(
478 const usb_hid_report_item_t *item)
479{
480 usb_hid_report_item_t *new_report_item;
481
482 if (!(new_report_item = malloc(sizeof(usb_hid_report_item_t)))) {
483 return NULL;
484 }
485 memcpy(new_report_item,item, sizeof(usb_hid_report_item_t));
486 link_initialize(&(new_report_item->link));
487
488 return new_report_item;
489}
490
491
492/**
493 * Function for sequence walking through the report. Returns next field in the
494 * report or the first one when no field is given.
495 *
496 * @param report Searched report structure
497 * @param field Current field. If NULL is given, the first one in the report
498 * is returned. Otherwise the next one i nthe list is returned.
499 * @param path Usage path specifying which fields wa are interested in.
500 * @param flags Flags defining mode of usage paths comparison
501 * @param type Type of report we search.
502 * @retval NULL if no field is founded
503 * @retval Pointer to the founded report structure when founded
504 */
505usb_hid_report_field_t *usb_hid_report_get_sibling(usb_hid_report_t *report,
506 usb_hid_report_field_t *field, usb_hid_report_path_t *path, int flags,
507 usb_hid_report_type_t type)
508{
509 usb_hid_report_description_t *report_des =
510 usb_hid_report_find_description(report, path->report_id, type);
511
512 link_t *field_it;
513
514 if (report_des == NULL) {
515 return NULL;
516 }
517
518 if (field == NULL) {
519 field_it = report_des->report_items.head.next;
520 } else {
521 field_it = field->ritems_link.next;
522 }
523
524 while (field_it != &report_des->report_items.head) {
525 field = list_get_instance(field_it, usb_hid_report_field_t,
526 ritems_link);
527
528 if (USB_HID_ITEM_FLAG_CONSTANT(field->item_flags) == 0) {
529 usb_hid_report_path_append_item(field->collection_path,
530 field->usage_page, field->usage);
531
532 if (usb_hid_report_compare_usage_path(
533 field->collection_path, path, flags) == EOK) {
534 usb_hid_report_remove_last_item(
535 field->collection_path);
536 return field;
537 }
538 usb_hid_report_remove_last_item(field->collection_path);
539 }
540 field_it = field_it->next;
541 }
542
543 return NULL;
544}
545
546
547/**
548 * Returns next report_id of report of specified type. If zero is given than
549 * first report_id of specified type is returned (0 is not legal value for
550 * repotr_id)
551 *
552 * @param report_id Current report_id, 0 if there is no current report_id
553 * @param type Type of searched report
554 * @param report Report structure inwhich we search
555 * @retval 0 if report structure is null or there is no specified report
556 * @retval report_id otherwise
557 */
558uint8_t usb_hid_get_next_report_id(usb_hid_report_t *report, uint8_t report_id,
559 usb_hid_report_type_t type)
560{
561 if (report == NULL) {
562 return 0;
563 }
564
565 usb_hid_report_description_t *report_des;
566 link_t *report_it;
567
568 if (report_id > 0) {
569 report_des = usb_hid_report_find_description(report, report_id,
570 type);
571 if (report_des == NULL) {
572 return 0;
573 } else {
574 report_it = report_des->reports_link.next;
575 }
576 } else {
577 report_it = report->reports.head.next;
578 }
579
580 while (report_it != &report->reports.head) {
581 report_des = list_get_instance(report_it,
582 usb_hid_report_description_t, reports_link);
583
584 if (report_des->type == type) {
585 return report_des->report_id;
586 }
587
588 report_it = report_it->next;
589 }
590
591 return 0;
592}
593
594
595/**
596 * Reset all local items in given state table
597 *
598 * @param report_item State table containing current state of report
599 * descriptor parsing
600 *
601 * @return void
602 */
603void usb_hid_report_reset_local_items(usb_hid_report_item_t *report_item)
604{
605 if (report_item == NULL) {
606 return;
607 }
608
609 report_item->usages_count = 0;
610 memset(report_item->usages, 0, USB_HID_MAX_USAGES);
611
612 report_item->extended_usage_page = 0;
613 report_item->usage_minimum = 0;
614 report_item->usage_maximum = 0;
615 report_item->designator_index = 0;
616 report_item->designator_minimum = 0;
617 report_item->designator_maximum = 0;
618 report_item->string_index = 0;
619 report_item->string_minimum = 0;
620 report_item->string_maximum = 0;
621}
622
623/**
624 * @}
625 */
Note: See TracBrowser for help on using the repository browser.