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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 79ae36dd was 0dd3e49, checked in by Matej Klonfar <maklf@…>, 14 years ago

find report description bug fix

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