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

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

HID parser refactoring

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