windows-nt/Source/XPSP1/NT/drivers/storage/ide/pciidex/power.c
2020-09-26 16:20:57 +08:00

826 lines
23 KiB
C

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: power.c
//
//--------------------------------------------------------------------------
#include "pciidex.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PciIdeIssueSetPowerState)
#pragma alloc_text(NONPAGE, PciIdePowerCompletionRoutine)
#pragma alloc_text(NONPAGE, PciIdeSetPdoPowerState)
#pragma alloc_text(NONPAGE, PciIdeSetFdoPowerState)
#pragma alloc_text(NONPAGE, FdoContingentPowerCompletionRoutine)
#pragma alloc_text(NONPAGE, FdoPowerCompletionRoutine)
#pragma alloc_text(NONPAGE, FdoChildReportPowerDown)
#pragma alloc_text(NONPAGE, FdoChildRequestPowerUp)
#pragma alloc_text(NONPAGE, FdoChildRequestPowerUpCompletionRoutine)
#endif // ALLOC_PRAGMA
NTSTATUS
PciIdeIssueSetPowerState (
IN PCTRLFDO_EXTENSION FdoExtension,
IN POWER_STATE_TYPE Type,
IN POWER_STATE State,
IN BOOLEAN Sync
)
{
PIRP irp = NULL;
PIO_STACK_LOCATION irpStack;
SET_POWER_STATE_CONTEXT context;
NTSTATUS status;
CCHAR stackSize;
PAGED_CODE();
if (Sync) {
KeInitializeEvent(
&context.Event,
NotificationEvent,
FALSE
);
}
stackSize = (CCHAR) (FdoExtension->DeviceObject->StackSize + 1);
irp = IoAllocateIrp(
stackSize,
FALSE
);
if (irp == NULL) {
status = STATUS_NO_MEMORY;
goto GetOut;
}
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->MajorFunction = IRP_MJ_POWER;
irpStack->MinorFunction = IRP_MN_SET_POWER;
irpStack->Parameters.Power.SystemContext = 0;
irpStack->Parameters.Power.Type = Type;
irpStack->Parameters.Power.State = State;
IoSetCompletionRoutine(irp,
PciIdePowerCompletionRoutine,
Sync ? &context : NULL,
TRUE,
TRUE,
TRUE);
status = PoCallDriver(FdoExtension->DeviceObject, irp);
//
//Wait for the completion routine. It will be called anyway.
//
// if ((status == STATUS_PENDING) && (Sync)) {
if (Sync) {
KeWaitForSingleObject(&context.Event,
Executive,
KernelMode,
FALSE,
NULL);
status = context.Status;
}
GetOut:
return status;
} // PciIdeIssueSetPowerState
NTSTATUS
PciIdePowerCompletionRoutine (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PSET_POWER_STATE_CONTEXT context = Context;
if (context) {
context->Status = Irp->IoStatus.Status;
KeSetEvent(
&context->Event,
EVENT_INCREMENT,
FALSE
);
}
IoFreeIrp (Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
} // PciIdePowerCompletionRoutine
NTSTATUS
PciIdeXQueryPowerState (
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
{
PDEVICE_EXTENSION_HEADER deviceExtensionHeader;
NTSTATUS status;
PIO_STACK_LOCATION irpStack;
deviceExtensionHeader = DeviceObject->DeviceExtension;
#if defined (DONT_POWER_DOWN_PAGING_DEVICE)
irpStack = IoGetCurrentIrpStackLocation (Irp);
if (!deviceExtensionHeader->CrashDumpPathCount ||
((irpStack->Parameters.Power.Type == SystemPowerState) &&
(irpStack->Parameters.Power.State.SystemState == PowerSystemWorking)) ||
((irpStack->Parameters.Power.Type == DevicePowerState) &&
(irpStack->Parameters.Power.State.SystemState == PowerDeviceD0))) {
status = STATUS_SUCCESS;
} else {
status = STATUS_DEVICE_POWER_FAILURE;
}
#else
status = STATUS_SUCCESS;
#endif // DONT_POWER_DOWN_PAGING_DEVICE
Irp->IoStatus.Status = status;
PoStartNextPowerIrp (Irp);
if (deviceExtensionHeader->AttacheeDeviceObject != NULL) {
IoCopyCurrentIrpStackLocationToNext (Irp);
status = PoCallDriver (deviceExtensionHeader->AttacheeDeviceObject, Irp);
}
else {
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
return status;
}
NTSTATUS
PciIdeSetPdoPowerState (
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
{
PIO_STACK_LOCATION irpStack;
PCHANPDO_EXTENSION pdoExtension;
NTSTATUS status;
pdoExtension = ChannelGetPdoExtension(DeviceObject);
if (pdoExtension == NULL) {
status = STATUS_NO_SUCH_DEVICE;
} else {
irpStack = IoGetCurrentIrpStackLocation (Irp);
status = STATUS_SUCCESS;
if (irpStack->Parameters.Power.Type == SystemPowerState) {
if (pdoExtension->SystemPowerState != irpStack->Parameters.Power.State.SystemState) {
POWER_STATE powerState;
pdoExtension->SystemPowerState = irpStack->Parameters.Power.State.SystemState;
DebugPrint ((1, "PciIdeX: New Pdo 0x%x system power state 0x%x\n", pdoExtension->ChannelNumber, irpStack->Parameters.Power.State.SystemState));
}
} else if (irpStack->Parameters.Power.Type == DevicePowerState) {
if (pdoExtension->DevicePowerState != irpStack->Parameters.Power.State.DeviceState) {
//
// checking old device state
//
if (pdoExtension->DevicePowerState == PowerDeviceD3) {
//
// waking up
//
IoMarkIrpPending(Irp);
Irp->IoStatus.Information = irpStack->Parameters.Power.State.DeviceState;
status = FdoChildRequestPowerUp (
pdoExtension->ParentDeviceExtension,
pdoExtension,
Irp
);
ASSERT (NT_SUCCESS(status));
status = STATUS_PENDING;
} else {
if (pdoExtension->DevicePowerState == PowerDeviceD0) {
//
// getting out of D0 state, better call PoSetPowerState now
//
PoSetPowerState (
DeviceObject,
DevicePowerState,
irpStack->Parameters.Power.State
);
}
DebugPrint ((1, "PciIdeX: New Pdo 0x%x device power state 0x%x\n", pdoExtension->ChannelNumber, irpStack->Parameters.Power.State.DeviceState));
pdoExtension->DevicePowerState = irpStack->Parameters.Power.State.DeviceState;
if (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD3) {
//
// tell parent that we just fell to sleep
//
FdoChildReportPowerDown (
pdoExtension->ParentDeviceExtension,
pdoExtension
);
}
}
}
} else {
ASSERT (FALSE);
status = STATUS_NOT_IMPLEMENTED;
}
}
if (status != STATUS_PENDING) {
if (NT_SUCCESS(status)) {
Irp->IoStatus.Information = irpStack->Parameters.Power.State.SystemState;
}
Irp->IoStatus.Status = status;
PoStartNextPowerIrp (Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
return status;
} // PciIdeSetPdoPowerState
NTSTATUS
PciIdeSetFdoPowerState (
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
{
NTSTATUS status = STATUS_SUCCESS;
PCTRLFDO_EXTENSION fdoExtension;
PIO_STACK_LOCATION irpStack;
PFDO_POWER_CONTEXT context;
BOOLEAN noCompletionRoutine;
BOOLEAN systemPowerContext = FALSE;
BOOLEAN devicePowerContext = FALSE;
fdoExtension = DeviceObject->DeviceExtension;
irpStack = IoGetCurrentIrpStackLocation (Irp);
//context = ExAllocatePool (NonPagedPool, sizeof(FDO_POWER_CONTEXT));
//
// We need two pre-alloced context structures. This is because a system power irp
// would result in a device power irp to be issued before the former is completed.
//
if (irpStack->Parameters.Power.Type == SystemPowerState) {
ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[0]), 1, 0) == 0);
context = &(fdoExtension->FdoPowerContext[0]);
systemPowerContext = TRUE;
} else {
ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[1]), 1, 0) == 0);
context = &(fdoExtension->FdoPowerContext[1]);
devicePowerContext = TRUE;
}
if (context == NULL) {
ASSERT(context);
status = STATUS_NO_MEMORY;
} else {
RtlZeroMemory (context, sizeof(FDO_POWER_CONTEXT));
//irpStack = IoGetCurrentIrpStackLocation (Irp);
context->OriginalPowerIrp = Irp;
context->newPowerType = irpStack->Parameters.Power.Type;
context->newPowerState = irpStack->Parameters.Power.State;
noCompletionRoutine = FALSE;
if (irpStack->Parameters.Power.Type == SystemPowerState) {
if (fdoExtension->SystemPowerState != irpStack->Parameters.Power.State.SystemState) {
POWER_STATE powerState;
BOOLEAN requestPowerState = FALSE;
if ((irpStack->Parameters.Power.State.SystemState == PowerSystemShutdown) &&
(irpStack->Parameters.Power.ShutdownType == PowerActionShutdownReset)) {
//
// spin up for BIOS POST
//
requestPowerState = TRUE;
powerState.DeviceState = PowerDeviceD0;
} else if (fdoExtension->SystemPowerState == PowerSystemWorking) {
//
// we are getting out of working state...power down
//
requestPowerState = TRUE;
powerState.DeviceState = PowerDeviceD3;
}
if (requestPowerState) {
IoMarkIrpPending(Irp);
PoRequestPowerIrp (
fdoExtension->DeviceObject,
IRP_MN_SET_POWER,
powerState,
FdoContingentPowerCompletionRoutine,
context,
NULL
);
return STATUS_PENDING;
}
} else {
//
// We are already in the given state
//
noCompletionRoutine = TRUE;
}
} else if (irpStack->Parameters.Power.Type == DevicePowerState) {
if (fdoExtension->DevicePowerState != irpStack->Parameters.Power.State.DeviceState) {
if (fdoExtension->DevicePowerState == PowerDeviceD0) {
//
// getting out of D0 state, better call PoSetPowerState now
//
PoSetPowerState (
DeviceObject,
DevicePowerState,
irpStack->Parameters.Power.State
);
}
} else {
//
// We are already in the given state
//
noCompletionRoutine = TRUE;
}
} else {
ASSERT (FALSE);
status = STATUS_NOT_IMPLEMENTED;
}
}
if (NT_SUCCESS(status)) {
IoMarkIrpPending (Irp);
IoCopyCurrentIrpStackLocationToNext (Irp);
if (!noCompletionRoutine) {
IoSetCompletionRoutine(Irp,
FdoPowerCompletionRoutine,
context,
TRUE,
TRUE,
TRUE);
} else {
if (context) {
//ExFreePool (context);
if (systemPowerContext) {
ASSERT(devicePowerContext == FALSE);
ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[0]), 0, 1) == 1);
}
if (devicePowerContext) {
ASSERT(systemPowerContext == FALSE);
ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[1]), 0, 1) == 1);
}
}
PoStartNextPowerIrp (Irp);
}
PoCallDriver (fdoExtension->AttacheeDeviceObject, Irp);
return STATUS_PENDING;
} else {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
if (context) {
//ExFreePool (context);
if (systemPowerContext) {
ASSERT(devicePowerContext == FALSE);
ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[0]), 0, 1) == 1);
}
if (devicePowerContext) {
ASSERT(systemPowerContext == FALSE);
ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[1]), 0, 1) == 1);
}
}
PoStartNextPowerIrp (Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
} // PciIdeSetFdoPowerState
NTSTATUS
FdoContingentPowerCompletionRoutine (
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PVOID Context,
IN PIO_STATUS_BLOCK IoStatus
)
{
PFDO_POWER_CONTEXT context = Context;
PIRP irp;
PCTRLFDO_EXTENSION fdoExtension;
fdoExtension = DeviceObject->DeviceExtension;
irp = context->OriginalPowerIrp;
if (NT_SUCCESS(IoStatus->Status)) {
IoCopyCurrentIrpStackLocationToNext (irp);
IoSetCompletionRoutine(irp,
FdoPowerCompletionRoutine,
context,
TRUE,
TRUE,
TRUE);
PoCallDriver (fdoExtension->AttacheeDeviceObject, irp);
} else {
irp->IoStatus.Information = 0;
irp->IoStatus.Status = IoStatus->Status;
//ExFreePool (context);
ASSERT(context->newPowerType == SystemPowerState);
ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[0]), 0, 1) == 1);
PoStartNextPowerIrp (irp);
IoCompleteRequest(irp, IO_NO_INCREMENT);
}
return IoStatus->Status;
} // FdoContingentPowerCompletionRoutine
NTSTATUS
FdoPowerCompletionRoutine (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PFDO_POWER_CONTEXT context = Context;
BOOLEAN callPoSetPowerState = FALSE;
PCTRLFDO_EXTENSION fdoExtension;
POWER_STATE newPowerState;
POWER_STATE_TYPE newPowerType;
BOOLEAN unlocked = FALSE;
BOOLEAN moreProcessingRequired = FALSE;
NTSTATUS status;
fdoExtension = DeviceObject->DeviceExtension;
newPowerType = context->newPowerType;
newPowerState = context->newPowerState;
if (NT_SUCCESS(Irp->IoStatus.Status)) {
callPoSetPowerState = TRUE;
if (context->newPowerType == SystemPowerState) {
fdoExtension->SystemPowerState = context->newPowerState.SystemState;
if (fdoExtension->SystemPowerState == PowerSystemWorking) {
POWER_STATE powerState;
ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[0]), 0, 1) == 1);
unlocked = TRUE;
moreProcessingRequired = TRUE;
//
// initiate a D0 here to cause a re-enumuration
//
powerState.DeviceState = PowerDeviceD0;
status = PoRequestPowerIrp (
fdoExtension->DeviceObject,
IRP_MN_SET_POWER,
powerState,
FdoSystemPowerUpCompletionRoutine,
Irp,
NULL
);
ASSERT(status == STATUS_PENDING);
}
DebugPrint ((1, "PciIdeX: New Fdo system power state 0x%x\n", fdoExtension->SystemPowerState));
} else if (context->newPowerType == DevicePowerState) {
if (fdoExtension->DevicePowerState == PowerDeviceD0) {
//
// PoSetPowerState is called before we get out of D0
//
callPoSetPowerState = FALSE;
}
fdoExtension->DevicePowerState = context->newPowerState.DeviceState;
if (fdoExtension->DevicePowerState == PowerDeviceD0) {
//
// Re-enumerate the devices on the channel
//
EnablePCIBusMastering (fdoExtension);
IoInvalidateDeviceRelations (
fdoExtension->AttacheePdo,
BusRelations
);
}
DebugPrint ((1, "PciIdeX: New Fdo device power state 0x%x\n", fdoExtension->DevicePowerState));
}
if (callPoSetPowerState) {
PoSetPowerState (
DeviceObject,
newPowerType,
newPowerState
);
}
}
//ExFreePool (Context);
if (!unlocked) {
if (context->newPowerType == SystemPowerState) {
ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[0]), 0, 1) == 1);
} else {
ASSERT(InterlockedCompareExchange(&(fdoExtension->PowerContextLock[1]), 0, 1) == 1);
}
}
//
// wait for the device irp to complete
//
if (moreProcessingRequired) {
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// If pending has be returned for this irp then mark the current stack as
// pending.
//
// if (Irp->PendingReturned) {
// IoMarkIrpPending(Irp);
//}
PoStartNextPowerIrp (Irp);
return Irp->IoStatus.Status;
} // FdoPowerCompletionRoutine
VOID
FdoChildReportPowerDown (
IN PCTRLFDO_EXTENSION FdoExtension,
IN PCHANPDO_EXTENSION PdoExtension
)
{
POWER_STATE powerState;
ULONG numChildrenPowerUp;
ASSERT(FdoExtension->NumberOfChildrenPowerUp > 0);
numChildrenPowerUp = InterlockedDecrement(&FdoExtension->NumberOfChildrenPowerUp);
if (numChildrenPowerUp == 0) {
DebugPrint ((1, "PciIdeX FdoChildReportPowerDown: sleep fdo 0x%x\n", FdoExtension));
//
// All the children are powered down, we can now power down
// the parent (the controller)
//
powerState.DeviceState = PowerDeviceD3;
PoRequestPowerIrp (
FdoExtension->DeviceObject,
IRP_MN_SET_POWER,
powerState,
NULL,
NULL,
NULL
);
}
// else if (numChildrenPowerUp < 0) {
//
// should never happen. If it did, pretend it didn't
//
// ASSERT (FALSE);
// InterlockedExchange(&FdoExtension->NumberOfChildrenPowerUp, 0);
//}
return;
} // FdoChildReportPowerDown
NTSTATUS
FdoChildRequestPowerUp (
IN PCTRLFDO_EXTENSION FdoExtension,
IN PCHANPDO_EXTENSION PdoExtension,
IN PIRP ChildPowerIrp
)
{
NTSTATUS status;
POWER_STATE powerState;
ULONG numberOfChildrenPowerUp;
IO_STATUS_BLOCK IoStatus;
status = STATUS_SUCCESS;
numberOfChildrenPowerUp = InterlockedExchange (
&FdoExtension->NumberOfChildrenPowerUp,
FdoExtension->NumberOfChildrenPowerUp
);
if (numberOfChildrenPowerUp == 0) {
DebugPrint ((1, "PciIdeX FdoChildRequestPowerUp: wake up fdo 0x%x\n", FdoExtension));
//
// One of the children is coming out of sleep,
// we need to power up the parent (the controller)
//
powerState.DeviceState = PowerDeviceD0;
status = PoRequestPowerIrp (
FdoExtension->DeviceObject,
IRP_MN_SET_POWER,
powerState,
FdoChildRequestPowerUpCompletionRoutine,
ChildPowerIrp,
NULL
);
ASSERT (NT_SUCCESS(status));
status = STATUS_PENDING;
} else {
powerState.DeviceState = PowerDeviceD0;
IoStatus.Information = PowerDeviceD0;
IoStatus.Status = STATUS_SUCCESS;
FdoChildRequestPowerUpCompletionRoutine (
FdoExtension->DeviceObject,
IRP_MN_SET_POWER,
powerState,
ChildPowerIrp,
&IoStatus
);
status = STATUS_PENDING;
}
return status;
} // FdoChildRequestPowerUp
NTSTATUS
FdoChildRequestPowerUpCompletionRoutine (
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PVOID Context,
IN PIO_STATUS_BLOCK IoStatus
)
{
PIRP childPowerIrp = Context;
PCTRLFDO_EXTENSION fdoExtension;
PCHANPDO_EXTENSION pdoExtension;
PIO_STACK_LOCATION irpStack;
fdoExtension = DeviceObject->DeviceExtension;
if (NT_SUCCESS(IoStatus->Status)) {
ULONG numberOfChildrenPowerUp;
numberOfChildrenPowerUp = InterlockedIncrement (&fdoExtension->NumberOfChildrenPowerUp);
irpStack = IoGetCurrentIrpStackLocation (childPowerIrp);
pdoExtension = irpStack->DeviceObject->DeviceExtension;
pdoExtension->DevicePowerState = irpStack->Parameters.Power.State.DeviceState;
if (numberOfChildrenPowerUp > fdoExtension->NumberOfChildren) {
//
// should never happen. If it did, pretend it didn't
//
ASSERT (FALSE);
fdoExtension->NumberOfChildrenPowerUp = fdoExtension->NumberOfChildren;
}
}
childPowerIrp->IoStatus.Status = IoStatus->Status;
PoStartNextPowerIrp (childPowerIrp);
IoCompleteRequest(childPowerIrp, IO_NO_INCREMENT);
return IoStatus->Status;
} // FdoChildRequestPowerUpCompletionRoutine
NTSTATUS
FdoSystemPowerUpCompletionRoutine (
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PVOID Context,
IN PIO_STATUS_BLOCK IoStatus
)
{
PIRP irp = Context;
//
// start the next system power irp
//
PoStartNextPowerIrp (irp);
if (!NT_SUCCESS(IoStatus->Status)) {
irp->IoStatus.Status = IoStatus->Status;
}
IoCompleteRequest(irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}