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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 32bfb96 was 32bfb96, checked in by Lubos Slovak <lubos.slovak@…>, 14 years ago

Replaced all printfs with usb_log_debug()

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