/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include /* * hidparser: Parser to generate parse tree for Report Descriptors * in HID devices. */ 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; /* * Array used to store corresponding strings for the * different item types for debugging. */ char *items[500]; /* Print items */ /* * modload support */ extern struct mod_ops mod_miscops; static struct modlmisc modlmisc = { &mod_miscops, /* Type of module */ "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)); } /* * These functions are used internally in the parser. * local declarations */ 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); /* * The hidparser_lookup_first(N) of a non-terminal N is stored as an array of * integer tokens, terminated by 0. Right now there is only one element. */ 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 }; /* * Each non-terminal is represented by a function. In a top-down parser, * whenever a non-terminal is encountered on the state diagram, the * corresponding function is called. Because of the grammar, there is NO * backtracking. If there is an error in the middle, the parser returns * HIDPARSER_FAILURE */ static hidparser_terminal_t *hid_first_list[] = { first_Items }; /* * hidparser_parse_report_descriptor: * Calls the main parser routine */ 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); } } /* * hidparser_free_report_descriptor_handle: * Frees the parse_handle which consists of a pointer to the parse * tree and a pointer to the Hid descriptor structure */ 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); } /* * hidparser_get_country_code: * Return the bCountryCode from the Hid Descriptor * to the hid module. */ 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); } /* * hidparser_get_packet_size: * Get the packet size(sum of REPORT_SIZE * REPORT_COUNT) * corresponding to a report id and an item type */ 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)); } /* * hidparser_get_packet_size_report_des: * Get the packet size(sum of REPORT_SIZE * REPORT_COUNT) * corresponding to a report id and an item type */ 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; } /* end while */ if (foundreportid) { if (right_report_id) { *size = *size + temp; } } else if (report_id == HID_REPORT_ID_UNDEFINED) { /* Just sanity checking */ *size = *size + temp; } right_report_id = 0; } /* end else if */ current = current->entity_item_right_sibling; } /* end while current */ return (HIDPARSER_SUCCESS); } /* * hidparser_get_usage_attribute: * Get the attribute value corresponding to a particular * report id, main item and usage */ 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)); } /* * hidparser_get_usage_attribute_report_des: * Called by the wrapper function hidparser_get_usage_attribute() */ 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) { /* Match 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 we are trying to find out * say, report size of usage = * 0, a m.i with a valid usage * will not contain that */ if (usage_id == HID_USAGE_UNDEFINED) { found_usage_id = 0; } } if (found_usage_id && attribute-> entity_attribute_length == 3) { /* * This is an extended usage ie. * usage page in upper 16 bits * or-ed with usage in the lower * 16 bits. */ 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) { /* Match 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) { /* Match attribute */ found_ret_value = 1; *usage_attribute_value = attribute->entity_attribute_value[0]; 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); } } } /* * search the next main item, right sibling of this one */ if (current->entity_item_right_sibling != NULL) { current = current->entity_item_right_sibling; found_usage_id = found_page = found_ret_value = 0; /* Don't change foundreportid */ right_report_id = 0; } else { break; } } /* Don't give junk result */ *usage_attribute_value = 0; return (HIDPARSER_NOT_FOUND); } /* * hidparser_get_main_item_data_descr: * Get the data value corresponding to a particular * Main Item (Input, Output, Feature) */ 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); } /* * hidparser_get_main_item_data_descr_main: * Called by the wrapper function hidparser_get_main_item_data_descr() */ 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) { /* Match 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)) { /* Match 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); } } } } /* * search the next main item, right sibling of this one */ 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 = (uint_t)NULL; return (HIDPARSER_NOT_FOUND); } /* * hidparser_get_top_level_collection_usage: * Get the usage page and usage for the top level collection item */ 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); } /* * hidparser_get_usage_list_in_order: * Find all the usages corresponding to a main item and report id. * Note that only short items are supported. * * Arguments: * parser_handle: * hid parser handle * report id: * report id of the particular report where the usages belong to * main_item_type: * type of report, either Input, Output, or Feature * usage_list: * Filled in with the pointer to the first element of the * usage list * * Return values: * HIDPARSER_SUCCESS - returned success * HIDPARSER_NOT_FOUND - usage specified by the parameters was not found * HIDPARSER_FAILURE - unspecified 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) { /* setup wrapper function */ 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) { /* * find collection usage information for this * 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) { /* Match 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 { /* different report id */ 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: /* skip over alternate usages */ do { attribute = attribute-> entity_attribute_next; } while (attribute-> entity_attribute_tag != R_ITEM_SET_DELIMITER); break; } attribute = attribute->entity_attribute_next; } /* * If we have a report id match (or report ids * are not present), and have a usage item or * usage min&max, put the usage item into the * list. Don't put undefined usage items * (HID_USAGE_UNDEFINED, 0) into the list; * a 0 usage item is used to match padding * fields that don't have an attached usage. */ if (!foundreportid || (foundreportid && right_report_id)) { for (j = 0; j < found_usage; j++) { /* Put in usage list */ 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]); /* * This is an extended usage ie. * usage page in upper 16 bits * or-ed with usage in the lower * 16 bits. */ 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) { /* Put in usage list */ if (rpt->no_of_usages >= USAGE_MAX) { return (HIDPARSER_FAILURE); } if (found_usage) { /* handle duplication */ 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; } /* * This is an extended usage ie. * usage page in upper 16 bits * or-ed with usage_max in the lower * 16 bits. */ if (usage_max >> 16) { ui->usage_page = HID_USAGE_PAGE(usage_max); } } } /* * This main item contains no usage * Fill in with usage "UNDEFINED". * If report id is valid, only the * main item with matched report id * can be filled in. */ 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; } /* end while current */ return (HIDPARSER_SUCCESS); } /* * hidparser_fill_usage_info(): * Fill in the mandatory item information for a main item. * See HID 6.2.2. */ 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; } } /* * hidparser_get_report_id_list: * Return a list of all report ids used for descriptor items * corresponding to a main item. * * Arguments: * parser_handle: * hid parser handle * main_item_type: * type of report, either Input, Output, or Feature * report_id_list: * Filled in with a list of report ids found in the descriptor * * Return values: * HIDPARSER_SUCCESS - returned success * HIDPARSER_FAILURE - unspecified failure */ 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)); } /* * hidparser_get_report_id_list_internal: * internal function that generates list of all report ids */ int hidparser_get_report_id_list_internal( entity_item_t *parser_handle, uint_t main_item_type, hidparser_report_id_list_t *id_lst) { /* setup wrapper function */ 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) { /* Match Item Type */ attribute = current->entity_item_attributes; while (attribute != NULL) { if (attribute->entity_attribute_tag == R_ITEM_REPORT_ID) { /* Found a Report ID */ report_id = attribute-> entity_attribute_value[0]; /* Report ID already in list? */ 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) { /* * New Report ID found, put * in list */ 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; } /* end while current */ return (HIDPARSER_SUCCESS); } /* * hidparser_print_report_descr_handle: * Functions to print the parse tree. Currently not * being called. */ 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); /* do children */ (void) hidparser_print_report_descr_handle( current->info.child, indent_level+1); } else /* just a regular entity */ { hidparser_print_entity(current, indent_level); } current = current->entity_item_right_sibling; } return (HIDPARSER_SUCCESS); } #define SPACE_PER_LEVEL 5 /* * hidparser_print_entity ; * Prints the entity items recursively */ 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)); } /* * hidparser_print_this_attribute: * Prints the attribute passed in the argument */ 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)); } } /* * The next few functions will be used for parsing using the * grammar: * * Start -> ReportDescriptor * * ReportDescriptor -> ItemList * * ItemList -> Items MainItem ItemList * | epsilon * * MainItem -> BeginCollection ItemList EndCollection * | Input * | Output * | Feature * * Items -> GlobalItem Items * | LocalItem Items * | SetDelimiterOpen LocalItemList * SetDelimiterClose Items * | epsilon * * LocalItemList -> LocalItem Temp2 * * Temp2 -> LocalItem Temp2 * | epsilon * * GlobalItem -> UsagePage * | LogicalMinimum * | LogicalMaximum * | PhysicalMinimum * | PhysicalMaximum * | Unit * | Exponent * | ReportSize * | ReportCount * | ReportID * * LocalItem -> Usage * | UsageMinimum * | UsageMaximum * | DesignatorIndex * | DesignatorMinimum * | StringIndex * | StringMinimum * | StringMaximum * */ /* * hidparser_lookup_first: * Looks up if token belongs to the FIRST of the function tag * that is passed through the first argument */ static int hidparser_lookup_first(int func_index, int token) { int *itemp; itemp = hid_first_list[func_index]; while (*itemp != 0) { /* get the next terminal on the list */ if (*itemp == token) { return (HIDPARSER_SUCCESS); } itemp++; } /* token is not on the FIRST list */ return (HIDPARSER_FAILURE); } /* * hidparser_main: * Function called from hidparser_parse_report_descriptor() * to parse the Report Descriptor */ 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); /* * Free the Local & Global item list * It maybe the case that no tree has been built * up but there have been allocation in the attribute * & control lists */ 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); } /* * hidparser_ReportDescriptorDash: * Synthetic start symbol, implements * hidparser_ReportDescriptor */ 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); } /* * In case of failure, free the kernel memory * allocated for partial building of the tree, * if any */ if (*item_ptr != NULL) { (void) hidparser_free_report_descr_handle(*item_ptr); } *item_ptr = NULL; return (HIDPARSER_FAILURE); } /* * hidparser_ReportDescriptor: * Implements the Rule: * ReportDescriptor -> ItemList */ static int hidparser_ReportDescriptor(entity_item_t ** item_ptr, hidparser_tok_t *scan_ifp) { hidparser_scan(scan_ifp); /* * We do not search for the token in FIRST(ReportDescriptor) * since - * * FIRST(ReportDescriptor) == FIRST(ItemList) * ReportDescriptor ----> ItemList */ if (hidparser_ItemList(item_ptr, scan_ifp) == HIDPARSER_SUCCESS) { return (HIDPARSER_SUCCESS); } return (HIDPARSER_FAILURE); } /* * hidparser_ItemList: * Implements the Rule: * ItemList -> Items MainItem ItemList | epsilon * * This function constructs the tree on which depends the "hidparser" * consumer functions. Basically the structure of the tree is * * C--[RS]->EC--[RS]->C--[RS]->EC..(and so on) * | * [CH] <== This relationship is true for other "C's" * | also. * v * C/-------------/I/O/F <== [ Any of these ] * | ------ * | | * v v * [CH | RS] [ RS ] * C/I/O/F | EC I/O/F * | * | * and so on... * * where C = Collection * EC = EndCollection * I = Input * O = Output * F = Feature "Main" Items. * * and the relationships are [RS] for right sibling and [CH] for * child. [CH | RS ] stands for "child or right sibling" with the * possible values below it. */ 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) { /* * As will be the case for final end * collection. */ 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) { /* Something wrong happened */ 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); } /* * hidparser_MainItem: * Implements the Rule: * MainItem -> BeginCollection ItemList EndCollection * | Input * | Output * | Feature */ static int hidparser_MainItem(entity_item_t ** item_ptr, hidparser_tok_t *scan_ifp) { switch (scan_ifp->hidparser_tok_token) { case R_ITEM_INPUT: /* FALLTHRU */ case R_ITEM_OUTPUT: /* FALLTHRU */ 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); } /* * hidparser_Items: * Implements the Rule: * Items -> GlobalItem Items * | LocalItem Items * | SetDelimiterOpen LocalItemList * SetDelimiterClose Items * | epsilon */ 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); /* epsilon */ } /* * hidparser_GlobalItem: * Implements the Rule: * GlobalItem -> UsagePage * | LogicalMinimum * | LocgicalMaximum * | PhysicalMinimum * | PhysicalMaximum * | Unit * | Exponent * | ReportSize * | ReportCount * | ReportID */ 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: /* Error check */ for (i = 0; i < scan_ifp->hidparser_tok_leng; i++) { /* Undefined data value: 0 */ 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"); } /* Reserved values 0x0A-0xFE */ 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: /* FALLTHRU */ case R_ITEM_EXPONENT: /* * Error check: * Nibble 7 should be zero */ 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: /* * Error Check: * Report Count should be nonzero */ 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: /* * Error check: * Report Id should be nonzero & <= 255 */ 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 { /* Free the current global list */ 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); /*NOTREACHED*/ } 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); } /* * hidparser_LocalItem: * Implements the Rule: * LocalItem -> Usage * | UsageMinimum * | UsageMaximum * | DesignatorIndex * | DesignatorMinimum * | StringIndex * | StringMinimum * | StringMaximum */ static int hidparser_LocalItem(hidparser_tok_t *scan_ifp) { int i; switch (scan_ifp->hidparser_tok_token) { case R_ITEM_USAGE: /* * Error Check: * Data Field should be nonzero */ 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"); } /* FALLTHRU */ case R_ITEM_USAGE_MIN: /* FALLTHRU */ case R_ITEM_USAGE_MAX: /* FALLTHRU */ case R_ITEM_DESIGNATOR_INDEX: /* FALLTHRU */ case R_ITEM_DESIGNATOR_MIN: /* FALLTHRU */ case R_ITEM_STRING_INDEX: /* FALLTHRU */ case R_ITEM_STRING_MIN: /* FALLTHRU */ case R_ITEM_STRING_MAX: /* FALLTHRU */ 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); /*NOTREACHED*/ default: break; } return (HIDPARSER_FAILURE); } /* * hidparser_allocate_entity: * Allocate Item of type 'type', length 'leng' and * params 'text'. Fill in the attributes allocated * so far from both the local and global item lists. * Make the child and sibling of the item NULL. */ 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); } /* * Copy attributes from entity attribute state table if not * end collection. */ if (entity_type != R_ITEM_END_COLLECTION) { entity->entity_item_attributes = hidparser_cp_attribute_list( scan_ifp->hidparser_tok_gitem_head); /* * append the control attributes, then clear out the control * attribute state table list */ 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); } /* * hidparser_add_attribute: * Add an attribute to the global or local item list * If the last 4th bit from right is 1, add to the local item list * Else add to the global item list */ 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; /* * remove attribute if it is already on list, except * for control attributes(local items), as we could have * multiple usages... * unless we want to hassle with checking for unique parameters. */ 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; } } /* create new attribute for this entry */ 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; /* attach to end of list */ *previous = newattrib; } /* * hidparser_alloc_attrib_list: * Allocate space for n attributes , create a linked list and * return the head */ 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); } /* * hidparser_cp_attribute_list: * Copies the Global item list pointed to by head * We create a clone of the global item list here * because we want to retain the Global items to * the next Main Item. */ 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); } /* * hidparser_find_attribute_end: * Find the last item in the attribute list pointed to by head */ 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); } /* * hidparser_free_report_descr_handle: * Free the parse tree pointed to by handle */ 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; } } /* * hidparser_free_attribute_list: * Free the attribute list pointed to by head */ 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; } } /* * hidparser_initialize_items: * Initialize items array before start scanning and parsing. * This array of strings are used for printing purpose. */ 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"; } /* * hidparser_scan: * This function scans the input entity descriptor, sees the data * length, returns the next token, data bytes and length in the * scan_ifp structure. */ 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++; /* * Error checking: * Unrecognized items should be passed over * by the parser. * Section 5.4 */ 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); /* 3 really means 4.. see p.21 HID */ 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; /* EOF */ } } /* * hidparser_report_err: * Construct and print the error code * Ref: Hidview error check list */ 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); } } /* * hidparser_isvalid_item: * Find if the item tag is a valid one */ 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); } } /* * hidparser_lookup_attribute: * Takes an item pointer(report structure) and a tag(e.g Logical * Min) as input. Returns the corresponding attribute structure. * Presently used for error checking only. */ 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); } /* * hidparser_global_err_check: * Error checking for Global Items that need to be * performed in MainItem */ 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"); } /* * hidparser_mainitem_err_check: * Error checking for Main Items */ 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; } /* switch */ attr = attr->entity_attribute_next; } /* while */ } /* if */ 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"); } } /* * hidparser_local_err_check: * Error checking for Local items that is done when a MainItem * is encountered */ 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); } /* * hidparser_find_unsigned_val: * Find the value for multibyte data * Ref: Section 5.8 of HID Spec 1.0 */ 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); } /* * hidparser_find_signed_val: * Find the value for signed multibyte data * Ref: Section 5.8 of HID Spec 1.0 */ 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); } /* * hidparser_check_correspondence: * Check if the item item2 corresponding to item1 exists and vice versa * If not report the appropriate error */ 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); } } /* * hidparser_check_minmax_val: * Check if the Min value <= Max and vice versa * Print for warnings and errors have been taken care separately. */ 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"); } } } /* if (temp1 != NULL) && (temp2 != NULL) */ } /* * hidparser_check_minmax_val_signed: * Check if the Min value <= Max and vice versa * Print for warnings and errors have been taken care separately. */ 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"); } } } /* if (temp1 != NULL) && (temp2 != NULL) */ } /* * hidparser_error_delim: * Error check for Delimiter Sets */ 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; } } /* * hidparser_find_max_packet_size_from_report_descriptor: * find packet size of the largest report in the report descriptor */ 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"); /* get a list of input reports */ 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)) { /* * since no report id is used, get the packet size * for the only report available */ (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 { /* * hid device uses multiple reports with report id prefix byte. * Find the longest input report. * See HID 8.4. */ 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); } }