[PATCH v3 4/4] services: Start root PnP devices on startup.

Zebediah Figura zfigura at codeweavers.com
Tue Jun 11 18:56:26 CDT 2019


Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>

On 6/11/19 6:53 PM, Zebediah Figura wrote:
> ---
>   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:
> 




More information about the wine-devel mailing list