[PATCH v2 2/4] ntoskrnl.exe: Use INF files to locate function drivers.
Zebediah Figura
zfigura at codeweavers.com
Fri Jun 7 10:48:36 CDT 2019
From: Zebediah Figura <z.figura12 at gmail.com>
Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
v2: avoid leaking the device set (thanks Zhiyi).
dlls/ntoskrnl.exe/pnp.c | 204 +++++++++++++++++++++++++---------------
1 file changed, 126 insertions(+), 78 deletions(-)
diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c
index e7303b6100..b8bdf93e54 100644
--- a/dlls/ntoskrnl.exe/pnp.c
+++ b/dlls/ntoskrnl.exe/pnp.c
@@ -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 )
--
2.20.1
More information about the wine-devel
mailing list