📄 power.cpp
字号:
if (pdx->devpower > pdx->devcaps.DeviceWake
|| pdx->syspower > pdx->devcaps.SystemWake
|| pdx->devpower != PowerDeviceD0)
{
KdPrint(("%s - Deferring WAIT_WAKE due to state or capabilities\n", pdx->DebugName));
return STATUS_INVALID_DEVICE_REQUEST;
}
if (InterlockedExchange(&pdx->wwoutstanding, 1))
break; // IRP already pending
pdx->wwcancelled = 0;
POWER_STATE junk;
junk.SystemState = pdx->devcaps.SystemWake; // lowest state from which this IRP will wake system
// I have seen systems where SystemState has a ridiculous value, such that we have to
// ask for S0 or else it doesn't work...
if (junk.SystemState > PowerSystemHibernate)
{ // correct bogus value
KdPrint(("%s - Requesting WAIT_WAKE with S0 instead of bogus S%d from capabilities\n", pdx->DebugName, junk.SystemState - 1));
junk.SystemState = PowerSystemWorking;
} // correct bogus value
KdPrint(("%s - Issuing WAIT_WAKE\n", pdx->DebugName));
status = PoRequestPowerIrp(pdx->Pdo, IRP_MN_WAIT_WAKE, junk, (PREQUEST_POWER_COMPLETE) WaitWakeCallback, pdx, &pdx->WaitWakeIrp);
if (!NT_SUCCESS(status))
{ // error starting wakeup IRP
KdPrint(("%s - PoRequestPowerIrp(IRP_MN_WAIT_WAIT) failed - %X\n", pdx->DebugName, status));
pdx->WakeupEnabled = FALSE;
pdx->wwoutstanding = 0;
break;
} // error starting wakeup IRP
status = STATUS_SUCCESS; // especially *not* STATUS_PENDING!
break;
} // wakeup is enabled
// Note fall-through to CanceWaitWake here!!
} // ManageWaitWake
case CancelWaitWake:
{ // CancelWaitWake
// If the following exchange operation returns NULL, WaitWakeComplete has run and
// the IRP may no longer exist.
PIRP Irp = (PIRP) InterlockedExchangePointer((PVOID*) &pdx->WaitWakeIrp, NULL);
if (Irp)
{ // cancel pending IRP
// We need to be sure that IoCancelIrp and any cancel routine it may
// call finish before the Power Manager calls IoFreeIrp to get rid
// of the WAIT_WAKE IRP. The call to IoFreeIrp can't happen sooner
// than the IRP gets all the way through IoCompleteRequest, however, so we establish
// an interlock such that whichever of us and our completion routine
// sees the IRP last allows it to complete.
IoCancelIrp(Irp);
KdPrint(("%s - IRP_MN_WAIT_WAKE cancelled\n", pdx->DebugName));
if (InterlockedExchange(&pdx->wwcancelled, 1))
IoCompleteRequest(Irp, IO_NO_INCREMENT); // pickup with aborted completion
} // cancel pending IRP
break;
} // CancelWaitWake
default:
ASSERT(FALSE);
status = STATUS_INVALID_DEVICE_REQUEST;
break;
} // perform requested function
return status;
} // GenericWakeupControl
///////////////////////////////////////////////////////////////////////////////
GENERICAPI NTSTATUS GENERIC_EXPORT GenericWakeupFromIdle(PGENERIC_EXTENSION pdx, BOOLEAN wait /* = FALSE */)
{ // GenericWakeupFromIdle
if (pdx->devpower == PowerDeviceD0)
return STATUS_SUCCESS;
NTSTATUS status = SendDeviceSetPower(pdx, PowerDeviceD0, wait);
if (status == STATUS_PENDING)
status = STATUS_SUCCESS;
return status;
} // GenericWakeupFromIdle
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
VOID AdjustSpecialFileCounter(PGENERIC_EXTENSION pdx, BOOLEAN inpath, PLONG counter)
{ // AdjustSpecialFileCounter
// Increment or decrement the appopriate special-file usage counter. Note
// that IoAdjustPagingPathCount (recommended for this use in the DDK doc) is
// just a macro that does exactly this...
if (inpath)
InterlockedIncrement(counter);
else
InterlockedDecrement(counter);
// Enable or disable idle detection, as appropriate. Alter the not-disableable
// status flag as well (SP-7, ver 1.6)
LONG totcount = pdx->ndumpfiles + pdx->nhibernatefiles + pdx->npagefiles;
if (totcount == 0)
{ // resume idle detection
if (pdx->cto && pdx->pto)
pdx->idlecount = PoRegisterDeviceForIdleDetection(pdx->Pdo, pdx->cto, pdx->pto, pdx->idlestate);
GenericSetDeviceState(pdx, 0);
} // resume idle detection
else if (inpath && totcount == 1)
{ // disable idle detection
pdx->idlecount = PoRegisterDeviceForIdleDetection(pdx->Pdo, 0, 0, PowerDeviceD0);
GenericSetDeviceState(pdx, PNP_DEVICE_NOT_DISABLEABLE);
} // disable idle detection
} // AdjustSpecialFileCounter
///////////////////////////////////////////////////////////////////////////////
NTSTATUS DefaultPowerHandler(PGENERIC_EXTENSION pdx, PIRP Irp)
{ // DefaultPowerHandler
PoStartNextPowerIrp(Irp); // must be done while we own the IRP
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(pdx->LowerDeviceObject, Irp);
} // DefaultPowerHandler
///////////////////////////////////////////////////////////////////////////////
// GetLowestDevicePowerState returns the lowest device power state that is
// consistent with a given system power state and the current wakeup state of
// the device.
DEVICE_POWER_STATE GetLowestDevicePowerState(PGENERIC_EXTENSION pdx, SYSTEM_POWER_STATE sysstate)
{ // GetLowestDevicePowerState
DEVICE_POWER_STATE maxstate = pdx->devcaps.DeviceState[sysstate];
DEVICE_POWER_STATE minstate = PowerDeviceD3;
if (pdx->WakeupEnabled && sysstate <= pdx->devcaps.SystemWake)
minstate = pdx->devcaps.DeviceWake; // select state that will preserve wakeup ability
DEVICE_POWER_STATE dstate = minstate > maxstate ? minstate : maxstate;
// Allow minidriver to pick the new state, subject to the minimum and maximum
// already established.
if (pdx->GetDevicePowerState)
{ // let minidriver pick state
dstate = (*pdx->GetDevicePowerState)(pdx->DeviceObject, sysstate, dstate);
if (dstate < maxstate)
dstate = maxstate;
else if (dstate > minstate)
dstate = minstate;
} // let minidriver pick state
return dstate;
} // GetLowestDevicePowerState
///////////////////////////////////////////////////////////////////////////////
NTSTATUS GetPowerInfoFromRegistry(PGENERIC_EXTENSION pdx, PPOWERINFO pip)
{ // GetPowerInfoFromRegistry
HANDLE hkey;
NTSTATUS status = IoOpenDeviceRegistryKey(pdx->Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_ALL_ACCESS, &hkey);
if (!NT_SUCCESS(status))
{
KdPrint((DRIVERNAME " - Error %X trying to open registry key\n", status));
return status;
}
UNICODE_STRING valname;
RtlInitUnicodeString(&valname, L"PowerSettings");
ULONG size = 0;
UCHAR junk;
status = ZwQueryValueKey(hkey, &valname, KeyValuePartialInformation, &junk, 0, &size);
if (status != STATUS_OBJECT_NAME_NOT_FOUND && size)
{ // value exists
PKEY_VALUE_PARTIAL_INFORMATION vpip = (PKEY_VALUE_PARTIAL_INFORMATION) ExAllocatePool(PagedPool, size);
status = ZwQueryValueKey(hkey, &valname, KeyValuePartialInformation, vpip, size, &size);
if (NT_SUCCESS(status))
{ // copy value
if (vpip->DataLength < 2 * sizeof(ULONG))
{
KdPrint(("%s - Ignoring PowerSettings value with strange size %d\n", pdx->DebugName, vpip->DataLength));
status = STATUS_UNSUCCESSFUL;
}
else
RtlCopyBytes(pip, vpip->Data, 2 * sizeof(ULONG));
} // copy value
else
KdPrint((DRIVERNAME " - ZwQueryValueKey failed - %X\n", status));
ExFreePool(vpip);
} // value exists
ZwClose(hkey);
return status;
} // GetPowerInfoFromRegistry
///////////////////////////////////////////////////////////////////////////////
NTSTATUS ImplementPowerPolicy(PGENERIC_EXTENSION pdx, PPOWERINFO pip)
{ // ImplementPowerPolicy
if (pdx->Flags & GENERIC_IDLE_DETECT)
GenericRegisterForIdleDetection(pdx,
pip->ulConservation, pip->ulPerformance, PowerDeviceD3);
GenericWakeupControl(pdx, pip->bWakeup ? EnableWakeup : DisableWakeup);
return STATUS_SUCCESS;
} // ImplementPowerPolicy
///////////////////////////////////////////////////////////////////////////////
BOOLEAN OkayToRemove(PGENERIC_EXTENSION pdx)
{ // OkayToRemove
if (pdx->ndumpfiles || pdx->nhibernatefiles || pdx->npagefiles)
return FALSE;
if (!pdx->OkayToRemove)
return TRUE;
return (*pdx->OkayToRemove)(pdx->DeviceObject);
} // OkayToRemove
///////////////////////////////////////////////////////////////////////////////
BOOLEAN OkayToStop(PGENERIC_EXTENSION pdx)
{ // OkayToStop
if (pdx->ndumpfiles || pdx->nhibernatefiles || pdx->npagefiles)
return FALSE;
if (!pdx->OkayToStop)
return TRUE;
return (*pdx->OkayToStop)(pdx->DeviceObject);
} // OkayToStop
///////////////////////////////////////////////////////////////////////////////
struct SDSP_CONTEXT {
PKEVENT pev; // event to signal when request complete
NTSTATUS status; // ending status
};
#pragma LOCKEDCODE
VOID SendDeviceSetPowerComplete(PDEVICE_OBJECT junk, UCHAR fcn, POWER_STATE state, SDSP_CONTEXT* context, PIO_STATUS_BLOCK pstatus)
{ // SendDeviceSetPowerComplete
context->status = pstatus->Status;
KeSetEvent(context->pev, EVENT_INCREMENT, FALSE);
} // SendDeviceSetPowerComplete
#pragma PAGEDCODE
NTSTATUS SendDeviceSetPower(PGENERIC_EXTENSION pdx, DEVICE_POWER_STATE devpower, BOOLEAN wait /* = FALSE */)
{ // SendDeviceSetPower
POWER_STATE state;
state.DeviceState = devpower;
NTSTATUS status;
if (wait)
{ // synchronous operation
KEVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
SDSP_CONTEXT context = {&event};
status = PoRequestPowerIrp(pdx->Pdo, IRP_MN_SET_POWER, state,
(PREQUEST_POWER_COMPLETE) SendDeviceSetPowerComplete, &context, NULL);
if (status == STATUS_PENDING)
{
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = context.status;
}
} // synchronous operation
else
status = PoRequestPowerIrp(pdx->Pdo, IRP_MN_SET_POWER, state, NULL, NULL, NULL);
return status;
} // SendDeviceSetPower
///////////////////////////////////////////////////////////////////////////////
#pragma LOCKEDCODE
VOID WaitWakeCallback(PDEVICE_OBJECT junk, UCHAR MinorFunction, POWER_STATE state,
PGENERIC_EXTENSION pdx, PIO_STATUS_BLOCK pstatus)
{ // WaitWakeCallback
// Indicate that wait-wake is no longer outstanding
pdx->wwoutstanding = 0;
if (!NT_SUCCESS(pstatus->Status))
{ // IRP failed
KdPrint(("%s - IRP_MN_WAIT_WAKE failed - %X\n", pdx->DebugName, pstatus->Status));
return;
} // IRP failed
// Wakeup our own device. This is necessary if the device signalled a wakeup while
// the system was already in S0 because, in this case, we won't otherwise get
// an IRP to power ourselves on.
SendDeviceSetPower(pdx, PowerDeviceD0, FALSE);
} // WaitWakeCallback
///////////////////////////////////////////////////////////////////////////////
#pragma LOCKEDCODE
NTSTATUS WaitWakeCompletionRoutine(PDEVICE_OBJECT junk, PIRP Irp, PGENERIC_EXTENSION pdx)
{ // WaitWakeCompletionRoutine
if (Irp->PendingReturned)
IoMarkIrpPending(Irp);
// Nullify our WaitWakeIrp pointer to prevent anyone from dereferncing
// the pointer once the IRP gets released as part of the completion process.
// If it's already NULL, someone has gotten into CancelWaitWake in GenericWakeupControl,
// meaning we have to avoid a race with IoCancelIrp.
if (InterlockedExchangePointer((PVOID*) &pdx->WaitWakeIrp, NULL))
return STATUS_SUCCESS; // we got here before anyone tried to cancel the IRP
// CancelWaitWake has run. If wwcancelled is nonzero, so has the call to
// IoCancelIrp and we can allow the IRP to complete. If wwcancelled is still zero
// (left over from when we launched the IRP), IoCancelIrp hasn't been called yet
// and we want to interrupt the completion process.
if (InterlockedExchange(&pdx->wwcancelled, 1))
return STATUS_SUCCESS; // allow it to complete
return STATUS_MORE_PROCESSING_REQUIRED; // let CancelWakeWait complete it
} // WaitWakeCompletionRoutine
///////////////////////////////////////////////////////////////////////////////
#pragma PAGEDCODE
NTSTATUS WritePowerInfoToRegistry(PGENERIC_EXTENSION pdx, PPOWERINFO pip)
{ // WritePowerInfoToRegistry
HANDLE hkey;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -