/***************************************************************************** * power.cpp - WDM Streaming port class driver ***************************************************************************** * Copyright (c) 1996-2000 Microsoft Corporation. All rights reserved. * * This file contains code related to ACPI / power management * for the audio adpaters/miniports */ #include "private.h" #ifndef DEBUGLVL_POWER #define DEBUGLVL_POWER DEBUGLVL_VERBOSE #endif NTSTATUS ProcessPowerIrp ( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpStack, IN PDEVICE_OBJECT pDeviceObject ); #pragma code_seg("PAGE") /***************************************************************************** * GetDeviceACPIInfo() ***************************************************************************** * Called in response to a PnP - IRP_MN_QUERY_CAPABILITIES * Call the bus driver to fill out the inital info, * Then overwrite with our own... * */ 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; } #pragma code_seg() VOID DevicePowerRequestRoutine( IN PKDPC Dpc, IN PVOID Context, IN PVOID SystemContext1, IN PVOID SystemContext2 ) { PDEVICE_CONTEXT pDeviceContext = (PDEVICE_CONTEXT) Context; POWER_STATE newPowerState; newPowerState.DeviceState = PowerDeviceD0; PoRequestPowerIrp(pDeviceContext->PhysicalDeviceObject, IRP_MN_SET_POWER, newPowerState, NULL, NULL, NULL ); } /***************************************************************************** * PowerIrpCompletionRoutine() ***************************************************************************** * Used when requested a new power irp. * Just signal an event and return. * */ VOID PowerIrpCompletionRoutine ( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus ) { ASSERT(Context); _DbgPrintF( DEBUGLVL_POWER, ("PowerIrpCompletionRoutine")); PPOWER_IRP_CONTEXT pPowerIrpContext = PPOWER_IRP_CONTEXT(Context); // set the return status pPowerIrpContext->Status = IoStatus->Status; // complete any pending system power irp if( pPowerIrpContext->PendingSystemPowerIrp ) { _DbgPrintF(DEBUGLVL_POWER,("Device Set/Query Power Irp completed, Completing Associated System Power Irp")); if (NT_SUCCESS(IoStatus->Status)) { // Forward the system set power irp to the PDO ForwardIrpSynchronous( pPowerIrpContext->DeviceContext, pPowerIrpContext->PendingSystemPowerIrp ); } else { pPowerIrpContext->PendingSystemPowerIrp->IoStatus.Status = IoStatus->Status; } // start the next power irp PoStartNextPowerIrp( pPowerIrpContext->PendingSystemPowerIrp ); // complete the system set power irp CompleteIrp( pPowerIrpContext->DeviceContext, pPowerIrpContext->PendingSystemPowerIrp, pPowerIrpContext->PendingSystemPowerIrp->IoStatus.Status ); // free the context (only when completing a pending system power irp) ExFreePool( pPowerIrpContext ); } else { // set the sync event (not used in conjunction with pending system power irps) if( pPowerIrpContext->PowerSyncEvent ) { KeSetEvent( pPowerIrpContext->PowerSyncEvent, 0, FALSE ); } } } #pragma code_seg("PAGE") /***************************************************************************** * DispatchPower() ***************************************************************************** * Deals with all the power/ACPI messages from the OS. * yay. * */ NTSTATUS DispatchPower ( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { PAGED_CODE(); ASSERT(pDeviceObject); ASSERT(pIrp); NTSTATUS ntStatus = STATUS_SUCCESS; PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); PDEVICE_CONTEXT pDeviceContext = PDEVICE_CONTEXT(pDeviceObject->DeviceExtension); ntStatus = PcValidateDeviceContext(pDeviceContext, pIrp); if (!NT_SUCCESS(ntStatus)) { // Don't know what to do, but this is probably a PDO. // We'll try to make this right by completing the IRP // untouched (per PnP, WMI, and Power rules). Note // that if this isn't a PDO, and isn't a portcls FDO, then // the driver messed up by using Portcls as a filter (huh?) // In this case the verifier will fail us, WHQL will catch // them, and the driver will be fixed. We'd be very surprised // to see such a case. PoStartNextPowerIrp( pIrp ); ntStatus = pIrp->IoStatus.Status; IoCompleteRequest( pIrp, IO_NO_INCREMENT ); return ntStatus; } IncrementPendingIrpCount( pDeviceContext ); #if (DBG) static PCHAR aszMnNames[] = { "IRP_MN_WAIT_WAKE", "IRP_MN_POWER_SEQUENCE", "IRP_MN_SET_POWER", "IRP_MN_QUERY_POWER" }; if (pIrpStack->MinorFunction >= SIZEOF_ARRAY(aszMnNames)) { _DbgPrintF( DEBUGLVL_POWER,("DispatchPower function 0x%02x",pIrpStack->MinorFunction)); } else { _DbgPrintF( DEBUGLVL_POWER,("DispatchPower function %s",aszMnNames[pIrpStack->MinorFunction])); } #endif // Assume we won't deal with the irp. BOOL IrpHandled = FALSE; switch (pIrpStack->MinorFunction) { case IRP_MN_QUERY_POWER: case IRP_MN_SET_POWER: // Is this a device state change? if( DevicePowerState == pIrpStack->Parameters.Power.Type ) { // yeah. Deal with it ntStatus = ProcessPowerIrp( pIrp, pIrpStack, pDeviceObject ); IrpHandled = TRUE; // And quit. } else { // A system state change if( IRP_MN_QUERY_POWER == pIrpStack->MinorFunction ) { _DbgPrintF(DEBUGLVL_POWER,(" IRP_MN_QUERY_POWER: ->S%d", pIrpStack->Parameters.Power.State.SystemState-1)); } else { _DbgPrintF(DEBUGLVL_POWER,(" IRP_MN_SET_POWER: ->S%d", pIrpStack->Parameters.Power.State.SystemState-1)); } POWER_STATE newPowerState; // determine appropriate device state newPowerState.DeviceState = pDeviceContext->DeviceStateMap[ pIrpStack->Parameters.Power.State.SystemState ]; // // do a sanity check on the device state if ((newPowerState.DeviceState < PowerDeviceD0) || (newPowerState.DeviceState > PowerDeviceD3) ) { if (pIrpStack->Parameters.Power.State.SystemState == PowerSystemWorking) { newPowerState.DeviceState = PowerDeviceD0; } else { newPowerState.DeviceState = PowerDeviceD3; } } _DbgPrintF(DEBUGLVL_POWER,(" ...Requesting Device Power IRP -> D%d",newPowerState.DeviceState-1)); if ((pIrpStack->MinorFunction == IRP_MN_SET_POWER) && (newPowerState.DeviceState == PowerDeviceD0)) { // // doing a resume, request the D irp, but complete S-irp immediately // KeInsertQueueDpc(&pDeviceContext->DevicePowerRequestDpc, NULL, NULL); break; } else { // allocate a completion context (can't be on the stack because we're not going to block) PPOWER_IRP_CONTEXT PowerIrpContext = PPOWER_IRP_CONTEXT(ExAllocatePoolWithTag(NonPagedPool, sizeof(POWER_IRP_CONTEXT), 'oPcP' ) ); // 'PcPo' if (PowerIrpContext) { _DbgPrintF(DEBUGLVL_POWER,("...Pending System Power Irp until Device Power Irp completes")); // set up device power irp completion context PowerIrpContext->PowerSyncEvent = NULL; #if DBG PowerIrpContext->Status = STATUS_PENDING; #endif PowerIrpContext->PendingSystemPowerIrp = pIrp; PowerIrpContext->DeviceContext = pDeviceContext; // pend the system set power irp // #if DBG pIrp->IoStatus.Status = STATUS_PENDING; #endif IoMarkIrpPending( pIrp ); // set our tracking of system power state if (pIrpStack->MinorFunction == IRP_MN_SET_POWER) { pDeviceContext->CurrentSystemState = pIrpStack->Parameters.Power.State.SystemState; } // request the new device state // ntStatus = PoRequestPowerIrp( pDeviceContext->PhysicalDeviceObject, pIrpStack->MinorFunction, newPowerState, PowerIrpCompletionRoutine, PowerIrpContext, NULL ); if (!NT_SUCCESS(ntStatus)) { _DbgPrintF(DEBUGLVL_TERSE,("PoRequestPowerIrp failed (%08x)", ntStatus)); CompleteIrp( pDeviceContext, pIrp, ntStatus ); } IrpHandled = TRUE; // set up return status ntStatus = STATUS_PENDING; } else { // couldn't allocate completion context ntStatus = STATUS_INSUFFICIENT_RESOURCES; PoStartNextPowerIrp( pIrp ); CompleteIrp( pDeviceContext, pIrp, ntStatus); return ntStatus; } } } break; } // If we didn't cope with the irp if( !IrpHandled ) { // Send it on it's way. ntStatus = ForwardIrpSynchronous( pDeviceContext, pIrp ); // and complete it. PoStartNextPowerIrp( pIrp ); CompleteIrp( pDeviceContext, pIrp, ntStatus ); } return ntStatus; } /***************************************************************************** * PcRegisterAdapterPowerManagement() ***************************************************************************** * Register the adapter's power management interface * with portcls. This routine also does a QI for a shutdown notification * interface. */ PORTCLASSAPI NTSTATUS NTAPI PcRegisterAdapterPowerManagement ( IN PUNKNOWN Unknown, IN PVOID pvContext1 ) { PAGED_CODE(); ASSERT(pvContext1); ASSERT(Unknown); _DbgPrintF(DEBUGLVL_POWER,("PcRegisterAdapterPowerManagement")); // // Validate Parameters. // if (NULL == pvContext1 || NULL == Unknown) { _DbgPrintF(DEBUGLVL_TERSE, ("PcRegisterAdapterPowerManagement : Invalid Parameter")); return STATUS_INVALID_PARAMETER; } NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; PDEVICE_OBJECT pDeviceObject = PDEVICE_OBJECT(pvContext1); PDEVICE_CONTEXT pDeviceContext = PDEVICE_CONTEXT(pDeviceObject->DeviceExtension); ASSERT( pDeviceContext ); // // Validate DeviceContext. // if (NULL == pDeviceContext) { _DbgPrintF(DEBUGLVL_TERSE, ("PcRegisterAdapterPowerManagement : Invalid DeviceContext")); return STATUS_INVALID_PARAMETER; } #if (DBG) if( pDeviceContext->pAdapterPower ) { _DbgPrintF( DEBUGLVL_POWER, ("Adapter overwriting PowerManagement interface")); } #endif // Make sure this is really the right // interface (Note: We have to release // it when the device is closed/stoped ) 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; } return ntStatus; } /***************************************************************************** * PowerNotifySubdevices() ***************************************************************************** * Called by ProcessPowerIrp to notify the device's subdevices of a power * state change. * */ void PowerNotifySubdevices ( IN PDEVICE_CONTEXT pDeviceContext, IN POWER_STATE PowerState ) { PAGED_CODE(); ASSERT(pDeviceContext); _DbgPrintF(DEBUGLVL_POWER,("PowerNotifySubdevices")); // only notify the subdevices if we're started and if there are subdevices if (pDeviceContext->DeviceStopState == DeviceStarted) { PKSOBJECT_CREATE_ITEM createItem = pDeviceContext->CreateItems; // iterate through the subdevices for( ULONG index=0; index < pDeviceContext->MaxObjects; index++,createItem++) { if( createItem && (createItem->Create) ) { PSUBDEVICE subDevice = PSUBDEVICE( createItem->Context ); if( subDevice ) { // notify the subdevice subDevice->PowerChangeNotify( PowerState ); } } } } } /***************************************************************************** * DevicePowerWorker() ***************************************************************************** * Called by ProcessPowerIrp in order to notify the device of the state change. * This is done in a work item so that the processing for the D0 irp doesn't * block the rest of the system from processing D0 irps. */ QUEUED_CALLBACK_RETURN DevicePowerWorker ( IN PDEVICE_OBJECT pDeviceObject, IN PVOID PowerState ) { PDEVICE_CONTEXT pDeviceContext = PDEVICE_CONTEXT(pDeviceObject->DeviceExtension); BOOL ProcessPendedIrps = FALSE; POWER_STATE NewPowerState; NewPowerState.DeviceState = (DEVICE_POWER_STATE)(ULONG_PTR)PowerState; // acquire the device so we're sync'ed with creates AcquireDevice(pDeviceContext); // 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; // notify everyone we're now in our lighter D-state PoSetPowerState( pDeviceObject, DevicePowerState, NewPowerState ); PowerNotifySubdevices( pDeviceContext, NewPowerState ); // set PendCreates appropriately if( pDeviceContext->DeviceStopState == DeviceStarted ) { // start allowing creates pDeviceContext->PendCreates = FALSE; // we have to process the pended irps after we release the device ProcessPendedIrps = TRUE; } ReleaseDevice(pDeviceContext); // complete if necessary any pended IRPs if ( ProcessPendedIrps ) { CompletePendedIrps( pDeviceObject, pDeviceContext, EMPTY_QUEUE_AND_PROCESS ); } return QUEUED_CALLBACK_FREE; } /***************************************************************************** * ProcessPowerIrp() ***************************************************************************** * Called by DispatchPower to call the Adapter driver and all other work * related to a request. Note that this routine MUST return STATUS_SUCCESS * for IRP_MN_SET_POWER requests. * */ 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; } /***************************************************************************** * UpdateActivePinCount() ***************************************************************************** * */ NTSTATUS UpdateActivePinCount ( IN PDEVICE_CONTEXT DeviceContext, IN BOOL Increment ) { PAGED_CODE(); ASSERT(DeviceContext); ULONG ActivePinCount; NTSTATUS ntStatus = STATUS_SUCCESS; BOOL DoSystemStateRegistration; // // PoRegisterSystemState and PoUnregisterSystemState are not available on WDM 1.0 (Win98 and Win98SE) DoSystemStateRegistration = IoIsWdmVersionAvailable( 0x01, 0x10 ); // adjust the active pin count if( Increment ) { ActivePinCount = InterlockedIncrement( PLONG(&DeviceContext->ActivePinCount) ); //#if COMPILED_FOR_WDM110 if ( 1 == ActivePinCount ) { // register the system state as busy DeviceContext->SystemStateHandle = PoRegisterSystemState( DeviceContext->SystemStateHandle, ES_SYSTEM_REQUIRED | ES_CONTINUOUS ); } //#endif // COMPILED_FOR_WDM110 } else { ActivePinCount = InterlockedDecrement( PLONG(&DeviceContext->ActivePinCount) ); //#if COMPILED_FOR_WDM110 if( 0 == ActivePinCount ) { PoUnregisterSystemState( DeviceContext->SystemStateHandle ); DeviceContext->SystemStateHandle = NULL; } //#endif // COMPILED_FOR_WDM110 } _DbgPrintF(DEBUGLVL_VERBOSE,("UpdateActivePinCount (%d)",ActivePinCount)); // _DbgPrintF(DEBUGLVL_POWER,("UpdateActivePinCount (%d)",ActivePinCount)); return ntStatus; } /***************************************************************************** * GetIdleInfoFromRegistry() ***************************************************************************** * */ NTSTATUS GetIdleInfoFromRegistry ( IN PDEVICE_CONTEXT DeviceContext, OUT PULONG ConservationIdleTime, OUT PULONG PerformanceIdleTime, OUT PDEVICE_POWER_STATE IdleDeviceState ) { PAGED_CODE(); ASSERT(DeviceContext); ASSERT(ConservationIdleTime); ASSERT(PerformanceIdleTime); ASSERT(IdleDeviceState); NTSTATUS ntStatus; HANDLE DriverRegistryKey; HANDLE PowerSettingsKey; // store default values in return parms *ConservationIdleTime = DEFAULT_CONSERVATION_IDLE_TIME; *PerformanceIdleTime = DEFAULT_PERFORMANCE_IDLE_TIME; *IdleDeviceState = DEFAULT_IDLE_DEVICE_POWER_STATE; // open the driver registry key ntStatus = IoOpenDeviceRegistryKey( DeviceContext->PhysicalDeviceObject, PLUGPLAY_REGKEY_DRIVER, KEY_READ, &DriverRegistryKey ); if(NT_SUCCESS(ntStatus)) { OBJECT_ATTRIBUTES PowerSettingsAttributes; UNICODE_STRING PowerSettingsKeyName; // init the power settings key name RtlInitUnicodeString( &PowerSettingsKeyName, L"PowerSettings" ); // init the power settings key object attributes InitializeObjectAttributes( &PowerSettingsAttributes, &PowerSettingsKeyName, OBJ_CASE_INSENSITIVE, DriverRegistryKey, NULL ); // open the power settings key ntStatus = ZwOpenKey( &PowerSettingsKey, KEY_READ, &PowerSettingsAttributes ); if(NT_SUCCESS(ntStatus)) { UNICODE_STRING ConservationKey,PerformanceKey,IdleStateKey; ULONG BytesReturned; // init the key names RtlInitUnicodeString( &ConservationKey, L"ConservationIdleTime" ); RtlInitUnicodeString( &PerformanceKey, L"PerformanceIdleTime" ); RtlInitUnicodeString( &IdleStateKey, L"IdlePowerState" ); // allocate a buffer to hold the query PVOID KeyData = ExAllocatePoolWithTag(PagedPool, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD), 'dKcP' ); // 'PcKd' if( NULL != KeyData ) { // get the conservation idle time ntStatus = ZwQueryValueKey( PowerSettingsKey, &ConservationKey, KeyValuePartialInformation, KeyData, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD), &BytesReturned ); if(NT_SUCCESS(ntStatus)) { PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = PKEY_VALUE_PARTIAL_INFORMATION(KeyData); if(PartialInfo->DataLength == sizeof(DWORD)) { // set the return value *ConservationIdleTime = *(PDWORD(PartialInfo->Data)); } } // get the performance idle time ntStatus = ZwQueryValueKey( PowerSettingsKey, &PerformanceKey, KeyValuePartialInformation, KeyData, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD), &BytesReturned ); if(NT_SUCCESS(ntStatus)) { PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = PKEY_VALUE_PARTIAL_INFORMATION(KeyData); if(PartialInfo->DataLength == sizeof(DWORD)) { // set the return value *PerformanceIdleTime = *(PDWORD(PartialInfo->Data)); } } // get the device idle state ntStatus = ZwQueryValueKey( PowerSettingsKey, &IdleStateKey, KeyValuePartialInformation, KeyData, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD), &BytesReturned ); if(NT_SUCCESS(ntStatus)) { PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = PKEY_VALUE_PARTIAL_INFORMATION(KeyData); if(PartialInfo->DataLength == sizeof(DWORD)) { // determine the return value switch( *(PDWORD(PartialInfo->Data)) ) { case 3: *IdleDeviceState = PowerDeviceD3; break; case 2: *IdleDeviceState = PowerDeviceD2; break; case 1: *IdleDeviceState = PowerDeviceD1; break; default: *IdleDeviceState = PowerDeviceD0; break; } } } // free the key info buffer ExFreePool( KeyData ); } // close the power settings key ZwClose( PowerSettingsKey ); } // close the driver registry key ZwClose( DriverRegistryKey ); } // always succeed since we return either the registry value(s) or the defaults return STATUS_SUCCESS; } /***************************************************************************** * PcRequestNewPowerState() ***************************************************************************** * This routine is used to request a new power state for the device. It is * normally used internally by portcls but is also exported to adapters so * that the adapters can also request power state changes. */ PORTCLASSAPI NTSTATUS NTAPI PcRequestNewPowerState ( IN PDEVICE_OBJECT pDeviceObject, IN DEVICE_POWER_STATE RequestedNewState ) { PAGED_CODE(); ASSERT(pDeviceObject); _DbgPrintF(DEBUGLVL_POWER,("PcRequestNewPowerState")); // // Validate Parameters. // if (NULL == pDeviceObject) { _DbgPrintF(DEBUGLVL_TERSE, ("PcRequestNewPowerState : Invalid Parameter")); return STATUS_INVALID_PARAMETER; } PDEVICE_CONTEXT pDeviceContext = PDEVICE_CONTEXT(pDeviceObject->DeviceExtension); ASSERT(pDeviceContext); NTSTATUS ntStatus = STATUS_SUCCESS; // // Validate DeviceContext. // if (NULL == pDeviceContext) { _DbgPrintF(DEBUGLVL_TERSE, ("PcRequestNewPowerState : Invalid DeviceContext")); return STATUS_INVALID_PARAMETER; } // check if this is actually a state change if( RequestedNewState != pDeviceContext->CurrentDeviceState ) { POWER_STATE newPowerState; POWER_IRP_CONTEXT PowerIrpContext; KEVENT SyncEvent; // prepare the requested state newPowerState.DeviceState = RequestedNewState; // setup the sync event and the completion routine context KeInitializeEvent( &SyncEvent, SynchronizationEvent, FALSE ); PowerIrpContext.PowerSyncEvent = &SyncEvent; #if DBG PowerIrpContext.Status = STATUS_PENDING; #endif // DBG PowerIrpContext.PendingSystemPowerIrp = NULL; PowerIrpContext.DeviceContext = NULL; // Set the new power state ntStatus = PoRequestPowerIrp( pDeviceContext->PhysicalDeviceObject, IRP_MN_SET_POWER, newPowerState, PowerIrpCompletionRoutine, &PowerIrpContext, NULL ); // Did this get allocated and sent?? // if( NT_SUCCESS(ntStatus) ) { // Wait for the completion event KeWaitForSingleObject( &SyncEvent, Suspended, KernelMode, FALSE, NULL ); ntStatus = PowerIrpContext.Status; } } return ntStatus; } /***************************************************************************** * CheckCurrentPowerState() ***************************************************************************** * This routine resets the idle timer and checks to see if the device is * current in the D0 (full power) state. If it isn't, it requests that the * device power up to D0. */ NTSTATUS CheckCurrentPowerState ( IN PDEVICE_OBJECT pDeviceObject ) { PAGED_CODE(); ASSERT(pDeviceObject); PDEVICE_CONTEXT pDeviceContext = PDEVICE_CONTEXT(pDeviceObject->DeviceExtension); NTSTATUS ntStatus = STATUS_SUCCESS; // reset the idle timer if( pDeviceContext->IdleTimer ) { PoSetDeviceBusy( pDeviceContext->IdleTimer ); } // check if we're in PowerDeviceD0 if( pDeviceContext->CurrentDeviceState != PowerDeviceD0 ) { ntStatus = STATUS_DEVICE_NOT_READY; } return ntStatus; } #pragma code_seg()