1153 lines
36 KiB
C
1153 lines
36 KiB
C
/*++
|
|
|
|
Copyright (C) 1997-99 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pdopower.c
|
|
|
|
Abstract:
|
|
|
|
--*/
|
|
|
|
#include "ideport.h"
|
|
|
|
|
|
VOID
|
|
IdePowerCheckBusyCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIDE_POWER_CONTEXT Context,
|
|
IN NTSTATUS Status
|
|
);
|
|
|
|
NTSTATUS
|
|
DeviceQueryPowerState (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
PPDO_EXTENSION pdoExtension;
|
|
|
|
pdoExtension = RefPdoWithTag(
|
|
DeviceObject,
|
|
FALSE,
|
|
Irp
|
|
);
|
|
|
|
if (pdoExtension) {
|
|
|
|
#if defined (DONT_POWER_DOWN_PAGING_DEVICE)
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
if (!pdoExtension->CrashDumpPathCount ||
|
|
((irpStack->Parameters.Power.Type == SystemPowerState) &&
|
|
(irpStack->Parameters.Power.State.SystemState == PowerSystemWorking)) ||
|
|
((irpStack->Parameters.Power.Type == DevicePowerState) &&
|
|
(irpStack->Parameters.Power.State.SystemState == PowerDeviceD0))) {
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Irp->IoStatus.Status = STATUS_DEVICE_POWER_FAILURE;
|
|
}
|
|
#else
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
#endif // DONT_POWER_DOWN_PAGING_DEVICE
|
|
|
|
UnrefPdoWithTag (pdoExtension, Irp);
|
|
|
|
} else {
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
PoStartNextPowerIrp (Irp);
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
//
|
|
// Do not send this Irp down
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IdePortSetPdoPowerState (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PPDO_EXTENSION pdoExtension;
|
|
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
pdoExtension = DeviceObject->DeviceExtension;
|
|
|
|
DebugPrint ((DBG_POWER,
|
|
"0x%x target %d got power irp 0x%x\n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
Irp
|
|
));
|
|
|
|
|
|
DebugPrint ((DBG_POWER,
|
|
"IdePort: 0x%x device %d: current System Power State = 0x%x\n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
pdoExtension->SystemPowerState));
|
|
DebugPrint ((DBG_POWER,
|
|
"IdePort: 0x%x device %d: current Device Power State = 0x%x\n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
pdoExtension->DevicePowerState));
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
// if (!(pdoExtension->LuFlags & PD_LOGICAL_UNIT_POWER_OK)) {
|
|
//
|
|
// //
|
|
// // The device does support power management commands
|
|
// // just STATUS_SUCCESS everything. If ACPI is around,
|
|
// // it will power manage our device
|
|
// //
|
|
// status = STATUS_SUCCESS;
|
|
//
|
|
// } else
|
|
if (irpStack->Parameters.Power.Type == SystemPowerState) {
|
|
|
|
DebugPrint ((DBG_POWER, "IdePortSetPdoPowerState: 0x%x target %d got a SYSTEM power irp 0x%x for system state 0x%x \n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
Irp,
|
|
irpStack->Parameters.Power.State.SystemState));
|
|
|
|
ASSERT (pdoExtension->PendingSystemPowerIrp == NULL);
|
|
#if DBG
|
|
pdoExtension->PendingSystemPowerIrp = Irp;
|
|
ASSERT (pdoExtension->PendingSystemPowerIrp);
|
|
#endif // DBG
|
|
|
|
status = IdePortSetPdoSystemPowerState (DeviceObject, Irp);
|
|
|
|
} else if (irpStack->Parameters.Power.Type == DevicePowerState) {
|
|
|
|
DebugPrint ((DBG_POWER, "IdePortSetPdoPowerState: 0x%x target %d got a DEVICE power irp 0x%x for device state 0x%x \n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
Irp,
|
|
irpStack->Parameters.Power.State.DeviceState));
|
|
|
|
ASSERT (pdoExtension->PendingDevicePowerIrp == NULL);
|
|
#if DBG
|
|
pdoExtension->PendingDevicePowerIrp = Irp;
|
|
ASSERT (pdoExtension->PendingDevicePowerIrp);
|
|
#endif // DBG
|
|
|
|
status = IdePortSetPdoDevicePowerState (DeviceObject, Irp);
|
|
|
|
} else {
|
|
|
|
status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
if (status != STATUS_PENDING) {
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
IdePortPdoCompletePowerIrp (
|
|
DeviceObject,
|
|
Irp
|
|
);
|
|
}
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IdePortSetPdoSystemPowerState (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PPDO_EXTENSION pdoExtension;
|
|
SYSTEM_POWER_STATE newSystemState;
|
|
POWER_STATE powerState;
|
|
POWER_ACTION shutdownType;
|
|
|
|
pdoExtension = DeviceObject->DeviceExtension;
|
|
status = STATUS_SUCCESS;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
newSystemState = irpStack->Parameters.Power.State.SystemState;
|
|
shutdownType = irpStack->Parameters.Power.ShutdownType;
|
|
|
|
if (pdoExtension->SystemPowerState != newSystemState) {
|
|
|
|
//
|
|
// new system state request
|
|
//
|
|
|
|
if (pdoExtension->SystemPowerState == PowerSystemWorking) {
|
|
|
|
//
|
|
// Getting out of working state.
|
|
//
|
|
if ((newSystemState == PowerSystemShutdown) &&
|
|
(shutdownType == PowerActionShutdownReset)) {
|
|
|
|
//
|
|
// spin up for BIOS POST
|
|
//
|
|
powerState.DeviceState = PowerDeviceD0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// put the device to D3 and freeze the device queue
|
|
//
|
|
|
|
//
|
|
// Issue a D3 to top of my drive stack
|
|
//
|
|
powerState.DeviceState = PowerDeviceD3;
|
|
|
|
pdoExtension->PendingPowerDownSystemIrp = Irp;
|
|
}
|
|
|
|
status = PoRequestPowerIrp (
|
|
pdoExtension->DeviceObject,
|
|
IRP_MN_SET_POWER,
|
|
powerState,
|
|
IdePortPdoRequestPowerCompletionRoutine,
|
|
Irp,
|
|
NULL
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = STATUS_PENDING;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (newSystemState == PowerSystemHibernate) {
|
|
|
|
//
|
|
// we can't hibernate when we are in some sleep state
|
|
//
|
|
ASSERT (FALSE);
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
IdePortSetPdoDevicePowerState (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
{
|
|
PPDO_EXTENSION pdoExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
NTSTATUS status;
|
|
|
|
DEVICE_POWER_STATE newDeviceState;
|
|
POWER_ACTION shutdownType;
|
|
|
|
BOOLEAN issueIdeCommand;
|
|
IDEREGS ideReg;
|
|
|
|
PIDE_POWER_CONTEXT context;
|
|
BOOLEAN powerUpParent;
|
|
|
|
BOOLEAN noopPassThrough;
|
|
|
|
|
|
pdoExtension = DeviceObject->DeviceExtension;
|
|
status = STATUS_SUCCESS;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
newDeviceState = irpStack->Parameters.Power.State.DeviceState;
|
|
shutdownType = irpStack->Parameters.Power.ShutdownType;
|
|
|
|
powerUpParent = FALSE;
|
|
|
|
issueIdeCommand = FALSE;
|
|
RtlZeroMemory (&ideReg, sizeof(ideReg));
|
|
|
|
if (pdoExtension->DevicePowerState != newDeviceState) {
|
|
|
|
if (pdoExtension->DevicePowerState == PowerDeviceD0) {
|
|
|
|
POWER_STATE newPowerState;
|
|
|
|
newPowerState.DeviceState = newDeviceState;
|
|
|
|
//
|
|
// getting out of D0 state, better call PoSetPowerState now
|
|
// this gives the system a chance to flush before we
|
|
// get out of D0
|
|
//
|
|
PoSetPowerState (
|
|
pdoExtension->DeviceObject,
|
|
DevicePowerState,
|
|
newPowerState
|
|
);
|
|
}
|
|
|
|
if (pdoExtension->DevicePowerState < newDeviceState) {
|
|
|
|
KIRQL currentIrql;
|
|
|
|
//
|
|
// we are powering down, try to clean out the Lu device queue
|
|
//
|
|
KeAcquireSpinLock(&pdoExtension->ParentDeviceExtension->SpinLock, ¤tIrql);
|
|
|
|
pdoExtension->CurrentKey = 0;
|
|
|
|
KeReleaseSpinLock(&pdoExtension->ParentDeviceExtension->SpinLock, currentIrql);
|
|
}
|
|
|
|
|
|
if ((newDeviceState == PowerDeviceD0) ||
|
|
(newDeviceState == PowerDeviceD1)) {
|
|
|
|
//
|
|
// spinning up to D0 or D1...
|
|
//
|
|
DebugPrint ((DBG_POWER, "IdePort: Irp 0x%x to spin UP 0x%x %d...\n",
|
|
Irp,
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId));
|
|
|
|
if (pdoExtension->DevicePowerState == PowerDeviceD1) {
|
|
|
|
//
|
|
// D0-->D1
|
|
// can't do much here
|
|
|
|
DebugPrint ((DBG_POWER, "ATAPI: reqeust for PowerDeviceD1 to PowerDeviceD0\n"));
|
|
|
|
} else if ((pdoExtension->DevicePowerState == PowerDeviceD0) ||
|
|
(pdoExtension->DevicePowerState == PowerDeviceD2)) {
|
|
|
|
//
|
|
// D1-->D0 or
|
|
// D2-->D0 or D1
|
|
//
|
|
issueIdeCommand = TRUE;
|
|
if (pdoExtension->ScsiDeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) {
|
|
|
|
ideReg.bCommandReg = IDE_COMMAND_ATAPI_RESET;
|
|
ideReg.bReserved = ATA_PTFLAGS_URGENT;
|
|
|
|
} else {
|
|
|
|
ideReg.bCommandReg = IDE_COMMAND_IDLE_IMMEDIATE;
|
|
ideReg.bReserved = ATA_PTFLAGS_URGENT | ATA_PTFLAGS_STATUS_DRDY_REQUIRED;
|
|
}
|
|
|
|
} else {
|
|
|
|
PFDO_EXTENSION fdoExtension = pdoExtension->ParentDeviceExtension;
|
|
|
|
//
|
|
// D3-->D0 or D1
|
|
//
|
|
issueIdeCommand = TRUE;
|
|
ideReg.bReserved = ATA_PTFLAGS_BUS_RESET | ATA_PTFLAGS_URGENT;
|
|
|
|
//
|
|
// wait for busy to clear
|
|
//
|
|
if (fdoExtension->WaitOnPowerUp) {
|
|
ideReg.bSectorNumberReg = 3;
|
|
}
|
|
|
|
//
|
|
// we are coming out of deeeeep sleep, make sure our parent
|
|
// is awake (power up) before we can wake up
|
|
//
|
|
powerUpParent = TRUE;
|
|
}
|
|
|
|
} else if ((newDeviceState == PowerDeviceD2) ||
|
|
(newDeviceState == PowerDeviceD3)) {
|
|
|
|
//
|
|
// spinning down to D2 or D3...
|
|
//
|
|
DebugPrint ((DBG_POWER, "IdePort: Irp 0x%x to spin DOWN 0x%x %d...\n",
|
|
Irp,
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId));
|
|
|
|
if ((pdoExtension->DevicePowerState == PowerDeviceD0) ||
|
|
(pdoExtension->DevicePowerState == PowerDeviceD1) ||
|
|
(pdoExtension->DevicePowerState == PowerDeviceD2)) {
|
|
|
|
//
|
|
// going to D3
|
|
//
|
|
if ((pdoExtension->PdoState & PDOS_NO_POWER_DOWN) ||
|
|
(shutdownType == PowerActionHibernate)) {
|
|
|
|
//
|
|
// send an no-op command to block the queue
|
|
//
|
|
issueIdeCommand = TRUE;
|
|
ideReg.bReserved = ATA_PTFLAGS_NO_OP;
|
|
|
|
} else {
|
|
|
|
//
|
|
// spin down
|
|
//
|
|
issueIdeCommand = TRUE;
|
|
ideReg.bCommandReg = IDE_COMMAND_STANDBY_IMMEDIATE;
|
|
ideReg.bReserved = ATA_PTFLAGS_STATUS_DRDY_REQUIRED;
|
|
}
|
|
|
|
} else if (pdoExtension->DevicePowerState == PowerDeviceD3) {
|
|
|
|
//
|
|
// PowerDeviceD3 -> PowerDeviceD2
|
|
//
|
|
// need to do a bus reset (spin up) and issue IDE_COMMAND_STANDBY_IMMEDIATE
|
|
// (spin down). this will cause uncessary INRUSH current. bad
|
|
// idea. fail the request for now
|
|
|
|
DebugPrint ((DBG_POWER, "ATAPI: reqeust for PowerDeviceD3 to PowerDeviceD2\n"));
|
|
|
|
status = STATUS_INVALID_DEVICE_STATE;
|
|
|
|
} else {
|
|
|
|
status = STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
|
|
} else {
|
|
|
|
status = STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
}
|
|
/*************
|
|
else if ( pdoExtension->DevicePowerState == PowerDeviceD0) {
|
|
|
|
//
|
|
// Send a no-op so that it can drain the device queue
|
|
//
|
|
issueIdeCommand = TRUE;
|
|
ideReg.bSectorCountReg = 1;
|
|
ideReg.bReserved = ATA_PTFLAGS_NO_OP;
|
|
}
|
|
***************/
|
|
|
|
if (issueIdeCommand && NT_SUCCESS(status)) {
|
|
|
|
|
|
if ((pdoExtension->PdoState & PDOS_DEADMEAT) ||
|
|
(!(pdoExtension->PdoState & PDOS_STARTED))) {
|
|
|
|
DebugPrint ((DBG_ALWAYS,
|
|
"ATAPI: power irp 0x%x for not-yet-started or deadmeat device 0x%x\n",
|
|
Irp, DeviceObject));
|
|
|
|
//
|
|
// even the device may not be ready to be
|
|
// "power-managed", we still need to go
|
|
// through all the power code so that all
|
|
// the flags/states will be consistent
|
|
//
|
|
RtlZeroMemory (&ideReg, sizeof(ideReg));
|
|
ideReg.bReserved = ATA_PTFLAGS_NO_OP | ATA_PTFLAGS_URGENT;
|
|
}
|
|
|
|
//context = ExAllocatePool (NonPagedPool, sizeof(IDE_POWER_CONTEXT));
|
|
ASSERT(InterlockedCompareExchange(&(pdoExtension->PowerContextLock), 1, 0) == 0);
|
|
context = &(pdoExtension->PdoPowerContext);
|
|
|
|
if (context) {
|
|
|
|
context->PdoExtension = pdoExtension;
|
|
context->PowerIrp = Irp;
|
|
|
|
RtlZeroMemory (&context->AtaPassThroughData, sizeof(ATA_PASS_THROUGH));
|
|
RtlMoveMemory (&context->AtaPassThroughData.IdeReg, &ideReg, sizeof(ideReg));
|
|
|
|
} else {
|
|
|
|
status = STATUS_NO_MEMORY;
|
|
issueIdeCommand = FALSE;
|
|
}
|
|
}
|
|
|
|
if (issueIdeCommand && NT_SUCCESS(status)) {
|
|
|
|
if (powerUpParent) {
|
|
|
|
status = FdoChildRequestPowerUp (
|
|
pdoExtension->ParentDeviceExtension,
|
|
pdoExtension,
|
|
context
|
|
);
|
|
ASSERT (NT_SUCCESS(status));
|
|
|
|
//
|
|
// the pass through will be issued by FdoChildRequestPowerUp() callback
|
|
//
|
|
issueIdeCommand = FALSE;
|
|
status = STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
if (issueIdeCommand && NT_SUCCESS(status)) {
|
|
|
|
status = PdoRequestParentPowerUpCompletionRoutine (
|
|
context,
|
|
STATUS_SUCCESS
|
|
);
|
|
|
|
//
|
|
// this call will complete the power irp
|
|
// always return STATUS_PENDING so that out callee
|
|
// will not try to complete the same irp
|
|
//
|
|
status = STATUS_PENDING;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PdoRequestParentPowerUpCompletionRoutine (
|
|
PVOID Context,
|
|
NTSTATUS ParentPowerUpStatus
|
|
)
|
|
{
|
|
PIDE_POWER_CONTEXT context = Context;
|
|
NTSTATUS status;
|
|
|
|
if (NT_SUCCESS(ParentPowerUpStatus)) {
|
|
|
|
PIDEREGS ideReg;
|
|
PATA_PASS_THROUGH ataPassThrough;
|
|
|
|
DebugPrint ((DBG_POWER, "PdoRequestParentPowerUpCompletionRoutine: calling IssueAsyncAtaPassThrough for pdo 0x%x\n", context->PdoExtension));
|
|
|
|
//
|
|
// hack. We need to check if the device is busy before we issue
|
|
// the reset. Since there are no bits left in the bReserved
|
|
// register, we use the sectorCount register. It serves 2 purposes.
|
|
// If it is non zero (and reserved has NO_OP set) then we would
|
|
// perform a waitForBusy. It also indicates the time to wiat
|
|
// in seconds.
|
|
//
|
|
ataPassThrough = &context->AtaPassThroughData;
|
|
ideReg = &ataPassThrough->IdeReg;
|
|
|
|
if ((ideReg->bReserved & ATA_PTFLAGS_BUS_RESET) &&
|
|
(ideReg->bSectorNumberReg != 0)) {
|
|
|
|
//
|
|
// busy check
|
|
//
|
|
ideReg->bReserved = ATA_PTFLAGS_NO_OP | ATA_PTFLAGS_URGENT;
|
|
|
|
status = IssueAsyncAtaPassThroughSafe (
|
|
context->PdoExtension->ParentDeviceExtension,
|
|
context->PdoExtension,
|
|
&context->AtaPassThroughData,
|
|
FALSE,
|
|
IdePowerCheckBusyCompletion,
|
|
context,
|
|
TRUE,
|
|
DEFAULT_ATA_PASS_THROUGH_TIMEOUT,
|
|
FALSE
|
|
);
|
|
} else {
|
|
|
|
//
|
|
// parent woke up
|
|
//
|
|
status = IssueAsyncAtaPassThroughSafe (
|
|
context->PdoExtension->ParentDeviceExtension,
|
|
context->PdoExtension,
|
|
&context->AtaPassThroughData,
|
|
FALSE,
|
|
IdePowerPassThroughCompletion,
|
|
context,
|
|
TRUE,
|
|
DEFAULT_ATA_PASS_THROUGH_TIMEOUT,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
status = ParentPowerUpStatus;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
context->PowerIrp->IoStatus.Status = status;
|
|
|
|
IdePortPdoCompletePowerIrp (
|
|
context->PdoExtension->DeviceObject,
|
|
context->PowerIrp
|
|
);
|
|
|
|
ASSERT(InterlockedCompareExchange(&(context->PdoExtension->PowerContextLock), 0, 1) == 1);
|
|
//ExFreePool (context);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
IdePortPdoRequestPowerCompletionRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR MinorFunction,
|
|
IN POWER_STATE PowerState,
|
|
IN PVOID Context,
|
|
IN PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
{
|
|
PPDO_EXTENSION pdoExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PIRP irp = Context;
|
|
|
|
pdoExtension = (PPDO_EXTENSION) DeviceObject->DeviceExtension;
|
|
irp->IoStatus.Status = IoStatus->Status;
|
|
IdePortPdoCompletePowerIrp (
|
|
pdoExtension->DeviceObject,
|
|
irp
|
|
);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
IdePortPdoCompletePowerIrp (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PPDO_EXTENSION pdoExtension;
|
|
PFDO_EXTENSION fdoExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
BOOLEAN callPoSetPowerState;
|
|
KIRQL currentIrql;
|
|
NTSTATUS status;
|
|
POWER_ACTION shutdownType;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
pdoExtension = DeviceObject->DeviceExtension;
|
|
//shutdownType = irpStack->Parameters.Power.ShutdownType;
|
|
|
|
fdoExtension = pdoExtension->ParentDeviceExtension;
|
|
|
|
status = Irp->IoStatus.Status;
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
callPoSetPowerState = TRUE;
|
|
|
|
Irp->IoStatus.Information = irpStack->Parameters.Power.State.DeviceState;
|
|
|
|
if (irpStack->Parameters.Power.Type == SystemPowerState) {
|
|
|
|
if (pdoExtension->SystemPowerState != irpStack->Parameters.Power.State.SystemState) {
|
|
|
|
DebugPrint ((DBG_POWER,
|
|
"ATAPI: 0x%x target%d completing power Irp 0x%x with a new system state 0x%x\n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
Irp,
|
|
irpStack->Parameters.Power.State.SystemState));
|
|
|
|
if (pdoExtension->SystemPowerState == PowerSystemWorking) {
|
|
|
|
KeAcquireSpinLock(&fdoExtension->SpinLock, ¤tIrql);
|
|
|
|
//
|
|
// got out of S0, block the device queue
|
|
//
|
|
pdoExtension->PdoState |= PDOS_QUEUE_FROZEN_BY_SLEEPING_SYSTEM;
|
|
|
|
DebugPrint ((DBG_POWER,
|
|
"IdePort: 0x%x target %d is powered down with 0x%x items queued\n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
pdoExtension->NumberOfIrpQueued));
|
|
|
|
KeReleaseSpinLock(&fdoExtension->SpinLock, currentIrql);
|
|
}
|
|
|
|
if (irpStack->Parameters.Power.State.SystemState == PowerSystemWorking) {
|
|
|
|
KeAcquireSpinLock(&fdoExtension->SpinLock, ¤tIrql);
|
|
|
|
//
|
|
// got into S0, unblock and restart the device queue
|
|
//
|
|
CLRMASK (pdoExtension->PdoState, PDOS_QUEUE_FROZEN_BY_SLEEPING_SYSTEM);
|
|
|
|
DebugPrint ((DBG_POWER,
|
|
"IdePort: 0x%x target %d is power up with 0x%x items queued\n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
pdoExtension->NumberOfIrpQueued));
|
|
|
|
GetNextLuPendingRequest (fdoExtension, pdoExtension);
|
|
|
|
KeLowerIrql(currentIrql);
|
|
}
|
|
|
|
pdoExtension->SystemPowerState = (int)Irp->IoStatus.Information;
|
|
}
|
|
|
|
pdoExtension->PendingPowerDownSystemIrp = NULL;
|
|
|
|
} else /* if (irpStack->Parameters.Power.Type == DevicePowerState) */ {
|
|
|
|
if (pdoExtension->DevicePowerState == PowerDeviceD0) {
|
|
|
|
//
|
|
// PoSetPowerState is called before we power down
|
|
//
|
|
|
|
callPoSetPowerState = FALSE;
|
|
}
|
|
|
|
if (pdoExtension->DevicePowerState != irpStack->Parameters.Power.State.DeviceState) {
|
|
|
|
DebugPrint ((DBG_POWER,
|
|
"ATAPI: 0x%x target %d completing power Irp 0x%x with a new device state 0x%x\n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
Irp,
|
|
irpStack->Parameters.Power.State.DeviceState));
|
|
|
|
if (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD3) {
|
|
|
|
#if 0
|
|
if (shutdownType == PowerActionHibernate) {
|
|
DebugPrint((0, "Don't power down the controller yet\n"));
|
|
} else {
|
|
#endif
|
|
//
|
|
// should never do that if we are crashdump pointer
|
|
// tell parent that we just fell to sleep
|
|
//
|
|
FdoChildReportPowerDown (
|
|
fdoExtension,
|
|
pdoExtension
|
|
);
|
|
#if 0
|
|
}
|
|
#endif
|
|
|
|
KeAcquireSpinLock(&fdoExtension->SpinLock, ¤tIrql);
|
|
|
|
//
|
|
// device is powered down. block the device queue
|
|
//
|
|
SETMASK(pdoExtension->PdoState, PDOS_QUEUE_FROZEN_BY_POWER_DOWN);
|
|
|
|
DebugPrint ((DBG_POWER,
|
|
"IdePort: 0x%x target %d is powered down with 0x%x items queued\n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
pdoExtension->NumberOfIrpQueued));
|
|
|
|
KeReleaseSpinLock(&fdoExtension->SpinLock, currentIrql);
|
|
|
|
if (pdoExtension->PendingPowerDownSystemIrp) {
|
|
|
|
//
|
|
// We get this power down irp
|
|
// because we are going to non-working state
|
|
// block the device queue
|
|
//
|
|
|
|
KeAcquireSpinLock(&fdoExtension->SpinLock, ¤tIrql);
|
|
|
|
DebugPrint ((DBG_POWER,
|
|
"ATAPI: blocking 0x%x target %d device queue\n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId));
|
|
|
|
pdoExtension->PdoState |= PDOS_QUEUE_FROZEN_BY_SLEEPING_SYSTEM;
|
|
|
|
KeReleaseSpinLock(&fdoExtension->SpinLock, currentIrql);
|
|
}
|
|
}
|
|
|
|
if (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD3) {
|
|
|
|
//
|
|
// get ready to reinit. the device via acpi data when
|
|
// we get out of D3
|
|
//
|
|
InterlockedIncrement (&pdoExtension->InitDeviceWithAcpiGtf);
|
|
}
|
|
|
|
if (pdoExtension->DevicePowerState == PowerDeviceD3) {
|
|
|
|
//
|
|
// just get out out D3, re-init. pdo
|
|
//
|
|
DebugPrint((DBG_POWER, "Calling DeviceInitDeviceState for irp 0x%x\n",
|
|
Irp));
|
|
status = DeviceInitDeviceState (
|
|
pdoExtension,
|
|
DevicePowerUpInitCompletionRoutine,
|
|
Irp
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
DevicePowerUpInitCompletionRoutine (
|
|
Irp,
|
|
status
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
pdoExtension->DevicePowerState = (int) Irp->IoStatus.Information;
|
|
}
|
|
}
|
|
|
|
if ((callPoSetPowerState) && NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// we didn't get out of device D0 state. calling PoSetPowerState now
|
|
//
|
|
|
|
PoSetPowerState (
|
|
pdoExtension->DeviceObject,
|
|
irpStack->Parameters.Power.Type,
|
|
irpStack->Parameters.Power.State
|
|
);
|
|
}
|
|
} else {
|
|
|
|
if (irpStack->Parameters.Power.Type == SystemPowerState) {
|
|
|
|
if (pdoExtension->SystemPowerState != irpStack->Parameters.Power.State.SystemState) {
|
|
|
|
if (pdoExtension->SystemPowerState == PowerSystemWorking) {
|
|
|
|
//
|
|
// failed a system power down irp
|
|
//
|
|
KeAcquireSpinLock(&fdoExtension->SpinLock, ¤tIrql);
|
|
|
|
//
|
|
// got into S0, unblock and restart the device queue
|
|
//
|
|
if (pdoExtension->PdoState & PDOS_QUEUE_FROZEN_BY_SLEEPING_SYSTEM) {
|
|
|
|
CLRMASK (pdoExtension->PdoState, PDOS_QUEUE_FROZEN_BY_SLEEPING_SYSTEM);
|
|
|
|
GetNextLuPendingRequest (fdoExtension, pdoExtension);
|
|
|
|
KeLowerIrql(currentIrql);
|
|
|
|
} else {
|
|
|
|
KeReleaseSpinLock(&fdoExtension->SpinLock, currentIrql);
|
|
}
|
|
}
|
|
}
|
|
|
|
pdoExtension->PendingPowerDownSystemIrp = NULL;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
DebugPrint ((DBG_ALWAYS,
|
|
"ATAPI: 0x%x target %d failed power Irp 0x%x. status = 0x%x\n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
Irp,
|
|
Irp->IoStatus.Status));
|
|
|
|
if (irpStack->Parameters.Power.Type == DevicePowerState) {
|
|
|
|
//
|
|
// ISSUE: 08/20/2000: Just failed a power D0 request...fail all pending irp
|
|
//
|
|
//ASSERT (irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0);
|
|
|
|
//
|
|
// we will fail all the pending irps if we failed with status
|
|
// no such device
|
|
//
|
|
if (status == STATUS_NO_SUCH_DEVICE) {
|
|
|
|
DebugPrint ((0,
|
|
"Restarting the Lu queue after marking the device dead\n"
|
|
));
|
|
|
|
DebugPrint((0,
|
|
"Device Power up irp failed with status 0x%x\n",
|
|
status
|
|
));
|
|
//
|
|
// mark the pdo as dead
|
|
// ISSUE: 12/19/2001. We should update deadmeat reason
|
|
// for ease of debugging.
|
|
//
|
|
KeAcquireSpinLock(&pdoExtension->PdoSpinLock, ¤tIrql);
|
|
|
|
SETMASK (pdoExtension->PdoState, PDOS_DEADMEAT);
|
|
|
|
KeReleaseSpinLock(&pdoExtension->PdoSpinLock, currentIrql);
|
|
|
|
//
|
|
// ISSUE: 12/19/2001: Should we call IoInvalidateDeviceRelations?
|
|
// we should get a remove irp in this case anyway.
|
|
//
|
|
IoInvalidateDeviceRelations (
|
|
fdoExtension->AttacheePdo,
|
|
BusRelations
|
|
);
|
|
|
|
//
|
|
// start any pending requests
|
|
// ISSUE: 12/19/2001: We should hold the pdospinlock at
|
|
// this time. But in the other routines we seem to be
|
|
// holding just the fdospinlock before modifying the
|
|
// pdostate. We will leave it as such to minimise regressions.
|
|
//
|
|
KeAcquireSpinLock(&fdoExtension->SpinLock, ¤tIrql);
|
|
|
|
if (pdoExtension->PdoState & PDOS_QUEUE_FROZEN_BY_POWER_DOWN) {
|
|
|
|
//
|
|
// ISSUE: 12/19/2001: We are not updating the device power
|
|
// state to D0. This would cause all the further requests
|
|
// to ask for a new power up irp. The system would be slow
|
|
// if we get too many requests. The remove irp should arrive
|
|
// eventually and end the misery.
|
|
//
|
|
CLRMASK (pdoExtension->PdoState, PDOS_QUEUE_FROZEN_BY_POWER_DOWN);
|
|
|
|
//
|
|
// restart the lu queue (on an lu that is marked dead)
|
|
// we didn't run GTF or do any of the other initialization.
|
|
// Since we marked the device dead above, we can restart the
|
|
// queue.They will get completed with status
|
|
// device_does_not_exist.
|
|
//
|
|
GetNextLuPendingRequest (fdoExtension, pdoExtension);
|
|
|
|
KeLowerIrql(currentIrql);
|
|
|
|
} else {
|
|
|
|
KeReleaseSpinLock(&fdoExtension->SpinLock, currentIrql);
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// ISSUE: 12/192001: we handle only status_no_such device
|
|
//
|
|
ASSERT (irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
if (irpStack->Parameters.Power.Type == SystemPowerState) {
|
|
|
|
DebugPrint ((DBG_POWER, "IdePortPdoCompletePowerIrp: 0x%x target %d completing a SYSTEM power irp 0x%x for system state 0x%x \n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
Irp,
|
|
irpStack->Parameters.Power.State.SystemState));
|
|
|
|
ASSERT (pdoExtension->PendingSystemPowerIrp == Irp);
|
|
pdoExtension->PendingSystemPowerIrp = NULL;
|
|
|
|
} else if (irpStack->Parameters.Power.Type == DevicePowerState) {
|
|
|
|
DebugPrint ((DBG_POWER, "IdePortPdoCompletePowerIrp: 0x%x target %d completing a DEVICE power irp 0x%x for device state 0x%x \n",
|
|
pdoExtension->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
pdoExtension->TargetId,
|
|
Irp,
|
|
irpStack->Parameters.Power.State.SystemState));
|
|
|
|
ASSERT (pdoExtension->PendingDevicePowerIrp == Irp);
|
|
pdoExtension->PendingDevicePowerIrp = NULL;
|
|
}
|
|
#endif
|
|
|
|
PoStartNextPowerIrp (Irp);
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
VOID
|
|
IdePowerCheckBusyCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIDE_POWER_CONTEXT Context,
|
|
IN NTSTATUS Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Completion routine for ide pass through that would check if the
|
|
device is busy. This is typically done before a reset to salvage
|
|
drives that hang when a reset is issued while they are busy (due
|
|
to a hardware reset)
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Context
|
|
Status : Not used
|
|
|
|
Return Value
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PATA_PASS_THROUGH ataPassThrough;
|
|
PIDEREGS ideReg;
|
|
NTSTATUS status;
|
|
|
|
ataPassThrough = &Context->AtaPassThroughData;
|
|
ideReg = &ataPassThrough->IdeReg;
|
|
|
|
//
|
|
// send down the reset
|
|
//
|
|
|
|
RtlZeroMemory(ideReg, sizeof(IDEREGS));
|
|
|
|
ideReg->bReserved = ATA_PTFLAGS_BUS_RESET | ATA_PTFLAGS_URGENT;
|
|
|
|
status = IssueAsyncAtaPassThroughSafe (
|
|
Context->PdoExtension->ParentDeviceExtension,
|
|
Context->PdoExtension,
|
|
&Context->AtaPassThroughData,
|
|
FALSE,
|
|
IdePowerPassThroughCompletion,
|
|
Context,
|
|
TRUE,
|
|
DEFAULT_ATA_PASS_THROUGH_TIMEOUT,
|
|
FALSE
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
IdePowerPassThroughCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIDE_POWER_CONTEXT Context,
|
|
IN NTSTATUS Status
|
|
)
|
|
{
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// device failed power management command
|
|
// will not try it anymore
|
|
//
|
|
KIRQL currentIrql;
|
|
|
|
KeAcquireSpinLock(&Context->PdoExtension->PdoSpinLock, ¤tIrql);
|
|
|
|
SETMASK (Context->PdoExtension->PdoState, PDOS_NO_POWER_DOWN);
|
|
|
|
KeReleaseSpinLock(&Context->PdoExtension->PdoSpinLock, currentIrql);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
Context->PowerIrp->IoStatus.Status = Status;
|
|
|
|
IdePortPdoCompletePowerIrp (
|
|
DeviceObject,
|
|
Context->PowerIrp
|
|
);
|
|
|
|
ASSERT(InterlockedCompareExchange(&(Context->PdoExtension->PowerContextLock), 0, 1) == 1);
|
|
//ExFreePool (Context);
|
|
}
|
|
|
|
VOID
|
|
DevicePowerUpInitCompletionRoutine (
|
|
PVOID Context,
|
|
NTSTATUS Status
|
|
)
|
|
{
|
|
PIRP irp = Context;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PPDO_EXTENSION pdoExtension;
|
|
KIRQL currentIrql;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (irp);
|
|
pdoExtension = (PPDO_EXTENSION) irpStack->DeviceObject->DeviceExtension;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//ASSERT (!"DevicePowerUpInitCompletionRoutine Failed\n");
|
|
DebugPrint((DBG_ALWAYS, "ATAPI: ERROR: DevicePowerUpInitComplete failed with status %x\n",
|
|
Status));
|
|
}
|
|
|
|
ASSERT (pdoExtension->PendingDevicePowerIrp == irp);
|
|
pdoExtension->PendingDevicePowerIrp = NULL;
|
|
|
|
pdoExtension->DevicePowerState = (ULONG)irp->IoStatus.Information;
|
|
|
|
PoSetPowerState (
|
|
pdoExtension->DeviceObject,
|
|
irpStack->Parameters.Power.Type,
|
|
irpStack->Parameters.Power.State
|
|
);
|
|
|
|
KeAcquireSpinLock(&pdoExtension->ParentDeviceExtension->SpinLock, ¤tIrql);
|
|
|
|
//
|
|
// got into D0, restart device queue
|
|
//
|
|
DebugPrint((DBG_POWER, "Clearing QUEUE_FROZEN_BY_POWER_DOWN flag\n"));
|
|
CLRMASK(pdoExtension->PdoState, PDOS_QUEUE_FROZEN_BY_POWER_DOWN);
|
|
|
|
GetNextLuPendingRequest (pdoExtension->ParentDeviceExtension, pdoExtension);
|
|
|
|
KeLowerIrql(currentIrql);
|
|
|
|
|
|
PoStartNextPowerIrp (irp);
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
|