PortClass电源管理回调函数及调用时机
2022-08-10
106
0
PortClass电源管理使用IAdapterPowerManagement类实例来回调。
class AdapterPowerMgr:
public IAdapterPowerManagement,
public CUnknown
其三个回调函数分别为:
- PowerChangeState:电源状态管理发生变化时回调
- QueryPowerChangeState:请求电源管理状态发生变化时回调
- QueryDeviceCapabilities:设备电源管理特性
AdapterPowerMgr实例指针会存储在DEVIE_OBJECT的扩展单元DEVICE_CONTEXT的成员pAdapterPower中。具体通过PcRegisterAdapterPowerManagement函数来实现。
PDEVICE_CONTEXT pDeviceContext = PDEVICE_CONTEXT(pDeviceObject->DeviceExtension);
...
PVOID pResult;
ntStatus = Unknown->QueryInterface
(
IID_IAdapterPowerManagement,
&pResult
);
if( NT_SUCCESS(ntStatus) )
{
// Store the interface for later use.
pDeviceContext->pAdapterPower = PADAPTERPOWERMANAGEMENT( pResult );
} else
{
pDeviceContext->pAdapterPower = 0;
}
PowerChangeState和QueryPowerChangeState
电源管理的PNP回调为DispatchPower。
DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower
DispatchPower实现的是电源IRP的回调。
switch (pIrpStack->MinorFunction)
{
case IRP_MN_QUERY_POWER:
case IRP_MN_SET_POWER:
if( DevicePowerState == pIrpStack->Parameters.Power.Type )
{
// yeah. Deal with it
ntStatus = ProcessPowerIrp( pIrp,
pIrpStack,
pDeviceObject );
IrpHandled = TRUE;
// And quit.
}
}
电源状态发生变化时,调用ProcessPowerIrp。
*/
NTSTATUS
ProcessPowerIrp
(
IN PIRP pIrp,
IN PIO_STACK_LOCATION pIrpStack,
IN PDEVICE_OBJECT pDeviceObject
)
{
PAGED_CODE();
ASSERT(pIrp);
ASSERT(pIrpStack);
ASSERT(pDeviceObject);
_DbgPrintF(DEBUGLVL_POWER,("ProcessPowerIrp"));
// Assume the worst
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
PDEVICE_CONTEXT pDeviceContext = PDEVICE_CONTEXT(pDeviceObject->DeviceExtension);
POWER_STATE NewPowerState = pIrpStack->Parameters.Power.State;
// Get the current count of open
// objects for this device (pins, streams, whatever).
// NOTE: This count is maintained by KSO.CPP
ULONG objectCount = pDeviceContext->ExistingObjectCount;
// get the active pin count
// NOTE: This count is maintained by IRPSTRM.CPP
ULONG activePinCount = pDeviceContext->ActivePinCount;
BOOL MovingToALighterState = (pDeviceContext->CurrentDeviceState > NewPowerState.DeviceState);
if (pDeviceContext->CurrentDeviceState != NewPowerState.DeviceState) {
// Deal with the particular IRP_MN
switch( pIrpStack->MinorFunction )
{
case IRP_MN_QUERY_POWER:
// simply query the driver if it has registered an interface
if( pDeviceContext->pAdapterPower )
{
ntStatus = pDeviceContext->pAdapterPower->QueryPowerChangeState( NewPowerState );
} else
{
// succeed the query
ntStatus = STATUS_SUCCESS;
}
_DbgPrintF(DEBUGLVL_POWER,(" IRP_MN_QUERY_POWER: D%d->D%d %s",
pDeviceContext->CurrentDeviceState-1,
NewPowerState.DeviceState-1,
NT_SUCCESS(ntStatus) ? "OKAY" : "FAIL"));
break;
case IRP_MN_SET_POWER:
_DbgPrintF(DEBUGLVL_POWER,(" IRP_MN_SET_POWER: D%d->D%d",
pDeviceContext->CurrentDeviceState-1,
NewPowerState.DeviceState-1));
// acquire the device so we're sync'ed with creates
AcquireDevice(pDeviceContext);
// if we're moving from a low power state to a higher power state
if( MovingToALighterState )
{
ASSERT(pDeviceContext->CurrentDeviceState != PowerDeviceD0);
ASSERT(NewPowerState.DeviceState == PowerDeviceD0);
// Then we need to forward to the PDO BEFORE doing our work.
ForwardIrpSynchronous( pDeviceContext, pIrp );
ReleaseDevice(pDeviceContext);
// Do the rest of the work in a work item in order to complete the D0 Irp
// as soon as possible
ntStatus = CallbackEnqueue(
&pDeviceContext->pWorkQueueItemStart,
DevicePowerWorker,
pDeviceObject,
(PVOID)(ULONG_PTR)NewPowerState.DeviceState,
PASSIVE_LEVEL,
EQCF_DIFFERENT_THREAD_REQUIRED
);
// If we fail to enqueue the callback, do this the slow way
if ( !NT_SUCCESS(ntStatus) )
{
DevicePowerWorker( pDeviceObject,
(PVOID)(ULONG_PTR)NewPowerState.DeviceState );
}
} else {
// warn everyone we're about to enter a deeper D-state
PoSetPowerState( pDeviceObject,
DevicePowerState,
NewPowerState );
// moving to a lower state, notify the subdevices
PowerNotifySubdevices( pDeviceContext, NewPowerState );
// keep track of suspends for debugging only
pDeviceContext->SuspendCount++;
// change the driver state if it has a registered POWER interface
if( pDeviceContext->pAdapterPower )
{
// notify the adapter
pDeviceContext->pAdapterPower->PowerChangeState( NewPowerState );
}
// keep track of new state
pDeviceContext->CurrentDeviceState = NewPowerState.DeviceState;
ReleaseDevice(pDeviceContext);
}
// this irp is non-failable
ntStatus = STATUS_SUCCESS;
break;
default:
ASSERT(!"Called with unknown PM IRP ");
break;
}
} else {
//
// We're already there...
//
ntStatus = STATUS_SUCCESS;
ASSERT(!MovingToALighterState);
}
// set the return status
pIrp->IoStatus.Status = ntStatus;
// if not moving to a higher state, forward to the PDO.
if( !MovingToALighterState )
{
ForwardIrpSynchronous( pDeviceContext, pIrp );
}
// start the next power irp
PoStartNextPowerIrp( pIrp );
// complete this irp
CompleteIrp( pDeviceContext, pIrp, ntStatus );
return ntStatus;
}
QueryDeviceCapabilities
case IRP_MN_QUERY_CAPABILITIES:
//
// Fill out power management / ACPI stuff
// for this device.
//
ntStatus = GetDeviceACPIInfo( pIrp, pDeviceObject );
break;
GetDeviceACPIInfo函数代码为:
NTSTATUS
GetDeviceACPIInfo
(
IN PIRP pIrp,
IN PDEVICE_OBJECT pDeviceObject
)
{
PAGED_CODE();
_DbgPrintF(DEBUGLVL_POWER,("GetDeviceACPIInfo"));
ASSERT( pDeviceObject );
PDEVICE_CONTEXT pDeviceContext
= PDEVICE_CONTEXT(pDeviceObject->DeviceExtension);
ASSERT( pDeviceContext );
// Gotta call down to the PDO (bus driver)
// and let it fill out the default for this bus
NTSTATUS ntStatus = ForwardIrpSynchronous( pDeviceContext, pIrp );
if( NT_SUCCESS(ntStatus) )
{
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(pIrp);
PDEVICE_CAPABILITIES pDeviceCaps = irpSp->Parameters.DeviceCapabilities.Capabilities;
ASSERT( pDeviceCaps );
ASSERT( pDeviceCaps->Size >= sizeof( DEVICE_CAPABILITIES ) );
if( pDeviceCaps && ( pDeviceCaps->Size >= sizeof( DEVICE_CAPABILITIES ) ) )
{
// pass the structure on down to the adapter
if( pDeviceContext )
{
if( pDeviceContext->pAdapterPower )
{
ntStatus = pDeviceContext->pAdapterPower->QueryDeviceCapabilities( pDeviceCaps );
ASSERT(ntStatus != STATUS_PENDING);
}
}
// make sure that we have sensible settings for the system sleep states
pDeviceCaps->DeviceState[PowerSystemWorking] = PowerDeviceD0;
for(ULONG i=ULONG(PowerSystemSleeping1); i <= ULONG(PowerSystemShutdown); i++ )
{
// and we want some sleeping in the sleep modes.
//
// DEADISSUE-00/11/11-MartinP
// We go ahead and include this code, even though it is possible that
// there are devices that exist that can maintain state in the device
// while sleeping.
//
if(pDeviceCaps->DeviceState[i] == PowerDeviceD0)
{
pDeviceCaps->DeviceState[i] = PowerDeviceD3;
}
}
// save in our device extension the stuff we're interested in
for( i=ULONG(PowerSystemUnspecified); i < ULONG(PowerSystemMaximum); i++)
{
pDeviceContext->DeviceStateMap[ i ] = pDeviceCaps->DeviceState[ i ];
}
_DbgPrintF( DEBUGLVL_POWER, ( "DeviceCaps: PowerSystemUnspecified = D%d", pDeviceCaps->DeviceState[PowerSystemUnspecified] - 1));
_DbgPrintF( DEBUGLVL_POWER, ( "DeviceCaps: PowerSystemWorking = D%d", pDeviceCaps->DeviceState[PowerSystemWorking] - 1));
_DbgPrintF( DEBUGLVL_POWER, ( "DeviceCaps: PowerSystemSleeping1 = D%d", pDeviceCaps->DeviceState[PowerSystemSleeping1] - 1));
_DbgPrintF( DEBUGLVL_POWER, ( "DeviceCaps: PowerSystemSleeping2 = D%d", pDeviceCaps->DeviceState[PowerSystemSleeping2] - 1));
_DbgPrintF( DEBUGLVL_POWER, ( "DeviceCaps: PowerSystemSleeping3 = D%d", pDeviceCaps->DeviceState[PowerSystemSleeping3] - 1));
_DbgPrintF( DEBUGLVL_POWER, ( "DeviceCaps: PowerSystemHibernate = D%d", pDeviceCaps->DeviceState[PowerSystemHibernate] - 1));
_DbgPrintF( DEBUGLVL_POWER, ( "DeviceCaps: PowerSystemShutdown = D%d", pDeviceCaps->DeviceState[PowerSystemShutdown] - 1));
_DbgPrintF( DEBUGLVL_POWER, ( "DeviceCaps: SystemWake = %d", pDeviceCaps->SystemWake ));
_DbgPrintF( DEBUGLVL_POWER, ( "DeviceCaps: DeviceWake = %d", pDeviceCaps->DeviceWake ));
}
}
// complete the irp
CompleteIrp( pDeviceContext, pIrp, ntStatus );
// set the current power states
pDeviceContext->CurrentDeviceState = PowerDeviceD0;
pDeviceContext->CurrentSystemState = PowerSystemWorking;
// attempt to get the idle info from the registry
if( NT_SUCCESS(ntStatus) )
{
ULONG ConservationIdleTime;
ULONG PerformanceIdleTime;
DEVICE_POWER_STATE IdleDeviceState;
NTSTATUS ntStatus2 = GetIdleInfoFromRegistry( pDeviceContext,
&ConservationIdleTime,
&PerformanceIdleTime,
&IdleDeviceState );
if(NT_SUCCESS(ntStatus2))
{
pDeviceContext->ConservationIdleTime = ConservationIdleTime;
pDeviceContext->PerformanceIdleTime = PerformanceIdleTime;
pDeviceContext->IdleDeviceState = IdleDeviceState;
}
// register for idle detection
pDeviceContext->IdleTimer = PoRegisterDeviceForIdleDetection( pDeviceContext->PhysicalDeviceObject,
pDeviceContext->ConservationIdleTime,
pDeviceContext->PerformanceIdleTime,
pDeviceContext->IdleDeviceState );
_DbgPrintF(DEBUGLVL_POWER,("Idle Detection Enabled (%d %d %d) %s", pDeviceContext->ConservationIdleTime,
pDeviceContext->PerformanceIdleTime,
ULONG(pDeviceContext->IdleDeviceState),
pDeviceContext->IdleTimer ? "" : "FAILED!"));
}
return ntStatus;
}