HelenOS sources
This source file includes following definitions.
- usb_pow
- usb_hid_report_size
- usb_hid_report_byte_size
- usb_hid_parse_report
- usb_hid_translate_data
- usb_hid_report_output
- usb_hid_report_output_free
- usb_hid_report_output_translate
- usb_hid_translate_data_reverse
- usb_hid_report_item_clone
- usb_hid_report_get_sibling
- usb_hid_get_next_report_id
- usb_hid_report_reset_local_items
#include <usb/hid/hidparser.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <mem.h>
#include <usb/debug.h>
#include <assert.h>
#include <bitops.h>
#include <macros.h>
uint32_t usb_hid_report_tag_data_uint32(const uint8_t *data, size_t size);
int usb_hid_translate_data(usb_hid_report_field_t *item, const uint8_t *data);
uint32_t usb_hid_translate_data_reverse(usb_hid_report_field_t *item,
int32_t value);
static int usb_pow(int a, int b)
{
switch (b) {
case 0:
return 1;
break;
case 1:
return a;
break;
default:
return a * usb_pow(a, b - 1);
break;
}
}
size_t usb_hid_report_size(usb_hid_report_t *report, uint8_t report_id,
usb_hid_report_type_t type)
{
usb_hid_report_description_t *report_des;
if (report == NULL) {
return 0;
}
report_des = usb_hid_report_find_description(report, report_id, type);
if (report_des == NULL) {
return 0;
} else {
return report_des->item_length;
}
}
size_t usb_hid_report_byte_size(usb_hid_report_t *report, uint8_t report_id,
usb_hid_report_type_t type)
{
usb_hid_report_description_t *report_des;
if (report == NULL) {
return 0;
}
report_des = usb_hid_report_find_description(report, report_id, type);
if (report_des == NULL) {
return 0;
} else {
return ((report_des->bit_length + 7) / 8);
}
}
errno_t usb_hid_parse_report(const usb_hid_report_t *report, const uint8_t *data,
size_t size, uint8_t *report_id)
{
usb_hid_report_description_t *report_des;
usb_hid_report_type_t type = USB_HID_REPORT_TYPE_INPUT;
if (report == NULL) {
return EINVAL;
}
if (report->use_report_ids != 0) {
*report_id = data[0];
} else {
*report_id = 0;
}
report_des = usb_hid_report_find_description(report, *report_id,
type);
if (report_des == NULL) {
return EINVAL;
}
list_foreach(report_des->report_items, ritems_link,
usb_hid_report_field_t, item) {
if (USB_HID_ITEM_FLAG_CONSTANT(item->item_flags) == 0) {
if (USB_HID_ITEM_FLAG_VARIABLE(item->item_flags) == 0) {
item->value =
usb_hid_translate_data(item, data);
item->usage = USB_HID_EXTENDED_USAGE(
item->usages[item->value -
item->physical_minimum]);
item->usage_page =
USB_HID_EXTENDED_USAGE_PAGE(
item->usages[item->value -
item->physical_minimum]);
usb_hid_report_set_last_item(
item->collection_path,
USB_HID_TAG_CLASS_GLOBAL,
item->usage_page);
usb_hid_report_set_last_item(
item->collection_path,
USB_HID_TAG_CLASS_LOCAL, item->usage);
} else {
item->value = usb_hid_translate_data(item,
data);
}
}
}
return EOK;
}
int usb_hid_translate_data(usb_hid_report_field_t *item, const uint8_t *data)
{
if (item->size > 32) {
return 0;
}
if ((item->physical_minimum == 0) && (item->physical_maximum == 0)) {
item->physical_minimum = item->logical_minimum;
item->physical_maximum = item->logical_maximum;
}
int resolution;
if (item->physical_maximum == item->physical_minimum) {
resolution = 1;
} else {
resolution = (item->logical_maximum - item->logical_minimum) /
((item->physical_maximum - item->physical_minimum) *
(usb_pow(10, (item->unit_exponent))));
}
int32_t value = 0;
data += item->offset / 8;
int bits = item->size;
int taken = 0;
const unsigned bit_offset = item->offset % 8;
const int lsb_bits = min((unsigned)bits, 8 - bit_offset);
value |= (*data >> bit_offset) & BIT_RRANGE(uint8_t, lsb_bits);
bits -= lsb_bits;
taken += lsb_bits;
data++;
while (bits > 8) {
value |= *data << taken;
taken += 8;
bits -= 8;
data++;
}
if (bits > 0) {
value |= (*data & BIT_RRANGE(uint8_t, bits)) << taken;
}
if ((item->logical_minimum < 0) || (item->logical_maximum < 0)) {
value = USB_HID_UINT32_TO_INT32(value, item->size);
}
return (int) (((value - item->logical_minimum) / resolution) +
item->physical_minimum);
}
uint8_t *usb_hid_report_output(usb_hid_report_t *report, size_t *size,
uint8_t report_id)
{
if (report == NULL) {
*size = 0;
return NULL;
}
usb_hid_report_description_t *report_des = NULL;
list_foreach(report->reports, reports_link,
usb_hid_report_description_t, rdes) {
if ((rdes->report_id == report_id) &&
(rdes->type == USB_HID_REPORT_TYPE_OUTPUT)) {
report_des = rdes;
break;
}
}
if (report_des == NULL) {
*size = 0;
return NULL;
} else {
*size = (report_des->bit_length + (8 - 1)) / 8;
uint8_t *ret = malloc((*size) * sizeof(uint8_t));
memset(ret, 0, (*size) * sizeof(uint8_t));
return ret;
}
}
void usb_hid_report_output_free(uint8_t *output)
{
if (output != NULL) {
free(output);
}
}
errno_t usb_hid_report_output_translate(usb_hid_report_t *report,
uint8_t report_id, uint8_t *buffer, size_t size)
{
int32_t value = 0;
int offset;
int length;
int32_t tmp_value;
if (report == NULL) {
return EINVAL;
}
if (report->use_report_ids != 0) {
buffer[0] = report_id;
}
usb_hid_report_description_t *report_des;
report_des = usb_hid_report_find_description(report, report_id,
USB_HID_REPORT_TYPE_OUTPUT);
if (report_des == NULL) {
return EINVAL;
}
list_foreach(report_des->report_items, ritems_link,
usb_hid_report_field_t, report_item) {
value = usb_hid_translate_data_reverse(report_item,
report_item->value);
offset = report_des->bit_length - report_item->offset - 1;
length = report_item->size;
usb_log_debug("\ttranslated value: %x", value);
if ((offset / 8) == ((offset + length - 1) / 8)) {
if (((size_t) (offset / 8) >= size) ||
((size_t) (offset + length - 1) / 8) >= size) {
break;
}
size_t shift = 8 - offset % 8 - length;
value = value << shift;
value = value & (((1 << length) - 1) << shift);
uint8_t mask = 0;
mask = 0xff - (((1 << length) - 1) << shift);
buffer[offset / 8] = (buffer[offset / 8] & mask) |
value;
} else {
int i = 0;
uint8_t mask = 0;
for (i = (offset / 8);
i <= ((offset + length - 1) / 8); i++) {
if (i == (offset / 8)) {
tmp_value = value;
tmp_value = tmp_value &
((1 << (8 - (offset % 8))) - 1);
tmp_value = tmp_value << (offset % 8);
mask = ~(((1 << (8 - (offset % 8))) - 1) << (offset % 8));
buffer[i] = (buffer[i] & mask) |
tmp_value;
} else if (i == ((offset + length - 1) / 8)) {
value = value >> (length -
((offset + length) % 8));
value = value & ((1 << (length -
((offset + length) % 8))) - 1);
mask = (1 << (length -
((offset + length) % 8))) - 1;
buffer[i] = (buffer[i] & mask) | value;
} else {
buffer[i] = value & (0xff << i);
}
}
}
report_item->value = 0;
}
return EOK;
}
uint32_t usb_hid_translate_data_reverse(usb_hid_report_field_t *item,
int value)
{
int ret = 0;
int resolution;
if (USB_HID_ITEM_FLAG_CONSTANT(item->item_flags)) {
return item->logical_minimum;
}
if ((item->physical_minimum == 0) && (item->physical_maximum == 0)) {
item->physical_minimum = item->logical_minimum;
item->physical_maximum = item->logical_maximum;
}
if (item->physical_maximum == item->physical_minimum) {
resolution = 1;
} else {
resolution = (item->logical_maximum - item->logical_minimum) /
((item->physical_maximum - item->physical_minimum) *
(usb_pow(10, (item->unit_exponent))));
}
ret = ((value - item->physical_minimum) * resolution) +
item->logical_minimum;
usb_log_debug("\tvalue(%x), resolution(%x), phymin(%x) logmin(%x), "
"ret(%x)\n", value, resolution, item->physical_minimum,
item->logical_minimum, ret);
if ((item->logical_minimum < 0) || (item->logical_maximum < 0)) {
return USB_HID_INT32_TO_UINT32(ret, item->size);
}
return (int32_t) 0 + ret;
}
usb_hid_report_item_t *usb_hid_report_item_clone(
const usb_hid_report_item_t *item)
{
usb_hid_report_item_t *new_report_item;
if (!(new_report_item = malloc(sizeof(usb_hid_report_item_t)))) {
return NULL;
}
memcpy(new_report_item, item, sizeof(usb_hid_report_item_t));
link_initialize(&(new_report_item->link));
return new_report_item;
}
usb_hid_report_field_t *usb_hid_report_get_sibling(usb_hid_report_t *report,
usb_hid_report_field_t *field, usb_hid_report_path_t *path, int flags,
usb_hid_report_type_t type)
{
usb_hid_report_description_t *report_des =
usb_hid_report_find_description(report, path->report_id, type);
link_t *field_it;
if (report_des == NULL) {
return NULL;
}
if (field == NULL) {
field_it = report_des->report_items.head.next;
} else {
field_it = field->ritems_link.next;
}
while (field_it != &report_des->report_items.head) {
field = list_get_instance(field_it, usb_hid_report_field_t,
ritems_link);
if (USB_HID_ITEM_FLAG_CONSTANT(field->item_flags) == 0) {
usb_hid_report_path_append_item(field->collection_path,
field->usage_page, field->usage);
if (usb_hid_report_compare_usage_path(
field->collection_path, path, flags) == 0) {
usb_hid_report_remove_last_item(
field->collection_path);
return field;
}
usb_hid_report_remove_last_item(field->collection_path);
}
field_it = field_it->next;
}
return NULL;
}
uint8_t usb_hid_get_next_report_id(usb_hid_report_t *report, uint8_t report_id,
usb_hid_report_type_t type)
{
if (report == NULL) {
return 0;
}
usb_hid_report_description_t *report_des;
link_t *report_it;
if (report_id > 0) {
report_des = usb_hid_report_find_description(report, report_id,
type);
if (report_des == NULL) {
return 0;
} else {
report_it = report_des->reports_link.next;
}
} else {
report_it = report->reports.head.next;
}
while (report_it != &report->reports.head) {
report_des = list_get_instance(report_it,
usb_hid_report_description_t, reports_link);
if (report_des->type == type) {
return report_des->report_id;
}
report_it = report_it->next;
}
return 0;
}
void usb_hid_report_reset_local_items(usb_hid_report_item_t *report_item)
{
if (report_item == NULL) {
return;
}
report_item->usages_count = 0;
memset(report_item->usages, 0, sizeof(report_item->usages));
report_item->extended_usage_page = 0;
report_item->usage_minimum = 0;
report_item->usage_maximum = 0;
report_item->designator_index = 0;
report_item->designator_minimum = 0;
report_item->designator_maximum = 0;
report_item->string_index = 0;
report_item->string_minimum = 0;
report_item->string_maximum = 0;
}
HelenOS homepage, sources at GitHub