diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index 583907262fd..92f9afab33e 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -3648,6 +3648,8 @@ NTSTATUS WINAPI ZwLoadDriver( const UNICODE_STRING *service_name ) driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry ); driver->service_handle = service_handle; + pnp_manager_enumerate_root_devices( service_name->Buffer + wcslen( servicesW ) ); + set_service_status( service_handle, SERVICE_RUNNING, SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN ); return STATUS_SUCCESS; diff --git a/dlls/ntoskrnl.exe/ntoskrnl_private.h b/dlls/ntoskrnl.exe/ntoskrnl_private.h index 1c5952f7c10..b5244ef1641 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl_private.h +++ b/dlls/ntoskrnl.exe/ntoskrnl_private.h @@ -76,6 +76,7 @@ extern POBJECT_TYPE SeTokenObjectType; void ObReferenceObject( void *obj ) DECLSPEC_HIDDEN; +void pnp_manager_enumerate_root_devices( const WCHAR *driver_name ) DECLSPEC_HIDDEN; void pnp_manager_start(void) DECLSPEC_HIDDEN; void pnp_manager_stop(void) DECLSPEC_HIDDEN; diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c index 6f2283bc834..48430f31123 100644 --- a/dlls/ntoskrnl.exe/pnp.c +++ b/dlls/ntoskrnl.exe/pnp.c @@ -375,6 +375,18 @@ static BOOL install_device_driver( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVIN return TRUE; } +/* Load the function driver for a newly created PDO, if one is present, and + * send IRPs to start the device. */ +static void start_device( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVINFO_DATA *sp_device ) +{ + load_function_driver( device, set, sp_device ); + if (device->DriverObject) + { + send_pnp_irp( device, IRP_MN_START_DEVICE ); + send_power_irp( device, PowerDeviceD0 ); + } +} + static void handle_bus_relations( DEVICE_OBJECT *device ) { static const WCHAR infpathW[] = {'I','n','f','P','a','t','h',0}; @@ -420,19 +432,14 @@ static void handle_bus_relations( DEVICE_OBJECT *device ) return; } - load_function_driver( device, set, &sp_device ); - if (device->DriverObject) - { - send_pnp_irp( device, IRP_MN_START_DEVICE ); - send_power_irp( device, PowerDeviceD0 ); - } + start_device( device, set, &sp_device ); SetupDiDestroyDeviceInfoList( set ); } -static void handle_removal_relations( DEVICE_OBJECT *device ) +static void remove_device( DEVICE_OBJECT *device ) { - TRACE( "(%p)\n", device ); + TRACE("Removing device %p.\n", device); send_power_irp( device, PowerDeviceD3 ); send_pnp_irp( device, IRP_MN_SURPRISE_REMOVAL ); @@ -452,7 +459,7 @@ void WINAPI IoInvalidateDeviceRelations( DEVICE_OBJECT *device_object, DEVICE_RE handle_bus_relations( device_object ); break; case RemovalRelations: - handle_removal_relations( device_object ); + remove_device( device_object ); break; default: FIXME("Unhandled relation %#x.\n", type); @@ -755,6 +762,23 @@ POWER_STATE WINAPI PoSetPowerState( DEVICE_OBJECT *device, POWER_STATE_TYPE type static DRIVER_OBJECT *pnp_manager; +struct root_pnp_device +{ + WCHAR id[MAX_DEVICE_ID_LEN]; + struct wine_rb_entry entry; + DEVICE_OBJECT *device; +}; + +static int root_pnp_devices_rb_compare( const void *key, const struct wine_rb_entry *entry ) +{ + const struct root_pnp_device *device = WINE_RB_ENTRY_VALUE( entry, const struct root_pnp_device, entry ); + const WCHAR *k = key; + + return wcsicmp( k, device->id ); +} + +static struct wine_rb_tree root_pnp_devices = { root_pnp_devices_rb_compare }; + static NTSTATUS WINAPI pnp_manager_device_pnp( DEVICE_OBJECT *device, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); @@ -783,7 +807,74 @@ void pnp_manager_start(void) ERR("Failed to create PnP manager driver, status %#x.\n", status); } +static void destroy_root_pnp_device( struct wine_rb_entry *entry, void *context ) +{ + struct root_pnp_device *device = WINE_RB_ENTRY_VALUE(entry, struct root_pnp_device, entry); + remove_device( device->device ); +} + void pnp_manager_stop(void) { + wine_rb_destroy( &root_pnp_devices, destroy_root_pnp_device, NULL ); IoDeleteDriver( pnp_manager ); } + +void pnp_manager_enumerate_root_devices( const WCHAR *driver_name ) +{ + static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0}; + static const WCHAR rootW[] = {'R','O','O','T',0}; + WCHAR buffer[MAX_SERVICE_NAME + ARRAY_SIZE(driverW)], id[MAX_DEVICE_ID_LEN]; + SP_DEVINFO_DATA sp_device = {sizeof(sp_device)}; + struct root_pnp_device *pnp_device; + DEVICE_OBJECT *device; + NTSTATUS status; + unsigned int i; + HDEVINFO set; + + TRACE("Searching for new root-enumerated devices for driver %s.\n", debugstr_w(driver_name)); + + set = SetupDiGetClassDevsW( NULL, rootW, NULL, DIGCF_ALLCLASSES ); + if (set == INVALID_HANDLE_VALUE) + { + ERR("Failed to build device set, error %#x.\n", GetLastError()); + return; + } + + for (i = 0; SetupDiEnumDeviceInfo( set, i, &sp_device ); ++i) + { + if (!SetupDiGetDeviceRegistryPropertyW( set, &sp_device, SPDRP_SERVICE, + NULL, (BYTE *)buffer, sizeof(buffer), NULL ) + || lstrcmpiW( buffer, driver_name )) + { + continue; + } + + SetupDiGetDeviceInstanceIdW( set, &sp_device, id, ARRAY_SIZE(id), NULL ); + + if (wine_rb_get( &root_pnp_devices, id )) + continue; + + TRACE("Adding new root-enumerated device %s.\n", debugstr_w(id)); + + if ((status = IoCreateDevice( pnp_manager, sizeof(struct root_pnp_device), NULL, + FILE_DEVICE_CONTROLLER, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &device ))) + { + ERR("Failed to create root-enumerated PnP device %s, status %#x.\n", debugstr_w(id), status); + continue; + } + + pnp_device = device->DeviceExtension; + wcscpy( pnp_device->id, id ); + pnp_device->device = device; + if (wine_rb_put( &root_pnp_devices, id, &pnp_device->entry )) + { + ERR("Failed to insert device %s into tree.\n", debugstr_w(id)); + IoDeleteDevice( device ); + continue; + } + + start_device( device, set, &sp_device ); + } + + SetupDiDestroyDeviceInfoList(set); +}