source: mainline/uspace/lib/usbhid/src/hidparser.c@ 1bab1c8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1bab1c8 was 4172db4a, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usb: fix some simple errors clang scan found

  • Property mode set to 100644
File size: 15.2 KB
RevLine 
[bf2063e9]1/*
[2571089]2 * Copyright (c) 2011 Matej Klonfar
[bf2063e9]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
[160b75e]29/** @addtogroup libusbhid
[bf2063e9]30 * @{
31 */
32/** @file
[5499a8b]33 * USB HID report data parser implementation.
[bf2063e9]34 */
[faa44e58]35#include <usb/hid/hidparser.h>
[bf2063e9]36#include <errno.h>
[976f546]37#include <stdio.h>
[e24e7b1]38#include <malloc.h>
39#include <mem.h>
[b7d9606]40#include <usb/debug.h>
[681f24b3]41#include <assert.h>
[6283cefb]42#include <bitops.h>
43#include <macros.h>
[976f546]44
[a76b01b4]45
[57d9c05e]46/*
47 * Data translation private functions
48 */
[1553cbf]49uint32_t usb_hid_report_tag_data_uint32(const uint8_t *data, size_t size);
[5499a8b]50
[cfbbe1d3]51int usb_hid_translate_data(usb_hid_report_field_t *item, const uint8_t *data);
[5499a8b]52
53uint32_t usb_hid_translate_data_reverse(usb_hid_report_field_t *item,
54 int32_t value);
55
[a76b01b4]56
[2571089]57
[069b80d]58static int usb_pow(int a, int b)
[fad14d7]59{
[069b80d]60 switch (b) {
[5499a8b]61 case 0:
62 return 1;
63 break;
64 case 1:
65 return a;
66 break;
67 default:
[069b80d]68 return a * usb_pow(a, b - 1);
[5499a8b]69 break;
[fad14d7]70 }
71}
[a76b01b4]72
[976f546]73
[3a6e423]74/** Returns size of report of specified report id and type in items
75 *
76 * @param parser Opaque report parser structure
77 * @param report_id
78 * @param type
79 * @return Number of items in specified report
80 */
81size_t usb_hid_report_size(usb_hid_report_t *report, uint8_t report_id,
[069b80d]82 usb_hid_report_type_t type)
[3a6e423]83{
84 usb_hid_report_description_t *report_des;
85
[069b80d]86 if (report == NULL) {
[3a6e423]87 return 0;
88 }
89
[069b80d]90 report_des = usb_hid_report_find_description(report, report_id, type);
91 if (report_des == NULL) {
[3a6e423]92 return 0;
[069b80d]93 } else {
[3a6e423]94 return report_des->item_length;
95 }
96}
[1eee99f2]97
98/** Returns size of report of specified report id and type in bytes
99 *
100 * @param parser Opaque report parser structure
101 * @param report_id
102 * @param type
103 * @return Number of items in specified report
104 */
105size_t usb_hid_report_byte_size(usb_hid_report_t *report, uint8_t report_id,
[069b80d]106 usb_hid_report_type_t type)
[1eee99f2]107{
108 usb_hid_report_description_t *report_des;
109
[069b80d]110 if (report == NULL) {
[1eee99f2]111 return 0;
112 }
113
[069b80d]114 report_des = usb_hid_report_find_description(report, report_id, type);
115 if (report_des == NULL) {
[1eee99f2]116 return 0;
[069b80d]117 } else {
[d861c22]118 return ((report_des->bit_length + 7) / 8) ;
[1eee99f2]119 }
120}
[a76b01b4]121
[c7a2e7e]122
[fad14d7]123/** Parse and act upon a HID report.
124 *
125 * @see usb_hid_parse_report_descriptor
126 *
127 * @param parser Opaque HID report parser structure.
128 * @param data Data for the report.
129 * @return Error code.
130 */
[5499a8b]131int usb_hid_parse_report(const usb_hid_report_t *report, const uint8_t *data,
[069b80d]132 size_t size, uint8_t *report_id)
[fad14d7]133{
[175ad13e]134 usb_hid_report_description_t *report_des;
135 usb_hid_report_type_t type = USB_HID_REPORT_TYPE_INPUT;
[07525cd]136
[069b80d]137 if (report == NULL) {
[55e388a1]138 return EINVAL;
139 }
[c156c2d]140
[069b80d]141 if (report->use_report_ids != 0) {
[cfbbe1d3]142 *report_id = data[0];
[069b80d]143 } else {
[cfbbe1d3]144 *report_id = 0;
[c156c2d]145 }
146
[8242dd86]147 report_des = usb_hid_report_find_description(report, *report_id,
[069b80d]148 type);
[8242dd86]149
[069b80d]150 if (report_des == NULL) {
[14e1bcc]151 return EINVAL;
152 }
[c156c2d]153
[175ad13e]154 /* read data */
[feeac0d]155 list_foreach(report_des->report_items, ritems_link,
156 usb_hid_report_field_t, item) {
[fad14d7]157
[069b80d]158 if (USB_HID_ITEM_FLAG_CONSTANT(item->item_flags) == 0) {
[07525cd]159
[069b80d]160 if (USB_HID_ITEM_FLAG_VARIABLE(item->item_flags) == 0) {
161 /* array */
[f3b39b4]162 item->value =
163 usb_hid_translate_data(item, data);
[07525cd]164
[5499a8b]165 item->usage = USB_HID_EXTENDED_USAGE(
[069b80d]166 item->usages[item->value -
167 item->physical_minimum]);
[f3b39b4]168
[8242dd86]169 item->usage_page =
170 USB_HID_EXTENDED_USAGE_PAGE(
[069b80d]171 item->usages[item->value -
172 item->physical_minimum]);
[3a6e423]173
[069b80d]174 usb_hid_report_set_last_item(
[8242dd86]175 item->collection_path,
176 USB_HID_TAG_CLASS_GLOBAL,
177 item->usage_page);
[f3b39b4]178
[069b80d]179 usb_hid_report_set_last_item(
[8242dd86]180 item->collection_path,
[5499a8b]181 USB_HID_TAG_CLASS_LOCAL, item->usage);
[069b80d]182 } else {
183 /* variable item */
[8242dd86]184 item->value = usb_hid_translate_data(item,
[feeac0d]185 data);
186 }
[fad14d7]187 }
188 }
[07525cd]189
[fad14d7]190 return EOK;
191}
192
[a76b01b4]193
[57d9c05e]194/**
[c156c2d]195 * Translate data from the report as specified in report descriptor item
[57d9c05e]196 *
197 * @param item Report descriptor item with definition of translation
198 * @param data Data to translate
199 * @return Translated data
200 */
[cfbbe1d3]201int usb_hid_translate_data(usb_hid_report_field_t *item, const uint8_t *data)
[c7a2e7e]202{
[069b80d]203 /* now only short tags are allowed */
204 if (item->size > 32) {
[fad14d7]205 return 0;
206 }
207
[069b80d]208 if ((item->physical_minimum == 0) && (item->physical_maximum == 0)) {
[fad14d7]209 item->physical_minimum = item->logical_minimum;
[6283cefb]210 item->physical_maximum = item->logical_maximum;
[fad14d7]211 }
212
[6283cefb]213 int resolution;
[069b80d]214 if (item->physical_maximum == item->physical_minimum) {
[60a228f]215 resolution = 1;
[069b80d]216 } else {
[6283cefb]217 resolution = (item->logical_maximum - item->logical_minimum) /
218 ((item->physical_maximum - item->physical_minimum) *
[069b80d]219 (usb_pow(10, (item->unit_exponent))));
[60a228f]220 }
[bda06a3]221
[6283cefb]222 int32_t value = 0;
223
224 /* First, skip all bytes we don't care */
225 data += item->offset / 8;
226
227 int bits = item->size;
228 int taken = 0;
229
230 /* Than we take the higher bits from the LSB */
231 const unsigned bit_offset = item->offset % 8;
232 const int lsb_bits = min(bits, 8);
233
234 value |= (*data >> bit_offset) & BIT_RRANGE(uint8_t, lsb_bits);
235 bits -= lsb_bits;
236 taken += lsb_bits;
237 data++;
238
239 /* Then there may be bytes, which we take as a whole. */
240 while (bits > 8) {
241 value |= *data << taken;
242 taken += 8;
243 bits -= 8;
244 data++;
245 }
246
247 /* And, finally, lower bits from HSB. */
248 if (bits > 0) {
249 value |= (*data & BIT_RRANGE(uint8_t, bits)) << taken;
[175ad13e]250 }
[fad14d7]251
[069b80d]252 if ((item->logical_minimum < 0) || (item->logical_maximum < 0)) {
[1553cbf]253 value = USB_HID_UINT32_TO_INT32(value, item->size);
[fad14d7]254 }
255
[069b80d]256 return (int) (((value - item->logical_minimum) / resolution) +
257 item->physical_minimum);
[c7a2e7e]258}
[0bd4810c]259
[a76b01b4]260
[5499a8b]261/* OUTPUT API */
[57d9c05e]262
[a694a58]263/**
264 * Allocates output report buffer for output report
[57d9c05e]265 *
[a694a58]266 * @param parser Report parsed structure
267 * @param size Size of returned buffer
268 * @param report_id Report id of created output report
269 * @return Returns allocated output buffer for specified output
[57d9c05e]270 */
[5499a8b]271uint8_t *usb_hid_report_output(usb_hid_report_t *report, size_t *size,
[069b80d]272 uint8_t report_id)
[57d9c05e]273{
[069b80d]274 if (report == NULL) {
[57d9c05e]275 *size = 0;
276 return NULL;
277 }
278
[175ad13e]279 usb_hid_report_description_t *report_des = NULL;
[b72efe8]280
[feeac0d]281 list_foreach(report->reports, reports_link,
[1e371cf]282 usb_hid_report_description_t, rdes) {
283 if ((rdes->report_id == report_id) &&
284 (rdes->type == USB_HID_REPORT_TYPE_OUTPUT)) {
285 report_des = rdes;
[a694a58]286 break;
[c156c2d]287 }
[175ad13e]288 }
[841e6e5]289
[069b80d]290 if (report_des == NULL) {
[175ad13e]291 *size = 0;
292 return NULL;
[069b80d]293 } else {
294 *size = (report_des->bit_length + (8 - 1)) / 8;
[175ad13e]295 uint8_t *ret = malloc((*size) * sizeof(uint8_t));
296 memset(ret, 0, (*size) * sizeof(uint8_t));
297 return ret;
[57d9c05e]298 }
299}
300
301
302/** Frees output report buffer
303 *
304 * @param output Output report buffer
[a694a58]305 * @return void
[57d9c05e]306 */
307void usb_hid_report_output_free(uint8_t *output)
308{
[069b80d]309 if (output != NULL) {
310 free(output);
[57d9c05e]311 }
312}
313
[175ad13e]314/** Makes the output report buffer for data given in the report structure
[57d9c05e]315 *
[a694a58]316 * @param parser Opaque report parser structure
317 * @param path Usage path specifing which parts of output will be set
318 * @param flags Usage path structure comparison flags
319 * @param buffer Output buffer
320 * @param size Size of output buffer
321 * @return Error code
[57d9c05e]322 */
[5499a8b]323int usb_hid_report_output_translate(usb_hid_report_t *report,
[069b80d]324 uint8_t report_id, uint8_t *buffer, size_t size)
[57d9c05e]325{
[069b80d]326 int32_t value = 0;
[841e6e5]327 int offset;
328 int length;
329 int32_t tmp_value;
[07525cd]330
[069b80d]331 if (report == NULL) {
[57d9c05e]332 return EINVAL;
333 }
334
[069b80d]335 if (report->use_report_ids != 0) {
[feeac0d]336 buffer[0] = report_id;
[bda06a3]337 }
338
[175ad13e]339 usb_hid_report_description_t *report_des;
[069b80d]340 report_des = usb_hid_report_find_description(report, report_id,
341 USB_HID_REPORT_TYPE_OUTPUT);
[07525cd]342
[069b80d]343 if (report_des == NULL) {
[175ad13e]344 return EINVAL;
345 }
[57d9c05e]346
[feeac0d]347 list_foreach(report_des->report_items, ritems_link,
348 usb_hid_report_field_t, report_item) {
[574f276]349 value = usb_hid_translate_data_reverse(report_item,
[069b80d]350 report_item->value);
[841e6e5]351
[574f276]352 offset = report_des->bit_length - report_item->offset - 1;
353 length = report_item->size;
[07525cd]354
[a1732929]355 usb_log_debug("\ttranslated value: %x", value);
[841e6e5]356
[069b80d]357 if ((offset / 8) == ((offset + length - 1) / 8)) {
358 if (((size_t) (offset / 8) >= size) ||
359 ((size_t) (offset + length - 1) / 8) >= size) {
[9be8669]360 break; // TODO ErrorCode
[841e6e5]361 }
[069b80d]362 size_t shift = 8 - offset % 8 - length;
[feeac0d]363 value = value << shift;
[069b80d]364 value = value & (((1 << length) - 1) << shift);
[07525cd]365
[9be8669]366 uint8_t mask = 0;
367 mask = 0xff - (((1 << length) - 1) << shift);
[069b80d]368 buffer[offset / 8] = (buffer[offset / 8] & mask) |
369 value;
370 } else {
[9be8669]371 int i = 0;
372 uint8_t mask = 0;
[069b80d]373 for (i = (offset / 8);
374 i <= ((offset + length - 1) / 8); i++) {
375 if (i == (offset / 8)) {
[9be8669]376 tmp_value = value;
[f3b39b4]377 tmp_value = tmp_value &
[069b80d]378 ((1 << (8 - (offset % 8))) - 1);
[f3b39b4]379
[069b80d]380 tmp_value = tmp_value << (offset % 8);
[07525cd]381
[069b80d]382 mask = ~(((1 << (8 - (offset % 8))) - 1)
383 << (offset % 8));
[f3b39b4]384
385 buffer[i] = (buffer[i] & mask) |
[069b80d]386 tmp_value;
387 } else if (i == ((offset + length - 1) / 8)) {
[07525cd]388
[f3b39b4]389 value = value >> (length -
[069b80d]390 ((offset + length) % 8));
[f3b39b4]391
392 value = value & ((1 << (length -
[069b80d]393 ((offset + length) % 8))) - 1);
[07525cd]394
[f3b39b4]395 mask = (1 << (length -
[069b80d]396 ((offset + length) % 8))) - 1;
[f3b39b4]397
[9be8669]398 buffer[i] = (buffer[i] & mask) | value;
[069b80d]399 } else {
400 buffer[i] = value & (0xff << i);
[175ad13e]401 }
[841e6e5]402 }
[9be8669]403 }
[841e6e5]404
[069b80d]405 /* reset value */
[cfbbe1d3]406 report_item->value = 0;
[57d9c05e]407 }
[07525cd]408
[57d9c05e]409 return EOK;
410}
411
[a76b01b4]412
[841e6e5]413/**
[a694a58]414 * Translate given data for putting them into the outoput report
415 * @param item Report item structure
416 * @param value Value to translate
417 * @return ranslated value
[841e6e5]418 */
[5499a8b]419uint32_t usb_hid_translate_data_reverse(usb_hid_report_field_t *item,
[069b80d]420 int value)
[841e6e5]421{
[069b80d]422 int ret = 0;
[841e6e5]423 int resolution;
424
[069b80d]425 if (USB_HID_ITEM_FLAG_CONSTANT(item->item_flags)) {
[4172db4a]426 return item->logical_minimum;
[70a71e5]427 }
428
[069b80d]429 if ((item->physical_minimum == 0) && (item->physical_maximum == 0)) {
[cfbbe1d3]430 item->physical_minimum = item->logical_minimum;
431 item->physical_maximum = item->logical_maximum;
432 }
433
[069b80d]434 /* variable item */
435 if (item->physical_maximum == item->physical_minimum) {
[9be8669]436 resolution = 1;
[069b80d]437 } else {
[9be8669]438 resolution = (item->logical_maximum - item->logical_minimum) /
439 ((item->physical_maximum - item->physical_minimum) *
[069b80d]440 (usb_pow(10, (item->unit_exponent))));
[841e6e5]441 }
442
[5499a8b]443 ret = ((value - item->physical_minimum) * resolution) +
[069b80d]444 item->logical_minimum;
[5499a8b]445
[069b80d]446 usb_log_debug("\tvalue(%x), resolution(%x), phymin(%x) logmin(%x), "
447 "ret(%x)\n", value, resolution, item->physical_minimum,
448 item->logical_minimum, ret);
[9be8669]449
[069b80d]450 if ((item->logical_minimum < 0) || (item->logical_maximum < 0)) {
[1553cbf]451 return USB_HID_INT32_TO_UINT32(ret, item->size);
452 }
[069b80d]453
454 return (int32_t) 0 + ret;
[841e6e5]455}
456
[a76b01b4]457
[5499a8b]458/**
459 * Clones given state table
460 *
461 * @param item State table to clone
462 * @return Pointer to the cloned item
463 */
464usb_hid_report_item_t *usb_hid_report_item_clone(
[069b80d]465 const usb_hid_report_item_t *item)
[64dbc83]466{
467 usb_hid_report_item_t *new_report_item;
468
[069b80d]469 if (!(new_report_item = malloc(sizeof(usb_hid_report_item_t)))) {
[64dbc83]470 return NULL;
471 }
472 memcpy(new_report_item,item, sizeof(usb_hid_report_item_t));
473 link_initialize(&(new_report_item->link));
474
475 return new_report_item;
476}
477
[a76b01b4]478
[5499a8b]479/**
480 * Function for sequence walking through the report. Returns next field in the
481 * report or the first one when no field is given.
482 *
483 * @param report Searched report structure
484 * @param field Current field. If NULL is given, the first one in the report
485 * is returned. Otherwise the next one i nthe list is returned.
486 * @param path Usage path specifying which fields wa are interested in.
487 * @param flags Flags defining mode of usage paths comparison
488 * @param type Type of report we search.
489 * @retval NULL if no field is founded
490 * @retval Pointer to the founded report structure when founded
491 */
[e50cd7f]492usb_hid_report_field_t *usb_hid_report_get_sibling(usb_hid_report_t *report,
[069b80d]493 usb_hid_report_field_t *field, usb_hid_report_path_t *path, int flags,
494 usb_hid_report_type_t type)
[e50cd7f]495{
[f3b39b4]496 usb_hid_report_description_t *report_des =
[069b80d]497 usb_hid_report_find_description(report, path->report_id, type);
[5499a8b]498
[e50cd7f]499 link_t *field_it;
500
[069b80d]501 if (report_des == NULL) {
[e50cd7f]502 return NULL;
503 }
504
[069b80d]505 if (field == NULL) {
[b72efe8]506 field_it = report_des->report_items.head.next;
[069b80d]507 } else {
[b72efe8]508 field_it = field->ritems_link.next;
[e50cd7f]509 }
510
[069b80d]511 while (field_it != &report_des->report_items.head) {
[f3b39b4]512 field = list_get_instance(field_it, usb_hid_report_field_t,
[069b80d]513 ritems_link);
[f3b39b4]514
[069b80d]515 if (USB_HID_ITEM_FLAG_CONSTANT(field->item_flags) == 0) {
516 usb_hid_report_path_append_item(field->collection_path,
517 field->usage_page, field->usage);
[5499a8b]518
[069b80d]519 if (usb_hid_report_compare_usage_path(
520 field->collection_path, path, flags) == EOK) {
[f3b39b4]521 usb_hid_report_remove_last_item(
[069b80d]522 field->collection_path);
[c7c0984a]523 return field;
524 }
[069b80d]525 usb_hid_report_remove_last_item(field->collection_path);
[e50cd7f]526 }
527 field_it = field_it->next;
528 }
529
530 return NULL;
531}
[cfbbe1d3]532
[a76b01b4]533
[5499a8b]534/**
[1d10ca1]535 * Returns next report_id of report of specified type. If zero is given than
536 * first report_id of specified type is returned (0 is not legal value for
537 * repotr_id)
[5499a8b]538 *
[1d10ca1]539 * @param report_id Current report_id, 0 if there is no current report_id
[5499a8b]540 * @param type Type of searched report
541 * @param report Report structure inwhich we search
542 * @retval 0 if report structure is null or there is no specified report
543 * @retval report_id otherwise
544 */
[069b80d]545uint8_t usb_hid_get_next_report_id(usb_hid_report_t *report, uint8_t report_id,
546 usb_hid_report_type_t type)
[cfbbe1d3]547{
[069b80d]548 if (report == NULL) {
[cfbbe1d3]549 return 0;
550 }
551
552 usb_hid_report_description_t *report_des;
553 link_t *report_it;
554
[069b80d]555 if (report_id > 0) {
[0dd3e49]556 report_des = usb_hid_report_find_description(report, report_id,
[069b80d]557 type);
558 if (report_des == NULL) {
[0dd3e49]559 return 0;
[069b80d]560 } else {
[b72efe8]561 report_it = report_des->reports_link.next;
[0dd3e49]562 }
[069b80d]563 } else {
[b72efe8]564 report_it = report->reports.head.next;
[cfbbe1d3]565 }
566
[069b80d]567 while (report_it != &report->reports.head) {
[1d10ca1]568 report_des = list_get_instance(report_it,
[069b80d]569 usb_hid_report_description_t, reports_link);
[5499a8b]570
[069b80d]571 if (report_des->type == type) {
[cfbbe1d3]572 return report_des->report_id;
573 }
[0c904a3]574
575 report_it = report_it->next;
[cfbbe1d3]576 }
577
578 return 0;
579}
580
[a76b01b4]581
[5499a8b]582/**
583 * Reset all local items in given state table
584 *
585 * @param report_item State table containing current state of report
586 * descriptor parsing
587 *
588 * @return void
589 */
[2020927]590void usb_hid_report_reset_local_items(usb_hid_report_item_t *report_item)
591{
[069b80d]592 if (report_item == NULL) {
[2020927]593 return;
594 }
595
596 report_item->usages_count = 0;
597 memset(report_item->usages, 0, USB_HID_MAX_USAGES);
598
599 report_item->extended_usage_page = 0;
600 report_item->usage_minimum = 0;
601 report_item->usage_maximum = 0;
602 report_item->designator_index = 0;
603 report_item->designator_minimum = 0;
604 report_item->designator_maximum = 0;
605 report_item->string_index = 0;
606 report_item->string_minimum = 0;
607 report_item->string_maximum = 0;
608}
[069b80d]609
[976f546]610/**
611 * @}
[c7a2e7e]612 */
Note: See TracBrowser for help on using the repository browser.