ntoskrnl.exe/tests: Add some pending / remove tests.
This shows that removing a device should send IRP_MN_SURPRISE_REMOVAL only, and that it's still possible to use DeviceIoControl on already opened handles. IRP_MN_REMOVE_DEVICE should only be sent when all then opened handles are closed. Signed-off-by: Rémi Bernon <rbernon@codeweavers.com> Signed-off-by: Zebediah Figura <zfigura@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
bd26083162
commit
19d1375bce
|
@ -35,6 +35,8 @@
|
|||
#define IOCTL_WINETEST_RETURN_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80a, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_MISMATCHED_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80b, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_COMPLETION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80c, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_MARK_PENDING CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80d, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_CHECK_REMOVED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80e, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_WINETEST_BUS_MAIN CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_BUS_REGISTER_IFACE CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
|
|
@ -41,6 +41,56 @@ static UNICODE_STRING control_symlink, bus_symlink;
|
|||
static DRIVER_OBJECT *driver_obj;
|
||||
static DEVICE_OBJECT *bus_fdo, *bus_pdo;
|
||||
|
||||
static DWORD remove_device_count;
|
||||
static DWORD surprise_removal_count;
|
||||
static DWORD query_remove_device_count;
|
||||
static DWORD cancel_remove_device_count;
|
||||
|
||||
struct irp_queue
|
||||
{
|
||||
KSPIN_LOCK lock;
|
||||
LIST_ENTRY list;
|
||||
};
|
||||
|
||||
static IRP *irp_queue_pop(struct irp_queue *queue)
|
||||
{
|
||||
KIRQL irql;
|
||||
IRP *irp;
|
||||
|
||||
KeAcquireSpinLock(&queue->lock, &irql);
|
||||
if (IsListEmpty(&queue->list)) irp = NULL;
|
||||
else irp = CONTAINING_RECORD(RemoveHeadList(&queue->list), IRP, Tail.Overlay.ListEntry);
|
||||
KeReleaseSpinLock(&queue->lock, irql);
|
||||
|
||||
return irp;
|
||||
}
|
||||
|
||||
static void irp_queue_push(struct irp_queue *queue, IRP *irp)
|
||||
{
|
||||
KIRQL irql;
|
||||
|
||||
KeAcquireSpinLock(&queue->lock, &irql);
|
||||
InsertTailList(&queue->list, &irp->Tail.Overlay.ListEntry);
|
||||
KeReleaseSpinLock(&queue->lock, irql);
|
||||
}
|
||||
|
||||
static void irp_queue_clear(struct irp_queue *queue)
|
||||
{
|
||||
IRP *irp;
|
||||
|
||||
while ((irp = irp_queue_pop(queue)))
|
||||
{
|
||||
irp->IoStatus.Status = STATUS_DELETE_PENDING;
|
||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
}
|
||||
}
|
||||
|
||||
static void irp_queue_init(struct irp_queue *queue)
|
||||
{
|
||||
KeInitializeSpinLock(&queue->lock);
|
||||
InitializeListHead(&queue->list);
|
||||
}
|
||||
|
||||
struct device
|
||||
{
|
||||
struct list entry;
|
||||
|
@ -49,6 +99,7 @@ struct device
|
|||
BOOL removed;
|
||||
UNICODE_STRING child_symlink;
|
||||
DEVICE_POWER_STATE power_state;
|
||||
struct irp_queue irp_queue;
|
||||
};
|
||||
|
||||
static struct list device_list = LIST_INIT(device_list);
|
||||
|
@ -207,6 +258,8 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
|
|||
POWER_STATE state = {.DeviceState = PowerDeviceD0};
|
||||
NTSTATUS status;
|
||||
|
||||
irp_queue_init(&device->irp_queue);
|
||||
|
||||
ok(!stack->Parameters.StartDevice.AllocatedResources, "expected no resources\n");
|
||||
ok(!stack->Parameters.StartDevice.AllocatedResourcesTranslated, "expected no translated resources\n");
|
||||
|
||||
|
@ -223,6 +276,14 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
|
|||
}
|
||||
|
||||
case IRP_MN_REMOVE_DEVICE:
|
||||
/* should've been checked and reset by IOCTL_WINETEST_CHECK_REMOVED */
|
||||
ok(remove_device_count == 0, "expected no IRP_MN_REMOVE_DEVICE\n");
|
||||
todo_wine ok(surprise_removal_count == 0, "expected no IRP_MN_SURPRISE_REMOVAL\n");
|
||||
ok(query_remove_device_count == 0, "expected no IRP_MN_QUERY_REMOVE_DEVICE\n");
|
||||
ok(cancel_remove_device_count == 0, "expected no IRP_MN_CANCEL_REMOVE_DEVICE\n");
|
||||
|
||||
remove_device_count++;
|
||||
irp_queue_clear(&device->irp_queue);
|
||||
if (device->removed)
|
||||
{
|
||||
IoSetDeviceInterfaceState(&device->child_symlink, FALSE);
|
||||
|
@ -289,8 +350,20 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
|
|||
break;
|
||||
}
|
||||
|
||||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||||
case IRP_MN_SURPRISE_REMOVAL:
|
||||
surprise_removal_count++;
|
||||
irp_queue_clear(&device->irp_queue);
|
||||
ret = STATUS_SUCCESS;
|
||||
break;
|
||||
|
||||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||||
query_remove_device_count++;
|
||||
irp_queue_clear(&device->irp_queue);
|
||||
ret = STATUS_SUCCESS;
|
||||
break;
|
||||
|
||||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||||
cancel_remove_device_count++;
|
||||
ret = STATUS_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
@ -579,8 +652,10 @@ static NTSTATUS fdo_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
|
|||
}
|
||||
}
|
||||
|
||||
static NTSTATUS pdo_ioctl(struct device *device, IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
|
||||
static NTSTATUS pdo_ioctl(DEVICE_OBJECT *device_obj, IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
|
||||
{
|
||||
struct device *device = device_obj->DeviceExtension;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case IOCTL_WINETEST_CHILD_GET_ID:
|
||||
|
@ -590,6 +665,22 @@ static NTSTATUS pdo_ioctl(struct device *device, IRP *irp, IO_STACK_LOCATION *st
|
|||
irp->IoStatus.Information = sizeof(device->id);
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
case IOCTL_WINETEST_MARK_PENDING:
|
||||
IoMarkIrpPending(irp);
|
||||
irp_queue_push(&device->irp_queue, irp);
|
||||
return STATUS_PENDING;
|
||||
|
||||
case IOCTL_WINETEST_CHECK_REMOVED:
|
||||
ok(remove_device_count == 0, "expected IRP_MN_REMOVE_DEVICE\n");
|
||||
ok(surprise_removal_count == 1, "expected IRP_MN_SURPRISE_REMOVAL\n");
|
||||
ok(query_remove_device_count == 0, "expected no IRP_MN_QUERY_REMOVE_DEVICE\n");
|
||||
ok(cancel_remove_device_count == 0, "expected no IRP_MN_CANCEL_REMOVE_DEVICE\n");
|
||||
remove_device_count = 0;
|
||||
surprise_removal_count = 0;
|
||||
query_remove_device_count = 0;
|
||||
cancel_remove_device_count = 0;
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
default:
|
||||
ok(0, "Unexpected ioctl %#x.\n", code);
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
|
@ -605,10 +696,10 @@ static NTSTATUS WINAPI driver_ioctl(DEVICE_OBJECT *device, IRP *irp)
|
|||
if (device == bus_fdo)
|
||||
status = fdo_ioctl(irp, stack, code);
|
||||
else
|
||||
status = pdo_ioctl(device->DeviceExtension, irp, stack, code);
|
||||
status = pdo_ioctl(device, irp, stack, code);
|
||||
|
||||
irp->IoStatus.Status = status;
|
||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
if (status != STATUS_PENDING) IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
|
@ -1150,10 +1150,11 @@ static void test_pnp_devices(void)
|
|||
};
|
||||
HDEVNOTIFY notify_handle;
|
||||
DWORD size, type, dword;
|
||||
HANDLE bus, child, tmp;
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
UNICODE_STRING string;
|
||||
OVERLAPPED ovl = {0};
|
||||
IO_STATUS_BLOCK io;
|
||||
HANDLE bus, child;
|
||||
HDEVINFO set;
|
||||
HWND window;
|
||||
BOOL ret;
|
||||
|
@ -1349,6 +1350,14 @@ static void test_pnp_devices(void)
|
|||
|
||||
CloseHandle(child);
|
||||
|
||||
ret = NtOpenFile(&child, SYNCHRONIZE, &attr, &io, 0, 0);
|
||||
ok(!ret, "failed to open child: %#x\n", ret);
|
||||
|
||||
ret = DeviceIoControl(child, IOCTL_WINETEST_MARK_PENDING, NULL, 0, NULL, 0, &size, &ovl);
|
||||
ok(!ret, "DeviceIoControl succeded\n");
|
||||
ok(GetLastError() == ERROR_IO_PENDING, "got error %u\n", GetLastError());
|
||||
ok(size == 0, "got size %u\n", size);
|
||||
|
||||
id = 1;
|
||||
ret = DeviceIoControl(bus, IOCTL_WINETEST_BUS_REMOVE_CHILD, &id, sizeof(id), NULL, 0, &size, NULL);
|
||||
ok(ret, "got error %u\n", GetLastError());
|
||||
|
@ -1357,7 +1366,24 @@ static void test_pnp_devices(void)
|
|||
ok(got_child_arrival == 1, "got %u child arrival messages\n", got_child_arrival);
|
||||
ok(got_child_removal == 1, "got %u child removal messages\n", got_child_removal);
|
||||
|
||||
ret = NtOpenFile(&child, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT);
|
||||
ret = DeviceIoControl(child, IOCTL_WINETEST_CHECK_REMOVED, NULL, 0, NULL, 0, &size, NULL);
|
||||
todo_wine ok(ret, "got error %u\n", GetLastError());
|
||||
|
||||
ret = NtOpenFile(&tmp, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT);
|
||||
todo_wine ok(ret == STATUS_NO_SUCH_DEVICE, "got %#x\n", ret);
|
||||
|
||||
ret = GetOverlappedResult(child, &ovl, &size, TRUE);
|
||||
ok(!ret, "unexpected success.\n");
|
||||
ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError());
|
||||
ok(size == 0, "got size %u\n", size);
|
||||
|
||||
CloseHandle(child);
|
||||
|
||||
pump_messages();
|
||||
ok(got_child_arrival == 1, "got %u child arrival messages\n", got_child_arrival);
|
||||
ok(got_child_removal == 1, "got %u child removal messages\n", got_child_removal);
|
||||
|
||||
ret = NtOpenFile(&tmp, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT);
|
||||
ok(ret == STATUS_OBJECT_NAME_NOT_FOUND, "got %#x\n", ret);
|
||||
|
||||
CloseHandle(bus);
|
||||
|
|
Loading…
Reference in New Issue