610 lines
16 KiB
C
610 lines
16 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1999 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
pnppower.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This file contains the routines that integrate PnP and Power
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Adrian J. Oney (AdriaO) 01-19-1999
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
Modified for nt kernel.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "pnpmgrp.h"
|
|||
|
|
|||
|
//
|
|||
|
// Internal References
|
|||
|
//
|
|||
|
|
|||
|
PWCHAR
|
|||
|
IopCaptureObjectName (
|
|||
|
IN PVOID Object
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
IopFreePoDeviceNotifyListHead (
|
|||
|
PLIST_ENTRY ListHead
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, IopWarmEjectDevice)
|
|||
|
#pragma alloc_text(PAGELK, IoBuildPoDeviceNotifyList)
|
|||
|
#pragma alloc_text(PAGELK, IoFreePoDeviceNotifyList)
|
|||
|
#pragma alloc_text(PAGELK, IopFreePoDeviceNotifyListHead)
|
|||
|
#pragma alloc_text(PAGELK, IoGetPoNotifyParent)
|
|||
|
#pragma alloc_text(PAGELK, IoMovePoNotifyChildren)
|
|||
|
#pragma alloc_text(PAGELK, IopCaptureObjectName)
|
|||
|
#endif
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IoBuildPoDeviceNotifyList (
|
|||
|
IN OUT PPO_DEVICE_NOTIFY_ORDER Order
|
|||
|
)
|
|||
|
{
|
|||
|
PLIST_ENTRY link;
|
|||
|
PPO_DEVICE_NOTIFY notify, parentnotify;
|
|||
|
PDEVICE_NODE node;
|
|||
|
PDEVICE_NODE parent;
|
|||
|
ULONG noLists, listIndex;
|
|||
|
PLIST_ENTRY notifyLists;
|
|||
|
LONG maxLevel, level;
|
|||
|
UCHAR orderLevel;
|
|||
|
PDEVICE_OBJECT nonPaged;
|
|||
|
PDEVICE_OBJECT current;
|
|||
|
PDEVICE_OBJECT next;
|
|||
|
LIST_ENTRY RebaseList;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
//
|
|||
|
// Block PnP operations like rebalance.
|
|||
|
//
|
|||
|
PiLockDeviceActionQueue();
|
|||
|
|
|||
|
RtlZeroMemory(Order, sizeof (*Order));
|
|||
|
Order->DevNodeSequence = IoDeviceNodeTreeSequence;
|
|||
|
for (i=0; i <= PO_ORDER_MAXIMUM; i++) {
|
|||
|
KeInitializeEvent(&Order->OrderLevel[i].LevelReady,
|
|||
|
NotificationEvent,
|
|||
|
FALSE);
|
|||
|
InitializeListHead(&Order->OrderLevel[i].WaitSleep);
|
|||
|
InitializeListHead(&Order->OrderLevel[i].ReadySleep);
|
|||
|
InitializeListHead(&Order->OrderLevel[i].Pending);
|
|||
|
InitializeListHead(&Order->OrderLevel[i].Complete);
|
|||
|
InitializeListHead(&Order->OrderLevel[i].ReadyS0);
|
|||
|
InitializeListHead(&Order->OrderLevel[i].WaitS0);
|
|||
|
}
|
|||
|
|
|||
|
InitializeListHead(&RebaseList);
|
|||
|
|
|||
|
//
|
|||
|
// Allocate notification structures for all nodes, and determine
|
|||
|
// maximum depth.
|
|||
|
//
|
|||
|
level = -1;
|
|||
|
node = IopRootDeviceNode;
|
|||
|
while (node->Child) {
|
|||
|
node = node->Child;
|
|||
|
level += 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// ADRIAO 01/12/1999 N.B. -
|
|||
|
//
|
|||
|
// Note that we include devices without the started flag. However, two
|
|||
|
// things prevent us from excluding devices that aren't started:
|
|||
|
// 1) We must be able to send power messages to a device we are warm
|
|||
|
// undocking.
|
|||
|
// 2) Many devices may not be started, that is no guarentee they are in D3!
|
|||
|
// For example, they could easily have a boot config, and PNP still
|
|||
|
// relies heavily on BIOS boot configs to keep us from placing hardware
|
|||
|
// ontop of other devices with boot configs we haven't found or started
|
|||
|
// yet!
|
|||
|
//
|
|||
|
|
|||
|
maxLevel = level;
|
|||
|
while (node != IopRootDeviceNode) {
|
|||
|
notify = ExAllocatePoolWithTag (
|
|||
|
NonPagedPool,
|
|||
|
sizeof(PO_DEVICE_NOTIFY),
|
|||
|
IOP_DPWR_TAG
|
|||
|
);
|
|||
|
|
|||
|
if (!notify) {
|
|||
|
|
|||
|
PiUnlockDeviceActionQueue();
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory (notify, sizeof(PO_DEVICE_NOTIFY));
|
|||
|
ASSERT(node->Notify == NULL) ;
|
|||
|
node->Notify = notify;
|
|||
|
notify->Node = node;
|
|||
|
notify->DeviceObject = node->PhysicalDeviceObject;
|
|||
|
notify->TargetDevice = IoGetAttachedDevice(node->PhysicalDeviceObject);
|
|||
|
notify->DriverName = IopCaptureObjectName(notify->TargetDevice->DriverObject);
|
|||
|
notify->DeviceName = IopCaptureObjectName(notify->TargetDevice);
|
|||
|
ObReferenceObject (notify->DeviceObject);
|
|||
|
ObReferenceObject (notify->TargetDevice);
|
|||
|
|
|||
|
orderLevel = 0;
|
|||
|
|
|||
|
if (notify->TargetDevice->DeviceType != FILE_DEVICE_SCREEN &&
|
|||
|
notify->TargetDevice->DeviceType != FILE_DEVICE_VIDEO) {
|
|||
|
orderLevel |= PO_ORDER_NOT_VIDEO;
|
|||
|
}
|
|||
|
|
|||
|
if (notify->TargetDevice->Flags & DO_POWER_PAGABLE) {
|
|||
|
orderLevel |= PO_ORDER_PAGABLE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is a level 0 node it's in the root. Look for
|
|||
|
// non-bus stuff in the root as those guys need to be re-based
|
|||
|
// below everything else.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
notify->OrderLevel = orderLevel;
|
|||
|
|
|||
|
//
|
|||
|
// If the node is root-enumerated, put it on the rebase list so
|
|||
|
// we can mark all its children later.
|
|||
|
// If the node is a leaf node it is ready to receive Sx irps.
|
|||
|
// If it has children, it must wait for its children to complete their Sx irps.
|
|||
|
//
|
|||
|
//
|
|||
|
if ((level == 0) &&
|
|||
|
(node->InterfaceType != Internal) &&
|
|||
|
!(node->Flags & DNF_HAL_NODE)) {
|
|||
|
InsertHeadList(&RebaseList, ¬ify->Link);
|
|||
|
} else {
|
|||
|
++Order->OrderLevel[orderLevel].DeviceCount;
|
|||
|
if (node->Child == NULL) {
|
|||
|
InsertHeadList(&Order->OrderLevel[orderLevel].ReadySleep, ¬ify->Link);
|
|||
|
} else {
|
|||
|
InsertHeadList(&Order->OrderLevel[orderLevel].WaitSleep, ¬ify->Link);
|
|||
|
}
|
|||
|
}
|
|||
|
//
|
|||
|
// Next node
|
|||
|
//
|
|||
|
|
|||
|
if (node->Sibling) {
|
|||
|
node = node->Sibling;
|
|||
|
while (node->Child) {
|
|||
|
node = node->Child;
|
|||
|
level += 1;
|
|||
|
if (level > maxLevel) {
|
|||
|
maxLevel = level;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
node = node->Parent;
|
|||
|
level -= 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Rebase anything on the rebase list to be after the normal pnp stuff
|
|||
|
//
|
|||
|
|
|||
|
while (!IsListEmpty(&RebaseList)) {
|
|||
|
link = RemoveHeadList(&RebaseList);
|
|||
|
notify = CONTAINING_RECORD (link, PO_DEVICE_NOTIFY, Link);
|
|||
|
|
|||
|
//
|
|||
|
// Rebase this node
|
|||
|
//
|
|||
|
|
|||
|
node = notify->Node;
|
|||
|
notify->OrderLevel |= PO_ORDER_ROOT_ENUM;
|
|||
|
|
|||
|
++Order->OrderLevel[notify->OrderLevel].DeviceCount;
|
|||
|
if (node->Child == NULL) {
|
|||
|
InsertHeadList(&Order->OrderLevel[notify->OrderLevel].ReadySleep, ¬ify->Link);
|
|||
|
} else {
|
|||
|
InsertHeadList(&Order->OrderLevel[notify->OrderLevel].WaitSleep, ¬ify->Link);
|
|||
|
}
|
|||
|
//
|
|||
|
// Now rebase all the node's children
|
|||
|
//
|
|||
|
|
|||
|
parent = node;
|
|||
|
while (node->Child) {
|
|||
|
node = node->Child;
|
|||
|
}
|
|||
|
|
|||
|
while (node != parent) {
|
|||
|
notify = node->Notify;
|
|||
|
if (notify) {
|
|||
|
RemoveEntryList(¬ify->Link);
|
|||
|
--Order->OrderLevel[notify->OrderLevel].DeviceCount;
|
|||
|
notify->OrderLevel |= PO_ORDER_ROOT_ENUM;
|
|||
|
++Order->OrderLevel[notify->OrderLevel].DeviceCount;
|
|||
|
if (node->Child == NULL) {
|
|||
|
InsertHeadList(&Order->OrderLevel[notify->OrderLevel].ReadySleep, ¬ify->Link);
|
|||
|
} else {
|
|||
|
InsertHeadList(&Order->OrderLevel[notify->OrderLevel].WaitSleep, ¬ify->Link);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// next node
|
|||
|
if (node->Sibling) {
|
|||
|
node = node->Sibling;
|
|||
|
while (node->Child) {
|
|||
|
node = node->Child;
|
|||
|
}
|
|||
|
} else {
|
|||
|
node = node->Parent;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// make one more pass through all the notify devices in order to count
|
|||
|
// the children of each parent. It would be nice if the PNP engine kept
|
|||
|
// track of the number of children in the devnode, but until that is done,
|
|||
|
// we need this second pass.
|
|||
|
//
|
|||
|
// Also make sure that each node's parent is an order level >= its children.
|
|||
|
//
|
|||
|
node = IopRootDeviceNode;
|
|||
|
while (node->Child) {
|
|||
|
node = node->Child;
|
|||
|
}
|
|||
|
while (node != IopRootDeviceNode) {
|
|||
|
if (node->Parent != IopRootDeviceNode) {
|
|||
|
parentnotify = node->Parent->Notify;
|
|||
|
parentnotify->ChildCount++;
|
|||
|
parentnotify->ActiveChild++;
|
|||
|
if (parentnotify->OrderLevel > node->Notify->OrderLevel) {
|
|||
|
|
|||
|
//
|
|||
|
// The parent is a higher order level than its child. Move the
|
|||
|
// parent down to the same order as its child
|
|||
|
//
|
|||
|
RemoveEntryList(&parentnotify->Link);
|
|||
|
--Order->OrderLevel[parentnotify->OrderLevel].DeviceCount;
|
|||
|
parentnotify->OrderLevel = node->Notify->OrderLevel;
|
|||
|
++Order->OrderLevel[parentnotify->OrderLevel].DeviceCount;
|
|||
|
InsertHeadList(&Order->OrderLevel[parentnotify->OrderLevel].WaitSleep, &parentnotify->Link);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Next node
|
|||
|
//
|
|||
|
|
|||
|
if (node->Sibling) {
|
|||
|
node = node->Sibling;
|
|||
|
while (node->Child) {
|
|||
|
node = node->Child;
|
|||
|
}
|
|||
|
} else {
|
|||
|
node = node->Parent;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Order->WarmEjectPdoPointer = &IopWarmEjectPdo;
|
|||
|
|
|||
|
//
|
|||
|
// The engine lock is release when the notify list is freed
|
|||
|
//
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PVOID
|
|||
|
IoGetPoNotifyParent(
|
|||
|
IN PPO_DEVICE_NOTIFY Notify
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Returns the notify structure of the specified device's parent.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Notify - Supplies the child device
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Parent's notify structure if present
|
|||
|
NULL if there is no parent
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_NODE Node;
|
|||
|
|
|||
|
Node = Notify->Node;
|
|||
|
if (Node->Parent != IopRootDeviceNode) {
|
|||
|
return(Node->Parent->Notify);
|
|||
|
} else {
|
|||
|
return(NULL);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
IoMovePoNotifyChildren(
|
|||
|
IN PPO_DEVICE_NOTIFY Notify,
|
|||
|
IN PPO_DEVICE_NOTIFY_ORDER Order
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Removes any children of the supplied device that are at the
|
|||
|
same orderlevel as the supplied parent and reinserts them
|
|||
|
on the ReadyS0 list.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Notify - Supplies the device notify structure
|
|||
|
|
|||
|
Orderr - Supplies the device notification order structure
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_NODE Node;
|
|||
|
PDEVICE_NODE Child;
|
|||
|
PPO_DEVICE_NOTIFY ChildNotify;
|
|||
|
PPO_NOTIFY_ORDER_LEVEL Level;
|
|||
|
|
|||
|
Node = Notify->Node;
|
|||
|
Child = Node->Child;
|
|||
|
while (Child) {
|
|||
|
ChildNotify = Child->Notify;
|
|||
|
if (ChildNotify->OrderLevel == Notify->OrderLevel) {
|
|||
|
RemoveEntryList(&ChildNotify->Link);
|
|||
|
Level = &Order->OrderLevel[ChildNotify->OrderLevel];
|
|||
|
InsertTailList(&Level->ReadyS0, &ChildNotify->Link);
|
|||
|
}
|
|||
|
Child = Child->Sibling;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
IopFreePoDeviceNotifyListHead (
|
|||
|
PLIST_ENTRY ListHead
|
|||
|
)
|
|||
|
{
|
|||
|
PLIST_ENTRY Link;
|
|||
|
PPO_DEVICE_NOTIFY Notify;
|
|||
|
PDEVICE_NODE Node;
|
|||
|
|
|||
|
while (!IsListEmpty(ListHead)) {
|
|||
|
Link = RemoveHeadList(ListHead);
|
|||
|
Notify = CONTAINING_RECORD (Link, PO_DEVICE_NOTIFY, Link);
|
|||
|
|
|||
|
Node = (PDEVICE_NODE) Notify->Node;
|
|||
|
Node->Notify = NULL;
|
|||
|
|
|||
|
ObDereferenceObject (Notify->DeviceObject);
|
|||
|
ObDereferenceObject (Notify->TargetDevice);
|
|||
|
if (Notify->DeviceName) {
|
|||
|
ExFreePool (Notify->DeviceName);
|
|||
|
}
|
|||
|
if (Notify->DriverName) {
|
|||
|
ExFreePool (Notify->DriverName);
|
|||
|
}
|
|||
|
ExFreePool(Notify);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
IoFreePoDeviceNotifyList (
|
|||
|
IN OUT PPO_DEVICE_NOTIFY_ORDER Order
|
|||
|
)
|
|||
|
{
|
|||
|
ULONG i;
|
|||
|
PLIST_ENTRY ListHead;
|
|||
|
PLIST_ENTRY Link;
|
|||
|
PPO_DEVICE_NOTIFY Notify;
|
|||
|
|
|||
|
if (Order->DevNodeSequence) {
|
|||
|
|
|||
|
Order->DevNodeSequence = 0;
|
|||
|
|
|||
|
PiUnlockDeviceActionQueue();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free the resources from the notify list
|
|||
|
//
|
|||
|
for (i=0; i <= PO_ORDER_MAXIMUM; i++) {
|
|||
|
IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].WaitSleep);
|
|||
|
IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].ReadySleep);
|
|||
|
IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].Pending);
|
|||
|
IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].Complete);
|
|||
|
IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].ReadyS0);
|
|||
|
IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].WaitS0);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PWCHAR
|
|||
|
IopCaptureObjectName (
|
|||
|
IN PVOID Object
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
UCHAR Buffer[512];
|
|||
|
POBJECT_NAME_INFORMATION ObName;
|
|||
|
ULONG len;
|
|||
|
PWCHAR Name;
|
|||
|
|
|||
|
ObName = (POBJECT_NAME_INFORMATION) Buffer;
|
|||
|
Status = ObQueryNameString (
|
|||
|
Object,
|
|||
|
ObName,
|
|||
|
sizeof (Buffer),
|
|||
|
&len
|
|||
|
);
|
|||
|
|
|||
|
Name = NULL;
|
|||
|
if (NT_SUCCESS(Status) && ObName->Name.Buffer) {
|
|||
|
Name = ExAllocatePoolWithTag (
|
|||
|
NonPagedPool,
|
|||
|
ObName->Name.Length + sizeof(WCHAR),
|
|||
|
IOP_DPWR_TAG
|
|||
|
);
|
|||
|
|
|||
|
if (Name) {
|
|||
|
memcpy (Name, ObName->Name.Buffer, ObName->Name.Length);
|
|||
|
Name[ObName->Name.Length/sizeof(WCHAR)] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return Name;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IopWarmEjectDevice(
|
|||
|
IN PDEVICE_OBJECT DeviceToEject,
|
|||
|
IN SYSTEM_POWER_STATE LightestSleepState
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is invoked to initiate a warm eject. The eject progresses
|
|||
|
from S1 to the passed in lightest sleep state.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceToEject - The device to eject
|
|||
|
|
|||
|
LightestSleepState - The lightest S state (at least S1) that the device
|
|||
|
may be ejected in. This might be S4 if we are truely
|
|||
|
low on power.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS value.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Acquire the warm eject device lock. A warm eject requires we enter a
|
|||
|
// specific S-state, and two different devices may have conflicting options.
|
|||
|
// Therefore only one is allowed to occur at once.
|
|||
|
//
|
|||
|
status = KeWaitForSingleObject(
|
|||
|
&IopWarmEjectLock,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
ASSERT(status == STATUS_SUCCESS) ;
|
|||
|
|
|||
|
//
|
|||
|
// Acquire engine lock. We are not allowed to set or clear this field
|
|||
|
// unless we are under this lock.
|
|||
|
//
|
|||
|
PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS);
|
|||
|
|
|||
|
//
|
|||
|
// Set the current Pdo to eject.
|
|||
|
//
|
|||
|
ASSERT(IopWarmEjectPdo == NULL);
|
|||
|
IopWarmEjectPdo = DeviceToEject;
|
|||
|
|
|||
|
//
|
|||
|
// Release the engine lock.
|
|||
|
//
|
|||
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|||
|
|
|||
|
//
|
|||
|
// Attempt to invalidate Po's cached notification list. This should cause
|
|||
|
// IoBuildPoDeviceNotifyList to be called at which time it will in theory
|
|||
|
// pickup the above placed warm eject Pdo.
|
|||
|
//
|
|||
|
// ADRIAO NOTE 01/07/1999 -
|
|||
|
// Actually, this whole IoDeviceNodeTreeSequence stuff isn't neccessary.
|
|||
|
// PnP will make no changes to the tree while the device tree lock is owned,
|
|||
|
// and it's owned for the duration of a power notification.
|
|||
|
//
|
|||
|
IoDeviceNodeTreeSequence++;
|
|||
|
|
|||
|
//
|
|||
|
// Sleep...
|
|||
|
//
|
|||
|
status = NtInitiatePowerAction(
|
|||
|
PowerActionWarmEject,
|
|||
|
LightestSleepState,
|
|||
|
POWER_ACTION_QUERY_ALLOWED |
|
|||
|
POWER_ACTION_UI_ALLOWED,
|
|||
|
FALSE // Asynchronous == FALSE
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Acquire the engine lock. We are not allowed to set or clear this field
|
|||
|
// unless we are under this lock.
|
|||
|
//
|
|||
|
PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS);
|
|||
|
|
|||
|
//
|
|||
|
// Clear the current PDO to eject, and see if the Pdo was actually picked
|
|||
|
// up.
|
|||
|
//
|
|||
|
if (IopWarmEjectPdo) {
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// If our device wasn't picked up, the return of
|
|||
|
// NtInitiatePowerAction should *not* be successful!
|
|||
|
//
|
|||
|
ASSERT(0);
|
|||
|
status = STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
IopWarmEjectPdo = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Release the engine lock.
|
|||
|
//
|
|||
|
PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
|
|||
|
|
|||
|
//
|
|||
|
// Release the warm eject device lock
|
|||
|
//
|
|||
|
KeSetEvent(
|
|||
|
&IopWarmEjectLock,
|
|||
|
IO_NO_INCREMENT,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|