mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
319 lines
8.2 KiB
319 lines
8.2 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
idle.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the power management idle timing code for
|
|
device objects
|
|
|
|
Author:
|
|
|
|
Bryan Willman (bryanwi) 7-Nov-96
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "pop.h"
|
|
|
|
NTKERNELAPI
|
|
PULONG
|
|
PoRegisterDeviceForIdleDetection (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG ConservationIdleTime,
|
|
IN ULONG PerformanceIdleTime,
|
|
IN DEVICE_POWER_STATE State
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A device driver calls this routine to either:
|
|
a. Create and initialize a new idle detection block
|
|
b. Reset values in an existing idle detection block
|
|
|
|
If the device object has an idle detection block, it is
|
|
filled in with new values.
|
|
|
|
Otherwise, an idle detect block is created and linked to the device
|
|
object.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device object which wants idle detection, set_power
|
|
IRPs will be sent here
|
|
|
|
ConservationIdleTime - timeout for system in "conserve mode"
|
|
|
|
PerformanceIdleTime - timeout for system in "performance mode"
|
|
|
|
Type - Type of set_power sent (for set_power irp)
|
|
|
|
State - what state to go to (for set_power irp)
|
|
|
|
Return Value:
|
|
|
|
NULL - if an attempt to create a new idle block failed
|
|
|
|
non-NULL - if an idle block was created, or if an existing one was reset
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_OBJECT_POWER_EXTENSION pdope;
|
|
KIRQL OldIrql;
|
|
NTSTATUS Status;
|
|
ULONG DeviceType, OldDeviceType;
|
|
|
|
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
|
|
//
|
|
// deal with the case where idle detection is being turned off
|
|
//
|
|
if ((ConservationIdleTime == 0) && (PerformanceIdleTime == 0)) {
|
|
PopLockDopeGlobal(&OldIrql);
|
|
pdope = DeviceObject->DeviceObjectExtension->Dope;
|
|
|
|
if (pdope == NULL) {
|
|
//
|
|
// cannot be linked into the chain, so must already be off,
|
|
// so we're done
|
|
//
|
|
|
|
} else {
|
|
//
|
|
// there is a pdope, so we may be on the idle list
|
|
//
|
|
if ((pdope->IdleList.Flink == &(pdope->IdleList)) &&
|
|
(pdope->IdleList.Blink == &(pdope->IdleList)))
|
|
{
|
|
//
|
|
// we're off the queue already, so we're done
|
|
//
|
|
|
|
} else {
|
|
//
|
|
// a dope vector exists and is on the idle scan list,
|
|
// so we must delist ourselves
|
|
//
|
|
RemoveEntryList(&(pdope->IdleList));
|
|
OldDeviceType = pdope->DeviceType | ES_CONTINUOUS;
|
|
pdope->DeviceType = 0;
|
|
PopApplyAttributeState (ES_CONTINUOUS, OldDeviceType);
|
|
|
|
pdope->ConservationIdleTime = 0L;
|
|
pdope->PerformanceIdleTime = 0L;
|
|
pdope->State = PowerDeviceUnspecified;
|
|
pdope->IdleCount = 0;
|
|
InitializeListHead(&(pdope->IdleList));
|
|
}
|
|
}
|
|
PopUnlockDopeGlobal(OldIrql);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Set DeviceType if this is an idle registration by type
|
|
//
|
|
|
|
DeviceType = 0;
|
|
if (ConservationIdleTime == (ULONG) -1 &&
|
|
PerformanceIdleTime == (ULONG) -1) {
|
|
|
|
switch (DeviceObject->DeviceType) {
|
|
case FILE_DEVICE_DISK:
|
|
case FILE_DEVICE_MASS_STORAGE:
|
|
DeviceType = POP_DISK_SPINDOWN | ES_CONTINUOUS;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Unsupported type
|
|
//
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// now, the case where it's being turned on
|
|
//
|
|
pdope = PopGetDope(DeviceObject);
|
|
if (pdope == NULL) {
|
|
//
|
|
// we didn't have a DOPE structure and couldn't allocate one, fail
|
|
//
|
|
return (PVOID)NULL;
|
|
}
|
|
|
|
//
|
|
// May be a newly allocated Dope, or an existing one.
|
|
// In either case, update values.
|
|
// Enqueue if not already in queue
|
|
//
|
|
|
|
PopLockDopeGlobal(&OldIrql);
|
|
|
|
OldDeviceType = pdope->DeviceType | ES_CONTINUOUS;
|
|
|
|
pdope->ConservationIdleTime = ConservationIdleTime;
|
|
pdope->PerformanceIdleTime = PerformanceIdleTime;
|
|
pdope->State = State;
|
|
pdope->IdleCount = 0;
|
|
pdope->DeviceType = (UCHAR) DeviceType;
|
|
|
|
if ((pdope->IdleList.Flink == &(pdope->IdleList)) &&
|
|
(pdope->IdleList.Blink == &(pdope->IdleList)))
|
|
{
|
|
//
|
|
// we're off the queue, and must be enqueued
|
|
//
|
|
InsertTailList(&PopIdleDetectList, &(pdope->IdleList));
|
|
}
|
|
|
|
PopUnlockDopeGlobal(OldIrql);
|
|
PopApplyAttributeState(DeviceType, OldDeviceType);
|
|
PopCheckForWork(TRUE);
|
|
|
|
return &(pdope->IdleCount); // success
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
PopScanIdleList(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by the PopIdleScanTimer at PopIdleScanTimeInseconds interval,
|
|
this routine runs the list of Idle Blocks, finding any that meet the
|
|
trip conditions, and sends commands to the appropriate device objects
|
|
to change state.
|
|
|
|
The timer that calls this DPC is setup in poinit.c.
|
|
|
|
Arguments:
|
|
|
|
Standard DPC arguments, all are ignored.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PLIST_ENTRY link;
|
|
ULONG idlelimit;
|
|
PDEVICE_OBJECT_POWER_EXTENSION pblock;
|
|
POWER_STATE PowerState;
|
|
PULONG pIdleCount;
|
|
ULONG oldCount;
|
|
|
|
PopLockDopeGlobal(&OldIrql);
|
|
|
|
link = PopIdleDetectList.Flink;
|
|
while (link != &PopIdleDetectList) {
|
|
|
|
|
|
pblock = CONTAINING_RECORD(link, DEVICE_OBJECT_POWER_EXTENSION, IdleList);
|
|
pIdleCount = &(pblock->IdleCount);
|
|
oldCount = InterlockedIncrement(pIdleCount);
|
|
|
|
switch (pblock->DeviceType) {
|
|
case 0:
|
|
idlelimit = pblock->PerformanceIdleTime;
|
|
if (PopIdleDetectionMode == PO_IDLE_CONSERVATION) {
|
|
idlelimit = pblock->ConservationIdleTime;
|
|
}
|
|
break;
|
|
|
|
case POP_DISK_SPINDOWN:
|
|
idlelimit = PopPolicy->SpindownTimeout;
|
|
break;
|
|
|
|
default:
|
|
PopInternalAddToDumpFile( NULL, 0, pblock->DeviceObject, NULL, NULL, pblock );
|
|
KeBugCheckEx( INTERNAL_POWER_ERROR,
|
|
0x200,
|
|
POP_IDLE,
|
|
(ULONG_PTR)pblock->DeviceObject,
|
|
(ULONG_PTR)pblock );
|
|
}
|
|
|
|
if ((idlelimit > 0) && ((oldCount+1) == idlelimit)) {
|
|
PowerState.DeviceState = pblock->State;
|
|
PoRequestPowerIrp (
|
|
pblock->DeviceObject,
|
|
IRP_MN_SET_POWER,
|
|
PowerState,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
link = link->Flink;
|
|
}
|
|
|
|
PopUnlockDopeGlobal(OldIrql);
|
|
return;
|
|
}
|
|
|
|
|
|
PDEVICE_OBJECT_POWER_EXTENSION
|
|
PopGetDope (
|
|
PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
PDEVOBJ_EXTENSION Doe;
|
|
PDEVICE_OBJECT_POWER_EXTENSION Dope;
|
|
KIRQL OldIrql;
|
|
|
|
Doe = (PDEVOBJ_EXTENSION) DeviceObject->DeviceObjectExtension;
|
|
|
|
if (!Doe->Dope) {
|
|
PopLockDopeGlobal(&OldIrql);
|
|
|
|
if (!Doe->Dope) {
|
|
Dope = (PDEVICE_OBJECT_POWER_EXTENSION)
|
|
ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(DEVICE_OBJECT_POWER_EXTENSION),
|
|
POP_DOPE_TAG
|
|
);
|
|
if (Dope) {
|
|
RtlZeroMemory (Dope, sizeof(DEVICE_OBJECT_POWER_EXTENSION));
|
|
Dope->DeviceObject = DeviceObject;
|
|
Dope->State = PowerDeviceUnspecified;
|
|
InitializeListHead(&(Dope->IdleList));
|
|
InitializeListHead(&(Dope->NotifySourceList));
|
|
InitializeListHead(&(Dope->NotifyTargetList));
|
|
|
|
// force the signature to 0 so buildpowerchannel gets called
|
|
Dope->PowerChannelSummary.Signature = (ULONG)0;
|
|
InitializeListHead(&(Dope->PowerChannelSummary.NotifyList));
|
|
|
|
Doe->Dope = Dope;
|
|
}
|
|
}
|
|
|
|
PopUnlockDopeGlobal(OldIrql);
|
|
}
|
|
|
|
return Doe->Dope;
|
|
}
|