[PATCH 5/6] hidparse.sys: Implement HidP_(Get|Free)CollectionDescription.

Rémi Bernon rbernon at codeweavers.com
Mon Aug 16 03:07:52 CDT 2021


Copying HID report parsing code from hidclass.sys, and supporting only
one collection descriptor for now.

Signed-off-by: Rémi Bernon <rbernon at 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 );
 }
-- 
2.32.0




More information about the wine-devel mailing list