Marking input report read requests as pending and completing them on write, otherwise Windows keeps reading input reports and never finishes setting up the device.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntoskrnl.exe/tests/driver_hid.c | 95 +++++++++++++++++++++++++++- dlls/ntoskrnl.exe/tests/ntoskrnl.c | 70 ++++++++++++++++++-- 2 files changed, 157 insertions(+), 8 deletions(-)
diff --git a/dlls/ntoskrnl.exe/tests/driver_hid.c b/dlls/ntoskrnl.exe/tests/driver_hid.c index e684e2531db..eb81426823b 100644 --- a/dlls/ntoskrnl.exe/tests/driver_hid.c +++ b/dlls/ntoskrnl.exe/tests/driver_hid.c @@ -42,10 +42,32 @@ static UNICODE_STRING control_symlink; static unsigned int got_start_device; static DWORD report_id;
+struct minidevice_extension +{ + LIST_ENTRY irp_queue; + BOOL removed; +}; + +static void cancel_pending_requests(DEVICE_OBJECT *device) +{ + HID_DEVICE_EXTENSION *ext = device->DeviceExtension; + struct minidevice_extension *mext = ext->MiniDeviceExtension; + LIST_ENTRY *entry; + IRP *irp; + + while ((entry = RemoveHeadList(&mext->irp_queue)) != &mext->irp_queue) + { + irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + irp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(irp, IO_NO_INCREMENT); + } +} + static NTSTATUS WINAPI driver_pnp(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); HID_DEVICE_EXTENSION *ext = device->DeviceExtension; + struct minidevice_extension *mext = ext->MiniDeviceExtension;
if (winetest_debug > 1) trace("pnp %#x\n", stack->MinorFunction);
@@ -53,6 +75,8 @@ static NTSTATUS WINAPI driver_pnp(DEVICE_OBJECT *device, IRP *irp) { case IRP_MN_START_DEVICE: ++got_start_device; + InitializeListHead(&mext->irp_queue); + mext->removed = FALSE; IoSetDeviceInterfaceState(&control_symlink, TRUE); irp->IoStatus.Status = STATUS_SUCCESS; break; @@ -60,10 +84,14 @@ static NTSTATUS WINAPI driver_pnp(DEVICE_OBJECT *device, IRP *irp) case IRP_MN_SURPRISE_REMOVAL: case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_STOP_DEVICE: + mext->removed = TRUE; + cancel_pending_requests(device); irp->IoStatus.Status = STATUS_SUCCESS; break;
case IRP_MN_REMOVE_DEVICE: + mext->removed = TRUE; + cancel_pending_requests(device); IoSetDeviceInterfaceState(&control_symlink, FALSE); irp->IoStatus.Status = STATUS_SUCCESS; break; @@ -290,6 +318,16 @@ static NTSTATUS WINAPI driver_internal_ioctl(DEVICE_OBJECT *device, IRP *irp) REPORT_SIZE(1, 1), FEATURE(1, Data|Var|Abs), END_COLLECTION, + + USAGE_PAGE(1, HID_USAGE_PAGE_LED), + USAGE(1, HID_USAGE_LED_GREEN), + COLLECTION(1, Report), + REPORT_ID_OR_USAGE_PAGE(1, report_id, 0), + USAGE_PAGE(1, HID_USAGE_PAGE_LED), + REPORT_COUNT(1, 8), + REPORT_SIZE(1, 1), + OUTPUT(1, Cnst|Var|Abs), + END_COLLECTION, END_COLLECTION, }; #undef REPORT_ID_OR_USAGE_PAGE @@ -297,6 +335,8 @@ static NTSTATUS WINAPI driver_internal_ioctl(DEVICE_OBJECT *device, IRP *irp)
static BOOL test_failed; IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + HID_DEVICE_EXTENSION *ext = device->DeviceExtension; + struct minidevice_extension *mext = ext->MiniDeviceExtension; const ULONG in_size = stack->Parameters.DeviceIoControl.InputBufferLength; const ULONG out_size = stack->Parameters.DeviceIoControl.OutputBufferLength; const ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; @@ -378,7 +418,50 @@ static NTSTATUS WINAPI driver_internal_ioctl(DEVICE_OBJECT *device, IRP *irp) } if (out_size != expected_size) test_failed = TRUE;
- ret = STATUS_NOT_IMPLEMENTED; + if (mext->removed) ret = STATUS_DEVICE_REMOVED; + else + { + InsertTailList(&mext->irp_queue, &irp->Tail.Overlay.ListEntry); + ret = STATUS_PENDING; + } + break; + } + + case IOCTL_HID_WRITE_REPORT: + { + HID_XFER_PACKET *packet = irp->UserBuffer; + ULONG expected_size = report_id ? 2 : 1; + LIST_ENTRY *entry; + todo_wine + ok(in_size == sizeof(*packet), "got input size %u\n", in_size); + todo_wine + ok(!out_size, "got output size %u\n", out_size); + todo_wine_if(!report_id) + ok(packet->reportBufferLen == expected_size, "got report size %u\n", packet->reportBufferLen); + + if (report_id) + { + todo_wine_if(packet->reportBuffer[0] == 0xa5) + ok(packet->reportBuffer[0] == report_id, "got report id %x\n", packet->reportBuffer[0]); + } + else + { + todo_wine + ok(packet->reportBuffer[0] == 0xcd, "got first byte %x\n", packet->reportBuffer[0]); + } + + if ((entry = RemoveHeadList(&mext->irp_queue)) != &mext->irp_queue) + { + IRP *tmp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + memset(tmp->UserBuffer, 0xa5, 23); + if (report_id) ((char *)tmp->UserBuffer)[0] = report_id; + tmp->IoStatus.Information = 23; + tmp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(tmp, IO_NO_INCREMENT); + } + + irp->IoStatus.Information = packet->reportBufferLen; + ret = STATUS_SUCCESS; break; }
@@ -389,7 +472,9 @@ static NTSTATUS WINAPI driver_internal_ioctl(DEVICE_OBJECT *device, IRP *irp) ok(!in_size, "got input size %u\n", in_size); ok(out_size == sizeof(*packet), "got output size %u\n", out_size);
+ todo_wine_if(report_id) ok(packet->reportId == report_id, "got packet report id %u\n", packet->reportId); + todo_wine_if(report_id) ok(packet->reportBufferLen == expected_size, "got packet buffer len %u\n", packet->reportBufferLen); ok(!!packet->reportBuffer, "got packet buffer %p\n", packet->reportBuffer);
@@ -414,8 +499,11 @@ static NTSTATUS WINAPI driver_internal_ioctl(DEVICE_OBJECT *device, IRP *irp) ret = STATUS_NOT_IMPLEMENTED; }
- irp->IoStatus.Status = ret; - IoCompleteRequest(irp, IO_NO_INCREMENT); + if (ret != STATUS_PENDING) + { + irp->IoStatus.Status = ret; + IoCompleteRequest(irp, IO_NO_INCREMENT); + } return ret; }
@@ -475,6 +563,7 @@ NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *registry) { .Revision = HID_REVISION, .DriverObject = driver, + .DeviceExtensionSize = sizeof(struct minidevice_extension), .RegistryPath = registry, }; UNICODE_STRING name_str; diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 5453af8ff1c..df661327b41 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -1609,8 +1609,9 @@ static void test_hidp(HANDLE file, int report_id) .Usage = HID_USAGE_GENERIC_JOYSTICK, .UsagePage = HID_USAGE_PAGE_GENERIC, .InputReportByteLength = 24, + .OutputReportByteLength = 2, .FeatureReportByteLength = 18, - .NumberLinkCollectionNodes = 8, + .NumberLinkCollectionNodes = 9, .NumberInputButtonCaps = 13, .NumberInputValueCaps = 7, .NumberInputDataIndices = 43, @@ -1623,8 +1624,9 @@ static void test_hidp(HANDLE file, int report_id) .Usage = HID_USAGE_GENERIC_JOYSTICK, .UsagePage = HID_USAGE_PAGE_GENERIC, .InputReportByteLength = 23, + .OutputReportByteLength = 2, .FeatureReportByteLength = 17, - .NumberLinkCollectionNodes = 8, + .NumberLinkCollectionNodes = 9, .NumberInputButtonCaps = 13, .NumberInputValueCaps = 7, .NumberInputDataIndices = 43, @@ -1766,8 +1768,8 @@ static void test_hidp(HANDLE file, int report_id) .LinkUsage = HID_USAGE_GENERIC_JOYSTICK, .LinkUsagePage = HID_USAGE_PAGE_GENERIC, .CollectionType = 1, - .NumberOfChildren = 5, - .FirstChild = 7, + .NumberOfChildren = 6, + .FirstChild = 8, }, { .LinkUsage = HID_USAGE_GENERIC_JOYSTICK, @@ -2569,6 +2571,64 @@ static void test_hidp(HANDLE file, int report_id) todo_wine ok(!memcmp(buffer, buffer + 16, 16), "unexpected report value\n");
+ memset(report, 0xcd, sizeof(report)); + status = HidP_InitializeReportForID(HidP_Input, report_id, preparsed_data, report, caps.InputReportByteLength); + todo_wine_if(report_id) + ok(status == HIDP_STATUS_SUCCESS, "HidP_InitializeReportForID returned %#x\n", status); + + SetLastError(0xdeadbeef); + ret = HidD_GetInputReport(file, report, caps.InputReportByteLength); + ok(ret, "HidD_GetInputReport failed, last error %u\n", GetLastError()); + + memset(report, 0xcd, sizeof(report)); + SetLastError(0xdeadbeef); + ret = ReadFile(file, report, 0, &value, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_USER_BUFFER, "ReadFile failed, last error %u\n", GetLastError()); + ok(value == 0, "ReadFile returned %x\n", value); + SetLastError(0xdeadbeef); + ret = ReadFile(file, report, caps.InputReportByteLength - 1, &value, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_USER_BUFFER, "ReadFile failed, last error %u\n", GetLastError()); + ok(value == 0, "ReadFile returned %x\n", value); + + SetLastError(0xdeadbeef); + ret = WriteFile(file, report, 0, &value, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_USER_BUFFER, "WriteFile failed, last error %u\n", GetLastError()); + ok(value == 0, "WriteFile returned %x\n", value); + SetLastError(0xdeadbeef); + ret = WriteFile(file, report, caps.OutputReportByteLength - 1, &value, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "WriteFile failed, last error %u\n", GetLastError()); + ok(value == 0, "WriteFile returned %x\n", value); + + memset(report, 0xcd, sizeof(report)); + report[0] = 0xa5; + SetLastError(0xdeadbeef); + ret = WriteFile(file, report, caps.OutputReportByteLength, &value, NULL); + if (report_id) + { + todo_wine + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "WriteFile succeeded, last error %u\n", GetLastError()); + todo_wine + ok(value == 0, "WriteFile returned %x\n", value); + + SetLastError(0xdeadbeef); + report[0] = report_id; + ret = WriteFile(file, report, caps.OutputReportByteLength, &value, NULL); + ok(ret, "WriteFile failed, last error %u\n", GetLastError()); + ok(value == caps.OutputReportByteLength, "WriteFile returned %x\n", value); + } + else + { + ok(ret, "WriteFile failed, last error %u\n", GetLastError()); + ok(value == caps.OutputReportByteLength, "WriteFile returned %x\n", value); + } + + memset( report, 0xcd, sizeof(report) ); + SetLastError(0xdeadbeef); + ret = ReadFile( file, report, caps.InputReportByteLength, &value, NULL ); + ok(ret, "ReadFile failed, last error %u\n", GetLastError()); + ok(value == caps.InputReportByteLength, "ReadFile returned %x\n", value); + ok(report[0] == report_id, "unexpected report data\n"); + HidD_FreePreparsedData(preparsed_data); CloseHandle(file); } @@ -2616,7 +2676,7 @@ static void test_hid_device(DWORD report_id)
todo_wine ok(found, "didn't find device\n");
- file = CreateFileA(iface_detail->DevicePath, FILE_READ_ACCESS, + file = CreateFileA(iface_detail->DevicePath, FILE_READ_ACCESS | FILE_WRITE_ACCESS, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); ok(file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError());