source: mainline/uspace/lib/usbhid/src/hiddescriptor.c@ a35b458

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 24.7 KB
RevLine 
[9d05599]1/*
2 * Copyright (c) 2011 Matej Klonfar
[e0a5d4c]3 * Copyright (c) 2018 Ondrej Hlavaty
[9d05599]4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
[160b75e]30/** @addtogroup libusbhid
[9d05599]31 * @{
32 */
33/** @file
34 * HID report descriptor and report data parser implementation.
35 */
[faa44e58]36#include <usb/hid/hidparser.h>
[9d05599]37#include <errno.h>
38#include <stdio.h>
39#include <mem.h>
40#include <usb/debug.h>
41#include <assert.h>
[38d150e]42#include <stdlib.h>
[9d05599]43
[a76b01b4]44
[f3b39b4]45/*
46 * Constants defining current parsing mode for correct parsing of the set of
47 * local tags (usage) enclosed in delimter tags.
48 */
49/**
50 * Second delimiter tag was read. The set of local items (usage) ended.
51 */
[22ded10]52#define OUTSIDE_DELIMITER_SET 0
[f3b39b4]53
54/**
55 * First delimiter tag was read. The set of local items (usage) started.
56 */
[22ded10]57#define START_DELIMITER_SET 1
[f3b39b4]58
59/**
60 * Parser is in the set of local items.
61 */
[22ded10]62#define INSIDE_DELIMITER_SET 2
[f3b39b4]63
[a76b01b4]64
[a35b458]65
[9d05599]66/** The new report item flag. Used to determine when the item is completly
67 * configured and should be added to the report structure
68 */
69#define USB_HID_NEW_REPORT_ITEM 1
70
71/** No special action after the report descriptor tag is processed should be
72 * done
73 */
74#define USB_HID_NO_ACTION 2
75
76#define USB_HID_RESET_OFFSET 3
77
[3ca4ae9]78#define USB_HID_INVALID -98
[9d05599]79/** Unknown tag was founded in report descriptor data*/
80#define USB_HID_UNKNOWN_TAG -99
81
[a76b01b4]82
[f3b39b4]83/**
84 * Checks if given collection path is already present in report structure and
85 * inserts it if not.
86 *
[1b20da0]87 * @param report Report structure
88 * @param cmp_path The collection path
[f3b39b4]89 * @return Pointer to the result collection path in report structure.
90 * @retval NULL If some error occurs
91 */
[b72efe8]92usb_hid_report_path_t *usb_hid_report_path_try_insert(usb_hid_report_t *report,
93 usb_hid_report_path_t *cmp_path)
94{
95 link_t *path_it = report->collection_paths.head.next;
[1519b91]96 usb_hid_report_path_t *path = NULL;
[a35b458]97
[f3b39b4]98 if((report == NULL) || (cmp_path == NULL)) {
99 return NULL;
100 }
[a35b458]101
[b72efe8]102 while(path_it != &report->collection_paths.head) {
[f3b39b4]103 path = list_get_instance(path_it, usb_hid_report_path_t,
[b72efe8]104 cpath_link);
[a35b458]105
[f3b39b4]106 if(usb_hid_report_compare_usage_path(path, cmp_path,
[3ca4ae9]107 USB_HID_PATH_COMPARE_STRICT) == 0){
[1519b91]108 break;
[b72efe8]109 }
[1519b91]110 path_it = path_it->next;
111 }
[b72efe8]112 if(path_it == &report->collection_paths.head) {
[f3b39b4]113 path = usb_hid_report_path_clone(cmp_path);
114 if(path == NULL) {
115 return NULL;
116 }
[b72efe8]117 list_append(&path->cpath_link, &report->collection_paths);
[1519b91]118 report->collection_paths_count++;
[b9d7965]119
120 return path;
121 }
122 else {
[f3b39b4]123 return list_get_instance(path_it, usb_hid_report_path_t,
[1b20da0]124 cpath_link);
[1519b91]125 }
126}
[9d05599]127
[a76b01b4]128
[9d05599]129/**
130 * Initialize the report descriptor parser structure
131 *
132 * @param parser Report descriptor parser structure
133 * @return Error code
[f3b39b4]134 * @retval EINVAL If no report structure was given
135 * @retval EOK If report structure was successfully initialized
[9d05599]136 */
[5a6cc679]137errno_t usb_hid_report_init(usb_hid_report_t *report)
[9d05599]138{
[8c62a71]139 if (report == NULL) {
[9d05599]140 return EINVAL;
141 }
142
143 memset(report, 0, sizeof(usb_hid_report_t));
144 list_initialize(&report->reports);
145 list_initialize(&report->collection_paths);
146
147 report->use_report_ids = 0;
[8c62a71]148 return EOK;
[9d05599]149}
150
[a76b01b4]151
[3b5d5b9d]152
[f3b39b4]153/**
[3b5d5b9d]154 *
155 *
[f3b39b4]156 * @param report Report structure in which the new report items should be
157 * stored
[1b20da0]158 * @param report_item Current report descriptor's parsing state table
[f3b39b4]159 * @return Error code
160 * @retval EOK If all fields were successfully append to report
161 * @retval EINVAL If invalid parameters (NULL) was given
162 * @retval ENOMEM If there is no memmory to store new report description
163 *
[3b5d5b9d]164 */
[5a6cc679]165errno_t usb_hid_report_append_fields(usb_hid_report_t *report,
[f3b39b4]166 usb_hid_report_item_t *report_item) {
167
[9d05599]168 usb_hid_report_field_t *field;
169 int i;
170
[3a6e423]171 uint32_t *usages;
172 int usages_used=0;
[f3b39b4]173
174 if((report == NULL) || (report_item == NULL)) {
175 return EINVAL;
176 }
177
[3a6e423]178 if(report_item->usages_count > 0){
[806a779]179 usages = malloc(sizeof(uint32_t) * report_item->usages_count);
[f3b39b4]180 memcpy(usages, report_item->usages, sizeof(int32_t) *
[1b20da0]181 report_item->usages_count);
[9d05599]182 }
[3a6e423]183 else {
184 usages = NULL;
185 }
[a35b458]186
[1b20da0]187 usb_hid_report_path_t *path = report_item->usage_path;
[9d05599]188 for(i=0; i<report_item->count; i++){
189
190 field = malloc(sizeof(usb_hid_report_field_t));
[574f276]191 if(field == NULL) {
192 return ENOMEM;
193 }
194
[9d05599]195 memset(field, 0, sizeof(usb_hid_report_field_t));
[b72efe8]196 link_initialize(&field->ritems_link);
[9d05599]197
[1b20da0]198 /* fill the attributes */
[9d05599]199 field->logical_minimum = report_item->logical_minimum;
200 field->logical_maximum = report_item->logical_maximum;
201 field->physical_minimum = report_item->physical_minimum;
202 field->physical_maximum = report_item->physical_maximum;
203
[3a6e423]204 if(USB_HID_ITEM_FLAG_VARIABLE(report_item->item_flags) == 0){
[1b20da0]205 /*
[f3b39b4]206 Store usage array. The Correct Usage Page and Usage is
207 depending on data in report and will be filled later
[3a6e423]208 */
209 field->usage = 0;
210 field->usage_page = 0; //report_item->usage_page;
211
212 field->usages_count = report_item->usages_count;
213 field->usages = usages;
214 usages_used = 1;
[9d05599]215 }
216 else {
217
[3a6e423]218 /* Fill the correct Usage and Usage Page */
219 int32_t usage;
220 if(i < report_item->usages_count) {
[3b5d5b9d]221 usage = report_item->usages[i];
[9d05599]222 }
223 else {
[ee7e7c93]224 usage = report_item->usages[
[1b20da0]225 report_item->usages_count- 1];
[9d05599]226 }
227
[3a6e423]228 if(USB_HID_IS_EXTENDED_USAGE(usage)){
229 field->usage = USB_HID_EXTENDED_USAGE(usage);
[1b20da0]230 field->usage_page =
[f3b39b4]231 USB_HID_EXTENDED_USAGE_PAGE(usage);
[9d05599]232 }
233 else {
[3a6e423]234 // should not occur
[9d05599]235 field->usage = usage;
[3a6e423]236 field->usage_page = report_item->usage_page;
[9d05599]237 }
238 }
[a35b458]239
[f3b39b4]240 usb_hid_report_set_last_item(path, USB_HID_TAG_CLASS_GLOBAL,
241 field->usage_page);
242 usb_hid_report_set_last_item(path, USB_HID_TAG_CLASS_LOCAL,
243 field->usage);
[1519b91]244
[f3b39b4]245 field->collection_path =
246 usb_hid_report_path_try_insert(report, path);
[1519b91]247
[9d05599]248 field->size = report_item->size;
[ee7e7c93]249
[6283cefb]250 field->offset = report_item->offset + (i * report_item->size);
[ee7e7c93]251
[574f276]252 if(report->use_report_ids != 0) {
[9d05599]253 field->offset += 8;
254 report->use_report_ids = 1;
255 }
[ee7e7c93]256
[9d05599]257 field->item_flags = report_item->item_flags;
258
259 /* find the right report list*/
260 usb_hid_report_description_t *report_des;
[f3b39b4]261 report_des = usb_hid_report_find_description(report,
262 report_item->id, report_item->type);
[a35b458]263
[9d05599]264 if(report_des == NULL){
[f3b39b4]265 report_des = malloc(
266 sizeof(usb_hid_report_description_t));
267 if(report_des == NULL) {
268 return ENOMEM;
269 }
270
271 memset(report_des, 0,
272 sizeof(usb_hid_report_description_t));
[9d05599]273
274 report_des->type = report_item->type;
275 report_des->report_id = report_item->id;
[1432fcf3]276 if(report_des->report_id != 0) {
277 /* set up the bit length by report_id field */
278 report_des->bit_length = 8;
279 }
280
[b72efe8]281 link_initialize (&report_des->reports_link);
[9d05599]282 list_initialize (&report_des->report_items);
283
[b72efe8]284 list_append(&report_des->reports_link, &report->reports);
[9d05599]285 report->report_count++;
286 }
287
288 /* append this field to the end of founded report list */
[b72efe8]289 list_append(&field->ritems_link, &report_des->report_items);
[a35b458]290
[9d05599]291 /* update the sizes */
292 report_des->bit_length += field->size;
293 report_des->item_length++;
294
295 }
296
[3a6e423]297 // free only when not used!!!
298 if(usages && usages_used == 0) {
299 free(usages);
300 }
[9d05599]301
302 return EOK;
303}
[a76b01b4]304
[f3b39b4]305/**
306 * Finds description of report with given report_id and of given type in
307 * opaque report structure.
308 *
309 * @param report Opaque structure containing the parsed report descriptor
310 * @param report_id ReportId of report we are searching
311 * @param type Type of report we are searching
312 * @return Pointer to the particular report description
313 * @retval NULL If no description is founded
314 */
315usb_hid_report_description_t * usb_hid_report_find_description(
316 const usb_hid_report_t *report, uint8_t report_id,
317 usb_hid_report_type_t type) {
[9d05599]318
[0dd3e49]319 if(report == NULL) {
320 return NULL;
321 }
322
[feeac0d]323 list_foreach(report->reports, reports_link,
324 usb_hid_report_description_t, report_des) {
[dcb7d7cd]325 // if report id not set, return the first of the type
[1b20da0]326 if(((report_des->report_id == report_id) || (report_id == 0)) &&
327 (report_des->type == type)) {
[9d05599]328 return report_des;
329 }
330 }
331
332 return NULL;
333}
[a76b01b4]334
[9d05599]335
336/** Parse HID report descriptor.
337 *
338 * @param parser Opaque HID report parser structure.
339 * @param data Data describing the report.
340 * @return Error code.
[f3b39b4]341 * @retval ENOMEM If no more memmory is available
342 * @retval EINVAL If invalid data are founded
343 * @retval EOK If report descriptor is successfully parsed
[9d05599]344 */
[1b20da0]345errno_t usb_hid_parse_report_descriptor(usb_hid_report_t *report,
[9d05599]346 const uint8_t *data, size_t size)
347{
348 size_t i=0;
349 uint8_t tag=0;
350 uint8_t item_size=0;
351 int class=0;
352 int ret;
353 usb_hid_report_item_t *report_item=0;
[1b20da0]354 usb_hid_report_item_t *new_report_item;
[9d05599]355 usb_hid_report_path_t *usage_path;
356
357 size_t offset_input=0;
358 size_t offset_output=0;
359 size_t offset_feature=0;
[a35b458]360
[b72efe8]361 link_t *item_link;
[9d05599]362
[b72efe8]363 list_t stack;
364 list_initialize(&stack);
[9d05599]365
366 /* parser structure initialization*/
367 if(usb_hid_report_init(report) != EOK) {
368 return EINVAL;
369 }
[a35b458]370
[9d05599]371 /*report item initialization*/
372 if(!(report_item=malloc(sizeof(usb_hid_report_item_t)))){
373 return ENOMEM;
374 }
375 memset(report_item, 0, sizeof(usb_hid_report_item_t));
[b72efe8]376 link_initialize(&(report_item->link));
[9d05599]377
378 /* usage path context initialization */
379 if(!(usage_path=usb_hid_report_path())){
380 return ENOMEM;
381 }
[1b20da0]382 usb_hid_report_path_append_item(usage_path, 0, 0);
[a35b458]383
[1b20da0]384 while(i<size){
[9d05599]385 if(!USB_HID_ITEM_IS_LONG(data[i])){
386
387 if((i+USB_HID_ITEM_SIZE(data[i]))>= size){
388 return EINVAL;
389 }
[a35b458]390
[9d05599]391 tag = USB_HID_ITEM_TAG(data[i]);
392 item_size = USB_HID_ITEM_SIZE(data[i]);
393 class = USB_HID_ITEM_TAG_CLASS(data[i]);
[a35b458]394
[9d05599]395 ret = usb_hid_report_parse_tag(tag,class,data+i+1,
[f3b39b4]396 item_size,report_item, usage_path);
397
[9d05599]398 switch(ret){
[f3b39b4]399 case USB_HID_NEW_REPORT_ITEM:
400 /* store report item to report and create the
401 * new one store current collection path
402 */
403 report_item->usage_path = usage_path;
[a35b458]404
[f3b39b4]405 usb_hid_report_path_set_report_id(
406 report_item->usage_path, report_item->id);
[a35b458]407
[f3b39b4]408 if(report_item->id != 0){
409 report->use_report_ids = 1;
410 }
[a35b458]411
[f3b39b4]412 switch(tag) {
413 case USB_HID_REPORT_TAG_INPUT:
[1b20da0]414 report_item->type =
[f3b39b4]415 USB_HID_REPORT_TYPE_INPUT;
416
417 report_item->offset = offset_input;
[1b20da0]418 offset_input += report_item->count *
[f3b39b4]419 report_item->size;
420 break;
[a35b458]421
[f3b39b4]422 case USB_HID_REPORT_TAG_OUTPUT:
[1b20da0]423 report_item->type =
[f3b39b4]424 USB_HID_REPORT_TYPE_OUTPUT;
[a35b458]425
[f3b39b4]426 report_item->offset = offset_output;
[1b20da0]427 offset_output += report_item->count *
[f3b39b4]428 report_item->size;
[9d05599]429 break;
[a35b458]430
[f3b39b4]431 case USB_HID_REPORT_TAG_FEATURE:
[1b20da0]432 report_item->type =
[f3b39b4]433 USB_HID_REPORT_TYPE_FEATURE;
[9d05599]434
[f3b39b4]435 report_item->offset = offset_feature;
[1b20da0]436 offset_feature += report_item->count *
[f3b39b4]437 report_item->size;
[9d05599]438 break;
[a35b458]439
[f3b39b4]440 default:
441 usb_log_debug2(
442 "\tjump over - tag %X\n", tag);
443 break;
444 }
[a35b458]445
[1b20da0]446 /*
447 * append new fields to the report structure
[f3b39b4]448 */
[1b20da0]449 usb_hid_report_append_fields(report,
[f3b39b4]450 report_item);
451
452 /* reset local items */
453 usb_hid_report_reset_local_items (report_item);
454 break;
455
456 case USB_HID_RESET_OFFSET:
457 offset_input = 0;
458 offset_output = 0;
459 offset_feature = 0;
[1b20da0]460 usb_hid_report_path_set_report_id (usage_path,
[f3b39b4]461 report_item->id);
462 break;
463
464 case USB_HID_REPORT_TAG_PUSH:
465 // push current state to stack
466 new_report_item = usb_hid_report_item_clone(
467 report_item);
[a35b458]468
[1b20da0]469 usb_hid_report_path_t *tmp_path =
[f3b39b4]470 usb_hid_report_path_clone(usage_path);
[9d05599]471
[1b20da0]472 new_report_item->usage_path = tmp_path;
[9d05599]473
[f3b39b4]474 list_prepend (&new_report_item->link, &stack);
475 break;
476 case USB_HID_REPORT_TAG_POP:
477 // restore current state from stack
[b72efe8]478 item_link = list_first(&stack);
479 if (item_link == NULL) {
[f3b39b4]480 return EINVAL;
481 }
482 free(report_item);
[a35b458]483
[b72efe8]484 report_item = list_get_instance(item_link,
[f3b39b4]485 usb_hid_report_item_t, link);
[a35b458]486
[f3b39b4]487 usb_hid_report_usage_path_t *tmp_usage_path;
488 tmp_usage_path = list_get_instance(
[b72efe8]489 report_item->usage_path->cpath_link.prev,
490 usb_hid_report_usage_path_t, rpath_items_link);
[a35b458]491
[1b20da0]492 usb_hid_report_set_last_item(usage_path,
[f3b39b4]493 USB_HID_TAG_CLASS_GLOBAL, tmp_usage_path->usage_page);
[a35b458]494
[1b20da0]495 usb_hid_report_set_last_item(usage_path,
[f3b39b4]496 USB_HID_TAG_CLASS_LOCAL, tmp_usage_path->usage);
[9d05599]497
[f3b39b4]498 usb_hid_report_path_free(report_item->usage_path);
[b72efe8]499 list_remove (item_link);
[a35b458]500
[f3b39b4]501 break;
[a35b458]502
[f3b39b4]503 default:
[1b20da0]504 // nothing special to do
[f3b39b4]505 break;
[9d05599]506 }
507
508 /* jump over the processed block */
509 i += 1 + USB_HID_ITEM_SIZE(data[i]);
510 }
511 else{
512 // TBD
513 i += 3 + USB_HID_ITEM_SIZE(data[i+1]);
514 }
[a35b458]515
[9d05599]516
517 }
[a35b458]518
[9d05599]519 return EOK;
520}
521
[a76b01b4]522
[9d05599]523
524/**
525 * Parse one tag of the report descriptor
526 *
527 * @param Tag to parse
528 * @param Report descriptor buffer
529 * @param Size of data belongs to this tag
530 * @param Current report item structe
531 * @return Code of action to be done next
532 */
[f3b39b4]533int usb_hid_report_parse_tag(uint8_t tag, uint8_t class, const uint8_t *data,
534 size_t item_size, usb_hid_report_item_t *report_item,
[1b20da0]535 usb_hid_report_path_t *usage_path) {
[a35b458]536
[9d05599]537 int ret;
[a35b458]538
[9d05599]539 switch(class){
[f3b39b4]540 case USB_HID_TAG_CLASS_MAIN:
[9d05599]541
[f3b39b4]542 if((ret=usb_hid_report_parse_main_tag(tag, data, item_size,
[3ca4ae9]543 report_item, usage_path)) == 0) {
[9d05599]544
[f3b39b4]545 return USB_HID_NEW_REPORT_ITEM;
546 }
547 else {
548 return ret;
549 }
550 break;
[9d05599]551
[1b20da0]552 case USB_HID_TAG_CLASS_GLOBAL:
[f3b39b4]553 return usb_hid_report_parse_global_tag(tag, data, item_size,
554 report_item, usage_path);
555 break;
556
[1b20da0]557 case USB_HID_TAG_CLASS_LOCAL:
[f3b39b4]558 return usb_hid_report_parse_local_tag(tag, data, item_size,
559 report_item, usage_path);
560 break;
[a35b458]561
[f3b39b4]562 default:
563 return USB_HID_NO_ACTION;
[9d05599]564 }
565}
566
567/**
568 * Parse main tags of report descriptor
569 *
570 * @param Tag identifier
571 * @param Data buffer
572 * @param Length of data buffer
573 * @param Current state table
[3ca4ae9]574 * @return 0 or USB_HID_ code
[9d05599]575 */
576
[1b20da0]577int usb_hid_report_parse_main_tag(uint8_t tag, const uint8_t *data,
[f3b39b4]578 size_t item_size, usb_hid_report_item_t *report_item,
579 usb_hid_report_path_t *usage_path)
[674cf89]580{
581 usb_hid_report_usage_path_t *path_item;
[a35b458]582
[9d05599]583 switch(tag)
584 {
[f3b39b4]585 case USB_HID_REPORT_TAG_INPUT:
586 case USB_HID_REPORT_TAG_OUTPUT:
587 case USB_HID_REPORT_TAG_FEATURE:
[1b20da0]588 report_item->item_flags = *data;
589 return 0;
[f3b39b4]590 break;
[a35b458]591
[f3b39b4]592 case USB_HID_REPORT_TAG_COLLECTION:
[3a6e423]593
[f3b39b4]594 /* store collection atributes */
[b72efe8]595 path_item = list_get_instance(list_first(&usage_path->items),
596 usb_hid_report_usage_path_t, rpath_items_link);
597 path_item->flags = *data;
[a35b458]598
[f3b39b4]599 /* set last item */
[1b20da0]600 usb_hid_report_set_last_item(usage_path,
601 USB_HID_TAG_CLASS_GLOBAL,
[f3b39b4]602 USB_HID_EXTENDED_USAGE_PAGE(report_item->usages[
603 report_item->usages_count-1]));
604
[1b20da0]605 usb_hid_report_set_last_item(usage_path,
606 USB_HID_TAG_CLASS_LOCAL,
[f3b39b4]607 USB_HID_EXTENDED_USAGE(report_item->usages[
608 report_item->usages_count-1]));
[a35b458]609
[f3b39b4]610 /* append the new one which will be set by common usage/usage
611 * page */
[1b20da0]612 usb_hid_report_path_append_item(usage_path,
613 report_item->usage_page,
[f3b39b4]614 report_item->usages[report_item->usages_count-1]);
615
616 usb_hid_report_reset_local_items (report_item);
617 return USB_HID_NO_ACTION;
618 break;
[a35b458]619
[f3b39b4]620 case USB_HID_REPORT_TAG_END_COLLECTION:
621 usb_hid_report_remove_last_item(usage_path);
622 return USB_HID_NO_ACTION;
623 break;
624
625 default:
626 return USB_HID_NO_ACTION;
[9d05599]627 }
628
[3ca4ae9]629 return 0;
[9d05599]630}
631
632/**
633 * Parse global tags of report descriptor
634 *
635 * @param Tag identifier
636 * @param Data buffer
637 * @param Length of data buffer
638 * @param Current state table
[3ca4ae9]639 * @return 0 or USB_HID_ code
[9d05599]640 */
[1b20da0]641int usb_hid_report_parse_global_tag(uint8_t tag, const uint8_t *data,
642 size_t item_size, usb_hid_report_item_t *report_item,
643 usb_hid_report_path_t *usage_path) {
[a35b458]644
[9d05599]645 switch(tag)
646 {
[f3b39b4]647 case USB_HID_REPORT_TAG_USAGE_PAGE:
[1b20da0]648 report_item->usage_page =
[f3b39b4]649 usb_hid_report_tag_data_uint32(data, item_size);
650 break;
651
652 case USB_HID_REPORT_TAG_LOGICAL_MINIMUM:
653 report_item->logical_minimum = USB_HID_UINT32_TO_INT32(
654 usb_hid_report_tag_data_uint32(data,item_size),
655 item_size * 8);
656 break;
657
658 case USB_HID_REPORT_TAG_LOGICAL_MAXIMUM:
659 report_item->logical_maximum = USB_HID_UINT32_TO_INT32(
[1b20da0]660 usb_hid_report_tag_data_uint32(data,item_size),
[f3b39b4]661 item_size * 8);
662 break;
663
664 case USB_HID_REPORT_TAG_PHYSICAL_MINIMUM:
665 report_item->physical_minimum = USB_HID_UINT32_TO_INT32(
[1b20da0]666 usb_hid_report_tag_data_uint32(data,item_size),
[f3b39b4]667 item_size * 8);
[1b20da0]668 break;
[f3b39b4]669
670 case USB_HID_REPORT_TAG_PHYSICAL_MAXIMUM:
671 report_item->physical_maximum = USB_HID_UINT32_TO_INT32(
[1b20da0]672 usb_hid_report_tag_data_uint32(data,item_size),
[f3b39b4]673 item_size * 8);
674 break;
675
676 case USB_HID_REPORT_TAG_UNIT_EXPONENT:
677 report_item->unit_exponent = usb_hid_report_tag_data_uint32(
678 data,item_size);
679 break;
680
681 case USB_HID_REPORT_TAG_UNIT:
682 report_item->unit = usb_hid_report_tag_data_uint32(
683 data,item_size);
684 break;
685
686 case USB_HID_REPORT_TAG_REPORT_SIZE:
687 report_item->size = usb_hid_report_tag_data_uint32(
688 data,item_size);
689 break;
690
691 case USB_HID_REPORT_TAG_REPORT_COUNT:
692 report_item->count = usb_hid_report_tag_data_uint32(
693 data,item_size);
694 break;
695
696 case USB_HID_REPORT_TAG_REPORT_ID:
[1b20da0]697 report_item->id = usb_hid_report_tag_data_uint32(data,
[f3b39b4]698 item_size);
699 return USB_HID_RESET_OFFSET;
700 break;
[a35b458]701
[f3b39b4]702 case USB_HID_REPORT_TAG_PUSH:
703 case USB_HID_REPORT_TAG_POP:
[1b20da0]704 /*
[f3b39b4]705 * stack operations are done in top level parsing
706 * function
707 */
708 return tag;
709 break;
[a35b458]710
[f3b39b4]711 default:
712 return USB_HID_NO_ACTION;
[9d05599]713 }
714
[3ca4ae9]715 return 0;
[9d05599]716}
717
718/**
719 * Parse local tags of report descriptor
720 *
721 * @param Tag identifier
722 * @param Data buffer
723 * @param Length of data buffer
724 * @param Current state table
[3ca4ae9]725 * @return 0 or USB_HID_ code
[9d05599]726 */
[e141281]727int usb_hid_report_parse_local_tag(uint8_t tag, const uint8_t *data,
728 size_t item_size, usb_hid_report_item_t *report_item,
729 usb_hid_report_path_t *usage_path)
[9d05599]730{
[3a6e423]731 int32_t extended_usage;
[a35b458]732
[e141281]733 switch (tag) {
[f3b39b4]734 case USB_HID_REPORT_TAG_USAGE:
[e141281]735 switch (report_item->in_delimiter) {
[f3b39b4]736 case INSIDE_DELIMITER_SET:
[e141281]737 /*
738 * Nothing to do.
739 * We catch only the first one
[f3b39b4]740 */
[1be39e9]741 break;
[a35b458]742
[f3b39b4]743 case START_DELIMITER_SET:
744 report_item->in_delimiter = INSIDE_DELIMITER_SET;
[dc12262]745 /* Fallthrough */
[f3b39b4]746 case OUTSIDE_DELIMITER_SET:
747 extended_usage = ((report_item->usage_page) << 16);
[e141281]748 extended_usage +=
749 usb_hid_report_tag_data_uint32(data, item_size);
[a35b458]750
[e141281]751 report_item->usages[report_item->usages_count] =
752 extended_usage;
[a35b458]753
[f3b39b4]754 report_item->usages_count++;
755 break;
756 }
757 break;
[a35b458]758
[e141281]759 case USB_HID_REPORT_TAG_USAGE_MINIMUM:
[f3b39b4]760 if (item_size == 3) {
[e141281]761 /* Usage extended usages */
762 report_item->extended_usage_page =
[f3b39b4]763 USB_HID_EXTENDED_USAGE_PAGE(
[e141281]764 usb_hid_report_tag_data_uint32(data, item_size));
[a35b458]765
766
[e141281]767 report_item->usage_minimum =
[f3b39b4]768 USB_HID_EXTENDED_USAGE(
[e141281]769 usb_hid_report_tag_data_uint32(data, item_size));
770 } else {
771 report_item->usage_minimum =
772 usb_hid_report_tag_data_uint32(data, item_size);
[f3b39b4]773 }
774 break;
[a35b458]775
[f3b39b4]776 case USB_HID_REPORT_TAG_USAGE_MAXIMUM:
777 if (item_size == 3) {
[e141281]778 if (report_item->extended_usage_page !=
779 USB_HID_EXTENDED_USAGE_PAGE(
780 usb_hid_report_tag_data_uint32(data, item_size))) {
[3ca4ae9]781 return USB_HID_INVALID;
[1be39e9]782 }
[a35b458]783
[e141281]784 /* Usage extended usages */
785 report_item->extended_usage_page =
786 USB_HID_EXTENDED_USAGE_PAGE(
787 usb_hid_report_tag_data_uint32(data,item_size));
[a35b458]788
[e141281]789 report_item->usage_maximum =
790 USB_HID_EXTENDED_USAGE(
791 usb_hid_report_tag_data_uint32(data,item_size));
792 } else {
793 report_item->usage_maximum =
794 usb_hid_report_tag_data_uint32(data,item_size);
[f3b39b4]795 }
[a35b458]796
[e141281]797 /* Put the records into the usages array */
798 for (int32_t i = report_item->usage_minimum;
[f3b39b4]799 i <= report_item->usage_maximum; i++) {
[a35b458]800
[e141281]801 if (report_item->extended_usage_page) {
802 report_item->usages[report_item->usages_count++] =
803 (report_item->extended_usage_page << 16) + i;
804 } else {
805 report_item->usages[report_item->usages_count++] =
806 (report_item->usage_page << 16) + i;
[f3b39b4]807 }
808 }
809 report_item->extended_usage_page = 0;
[a35b458]810
[f3b39b4]811 break;
[a35b458]812
[f3b39b4]813 case USB_HID_REPORT_TAG_DESIGNATOR_INDEX:
[e141281]814 report_item->designator_index =
815 usb_hid_report_tag_data_uint32(data, item_size);
[f3b39b4]816 break;
[a35b458]817
[f3b39b4]818 case USB_HID_REPORT_TAG_DESIGNATOR_MINIMUM:
[e141281]819 report_item->designator_minimum =
820 usb_hid_report_tag_data_uint32(data, item_size);
[f3b39b4]821 break;
[a35b458]822
[f3b39b4]823 case USB_HID_REPORT_TAG_DESIGNATOR_MAXIMUM:
[e141281]824 report_item->designator_maximum =
825 usb_hid_report_tag_data_uint32(data, item_size);
[f3b39b4]826 break;
[a35b458]827
[f3b39b4]828 case USB_HID_REPORT_TAG_STRING_INDEX:
[e141281]829 report_item->string_index =
830 usb_hid_report_tag_data_uint32(data, item_size);
[f3b39b4]831 break;
[a35b458]832
[f3b39b4]833 case USB_HID_REPORT_TAG_STRING_MINIMUM:
[e141281]834 report_item->string_minimum =
835 usb_hid_report_tag_data_uint32(data, item_size);
[f3b39b4]836 break;
[a35b458]837
[f3b39b4]838 case USB_HID_REPORT_TAG_STRING_MAXIMUM:
[e141281]839 report_item->string_maximum =
840 usb_hid_report_tag_data_uint32(data, item_size);
841 break;
[a35b458]842
[f3b39b4]843 case USB_HID_REPORT_TAG_DELIMITER:
[e141281]844 report_item->in_delimiter =
845 usb_hid_report_tag_data_uint32(data, item_size);
[f3b39b4]846 break;
[a35b458]847
[f3b39b4]848 default:
849 return USB_HID_NO_ACTION;
[9d05599]850 }
[a35b458]851
[3ca4ae9]852 return 0;
[9d05599]853}
[a76b01b4]854
[9d05599]855/**
856 * Converts raw data to uint32 (thats the maximum length of short item data)
857 *
858 * @param Data buffer
859 * @param Size of buffer
860 * @return Converted int32 number
861 */
862uint32_t usb_hid_report_tag_data_uint32(const uint8_t *data, size_t size)
863{
864 unsigned int i;
865 uint32_t result;
866
867 result = 0;
868 for(i=0; i<size; i++) {
869 result = (result | (data[i]) << (i*8));
870 }
871
872 return result;
873}
[a76b01b4]874
[9d05599]875
876/**
877 * Prints content of given list of report items.
878 *
879 * @param List of report items (usb_hid_report_item_t)
880 * @return void
881 */
[b72efe8]882void usb_hid_descriptor_print_list(list_t *list)
[9d05599]883{
[b72efe8]884 if(list == NULL || list_empty(list)) {
[a1732929]885 usb_log_debug("\tempty");
[9d05599]886 return;
887 }
[b72efe8]888
[feeac0d]889 list_foreach(*list, ritems_link, usb_hid_report_field_t,
890 report_item) {
[a1732929]891 usb_log_debug("\t\tOFFSET: %u", report_item->offset);
892 usb_log_debug("\t\tSIZE: %zu", report_item->size);
893 usb_log_debug("\t\tLOGMIN: %d",
[f3b39b4]894 report_item->logical_minimum);
[a1732929]895 usb_log_debug("\t\tLOGMAX: %d",
[b72efe8]896 report_item->logical_maximum);
[a1732929]897 usb_log_debug("\t\tPHYMIN: %d",
[b72efe8]898 report_item->physical_minimum);
[a1732929]899 usb_log_debug("\t\tPHYMAX: %d",
[b72efe8]900 report_item->physical_maximum);
[a1732929]901 usb_log_debug("\t\ttUSAGEMIN: %X",
[f3b39b4]902 report_item->usage_minimum);
[a1732929]903 usb_log_debug("\t\tUSAGEMAX: %X",
[f3b39b4]904 report_item->usage_maximum);
[a1732929]905 usb_log_debug("\t\tUSAGES COUNT: %zu",
[f3b39b4]906 report_item->usages_count);
[9d05599]907
[a1732929]908 usb_log_debug("\t\tVALUE: %X", report_item->value);
909 usb_log_debug("\t\ttUSAGE: %X", report_item->usage);
910 usb_log_debug("\t\tUSAGE PAGE: %X", report_item->usage_page);
[b72efe8]911
[3a6e423]912 usb_hid_print_usage_path(report_item->collection_path);
[9d05599]913 }
914}
[a76b01b4]915
[f3b39b4]916
[9d05599]917/**
918 * Prints content of given report descriptor in human readable format.
919 *
920 * @param parser Parsed descriptor to print
921 * @return void
922 */
923void usb_hid_descriptor_print(usb_hid_report_t *report)
924{
[feeac0d]925 if (report == NULL)
[9d05599]926 return;
927
[feeac0d]928 list_foreach(report->reports, reports_link,
929 usb_hid_report_description_t, report_des) {
[a1732929]930 usb_log_debug("Report ID: %d", report_des->report_id);
931 usb_log_debug("\tType: %d", report_des->type);
932 usb_log_debug("\tLength: %zu", report_des->bit_length);
933 usb_log_debug("\tB Size: %zu",
[b72efe8]934 usb_hid_report_byte_size(report,
935 report_des->report_id,
[d861c22]936 report_des->type));
[a1732929]937 usb_log_debug("\tItems: %zu", report_des->item_length);
[9d05599]938
939 usb_hid_descriptor_print_list(&report_des->report_items);
940 }
941}
[a76b01b4]942
[9d05599]943
944
[1b20da0]945/** Frees the HID report descriptor parser structure
[9d05599]946 *
947 * @param parser Opaque HID report parser structure
948 * @return void
949 */
[a8c4e871]950void usb_hid_report_deinit(usb_hid_report_t *report)
[9d05599]951{
952 if(report == NULL){
953 return;
954 }
955
956 // free collection paths
[b72efe8]957 link_t *path_link;
[9d05599]958 usb_hid_report_path_t *path;
959 while(!list_empty(&report->collection_paths)) {
[b72efe8]960 path_link = list_first(&report->collection_paths);
961 path = list_get_instance(path_link,
962 usb_hid_report_path_t, cpath_link);
[f3b39b4]963
[b72efe8]964 list_remove(path_link);
965 usb_hid_report_path_free(path);
[9d05599]966 }
[a35b458]967
[9d05599]968 // free report items
969 usb_hid_report_description_t *report_des;
970 usb_hid_report_field_t *field;
971 while(!list_empty(&report->reports)) {
[b72efe8]972 report_des = list_get_instance(list_first(&report->reports),
973 usb_hid_report_description_t, reports_link);
[f3b39b4]974
[b72efe8]975 list_remove(&report_des->reports_link);
[a35b458]976
[9d05599]977 while(!list_empty(&report_des->report_items)) {
[f3b39b4]978 field = list_get_instance(
[b72efe8]979 list_first(&report_des->report_items),
980 usb_hid_report_field_t, ritems_link);
[f3b39b4]981
[b72efe8]982 list_remove(&field->ritems_link);
[9d05599]983
984 free(field);
985 }
[a35b458]986
[9d05599]987 free(report_des);
988 }
[a35b458]989
[9d05599]990 return;
991}
[a76b01b4]992
[9d05599]993
994/**
995 * @}
996 */
Note: See TracBrowser for help on using the repository browser.