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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6c5abf9 was b72efe8, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Separate list_t typedef from link_t (user-space part).

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