From: Matthias Gorzellik matthias.gorzellik@gmail.com
--- dlls/hidparse.sys/main.c | 133 ++++++++++++++++++++++++++------------- 1 file changed, 88 insertions(+), 45 deletions(-)
diff --git a/dlls/hidparse.sys/main.c b/dlls/hidparse.sys/main.c index feb5b3531a4..e46640ebe4b 100644 --- a/dlls/hidparse.sys/main.c +++ b/dlls/hidparse.sys/main.c @@ -647,67 +647,110 @@ done: NTSTATUS WINAPI HidP_GetCollectionDescription( PHIDP_REPORT_DESCRIPTOR report_desc, ULONG report_desc_len, POOL_TYPE pool_type, HIDP_DEVICE_DESC *device_desc ) { - ULONG i, len, report_count = 0, input_len[256] = {0}, output_len[256] = {0}, feature_len[256] = {0}; + ULONG i, len, report_count = 0, input_len[256] = {0}, output_len[256] = {0}, feature_len[256] = {0}, collection[256] = {0}; struct hid_value_caps *caps, *caps_end; struct hid_preparsed_data *preparsed; + BYTE *ptr, *end; + UINT32 size, depth = 0; + // FIXME: how many TLCs are allowed? + BYTE *toplevel_collections[10];
TRACE( "report_desc %p, report_desc_len %lu, pool_type %u, device_desc %p.\n", report_desc, report_desc_len, pool_type, device_desc );
memset( device_desc, 0, sizeof(*device_desc) );
- if (!(preparsed = parse_descriptor( report_desc, report_desc_len, pool_type ))) - return HIDP_STATUS_INTERNAL_ERROR; - - if (!(device_desc->CollectionDesc = ExAllocatePool( pool_type, sizeof(*device_desc->CollectionDesc) ))) + toplevel_collections[0] = report_desc; + for (ptr = report_desc, end = report_desc + report_desc_len; ptr != end; ptr += size + 1) { - free( preparsed ); - return STATUS_NO_MEMORY; - } + size = (*ptr & 0x03); + if (size == 3) size = 4; + if (ptr + size > end) + { + ERR( "Need %d bytes to read item value\n", size ); + return HIDP_STATUS_INTERNAL_ERROR; + }
- len = preparsed->caps_size + FIELD_OFFSET(struct hid_preparsed_data, value_caps[0]) + - preparsed->number_link_collection_nodes * sizeof(struct hid_collection_node); - - device_desc->CollectionDescLength = 1; - device_desc->CollectionDesc[0].UsagePage = preparsed->usage_page; - device_desc->CollectionDesc[0].Usage = preparsed->usage; - device_desc->CollectionDesc[0].CollectionNumber = 1; - device_desc->CollectionDesc[0].InputLength = preparsed->input_report_byte_length; - device_desc->CollectionDesc[0].OutputLength = preparsed->output_report_byte_length; - device_desc->CollectionDesc[0].FeatureLength = preparsed->feature_report_byte_length; - device_desc->CollectionDesc[0].PreparsedDataLength = len; - device_desc->CollectionDesc[0].PreparsedData = (PHIDP_PREPARSED_DATA)preparsed; - - caps = HID_INPUT_VALUE_CAPS( preparsed ); - caps_end = caps + preparsed->input_caps_end - preparsed->input_caps_start; - for (; caps != caps_end; ++caps) - { - len = caps->start_byte * 8 + caps->start_bit + caps->bit_size * caps->report_count; - if (!input_len[caps->report_id]) report_count++; - input_len[caps->report_id] = max(input_len[caps->report_id], len); +#define SHORT_ITEM( tag, type ) (((tag) << 4) | ((type) << 2)) + switch (*ptr & SHORT_ITEM( 0xf, 0x3 )) + { + case SHORT_ITEM( TAG_MAIN_COLLECTION, TAG_TYPE_MAIN ): + depth++; + break; + case SHORT_ITEM( TAG_MAIN_END_COLLECTION, TAG_TYPE_MAIN ): + if (--depth == 0) { + device_desc->CollectionDescLength++; + if (device_desc->CollectionDescLength < ARRAY_SIZE(toplevel_collections)) + toplevel_collections[device_desc->CollectionDescLength] = ptr + size + 1; + else { + ERR( "Too many toplevel collections." ); + return HIDP_STATUS_INTERNAL_ERROR; + } + } + break; + default: + break; + } }
- caps = HID_OUTPUT_VALUE_CAPS( preparsed ); - caps_end = caps + preparsed->output_caps_end - preparsed->output_caps_start; - for (; caps != caps_end; ++caps) - { - len = caps->start_byte * 8 + caps->start_bit + caps->bit_size * caps->report_count; - if (!input_len[caps->report_id] && !output_len[caps->report_id]) report_count++; - output_len[caps->report_id] = max(output_len[caps->report_id], len); - } + if (!(device_desc->CollectionDesc = ExAllocatePool( pool_type, device_desc->CollectionDescLength * sizeof(*device_desc->CollectionDesc) ))) + return STATUS_NO_MEMORY;
- caps = HID_FEATURE_VALUE_CAPS( preparsed ); - caps_end = caps + preparsed->feature_caps_end - preparsed->feature_caps_start; - for (; caps != caps_end; ++caps) - { - len = caps->start_byte * 8 + caps->start_bit + caps->bit_size * caps->report_count; - if (!input_len[caps->report_id] && !output_len[caps->report_id] && !feature_len[caps->report_id]) report_count++; - feature_len[caps->report_id] = max(feature_len[caps->report_id], len); + for (i = 0; i < device_desc->CollectionDescLength; ++i) { + + TRACE( "num tlcs %lu, tlc %lu, start %p, end %p\n", device_desc->CollectionDescLength, i, toplevel_collections[i], toplevel_collections[i+1]); + + if (!(preparsed = parse_descriptor( toplevel_collections[i], toplevel_collections[i+1] - toplevel_collections[i], pool_type ))) + return HIDP_STATUS_INTERNAL_ERROR; + + len = preparsed->caps_size + FIELD_OFFSET(struct hid_preparsed_data, value_caps[0]) + + preparsed->number_link_collection_nodes * sizeof(struct hid_collection_node); + + device_desc->CollectionDesc[i].UsagePage = preparsed->usage_page; + device_desc->CollectionDesc[i].Usage = preparsed->usage; + device_desc->CollectionDesc[i].CollectionNumber = i + 1; + device_desc->CollectionDesc[i].InputLength = preparsed->input_report_byte_length; + device_desc->CollectionDesc[i].OutputLength = preparsed->output_report_byte_length; + device_desc->CollectionDesc[i].FeatureLength = preparsed->feature_report_byte_length; + device_desc->CollectionDesc[i].PreparsedDataLength = len; + device_desc->CollectionDesc[i].PreparsedData = (PHIDP_PREPARSED_DATA)preparsed; + + caps = HID_INPUT_VALUE_CAPS( preparsed ); + caps_end = caps + preparsed->input_caps_end - preparsed->input_caps_start; + for (; caps != caps_end; ++caps) + { + len = caps->start_byte * 8 + caps->start_bit + caps->bit_size * caps->report_count; + if (!input_len[caps->report_id]) + report_count++; + input_len[caps->report_id] = max(input_len[caps->report_id], len); + collection[caps->report_id] = i; + } + + caps = HID_OUTPUT_VALUE_CAPS( preparsed ); + caps_end = caps + preparsed->output_caps_end - preparsed->output_caps_start; + for (; caps != caps_end; ++caps) + { + len = caps->start_byte * 8 + caps->start_bit + caps->bit_size * caps->report_count; + if (!input_len[caps->report_id] && !output_len[caps->report_id]) report_count++; + output_len[caps->report_id] = max(output_len[caps->report_id], len); + collection[caps->report_id] = i; + } + + caps = HID_FEATURE_VALUE_CAPS( preparsed ); + caps_end = caps + preparsed->feature_caps_end - preparsed->feature_caps_start; + for (; caps != caps_end; ++caps) + { + len = caps->start_byte * 8 + caps->start_bit + caps->bit_size * caps->report_count; + if (!input_len[caps->report_id] && !output_len[caps->report_id] && !feature_len[caps->report_id]) report_count++; + feature_len[caps->report_id] = max(feature_len[caps->report_id], len); + collection[caps->report_id] = i; + } }
if (!(device_desc->ReportIDs = ExAllocatePool( pool_type, sizeof(*device_desc->ReportIDs) * report_count ))) { - free( preparsed ); + for (i = 0; i < device_desc->CollectionDescLength; ++i) + free( device_desc->CollectionDesc[i].PreparsedData ); ExFreePool( device_desc->CollectionDesc ); return STATUS_NO_MEMORY; } @@ -716,7 +759,7 @@ NTSTATUS WINAPI HidP_GetCollectionDescription( PHIDP_REPORT_DESCRIPTOR report_de { if (!input_len[i] && !output_len[i] && !feature_len[i]) continue; device_desc->ReportIDs[report_count].ReportID = i; - device_desc->ReportIDs[report_count].CollectionNumber = 1; + device_desc->ReportIDs[report_count].CollectionNumber = collection[i] + 1; device_desc->ReportIDs[report_count].InputLength = (input_len[i] + 7) / 8; device_desc->ReportIDs[report_count].OutputLength = (output_len[i] + 7) / 8; device_desc->ReportIDs[report_count].FeatureLength = (feature_len[i] + 7) / 8;