hidparser.c revision 4610e4a00999c6d2291b3fc263926b890ec500a5
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#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>
/*
* 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 %I%"
};
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 entity_item_t *hidparser_find_item_end(entity_item_t *);
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 = temp * attribute->
entity_attribute_value[0];
if (foundcount == 1) {
if (report_id &&
right_report_id) {
break;
}
}
} else if (attribute->entity_attribute_tag ==
R_ITEM_REPORT_COUNT) {
foundcount = 1;
temp = temp * attribute->
entity_attribute_value[0];
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;
int i = 0;
int rval;
uint32_t usage, usage_min, usage_max;
hidparser_usage_info_t ui;
boolean_t delim_pre = B_FALSE;
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;
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_MIN:
found_usage_min = 1;
usage_min = hidparser_find_unsigned_val(
attribute);
ui.usage_min = HID_USAGE_ID(usage_min);
break;
case R_ITEM_USAGE_MAX:
found_usage_max = 1;
usage_max = hidparser_find_unsigned_val(
attribute);
ui.usage_max = HID_USAGE_ID(usage_max);
if (attribute->
entity_attribute_length == 3) {
ui.usage_page = HID_USAGE_PAGE(
usage_max);
}
break;
case R_ITEM_SET_DELIMITER:
delim_pre = B_TRUE;
attribute =
attribute->entity_attribute_next;
break;
}
/*
* If we have a usage item, and we had
* a report id match (or report ids
* are not present), 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)) &&
(attribute->entity_attribute_tag ==
R_ITEM_USAGE) &&
hidparser_find_unsigned_val(attribute)) {
/* Put in usage list */
if (rpt->no_of_usages >= USAGE_MAX) {
return (HIDPARSER_FAILURE);
}
hidparser_fill_usage_info(&ui,
current->entity_item_attributes);
ui.collection_usage = collection_usage;
usage = hidparser_find_unsigned_val(
attribute);
ui.usage_id = HID_USAGE_ID(usage);
if (attribute->
entity_attribute_length == 3) {
if (HID_USAGE_PAGE(usage))
ui.usage_page =
HID_USAGE_PAGE(
usage);
}
i = rpt->no_of_usages++;
rpt->usage_descr[i] = ui;
rpt->report_id = report_id;
valid_usage = 1;
if (delim_pre == B_TRUE) {
/* skip over alternate usages */
while (attribute->
entity_attribute_tag !=
R_ITEM_SET_DELIMITER) {
attribute = attribute->
entity_attribute_next;
}
delim_pre = B_FALSE;
}
}
/*
* If we have a usage min & max, and
* we had a report id match(or report
* ids are not present) put the usage
* range into the list.
*/
if ((!foundreportid || (foundreportid &&
right_report_id)) &&
found_usage_min && found_usage_max) {
/* Put in usage list */
if (rpt->no_of_usages >= USAGE_MAX) {
return (HIDPARSER_FAILURE);
}
hidparser_fill_usage_info(&ui,
current->entity_item_attributes);
ui.collection_usage = collection_usage;
i = rpt->no_of_usages++;
rpt->usage_descr[i] = ui;
rpt->report_id = report_id;
valid_usage = 1;
if (delim_pre == B_TRUE) {
/* skip over alternate usages */
while (attribute->
entity_attribute_tag !=
R_ITEM_SET_DELIMITER) {
attribute = attribute->
entity_attribute_next;
}
delim_pre = B_FALSE;
}
found_usage_min = found_usage_max = 0;
}
attribute = attribute->entity_attribute_next;
}
/*
* 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);
}
hidparser_fill_usage_info(&ui,
current->entity_item_attributes);
ui.collection_usage = collection_usage;
ui.usage_id = HID_USAGE_UNDEFINED;
i = rpt->no_of_usages++;
rpt->usage_descr[i] = ui;
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--; 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 <EOF>
*
* 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 <EOF>
*/
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%lx,"
" curr_ei = 0x%lx",
cache_ei, 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%lx, "
"curr_ei = 0x%lx",
cache_ei, 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->entity_item_type ==
R_ITEM_COLLECTION) {
USB_DPRINTF_L3(PRINT_MASK_ALL,
hparser_log_handle,
"Main Item: token = 0x%x, "
"curr_ei = 0x%lx "
"will be the child of prev_ei "
"= 0x%lx, "
"cache_ei being 0x%lx",
curr_ei->entity_item_type,
curr_ei, prev_ei, 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%lx "
"will be the right sibling of "
"prev_ei = 0x%lx, "
"cache_ei being 0x%lx",
curr_ei->entity_item_type,
curr_ei, prev_ei, 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%x 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%x 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%x 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_find_item_end:
* Search the siblings of items and find the last item in the list
*/
static entity_item_t *
hidparser_find_item_end(entity_item_t *head)
{
if (!head) {
return (NULL);
}
while (head->entity_item_right_sibling) {
head = head->entity_item_right_sibling;
}
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);
}
}