[PATCH 5/6] ntoskrnl.exe/tests: Add tests to read/write reports on device.

Rémi Bernon rbernon at codeweavers.com
Fri Jun 18 07:06:10 CDT 2021


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 at 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());
 
-- 
2.31.0




More information about the wine-devel mailing list