Copying HID report parsing code from hidclass.sys, and supporting only one collection descriptor for now.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/hidparse.sys/Makefile.in | 1 + dlls/hidparse.sys/main.c | 665 +++++++++++++++++++++++++++++++++- 2 files changed, 662 insertions(+), 4 deletions(-)
diff --git a/dlls/hidparse.sys/Makefile.in b/dlls/hidparse.sys/Makefile.in index 3bebabb9878..cdb2f1ce578 100644 --- a/dlls/hidparse.sys/Makefile.in +++ b/dlls/hidparse.sys/Makefile.in @@ -1,5 +1,6 @@ MODULE = hidparse.sys IMPORTLIB = hidparse +IMPORTS = ntoskrnl EXTRADLLFLAGS = -mno-cygwin PARENTSRC = ../hid
diff --git a/dlls/hidparse.sys/main.c b/dlls/hidparse.sys/main.c index b8edc3e1f6a..185cefbb3e5 100644 --- a/dlls/hidparse.sys/main.c +++ b/dlls/hidparse.sys/main.c @@ -19,6 +19,7 @@ */
#include <stdarg.h> +#include <stdlib.h>
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -30,20 +31,676 @@ #include <ddk/wdm.h> #include <ddk/hidpddi.h>
+#include "wine/hid.h" +#include "wine/list.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(hidp);
+/* Flags that are defined in the document + "Device Class Definition for Human Interface Devices" */ +enum +{ + INPUT_DATA_CONST = 0x01, /* Data (0) | Constant (1) */ + INPUT_ARRAY_VAR = 0x02, /* Array (0) | Variable (1) */ + INPUT_ABS_REL = 0x04, /* Absolute (0) | Relative (1) */ + INPUT_WRAP = 0x08, /* No Wrap (0) | Wrap (1) */ + INPUT_LINEAR = 0x10, /* Linear (0) | Non Linear (1) */ + INPUT_PREFSTATE = 0x20, /* Preferred State (0) | No Preferred (1) */ + INPUT_NULL = 0x40, /* No Null position (0) | Null state(1) */ + INPUT_VOLATILE = 0x80, /* Non Volatile (0) | Volatile (1) */ + INPUT_BITFIELD = 0x100 /* Bit Field (0) | Buffered Bytes (1) */ +}; + +enum +{ + TAG_TYPE_MAIN = 0x0, + TAG_TYPE_GLOBAL, + TAG_TYPE_LOCAL, + TAG_TYPE_RESERVED, +}; + +enum +{ + TAG_MAIN_INPUT = 0x08, + TAG_MAIN_OUTPUT = 0x09, + TAG_MAIN_FEATURE = 0x0B, + TAG_MAIN_COLLECTION = 0x0A, + TAG_MAIN_END_COLLECTION = 0x0C +}; + +enum +{ + TAG_GLOBAL_USAGE_PAGE = 0x0, + TAG_GLOBAL_LOGICAL_MINIMUM, + TAG_GLOBAL_LOGICAL_MAXIMUM, + TAG_GLOBAL_PHYSICAL_MINIMUM, + TAG_GLOBAL_PHYSICAL_MAXIMUM, + TAG_GLOBAL_UNIT_EXPONENT, + TAG_GLOBAL_UNIT, + TAG_GLOBAL_REPORT_SIZE, + TAG_GLOBAL_REPORT_ID, + TAG_GLOBAL_REPORT_COUNT, + TAG_GLOBAL_PUSH, + TAG_GLOBAL_POP +}; + +enum +{ + TAG_LOCAL_USAGE = 0x0, + TAG_LOCAL_USAGE_MINIMUM, + TAG_LOCAL_USAGE_MAXIMUM, + TAG_LOCAL_DESIGNATOR_INDEX, + TAG_LOCAL_DESIGNATOR_MINIMUM, + TAG_LOCAL_DESIGNATOR_MAXIMUM, + TAG_LOCAL_STRING_INDEX, + TAG_LOCAL_STRING_MINIMUM, + TAG_LOCAL_STRING_MAXIMUM, + TAG_LOCAL_DELIMITER +}; + +static inline const char *debugstr_hid_value_caps( struct hid_value_caps *caps ) +{ + if (!caps) return "(null)"; + return wine_dbg_sprintf( "RId %d, Usg %02x:%02x-%02x Dat %02x-%02x (%d), Str %d-%d (%d), Des %d-%d (%d), " + "Bits %02x, LCol %d LUsg %02x:%02x, BitSz %d, RCnt %d, Unit %x E%+d, Log %+d-%+d, Phy %+d-%+d", + caps->report_id, caps->usage_page, caps->usage_min, caps->usage_max, caps->data_index_min, caps->data_index_max, caps->is_range, + caps->string_min, caps->string_max, caps->is_string_range, caps->designator_min, caps->designator_max, caps->is_designator_range, + caps->bit_field, caps->link_collection, caps->link_usage_page, caps->link_usage, caps->bit_size, caps->report_count, + caps->units, caps->units_exp, caps->logical_min, caps->logical_max, caps->physical_min, caps->physical_max ); +} + +static void debug_print_preparsed( struct hid_preparsed_data *data ) +{ + unsigned int i, end; + + if (TRACE_ON( hidp )) + { + TRACE( "START PREPARSED Data <<< Usage: %i, UsagePage: %i, " + "InputReportByteLength: %i, tOutputReportByteLength: %i, " + "FeatureReportByteLength: %i, NumberLinkCollectionNodes: %i, " + "NumberInputButtonCaps: %i, NumberInputValueCaps: %i, " + "NumberInputDataIndices: %i, NumberOutputButtonCaps: %i, " + "NumberOutputValueCaps: %i, NumberOutputDataIndices: %i, " + "NumberFeatureButtonCaps: %i, NumberFeatureValueCaps: %i, " + "NumberFeatureDataIndices: %i\n", + data->caps.Usage, data->caps.UsagePage, data->caps.InputReportByteLength, + data->caps.OutputReportByteLength, data->caps.FeatureReportByteLength, + data->caps.NumberLinkCollectionNodes, data->caps.NumberInputButtonCaps, + data->caps.NumberInputValueCaps, data->caps.NumberInputDataIndices, + data->caps.NumberOutputButtonCaps, data->caps.NumberOutputValueCaps, + data->caps.NumberOutputDataIndices, data->caps.NumberFeatureButtonCaps, + data->caps.NumberFeatureValueCaps, data->caps.NumberFeatureDataIndices ); + end = data->value_caps_count[HidP_Input]; + for (i = 0; i < end; i++) TRACE( "INPUT: %s\n", debugstr_hid_value_caps( HID_INPUT_VALUE_CAPS( data ) + i ) ); + end = data->value_caps_count[HidP_Output]; + for (i = 0; i < end; i++) TRACE( "OUTPUT: %s\n", debugstr_hid_value_caps( HID_OUTPUT_VALUE_CAPS( data ) + i ) ); + end = data->value_caps_count[HidP_Feature]; + for (i = 0; i < end; i++) TRACE( "FEATURE: %s\n", debugstr_hid_value_caps( HID_FEATURE_VALUE_CAPS( data ) + i ) ); + end = data->caps.NumberLinkCollectionNodes; + for (i = 0; i < end; i++) TRACE( "COLLECTION: %s\n", debugstr_hid_value_caps( HID_COLLECTION_VALUE_CAPS( data ) + i ) ); + TRACE(">>> END Preparsed Data\n"); + } +} + +struct hid_parser_state +{ + HIDP_CAPS caps; + + USAGE usages_page[256]; + USAGE usages_min[256]; + USAGE usages_max[256]; + DWORD usages_size; + + struct hid_value_caps items; + + struct hid_value_caps *stack; + DWORD stack_size; + DWORD global_idx; + DWORD collection_idx; + + struct hid_value_caps *collections; + DWORD collections_size; + + struct hid_value_caps *values[3]; + ULONG values_size[3]; + + ULONG bit_size[3][256]; + USHORT *byte_size[3]; /* pointers to caps */ + USHORT *value_idx[3]; /* pointers to caps */ + USHORT *data_idx[3]; /* pointers to caps */ +}; + +static BOOL array_reserve( struct hid_value_caps **array, DWORD *array_size, DWORD index ) +{ + if (index < *array_size) return TRUE; + if ((*array_size = *array_size ? (*array_size * 3 / 2) : 32) <= index) return FALSE; + if (!(*array = realloc( *array, *array_size * sizeof(**array) ))) return FALSE; + return TRUE; +} + +static void copy_global_items( struct hid_value_caps *dst, const struct hid_value_caps *src ) +{ + dst->usage_page = src->usage_page; + dst->logical_min = src->logical_min; + dst->logical_max = src->logical_max; + dst->physical_min = src->physical_min; + dst->physical_max = src->physical_max; + dst->units_exp = src->units_exp; + dst->units = src->units; + dst->bit_size = src->bit_size; + dst->report_id = src->report_id; + dst->report_count = src->report_count; +} + +static void copy_collection_items( struct hid_value_caps *dst, const struct hid_value_caps *src ) +{ + dst->link_collection = src->link_collection; + dst->link_usage_page = src->link_usage_page; + dst->link_usage = src->link_usage; +} + +static void reset_local_items( struct hid_parser_state *state ) +{ + struct hid_value_caps tmp; + copy_global_items( &tmp, &state->items ); + copy_collection_items( &tmp, &state->items ); + memset( &state->items, 0, sizeof(state->items) ); + copy_global_items( &state->items, &tmp ); + copy_collection_items( &state->items, &tmp ); + memset( &state->usages_page, 0, sizeof(state->usages_page) ); + memset( &state->usages_min, 0, sizeof(state->usages_min) ); + memset( &state->usages_max, 0, sizeof(state->usages_max) ); + state->usages_size = 0; +} + +static BOOL parse_global_push( struct hid_parser_state *state ) +{ + if (!array_reserve( &state->stack, &state->stack_size, state->global_idx )) + { + ERR( "HID parser stack overflow!\n" ); + return FALSE; + } + + copy_global_items( state->stack + state->global_idx, &state->items ); + state->global_idx++; + return TRUE; +} + +static BOOL parse_global_pop( struct hid_parser_state *state ) +{ + if (!state->global_idx) + { + ERR( "HID parser global stack underflow!\n" ); + return FALSE; + } + + state->global_idx--; + copy_global_items( &state->items, state->stack + state->global_idx ); + return TRUE; +} + +static BOOL parse_local_usage( struct hid_parser_state *state, USAGE usage_page, USAGE usage ) +{ + if (!usage_page) usage_page = state->items.usage_page; + if (state->items.is_range) state->usages_size = 0; + state->usages_page[state->usages_size] = usage_page; + state->usages_min[state->usages_size] = usage; + state->usages_max[state->usages_size] = usage; + state->items.usage_min = usage; + state->items.usage_max = usage; + state->items.is_range = FALSE; + if (state->usages_size++ == 255) ERR( "HID parser usages stack overflow!\n" ); + return state->usages_size <= 255; +} + +static void parse_local_usage_min( struct hid_parser_state *state, USAGE usage_page, USAGE usage ) +{ + if (!usage_page) usage_page = state->items.usage_page; + if (!state->items.is_range) state->usages_max[0] = 0; + state->usages_page[0] = usage_page; + state->usages_min[0] = usage; + state->items.usage_min = usage; + state->items.is_range = TRUE; + state->usages_size = 1; +} + +static void parse_local_usage_max( struct hid_parser_state *state, USAGE usage_page, USAGE usage ) +{ + if (!usage_page) usage_page = state->items.usage_page; + if (!state->items.is_range) state->usages_min[0] = 0; + state->usages_page[0] = usage_page; + state->usages_max[0] = usage; + state->items.usage_max = usage; + state->items.is_range = TRUE; + state->usages_size = 1; +} + +static BOOL parse_new_collection( struct hid_parser_state *state ) +{ + if (!array_reserve( &state->stack, &state->stack_size, state->collection_idx )) + { + ERR( "HID parser stack overflow!\n" ); + return FALSE; + } + + if (!array_reserve( &state->collections, &state->collections_size, state->caps.NumberLinkCollectionNodes )) + { + ERR( "HID parser collections overflow!\n" ); + return FALSE; + } + + copy_collection_items( state->stack + state->collection_idx, &state->items ); + state->collection_idx++; + + state->items.usage_min = state->usages_min[0]; + state->items.usage_max = state->usages_max[0]; + + state->collections[state->caps.NumberLinkCollectionNodes] = state->items; + state->items.link_collection = state->caps.NumberLinkCollectionNodes; + state->items.link_usage_page = state->items.usage_page; + state->items.link_usage = state->items.usage_min; + if (!state->caps.NumberLinkCollectionNodes) + { + state->caps.UsagePage = state->items.usage_page; + state->caps.Usage = state->items.usage_min; + } + state->caps.NumberLinkCollectionNodes++; + + reset_local_items( state ); + return TRUE; +} + +static BOOL parse_end_collection( struct hid_parser_state *state ) +{ + if (!state->collection_idx) + { + ERR( "HID parser collection stack underflow!\n" ); + return FALSE; + } + + state->collection_idx--; + copy_collection_items( &state->items, state->stack + state->collection_idx ); + reset_local_items( state ); + return TRUE; +} + +static BOOL parse_new_value_caps( struct hid_parser_state *state, HIDP_REPORT_TYPE type ) +{ + struct hid_value_caps *value; + USAGE usage_page = state->items.usage_page; + DWORD usages_size = max( 1, state->usages_size ); + USHORT *byte_size = state->byte_size[type]; + USHORT *value_idx = state->value_idx[type]; + USHORT *data_idx = state->data_idx[type]; + ULONG *bit_size = &state->bit_size[type][state->items.report_id]; + BOOL is_array; + + if (!*bit_size) *bit_size = 8; + *bit_size += state->items.bit_size * state->items.report_count; + *byte_size = max( *byte_size, (*bit_size + 7) / 8 ); + state->items.start_bit = *bit_size; + + if (!state->items.report_count) + { + reset_local_items( state ); + return TRUE; + } + + if (!array_reserve( &state->values[type], &state->values_size[type], *value_idx + usages_size )) + { + ERR( "HID parser values overflow!\n" ); + return FALSE; + } + value = state->values[type] + *value_idx; + + state->items.start_index = 0; + if (!(is_array = HID_VALUE_CAPS_IS_ARRAY( &state->items ))) state->items.report_count -= usages_size - 1; + else state->items.start_bit -= state->items.report_count * state->items.bit_size; + + while (usages_size--) + { + if (!is_array) state->items.start_bit -= state->items.report_count * state->items.bit_size; + else state->items.start_index += 1; + state->items.usage_page = state->usages_page[usages_size]; + state->items.usage_min = state->usages_min[usages_size]; + state->items.usage_max = state->usages_max[usages_size]; + state->items.data_index_min = *data_idx; + state->items.data_index_max = *data_idx + state->items.usage_max - state->items.usage_min; + if (state->items.usage_max || state->items.usage_min) *data_idx = state->items.data_index_max + 1; + *value++ = state->items; + *value_idx += 1; + if (!is_array) state->items.report_count = 1; + } + + state->items.usage_page = usage_page; + reset_local_items( state ); + return TRUE; +} + +static void init_parser_state( struct hid_parser_state *state ) +{ + memset( state, 0, sizeof(*state) ); + state->byte_size[HidP_Input] = &state->caps.InputReportByteLength; + state->byte_size[HidP_Output] = &state->caps.OutputReportByteLength; + state->byte_size[HidP_Feature] = &state->caps.FeatureReportByteLength; + + state->value_idx[HidP_Input] = &state->caps.NumberInputValueCaps; + state->value_idx[HidP_Output] = &state->caps.NumberOutputValueCaps; + state->value_idx[HidP_Feature] = &state->caps.NumberFeatureValueCaps; + + state->data_idx[HidP_Input] = &state->caps.NumberInputDataIndices; + state->data_idx[HidP_Output] = &state->caps.NumberOutputDataIndices; + state->data_idx[HidP_Feature] = &state->caps.NumberFeatureDataIndices; +} + +static void free_parser_state( struct hid_parser_state *state ) +{ + if (state->global_idx) ERR( "%u unpopped device caps on the stack\n", state->global_idx ); + if (state->collection_idx) ERR( "%u unpopped device collection on the stack\n", state->collection_idx ); + free( state->stack ); + free( state->collections ); + free( state->values[HidP_Input] ); + free( state->values[HidP_Output] ); + free( state->values[HidP_Feature] ); + free( state ); +} + +static struct hid_preparsed_data *build_preparsed_data( struct hid_parser_state *state, POOL_TYPE pool_type ) +{ + struct hid_preparsed_data *data; + struct hid_value_caps *caps; + DWORD i, button, filler, caps_len, size; + + caps_len = state->caps.NumberInputValueCaps + state->caps.NumberOutputValueCaps + + state->caps.NumberFeatureValueCaps + state->caps.NumberLinkCollectionNodes; + size = FIELD_OFFSET( struct hid_preparsed_data, value_caps[caps_len] ); + + if (!(data = ExAllocatePool( pool_type, size ))) return NULL; + memset( data, 0, size ); + data->magic = HID_MAGIC; + data->size = size; + data->caps = state->caps; + data->value_caps_count[HidP_Input] = state->caps.NumberInputValueCaps; + data->value_caps_count[HidP_Output] = state->caps.NumberOutputValueCaps; + data->value_caps_count[HidP_Feature] = state->caps.NumberFeatureValueCaps; + + /* fixup value vs button vs filler counts */ + + caps = HID_INPUT_VALUE_CAPS( data ); + memcpy( caps, state->values[0], data->caps.NumberInputValueCaps * sizeof(*caps) ); + for (i = 0, button = 0, filler = 0; i < data->caps.NumberInputValueCaps; ++i) + { + if (!caps[i].usage_min && !caps[i].usage_max) filler++; + else if (HID_VALUE_CAPS_IS_BUTTON( caps + i )) button++; + } + data->caps.NumberInputButtonCaps = button; + data->caps.NumberInputValueCaps -= filler + button; + + caps = HID_OUTPUT_VALUE_CAPS( data ); + memcpy( caps, state->values[1], data->caps.NumberOutputValueCaps * sizeof(*caps) ); + for (i = 0, button = 0, filler = 0; i < data->caps.NumberOutputValueCaps; ++i) + { + if (!caps[i].usage_min && !caps[i].usage_max) filler++; + else if (HID_VALUE_CAPS_IS_BUTTON( caps + i )) button++; + } + caps += data->caps.NumberOutputValueCaps; + data->caps.NumberOutputButtonCaps = button; + data->caps.NumberOutputValueCaps -= filler + button; + + caps = HID_FEATURE_VALUE_CAPS( data ); + memcpy( caps, state->values[2], data->caps.NumberFeatureValueCaps * sizeof(*caps) ); + for (i = 0, button = 0, filler = 0; i < data->caps.NumberFeatureValueCaps; ++i) + { + if (!caps[i].usage_min && !caps[i].usage_max) filler++; + else if (HID_VALUE_CAPS_IS_BUTTON( caps + i )) button++; + } + caps += data->caps.NumberFeatureValueCaps; + data->caps.NumberFeatureButtonCaps = button; + data->caps.NumberFeatureValueCaps -= filler + button; + + caps = HID_COLLECTION_VALUE_CAPS( data ); + memcpy( caps, state->collections, data->caps.NumberLinkCollectionNodes * sizeof(*caps) ); + + return data; +} + +struct hid_preparsed_data *parse_descriptor( BYTE *descriptor, unsigned int length, POOL_TYPE pool_type ) +{ + struct hid_preparsed_data *data = NULL; + struct hid_parser_state *state; + UINT32 size, value; + INT32 signed_value; + BYTE *ptr, *end; + int i; + + if (TRACE_ON( hidp )) + { + TRACE( "descriptor %p, length %u:\n", descriptor, length ); + for (i = 0; i < length;) + { + TRACE( "%08x ", i ); + do { TRACE( " %02x", descriptor[i] ); } while (++i % 16 && i < length); + TRACE( "\n" ); + } + } + + if (!(state = malloc( sizeof(*state) ))) return NULL; + init_parser_state( state ); + + for (ptr = descriptor, end = descriptor + length; ptr != end; ptr += size + 1) + { + size = (*ptr & 0x03); + if (size == 3) size = 4; + if (ptr + size > end) + { + ERR( "Need %d bytes to read item value\n", size ); + goto done; + } + + if (size == 0) signed_value = value = 0; + else if (size == 1) signed_value = (INT8)(value = *(UINT8 *)(ptr + 1)); + else if (size == 2) signed_value = (INT16)(value = *(UINT16 *)(ptr + 1)); + else if (size == 4) signed_value = (INT32)(value = *(UINT32 *)(ptr + 1)); + else + { + ERR( "Unexpected item value size %d.\n", size ); + goto done; + } + + state->items.bit_field = value; + +#define SHORT_ITEM( tag, type ) (((tag) << 4) | ((type) << 2)) + switch (*ptr & SHORT_ITEM( 0xf, 0x3 )) + { + case SHORT_ITEM( TAG_MAIN_INPUT, TAG_TYPE_MAIN ): + if (!parse_new_value_caps( state, HidP_Input )) goto done; + break; + case SHORT_ITEM( TAG_MAIN_OUTPUT, TAG_TYPE_MAIN ): + if (!parse_new_value_caps( state, HidP_Output )) goto done; + break; + case SHORT_ITEM( TAG_MAIN_FEATURE, TAG_TYPE_MAIN ): + if (!parse_new_value_caps( state, HidP_Feature )) goto done; + break; + case SHORT_ITEM( TAG_MAIN_COLLECTION, TAG_TYPE_MAIN ): + if (!parse_new_collection( state )) goto done; + break; + case SHORT_ITEM( TAG_MAIN_END_COLLECTION, TAG_TYPE_MAIN ): + if (!parse_end_collection( state )) goto done; + break; + + case SHORT_ITEM( TAG_GLOBAL_USAGE_PAGE, TAG_TYPE_GLOBAL ): + state->items.usage_page = value; + break; + case SHORT_ITEM( TAG_GLOBAL_LOGICAL_MINIMUM, TAG_TYPE_GLOBAL ): + state->items.logical_min = signed_value; + break; + case SHORT_ITEM( TAG_GLOBAL_LOGICAL_MAXIMUM, TAG_TYPE_GLOBAL ): + state->items.logical_max = signed_value; + break; + case SHORT_ITEM( TAG_GLOBAL_PHYSICAL_MINIMUM, TAG_TYPE_GLOBAL ): + state->items.physical_min = signed_value; + break; + case SHORT_ITEM( TAG_GLOBAL_PHYSICAL_MAXIMUM, TAG_TYPE_GLOBAL ): + state->items.physical_max = signed_value; + break; + case SHORT_ITEM( TAG_GLOBAL_UNIT_EXPONENT, TAG_TYPE_GLOBAL ): + state->items.units_exp = signed_value; + break; + case SHORT_ITEM( TAG_GLOBAL_UNIT, TAG_TYPE_GLOBAL ): + state->items.units = signed_value; + break; + case SHORT_ITEM( TAG_GLOBAL_REPORT_SIZE, TAG_TYPE_GLOBAL ): + state->items.bit_size = value; + break; + case SHORT_ITEM( TAG_GLOBAL_REPORT_ID, TAG_TYPE_GLOBAL ): + state->items.report_id = value; + break; + case SHORT_ITEM( TAG_GLOBAL_REPORT_COUNT, TAG_TYPE_GLOBAL ): + state->items.report_count = value; + break; + case SHORT_ITEM( TAG_GLOBAL_PUSH, TAG_TYPE_GLOBAL ): + if (!parse_global_push( state )) goto done; + break; + case SHORT_ITEM( TAG_GLOBAL_POP, TAG_TYPE_GLOBAL ): + if (!parse_global_pop( state )) goto done; + break; + + case SHORT_ITEM( TAG_LOCAL_USAGE, TAG_TYPE_LOCAL ): + if (!parse_local_usage( state, value >> 16, value & 0xffff )) goto done; + break; + case SHORT_ITEM( TAG_LOCAL_USAGE_MINIMUM, TAG_TYPE_LOCAL ): + parse_local_usage_min( state, value >> 16, value & 0xffff ); + break; + case SHORT_ITEM( TAG_LOCAL_USAGE_MAXIMUM, TAG_TYPE_LOCAL ): + parse_local_usage_max( state, value >> 16, value & 0xffff ); + break; + case SHORT_ITEM( TAG_LOCAL_DESIGNATOR_INDEX, TAG_TYPE_LOCAL ): + state->items.designator_min = state->items.designator_max = value; + state->items.is_designator_range = FALSE; + break; + case SHORT_ITEM( TAG_LOCAL_DESIGNATOR_MINIMUM, TAG_TYPE_LOCAL ): + state->items.designator_min = value; + state->items.is_designator_range = TRUE; + break; + case SHORT_ITEM( TAG_LOCAL_DESIGNATOR_MAXIMUM, TAG_TYPE_LOCAL ): + state->items.designator_max = value; + state->items.is_designator_range = TRUE; + break; + case SHORT_ITEM( TAG_LOCAL_STRING_INDEX, TAG_TYPE_LOCAL ): + state->items.string_min = state->items.string_max = value; + state->items.is_string_range = FALSE; + break; + case SHORT_ITEM( TAG_LOCAL_STRING_MINIMUM, TAG_TYPE_LOCAL ): + state->items.string_min = value; + state->items.is_string_range = TRUE; + break; + case SHORT_ITEM( TAG_LOCAL_STRING_MAXIMUM, TAG_TYPE_LOCAL ): + state->items.string_max = value; + state->items.is_string_range = TRUE; + break; + case SHORT_ITEM( TAG_LOCAL_DELIMITER, TAG_TYPE_LOCAL ): + FIXME( "delimiter %d not implemented!\n", value ); + goto done; + + default: + FIXME( "item type %x not implemented!\n", *ptr ); + goto done; + } +#undef SHORT_ITEM + } + + if ((data = build_preparsed_data( state, pool_type ))) debug_print_preparsed( data ); + +done: + free_parser_state( state ); + return data; +} + NTSTATUS WINAPI HidP_GetCollectionDescription( PHIDP_REPORT_DESCRIPTOR report_desc, ULONG report_desc_len, POOL_TYPE pool_type, HIDP_DEVICE_DESC *device_desc ) { - FIXME( "report_desc %p, report_desc_len %u, pool_type %u, device_desc %p stub!\n", - report_desc, report_desc_len, pool_type, device_desc ); + ULONG i, len, report_count = 0, input_len[256] = {0}, output_len[256] = {0}, feature_len[256] = {0}; + struct hid_value_caps *caps, *caps_end; + struct hid_preparsed_data *preparsed; + + TRACE( "report_desc %p, report_desc_len %u, 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) ))) + { + free( preparsed ); + return STATUS_NO_MEMORY; + }
- return STATUS_NOT_IMPLEMENTED; + device_desc->CollectionDescLength = 1; + device_desc->CollectionDesc[0].UsagePage = preparsed->caps.UsagePage; + device_desc->CollectionDesc[0].Usage = preparsed->caps.Usage; + device_desc->CollectionDesc[0].CollectionNumber = 1; + device_desc->CollectionDesc[0].InputLength = preparsed->caps.InputReportByteLength; + device_desc->CollectionDesc[0].OutputLength = preparsed->caps.OutputReportByteLength; + device_desc->CollectionDesc[0].FeatureLength = preparsed->caps.FeatureReportByteLength; + device_desc->CollectionDesc[0].PreparsedDataLength = preparsed->size; + device_desc->CollectionDesc[0].PreparsedData = (PHIDP_PREPARSED_DATA)preparsed; + + caps = HID_INPUT_VALUE_CAPS( preparsed ); + caps_end = caps + preparsed->value_caps_count[HidP_Input]; + for (; caps != caps_end; ++caps) + { + len = 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); + } + + caps = HID_OUTPUT_VALUE_CAPS( preparsed ); + caps_end = caps + preparsed->value_caps_count[HidP_Output]; + for (; caps != caps_end; ++caps) + { + len = 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); + } + + caps = HID_FEATURE_VALUE_CAPS( preparsed ); + caps_end = caps + preparsed->value_caps_count[HidP_Feature]; + for (; caps != caps_end; ++caps) + { + len = 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); + } + + if (!(device_desc->ReportIDs = ExAllocatePool( pool_type, sizeof(*device_desc->ReportIDs) * report_count ))) + { + free( preparsed ); + ExFreePool( device_desc->CollectionDesc ); + return STATUS_NO_MEMORY; + } + + for (i = 0, report_count = 0; i < 256; ++i) + { + 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].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; + report_count++; + } + device_desc->ReportIDsLength = report_count; + + return HIDP_STATUS_SUCCESS; }
void WINAPI HidP_FreeCollectionDescription( HIDP_DEVICE_DESC *device_desc ) { - FIXME( "device_desc %p stub!\n", device_desc ); + TRACE( "device_desc %p.\n", device_desc ); + + ExFreePool( device_desc->CollectionDesc ); + ExFreePool( device_desc->ReportIDs ); }