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

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

report byte size function considers the report_id field

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