This adds a todo_wine, because although Wine didn't get the correct caps, some transient value was used incorrectly in Range.UsageMax, this is resetting local items as it should.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/hidclass.sys/descriptor.c | 124 ++++++++++++++++------------- dlls/ntoskrnl.exe/tests/ntoskrnl.c | 1 + 2 files changed, 71 insertions(+), 54 deletions(-)
diff --git a/dlls/hidclass.sys/descriptor.c b/dlls/hidclass.sys/descriptor.c index b66e9deb9a0..b57ab2a7da8 100644 --- a/dlls/hidclass.sys/descriptor.c +++ b/dlls/hidclass.sys/descriptor.c @@ -128,11 +128,6 @@ struct collection { struct list collections; };
-struct caps_stack { - struct list entry; - struct hid_value_caps caps; -}; - static inline const char *debugstr_hidp_value_caps( HIDP_VALUE_CAPS *caps ) { if (!caps) return "(null)"; @@ -298,17 +293,69 @@ struct hid_parser_state DWORD usages_size;
struct hid_value_caps items; + + struct hid_value_caps *stack; + DWORD stack_size; + DWORD global_idx; };
+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 reset_local_items( struct hid_parser_state *state ) { - state->items.is_range = FALSE; - state->items.is_string_range = FALSE; - state->items.is_designator_range = FALSE; - state->items.usage_min = FALSE; + struct hid_value_caps tmp; + copy_global_items( &tmp, &state->items ); + memset( &state->items, 0, sizeof(state->items) ); + copy_global_items( &state->items, &tmp ); 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 ) { state->usages[state->usages_size] = usage; @@ -317,6 +364,13 @@ static BOOL parse_local_usage( struct hid_parser_state *state, USAGE usage ) return state->usages_size <= 255; }
+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 ); + free( state->stack ); + free( state ); +} + static void parse_io_feature(unsigned int bSize, int itemVal, int bTag, unsigned int *feature_index, struct feature *feature) @@ -366,7 +420,7 @@ static void parse_collection(unsigned int bSize, int itemVal,
static int parse_descriptor( BYTE *descriptor, unsigned int index, unsigned int length, unsigned int *feature_index, unsigned int *collection_index, - struct collection *collection, struct hid_parser_state *state, struct list *stack ) + struct collection *collection, struct hid_parser_state *state ) { int i, j; UINT32 value; @@ -445,7 +499,7 @@ static int parse_descriptor( BYTE *descriptor, unsigned int index, unsigned int reset_local_items( state );
if ((i = parse_descriptor( descriptor, i, length, feature_index, collection_index, - subcollection, state, stack )) < 0) + subcollection, state )) < 0) return i; continue; } @@ -484,34 +538,11 @@ static int parse_descriptor( BYTE *descriptor, unsigned int index, unsigned int state->items.report_count = value; break; case SHORT_ITEM(TAG_GLOBAL_PUSH, TAG_TYPE_GLOBAL): - { - struct caps_stack *saved; - if (!(saved = malloc(sizeof(*saved)))) return -1; - saved->caps = state->items; - TRACE("Push\n"); - list_add_tail(stack, &saved->entry); + if (!parse_global_push( state )) return -1; break; - } case SHORT_ITEM(TAG_GLOBAL_POP, TAG_TYPE_GLOBAL): - { - struct list *tail; - struct caps_stack *saved; - TRACE("Pop\n"); - tail = list_tail(stack); - if (tail) - { - saved = LIST_ENTRY(tail, struct caps_stack, entry); - state->items = saved->caps; - list_remove(tail); - free(saved); - } - else - { - ERR("Pop but no stack!\n"); - return -1; - } + if (!parse_global_pop( state )) return -1; break; - }
case SHORT_ITEM(TAG_LOCAL_USAGE, TAG_TYPE_LOCAL): if (!parse_local_usage( state, value )) return -1; @@ -775,8 +806,6 @@ WINE_HIDP_PREPARSED_DATA* ParseDescriptor(BYTE *descriptor, unsigned int length) struct collection *base; int i;
- struct list caps_stack; - unsigned int feature_count = 0; unsigned int cidx;
@@ -791,8 +820,6 @@ WINE_HIDP_PREPARSED_DATA* ParseDescriptor(BYTE *descriptor, unsigned int length) } }
- list_init(&caps_stack); - if (!(state = calloc( 1, sizeof(*state) ))) return NULL; if (!(base = calloc( 1, sizeof(*base) ))) { @@ -804,30 +831,19 @@ WINE_HIDP_PREPARSED_DATA* ParseDescriptor(BYTE *descriptor, unsigned int length) list_init(&base->collections);
cidx = 0; - if (parse_descriptor( descriptor, 0, length, &feature_count, &cidx, base, state, &caps_stack ) < 0) + if (parse_descriptor( descriptor, 0, length, &feature_count, &cidx, base, state ) < 0) { free_collection(base); - free( state ); + free_parser_state( state ); return NULL; }
debug_collection(base);
- if (!list_empty(&caps_stack)) - { - struct caps_stack *entry, *cursor; - ERR("%i unpopped device caps on the stack\n", list_count(&caps_stack)); - LIST_FOR_EACH_ENTRY_SAFE(entry, cursor, &caps_stack, struct caps_stack, entry) - { - list_remove(&entry->entry); - free(entry); - } - } - if ((data = build_PreparseData(base, cidx))) debug_print_preparsed(data); free_collection(base);
- free( state ); + free_parser_state( state ); return data; } diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 735d1a0d996..ff1db96a06a 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -2093,6 +2093,7 @@ static void test_hidp(HANDLE file, int report_id) check_member(value_caps[4], value_caps[3], "%d", PhysicalMax); todo_wine check_member(value_caps[4], value_caps[3], "%04x", Range.UsageMin); + todo_wine check_member(value_caps[4], value_caps[3], "%04x", Range.UsageMax); check_member(value_caps[4], value_caps[3], "%d", Range.StringMin); check_member(value_caps[4], value_caps[3], "%d", Range.StringMax);