Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS text-mode setup * PURPOSE: Device installation * PROGRAMMER: Herv� Poussineau (hpoussin@reactos.org) * Hermes Belusca-Maito */
#include <usetup.h>
#define NDEBUG#include <debug.h>
#define INITGUID#include <guiddef.h>#include <libs/umpnpmgr/sysguid.h>
/* LOCALS *******************************************************************/
static HANDLE hEnumKey = NULL;static HANDLE hServicesKey = NULL;
static HANDLE hNoPendingInstalls = NULL;
static HANDLE hPnpThread = NULL;static HANDLE hDeviceInstallThread = NULL;
/* Device-install event list */static HANDLE hDeviceInstallListMutex = NULL;static LIST_ENTRY DeviceInstallListHead;static HANDLE hDeviceInstallListNotEmpty = NULL;
typedef struct{ LIST_ENTRY ListEntry; WCHAR DeviceIds[ANYSIZE_ARRAY];} DeviceInstallParams;
/* FUNCTIONS ****************************************************************/
static BOOLEANAreDriversLoaded( IN PCWSTR DeviceId){ PLUGPLAY_CONTROL_STATUS_DATA PlugPlayData; NTSTATUS Status;
RtlInitUnicodeString(&PlugPlayData.DeviceInstance, DeviceId); PlugPlayData.Operation = PNP_GET_DEVICE_STATUS;
Status = NtPlugPlayControl(PlugPlayControlDeviceStatus, &PlugPlayData, sizeof(PlugPlayData)); if (NT_SUCCESS(Status)) { return (_Bool)((PlugPlayData.DeviceStatus & DN_DRIVER_LOADED) && !(PlugPlayData.DeviceStatus & DN_HAS_PROBLEM)); } else { return FALSE; }}
static BOOLEANInstallDriver( IN HINF hInf, IN HANDLE hServices, IN HANDLE hDeviceKey, IN LPCWSTR DeviceId, IN LPCWSTR HardwareId){ UNICODE_STRING ServiceU = RTL_CONSTANT_STRING(L"Service"); UNICODE_STRING ErrorControlU = RTL_CONSTANT_STRING(L"ErrorControl"); UNICODE_STRING StartU = RTL_CONSTANT_STRING(L"Start"); UNICODE_STRING TypeU = RTL_CONSTANT_STRING(L"Type"); UNICODE_STRING UpperFiltersU = RTL_CONSTANT_STRING(L"UpperFilters"); PWSTR keyboardClass = L"kbdclass\0"; PWSTR partMgr = L"partmgr\0";
UNICODE_STRING StringU; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE hService; INFCONTEXT Context; PCWSTR Driver, ClassGuid, ImagePath; ULONG dwValue; ULONG Disposition; NTSTATUS Status; BOOLEAN deviceInstalled = FALSE;
/* First check if the driver needs any action at all */ if (AreDriversLoaded(DeviceId)) return TRUE;
/* Check if we know the hardware */ if (!SpInfFindFirstLine(hInf, L"HardwareIdsDatabase", HardwareId, &Context)) return FALSE; if (!INF_GetDataField(&Context, 1, &Driver)) return FALSE;
/* Get associated class GUID (if any) */ if (!INF_GetDataField(&Context, 2, &ClassGuid)) ClassGuid = NULL;
/* Find associated driver name */ /* FIXME: check in other sections too! */ if (!SpInfFindFirstLine(hInf, L"BootBusExtenders.Load", Driver, &Context) && !SpInfFindFirstLine(hInf, L"BusExtenders.Load", Driver, &Context) && !SpInfFindFirstLine(hInf, L"SCSI.Load", Driver, &Context) && !SpInfFindFirstLine(hInf, L"InputDevicesSupport.Load", Driver, &Context) && !SpInfFindFirstLine(hInf, L"Keyboard.Load", Driver, &Context)) { INF_FreeData(ClassGuid); INF_FreeData(Driver); return FALSE; }
if (!INF_GetDataField(&Context, 1, &ImagePath)) { INF_FreeData(ClassGuid); INF_FreeData(Driver); return FALSE; }
DPRINT1("Using driver '%S' for device '%S'\n", ImagePath, DeviceId);
/* Create service key */ RtlInitUnicodeString(&StringU, Driver); InitializeObjectAttributes(&ObjectAttributes, &StringU, OBJ_CASE_INSENSITIVE, hServices, NULL); Status = NtCreateKey(&hService, KEY_SET_VALUE, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, &Disposition); if (!NT_SUCCESS(Status)) { DPRINT1("NtCreateKey('%wZ') failed with status 0x%08x\n", &StringU, Status); INF_FreeData(ImagePath); INF_FreeData(ClassGuid); INF_FreeData(Driver); return FALSE; }
/* Fill service key */ if (Disposition == REG_CREATED_NEW_KEY) { dwValue = 0; NtSetValueKey(hService, &ErrorControlU, 0, REG_DWORD, &dwValue, sizeof(dwValue));
dwValue = 0; NtSetValueKey(hService, &StartU, 0, REG_DWORD, &dwValue, sizeof(dwValue));
dwValue = SERVICE_KERNEL_DRIVER; NtSetValueKey(hService, &TypeU, 0, REG_DWORD, &dwValue, sizeof(dwValue)); }
INF_FreeData(ImagePath); NtClose(hService);
/* Add kbdclass and partmgr upper filters */ if (ClassGuid &&_wcsicmp(ClassGuid, L"{4D36E96B-E325-11CE-BFC1-08002BE10318}") == 0) { DPRINT1("Installing keyboard class driver for '%S'\n", DeviceId); NtSetValueKey(hDeviceKey, &UpperFiltersU, 0, REG_MULTI_SZ, keyboardClass, (wcslen(keyboardClass) + 2) * sizeof(WCHAR)); } else if (ClassGuid && _wcsicmp(ClassGuid, L"{4D36E967-E325-11CE-BFC1-08002BE10318}") == 0) { DPRINT1("Installing partition manager driver for '%S'\n", DeviceId); NtSetValueKey(hDeviceKey, &UpperFiltersU, 0, REG_MULTI_SZ, partMgr, (wcslen(partMgr) + 2) * sizeof(WCHAR)); }
INF_FreeData(ClassGuid);
/* Associate device with the service we just filled */ Status = NtSetValueKey(hDeviceKey, &ServiceU, 0, REG_SZ, (PVOID)Driver, (wcslen(Driver) + 1) * sizeof(WCHAR)); if (NT_SUCCESS(Status)) { /* We've registered the driver, time to start a device */ PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData; RtlInitUnicodeString(&ControlData.DeviceInstance, DeviceId);
Status = NtPlugPlayControl(PlugPlayControlStartDevice, &ControlData, sizeof(ControlData)); if (!NT_SUCCESS(Status)) { DPRINT1("NtPlugPlayControl() failed with status 0x%08x\n", Status); }
deviceInstalled = NT_SUCCESS(Status); }
INF_FreeData(Driver);
return deviceInstalled;}
static VOIDInstallDevice( IN HINF hInf, IN HANDLE hEnum, IN HANDLE hServices, IN LPCWSTR DeviceId){ UNICODE_STRING HardwareIDU = RTL_CONSTANT_STRING(L"HardwareID"); UNICODE_STRING CompatibleIDsU = RTL_CONSTANT_STRING(L"CompatibleIDs");
UNICODE_STRING DeviceIdU; OBJECT_ATTRIBUTES ObjectAttributes; LPCWSTR HardwareID; PKEY_VALUE_PARTIAL_INFORMATION pPartialInformation = NULL; HANDLE hDeviceKey; ULONG ulRequired; BOOLEAN bDriverInstalled = FALSE; NTSTATUS Status;
RtlInitUnicodeString(&DeviceIdU, DeviceId); InitializeObjectAttributes(&ObjectAttributes, &DeviceIdU, OBJ_CASE_INSENSITIVE, hEnum, NULL); Status = NtOpenKey(&hDeviceKey, KEY_QUERY_VALUE | KEY_SET_VALUE, &ObjectAttributes); if (!NT_SUCCESS(Status)) { DPRINT("Unable to open subkey '%S'\n", DeviceId); return; }
Status = NtQueryValueKey( hDeviceKey, &HardwareIDU, KeyValuePartialInformation, NULL, 0, &ulRequired); if (Status == STATUS_BUFFER_TOO_SMALL) { pPartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)RtlAllocateHeap(ProcessHeap, 0, ulRequired); if (!pPartialInformation) { DPRINT1("RtlAllocateHeap() failed\n"); NtClose(hDeviceKey); return; } Status = NtQueryValueKey( hDeviceKey, &HardwareIDU, KeyValuePartialInformation, pPartialInformation, ulRequired, &ulRequired); } if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { /* Nothing to do */ } else if (!NT_SUCCESS(Status)) { DPRINT1("NtQueryValueKey() failed with status 0x%08x\n", Status); if (pPartialInformation) RtlFreeHeap(ProcessHeap, 0, pPartialInformation); NtClose(hDeviceKey); return; } else if (pPartialInformation) { for (HardwareID = (LPCWSTR)pPartialInformation->Data; (PUCHAR)HardwareID < pPartialInformation->Data + pPartialInformation->DataLength && *HardwareID && !bDriverInstalled; HardwareID += wcslen(HardwareID) + 1) { bDriverInstalled = InstallDriver(hInf, hServices,hDeviceKey, DeviceId, HardwareID); } }
if (!bDriverInstalled) { if (pPartialInformation) { RtlFreeHeap(ProcessHeap, 0, pPartialInformation); pPartialInformation = NULL; } Status = NtQueryValueKey( hDeviceKey, &CompatibleIDsU, KeyValuePartialInformation, NULL, 0, &ulRequired); if (Status == STATUS_BUFFER_TOO_SMALL) { pPartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)RtlAllocateHeap(ProcessHeap, 0, ulRequired); if (!pPartialInformation) { DPRINT("RtlAllocateHeap() failed\n"); NtClose(hDeviceKey); return; } Status = NtQueryValueKey( hDeviceKey, &CompatibleIDsU, KeyValuePartialInformation, pPartialInformation, ulRequired, &ulRequired); } if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { /* Nothing to do */ } else if (!NT_SUCCESS(Status)) { if (pPartialInformation) RtlFreeHeap(ProcessHeap, 0, pPartialInformation); NtClose(hDeviceKey); DPRINT1("NtQueryValueKey() failed with status 0x%08x\n", Status); return; } else if (pPartialInformation) { for (HardwareID = (LPCWSTR)pPartialInformation->Data; (PUCHAR)HardwareID < pPartialInformation->Data + pPartialInformation->DataLength && *HardwareID && !bDriverInstalled; HardwareID += wcslen(HardwareID) + 1) { bDriverInstalled = InstallDriver(hInf, hServices,hDeviceKey, DeviceId, HardwareID); } } } if (!bDriverInstalled) DPRINT("No driver available for %S\n", DeviceId);
RtlFreeHeap(ProcessHeap, 0, pPartialInformation); NtClose(hDeviceKey);}
/* Loop to install all queued devices installations */static ULONG NTAPIDeviceInstallThread(IN PVOID Parameter){ HINF hSetupInf = *(HINF*)Parameter; PLIST_ENTRY ListEntry; DeviceInstallParams* Params; LARGE_INTEGER Timeout;
for (;;) { /* Dequeue the next oldest device-install event */ NtWaitForSingleObject(hDeviceInstallListMutex, FALSE, NULL); ListEntry = (IsListEmpty(&DeviceInstallListHead) ? NULL : RemoveHeadList(&DeviceInstallListHead)); NtReleaseMutant(hDeviceInstallListMutex, NULL);
if (ListEntry == NULL) { /* * The list is now empty, but there may be a new enumerated device * that is going to be added to the list soon. In order to avoid * setting the hNoPendingInstalls event to release it soon after, * we wait for maximum 1 second for no PnP enumeration event being * received before declaring that no pending installations are * taking place and setting the corresponding event. */ Timeout.QuadPart = -10000000LL; /* Wait for 1 second */ if (NtWaitForSingleObject(hDeviceInstallListNotEmpty, FALSE, &Timeout) == STATUS_TIMEOUT) { /* We timed out: set the event and do the actual wait */ NtSetEvent(hNoPendingInstalls, NULL); NtWaitForSingleObject(hDeviceInstallListNotEmpty, FALSE, NULL); } } else { NtResetEvent(hNoPendingInstalls, NULL); Params = CONTAINING_RECORD(ListEntry, DeviceInstallParams, ListEntry); InstallDevice(hSetupInf, hEnumKey, hServicesKey, Params->DeviceIds); RtlFreeHeap(ProcessHeap, 0, Params); } }
return 0;}
static ULONG NTAPIPnpEventThread(IN PVOID Parameter){ NTSTATUS Status; PLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData = {0, 0, 0, 0}; PPLUGPLAY_EVENT_BLOCK PnpEvent, NewPnpEvent; ULONG PnpEventSize;
UNREFERENCED_PARAMETER(Parameter);
PnpEventSize = 0x1000; PnpEvent = RtlAllocateHeap(ProcessHeap, 0, PnpEventSize); if (PnpEvent == NULL) { Status = STATUS_NO_MEMORY; goto Quit; }
for (;;) { DPRINT("Calling NtGetPlugPlayEvent()\n");
/* Wait for the next PnP event */ Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize);
/* Resize the buffer for the PnP event if it's too small */ if (Status == STATUS_BUFFER_TOO_SMALL) { PnpEventSize += 0x400; NewPnpEvent = RtlReAllocateHeap(ProcessHeap, 0, PnpEvent, PnpEventSize); if (NewPnpEvent == NULL) { Status = STATUS_NO_MEMORY; goto Quit; } PnpEvent = NewPnpEvent; continue; }
if (!NT_SUCCESS(Status)) { DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status); goto Quit; }
/* Process the PnP event */ DPRINT("Received PnP Event\n"); if (IsEqualGUID(&PnpEvent->EventGuid, &GUID_DEVICE_ENUMERATED)) { DeviceInstallParams* Params; ULONG len; ULONG DeviceIdLength;
DPRINT("Device enumerated: %S\n", PnpEvent->TargetDevice.DeviceIds);
DeviceIdLength = wcslen(PnpEvent->TargetDevice.DeviceIds); if (DeviceIdLength) { /* Allocate a new device-install event */ len = FIELD_OFFSET(DeviceInstallParams, DeviceIds) + (DeviceIdLength + 1) * sizeof(WCHAR); Params = RtlAllocateHeap(ProcessHeap, 0, len); if (Params) { wcscpy(Params->DeviceIds, PnpEvent->TargetDevice.DeviceIds);
/* Queue the event (will be dequeued by DeviceInstallThread) */ NtWaitForSingleObject(hDeviceInstallListMutex, FALSE, NULL); InsertTailList(&DeviceInstallListHead, &Params->ListEntry); NtReleaseMutant(hDeviceInstallListMutex, NULL);
NtSetEvent(hDeviceInstallListNotEmpty, NULL); } else { DPRINT1("Not enough memory (size %lu)\n", len); } } } else { DPRINT("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n", PnpEvent->EventGuid.Data1, PnpEvent->EventGuid.Data2, PnpEvent->EventGuid.Data3, PnpEvent->EventGuid.Data4[0], PnpEvent->EventGuid.Data4[1], PnpEvent->EventGuid.Data4[2], PnpEvent->EventGuid.Data4[3], PnpEvent->EventGuid.Data4[4], PnpEvent->EventGuid.Data4[5], PnpEvent->EventGuid.Data4[6], PnpEvent->EventGuid.Data4[7]); }
/* Dequeue the current PnP event and signal the next one */ Status = NtPlugPlayControl(PlugPlayControlUserResponse, &ResponseData, sizeof(ResponseData)); if (!NT_SUCCESS(Status)) { DPRINT1("NtPlugPlayControl(PlugPlayControlUserResponse) failed (Status 0x%08lx)\n", Status); goto Quit; } }
Status = STATUS_SUCCESS;
Quit: if (PnpEvent) RtlFreeHeap(ProcessHeap, 0, PnpEvent);
NtTerminateThread(NtCurrentThread(), Status); return Status;}
NTSTATUSWaitNoPendingInstallEvents( IN PLARGE_INTEGER Timeout OPTIONAL){ return NtWaitForSingleObject(hNoPendingInstalls, FALSE, Timeout);}
BOOLEANEnableUserModePnpManager(VOID){ LARGE_INTEGER Timeout;
/* Start the PnP thread */ if (hPnpThread != NULL) NtResumeThread(hPnpThread, NULL);
/* * Wait a little bit so that we get a chance to have some events being * queued by the time the device-installation thread becomes resumed. */ Timeout.QuadPart = -10000000LL; /* Wait for 1 second */ NtWaitForSingleObject(hDeviceInstallListNotEmpty, FALSE, &Timeout);
/* Start the device installation thread */ if (hDeviceInstallThread != NULL) NtResumeThread(hDeviceInstallThread, NULL);
return TRUE;}
BOOLEANDisableUserModePnpManager(VOID){ /* Wait until all pending installations are done, then freeze the threads */ if (WaitNoPendingInstallEvents(NULL) != STATUS_WAIT_0) DPRINT1("WaitNoPendingInstallEvents() failed to wait!\n");
// TODO: use signalling events
NtSuspendThread(hPnpThread, NULL); NtSuspendThread(hDeviceInstallThread, NULL);
return TRUE;}
NTSTATUSInitializeUserModePnpManager( IN HINF* phSetupInf){ NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING EnumU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Enum"); UNICODE_STRING ServicesU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services");
Status = NtCreateEvent(&hNoPendingInstalls, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); if (!NT_SUCCESS(Status)) { DPRINT1("Could not create the Pending-Install Event! (Status 0x%08lx)\n", Status); goto Failure; }
/* * Initialize the device-install event list */
Status = NtCreateEvent(&hDeviceInstallListNotEmpty, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE); if (!NT_SUCCESS(Status)) { DPRINT1("Could not create the List Event! (Status 0x%08lx)\n", Status); goto Failure; }
Status = NtCreateMutant(&hDeviceInstallListMutex, MUTANT_ALL_ACCESS, NULL, FALSE); if (!NT_SUCCESS(Status)) { DPRINT1("Could not create the List Mutex! (Status 0x%08lx)\n", Status); goto Failure; } InitializeListHead(&DeviceInstallListHead);
InitializeObjectAttributes(&ObjectAttributes, &EnumU, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&hEnumKey, KEY_QUERY_VALUE, &ObjectAttributes); if (!NT_SUCCESS(Status)) { DPRINT1("NtOpenKey('%wZ') failed (Status 0x%08lx)\n", &EnumU, Status); goto Failure; }
InitializeObjectAttributes(&ObjectAttributes, &ServicesU, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtCreateKey(&hServicesKey, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("NtCreateKey('%wZ') failed (Status 0x%08lx)\n", &ServicesU, Status); goto Failure; }
/* Create the PnP event thread in suspended state */ Status = RtlCreateUserThread(NtCurrentProcess(), NULL, TRUE, 0, 0, 0, PnpEventThread, NULL, &hPnpThread, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to create the PnP event thread (Status 0x%08lx)\n", Status); hPnpThread = NULL; goto Failure; }
/* Create the device installation thread in suspended state */ Status = RtlCreateUserThread(NtCurrentProcess(), NULL, TRUE, 0, 0, 0, DeviceInstallThread, phSetupInf, &hDeviceInstallThread, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to create the device installation thread (Status 0x%08lx)\n", Status); hDeviceInstallThread = NULL; goto Failure; }
return STATUS_SUCCESS;
Failure: if (hPnpThread) { NtTerminateThread(hPnpThread, STATUS_SUCCESS); NtClose(hPnpThread); } hPnpThread = NULL;
if (hServicesKey) NtClose(hServicesKey); hServicesKey = NULL;
if (hEnumKey) NtClose(hEnumKey); hEnumKey = NULL;
if (hDeviceInstallListMutex) NtClose(hDeviceInstallListMutex); hDeviceInstallListMutex = NULL;
if (hDeviceInstallListNotEmpty) NtClose(hDeviceInstallListNotEmpty); hDeviceInstallListNotEmpty = NULL;
if (hNoPendingInstalls) NtClose(hNoPendingInstalls); hNoPendingInstalls = NULL;
return Status;}
VOIDTerminateUserModePnpManager(VOID){ DisableUserModePnpManager();
// TODO: use signalling events
/* Kill the PnP thread as it blocks inside the NtGetPlugPlayEvent() call */ if (hPnpThread) { NtTerminateThread(hPnpThread, STATUS_SUCCESS); NtClose(hPnpThread); } hPnpThread = NULL;
/* Kill the device installation thread */ if (hDeviceInstallThread) { NtTerminateThread(hDeviceInstallThread, STATUS_SUCCESS); NtClose(hDeviceInstallThread); } hDeviceInstallThread = NULL;
/* Close the opened handles */
if (hServicesKey) NtClose(hServicesKey); hServicesKey = NULL;
if (hEnumKey) NtClose(hEnumKey); hEnumKey = NULL;
if (hNoPendingInstalls) NtClose(hNoPendingInstalls); hNoPendingInstalls = NULL;
if (hDeviceInstallListNotEmpty) NtClose(hDeviceInstallListNotEmpty); hDeviceInstallListNotEmpty = NULL;}