hidclass.sys: Use hidparse.sys instead of internal parser.
Signed-off-by: Rémi Bernon <rbernon@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
a290c5bf7c
commit
de0fb2adfd
|
@ -1,10 +1,9 @@
|
||||||
MODULE = hidclass.sys
|
MODULE = hidclass.sys
|
||||||
IMPORTLIB = hidclass
|
IMPORTLIB = hidclass
|
||||||
IMPORTS = hal ntoskrnl user32
|
IMPORTS = hal ntoskrnl user32 hidparse
|
||||||
|
|
||||||
EXTRADLLFLAGS = -mno-cygwin
|
EXTRADLLFLAGS = -mno-cygwin
|
||||||
|
|
||||||
C_SRCS = \
|
C_SRCS = \
|
||||||
descriptor.c \
|
|
||||||
device.c \
|
device.c \
|
||||||
pnp.c
|
pnp.c
|
||||||
|
|
|
@ -1,602 +0,0 @@
|
||||||
/*
|
|
||||||
* HID descriptor parsing
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Aric Stewart
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "hid.h"
|
|
||||||
|
|
||||||
#include "wine/debug.h"
|
|
||||||
#include "wine/list.h"
|
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(hid);
|
|
||||||
|
|
||||||
/* 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(hid))
|
|
||||||
{
|
|
||||||
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 )
|
|
||||||
{
|
|
||||||
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 = calloc( 1, size ))) return NULL;
|
|
||||||
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 )
|
|
||||||
{
|
|
||||||
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( hid ))
|
|
||||||
{
|
|
||||||
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 = calloc( 1, 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 ))) debug_print_preparsed( data );
|
|
||||||
|
|
||||||
done:
|
|
||||||
free_parser_state( state );
|
|
||||||
return data;
|
|
||||||
}
|
|
|
@ -181,7 +181,7 @@ static struct hid_report *hid_report_queue_pop( struct hid_report_queue *queue )
|
||||||
static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *packet )
|
static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *packet )
|
||||||
{
|
{
|
||||||
BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
|
BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
|
||||||
struct hid_preparsed_data *preparsed = ext->u.pdo.preparsed_data;
|
HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
|
||||||
struct hid_report *last_report, *report;
|
struct hid_report *last_report, *report;
|
||||||
struct hid_report_queue *queue;
|
struct hid_report_queue *queue;
|
||||||
RAWINPUT *rawinput;
|
RAWINPUT *rawinput;
|
||||||
|
@ -228,7 +228,7 @@ static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *pack
|
||||||
queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
|
queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
|
||||||
|
|
||||||
if (!(report = hid_report_queue_pop( queue ))) hid_report_incref( (report = last_report) );
|
if (!(report = hid_report_queue_pop( queue ))) hid_report_incref( (report = last_report) );
|
||||||
memcpy( irp->AssociatedIrp.SystemBuffer, report->buffer, preparsed->caps.InputReportByteLength );
|
memcpy( irp->AssociatedIrp.SystemBuffer, report->buffer, desc->InputLength );
|
||||||
irp->IoStatus.Information = report->length;
|
irp->IoStatus.Information = report->length;
|
||||||
irp->IoStatus.Status = STATUS_SUCCESS;
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||||
hid_report_decref( report );
|
hid_report_decref( report );
|
||||||
|
@ -243,25 +243,34 @@ static DWORD CALLBACK hid_device_thread(void *args)
|
||||||
{
|
{
|
||||||
DEVICE_OBJECT *device = (DEVICE_OBJECT*)args;
|
DEVICE_OBJECT *device = (DEVICE_OBJECT*)args;
|
||||||
BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
|
BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
|
||||||
struct hid_preparsed_data *preparsed = ext->u.pdo.preparsed_data;
|
HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
|
||||||
BYTE report_id = HID_INPUT_VALUE_CAPS( preparsed )->report_id;
|
HIDP_REPORT_IDS *reports = ext->u.pdo.device_desc.ReportIDs;
|
||||||
ULONG buffer_len = preparsed->caps.InputReportByteLength;
|
ULONG report_count = ext->u.pdo.device_desc.ReportIDsLength;
|
||||||
|
ULONG i, report_id = 0, poll_interval = 0;
|
||||||
HID_XFER_PACKET *packet;
|
HID_XFER_PACKET *packet;
|
||||||
ULONG poll_interval = 0;
|
|
||||||
IO_STATUS_BLOCK io;
|
IO_STATUS_BLOCK io;
|
||||||
BYTE *buffer;
|
BYTE *buffer;
|
||||||
DWORD res;
|
DWORD res;
|
||||||
|
|
||||||
packet = malloc( sizeof(*packet) + buffer_len );
|
packet = malloc( sizeof(*packet) + desc->InputLength );
|
||||||
buffer = (BYTE *)(packet + 1);
|
buffer = (BYTE *)(packet + 1);
|
||||||
packet->reportBuffer = buffer;
|
packet->reportBuffer = buffer;
|
||||||
|
|
||||||
if (ext->u.pdo.information.Polled) poll_interval = ext->u.pdo.poll_interval;
|
if (ext->u.pdo.information.Polled) poll_interval = ext->u.pdo.poll_interval;
|
||||||
|
|
||||||
|
for (i = 0; i < report_count; ++i)
|
||||||
|
{
|
||||||
|
if (!reports[i].ReportID || reports[i].InputLength)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == report_count) WARN("no input report found.\n");
|
||||||
|
else report_id = reports[i].ReportID;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
packet->reportId = buffer[0] = report_id;
|
packet->reportId = buffer[0] = report_id;
|
||||||
packet->reportBufferLen = buffer_len;
|
packet->reportBufferLen = desc->InputLength;
|
||||||
|
|
||||||
if (!report_id)
|
if (!report_id)
|
||||||
{
|
{
|
||||||
|
@ -318,18 +327,18 @@ static void handle_IOCTL_HID_GET_COLLECTION_INFORMATION( IRP *irp, BASE_DEVICE_E
|
||||||
|
|
||||||
static void handle_IOCTL_HID_GET_COLLECTION_DESCRIPTOR( IRP *irp, BASE_DEVICE_EXTENSION *ext )
|
static void handle_IOCTL_HID_GET_COLLECTION_DESCRIPTOR( IRP *irp, BASE_DEVICE_EXTENSION *ext )
|
||||||
{
|
{
|
||||||
|
HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
|
||||||
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
|
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
|
||||||
struct hid_preparsed_data *preparsed = ext->u.pdo.preparsed_data;
|
|
||||||
|
|
||||||
if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < preparsed->size)
|
if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < desc->PreparsedDataLength)
|
||||||
{
|
{
|
||||||
irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
|
irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
|
||||||
irp->IoStatus.Information = 0;
|
irp->IoStatus.Information = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memcpy( irp->UserBuffer, preparsed, preparsed->size );
|
memcpy( irp->UserBuffer, desc->PreparsedData, desc->PreparsedDataLength );
|
||||||
irp->IoStatus.Information = preparsed->size;
|
irp->IoStatus.Information = desc->PreparsedDataLength;
|
||||||
irp->IoStatus.Status = STATUS_SUCCESS;
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -357,10 +366,10 @@ static void handle_minidriver_string( BASE_DEVICE_EXTENSION *ext, IRP *irp, SHOR
|
||||||
|
|
||||||
static void hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP *irp )
|
static void hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP *irp )
|
||||||
{
|
{
|
||||||
struct hid_preparsed_data *preparsed = ext->u.pdo.preparsed_data;
|
HIDP_REPORT_IDS *reports = ext->u.pdo.device_desc.ReportIDs;
|
||||||
|
ULONG report_count = ext->u.pdo.device_desc.ReportIDsLength;
|
||||||
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp );
|
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp );
|
||||||
struct hid_value_caps *caps = NULL, *caps_end = NULL;
|
ULONG i, report_len = 0, buffer_len = 0;
|
||||||
ULONG report_len = 0, buffer_len = 0;
|
|
||||||
HID_XFER_PACKET packet;
|
HID_XFER_PACKET packet;
|
||||||
BYTE *buffer = NULL;
|
BYTE *buffer = NULL;
|
||||||
|
|
||||||
|
@ -381,41 +390,38 @@ static void hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP
|
||||||
buffer = irp->AssociatedIrp.SystemBuffer;
|
buffer = irp->AssociatedIrp.SystemBuffer;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (code)
|
|
||||||
{
|
|
||||||
case IOCTL_HID_GET_INPUT_REPORT:
|
|
||||||
report_len = preparsed->caps.InputReportByteLength;
|
|
||||||
caps = HID_INPUT_VALUE_CAPS( preparsed );
|
|
||||||
caps_end = caps + preparsed->value_caps_count[HidP_Input];
|
|
||||||
break;
|
|
||||||
case IOCTL_HID_SET_OUTPUT_REPORT:
|
|
||||||
case IOCTL_HID_WRITE_REPORT:
|
|
||||||
report_len = preparsed->caps.OutputReportByteLength;
|
|
||||||
caps = HID_OUTPUT_VALUE_CAPS( preparsed );
|
|
||||||
caps_end = caps + preparsed->value_caps_count[HidP_Output];
|
|
||||||
break;
|
|
||||||
case IOCTL_HID_GET_FEATURE:
|
|
||||||
case IOCTL_HID_SET_FEATURE:
|
|
||||||
report_len = preparsed->caps.FeatureReportByteLength;
|
|
||||||
caps = HID_FEATURE_VALUE_CAPS( preparsed );
|
|
||||||
caps_end = caps + preparsed->value_caps_count[HidP_Feature];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!buffer || !buffer_len)
|
if (!buffer || !buffer_len)
|
||||||
{
|
{
|
||||||
irp->IoStatus.Status = STATUS_INVALID_USER_BUFFER;
|
irp->IoStatus.Status = STATUS_INVALID_USER_BUFFER;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (buffer_len < report_len)
|
|
||||||
|
for (i = 0; i < report_count; ++i)
|
||||||
|
{
|
||||||
|
if (!reports[i].ReportID || reports[i].ReportID == buffer[0])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == report_count)
|
||||||
{
|
{
|
||||||
irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; caps != caps_end; ++caps) if (!caps->report_id || caps->report_id == buffer[0]) break;
|
switch (code)
|
||||||
if (caps == caps_end)
|
{
|
||||||
|
case IOCTL_HID_GET_INPUT_REPORT:
|
||||||
|
report_len = reports[i].InputLength;
|
||||||
|
break;
|
||||||
|
case IOCTL_HID_SET_OUTPUT_REPORT:
|
||||||
|
case IOCTL_HID_WRITE_REPORT:
|
||||||
|
report_len = reports[i].OutputLength;
|
||||||
|
break;
|
||||||
|
case IOCTL_HID_GET_FEATURE:
|
||||||
|
case IOCTL_HID_SET_FEATURE:
|
||||||
|
report_len = reports[i].FeatureLength;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (buffer_len < report_len)
|
||||||
{
|
{
|
||||||
irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
||||||
return;
|
return;
|
||||||
|
@ -425,7 +431,7 @@ static void hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP
|
||||||
packet.reportBuffer = buffer;
|
packet.reportBuffer = buffer;
|
||||||
packet.reportBufferLen = buffer_len;
|
packet.reportBufferLen = buffer_len;
|
||||||
|
|
||||||
if (!caps->report_id)
|
if (!reports[i].ReportID)
|
||||||
{
|
{
|
||||||
packet.reportId = 0;
|
packet.reportId = 0;
|
||||||
packet.reportBuffer++;
|
packet.reportBuffer++;
|
||||||
|
@ -575,7 +581,7 @@ NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp)
|
||||||
{
|
{
|
||||||
struct hid_report_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
|
struct hid_report_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
|
||||||
BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
|
BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
|
||||||
struct hid_preparsed_data *preparsed = ext->u.pdo.preparsed_data;
|
HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
|
||||||
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
|
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
|
@ -593,7 +599,7 @@ NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp)
|
||||||
return STATUS_DELETE_PENDING;
|
return STATUS_DELETE_PENDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (irpsp->Parameters.Read.Length < preparsed->caps.InputReportByteLength)
|
if (irpsp->Parameters.Read.Length < desc->InputLength)
|
||||||
{
|
{
|
||||||
irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
|
irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
|
||||||
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
||||||
|
@ -603,7 +609,7 @@ NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp)
|
||||||
irp->IoStatus.Information = 0;
|
irp->IoStatus.Information = 0;
|
||||||
if ((report = hid_report_queue_pop( queue )))
|
if ((report = hid_report_queue_pop( queue )))
|
||||||
{
|
{
|
||||||
memcpy( irp->AssociatedIrp.SystemBuffer, report->buffer, preparsed->caps.InputReportByteLength );
|
memcpy( irp->AssociatedIrp.SystemBuffer, report->buffer, desc->InputLength );
|
||||||
irp->IoStatus.Information = report->length;
|
irp->IoStatus.Information = report->length;
|
||||||
irp->IoStatus.Status = STATUS_SUCCESS;
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||||
hid_report_decref( report );
|
hid_report_decref( report );
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "ddk/hidport.h"
|
#include "ddk/hidport.h"
|
||||||
#include "ddk/hidclass.h"
|
#include "ddk/hidclass.h"
|
||||||
#include "ddk/hidpi.h"
|
#include "ddk/hidpi.h"
|
||||||
|
#include "ddk/hidpddi.h"
|
||||||
#include "cfgmgr32.h"
|
#include "cfgmgr32.h"
|
||||||
#include "wine/list.h"
|
#include "wine/list.h"
|
||||||
#include "wine/hid.h"
|
#include "wine/hid.h"
|
||||||
|
@ -54,7 +55,7 @@ typedef struct _BASE_DEVICE_EXTENSION
|
||||||
DEVICE_OBJECT *parent_fdo;
|
DEVICE_OBJECT *parent_fdo;
|
||||||
|
|
||||||
HID_COLLECTION_INFORMATION information;
|
HID_COLLECTION_INFORMATION information;
|
||||||
struct hid_preparsed_data *preparsed_data;
|
HIDP_DEVICE_DESC device_desc;
|
||||||
|
|
||||||
ULONG poll_interval;
|
ULONG poll_interval;
|
||||||
HANDLE halt_event;
|
HANDLE halt_event;
|
||||||
|
@ -129,6 +130,3 @@ NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN;
|
||||||
NTSTATUS WINAPI pdo_write(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN;
|
NTSTATUS WINAPI pdo_write(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN;
|
||||||
NTSTATUS WINAPI pdo_create(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN;
|
NTSTATUS WINAPI pdo_create(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN;
|
||||||
NTSTATUS WINAPI pdo_close(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN;
|
NTSTATUS WINAPI pdo_close(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN;
|
||||||
|
|
||||||
/* Parsing HID Report Descriptors into preparsed data */
|
|
||||||
struct hid_preparsed_data *parse_descriptor( BYTE *descriptor, unsigned int length ) DECLSPEC_HIDDEN;
|
|
||||||
|
|
|
@ -109,6 +109,7 @@ C_ASSERT(offsetof(RAWINPUT, data.hid.bRawData[2 * sizeof(USAGE)]) < sizeof(RAWIN
|
||||||
|
|
||||||
static void send_wm_input_device_change(BASE_DEVICE_EXTENSION *ext, LPARAM param)
|
static void send_wm_input_device_change(BASE_DEVICE_EXTENSION *ext, LPARAM param)
|
||||||
{
|
{
|
||||||
|
HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
|
||||||
RAWINPUT rawinput;
|
RAWINPUT rawinput;
|
||||||
INPUT input;
|
INPUT input;
|
||||||
|
|
||||||
|
@ -118,8 +119,8 @@ static void send_wm_input_device_change(BASE_DEVICE_EXTENSION *ext, LPARAM param
|
||||||
rawinput.header.wParam = param;
|
rawinput.header.wParam = param;
|
||||||
rawinput.data.hid.dwCount = 0;
|
rawinput.data.hid.dwCount = 0;
|
||||||
rawinput.data.hid.dwSizeHid = 0;
|
rawinput.data.hid.dwSizeHid = 0;
|
||||||
((USAGE *)rawinput.data.hid.bRawData)[0] = ext->u.pdo.preparsed_data->caps.UsagePage;
|
((USAGE *)rawinput.data.hid.bRawData)[0] = desc->UsagePage;
|
||||||
((USAGE *)rawinput.data.hid.bRawData)[1] = ext->u.pdo.preparsed_data->caps.Usage;
|
((USAGE *)rawinput.data.hid.bRawData)[1] = desc->Usage;
|
||||||
|
|
||||||
input.type = INPUT_HARDWARE;
|
input.type = INPUT_HARDWARE;
|
||||||
input.hi.uMsg = WM_INPUT_DEVICE_CHANGE;
|
input.hi.uMsg = WM_INPUT_DEVICE_CHANGE;
|
||||||
|
@ -183,6 +184,7 @@ static void create_child(minidriver *minidriver, DEVICE_OBJECT *fdo)
|
||||||
{
|
{
|
||||||
BASE_DEVICE_EXTENSION *fdo_ext = fdo->DeviceExtension, *pdo_ext;
|
BASE_DEVICE_EXTENSION *fdo_ext = fdo->DeviceExtension, *pdo_ext;
|
||||||
HID_DEVICE_ATTRIBUTES attr = {0};
|
HID_DEVICE_ATTRIBUTES attr = {0};
|
||||||
|
HIDP_COLLECTION_DESC *desc;
|
||||||
HID_DESCRIPTOR descriptor;
|
HID_DESCRIPTOR descriptor;
|
||||||
DEVICE_OBJECT *child_pdo;
|
DEVICE_OBJECT *child_pdo;
|
||||||
BYTE *reportDescriptor;
|
BYTE *reportDescriptor;
|
||||||
|
@ -253,19 +255,21 @@ static void create_child(minidriver *minidriver, DEVICE_OBJECT *fdo)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pdo_ext->u.pdo.preparsed_data = parse_descriptor( reportDescriptor, descriptor.DescriptorList[i].wReportLength );
|
io.Status = HidP_GetCollectionDescription( reportDescriptor, descriptor.DescriptorList[i].wReportLength,
|
||||||
|
PagedPool, &pdo_ext->u.pdo.device_desc );
|
||||||
free(reportDescriptor);
|
free(reportDescriptor);
|
||||||
if (!pdo_ext->u.pdo.preparsed_data)
|
if (io.Status != HIDP_STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
ERR("Cannot parse Report Descriptor\n");
|
ERR("Cannot parse Report Descriptor\n");
|
||||||
IoDeleteDevice(child_pdo);
|
IoDeleteDevice(child_pdo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pdo_ext->u.pdo.information.DescriptorSize = pdo_ext->u.pdo.preparsed_data->size;
|
desc = pdo_ext->u.pdo.device_desc.CollectionDesc;
|
||||||
|
pdo_ext->u.pdo.information.DescriptorSize = desc->PreparsedDataLength;
|
||||||
|
|
||||||
page = pdo_ext->u.pdo.preparsed_data->caps.UsagePage;
|
page = desc->UsagePage;
|
||||||
usage = pdo_ext->u.pdo.preparsed_data->caps.Usage;
|
usage = desc->Usage;
|
||||||
if (page == HID_USAGE_PAGE_GENERIC && usage == HID_USAGE_GENERIC_MOUSE)
|
if (page == HID_USAGE_PAGE_GENERIC && usage == HID_USAGE_GENERIC_MOUSE)
|
||||||
pdo_ext->u.pdo.rawinput_handle = WINE_MOUSE_HANDLE;
|
pdo_ext->u.pdo.rawinput_handle = WINE_MOUSE_HANDLE;
|
||||||
else if (page == HID_USAGE_PAGE_GENERIC && usage == HID_USAGE_GENERIC_KEYBOARD)
|
else if (page == HID_USAGE_PAGE_GENERIC && usage == HID_USAGE_GENERIC_KEYBOARD)
|
||||||
|
@ -374,6 +378,7 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp)
|
||||||
{
|
{
|
||||||
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
|
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
|
||||||
BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
|
BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
|
||||||
|
HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
|
||||||
NTSTATUS status = irp->IoStatus.Status;
|
NTSTATUS status = irp->IoStatus.Status;
|
||||||
KIRQL irql;
|
KIRQL irql;
|
||||||
|
|
||||||
|
@ -447,14 +452,12 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: This should probably be done in mouhid.sys. */
|
/* FIXME: This should probably be done in mouhid.sys. */
|
||||||
if (ext->u.pdo.preparsed_data->caps.UsagePage == HID_USAGE_PAGE_GENERIC
|
if (desc->UsagePage == HID_USAGE_PAGE_GENERIC && desc->Usage == HID_USAGE_GENERIC_MOUSE)
|
||||||
&& ext->u.pdo.preparsed_data->caps.Usage == HID_USAGE_GENERIC_MOUSE)
|
|
||||||
{
|
{
|
||||||
if (!IoRegisterDeviceInterface(device, &GUID_DEVINTERFACE_MOUSE, NULL, &ext->u.pdo.mouse_link_name))
|
if (!IoRegisterDeviceInterface(device, &GUID_DEVINTERFACE_MOUSE, NULL, &ext->u.pdo.mouse_link_name))
|
||||||
ext->u.pdo.is_mouse = TRUE;
|
ext->u.pdo.is_mouse = TRUE;
|
||||||
}
|
}
|
||||||
if (ext->u.pdo.preparsed_data->caps.UsagePage == HID_USAGE_PAGE_GENERIC
|
if (desc->UsagePage == HID_USAGE_PAGE_GENERIC && desc->Usage == HID_USAGE_GENERIC_KEYBOARD)
|
||||||
&& ext->u.pdo.preparsed_data->caps.Usage == HID_USAGE_GENERIC_KEYBOARD)
|
|
||||||
{
|
{
|
||||||
if (!IoRegisterDeviceInterface(device, &GUID_DEVINTERFACE_KEYBOARD, NULL, &ext->u.pdo.keyboard_link_name))
|
if (!IoRegisterDeviceInterface(device, &GUID_DEVINTERFACE_KEYBOARD, NULL, &ext->u.pdo.keyboard_link_name))
|
||||||
ext->u.pdo.is_keyboard = TRUE;
|
ext->u.pdo.is_keyboard = TRUE;
|
||||||
|
@ -488,7 +491,7 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp)
|
||||||
}
|
}
|
||||||
CloseHandle(ext->u.pdo.halt_event);
|
CloseHandle(ext->u.pdo.halt_event);
|
||||||
|
|
||||||
free(ext->u.pdo.preparsed_data);
|
HidP_FreeCollectionDescription(&ext->u.pdo.device_desc);
|
||||||
|
|
||||||
RtlFreeUnicodeString(&ext->u.pdo.link_name);
|
RtlFreeUnicodeString(&ext->u.pdo.link_name);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue