ntoskrnl.exe: Use INF files to locate function drivers.

Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zebediah Figura 2019-06-07 11:48:36 -04:00 committed by Alexandre Julliard
parent eafb4aff5a
commit 4ca91cfd5d
1 changed files with 126 additions and 78 deletions

View File

@ -44,6 +44,9 @@
#include "ntoskrnl_private.h"
#include "initguid.h"
DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
#define MAX_SERVICE_NAME 260
@ -238,57 +241,6 @@ static NTSTATUS get_device_instance_id( DEVICE_OBJECT *device, WCHAR *buffer )
return STATUS_SUCCESS;
}
static BOOL get_driver_for_id( const WCHAR *id, WCHAR *driver )
{
static const WCHAR serviceW[] = {'S','e','r','v','i','c','e',0};
static const UNICODE_STRING service_str = { sizeof(serviceW) - sizeof(WCHAR), sizeof(serviceW), (WCHAR *)serviceW };
static const WCHAR critical_fmtW[] =
{'\\','R','e','g','i','s','t','r','y',
'\\','M','a','c','h','i','n','e',
'\\','S','y','s','t','e','m',
'\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
'\\','C','o','n','t','r','o','l',
'\\','C','r','i','t','i','c','a','l','D','e','v','i','c','e','D','a','t','a','b','a','s','e',
'\\','%','s',0};
WCHAR buffer[FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data[MAX_SERVICE_NAME * sizeof(WCHAR)] )];
KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
OBJECT_ATTRIBUTES attr;
UNICODE_STRING key;
NTSTATUS status;
HANDLE hkey;
WCHAR *keyW;
DWORD len;
if (!(keyW = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(critical_fmtW) + strlenW(id) * sizeof(WCHAR) )))
return STATUS_NO_MEMORY;
sprintfW( keyW, critical_fmtW, id );
RtlInitUnicodeString( &key, keyW );
InitializeObjectAttributes( &attr, &key, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL );
status = NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr );
RtlFreeUnicodeString( &key );
if (status != STATUS_SUCCESS)
{
TRACE("No driver found for ID %s.\n", debugstr_w(id));
return FALSE;
}
status = NtQueryValueKey( hkey, &service_str, KeyValuePartialInformation,
info, sizeof(buffer) - sizeof(WCHAR), &len );
NtClose( hkey );
if (status != STATUS_SUCCESS || info->Type != REG_SZ)
{
TRACE("No driver found for device ID %s.\n", debugstr_w(id));
return FALSE;
}
memcpy( driver, info->Data, info->DataLength );
driver[ info->DataLength / sizeof(WCHAR) ] = 0;
TRACE("Found driver %s for ID %s.\n", debugstr_w(driver), debugstr_w(id));
return TRUE;
}
static NTSTATUS send_power_irp( DEVICE_OBJECT *device, DEVICE_POWER_STATE power )
{
IO_STATUS_BLOCK irp_status;
@ -309,39 +261,19 @@ static NTSTATUS send_power_irp( DEVICE_OBJECT *device, DEVICE_POWER_STATE power
return send_device_irp( device, irp, NULL );
}
static void handle_bus_relations( DEVICE_OBJECT *device )
static void load_function_driver( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVINFO_DATA *sp_device )
{
static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
WCHAR buffer[MAX_SERVICE_NAME + ARRAY_SIZE(servicesW)];
WCHAR driver[MAX_SERVICE_NAME] = {0};
DRIVER_OBJECT *driver_obj;
UNICODE_STRING string;
WCHAR *ids, *ptr;
NTSTATUS status;
TRACE( "(%p)\n", device );
/* We could (should?) do a full IRP_MN_QUERY_DEVICE_RELATIONS query,
* but we don't have to, we have the DEVICE_OBJECT of the new device
* so we can simply handle the process here */
status = get_device_id( device, BusQueryCompatibleIDs, &ids );
if (status != STATUS_SUCCESS || !ids)
if (!SetupDiGetDeviceRegistryPropertyW( set, sp_device, SPDRP_SERVICE,
NULL, (BYTE *)driver, sizeof(driver), NULL ))
{
ERR("Failed to get compatible IDs, status %#x.\n", status);
return;
}
for (ptr = ids; *ptr; ptr += strlenW(ptr) + 1)
{
if (get_driver_for_id( ptr, driver ))
break;
}
ExFreePool( ids );
if (!driver[0])
{
ERR("No matching driver found for device.\n");
WARN("No driver registered for device %p.\n", device);
return;
}
@ -373,13 +305,129 @@ static void handle_bus_relations( DEVICE_OBJECT *device )
ObDereferenceObject( driver_obj );
if (status != STATUS_SUCCESS)
{
ERR("AddDevice failed for driver %s, status %#x.\n", debugstr_w(driver), status);
}
/* Return the total number of characters in a REG_MULTI_SZ string, including
* the final terminating null. */
static size_t sizeof_multiszW( const WCHAR *str )
{
const WCHAR *p;
for (p = str; *p; p += strlenW(p) + 1);
return p + 1 - str;
}
/* This does almost the same thing as UpdateDriverForPlugAndPlayDevices(),
* except that we don't know the INF path beforehand. */
static BOOL install_device_driver( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVINFO_DATA *sp_device )
{
static const DWORD dif_list[] =
{
DIF_REGISTERDEVICE,
DIF_SELECTBESTCOMPATDRV,
DIF_ALLOW_INSTALL,
DIF_INSTALLDEVICEFILES,
DIF_REGISTER_COINSTALLERS,
DIF_INSTALLINTERFACES,
DIF_INSTALLDEVICE,
DIF_NEWDEVICEWIZARD_FINISHINSTALL,
};
NTSTATUS status;
unsigned int i;
WCHAR *ids;
if ((status = get_device_id( device, BusQueryHardwareIDs, &ids )) || !ids)
{
ERR("Failed to get hardware IDs, status %#x.\n", status);
return FALSE;
}
SetupDiSetDeviceRegistryPropertyW( set, sp_device, SPDRP_HARDWAREID, (BYTE *)ids,
sizeof_multiszW( ids ) * sizeof(WCHAR) );
ExFreePool( ids );
if ((status = get_device_id( device, BusQueryCompatibleIDs, &ids )) || !ids)
{
ERR("Failed to get compatible IDs, status %#x.\n", status);
return FALSE;
}
SetupDiSetDeviceRegistryPropertyW( set, sp_device, SPDRP_COMPATIBLEIDS, (BYTE *)ids,
sizeof_multiszW( ids ) * sizeof(WCHAR) );
ExFreePool( ids );
if (!SetupDiBuildDriverInfoList( set, sp_device, SPDIT_COMPATDRIVER ))
{
ERR("Failed to build compatible driver list, error %#x.\n", GetLastError());
return FALSE;
}
for (i = 0; i < ARRAY_SIZE(dif_list); ++i)
{
if (!SetupDiCallClassInstaller(dif_list[i], set, sp_device) && GetLastError() != ERROR_DI_DO_DEFAULT)
{
ERR("Install function %#x failed, error %#x.\n", dif_list[i], GetLastError());
return FALSE;
}
}
return TRUE;
}
static void handle_bus_relations( DEVICE_OBJECT *device )
{
static const WCHAR infpathW[] = {'I','n','f','P','a','t','h',0};
SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
WCHAR device_instance_id[MAX_DEVICE_ID_LEN];
BOOL need_driver = TRUE;
HDEVINFO set;
HKEY key;
/* We could (should?) do a full IRP_MN_QUERY_DEVICE_RELATIONS query,
* but we don't have to, we have the DEVICE_OBJECT of the new device
* so we can simply handle the process here */
if (get_device_instance_id( device, device_instance_id ))
return;
set = SetupDiCreateDeviceInfoList( NULL, NULL );
if (!SetupDiCreateDeviceInfoW( set, device_instance_id, &GUID_NULL, NULL, NULL, 0, &sp_device )
&& !SetupDiOpenDeviceInfoW( set, device_instance_id, NULL, 0, &sp_device ))
{
ERR("Failed to create or open device %s, error %#x.\n", debugstr_w(device_instance_id), GetLastError());
SetupDiDestroyDeviceInfoList( set );
return;
}
send_pnp_irp( device, IRP_MN_START_DEVICE );
send_power_irp( device, PowerDeviceD0 );
TRACE("Creating new device %s.\n", debugstr_w(device_instance_id));
/* Check if the device already has a driver registered; if not, find one
* and install it. */
key = SetupDiOpenDevRegKey( set, &sp_device, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ );
if (key != INVALID_HANDLE_VALUE)
{
if (!RegQueryValueExW( key, infpathW, NULL, NULL, NULL, NULL ))
need_driver = FALSE;
RegCloseKey( key );
}
if (need_driver && !install_device_driver( device, set, &sp_device ))
{
SetupDiDestroyDeviceInfoList( set );
return;
}
load_function_driver( device, set, &sp_device );
if (device->DriverObject)
{
send_pnp_irp( device, IRP_MN_START_DEVICE );
send_power_irp( device, PowerDeviceD0 );
}
SetupDiDestroyDeviceInfoList( set );
}
static void handle_removal_relations( DEVICE_OBJECT *device )