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

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

Reports traversing fixed

  • Property mode set to 100644
File size: 16.8 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) << part_size;
261 }
262 else if(i == ((offset+item->size-1)/8)){
263 // the lower one
264 foo = data + i;
265 mask = ((1 << part_size)-1) << (8-part_size);
266 value += ((*foo & mask) >> (8-part_size));
267 }
268 else {
269 value = value << 8;
270 value += *(data + 1);
271 part_size += 8;
272 }
273 }
274 }
275 else {
276 foo = data+(offset/8);
277 mask = ((1 << item->size)-1) << (8-((offset%8)+item->size));
278 value = (*foo & mask) >> (8-((offset%8)+item->size));
279 }
280
281 if((item->logical_minimum < 0) || (item->logical_maximum < 0)){
282 value = USB_HID_UINT32_TO_INT32(value, item->size);
283 }
284
285 return (int)(((value - item->logical_minimum) / resolution) +
286 item->physical_minimum);
287
288}
289
290/*---------------------------------------------------------------------------*/
291/* OUTPUT API */
292
293/**
294 * Allocates output report buffer for output report
295 *
296 * @param parser Report parsed structure
297 * @param size Size of returned buffer
298 * @param report_id Report id of created output report
299 * @return Returns allocated output buffer for specified output
300 */
301uint8_t *usb_hid_report_output(usb_hid_report_t *report, size_t *size,
302 uint8_t report_id)
303{
304 if(report == NULL) {
305 *size = 0;
306 return NULL;
307 }
308
309 link_t *report_it = report->reports.next;
310 usb_hid_report_description_t *report_des = NULL;
311 while(report_it != &report->reports) {
312 report_des = list_get_instance(report_it,
313 usb_hid_report_description_t, link);
314
315 if((report_des->report_id == report_id) &&
316 (report_des->type == USB_HID_REPORT_TYPE_OUTPUT)){
317 break;
318 }
319
320 report_it = report_it->next;
321 }
322
323 if(report_des == NULL){
324 *size = 0;
325 return NULL;
326 }
327 else {
328 *size = (report_des->bit_length + (8 - 1))/8;
329 uint8_t *ret = malloc((*size) * sizeof(uint8_t));
330 memset(ret, 0, (*size) * sizeof(uint8_t));
331 return ret;
332 }
333}
334
335
336/** Frees output report buffer
337 *
338 * @param output Output report buffer
339 * @return void
340 */
341void usb_hid_report_output_free(uint8_t *output)
342
343{
344 if(output != NULL) {
345 free (output);
346 }
347}
348
349/** Makes the output report buffer for data given in the report structure
350 *
351 * @param parser Opaque report parser structure
352 * @param path Usage path specifing which parts of output will be set
353 * @param flags Usage path structure comparison flags
354 * @param buffer Output buffer
355 * @param size Size of output buffer
356 * @return Error code
357 */
358int usb_hid_report_output_translate(usb_hid_report_t *report,
359 uint8_t report_id, uint8_t *buffer, size_t size)
360{
361 link_t *item;
362 int32_t value=0;
363 int offset;
364 int length;
365 int32_t tmp_value;
366
367 if(report == NULL) {
368 return EINVAL;
369 }
370
371 if(report->use_report_ids != 0) {
372 buffer[0] = report_id;
373 }
374
375 usb_hid_report_description_t *report_des;
376 report_des = usb_hid_report_find_description (report, report_id,
377 USB_HID_REPORT_TYPE_OUTPUT);
378
379 if(report_des == NULL){
380 return EINVAL;
381 }
382
383 usb_hid_report_field_t *report_item;
384 item = report_des->report_items.next;
385 while(item != &report_des->report_items) {
386 report_item = list_get_instance(item, usb_hid_report_field_t, link);
387
388 if(USB_HID_ITEM_FLAG_VARIABLE(report_item->item_flags) == 0) {
389
390 // array
391 value = usb_hid_translate_data_reverse(report_item,
392 report_item->value);
393
394 offset = report_item->offset;
395 length = report_item->size;
396 }
397 else {
398 // variable item
399 value = usb_hid_translate_data_reverse(report_item,
400 report_item->value);
401
402 offset = report_item->offset;
403 length = report_item->size;
404 }
405
406 usb_log_debug("\ttranslated value: %x\n", value);
407
408 if((offset/8) == ((offset+length-1)/8)) {
409 // je to v jednom bytu
410 if(((size_t)(offset/8) >= size) ||
411 ((size_t)(offset+length-1)/8) >= size) {
412 break; // TODO ErrorCode
413 }
414 size_t shift = 8 - offset%8 - length;
415 value = value << shift;
416 value = value & (((1 << length)-1) << shift);
417
418 uint8_t mask = 0;
419 mask = 0xff - (((1 << length) - 1) << shift);
420 buffer[offset/8] = (buffer[offset/8] & mask) | value;
421 }
422 else {
423 int i = 0;
424 uint8_t mask = 0;
425 for(i = (offset/8); i <= ((offset+length-1)/8); i++) {
426 if(i == (offset/8)) {
427 tmp_value = value;
428 tmp_value = tmp_value &
429 ((1 << (8-(offset%8)))-1);
430
431 tmp_value = tmp_value << (offset%8);
432
433 mask = ~(((1 << (8-(offset%8)))-1) <<
434 (offset%8));
435
436 buffer[i] = (buffer[i] & mask) |
437 tmp_value;
438 }
439 else if (i == ((offset + length -1)/8)) {
440
441 value = value >> (length -
442 ((offset + length) % 8));
443
444 value = value & ((1 << (length -
445 ((offset + length) % 8))) - 1);
446
447 mask = (1 << (length -
448 ((offset + length) % 8))) - 1;
449
450 buffer[i] = (buffer[i] & mask) | value;
451 }
452 else {
453 buffer[i] = value & (0xFF << i);
454 }
455 }
456 }
457
458 // reset value
459 report_item->value = 0;
460
461 item = item->next;
462 }
463
464 return EOK;
465}
466
467/*---------------------------------------------------------------------------*/
468/**
469 * Translate given data for putting them into the outoput report
470 * @param item Report item structure
471 * @param value Value to translate
472 * @return ranslated value
473 */
474uint32_t usb_hid_translate_data_reverse(usb_hid_report_field_t *item,
475 int value)
476{
477 int ret=0;
478 int resolution;
479
480 if(USB_HID_ITEM_FLAG_CONSTANT(item->item_flags)) {
481 ret = item->logical_minimum;
482 }
483
484 if((item->physical_minimum == 0) && (item->physical_maximum == 0)){
485 item->physical_minimum = item->logical_minimum;
486 item->physical_maximum = item->logical_maximum;
487 }
488
489 // variable item
490 if(item->physical_maximum == item->physical_minimum){
491 resolution = 1;
492 }
493 else {
494 resolution = (item->logical_maximum - item->logical_minimum) /
495 ((item->physical_maximum - item->physical_minimum) *
496 (usb_pow(10,(item->unit_exponent))));
497 }
498
499 ret = ((value - item->physical_minimum) * resolution) +
500 item->logical_minimum;
501
502 usb_log_debug("\tvalue(%x), resolution(%x), phymin(%x) logmin(%x), \
503 ret(%x)\n", value, resolution, item->physical_minimum,
504 item->logical_minimum, ret);
505
506 if((item->logical_minimum < 0) || (item->logical_maximum < 0)){
507 return USB_HID_INT32_TO_UINT32(ret, item->size);
508 }
509 return (int32_t)0 + ret;
510}
511
512/*---------------------------------------------------------------------------*/
513/**
514 * Clones given state table
515 *
516 * @param item State table to clone
517 * @return Pointer to the cloned item
518 */
519usb_hid_report_item_t *usb_hid_report_item_clone(
520 const usb_hid_report_item_t *item)
521{
522 usb_hid_report_item_t *new_report_item;
523
524 if(!(new_report_item = malloc(sizeof(usb_hid_report_item_t)))) {
525 return NULL;
526 }
527 memcpy(new_report_item,item, sizeof(usb_hid_report_item_t));
528 link_initialize(&(new_report_item->link));
529
530 return new_report_item;
531}
532
533/*---------------------------------------------------------------------------*/
534/**
535 * Function for sequence walking through the report. Returns next field in the
536 * report or the first one when no field is given.
537 *
538 * @param report Searched report structure
539 * @param field Current field. If NULL is given, the first one in the report
540 * is returned. Otherwise the next one i nthe list is returned.
541 * @param path Usage path specifying which fields wa are interested in.
542 * @param flags Flags defining mode of usage paths comparison
543 * @param type Type of report we search.
544 * @retval NULL if no field is founded
545 * @retval Pointer to the founded report structure when founded
546 */
547usb_hid_report_field_t *usb_hid_report_get_sibling(usb_hid_report_t *report,
548 usb_hid_report_field_t *field, usb_hid_report_path_t *path, int flags,
549 usb_hid_report_type_t type)
550{
551 usb_hid_report_description_t *report_des =
552 usb_hid_report_find_description(report, path->report_id, type);
553
554 link_t *field_it;
555
556 if(report_des == NULL){
557 return NULL;
558 }
559
560 if(field == NULL){
561 field_it = report_des->report_items.next;
562 }
563 else {
564 field_it = field->link.next;
565 }
566
567 while(field_it != &report_des->report_items) {
568 field = list_get_instance(field_it, usb_hid_report_field_t,
569 link);
570
571 if(USB_HID_ITEM_FLAG_CONSTANT(field->item_flags) == 0) {
572 usb_hid_report_path_append_item (
573 field->collection_path, field->usage_page,
574 field->usage);
575
576 if(usb_hid_report_compare_usage_path(
577 field->collection_path, path, flags) == EOK){
578
579 usb_hid_report_remove_last_item(
580 field->collection_path);
581
582 return field;
583 }
584 usb_hid_report_remove_last_item (
585 field->collection_path);
586 }
587 field_it = field_it->next;
588 }
589
590 return NULL;
591}
592
593/*---------------------------------------------------------------------------*/
594/**
595 * Returns next report_id of report of specified type. If zero is given than
596 * first report_id of specified type is returned (0 is not legal value for
597 * repotr_id)
598 *
599 * @param report_id Current report_id, 0 if there is no current report_id
600 * @param type Type of searched report
601 * @param report Report structure inwhich we search
602 * @retval 0 if report structure is null or there is no specified report
603 * @retval report_id otherwise
604 */
605uint8_t usb_hid_get_next_report_id(usb_hid_report_t *report,
606 uint8_t report_id, usb_hid_report_type_t type)
607{
608 if(report == NULL){
609 return 0;
610 }
611
612 usb_hid_report_description_t *report_des;
613 link_t *report_it;
614
615 if(report_id > 0) {
616 report_it = usb_hid_report_find_description(report, report_id,
617 type)->link.next;
618 }
619 else {
620 report_it = report->reports.next;
621 }
622
623 while(report_it != &report->reports) {
624 report_des = list_get_instance(report_it,
625 usb_hid_report_description_t, link);
626
627 if(report_des->type == type){
628 return report_des->report_id;
629 }
630
631 report_it = report_it->next;
632 }
633
634 return 0;
635}
636
637/*---------------------------------------------------------------------------*/
638/**
639 * Reset all local items in given state table
640 *
641 * @param report_item State table containing current state of report
642 * descriptor parsing
643 *
644 * @return void
645 */
646void usb_hid_report_reset_local_items(usb_hid_report_item_t *report_item)
647{
648 if(report_item == NULL) {
649 return;
650 }
651
652 report_item->usages_count = 0;
653 memset(report_item->usages, 0, USB_HID_MAX_USAGES);
654
655 report_item->extended_usage_page = 0;
656 report_item->usage_minimum = 0;
657 report_item->usage_maximum = 0;
658 report_item->designator_index = 0;
659 report_item->designator_minimum = 0;
660 report_item->designator_maximum = 0;
661 report_item->string_index = 0;
662 report_item->string_minimum = 0;
663 report_item->string_maximum = 0;
664
665 return;
666}
667/**
668 * @}
669 */
Note: See TracBrowser for help on using the repository browser.