Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/hidclass.sys/device.c | 9 ++------- dlls/ntoskrnl.exe/tests/ntoskrnl.c | 27 ++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c index 6e82608aa2e..ed59a7794a3 100644 --- a/dlls/hidclass.sys/device.c +++ b/dlls/hidclass.sys/device.c @@ -535,13 +535,8 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) break; } poll_interval = *(ULONG *)irp->AssociatedIrp.SystemBuffer; - if (poll_interval <= MAX_POLL_INTERVAL_MSEC) - { - ext->u.pdo.poll_interval = poll_interval; - irp->IoStatus.Status = STATUS_SUCCESS; - } - else - irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + if (poll_interval) ext->u.pdo.poll_interval = min(poll_interval, MAX_POLL_INTERVAL_MSEC); + irp->IoStatus.Status = STATUS_SUCCESS; break; } case IOCTL_HID_GET_PRODUCT_STRING: diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 50e381c8983..f11dce1cc93 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -2833,7 +2833,21 @@ static void test_hid_device(DWORD report_id, DWORD polled) poll_freq = 500; SetLastError(0xdeadbeef); ret = sync_ioctl(file, IOCTL_HID_SET_POLL_FREQUENCY_MSEC, &poll_freq, sizeof(ULONG), NULL, &out_len); - ok(ret, "IOCTL_HID_GET_POLL_FREQUENCY_MSEC failed last error %u\n", GetLastError()); + ok(ret, "IOCTL_HID_SET_POLL_FREQUENCY_MSEC failed last error %u\n", GetLastError()); + ok(out_len == 0, "got out_len %u, expected 0\n", out_len); + + out_len = 0; + poll_freq = 10001; + SetLastError(0xdeadbeef); + ret = sync_ioctl(file, IOCTL_HID_SET_POLL_FREQUENCY_MSEC, &poll_freq, sizeof(ULONG), NULL, &out_len); + ok(ret, "IOCTL_HID_SET_POLL_FREQUENCY_MSEC failed last error %u\n", GetLastError()); + ok(out_len == 0, "got out_len %u, expected 0\n", out_len); + + out_len = 0; + poll_freq = 0; + SetLastError(0xdeadbeef); + ret = sync_ioctl(file, IOCTL_HID_SET_POLL_FREQUENCY_MSEC, &poll_freq, sizeof(ULONG), NULL, &out_len); + ok(ret, "IOCTL_HID_SET_POLL_FREQUENCY_MSEC failed last error %u\n", GetLastError()); ok(out_len == 0, "got out_len %u, expected 0\n", out_len);
out_len = sizeof(ULONG); @@ -2841,14 +2855,21 @@ static void test_hid_device(DWORD report_id, DWORD polled) ret = sync_ioctl(file, IOCTL_HID_GET_POLL_FREQUENCY_MSEC, NULL, 0, &poll_freq, &out_len); ok(ret, "IOCTL_HID_GET_POLL_FREQUENCY_MSEC failed last error %u\n", GetLastError()); ok(out_len == sizeof(ULONG), "got out_len %u, expected sizeof(ULONG)\n", out_len); - ok(poll_freq == 500, "got poll_freq %u, expected 100\n", poll_freq); + ok(poll_freq == 10000, "got poll_freq %u, expected 10000\n", poll_freq); + + out_len = 0; + poll_freq = 500; + SetLastError(0xdeadbeef); + ret = sync_ioctl(file, IOCTL_HID_SET_POLL_FREQUENCY_MSEC, &poll_freq, sizeof(ULONG), NULL, &out_len); + ok(ret, "IOCTL_HID_SET_POLL_FREQUENCY_MSEC failed last error %u\n", GetLastError()); + ok(out_len == 0, "got out_len %u, expected 0\n", out_len);
out_len = sizeof(ULONG); SetLastError(0xdeadbeef); ret = sync_ioctl(async_file, IOCTL_HID_GET_POLL_FREQUENCY_MSEC, NULL, 0, &poll_freq, &out_len); ok(ret, "IOCTL_HID_GET_POLL_FREQUENCY_MSEC failed last error %u\n", GetLastError()); ok(out_len == sizeof(ULONG), "got out_len %u, expected sizeof(ULONG)\n", out_len); - ok(poll_freq == 500, "got poll_freq %u, expected 100\n", poll_freq); + ok(poll_freq == 500, "got poll_freq %u, expected 500\n", poll_freq); }
test_hidp(file, async_file, report_id, polled);
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/hidclass.sys/device.c | 57 +++++++++----------------------------- 1 file changed, 13 insertions(+), 44 deletions(-)
diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c index ed59a7794a3..006b19ffa32 100644 --- a/dlls/hidclass.sys/device.c +++ b/dlls/hidclass.sys/device.c @@ -32,7 +32,6 @@ #include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(hid); -WINE_DECLARE_DEBUG_CHANNEL(hid_report);
IRP *pop_irp_from_queue(BASE_DEVICE_EXTENSION *ext) { @@ -282,8 +281,7 @@ static DWORD CALLBACK hid_device_thread(void *args) hid_device_queue_input( device, packet ); }
- rc = WaitForSingleObject(ext->u.pdo.halt_event, - ext->u.pdo.poll_interval ? ext->u.pdo.poll_interval : DEFAULT_POLL_INTERVAL); + rc = WaitForSingleObject(ext->u.pdo.halt_event, ext->u.pdo.poll_interval);
if (rc == WAIT_OBJECT_0) break; @@ -616,7 +614,6 @@ NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp) BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; struct hid_preparsed_data *preparsed = ext->u.pdo.preparsed_data; IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp); - BYTE report_id = HID_INPUT_VALUE_CAPS( preparsed )->report_id; struct hid_report *report; NTSTATUS status; BOOL removed; @@ -650,50 +647,22 @@ NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp) } else { - if (ext->u.pdo.poll_interval) - { - KIRQL old_irql; - TRACE_(hid_report)("Queue irp\n"); - - KeAcquireSpinLock(&ext->u.pdo.irp_queue_lock, &old_irql); - - IoSetCancelRoutine(irp, read_cancel_routine); - if (irp->Cancel && !IoSetCancelRoutine(irp, NULL)) - { - /* IRP was canceled before we set cancel routine */ - InitializeListHead(&irp->Tail.Overlay.ListEntry); - KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, old_irql); - return STATUS_CANCELLED; - } + KeAcquireSpinLock(&ext->u.pdo.irp_queue_lock, &irql);
- InsertTailList(&ext->u.pdo.irp_queue, &irp->Tail.Overlay.ListEntry); - irp->IoStatus.Status = STATUS_PENDING; - IoMarkIrpPending(irp); - - KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, old_irql); - } - else + IoSetCancelRoutine(irp, read_cancel_routine); + if (irp->Cancel && !IoSetCancelRoutine(irp, NULL)) { - HID_XFER_PACKET packet; - BYTE *buffer = irp->AssociatedIrp.SystemBuffer; - ULONG buffer_len = irpsp->Parameters.Read.Length; - - TRACE("No packet, but opportunistic reads enabled\n"); - - packet.reportId = buffer[0]; - packet.reportBuffer = buffer; - packet.reportBufferLen = buffer_len; + /* IRP was canceled before we set cancel routine */ + InitializeListHead(&irp->Tail.Overlay.ListEntry); + KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, irql); + return STATUS_CANCELLED; + }
- if (!report_id) - { - packet.reportId = 0; - packet.reportBuffer++; - packet.reportBufferLen--; - } + InsertTailList(&ext->u.pdo.irp_queue, &irp->Tail.Overlay.ListEntry); + irp->IoStatus.Status = STATUS_PENDING; + IoMarkIrpPending(irp);
- call_minidriver( IOCTL_HID_GET_INPUT_REPORT, ext->u.pdo.parent_fdo, NULL, 0, &packet, - sizeof(packet), &irp->IoStatus ); - } + KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, irql); }
status = irp->IoStatus.Status;
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/hidclass.sys/device.c | 89 +++++++++++--------------------------- 1 file changed, 26 insertions(+), 63 deletions(-)
diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c index 006b19ffa32..84eda78019c 100644 --- a/dlls/hidclass.sys/device.c +++ b/dlls/hidclass.sys/device.c @@ -246,87 +246,50 @@ static DWORD CALLBACK hid_device_thread(void *args) struct hid_preparsed_data *preparsed = ext->u.pdo.preparsed_data; BYTE report_id = HID_INPUT_VALUE_CAPS( preparsed )->report_id; ULONG buffer_len = preparsed->caps.InputReportByteLength; - IO_STATUS_BLOCK io; HID_XFER_PACKET *packet; + ULONG poll_interval = 0; + IO_STATUS_BLOCK io; BYTE *buffer; - DWORD rc; + DWORD res;
packet = malloc( sizeof(*packet) + buffer_len ); buffer = (BYTE *)(packet + 1); packet->reportBuffer = buffer;
- if (ext->u.pdo.information.Polled) - { - while(1) - { - packet->reportId = buffer[0] = report_id; - packet->reportBufferLen = buffer_len; - - if (!report_id) - { - packet->reportBuffer++; - packet->reportBufferLen--; - } - - call_minidriver( IOCTL_HID_GET_INPUT_REPORT, ext->u.pdo.parent_fdo, NULL, 0, packet, - sizeof(*packet), &io ); + if (ext->u.pdo.information.Polled) poll_interval = ext->u.pdo.poll_interval;
- if (io.Status == STATUS_SUCCESS) - { - if (!report_id) io.Information++; - packet->reportId = buffer[0]; - packet->reportBuffer = buffer; - packet->reportBufferLen = io.Information; - - hid_device_queue_input( device, packet ); - } - - rc = WaitForSingleObject(ext->u.pdo.halt_event, ext->u.pdo.poll_interval); - - if (rc == WAIT_OBJECT_0) - break; - else if (rc != WAIT_TIMEOUT) - ERR("Wait returned unexpected value %x\n",rc); - } - } - else + do { - INT exit_now = FALSE; + packet->reportId = buffer[0] = report_id; + packet->reportBufferLen = buffer_len;
- while(1) + if (!report_id) { - packet->reportId = buffer[0] = report_id; - packet->reportBufferLen = buffer_len; - - if (!report_id) - { - packet->reportBuffer++; - packet->reportBufferLen--; - } + packet->reportBuffer++; + packet->reportBufferLen--; + }
+ if (!poll_interval) call_minidriver( IOCTL_HID_READ_REPORT, ext->u.pdo.parent_fdo, NULL, 0, packet->reportBuffer, packet->reportBufferLen, &io ); + else + call_minidriver( IOCTL_HID_GET_INPUT_REPORT, ext->u.pdo.parent_fdo, NULL, 0, + packet, sizeof(*packet), &io );
- rc = WaitForSingleObject(ext->u.pdo.halt_event, 0); - if (rc == WAIT_OBJECT_0) - exit_now = TRUE; - - if (!exit_now && io.Status == STATUS_SUCCESS) - { - if (!report_id) io.Information++; - packet->reportId = buffer[0]; - packet->reportBuffer = buffer; - packet->reportBufferLen = io.Information; - - hid_device_queue_input( device, packet ); - } + if (io.Status == STATUS_SUCCESS) + { + if (!report_id) io.Information++; + packet->reportId = buffer[0]; + packet->reportBuffer = buffer; + packet->reportBufferLen = io.Information;
- if (exit_now) - break; + hid_device_queue_input( device, packet ); } - }
- TRACE("Device thread exiting\n"); + res = WaitForSingleObject(ext->u.pdo.halt_event, poll_interval); + } while (res == WAIT_TIMEOUT); + + TRACE("device thread exiting, res %#x\n", res); return 1; }
Sharing the HidP_* functions with dlls/hid/hidp.c.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- configure.ac | 1 + dlls/hidparse.sys/Makefile.in | 8 ++++ dlls/hidparse.sys/hidparse.sys.spec | 32 +++++++++++++ dlls/hidparse.sys/main.c | 49 ++++++++++++++++++++ include/ddk/hidpddi.h | 69 +++++++++++++++++++++++++++++ include/ddk/hidpi.h | 2 + 6 files changed, 161 insertions(+) create mode 100644 dlls/hidparse.sys/Makefile.in create mode 100644 dlls/hidparse.sys/hidparse.sys.spec create mode 100644 dlls/hidparse.sys/main.c create mode 100644 include/ddk/hidpddi.h
diff --git a/configure.ac b/configure.ac index 065b3f231bf..7237289a2ad 100644 --- a/configure.ac +++ b/configure.ac @@ -3292,6 +3292,7 @@ WINE_CONFIG_MAKEFILE(dlls/hhctrl.ocx) WINE_CONFIG_MAKEFILE(dlls/hid) WINE_CONFIG_MAKEFILE(dlls/hid/tests) WINE_CONFIG_MAKEFILE(dlls/hidclass.sys) +WINE_CONFIG_MAKEFILE(dlls/hidparse.sys) WINE_CONFIG_MAKEFILE(dlls/hlink) WINE_CONFIG_MAKEFILE(dlls/hlink/tests) WINE_CONFIG_MAKEFILE(dlls/hnetcfg) diff --git a/dlls/hidparse.sys/Makefile.in b/dlls/hidparse.sys/Makefile.in new file mode 100644 index 00000000000..3bebabb9878 --- /dev/null +++ b/dlls/hidparse.sys/Makefile.in @@ -0,0 +1,8 @@ +MODULE = hidparse.sys +IMPORTLIB = hidparse +EXTRADLLFLAGS = -mno-cygwin +PARENTSRC = ../hid + +C_SRCS = \ + hidp.c \ + main.c diff --git a/dlls/hidparse.sys/hidparse.sys.spec b/dlls/hidparse.sys/hidparse.sys.spec new file mode 100644 index 00000000000..c723b51bed5 --- /dev/null +++ b/dlls/hidparse.sys/hidparse.sys.spec @@ -0,0 +1,32 @@ +@ stub DllInitialize +@ stub DllUnload +@ stdcall HidP_FreeCollectionDescription(ptr) +@ stdcall HidP_GetButtonCaps(long ptr ptr ptr) +@ stdcall HidP_GetCaps(ptr ptr) +@ stdcall HidP_GetCollectionDescription(ptr long long ptr) +@ stdcall HidP_GetData(long ptr ptr ptr ptr long) +@ stub HidP_GetExtendedAttributes +@ stdcall HidP_GetLinkCollectionNodes(ptr ptr ptr) +@ stdcall HidP_GetScaledUsageValue(long long long long ptr ptr ptr long) +@ stdcall HidP_GetSpecificButtonCaps(long long long long ptr ptr ptr) +@ stdcall HidP_GetSpecificValueCaps(long long long long ptr ptr ptr) +@ stdcall HidP_GetUsages(long long long ptr ptr ptr ptr long) +@ stdcall HidP_GetUsagesEx(long long ptr ptr ptr ptr long) +@ stdcall HidP_GetUsageValue(long long long long ptr ptr ptr long) +@ stdcall HidP_GetUsageValueArray(long long long long ptr long ptr ptr long) +@ stdcall HidP_GetValueCaps(long ptr ptr ptr) +@ stdcall HidP_InitializeReportForID(long long ptr ptr long) +@ stdcall HidP_MaxDataListLength(long ptr) +@ stdcall HidP_MaxUsageListLength(long long ptr) +@ stub HidP_SetData +@ stub HidP_SetScaledUsageValue +@ stdcall HidP_SetUsages(long long long ptr ptr ptr ptr long) +@ stdcall HidP_SetUsageValue(long long long long long ptr ptr long) +@ stdcall HidP_SetUsageValueArray(long long long long ptr long ptr ptr long) +@ stub HidP_SysPowerCaps +@ stub HidP_SysPowerEvent +@ stub HidP_TranslateUsageAndPagesToI8042ScanCodes +@ stdcall HidP_TranslateUsagesToI8042ScanCodes(ptr long long ptr ptr ptr) +@ stub HidP_UnsetUsages +@ stub HidP_UsageAndPageListDifference +@ stub HidP_UsageListDifference diff --git a/dlls/hidparse.sys/main.c b/dlls/hidparse.sys/main.c new file mode 100644 index 00000000000..b8edc3e1f6a --- /dev/null +++ b/dlls/hidparse.sys/main.c @@ -0,0 +1,49 @@ +/* + * HID parsing library + * + * Copyright 2021 Rémi Bernon for CodeWeavers + * + * 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 "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winioctl.h" + +#include <ddk/wdm.h> +#include <ddk/hidpddi.h> + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(hidp); + +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 ); + + return STATUS_NOT_IMPLEMENTED; +} + +void WINAPI HidP_FreeCollectionDescription( HIDP_DEVICE_DESC *device_desc ) +{ + FIXME( "device_desc %p stub!\n", device_desc ); +} diff --git a/include/ddk/hidpddi.h b/include/ddk/hidpddi.h new file mode 100644 index 00000000000..87e68e31869 --- /dev/null +++ b/include/ddk/hidpddi.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) the Wine project + * + * 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 + */ + +#ifndef __WINE_HIDPDDI_H +#define __WINE_HIDPDDI_H + +#include <hidusage.h> +#include <ddk/hidpi.h> + +typedef struct _HIDP_COLLECTION_DESC +{ + USAGE UsagePage; + USAGE Usage; + UCHAR CollectionNumber; + UCHAR Reserved[15]; + USHORT InputLength; + USHORT OutputLength; + USHORT FeatureLength; + USHORT PreparsedDataLength; + PHIDP_PREPARSED_DATA PreparsedData; +} HIDP_COLLECTION_DESC, *PHIDP_COLLECTION_DESC; + +typedef struct _HIDP_REPORT_IDS +{ + UCHAR ReportID; + UCHAR CollectionNumber; + USHORT InputLength; + USHORT OutputLength; + USHORT FeatureLength; +} HIDP_REPORT_IDS, *PHIDP_REPORT_IDS; + +typedef struct _HIDP_GETCOLDESC_DBG +{ + ULONG BreakOffset; + ULONG ErrorCode; + ULONG Args[6]; +} HIDP_GETCOLDESC_DBG, *PHIDP_GETCOLDESC_DBG; + +typedef struct _HIDP_DEVICE_DESC +{ + HIDP_COLLECTION_DESC *CollectionDesc; + ULONG CollectionDescLength; + + HIDP_REPORT_IDS *ReportIDs; + ULONG ReportIDsLength; + + HIDP_GETCOLDESC_DBG Dbg; +} HIDP_DEVICE_DESC, *PHIDP_DEVICE_DESC; + +NTSTATUS WINAPI HidP_GetCollectionDescription(PHIDP_REPORT_DESCRIPTOR report_desc, ULONG report_desc_len, + POOL_TYPE pool_type, HIDP_DEVICE_DESC *device_desc); +void WINAPI HidP_FreeCollectionDescription(HIDP_DEVICE_DESC *device_desc); + +#endif /* __WINE_HIDPDDI_H */ diff --git a/include/ddk/hidpi.h b/include/ddk/hidpi.h index 8c970673ec1..6dfe5919b4f 100644 --- a/include/ddk/hidpi.h +++ b/include/ddk/hidpi.h @@ -19,6 +19,8 @@ #ifndef __HIDPI_H__ #define __HIDPI_H__
+typedef UCHAR *PHIDP_REPORT_DESCRIPTOR; + typedef enum _HIDP_REPORT_TYPE { HidP_Input,
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 ); }
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/hidclass.sys/Makefile.in | 3 +- dlls/hidclass.sys/descriptor.c | 602 --------------------------------- dlls/hidclass.sys/device.c | 88 ++--- dlls/hidclass.sys/hid.h | 6 +- dlls/hidclass.sys/pnp.c | 27 +- 5 files changed, 65 insertions(+), 661 deletions(-) delete mode 100644 dlls/hidclass.sys/descriptor.c
diff --git a/dlls/hidclass.sys/Makefile.in b/dlls/hidclass.sys/Makefile.in index 344fd10bc86..a6a131df87e 100644 --- a/dlls/hidclass.sys/Makefile.in +++ b/dlls/hidclass.sys/Makefile.in @@ -1,10 +1,9 @@ MODULE = hidclass.sys IMPORTLIB = hidclass -IMPORTS = hal ntoskrnl user32 +IMPORTS = hal ntoskrnl user32 hidparse
EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \ - descriptor.c \ device.c \ pnp.c diff --git a/dlls/hidclass.sys/descriptor.c b/dlls/hidclass.sys/descriptor.c deleted file mode 100644 index 546e589d01b..00000000000 --- a/dlls/hidclass.sys/descriptor.c +++ /dev/null @@ -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; -} diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c index 84eda78019c..ed7cd76fcde 100644 --- a/dlls/hidclass.sys/device.c +++ b/dlls/hidclass.sys/device.c @@ -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 ) { 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_queue *queue; 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;
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.Status = STATUS_SUCCESS; hid_report_decref( report ); @@ -243,25 +243,34 @@ static DWORD CALLBACK hid_device_thread(void *args) { DEVICE_OBJECT *device = (DEVICE_OBJECT*)args; BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; - struct hid_preparsed_data *preparsed = ext->u.pdo.preparsed_data; - BYTE report_id = HID_INPUT_VALUE_CAPS( preparsed )->report_id; - ULONG buffer_len = preparsed->caps.InputReportByteLength; + HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc; + HIDP_REPORT_IDS *reports = ext->u.pdo.device_desc.ReportIDs; + ULONG report_count = ext->u.pdo.device_desc.ReportIDsLength; + ULONG i, report_id = 0, poll_interval = 0; HID_XFER_PACKET *packet; - ULONG poll_interval = 0; IO_STATUS_BLOCK io; BYTE *buffer; DWORD res;
- packet = malloc( sizeof(*packet) + buffer_len ); + packet = malloc( sizeof(*packet) + desc->InputLength ); buffer = (BYTE *)(packet + 1); packet->reportBuffer = buffer;
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 { packet->reportId = buffer[0] = report_id; - packet->reportBufferLen = buffer_len; + packet->reportBufferLen = desc->InputLength;
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 ) { + HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc; 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.Information = 0; } else { - memcpy( irp->UserBuffer, preparsed, preparsed->size ); - irp->IoStatus.Information = preparsed->size; + memcpy( irp->UserBuffer, desc->PreparsedData, desc->PreparsedDataLength ); + irp->IoStatus.Information = desc->PreparsedDataLength; 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 ) { - 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 ); - struct hid_value_caps *caps = NULL, *caps_end = NULL; - ULONG report_len = 0, buffer_len = 0; + ULONG i, report_len = 0, buffer_len = 0; HID_XFER_PACKET packet; BYTE *buffer = NULL;
@@ -381,51 +390,48 @@ static void hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP buffer = irp->AssociatedIrp.SystemBuffer; break; } + if (!buffer || !buffer_len) + { + irp->IoStatus.Status = STATUS_INVALID_USER_BUFFER; + return; + } + + 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; + return; + }
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]; + report_len = reports[i].InputLength; 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]; + report_len = reports[i].OutputLength; 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]; + report_len = reports[i].FeatureLength; break; } - - if (!buffer || !buffer_len) - { - irp->IoStatus.Status = STATUS_INVALID_USER_BUFFER; - return; - } if (buffer_len < report_len) { irp->IoStatus.Status = STATUS_INVALID_PARAMETER; return; }
- for (; caps != caps_end; ++caps) if (!caps->report_id || caps->report_id == buffer[0]) break; - if (caps == caps_end) - { - irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - return; - } - packet.reportId = buffer[0]; packet.reportBuffer = buffer; packet.reportBufferLen = buffer_len;
- if (!caps->report_id) + if (!reports[i].ReportID) { packet.reportId = 0; 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; 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); struct hid_report *report; NTSTATUS status; @@ -593,7 +599,7 @@ NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp) 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; IoCompleteRequest( irp, IO_NO_INCREMENT ); @@ -603,7 +609,7 @@ NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp) irp->IoStatus.Information = 0; 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.Status = STATUS_SUCCESS; hid_report_decref( report ); diff --git a/dlls/hidclass.sys/hid.h b/dlls/hidclass.sys/hid.h index 2dfdfe4d0f7..1ca6a926872 100644 --- a/dlls/hidclass.sys/hid.h +++ b/dlls/hidclass.sys/hid.h @@ -27,6 +27,7 @@ #include "ddk/hidport.h" #include "ddk/hidclass.h" #include "ddk/hidpi.h" +#include "ddk/hidpddi.h" #include "cfgmgr32.h" #include "wine/list.h" #include "wine/hid.h" @@ -54,7 +55,7 @@ typedef struct _BASE_DEVICE_EXTENSION DEVICE_OBJECT *parent_fdo;
HID_COLLECTION_INFORMATION information; - struct hid_preparsed_data *preparsed_data; + HIDP_DEVICE_DESC device_desc;
ULONG poll_interval; 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_create(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; diff --git a/dlls/hidclass.sys/pnp.c b/dlls/hidclass.sys/pnp.c index 9752a47326f..db45aea2fd3 100644 --- a/dlls/hidclass.sys/pnp.c +++ b/dlls/hidclass.sys/pnp.c @@ -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) { + HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc; RAWINPUT rawinput; INPUT input;
@@ -118,8 +119,8 @@ static void send_wm_input_device_change(BASE_DEVICE_EXTENSION *ext, LPARAM param rawinput.header.wParam = param; rawinput.data.hid.dwCount = 0; rawinput.data.hid.dwSizeHid = 0; - ((USAGE *)rawinput.data.hid.bRawData)[0] = ext->u.pdo.preparsed_data->caps.UsagePage; - ((USAGE *)rawinput.data.hid.bRawData)[1] = ext->u.pdo.preparsed_data->caps.Usage; + ((USAGE *)rawinput.data.hid.bRawData)[0] = desc->UsagePage; + ((USAGE *)rawinput.data.hid.bRawData)[1] = desc->Usage;
input.type = INPUT_HARDWARE; 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; HID_DEVICE_ATTRIBUTES attr = {0}; + HIDP_COLLECTION_DESC *desc; HID_DESCRIPTOR descriptor; DEVICE_OBJECT *child_pdo; BYTE *reportDescriptor; @@ -253,19 +255,21 @@ static void create_child(minidriver *minidriver, DEVICE_OBJECT *fdo) 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); - if (!pdo_ext->u.pdo.preparsed_data) + if (io.Status != HIDP_STATUS_SUCCESS) { ERR("Cannot parse Report Descriptor\n"); IoDeleteDevice(child_pdo); 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; - usage = pdo_ext->u.pdo.preparsed_data->caps.Usage; + page = desc->UsagePage; + usage = desc->Usage; if (page == HID_USAGE_PAGE_GENERIC && usage == HID_USAGE_GENERIC_MOUSE) pdo_ext->u.pdo.rawinput_handle = WINE_MOUSE_HANDLE; 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); BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; + HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc; NTSTATUS status = irp->IoStatus.Status; KIRQL irql;
@@ -447,14 +452,12 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp) }
/* FIXME: This should probably be done in mouhid.sys. */ - if (ext->u.pdo.preparsed_data->caps.UsagePage == HID_USAGE_PAGE_GENERIC - && ext->u.pdo.preparsed_data->caps.Usage == HID_USAGE_GENERIC_MOUSE) + if (desc->UsagePage == HID_USAGE_PAGE_GENERIC && desc->Usage == HID_USAGE_GENERIC_MOUSE) { if (!IoRegisterDeviceInterface(device, &GUID_DEVINTERFACE_MOUSE, NULL, &ext->u.pdo.mouse_link_name)) ext->u.pdo.is_mouse = TRUE; } - if (ext->u.pdo.preparsed_data->caps.UsagePage == HID_USAGE_PAGE_GENERIC - && ext->u.pdo.preparsed_data->caps.Usage == HID_USAGE_GENERIC_KEYBOARD) + if (desc->UsagePage == HID_USAGE_PAGE_GENERIC && desc->Usage == HID_USAGE_GENERIC_KEYBOARD) { if (!IoRegisterDeviceInterface(device, &GUID_DEVINTERFACE_KEYBOARD, NULL, &ext->u.pdo.keyboard_link_name)) 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);
- free(ext->u.pdo.preparsed_data); + HidP_FreeCollectionDescription(&ext->u.pdo.device_desc);
RtlFreeUnicodeString(&ext->u.pdo.link_name);