/*++ Copyright (C) Microsoft Corporation, 1996 - 1999 Module Name: power.c Abstract: This module contains the routines for port driver power support Authors: Peter Wieland Environment: Kernel mode only Notes: Revision History: --*/ #include "port.h" PUCHAR PowerMinorStrings[] = { "IRP_MN_WAIT_WAKE", "IRP_MN_POWER_SEQUENCE", "IRP_MN_SET_POWER", "IRP_MN_QUERY_POWER" }; VOID SpProcessAdapterSystemState( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); VOID SpProcessAdapterDeviceState( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS SpQueryTargetPower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN POWER_STATE_TYPE Type, IN POWER_STATE State ); NTSTATUS SpQueryAdapterPower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN POWER_STATE_TYPE Type, IN POWER_STATE State ); NTSTATUS SpSetTargetDeviceState( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN DEVICE_POWER_STATE DeviceState ); VOID SpSetTargetDeviceStateLockedCompletion( IN PDEVICE_OBJECT DeviceObject, IN NTSTATUS Status, IN PIRP OriginalIrp ); VOID SpSetTargetDeviceStateUnlockedCompletion( IN PDEVICE_OBJECT DeviceObject, IN NTSTATUS Status, IN PIRP OriginalIrp ); NTSTATUS SpSetTargetSystemState( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN SYSTEM_POWER_STATE SystemState ); VOID SpSetTargetSystemStateLockedCompletion( IN PDEVICE_OBJECT DeviceObject, IN NTSTATUS Status, IN PIRP OriginalIrp ); VOID SpSetTargetSystemStateUnlockedCompletion( IN PDEVICE_OBJECT DeviceObject, IN NTSTATUS Status, IN PIRP OriginalIrp ); VOID SpSetTargetDeviceStateForSystemStateCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PIRP OriginalIrp, IN PIO_STATUS_BLOCK IoStatus ); NTSTATUS SpSetAdapterPower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN POWER_STATE_TYPE Type, IN POWER_STATE State ); VOID SpRequestAdapterPowerCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PIRP OriginalIrp, IN PIO_STATUS_BLOCK IoStatus ); VOID SpPowerAdapterForTargetCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PIRP OriginalIrp, IN PIO_STATUS_BLOCK IoStatus ); NTSTATUS SpSetLowerPowerCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); VOID SpSetTargetDesiredPowerCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PIRP OriginalIrp, IN PIO_STATUS_BLOCK IoStatus ); VOID SpRequestValidPowerStateCompletion ( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PLOGICAL_UNIT_EXTENSION LogicalUnit, IN PIO_STATUS_BLOCK IoStatus ); typedef struct { NTSTATUS Status; KEVENT Event; } SP_SIGNAL_POWER_COMPLETION_CONTEXT, *PSP_SIGNAL_POWER_COMPLETION_CONTEXT; VOID SpSignalPowerCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PSP_SIGNAL_POWER_COMPLETION_CONTEXT Context, IN PIO_STATUS_BLOCK IoStatus ); #pragma alloc_text(PAGE, SpRequestValidAdapterPowerStateSynchronous) NTSTATUS ScsiPortDispatchPower( IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ) /*++ Routine Description: This routine handles all IRP_MJ_POWER IRPs for the target device objects. N.B. This routine is NOT pageable as it may be called at dispatch level. Arguments: DeviceObject - Pointer to the device object for which this IRP applies. Irp - Pointer to the IRP_MJ_PNP IRP to dispatch. Return Value: NT status. --*/ { PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); BOOLEAN isPdo = commonExtension->IsPdo; NTSTATUS status; // // Get the current status of the request. // status = Irp->IoStatus.Status; DebugPrint((4, "ScsiPortDispatchPower: irp %p is %s for %s %p\n", Irp, PowerMinorStrings[irpStack->MinorFunction], isPdo ? "pdo" : "fdo", DeviceObject)); switch (irpStack->MinorFunction) { case IRP_MN_SET_POWER: { POWER_STATE_TYPE type = irpStack->Parameters.Power.Type; POWER_STATE state = irpStack->Parameters.Power.State; DebugPrint((4, "ScsiPortDispatchPower: SET_POWER type %d state %d\n", type, state)); #if DBG if (type == SystemPowerState) { ASSERT(state.SystemState >= PowerSystemUnspecified); ASSERT(state.SystemState < PowerSystemMaximum); } else { ASSERT(state.DeviceState >= PowerDeviceUnspecified); ASSERT(state.DeviceState < PowerDeviceMaximum); } #endif // // If this is a power-down request then call PoSetPowerState now // while we're not actually holding any resources or locks. // if ((state.SystemState != PowerSystemWorking) || (state.DeviceState != PowerDeviceD0)) { PoSetPowerState(DeviceObject, type, state); } // // Special case system shutdown request. // if ((type == SystemPowerState) && (state.SystemState > PowerSystemHibernate)) { if (isPdo) { // // Do not pwer-downdown PDOs on shutdown. There is no // reliable way to ensure that disks will spin up on restart. // status = STATUS_SUCCESS; break; } else { PADAPTER_EXTENSION adapter; // // If the adapter is not configured to receive power-down // requests at shutdown, just pass the request down. // adapter = (PADAPTER_EXTENSION)commonExtension; if (adapter->NeedsShutdown == FALSE) { PoStartNextPowerIrp(Irp); IoCopyCurrentIrpStackLocationToNext(Irp); return PoCallDriver(commonExtension->LowerDeviceObject, Irp); } } } if (isPdo) { if (type == DevicePowerState) { status = SpSetTargetDeviceState(DeviceObject, Irp, state.DeviceState); } else { status = SpSetTargetSystemState(DeviceObject, Irp, state.SystemState); } } else { PADAPTER_EXTENSION adapter = DeviceObject->DeviceExtension; // // If we have disabled power then ignore any non-working power irps. // if ((adapter->DisablePower) && ((state.DeviceState != PowerDeviceD0) || (state.SystemState != PowerSystemWorking))) { status = STATUS_SUCCESS; break; } else { status = SpSetAdapterPower(DeviceObject, Irp, type, state); } } if(status == STATUS_PENDING) { return status; } break; } case IRP_MN_QUERY_POWER: { POWER_STATE_TYPE type = irpStack->Parameters.Power.Type; POWER_STATE state = irpStack->Parameters.Power.State; DebugPrint((4, "ScsiPortDispatchPower: QUERY_POWER type %d " "state %d\n", type, state)); if ((type == SystemPowerState) && (state.SystemState > PowerSystemHibernate)) { // // Ignore shutdown irps. // DebugPrint((4, "ScsiPortDispatch power - ignoring shutdown " "query irp for level %d\n", state.SystemState)); status = STATUS_SUCCESS; break; } if (isPdo) { if ((type == SystemPowerState) && (state.SystemState > PowerSystemHibernate)) { // // Ignore shutdown irps. // DebugPrint((4, "ScsiPortDispatch power - ignoring shutdown " "query irp for level %d\n", state.SystemState)); status = STATUS_SUCCESS; } else { status = SpQueryTargetPower(DeviceObject, Irp, type, state); } } else { PADAPTER_EXTENSION adapter = (PADAPTER_EXTENSION)commonExtension; // // If we don't support power for this adapter then fail all // queries. // if (adapter->DisablePower) { status = STATUS_NOT_SUPPORTED; break; } status = SpQueryAdapterPower(DeviceObject, Irp, type, state); if (NT_SUCCESS(status)) { // // See what the lower driver wants to do. // PoStartNextPowerIrp(Irp); IoCopyCurrentIrpStackLocationToNext(Irp); return PoCallDriver(commonExtension->LowerDeviceObject, Irp); } } break; } case IRP_MN_WAIT_WAKE: { if (isPdo) { // // We don't support WAIT WAKE, so just fail the request. // status = STATUS_INVALID_DEVICE_REQUEST; PoStartNextPowerIrp(Irp); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } else { // // Pass the request down. // PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); status = PoCallDriver(commonExtension->LowerDeviceObject, Irp); } return status; } default: { // // We pass down FDO requests we don't handle. // if (!isPdo) { PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); return PoCallDriver(commonExtension->LowerDeviceObject, Irp); } break; } } // // Complete the request. // PoStartNextPowerIrp(Irp); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } NTSTATUS SpSetAdapterPower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN POWER_STATE_TYPE Type, IN POWER_STATE State ) /*++ Routine Description: Wrapper routine to dump adapter power requests into the device i/o queue. Power requests are processed by the StartIo routine which calls SpProcessAdapterPower to do the actual work. Arguments: DeviceObject - the device object being power managed. Irp - the power management irp. Type - the type of set_power irp (device or system) State - the state the adapter is being put into. Return Value: STATUS_PENDING --*/ { PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PADAPTER_EXTENSION adapter = DeviceObject->DeviceExtension; NTSTATUS status; ASSERT_FDO(DeviceObject); DebugPrint((2, "SpSetAdapterPower - starting packet\n")); if(SpIsAdapterControlTypeSupported(adapter, ScsiStopAdapter)) { IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, 0L, FALSE); return STATUS_PENDING; } else if((commonExtension->CurrentPnpState != IRP_MN_START_DEVICE) && (commonExtension->PreviousPnpState != IRP_MN_START_DEVICE)) { // // Fine, we're in a low power state. If we get a start or a remove // then there's an implicit power transition there so we don't really // need to set our current power state. // IoMarkIrpPending(Irp); IoCopyCurrentIrpStackLocationToNext(Irp); PoStartNextPowerIrp(Irp); PoCallDriver(commonExtension->LowerDeviceObject, Irp); return STATUS_PENDING; } else { Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; return Irp->IoStatus.Status; } } VOID SpPowerAdapterForTargetCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PIRP OriginalIrp, IN PIO_STATUS_BLOCK IoStatus ) /*++ Routine Description: This routine is called for a D0 request to a target device after it's adapter has been powered back on. The routine will call back into SpSetTargetDeviceState to restart the power request or complete the target D request if the adapter power up was not successful. Arguments: DeviceObject - the adapter which was powered up. MinorFunction - IRP_MN_SET_POWER PowerState - PowerDeviceD0 OriginalIrp - The original target D0 irp. This is the irp which will be reprocessed. IoStatus - the status of the adapter power up request. Return Value: none. --*/ { PADAPTER_EXTENSION adapter = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(OriginalIrp); POWER_STATE_TYPE type = irpStack->Parameters.Power.Type; POWER_STATE state = irpStack->Parameters.Power.State; NTSTATUS status = IoStatus->Status; DebugPrint((1, "SpPowerAdapterForTargetCompletion: DevObj %#p, Irp " "%#p, Status %#08lx\n", DeviceObject, OriginalIrp, IoStatus)); ASSERT_FDO(DeviceObject); ASSERT_PDO(irpStack->DeviceObject); ASSERT(type == DevicePowerState); ASSERT(state.DeviceState == PowerDeviceD0); ASSERT(NT_SUCCESS(status)); if(NT_SUCCESS(status)) { ASSERT(adapter->CommonExtension.CurrentDeviceState == PowerDeviceD0); status = SpSetTargetDeviceState(irpStack->DeviceObject, OriginalIrp, PowerDeviceD0); } if(status != STATUS_PENDING) { PoStartNextPowerIrp(OriginalIrp); OriginalIrp->IoStatus.Status = status; IoCompleteRequest(OriginalIrp, IO_NO_INCREMENT); } return; } NTSTATUS SpQueryAdapterPower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN POWER_STATE_TYPE Type, IN POWER_STATE State ) { PADAPTER_EXTENSION adapter = DeviceObject->DeviceExtension; PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; NTSTATUS status; if((adapter->HwAdapterControl != NULL) && (adapter->IsPnp)) { status = STATUS_SUCCESS; } else if((commonExtension->CurrentPnpState != IRP_MN_START_DEVICE) && (commonExtension->PreviousPnpState != IRP_MN_START_DEVICE)) { // // If the adapter's not been started yet then we can blindly go to // a lower power state - START irps imply a transition into the D0 state // status = STATUS_SUCCESS; } else { status = STATUS_NOT_SUPPORTED; } return status; } NTSTATUS SpQueryTargetPower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN POWER_STATE_TYPE Type, IN POWER_STATE State ) { return STATUS_SUCCESS; } NTSTATUS SpRequestValidPowerState( IN PADAPTER_EXTENSION Adapter, IN PLOGICAL_UNIT_EXTENSION LogicalUnit, IN PSCSI_REQUEST_BLOCK Srb ) { PCOMMON_EXTENSION commonExtension = &(LogicalUnit->CommonExtension); PSRB_DATA srbData = Srb->OriginalRequest; BOOLEAN needsPower = SpSrbRequiresPower(Srb); NTSTATUS status = STATUS_SUCCESS; // // Make sure we're at a power level which can process this request. If // we aren't then make this a pending request, lock the queues and // ask the power system to bring us up to a more energetic level. // if((Srb->Function == SRB_FUNCTION_UNLOCK_QUEUE) || (Srb->Function == SRB_FUNCTION_LOCK_QUEUE)) { // // Lock and unlock commands don't require power and will work // regardless of the current power state. // return status; } // // Even if this is a bypass request, the class driver may not request // actual miniport operations on the unpowered side of a power sequence. // This means that this is either: // * a request to an idle device - powering up the device will power // up the adapter if necessary. // * a request to power down a device - adapter cannot have powered // off until this is done. // * a part of a power up sequence - the only real SCSI commands come // after the power up irp has been processed and that irp will // already have turned the adapter on. // This boils down to - we don't need to do anything special here to // power up the adapter. The device power sequences will take care of // it automatically. // // // If the device or system isn't working AND this is not a request to // unlock the queue then let it go through. The class driver is going // to unlock the queue after sending us a power request so we need to // be able to handle one. // if((commonExtension->CurrentDeviceState != PowerDeviceD0) || ((commonExtension->CurrentSystemState != PowerSystemWorking) && (!TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_BYPASS_LOCKED_QUEUE)))) { // // This request cannot be executed now. Mark it as pending // in the logical unit structure and return. // GetNextLogicalUnit will restart the commnad after all of the // active commands have completed. // ASSERT(!TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_BYPASS_LOCKED_QUEUE)); ASSERT(!(LogicalUnit->LuFlags & LU_PENDING_LU_REQUEST)); DebugPrint((4, "ScsiPortStartIo: logical unit (%d,%d,%d) [%#p] is " "in power state (%d,%d) - must power up for irp " "%#p\n", Srb->PathId, Srb->TargetId, Srb->Lun, commonExtension->DeviceObject, commonExtension->CurrentDeviceState, commonExtension->CurrentSystemState, srbData->CurrentIrp)); ASSERT(LogicalUnit->PendingRequest == NULL); LogicalUnit->PendingRequest = Srb->OriginalRequest; // // Indicate that the logical unit is still active so that the // request will get processed when the request list is empty. // SET_FLAG(LogicalUnit->LuFlags, LU_PENDING_LU_REQUEST | LU_LOGICAL_UNIT_IS_ACTIVE); if(commonExtension->CurrentSystemState != PowerSystemWorking) { DebugPrint((1, "SpRequestValidPowerState: can't power up target " "since it's in system state %d\n", commonExtension->CurrentSystemState)); // // Set the desired D state in the device extension. This is // necessary when we're in a low system state as well as useful for // debugging when we're in low D states. // commonExtension->DesiredDeviceState = PowerDeviceD0; // // If we aren't in a valid system state then just release the // spinlock and return. The next time we receive a system // state irp we'll issue the appropriate D state irp as well. // return STATUS_PENDING; } else if(commonExtension->DesiredDeviceState == PowerDeviceD0) { // // Scsiport is already asking to power up this lun. Once that's // happened this request will be restarted. For now just leave // it as the pending request. // return STATUS_PENDING; } // // Tell Po that we're not idle in case this was stuck in the queue // for some reason. // if(commonExtension->IdleTimer != NULL) { PoSetDeviceBusy(commonExtension->IdleTimer); } // // Get PO to send a power request to this device stack to put it // back into the D0 state. // { POWER_STATE powerState; powerState.DeviceState = PowerDeviceD0; status = PoRequestPowerIrp( commonExtension->DeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL); } // // CODEWORK - if we can't power up the device here we'll need to // hang around in the tick handler for a while and try to do // it from there. // ASSERT(NT_SUCCESS(status)); return STATUS_PENDING; } ASSERT(Adapter->CommonExtension.CurrentDeviceState == PowerDeviceD0); ASSERT(Adapter->CommonExtension.CurrentSystemState == PowerSystemWorking); return status; } NTSTATUS SpSetLowerPowerCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PADAPTER_EXTENSION adapter = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); POWER_STATE_TYPE type = irpStack->Parameters.Power.Type; POWER_STATE state = irpStack->Parameters.Power.State; DebugPrint((2, "SpSetLowerPowerCompletion: DevObj %#p Irp %#p " "%sState %d\n", DeviceObject, Irp, ((type == SystemPowerState) ? "System" : "Device"), state.DeviceState - 1)); if(NT_SUCCESS(Irp->IoStatus.Status)) { if(type == SystemPowerState) { DebugPrint((2, "SpSetLowerPowerCompletion: Lower device succeeded " "the system state transition. Reprocessing power " "irp\n")); SpProcessAdapterSystemState(DeviceObject, Irp); } else { DebugPrint((2, "SpSetLowerPowerCompletion: Lower device power up " "was successful. Reprocessing power irp\n")); SpProcessAdapterDeviceState(DeviceObject, Irp); } return STATUS_MORE_PROCESSING_REQUIRED; } else { DebugPrint((1, "SpSetLowerPowerCompletion: Lower device power operation" "failed - completing power irp with status %#08lx\n", Irp->IoStatus.Status)); PoStartNextPowerIrp(Irp); SpStartNextPacket(DeviceObject, FALSE); return STATUS_SUCCESS; } } VOID SpProcessAdapterSystemState( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine handles a system power IRP for the adapter. Arguments: DeviceObject - the device object for the adapter. Irp - the power irp. Return Value: none --*/ { PADAPTER_EXTENSION adapterExtension = DeviceObject->DeviceExtension; PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); SYSTEM_POWER_STATE state = irpStack->Parameters.Power.State.SystemState; SYSTEM_POWER_STATE originalSystemState; NTSTATUS status = STATUS_SUCCESS; ASSERT_FDO(DeviceObject); ASSERT(irpStack->Parameters.Power.Type == SystemPowerState); DebugPrint((2, "SpProcessAdapterSystemState: DevObj %#p Irp %#p " "SystemState %d\n", DeviceObject, Irp, state)); originalSystemState = commonExtension->CurrentSystemState; try { POWER_STATE newDeviceState; if(((commonExtension->CurrentSystemState != PowerSystemWorking) && (state != PowerSystemWorking)) || (commonExtension->CurrentSystemState == state)) { DebugPrint((2, "SpProcessAdapterSystemState: no work required for " "transition from system state %d to %d\n", commonExtension->CurrentSystemState, state)); commonExtension->CurrentSystemState = state; leave; } // // Set the new system state. We'll back it out if any errors occur. // commonExtension->CurrentSystemState = state; if(state != PowerSystemWorking) { // // Going into a non-working state - power down the device. // DebugPrint((1, "SpProcessAdapterSystemState: need to power " "down adapter for non-working system state " "%d\n", state)); newDeviceState.DeviceState = PowerDeviceD3; // // This system irp cannot be completed until we've successfully // powered the adapter off (or on). // status = PoRequestPowerIrp(DeviceObject, IRP_MN_SET_POWER, newDeviceState, SpRequestAdapterPowerCompletion, Irp, NULL); DebugPrint((2, "SpProcessAdapterSystemState: PoRequestPowerIrp " "returned %#08lx\n", status)); } else { // // Transitioning the device into a system working state. Just // do the enable. When a child device is put into S0 and has // work to process it will request a D0 of the child which will // in turn request a D0 of the parent (ie. the adapter). We can // defer adapter power on until that occurs. // // Going into a working device state. When the targets are // powered on and we have work to do they will request an // adapter power up for us. // DebugPrint((1, "SpProcessAdapterSystemState: going to working " "state - no need to take adapter out of power " "state %d\n", commonExtension->CurrentDeviceState)); ASSERT(state == PowerSystemWorking); status = SpEnableDisableAdapter(adapterExtension, TRUE); ASSERT(status != STATUS_PENDING); DebugPrint((1, "SpProcessAdapterSystemState: SpEnableDisableAd. " "returned %#08lx\n", status)); } } finally { SpStartNextPacket(DeviceObject, FALSE); if(status != STATUS_PENDING) { if(!NT_SUCCESS(status)) { // // Something went wrong above. Restore the original system // state. // commonExtension->CurrentSystemState = originalSystemState; } PoStartNextPowerIrp(Irp); IoCopyCurrentIrpStackLocationToNext(Irp); PoCallDriver(adapterExtension->CommonExtension.LowerDeviceObject, Irp); } } return; } VOID SpProcessAdapterDeviceState( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine handles a device power IRP for the adapter. Arguments: DeviceObject - the device object for the adapter. Irp - the power irp. Return Value: none --*/ { PADAPTER_EXTENSION adapterExtension = DeviceObject->DeviceExtension; PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); DEVICE_POWER_STATE state = irpStack->Parameters.Power.State.DeviceState; NTSTATUS status = STATUS_SUCCESS; ASSERT_FDO(DeviceObject); ASSERT(irpStack->Parameters.Power.Type == DevicePowerState); DebugPrint((1, "SpProcessAdapterDevicePower: DevObj %#p Irp %#p " "State %d\n", DeviceObject, Irp, state)); // // First check to see if we actually need to touch the queue. // If both the current and new state are working or non-working then // we don't have a thing to do. // if(((commonExtension->CurrentDeviceState != PowerDeviceD0) && (state != PowerDeviceD0)) || (commonExtension->CurrentDeviceState == state)) { DebugPrint((2, "SpProcessAdapterDeviceState: no work required " "for transition from device state %d to %d\n", commonExtension->CurrentDeviceState, state)); } else { BOOLEAN enable = (state == PowerDeviceD0); status = SpEnableDisableAdapter(adapterExtension, enable); ASSERT(status != STATUS_PENDING); DebugPrint((2, "SpProcessAdapterDeviceState: SpEnableDisableAd. " "returned %#08lx\n", status)); } ASSERT(status != STATUS_PENDING); if(NT_SUCCESS(status)) { commonExtension->CurrentDeviceState = state; } // // If this is not a D0 irp then throw it down to the lower driver, // otherwise complete it. // SpStartNextPacket(DeviceObject, FALSE); Irp->IoStatus.Status = status; PoStartNextPowerIrp(Irp); if(irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0) { IoCopyCurrentIrpStackLocationToNext(Irp); PoCallDriver(commonExtension->LowerDeviceObject, Irp); } else { IoCompleteRequest(Irp, IO_NO_INCREMENT); } // // If we successfully powered up the adapter, initiate a rescan so our child // device state is accurate. // if (NT_SUCCESS(status) && (state == PowerDeviceD0)) { DebugPrint((1, "SpProcessAdapterDeviceState: powered up.. rescan %p\n", adapterExtension->LowerPdo)); IoInvalidateDeviceRelations( adapterExtension->LowerPdo, BusRelations); } return; } VOID ScsiPortProcessAdapterPower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PADAPTER_EXTENSION adapter = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); ASSERT(irpStack->MajorFunction == IRP_MJ_POWER); ASSERT(irpStack->MinorFunction == IRP_MN_SET_POWER); ASSERT_FDO(DeviceObject); // // Determine if the irp should be sent to the lower driver first. // If so send it down without calling SpStartNextPacket so we'll // still have synchronization in the completion routine. // // The completion routine calls ScsiPortProcessAdapterPower for this // irp. // if (irpStack->Parameters.Power.Type == SystemPowerState) { // // Process system state irps before we send them down. // SpProcessAdapterSystemState(DeviceObject, Irp); } else if(irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0) { NTSTATUS status; // // System power IRP or a power-up request. These should be // processed by the lower driver before being processed by // scsiport. // IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, SpSetLowerPowerCompletion, NULL, TRUE, TRUE, TRUE); status = PoCallDriver( adapter->CommonExtension.LowerDeviceObject, Irp); } else { SpProcessAdapterDeviceState(DeviceObject, Irp); } return; } VOID SpRequestAdapterPowerCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PIRP SystemIrp, IN PIO_STATUS_BLOCK IoStatus ) { PADAPTER_EXTENSION adapter = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(SystemIrp); SYSTEM_POWER_STATE state = irpStack->Parameters.Power.State.SystemState; BOOLEAN enable = FALSE; NTSTATUS status = IoStatus->Status; ASSERT_FDO(DeviceObject); ASSERT(IoGetCurrentIrpStackLocation(SystemIrp)->Parameters.Power.Type == SystemPowerState); DebugPrint((2, "SpRequestAdapterPowerCompletion: Adapter %#p, " "Irp %#p, State %d, Status %#08lx\n", adapter, SystemIrp, PowerState.DeviceState, IoStatus->Status)); SystemIrp->IoStatus.Status = IoStatus->Status; if(NT_SUCCESS(status)) { enable = (state == PowerSystemWorking); status = SpEnableDisableAdapter(adapter, enable); DebugPrint((1, "SpRequestAdapterPowerCompletion: %s adapter call " "returned %#08lx\n", enable ? "Enable" : "Disable", status)); ASSERT(status != STATUS_PENDING); if((NT_SUCCESS(status)) && enable) { POWER_STATE setState; setState.SystemState = PowerSystemWorking; PoSetPowerState(DeviceObject, SystemPowerState, setState); } } IoCopyCurrentIrpStackLocationToNext(SystemIrp); PoStartNextPowerIrp(SystemIrp); PoCallDriver(adapter->CommonExtension.LowerDeviceObject, SystemIrp); return; } VOID SpSetTargetDesiredPowerCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PIRP OriginalIrp, IN PIO_STATUS_BLOCK IoStatus ) { PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; ASSERT_PDO(DeviceObject); ASSERT(MinorFunction == IRP_MN_SET_POWER); ASSERT(NT_SUCCESS(IoStatus->Status)); ASSERT(PowerState.DeviceState == PowerDeviceD0); ASSERT(commonExtension->CurrentDeviceState == PowerDeviceD0); ASSERT(commonExtension->DesiredDeviceState == PowerState.DeviceState); ASSERT(OriginalIrp == NULL); DebugPrint((4, "SpSetTargetDesiredPowerCompletion: power irp completed " "with status %#08lx\n", IoStatus->Status)); commonExtension->DesiredDeviceState = PowerDeviceUnspecified; return; } VOID SpSetTargetDeviceStateLockedCompletion( IN PDEVICE_OBJECT DeviceObject, IN NTSTATUS Status, IN PIRP OriginalIrp ) /*++ Routine Description: This routine is called after locking the queue for handling of a device state. The proper device state is set and an unlock request is sent to the queue. The OriginalIrp (whatever it may have been) will be completed after the unlock has been completed. Arguments: DeviceObject - the device object Status - the status of the enable/disable operation OriginalIrp - the original power irp. Return Value: none --*/ { PLOGICAL_UNIT_EXTENSION logicalUnit = DeviceObject->DeviceExtension; PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(OriginalIrp); ASSERT(irpStack->Parameters.Power.Type == DevicePowerState); DebugPrint((4, "SpSetTargetDeviceStateLockedCompletion: DO %#p Irp %#p " "Status %#08lx\n", DeviceObject, OriginalIrp, Status)); if(NT_SUCCESS(Status)) { DEVICE_POWER_STATE newState = irpStack->Parameters.Power.State.DeviceState; DebugPrint((4, "SpSetTargetDeviceStateLockedCompletion: old device state " "was %d - new device state is %d\n", commonExtension->CurrentDeviceState, irpStack->Parameters.Power.State.DeviceState)); SpAdjustDisabledBit(logicalUnit, (BOOLEAN) ((newState == PowerDeviceD0) ? TRUE : FALSE)); commonExtension->CurrentDeviceState = newState; SpEnableDisableLogicalUnit( logicalUnit, TRUE, SpSetTargetDeviceStateUnlockedCompletion, OriginalIrp); return; } OriginalIrp->IoStatus.Status = Status; PoStartNextPowerIrp(OriginalIrp); IoCompleteRequest(OriginalIrp, IO_NO_INCREMENT); return; } VOID SpSetTargetDeviceStateUnlockedCompletion( IN PDEVICE_OBJECT DeviceObject, IN NTSTATUS Status, IN PIRP OriginalIrp ) /*++ Routine Description: This routine is called after unlocking the queue following the setting of the new device state. It simply completes the original power request. Arguments: DeviceObject - the device object Status - the status of the enable/disable operation OriginalIrp - the original power irp. Return Value: none --*/ { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(OriginalIrp); ASSERT(irpStack->Parameters.Power.Type == DevicePowerState); DebugPrint((4, "SpSetTargetDeviceStateUnlockedCompletion: DO %#p Irp %#p " "Status %#08lx\n", DeviceObject, OriginalIrp, Status)); if(NT_SUCCESS(Status) && (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0)) { // // Power up completed - fire notifications. // PoSetPowerState(DeviceObject, DevicePowerState, irpStack->Parameters.Power.State); } OriginalIrp->IoStatus.Status = Status; PoStartNextPowerIrp(OriginalIrp); IoCompleteRequest(OriginalIrp, IO_NO_INCREMENT); return; } NTSTATUS SpSetTargetDeviceState( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN DEVICE_POWER_STATE DeviceState ) /*++ Routine Description: Arguments: Return Value: --*/ { PLOGICAL_UNIT_EXTENSION logicalUnit = DeviceObject->DeviceExtension; PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PADAPTER_EXTENSION adapter = logicalUnit->AdapterExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); BOOLEAN isHibernation; NTSTATUS status = STATUS_SUCCESS; ASSERT_PDO(DeviceObject); ASSERT(irpStack->Parameters.Power.Type == DevicePowerState); DebugPrint((4, "SpSetTargetDeviceState: device %#p irp %#p\n", DeviceObject, Irp)); // // First check to see if we actually need to touch the queue. // If both the current and new state are working or non-working then // we don't have a thing to do. // if(((commonExtension->CurrentDeviceState != PowerDeviceD0) && (DeviceState != PowerDeviceD0)) || (commonExtension->CurrentDeviceState == DeviceState)) { DebugPrint((4, "SpSetTargetDeviceState: Transition from D%d to D%d " "requires no extra work\n", commonExtension->CurrentDeviceState, DeviceState)); commonExtension->CurrentDeviceState = DeviceState; return STATUS_SUCCESS; } // // We can't power up the target device if the adapter isn't already // powered up. // if((DeviceState == PowerDeviceD0) && (adapter->CommonExtension.CurrentDeviceState != PowerDeviceD0)) { POWER_STATE newAdapterState; DebugPrint((1, "SpSetTargetPower: Unable to power up target " "before adapter - requesting adapter %#p " "powerup\n", adapter)); irpStack->DeviceObject = DeviceObject; newAdapterState.DeviceState = PowerDeviceD0; return PoRequestPowerIrp(adapter->DeviceObject, IRP_MN_SET_POWER, newAdapterState, SpPowerAdapterForTargetCompletion, Irp, NULL); } // // Device power operations use queue locks to ensure // synchronization with i/o requests. However they never leave // the logical unit queue permenantly locked - otherwise we'd be // unable to power-up the device when i/o came in. Lock the queue // so we can set the power state. // IoMarkIrpPending(Irp); SpEnableDisableLogicalUnit( logicalUnit, FALSE, SpSetTargetDeviceStateLockedCompletion, Irp); return STATUS_PENDING; } NTSTATUS SpSetTargetSystemState( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN SYSTEM_POWER_STATE SystemState ) /*++ Routine Description: Arguments: Return Value: --*/ { PLOGICAL_UNIT_EXTENSION logicalUnit = DeviceObject->DeviceExtension; PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); POWER_STATE newDeviceState; NTSTATUS status = STATUS_SUCCESS; ASSERT_PDO(DeviceObject); ASSERT(irpStack->Parameters.Power.Type == SystemPowerState); DebugPrint((2, "SpSetTargetSystemState: device %#p irp %#p\n", DeviceObject, Irp)); // // First check to see if we actually need to touch the queue. // If both the current and new state are working or non-working then // we don't have a thing to do. // if(((commonExtension->CurrentSystemState != PowerSystemWorking) && (SystemState != PowerSystemWorking)) || (commonExtension->CurrentSystemState == SystemState)) { DebugPrint((2, "SpSetTargetPower: Transition from S%d to S%d " "requires no extra work\n", commonExtension->CurrentSystemState, SystemState)); commonExtension->CurrentSystemState = SystemState; return STATUS_SUCCESS; } // // Disable the logical unit so we can set it's power state safely. // IoMarkIrpPending(Irp); SpEnableDisableLogicalUnit( logicalUnit, FALSE, SpSetTargetSystemStateLockedCompletion, Irp); return STATUS_PENDING; } VOID SpSetTargetSystemStateLockedCompletion( IN PDEVICE_OBJECT DeviceObject, IN NTSTATUS Status, IN PIRP PowerIrp ) /*++ Routine Description: This routine is called after the logical unit has been locked and is responsible for setting the new system state of the logical unit. If the logical unit currently has a desired power state (other than unspecified) this routine will request that the device be put into that power state after the logical unit is re-enabled. Once the work is done this routine will request that the logical unit be re-enabled. After that the power irp will be completed. Arguments: DeviceObject - the device object which has been disabled Status - the status of the disable request PowerIrp - the power irp we are processing Return Value: none --*/ { PLOGICAL_UNIT_EXTENSION logicalUnit = DeviceObject->DeviceExtension; PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(PowerIrp); POWER_STATE_TYPE type = irpStack->Parameters.Power.Type; SYSTEM_POWER_STATE state = irpStack->Parameters.Power.State.SystemState; POWER_STATE powerState; ASSERT_PDO(DeviceObject); ASSERT(type == SystemPowerState); ASSERT(PowerIrp != NULL); DebugPrint((2, "SpSetTargetSystemStateLockedCompletion: DevObj %#p, " "Status %#08lx PowerIrp %#p\n", DeviceObject, Status, PowerIrp)); // // If the enable/disable failed then the power operation is obviously // unsuccessful. Fail the power irp. // if(!NT_SUCCESS(Status)) { ASSERT(FALSE); PowerIrp->IoStatus.Status = Status; PoStartNextPowerIrp(PowerIrp); IoCompleteRequest(PowerIrp, IO_NO_INCREMENT); return; } SpAdjustDisabledBit( logicalUnit, (BOOLEAN) ((state == PowerSystemWorking) ? TRUE : FALSE)); commonExtension->CurrentSystemState = state; DebugPrint((2, "SpSetTargetSystemStateLockedCompletion: new system state %d " "set - desired device state is %d\n", state, commonExtension->DesiredDeviceState)); // // Re-enable the logical unit. We'll put it into the correct D state // after it's been turned back on. // SpEnableDisableLogicalUnit(logicalUnit, TRUE, SpSetTargetSystemStateUnlockedCompletion, PowerIrp); return; } VOID SpSetTargetSystemStateUnlockedCompletion( IN PDEVICE_OBJECT DeviceObject, IN NTSTATUS Status, IN PIRP PowerIrp ) /*++ Routine Description: This routine is called after the system state of the logical unit has been set and it has been re-enabled. If the device has a desired power state or if it needs to be turned off (or on) for hibernation the power irp will be sent from here. Arguments: DeviceObject - the logical unit which has been unlocked Status - the status of the unlock request PowerIrp - the original power irp. Return Value: none --*/ { PLOGICAL_UNIT_EXTENSION logicalUnit = DeviceObject->DeviceExtension; PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(PowerIrp); POWER_STATE_TYPE type = irpStack->Parameters.Power.Type; SYSTEM_POWER_STATE state = irpStack->Parameters.Power.State.SystemState; POWER_STATE powerState; ASSERT_PDO(DeviceObject); ASSERT(type == SystemPowerState); ASSERT(PowerIrp != NULL); DebugPrint((2, "SpSetTargetSystemStateUnlockedCompletion: DevObj %#p, " "Status %#08lx PowerIrp %#p\n", DeviceObject, Status, PowerIrp)); if(!NT_SUCCESS(Status)) { // // Oh dear - this device couldn't be re-enabled. The logical unit is // useless until this can be done. // // // CODEWORK - figure out a way to deal with this case. // ASSERT(FALSE); PowerIrp->IoStatus.Status = Status; PoStartNextPowerIrp(PowerIrp); IoCompleteRequest(PowerIrp, IO_NO_INCREMENT); return; } if(state == PowerSystemWorking) { // // We're waking up the system. Check to see if the device needs // to be powered immediately as well. // // // Power up - fire notifications. // powerState.SystemState = state; PoSetPowerState(DeviceObject, SystemPowerState, powerState); if(commonExtension->DesiredDeviceState != PowerDeviceUnspecified) { // // Request a power up of the target device now. We'll complete // the system irp immediately without waiting for the device irp // to finish. // powerState.DeviceState = commonExtension->DesiredDeviceState; DebugPrint((1, "SpSetTargetSystemStateUnlockedCompletion: Target has " "desired device state of %d - issuing irp to " "request transition\n", powerState.DeviceState)); Status = PoRequestPowerIrp(DeviceObject, IRP_MN_SET_POWER, powerState, SpSetTargetDesiredPowerCompletion, NULL, NULL); ASSERT(Status == STATUS_PENDING); } if(Status != STATUS_PENDING) { PowerIrp->IoStatus.Status = Status; } else { PowerIrp->IoStatus.Status = STATUS_SUCCESS; } PoStartNextPowerIrp(PowerIrp); IoCompleteRequest(PowerIrp, IO_NO_INCREMENT); } else { // // We're going to put the device into a D state based on the current // S state. // DebugPrint((2, "SpSetTargetSystemStateUnlockedCompletion: power down " "target for non-working system state " "transition\n")); powerState.DeviceState = PowerDeviceD3; // // Request the appropriate D irp. We'll block the S irp until the // D transition has been completed. // Status = PoRequestPowerIrp( DeviceObject, IRP_MN_SET_POWER, powerState, SpSetTargetDeviceStateForSystemStateCompletion, PowerIrp, NULL); if(!NT_SUCCESS(Status)) { // // STATUS_PENDING is still successful. // PowerIrp->IoStatus.Status = Status; PoStartNextPowerIrp(PowerIrp); IoCompleteRequest(PowerIrp, IO_NO_INCREMENT); } } return; } VOID SpSetTargetDeviceStateForSystemStateCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PIRP OriginalIrp, IN PIO_STATUS_BLOCK IoStatus ) { PIO_STACK_LOCATION irpStack; irpStack = IoGetCurrentIrpStackLocation(OriginalIrp); ASSERT_PDO(DeviceObject); ASSERT(irpStack->Parameters.Power.Type == SystemPowerState); ASSERT(irpStack->Parameters.Power.State.SystemState != PowerSystemWorking); DebugPrint((2, "SpSetTargetDeviceStateForSystemCompletion: DevObj %#p, " "Irp %#p, Status %#08lx\n", DeviceObject, OriginalIrp, IoStatus)); OriginalIrp->IoStatus.Status = IoStatus->Status; PoStartNextPowerIrp(OriginalIrp); IoCompleteRequest(OriginalIrp, IO_NO_INCREMENT); return; } VOID SpRequestValidPowerStateCompletion ( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PLOGICAL_UNIT_EXTENSION LogicalUnit, IN PIO_STATUS_BLOCK IoStatus ) { // // re-enable the logical unit. Don't bother with a completion routine // because there's nothing to do. // ASSERT(NT_SUCCESS(IoStatus->Status)); SpEnableDisableLogicalUnit(LogicalUnit, TRUE, NULL, NULL); return; } NTSTATUS SpRequestValidAdapterPowerStateSynchronous( IN PADAPTER_EXTENSION Adapter ) { PCOMMON_EXTENSION commonExtension = &(Adapter->CommonExtension); NTSTATUS status = STATUS_SUCCESS; // // Interlock with other calls to this routine - there's no reason to have // several D0 irps in flight at any given time. // ExAcquireFastMutex(&(Adapter->PowerMutex)); try { // // Check to see if we're already in a working state. If so we can just // continue. // if((commonExtension->CurrentSystemState == PowerSystemWorking) && (commonExtension->CurrentDeviceState == PowerDeviceD0)) { leave; } // // First check the system state. If the device is in a non-working system // state then we'll need to block waiting for the system to wake up. // if(commonExtension->CurrentSystemState != PowerSystemWorking) { // // If we're not in a system working state then fail the attempt // to set a new device state. The caller should retry when the // system is powered on. Ideally we won't be getting requests // which cause us to try and power up while the system is // suspended. // status = STATUS_UNSUCCESSFUL; leave; } // // Request a power change request. // { POWER_STATE newAdapterState; SP_SIGNAL_POWER_COMPLETION_CONTEXT context; DebugPrint((1, "SpRequestValidAdapterPowerState: Requesting D0 power " "irp for adapter %p\n", Adapter)); newAdapterState.DeviceState = PowerDeviceD0; KeInitializeEvent(&(context.Event), SynchronizationEvent, FALSE); status = PoRequestPowerIrp(Adapter->DeviceObject, IRP_MN_SET_POWER, newAdapterState, SpSignalPowerCompletion, &context, NULL); if(status == STATUS_PENDING) { KeWaitForSingleObject(&(context.Event), KernelMode, Executive, FALSE, NULL); } status = context.Status; leave; } } finally { ExReleaseFastMutex(&(Adapter->PowerMutex)); } return status; } VOID SpSignalPowerCompletion( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PSP_SIGNAL_POWER_COMPLETION_CONTEXT Context, IN PIO_STATUS_BLOCK IoStatus ) { Context->Status = IoStatus->Status; KeSetEvent(&(Context->Event), IO_NO_INCREMENT, FALSE); return; }