609 lines
22 KiB
C
609 lines
22 KiB
C
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <ntlite/ddi/io_driver.h>
|
|
#include <ntlite/ddk/device.h>
|
|
#include <kernel/io_pnp.h>
|
|
#include <kernel/ke_event.h>
|
|
#include <kernel/ke_thread.h>
|
|
#include <kernel/ke_spinlock.h>
|
|
#include <kernel/ke_work.h>
|
|
#include <kernel/ke_time.h>
|
|
#include <kernel/mm_virt.h>
|
|
#include <kernel/ke_debug.h>
|
|
|
|
#define PNP_WORKERS 2u
|
|
|
|
typedef enum PNP_BIND_STATE {
|
|
PnpUnbound,
|
|
PnpReserved,
|
|
PnpStarting,
|
|
PnpPending,
|
|
PnpCancelling,
|
|
PnpStarted,
|
|
PnpFailed,
|
|
PnpCleaning,
|
|
PnpStopping,
|
|
} PNP_BIND_STATE;
|
|
|
|
typedef struct PNP_DRIVER {
|
|
struct PNP_DRIVER* Next;
|
|
const NTL_DEVICE_DESC* Descriptor;
|
|
DRIVER_OBJECT* IoDriver;
|
|
struct NTL_MODULE* Module;
|
|
bool Committed;
|
|
} PNP_DRIVER;
|
|
|
|
typedef struct PNP_NODE {
|
|
struct PNP_NODE* Next;
|
|
DEVICE_OBJECT* Pdo;
|
|
DEVICE_OBJECT* ParentPdo;
|
|
NTL_DEVICE* Binding;
|
|
const PNP_DRIVER* Driver;
|
|
PNP_BIND_STATE State;
|
|
bool Removed;
|
|
bool Published;
|
|
bool CancelIssued;
|
|
} PNP_NODE;
|
|
|
|
struct NTL_DEVICE_RESOURCE {
|
|
NTL_DEVICE_RESOURCE* Next;
|
|
NTL_DEVICE* Owner;
|
|
NTL_DEVICE_RESOURCE_RELEASE Release;
|
|
void* Context;
|
|
};
|
|
|
|
struct NTL_DEVICE {
|
|
SPINLOCK Lock;
|
|
PNP_NODE* Node;
|
|
void* Context;
|
|
NTL_DEVICE_RESOURCE* Resources;
|
|
KSTATUS CompletionStatus;
|
|
bool CompletionSet;
|
|
bool Closing;
|
|
};
|
|
|
|
static SPINLOCK gPnpLock = SPINLOCK_INIT;
|
|
static PNP_DRIVER* gPnpDrivers;
|
|
static PNP_DRIVER* gPnpDriversTail;
|
|
static PNP_NODE* gPnpNodes;
|
|
static PNP_NODE* gPnpNodesTail;
|
|
static size_t gPnpNodeCount;
|
|
static EVENT* gPnpReady;
|
|
static EVENT* gPnpChanged;
|
|
static bool gPnpStarted;
|
|
static bool gPnpShutdown;
|
|
static KWORK_ITEM gPnpWakeItem;
|
|
|
|
static void IoPnpSignal(void* unused) {
|
|
(void)unused;
|
|
for (uint32_t i = 0; i < PNP_WORKERS; ++i)
|
|
KeSignalEvent(gPnpReady);
|
|
KeSignalEvent(gPnpChanged);
|
|
}
|
|
|
|
static void IoPnpWake(void) {
|
|
if (gPnpStarted)
|
|
(void)KeQueueWorkItem(&gPnpWakeItem);
|
|
}
|
|
|
|
static bool IoPnpMatches(const NTL_DEVICE_DESC* descriptor, const DEVICE_OBJECT* pdo) {
|
|
for (uint32_t i = 0; i < descriptor->MatchCount; ++i) {
|
|
const NTL_DEVICE_MATCH* match = &descriptor->Matches[i];
|
|
if ((uint32_t)match->Bus != (uint32_t)pdo->u.Pdo.BusType)
|
|
continue;
|
|
size_t length = strlen(match->Id);
|
|
for (uint8_t j = 0; j < pdo->u.Pdo.IdCount; ++j) {
|
|
SYS_STRING_VIEW id = pdo->u.Pdo.Ids[j].Id;
|
|
if (id.Length == length && !memcmp(id.Data, match->Id, length))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool IoPnpMatchesIoDriver(const DRIVER_OBJECT* driver, const DEVICE_OBJECT* pdo) {
|
|
if (!driver->IdTable || !driver->DriverAddDevice) return false;
|
|
for (size_t i = 0; i < driver->IdTable->Count; ++i) {
|
|
const IO_DEVICE_ID* wanted = &driver->IdTable->Ids[i];
|
|
if (wanted->BusType != IoBusUnknown && wanted->BusType != pdo->u.Pdo.BusType)
|
|
continue;
|
|
for (uint8_t j = 0; j < pdo->u.Pdo.IdCount; ++j)
|
|
if (SysSvEqual(wanted->Id, pdo->u.Pdo.Ids[j].Id)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static const PNP_DRIVER* IoPnpFindDriver(const DEVICE_OBJECT* pdo) {
|
|
for (PNP_DRIVER* driver = gPnpDrivers; driver; driver = driver->Next) {
|
|
if (!driver->Committed) continue;
|
|
if ((driver->Descriptor && IoPnpMatches(driver->Descriptor, pdo)) ||
|
|
(driver->IoDriver && IoPnpMatchesIoDriver(driver->IoDriver, pdo)))
|
|
return driver;
|
|
}
|
|
return (const PNP_DRIVER*)0;
|
|
}
|
|
|
|
static PNP_NODE* IoPnpFindNode(const DEVICE_OBJECT* pdo) {
|
|
for (PNP_NODE* node = gPnpNodes; node; node = node->Next)
|
|
if (node->Pdo == pdo)
|
|
return node;
|
|
return (PNP_NODE*)0;
|
|
}
|
|
|
|
static DEVICE_OBJECT* IoPnpParentPdo(const DEVICE_OBJECT* pdo) {
|
|
const DEVICE_OBJECT* parent = pdo->Parent;
|
|
for (uint32_t i = 0; parent && i < 64u; ++i) {
|
|
if (parent->Type == DeviceObjectPhysical)
|
|
return (DEVICE_OBJECT*)parent;
|
|
parent = parent->LowerDevice ? parent->LowerDevice : parent->Parent;
|
|
}
|
|
return (DEVICE_OBJECT*)0;
|
|
}
|
|
|
|
static bool IoPnpHasChildren(const DEVICE_OBJECT* parent) {
|
|
for (PNP_NODE* candidate = gPnpNodes; candidate; candidate = candidate->Next) {
|
|
const DEVICE_OBJECT* ancestor = candidate->ParentPdo;
|
|
for (size_t depth = 0; ancestor && depth < gPnpNodeCount; ++depth) {
|
|
if (ancestor == parent) return true;
|
|
const PNP_NODE* node = IoPnpFindNode(ancestor);
|
|
ancestor = node ? node->ParentPdo : (const DEVICE_OBJECT*)0;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void IoPnpReleaseResources(NTL_DEVICE* device) {
|
|
uintptr_t flags = KeAcquireSpinlock(&device->Lock);
|
|
device->Closing = true;
|
|
NTL_DEVICE_RESOURCE* resource = device->Resources;
|
|
device->Resources = (NTL_DEVICE_RESOURCE*)0;
|
|
KeReleaseSpinlock(&device->Lock, flags);
|
|
|
|
while (resource) {
|
|
NTL_DEVICE_RESOURCE* next = resource->Next;
|
|
resource->Owner = (NTL_DEVICE*)0;
|
|
resource->Release(resource->Context);
|
|
MmFreePool(resource);
|
|
resource = next;
|
|
}
|
|
}
|
|
|
|
static void IoPnpDispose(PNP_NODE* node) {
|
|
NTL_DEVICE* device = node->Binding;
|
|
if (device) {
|
|
if (node->State == PnpStopping) {
|
|
if (node->Driver->Descriptor && node->Driver->Descriptor->Stop)
|
|
node->Driver->Descriptor->Stop(device);
|
|
else if (node->Driver->IoDriver && !node->Pdo->DeletePending)
|
|
(void)IoSendPnpIrpSync(node->Pdo, IRP_MN_REMOVE_DEVICE);
|
|
}
|
|
IoPnpReleaseResources(device);
|
|
MmFreePool(device);
|
|
}
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
PNP_NODE** link = &gPnpNodes;
|
|
while (*link && *link != node) link = &(*link)->Next;
|
|
if (*link) {
|
|
*link = node->Next;
|
|
if (gPnpNodesTail == node) {
|
|
gPnpNodesTail = (PNP_NODE*)0;
|
|
for (PNP_NODE* it = gPnpNodes; it; it = it->Next) gPnpNodesTail = it;
|
|
}
|
|
--gPnpNodeCount;
|
|
}
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoDereferenceDeviceObject(node->Pdo);
|
|
MmFreePool(node);
|
|
IoPnpWake();
|
|
}
|
|
|
|
static void IoPnpStart(PNP_NODE* node) {
|
|
const NTL_DEVICE_DESC* desc = node->Driver->Descriptor;
|
|
size_t contextSize = desc ? desc->ContextSize : 0;
|
|
NTL_DEVICE* device = (NTL_DEVICE*)MmAllocatePool(sizeof(*device) + contextSize);
|
|
if (!device) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
node->State = PnpFailed;
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoPnpWake();
|
|
return;
|
|
}
|
|
memset(device, 0, sizeof(*device) + contextSize);
|
|
device->Lock = (SPINLOCK)SPINLOCK_INIT;
|
|
device->Node = node;
|
|
device->Context = (void*)(device + 1);
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
node->Binding = device;
|
|
node->State = PnpStarting;
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
|
|
KSTATUS result;
|
|
if (desc) {
|
|
result = desc->Start(device);
|
|
} else {
|
|
DRIVER_OBJECT* io = node->Driver->IoDriver;
|
|
result = io->DriverAddDevice(io, node->Pdo);
|
|
if (KSUCCESS(result)) {
|
|
result = IoSendPnpIrpSync(node->Pdo, IRP_MN_START_DEVICE);
|
|
if (result == NTLSTATUS_NOT_SUPPORTED) result = NTLSTATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
flags = KeAcquireSpinlock(&gPnpLock);
|
|
if (device->CompletionSet)
|
|
result = device->CompletionStatus;
|
|
node->State = result == NTLSTATUS_PENDING ? PnpPending :
|
|
KSUCCESS(result) ? PnpStarted : PnpFailed;
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
if (KFAILED(result))
|
|
DbgPrint("pnp: start failed (0x%x)\n", result);
|
|
IoPnpWake();
|
|
}
|
|
|
|
static void IoPnpWorker(void* ignored) {
|
|
(void)ignored;
|
|
for (;;) {
|
|
(void)KeWaitForEvent(gPnpReady, false);
|
|
for (;;) {
|
|
PNP_NODE* node = (PNP_NODE*)0;
|
|
bool stopping = false;
|
|
bool cleanup = false;
|
|
bool cancel = false;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
for (PNP_NODE* candidate = gPnpNodes; candidate; candidate = candidate->Next) {
|
|
if (candidate->State == PnpPending && candidate->Removed &&
|
|
!candidate->CancelIssued && (candidate->Driver->Descriptor && candidate->Driver->Descriptor->CancelStart)) {
|
|
candidate->CancelIssued = true;
|
|
candidate->State = PnpCancelling;
|
|
node = candidate;
|
|
cancel = true;
|
|
break;
|
|
}
|
|
if (candidate->State == PnpFailed && candidate->Binding && !candidate->Removed) {
|
|
candidate->State = PnpCleaning;
|
|
node = candidate;
|
|
cleanup = true;
|
|
break;
|
|
}
|
|
if (gPnpShutdown && candidate->Removed &&
|
|
IoPnpHasChildren(candidate->Pdo))
|
|
continue;
|
|
if (candidate->Removed && candidate->State != PnpReserved &&
|
|
candidate->State != PnpStarting && candidate->State != PnpPending &&
|
|
candidate->State != PnpCancelling && candidate->State != PnpStopping &&
|
|
candidate->State != PnpCleaning) {
|
|
candidate->State = PnpStopping;
|
|
node = candidate;
|
|
stopping = true;
|
|
break;
|
|
}
|
|
if (candidate->State != PnpUnbound || candidate->Removed ||
|
|
gPnpShutdown || !candidate->Published)
|
|
continue;
|
|
const PNP_DRIVER* driver = IoPnpFindDriver(candidate->Pdo);
|
|
if (driver) {
|
|
candidate->Driver = driver;
|
|
candidate->State = PnpReserved;
|
|
node = candidate;
|
|
break;
|
|
}
|
|
}
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
if (!node) break;
|
|
KeSignalEvent(gPnpReady);
|
|
if (stopping) {
|
|
IoPnpDispose(node);
|
|
} else if (cancel) {
|
|
node->Driver->Descriptor->CancelStart(node->Binding);
|
|
uintptr_t update = KeAcquireSpinlock(&gPnpLock);
|
|
node->State = node->Binding->CompletionSet ?
|
|
(KSUCCESS(node->Binding->CompletionStatus) ? PnpStarted : PnpFailed) :
|
|
PnpPending;
|
|
KeReleaseSpinlock(&gPnpLock, update);
|
|
IoPnpWake();
|
|
} else if (cleanup) {
|
|
NTL_DEVICE* device = node->Binding;
|
|
if (node->Driver->Descriptor && node->Driver->Descriptor->Stop)
|
|
node->Driver->Descriptor->Stop(device);
|
|
else if (node->Driver->IoDriver && node->Pdo->AttachedDevice)
|
|
(void)IoSendPnpIrpSync(node->Pdo, IRP_MN_REMOVE_DEVICE);
|
|
IoPnpReleaseResources(device);
|
|
uintptr_t update = KeAcquireSpinlock(&gPnpLock);
|
|
node->Binding = (NTL_DEVICE*)0;
|
|
node->State = PnpFailed;
|
|
KeReleaseSpinlock(&gPnpLock, update);
|
|
MmFreePool(device);
|
|
IoPnpWake();
|
|
} else {
|
|
IoPnpStart(node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
KSTATUS IoPnpInitialize(void) {
|
|
if (gPnpStarted) return NTLSTATUS_ALREADY_EXISTS;
|
|
KSTATUS st = KeEventCreate(EventAutoReset, false, &gPnpReady);
|
|
if (!KSUCCESS(st)) return st;
|
|
st = KeEventCreate(EventAutoReset, false, &gPnpChanged);
|
|
if (!KSUCCESS(st)) return st;
|
|
|
|
for (uint32_t i = 0; i < PNP_WORKERS; ++i) {
|
|
const KE_THREAD_CREATE_PARAMETERS params =
|
|
KE_THREAD_CREATE_NAMED_NORMAL(PriorityNormal, "pnp.device");
|
|
st = KeCreateKernelThread((uintptr_t)IoPnpWorker, 0, ¶ms, (THREAD**)0);
|
|
if (!KSUCCESS(st)) return st;
|
|
}
|
|
KeInitializeWorkItem(&gPnpWakeItem, IoPnpSignal, (void*)0);
|
|
gPnpStarted = true;
|
|
IoPnpWake();
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Reserve declarative and DRIVER_OBJECT names in one namespace.
|
|
KSTATUS IoPnpPrepareModule(struct NTL_MODULE* module,
|
|
const NTL_DEVICE_DESC* descriptors, uint32_t count,
|
|
DRIVER_OBJECT* const* ioDrivers, uint32_t ioCount) {
|
|
if (!gPnpStarted || !module || (count && !descriptors) || (ioCount && !ioDrivers))
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
if (count > UINT32_MAX - ioCount) return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
PNP_DRIVER* first = (PNP_DRIVER*)0;
|
|
PNP_DRIVER* last = (PNP_DRIVER*)0;
|
|
for (uint32_t i = 0; i < count + ioCount; ++i) {
|
|
PNP_DRIVER* driver = (PNP_DRIVER*)MmAllocatePool(sizeof(*driver));
|
|
if (!driver) {
|
|
while (first) {
|
|
PNP_DRIVER* next = first->Next;
|
|
MmFreePool(first);
|
|
first = next;
|
|
}
|
|
return NTLSTATUS_NO_MEMORY;
|
|
}
|
|
memset(driver, 0, sizeof(*driver));
|
|
driver->Module = module;
|
|
if (i < count) driver->Descriptor = &descriptors[i];
|
|
else driver->IoDriver = ioDrivers[i - count];
|
|
if (last) last->Next = driver;
|
|
else first = driver;
|
|
last = driver;
|
|
}
|
|
|
|
KSTATUS status = NTLSTATUS_SUCCESS;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
if (gPnpShutdown) status = NTLSTATUS_DEVICE_REMOVED;
|
|
for (PNP_DRIVER* proposed = first; KSUCCESS(status) && proposed;
|
|
proposed = proposed->Next) {
|
|
SYS_STRING_VIEW name = proposed->IoDriver ? proposed->IoDriver->Name :
|
|
SysSvFromCstr(proposed->Descriptor->Name);
|
|
for (PNP_DRIVER* existing = gPnpDrivers; existing; existing = existing->Next) {
|
|
SYS_STRING_VIEW oldName = existing->IoDriver ? existing->IoDriver->Name :
|
|
SysSvFromCstr(existing->Descriptor->Name);
|
|
if (SysSvEqual(name, oldName)) {
|
|
status = NTLSTATUS_ALREADY_EXISTS;
|
|
break;
|
|
}
|
|
}
|
|
for (PNP_DRIVER* other = proposed->Next; KSUCCESS(status) && other;
|
|
other = other->Next) {
|
|
SYS_STRING_VIEW otherName = other->IoDriver ? other->IoDriver->Name :
|
|
SysSvFromCstr(other->Descriptor->Name);
|
|
if (SysSvEqual(name, otherName)) status = NTLSTATUS_ALREADY_EXISTS;
|
|
}
|
|
}
|
|
if (KSUCCESS(status) && first) {
|
|
if (gPnpDriversTail) gPnpDriversTail->Next = first;
|
|
else gPnpDrivers = first;
|
|
gPnpDriversTail = last;
|
|
}
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
if (!KSUCCESS(status)) {
|
|
while (first) {
|
|
PNP_DRIVER* next = first->Next;
|
|
MmFreePool(first);
|
|
first = next;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
// Reserved registrations become eligible for binding without allocation.
|
|
void IoPnpCommitModule(struct NTL_MODULE* module) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
for (PNP_DRIVER* driver = gPnpDrivers; driver; driver = driver->Next)
|
|
if (driver->Module == module) driver->Committed = true;
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoPnpWake();
|
|
}
|
|
|
|
// Abort releases reservations before any binding can reference them.
|
|
void IoPnpAbortModule(struct NTL_MODULE* module) {
|
|
PNP_DRIVER* removed = (PNP_DRIVER*)0;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
PNP_DRIVER** link = &gPnpDrivers;
|
|
while (*link) {
|
|
PNP_DRIVER* driver = *link;
|
|
if (driver->Module != module || driver->Committed) {
|
|
link = &driver->Next;
|
|
continue;
|
|
}
|
|
*link = driver->Next;
|
|
driver->Next = removed;
|
|
removed = driver;
|
|
}
|
|
gPnpDriversTail = (PNP_DRIVER*)0;
|
|
for (PNP_DRIVER* it = gPnpDrivers; it; it = it->Next) gPnpDriversTail = it;
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
while (removed) {
|
|
PNP_DRIVER* next = removed->Next;
|
|
MmFreePool(removed);
|
|
removed = next;
|
|
}
|
|
}
|
|
|
|
KSTATUS IoPnpPhysicalDeviceArrived(DEVICE_OBJECT* pdo) {
|
|
if (!pdo || pdo->Type != DeviceObjectPhysical) return NTLSTATUS_INVALID_PARAMETER;
|
|
if (!gPnpStarted) return NTLSTATUS_SUCCESS;
|
|
PNP_NODE* node = (PNP_NODE*)MmAllocatePool(sizeof(*node));
|
|
if (!node) return NTLSTATUS_NO_MEMORY;
|
|
memset(node, 0, sizeof(*node));
|
|
node->Pdo = pdo;
|
|
node->ParentPdo = IoPnpParentPdo(pdo);
|
|
IoReferenceDeviceObject(pdo);
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
if (gPnpShutdown) {
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoDereferenceDeviceObject(pdo);
|
|
MmFreePool(node);
|
|
return NTLSTATUS_DEVICE_REMOVED;
|
|
}
|
|
if (IoPnpFindNode(pdo)) {
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoDereferenceDeviceObject(pdo);
|
|
MmFreePool(node);
|
|
return NTLSTATUS_ALREADY_EXISTS;
|
|
}
|
|
if (gPnpNodesTail) gPnpNodesTail->Next = node;
|
|
else gPnpNodes = node;
|
|
gPnpNodesTail = node;
|
|
++gPnpNodeCount;
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
void IoPnpPhysicalDevicePublished(DEVICE_OBJECT* pdo) {
|
|
if (!pdo || !gPnpStarted) return;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
PNP_NODE* node = IoPnpFindNode(pdo);
|
|
if (node) node->Published = true;
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoPnpWake();
|
|
}
|
|
|
|
void IoPnpPhysicalDeviceRemoved(DEVICE_OBJECT* pdo) {
|
|
if (!pdo || !gPnpStarted) return;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
PNP_NODE* node = IoPnpFindNode(pdo);
|
|
if (node) node->Removed = true;
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoPnpWake();
|
|
}
|
|
|
|
KSTATUS IoPnpWaitForIdle(uint64_t timeoutMs) {
|
|
if (!gPnpStarted) return NTLSTATUS_INVALID_PARAMETER;
|
|
uint64_t deadline = KeMonotonicDeadlineAfterMilliseconds(timeoutMs);
|
|
for (;;) {
|
|
bool idle = true;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
for (PNP_NODE* node = gPnpNodes; node; node = node->Next) {
|
|
if (node->State == PnpReserved || node->State == PnpStarting ||
|
|
node->State == PnpPending || node->State == PnpCancelling ||
|
|
node->State == PnpCleaning ||
|
|
node->State == PnpStopping ||
|
|
(node->State == PnpFailed && node->Binding) ||
|
|
(node->Removed && node->State != PnpPending) ||
|
|
(!gPnpShutdown && node->State == PnpUnbound && node->Published &&
|
|
IoPnpFindDriver(node->Pdo))) {
|
|
idle = false;
|
|
break;
|
|
}
|
|
}
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
if (idle) return NTLSTATUS_SUCCESS;
|
|
uint64_t remaining = KeMonotonicMillisecondsUntil(deadline);
|
|
if (!remaining) return NTLSTATUS_TIMEOUT;
|
|
uint32_t index = 0;
|
|
KSTATUS st = KeWaitForMultipleEvents(&gPnpChanged, 1, false, remaining, &index);
|
|
if (st == NTLSTATUS_TIMEOUT) return NTLSTATUS_TIMEOUT;
|
|
if (!KSUCCESS(st)) return st;
|
|
}
|
|
}
|
|
|
|
KSTATUS IoPnpShutdown(uint64_t timeoutMs) {
|
|
if (!gPnpStarted) return NTLSTATUS_INVALID_PARAMETER;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
gPnpShutdown = true;
|
|
for (PNP_NODE* node = gPnpNodes; node; node = node->Next)
|
|
node->Removed = true;
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoPnpWake();
|
|
return IoPnpWaitForIdle(timeoutMs);
|
|
}
|
|
|
|
__declspec(dllexport) void* NtlDeviceContext(NTL_DEVICE* device) {
|
|
return device ? device->Context : (void*)0;
|
|
}
|
|
|
|
__declspec(dllexport) const char* NtlDeviceId(NTL_DEVICE* device, uint32_t index) {
|
|
if (!device || index >= device->Node->Pdo->u.Pdo.IdCount) return (const char*)0;
|
|
return device->Node->Pdo->u.Pdo.Ids[index].Id.Data;
|
|
}
|
|
|
|
__declspec(dllexport) NTL_BUS_TYPE NtlDeviceBus(NTL_DEVICE* device) {
|
|
return device ? (NTL_BUS_TYPE)device->Node->Pdo->u.Pdo.BusType : (NTL_BUS_TYPE)0;
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS NtlDeviceCompleteStart(NTL_DEVICE* device, KSTATUS result) {
|
|
if (!device || result == NTLSTATUS_PENDING || !device->Node) return NTLSTATUS_INVALID_PARAMETER;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
PNP_NODE* node = device->Node;
|
|
if (node->Binding != device || device->CompletionSet ||
|
|
(node->State != PnpStarting && node->State != PnpPending &&
|
|
node->State != PnpCancelling)) {
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
return NTLSTATUS_OBJECT_DEAD;
|
|
}
|
|
device->CompletionSet = true;
|
|
device->CompletionStatus = result;
|
|
if (node->State == PnpPending) {
|
|
node->State = KSUCCESS(result) ? PnpStarted : PnpFailed;
|
|
}
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoPnpWake();
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS NtlDeviceOwnResource(NTL_DEVICE* device, void* context,
|
|
NTL_DEVICE_RESOURCE_RELEASE release,
|
|
NTL_DEVICE_RESOURCE** out) {
|
|
if (!device || !release || !out) return NTLSTATUS_INVALID_PARAMETER;
|
|
*out = (NTL_DEVICE_RESOURCE*)0;
|
|
NTL_DEVICE_RESOURCE* resource = (NTL_DEVICE_RESOURCE*)MmAllocatePool(sizeof(*resource));
|
|
if (!resource) return NTLSTATUS_NO_MEMORY;
|
|
resource->Owner = device;
|
|
resource->Context = context;
|
|
resource->Release = release;
|
|
uintptr_t flags = KeAcquireSpinlock(&device->Lock);
|
|
if (device->Closing) {
|
|
KeReleaseSpinlock(&device->Lock, flags);
|
|
MmFreePool(resource);
|
|
return NTLSTATUS_OBJECT_DEAD;
|
|
}
|
|
resource->Next = device->Resources;
|
|
device->Resources = resource;
|
|
KeReleaseSpinlock(&device->Lock, flags);
|
|
*out = resource;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
__declspec(dllexport) KSTATUS NtlDeviceReleaseResource(NTL_DEVICE_RESOURCE* resource) {
|
|
if (!resource || !resource->Owner) return NTLSTATUS_INVALID_PARAMETER;
|
|
NTL_DEVICE* device = resource->Owner;
|
|
uintptr_t flags = KeAcquireSpinlock(&device->Lock);
|
|
NTL_DEVICE_RESOURCE** link = &device->Resources;
|
|
while (*link && *link != resource) link = &(*link)->Next;
|
|
if (*link != resource) {
|
|
KeReleaseSpinlock(&device->Lock, flags);
|
|
return NTLSTATUS_OBJECT_DEAD;
|
|
}
|
|
*link = resource->Next;
|
|
resource->Owner = (NTL_DEVICE*)0;
|
|
KeReleaseSpinlock(&device->Lock, flags);
|
|
resource->Release(resource->Context);
|
|
MmFreePool(resource);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|