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

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

Development changes merged

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