974 lines
26 KiB
C
974 lines
26 KiB
C
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
notify.c
|
|
|
|
Abstract:
|
|
|
|
Po/Driver Notify functions
|
|
|
|
Author:
|
|
|
|
Bryan Willman (bryanwi) 11-Mar-97
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "pop.h"
|
|
|
|
//
|
|
// constants
|
|
//
|
|
#define POP_RECURSION_LIMIT 30
|
|
|
|
//
|
|
// macros
|
|
//
|
|
#define IS_DO_PDO(DeviceObject) \
|
|
((DeviceObject->Flags & DO_BUS_ENUMERATED_DEVICE) && (DeviceObject->DeviceObjectExtension->DeviceNode))
|
|
|
|
|
|
//
|
|
// procedures private to notification
|
|
//
|
|
NTSTATUS
|
|
PopEnterNotification(
|
|
PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PPO_NOTIFY NotificationFunction,
|
|
PVOID NotificationContext,
|
|
ULONG NotificationType,
|
|
PDEVICE_POWER_STATE DeviceState,
|
|
PVOID *NotificationHandle
|
|
);
|
|
|
|
NTSTATUS
|
|
PopBuildPowerChannel(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
|
|
ULONG RecursionThrottle
|
|
);
|
|
|
|
NTSTATUS
|
|
PopCompleteFindIrp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
PopFindPowerDependencies(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
|
|
ULONG RecursionThrottle
|
|
);
|
|
|
|
|
|
VOID
|
|
PopPresentNotify(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
|
|
ULONG NotificationType
|
|
);
|
|
|
|
//
|
|
// Speced (public) entry points
|
|
//
|
|
|
|
NTKERNELAPI
|
|
NTSTATUS
|
|
PoRegisterDeviceNotify (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PPO_NOTIFY NotificationFunction,
|
|
IN PVOID NotificationContext,
|
|
IN ULONG NotificationType,
|
|
OUT PDEVICE_POWER_STATE DeviceState,
|
|
OUT PVOID *NotificationHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Registers the caller to receive notification of power state changes or
|
|
dependency invalidations related to the "channel" underneath and including
|
|
the Physical Device Object (PDO) refereced via DeviceObject.
|
|
|
|
The "channel" is the set of PDOs, always including the one supplied
|
|
by DeviceObject, which form the hardware stack necessary to perform
|
|
operations. The channel is on when all of these PDOs are on. It is off
|
|
when any of them is not on.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - supplies a PDO
|
|
|
|
NotificationFunction - routine to call to post the notification
|
|
|
|
NotificationContext - argument passed through as is to the NotificationFunction
|
|
|
|
NotificationType - mask of the notifications that the caller wishes to recieve.
|
|
Note that INVALID will always be reported, regardless of whether the
|
|
caller asked for it.
|
|
|
|
DeviceState - the current state of the PDO, as last reported to the
|
|
system via PoSetPowerState
|
|
|
|
NotificationHandle - reference to the notification instance, used to
|
|
cancel the notification.
|
|
|
|
Return Value:
|
|
|
|
Standard NTSTATUS values, including:
|
|
STATUS_INVALID_PARAMETER if DeviceObject is not a PDO, or
|
|
any other parameter is nonsense.
|
|
|
|
STATUS_SUCCESS
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - ususally out of memory
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PPOWER_CHANNEL_SUMMARY pchannel;
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
|
|
//
|
|
// return error for nonsense parameters, or DeviceObject not PDO
|
|
//
|
|
if ( (NotificationFunction == NULL) ||
|
|
(NotificationType == 0) ||
|
|
(NotificationHandle == NULL) ||
|
|
(DeviceState == NULL) ||
|
|
(DeviceObject == NULL) )
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( ! (IS_DO_PDO(DeviceObject)) ) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// acquire the notification channel lock, since we will be
|
|
// changing channel structures
|
|
//
|
|
ExAcquireResourceExclusiveLite(&PopNotifyLock, TRUE);
|
|
|
|
//
|
|
// if the channel isn't already in place, create it
|
|
//
|
|
if (!PopGetDope(DeviceObject)) {
|
|
ExReleaseResourceLite(&PopNotifyLock);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
pchannel = &(DeviceObject->DeviceObjectExtension->Dope->PowerChannelSummary);
|
|
|
|
|
|
if (pchannel->Signature == 0) {
|
|
|
|
//
|
|
// we do NOT already have a channel, bug GetDope has
|
|
// inited the notify list and set the signature and owner for us
|
|
//
|
|
|
|
if (!NT_SUCCESS(status = PopBuildPowerChannel(DeviceObject, pchannel, 0))) {
|
|
ExReleaseResourceLite(&PopNotifyLock);
|
|
return status;
|
|
}
|
|
pchannel->Signature = (ULONG)POP_PNCS_TAG;
|
|
}
|
|
|
|
|
|
//
|
|
// since we are here, pchannel points to a filled in power channel for
|
|
// the request PDO, so we just add this notification instance to it
|
|
// and we are done
|
|
//
|
|
status = PopEnterNotification(
|
|
pchannel,
|
|
DeviceObject,
|
|
NotificationFunction,
|
|
NotificationContext,
|
|
NotificationType,
|
|
DeviceState,
|
|
NotificationHandle
|
|
);
|
|
ExReleaseResourceLite(&PopNotifyLock);
|
|
return status;
|
|
}
|
|
|
|
|
|
NTKERNELAPI
|
|
NTSTATUS
|
|
PoCancelDeviceNotify (
|
|
IN PVOID NotificationHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check that NotificationHandle points to a notify block and that
|
|
it makes sense to cancel it. Decrement ref count. If new ref count
|
|
is 0, blast the entry, cut it from the list, and free its memory.
|
|
|
|
Arguments:
|
|
|
|
NotificationHandle - reference to the notification list entry of interest
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PPOWER_NOTIFY_BLOCK pnb;
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
|
|
pnb = (PPOWER_NOTIFY_BLOCK)NotificationHandle;
|
|
|
|
|
|
ExAcquireResourceExclusiveLite(&PopNotifyLock, TRUE);
|
|
PopLockDopeGlobal(&OldIrql);
|
|
|
|
//
|
|
// check for blatant errors
|
|
//
|
|
if ( (pnb->Signature != (ULONG)POP_PNB_TAG) ||
|
|
(pnb->RefCount < 0) )
|
|
{
|
|
ASSERT(0); // force a break on a debug build
|
|
ExReleaseResourceLite(&PopNotifyLock);
|
|
PopUnlockDopeGlobal(OldIrql);
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
//
|
|
// decrement ref count. if it's 0 afterwards we are done with this node
|
|
//
|
|
pnb->RefCount--;
|
|
|
|
if (pnb->RefCount == 0) {
|
|
|
|
//
|
|
// blast it all, just to be paranoid (it's a low freq operation)
|
|
//
|
|
RemoveEntryList(&(pnb->NotifyList));
|
|
pnb->Signature = POP_NONO;
|
|
pnb->RefCount = -1;
|
|
pnb->NotificationFunction = NULL;
|
|
pnb->NotificationContext = 0L;
|
|
pnb->NotificationType = 0;
|
|
InitializeListHead(&(pnb->NotifyList));
|
|
|
|
if (pnb->Invalidated) {
|
|
PopInvalidNotifyBlockCount--;
|
|
}
|
|
|
|
ExFreePool(pnb);
|
|
|
|
}
|
|
|
|
PopUnlockDopeGlobal(OldIrql);
|
|
ExReleaseResourceLite(&PopNotifyLock);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// worker code
|
|
//
|
|
|
|
NTSTATUS
|
|
PopEnterNotification(
|
|
PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PPO_NOTIFY NotificationFunction,
|
|
PVOID NotificationContext,
|
|
ULONG NotificationType,
|
|
PDEVICE_POWER_STATE DeviceState,
|
|
PVOID *NotificationHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Scans the Power Notification Instance list of the power channel
|
|
looking for one that matches the parameters, which we'll use
|
|
if possible.
|
|
|
|
If no candidate is found, make a new one and put it on the list.
|
|
|
|
|
|
Arguments:
|
|
|
|
PowerChannelSummary - pointer to the power channel structure for the devobj
|
|
|
|
DeviceObject - supplies a PDO
|
|
|
|
NotificationFunction - routine to call to post the notification
|
|
|
|
NotificationContext - argument passed through as is to the NotificationFunction
|
|
|
|
NotificationType - mask of the notifications that the caller wishes to recieve.
|
|
Note that INVALID will always be reported, regardless of whether the
|
|
caller asked for it.
|
|
|
|
DeviceState - the current state of the PDO, as last reported to the
|
|
system via PoSetPowerState
|
|
|
|
NotificationHandle - reference to the notification instance, used to
|
|
cancel the notification.
|
|
|
|
Return Value:
|
|
|
|
Standard NTSTATUS values, including:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - ususally out of memory
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY plist;
|
|
PPOWER_NOTIFY_BLOCK pnb;
|
|
KIRQL oldIrql, oldIrql2;
|
|
|
|
PopLockDopeGlobal(&oldIrql);
|
|
|
|
//
|
|
// run the notify list looking for an existing instance to use
|
|
//
|
|
for (plist = PowerChannelSummary->NotifyList.Flink;
|
|
plist != &(PowerChannelSummary->NotifyList);
|
|
plist = plist->Flink)
|
|
{
|
|
pnb = CONTAINING_RECORD(plist, POWER_NOTIFY_BLOCK, NotifyList);
|
|
if ( (pnb->NotificationFunction == NotificationFunction) &&
|
|
(pnb->NotificationContext == NotificationContext) &&
|
|
(pnb->NotificationType == NotificationType) )
|
|
{
|
|
//
|
|
// we have found an existing list entry that works for us
|
|
//
|
|
pnb->RefCount++;
|
|
*DeviceState = PopLockGetDoDevicePowerState(DeviceObject->DeviceObjectExtension);
|
|
*NotificationHandle = (PVOID)pnb;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// didn't find an instance we can use, so make a new one
|
|
//
|
|
pnb = ExAllocatePoolWithTag(NonPagedPool, sizeof(POWER_NOTIFY_BLOCK), POP_PNB_TAG);
|
|
if (!pnb) {
|
|
PopUnlockDopeGlobal(oldIrql);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
pnb->Signature = (ULONG)(POP_PNB_TAG);
|
|
pnb->RefCount = 1;
|
|
pnb->Invalidated = FALSE;
|
|
InitializeListHead(&(pnb->NotifyList));
|
|
pnb->NotificationFunction = NotificationFunction;
|
|
pnb->NotificationContext = NotificationContext;
|
|
pnb->NotificationType = NotificationType;
|
|
pnb->PowerChannel = PowerChannelSummary;
|
|
InsertHeadList(&(PowerChannelSummary->NotifyList), &(pnb->NotifyList));
|
|
*DeviceState = PopLockGetDoDevicePowerState(DeviceObject->DeviceObjectExtension);
|
|
*NotificationHandle = (PVOID)pnb;
|
|
PopUnlockDopeGlobal(oldIrql);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PopBuildPowerChannel(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
|
|
ULONG RecursionThrottle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds DeviceObject to the power notify channel focused at PowerChannelSummary,
|
|
and then repeat on dependencies.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - supplies a PDO
|
|
|
|
PowerChannelSummary - the power channel structure to add to the PDO's run list
|
|
|
|
RecursionThrottle - number of times we're recursed into this routine,
|
|
punt if it exceeds threshold
|
|
|
|
Return Value:
|
|
|
|
Standard NTSTATUS values, including:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - ususally out of memory
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY pSourceHead;
|
|
PPOWER_NOTIFY_SOURCE pSourceEntry, pEntry;
|
|
PPOWER_NOTIFY_TARGET pTargetEntry;
|
|
KIRQL OldIrql;
|
|
PDEVICE_OBJECT_POWER_EXTENSION pdope;
|
|
PLIST_ENTRY plink;
|
|
|
|
|
|
//
|
|
// bugcheck if we get all confused
|
|
//
|
|
if ( ! (IS_DO_PDO(DeviceObject))) {
|
|
PopInternalAddToDumpFile ( PowerChannelSummary, sizeof(POWER_CHANNEL_SUMMARY), DeviceObject, NULL, NULL, NULL );
|
|
KeBugCheckEx(INTERNAL_POWER_ERROR, 2, 1, (ULONG_PTR)DeviceObject, (ULONG_PTR)PowerChannelSummary);
|
|
}
|
|
|
|
if (RecursionThrottle > POP_RECURSION_LIMIT) {
|
|
ASSERT(0);
|
|
return STATUS_STACK_OVERFLOW;
|
|
}
|
|
|
|
if (!PopGetDope(DeviceObject)) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
|
|
//
|
|
// allocate entries in case we need them
|
|
//
|
|
pSourceEntry =
|
|
ExAllocatePoolWithTag(NonPagedPool, sizeof(POWER_NOTIFY_SOURCE), POP_PNSC_TAG);
|
|
|
|
pTargetEntry =
|
|
ExAllocatePoolWithTag(NonPagedPool, sizeof(POWER_NOTIFY_TARGET), POP_PNTG_TAG);
|
|
|
|
if ((!pSourceEntry) || (!pTargetEntry)) {
|
|
if (pSourceEntry) ExFreePool(pSourceEntry);
|
|
if (pTargetEntry) ExFreePool(pTargetEntry);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// run the source list
|
|
//
|
|
PopLockDopeGlobal(&OldIrql);
|
|
|
|
pdope = DeviceObject->DeviceObjectExtension->Dope;
|
|
pSourceHead = &(pdope->NotifySourceList);
|
|
|
|
for (plink = pSourceHead->Flink;
|
|
plink != pSourceHead;
|
|
plink = plink->Flink)
|
|
{
|
|
pEntry = CONTAINING_RECORD(plink, POWER_NOTIFY_SOURCE, List);
|
|
|
|
if (pEntry->Target->ChannelSummary == PowerChannelSummary) {
|
|
//
|
|
// the supplied device object already points to the supplied
|
|
// channel, so just say we're done.
|
|
//
|
|
ExFreePool(pSourceEntry);
|
|
ExFreePool(pTargetEntry);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// we're not already in the list, so use the source and target entries
|
|
// we created above
|
|
//
|
|
pSourceEntry->Signature = POP_PNSC_TAG;
|
|
pSourceEntry->Target = pTargetEntry;
|
|
pSourceEntry->Dope = pdope;
|
|
InsertHeadList(pSourceHead, &(pSourceEntry->List));
|
|
|
|
pTargetEntry->Signature = POP_PNTG_TAG;
|
|
pTargetEntry->Source = pSourceEntry;
|
|
pTargetEntry->ChannelSummary = PowerChannelSummary;
|
|
pdope = CONTAINING_RECORD(PowerChannelSummary, DEVICE_OBJECT_POWER_EXTENSION, PowerChannelSummary);
|
|
InsertHeadList(&(pdope->NotifyTargetList), &(pTargetEntry->List));
|
|
|
|
//
|
|
// adjust the counts in the PowerChannelSummary
|
|
//
|
|
PowerChannelSummary->TotalCount++;
|
|
if (PopGetDoDevicePowerState(DeviceObject->DeviceObjectExtension) == PowerDeviceD0) {
|
|
PowerChannelSummary->D0Count++;
|
|
}
|
|
|
|
//
|
|
// at this point the one PDO we know about refers to the channel
|
|
// so we now look for things it depends on...
|
|
//
|
|
PopUnlockDopeGlobal(OldIrql);
|
|
PopFindPowerDependencies(DeviceObject, PowerChannelSummary, RecursionThrottle);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PopCompleteFindIrp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
KeSetEvent((PKEVENT)Context, 0, FALSE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
PopFindPowerDependencies(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
|
|
ULONG RecursionThrottle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the power relations for device object, step through them
|
|
looking for pdos. call PopBuildPowerChannel to add PDOs to
|
|
channel inclusion list. recurse into non-pdos looking for pdos.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - supplies a PDO
|
|
|
|
PowerChannel - the power channel structure to add to the PDO's run list
|
|
|
|
RecursionThrottle - number of times we're recursed into this routine,
|
|
punt if it exceeds threshold
|
|
|
|
Return Value:
|
|
|
|
Standard NTSTATUS values, including:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - ususally out of memory
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_RELATIONS pdr;
|
|
KEVENT findevent;
|
|
ULONG i;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpsp;
|
|
PDEVICE_OBJECT childDeviceObject;
|
|
NTSTATUS status;
|
|
|
|
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
|
|
|
|
if (RecursionThrottle > POP_RECURSION_LIMIT) {
|
|
ASSERT(0);
|
|
return STATUS_STACK_OVERFLOW;
|
|
}
|
|
|
|
//
|
|
// allocate and fill an irp to send to the device object
|
|
//
|
|
irp = IoAllocateIrp(
|
|
(CCHAR)(DeviceObject->StackSize),
|
|
TRUE
|
|
);
|
|
|
|
if (!irp) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
irpsp = IoGetNextIrpStackLocation(irp);
|
|
irpsp->MajorFunction = IRP_MJ_PNP;
|
|
irpsp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
|
|
irpsp->Parameters.QueryDeviceRelations.Type = PowerRelations;
|
|
irpsp->DeviceObject = DeviceObject;
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
irp->IoStatus.Information = 0;
|
|
|
|
IoSetCompletionRoutine(
|
|
irp,
|
|
PopCompleteFindIrp,
|
|
(PVOID)(&findevent),
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
KeInitializeEvent(&findevent, SynchronizationEvent, FALSE);
|
|
KeResetEvent(&findevent);
|
|
|
|
IoCallDriver(DeviceObject, irp);
|
|
|
|
KeWaitForSingleObject(
|
|
&findevent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// we have in hand the completed irp, if it worked, it will list
|
|
// device objects that the subject device object has a power relation with
|
|
//
|
|
|
|
if (!NT_SUCCESS(irp->IoStatus.Status)) {
|
|
return irp->IoStatus.Status;
|
|
}
|
|
|
|
pdr = (PDEVICE_RELATIONS)(irp->IoStatus.Information);
|
|
IoFreeIrp(irp);
|
|
|
|
if (!pdr) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (pdr->Count == 0) {
|
|
ExFreePool(pdr);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// walk the pdr, for each entry, either add it as a reference and recurse down
|
|
// (if it's a pdo) or skip the add and simply recurse down (for a !pdo)
|
|
//
|
|
RecursionThrottle++;
|
|
for (i = 0; i < pdr->Count; i++) {
|
|
childDeviceObject = pdr->Objects[i];
|
|
if (IS_DO_PDO(childDeviceObject)) {
|
|
status = PopBuildPowerChannel(
|
|
childDeviceObject,
|
|
PowerChannelSummary,
|
|
RecursionThrottle
|
|
);
|
|
} else {
|
|
status = PopFindPowerDependencies(
|
|
childDeviceObject,
|
|
PowerChannelSummary,
|
|
RecursionThrottle
|
|
);
|
|
}
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
//
|
|
// regardless of how far we got before we hit any errors,
|
|
// we must deref the device objects in the list and free the list itself
|
|
//
|
|
for (i = 0; i < pdr->Count; i++) {
|
|
ObDereferenceObject(pdr->Objects[i]);
|
|
}
|
|
ExFreePool(pdr);
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
PopStateChangeNotify(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
ULONG NotificationType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by PoSetPowerState to execute notifications.
|
|
|
|
Arguments:
|
|
|
|
Dope - the dope for the dev obj that expereinced the change
|
|
|
|
NotificationType - what happened
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PPOWER_CHANNEL_SUMMARY pchannel;
|
|
PDEVICE_OBJECT_POWER_EXTENSION Dope;
|
|
PLIST_ENTRY pSourceHead;
|
|
PPOWER_NOTIFY_SOURCE pSourceEntry;
|
|
PPOWER_NOTIFY_TARGET pTargetEntry;
|
|
PLIST_ENTRY plink;
|
|
KIRQL oldIrql;
|
|
KIRQL oldIrql2;
|
|
|
|
oldIrql = KeGetCurrentIrql();
|
|
|
|
if (oldIrql != PASSIVE_LEVEL) {
|
|
//
|
|
// caller had BETTER be doing a Power up, and we will use
|
|
// the DopeGlobal local to protect access
|
|
//
|
|
PopLockDopeGlobal(&oldIrql2);
|
|
} else {
|
|
//
|
|
// caller could be going up or down, we can just grab the resource
|
|
//
|
|
ExAcquireResourceExclusiveLite(&PopNotifyLock, TRUE);
|
|
}
|
|
|
|
Dope = DeviceObject->DeviceObjectExtension->Dope;
|
|
ASSERT((Dope));
|
|
|
|
//
|
|
// run the notify source structures hanging off the dope
|
|
//
|
|
pSourceHead = &(Dope->NotifySourceList);
|
|
for (plink = pSourceHead->Flink;
|
|
plink != pSourceHead;
|
|
plink = plink->Flink)
|
|
{
|
|
pSourceEntry = CONTAINING_RECORD(plink, POWER_NOTIFY_SOURCE, List);
|
|
ASSERT((pSourceEntry->Signature == POP_PNSC_TAG));
|
|
|
|
pTargetEntry = pSourceEntry->Target;
|
|
ASSERT((pTargetEntry->Signature == POP_PNTG_TAG));
|
|
|
|
pchannel = pTargetEntry->ChannelSummary;
|
|
|
|
if (NotificationType & PO_NOTIFY_D0) {
|
|
//
|
|
// going to D0
|
|
//
|
|
pchannel->D0Count++;
|
|
if (pchannel->D0Count == pchannel->TotalCount) {
|
|
PopPresentNotify(DeviceObject, pchannel, NotificationType);
|
|
}
|
|
} else if (NotificationType & PO_NOTIFY_TRANSITIONING_FROM_D0) {
|
|
//
|
|
// dropping from D0
|
|
//
|
|
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
pchannel->D0Count--;
|
|
if (pchannel->D0Count == (pchannel->TotalCount - 1)) {
|
|
PopPresentNotify(DeviceObject, pchannel, NotificationType);
|
|
}
|
|
} else if (NotificationType & PO_NOTIFY_INVALID) {
|
|
PopPresentNotify(DeviceObject, pchannel, NotificationType);
|
|
}
|
|
}
|
|
|
|
if (oldIrql != PASSIVE_LEVEL) {
|
|
PopUnlockDopeGlobal(oldIrql2);
|
|
} else {
|
|
ExReleaseResourceLite(&PopNotifyLock);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
PopPresentNotify(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PPOWER_CHANNEL_SUMMARY PowerChannelSummary,
|
|
ULONG NotificationType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Run the device object's list of notify nodes, and call the handler
|
|
each one refers to.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - device object that is the source of the notification,
|
|
is NOT necessarily the device object that the power
|
|
channel applies to
|
|
|
|
PowerChannelSummary - base of list of notification blocks to call through
|
|
|
|
NotificationType - what sort of event occurred
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY plisthead;
|
|
PLIST_ENTRY plist;
|
|
PPOWER_NOTIFY_BLOCK pnb;
|
|
|
|
plisthead = &(PowerChannelSummary->NotifyList);
|
|
for (plist = plisthead->Flink; plist != plisthead;) {
|
|
pnb = CONTAINING_RECORD(plist, POWER_NOTIFY_BLOCK, NotifyList);
|
|
ASSERT(pnb->Invalidated == FALSE);
|
|
if ( (NotificationType & PO_NOTIFY_INVALID) ||
|
|
(pnb->NotificationType & NotificationType) )
|
|
{
|
|
(pnb->NotificationFunction)(
|
|
DeviceObject,
|
|
pnb->NotificationContext,
|
|
NotificationType,
|
|
0
|
|
);
|
|
}
|
|
if (NotificationType & PO_NOTIFY_INVALID) {
|
|
//
|
|
// this pnb is no longer valid, so take it off the list.
|
|
// right now this is all we do.
|
|
// N.B. caller is holding the right locks...
|
|
//
|
|
plist = plist->Flink;
|
|
RemoveEntryList(&(pnb->NotifyList));
|
|
InitializeListHead(&(pnb->NotifyList));
|
|
pnb->Invalidated = TRUE;
|
|
PopInvalidNotifyBlockCount += 1;
|
|
} else {
|
|
plist = plist->Flink;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
PopRunDownSourceTargetList(
|
|
PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine runs the source and target lists for the notify
|
|
network hanging off a particular device object. It knocks down
|
|
these entries and their mates, and sends invalidates for notify
|
|
blocks as needed.
|
|
|
|
The caller is expected to be holding PopNotifyLock and the
|
|
PopDopeGlobalLock.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - supplies the address of the device object to
|
|
be cut out of the notify network.
|
|
|
|
D0Count - 1 if the D0Count in target channel summaries is to
|
|
be decremented (run down devobj is in d0) else 0.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_OBJECT_POWER_EXTENSION Dope;
|
|
PDEVOBJ_EXTENSION Doe;
|
|
PLIST_ENTRY pListHead;
|
|
PLIST_ENTRY plink;
|
|
PPOWER_NOTIFY_SOURCE pSourceEntry;
|
|
PPOWER_NOTIFY_TARGET pTargetEntry;
|
|
PPOWER_CHANNEL_SUMMARY targetchannel;
|
|
ULONG D0Count;
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
|
|
|
|
Doe = DeviceObject->DeviceObjectExtension;
|
|
Dope = DeviceObject->DeviceObjectExtension->Dope;
|
|
|
|
PopLockIrpSerialList(&OldIrql);
|
|
if (PopGetDoSystemPowerState(Doe) == PowerDeviceD0) {
|
|
D0Count = 1;
|
|
} else {
|
|
D0Count = 0;
|
|
}
|
|
PopUnlockIrpSerialList(OldIrql);
|
|
|
|
if (!Dope) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// run all of the source nodes for the device object
|
|
//
|
|
pListHead = &(Dope->NotifySourceList);
|
|
for (plink = pListHead->Flink; plink != pListHead; ) {
|
|
pSourceEntry = CONTAINING_RECORD(plink, POWER_NOTIFY_SOURCE, List);
|
|
ASSERT((pSourceEntry->Signature == POP_PNSC_TAG));
|
|
|
|
pTargetEntry = pSourceEntry->Target;
|
|
ASSERT((pTargetEntry->Signature == POP_PNTG_TAG));
|
|
|
|
//
|
|
// free the target node
|
|
//
|
|
targetchannel = pTargetEntry->ChannelSummary;
|
|
RemoveEntryList(&(pTargetEntry->List));
|
|
pTargetEntry->Signature = POP_NONO;
|
|
ExFreePool(pTargetEntry);
|
|
targetchannel->TotalCount--;
|
|
targetchannel->D0Count -= D0Count;
|
|
|
|
//
|
|
// have PopPresentNotify call anybody listening, and remove
|
|
// notify blocks for us
|
|
//
|
|
PopPresentNotify(DeviceObject, targetchannel, PO_NOTIFY_INVALID);
|
|
|
|
//
|
|
// knock down the source entry
|
|
//
|
|
plink = plink->Flink;
|
|
RemoveEntryList(&(pSourceEntry->List));
|
|
pSourceEntry->Signature = POP_NONO;
|
|
ExFreePool(pSourceEntry);
|
|
}
|
|
|
|
//
|
|
// run the target list and shoot down the targets and their source mates
|
|
//
|
|
pListHead = &(Dope->NotifyTargetList);
|
|
for (plink = pListHead->Flink; plink != pListHead; ) {
|
|
pTargetEntry = CONTAINING_RECORD(plink, POWER_NOTIFY_TARGET, List);
|
|
ASSERT((pTargetEntry->Signature == POP_PNTG_TAG));
|
|
|
|
pSourceEntry = pTargetEntry->Source;
|
|
ASSERT((pSourceEntry->Signature == POP_PNSC_TAG));
|
|
|
|
//
|
|
// free the source node on the other end
|
|
//
|
|
RemoveEntryList(&(pSourceEntry->List));
|
|
pSourceEntry->Signature = POP_NONO;
|
|
ExFreePool(pSourceEntry);
|
|
|
|
//
|
|
// free this target node
|
|
//
|
|
plink = plink->Flink;
|
|
RemoveEntryList(&(pTargetEntry->List));
|
|
pTargetEntry->Signature = POP_NONO;
|
|
ExFreePool(pTargetEntry);
|
|
}
|
|
|
|
//
|
|
// since we ran our own target list, and emptied it, we should
|
|
// also have shot down our own notify list. So this devobj's
|
|
// channel summary should be totally clean now.
|
|
//
|
|
Dope->PowerChannelSummary.TotalCount = 0;
|
|
Dope->PowerChannelSummary.D0Count = 0;
|
|
ASSERT(Dope->PowerChannelSummary.NotifyList.Flink == &(Dope->PowerChannelSummary.NotifyList));
|
|
return;
|
|
}
|
|
|
|
|
|
|