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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 711e7fe5 was 711e7fe5, checked in by Prutkov Alex <prutkov.alex@…>, 13 years ago

Added printf module

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