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
RevLine 
[bf2063e9]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
[976f546]29/** @addtogroup libusb
[bf2063e9]30 * @{
31 */
32/** @file
33 * @brief HID parser implementation.
34 */
35#include <usb/classes/hidparser.h>
36#include <errno.h>
[976f546]37#include <stdio.h>
[e24e7b1]38#include <malloc.h>
39#include <mem.h>
[b7d9606]40#include <assert.h>
41#include <usb/debug.h>
[976f546]42
[b7d9606]43#define USB_HID_NEW_REPORT_ITEM 1
44#define USB_HID_NO_ACTION 2
45#define USB_HID_UNKNOWN_TAG -99
[976f546]46
47
[b7d9606]48int usb_hid_report_parse_tag(uint8_t tag, uint8_t class, const uint8_t *data, size_t item_size,
[976f546]49 usb_hid_report_item_t *report_item);
[c7a2e7e]50int usb_hid_report_parse_main_tag(uint8_t tag, const uint8_t *data, size_t item_size,
[976f546]51 usb_hid_report_item_t *report_item);
[c7a2e7e]52int usb_hid_report_parse_global_tag(uint8_t tag, const uint8_t *data, size_t item_size,
[976f546]53 usb_hid_report_item_t *report_item);
[c7a2e7e]54int usb_hid_report_parse_local_tag(uint8_t tag, const uint8_t *data, size_t item_size,
[976f546]55 usb_hid_report_item_t *report_item);
56
[e24e7b1]57void usb_hid_descriptor_print_list(link_t *head);
[976f546]58int usb_hid_report_reset_local_items();
[e24e7b1]59void usb_hid_free_report_list(link_t *head);
[c7a2e7e]60int32_t usb_hid_report_tag_data_int32(const uint8_t *data, size_t size);
[b7d9606]61inline size_t usb_hid_count_item_offset(usb_hid_report_item_t * report_item, size_t offset);
[976f546]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));
[da3965e]74
75 return EOK;
[976f546]76}
77
[bf2063e9]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,
[b7d9606]86 const uint8_t *data, size_t size)
[bf2063e9]87{
[976f546]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;
[c7a2e7e]95
96 size_t offset=0;
[976f546]97
98
99 if(!(report_item=malloc(sizeof(usb_hid_report_item_t)))){
100 return ENOMEM;
101 }
102 link_initialize(&(report_item->link));
103
[b7d9606]104 while(i<size){
[976f546]105 if(!USB_HID_ITEM_IS_LONG(data[i])){
[b7d9606]106
107 if((i+1) >= size){
108 return -1; // TODO ERROR CODE
109 }
110
[976f546]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]);
[b7d9606]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,
[976f546]121 item_size,report_item);
[b7d9606]122 printf("ret: %u\n", ret);
[976f546]123 switch(ret){
124 case USB_HID_NEW_REPORT_ITEM:
125 // store report item to report and create the new one
[b7d9606]126 usb_log_debug("\nNEW REPORT ITEM: %X",tag);
[c7a2e7e]127
128 report_item->offset = offset;
[b7d9606]129 offset = usb_hid_count_item_offset(report_item, offset);
[976f546]130 switch(tag) {
131 case USB_HID_REPORT_TAG_INPUT:
[b7d9606]132 usb_log_debug(" - INPUT\n");
[976f546]133 list_append(&(report_item->link), &(parser->input));
134 break;
135 case USB_HID_REPORT_TAG_OUTPUT:
[b7d9606]136 usb_log_debug(" - OUTPUT\n");
[976f546]137 list_append(&(report_item->link), &(parser->output));
138
139 break;
140 case USB_HID_REPORT_TAG_FEATURE:
[b7d9606]141 usb_log_debug(" - FEATURE\n");
[976f546]142 list_append(&(report_item->link), &(parser->feature));
143 break;
144 default:
[b7d9606]145 usb_log_debug("\tjump over - tag %X\n", tag);
[976f546]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:
[b7d9606]168 // nothing special to do
[976f546]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;
[bf2063e9]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,
[b7d9606]198 const uint8_t *data, size_t size,
[bf2063e9]199 const usb_hid_report_in_callbacks_t *callbacks, void *arg)
200{
[243cb86]201 int i;
202
[2f60e57d]203 /* main parsing loop */
204 while(0){
205 }
206
[243cb86]207
[2f60e57d]208 uint8_t keys[6];
[243cb86]209
210 for (i = 0; i < 6; ++i) {
211 keys[i] = data[i];
212 }
213
[2f60e57d]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 */
[b7d9606]230int usb_hid_boot_keyboard_input_report(const uint8_t *data, size_t size,
[2f60e57d]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;
[976f546]243 item.usage_minimum = 0;
244 item.usage_maximum = 255;
245 item.logical_minimum = 0;
246 item.logical_maximum = 255;
[2f60e57d]247
[976f546]248 if (size != 8) {
[e24e7b1]249 return -1; //ERANGE;
[2f60e57d]250 }
251
252 uint8_t keys[6];
[976f546]253 for (i = 0; i < item.count; i++) {
254 keys[i] = data[i + item.offset];
[2f60e57d]255 }
256
257 callbacks->keyboard(keys, 6, modifiers, arg);
[e7726a4]258 return EOK;
[bf2063e9]259}
260
[2f60e57d]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}
[bf2063e9]279
[976f546]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 */
[b7d9606]288int usb_hid_report_parse_tag(uint8_t tag, uint8_t class, const uint8_t *data, size_t item_size,
[976f546]289 usb_hid_report_item_t *report_item)
290{
[b7d9606]291 int ret;
292
[976f546]293 switch(class){
294 case USB_HID_TAG_CLASS_MAIN:
295
[b7d9606]296 if((ret=usb_hid_report_parse_main_tag(tag,data,item_size,report_item)) == EOK) {
[976f546]297 return USB_HID_NEW_REPORT_ITEM;
298 }
299 else {
300 /*TODO process the error */
[b7d9606]301 return ret;
[976f546]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:
[b7d9606]313 return USB_HID_NO_ACTION;
[976f546]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
[c7a2e7e]327int usb_hid_report_parse_main_tag(uint8_t tag, const uint8_t *data, size_t item_size,
[976f546]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:
[b7d9606]335 report_item->item_flags = *data;
336 return EOK;
[976f546]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:
[b7d9606]347 return USB_HID_NO_ACTION;
[976f546]348 }
349
[b7d9606]350 return USB_HID_NO_ACTION;
[976f546]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
[c7a2e7e]363int usb_hid_report_parse_global_tag(uint8_t tag, const uint8_t *data, size_t item_size,
[976f546]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:
[b7d9606]405 return USB_HID_NO_ACTION;
[976f546]406 }
[da3965e]407
408 return EOK;
[976f546]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 */
[c7a2e7e]420int usb_hid_report_parse_local_tag(uint8_t tag, const uint8_t *data, size_t item_size,
[976f546]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;
[e24e7b1]452/*
[976f546]453 case USB_HID_REPORT_TAG_DELIMITER:
454 report_item->delimiter = usb_hid_report_tag_data_int32(data,item_size);
455 break;
[e24e7b1]456*/
[976f546]457 default:
[b7d9606]458 return USB_HID_NO_ACTION;
[976f546]459 }
[da3965e]460
461 return EOK;
[976f546]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 */
[c7a2e7e]471int32_t usb_hid_report_tag_data_int32(const uint8_t *data, size_t size)
[976f546]472{
[da3965e]473 unsigned int i;
[976f546]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 }
[b7d9606]501
[976f546]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 */
[e24e7b1]542void usb_hid_free_report_list(link_t *head)
[976f546]543{
[e24e7b1]544 return;
[e259d95]545
[976f546]546 usb_hid_report_item_t *report_item;
[e259d95]547 link_t *next;
[976f546]548
549 if(head == NULL || list_empty(head)) {
[e24e7b1]550 return;
[976f546]551 }
[e259d95]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;
[976f546]558
[e259d95]559 free(report_item);
[976f546]560 }
[e259d95]561
[e24e7b1]562 return;
[e259d95]563
[976f546]564}
565
[19a1800]566/** Free the HID report parser structure
567 *
568 * @param parser Opaque HID report parser structure
[976f546]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}
[c7a2e7e]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}
[976f546]588/**
589 * @}
[c7a2e7e]590 */
Note: See TracBrowser for help on using the repository browser.