#include <sys/usb/usba/usbai_version.h>
#include <sys/usb/usba.h>
#include <sys/usb/clients/hid/hid.h>
#include <sys/usb/clients/hidparser/hidparser.h>
#include <sys/usb/clients/hidparser/hid_parser_driver.h>
#include <sys/usb/clients/hidparser/hidparser_impl.h>
uint_t hparser_errmask = (uint_t)PRINT_MASK_ALL;
uint_t hparser_errlevel = (uint_t)USB_LOG_L1;
static usb_log_handle_t hparser_log_handle;
char *items[500];
extern struct mod_ops mod_miscops;
static struct modlmisc modlmisc = {
&mod_miscops,
"HID Parser"
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, NULL
};
int
_init(void)
{
int rval = mod_install(&modlinkage);
if (rval == 0) {
hparser_log_handle = usb_alloc_log_hdl(NULL, "hidparser",
&hparser_errlevel, &hparser_errmask, NULL, 0);
}
return (rval);
}
int
_fini()
{
int rval = mod_remove(&modlinkage);
if (rval == 0) {
usb_free_log_hdl(hparser_log_handle);
}
return (rval);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static void hidparser_scan(hidparser_tok_t *);
static int hidparser_Items(hidparser_tok_t *);
static int hidparser_LocalItem(hidparser_tok_t *);
static int hidparser_GlobalItem(hidparser_tok_t *);
static int hidparser_ItemList(entity_item_t **,
hidparser_tok_t *);
static int hidparser_ReportDescriptor(entity_item_t **,
hidparser_tok_t *);
static int hidparser_ReportDescriptorDash(entity_item_t **,
hidparser_tok_t *);
static int hidparser_MainItem(entity_item_t **,
hidparser_tok_t *);
static void hidparser_free_attribute_list(
entity_attribute_t *);
static entity_item_t *hidparser_allocate_entity(hidparser_tok_t *);
static void hidparser_add_attribute(hidparser_tok_t *);
static entity_attribute_t *hidparser_cp_attribute_list(
entity_attribute_t *);
static entity_attribute_t *hidparser_find_attribute_end(
entity_attribute_t *);
static entity_attribute_t *hidparser_alloc_attrib_list(int);
static void hidparser_report_err(int, int,
int, int, char *);
static int hidparser_isvalid_item(int);
static entity_attribute_t *hidparser_lookup_attribute(entity_item_t *,
int);
static void hidparser_global_err_check(entity_item_t *);
static void hidparser_local_err_check(entity_item_t *);
static void hidparser_mainitem_err_check(entity_item_t *);
static unsigned int hidparser_find_unsigned_val(
entity_attribute_t *);
static int hidparser_find_signed_val(
entity_attribute_t *);
static void hidparser_check_correspondence(
entity_item_t *, int, int, int,
int, char *, char *);
static void hidparser_check_minmax_val(entity_item_t *,
int, int, int, int);
static void hidparser_check_minmax_val_signed(
entity_item_t *,
int, int, int, int);
static void hidparser_error_delim(entity_item_t *, int);
static int hidparser_get_usage_attribute_report_des(
entity_item_t *,
uint32_t, uint32_t, uint32_t,
uint32_t, uint32_t, int32_t *);
static int hidparser_get_packet_size_report_des(
entity_item_t *, uint32_t, uint32_t,
uint32_t *);
static int hidparser_get_main_item_data_descr_main(
entity_item_t *, uint32_t,
uint32_t, uint32_t, uint32_t,
uint32_t *);
static void hidparser_print_entity(
entity_item_t *entity,
int indent_level);
static void hidparser_print_this_attribute(
entity_attribute_t *attribute,
char *ident_space);
static int hidparser_main(unsigned char *, size_t,
entity_item_t **);
static void hidparser_initialize_items();
static void hidparser_free_report_descr_handle(
entity_item_t *);
static int hidparser_print_report_descr_handle(
entity_item_t *handle,
int indent_level);
static int hidparser_get_usage_list_in_order_internal(
entity_item_t *parse_handle,
uint_t collection_usage,
uint_t report_id,
uint_t main_item_type,
hidparser_rpt_t *rpt);
static void hidparser_fill_usage_info(
hidparser_usage_info_t *ui,
entity_attribute_t *attribute);
static int hidparser_get_report_id_list_internal(
entity_item_t *parser_handle,
uint_t main_item_type,
hidparser_report_id_list_t *id_lst);
static hidparser_terminal_t first_Items[] = {
R_ITEM_USAGE_PAGE, R_ITEM_LOGICAL_MINIMUM, R_ITEM_LOGICAL_MAXIMUM, \
R_ITEM_PHYSICAL_MINIMUM, R_ITEM_PHYSICAL_MAXIMUM, R_ITEM_UNIT, \
R_ITEM_EXPONENT, R_ITEM_REPORT_SIZE, R_ITEM_REPORT_COUNT, \
R_ITEM_REPORT_ID, \
R_ITEM_USAGE, R_ITEM_USAGE_MIN, R_ITEM_USAGE_MAX, \
R_ITEM_DESIGNATOR_INDEX, \
R_ITEM_DESIGNATOR_MIN, R_ITEM_STRING_INDEX, R_ITEM_STRING_MIN, \
R_ITEM_STRING_MAX, \
R_ITEM_SET_DELIMITER, \
0
};
static hidparser_terminal_t *hid_first_list[] = {
first_Items
};
int
hidparser_parse_report_descriptor(unsigned char *descriptor, size_t size,
usb_hid_descr_t *hid_descriptor, hidparser_handle_t *parse_handle)
{
int error = 0;
entity_item_t *root;
hidparser_initialize_items();
error = hidparser_main(descriptor, size, &root);
if (error != HIDPARSER_SUCCESS) {
return (HIDPARSER_FAILURE);
} else {
*parse_handle = kmem_zalloc(
sizeof (hidparser_handle), KM_SLEEP);
(*parse_handle)->hidparser_handle_hid_descr = hid_descriptor;
(*parse_handle)->hidparser_handle_parse_tree = root;
return (HIDPARSER_SUCCESS);
}
}
int
hidparser_free_report_descriptor_handle(hidparser_handle_t parse_handle)
{
if (parse_handle != NULL) {
hidparser_free_report_descr_handle(
parse_handle->hidparser_handle_parse_tree);
if (parse_handle != NULL) {
kmem_free(parse_handle, sizeof (hidparser_handle));
}
}
return (HIDPARSER_SUCCESS);
}
int
hidparser_get_country_code(hidparser_handle_t parser_handle,
uint16_t *country_code)
{
if ((parser_handle == NULL) ||
(parser_handle->hidparser_handle_hid_descr == NULL)) {
return (HIDPARSER_FAILURE);
}
*country_code =
parser_handle->hidparser_handle_hid_descr->bCountryCode;
return (HIDPARSER_SUCCESS);
}
int
hidparser_get_packet_size(hidparser_handle_t parser_handle,
uint_t report_id, uint_t main_item_type, uint_t *size)
{
if ((parser_handle == NULL) || (parser_handle->
hidparser_handle_parse_tree == NULL)) {
return (HIDPARSER_FAILURE);
}
*size = 0;
return (hidparser_get_packet_size_report_des(
parser_handle->hidparser_handle_parse_tree,
report_id, main_item_type, size));
}
int
hidparser_get_packet_size_report_des(entity_item_t *parser_handle,
uint32_t report_id, uint32_t main_item_type, uint32_t *size)
{
entity_item_t *current = parser_handle;
entity_attribute_t *attribute;
uint32_t temp;
uchar_t foundsize, foundcount, foundreportid, right_report_id;
foundsize = 0;
foundcount = 0;
right_report_id = 0;
while (current) {
if (current->entity_item_type == R_ITEM_COLLECTION) {
(void) hidparser_get_packet_size_report_des(
current->info.child, report_id, main_item_type,
size);
} else if (current->entity_item_type == main_item_type) {
temp = 1;
foundsize = 0;
foundcount = 0;
foundreportid = 0;
attribute = current->entity_item_attributes;
while (attribute != NULL) {
if (attribute->entity_attribute_tag ==
R_ITEM_REPORT_ID) {
foundreportid = 1;
if ((attribute->
entity_attribute_value[0]) ==
report_id) {
right_report_id = 1;
}
} else if (attribute->entity_attribute_tag ==
R_ITEM_REPORT_SIZE) {
foundsize = 1;
temp *= hidparser_find_unsigned_val(
attribute);
if (foundcount == 1) {
if (report_id &&
right_report_id) {
break;
}
}
} else if (attribute->entity_attribute_tag ==
R_ITEM_REPORT_COUNT) {
foundcount = 1;
temp *= hidparser_find_unsigned_val(
attribute);
if (foundsize == 1) {
if (report_id &&
right_report_id) {
break;
}
}
}
attribute = attribute->entity_attribute_next;
}
if (foundreportid) {
if (right_report_id) {
*size = *size + temp;
}
} else if (report_id == HID_REPORT_ID_UNDEFINED) {
*size = *size + temp;
}
right_report_id = 0;
}
current = current->entity_item_right_sibling;
}
return (HIDPARSER_SUCCESS);
}
int
hidparser_get_usage_attribute(hidparser_handle_t parser_handle,
uint_t report_id, uint_t main_item_type, uint_t usage_page,
uint_t usage_id, uint_t usage_attribute, int *usage_attribute_value)
{
return (hidparser_get_usage_attribute_report_des(
parser_handle->hidparser_handle_parse_tree,
report_id, main_item_type, usage_page,
usage_id, usage_attribute, usage_attribute_value));
}
static int
hidparser_get_usage_attribute_report_des(entity_item_t *parser_handle,
uint_t report_id, uint_t main_item_type, uint_t usage_page,
uint_t usage_id, uint_t usage_attribute, int *usage_attribute_value)
{
entity_item_t *current = parser_handle;
entity_attribute_t *attribute;
uchar_t found_page, found_ret_value, found_usage_id;
uchar_t foundreportid, right_report_id;
uint32_t usage;
short attvalue;
found_page = 0;
found_ret_value = 0;
found_usage_id = 0;
foundreportid = 0;
right_report_id = 0;
while (current) {
if (usage_id == HID_USAGE_UNDEFINED) {
found_usage_id = 1;
}
if (current->entity_item_type == R_ITEM_COLLECTION) {
if (hidparser_get_usage_attribute_report_des(
current->info.child, report_id, main_item_type,
usage_page, usage_id, usage_attribute,
usage_attribute_value) ==
HIDPARSER_SUCCESS) {
return (HIDPARSER_SUCCESS);
}
} else if (current->entity_item_type == main_item_type) {
attribute = current->entity_item_attributes;
while (attribute != NULL) {
if (attribute->entity_attribute_tag ==
R_ITEM_USAGE) {
usage = hidparser_find_unsigned_val(
attribute);
if (usage_id == HID_USAGE_ID(usage)) {
found_usage_id = 1;
} else {
if (usage_id ==
HID_USAGE_UNDEFINED) {
found_usage_id = 0;
}
}
if (found_usage_id && attribute->
entity_attribute_length == 3) {
if (HID_USAGE_PAGE(usage) &&
HID_USAGE_PAGE(usage) ==
usage_page) {
found_page = 1;
} else {
found_usage_id = 0;
}
}
} else if (attribute->entity_attribute_tag ==
R_ITEM_USAGE_PAGE) {
if (attribute->
entity_attribute_value[0] ==
usage_page) {
found_page = 1;
}
} else if (attribute->entity_attribute_tag ==
R_ITEM_REPORT_ID) {
foundreportid = 1;
if (attribute->
entity_attribute_value[0] ==
report_id) {
right_report_id = 1;
}
}
if (attribute->entity_attribute_tag ==
usage_attribute) {
found_ret_value = 1;
*usage_attribute_value =
*attribute->entity_attribute_value;
if (attribute->
entity_attribute_length == 2) {
attvalue =
(attribute->
entity_attribute_value[0] &
0xff) |
(attribute->
entity_attribute_value[1] <<
8);
*usage_attribute_value =
attvalue;
}
}
attribute = attribute->entity_attribute_next;
}
if (found_usage_id && found_page && found_ret_value) {
if (foundreportid) {
if (right_report_id) {
return (HIDPARSER_SUCCESS);
} else if (report_id ==
HID_REPORT_ID_UNDEFINED) {
return (HIDPARSER_SUCCESS);
}
} else {
return (HIDPARSER_SUCCESS);
}
}
}
if (current->entity_item_right_sibling != NULL) {
current = current->entity_item_right_sibling;
found_usage_id = found_page = found_ret_value = 0;
right_report_id = 0;
} else {
break;
}
}
*usage_attribute_value = 0;
return (HIDPARSER_NOT_FOUND);
}
int
hidparser_get_main_item_data_descr(hidparser_handle_t parser_handle,
uint_t report_id, uint_t main_item_type, uint_t usage_page,
uint_t usage_id, uint_t *main_item_descr_value)
{
return hidparser_get_main_item_data_descr_main(
parser_handle->hidparser_handle_parse_tree,
report_id, main_item_type, usage_page, usage_id,
main_item_descr_value);
}
static int
hidparser_get_main_item_data_descr_main(entity_item_t *parser_handle,
uint_t report_id, uint_t main_item_type, uint_t usage_page,
uint_t usage_id, uint_t *main_item_descr_value)
{
entity_item_t *current = parser_handle;
entity_attribute_t *attribute;
uchar_t found_page, found_usage_id;
uchar_t foundreportid, right_report_id;
uint32_t usage;
found_page = 0;
found_usage_id = 0;
foundreportid = 0;
right_report_id = 0;
while (current) {
if (usage_id == HID_USAGE_UNDEFINED) {
found_usage_id = 1;
}
if (current->entity_item_type == R_ITEM_COLLECTION) {
if (hidparser_get_main_item_data_descr_main(
current->info.child, report_id, main_item_type,
usage_page, usage_id, main_item_descr_value) ==
HIDPARSER_SUCCESS) {
return (HIDPARSER_SUCCESS);
}
} else if (current->entity_item_type == main_item_type) {
attribute = current->entity_item_attributes;
if (report_id == HID_REPORT_ID_UNDEFINED) {
foundreportid = right_report_id = 1;
}
while (attribute != NULL) {
if (attribute->entity_attribute_tag ==
R_ITEM_USAGE) {
usage = hidparser_find_unsigned_val(
attribute);
if (usage_id == HID_USAGE_ID(usage)) {
found_usage_id = 1;
if (attribute->
entity_attribute_length ==
3) {
if (HID_USAGE_PAGE(
usage) &&
HID_USAGE_PAGE(
usage) ==
usage_page) {
found_page = 1;
} else {
found_usage_id = 0;
}
}
if (found_usage_id &&
found_page &&
foundreportid &&
right_report_id) {
*main_item_descr_value =
current->
entity_item_params[0];
break;
}
}
} else if ((attribute->entity_attribute_tag ==
R_ITEM_USAGE_PAGE) &&
(attribute->entity_attribute_value[0] ==
usage_page)) {
found_page = 1;
if (found_usage_id && foundreportid &&
right_report_id) {
*main_item_descr_value =
current->
entity_item_params[0];
break;
}
} else if (attribute->entity_attribute_tag ==
R_ITEM_REPORT_ID) {
foundreportid = 1;
if (attribute->
entity_attribute_value[0] ==
report_id) {
right_report_id = 1;
} else {
break;
}
}
attribute = attribute->entity_attribute_next;
}
if (foundreportid) {
if (right_report_id) {
if (found_usage_id && found_page) {
return (HIDPARSER_SUCCESS);
}
}
}
}
if (current->entity_item_right_sibling != NULL) {
current = current->entity_item_right_sibling;
found_page = found_usage_id = right_report_id = 0;
} else {
break;
}
}
*main_item_descr_value = 0;
return (HIDPARSER_NOT_FOUND);
}
int
hidparser_lookup_usage_collection(hidparser_handle_t parse_handle,
uint_t lusage_page, uint_t lusage_id)
{
entity_item_t *current;
entity_attribute_t *attribute;
int found_usage_id = 0;
int found_page = 0;
uint32_t usage;
uint_t usage_page;
uint_t usage_id;
if ((parse_handle == NULL) ||
(parse_handle->hidparser_handle_parse_tree == NULL))
return (HIDPARSER_FAILURE);
current = parse_handle->hidparser_handle_parse_tree;
while (current != NULL) {
if (current->entity_item_type != R_ITEM_COLLECTION) {
current = current->entity_item_right_sibling;
continue;
}
attribute = current->entity_item_attributes;
found_usage_id = 0;
found_page = 0;
while (attribute != NULL) {
if (attribute->entity_attribute_tag == R_ITEM_USAGE) {
found_usage_id = 1;
usage = hidparser_find_unsigned_val(attribute);
usage_id = HID_USAGE_ID(usage);
if (attribute->entity_attribute_length == 3) {
if (HID_USAGE_PAGE(usage)) {
found_page = 1;
usage_page =
HID_USAGE_PAGE(usage);
}
}
if (found_page) {
goto check_usage;
}
} else if (attribute->entity_attribute_tag ==
R_ITEM_USAGE_PAGE) {
found_page = 1;
usage_page =
attribute->entity_attribute_value[0];
if (found_usage_id) {
goto check_usage;
}
}
attribute = attribute->entity_attribute_next;
}
check_usage:
if ((usage_page == lusage_page) && (usage_id == lusage_id))
return (HIDPARSER_SUCCESS);
else
current = current->entity_item_right_sibling;
}
return (HIDPARSER_FAILURE);
}
int
hidparser_get_top_level_collection_usage(hidparser_handle_t parse_handle,
uint_t *usage_page, uint_t *usage_id)
{
entity_item_t *current;
entity_attribute_t *attribute;
int found_usage_id = 0;
int found_page = 0;
uint32_t usage;
if ((parse_handle == NULL) ||
(parse_handle->hidparser_handle_parse_tree == NULL))
return (HIDPARSER_FAILURE);
current = parse_handle->hidparser_handle_parse_tree;
if (current->entity_item_type != R_ITEM_COLLECTION) {
return (HIDPARSER_FAILURE);
}
attribute = current->entity_item_attributes;
while (attribute != NULL) {
if (attribute->entity_attribute_tag == R_ITEM_USAGE) {
found_usage_id = 1;
usage = hidparser_find_unsigned_val(attribute);
*usage_id = HID_USAGE_ID(usage);
if (attribute->entity_attribute_length == 3) {
if (HID_USAGE_PAGE(usage)) {
found_page = 1;
*usage_page = HID_USAGE_PAGE(usage);
}
}
if (found_usage_id && found_page) {
return (HIDPARSER_SUCCESS);
}
} else if (attribute->entity_attribute_tag ==
R_ITEM_USAGE_PAGE) {
found_page = 1;
*usage_page = attribute->entity_attribute_value[0];
if (found_usage_id && found_page) {
return (HIDPARSER_SUCCESS);
}
}
attribute = attribute->entity_attribute_next;
}
return (HIDPARSER_FAILURE);
}
int
hidparser_get_usage_list_in_order(hidparser_handle_t parser_handle,
uint_t report_id, uint_t main_item_type, hidparser_rpt_t *rpt)
{
if ((parser_handle == NULL) ||
(parser_handle->hidparser_handle_parse_tree == NULL)) {
return (HIDPARSER_FAILURE);
}
rpt->no_of_usages = 0;
return (hidparser_get_usage_list_in_order_internal(
parser_handle->hidparser_handle_parse_tree, HID_USAGE_UNDEFINED,
report_id, main_item_type, rpt));
}
static int
hidparser_get_usage_list_in_order_internal(entity_item_t *parser_handle,
uint_t collection_usage, uint_t report_id, uint_t main_item_type,
hidparser_rpt_t *rpt)
{
entity_item_t *current = parser_handle;
entity_attribute_t *attribute;
uchar_t foundreportid, right_report_id, valid_usage;
uchar_t found_usage_min, found_usage_max, found_usage;
int i, j;
int rval;
uint32_t usage, usage_min, usage_max, usage_id[USAGE_MAX];
hidparser_usage_info_t *ui;
found_usage_min = 0;
found_usage_max = 0;
foundreportid = 0;
right_report_id = 0;
while (current) {
if (current->entity_item_type == R_ITEM_COLLECTION) {
valid_usage = 0;
attribute = current->entity_item_attributes;
while (attribute != NULL) {
if (attribute->entity_attribute_tag ==
R_ITEM_USAGE) {
usage = hidparser_find_unsigned_val(
attribute);
valid_usage = 1;
}
attribute = attribute->entity_attribute_next;
}
if (!valid_usage) {
usage = HID_USAGE_UNDEFINED;
}
rval = hidparser_get_usage_list_in_order_internal(
current->info.child, usage,
report_id, main_item_type, rpt);
if (rval != HIDPARSER_SUCCESS) {
return (rval);
}
} else if (current->entity_item_type == main_item_type) {
foundreportid = 0;
right_report_id = 0;
found_usage_min = 0;
found_usage_max = 0;
found_usage = 0;
valid_usage = 0;
attribute = current->entity_item_attributes;
while (attribute != NULL) {
switch (attribute->entity_attribute_tag) {
case R_ITEM_REPORT_ID:
foundreportid = 1;
if (attribute->
entity_attribute_value[0] ==
report_id) {
right_report_id = 1;
} else {
valid_usage = 1;
}
break;
case R_ITEM_USAGE:
if (found_usage >= USAGE_MAX) {
return (HIDPARSER_FAILURE);
}
usage = hidparser_find_unsigned_val(
attribute);
if (usage) {
usage_id[found_usage] = usage;
found_usage++;
}
break;
case R_ITEM_USAGE_MIN:
found_usage_min = 1;
usage_min = hidparser_find_unsigned_val(
attribute);
break;
case R_ITEM_USAGE_MAX:
found_usage_max = 1;
usage_max = hidparser_find_unsigned_val(
attribute);
break;
case R_ITEM_SET_DELIMITER:
do {
attribute = attribute->
entity_attribute_next;
} while (attribute->
entity_attribute_tag !=
R_ITEM_SET_DELIMITER);
break;
}
attribute = attribute->entity_attribute_next;
}
if (!foundreportid ||
(foundreportid && right_report_id)) {
for (j = 0; j < found_usage; j++) {
if (rpt->no_of_usages >= USAGE_MAX) {
return (HIDPARSER_FAILURE);
}
i = rpt->no_of_usages++;
ui = &(rpt->usage_descr[i]);
hidparser_fill_usage_info(ui,
current->entity_item_attributes);
ui->rptcnt /= found_usage;
ui->collection_usage = collection_usage;
ui->usage_id = HID_USAGE_ID(
usage_id[j]);
if (usage_id[j] >> 16) {
ui->usage_page =
HID_USAGE_PAGE(usage_id[j]);
}
rpt->report_id = report_id;
valid_usage = 1;
}
if (found_usage_min && found_usage_max) {
if (rpt->no_of_usages >= USAGE_MAX) {
return (HIDPARSER_FAILURE);
}
if (found_usage) {
ui->usage_min = HID_USAGE_ID(
usage_min);
ui->usage_max = HID_USAGE_ID(
usage_max);
} else {
i = rpt->no_of_usages++;
ui = &(rpt->usage_descr[i]);
hidparser_fill_usage_info(ui,
current->
entity_item_attributes);
ui->collection_usage =
collection_usage;
ui->usage_min = HID_USAGE_ID(
usage_min);
ui->usage_max = HID_USAGE_ID(
usage_max);
rpt->report_id = report_id;
valid_usage = 1;
}
if (usage_max >> 16) {
ui->usage_page =
HID_USAGE_PAGE(usage_max);
}
}
}
if (!valid_usage) {
if (rpt->no_of_usages >= USAGE_MAX) {
return (HIDPARSER_FAILURE);
}
i = rpt->no_of_usages++;
ui = &(rpt->usage_descr[i]);
hidparser_fill_usage_info(ui,
current->entity_item_attributes);
ui->collection_usage = collection_usage;
ui->usage_id = HID_USAGE_UNDEFINED;
rpt->report_id = report_id;
}
}
current = current->entity_item_right_sibling;
}
return (HIDPARSER_SUCCESS);
}
static void
hidparser_fill_usage_info(hidparser_usage_info_t *ui,
entity_attribute_t *attribute)
{
bzero(ui, sizeof (*ui));
while (attribute) {
switch (attribute->entity_attribute_tag) {
case R_ITEM_LOGICAL_MINIMUM:
ui->lmin = hidparser_find_signed_val(attribute);
break;
case R_ITEM_LOGICAL_MAXIMUM:
ui->lmax = hidparser_find_signed_val(attribute);
break;
case R_ITEM_REPORT_COUNT:
ui->rptcnt = hidparser_find_unsigned_val(attribute);
break;
case R_ITEM_REPORT_SIZE:
ui->rptsz = hidparser_find_unsigned_val(attribute);
break;
case R_ITEM_USAGE_PAGE:
ui->usage_page = hidparser_find_unsigned_val(attribute)
& 0xffff;
break;
}
attribute = attribute->entity_attribute_next;
}
}
int
hidparser_get_report_id_list(hidparser_handle_t parser_handle,
uint_t main_item_type, hidparser_report_id_list_t *report_id_list)
{
if ((parser_handle == NULL) ||
(parser_handle->hidparser_handle_parse_tree == NULL)) {
return (HIDPARSER_FAILURE);
}
report_id_list->no_of_report_ids = 0;
return (hidparser_get_report_id_list_internal(
parser_handle->hidparser_handle_parse_tree,
main_item_type, report_id_list));
}
int
hidparser_get_report_id_list_internal(entity_item_t *parser_handle,
uint_t main_item_type, hidparser_report_id_list_t *id_lst)
{
entity_item_t *current = parser_handle;
entity_attribute_t *attribute;
uint_t report_id = 0;
int i = 0;
int rval;
while (current) {
if (current->entity_item_type == R_ITEM_COLLECTION) {
rval = hidparser_get_report_id_list_internal(
current->info.child, main_item_type, id_lst);
if (rval != HIDPARSER_SUCCESS) {
return (rval);
}
} else if (current->entity_item_type == main_item_type) {
attribute = current->entity_item_attributes;
while (attribute != NULL) {
if (attribute->entity_attribute_tag ==
R_ITEM_REPORT_ID) {
report_id = attribute->
entity_attribute_value[0];
for (i = 0;
i < id_lst->no_of_report_ids;
i++) {
if (report_id == id_lst->
report_id[i]) {
break;
}
}
if (i >= id_lst->no_of_report_ids) {
if (i >= REPORT_ID_MAX) {
return
(HIDPARSER_FAILURE);
}
id_lst->report_id[i] =
report_id;
id_lst->no_of_report_ids++;
}
}
attribute = attribute->entity_attribute_next;
}
}
current = current->entity_item_right_sibling;
}
return (HIDPARSER_SUCCESS);
}
static int
hidparser_print_report_descr_handle(entity_item_t *handle, int indent_level)
{
entity_item_t *current = handle;
while (current) {
if (current->info.child) {
hidparser_print_entity(current, indent_level);
(void) hidparser_print_report_descr_handle(
current->info.child, indent_level+1);
} else {
hidparser_print_entity(current, indent_level);
}
current = current->entity_item_right_sibling;
}
return (HIDPARSER_SUCCESS);
}
#define SPACE_PER_LEVEL 5
static void
hidparser_print_entity(entity_item_t *entity, int indent_level)
{
char indent_space[256];
int count;
entity_attribute_t *attr;
indent_level *= SPACE_PER_LEVEL;
for (count = 0; indent_level--; count++)
indent_space[count] = ' ';
indent_space[count] = 0;
attr = entity->entity_item_attributes;
while (attr) {
hidparser_print_this_attribute(attr, indent_space);
attr = attr->entity_attribute_next;
}
USB_DPRINTF_L3(PRINT_MASK_ALL, hparser_log_handle, "%s%s(0x%x)",
indent_space, items[entity->entity_item_type],
(entity->entity_item_params_leng ?
entity->entity_item_params[0] & 0xFF : 0x00));
}
static void
hidparser_print_this_attribute(entity_attribute_t *attribute, char *ident_space)
{
if (ident_space == NULL) {
USB_DPRINTF_L3(PRINT_MASK_ALL, hparser_log_handle,
"%s(0x%X)",
items[attribute->entity_attribute_tag],
hidparser_find_unsigned_val(attribute));
} else {
USB_DPRINTF_L3(PRINT_MASK_ALL, hparser_log_handle,
"%s%s(0x%X)", ident_space,
items[attribute->entity_attribute_tag],
hidparser_find_unsigned_val(attribute));
}
}
static int
hidparser_lookup_first(int func_index, int token)
{
int *itemp;
itemp = hid_first_list[func_index];
while (*itemp != 0) {
if (*itemp == token) {
return (HIDPARSER_SUCCESS);
}
itemp++;
}
return (HIDPARSER_FAILURE);
}
static int
hidparser_main(unsigned char *descriptor, size_t size, entity_item_t **item_ptr)
{
hidparser_tok_t *scan_ifp;
int retval;
scan_ifp = kmem_zalloc(sizeof (hidparser_tok_t), KM_SLEEP);
scan_ifp->hidparser_tok_text =
kmem_zalloc(HIDPARSER_TEXT_LENGTH, KM_SLEEP);
scan_ifp->hidparser_tok_max_bsize = size;
scan_ifp->hidparser_tok_entity_descriptor = descriptor;
*item_ptr = NULL;
retval = hidparser_ReportDescriptorDash(item_ptr, scan_ifp);
if (scan_ifp->hidparser_tok_gitem_head) {
hidparser_free_attribute_list(
scan_ifp->hidparser_tok_gitem_head);
}
if (scan_ifp->hidparser_tok_litem_head) {
hidparser_free_attribute_list(
scan_ifp->hidparser_tok_litem_head);
}
kmem_free(scan_ifp->hidparser_tok_text, HIDPARSER_TEXT_LENGTH);
kmem_free(scan_ifp, sizeof (hidparser_tok_t));
return (retval);
}
static int
hidparser_ReportDescriptorDash(entity_item_t **item_ptr,
hidparser_tok_t *scan_ifp)
{
if ((hidparser_ReportDescriptor(item_ptr, scan_ifp) ==
HIDPARSER_SUCCESS) && (scan_ifp->hidparser_tok_token == 0)) {
return (HIDPARSER_SUCCESS);
}
if (*item_ptr != NULL) {
(void) hidparser_free_report_descr_handle(*item_ptr);
}
*item_ptr = NULL;
return (HIDPARSER_FAILURE);
}
static int
hidparser_ReportDescriptor(entity_item_t **item_ptr, hidparser_tok_t *scan_ifp)
{
hidparser_scan(scan_ifp);
if (hidparser_ItemList(item_ptr, scan_ifp) == HIDPARSER_SUCCESS) {
return (HIDPARSER_SUCCESS);
}
return (HIDPARSER_FAILURE);
}
static int
hidparser_ItemList(entity_item_t **item_ptr, hidparser_tok_t *scan_ifp)
{
entity_item_t *curr_ei, *cache_ei, *prev_ei, *tmp_ei;
boolean_t root_coll = B_FALSE;
curr_ei = cache_ei = prev_ei = tmp_ei = NULL;
while (scan_ifp->hidparser_tok_token != 0) {
if (hidparser_Items(scan_ifp) == HIDPARSER_FAILURE) {
return (HIDPARSER_FAILURE);
}
if (hidparser_MainItem(&curr_ei, scan_ifp) ==
HIDPARSER_FAILURE) {
USB_DPRINTF_L2(PRINT_MASK_ALL,
hparser_log_handle,
"Invalid MAIN item 0x%x in input stream",
scan_ifp->hidparser_tok_token);
return (HIDPARSER_FAILURE);
}
if (curr_ei->entity_item_type == R_ITEM_COLLECTION) {
if (root_coll == B_FALSE) {
*item_ptr = curr_ei;
root_coll = B_TRUE;
}
curr_ei->prev_coll = cache_ei;
cache_ei = curr_ei;
USB_DPRINTF_L3(PRINT_MASK_ALL,
hparser_log_handle,
"Start Collection:cache_ei = 0x%p,"
" curr_ei = 0x%p",
(void *)cache_ei, (void *)curr_ei);
if (prev_ei == NULL) {
prev_ei = curr_ei;
continue;
}
if (prev_ei->entity_item_type ==
R_ITEM_COLLECTION) {
prev_ei->info.child = curr_ei;
} else {
prev_ei->entity_item_right_sibling =
curr_ei;
}
} else if (curr_ei->entity_item_type ==
R_ITEM_END_COLLECTION) {
tmp_ei = cache_ei->prev_coll;
cache_ei->entity_item_right_sibling = curr_ei;
USB_DPRINTF_L3(PRINT_MASK_ALL,
hparser_log_handle,
"End Collection: cache_ei = 0x%p, "
"curr_ei = 0x%p",
(void *)cache_ei, (void *)curr_ei);
if (tmp_ei != NULL) {
cache_ei = tmp_ei;
}
tmp_ei = NULL;
} else {
if (prev_ei == NULL) {
USB_DPRINTF_L2(PRINT_MASK_ALL,
hparser_log_handle,
"Invalid First MAIN item 0x%x",
scan_ifp->hidparser_tok_token);
return (HIDPARSER_FAILURE);
}
if (prev_ei->entity_item_type ==
R_ITEM_COLLECTION) {
USB_DPRINTF_L3(PRINT_MASK_ALL,
hparser_log_handle,
"Main Item: token = 0x%x, "
"curr_ei = 0x%p "
"will be the child of prev_ei "
"= 0x%p, "
"cache_ei being 0x%p",
curr_ei->entity_item_type,
(void *)curr_ei, (void *)prev_ei,
(void *)cache_ei);
prev_ei->info.child = curr_ei;
} else {
USB_DPRINTF_L3(PRINT_MASK_ALL,
hparser_log_handle,
"Main Item: token = 0x%x, "
"curr_ei = 0x%p "
"will be the right sibling of "
"prev_ei = 0x%p, "
"cache_ei being 0x%p",
curr_ei->entity_item_type,
(void *)curr_ei, (void *)prev_ei,
(void *)cache_ei);
prev_ei->entity_item_right_sibling =
curr_ei;
}
}
prev_ei = curr_ei;
}
if (*item_ptr != cache_ei) {
USB_DPRINTF_L2(PRINT_MASK_ALL, hparser_log_handle,
"Failed to parse report descriptor");
return (HIDPARSER_FAILURE);
}
(void) hidparser_print_report_descr_handle(cache_ei, 0);
return (HIDPARSER_SUCCESS);
}
static int
hidparser_MainItem(entity_item_t **item_ptr, hidparser_tok_t *scan_ifp)
{
switch (scan_ifp->hidparser_tok_token) {
case R_ITEM_INPUT:
case R_ITEM_OUTPUT:
case R_ITEM_FEATURE:
case R_ITEM_COLLECTION:
case R_ITEM_END_COLLECTION:
*item_ptr = hidparser_allocate_entity(scan_ifp);
USB_DPRINTF_L4(PRINT_MASK_ALL, hparser_log_handle,
"hidparser_MainItem:index = 0x%lx token = 0x%x",
scan_ifp->hidparser_tok_index -
(*item_ptr)->entity_item_params_leng - 1,
scan_ifp->hidparser_tok_token);
hidparser_scan(scan_ifp);
hidparser_global_err_check(*item_ptr);
hidparser_local_err_check(*item_ptr);
hidparser_mainitem_err_check(*item_ptr);
return (HIDPARSER_SUCCESS);
default:
break;
}
*item_ptr = NULL;
return (HIDPARSER_FAILURE);
}
static int
hidparser_Items(hidparser_tok_t *scan_ifp)
{
boolean_t delim_pre = B_FALSE;
int token = scan_ifp->hidparser_tok_token;
while (hidparser_lookup_first(HIDPARSER_ITEMS, token) ==
HIDPARSER_SUCCESS) {
if (token == R_ITEM_SET_DELIMITER) {
if (delim_pre == B_FALSE) {
if (scan_ifp->hidparser_tok_text[0] != 1) {
hidparser_error_delim(NULL,
HIDPARSER_DELIM_ERR1);
} else {
delim_pre = B_TRUE;
}
} else {
if (scan_ifp->hidparser_tok_text[0] !=
0) {
hidparser_error_delim(NULL,
HIDPARSER_DELIM_ERR2);
} else {
delim_pre = B_FALSE;
}
}
(void) hidparser_LocalItem(scan_ifp);
token = scan_ifp->hidparser_tok_token;
} else if (hidparser_GlobalItem(scan_ifp) ==
HIDPARSER_SUCCESS) {
token = scan_ifp->hidparser_tok_token;
} else if (hidparser_LocalItem(scan_ifp) == HIDPARSER_SUCCESS) {
token = scan_ifp->hidparser_tok_token;
}
}
return (HIDPARSER_SUCCESS);
}
static int
hidparser_GlobalItem(hidparser_tok_t *scan_ifp)
{
int i;
entity_attribute_stack_t *elem;
switch (scan_ifp->hidparser_tok_token) {
case R_ITEM_USAGE_PAGE:
for (i = 0; i < scan_ifp->hidparser_tok_leng; i++) {
if (scan_ifp->hidparser_tok_text[i] == 0) {
hidparser_report_err(
HIDPARSER_ERR_WARN,
HIDPARSER_ERR_STANDARD,
R_ITEM_USAGE_PAGE,
0,
"Data field should be non-Zero");
}
else if ((scan_ifp->hidparser_tok_text[i] >=
0x0a) &&
(scan_ifp->hidparser_tok_text[i] <=
0xFE)) {
hidparser_report_err(
HIDPARSER_ERR_WARN,
HIDPARSER_ERR_STANDARD,
R_ITEM_USAGE_PAGE,
1,
"Data field should not use "
"reserved values");
}
}
break;
case R_ITEM_UNIT:
case R_ITEM_EXPONENT:
if (scan_ifp->hidparser_tok_leng == 4) {
if ((scan_ifp->hidparser_tok_text[3] &
0xf0) != 0) {
hidparser_report_err(
HIDPARSER_ERR_WARN,
HIDPARSER_ERR_STANDARD,
scan_ifp->hidparser_tok_token,
0,
"Data field reserved bits should "
"be Zero");
}
}
break;
case R_ITEM_REPORT_COUNT:
for (i = 0; i < scan_ifp->hidparser_tok_leng; i++) {
if (scan_ifp->hidparser_tok_text[i])
break;
}
if (i == scan_ifp->hidparser_tok_leng) {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
R_ITEM_REPORT_COUNT,
0,
"Report Count = 0");
}
break;
case R_ITEM_REPORT_ID:
if (scan_ifp->hidparser_tok_leng != 1) {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
R_ITEM_REPORT_ID,
1,
"Must be contained in a byte");
}
if (!scan_ifp->hidparser_tok_text[0]) {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
R_ITEM_REPORT_ID,
0,
"Report Id must be non-zero");
}
break;
case R_ITEM_LOGICAL_MINIMUM:
break;
case R_ITEM_LOGICAL_MAXIMUM:
break;
case R_ITEM_PHYSICAL_MINIMUM:
break;
case R_ITEM_PHYSICAL_MAXIMUM:
break;
case R_ITEM_REPORT_SIZE:
break;
case R_ITEM_PUSH:
if (scan_ifp->hidparser_tok_leng != 0) {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
scan_ifp->hidparser_tok_token,
0,
"Data Field size should be zero");
} else {
elem = (entity_attribute_stack_t *)kmem_zalloc(
sizeof (entity_attribute_stack_t),
KM_SLEEP);
elem->list = hidparser_cp_attribute_list(
scan_ifp->hidparser_tok_gitem_head);
if (scan_ifp->hidparser_head) {
elem->next = scan_ifp->hidparser_head;
}
scan_ifp->hidparser_head = elem;
}
break;
case R_ITEM_POP:
if (scan_ifp->hidparser_tok_leng != 0) {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
scan_ifp->hidparser_tok_token,
0,
"Data Field size should be zero");
} else {
hidparser_free_attribute_list(scan_ifp->
hidparser_tok_gitem_head);
scan_ifp->hidparser_tok_gitem_head =
scan_ifp->hidparser_head->list;
scan_ifp->hidparser_head->list = NULL;
elem = scan_ifp->hidparser_head;
scan_ifp->hidparser_head = elem->next;
kmem_free(elem,
sizeof (entity_attribute_stack_t));
}
break;
default:
return (HIDPARSER_FAILURE);
}
hidparser_add_attribute(scan_ifp);
USB_DPRINTF_L4(PRINT_MASK_ALL, hparser_log_handle,
"hidparser_GlobalItem:index = 0x%lx token = 0x%x",
scan_ifp->hidparser_tok_index -
scan_ifp->hidparser_tok_leng - 1,
scan_ifp->hidparser_tok_token);
hidparser_scan(scan_ifp);
return (HIDPARSER_SUCCESS);
}
static int
hidparser_LocalItem(hidparser_tok_t *scan_ifp)
{
int i;
switch (scan_ifp->hidparser_tok_token) {
case R_ITEM_USAGE:
for (i = 0; i < scan_ifp->hidparser_tok_leng; i++) {
if (scan_ifp->hidparser_tok_text[i])
break;
}
if (i == scan_ifp->hidparser_tok_leng) {
hidparser_report_err(
HIDPARSER_ERR_WARN,
HIDPARSER_ERR_STANDARD,
R_ITEM_USAGE,
0,
"Data Field should be non-zero");
}
case R_ITEM_USAGE_MIN:
case R_ITEM_USAGE_MAX:
case R_ITEM_DESIGNATOR_INDEX:
case R_ITEM_DESIGNATOR_MIN:
case R_ITEM_STRING_INDEX:
case R_ITEM_STRING_MIN:
case R_ITEM_STRING_MAX:
case R_ITEM_SET_DELIMITER:
hidparser_add_attribute(scan_ifp);
USB_DPRINTF_L4(PRINT_MASK_ALL, hparser_log_handle,
"hidparser_LocalItem:index = 0x%lx token = 0x%x",
scan_ifp->hidparser_tok_index -
scan_ifp->hidparser_tok_leng - 1,
scan_ifp->hidparser_tok_token);
hidparser_scan(scan_ifp);
return (HIDPARSER_SUCCESS);
default:
break;
}
return (HIDPARSER_FAILURE);
}
static entity_item_t *
hidparser_allocate_entity(hidparser_tok_t *scan_ifp)
{
entity_item_t *entity;
entity_attribute_t *aend;
int entity_type = scan_ifp->hidparser_tok_token;
unsigned char *text = scan_ifp->hidparser_tok_text;
int len = scan_ifp->hidparser_tok_leng;
entity = kmem_zalloc(sizeof (entity_item_t), KM_SLEEP);
entity->entity_item_type = entity_type;
entity->entity_item_params_leng = len;
if (len != 0) {
entity->entity_item_params = kmem_zalloc(len, KM_SLEEP);
(void) bcopy(text, entity->entity_item_params, len);
}
if (entity_type != R_ITEM_END_COLLECTION) {
entity->entity_item_attributes = hidparser_cp_attribute_list(
scan_ifp->hidparser_tok_gitem_head);
if (entity->entity_item_attributes) {
aend = hidparser_find_attribute_end(
entity->entity_item_attributes);
aend->entity_attribute_next =
scan_ifp->hidparser_tok_litem_head;
scan_ifp->hidparser_tok_litem_head = NULL;
} else {
entity->entity_item_attributes =
scan_ifp->hidparser_tok_litem_head;
scan_ifp->hidparser_tok_litem_head = NULL;
}
}
entity->info.child = entity->entity_item_right_sibling = 0;
return (entity);
}
static void
hidparser_add_attribute(hidparser_tok_t *scan_ifp)
{
entity_attribute_t *newattrib, **previous, *elem;
int entity = scan_ifp->hidparser_tok_token;
unsigned char *text = scan_ifp->hidparser_tok_text;
int len = scan_ifp->hidparser_tok_leng;
if (len == 0) {
USB_DPRINTF_L2(PRINT_MASK_ALL, hparser_log_handle,
"hidparser_add_attribute: len = 0 for item = 0x%x",
entity);
return;
}
if (entity & HIDPARSER_ISLOCAL_MASK) {
previous = &scan_ifp->hidparser_tok_litem_head;
} else {
previous = &scan_ifp->hidparser_tok_gitem_head;
}
elem = *previous;
while (elem) {
if (elem->entity_attribute_tag == entity &&
!(entity & HIDPARSER_ISLOCAL_MASK)) {
*previous = elem->entity_attribute_next;
kmem_free(elem->entity_attribute_value,
elem->entity_attribute_length);
kmem_free(elem, sizeof (entity_attribute_t));
elem = *previous;
} else {
previous = &elem->entity_attribute_next;
elem = elem->entity_attribute_next;
}
}
newattrib = hidparser_alloc_attrib_list(1);
newattrib->entity_attribute_tag = entity;
newattrib->entity_attribute_value = kmem_zalloc(len, KM_SLEEP);
(void) bcopy(text, newattrib->entity_attribute_value, len);
newattrib->entity_attribute_length = len;
*previous = newattrib;
}
static entity_attribute_t *
hidparser_alloc_attrib_list(int count)
{
entity_attribute_t *head, *current;
if (count <= 0) {
return (NULL);
}
head = kmem_zalloc(sizeof (entity_attribute_t), KM_SLEEP);
count--;
current = head;
while (count--) {
current->entity_attribute_next = kmem_zalloc(
sizeof (entity_attribute_t), KM_SLEEP);
current = current->entity_attribute_next;
}
current->entity_attribute_next = NULL;
return (head);
}
static entity_attribute_t *
hidparser_cp_attribute_list(entity_attribute_t *head)
{
entity_attribute_t *return_value, *current_src, *current_dst;
if (!head) {
return (NULL);
}
current_src = head;
current_dst = return_value = hidparser_alloc_attrib_list(1);
while (current_src) {
current_dst->entity_attribute_tag =
current_src->entity_attribute_tag;
current_dst->entity_attribute_length =
current_src->entity_attribute_length;
current_dst->entity_attribute_value = kmem_zalloc(
current_dst->entity_attribute_length, KM_SLEEP);
(void) bcopy(current_src->entity_attribute_value,
current_dst->entity_attribute_value,
current_src->entity_attribute_length);
if (current_src->entity_attribute_next) {
current_dst->entity_attribute_next =
hidparser_alloc_attrib_list(1);
} else {
current_dst->entity_attribute_next = NULL;
}
current_src = current_src->entity_attribute_next;
current_dst = current_dst->entity_attribute_next;
}
return (return_value);
}
static entity_attribute_t *
hidparser_find_attribute_end(entity_attribute_t *head)
{
if (head == NULL) {
return (NULL);
}
while (head->entity_attribute_next != NULL) {
head = head->entity_attribute_next;
}
return (head);
}
static void
hidparser_free_report_descr_handle(entity_item_t *handle)
{
entity_item_t *next, *current, *child;
current = handle;
while (current) {
child = current->info.child;
next = current->entity_item_right_sibling;
if (current->entity_item_type == R_ITEM_COLLECTION) {
if (current->entity_item_params != NULL)
kmem_free(current->entity_item_params,
current->entity_item_params_leng);
if (current->entity_item_attributes != NULL)
hidparser_free_attribute_list(
current->entity_item_attributes);
USB_DPRINTF_L4(PRINT_MASK_ALL, hparser_log_handle,
"FREE 1: %s",
items[current->entity_item_type]);
kmem_free(current, sizeof (entity_item_t));
(void) hidparser_free_report_descr_handle(child);
} else {
if (current->entity_item_params != NULL) {
kmem_free(current->entity_item_params,
current->entity_item_params_leng);
}
if (current->entity_item_attributes != NULL) {
hidparser_free_attribute_list(
current->entity_item_attributes);
}
USB_DPRINTF_L4(PRINT_MASK_ALL,
hparser_log_handle, "FREE 2: %s",
items[current->entity_item_type]);
kmem_free(current, sizeof (entity_item_t));
}
current = next;
}
}
static void
hidparser_free_attribute_list(entity_attribute_t *head)
{
entity_attribute_t *next, *current;
current = head;
while (current) {
next = current->entity_attribute_next;
USB_DPRINTF_L4(PRINT_MASK_ALL,
hparser_log_handle, "FREE: %s value_length = %d",
items[current->entity_attribute_tag],
current->entity_attribute_length);
if (current->entity_attribute_value != NULL) {
USB_DPRINTF_L4(PRINT_MASK_ALL,
hparser_log_handle,
"\tvalue = 0x%x",
current->entity_attribute_value[0]);
kmem_free(current->entity_attribute_value,
current->entity_attribute_length);
}
kmem_free(current, sizeof (entity_attribute_t));
current = next;
}
}
static void
hidparser_initialize_items(void)
{
items[R_ITEM_USAGE] = "Usage";
items[R_ITEM_USAGE_MIN] = "Usage Minimum";
items[R_ITEM_USAGE_MAX] = "Usage Maximum";
items[R_ITEM_DESIGNATOR_INDEX] = "Designator Index";
items[R_ITEM_DESIGNATOR_MIN] = "Designator Minimum";
items[R_ITEM_DESIGNATOR_MAX] = "Designator Maximum";
items[R_ITEM_STRING_INDEX] = "String Index";
items[R_ITEM_STRING_MIN] = "String Minimum";
items[R_ITEM_STRING_MAX] = "String Maximum";
items[R_ITEM_USAGE_PAGE] = "Usage Page";
items[R_ITEM_LOGICAL_MINIMUM] = "Logical Minimum";
items[R_ITEM_LOGICAL_MAXIMUM] = "Logical Maximum";
items[R_ITEM_PHYSICAL_MINIMUM] = "Physical Minimum";
items[R_ITEM_PHYSICAL_MAXIMUM] = "Physical Maximum";
items[R_ITEM_EXPONENT] = "Exponent";
items[R_ITEM_UNIT] = "Unit";
items[R_ITEM_REPORT_SIZE] = "Report Size";
items[R_ITEM_REPORT_ID] = "Report Id";
items[R_ITEM_REPORT_COUNT] = "Report Count";
items[R_ITEM_PUSH] = "Push";
items[R_ITEM_POP] = "Pop";
items[R_ITEM_INPUT] = "Input";
items[R_ITEM_OUTPUT] = "Output";
items[R_ITEM_COLLECTION] = "Collection";
items[R_ITEM_FEATURE] = "Feature";
items[R_ITEM_END_COLLECTION] = "End Collection";
items[R_ITEM_SET_DELIMITER] = "Delimiter";
}
static void
hidparser_scan(hidparser_tok_t *scan_ifp)
{
int count;
int ch;
int parsed_length;
unsigned char *parsed_text;
unsigned char *entity_descriptor;
char err_str[32];
size_t entity_buffer_size, index;
index = scan_ifp->hidparser_tok_index;
entity_buffer_size = scan_ifp->hidparser_tok_max_bsize;
parsed_length = 0;
parsed_text = scan_ifp->hidparser_tok_text;
entity_descriptor = scan_ifp->hidparser_tok_entity_descriptor;
next_item:
if (index <= entity_buffer_size -1) {
ch = 0xFF & entity_descriptor[index];
USB_DPRINTF_L4(PRINT_MASK_ALL,
hparser_log_handle, "scanner: index = 0x%lx ch = 0x%x",
index, ch);
index++;
if (!(hidparser_isvalid_item(ch))) {
(void) sprintf(err_str, "%s: 0x%2x",
"Unknown or reserved item", ch);
hidparser_report_err(HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD, 0, 0x3F, err_str);
goto next_item;
}
if (ch == EXTENDED_ITEM) {
parsed_length = entity_descriptor[index++];
ch = entity_descriptor[index++];
hidparser_report_err(HIDPARSER_ERR_WARN,
HIDPARSER_ERR_STANDARD,
0,
0x3E,
"Long item defined");
} else {
parsed_length = ch & 0x03;
USB_DPRINTF_L4(PRINT_MASK_ALL,
hparser_log_handle,
"scanner: parsed_length = %x", parsed_length);
if (parsed_length == 3)
parsed_length++;
}
for (count = 0; count < parsed_length; count++) {
parsed_text[count] = entity_descriptor[index];
USB_DPRINTF_L4(PRINT_MASK_ALL, hparser_log_handle,
"scanner: parsed_text[%d] = 0x%x,"
"index = 0x%lx",
count, parsed_text[count], index);
index++;
}
USB_DPRINTF_L4(PRINT_MASK_ALL,
hparser_log_handle, "scanner: lexical analyzer found 0x%x "
"before translation", ch);
scan_ifp->hidparser_tok_index = index;
scan_ifp->hidparser_tok_leng = parsed_length;
scan_ifp->hidparser_tok_token = ch & 0xFC;
USB_DPRINTF_L4(PRINT_MASK_ALL,
hparser_log_handle, "scanner: aindex = 0x%lx", index);
} else {
USB_DPRINTF_L4(PRINT_MASK_ALL,
hparser_log_handle, "scanner: eindex = 0x%lx", index);
scan_ifp->hidparser_tok_leng = 0;
scan_ifp->hidparser_tok_token = 0;
}
}
static void
hidparser_report_err(int err_level, int err_type, int tag, int subcode,
char *msg)
{
unsigned int BmParserErrorCode = 0;
if (err_level) {
BmParserErrorCode |= HIDPARSER_ERR_ERROR;
}
if (err_type) {
BmParserErrorCode |= HIDPARSER_ERR_STANDARD;
}
BmParserErrorCode |= (tag << 8) & HIDPARSER_ERR_TAG_MASK;
BmParserErrorCode |= subcode & HIDPARSER_ERR_SUBCODE_MASK;
if (err_level) {
USB_DPRINTF_L2(PRINT_MASK_ALL, hparser_log_handle,
"err code = 0x%4x, err str = %s",
BmParserErrorCode, msg);
} else {
USB_DPRINTF_L2(PRINT_MASK_ALL, hparser_log_handle,
"wrn code = 0x%4x, wrn str = %s",
BmParserErrorCode, msg);
}
}
static int
hidparser_isvalid_item(int tag)
{
if (tag == EXTENDED_ITEM) {
return (1);
}
tag &= 0xFC;
if ((tag == R_ITEM_INPUT) ||
(tag == R_ITEM_OUTPUT) ||
(tag == R_ITEM_COLLECTION) ||
(tag == R_ITEM_FEATURE) ||
(tag == R_ITEM_END_COLLECTION) ||
(tag == R_ITEM_USAGE_PAGE) ||
(tag == R_ITEM_LOGICAL_MINIMUM) ||
(tag == R_ITEM_LOGICAL_MAXIMUM) ||
(tag == R_ITEM_PHYSICAL_MINIMUM) ||
(tag == R_ITEM_PHYSICAL_MAXIMUM) ||
(tag == R_ITEM_EXPONENT) ||
(tag == R_ITEM_UNIT) ||
(tag == R_ITEM_REPORT_SIZE) ||
(tag == R_ITEM_REPORT_ID) ||
(tag == R_ITEM_REPORT_COUNT) ||
(tag == R_ITEM_PUSH) ||
(tag == R_ITEM_POP) ||
(tag == R_ITEM_USAGE) ||
(tag == R_ITEM_USAGE_MIN) ||
(tag == R_ITEM_USAGE_MAX) ||
(tag == R_ITEM_DESIGNATOR_INDEX) ||
(tag == R_ITEM_DESIGNATOR_MIN) ||
(tag == R_ITEM_DESIGNATOR_MAX) ||
(tag == R_ITEM_STRING_INDEX) ||
(tag == R_ITEM_STRING_MIN) ||
(tag == R_ITEM_STRING_MAX) ||
(tag == R_ITEM_SET_DELIMITER)) {
return (1);
} else {
return (0);
}
}
static entity_attribute_t *
hidparser_lookup_attribute(entity_item_t *item, int attr_tag)
{
entity_attribute_t *temp;
if (item == NULL) {
return (NULL);
}
temp = item->entity_item_attributes;
while (temp != NULL) {
if (temp->entity_attribute_tag == attr_tag) {
return (temp);
}
temp = temp->entity_attribute_next;
}
return (NULL);
}
static void
hidparser_global_err_check(entity_item_t *mainitem)
{
hidparser_check_minmax_val_signed(mainitem, R_ITEM_LOGICAL_MINIMUM,
R_ITEM_LOGICAL_MAXIMUM, 0, 0);
hidparser_check_minmax_val_signed(mainitem, R_ITEM_PHYSICAL_MINIMUM,
R_ITEM_PHYSICAL_MAXIMUM, 0, 0);
hidparser_check_correspondence(mainitem, R_ITEM_PHYSICAL_MINIMUM,
R_ITEM_PHYSICAL_MAXIMUM, 0, 0,
"Must have a corresponding Physical min",
"Must have a corresponding Physical max");
hidparser_check_correspondence(mainitem, R_ITEM_PUSH, R_ITEM_POP,
1, 0, "Should have a corresponding Pop",
"Must have a corresponding Push");
}
static void
hidparser_mainitem_err_check(entity_item_t *mainitem)
{
int itemmask = 0;
entity_attribute_t *attr;
attr = mainitem->entity_item_attributes;
if (attr != NULL) {
while (attr) {
switch (attr->entity_attribute_tag) {
case R_ITEM_LOGICAL_MINIMUM:
itemmask |= 0x01;
break;
case R_ITEM_LOGICAL_MAXIMUM:
itemmask |= 0x02;
break;
case R_ITEM_REPORT_SIZE:
itemmask |= 0x04;
break;
case R_ITEM_REPORT_COUNT:
itemmask |= 0x08;
break;
case R_ITEM_USAGE_PAGE:
itemmask |= 0x10;
break;
default:
break;
}
attr = attr->entity_attribute_next;
}
}
if ((mainitem->entity_item_type == R_ITEM_COLLECTION) ||
(mainitem->entity_item_type == R_ITEM_END_COLLECTION)) {
return;
}
if (itemmask != 0x1f) {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
mainitem->entity_item_type,
0,
"Required Global/Local items must be defined");
}
}
static void
hidparser_local_err_check(entity_item_t *mainitem)
{
hidparser_check_correspondence(mainitem, R_ITEM_USAGE_MIN,
R_ITEM_USAGE_MAX, 0, 0,
"Must have a corresponding Usage Min",
"Must have a corresponding Usage Max");
hidparser_check_minmax_val(mainitem, R_ITEM_USAGE_MIN,
R_ITEM_USAGE_MAX, 1, 1);
hidparser_check_correspondence(mainitem, R_ITEM_DESIGNATOR_MIN,
R_ITEM_DESIGNATOR_MAX, 0, 0,
"Must have a corresponding Designator min",
"Must have a corresponding Designator Max");
hidparser_check_minmax_val(mainitem, R_ITEM_DESIGNATOR_MIN,
R_ITEM_DESIGNATOR_MAX, 1, 1);
hidparser_check_correspondence(mainitem, R_ITEM_STRING_MIN,
R_ITEM_STRING_MAX, 0, 0,
"Must have a corresponding String min",
"Must have a corresponding String Max");
hidparser_check_minmax_val(mainitem, R_ITEM_STRING_MIN,
R_ITEM_STRING_MAX, 1, 1);
}
static unsigned int
hidparser_find_unsigned_val(entity_attribute_t *attr)
{
char *text;
int len, i;
unsigned int ret = 0;
text = attr->entity_attribute_value;
len = attr->entity_attribute_length;
for (i = 0; i < len; i++) {
ret |= ((text[i] & 0xff) << (8*i));
}
return (ret);
}
static signed int
hidparser_find_signed_val(entity_attribute_t *attr)
{
char *text;
int len, i;
int ret = 0;
text = attr->entity_attribute_value;
len = attr->entity_attribute_length;
for (i = 0; i < len - 1; i++) {
ret |= ((text[i] & 0xff) << (8 * i));
}
if (len > 0) {
ret |= (text[i] << (8 * i));
}
return (ret);
}
static void
hidparser_check_correspondence(entity_item_t *mainitem, int item_tag1,
int item_tag2, int val1, int val2, char *str1, char *str2)
{
entity_attribute_t *temp1, *temp2;
temp1 = hidparser_lookup_attribute(mainitem, item_tag1);
temp2 = hidparser_lookup_attribute(mainitem, item_tag2);
if ((temp1 != NULL) && (temp2 == NULL)) {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
item_tag1,
val1,
str1);
}
if ((temp2 != NULL) && (temp1 == NULL)) {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
item_tag2,
val2,
str2);
}
}
static void
hidparser_check_minmax_val(entity_item_t *mainitem, int item_tag1,
int item_tag2, int val1, int val2)
{
entity_attribute_t *temp1, *temp2;
temp1 = hidparser_lookup_attribute(mainitem, item_tag1);
temp2 = hidparser_lookup_attribute(mainitem, item_tag2);
if ((temp1 != NULL) && (temp2 != NULL)) {
if (hidparser_find_unsigned_val(temp1) >
hidparser_find_unsigned_val(temp2)) {
if ((item_tag1 == R_ITEM_LOGICAL_MINIMUM) ||
(item_tag1 == R_ITEM_PHYSICAL_MINIMUM)) {
hidparser_report_err(
HIDPARSER_ERR_WARN,
HIDPARSER_ERR_STANDARD,
item_tag1,
val1,
"unsigned: Min should be <= to Max");
} else {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
item_tag1,
val1,
"Min must be <= to Max");
}
}
if (hidparser_find_unsigned_val(temp2) <
hidparser_find_unsigned_val(temp1)) {
if ((item_tag2 == R_ITEM_LOGICAL_MAXIMUM) ||
(item_tag2 == R_ITEM_PHYSICAL_MAXIMUM)) {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
item_tag2,
val2,
"unsigned: Max should be >= to Min");
} else {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
item_tag2,
val2,
"Max must be >= to Min");
}
}
}
}
static void
hidparser_check_minmax_val_signed(entity_item_t *mainitem, int item_tag1,
int item_tag2, int val1, int val2)
{
entity_attribute_t *temp1, *temp2;
temp1 = hidparser_lookup_attribute(mainitem, item_tag1);
temp2 = hidparser_lookup_attribute(mainitem, item_tag2);
if ((temp1 != NULL) && (temp2 != NULL)) {
if (hidparser_find_signed_val(temp1) >
hidparser_find_signed_val(temp2)) {
if ((item_tag1 == R_ITEM_LOGICAL_MINIMUM) ||
(item_tag1 == R_ITEM_PHYSICAL_MINIMUM)) {
hidparser_report_err(
HIDPARSER_ERR_WARN,
HIDPARSER_ERR_STANDARD,
item_tag1,
val1,
"signed: Min should be <= to Max");
} else {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
item_tag1,
val1,
"Min must be <= to Max");
}
}
if (hidparser_find_signed_val(temp2) <
hidparser_find_signed_val(temp1)) {
if ((item_tag2 == R_ITEM_LOGICAL_MAXIMUM) ||
(item_tag2 == R_ITEM_PHYSICAL_MAXIMUM)) {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
item_tag2,
val2,
"signed: Max should be >= to Min");
} else {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
item_tag2,
val2,
"Max must be >= to Min");
}
}
}
}
static void
hidparser_error_delim(entity_item_t *item, int err)
{
entity_attribute_t *attr;
switch (err) {
case HIDPARSER_DELIM_ERR1:
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
R_ITEM_SET_DELIMITER,
0,
"Must be Delimiter Open");
break;
case HIDPARSER_DELIM_ERR2:
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
R_ITEM_SET_DELIMITER,
0,
"Must be Delimiter Close");
break;
case HIDPARSER_DELIM_ERR3:
attr = item->entity_item_attributes;
while (attr != NULL) {
if ((attr->entity_attribute_tag !=
R_ITEM_USAGE) &&
(attr->entity_attribute_tag !=
R_ITEM_USAGE_MIN) &&
(attr->entity_attribute_tag !=
R_ITEM_USAGE_MAX)) {
hidparser_report_err(
HIDPARSER_ERR_ERROR,
HIDPARSER_ERR_STANDARD,
R_ITEM_SET_DELIMITER,
3,
"May only contain Usage, "
"Usage Min and Usage Max");
}
attr = attr->entity_attribute_next;
}
break;
default:
break;
}
}
void
hidparser_find_max_packet_size_from_report_descriptor(
hidparser_handle_t hparser_handle,
hidparser_packet_info_t *hpack)
{
int rval, i;
uint_t packet_size;
uint_t max_packet_size;
uint_t max_report_id;
hidparser_report_id_list_t report_id_list;
USB_DPRINTF_L4(PRINT_MASK_ALL, hparser_log_handle,
"hidparser_find_max_packet_size_from_report_descriptor");
rval = hidparser_get_report_id_list(hparser_handle,
R_ITEM_INPUT, &report_id_list);
if (rval != HIDPARSER_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ALL, hparser_log_handle,
"No report id used");
} else {
USB_DPRINTF_L3(PRINT_MASK_ALL, hparser_log_handle,
"%d unique report IDs found in hid report descriptor",
report_id_list.no_of_report_ids);
for (i = 0; i < (report_id_list.no_of_report_ids); i++) {
USB_DPRINTF_L3(PRINT_MASK_ALL, hparser_log_handle,
"report_id: %d", report_id_list.report_id[i]);
}
}
if ((rval != HIDPARSER_SUCCESS) ||
(report_id_list.no_of_report_ids == 0)) {
(void) hidparser_get_packet_size(hparser_handle,
0, R_ITEM_INPUT, &packet_size);
USB_DPRINTF_L2(PRINT_MASK_ALL, hparser_log_handle,
"Not using report id prefix. HID packet size = %d",
packet_size);
hpack->max_packet_size = packet_size;
hpack->report_id = HID_REPORT_ID_UNDEFINED;
} else {
max_packet_size = 0;
max_report_id = 0;
for (i = 0; i < (report_id_list.no_of_report_ids); i++) {
(void) hidparser_get_packet_size(hparser_handle,
report_id_list.report_id[i], R_ITEM_INPUT,
&packet_size);
if (packet_size > max_packet_size) {
max_packet_size = packet_size;
max_report_id = report_id_list.report_id[i];
}
USB_DPRINTF_L2(PRINT_MASK_ALL, hparser_log_handle,
"Report ID %d has a packet size of %d",
report_id_list.report_id[i], packet_size);
}
hpack->max_packet_size = max_packet_size;
hpack->report_id = max_report_id;
USB_DPRINTF_L2(PRINT_MASK_ALL, hparser_log_handle,
"Report ID %d has the maximum packet size of %d",
max_report_id, max_packet_size);
}
}