Attempt to add support for multi toplevel-collections (TLCs) in HID descriptors.
TLCs are found by iterating over the descriptor once. HID descriptor parser is then called for each TLC individually (no changes in the parser itself). HID reports are bound to their TLC. A child-pdo is created for each TLC. Each child-pdo registers its input report-id for `pending_reads` in winbus.sys.
From: Matthias Gorzellik matthias.gorzellik@gmail.com
--- dlls/dinput/tests/hid.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/dlls/dinput/tests/hid.c b/dlls/dinput/tests/hid.c index 4dc3a60cdff..fb5a3bb457d 100644 --- a/dlls/dinput/tests/hid.c +++ b/dlls/dinput/tests/hid.c @@ -730,7 +730,6 @@ void hid_device_stop( struct hid_device_desc *desc, UINT count ) for (i = 0; i < count; ++i) { ret = WaitForSingleObject( device_removed, i > 0 ? 500 : 5000 ); - todo_wine_if(i > 0) ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); } } @@ -755,7 +754,6 @@ BOOL hid_device_start_( struct hid_device_desc *desc, UINT count, DWORD timeout for (i = 0; i < count; ++i) { ret = WaitForSingleObject( device_added, timeout ); - todo_wine_if(i > 0) ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); }
@@ -4049,7 +4047,6 @@ static void test_hid_multiple_tlc(void) swprintf( device_path, MAX_PATH, L"\\?\hid#vid_%04x&pid_%04x&col01", desc.attributes.VendorID, desc.attributes.ProductID ); ret = find_hid_device_path( device_path ); - todo_wine ok( ret, "Failed to find HID device matching %s\n", debugstr_w( device_path ) ); if (!ret) goto done;
From: Matthias Gorzellik matthias.gorzellik@gmail.com
--- dlls/hidparse.sys/main.c | 165 +++++++++++++++++++++++-------------- dlls/winexinput.sys/main.c | 4 +- include/ddk/hidpddi.h | 23 +++--- 3 files changed, 116 insertions(+), 76 deletions(-)
diff --git a/dlls/hidparse.sys/main.c b/dlls/hidparse.sys/main.c index feb5b3531a4..d56c1a93860 100644 --- a/dlls/hidparse.sys/main.c +++ b/dlls/hidparse.sys/main.c @@ -647,83 +647,123 @@ done: NTSTATUS WINAPI HidP_GetCollectionDescription( PHIDP_REPORT_DESCRIPTOR report_desc, ULONG report_desc_len, POOL_TYPE pool_type, HIDP_DEVICE_DESC *device_desc ) { - ULONG i, len, report_count = 0, input_len[256] = {0}, output_len[256] = {0}, feature_len[256] = {0}; + ULONG i, j; struct hid_value_caps *caps, *caps_end; struct hid_preparsed_data *preparsed; + BYTE *ptr, *end; + UINT32 size, depth = 0; + // FIXME: how many TLCs are allowed? + BYTE *toplevel_collections[10];
TRACE( "report_desc %p, report_desc_len %lu, 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) ))) + toplevel_collections[0] = report_desc; + for (ptr = report_desc, end = report_desc + report_desc_len; ptr != end; ptr += size + 1) { - free( preparsed ); - return STATUS_NO_MEMORY; - } + size = (*ptr & 0x03); + if (size == 3) size = 4; + if (ptr + size > end) + { + ERR( "Need %d bytes to read item value\n", size ); + return HIDP_STATUS_INTERNAL_ERROR; + }
- len = preparsed->caps_size + FIELD_OFFSET(struct hid_preparsed_data, value_caps[0]) + - preparsed->number_link_collection_nodes * sizeof(struct hid_collection_node); - - device_desc->CollectionDescLength = 1; - device_desc->CollectionDesc[0].UsagePage = preparsed->usage_page; - device_desc->CollectionDesc[0].Usage = preparsed->usage; - device_desc->CollectionDesc[0].CollectionNumber = 1; - device_desc->CollectionDesc[0].InputLength = preparsed->input_report_byte_length; - device_desc->CollectionDesc[0].OutputLength = preparsed->output_report_byte_length; - device_desc->CollectionDesc[0].FeatureLength = preparsed->feature_report_byte_length; - device_desc->CollectionDesc[0].PreparsedDataLength = len; - device_desc->CollectionDesc[0].PreparsedData = (PHIDP_PREPARSED_DATA)preparsed; - - caps = HID_INPUT_VALUE_CAPS( preparsed ); - caps_end = caps + preparsed->input_caps_end - preparsed->input_caps_start; - for (; caps != caps_end; ++caps) - { - len = caps->start_byte * 8 + 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); +#define SHORT_ITEM( tag, type ) (((tag) << 4) | ((type) << 2)) + switch (*ptr & SHORT_ITEM( 0xf, 0x3 )) + { + case SHORT_ITEM( TAG_MAIN_COLLECTION, TAG_TYPE_MAIN ): + depth++; + break; + case SHORT_ITEM( TAG_MAIN_END_COLLECTION, TAG_TYPE_MAIN ): + if (--depth == 0) { + device_desc->CollectionDescLength++; + if (device_desc->CollectionDescLength < ARRAY_SIZE(toplevel_collections)) + toplevel_collections[device_desc->CollectionDescLength] = ptr + size + 1; + else { + ERR( "Too many toplevel collections." ); + return HIDP_STATUS_INTERNAL_ERROR; + } + } + break; + default: + break; + } }
- caps = HID_OUTPUT_VALUE_CAPS( preparsed ); - caps_end = caps + preparsed->output_caps_end - preparsed->output_caps_start; - for (; caps != caps_end; ++caps) - { - len = caps->start_byte * 8 + 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); - } + if (!(device_desc->CollectionDesc = ExAllocatePool( pool_type, device_desc->CollectionDescLength * sizeof(*device_desc->CollectionDesc) ))) + return STATUS_NO_MEMORY;
- caps = HID_FEATURE_VALUE_CAPS( preparsed ); - caps_end = caps + preparsed->feature_caps_end - preparsed->feature_caps_start; - for (; caps != caps_end; ++caps) - { - len = caps->start_byte * 8 + 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); - } + for (i = 0; i < device_desc->CollectionDescLength; ++i) { + ULONG report_count = 0, input_len[256] = {0}, output_len[256] = {0}, feature_len[256] = {0}, len;
- if (!(device_desc->ReportIDs = ExAllocatePool( pool_type, sizeof(*device_desc->ReportIDs) * report_count ))) - { - free( preparsed ); - ExFreePool( device_desc->CollectionDesc ); - return STATUS_NO_MEMORY; - } + TRACE( "num tlcs %lu, tlc %lu, start %p, end %p\n", device_desc->CollectionDescLength, i, toplevel_collections[i], toplevel_collections[i+1]);
- 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; + if (!(preparsed = parse_descriptor( toplevel_collections[i], toplevel_collections[i+1] - toplevel_collections[i], pool_type ))) + return HIDP_STATUS_INTERNAL_ERROR; + + len = preparsed->caps_size + FIELD_OFFSET(struct hid_preparsed_data, value_caps[0]) + + preparsed->number_link_collection_nodes * sizeof(struct hid_collection_node); + + device_desc->CollectionDesc[i].UsagePage = preparsed->usage_page; + device_desc->CollectionDesc[i].Usage = preparsed->usage; + device_desc->CollectionDesc[i].CollectionNumber = i+1; + device_desc->CollectionDesc[i].InputLength = preparsed->input_report_byte_length; + device_desc->CollectionDesc[i].OutputLength = preparsed->output_report_byte_length; + device_desc->CollectionDesc[i].FeatureLength = preparsed->feature_report_byte_length; + device_desc->CollectionDesc[i].PreparsedDataLength = len; + device_desc->CollectionDesc[i].PreparsedData = (PHIDP_PREPARSED_DATA)preparsed; + + caps = HID_INPUT_VALUE_CAPS( preparsed ); + caps_end = caps + preparsed->input_caps_end - preparsed->input_caps_start; + for (; caps != caps_end; ++caps) + { + len = caps->start_byte * 8 + 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->output_caps_end - preparsed->output_caps_start; + for (; caps != caps_end; ++caps) + { + len = caps->start_byte * 8 + 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->feature_caps_end - preparsed->feature_caps_start; + for (; caps != caps_end; ++caps) + { + len = caps->start_byte * 8 + 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->CollectionDesc[i].ReportIDs = ExAllocatePool( pool_type, sizeof(*device_desc->CollectionDesc[i].ReportIDs) * report_count ))) + { + for (j = 0; j < i; ++j) { + free( device_desc->CollectionDesc[j].PreparsedData ); + ExFreePool( device_desc->CollectionDesc[j].ReportIDs ); + } + ExFreePool( device_desc->CollectionDesc ); + return STATUS_NO_MEMORY; + } + + for (j = 0, report_count = 0; j < 256; ++j) + { + if (!input_len[j] && !output_len[j] && !feature_len[j]) continue; + device_desc->CollectionDesc[i].ReportIDs[report_count].ReportID = j; + device_desc->CollectionDesc[i].ReportIDs[report_count].InputLength = (input_len[j] + 7) / 8; + device_desc->CollectionDesc[i].ReportIDs[report_count].OutputLength = (output_len[j] + 7) / 8; + device_desc->CollectionDesc[i].ReportIDs[report_count].FeatureLength = (feature_len[j] + 7) / 8; + report_count++; + } + device_desc->CollectionDesc[i].ReportIDsLength = report_count; + } return HIDP_STATUS_SUCCESS; }
@@ -731,6 +771,7 @@ void WINAPI HidP_FreeCollectionDescription( HIDP_DEVICE_DESC *device_desc ) { TRACE( "device_desc %p.\n", device_desc );
+ for ( UINT i = 0; i < device_desc->CollectionDescLength; i++) + ExFreePool( device_desc->CollectionDesc[i].ReportIDs ); ExFreePool( device_desc->CollectionDesc ); - ExFreePool( device_desc->ReportIDs ); } diff --git a/dlls/winexinput.sys/main.c b/dlls/winexinput.sys/main.c index 5a5c6ca8f00..e11ab5b7303 100644 --- a/dlls/winexinput.sys/main.c +++ b/dlls/winexinput.sys/main.c @@ -700,8 +700,8 @@ static NTSTATUS initialize_device(DEVICE_OBJECT *device) if (!fdo->ry_caps.UsagePage) WARN("missing ry axis\n"); if (!fdo->rt_caps.UsagePage) WARN("missing rt axis\n");
- reports = fdo->device_desc.ReportIDs; - report_count = fdo->device_desc.ReportIDsLength; + reports = fdo->device_desc.CollectionDesc->ReportIDs; + report_count = fdo->device_desc.CollectionDesc->ReportIDsLength; for (i = 0; i < report_count; ++i) if (!reports[i].ReportID || reports[i].InputLength) break; if (i == report_count) i = 0; /* no input report?!, just use first ID */
diff --git a/include/ddk/hidpddi.h b/include/ddk/hidpddi.h index 87e68e31869..57ac02f61ee 100644 --- a/include/ddk/hidpddi.h +++ b/include/ddk/hidpddi.h @@ -22,6 +22,14 @@ #include <hidusage.h> #include <ddk/hidpi.h>
+typedef struct _HIDP_REPORT_IDS +{ + UCHAR ReportID; + USHORT InputLength; + USHORT OutputLength; + USHORT FeatureLength; +} HIDP_REPORT_IDS, *PHIDP_REPORT_IDS; + typedef struct _HIDP_COLLECTION_DESC { USAGE UsagePage; @@ -33,16 +41,10 @@ typedef struct _HIDP_COLLECTION_DESC 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; + HIDP_REPORT_IDS *ReportIDs; + ULONG ReportIDsLength; +} HIDP_COLLECTION_DESC, *PHIDP_COLLECTION_DESC;
typedef struct _HIDP_GETCOLDESC_DBG { @@ -56,9 +58,6 @@ 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;
From: Matthias Gorzellik matthias.gorzellik@gmail.com
--- dlls/hidclass.sys/device.c | 12 ++-- dlls/hidclass.sys/hid.h | 4 +- dlls/hidclass.sys/pnp.c | 134 +++++++++++++++++++++---------------- 3 files changed, 87 insertions(+), 63 deletions(-)
diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c index e6b34077a77..09e2ea3818e 100644 --- a/dlls/hidclass.sys/device.c +++ b/dlls/hidclass.sys/device.c @@ -219,7 +219,7 @@ static struct hid_report *hid_queue_pop_report( struct hid_queue *queue ) static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *packet ) { BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; - HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc; + HIDP_COLLECTION_DESC *desc = &ext->u.pdo.device_desc.CollectionDesc[ext->u.pdo.desc_number]; const BOOL polled = ext->u.pdo.information.Polled; ULONG size, report_len = polled ? packet->reportBufferLen : desc->InputLength; struct hid_report *last_report, *report; @@ -297,8 +297,8 @@ static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *pack
static HIDP_REPORT_IDS *find_report_with_type_and_id( BASE_DEVICE_EXTENSION *ext, BYTE type, BYTE id, BOOL any_id ) { - HIDP_REPORT_IDS *report, *reports = ext->u.pdo.device_desc.ReportIDs; - ULONG report_count = ext->u.pdo.device_desc.ReportIDsLength; + HIDP_REPORT_IDS *report, *reports = ext->u.pdo.device_desc.CollectionDesc[ext->u.pdo.desc_number].ReportIDs; + ULONG report_count = ext->u.pdo.device_desc.CollectionDesc[ext->u.pdo.desc_number].ReportIDsLength;
for (report = reports; report != reports + report_count; report++) { @@ -315,7 +315,7 @@ static DWORD CALLBACK hid_device_thread(void *args) { DEVICE_OBJECT *device = (DEVICE_OBJECT*)args; BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; - HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc; + HIDP_COLLECTION_DESC *desc = &ext->u.pdo.device_desc.CollectionDesc[ext->u.pdo.desc_number]; BOOL polled = ext->u.pdo.information.Polled; HIDP_REPORT_IDS *report; HID_XFER_PACKET *packet; @@ -618,7 +618,7 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) } case IOCTL_HID_GET_COLLECTION_DESCRIPTOR: { - HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc; + HIDP_COLLECTION_DESC *desc = &ext->u.pdo.device_desc.CollectionDesc[ext->u.pdo.desc_number];
irp->IoStatus.Information = desc->PreparsedDataLength; if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < desc->PreparsedDataLength) @@ -694,7 +694,7 @@ NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp) { struct hid_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext; BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; - HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc; + HIDP_COLLECTION_DESC *desc = &ext->u.pdo.device_desc.CollectionDesc[ext->u.pdo.desc_number]; IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp); struct hid_report *report; BOOL removed; diff --git a/dlls/hidclass.sys/hid.h b/dlls/hidclass.sys/hid.h index 7b5d5f19298..1d35ed9f115 100644 --- a/dlls/hidclass.sys/hid.h +++ b/dlls/hidclass.sys/hid.h @@ -48,7 +48,8 @@ typedef struct _BASE_DEVICE_EXTENSION /* this must be the first member */ HID_DEVICE_EXTENSION hid_ext;
- DEVICE_OBJECT *child_pdo; + DEVICE_OBJECT *child_pdos[10]; + UINT32 num_childs; } fdo;
struct @@ -57,6 +58,7 @@ typedef struct _BASE_DEVICE_EXTENSION
HID_COLLECTION_INFORMATION information; HIDP_DEVICE_DESC device_desc; + UINT32 desc_number;
ULONG poll_interval; HANDLE halt_event; diff --git a/dlls/hidclass.sys/pnp.c b/dlls/hidclass.sys/pnp.c index ddccffb91e2..c8a04f47cf4 100644 --- a/dlls/hidclass.sys/pnp.c +++ b/dlls/hidclass.sys/pnp.c @@ -113,7 +113,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; + HIDP_COLLECTION_DESC *desc = &ext->u.pdo.device_desc.CollectionDesc[ext->u.pdo.desc_number]; INPUT input = {.type = INPUT_HARDWARE}; struct hid_packet hid = {0};
@@ -195,6 +195,7 @@ static void create_child(minidriver *minidriver, DEVICE_OBJECT *fdo) HID_DEVICE_ATTRIBUTES attr = {0}; HID_DESCRIPTOR descriptor = {0}; HIDP_COLLECTION_DESC *desc; + HIDP_DEVICE_DESC device_desc; DEVICE_OBJECT *child_pdo; BYTE *reportDescriptor; UNICODE_STRING string; @@ -203,43 +204,28 @@ static void create_child(minidriver *minidriver, DEVICE_OBJECT *fdo) USAGE page, usage; NTSTATUS status; INT i; + WCHAR serialnumber[255]; + ULONG index = HID_STRING_ID_ISERIALNUMBER;
- call_minidriver( IOCTL_HID_GET_DEVICE_ATTRIBUTES, fdo, NULL, 0, &attr, sizeof(attr), &io ); + call_minidriver( IOCTL_HID_GET_STRING, fdo, ULongToPtr( index ), sizeof(index), + &serialnumber, sizeof(serialnumber), &io ); if (io.Status != STATUS_SUCCESS) { ERR( "Minidriver failed to get attributes, status %#lx.\n", io.Status ); return; }
- swprintf(pdo_name, ARRAY_SIZE(pdo_name), L"\Device\HID#%p&%p", fdo->DriverObject, - fdo_ext->u.fdo.hid_ext.PhysicalDeviceObject); - RtlInitUnicodeString(&string, pdo_name); - if ((status = IoCreateDevice(fdo->DriverObject, sizeof(*pdo_ext), &string, 0, 0, FALSE, &child_pdo))) + call_minidriver( IOCTL_HID_GET_DEVICE_ATTRIBUTES, fdo, NULL, 0, &attr, sizeof(attr), &io ); + if (io.Status != STATUS_SUCCESS) { - ERR( "Failed to create child PDO, status %#lx.\n", io.Status ); + ERR( "Minidriver failed to get attributes, status %#lx.\n", io.Status ); return; } - fdo_ext->u.fdo.child_pdo = child_pdo; - - pdo_ext = child_pdo->DeviceExtension; - pdo_ext->u.pdo.parent_fdo = fdo; - list_init( &pdo_ext->u.pdo.queues ); - KeInitializeSpinLock( &pdo_ext->u.pdo.queues_lock ); - wcscpy(pdo_ext->device_id, fdo_ext->device_id); - wcscpy(pdo_ext->instance_id, fdo_ext->instance_id); - wcscpy(pdo_ext->container_id, fdo_ext->container_id); - pdo_ext->class_guid = fdo_ext->class_guid; - - pdo_ext->u.pdo.information.VendorID = attr.VendorID; - pdo_ext->u.pdo.information.ProductID = attr.ProductID; - pdo_ext->u.pdo.information.VersionNumber = attr.VersionNumber; - pdo_ext->u.pdo.information.Polled = minidriver->minidriver.DevicesArePolled;
call_minidriver( IOCTL_HID_GET_DEVICE_DESCRIPTOR, fdo, NULL, 0, &descriptor, sizeof(descriptor), &io ); if (io.Status != STATUS_SUCCESS) { - ERR( "Cannot get Device Descriptor, status %#lx\n", status ); - IoDeleteDevice(child_pdo); + ERR( "Cannot get Device Descriptor, status %#lx\n", io.Status ); return; } for (i = 0; i < descriptor.bNumDescriptors; i++) @@ -249,7 +235,6 @@ static void create_child(minidriver *minidriver, DEVICE_OBJECT *fdo) if (i >= descriptor.bNumDescriptors) { ERR("No Report Descriptor found in reply\n"); - IoDeleteDevice(child_pdo); return; }
@@ -258,43 +243,79 @@ static void create_child(minidriver *minidriver, DEVICE_OBJECT *fdo) descriptor.DescriptorList[i].wReportLength, &io ); if (io.Status != STATUS_SUCCESS) { - ERR( "Cannot get Report Descriptor, status %#lx\n", status ); + ERR( "Cannot get Report Descriptor, status %#lx\n", io.Status ); free(reportDescriptor); - IoDeleteDevice(child_pdo); return; }
io.Status = HidP_GetCollectionDescription( reportDescriptor, descriptor.DescriptorList[i].wReportLength, - PagedPool, &pdo_ext->u.pdo.device_desc ); + PagedPool, &device_desc ); free(reportDescriptor); if (io.Status != HIDP_STATUS_SUCCESS) { ERR("Cannot parse Report Descriptor\n"); - IoDeleteDevice(child_pdo); return; }
- desc = pdo_ext->u.pdo.device_desc.CollectionDesc; - pdo_ext->u.pdo.information.DescriptorSize = desc->PreparsedDataLength; + for (i = 0; i < device_desc.CollectionDescLength; ++i) { + swprintf(pdo_name, ARRAY_SIZE(pdo_name), L"\Device\HID#%p&%p&%d", fdo->DriverObject, + fdo_ext->u.fdo.hid_ext.PhysicalDeviceObject, i); + RtlInitUnicodeString(&string, pdo_name); + if ((status = IoCreateDevice(fdo->DriverObject, sizeof(*pdo_ext), &string, 0, 0, FALSE, &child_pdo))) + { + ERR( "Failed to create child PDO, status %#lx.\n", status ); + return; + } + + fdo_ext->u.fdo.child_pdos[i] = child_pdo; + fdo_ext->u.fdo.num_childs++; + + pdo_ext = child_pdo->DeviceExtension; + pdo_ext->u.pdo.parent_fdo = fdo; + list_init( &pdo_ext->u.pdo.queues ); + KeInitializeSpinLock( &pdo_ext->u.pdo.queues_lock ); + if (device_desc.CollectionDescLength > 1) { + swprintf(pdo_ext->device_id, ARRAY_SIZE(pdo_ext->device_id), L"%s&Col%02d", + fdo_ext->device_id, device_desc.CollectionDesc[i].CollectionNumber); + swprintf(pdo_ext->instance_id, ARRAY_SIZE(pdo_ext->instance_id), L"%u&%s&%x&%u&%04u", + attr.VersionNumber, serialnumber, 0, 0, i); + } else { + wcscpy(pdo_ext->device_id, fdo_ext->device_id); + wcscpy(pdo_ext->instance_id, fdo_ext->instance_id); + } + wcscpy(pdo_ext->container_id, fdo_ext->container_id); + pdo_ext->class_guid = fdo_ext->class_guid;
- 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) - pdo_ext->u.pdo.rawinput_handle = WINE_KEYBOARD_HANDLE; - else - pdo_ext->u.pdo.rawinput_handle = alloc_rawinput_handle(); + pdo_ext->u.pdo.information.VendorID = attr.VendorID; + pdo_ext->u.pdo.information.ProductID = attr.ProductID; + pdo_ext->u.pdo.information.VersionNumber = attr.VersionNumber; + pdo_ext->u.pdo.information.Polled = minidriver->minidriver.DevicesArePolled;
- IoInvalidateDeviceRelations(fdo_ext->u.fdo.hid_ext.PhysicalDeviceObject, BusRelations); + memcpy(&pdo_ext->u.pdo.device_desc, &device_desc, sizeof(HIDP_DEVICE_DESC)); + pdo_ext->u.pdo.desc_number = i;
- pdo_ext->u.pdo.poll_interval = DEFAULT_POLL_INTERVAL; + desc = &pdo_ext->u.pdo.device_desc.CollectionDesc[i]; + pdo_ext->u.pdo.information.DescriptorSize = desc->PreparsedDataLength;
- HID_StartDeviceThread(child_pdo); + 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) + pdo_ext->u.pdo.rawinput_handle = WINE_KEYBOARD_HANDLE; + else + pdo_ext->u.pdo.rawinput_handle = alloc_rawinput_handle();
- send_wm_input_device_change(pdo_ext, GIDC_ARRIVAL); + pdo_ext->u.pdo.poll_interval = DEFAULT_POLL_INTERVAL;
- TRACE( "created device %p, rawinput handle %#x\n", pdo_ext, pdo_ext->u.pdo.rawinput_handle ); + HID_StartDeviceThread(child_pdo); + + send_wm_input_device_change(pdo_ext, GIDC_ARRIVAL); + + TRACE( "created device %p, rawinput handle %#x\n", pdo_ext, pdo_ext->u.pdo.rawinput_handle ); + } + + IoInvalidateDeviceRelations(fdo_ext->u.fdo.hid_ext.PhysicalDeviceObject, BusRelations); }
static NTSTATUS fdo_pnp(DEVICE_OBJECT *device, IRP *irp) @@ -311,26 +332,27 @@ static NTSTATUS fdo_pnp(DEVICE_OBJECT *device, IRP *irp) { DEVICE_RELATIONS *devices; DEVICE_OBJECT *child; + UINT32 i = 0;
if (stack->Parameters.QueryDeviceRelations.Type != BusRelations) return minidriver->PNPDispatch(device, irp);
- if (!(devices = ExAllocatePool(PagedPool, offsetof(DEVICE_RELATIONS, Objects[1])))) + if (!(devices = ExAllocatePool(PagedPool, + offsetof(DEVICE_RELATIONS, Objects[ext->u.fdo.num_childs])))) { irp->IoStatus.Status = STATUS_NO_MEMORY; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_NO_MEMORY; }
- if ((child = ext->u.fdo.child_pdo)) - { - devices->Objects[0] = ext->u.fdo.child_pdo; - call_fastcall_func1(ObfReferenceObject, ext->u.fdo.child_pdo); - devices->Count = 1; - } - else - { - devices->Count = 0; + devices->Count = 0; + for ( i = 0; i < ext->u.fdo.num_childs; ++i) { + if ((child = ext->u.fdo.child_pdos[i])) + { + devices->Objects[i] = child; + call_fastcall_func1(ObfReferenceObject, child); + devices->Count++; + } }
irp->IoStatus.Information = (ULONG_PTR)devices; @@ -373,7 +395,7 @@ static WCHAR *query_hardware_ids(DEVICE_OBJECT *device) static const WCHAR hid_format[] = L"HID_DEVICE";
BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; - HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc; + HIDP_COLLECTION_DESC *desc = &ext->u.pdo.device_desc.CollectionDesc[ext->u.pdo.desc_number]; HID_COLLECTION_INFORMATION *info = &ext->u.pdo.information; WCHAR *dst; DWORD size; @@ -443,7 +465,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; + HIDP_COLLECTION_DESC *desc = &ext->u.pdo.device_desc.CollectionDesc[ext->u.pdo.desc_number]; NTSTATUS status = irp->IoStatus.Status; struct hid_queue *queue, *next; KIRQL irql;
From: Matthias Gorzellik matthias.gorzellik@gmail.com
--- dlls/winebus.sys/main.c | 72 ++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 30 deletions(-)
diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index f7a634dbfe2..c48bb593164 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -87,7 +87,7 @@ struct device_extension
struct hid_report *last_reports[256]; struct list reports; - IRP *pending_read; + IRP *pending_reads[256];
UINT32 report_fixups; UINT64 unix_device; @@ -252,13 +252,13 @@ static WCHAR *get_compatible_ids(DEVICE_OBJECT *device) return dst; }
-static IRP *pop_pending_read(struct device_extension *ext) +static IRP *pop_pending_read(struct device_extension *ext, ULONG report_id) { IRP *pending;
RtlEnterCriticalSection(&ext->cs); - pending = ext->pending_read; - ext->pending_read = NULL; + pending = ext->pending_reads[report_id]; + ext->pending_reads[report_id] = NULL; RtlLeaveCriticalSection(&ext->cs);
return pending; @@ -269,11 +269,13 @@ static void remove_pending_irps(DEVICE_OBJECT *device) struct device_extension *ext = device->DeviceExtension; IRP *pending;
- if ((pending = pop_pending_read(ext))) - { - pending->IoStatus.Status = STATUS_DELETE_PENDING; - pending->IoStatus.Information = 0; - IoCompleteRequest(pending, IO_NO_INCREMENT); + for (UINT i = 0; i < ARRAY_SIZE(ext->pending_reads); ++i) { + if ((pending = pop_pending_read(ext, i))) + { + pending->IoStatus.Status = STATUS_DELETE_PENDING; + pending->IoStatus.Information = 0; + IoCompleteRequest(pending, IO_NO_INCREMENT); + } } }
@@ -539,11 +541,13 @@ static void process_hid_report(DEVICE_OBJECT *device, BYTE *report_buf, DWORD re RtlEnterCriticalSection(&ext->cs); list_add_tail(&ext->reports, &report->entry);
- if (!ext->collection_desc.ReportIDs[0].ReportID) last_report = ext->last_reports[0]; - else last_report = ext->last_reports[report_buf[0]]; - memcpy(last_report->buffer, report_buf, report_len); + for ( UINT i = 0; i < ext->collection_desc.CollectionDescLength; ++i ) { + if (!ext->collection_desc.CollectionDesc[i].ReportIDs[0].ReportID) last_report = ext->last_reports[0]; + else last_report = ext->last_reports[report_buf[0]]; + memcpy(last_report->buffer, report_buf, report_len); + }
- if ((irp = pop_pending_read(ext))) + if ((irp = pop_pending_read(ext, report_buf[0]))) { deliver_next_report(ext, irp); IoCompleteRequest(irp, IO_NO_INCREMENT); @@ -1027,17 +1031,22 @@ static NTSTATUS pdo_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp) &ext->collection_desc))) { status = STATUS_SUCCESS; - reports = ext->collection_desc.ReportIDs; - for (i = 0; i < ext->collection_desc.ReportIDsLength; ++i) - { - if (!(size = reports[i].InputLength)) continue; - size = offsetof( struct hid_report, buffer[size] ); - if (!(report = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, size))) status = STATUS_NO_MEMORY; - else + for ( ULONG j = 0; j < ext->collection_desc.CollectionDescLength; ++j) { + reports = ext->collection_desc.CollectionDesc[j].ReportIDs; + for (i = 0; i < ext->collection_desc.CollectionDesc[j].ReportIDsLength; ++i) { - report->length = reports[i].InputLength; - report->buffer[0] = reports[i].ReportID; - ext->last_reports[reports[i].ReportID] = report; + if (!(size = reports[i].InputLength)) continue; + size = offsetof( struct hid_report, buffer[size] ); + if (!(report = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, size))) { + status = STATUS_NO_MEMORY; + break; + } + else + { + report->length = reports[i].InputLength; + report->buffer[0] = reports[i].ReportID; + ext->last_reports[reports[i].ReportID] = report; + } } } if (!status) ext->state = DEVICE_STATE_STARTED; @@ -1068,11 +1077,13 @@ static NTSTATUS pdo_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp) LIST_FOR_EACH_ENTRY_SAFE(report, next, &ext->reports, struct hid_report, entry) RtlFreeHeap(GetProcessHeap(), 0, report);
- reports = ext->collection_desc.ReportIDs; - for (i = 0; i < ext->collection_desc.ReportIDsLength; ++i) - { - if (!reports[i].InputLength) continue; - RtlFreeHeap(GetProcessHeap(), 0, ext->last_reports[reports[i].ReportID]); + for ( UINT j = 0; j < ext->collection_desc.CollectionDescLength; ++j ) { + reports = ext->collection_desc.CollectionDesc[j].ReportIDs; + for (i = 0; i < ext->collection_desc.CollectionDesc[j].ReportIDsLength; ++i) + { + if (!reports[i].InputLength) continue; + RtlFreeHeap(GetProcessHeap(), 0, ext->last_reports[reports[i].ReportID]); + } } HidP_FreeCollectionDescription(&ext->collection_desc); RtlFreeHeap(GetProcessHeap(), 0, ext->report_desc); @@ -1259,9 +1270,10 @@ static NTSTATUS WINAPI hid_internal_dispatch(DEVICE_OBJECT *device, IRP *irp) { if (!deliver_next_report(ext, irp)) { + UCHAR reportId = ((PUCHAR)irp->UserBuffer)[0]; /* hidclass.sys should guarantee this */ - assert(!ext->pending_read); - ext->pending_read = irp; + assert(!ext->pending_reads[reportId]); + ext->pending_reads[reportId] = irp; IoMarkIrpPending(irp); irp->IoStatus.Status = STATUS_PENDING; }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=147070
Your paranoid android.
=== debian11b (64 bit WoW report) ===
user32: input.c:4305: Test succeeded inside todo block: button_down_hwnd_todo 1: got MSG_TEST_WIN hwnd 0000000005C200EA, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032
Rémi Bernon (@rbernon) commented about include/ddk/hidpddi.h:
HIDP_COLLECTION_DESC *CollectionDesc; ULONG CollectionDescLength;
- HIDP_REPORT_IDS *ReportIDs;
- ULONG ReportIDsLength;
- HIDP_GETCOLDESC_DBG Dbg;
} HIDP_DEVICE_DESC, *PHIDP_DEVICE_DESC;
These are the structs defined in the Windows SDK, you cannot change them (unless they change in the SDK headers of course).
On Thu Jul 18 06:22:41 2024 +0000, Rémi Bernon wrote:
These are the structs defined in the Windows SDK, you cannot change them (unless they change in the SDK headers of course).
Oh, right, I see.. I'll change that, think it even makes some of the changes obsolete.
On Thu Jul 18 08:45:36 2024 +0000, Matthias Gorzellik wrote:
Oh, right, I see.. I'll change that, think it even makes some of the changes obsolete.
Fwiw I'll be off for a couple of weeks, and won't be able to make a proper review until I get back.
On Thu Jul 18 08:59:14 2024 +0000, Rémi Bernon wrote:
Fwiw I'll be off for a couple of weeks, and won't be able to make a proper review until I get back.
Ok, thanks for the heads up!