[PATCH v2 3/3] services: Start root PnP devices on startup.
Zebediah Figura
zfigura at codeweavers.com
Tue Jun 11 14:52:46 CDT 2019
Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
v2: move from wineboot to services. Also remove the separate control
from winedevice. The motivation for this is that it should also be
possible to manually start root-enumerated services from user-mode via
SetupDiInstallDevice(), but that can be kept for a separate patch.
dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 1 +
dlls/ntoskrnl.exe/pnp.c | 111 +++++++++++++++++++++++++---
programs/services/Makefile.in | 2 +-
programs/services/services.c | 29 +++++++-
programs/winedevice/device.c | 4 +-
5 files changed, 135 insertions(+), 12 deletions(-)
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
index 7c9370579dd..114b764fbe9 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
+++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
@@ -1522,3 +1522,4 @@
# or 'wine_' (for user-visible functions) to avoid namespace conflicts.
@ cdecl wine_ntoskrnl_main_loop(long)
+@ cdecl wine_enumerate_root_pnp_devices(wstr)
diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c
index fc99928a247..2c907d16314 100644
--- a/dlls/ntoskrnl.exe/pnp.c
+++ b/dlls/ntoskrnl.exe/pnp.c
@@ -376,6 +376,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};
@@ -421,19 +433,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 );
@@ -453,7 +460,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);
@@ -754,6 +761,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 strcmpiW( 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 );
@@ -782,7 +806,76 @@ 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 );
}
+
+/***********************************************************************
+ * wine_enumerate_root_pnp_devices (Not a Windows API)
+ */
+NTSTATUS CDECL wine_enumerate_root_pnp_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)
+ return GetLastError();
+
+ 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;
+ strcpyW( 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);
+
+ return ERROR_SUCCESS;
+}
diff --git a/programs/services/Makefile.in b/programs/services/Makefile.in
index e06514cbdd1..560e277a9bf 100644
--- a/programs/services/Makefile.in
+++ b/programs/services/Makefile.in
@@ -1,5 +1,5 @@
MODULE = services.exe
-IMPORTS = rpcrt4 advapi32 userenv
+IMPORTS = rpcrt4 advapi32 userenv setupapi
EXTRADLLFLAGS = -mconsole -mno-cygwin
diff --git a/programs/services/services.c b/programs/services/services.c
index 7ca249c7a9d..3099b7805b1 100644
--- a/programs/services/services.c
+++ b/programs/services/services.c
@@ -27,6 +27,7 @@
#include <winsvc.h>
#include <rpc.h>
#include <userenv.h>
+#include <setupapi.h>
#include "wine/debug.h"
#include "svcctl.h"
@@ -329,24 +330,49 @@ static int __cdecl compare_tags(const void *a, const void *b)
return service_a->config.dwTagId - service_b->config.dwTagId;
}
+static BOOL is_root_pnp_service(const struct service_entry *service, HDEVINFO set)
+{
+ SP_DEVINFO_DATA device = {sizeof(device)};
+ WCHAR name[MAX_SERVICE_NAME];
+ unsigned int i;
+
+ for (i = 0; SetupDiEnumDeviceInfo(set, i, &device); ++i)
+ {
+ if (SetupDiGetDeviceRegistryPropertyW(set, &device, SPDRP_SERVICE, NULL,
+ (BYTE *)name, sizeof(name), NULL)
+ && !wcsicmp(name, service->name))
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
static void scmdatabase_autostart_services(struct scmdatabase *db)
{
+ static const WCHAR rootW[] = {'R','O','O','T',0};
struct service_entry **services_list;
unsigned int i = 0;
unsigned int size = 32;
struct service_entry *service;
+ HDEVINFO set;
services_list = HeapAlloc(GetProcessHeap(), 0, size * sizeof(services_list[0]));
if (!services_list)
return;
+ if ((set = SetupDiGetClassDevsW( NULL, rootW, NULL, DIGCF_ALLCLASSES )) == INVALID_HANDLE_VALUE)
+ WINE_ERR("Failed to enumerate devices, error %#x.\n", GetLastError());
+
scmdatabase_lock(db);
LIST_FOR_EACH_ENTRY(service, &db->services, struct service_entry, entry)
{
if (service->config.dwStartType == SERVICE_BOOT_START ||
service->config.dwStartType == SERVICE_SYSTEM_START ||
- service->config.dwStartType == SERVICE_AUTO_START)
+ service->config.dwStartType == SERVICE_AUTO_START ||
+ (set != INVALID_HANDLE_VALUE && is_root_pnp_service(set, service)))
{
if (i+1 >= size)
{
@@ -379,6 +405,7 @@ static void scmdatabase_autostart_services(struct scmdatabase *db)
scmdatabase_unlock_startup(db);
HeapFree(GetProcessHeap(), 0, services_list);
+ SetupDiDestroyDeviceInfoList(set);
}
static void scmdatabase_wait_terminate(struct scmdatabase *db)
diff --git a/programs/winedevice/device.c b/programs/winedevice/device.c
index f4ebad2f2d0..9c90e4e2963 100644
--- a/programs/winedevice/device.c
+++ b/programs/winedevice/device.c
@@ -40,6 +40,7 @@ static const WCHAR servicesW[] = {'\\','R','e','g','i','s','t','r','y',
'\\',0};
extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event );
+extern NTSTATUS CDECL wine_enumerate_root_pnp_devices( const WCHAR *driver_name );
static const WCHAR winedeviceW[] = {'w','i','n','e','d','e','v','i','c','e',0};
static SERVICE_STATUS_HANDLE service_handle;
@@ -76,7 +77,8 @@ static DWORD device_handler( DWORD ctrl, const WCHAR *driver_name )
switch (ctrl)
{
case SERVICE_CONTROL_START:
- result = RtlNtStatusToDosError(ZwLoadDriver( &service_name ));
+ if (!(result = RtlNtStatusToDosError(ZwLoadDriver( &service_name ))))
+ result = RtlNtStatusToDosError(wine_enumerate_root_pnp_devices( driver_name ));
break;
case SERVICE_CONTROL_STOP:
--
2.20.1
More information about the wine-devel
mailing list