595 lines
22 KiB
C
595 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_MAX_DESCRIPTORS 64u
|
|
#define PNP_MAX_DEVICES 128u
|
|
#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 {
|
|
const NTL_DEVICE_DESC* Descriptor;
|
|
DRIVER_OBJECT* IoDriver;
|
|
struct NTL_MODULE* Module;
|
|
} PNP_DRIVER;
|
|
|
|
typedef struct PNP_NODE {
|
|
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[PNP_MAX_DESCRIPTORS];
|
|
static uint32_t gPnpDriverCount;
|
|
static PNP_NODE* gPnpNodes[PNP_MAX_DEVICES];
|
|
static uint32_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 (uint32_t i = 0; i < gPnpDriverCount; ++i) {
|
|
if ((gPnpDrivers[i].Descriptor && IoPnpMatches(gPnpDrivers[i].Descriptor, pdo)) ||
|
|
(gPnpDrivers[i].IoDriver && IoPnpMatchesIoDriver(gPnpDrivers[i].IoDriver, pdo)))
|
|
return &gPnpDrivers[i];
|
|
}
|
|
return (const PNP_DRIVER*)0;
|
|
}
|
|
|
|
static PNP_NODE* IoPnpFindNode(const DEVICE_OBJECT* pdo) {
|
|
for (uint32_t i = 0; i < gPnpNodeCount; ++i)
|
|
if (gPnpNodes[i]->Pdo == pdo)
|
|
return gPnpNodes[i];
|
|
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 (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
|
const DEVICE_OBJECT* ancestor = gPnpNodes[i]->ParentPdo;
|
|
for (uint32_t depth = 0; ancestor && depth < PNP_MAX_DEVICES; ++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);
|
|
for (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
|
if (gPnpNodes[i] == node) {
|
|
gPnpNodes[i] = gPnpNodes[--gPnpNodeCount];
|
|
gPnpNodes[gPnpNodeCount] = (PNP_NODE*)0;
|
|
break;
|
|
}
|
|
}
|
|
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 (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
|
PNP_NODE* candidate = gPnpNodes[i];
|
|
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;
|
|
}
|
|
|
|
KSTATUS IoPnpPublishModule(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;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
if (gPnpShutdown) {
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
return NTLSTATUS_DEVICE_REMOVED;
|
|
}
|
|
if (count > PNP_MAX_DESCRIPTORS - gPnpDriverCount ||
|
|
ioCount > PNP_MAX_DESCRIPTORS - gPnpDriverCount - count) {
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
return NTLSTATUS_NO_MEMORY;
|
|
}
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
for (uint32_t j = 0; j < gPnpDriverCount; ++j) {
|
|
SYS_STRING_VIEW existing = gPnpDrivers[j].IoDriver ?
|
|
gPnpDrivers[j].IoDriver->Name :
|
|
SysSvFromParts(gPnpDrivers[j].Descriptor->Name,
|
|
strlen(gPnpDrivers[j].Descriptor->Name));
|
|
SYS_STRING_VIEW incoming = SysSvFromParts(descriptors[i].Name,
|
|
strlen(descriptors[i].Name));
|
|
if (SysSvEqual(existing, incoming)) {
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
return NTLSTATUS_ALREADY_EXISTS;
|
|
}
|
|
}
|
|
for (uint32_t j = i + 1; j < count; ++j) {
|
|
if (!strcmp(descriptors[i].Name, descriptors[j].Name)) {
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
return NTLSTATUS_ALREADY_EXISTS;
|
|
}
|
|
}
|
|
}
|
|
for (uint32_t i = 0; i < ioCount; ++i) {
|
|
DRIVER_OBJECT* driver = ioDrivers[i];
|
|
for (uint32_t j = 0; j < gPnpDriverCount; ++j) {
|
|
SYS_STRING_VIEW existing = gPnpDrivers[j].IoDriver ?
|
|
gPnpDrivers[j].IoDriver->Name :
|
|
SysSvFromParts(gPnpDrivers[j].Descriptor->Name,
|
|
strlen(gPnpDrivers[j].Descriptor->Name));
|
|
if (SysSvEqual(driver->Name, existing)) {
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
return NTLSTATUS_ALREADY_EXISTS;
|
|
}
|
|
}
|
|
for (uint32_t j = i + 1; j < ioCount; ++j) {
|
|
if (SysSvEqual(driver->Name, ioDrivers[j]->Name)) {
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
return NTLSTATUS_ALREADY_EXISTS;
|
|
}
|
|
}
|
|
for (uint32_t j = 0; j < count; ++j) {
|
|
if (SysSvEqual(driver->Name, SysSvFromParts(descriptors[j].Name,
|
|
strlen(descriptors[j].Name)))) {
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
return NTLSTATUS_ALREADY_EXISTS;
|
|
}
|
|
}
|
|
}
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
gPnpDrivers[gPnpDriverCount].Descriptor = &descriptors[i];
|
|
gPnpDrivers[gPnpDriverCount].IoDriver = (DRIVER_OBJECT*)0;
|
|
gPnpDrivers[gPnpDriverCount].Module = module;
|
|
++gPnpDriverCount;
|
|
}
|
|
for (uint32_t i = 0; i < ioCount; ++i) {
|
|
gPnpDrivers[gPnpDriverCount].Descriptor = (const NTL_DEVICE_DESC*)0;
|
|
gPnpDrivers[gPnpDriverCount].IoDriver = ioDrivers[i];
|
|
gPnpDrivers[gPnpDriverCount].Module = module;
|
|
++gPnpDriverCount;
|
|
}
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoPnpWake();
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
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;
|
|
}
|
|
for (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
|
if (gPnpNodes[i]->Pdo == pdo) {
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoDereferenceDeviceObject(pdo);
|
|
MmFreePool(node);
|
|
return NTLSTATUS_ALREADY_EXISTS;
|
|
}
|
|
}
|
|
if (gPnpNodeCount == PNP_MAX_DEVICES) {
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoDereferenceDeviceObject(pdo);
|
|
MmFreePool(node);
|
|
return NTLSTATUS_NO_MEMORY;
|
|
}
|
|
gPnpNodes[gPnpNodeCount++] = node;
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
void IoPnpPhysicalDevicePublished(DEVICE_OBJECT* pdo) {
|
|
if (!pdo || !gPnpStarted) return;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
for (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
|
if (gPnpNodes[i]->Pdo == pdo) {
|
|
gPnpNodes[i]->Published = true;
|
|
break;
|
|
}
|
|
}
|
|
KeReleaseSpinlock(&gPnpLock, flags);
|
|
IoPnpWake();
|
|
}
|
|
|
|
void IoPnpPhysicalDeviceRemoved(DEVICE_OBJECT* pdo) {
|
|
if (!pdo || !gPnpStarted) return;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
|
for (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
|
if (gPnpNodes[i]->Pdo == pdo) {
|
|
gPnpNodes[i]->Removed = true;
|
|
break;
|
|
}
|
|
}
|
|
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 (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
|
PNP_NODE* node = gPnpNodes[i];
|
|
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 (uint32_t i = 0; i < gPnpNodeCount; ++i)
|
|
gPnpNodes[i]->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;
|
|
}
|