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

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

some little changes

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