PortClass音频
+ -

PortClass电源管理回调函数及调用时机

2022-08-10 60 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;
}

0 篇笔记 写笔记

PortClass 电源管理
电源管理实现的COM是AdapterPowerMgr,继承于IAdapterPowerManagement和CUnknown,用于电源的管理。class AdapterPowerMgr: public IAdapterPowerManagement, public CUnkn......
WDF即插即用和电源管理支持
WDF的主要设计目标简化即插即用和电源管理的驱动程序支持,在内核模式和用户模式下都能使用即插即用和电源管理。无缝地处理即插即用事件和电源事件,对于系统的可靠性和良好用户体验来说至关重要,但要想正确实现也出奇地复杂。 这种复杂性多数是因为驱动程序必须确定处理每个即插即用或电源管理请求的正确方式。正确......
PortClass电源管理回调函数及调用时机
PortClass电源管理使用IAdapterPowerManagement类实例来回调。class AdapterPowerMgr: public IAdapterPowerManagement, public CUnknown其三个回调函数分别为:PowerChan......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!