ntoskrnl/tests: Test child device enumeration.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
cac26c77c9
commit
5e63c849b4
|
@ -261,8 +261,10 @@ NTSTATUS WINAPI HID_PNP_Dispatch(DEVICE_OBJECT *device, IRP *irp)
|
|||
irp->IoStatus.Information = (ULONG_PTR)id;
|
||||
rc = STATUS_SUCCESS;
|
||||
break;
|
||||
|
||||
case BusQueryContainerID:
|
||||
case BusQueryDeviceSerialNumber:
|
||||
FIXME("BusQueryDeviceSerialNumber not implemented\n");
|
||||
FIXME("unimplemented id type %#x\n", irpsp->Parameters.QueryId.IdType);
|
||||
ExFreePool(id);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ driver3_IMPORTS = winecrt0 ntoskrnl
|
|||
driver3_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
|
||||
driver_netio_IMPORTS = winecrt0 ntoskrnl netio
|
||||
driver_netio_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
|
||||
driver_pnp_IMPORTS = winecrt0 ntoskrnl
|
||||
driver_pnp_IMPORTS = winecrt0 ntoskrnl hal
|
||||
driver_pnp_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native
|
||||
|
||||
SOURCES = \
|
||||
|
|
|
@ -40,6 +40,10 @@
|
|||
#define IOCTL_WINETEST_BUS_REGISTER_IFACE CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_BUS_ENABLE_IFACE CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_BUS_DISABLE_IFACE CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_BUS_ADD_CHILD CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_BUS_REMOVE_CHILD CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_WINETEST_CHILD_GET_ID CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
static const char teststr[] = "Wine is not an emulator";
|
||||
|
||||
|
|
|
@ -29,15 +29,33 @@
|
|||
#include "winioctl.h"
|
||||
#include "ddk/wdm.h"
|
||||
|
||||
#include "wine/list.h"
|
||||
|
||||
#include "driver.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const GUID control_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc0}};
|
||||
static const GUID bus_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc1}};
|
||||
static const GUID child_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc2}};
|
||||
static UNICODE_STRING control_symlink, bus_symlink;
|
||||
|
||||
static DRIVER_OBJECT *driver_obj;
|
||||
static DEVICE_OBJECT *bus_fdo, *bus_pdo;
|
||||
|
||||
struct device
|
||||
{
|
||||
struct list entry;
|
||||
DEVICE_OBJECT *device_obj;
|
||||
unsigned int id;
|
||||
BOOL removed;
|
||||
UNICODE_STRING child_symlink;
|
||||
DEVICE_POWER_STATE power_state;
|
||||
};
|
||||
|
||||
static struct list device_list = LIST_INIT(device_list);
|
||||
|
||||
static FAST_MUTEX driver_lock;
|
||||
|
||||
static NTSTATUS fdo_pnp(IRP *irp)
|
||||
{
|
||||
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
|
||||
|
@ -65,24 +83,214 @@ static NTSTATUS fdo_pnp(IRP *irp)
|
|||
RtlFreeUnicodeString(&control_symlink);
|
||||
RtlFreeUnicodeString(&bus_symlink);
|
||||
return ret;
|
||||
|
||||
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
||||
{
|
||||
DEVICE_RELATIONS *devices;
|
||||
struct device *device;
|
||||
unsigned int i = 0;
|
||||
|
||||
if (stack->Parameters.QueryDeviceRelations.Type == RemovalRelations)
|
||||
break;
|
||||
|
||||
if (stack->Parameters.QueryDeviceRelations.Type != BusRelations)
|
||||
{
|
||||
ok(0, "Unexpected relations type %#x.\n", stack->Parameters.QueryDeviceRelations.Type);
|
||||
break;
|
||||
}
|
||||
|
||||
ExAcquireFastMutex(&driver_lock);
|
||||
|
||||
if (!(devices = ExAllocatePool(PagedPool,
|
||||
offsetof(DEVICE_RELATIONS, Objects[list_count(&device_list)]))))
|
||||
{
|
||||
ExReleaseFastMutex(&driver_lock);
|
||||
irp->IoStatus.Status = STATUS_NO_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
LIST_FOR_EACH_ENTRY(device, &device_list, struct device, entry)
|
||||
{
|
||||
devices->Objects[i++] = device->device_obj;
|
||||
ObfReferenceObject(device->device_obj);
|
||||
}
|
||||
|
||||
ExReleaseFastMutex(&driver_lock);
|
||||
|
||||
devices->Count = i;
|
||||
irp->IoStatus.Information = (ULONG_PTR)devices;
|
||||
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IoSkipCurrentIrpStackLocation(irp);
|
||||
return IoCallDriver(bus_pdo, irp);
|
||||
}
|
||||
|
||||
static NTSTATUS query_id(struct device *device, IRP *irp, BUS_QUERY_ID_TYPE type)
|
||||
{
|
||||
static const WCHAR device_id[] = L"wine\\test";
|
||||
WCHAR *id = NULL;
|
||||
|
||||
irp->IoStatus.Information = 0;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case BusQueryDeviceID:
|
||||
if (!(id = ExAllocatePool(PagedPool, sizeof(device_id))))
|
||||
return STATUS_NO_MEMORY;
|
||||
wcscpy(id, device_id);
|
||||
break;
|
||||
|
||||
case BusQueryInstanceID:
|
||||
if (!(id = ExAllocatePool(PagedPool, 9 * sizeof(WCHAR))))
|
||||
return STATUS_NO_MEMORY;
|
||||
swprintf(id, 9, L"%x", device->id);
|
||||
break;
|
||||
|
||||
case BusQueryHardwareIDs:
|
||||
{
|
||||
static const WCHAR hardware_id[] = L"winetest_hardware";
|
||||
const size_t size = ARRAY_SIZE(hardware_id) + 27 + 1;
|
||||
size_t len;
|
||||
|
||||
if (!(id = ExAllocatePool(PagedPool, size * sizeof(WCHAR))))
|
||||
return STATUS_NO_MEMORY;
|
||||
wcscpy(id, hardware_id);
|
||||
len = swprintf(id + ARRAY_SIZE(hardware_id), size - ARRAY_SIZE(hardware_id),
|
||||
L"winetest_hardware_%x", device->id);
|
||||
id[ARRAY_SIZE(hardware_id) + len + 1] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case BusQueryCompatibleIDs:
|
||||
{
|
||||
static const WCHAR compat_id[] = L"winetest_compat";
|
||||
const size_t size = ARRAY_SIZE(compat_id) + 25 + 1;
|
||||
size_t len;
|
||||
|
||||
if (!(id = ExAllocatePool(PagedPool, size * sizeof(WCHAR))))
|
||||
return STATUS_NO_MEMORY;
|
||||
wcscpy(id, compat_id);
|
||||
len = swprintf(id + ARRAY_SIZE(compat_id), size - ARRAY_SIZE(compat_id),
|
||||
L"winetest_compat_%x", device->id);
|
||||
id[ARRAY_SIZE(compat_id) + len + 1] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case BusQueryContainerID:
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
default:
|
||||
ok(0, "Unexpected ID query type %#x.\n", type);
|
||||
return irp->IoStatus.Status;
|
||||
}
|
||||
|
||||
irp->IoStatus.Information = (ULONG_PTR)id;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
|
||||
{
|
||||
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
|
||||
struct device *device = device_obj->DeviceExtension;
|
||||
NTSTATUS ret = irp->IoStatus.Status;
|
||||
|
||||
switch (stack->MinorFunction)
|
||||
{
|
||||
case IRP_MN_QUERY_ID:
|
||||
ret = query_id(device, irp, stack->Parameters.QueryId.IdType);
|
||||
break;
|
||||
|
||||
case IRP_MN_START_DEVICE:
|
||||
{
|
||||
POWER_STATE state = {.DeviceState = PowerDeviceD0};
|
||||
NTSTATUS status;
|
||||
|
||||
ok(!stack->Parameters.StartDevice.AllocatedResources, "expected no resources\n");
|
||||
ok(!stack->Parameters.StartDevice.AllocatedResourcesTranslated, "expected no translated resources\n");
|
||||
|
||||
status = IoRegisterDeviceInterface(device_obj, &child_class, NULL, &device->child_symlink);
|
||||
ok(!status, "Failed to register interface, status %#x.\n", status);
|
||||
|
||||
IoSetDeviceInterfaceState(&device->child_symlink, TRUE);
|
||||
|
||||
state = PoSetPowerState(device_obj, DevicePowerState, state);
|
||||
ok(state.DeviceState == device->power_state, "got previous state %u\n", state.DeviceState);
|
||||
device->power_state = PowerDeviceD0;
|
||||
ret = STATUS_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case IRP_MN_REMOVE_DEVICE:
|
||||
if (device->removed)
|
||||
{
|
||||
IoSetDeviceInterfaceState(&device->child_symlink, FALSE);
|
||||
RtlFreeUnicodeString(&device->child_symlink);
|
||||
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
IoDeleteDevice(device->device_obj);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ret = STATUS_SUCCESS;
|
||||
break;
|
||||
|
||||
case IRP_MN_QUERY_CAPABILITIES:
|
||||
{
|
||||
DEVICE_CAPABILITIES *caps = stack->Parameters.DeviceCapabilities.Capabilities;
|
||||
unsigned int i;
|
||||
|
||||
ok(caps->Size == sizeof(*caps), "wrong size %u\n", caps->Size);
|
||||
ok(caps->Version == 1, "wrong version %u\n", caps->Version);
|
||||
ok(!caps->DeviceD1, "got DeviceD1 %u\n", caps->DeviceD1);
|
||||
ok(!caps->DeviceD2, "got DeviceD2 %u\n", caps->DeviceD2);
|
||||
ok(!caps->LockSupported, "got LockSupported %u\n", caps->LockSupported);
|
||||
ok(!caps->EjectSupported, "got EjectSupported %u\n", caps->EjectSupported);
|
||||
ok(!caps->Removable, "got Removable %u\n", caps->Removable);
|
||||
ok(!caps->DockDevice, "got DockDevice %u\n", caps->DockDevice);
|
||||
ok(!caps->UniqueID, "got UniqueID %u\n", caps->UniqueID);
|
||||
ok(!caps->SilentInstall, "got SilentInstall %u\n", caps->SilentInstall);
|
||||
ok(!caps->RawDeviceOK, "got RawDeviceOK %u\n", caps->RawDeviceOK);
|
||||
ok(!caps->SurpriseRemovalOK, "got SurpriseRemovalOK %u\n", caps->SurpriseRemovalOK);
|
||||
ok(!caps->WakeFromD0, "got WakeFromD0 %u\n", caps->WakeFromD0);
|
||||
ok(!caps->WakeFromD1, "got WakeFromD1 %u\n", caps->WakeFromD1);
|
||||
ok(!caps->WakeFromD2, "got WakeFromD2 %u\n", caps->WakeFromD2);
|
||||
ok(!caps->WakeFromD3, "got WakeFromD3 %u\n", caps->WakeFromD3);
|
||||
ok(!caps->HardwareDisabled, "got HardwareDisabled %u\n", caps->HardwareDisabled);
|
||||
ok(!caps->NonDynamic, "got NonDynamic %u\n", caps->NonDynamic);
|
||||
ok(!caps->WarmEjectSupported, "got WarmEjectSupported %u\n", caps->WarmEjectSupported);
|
||||
ok(!caps->NoDisplayInUI, "got NoDisplayInUI %u\n", caps->NoDisplayInUI);
|
||||
ok(caps->Address == 0xffffffff, "got Address %#x\n", caps->Address);
|
||||
ok(caps->UINumber == 0xffffffff, "got UINumber %#x\n", caps->UINumber);
|
||||
for (i = 0; i < PowerSystemMaximum; ++i)
|
||||
ok(caps->DeviceState[i] == PowerDeviceUnspecified, "got DeviceState[%u] %u\n", i, caps->DeviceState[i]);
|
||||
ok(caps->SystemWake == PowerSystemUnspecified, "got SystemWake %u\n", caps->SystemWake);
|
||||
ok(caps->DeviceWake == PowerDeviceUnspecified, "got DeviceWake %u\n", caps->DeviceWake);
|
||||
ok(!caps->D1Latency, "got D1Latency %u\n", caps->D1Latency);
|
||||
ok(!caps->D2Latency, "got D2Latency %u\n", caps->D2Latency);
|
||||
ok(!caps->D3Latency, "got D3Latency %u\n", caps->D3Latency);
|
||||
|
||||
/* If caps->RawDeviceOK is not set, we won't receive
|
||||
* IRP_MN_START_DEVICE unless there's a function driver. */
|
||||
caps->RawDeviceOK = 1;
|
||||
caps->SurpriseRemovalOK = 1;
|
||||
caps->EjectSupported = 1;
|
||||
caps->UniqueID = 1;
|
||||
|
||||
caps->DeviceState[PowerSystemWorking] = PowerDeviceD0;
|
||||
caps->DeviceState[PowerSystemSleeping1] = PowerDeviceD3;
|
||||
caps->DeviceState[PowerSystemSleeping2] = PowerDeviceD3;
|
||||
caps->DeviceState[PowerSystemSleeping3] = PowerDeviceD3;
|
||||
caps->DeviceState[PowerSystemHibernate] = PowerDeviceD3;
|
||||
caps->DeviceState[PowerSystemShutdown] = PowerDeviceD3;
|
||||
|
||||
ret = STATUS_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||||
case IRP_MN_SURPRISE_REMOVAL:
|
||||
ret = STATUS_SUCCESS;
|
||||
break;
|
||||
|
@ -274,6 +482,87 @@ static NTSTATUS fdo_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
|
|||
IoSetDeviceInterfaceState(&bus_symlink, FALSE);
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
case IOCTL_WINETEST_BUS_ADD_CHILD:
|
||||
{
|
||||
DEVICE_OBJECT *device_obj;
|
||||
UNICODE_STRING string;
|
||||
struct device *device;
|
||||
NTSTATUS status;
|
||||
WCHAR name[30];
|
||||
int id;
|
||||
|
||||
if (stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(int))
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
id = *(int *)irp->AssociatedIrp.SystemBuffer;
|
||||
|
||||
swprintf(name, ARRAY_SIZE(name), L"\\Device\\winetest_pnp_%x", id);
|
||||
RtlInitUnicodeString(&string, name);
|
||||
status = IoCreateDevice(driver_obj, sizeof(*device), &string, FILE_DEVICE_UNKNOWN, 0, FALSE, &device_obj);
|
||||
ok(!status, "Failed to create device, status %#x.\n", status);
|
||||
|
||||
device = device_obj->DeviceExtension;
|
||||
memset(device, 0, sizeof(*device));
|
||||
device->device_obj = device_obj;
|
||||
device->id = id;
|
||||
device->removed = FALSE;
|
||||
|
||||
ExAcquireFastMutex(&driver_lock);
|
||||
list_add_tail(&device_list, &device->entry);
|
||||
ExReleaseFastMutex(&driver_lock);
|
||||
|
||||
device_obj->Flags &= ~DO_DEVICE_INITIALIZING;
|
||||
|
||||
IoInvalidateDeviceRelations(bus_pdo, BusRelations);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
case IOCTL_WINETEST_BUS_REMOVE_CHILD:
|
||||
{
|
||||
struct device *device;
|
||||
int id;
|
||||
|
||||
if (stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(int))
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
id = *(int *)irp->AssociatedIrp.SystemBuffer;
|
||||
|
||||
ExAcquireFastMutex(&driver_lock);
|
||||
LIST_FOR_EACH_ENTRY(device, &device_list, struct device, entry)
|
||||
{
|
||||
if (device->id == id)
|
||||
{
|
||||
list_remove(&device->entry);
|
||||
device->removed = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ExReleaseFastMutex(&driver_lock);
|
||||
|
||||
IoInvalidateDeviceRelations(bus_pdo, BusRelations);
|
||||
|
||||
/* The actual removal might be asynchronous; we can't test that the
|
||||
* device is gone here. */
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
default:
|
||||
ok(0, "Unexpected ioctl %#x.\n", code);
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS pdo_ioctl(struct device *device, IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case IOCTL_WINETEST_CHILD_GET_ID:
|
||||
if (stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(int))
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
*(int *)irp->AssociatedIrp.SystemBuffer = device->id;
|
||||
irp->IoStatus.Information = sizeof(device->id);
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
default:
|
||||
ok(0, "Unexpected ioctl %#x.\n", code);
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
|
@ -288,6 +577,8 @@ 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);
|
||||
|
||||
irp->IoStatus.Status = status;
|
||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
|
@ -346,5 +637,9 @@ NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *registry)
|
|||
driver->MajorFunction[IRP_MJ_CREATE] = driver_create;
|
||||
driver->MajorFunction[IRP_MJ_CLOSE] = driver_close;
|
||||
|
||||
driver_obj = driver;
|
||||
|
||||
ExInitializeFastMutex(&driver_lock);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "mscat.h"
|
||||
#include "mssip.h"
|
||||
#include "setupapi.h"
|
||||
#include "cfgmgr32.h"
|
||||
#include "newdev.h"
|
||||
#include "dbt.h"
|
||||
#include "initguid.h"
|
||||
|
@ -987,8 +988,9 @@ static void add_file_to_catalog(HANDLE catalog, const WCHAR *file)
|
|||
|
||||
static const GUID control_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc0}};
|
||||
static const GUID bus_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc1}};
|
||||
static const GUID child_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc2}};
|
||||
|
||||
static unsigned int got_bus_arrival, got_bus_removal;
|
||||
static unsigned int got_bus_arrival, got_bus_removal, got_child_arrival, got_child_removal;
|
||||
|
||||
static LRESULT WINAPI device_notify_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
|
@ -1023,6 +1025,12 @@ static LRESULT WINAPI device_notify_proc(HWND window, UINT message, WPARAM wpara
|
|||
todo_wine ok(!strcmp(iface->dbcc_name, "\\\\?\\ROOT#WINETEST#0#{deadbeef-29ef-4538-a5fd-b69573a362c1}"),
|
||||
"got name %s\n", debugstr_a(iface->dbcc_name));
|
||||
}
|
||||
else if (IsEqualGUID(&iface->dbcc_classguid, &child_class))
|
||||
{
|
||||
++got_child_arrival;
|
||||
todo_wine ok(!strcmp(iface->dbcc_name, "\\\\?\\wine#test#1#{deadbeef-29ef-4538-a5fd-b69573a362c2}"),
|
||||
"got name %s\n", debugstr_a(iface->dbcc_name));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1045,6 +1053,12 @@ static LRESULT WINAPI device_notify_proc(HWND window, UINT message, WPARAM wpara
|
|||
todo_wine ok(!strcmp(iface->dbcc_name, "\\\\?\\ROOT#WINETEST#0#{deadbeef-29ef-4538-a5fd-b69573a362c1}"),
|
||||
"got name %s\n", debugstr_a(iface->dbcc_name));
|
||||
}
|
||||
else if (IsEqualGUID(&iface->dbcc_classguid, &child_class))
|
||||
{
|
||||
++got_child_removal;
|
||||
todo_wine ok(!strcmp(iface->dbcc_name, "\\\\?\\wine#test#1#{deadbeef-29ef-4538-a5fd-b69573a362c2}"),
|
||||
"got name %s\n", debugstr_a(iface->dbcc_name));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1067,6 +1081,9 @@ static void pump_messages(void)
|
|||
|
||||
static void test_pnp_devices(void)
|
||||
{
|
||||
static const char expect_hardware_id[] = "winetest_hardware\0winetest_hardware_1\0";
|
||||
static const char expect_compat_id[] = "winetest_compat\0winetest_compat_1\0";
|
||||
|
||||
char buffer[200];
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_A *iface_detail = (void *)buffer;
|
||||
SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)};
|
||||
|
@ -1082,11 +1099,15 @@ static void test_pnp_devices(void)
|
|||
.lpfnWndProc = device_notify_proc,
|
||||
};
|
||||
HDEVNOTIFY notify_handle;
|
||||
HANDLE window;
|
||||
DWORD size, type, dword;
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
UNICODE_STRING string;
|
||||
IO_STATUS_BLOCK io;
|
||||
HANDLE bus, child;
|
||||
HDEVINFO set;
|
||||
HANDLE bus;
|
||||
DWORD size;
|
||||
HWND window;
|
||||
BOOL ret;
|
||||
int id;
|
||||
|
||||
ret = RegisterClassA(&class);
|
||||
ok(ret, "failed to register class\n");
|
||||
|
@ -1193,6 +1214,98 @@ static void test_pnp_devices(void)
|
|||
ok(GetLastError() == ERROR_NO_MORE_ITEMS, "got error %#x\n", GetLastError());
|
||||
SetupDiDestroyDeviceInfoList(set);
|
||||
|
||||
/* Test exposing a child device. */
|
||||
|
||||
id = 1;
|
||||
ret = DeviceIoControl(bus, IOCTL_WINETEST_BUS_ADD_CHILD, &id, sizeof(id), NULL, 0, &size, NULL);
|
||||
ok(ret, "got error %u\n", GetLastError());
|
||||
|
||||
pump_messages();
|
||||
todo_wine ok(got_child_arrival == 1, "got %u child arrival messages\n", got_child_arrival);
|
||||
ok(!got_child_removal, "got %u child removal messages\n", got_child_removal);
|
||||
|
||||
set = SetupDiGetClassDevsA(&child_class, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
||||
ok(set != INVALID_HANDLE_VALUE, "failed to get device list, error %#x\n", GetLastError());
|
||||
|
||||
ret = SetupDiEnumDeviceInfo(set, 0, &device);
|
||||
todo_wine ok(ret, "failed to get device, error %#x\n", GetLastError());
|
||||
if (ret)
|
||||
{
|
||||
ok(IsEqualGUID(&device.ClassGuid, &GUID_NULL), "wrong class %s\n", debugstr_guid(&device.ClassGuid));
|
||||
|
||||
ret = SetupDiGetDeviceInstanceIdA(set, &device, buffer, sizeof(buffer), NULL);
|
||||
ok(ret, "failed to get device ID, error %#x\n", GetLastError());
|
||||
ok(!strcasecmp(buffer, "wine\\test\\1"), "got ID %s\n", debugstr_a(buffer));
|
||||
|
||||
ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_CAPABILITIES,
|
||||
&type, (BYTE *)&dword, sizeof(dword), NULL);
|
||||
ok(ret, "got error %#x\n", GetLastError());
|
||||
ok(dword == (CM_DEVCAP_EJECTSUPPORTED | CM_DEVCAP_UNIQUEID
|
||||
| CM_DEVCAP_RAWDEVICEOK | CM_DEVCAP_SURPRISEREMOVALOK), "got flags %#x\n", dword);
|
||||
ok(type == REG_DWORD, "got type %u\n", type);
|
||||
|
||||
ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_CLASSGUID,
|
||||
&type, (BYTE *)buffer, sizeof(buffer), NULL);
|
||||
ok(!ret, "expected failure\n");
|
||||
ok(GetLastError() == ERROR_INVALID_DATA, "got error %#x\n", GetLastError());
|
||||
|
||||
ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_DEVTYPE,
|
||||
&type, (BYTE *)&dword, sizeof(dword), NULL);
|
||||
ok(!ret, "expected failure\n");
|
||||
ok(GetLastError() == ERROR_INVALID_DATA, "got error %#x\n", GetLastError());
|
||||
|
||||
ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_DRIVER,
|
||||
&type, (BYTE *)buffer, sizeof(buffer), NULL);
|
||||
ok(!ret, "expected failure\n");
|
||||
ok(GetLastError() == ERROR_INVALID_DATA, "got error %#x\n", GetLastError());
|
||||
|
||||
ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_HARDWAREID,
|
||||
&type, (BYTE *)buffer, sizeof(buffer), &size);
|
||||
ok(ret, "got error %#x\n", GetLastError());
|
||||
ok(type == REG_MULTI_SZ, "got type %u\n", type);
|
||||
ok(size == sizeof(expect_hardware_id), "got size %u\n", size);
|
||||
ok(!memcmp(buffer, expect_hardware_id, size), "got hardware IDs %s\n", debugstr_an(buffer, size));
|
||||
|
||||
ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_COMPATIBLEIDS,
|
||||
&type, (BYTE *)buffer, sizeof(buffer), &size);
|
||||
ok(ret, "got error %#x\n", GetLastError());
|
||||
ok(type == REG_MULTI_SZ, "got type %u\n", type);
|
||||
ok(size == sizeof(expect_compat_id), "got size %u\n", size);
|
||||
ok(!memcmp(buffer, expect_compat_id, size), "got compatible IDs %s\n", debugstr_an(buffer, size));
|
||||
|
||||
ret = SetupDiGetDeviceRegistryPropertyA(set, &device, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,
|
||||
&type, (BYTE *)buffer, sizeof(buffer), NULL);
|
||||
ok(ret, "got error %#x\n", GetLastError());
|
||||
ok(type == REG_SZ, "got type %u\n", type);
|
||||
ok(!strcmp(buffer, "\\Device\\winetest_pnp_1"), "got PDO name %s\n", debugstr_a(buffer));
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(set);
|
||||
|
||||
RtlInitUnicodeString(&string, L"\\Device\\winetest_pnp_1");
|
||||
InitializeObjectAttributes(&attr, &string, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||||
ret = NtOpenFile(&child, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT);
|
||||
ok(!ret, "failed to open child: %#x\n", ret);
|
||||
|
||||
id = 0xdeadbeef;
|
||||
ret = DeviceIoControl(child, IOCTL_WINETEST_CHILD_GET_ID, NULL, 0, &id, sizeof(id), &size, NULL);
|
||||
ok(ret, "got error %u\n", GetLastError());
|
||||
ok(id == 1, "got id %d\n", id);
|
||||
ok(size == sizeof(id), "got size %u\n", size);
|
||||
|
||||
CloseHandle(child);
|
||||
|
||||
id = 1;
|
||||
ret = DeviceIoControl(bus, IOCTL_WINETEST_BUS_REMOVE_CHILD, &id, sizeof(id), NULL, 0, &size, NULL);
|
||||
ok(ret, "got error %u\n", GetLastError());
|
||||
|
||||
pump_messages();
|
||||
todo_wine ok(got_child_arrival == 1, "got %u child arrival messages\n", got_child_arrival);
|
||||
todo_wine 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);
|
||||
ok(ret == STATUS_OBJECT_NAME_NOT_FOUND, "got %#x\n", ret);
|
||||
|
||||
CloseHandle(bus);
|
||||
|
||||
UnregisterDeviceNotification(notify_handle);
|
||||
|
@ -1210,6 +1323,7 @@ static void test_pnp_driver(struct testsign_context *ctx)
|
|||
SC_HANDLE manager, service;
|
||||
BOOL ret, need_reboot;
|
||||
HANDLE catalog, file;
|
||||
unsigned int i;
|
||||
HDEVINFO set;
|
||||
FILE *f;
|
||||
|
||||
|
@ -1340,6 +1454,15 @@ static void test_pnp_driver(struct testsign_context *ctx)
|
|||
ret = SetupDiDestroyDeviceInfoList(set);
|
||||
ok(ret, "failed to destroy set, error %#x\n", GetLastError());
|
||||
|
||||
set = SetupDiGetClassDevsA(NULL, "wine", NULL, DIGCF_ALLCLASSES);
|
||||
ok(set != INVALID_HANDLE_VALUE, "failed to get device list, error %#x\n", GetLastError());
|
||||
|
||||
for (i = 0; SetupDiEnumDeviceInfo(set, i, &device); ++i)
|
||||
{
|
||||
ret = SetupDiCallClassInstaller(DIF_REMOVE, set, &device);
|
||||
ok(ret, "failed to remove device, error %#x\n", GetLastError());
|
||||
}
|
||||
|
||||
/* Windows stops the service but does not delete it. */
|
||||
manager = OpenSCManagerA(NULL, NULL, SC_MANAGER_CONNECT);
|
||||
ok(!!manager, "failed to open service manager, error %u\n", GetLastError());
|
||||
|
|
|
@ -110,6 +110,18 @@ typedef DWORD CONFIGRET;
|
|||
#define MAX_GUID_STRING_LEN 39
|
||||
#define MAX_PROFILE_LEN 80
|
||||
|
||||
#define CM_DEVCAP_LOCKSUPPORTED 0x00000001
|
||||
#define CM_DEVCAP_EJECTSUPPORTED 0x00000002
|
||||
#define CM_DEVCAP_REMOVABLE 0x00000004
|
||||
#define CM_DEVCAP_DOCKDEVICE 0x00000008
|
||||
#define CM_DEVCAP_UNIQUEID 0x00000010
|
||||
#define CM_DEVCAP_SILENTINSTALL 0x00000020
|
||||
#define CM_DEVCAP_RAWDEVICEOK 0x00000040
|
||||
#define CM_DEVCAP_SURPRISEREMOVALOK 0x00000080
|
||||
#define CM_DEVCAP_HARDWAREDISABLED 0x00000100
|
||||
#define CM_DEVCAP_NONDYNAMIC 0x00000200
|
||||
#define CM_DEVCAP_SECUREDEVICE 0x00000400
|
||||
|
||||
#define CM_DRP_DEVICEDESC 0x01
|
||||
#define CM_DRP_HARDWAREID 0x02
|
||||
#define CM_DRP_COMPATIBLEIDS 0x03
|
||||
|
|
|
@ -831,7 +831,8 @@ typedef enum _BUS_QUERY_ID_TYPE {
|
|||
BusQueryHardwareIDs,
|
||||
BusQueryCompatibleIDs,
|
||||
BusQueryInstanceID,
|
||||
BusQueryDeviceSerialNumber
|
||||
BusQueryDeviceSerialNumber,
|
||||
BusQueryContainerID,
|
||||
} BUS_QUERY_ID_TYPE, *PBUS_QUERY_ID_TYPE;
|
||||
|
||||
typedef enum _CREATE_FILE_TYPE {
|
||||
|
@ -1646,6 +1647,8 @@ static inline void IoCopyCurrentIrpStackLocationToNext(IRP *irp)
|
|||
#define SYMBOLIC_LINK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1)
|
||||
|
||||
NTSTATUS WINAPI DbgQueryDebugFilterState(ULONG, ULONG);
|
||||
|
||||
void FASTCALL ExAcquireFastMutex(FAST_MUTEX*);
|
||||
void FASTCALL ExAcquireFastMutexUnsafe(PFAST_MUTEX);
|
||||
BOOLEAN WINAPI ExAcquireResourceExclusiveLite(ERESOURCE*,BOOLEAN);
|
||||
BOOLEAN WINAPI ExAcquireResourceSharedLite(ERESOURCE*,BOOLEAN);
|
||||
|
@ -1672,6 +1675,7 @@ LIST_ENTRY * WINAPI ExInterlockedRemoveHeadList(LIST_ENTRY*,KSPIN_LOCK*);
|
|||
BOOLEAN WINAPI ExIsResourceAcquiredExclusiveLite(ERESOURCE*);
|
||||
ULONG WINAPI ExIsResourceAcquiredSharedLite(ERESOURCE*);
|
||||
void * WINAPI ExRegisterCallback(PCALLBACK_OBJECT,PCALLBACK_FUNCTION,void*);
|
||||
void FASTCALL ExReleaseFastMutex(FAST_MUTEX*);
|
||||
void FASTCALL ExReleaseFastMutexUnsafe(PFAST_MUTEX);
|
||||
void WINAPI ExReleaseResourceForThreadLite(ERESOURCE*,ERESOURCE_THREAD);
|
||||
ULONG WINAPI ExSetTimerResolution(ULONG,BOOLEAN);
|
||||
|
|
Loading…
Reference in New Issue