source: mainline/uspace/lib/usb/src/hidparser.c@ fad14d7

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

Basic report descriptor imlementation

  • Property mode set to 100644
File size: 17.9 KB
Line 
1/*
2 * Copyright (c) 2010 Vojtech Horky
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 libusb
30 * @{
31 */
32/** @file
33 * @brief HID parser implementation.
34 */
35
36
37#include <usb/classes/hidparser.h>
38#include <errno.h>
39#include <stdio.h>
40#include <malloc.h>
41#include <mem.h>
42#include <usb/debug.h>
43
44
45#define USB_HID_NEW_REPORT_ITEM 1
46#define USB_HID_NO_ACTION 2
47#define USB_HID_UNKNOWN_TAG -99
48
49#define BAD_HACK_USAGE_PAGE 0x07
50
51int usb_hid_report_parse_tag(uint8_t tag, uint8_t class, const uint8_t *data, size_t item_size,
52 usb_hid_report_item_t *report_item);
53int usb_hid_report_parse_main_tag(uint8_t tag, const uint8_t *data, size_t item_size,
54 usb_hid_report_item_t *report_item);
55int usb_hid_report_parse_global_tag(uint8_t tag, const uint8_t *data, size_t item_size,
56 usb_hid_report_item_t *report_item);
57int usb_hid_report_parse_local_tag(uint8_t tag, const uint8_t *data, size_t item_size,
58 usb_hid_report_item_t *report_item);
59
60void usb_hid_descriptor_print_list(link_t *head);
61int usb_hid_report_reset_local_items();
62void usb_hid_free_report_list(link_t *head);
63int32_t usb_hid_report_tag_data_int32(const uint8_t *data, size_t size);
64inline size_t usb_hid_count_item_offset(usb_hid_report_item_t * report_item, size_t offset);
65int usb_hid_translate_data(usb_hid_report_item_t *item, const uint8_t *data, size_t j);
66int usb_pow(int a, int b);
67
68int usb_pow(int a, int b)
69{
70 switch(b) {
71 case 0:
72 return 1;
73 break;
74 case 1:
75 return a;
76 break;
77 default:
78 return a * usb_pow(a, b-1);
79 break;
80 }
81}
82
83/**
84 *
85 */
86int usb_hid_parser_init(usb_hid_report_parser_t *parser)
87{
88 if(parser == NULL) {
89 return -1;
90 }
91
92 list_initialize(&(parser->input));
93 list_initialize(&(parser->output));
94 list_initialize(&(parser->feature));
95
96 return EOK;
97}
98
99
100/** Parse HID report descriptor.
101 *
102 * @param parser Opaque HID report parser structure.
103 * @param data Data describing the report.
104 * @return Error code.
105 */
106int usb_hid_parse_report_descriptor(usb_hid_report_parser_t *parser,
107 const uint8_t *data, size_t size)
108{
109 size_t i=0;
110 uint8_t tag=0;
111 uint8_t item_size=0;
112 int class=0;
113 int ret;
114 usb_hid_report_item_t *report_item=0;
115 usb_hid_report_item_t *new_report_item;
116
117 size_t offset=0;
118
119
120 if(!(report_item=malloc(sizeof(usb_hid_report_item_t)))){
121 return ENOMEM;
122 }
123 link_initialize(&(report_item->link));
124
125 while(i<size){
126 if(!USB_HID_ITEM_IS_LONG(data[i])){
127
128 if((i+1) >= size){
129 return -1; // TODO ERROR CODE
130 }
131
132 tag = USB_HID_ITEM_TAG(data[i]);
133 item_size = USB_HID_ITEM_SIZE(data[i]);
134 class = USB_HID_ITEM_TAG_CLASS(data[i]);
135
136 usb_log_debug2(
137 "i(%u) data(%X) value(%X): TAG %u, class %u, size %u - ", i,
138 data[i], usb_hid_report_tag_data_int32(data+i+1,item_size),
139 tag, class, item_size);
140
141 ret = usb_hid_report_parse_tag(tag,class,data+i+1,
142 item_size,report_item);
143 usb_log_debug2("ret: %u\n", ret);
144 switch(ret){
145 case USB_HID_NEW_REPORT_ITEM:
146 // store report item to report and create the new one
147 usb_log_debug("\nNEW REPORT ITEM: %X",tag);
148
149 report_item->offset = offset;
150 offset += report_item->count * report_item->size;
151
152 switch(tag) {
153 case USB_HID_REPORT_TAG_INPUT:
154 usb_log_debug(" - INPUT\n");
155 list_append(&(report_item->link), &(parser->input));
156 break;
157 case USB_HID_REPORT_TAG_OUTPUT:
158 usb_log_debug(" - OUTPUT\n");
159 list_append(&(report_item->link), &(parser->output));
160
161 break;
162 case USB_HID_REPORT_TAG_FEATURE:
163 usb_log_debug(" - FEATURE\n");
164 list_append(&(report_item->link), &(parser->feature));
165 break;
166 default:
167 usb_log_debug("\tjump over - tag %X\n", tag);
168 break;
169 }
170
171 /* clone current state table to the new item */
172 if(!(new_report_item = malloc(sizeof(usb_hid_report_item_t)))) {
173 return ENOMEM;
174 }
175 memcpy(new_report_item,report_item, sizeof(usb_hid_report_item_t));
176 link_initialize(&(new_report_item->link));
177 report_item = new_report_item;
178
179 break;
180 case USB_HID_REPORT_TAG_PUSH:
181 // push current state to stack
182 // not yet implemented
183 break;
184 case USB_HID_REPORT_TAG_POP:
185 // restore current state from stack
186 // not yet implemented
187 break;
188
189 default:
190 // nothing special to do
191 break;
192 }
193
194 /* jump over the processed block */
195 i += 1 + USB_HID_ITEM_SIZE(data[i]);
196 }
197 else{
198 // TBD
199 i += 3 + USB_HID_ITEM_SIZE(data[i+1]);
200 }
201
202
203 }
204
205
206 return EOK;
207}
208
209
210/**
211 * Parse input report.
212 *
213 * @param data Data for report
214 * @param size Size of report
215 * @param callbacks Callbacks for report actions
216 * @param arg Custom arguments
217 *
218 * @return Error code
219 */
220int usb_hid_boot_keyboard_input_report(const uint8_t *data, size_t size,
221 const usb_hid_report_in_callbacks_t *callbacks, void *arg)
222{
223 int i;
224 usb_hid_report_item_t item;
225
226 /* fill item due to the boot protocol report descriptor */
227 // modifier keys are in the first byte
228 uint8_t modifiers = data[0];
229
230 item.offset = 2; /* second byte is reserved */
231 item.size = 8;
232 item.count = 6;
233 item.usage_minimum = 0;
234 item.usage_maximum = 255;
235 item.logical_minimum = 0;
236 item.logical_maximum = 255;
237
238 if (size != 8) {
239 return -1; //ERANGE;
240 }
241
242 uint8_t keys[6];
243 for (i = 0; i < item.count; i++) {
244 keys[i] = data[i + item.offset];
245 }
246
247 callbacks->keyboard(keys, 6, modifiers, arg);
248 return EOK;
249}
250
251/**
252 * Makes output report for keyboard boot protocol
253 *
254 * @param leds
255 * @param output Output report data buffer
256 * @param size Size of the output buffer
257 * @return Error code
258 */
259int usb_hid_boot_keyboard_output_report(uint8_t leds, uint8_t *data, size_t size)
260{
261 if(size != 1){
262 return -1;
263 }
264
265 /* used only first five bits, others are only padding*/
266 *data = leds;
267 return EOK;
268}
269
270/**
271 *
272 * @param Tag to parse
273 * @param Report descriptor buffer
274 * @param Size of data belongs to this tag
275 * @param Current report item structe
276 * @return Code of action to be done next
277 */
278int usb_hid_report_parse_tag(uint8_t tag, uint8_t class, const uint8_t *data, size_t item_size,
279 usb_hid_report_item_t *report_item)
280{
281 int ret;
282
283 switch(class){
284 case USB_HID_TAG_CLASS_MAIN:
285
286 if((ret=usb_hid_report_parse_main_tag(tag,data,item_size,report_item)) == EOK) {
287 return USB_HID_NEW_REPORT_ITEM;
288 }
289 else {
290 /*TODO process the error */
291 return ret;
292 }
293 break;
294
295 case USB_HID_TAG_CLASS_GLOBAL:
296 return usb_hid_report_parse_global_tag(tag,data,item_size,report_item);
297 break;
298
299 case USB_HID_TAG_CLASS_LOCAL:
300 return usb_hid_report_parse_local_tag(tag,data,item_size,report_item);
301 break;
302 default:
303 return USB_HID_NO_ACTION;
304 }
305}
306
307/**
308 * Parse main tags of report descriptor
309 *
310 * @param Tag identifier
311 * @param Data buffer
312 * @param Length of data buffer
313 * @param Current state table
314 * @return Error code
315 */
316
317int usb_hid_report_parse_main_tag(uint8_t tag, const uint8_t *data, size_t item_size,
318 usb_hid_report_item_t *report_item)
319{
320 switch(tag)
321 {
322 case USB_HID_REPORT_TAG_INPUT:
323 case USB_HID_REPORT_TAG_OUTPUT:
324 case USB_HID_REPORT_TAG_FEATURE:
325 report_item->item_flags = *data;
326 return EOK;
327 break;
328
329 case USB_HID_REPORT_TAG_COLLECTION:
330 // TODO
331 break;
332
333 case USB_HID_REPORT_TAG_END_COLLECTION:
334 /* should be ignored */
335 break;
336 default:
337 return USB_HID_NO_ACTION;
338 }
339
340 return USB_HID_NO_ACTION;
341}
342
343/**
344 * Parse global tags of report descriptor
345 *
346 * @param Tag identifier
347 * @param Data buffer
348 * @param Length of data buffer
349 * @param Current state table
350 * @return Error code
351 */
352
353int usb_hid_report_parse_global_tag(uint8_t tag, const uint8_t *data, size_t item_size,
354 usb_hid_report_item_t *report_item)
355{
356 // TODO take care about the bit length of data
357 switch(tag)
358 {
359 case USB_HID_REPORT_TAG_USAGE_PAGE:
360 report_item->usage_page = usb_hid_report_tag_data_int32(data,item_size);
361 break;
362 case USB_HID_REPORT_TAG_LOGICAL_MINIMUM:
363 report_item->logical_minimum = usb_hid_report_tag_data_int32(data,item_size);
364 break;
365 case USB_HID_REPORT_TAG_LOGICAL_MAXIMUM:
366 report_item->logical_maximum = usb_hid_report_tag_data_int32(data,item_size);
367 break;
368 case USB_HID_REPORT_TAG_PHYSICAL_MINIMUM:
369 report_item->physical_minimum = usb_hid_report_tag_data_int32(data,item_size);
370 break;
371 case USB_HID_REPORT_TAG_PHYSICAL_MAXIMUM:
372 report_item->physical_maximum = usb_hid_report_tag_data_int32(data,item_size);
373 break;
374 case USB_HID_REPORT_TAG_UNIT_EXPONENT:
375 report_item->unit_exponent = usb_hid_report_tag_data_int32(data,item_size);
376 break;
377 case USB_HID_REPORT_TAG_UNIT:
378 report_item->unit = usb_hid_report_tag_data_int32(data,item_size);
379 break;
380 case USB_HID_REPORT_TAG_REPORT_SIZE:
381 report_item->size = usb_hid_report_tag_data_int32(data,item_size);
382 break;
383 case USB_HID_REPORT_TAG_REPORT_COUNT:
384 report_item->count = usb_hid_report_tag_data_int32(data,item_size);
385 break;
386 case USB_HID_REPORT_TAG_REPORT_ID:
387 report_item->id = usb_hid_report_tag_data_int32(data,item_size);
388 break;
389 case USB_HID_REPORT_TAG_PUSH:
390 case USB_HID_REPORT_TAG_POP:
391 return tag;
392 break;
393
394 default:
395 return USB_HID_NO_ACTION;
396 }
397
398 return EOK;
399}
400
401/**
402 * Parse local tags of report descriptor
403 *
404 * @param Tag identifier
405 * @param Data buffer
406 * @param Length of data buffer
407 * @param Current state table
408 * @return Error code
409 */
410int usb_hid_report_parse_local_tag(uint8_t tag, const uint8_t *data, size_t item_size,
411 usb_hid_report_item_t *report_item)
412{
413 switch(tag)
414 {
415 case USB_HID_REPORT_TAG_USAGE:
416 report_item->usage = usb_hid_report_tag_data_int32(data,item_size);
417 break;
418 case USB_HID_REPORT_TAG_USAGE_MINIMUM:
419 report_item->usage_minimum = usb_hid_report_tag_data_int32(data,item_size);
420 break;
421 case USB_HID_REPORT_TAG_USAGE_MAXIMUM:
422 report_item->usage_maximum = usb_hid_report_tag_data_int32(data,item_size);
423 break;
424 case USB_HID_REPORT_TAG_DESIGNATOR_INDEX:
425 report_item->designator_index = usb_hid_report_tag_data_int32(data,item_size);
426 break;
427 case USB_HID_REPORT_TAG_DESIGNATOR_MINIMUM:
428 report_item->designator_minimum = usb_hid_report_tag_data_int32(data,item_size);
429 break;
430 case USB_HID_REPORT_TAG_DESIGNATOR_MAXIMUM:
431 report_item->designator_maximum = usb_hid_report_tag_data_int32(data,item_size);
432 break;
433 case USB_HID_REPORT_TAG_STRING_INDEX:
434 report_item->string_index = usb_hid_report_tag_data_int32(data,item_size);
435 break;
436 case USB_HID_REPORT_TAG_STRING_MINIMUM:
437 report_item->string_minimum = usb_hid_report_tag_data_int32(data,item_size);
438 break;
439 case USB_HID_REPORT_TAG_STRING_MAXIMUM:
440 report_item->string_maximum = usb_hid_report_tag_data_int32(data,item_size);
441 break;
442/*
443 case USB_HID_REPORT_TAG_DELIMITER:
444 report_item->delimiter = usb_hid_report_tag_data_int32(data,item_size);
445 break;
446*/
447 default:
448 return USB_HID_NO_ACTION;
449 }
450
451 return EOK;
452}
453
454/**
455 * Converts raw data to int32 (thats the maximum length of short item data)
456 *
457 * @param Data buffer
458 * @param Size of buffer
459 * @return Converted int32 number
460 */
461int32_t usb_hid_report_tag_data_int32(const uint8_t *data, size_t size)
462{
463 unsigned int i;
464 int32_t result;
465
466 result = 0;
467 for(i=0; i<size; i++) {
468 result = (result | (data[i]) << (i*8));
469 }
470
471 return result;
472}
473
474
475
476/**
477 * Prints content of given list of report items.
478 *
479 * @param List of report items
480 * @return void
481 */
482void usb_hid_descriptor_print_list(link_t *head)
483{
484 usb_hid_report_item_t *report_item;
485 link_t *item;
486
487 if(head == NULL || list_empty(head)) {
488 printf("\tempty\n");
489 return;
490 }
491
492 for(item = head->next; item != head; item = item->next) {
493
494 report_item = list_get_instance(item, usb_hid_report_item_t, link);
495
496 printf("\tOFFSET: %X\n", report_item->offset);
497 printf("\tCOUNT: %X\n", report_item->count);
498 printf("\tSIZE: %X\n", report_item->size);
499 printf("\tCONSTANT: %X\n", USB_HID_ITEM_FLAG_CONSTANT(report_item->item_flags));
500 printf("\tUSAGE: %X\n", report_item->usage);
501 printf("\tUSAGE PAGE: %X\n", report_item->usage_page);
502 printf("\n");
503
504 }
505
506
507}
508/**
509 * Prints content of given descriptor in human readable format.
510 *
511 * @param Parsed descriptor to print
512 * @return void
513 */
514void usb_hid_descriptor_print(usb_hid_report_parser_t *parser)
515{
516 printf("INPUT:\n");
517 usb_hid_descriptor_print_list(&parser->input);
518
519 printf("OUTPUT: \n");
520 usb_hid_descriptor_print_list(&parser->output);
521
522 printf("FEATURE:\n");
523 usb_hid_descriptor_print_list(&parser->feature);
524
525}
526
527/**
528 * Releases whole linked list of report items
529 *
530 *
531 */
532void usb_hid_free_report_list(link_t *head)
533{
534 return;
535
536 usb_hid_report_item_t *report_item;
537 link_t *next;
538
539 if(head == NULL || list_empty(head)) {
540 return;
541 }
542
543 next = head->next;
544 while(next != head) {
545
546 report_item = list_get_instance(next, usb_hid_report_item_t, link);
547 next = next->next;
548
549 free(report_item);
550 }
551
552 return;
553
554}
555
556/** Free the HID report parser structure
557 *
558 * @param parser Opaque HID report parser structure
559 * @return Error code
560 */
561void usb_hid_free_report_parser(usb_hid_report_parser_t *parser)
562{
563 if(parser == NULL){
564 return;
565 }
566
567 usb_hid_free_report_list(&parser->input);
568 usb_hid_free_report_list(&parser->output);
569 usb_hid_free_report_list(&parser->feature);
570
571 return;
572}
573
574/** Parse and act upon a HID report.
575 *
576 * @see usb_hid_parse_report_descriptor
577 *
578 * @param parser Opaque HID report parser structure.
579 * @param data Data for the report.
580 * @param callbacks Callbacks for report actions.
581 * @param arg Custom argument (passed through to the callbacks).
582 * @return Error code.
583 */
584int usb_hid_parse_report(const usb_hid_report_parser_t *parser,
585 const uint8_t *data, size_t size,
586 const usb_hid_report_in_callbacks_t *callbacks, void *arg)
587{
588 /*
589 *
590 * only key codes (usage page 0x07) will be processed
591 * other usages will be ignored
592 */
593 link_t *list_item;
594 usb_hid_report_item_t *item;
595 uint8_t *keys;
596 size_t key_count=0;
597 size_t i=0;
598 size_t j=0;
599
600 // get the size of result keycodes array
601 list_item = parser->input.next;
602 while(list_item != &(parser->input)) {
603
604 item = list_get_instance(list_item, usb_hid_report_item_t, link);
605 if(item->usage_page == BAD_HACK_USAGE_PAGE) {
606 key_count += item->count;
607 }
608
609 list_item = list_item->next;
610 }
611
612
613 if(!(keys = malloc(sizeof(uint8_t) * key_count))){
614 return ENOMEM;
615 }
616
617 // read data
618 list_item = parser->input.next;
619 while(list_item != &(parser->input)) {
620
621 item = list_get_instance(list_item, usb_hid_report_item_t, link);
622 if(item->usage_page == BAD_HACK_USAGE_PAGE) {
623 for(j=0; j<(size_t)(item->count); j++) {
624 keys[i++] = usb_hid_translate_data(item, data,j);
625 }
626 }
627 list_item = list_item->next;
628 }
629
630 callbacks->keyboard(keys, key_count, 0, arg);
631
632 free(keys);
633 return EOK;
634
635}
636
637
638int usb_hid_translate_data(usb_hid_report_item_t *item, const uint8_t *data, size_t j)
639{
640 int resolution;
641 int offset;
642 int part_size;
643
644 int32_t value;
645 int32_t mask;
646 const uint8_t *foo;
647
648 // now only common numbers llowed
649 if(item->size > 32) {
650 return 0;
651 }
652
653 if((item->physical_minimum == 0) && (item->physical_maximum ==0)) {
654 item->physical_minimum = item->logical_minimum;
655 item->physical_maximum = item->logical_maximum;
656 }
657
658 resolution = (item->logical_maximum - item->logical_minimum) / ((item->physical_maximum - item->physical_minimum) * (usb_pow(10,(item->unit_exponent))));
659 offset = item->offset + (j * item->size);
660
661 // FIXME
662 if((offset/8) != ((offset+item->size)/8)) {
663 usb_log_debug2("offset %d\n", offset);
664
665 part_size = ((offset+item->size)%8);
666 usb_log_debug2("part size %d\n",part_size);
667
668 // the higher one
669 foo = data+(offset/8);
670 mask = ((1 << (item->size-part_size))-1);
671 value = (*foo & mask) << part_size;
672
673 usb_log_debug2("hfoo %x\n", *foo);
674 usb_log_debug2("hmaska %x\n", mask);
675 usb_log_debug2("hval %d\n", value);
676
677 // the lower one
678 foo = data+((offset+item->size)/8);
679 mask = ((1 << part_size)-1) << (8-part_size);
680 value += ((*foo & mask) >> (8-part_size));
681
682 usb_log_debug2("lfoo %x\n", *foo);
683 usb_log_debug2("lmaska %x\n", mask);
684 usb_log_debug2("lval %d\n", ((*foo & mask) >> (8-(item->size-part_size))));
685 usb_log_debug2("val %d\n", value);
686
687
688 }
689 else {
690 foo = data+(offset/8);
691 mask = ((1 << item->size)-1) << (8-((offset%8)+item->size));
692 value = (*foo & mask) >> (8-((offset%8)+item->size));
693
694 usb_log_debug2("offset %d\n", offset);
695 usb_log_debug2("foo %x\n", *foo);
696 usb_log_debug2("maska %x\n", mask);
697 usb_log_debug2("val %d\n", value);
698 }
699
700 usb_log_debug2("---\n\n");
701
702 return (int)(((value - item->physical_minimum) / resolution) + item->logical_minimum);
703
704}
705/**
706 * @}
707 */
Note: See TracBrowser for help on using the repository browser.