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

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

report descriptor bug fix

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