mirror of
https://github.com/reactos/reactos.git
synced 2024-08-30 23:28:58 +00:00
![Victor Perevertkin](/assets/img/avatar_default.png)
The source code is licensed under MS-PL license, taken from Windows Driver Samples repository (https://github.com/microsoft/Windows-driver-samples/tree/master/storage/class/classpnp/) Synched with commit 88541f70c4273ecd30c8c7c72135bc038a00fd88 The driver is written for Windows 8+, so we compile it with ntoskrnl_vista statically linked and with NTDDI_WIN8 defined CORE-17129
2664 lines
99 KiB
C
2664 lines
99 KiB
C
/*++
|
||
|
||
Copyright (C) Microsoft Corporation, 1991 - 2010
|
||
|
||
Module Name:
|
||
|
||
power.c
|
||
|
||
Abstract:
|
||
|
||
SCSI class driver routines
|
||
|
||
Environment:
|
||
|
||
kernel mode only
|
||
|
||
Notes:
|
||
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#ifndef __REACTOS__
|
||
#include "stddef.h"
|
||
#include "ntddk.h"
|
||
#include "scsi.h"
|
||
#endif
|
||
#include "classp.h"
|
||
|
||
// #include <stdarg.h> __REACTOS__
|
||
|
||
#ifdef DEBUG_USE_WPP
|
||
#include "power.tmh"
|
||
#endif
|
||
|
||
#define CLASS_TAG_POWER 'WLcS'
|
||
|
||
// constants for power transition process. (UNIT: seconds)
|
||
#define DEFAULT_POWER_IRP_TIMEOUT_VALUE 10*60
|
||
#define TIME_LEFT_FOR_LOWER_DRIVERS 30
|
||
#define TIME_LEFT_FOR_UPPER_DRIVERS 5
|
||
#define DEFAULT_IO_TIMEOUT_VALUE 10
|
||
#define MINIMUM_STOP_UNIT_TIMEOUT_VALUE 2
|
||
|
||
//
|
||
// MINIMAL value is one that has some slack and is the value to use
|
||
// if there is a shortened POWER IRP timeout value. If time remaining
|
||
// is less than MINIMAL, we will use the MINIMUM value. Both values
|
||
// are in the same unit as above (seconds).
|
||
//
|
||
#define MINIMAL_START_UNIT_TIMEOUT_VALUE 60
|
||
#define MINIMUM_START_UNIT_TIMEOUT_VALUE 30
|
||
|
||
// PoQueryWatchdogTime was introduced in Windows 7.
|
||
// Returns TRUE if a watchdog-enabled power IRP is found, otherwise FALSE.
|
||
#if (NTDDI_VERSION < NTDDI_WIN7)
|
||
#define PoQueryWatchdogTime(A, B) FALSE
|
||
#endif
|
||
|
||
IO_COMPLETION_ROUTINE ClasspPowerDownCompletion;
|
||
|
||
IO_COMPLETION_ROUTINE ClasspPowerUpCompletion;
|
||
|
||
IO_COMPLETION_ROUTINE ClasspStartNextPowerIrpCompletion;
|
||
IO_COMPLETION_ROUTINE ClasspDeviceLockFailurePowerIrpCompletion;
|
||
|
||
NTSTATUS
|
||
ClasspPowerHandler(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN CLASS_POWER_OPTIONS Options
|
||
);
|
||
|
||
VOID
|
||
RetryPowerRequest(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PIRP Irp,
|
||
PCLASS_POWER_CONTEXT Context
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, ClasspPowerSettingCallback)
|
||
#endif
|
||
|
||
/*++////////////////////////////////////////////////////////////////////////////
|
||
|
||
ClassDispatchPower()
|
||
|
||
Routine Description:
|
||
|
||
This routine acquires the removelock for the irp and then calls the
|
||
appropriate power callback.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject -
|
||
Irp -
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
NTSTATUS
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
ClassDispatchPower(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
||
ULONG isRemoved;
|
||
|
||
//
|
||
// NOTE: This code may be called at PASSIVE or DISPATCH, depending
|
||
// upon the device object it is being called for.
|
||
// don't do anything that would break under either circumstance.
|
||
//
|
||
|
||
//
|
||
// If device is added but not yet started, we need to send the Power
|
||
// request down the stack. If device is started and then stopped,
|
||
// we have enough state to process the power request.
|
||
//
|
||
|
||
if (!commonExtension->IsInitialized) {
|
||
|
||
PoStartNextPowerIrp(Irp);
|
||
IoSkipCurrentIrpStackLocation(Irp);
|
||
return PoCallDriver(commonExtension->LowerDeviceObject, Irp);
|
||
}
|
||
|
||
isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
|
||
|
||
if (isRemoved) {
|
||
ClassReleaseRemoveLock(DeviceObject, Irp);
|
||
Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
|
||
PoStartNextPowerIrp(Irp);
|
||
ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
|
||
return STATUS_DEVICE_DOES_NOT_EXIST;
|
||
}
|
||
|
||
return commonExtension->DevInfo->ClassPowerDevice(DeviceObject, Irp);
|
||
} // end ClassDispatchPower()
|
||
|
||
/*++////////////////////////////////////////////////////////////////////////////
|
||
|
||
ClasspPowerUpCompletion()
|
||
|
||
Routine Description:
|
||
|
||
This routine is used for intermediate completion of a power up request.
|
||
PowerUp requires four requests to be sent to the lower driver in sequence.
|
||
|
||
* The queue is "power locked" to ensure that the class driver power-up
|
||
work can be done before request processing resumes.
|
||
|
||
* The power irp is sent down the stack for any filter drivers and the
|
||
port driver to return power and resume command processing for the
|
||
device. Since the queue is locked, no queued irps will be sent
|
||
immediately.
|
||
|
||
* A start unit command is issued to the device with appropriate flags
|
||
to override the "power locked" queue.
|
||
|
||
* The queue is "power unlocked" to start processing requests again.
|
||
|
||
This routine uses the function in the srb which just completed to determine
|
||
which state it is in.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - the device object being powered up
|
||
|
||
Irp - Context->Irp: original power irp; fdoExtension->PrivateFdoData->PowerProcessIrp: power process irp
|
||
|
||
Context - Class power context used to perform port/class operations.
|
||
|
||
Return Value:
|
||
|
||
STATUS_MORE_PROCESSING_REQUIRED or
|
||
STATUS_SUCCESS
|
||
|
||
--*/
|
||
NTSTATUS
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
ClasspPowerUpCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
PCLASS_POWER_CONTEXT PowerContext = (PCLASS_POWER_CONTEXT)Context;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension;
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
||
PIRP OriginalIrp;
|
||
PIO_STACK_LOCATION currentStack;
|
||
PIO_STACK_LOCATION nextStack;
|
||
|
||
NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
PSTORAGE_REQUEST_BLOCK_HEADER srbHeader;
|
||
ULONG srbFlags;
|
||
BOOLEAN FailurePredictionEnabled = FALSE;
|
||
|
||
UNREFERENCED_PARAMETER(DeviceObject);
|
||
|
||
if (PowerContext == NULL) {
|
||
NT_ASSERT(PowerContext != NULL);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
commonExtension = PowerContext->DeviceObject->DeviceExtension;
|
||
fdoExtension = PowerContext->DeviceObject->DeviceExtension;
|
||
OriginalIrp = PowerContext->Irp;
|
||
|
||
// currentStack - from original power irp
|
||
// nextStack - from power process irp
|
||
currentStack = IoGetCurrentIrpStackLocation(OriginalIrp);
|
||
nextStack = IoGetNextIrpStackLocation(fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "ClasspPowerUpCompletion: Device Object %p, Irp %p, "
|
||
"Context %p\n",
|
||
PowerContext->DeviceObject, Irp, Context));
|
||
|
||
if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->PowerSrb.SrbEx);
|
||
|
||
//
|
||
// Check if reverted to using legacy SRB.
|
||
//
|
||
if (PowerContext->Srb.Length == sizeof(SCSI_REQUEST_BLOCK)) {
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
|
||
}
|
||
} else {
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
|
||
}
|
||
|
||
srbFlags = SrbGetSrbFlags(srbHeader);
|
||
NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
|
||
NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
|
||
NT_ASSERT(PowerContext->Options.PowerDown == FALSE);
|
||
NT_ASSERT(PowerContext->Options.HandleSpinUp);
|
||
|
||
if ((Irp == OriginalIrp) && (Irp->PendingReturned)) {
|
||
// only for original power irp
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
|
||
PowerContext->PowerChangeState.PowerUp++;
|
||
|
||
switch (PowerContext->PowerChangeState.PowerUp) {
|
||
|
||
case PowerUpDeviceLocked: {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously sent power lock\n", Irp));
|
||
|
||
//
|
||
// Lock Queue operation has been sent.
|
||
// Now, send the original power irp down to get lower driver and device ready.
|
||
//
|
||
|
||
IoCopyCurrentIrpStackLocationToNext(OriginalIrp);
|
||
|
||
if ((PowerContext->Options.LockQueue == TRUE) &&
|
||
(!NT_SUCCESS(Irp->IoStatus.Status))) {
|
||
|
||
//
|
||
// Lock was not successful:
|
||
// Issue the original power request to the lower driver and next power irp will be started in completion routine.
|
||
//
|
||
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIrp status was %lx\n",
|
||
Irp, Irp->IoStatus.Status));
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSrb status was %lx\n",
|
||
Irp, srbHeader->SrbStatus));
|
||
|
||
IoSetCompletionRoutine(OriginalIrp,
|
||
ClasspDeviceLockFailurePowerIrpCompletion,
|
||
PowerContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
PoCallDriver(commonExtension->LowerDeviceObject, OriginalIrp);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} else {
|
||
PowerContext->QueueLocked = (UCHAR)PowerContext->Options.LockQueue;
|
||
}
|
||
|
||
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
||
|
||
PowerContext->PowerChangeState.PowerUp = PowerUpDeviceLocked;
|
||
|
||
IoSetCompletionRoutine(OriginalIrp,
|
||
ClasspPowerUpCompletion,
|
||
PowerContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
status = PoCallDriver(commonExtension->LowerDeviceObject, OriginalIrp);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n", OriginalIrp, status));
|
||
break;
|
||
}
|
||
|
||
case PowerUpDeviceOn: {
|
||
|
||
//
|
||
// Original power irp has been completed by lower driver.
|
||
//
|
||
|
||
if (NT_SUCCESS(Irp->IoStatus.Status)) {
|
||
//
|
||
// If power irp succeeded, START UNIT command will be sent.
|
||
//
|
||
PCDB cdb;
|
||
ULONG secondsRemaining = 0;
|
||
ULONG timeoutValue = 0;
|
||
ULONG startUnitTimeout;
|
||
|
||
if (PoQueryWatchdogTime(fdoExtension->LowerPdo, &secondsRemaining)) {
|
||
|
||
// do not exceed DEFAULT_POWER_IRP_TIMEOUT_VALUE.
|
||
secondsRemaining = min(secondsRemaining, DEFAULT_POWER_IRP_TIMEOUT_VALUE);
|
||
|
||
//
|
||
// It's possible for POWER IRP timeout value to be smaller than default of
|
||
// START_UNIT_TIMEOUT. If this is the case, use a smaller timeout value.
|
||
//
|
||
if (secondsRemaining >= START_UNIT_TIMEOUT) {
|
||
startUnitTimeout = START_UNIT_TIMEOUT;
|
||
} else {
|
||
startUnitTimeout = MINIMAL_START_UNIT_TIMEOUT_VALUE;
|
||
}
|
||
|
||
// plan to leave (TIME_LEFT_FOR_UPPER_DRIVERS) seconds to upper level drivers
|
||
// for processing original power irp.
|
||
if (secondsRemaining >= (TIME_LEFT_FOR_UPPER_DRIVERS + startUnitTimeout)) {
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount =
|
||
(secondsRemaining - TIME_LEFT_FOR_UPPER_DRIVERS) / startUnitTimeout;
|
||
|
||
// * No 'short' timeouts
|
||
//
|
||
//
|
||
// timeoutValue = (secondsRemaining - TIME_LEFT_FOR_UPPER_DRIVERS) %
|
||
// startUnitTimeout;
|
||
//
|
||
|
||
if (--fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount)
|
||
{
|
||
timeoutValue = startUnitTimeout;
|
||
} else {
|
||
timeoutValue = secondsRemaining - TIME_LEFT_FOR_UPPER_DRIVERS;
|
||
}
|
||
} else {
|
||
// issue the command with minimum timeout value and do not retry on it.
|
||
// case of (secondsRemaining < DEFAULT_IO_TIMEOUT_VALUE) is ignored as it should not happen.
|
||
NT_ASSERT(secondsRemaining >= DEFAULT_IO_TIMEOUT_VALUE);
|
||
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = 0;
|
||
timeoutValue = MINIMUM_START_UNIT_TIMEOUT_VALUE; // use the minimum value for this corner case.
|
||
}
|
||
|
||
} else {
|
||
// don't know how long left, do not exceed DEFAULT_POWER_IRP_TIMEOUT_VALUE.
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount =
|
||
DEFAULT_POWER_IRP_TIMEOUT_VALUE / START_UNIT_TIMEOUT - 1;
|
||
timeoutValue = START_UNIT_TIMEOUT;
|
||
}
|
||
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSending start unit to device\n", Irp));
|
||
|
||
//
|
||
// Issue the start unit command to the device.
|
||
//
|
||
|
||
PowerContext->RetryCount = fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount;
|
||
|
||
if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
|
||
status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
|
||
STORAGE_ADDRESS_TYPE_BTL8,
|
||
CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
|
||
1,
|
||
SrbExDataTypeScsiCdb16);
|
||
if (NT_SUCCESS(status)) {
|
||
((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
|
||
|
||
//
|
||
// Set length field in Power Context SRB so we know legacy SRB is not being used.
|
||
//
|
||
PowerContext->Srb.Length = 0;
|
||
|
||
} else {
|
||
//
|
||
// Should not happen. Revert to legacy SRB.
|
||
//
|
||
NT_ASSERT(FALSE);
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
|
||
RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
}
|
||
|
||
} else {
|
||
RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
}
|
||
|
||
SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
SrbSetSenseInfoBuffer(srbHeader, commonExtension->PartitionZeroExtension->SenseData);
|
||
SrbSetSenseInfoBufferLength(srbHeader, GET_FDO_EXTENSON_SENSE_DATA_LENGTH(commonExtension->PartitionZeroExtension));
|
||
|
||
SrbSetTimeOutValue(srbHeader, timeoutValue);
|
||
SrbAssignSrbFlags(srbHeader,
|
||
(SRB_FLAGS_NO_DATA_TRANSFER |
|
||
SRB_FLAGS_DISABLE_AUTOSENSE |
|
||
SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
|
||
SRB_FLAGS_NO_QUEUE_FREEZE));
|
||
|
||
if (PowerContext->Options.LockQueue) {
|
||
SrbSetSrbFlags(srbHeader, SRB_FLAGS_BYPASS_LOCKED_QUEUE);
|
||
}
|
||
|
||
SrbSetCdbLength(srbHeader, 6);
|
||
|
||
cdb = SrbGetCdb(srbHeader);
|
||
RtlZeroMemory(cdb, sizeof(CDB));
|
||
|
||
cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
|
||
cdb->START_STOP.Start = 1;
|
||
|
||
PowerContext->PowerChangeState.PowerUp = PowerUpDeviceOn;
|
||
|
||
IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
|
||
ClasspPowerUpCompletion,
|
||
PowerContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
nextStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
|
||
nextStack->MajorFunction = IRP_MJ_SCSI;
|
||
|
||
status = IoCallDriver(commonExtension->LowerDeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n", fdoExtension->PrivateFdoData->PowerProcessIrp, status));
|
||
|
||
} else {
|
||
|
||
//
|
||
// power irp is failed by lower driver. we're done.
|
||
//
|
||
|
||
PowerContext->FinalStatus = Irp->IoStatus.Status;
|
||
goto ClasspPowerUpCompletionFailure;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case PowerUpDeviceStarted: { // 3
|
||
|
||
//
|
||
// First deal with an error if one occurred.
|
||
//
|
||
|
||
if (SRB_STATUS(srbHeader->SrbStatus) != SRB_STATUS_SUCCESS) {
|
||
|
||
BOOLEAN retry;
|
||
LONGLONG delta100nsUnits = 0;
|
||
ULONG secondsRemaining = 0;
|
||
ULONG startUnitTimeout = START_UNIT_TIMEOUT;
|
||
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_POWER, "%p\tError occured when issuing START_UNIT "
|
||
"command to device. Srb %p, Status %x\n",
|
||
Irp,
|
||
srbHeader,
|
||
srbHeader->SrbStatus));
|
||
|
||
NT_ASSERT(!(TEST_FLAG(srbHeader->SrbStatus, SRB_STATUS_QUEUE_FROZEN)));
|
||
NT_ASSERT((srbHeader->Function == SRB_FUNCTION_EXECUTE_SCSI) ||
|
||
(((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI));
|
||
|
||
PowerContext->RetryInterval = 0;
|
||
retry = InterpretSenseInfoWithoutHistory(
|
||
fdoExtension->DeviceObject,
|
||
Irp,
|
||
(PSCSI_REQUEST_BLOCK)srbHeader,
|
||
IRP_MJ_SCSI,
|
||
IRP_MJ_POWER,
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount - PowerContext->RetryCount,
|
||
&status,
|
||
&delta100nsUnits);
|
||
|
||
// NOTE: Power context is a public structure, and thus cannot be
|
||
// updated to use 100ns units. Therefore, must store the
|
||
// one-second equivalent. Round up to ensure minimum delay
|
||
// requirements have been met.
|
||
delta100nsUnits += (10*1000*1000) - 1;
|
||
delta100nsUnits /= (10*1000*1000);
|
||
// guaranteed not to have high bits set per SAL annotations
|
||
PowerContext->RetryInterval = (ULONG)(delta100nsUnits);
|
||
|
||
|
||
if ((retry == TRUE) && (PowerContext->RetryCount-- != 0)) {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tRetrying failed request\n", Irp));
|
||
|
||
//
|
||
// Decrement the state so we come back through here the
|
||
// next time.
|
||
//
|
||
|
||
PowerContext->PowerChangeState.PowerUp--;
|
||
|
||
//
|
||
// Adjust start unit timeout based on remaining time if needed.
|
||
//
|
||
if (PoQueryWatchdogTime(fdoExtension->LowerPdo, &secondsRemaining)) {
|
||
|
||
if (secondsRemaining >= TIME_LEFT_FOR_UPPER_DRIVERS) {
|
||
secondsRemaining -= TIME_LEFT_FOR_UPPER_DRIVERS;
|
||
}
|
||
|
||
if (secondsRemaining < MINIMAL_START_UNIT_TIMEOUT_VALUE) {
|
||
startUnitTimeout = MINIMUM_START_UNIT_TIMEOUT_VALUE;
|
||
} else if (secondsRemaining < START_UNIT_TIMEOUT) {
|
||
startUnitTimeout = MINIMAL_START_UNIT_TIMEOUT_VALUE;
|
||
}
|
||
}
|
||
|
||
SrbSetTimeOutValue(srbHeader, startUnitTimeout);
|
||
|
||
RetryPowerRequest(commonExtension->DeviceObject,
|
||
Irp,
|
||
PowerContext);
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
// reset retry count for UNLOCK command.
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
|
||
PowerContext->RetryCount = MAXIMUM_RETRIES;
|
||
}
|
||
|
||
ClasspPowerUpCompletionFailure:
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously spun device up\n", Irp));
|
||
|
||
if (PowerContext->QueueLocked) {
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tUnlocking queue\n", Irp));
|
||
|
||
if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
|
||
//
|
||
// Will reuse SRB for a non-SCSI SRB.
|
||
//
|
||
status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
|
||
STORAGE_ADDRESS_TYPE_BTL8,
|
||
CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE,
|
||
0);
|
||
if (NT_SUCCESS(status)) {
|
||
((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_UNLOCK_QUEUE;
|
||
|
||
//
|
||
// Set length field in Power Context SRB so we know legacy SRB is not being used.
|
||
//
|
||
PowerContext->Srb.Length = 0;
|
||
|
||
} else {
|
||
//
|
||
// Should not occur. Revert to legacy SRB.
|
||
NT_ASSERT(FALSE);
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
|
||
RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_UNLOCK_QUEUE;
|
||
}
|
||
} else {
|
||
RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_UNLOCK_QUEUE;
|
||
}
|
||
SrbAssignSrbFlags(srbHeader, SRB_FLAGS_BYPASS_LOCKED_QUEUE);
|
||
SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
|
||
nextStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
|
||
nextStack->MajorFunction = IRP_MJ_SCSI;
|
||
|
||
PowerContext->PowerChangeState.PowerUp = PowerUpDeviceStarted;
|
||
|
||
IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
|
||
ClasspPowerUpCompletion,
|
||
PowerContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
status = IoCallDriver(commonExtension->LowerDeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n",
|
||
fdoExtension->PrivateFdoData->PowerProcessIrp, status));
|
||
break;
|
||
}
|
||
|
||
// Fall-through to next case...
|
||
|
||
}
|
||
|
||
case PowerUpDeviceUnlocked: {
|
||
|
||
//
|
||
// This is the end of the dance.
|
||
// We're ignoring possible intermediate error conditions ....
|
||
//
|
||
|
||
if (PowerContext->QueueLocked) {
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously unlocked queue\n", OriginalIrp));
|
||
|
||
//
|
||
// If the lower device is being removed, the IRP's status may be STATUS_DELETE_PENDING or
|
||
// STATUS_DEVICE_DOES_NOT_EXIST.
|
||
//
|
||
if((NT_SUCCESS(Irp->IoStatus.Status) == FALSE) &&
|
||
(Irp->IoStatus.Status != STATUS_DELETE_PENDING) &&
|
||
(Irp->IoStatus.Status != STATUS_DEVICE_DOES_NOT_EXIST)) {
|
||
|
||
|
||
NT_ASSERT(FALSE);
|
||
}
|
||
|
||
} else {
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tFall-through (queue not locked)\n", OriginalIrp));
|
||
}
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tFreeing srb and completing\n", OriginalIrp));
|
||
|
||
status = PowerContext->FinalStatus;
|
||
OriginalIrp->IoStatus.Status = status;
|
||
|
||
//
|
||
// Set the new power state
|
||
//
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
fdoExtension->DevicePowerState = currentStack->Parameters.Power.State.DeviceState;
|
||
}
|
||
|
||
//
|
||
// Check whether failure detection is enabled
|
||
//
|
||
|
||
if ((fdoExtension->FailurePredictionInfo != NULL) &&
|
||
(fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone)) {
|
||
FailurePredictionEnabled = TRUE;
|
||
}
|
||
|
||
//
|
||
// Enable tick timer at end of D0 processing if it was previously enabled.
|
||
//
|
||
|
||
if ((commonExtension->DriverExtension->InitData.ClassTick != NULL) ||
|
||
((fdoExtension->MediaChangeDetectionInfo != NULL) &&
|
||
(fdoExtension->FunctionSupportInfo != NULL) &&
|
||
(fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported == FALSE)) ||
|
||
(FailurePredictionEnabled)) {
|
||
|
||
|
||
//
|
||
// If failure prediction is turned on and we've been powered
|
||
// off longer than the failure prediction query period then
|
||
// force the query on the next timer tick.
|
||
//
|
||
|
||
if ((FailurePredictionEnabled) && (ClasspFailurePredictionPeriodMissed(fdoExtension))) {
|
||
fdoExtension->FailurePredictionInfo->CountDown = 1;
|
||
}
|
||
|
||
//
|
||
// Finally, enable the timer.
|
||
//
|
||
|
||
ClasspEnableTimer(fdoExtension);
|
||
}
|
||
|
||
//
|
||
// Indicate to Po that we've been successfully powered up so
|
||
// it can do it's notification stuff.
|
||
//
|
||
|
||
PoSetPowerState(PowerContext->DeviceObject,
|
||
currentStack->Parameters.Power.Type,
|
||
currentStack->Parameters.Power.State);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tStarting next power irp\n", OriginalIrp));
|
||
|
||
ClassReleaseRemoveLock(PowerContext->DeviceObject, OriginalIrp);
|
||
|
||
PowerContext->InUse = FALSE;
|
||
|
||
PoStartNextPowerIrp(OriginalIrp);
|
||
|
||
// prevent from completing the irp allocated by ourselves
|
||
if ((fdoExtension->PrivateFdoData) && (Irp == fdoExtension->PrivateFdoData->PowerProcessIrp)) {
|
||
// complete original irp if we are processing powerprocess irp,
|
||
// otherwise, by returning status other than STATUS_MORE_PROCESSING_REQUIRED, IO manager will complete it.
|
||
ClassCompleteRequest(commonExtension->DeviceObject, OriginalIrp, IO_NO_INCREMENT);
|
||
status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
}
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
} // end ClasspPowerUpCompletion()
|
||
|
||
/*++////////////////////////////////////////////////////////////////////////////
|
||
|
||
ClasspPowerDownCompletion()
|
||
|
||
Routine Description:
|
||
|
||
This routine is used for intermediate completion of a power down request.
|
||
PowerDown performs the following sequence to power down the device.
|
||
|
||
1. The queue(s) in the lower stack is/are "power locked" to ensure new
|
||
requests are held until the power-down process is complete.
|
||
|
||
2. A request to the lower layers to wait for all outstanding IO to
|
||
complete ("quiescence") is sent. This ensures we don't power down
|
||
the device while it's in the middle of handling IO.
|
||
|
||
3. A request to flush the device's cache is sent. The device may lose
|
||
power when we forward the D-IRP so any data in volatile storage must
|
||
be committed to non-volatile storage first.
|
||
|
||
4. A "stop unit" request is sent to the device to notify it that it
|
||
is about to be powered down.
|
||
|
||
5. The D-IRP is forwarded down the stack. If D3Cold is supported and
|
||
enabled via ACPI, the ACPI filter driver may power off the device.
|
||
|
||
6. Once the D-IRP is completed by the lower stack, we will "power
|
||
unlock" the queue(s). (It is the lower stack's responsibility to
|
||
continue to queue any IO that requires hardware access until the
|
||
device is powered up again.)
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - the device object being powered down
|
||
|
||
Irp - the IO_REQUEST_PACKET containing the power request
|
||
|
||
Context - the class power context used to perform port/class operations.
|
||
|
||
Return Value:
|
||
|
||
STATUS_MORE_PROCESSING_REQUIRED or
|
||
STATUS_SUCCESS
|
||
|
||
--*/
|
||
NTSTATUS
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
ClasspPowerDownCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
PCLASS_POWER_CONTEXT PowerContext = (PCLASS_POWER_CONTEXT)Context;
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = PowerContext->DeviceObject->DeviceExtension;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = PowerContext->DeviceObject->DeviceExtension;
|
||
PIRP OriginalIrp = PowerContext->Irp;
|
||
|
||
// currentStack is for original power irp
|
||
// nextStack is for power process irp
|
||
PIO_STACK_LOCATION currentStack = IoGetCurrentIrpStackLocation(OriginalIrp);
|
||
PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
|
||
NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
PSTORAGE_REQUEST_BLOCK_HEADER srbHeader;
|
||
ULONG srbFlags;
|
||
|
||
UNREFERENCED_PARAMETER(DeviceObject);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "ClasspPowerDownCompletion: Device Object %p, "
|
||
"Irp %p, Context %p\n",
|
||
PowerContext->DeviceObject, Irp, Context));
|
||
|
||
if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->PowerSrb.SrbEx);
|
||
|
||
//
|
||
// Check if reverted to using legacy SRB.
|
||
//
|
||
if (PowerContext->Srb.Length == sizeof(SCSI_REQUEST_BLOCK)) {
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
|
||
}
|
||
} else {
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
|
||
}
|
||
|
||
srbFlags = SrbGetSrbFlags(srbHeader);
|
||
NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
|
||
NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
|
||
NT_ASSERT(PowerContext->Options.PowerDown == TRUE);
|
||
NT_ASSERT(PowerContext->Options.HandleSpinDown);
|
||
|
||
if ((Irp == OriginalIrp) && (Irp->PendingReturned)) {
|
||
// only for original power irp
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
|
||
PowerContext->PowerChangeState.PowerDown3++;
|
||
|
||
switch(PowerContext->PowerChangeState.PowerDown3) {
|
||
|
||
case PowerDownDeviceLocked3: {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously sent power lock\n", Irp));
|
||
|
||
if ((PowerContext->Options.LockQueue == TRUE) &&
|
||
(!NT_SUCCESS(Irp->IoStatus.Status))) {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIrp status was %lx\n",
|
||
Irp,
|
||
Irp->IoStatus.Status));
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSrb status was %lx\n",
|
||
Irp,
|
||
srbHeader->SrbStatus));
|
||
|
||
|
||
|
||
//
|
||
// Lock was not successful - throw down the power IRP
|
||
// by itself and don't try to spin down the drive or unlock
|
||
// the queue.
|
||
//
|
||
|
||
//
|
||
// Set the new power state
|
||
//
|
||
|
||
fdoExtension->DevicePowerState =
|
||
currentStack->Parameters.Power.State.DeviceState;
|
||
|
||
//
|
||
// Indicate to Po that we've been successfully powered down
|
||
// so it can do it's notification stuff.
|
||
//
|
||
|
||
IoCopyCurrentIrpStackLocationToNext(OriginalIrp);
|
||
IoSetCompletionRoutine(OriginalIrp,
|
||
ClasspStartNextPowerIrpCompletion,
|
||
PowerContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
PoSetPowerState(PowerContext->DeviceObject,
|
||
currentStack->Parameters.Power.Type,
|
||
currentStack->Parameters.Power.State);
|
||
|
||
fdoExtension->PowerDownInProgress = FALSE;
|
||
|
||
ClassReleaseRemoveLock(commonExtension->DeviceObject,
|
||
OriginalIrp);
|
||
|
||
PoCallDriver(commonExtension->LowerDeviceObject, OriginalIrp);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} else {
|
||
//
|
||
// Lock the device queue succeeded. Now wait for all outstanding IO to complete.
|
||
// To do this, Srb with SRB_FUNCTION_QUIESCE_DEVICE will be sent down with default timeout value.
|
||
// We need to tolerant failure of this request, no retry will be made.
|
||
//
|
||
PowerContext->QueueLocked = (UCHAR) PowerContext->Options.LockQueue;
|
||
|
||
//
|
||
// No retry on device quiescence reqeust
|
||
//
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = 0;
|
||
PowerContext->RetryCount = 0;
|
||
|
||
if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->PowerSrb.SrbEx);
|
||
|
||
//
|
||
// Initialize extended SRB for a SRB_FUNCTION_LOCK_QUEUE
|
||
//
|
||
status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
|
||
STORAGE_ADDRESS_TYPE_BTL8,
|
||
CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE,
|
||
0);
|
||
if (NT_SUCCESS(status)) {
|
||
((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_QUIESCE_DEVICE;
|
||
} else {
|
||
//
|
||
// Should not happen. Revert to legacy SRB.
|
||
//
|
||
NT_ASSERT(FALSE);
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_QUIESCE_DEVICE;
|
||
}
|
||
} else {
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_QUIESCE_DEVICE;
|
||
}
|
||
|
||
SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
SrbSetTimeOutValue(srbHeader, fdoExtension->TimeOutValue);
|
||
|
||
SrbAssignSrbFlags(srbHeader,
|
||
(SRB_FLAGS_NO_DATA_TRANSFER |
|
||
SRB_FLAGS_DISABLE_AUTOSENSE |
|
||
SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
|
||
SRB_FLAGS_NO_QUEUE_FREEZE |
|
||
SRB_FLAGS_BYPASS_LOCKED_QUEUE |
|
||
SRB_FLAGS_D3_PROCESSING));
|
||
|
||
IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
|
||
ClasspPowerDownCompletion,
|
||
PowerContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
nextStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
|
||
nextStack->MajorFunction = IRP_MJ_SCSI;
|
||
|
||
status = IoCallDriver(commonExtension->LowerDeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n", fdoExtension->PrivateFdoData->PowerProcessIrp, status));
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
case PowerDownDeviceQuiesced3: {
|
||
|
||
PCDB cdb;
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously sent device quiesce\n", Irp));
|
||
|
||
//
|
||
// don't care the result of device quiesce, we've made the effort.
|
||
// continue on sending other SCSI commands anyway.
|
||
//
|
||
|
||
|
||
if (!TEST_FLAG(fdoExtension->PrivateFdoData->HackFlags,
|
||
FDO_HACK_NO_SYNC_CACHE)) {
|
||
|
||
//
|
||
// send SCSIOP_SYNCHRONIZE_CACHE
|
||
//
|
||
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
|
||
PowerContext->RetryCount = MAXIMUM_RETRIES;
|
||
|
||
if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
|
||
status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
|
||
STORAGE_ADDRESS_TYPE_BTL8,
|
||
CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
|
||
1,
|
||
SrbExDataTypeScsiCdb16);
|
||
if (NT_SUCCESS(status)) {
|
||
((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
|
||
|
||
//
|
||
// Set length field in Power Context SRB so we know legacy SRB is not being used.
|
||
//
|
||
PowerContext->Srb.Length = 0;
|
||
|
||
} else {
|
||
//
|
||
// Should not occur. Revert to legacy SRB.
|
||
NT_ASSERT(FALSE);
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
|
||
RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
}
|
||
|
||
} else {
|
||
RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
}
|
||
|
||
|
||
SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
SrbSetSenseInfoBuffer(srbHeader, commonExtension->PartitionZeroExtension->SenseData);
|
||
SrbSetSenseInfoBufferLength(srbHeader, GET_FDO_EXTENSON_SENSE_DATA_LENGTH(commonExtension->PartitionZeroExtension));
|
||
SrbSetTimeOutValue(srbHeader, fdoExtension->TimeOutValue);
|
||
|
||
SrbAssignSrbFlags(srbHeader,
|
||
(SRB_FLAGS_NO_DATA_TRANSFER |
|
||
SRB_FLAGS_DISABLE_AUTOSENSE |
|
||
SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
|
||
SRB_FLAGS_NO_QUEUE_FREEZE |
|
||
SRB_FLAGS_BYPASS_LOCKED_QUEUE |
|
||
SRB_FLAGS_D3_PROCESSING));
|
||
|
||
SrbSetCdbLength(srbHeader, 10);
|
||
|
||
cdb = SrbGetCdb(srbHeader);
|
||
|
||
RtlZeroMemory(cdb, sizeof(CDB));
|
||
cdb->SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
|
||
|
||
IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
|
||
ClasspPowerDownCompletion,
|
||
PowerContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
nextStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
|
||
nextStack->MajorFunction = IRP_MJ_SCSI;
|
||
|
||
status = IoCallDriver(commonExtension->LowerDeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n", fdoExtension->PrivateFdoData->PowerProcessIrp, status));
|
||
break;
|
||
|
||
} else {
|
||
|
||
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_POWER, "(%p)\tPower Down: not sending SYNCH_CACHE\n",
|
||
PowerContext->DeviceObject));
|
||
PowerContext->PowerChangeState.PowerDown3++;
|
||
srbHeader->SrbStatus = SRB_STATUS_SUCCESS;
|
||
// and fall through....
|
||
}
|
||
// no break in case the device doesn't like synch_cache commands
|
||
|
||
}
|
||
|
||
case PowerDownDeviceFlushed3: {
|
||
|
||
PCDB cdb;
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously send SCSIOP_SYNCHRONIZE_CACHE\n",
|
||
Irp));
|
||
|
||
//
|
||
// SCSIOP_SYNCHRONIZE_CACHE was sent
|
||
//
|
||
|
||
if (SRB_STATUS(srbHeader->SrbStatus) != SRB_STATUS_SUCCESS) {
|
||
|
||
BOOLEAN retry;
|
||
LONGLONG delta100nsUnits = 0;
|
||
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_POWER, "(%p)\tError occured when issuing "
|
||
"SYNCHRONIZE_CACHE command to device. "
|
||
"Srb %p, Status %lx\n",
|
||
Irp,
|
||
srbHeader,
|
||
srbHeader->SrbStatus));
|
||
|
||
NT_ASSERT(!(TEST_FLAG(srbHeader->SrbStatus, SRB_STATUS_QUEUE_FROZEN)));
|
||
NT_ASSERT((srbHeader->Function == SRB_FUNCTION_EXECUTE_SCSI) ||
|
||
(((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI));
|
||
|
||
PowerContext->RetryInterval = 0;
|
||
retry = InterpretSenseInfoWithoutHistory(
|
||
fdoExtension->DeviceObject,
|
||
Irp,
|
||
(PSCSI_REQUEST_BLOCK)srbHeader,
|
||
IRP_MJ_SCSI,
|
||
IRP_MJ_POWER,
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount - PowerContext->RetryCount,
|
||
&status,
|
||
&delta100nsUnits);
|
||
|
||
// NOTE: Power context is a public structure, and thus cannot be
|
||
// updated to use 100ns units. Therefore, must store the
|
||
// one-second equivalent. Round up to ensure minimum delay
|
||
// requirements have been met.
|
||
delta100nsUnits += (10*1000*1000) - 1;
|
||
delta100nsUnits /= (10*1000*1000);
|
||
// guaranteed not to have high bits set per SAL annotations
|
||
PowerContext->RetryInterval = (ULONG)(delta100nsUnits);
|
||
|
||
|
||
if ((retry == TRUE) && (PowerContext->RetryCount-- != 0)) {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tRetrying failed request\n", Irp));
|
||
|
||
//
|
||
// decrement the state so we come back through here
|
||
// the next time.
|
||
//
|
||
|
||
PowerContext->PowerChangeState.PowerDown3--;
|
||
RetryPowerRequest(commonExtension->DeviceObject,
|
||
Irp,
|
||
PowerContext);
|
||
break;
|
||
}
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSYNCHRONIZE_CACHE not retried\n", Irp));
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
|
||
PowerContext->RetryCount = MAXIMUM_RETRIES;
|
||
} // end !SRB_STATUS_SUCCESS
|
||
|
||
//
|
||
// note: we are purposefully ignoring any errors. if the drive
|
||
// doesn't support a synch_cache, then we're up a creek
|
||
// anyways.
|
||
//
|
||
|
||
if ((currentStack->Parameters.Power.State.DeviceState == PowerDeviceD3) &&
|
||
(currentStack->Parameters.Power.ShutdownType == PowerActionHibernate) &&
|
||
(commonExtension->HibernationPathCount != 0)) {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPower Down: not sending SPIN DOWN due to hibernation path\n",
|
||
PowerContext->DeviceObject));
|
||
|
||
PowerContext->PowerChangeState.PowerDown3++;
|
||
srbHeader->SrbStatus = SRB_STATUS_SUCCESS;
|
||
status = STATUS_SUCCESS;
|
||
|
||
// Fall through to next case...
|
||
|
||
} else {
|
||
// Send STOP UNIT command. As "Imme" bit is set to '1', this command should be completed in short time.
|
||
// This command is at low importance, failure of this command has very small impact.
|
||
|
||
ULONG secondsRemaining;
|
||
ULONG timeoutValue;
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSending stop unit to device\n", Irp));
|
||
|
||
if (PoQueryWatchdogTime(fdoExtension->LowerPdo, &secondsRemaining)) {
|
||
// plan to leave some time (TIME_LEFT_FOR_LOWER_DRIVERS) to lower level drivers
|
||
// for processing the original power irp.
|
||
if (secondsRemaining >= (TIME_LEFT_FOR_LOWER_DRIVERS + DEFAULT_IO_TIMEOUT_VALUE)) {
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount =
|
||
(secondsRemaining - TIME_LEFT_FOR_LOWER_DRIVERS) / DEFAULT_IO_TIMEOUT_VALUE;
|
||
|
||
// * No 'short' timeouts
|
||
//
|
||
// timeoutValue = (secondsRemaining - TIME_LEFT_FOR_LOWER_DRIVERS) %
|
||
// DEFAULT_IO_TIMEOUT_VALUE;
|
||
// if (timeoutValue < MINIMUM_STOP_UNIT_TIMEOUT_VALUE)
|
||
// {
|
||
if (--fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount)
|
||
{
|
||
timeoutValue = DEFAULT_IO_TIMEOUT_VALUE;
|
||
} else {
|
||
timeoutValue = secondsRemaining - TIME_LEFT_FOR_LOWER_DRIVERS;
|
||
}
|
||
// }
|
||
|
||
// Limit to maximum retry count.
|
||
if (fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount > MAXIMUM_RETRIES) {
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
|
||
}
|
||
} else {
|
||
// issue the command with minimum timeout value and do not retry on it.
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = 0;
|
||
|
||
// minimum as MINIMUM_STOP_UNIT_TIMEOUT_VALUE.
|
||
if (secondsRemaining > 2 * MINIMUM_STOP_UNIT_TIMEOUT_VALUE) {
|
||
timeoutValue = secondsRemaining - MINIMUM_STOP_UNIT_TIMEOUT_VALUE;
|
||
} else {
|
||
timeoutValue = MINIMUM_STOP_UNIT_TIMEOUT_VALUE;
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
// do not know how long, use default values.
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
|
||
timeoutValue = DEFAULT_IO_TIMEOUT_VALUE;
|
||
}
|
||
|
||
//
|
||
// Issue STOP UNIT command to the device.
|
||
//
|
||
|
||
PowerContext->RetryCount = fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount;
|
||
|
||
if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
|
||
status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
|
||
STORAGE_ADDRESS_TYPE_BTL8,
|
||
CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
|
||
1,
|
||
SrbExDataTypeScsiCdb16);
|
||
if (NT_SUCCESS(status)) {
|
||
((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
|
||
|
||
//
|
||
// Set length field in Power Context SRB so we know legacy SRB is not being used.
|
||
//
|
||
PowerContext->Srb.Length = 0;
|
||
|
||
} else {
|
||
//
|
||
// Should not occur. Revert to legacy SRB.
|
||
//
|
||
NT_ASSERT(FALSE);
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
|
||
RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
}
|
||
|
||
} else {
|
||
RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
}
|
||
|
||
SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
SrbSetSenseInfoBuffer(srbHeader, commonExtension->PartitionZeroExtension->SenseData);
|
||
SrbSetSenseInfoBufferLength(srbHeader, GET_FDO_EXTENSON_SENSE_DATA_LENGTH(commonExtension->PartitionZeroExtension));
|
||
SrbSetTimeOutValue(srbHeader, timeoutValue);
|
||
|
||
|
||
SrbAssignSrbFlags(srbHeader,
|
||
(SRB_FLAGS_NO_DATA_TRANSFER |
|
||
SRB_FLAGS_DISABLE_AUTOSENSE |
|
||
SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
|
||
SRB_FLAGS_NO_QUEUE_FREEZE |
|
||
SRB_FLAGS_BYPASS_LOCKED_QUEUE |
|
||
SRB_FLAGS_D3_PROCESSING));
|
||
|
||
SrbSetCdbLength(srbHeader, 6);
|
||
|
||
cdb = SrbGetCdb(srbHeader);
|
||
RtlZeroMemory(cdb, sizeof(CDB));
|
||
|
||
cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
|
||
cdb->START_STOP.Start = 0;
|
||
cdb->START_STOP.Immediate = 1;
|
||
|
||
IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
|
||
ClasspPowerDownCompletion,
|
||
PowerContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
nextStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
|
||
nextStack->MajorFunction = IRP_MJ_SCSI;
|
||
|
||
status = IoCallDriver(commonExtension->LowerDeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n", fdoExtension->PrivateFdoData->PowerProcessIrp, status));
|
||
break;
|
||
}
|
||
}
|
||
|
||
case PowerDownDeviceStopped3: {
|
||
|
||
BOOLEAN ignoreError = TRUE;
|
||
|
||
//
|
||
// stop was sent
|
||
//
|
||
|
||
if (SRB_STATUS(srbHeader->SrbStatus) != SRB_STATUS_SUCCESS) {
|
||
|
||
BOOLEAN retry;
|
||
LONGLONG delta100nsUnits = 0;
|
||
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_POWER, "(%p)\tError occured when issueing STOP_UNIT "
|
||
"command to device. Srb %p, Status %lx\n",
|
||
Irp,
|
||
srbHeader,
|
||
srbHeader->SrbStatus));
|
||
|
||
NT_ASSERT(!(TEST_FLAG(srbHeader->SrbStatus, SRB_STATUS_QUEUE_FROZEN)));
|
||
NT_ASSERT((srbHeader->Function == SRB_FUNCTION_EXECUTE_SCSI) ||
|
||
(((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI));
|
||
|
||
PowerContext->RetryInterval = 0;
|
||
retry = InterpretSenseInfoWithoutHistory(
|
||
fdoExtension->DeviceObject,
|
||
Irp,
|
||
(PSCSI_REQUEST_BLOCK)srbHeader,
|
||
IRP_MJ_SCSI,
|
||
IRP_MJ_POWER,
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount - PowerContext->RetryCount,
|
||
&status,
|
||
&delta100nsUnits);
|
||
|
||
// NOTE: Power context is a public structure, and thus cannot be
|
||
// updated to use 100ns units. Therefore, must store the
|
||
// one-second equivalent. Round up to ensure minimum delay
|
||
// requirements have been met.
|
||
delta100nsUnits += (10*1000*1000) - 1;
|
||
delta100nsUnits /= (10*1000*1000);
|
||
// guaranteed not to have high bits set per SAL annotations
|
||
PowerContext->RetryInterval = (ULONG)(delta100nsUnits);
|
||
|
||
|
||
if ((retry == TRUE) && (PowerContext->RetryCount-- != 0)) {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tRetrying failed request\n", Irp));
|
||
|
||
//
|
||
// decrement the state so we come back through here
|
||
// the next time.
|
||
//
|
||
|
||
PowerContext->PowerChangeState.PowerDown3--;
|
||
|
||
SrbSetTimeOutValue(srbHeader, DEFAULT_IO_TIMEOUT_VALUE);
|
||
|
||
RetryPowerRequest(commonExtension->DeviceObject,
|
||
Irp,
|
||
PowerContext);
|
||
break;
|
||
}
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSTOP_UNIT not retried\n", Irp));
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
|
||
PowerContext->RetryCount = MAXIMUM_RETRIES;
|
||
|
||
} // end !SRB_STATUS_SUCCESS
|
||
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously sent stop unit\n", Irp));
|
||
|
||
//
|
||
// some operations, such as a physical format in progress,
|
||
// should not be ignored and should fail the power operation.
|
||
//
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
PVOID senseBuffer = SrbGetSenseInfoBuffer(srbHeader);
|
||
|
||
if (TEST_FLAG(srbHeader->SrbStatus, SRB_STATUS_AUTOSENSE_VALID) &&
|
||
(senseBuffer != NULL)) {
|
||
|
||
BOOLEAN validSense = FALSE;
|
||
UCHAR senseKey = 0;
|
||
UCHAR additionalSenseCode = 0;
|
||
UCHAR additionalSenseCodeQualifier = 0;
|
||
|
||
validSense = ScsiGetSenseKeyAndCodes(senseBuffer,
|
||
SrbGetSenseInfoBufferLength(srbHeader),
|
||
SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
|
||
&senseKey,
|
||
&additionalSenseCode,
|
||
&additionalSenseCodeQualifier);
|
||
|
||
if (validSense) {
|
||
if ((senseKey == SCSI_SENSE_NOT_READY) &&
|
||
(additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
|
||
(additionalSenseCodeQualifier == SCSI_SENSEQ_FORMAT_IN_PROGRESS)) {
|
||
|
||
ignoreError = FALSE;
|
||
PowerContext->FinalStatus = STATUS_DEVICE_BUSY;
|
||
status = PowerContext->FinalStatus;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(status) || ignoreError) {
|
||
|
||
//
|
||
// Issue the original power request to the lower driver.
|
||
//
|
||
|
||
IoCopyCurrentIrpStackLocationToNext(OriginalIrp);
|
||
|
||
IoSetCompletionRoutine(OriginalIrp,
|
||
ClasspPowerDownCompletion,
|
||
PowerContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
status = PoCallDriver(commonExtension->LowerDeviceObject, OriginalIrp);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPoCallDriver returned %lx\n", OriginalIrp, status));
|
||
break;
|
||
}
|
||
|
||
// else fall through w/o sending the power irp, since the device
|
||
// is reporting an error that would be "really bad" to power down
|
||
// during.
|
||
|
||
}
|
||
|
||
case PowerDownDeviceOff3: {
|
||
|
||
//
|
||
// SpinDown request completed ... whether it succeeded or not is
|
||
// another matter entirely.
|
||
//
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously sent power irp\n", OriginalIrp));
|
||
|
||
if (PowerContext->QueueLocked) {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tUnlocking queue\n", OriginalIrp));
|
||
|
||
if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
|
||
//
|
||
// Will reuse SRB for a non-SCSI SRB.
|
||
//
|
||
status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
|
||
STORAGE_ADDRESS_TYPE_BTL8,
|
||
CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE,
|
||
0);
|
||
if (NT_SUCCESS(status)) {
|
||
((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_UNLOCK_QUEUE;
|
||
|
||
//
|
||
// Set length field in Power Context SRB so we know legacy SRB is not being used.
|
||
//
|
||
PowerContext->Srb.Length = 0;
|
||
|
||
} else {
|
||
//
|
||
// Should not occur. Revert to legacy SRB.
|
||
//
|
||
NT_ASSERT(FALSE);
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
|
||
RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_UNLOCK_QUEUE;
|
||
}
|
||
} else {
|
||
RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_UNLOCK_QUEUE;
|
||
}
|
||
|
||
SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
SrbAssignSrbFlags(srbHeader, (SRB_FLAGS_BYPASS_LOCKED_QUEUE |
|
||
SRB_FLAGS_D3_PROCESSING));
|
||
|
||
nextStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
|
||
nextStack->MajorFunction = IRP_MJ_SCSI;
|
||
|
||
IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
|
||
ClasspPowerDownCompletion,
|
||
PowerContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
status = IoCallDriver(commonExtension->LowerDeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n",
|
||
fdoExtension->PrivateFdoData->PowerProcessIrp,
|
||
status));
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
case PowerDownDeviceUnlocked3: {
|
||
|
||
//
|
||
// This is the end of the dance.
|
||
// We're ignoring possible intermediate error conditions ....
|
||
//
|
||
|
||
if (PowerContext->QueueLocked == FALSE) {
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tFall through (queue not locked)\n", OriginalIrp));
|
||
} else {
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously unlocked queue\n", OriginalIrp));
|
||
NT_ASSERT(NT_SUCCESS(Irp->IoStatus.Status));
|
||
NT_ASSERT(srbHeader->SrbStatus == SRB_STATUS_SUCCESS);
|
||
|
||
if (NT_SUCCESS(Irp->IoStatus.Status)) {
|
||
PowerContext->QueueLocked = FALSE;
|
||
}
|
||
}
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tFreeing srb and completing\n", OriginalIrp));
|
||
status = PowerContext->FinalStatus; // allow failure to propogate
|
||
|
||
OriginalIrp->IoStatus.Status = status;
|
||
OriginalIrp->IoStatus.Information = 0;
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Set the new power state
|
||
//
|
||
|
||
fdoExtension->DevicePowerState =
|
||
currentStack->Parameters.Power.State.DeviceState;
|
||
|
||
}
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tStarting next power irp\n", OriginalIrp));
|
||
|
||
ClassReleaseRemoveLock(PowerContext->DeviceObject, OriginalIrp);
|
||
|
||
PowerContext->InUse = FALSE;
|
||
|
||
PoStartNextPowerIrp(OriginalIrp);
|
||
|
||
fdoExtension->PowerDownInProgress = FALSE;
|
||
|
||
// prevent from completing the irp allocated by ourselves
|
||
if (Irp == fdoExtension->PrivateFdoData->PowerProcessIrp) {
|
||
// complete original irp if we are processing powerprocess irp,
|
||
// otherwise, by returning status other than STATUS_MORE_PROCESSING_REQUIRED, IO manager will complete it.
|
||
ClassCompleteRequest(commonExtension->DeviceObject, OriginalIrp, IO_NO_INCREMENT);
|
||
status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
}
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
} // end ClasspPowerDownCompletion()
|
||
|
||
/*++////////////////////////////////////////////////////////////////////////////
|
||
|
||
ClasspPowerHandler()
|
||
|
||
Routine Description:
|
||
|
||
This routine reduces the number of useless spinups and spindown requests
|
||
sent to a given device by ignoring transitions to power states we are
|
||
currently in.
|
||
|
||
ISSUE-2000/02/20-henrygab - by ignoring spin-up requests, we may be
|
||
allowing the drive
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - the device object which is transitioning power states
|
||
Irp - the power irp
|
||
Options - a set of flags indicating what the device handles
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
NTSTATUS
|
||
ClasspPowerHandler(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN CLASS_POWER_OPTIONS Options // ISSUE-2000/02/20-henrygab - pass pointer, not whole struct
|
||
)
|
||
{
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
||
PDEVICE_OBJECT lowerDevice = commonExtension->LowerDeviceObject;
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
PIO_STACK_LOCATION nextIrpStack;
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
||
PCLASS_POWER_CONTEXT context;
|
||
PSTORAGE_REQUEST_BLOCK_HEADER srbHeader;
|
||
ULONG srbFlags;
|
||
NTSTATUS status;
|
||
|
||
_Analysis_assume_(fdoExtension);
|
||
_Analysis_assume_(fdoExtension->PrivateFdoData);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "ClasspPowerHandler: Power irp %p to %s %p\n",
|
||
Irp, (commonExtension->IsFdo ? "fdo" : "pdo"), DeviceObject));
|
||
|
||
if (!commonExtension->IsFdo) {
|
||
|
||
//
|
||
// certain assumptions are made here,
|
||
// particularly: having the fdoExtension
|
||
//
|
||
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_POWER, "ClasspPowerHandler: Called for PDO %p???\n",
|
||
DeviceObject));
|
||
NT_ASSERT(!"PDO using ClasspPowerHandler");
|
||
|
||
ClassReleaseRemoveLock(DeviceObject, Irp);
|
||
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
||
PoStartNextPowerIrp(Irp);
|
||
ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
|
||
return STATUS_NOT_SUPPORTED;
|
||
}
|
||
|
||
switch (irpStack->MinorFunction) {
|
||
|
||
case IRP_MN_SET_POWER: {
|
||
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIRP_MN_SET_POWER\n", Irp));
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSetting %s state to %d\n",
|
||
Irp,
|
||
(irpStack->Parameters.Power.Type == SystemPowerState ?
|
||
"System" : "Device"),
|
||
irpStack->Parameters.Power.State.SystemState));
|
||
|
||
switch (irpStack->Parameters.Power.ShutdownType){
|
||
|
||
case PowerActionNone:
|
||
|
||
//
|
||
// Skip if device doesn't need volume verification during idle power
|
||
// transitions.
|
||
//
|
||
if ((fdoExtension->FunctionSupportInfo) &&
|
||
(fdoExtension->FunctionSupportInfo->IdlePower.NoVerifyDuringIdlePower)) {
|
||
break;
|
||
}
|
||
|
||
case PowerActionSleep:
|
||
case PowerActionHibernate:
|
||
if (fdoData->HotplugInfo.MediaRemovable || fdoData->HotplugInfo.MediaHotplug) {
|
||
/*
|
||
* We are suspending device and this drive is either hot-pluggable
|
||
* or contains removeable media.
|
||
* Set the media dirty bit, since the media may change while
|
||
* we are suspended.
|
||
*/
|
||
SET_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME);
|
||
|
||
//
|
||
// Bumping the media change count will force the
|
||
// file system to verify the volume when we resume
|
||
//
|
||
|
||
InterlockedIncrement((volatile LONG *)&fdoExtension->MediaChangeCount);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
default: {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIrp minor code = %#x\n",
|
||
Irp, irpStack->MinorFunction));
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (irpStack->Parameters.Power.Type != DevicePowerState ||
|
||
irpStack->MinorFunction != IRP_MN_SET_POWER) {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSending to lower device\n", Irp));
|
||
|
||
goto ClasspPowerHandlerCleanup;
|
||
|
||
}
|
||
|
||
//
|
||
// already in exact same state, don't work to transition to it.
|
||
//
|
||
|
||
if (irpStack->Parameters.Power.State.DeviceState ==
|
||
fdoExtension->DevicePowerState) {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tAlready in device state %x\n",
|
||
Irp, fdoExtension->DevicePowerState));
|
||
goto ClasspPowerHandlerCleanup;
|
||
|
||
}
|
||
|
||
//
|
||
// or powering down from non-d0 state (device already stopped)
|
||
// NOTE -- we're not sure whether this case can exist or not (the
|
||
// power system may never send this sort of request) but it's trivial
|
||
// to deal with.
|
||
//
|
||
|
||
if ((irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0) &&
|
||
(fdoExtension->DevicePowerState != PowerDeviceD0)) {
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tAlready powered down to %x???\n",
|
||
Irp, fdoExtension->DevicePowerState));
|
||
fdoExtension->DevicePowerState =
|
||
irpStack->Parameters.Power.State.DeviceState;
|
||
goto ClasspPowerHandlerCleanup;
|
||
}
|
||
|
||
//
|
||
// or when not handling powering up and are powering up
|
||
//
|
||
|
||
if ((!Options.HandleSpinUp) &&
|
||
(irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0)) {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tNot handling spinup to state %x\n",
|
||
Irp, fdoExtension->DevicePowerState));
|
||
fdoExtension->DevicePowerState =
|
||
irpStack->Parameters.Power.State.DeviceState;
|
||
goto ClasspPowerHandlerCleanup;
|
||
|
||
}
|
||
|
||
//
|
||
// or when not handling powering down and are powering down
|
||
//
|
||
|
||
if ((!Options.HandleSpinDown) &&
|
||
(irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0)) {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tNot handling spindown to state %x\n",
|
||
Irp, fdoExtension->DevicePowerState));
|
||
fdoExtension->DevicePowerState =
|
||
irpStack->Parameters.Power.State.DeviceState;
|
||
goto ClasspPowerHandlerCleanup;
|
||
|
||
}
|
||
|
||
//
|
||
// validation completed, start the real work.
|
||
//
|
||
|
||
IoReuseIrp(fdoExtension->PrivateFdoData->PowerProcessIrp, STATUS_SUCCESS);
|
||
IoSetNextIrpStackLocation(fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
nextIrpStack = IoGetNextIrpStackLocation(fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
|
||
context = &(fdoExtension->PowerContext);
|
||
|
||
NT_ASSERT(context->InUse == FALSE);
|
||
|
||
RtlZeroMemory(context, sizeof(CLASS_POWER_CONTEXT));
|
||
context->InUse = TRUE;
|
||
|
||
if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->PowerSrb.SrbEx);
|
||
|
||
//
|
||
// Initialize extended SRB for a SRB_FUNCTION_LOCK_QUEUE
|
||
//
|
||
status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
|
||
STORAGE_ADDRESS_TYPE_BTL8,
|
||
CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE,
|
||
0);
|
||
if (NT_SUCCESS(status)) {
|
||
((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_LOCK_QUEUE;
|
||
} else {
|
||
//
|
||
// Should not happen. Revert to legacy SRB.
|
||
//
|
||
NT_ASSERT(FALSE);
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(context->Srb);
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_LOCK_QUEUE;
|
||
}
|
||
} else {
|
||
srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(context->Srb);
|
||
srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srbHeader->Function = SRB_FUNCTION_LOCK_QUEUE;
|
||
}
|
||
nextIrpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
|
||
nextIrpStack->MajorFunction = IRP_MJ_SCSI;
|
||
|
||
context->FinalStatus = STATUS_SUCCESS;
|
||
|
||
SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
SrbSetSrbFlags(srbHeader, (SRB_FLAGS_BYPASS_LOCKED_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE));
|
||
|
||
fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
|
||
context->RetryCount = MAXIMUM_RETRIES;
|
||
|
||
context->Options = Options;
|
||
context->DeviceObject = DeviceObject;
|
||
context->Irp = Irp;
|
||
|
||
if (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0) {
|
||
|
||
NT_ASSERT(Options.HandleSpinUp);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tpower up - locking queue\n", Irp));
|
||
|
||
//
|
||
// We need to issue a queue lock request so that we
|
||
// can spin the drive back up after the power is restored
|
||
// but before any requests are processed.
|
||
//
|
||
|
||
context->Options.PowerDown = FALSE;
|
||
context->PowerChangeState.PowerUp = PowerUpDeviceInitial;
|
||
context->CompletionRoutine = ClasspPowerUpCompletion;
|
||
|
||
} else {
|
||
|
||
NT_ASSERT(Options.HandleSpinDown);
|
||
|
||
fdoExtension->PowerDownInProgress = TRUE;
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPowering down - locking queue\n", Irp));
|
||
|
||
//
|
||
// Disable tick timer at beginning of D3 processing if running.
|
||
//
|
||
if ((fdoExtension->PrivateFdoData->TickTimerEnabled)) {
|
||
ClasspDisableTimer(fdoExtension);
|
||
}
|
||
|
||
PoSetPowerState(DeviceObject,
|
||
irpStack->Parameters.Power.Type,
|
||
irpStack->Parameters.Power.State);
|
||
|
||
context->Options.PowerDown = TRUE;
|
||
context->PowerChangeState.PowerDown3 = PowerDownDeviceInitial3;
|
||
context->CompletionRoutine = ClasspPowerDownCompletion;
|
||
|
||
}
|
||
|
||
//
|
||
// we are not dealing with port-allocated sense in these routines.
|
||
//
|
||
|
||
srbFlags = SrbGetSrbFlags(srbHeader);
|
||
NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
|
||
NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
|
||
|
||
//
|
||
// Mark the original power irp pending.
|
||
//
|
||
|
||
IoMarkIrpPending(Irp);
|
||
|
||
if (Options.LockQueue) {
|
||
|
||
//
|
||
// Send the lock irp down.
|
||
//
|
||
|
||
IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
|
||
context->CompletionRoutine,
|
||
context,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
IoCallDriver(lowerDevice, fdoExtension->PrivateFdoData->PowerProcessIrp);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Call the completion routine directly. It won't care what the
|
||
// status of the "lock" was - it will just go and do the next
|
||
// step of the operation.
|
||
//
|
||
|
||
context->CompletionRoutine(DeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp, context);
|
||
}
|
||
|
||
return STATUS_PENDING;
|
||
|
||
ClasspPowerHandlerCleanup:
|
||
|
||
//
|
||
// Send the original power irp down, we will start the next power irp in completion routine.
|
||
//
|
||
ClassReleaseRemoveLock(DeviceObject, Irp);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tStarting next power irp\n", Irp));
|
||
IoCopyCurrentIrpStackLocationToNext(Irp);
|
||
IoSetCompletionRoutine(Irp,
|
||
ClasspStartNextPowerIrpCompletion,
|
||
NULL,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
return PoCallDriver(lowerDevice, Irp);
|
||
} // end ClasspPowerHandler()
|
||
|
||
/*++////////////////////////////////////////////////////////////////////////////
|
||
|
||
ClassMinimalPowerHandler()
|
||
|
||
Routine Description:
|
||
|
||
This routine is the minimum power handler for a storage driver. It does
|
||
the least amount of work possible.
|
||
|
||
--*/
|
||
NTSTATUS
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
ClassMinimalPowerHandler(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
NTSTATUS status;
|
||
|
||
ClassReleaseRemoveLock(DeviceObject, Irp);
|
||
PoStartNextPowerIrp(Irp);
|
||
|
||
switch (irpStack->MinorFunction)
|
||
{
|
||
case IRP_MN_SET_POWER:
|
||
{
|
||
switch (irpStack->Parameters.Power.ShutdownType)
|
||
{
|
||
case PowerActionNone:
|
||
case PowerActionSleep:
|
||
case PowerActionHibernate:
|
||
{
|
||
if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA))
|
||
{
|
||
if ((ClassGetVpb(DeviceObject) != NULL) && (ClassGetVpb(DeviceObject)->Flags & VPB_MOUNTED))
|
||
{
|
||
//
|
||
// This flag will cause the filesystem to verify the
|
||
// volume when coming out of hibernation or standby or runtime power
|
||
//
|
||
SET_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Fall through
|
||
//
|
||
|
||
case IRP_MN_QUERY_POWER:
|
||
{
|
||
if (!commonExtension->IsFdo)
|
||
{
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (commonExtension->IsFdo)
|
||
{
|
||
IoCopyCurrentIrpStackLocationToNext(Irp);
|
||
status = PoCallDriver(commonExtension->LowerDeviceObject, Irp);
|
||
}
|
||
else
|
||
{
|
||
status = Irp->IoStatus.Status;
|
||
ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
|
||
}
|
||
|
||
return status;
|
||
} // end ClassMinimalPowerHandler()
|
||
|
||
/*++////////////////////////////////////////////////////////////////////////////
|
||
|
||
ClassSpinDownPowerHandler()
|
||
|
||
Routine Description:
|
||
|
||
This routine is a callback for disks and other things which require both
|
||
a start and a stop to be sent to the device. (actually the starts are
|
||
almost always optional, since most device power themselves on to process
|
||
commands, but i digress).
|
||
|
||
Determines proper use of spinup, spindown, and queue locking based upon
|
||
ScanForSpecialFlags in the FdoExtension. This is the most common power
|
||
handler passed into classpnp.sys
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Supplies the functional device object
|
||
|
||
Irp - Supplies the request to be retried.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
__control_entrypoint(DeviceDriver)
|
||
NTSTATUS
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
ClassSpinDownPowerHandler(
|
||
_In_ PDEVICE_OBJECT DeviceObject,
|
||
_In_ PIRP Irp
|
||
)
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
||
CLASS_POWER_OPTIONS options = {0};
|
||
|
||
fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
||
|
||
//
|
||
// check the flags to see what options we need to worry about
|
||
//
|
||
|
||
if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
|
||
CLASS_SPECIAL_DISABLE_SPIN_DOWN)) {
|
||
options.HandleSpinDown = TRUE;
|
||
}
|
||
|
||
if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
|
||
CLASS_SPECIAL_DISABLE_SPIN_UP)) {
|
||
options.HandleSpinUp = TRUE;
|
||
}
|
||
|
||
if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
|
||
CLASS_SPECIAL_NO_QUEUE_LOCK)) {
|
||
options.LockQueue = TRUE;
|
||
}
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "ClasspPowerHandler: Devobj %p\n"
|
||
"\t%shandling spin down\n"
|
||
"\t%shandling spin up\n"
|
||
"\t%slocking queue\n",
|
||
DeviceObject,
|
||
(options.HandleSpinDown ? "" : "not "),
|
||
(options.HandleSpinUp ? "" : "not "),
|
||
(options.LockQueue ? "" : "not ")
|
||
));
|
||
|
||
//
|
||
// do all the dirty work
|
||
//
|
||
|
||
return ClasspPowerHandler(DeviceObject, Irp, options);
|
||
} // end ClassSpinDownPowerHandler()
|
||
|
||
/*++////////////////////////////////////////////////////////////////////////////
|
||
|
||
ClassStopUnitPowerHandler()
|
||
|
||
Routine Description:
|
||
|
||
This routine is an outdated call. To achieve equivalent functionality,
|
||
the driver should set the following flags in ScanForSpecialFlags in the
|
||
FdoExtension:
|
||
|
||
CLASS_SPECIAL_DISABLE_SPIN_UP
|
||
CLASS_SPECIAL_NO_QUEUE_LOCK
|
||
|
||
--*/
|
||
NTSTATUS
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
ClassStopUnitPowerHandler(
|
||
_In_ PDEVICE_OBJECT DeviceObject,
|
||
_In_ PIRP Irp
|
||
)
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
||
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_POWER, "ClassStopUnitPowerHandler - Devobj %p using outdated call\n"
|
||
"Drivers should set the following flags in ScanForSpecialFlags "
|
||
" in the FDO extension:\n"
|
||
"\tCLASS_SPECIAL_DISABLE_SPIN_UP\n"
|
||
"\tCLASS_SPECIAL_NO_QUEUE_LOCK\n"
|
||
"This will provide equivalent functionality if the power "
|
||
"routine is then set to ClassSpinDownPowerHandler\n\n",
|
||
DeviceObject));
|
||
|
||
fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
||
|
||
SET_FLAG(fdoExtension->ScanForSpecialFlags,
|
||
CLASS_SPECIAL_DISABLE_SPIN_UP);
|
||
SET_FLAG(fdoExtension->ScanForSpecialFlags,
|
||
CLASS_SPECIAL_NO_QUEUE_LOCK);
|
||
|
||
return ClassSpinDownPowerHandler(DeviceObject, Irp);
|
||
} // end ClassStopUnitPowerHandler()
|
||
|
||
/*++////////////////////////////////////////////////////////////////////////////
|
||
|
||
RetryPowerRequest()
|
||
|
||
Routine Description:
|
||
|
||
This routine reinitalizes the necessary fields, and sends the request
|
||
to the lower driver.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Supplies the device object associated with this request.
|
||
|
||
Irp - Supplies the request to be retried.
|
||
|
||
Context - Supplies a pointer to the power up context for this request.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
VOID
|
||
RetryPowerRequest(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PIRP Irp,
|
||
PCLASS_POWER_CONTEXT Context
|
||
)
|
||
{
|
||
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
|
||
(PFUNCTIONAL_DEVICE_EXTENSION)Context->DeviceObject->DeviceExtension;
|
||
PSTORAGE_REQUEST_BLOCK_HEADER srb;
|
||
LONGLONG dueTime;
|
||
ULONG srbFlags;
|
||
ULONG srbFunction;
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tDelaying retry by queueing DPC\n", Irp));
|
||
|
||
//NT_ASSERT(Context->Irp == Irp);
|
||
if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
|
||
srb = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->PowerSrb.SrbEx);
|
||
|
||
//
|
||
// Check if reverted to using legacy SRB.
|
||
//
|
||
if (Context->Srb.Length == sizeof(SCSI_REQUEST_BLOCK)) {
|
||
srb = (PSTORAGE_REQUEST_BLOCK_HEADER)&(Context->Srb);
|
||
srbFunction = srb->Function;
|
||
} else {
|
||
srbFunction = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction;
|
||
}
|
||
} else {
|
||
srb = (PSTORAGE_REQUEST_BLOCK_HEADER)&(Context->Srb);
|
||
srbFunction = srb->Function;
|
||
}
|
||
|
||
NT_ASSERT(Context->DeviceObject == DeviceObject);
|
||
srbFlags = SrbGetSrbFlags(srb);
|
||
NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
|
||
NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
|
||
|
||
if (Context->RetryInterval == 0) {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tDelaying minimum time (.2 sec)\n", Irp));
|
||
dueTime = (LONGLONG)1000000 * 2;
|
||
|
||
} else {
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tDelaying %x seconds\n",
|
||
Irp, Context->RetryInterval));
|
||
dueTime = (LONGLONG)1000000 * 10 * Context->RetryInterval;
|
||
|
||
}
|
||
|
||
//
|
||
// reset the retry interval
|
||
//
|
||
|
||
Context->RetryInterval = 0;
|
||
|
||
//
|
||
// Reset byte count of transfer in SRB Extension.
|
||
//
|
||
|
||
SrbSetDataTransferLength(srb, 0);
|
||
|
||
//
|
||
// Zero SRB statuses.
|
||
//
|
||
|
||
srb->SrbStatus = 0;
|
||
if (srbFunction == SRB_FUNCTION_EXECUTE_SCSI) {
|
||
SrbSetScsiStatus(srb, 0);
|
||
}
|
||
|
||
//
|
||
// Set up major SCSI function.
|
||
//
|
||
|
||
nextIrpStack->MajorFunction = IRP_MJ_SCSI;
|
||
|
||
//
|
||
// Save SRB address in next stack for port driver.
|
||
//
|
||
|
||
nextIrpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srb;
|
||
|
||
//
|
||
// Set the completion routine up again.
|
||
//
|
||
|
||
IoSetCompletionRoutine(Irp, Context->CompletionRoutine, Context,
|
||
TRUE, TRUE, TRUE);
|
||
|
||
ClassRetryRequest(DeviceObject, Irp, dueTime);
|
||
|
||
return;
|
||
|
||
} // end RetryRequest()
|
||
|
||
/*++////////////////////////////////////////////////////////////////////////////
|
||
|
||
ClasspStartNextPowerIrpCompletion()
|
||
|
||
Routine Description:
|
||
|
||
This routine guarantees that the next power irp (power up or down) is not
|
||
sent until the previous one has fully completed.
|
||
|
||
--*/
|
||
NTSTATUS
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
ClasspStartNextPowerIrpCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
PCLASS_POWER_CONTEXT PowerContext = (PCLASS_POWER_CONTEXT)Context;
|
||
|
||
UNREFERENCED_PARAMETER(DeviceObject);
|
||
|
||
if (Irp->PendingReturned) {
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
|
||
if (PowerContext != NULL)
|
||
{
|
||
PowerContext->InUse = FALSE;
|
||
}
|
||
|
||
|
||
PoStartNextPowerIrp(Irp);
|
||
return STATUS_SUCCESS;
|
||
} // end ClasspStartNextPowerIrpCompletion()
|
||
|
||
NTSTATUS
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
ClasspDeviceLockFailurePowerIrpCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
PCLASS_POWER_CONTEXT PowerContext = (PCLASS_POWER_CONTEXT)Context;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension;
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
||
PIO_STACK_LOCATION currentStack;
|
||
BOOLEAN FailurePredictionEnabled = FALSE;
|
||
|
||
UNREFERENCED_PARAMETER(DeviceObject);
|
||
|
||
commonExtension = PowerContext->DeviceObject->DeviceExtension;
|
||
fdoExtension = PowerContext->DeviceObject->DeviceExtension;
|
||
|
||
currentStack = IoGetCurrentIrpStackLocation(Irp);
|
||
|
||
//
|
||
// Set the new power state
|
||
//
|
||
|
||
fdoExtension->DevicePowerState = currentStack->Parameters.Power.State.DeviceState;
|
||
|
||
//
|
||
// We reach here becasue LockQueue operation was not successful.
|
||
// However, media change detection would not happen in case of resume becasue we
|
||
// had disabled the timer while going into lower power state.
|
||
// So, if the device goes into D0 then enable the tick timer.
|
||
//
|
||
|
||
if (fdoExtension->DevicePowerState == PowerDeviceD0) {
|
||
//
|
||
// Check whether failure detection is enabled
|
||
//
|
||
|
||
if ((fdoExtension->FailurePredictionInfo != NULL) &&
|
||
(fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone)) {
|
||
FailurePredictionEnabled = TRUE;
|
||
}
|
||
|
||
//
|
||
// Enable tick timer at end of D0 processing if it was previously enabled.
|
||
//
|
||
|
||
if ((commonExtension->DriverExtension->InitData.ClassTick != NULL) ||
|
||
((fdoExtension->MediaChangeDetectionInfo != NULL) &&
|
||
(fdoExtension->FunctionSupportInfo != NULL) &&
|
||
(fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported == FALSE)) ||
|
||
(FailurePredictionEnabled)) {
|
||
|
||
//
|
||
// If failure prediction is turned on and we've been powered
|
||
// off longer than the failure prediction query period then
|
||
// force the query on the next timer tick.
|
||
//
|
||
|
||
if ((FailurePredictionEnabled) && (ClasspFailurePredictionPeriodMissed(fdoExtension))) {
|
||
fdoExtension->FailurePredictionInfo->CountDown = 1;
|
||
}
|
||
|
||
//
|
||
// Finally, enable the timer.
|
||
//
|
||
|
||
ClasspEnableTimer(fdoExtension);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Indicate to Po that we've been successfully powered up so
|
||
// it can do it's notification stuff.
|
||
//
|
||
|
||
PoSetPowerState(PowerContext->DeviceObject,
|
||
currentStack->Parameters.Power.Type,
|
||
currentStack->Parameters.Power.State);
|
||
|
||
PowerContext->InUse = FALSE;
|
||
|
||
|
||
ClassReleaseRemoveLock(commonExtension->DeviceObject, Irp);
|
||
|
||
//
|
||
// Start the next power IRP
|
||
//
|
||
|
||
if (Irp->PendingReturned) {
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
|
||
PoStartNextPowerIrp(Irp);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
_IRQL_requires_same_
|
||
NTSTATUS
|
||
ClasspSendEnableIdlePowerIoctl(
|
||
_In_ PDEVICE_OBJECT DeviceObject
|
||
)
|
||
/*++
|
||
Description:
|
||
|
||
This function is used to send IOCTL_STORAGE_ENABLE_IDLE_POWER to the port
|
||
driver. It pulls the relevant idle power management properties from the
|
||
FDO's device extension.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The class FDO.
|
||
|
||
Return Value:
|
||
|
||
The NTSTATUS code returned from the port driver. STATUS_SUCCESS indicates
|
||
this device is now enabled for idle (runtime) power management.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
STORAGE_IDLE_POWER idlePower = {0};
|
||
IO_STATUS_BLOCK ioStatus = {0};
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = &(fdoExtension->CommonExtension);
|
||
|
||
idlePower.Version = 1;
|
||
idlePower.Size = sizeof(STORAGE_IDLE_POWER);
|
||
idlePower.WakeCapableHint = fdoExtension->FunctionSupportInfo->IdlePower.DeviceWakeable;
|
||
idlePower.D3ColdSupported = fdoExtension->FunctionSupportInfo->IdlePower.D3ColdSupported;
|
||
idlePower.D3IdleTimeout = fdoExtension->FunctionSupportInfo->IdlePower.D3IdleTimeout;
|
||
|
||
ClassSendDeviceIoControlSynchronous(
|
||
IOCTL_STORAGE_ENABLE_IDLE_POWER,
|
||
commonExtension->LowerDeviceObject,
|
||
&idlePower,
|
||
sizeof(STORAGE_IDLE_POWER),
|
||
0,
|
||
FALSE,
|
||
&ioStatus
|
||
);
|
||
|
||
status = ioStatus.Status;
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION,
|
||
TRACE_FLAG_POWER,
|
||
"ClasspSendEnableIdlePowerIoctl: Port driver returned status (%x) for FDO (%p)\n"
|
||
"\tWakeCapableHint: %u\n"
|
||
"\tD3ColdSupported: %u\n"
|
||
"\tD3IdleTimeout: %u (ms)",
|
||
status,
|
||
DeviceObject,
|
||
idlePower.WakeCapableHint,
|
||
idlePower.D3ColdSupported,
|
||
idlePower.D3IdleTimeout));
|
||
|
||
return status;
|
||
}
|
||
|
||
_Function_class_(POWER_SETTING_CALLBACK)
|
||
_IRQL_requires_same_
|
||
NTSTATUS
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
ClasspPowerSettingCallback(
|
||
_In_ LPCGUID SettingGuid,
|
||
_In_reads_bytes_(ValueLength) PVOID Value,
|
||
_In_ ULONG ValueLength,
|
||
_Inout_opt_ PVOID Context
|
||
)
|
||
/*++
|
||
Description:
|
||
|
||
This function is the callback for power setting notifications (registered
|
||
when ClasspGetD3IdleTimeout() is called for the first time).
|
||
|
||
Currently, this function is used to get the disk idle timeout value from
|
||
the system power settings.
|
||
|
||
This function is guaranteed to be called at PASSIVE_LEVEL.
|
||
|
||
Arguments:
|
||
|
||
SettingGuid - The power setting GUID.
|
||
Value - Pointer to the power setting value.
|
||
ValueLength - Size of the Value buffer.
|
||
Context - The FDO's device extension.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS
|
||
|
||
--*/
|
||
{
|
||
PIDLE_POWER_FDO_LIST_ENTRY fdoEntry = NULL;
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
|
||
#endif
|
||
PIRP removeLockTag = (PIRP)&ClasspPowerSettingCallback;
|
||
|
||
UNREFERENCED_PARAMETER(Context);
|
||
|
||
PAGED_CODE();
|
||
|
||
if (IsEqualGUID(SettingGuid, &GUID_DISK_IDLE_TIMEOUT)) {
|
||
if (ValueLength != sizeof(ULONG) || Value == NULL) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// The value supplied by this GUID is already in milliseconds.
|
||
//
|
||
DiskIdleTimeoutInMS = *((PULONG)Value);
|
||
|
||
//
|
||
// For each FDO on the idle power list, grab the remove lock and send
|
||
// IOCTL_STORAGE_ENABLE_IDLE_POWER to the port driver to update the
|
||
// idle timeout value.
|
||
//
|
||
KeAcquireGuardedMutex(&IdlePowerFDOListMutex);
|
||
fdoEntry = (PIDLE_POWER_FDO_LIST_ENTRY)IdlePowerFDOList.Flink;
|
||
while ((PLIST_ENTRY)fdoEntry != &IdlePowerFDOList) {
|
||
|
||
ULONG isRemoved = ClassAcquireRemoveLock(fdoEntry->Fdo, removeLockTag);
|
||
|
||
if (!isRemoved) {
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)fdoEntry->Fdo->DeviceExtension;
|
||
|
||
//
|
||
// Apply the new timeout if the user hasn't overridden it via the registry.
|
||
//
|
||
if (!fdoExtension->FunctionSupportInfo->IdlePower.D3IdleTimeoutOverridden) {
|
||
fdoExtension->FunctionSupportInfo->IdlePower.D3IdleTimeout = DiskIdleTimeoutInMS;
|
||
ClasspSendEnableIdlePowerIoctl(fdoEntry->Fdo);
|
||
}
|
||
}
|
||
|
||
ClassReleaseRemoveLock(fdoEntry->Fdo, removeLockTag);
|
||
|
||
fdoEntry = (PIDLE_POWER_FDO_LIST_ENTRY)fdoEntry->ListEntry.Flink;
|
||
}
|
||
KeReleaseGuardedMutex(&IdlePowerFDOListMutex);
|
||
|
||
} else if (IsEqualGUID(SettingGuid, &GUID_CONSOLE_DISPLAY_STATE)) {
|
||
|
||
//
|
||
// If monitor is off, change media change requests to not
|
||
// keep device active. This allows removable media devices to
|
||
// go to sleep if there are no other active requests. Otherwise,
|
||
// let media change requests keep the device active.
|
||
//
|
||
if ((ValueLength == sizeof(ULONG)) && (Value != NULL)) {
|
||
if (*((PULONG)Value) == PowerMonitorOff) {
|
||
ClasspScreenOff = TRUE;
|
||
} else {
|
||
ClasspScreenOff = FALSE;
|
||
}
|
||
|
||
KeAcquireGuardedMutex(&IdlePowerFDOListMutex);
|
||
fdoEntry = (PIDLE_POWER_FDO_LIST_ENTRY)IdlePowerFDOList.Flink;
|
||
while ((PLIST_ENTRY)fdoEntry != &IdlePowerFDOList) {
|
||
|
||
ULONG isRemoved = ClassAcquireRemoveLock(fdoEntry->Fdo, removeLockTag);
|
||
if (!isRemoved) {
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)fdoEntry->Fdo->DeviceExtension;
|
||
|
||
if (ClasspScreenOff == FALSE) {
|
||
//
|
||
// Now that the screen is on, we may need to check for media
|
||
// for devices that are not in D0 and may have removable media.
|
||
// This is because the media change polling has been disabled
|
||
// for devices in D3 and now that the screen is on the user may
|
||
// have inserted some media that they want to interact with.
|
||
//
|
||
if ((fdoExtension->DevicePowerState != PowerDeviceD0) &&
|
||
(fdoExtension->MediaChangeDetectionInfo != NULL) &&
|
||
(fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported == FALSE)) {
|
||
ClassCheckMediaState(fdoExtension);
|
||
}
|
||
|
||
//
|
||
// We disabled failure prediction polling during screen-off
|
||
// so now check to see if we missed a failure prediction
|
||
// period and if so, force the IOCTL to be sent now.
|
||
//
|
||
if ((fdoExtension->FailurePredictionInfo != NULL) &&
|
||
(fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone)) {
|
||
if (ClasspFailurePredictionPeriodMissed(fdoExtension)) {
|
||
fdoExtension->FailurePredictionInfo->CountDown = 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
|
||
//
|
||
// Screen state has changed so attempt to update the tick
|
||
// timer's no-wake tolerance accordingly.
|
||
//
|
||
ClasspUpdateTimerNoWakeTolerance(fdoExtension);
|
||
#endif
|
||
}
|
||
ClassReleaseRemoveLock(fdoEntry->Fdo, removeLockTag);
|
||
|
||
fdoEntry = (PIDLE_POWER_FDO_LIST_ENTRY)fdoEntry->ListEntry.Flink;
|
||
}
|
||
KeReleaseGuardedMutex(&IdlePowerFDOListMutex);
|
||
}
|
||
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
_IRQL_requires_same_
|
||
NTSTATUS
|
||
ClasspEnableIdlePower(
|
||
_In_ PDEVICE_OBJECT DeviceObject
|
||
)
|
||
/*++
|
||
Description:
|
||
|
||
This function is used to enable idle (runtime) power management for the
|
||
device. It will do the work to determine D3Cold support, idle timeout,
|
||
etc. and then notify the port driver that it wants to enable idle power
|
||
management.
|
||
|
||
This function may modify some of the idle power fields in the FDO's device
|
||
extension.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The class FDO.
|
||
|
||
Return Value:
|
||
|
||
An NTSTATUS code indicating the status of the operation.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
ULONG d3ColdDisabledByUser = FALSE;
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
||
ULONG idleTimeoutOverrideInSeconds = 0;
|
||
|
||
//
|
||
// This function should only be called once.
|
||
//
|
||
NT_ASSERT(fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled == FALSE);
|
||
|
||
ClassGetDeviceParameter(fdoExtension,
|
||
CLASSP_REG_SUBKEY_NAME,
|
||
CLASSP_REG_DISABLE_D3COLD,
|
||
&d3ColdDisabledByUser);
|
||
|
||
//
|
||
// If the device is hot-pluggable or the user has explicitly
|
||
// disabled D3Cold, do not enable D3Cold for this device.
|
||
//
|
||
if (d3ColdDisabledByUser || fdoExtension->PrivateFdoData->HotplugInfo.DeviceHotplug) {
|
||
fdoExtension->FunctionSupportInfo->IdlePower.D3ColdSupported = 0;
|
||
}
|
||
|
||
ClassGetDeviceParameter(fdoExtension,
|
||
CLASSP_REG_SUBKEY_NAME,
|
||
CLASSP_REG_IDLE_TIMEOUT_IN_SECONDS,
|
||
&idleTimeoutOverrideInSeconds);
|
||
|
||
//
|
||
// Set the idle timeout. If the user has not specified an override value,
|
||
// this will either be a default value or will have been updated by the
|
||
// power setting notification callback.
|
||
//
|
||
if (idleTimeoutOverrideInSeconds != 0) {
|
||
fdoExtension->FunctionSupportInfo->IdlePower.D3IdleTimeout = (idleTimeoutOverrideInSeconds * 1000);
|
||
fdoExtension->FunctionSupportInfo->IdlePower.D3IdleTimeoutOverridden = TRUE;
|
||
} else {
|
||
fdoExtension->FunctionSupportInfo->IdlePower.D3IdleTimeout = DiskIdleTimeoutInMS;
|
||
}
|
||
|
||
//
|
||
// We don't allow disks to be wakeable.
|
||
//
|
||
fdoExtension->FunctionSupportInfo->IdlePower.DeviceWakeable = FALSE;
|
||
|
||
//
|
||
// Send IOCTL_STORAGE_ENABLE_IDLE_POWER to the port driver to enable idle
|
||
// power management by the port driver.
|
||
//
|
||
status = ClasspSendEnableIdlePowerIoctl(DeviceObject);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
PIDLE_POWER_FDO_LIST_ENTRY fdoEntry = NULL;
|
||
|
||
//
|
||
// Put this FDO on the list of devices that are idle power managed.
|
||
//
|
||
fdoEntry = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(IDLE_POWER_FDO_LIST_ENTRY), CLASS_TAG_POWER);
|
||
if (fdoEntry) {
|
||
|
||
fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled = TRUE;
|
||
|
||
fdoEntry->Fdo = DeviceObject;
|
||
|
||
KeAcquireGuardedMutex(&IdlePowerFDOListMutex);
|
||
InsertHeadList(&IdlePowerFDOList, &(fdoEntry->ListEntry));
|
||
KeReleaseGuardedMutex(&IdlePowerFDOListMutex);
|
||
|
||
//
|
||
// If not registered already, register for disk idle timeout power
|
||
// setting notifications. The power manager will call our power
|
||
// setting callback very soon to set the idle timeout to the actual
|
||
// value.
|
||
//
|
||
if (PowerSettingNotificationHandle == NULL) {
|
||
PoRegisterPowerSettingCallback(DeviceObject,
|
||
&GUID_DISK_IDLE_TIMEOUT,
|
||
&ClasspPowerSettingCallback,
|
||
NULL,
|
||
&(PowerSettingNotificationHandle));
|
||
}
|
||
} else {
|
||
fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled = FALSE;
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|