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

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

report descriptor parsing - local items reset fixed

  • Property mode set to 100644
File size: 34.1 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 * HID report descriptor and report data 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/** */
43#define USB_HID_NEW_REPORT_ITEM 1
44
45/** */
46#define USB_HID_NO_ACTION 2
47
48/** */
49#define USB_HID_UNKNOWN_TAG -99
50
51/*
52 * Private descriptor parser functions
53 */
54int usb_hid_report_parse_tag(uint8_t tag, uint8_t class, const uint8_t *data, size_t item_size,
55 usb_hid_report_item_t *report_item, usb_hid_report_path_t *usage_path);
56int usb_hid_report_parse_main_tag(uint8_t tag, const uint8_t *data, size_t item_size,
57 usb_hid_report_item_t *report_item, usb_hid_report_path_t *usage_path);
58int usb_hid_report_parse_global_tag(uint8_t tag, const uint8_t *data, size_t item_size,
59 usb_hid_report_item_t *report_item, usb_hid_report_path_t *usage_path);
60int usb_hid_report_parse_local_tag(uint8_t tag, const uint8_t *data, size_t item_size,
61 usb_hid_report_item_t *report_item, usb_hid_report_path_t *usage_path);
62
63void usb_hid_descriptor_print_list(link_t *head);
64int usb_hid_report_reset_local_items();
65void usb_hid_free_report_list(link_t *head);
66
67/*
68 * Data translation private functions
69 */
70int32_t usb_hid_report_tag_data_int32(const uint8_t *data, size_t size);
71inline size_t usb_hid_count_item_offset(usb_hid_report_item_t * report_item, size_t offset);
72int usb_hid_translate_data(usb_hid_report_item_t *item, const uint8_t *data, size_t j);
73int32_t usb_hid_translate_data_reverse(usb_hid_report_item_t *item, int32_t value);
74int usb_pow(int a, int b);
75
76// TODO: tohle ma bejt asi jinde
77int usb_pow(int a, int b)
78{
79 switch(b) {
80 case 0:
81 return 1;
82 break;
83 case 1:
84 return a;
85 break;
86 default:
87 return a * usb_pow(a, b-1);
88 break;
89 }
90}
91
92/**
93 * Initialize the report descriptor parser structure
94 *
95 * @param parser Report descriptor parser structure
96 * @return Error code
97 */
98int usb_hid_parser_init(usb_hid_report_parser_t *parser)
99{
100 if(parser == NULL) {
101 return EINVAL;
102 }
103
104 list_initialize(&(parser->input));
105 list_initialize(&(parser->output));
106 list_initialize(&(parser->feature));
107
108 parser->use_report_id = 0;
109 return EOK;
110}
111
112
113/** Parse HID report descriptor.
114 *
115 * @param parser Opaque HID report parser structure.
116 * @param data Data describing the report.
117 * @return Error code.
118 */
119int usb_hid_parse_report_descriptor(usb_hid_report_parser_t *parser,
120 const uint8_t *data, size_t size)
121{
122 size_t i=0;
123 uint8_t tag=0;
124 uint8_t item_size=0;
125 int class=0;
126 int ret;
127 usb_hid_report_item_t *report_item=0;
128 usb_hid_report_item_t *new_report_item;
129 usb_hid_report_path_t *usage_path;
130 usb_hid_report_path_t *tmp_usage_path;
131
132 size_t offset_input=0;
133 size_t offset_output=0;
134 size_t offset_feature=0;
135
136
137 /* parser structure initialization*/
138 if(usb_hid_parser_init(parser) != EOK) {
139 return EINVAL;
140 }
141
142
143 /*report item initialization*/
144 if(!(report_item=malloc(sizeof(usb_hid_report_item_t)))){
145 return ENOMEM;
146 }
147 memset(report_item, 0, sizeof(usb_hid_report_item_t));
148 list_initialize(&(report_item->link));
149
150 /* usage path context initialization */
151 if(!(usage_path=usb_hid_report_path())){
152 return ENOMEM;
153 }
154
155 while(i<size){
156 if(!USB_HID_ITEM_IS_LONG(data[i])){
157
158 if((i+USB_HID_ITEM_SIZE(data[i]))>= size){
159 return EINVAL; // TODO ERROR CODE
160 }
161
162 tag = USB_HID_ITEM_TAG(data[i]);
163 item_size = USB_HID_ITEM_SIZE(data[i]);
164 class = USB_HID_ITEM_TAG_CLASS(data[i]);
165
166 usb_log_debug2(
167 "i(%u) data(%X) value(%X): TAG %u, class %u, size %u - ", i,
168 data[i], usb_hid_report_tag_data_int32(data+i+1,item_size),
169 tag, class, item_size);
170
171 ret = usb_hid_report_parse_tag(tag,class,data+i+1,
172 item_size,report_item, usage_path);
173 usb_log_debug2("ret: %u\n", ret);
174 switch(ret){
175 case USB_HID_NEW_REPORT_ITEM:
176 // store report item to report and create the new one
177 usb_log_debug("\nNEW REPORT ITEM: %X",ret);
178
179 // store current usage path
180 report_item->usage_path = usage_path;
181
182 // clone path to the new one
183 tmp_usage_path = usb_hid_report_path_clone(usage_path);
184
185 // swap
186 usage_path = tmp_usage_path;
187 tmp_usage_path = NULL;
188
189 usb_hid_report_path_set_report_id(report_item->usage_path, report_item->id);
190 if(report_item->id != 0){
191 parser->use_report_id = 1;
192 }
193
194 switch(tag) {
195 case USB_HID_REPORT_TAG_INPUT:
196 report_item->offset = offset_input;
197 offset_input += report_item->count * report_item->size;
198 usb_log_debug(" - INPUT\n");
199 list_append(&(report_item->link), &(parser->input));
200 break;
201 case USB_HID_REPORT_TAG_OUTPUT:
202 report_item->offset = offset_output;
203 offset_output += report_item->count * report_item->size;
204 usb_log_debug(" - OUTPUT\n");
205 list_append(&(report_item->link), &(parser->output));
206
207 break;
208 case USB_HID_REPORT_TAG_FEATURE:
209 report_item->offset = offset_feature;
210 offset_feature += report_item->count * report_item->size;
211 usb_log_debug(" - FEATURE\n");
212 list_append(&(report_item->link), &(parser->feature));
213 break;
214 default:
215 usb_log_debug("\tjump over - tag %X\n", tag);
216 break;
217 }
218
219 /* clone current state table to the new item */
220 if(!(new_report_item = malloc(sizeof(usb_hid_report_item_t)))) {
221 return ENOMEM;
222 }
223 memcpy(new_report_item,report_item, sizeof(usb_hid_report_item_t));
224
225 /* reset local items */
226 new_report_item->usage_minimum = 0;
227 new_report_item->usage_maximum = 0;
228 new_report_item->usage = 0;
229 new_report_item->designator_index = 0;
230 new_report_item->designator_minimum = 0;
231 new_report_item->designator_maximum = 0;
232 new_report_item->string_index = 0;
233 new_report_item->string_minimum = 0;
234 new_report_item->string_maximum = 0;
235
236 link_initialize(&(new_report_item->link));
237 report_item = new_report_item;
238
239 break;
240 case USB_HID_REPORT_TAG_PUSH:
241 // push current state to stack
242 // not yet implemented
243 break;
244 case USB_HID_REPORT_TAG_POP:
245 // restore current state from stack
246 // not yet implemented
247 break;
248
249 default:
250 // nothing special to do
251 break;
252 }
253
254 /* jump over the processed block */
255 i += 1 + USB_HID_ITEM_SIZE(data[i]);
256 }
257 else{
258 // TBD
259 i += 3 + USB_HID_ITEM_SIZE(data[i+1]);
260 }
261
262
263 }
264
265 return EOK;
266}
267
268
269/**
270 * Parse input report.
271 *
272 * @param data Data for report
273 * @param size Size of report
274 * @param callbacks Callbacks for report actions
275 * @param arg Custom arguments
276 *
277 * @return Error code
278 */
279int usb_hid_boot_keyboard_input_report(const uint8_t *data, size_t size,
280 const usb_hid_report_in_callbacks_t *callbacks, void *arg)
281{
282 int i;
283 usb_hid_report_item_t item;
284
285 /* fill item due to the boot protocol report descriptor */
286 // modifier keys are in the first byte
287 uint8_t modifiers = data[0];
288
289 item.offset = 2; /* second byte is reserved */
290 item.size = 8;
291 item.count = 6;
292 item.usage_minimum = 0;
293 item.usage_maximum = 255;
294 item.logical_minimum = 0;
295 item.logical_maximum = 255;
296
297 if (size != 8) {
298 return -1; //ERANGE;
299 }
300
301 uint8_t keys[6];
302 for (i = 0; i < item.count; i++) {
303 keys[i] = data[i + item.offset];
304 }
305
306 callbacks->keyboard(keys, 6, modifiers, arg);
307 return EOK;
308}
309
310/**
311 * Makes output report for keyboard boot protocol
312 *
313 * @param leds
314 * @param output Output report data buffer
315 * @param size Size of the output buffer
316 * @return Error code
317 */
318int usb_hid_boot_keyboard_output_report(uint8_t leds, uint8_t *data, size_t size)
319{
320 if(size != 1){
321 return -1;
322 }
323
324 /* used only first five bits, others are only padding*/
325 *data = leds;
326 return EOK;
327}
328
329/**
330 * Parse one tag of the report descriptor
331 *
332 * @param Tag to parse
333 * @param Report descriptor buffer
334 * @param Size of data belongs to this tag
335 * @param Current report item structe
336 * @return Code of action to be done next
337 */
338int usb_hid_report_parse_tag(uint8_t tag, uint8_t class, const uint8_t *data, size_t item_size,
339 usb_hid_report_item_t *report_item, usb_hid_report_path_t *usage_path)
340{
341 int ret;
342
343 switch(class){
344 case USB_HID_TAG_CLASS_MAIN:
345
346 if((ret=usb_hid_report_parse_main_tag(tag,data,item_size,report_item, usage_path)) == EOK) {
347 return USB_HID_NEW_REPORT_ITEM;
348 }
349 else {
350 /*TODO process the error */
351 return ret;
352 }
353 break;
354
355 case USB_HID_TAG_CLASS_GLOBAL:
356 return usb_hid_report_parse_global_tag(tag,data,item_size,report_item, usage_path);
357 break;
358
359 case USB_HID_TAG_CLASS_LOCAL:
360 return usb_hid_report_parse_local_tag(tag,data,item_size,report_item, usage_path);
361 break;
362 default:
363 return USB_HID_NO_ACTION;
364 }
365}
366
367/**
368 * Parse main tags of report descriptor
369 *
370 * @param Tag identifier
371 * @param Data buffer
372 * @param Length of data buffer
373 * @param Current state table
374 * @return Error code
375 */
376
377int usb_hid_report_parse_main_tag(uint8_t tag, const uint8_t *data, size_t item_size,
378 usb_hid_report_item_t *report_item, usb_hid_report_path_t *usage_path)
379{
380 switch(tag)
381 {
382 case USB_HID_REPORT_TAG_INPUT:
383 case USB_HID_REPORT_TAG_OUTPUT:
384 case USB_HID_REPORT_TAG_FEATURE:
385 report_item->item_flags = *data;
386 return EOK;
387 break;
388
389 case USB_HID_REPORT_TAG_COLLECTION:
390 usb_hid_report_path_append_item(usage_path, 0, 0);
391
392 return USB_HID_NO_ACTION;
393 break;
394
395 case USB_HID_REPORT_TAG_END_COLLECTION:
396 // TODO
397 // znici posledni uroven ve vsech usage paths
398 // otazka jestli nema nicit dve, respektive novou posledni vynulovat?
399 usb_hid_report_remove_last_item(usage_path);
400 return USB_HID_NO_ACTION;
401 break;
402 default:
403 return USB_HID_NO_ACTION;
404 }
405
406 return EOK;
407}
408
409/**
410 * Parse global tags of report descriptor
411 *
412 * @param Tag identifier
413 * @param Data buffer
414 * @param Length of data buffer
415 * @param Current state table
416 * @return Error code
417 */
418int usb_hid_report_parse_global_tag(uint8_t tag, const uint8_t *data, size_t item_size,
419 usb_hid_report_item_t *report_item, usb_hid_report_path_t *usage_path)
420{
421 // TODO take care about the bit length of data
422 switch(tag)
423 {
424 case USB_HID_REPORT_TAG_USAGE_PAGE:
425 // zmeni to jenom v poslednim poli aktualni usage path
426 usb_hid_report_set_last_item(usage_path, USB_HID_TAG_CLASS_GLOBAL,
427 usb_hid_report_tag_data_int32(data,item_size));
428 break;
429 case USB_HID_REPORT_TAG_LOGICAL_MINIMUM:
430 report_item->logical_minimum = usb_hid_report_tag_data_int32(data,item_size);
431 break;
432 case USB_HID_REPORT_TAG_LOGICAL_MAXIMUM:
433 report_item->logical_maximum = usb_hid_report_tag_data_int32(data,item_size);
434 break;
435 case USB_HID_REPORT_TAG_PHYSICAL_MINIMUM:
436 report_item->physical_minimum = usb_hid_report_tag_data_int32(data,item_size);
437 break;
438 case USB_HID_REPORT_TAG_PHYSICAL_MAXIMUM:
439 report_item->physical_maximum = usb_hid_report_tag_data_int32(data,item_size);
440 break;
441 case USB_HID_REPORT_TAG_UNIT_EXPONENT:
442 report_item->unit_exponent = usb_hid_report_tag_data_int32(data,item_size);
443 break;
444 case USB_HID_REPORT_TAG_UNIT:
445 report_item->unit = usb_hid_report_tag_data_int32(data,item_size);
446 break;
447 case USB_HID_REPORT_TAG_REPORT_SIZE:
448 report_item->size = usb_hid_report_tag_data_int32(data,item_size);
449 break;
450 case USB_HID_REPORT_TAG_REPORT_COUNT:
451 report_item->count = usb_hid_report_tag_data_int32(data,item_size);
452 break;
453 case USB_HID_REPORT_TAG_REPORT_ID:
454 report_item->id = usb_hid_report_tag_data_int32(data,item_size);
455 break;
456 case USB_HID_REPORT_TAG_PUSH:
457 case USB_HID_REPORT_TAG_POP:
458 return tag;
459 break;
460
461 default:
462 return USB_HID_NO_ACTION;
463 }
464
465 return EOK;
466}
467
468/**
469 * Parse local tags of report descriptor
470 *
471 * @param Tag identifier
472 * @param Data buffer
473 * @param Length of data buffer
474 * @param Current state table
475 * @return Error code
476 */
477int usb_hid_report_parse_local_tag(uint8_t tag, const uint8_t *data, size_t item_size,
478 usb_hid_report_item_t *report_item, usb_hid_report_path_t *usage_path)
479{
480 switch(tag)
481 {
482 case USB_HID_REPORT_TAG_USAGE:
483 usb_hid_report_set_last_item(usage_path, USB_HID_TAG_CLASS_LOCAL,
484 usb_hid_report_tag_data_int32(data,item_size));
485 break;
486 case USB_HID_REPORT_TAG_USAGE_MINIMUM:
487 report_item->usage_minimum = usb_hid_report_tag_data_int32(data,item_size);
488 break;
489 case USB_HID_REPORT_TAG_USAGE_MAXIMUM:
490 report_item->usage_maximum = usb_hid_report_tag_data_int32(data,item_size);
491 break;
492 case USB_HID_REPORT_TAG_DESIGNATOR_INDEX:
493 report_item->designator_index = usb_hid_report_tag_data_int32(data,item_size);
494 break;
495 case USB_HID_REPORT_TAG_DESIGNATOR_MINIMUM:
496 report_item->designator_minimum = usb_hid_report_tag_data_int32(data,item_size);
497 break;
498 case USB_HID_REPORT_TAG_DESIGNATOR_MAXIMUM:
499 report_item->designator_maximum = usb_hid_report_tag_data_int32(data,item_size);
500 break;
501 case USB_HID_REPORT_TAG_STRING_INDEX:
502 report_item->string_index = usb_hid_report_tag_data_int32(data,item_size);
503 break;
504 case USB_HID_REPORT_TAG_STRING_MINIMUM:
505 report_item->string_minimum = usb_hid_report_tag_data_int32(data,item_size);
506 break;
507 case USB_HID_REPORT_TAG_STRING_MAXIMUM:
508 report_item->string_maximum = usb_hid_report_tag_data_int32(data,item_size);
509 break;
510 case USB_HID_REPORT_TAG_DELIMITER:
511 report_item->delimiter = usb_hid_report_tag_data_int32(data,item_size);
512 break;
513
514 default:
515 return USB_HID_NO_ACTION;
516 }
517
518 return EOK;
519}
520
521/**
522 * Converts raw data to int32 (thats the maximum length of short item data)
523 *
524 * @param Data buffer
525 * @param Size of buffer
526 * @return Converted int32 number
527 */
528int32_t usb_hid_report_tag_data_int32(const uint8_t *data, size_t size)
529{
530 unsigned int i;
531 int32_t result;
532
533 result = 0;
534 for(i=0; i<size; i++) {
535 result = (result | (data[i]) << (i*8));
536 }
537
538 return result;
539}
540
541
542
543/**
544 * Prints content of given list of report items.
545 *
546 * @param List of report items (usb_hid_report_item_t)
547 * @return void
548 */
549void usb_hid_descriptor_print_list(link_t *head)
550{
551 usb_hid_report_item_t *report_item;
552 usb_hid_report_usage_path_t *path_item;
553 link_t *path;
554 link_t *item;
555
556 if(head == NULL || list_empty(head)) {
557 usb_log_debug("\tempty\n");
558 return;
559 }
560
561 for(item = head->next; item != head; item = item->next) {
562
563 report_item = list_get_instance(item, usb_hid_report_item_t, link);
564
565 usb_log_debug("\tOFFSET: %X\n", report_item->offset);
566 usb_log_debug("\tCOUNT: %X\n", report_item->count);
567 usb_log_debug("\tSIZE: %X\n", report_item->size);
568 usb_log_debug("\tCONSTANT/VAR: %X\n", USB_HID_ITEM_FLAG_CONSTANT(report_item->item_flags));
569 usb_log_debug("\tVARIABLE/ARRAY: %X\n", USB_HID_ITEM_FLAG_VARIABLE(report_item->item_flags));
570 usb_log_debug("\tUSAGE PATH:\n");
571
572 path = report_item->usage_path->link.next;
573 while(path != &report_item->usage_path->link) {
574 path_item = list_get_instance(path, usb_hid_report_usage_path_t, link);
575 usb_log_debug("\t\tUSAGE PAGE: %X, USAGE: %X\n", path_item->usage_page, path_item->usage);
576 path = path->next;
577 }
578
579 usb_log_debug("\tLOGMIN: %X\n", report_item->logical_minimum);
580 usb_log_debug("\tLOGMAX: %X\n", report_item->logical_maximum);
581 usb_log_debug("\tPHYMIN: %X\n", report_item->physical_minimum);
582 usb_log_debug("\tPHYMAX: %X\n", report_item->physical_maximum);
583 usb_log_debug("\tUSAGEMIN: %X\n", report_item->usage_minimum);
584 usb_log_debug("\tUSAGEMAX: %X\n", report_item->usage_maximum);
585
586 usb_log_debug("\n");
587
588 }
589
590
591}
592/**
593 * Prints content of given report descriptor in human readable format.
594 *
595 * @param parser Parsed descriptor to print
596 * @return void
597 */
598void usb_hid_descriptor_print(usb_hid_report_parser_t *parser)
599{
600 if(parser == NULL) {
601 return;
602 }
603
604 usb_log_debug("INPUT:\n");
605 usb_hid_descriptor_print_list(&parser->input);
606
607 usb_log_debug("OUTPUT: \n");
608 usb_hid_descriptor_print_list(&parser->output);
609
610 usb_log_debug("FEATURE:\n");
611 usb_hid_descriptor_print_list(&parser->feature);
612
613}
614
615/**
616 * Releases whole linked list of report items
617 *
618 * @param head Head of list of report descriptor items (usb_hid_report_item_t)
619 * @return void
620 */
621void usb_hid_free_report_list(link_t *head)
622{
623 return;
624
625 usb_hid_report_item_t *report_item;
626 link_t *next;
627
628 if(head == NULL || list_empty(head)) {
629 return;
630 }
631
632 next = head->next;
633 while(next != head) {
634
635 report_item = list_get_instance(next, usb_hid_report_item_t, link);
636
637 while(!list_empty(&report_item->usage_path->link)) {
638 usb_hid_report_remove_last_item(report_item->usage_path);
639 }
640
641
642 next = next->next;
643
644 free(report_item);
645 }
646
647 return;
648
649}
650
651/** Frees the HID report descriptor parser structure
652 *
653 * @param parser Opaque HID report parser structure
654 * @return void
655 */
656void usb_hid_free_report_parser(usb_hid_report_parser_t *parser)
657{
658 if(parser == NULL){
659 return;
660 }
661
662 parser->use_report_id = 0;
663
664 usb_hid_free_report_list(&parser->input);
665 usb_hid_free_report_list(&parser->output);
666 usb_hid_free_report_list(&parser->feature);
667
668 return;
669}
670
671/** Parse and act upon a HID report.
672 *
673 * @see usb_hid_parse_report_descriptor
674 *
675 * @param parser Opaque HID report parser structure.
676 * @param data Data for the report.
677 * @param callbacks Callbacks for report actions.
678 * @param arg Custom argument (passed through to the callbacks).
679 * @return Error code.
680 */
681int usb_hid_parse_report(const usb_hid_report_parser_t *parser,
682 const uint8_t *data, size_t size,
683 usb_hid_report_path_t *path, int flags,
684 const usb_hid_report_in_callbacks_t *callbacks, void *arg)
685{
686 link_t *list_item;
687 usb_hid_report_item_t *item;
688 uint8_t *keys;
689 uint8_t item_value;
690 size_t key_count=0;
691 size_t i=0;
692 size_t j=0;
693 uint8_t report_id = 0;
694
695 if(parser == NULL) {
696 return EINVAL;
697 }
698
699 /* get the size of result array */
700 key_count = usb_hid_report_input_length(parser, path, flags);
701
702 if(!(keys = malloc(sizeof(uint8_t) * key_count))){
703 return ENOMEM;
704 }
705
706 if(parser->use_report_id != 0) {
707 report_id = data[0];
708 usb_hid_report_path_set_report_id(path, report_id);
709 }
710
711 /* read data */
712 list_item = parser->input.next;
713 while(list_item != &(parser->input)) {
714
715 item = list_get_instance(list_item, usb_hid_report_item_t, link);
716
717 if(!USB_HID_ITEM_FLAG_CONSTANT(item->item_flags) &&
718 (usb_hid_report_compare_usage_path(item->usage_path, path, flags) == EOK)) {
719 for(j=0; j<(size_t)(item->count); j++) {
720 if((USB_HID_ITEM_FLAG_VARIABLE(item->item_flags) == 0) ||
721 ((item->usage_minimum == 0) && (item->usage_maximum == 0))) {
722 // variable item
723 keys[i++] = usb_hid_translate_data(item, data,j);
724 }
725 else {
726 // bitmapa
727 if((item_value = usb_hid_translate_data(item, data, j)) != 0) {
728 keys[i++] = (item->count - 1 - j) + item->usage_minimum;
729 }
730 else {
731 keys[i++] = 0;
732 }
733 }
734 }
735 }
736 list_item = list_item->next;
737 }
738
739 callbacks->keyboard(keys, key_count, report_id, arg);
740
741 free(keys);
742 return EOK;
743
744}
745
746/**
747 * Translate data from the report as specified in report descriptor
748 *
749 * @param item Report descriptor item with definition of translation
750 * @param data Data to translate
751 * @param j Index of processed field in report descriptor item
752 * @return Translated data
753 */
754int usb_hid_translate_data(usb_hid_report_item_t *item, const uint8_t *data, size_t j)
755{
756 int resolution;
757 int offset;
758 int part_size;
759
760 int32_t value;
761 int32_t mask;
762 const uint8_t *foo;
763
764 // now only common numbers llowed
765 if(item->size > 32) {
766 return 0;
767 }
768
769 if((item->physical_minimum == 0) && (item->physical_maximum == 0)) {
770 item->physical_minimum = item->logical_minimum;
771 item->physical_maximum = item->logical_maximum;
772 }
773
774 if(item->physical_maximum == item->physical_minimum){
775 resolution = 1;
776 }
777 else {
778 resolution = (item->logical_maximum - item->logical_minimum) /
779 ((item->physical_maximum - item->physical_minimum) *
780 (usb_pow(10,(item->unit_exponent))));
781 }
782
783 offset = item->offset + (j * item->size);
784 if(item->id != 0) {
785 offset += 8;
786 usb_log_debug("MOVED OFFSET BY 1Byte, REPORT_ID(%d)\n", item->id);
787 }
788
789 // FIXME
790 if((offset/8) != ((offset+item->size)/8)) {
791 usb_log_debug2("offset %d\n", offset);
792
793 part_size = ((offset+item->size)%8);
794 usb_log_debug2("part size %d\n",part_size);
795
796 // the higher one
797 foo = data+(offset/8);
798 mask = ((1 << (item->size-part_size))-1);
799 value = (*foo & mask) << part_size;
800
801 usb_log_debug2("hfoo %x\n", *foo);
802 usb_log_debug2("hmaska %x\n", mask);
803 usb_log_debug2("hval %d\n", value);
804
805 // the lower one
806 foo = data+((offset+item->size)/8);
807 mask = ((1 << part_size)-1) << (8-part_size);
808 value += ((*foo & mask) >> (8-part_size));
809
810 usb_log_debug2("lfoo %x\n", *foo);
811 usb_log_debug2("lmaska %x\n", mask);
812 usb_log_debug2("lval %d\n", ((*foo & mask) >> (8-(item->size-part_size))));
813 usb_log_debug2("val %d\n", value);
814
815
816 }
817 else {
818 foo = data+(offset/8);
819 mask = ((1 << item->size)-1) << (8-((offset%8)+item->size));
820 value = (*foo & mask) >> (8-((offset%8)+item->size));
821
822 usb_log_debug2("offset %d\n", offset);
823
824 usb_log_debug2("foo %x\n", *foo);
825 usb_log_debug2("maska %x\n", mask);
826 usb_log_debug2("val %d\n", value);
827 }
828
829 usb_log_debug2("---\n\n");
830
831 return (int)(((value - item->logical_minimum) / resolution) + item->physical_minimum);
832
833}
834
835/**
836 *
837 *
838 * @param parser
839 * @param path
840 * @param flags
841 * @return
842 */
843size_t usb_hid_report_input_length(const usb_hid_report_parser_t *parser,
844 usb_hid_report_path_t *path, int flags)
845{
846 size_t ret = 0;
847 link_t *item;
848 usb_hid_report_item_t *report_item;
849
850 if(parser == NULL) {
851 return 0;
852 }
853
854 item = parser->input.next;
855 while(&parser->input != item) {
856 report_item = list_get_instance(item, usb_hid_report_item_t, link);
857 if(!USB_HID_ITEM_FLAG_CONSTANT(report_item->item_flags) &&
858 (usb_hid_report_compare_usage_path(report_item->usage_path, path, flags) == EOK)) {
859 ret += report_item->count;
860 }
861
862 item = item->next;
863 }
864
865 return ret;
866}
867
868
869/**
870 *
871 * @param usage_path
872 * @param usage_page
873 * @param usage
874 * @return
875 */
876int usb_hid_report_path_append_item(usb_hid_report_path_t *usage_path,
877 int32_t usage_page, int32_t usage)
878{
879 usb_hid_report_usage_path_t *item;
880
881 if(!(item=malloc(sizeof(usb_hid_report_usage_path_t)))) {
882 return ENOMEM;
883 }
884 list_initialize(&item->link);
885
886 item->usage = usage;
887 item->usage_page = usage_page;
888
889 list_append (&usage_path->link, &item->link);
890 usage_path->depth++;
891 return EOK;
892}
893
894/**
895 *
896 * @param usage_path
897 * @return
898 */
899void usb_hid_report_remove_last_item(usb_hid_report_path_t *usage_path)
900{
901 usb_hid_report_usage_path_t *item;
902
903 if(!list_empty(&usage_path->link)){
904 item = list_get_instance(usage_path->link.prev, usb_hid_report_usage_path_t, link);
905 list_remove(usage_path->link.prev);
906 usage_path->depth--;
907 free(item);
908 }
909}
910
911/**
912 *
913 * @param usage_path
914 * @return
915 */
916void usb_hid_report_null_last_item(usb_hid_report_path_t *usage_path)
917{
918 usb_hid_report_usage_path_t *item;
919
920 if(!list_empty(&usage_path->link)){
921 item = list_get_instance(usage_path->link.prev, usb_hid_report_usage_path_t, link);
922 memset(item, 0, sizeof(usb_hid_report_usage_path_t));
923 }
924}
925
926/**
927 *
928 * @param usage_path
929 * @param tag
930 * @param data
931 * @return
932 */
933void usb_hid_report_set_last_item(usb_hid_report_path_t *usage_path, int32_t tag, int32_t data)
934{
935 usb_hid_report_usage_path_t *item;
936
937 if(!list_empty(&usage_path->link)){
938 item = list_get_instance(usage_path->link.prev, usb_hid_report_usage_path_t, link);
939
940 switch(tag) {
941 case USB_HID_TAG_CLASS_GLOBAL:
942 item->usage_page = data;
943 break;
944 case USB_HID_TAG_CLASS_LOCAL:
945 item->usage = data;
946 break;
947 }
948 }
949
950}
951
952/**
953 *
954 *
955 * @param report_path
956 * @param path
957 * @param flags
958 * @return
959 */
960int usb_hid_report_compare_usage_path(usb_hid_report_path_t *report_path,
961 usb_hid_report_path_t *path,
962 int flags)
963{
964 usb_hid_report_usage_path_t *report_item;
965 usb_hid_report_usage_path_t *path_item;
966
967 link_t *report_link;
968 link_t *path_link;
969
970 int only_page;
971
972 if(report_path->report_id != path->report_id) {
973 return 1;
974 }
975
976 if(path->depth == 0){
977 return EOK;
978 }
979
980
981 if((only_page = flags & USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY) != 0){
982 flags -= USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY;
983 }
984
985 switch(flags){
986 /* path must be completly identical */
987 case USB_HID_PATH_COMPARE_STRICT:
988 if(report_path->depth != path->depth){
989 return 1;
990 }
991
992 report_link = report_path->link.next;
993 path_link = path->link.next;
994
995 while((report_link != &report_path->link) && (path_link != &path->link)) {
996 report_item = list_get_instance(report_link, usb_hid_report_usage_path_t, link);
997 path_item = list_get_instance(path_link, usb_hid_report_usage_path_t, link);
998
999 if((report_item->usage_page != path_item->usage_page) ||
1000 ((only_page == 0) && (report_item->usage != path_item->usage))) {
1001 return 1;
1002 } else {
1003 report_link = report_link->next;
1004 path_link = path_link->next;
1005 }
1006
1007 }
1008
1009 if((report_link == &report_path->link) && (path_link == &path->link)) {
1010 return EOK;
1011 }
1012 else {
1013 return 1;
1014 }
1015 break;
1016
1017 /* compare with only the end of path*/
1018 case USB_HID_PATH_COMPARE_END:
1019 report_link = report_path->link.prev;
1020 path_link = path->link.prev;
1021
1022 if(list_empty(&path->link)){
1023 return EOK;
1024 }
1025
1026 while((report_link != &report_path->link) && (path_link != &path->link)) {
1027 report_item = list_get_instance(report_link, usb_hid_report_usage_path_t, link);
1028 path_item = list_get_instance(path_link, usb_hid_report_usage_path_t, link);
1029
1030 if((report_item->usage_page != path_item->usage_page) ||
1031 ((only_page == 0) && (report_item->usage != path_item->usage))) {
1032 return 1;
1033 } else {
1034 report_link = report_link->prev;
1035 path_link = path_link->prev;
1036 }
1037
1038 }
1039
1040 if(path_link == &path->link) {
1041 return EOK;
1042 }
1043 else {
1044 return 1;
1045 }
1046
1047 break;
1048
1049 default:
1050 return EINVAL;
1051 }
1052
1053
1054
1055
1056}
1057
1058/**
1059 *
1060 * @return
1061 */
1062usb_hid_report_path_t *usb_hid_report_path(void)
1063{
1064 usb_hid_report_path_t *path;
1065 path = malloc(sizeof(usb_hid_report_path_t));
1066 if(!path){
1067 return NULL;
1068 }
1069 else {
1070 path->depth = 0;
1071 path->report_id = 0;
1072 list_initialize(&path->link);
1073 return path;
1074 }
1075}
1076
1077/**
1078 *
1079 * @param path
1080 * @return void
1081 */
1082void usb_hid_report_path_free(usb_hid_report_path_t *path)
1083{
1084 while(!list_empty(&path->link)){
1085 usb_hid_report_remove_last_item(path);
1086 }
1087}
1088
1089
1090/**
1091 * Clone content of given usage path to the new one
1092 *
1093 * @param usage_path
1094 * @return
1095 */
1096usb_hid_report_path_t *usb_hid_report_path_clone(usb_hid_report_path_t *usage_path)
1097{
1098 usb_hid_report_usage_path_t *path_item;
1099 link_t *path_link;
1100 usb_hid_report_path_t *new_usage_path = usb_hid_report_path ();
1101
1102 if(new_usage_path == NULL){
1103 return NULL;
1104 }
1105
1106 if(list_empty(&usage_path->link)){
1107 return new_usage_path;
1108 }
1109
1110 path_link = usage_path->link.next;
1111 while(path_link != &usage_path->link) {
1112 path_item = list_get_instance(path_link, usb_hid_report_usage_path_t, link);
1113 usb_hid_report_path_append_item (new_usage_path, path_item->usage_page, path_item->usage);
1114
1115 path_link = path_link->next;
1116 }
1117
1118 return new_usage_path;
1119}
1120
1121
1122/*** OUTPUT API **/
1123
1124/** Allocates output report buffer
1125 *
1126 * @param parser
1127 * @param size
1128 * @return
1129 */
1130uint8_t *usb_hid_report_output(usb_hid_report_parser_t *parser, size_t *size)
1131{
1132 if(parser == NULL) {
1133 *size = 0;
1134 return NULL;
1135 }
1136
1137 // read the last output report item
1138 usb_hid_report_item_t *last;
1139 link_t *link;
1140
1141 link = parser->output.prev;
1142 if(link != &parser->output) {
1143 last = list_get_instance(link, usb_hid_report_item_t, link);
1144 *size = (last->offset + (last->size * last->count)) / 8;
1145
1146 uint8_t *buffer = malloc(sizeof(uint8_t) * (*size));
1147 memset(buffer, 0, sizeof(uint8_t) * (*size));
1148 usb_log_debug("output buffer: %s\n", usb_debug_str_buffer(buffer, *size, 0));
1149
1150 return buffer;
1151 }
1152 else {
1153 *size = 0;
1154 return NULL;
1155 }
1156}
1157
1158
1159/** Frees output report buffer
1160 *
1161 * @param output Output report buffer
1162 * @return
1163 */
1164void usb_hid_report_output_free(uint8_t *output)
1165
1166{
1167 if(output != NULL) {
1168 free (output);
1169 }
1170}
1171
1172/** Returns size of output for given usage path
1173 *
1174 * @param parser
1175 * @param path
1176 * @param flags
1177 * @return
1178 */
1179size_t usb_hid_report_output_size(usb_hid_report_parser_t *parser,
1180 usb_hid_report_path_t *path, int flags)
1181{
1182 size_t ret = 0;
1183 link_t *item;
1184 usb_hid_report_item_t *report_item;
1185
1186 if(parser == NULL) {
1187 return 0;
1188 }
1189
1190 item = parser->output.next;
1191 while(&parser->output != item) {
1192 report_item = list_get_instance(item, usb_hid_report_item_t, link);
1193 if(!USB_HID_ITEM_FLAG_CONSTANT(report_item->item_flags) &&
1194 (usb_hid_report_compare_usage_path(report_item->usage_path, path, flags) == EOK)) {
1195 ret += report_item->count;
1196 }
1197
1198 item = item->next;
1199 }
1200
1201 return ret;
1202
1203}
1204
1205/** Updates the output report buffer by translated given data
1206 *
1207 * @param parser
1208 * @param path
1209 * @param flags
1210 * @param buffer
1211 * @param size
1212 * @param data
1213 * @param data_size
1214 * @return
1215 */
1216int usb_hid_report_output_translate(usb_hid_report_parser_t *parser,
1217 usb_hid_report_path_t *path, int flags,
1218 uint8_t *buffer, size_t size,
1219 int32_t *data, size_t data_size)
1220{
1221 usb_hid_report_item_t *report_item;
1222 link_t *item;
1223 size_t idx=0;
1224 int i=0;
1225 int32_t value=0;
1226 int offset;
1227 int length;
1228 int32_t tmp_value;
1229 size_t offset_prefix = 0;
1230
1231 if(parser == NULL) {
1232 return EINVAL;
1233 }
1234
1235 if(parser->use_report_id != 0) {
1236 buffer[0] = path->report_id;
1237 offset_prefix = 8;
1238 }
1239
1240 usb_log_debug("OUTPUT BUFFER: %s\n", usb_debug_str_buffer(buffer,size, 0));
1241 usb_log_debug("OUTPUT DATA[0]: %d, DATA[1]: %d, DATA[2]: %d\n", data[0], data[1], data[2]);
1242
1243 item = parser->output.next;
1244 while(item != &parser->output) {
1245 report_item = list_get_instance(item, usb_hid_report_item_t, link);
1246
1247 for(i=0; i<report_item->count; i++) {
1248
1249 if(idx >= data_size) {
1250 break;
1251 }
1252
1253 if((USB_HID_ITEM_FLAG_VARIABLE(report_item->item_flags) == 0) ||
1254 ((report_item->usage_minimum == 0) && (report_item->usage_maximum == 0))) {
1255
1256// // variable item
1257 value = usb_hid_translate_data_reverse(report_item, data[idx++]);
1258 offset = report_item->offset + (i * report_item->size) + offset_prefix;
1259 length = report_item->size;
1260 }
1261 else {
1262 //bitmap
1263 value += usb_hid_translate_data_reverse(report_item, data[idx++]);
1264 offset = report_item->offset + offset_prefix;
1265 length = report_item->size * report_item->count;
1266 }
1267
1268 if((offset/8) == ((offset+length-1)/8)) {
1269 // je to v jednom bytu
1270 if(((size_t)(offset/8) >= size) || ((size_t)(offset+length-1)/8) >= size) {
1271 break; // TODO ErrorCode
1272 }
1273
1274 size_t shift = offset%8;
1275
1276 value = value << shift;
1277 value = value & (((1 << length)-1) << shift);
1278
1279 uint8_t mask = 0;
1280 mask = 0xff - (((1 << length) - 1) << shift);
1281 buffer[offset/8] = (buffer[offset/8] & mask) | value;
1282 }
1283 else {
1284 // je to ve dvou!! FIXME: melo by to umet delsi jak 2
1285
1286 // konec prvniho -- dolni x bitu
1287 tmp_value = value;
1288 tmp_value = tmp_value & ((1 << (8-(offset%8)))-1);
1289 tmp_value = tmp_value << (offset%8);
1290
1291 uint8_t mask = 0;
1292 mask = ~(((1 << (8-(offset%8)))-1) << (offset%8));
1293 buffer[offset/8] = (buffer[offset/8] & mask) | tmp_value;
1294
1295 // a ted druhej -- hornich length-x bitu
1296 value = value >> (8 - (offset % 8));
1297 value = value & ((1 << (length - (8 - (offset % 8)))) - 1);
1298
1299 mask = ((1 << (length - (8 - (offset % 8)))) - 1);
1300 buffer[(offset+length-1)/8] = (buffer[(offset+length-1)/8] & mask) | value;
1301 }
1302
1303 }
1304
1305 item = item->next;
1306 }
1307
1308 usb_log_debug("OUTPUT BUFFER: %s\n", usb_debug_str_buffer(buffer,size, 0));
1309
1310 return EOK;
1311}
1312
1313/**
1314 *
1315 * @param item
1316 * @param value
1317 * @return
1318 */
1319int32_t usb_hid_translate_data_reverse(usb_hid_report_item_t *item, int value)
1320{
1321 int ret=0;
1322 int resolution;
1323
1324 if(USB_HID_ITEM_FLAG_CONSTANT(item->item_flags)) {
1325 ret = item->logical_minimum;
1326 }
1327
1328 if((USB_HID_ITEM_FLAG_VARIABLE(item->item_flags) == 0)) {
1329
1330 // variable item
1331 if((item->physical_minimum == 0) && (item->physical_maximum == 0)) {
1332 item->physical_minimum = item->logical_minimum;
1333 item->physical_maximum = item->logical_maximum;
1334 }
1335
1336 if(item->physical_maximum == item->physical_minimum){
1337 resolution = 1;
1338 }
1339 else {
1340 resolution = (item->logical_maximum - item->logical_minimum) /
1341 ((item->physical_maximum - item->physical_minimum) *
1342 (usb_pow(10,(item->unit_exponent))));
1343 }
1344
1345 ret = ((value - item->physical_minimum) * resolution) + item->logical_minimum;
1346 }
1347 else {
1348 // bitmapa
1349 if(value == 0) {
1350 ret = 0;
1351 }
1352 else {
1353 size_t bitmap_idx = (value - item->usage_minimum);
1354 ret = 1 << bitmap_idx;
1355 }
1356 }
1357
1358
1359 return ret;
1360}
1361
1362
1363int usb_hid_report_path_set_report_id(usb_hid_report_path_t *path, uint8_t report_id)
1364{
1365 if(path == NULL){
1366 return EINVAL;
1367 }
1368
1369 path->report_id = report_id;
1370 return EOK;
1371}
1372
1373/**
1374 * @}
1375 */
Note: See TracBrowser for help on using the repository browser.