Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1613 lines
49 KiB

/*++
Copyright (C) Microsoft Corporation, 1991 - 1999
Module Name:
class.c
Abstract:
SCSI class driver routines
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "stddef.h"
#include "ntddk.h"
#include "scsi.h"
#include "classp.h"
#include <stdarg.h>
#define CLASS_TAG_POWER 'WLcS'
NTSTATUS
ClasspPowerHandler(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN CLASS_POWER_OPTIONS Options
);
NTSTATUS
ClasspPowerDownCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PCLASS_POWER_CONTEXT Context
);
NTSTATUS
ClasspPowerUpCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PCLASS_POWER_CONTEXT Context
);
VOID
RetryPowerRequest(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PCLASS_POWER_CONTEXT Context
);
NTSTATUS
ClasspStartNextPowerIrpCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
/*++////////////////////////////////////////////////////////////////////////////
ClassDispatchPower()
Routine Description:
This routine acquires the removelock for the irp and then calls the
appropriate power callback.
Arguments:
DeviceObject -
Irp -
Return Value:
--*/
NTSTATUS
ClassDispatchPower(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
ULONG isRemoved;
PCLASS_POWER_DEVICE powerRoutine = NULL;
//
// 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.
//
NTSTATUS status;
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 - the IO_REQUEST_PACKET containing the power request
Srb - the SRB used to perform port/class operations.
Return Value:
STATUS_MORE_PROCESSING_REQUIRED or
STATUS_SUCCESS
--*/
NTSTATUS
ClasspPowerUpCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PCLASS_POWER_CONTEXT Context
)
{
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION currentStack = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
DebugPrint((1, "ClasspPowerUpCompletion: Device Object %p, Irp %p, "
"Context %p\n",
DeviceObject, Irp, Context));
ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
ASSERT(Context->Options.PowerDown == FALSE);
ASSERT(Context->Options.HandleSpinUp);
if(Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
Context->PowerChangeState.PowerUp++;
switch(Context->PowerChangeState.PowerUp) {
case PowerUpDeviceLocked: {
DebugPrint((1, "(%p)\tPreviously sent power lock\n", Irp));
//
// Issue the actual power request to the lower driver.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
//
// If the lock wasn't successful then just bail out on the power
// request unless we can ignore failed locks
//
if((Context->Options.LockQueue == TRUE) &&
(!NT_SUCCESS(Irp->IoStatus.Status))) {
DebugPrint((1, "(%p)\tIrp status was %lx\n",
Irp, Irp->IoStatus.Status));
DebugPrint((1, "(%p)\tSrb status was %lx\n",
Irp, Context->Srb.SrbStatus));
//
// Lock was not successful - throw down the power IRP
// by itself and don't try to spin up the drive or unlock
// the queue.
//
Context->InUse = FALSE;
Context = NULL;
//
// Set the new power state
//
fdoExtension->DevicePowerState =
currentStack->Parameters.Power.State.DeviceState;
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
ClasspStartNextPowerIrpCompletion,
NULL,
TRUE,
TRUE,
TRUE);
//
// Indicate to Po that we've been successfully powered up so
// it can do it's notification stuff.
//
PoSetPowerState(DeviceObject,
currentStack->Parameters.Power.Type,
currentStack->Parameters.Power.State);
PoCallDriver(commonExtension->LowerDeviceObject, Irp);
ClassReleaseRemoveLock(commonExtension->DeviceObject,
Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
} else {
Context->QueueLocked = (UCHAR) Context->Options.LockQueue;
}
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
Context->PowerChangeState.PowerUp = PowerUpDeviceLocked;
IoSetCompletionRoutine(Irp,
ClasspPowerUpCompletion,
Context,
TRUE,
TRUE,
TRUE);
status = PoCallDriver(commonExtension->LowerDeviceObject, Irp);
DebugPrint((2, "(%p)\tPoCallDriver returned %lx\n", Irp, status));
break;
}
case PowerUpDeviceOn: {
PCDB cdb;
if(NT_SUCCESS(Irp->IoStatus.Status)) {
DebugPrint((1, "(%p)\tSending start unit to device\n", Irp));
//
// Issue the start unit command to the device.
//
Context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
Context->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0;
Context->Srb.DataTransferLength = 0;
Context->Srb.TimeOutValue = START_UNIT_TIMEOUT;
Context->Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
SRB_FLAGS_DISABLE_AUTOSENSE |
SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
SRB_FLAGS_NO_QUEUE_FREEZE;
if(Context->Options.LockQueue) {
SET_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_BYPASS_LOCKED_QUEUE);
}
Context->Srb.CdbLength = 6;
cdb = (PCDB) (Context->Srb.Cdb);
RtlZeroMemory(cdb, sizeof(CDB));
cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
cdb->START_STOP.Start = 1;
Context->PowerChangeState.PowerUp = PowerUpDeviceOn;
IoSetCompletionRoutine(Irp,
ClasspPowerUpCompletion,
Context,
TRUE,
TRUE,
TRUE);
nextStack->Parameters.Scsi.Srb = &(Context->Srb);
nextStack->MajorFunction = IRP_MJ_SCSI;
status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
DebugPrint((2, "(%p)\tIoCallDriver returned %lx\n", Irp, status));
} else {
//
// we're done.
//
Context->FinalStatus = Irp->IoStatus.Status;
goto ClasspPowerUpCompletionFailure;
}
break;
}
case PowerUpDeviceStarted: { // 3
//
// First deal with an error if one occurred.
//
if(SRB_STATUS(Context->Srb.SrbStatus) != SRB_STATUS_SUCCESS) {
BOOLEAN retry;
DebugPrint((1, "%p\tError occured when issuing START_UNIT "
"command to device. Srb %p, Status %x\n",
Irp,
&Context->Srb,
Context->Srb.SrbStatus));
ASSERT(!(TEST_FLAG(Context->Srb.SrbStatus,
SRB_STATUS_QUEUE_FROZEN)));
ASSERT(Context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI);
Context->RetryInterval = 0;
retry = ClassInterpretSenseInfo(
commonExtension->DeviceObject,
&Context->Srb,
IRP_MJ_SCSI,
IRP_MJ_POWER,
MAXIMUM_RETRIES - Context->RetryCount,
&status,
&Context->RetryInterval);
if((retry == TRUE) && (Context->RetryCount-- != 0)) {
DebugPrint((1, "(%p)\tRetrying failed request\n", Irp));
//
// Decrement the state so we come back through here the
// next time.
//
Context->PowerChangeState.PowerUp--;
RetryPowerRequest(commonExtension->DeviceObject,
Irp,
Context);
break;
}
// reset retries
Context->RetryCount = MAXIMUM_RETRIES;
}
ClasspPowerUpCompletionFailure:
DebugPrint((1, "(%p)\tPreviously spun device up\n", Irp));
if (Context->QueueLocked) {
DebugPrint((1, "(%p)\tUnlocking queue\n", Irp));
Context->Srb.Function = SRB_FUNCTION_UNLOCK_QUEUE;
Context->Srb.SrbFlags = SRB_FLAGS_BYPASS_LOCKED_QUEUE;
Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0;
Context->Srb.DataTransferLength = 0;
nextStack->Parameters.Scsi.Srb = &(Context->Srb);
nextStack->MajorFunction = IRP_MJ_SCSI;
Context->PowerChangeState.PowerUp = PowerUpDeviceStarted;
IoSetCompletionRoutine(Irp,
ClasspPowerUpCompletion,
Context,
TRUE,
TRUE,
TRUE);
status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n",
Irp, status));
break;
}
// Fall-through to next case...
}
case PowerUpDeviceUnlocked: {
//
// This is the end of the dance. Free the srb and complete the
// request finally. We're ignoring possible intermediate
// error conditions ....
//
if (Context->QueueLocked) {
DebugPrint((1, "(%p)\tPreviously unlocked queue\n", Irp));
ASSERT(NT_SUCCESS(Irp->IoStatus.Status));
ASSERT(Context->Srb.SrbStatus == SRB_STATUS_SUCCESS);
} else {
DebugPrint((1, "(%p)\tFall-through (queue not locked)\n", Irp));
}
DebugPrint((1, "(%p)\tFreeing srb and completing\n", Irp));
Context->InUse = FALSE;
status = Context->FinalStatus;
Irp->IoStatus.Status = status;
Context = NULL;
//
// Set the new power state
//
if(NT_SUCCESS(status)) {
fdoExtension->DevicePowerState =
currentStack->Parameters.Power.State.DeviceState;
}
//
// Indicate to Po that we've been successfully powered up so
// it can do it's notification stuff.
//
PoSetPowerState(DeviceObject,
currentStack->Parameters.Power.Type,
currentStack->Parameters.Power.State);
DebugPrint((1, "(%p)\tStarting next power irp\n", Irp));
ClassReleaseRemoveLock(DeviceObject, Irp);
PoStartNextPowerIrp(Irp);
return status;
}
}
return STATUS_MORE_PROCESSING_REQUIRED;
} // end ClasspPowerUpCompletion()
/*++////////////////////////////////////////////////////////////////////////////
ClasspPowerDownCompletion()
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 - the IO_REQUEST_PACKET containing the power request
Srb - the SRB used to perform port/class operations.
Return Value:
STATUS_MORE_PROCESSING_REQUIRED or
STATUS_SUCCESS
--*/
NTSTATUS
ClasspPowerDownCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PCLASS_POWER_CONTEXT Context
)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION currentStack = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
DebugPrint((1, "ClasspPowerDownCompletion: Device Object %p, "
"Irp %p, Context %p\n",
DeviceObject, Irp, Context));
ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
ASSERT(Context->Options.PowerDown == TRUE);
ASSERT(Context->Options.HandleSpinDown);
if(Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
Context->PowerChangeState.PowerDown2++;
switch(Context->PowerChangeState.PowerDown2) {
case PowerDownDeviceLocked2: {
PCDB cdb;
DebugPrint((1, "(%p)\tPreviously sent power lock\n", Irp));
if((Context->Options.LockQueue == TRUE) &&
(!NT_SUCCESS(Irp->IoStatus.Status))) {
DebugPrint((1, "(%p)\tIrp status was %lx\n",
Irp,
Irp->IoStatus.Status));
DebugPrint((1, "(%p)\tSrb status was %lx\n",
Irp,
Context->Srb.SrbStatus));
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
//
// Lock was not successful - throw down the power IRP
// by itself and don't try to spin down the drive or unlock
// the queue.
//
Context->InUse = FALSE;
Context = NULL;
//
// 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(Irp);
IoSetCompletionRoutine(Irp,
ClasspStartNextPowerIrpCompletion,
NULL,
TRUE,
TRUE,
TRUE);
PoSetPowerState(DeviceObject,
currentStack->Parameters.Power.Type,
currentStack->Parameters.Power.State);
fdoExtension->PowerDownInProgress = FALSE;
PoCallDriver(commonExtension->LowerDeviceObject, Irp);
ClassReleaseRemoveLock(commonExtension->DeviceObject,
Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
} else {
Context->QueueLocked = (UCHAR) Context->Options.LockQueue;
}
if (!TEST_FLAG(fdoExtension->PrivateFdoData->HackFlags,
FDO_HACK_NO_SYNC_CACHE)) {
//
// send SCSIOP_SYNCHRONIZE_CACHE
//
Context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
Context->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
Context->Srb.TimeOutValue = fdoExtension->TimeOutValue;
Context->Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
SRB_FLAGS_DISABLE_AUTOSENSE |
SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
SRB_FLAGS_NO_QUEUE_FREEZE |
SRB_FLAGS_BYPASS_LOCKED_QUEUE;
Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0;
Context->Srb.DataTransferLength = 0;
Context->Srb.CdbLength = 10;
cdb = (PCDB) Context->Srb.Cdb;
RtlZeroMemory(cdb, sizeof(CDB));
cdb->SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
IoSetCompletionRoutine(Irp,
ClasspPowerDownCompletion,
Context,
TRUE,
TRUE,
TRUE);
nextStack->Parameters.Scsi.Srb = &(Context->Srb);
nextStack->MajorFunction = IRP_MJ_SCSI;
status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n", Irp, status));
break;
} else {
DebugPrint((1, "(%p)\tPower Down: not sending SYNCH_CACHE\n",
DeviceObject));
Context->PowerChangeState.PowerDown2++;
Context->Srb.SrbStatus = SRB_STATUS_SUCCESS;
// and fall through....
}
// no break in case the device doesn't like synch_cache commands
}
case PowerDownDeviceFlushed2: {
PCDB cdb;
DebugPrint((1, "(%p)\tPreviously send SCSIOP_SYNCHRONIZE_CACHE\n",
Irp));
//
// SCSIOP_SYNCHRONIZE_CACHE was sent
//
if(SRB_STATUS(Context->Srb.SrbStatus) != SRB_STATUS_SUCCESS) {
BOOLEAN retry;
DebugPrint((1, "(%p)\tError occured when issuing "
"SYNCHRONIZE_CACHE command to device. "
"Srb %p, Status %lx\n",
Irp,
&Context->Srb,
Context->Srb.SrbStatus));
ASSERT(!(TEST_FLAG(Context->Srb.SrbStatus,
SRB_STATUS_QUEUE_FROZEN)));
ASSERT(Context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI);
Context->RetryInterval = 0;
retry = ClassInterpretSenseInfo(
commonExtension->DeviceObject,
&Context->Srb,
IRP_MJ_SCSI,
IRP_MJ_POWER,
MAXIMUM_RETRIES - Context->RetryCount,
&status,
&Context->RetryInterval);
if((retry == TRUE) && (Context->RetryCount-- != 0)) {
DebugPrint((1, "(%p)\tRetrying failed request\n", Irp));
//
// decrement the state so we come back through here
// the next time.
//
Context->PowerChangeState.PowerDown2--;
RetryPowerRequest(commonExtension->DeviceObject,
Irp,
Context);
break;
}
DebugPrint((1, "(%p)\tSYNCHRONIZE_CACHE not retried\n", Irp));
Context->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.
//
DebugPrint((1, "(%p)\tSending stop unit to device\n", Irp));
//
// Issue the start unit command to the device.
//
Context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
Context->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
Context->Srb.TimeOutValue = START_UNIT_TIMEOUT;
Context->Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
SRB_FLAGS_DISABLE_AUTOSENSE |
SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
SRB_FLAGS_NO_QUEUE_FREEZE |
SRB_FLAGS_BYPASS_LOCKED_QUEUE;
Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0;
Context->Srb.DataTransferLength = 0;
Context->Srb.CdbLength = 6;
cdb = (PCDB) Context->Srb.Cdb;
RtlZeroMemory(cdb, sizeof(CDB));
cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
cdb->START_STOP.Start = 0;
cdb->START_STOP.Immediate = 1;
IoSetCompletionRoutine(Irp,
ClasspPowerDownCompletion,
Context,
TRUE,
TRUE,
TRUE);
nextStack->Parameters.Scsi.Srb = &(Context->Srb);
nextStack->MajorFunction = IRP_MJ_SCSI;
status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n", Irp, status));
break;
}
case PowerDownDeviceStopped2: {
BOOLEAN ignoreError = TRUE;
//
// stop was sent
//
if(SRB_STATUS(Context->Srb.SrbStatus) != SRB_STATUS_SUCCESS) {
BOOLEAN retry;
DebugPrint((1, "(%p)\tError occured when issueing STOP_UNIT "
"command to device. Srb %p, Status %lx\n",
Irp,
&Context->Srb,
Context->Srb.SrbStatus));
ASSERT(!(TEST_FLAG(Context->Srb.SrbStatus,
SRB_STATUS_QUEUE_FROZEN)));
ASSERT(Context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI);
Context->RetryInterval = 0;
retry = ClassInterpretSenseInfo(
commonExtension->DeviceObject,
&Context->Srb,
IRP_MJ_SCSI,
IRP_MJ_POWER,
MAXIMUM_RETRIES - Context->RetryCount,
&status,
&Context->RetryInterval);
if((retry == TRUE) && (Context->RetryCount-- != 0)) {
DebugPrint((1, "(%p)\tRetrying failed request\n", Irp));
//
// decrement the state so we come back through here
// the next time.
//
Context->PowerChangeState.PowerDown2--;
RetryPowerRequest(commonExtension->DeviceObject,
Irp,
Context);
break;
}
DebugPrint((1, "(%p)\tSTOP_UNIT not retried\n", Irp));
Context->RetryCount = MAXIMUM_RETRIES;
} // end !SRB_STATUS_SUCCESS
DebugPrint((1, "(%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)) {
PSENSE_DATA senseBuffer = Context->Srb.SenseInfoBuffer;
if (TEST_FLAG(Context->Srb.SrbStatus,
SRB_STATUS_AUTOSENSE_VALID) &&
((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_NOT_READY) &&
(senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
(senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_FORMAT_IN_PROGRESS)
) {
ignoreError = FALSE;
Context->FinalStatus = STATUS_DEVICE_BUSY;
status = Context->FinalStatus;
}
}
if (NT_SUCCESS(status) || ignoreError) {
//
// Issue the actual power request to the lower driver.
//
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
ClasspPowerDownCompletion,
Context,
TRUE,
TRUE,
TRUE);
status = PoCallDriver(commonExtension->LowerDeviceObject, Irp);
DebugPrint((1, "(%p)\tPoCallDriver returned %lx\n", Irp, 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 PowerDownDeviceOff2: {
//
// SpinDown request completed ... whether it succeeded or not is
// another matter entirely.
//
DebugPrint((1, "(%p)\tPreviously sent power irp\n", Irp));
if (Context->QueueLocked) {
DebugPrint((1, "(%p)\tUnlocking queue\n", Irp));
Context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0;
Context->Srb.DataTransferLength = 0;
Context->Srb.Function = SRB_FUNCTION_UNLOCK_QUEUE;
Context->Srb.SrbFlags = SRB_FLAGS_BYPASS_LOCKED_QUEUE;
nextStack->Parameters.Scsi.Srb = &(Context->Srb);
nextStack->MajorFunction = IRP_MJ_SCSI;
IoSetCompletionRoutine(Irp,
ClasspPowerDownCompletion,
Context,
TRUE,
TRUE,
TRUE);
status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n",
Irp,
status));
break;
}
}
case PowerDownDeviceUnlocked2: {
//
// This is the end of the dance. Free the srb and complete the
// request finally. We're ignoring possible intermediate
// error conditions ....
//
if (Context->QueueLocked == FALSE) {
DebugPrint((1, "(%p)\tFall through (queue not locked)\n", Irp));
} else {
DebugPrint((1, "(%p)\tPreviously unlocked queue\n", Irp));
ASSERT(NT_SUCCESS(Irp->IoStatus.Status));
ASSERT(Context->Srb.SrbStatus == SRB_STATUS_SUCCESS);
}
DebugPrint((1, "(%p)\tFreeing srb and completing\n", Irp));
Context->InUse = FALSE;
status = Context->FinalStatus; // allow failure to propogate
Context = NULL;
if(Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
if (NT_SUCCESS(status)) {
//
// Set the new power state
//
fdoExtension->DevicePowerState =
currentStack->Parameters.Power.State.DeviceState;
}
DebugPrint((1, "(%p)\tStarting next power irp\n", Irp));
ClassReleaseRemoveLock(DeviceObject, Irp);
PoStartNextPowerIrp(Irp);
fdoExtension->PowerDownInProgress = FALSE;
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;
if (!commonExtension->IsFdo) {
//
// certain assumptions are made here,
// particularly: having the fdoExtension
//
DebugPrint((0, "ClasspPowerHandler: Called for PDO %p???\n",
DeviceObject));
ASSERT(!"PDO using ClasspPowerHandler");
return STATUS_NOT_SUPPORTED;
}
DebugPrint((1, "ClasspPowerHandler: Power irp %p to %s %p\n",
Irp, (commonExtension->IsFdo ? "fdo" : "pdo"), DeviceObject));
switch(irpStack->MinorFunction) {
case IRP_MN_SET_POWER: {
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
DebugPrint((1, "(%p)\tIRP_MN_SET_POWER\n", Irp));
DebugPrint((1, "(%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 PowerActionSleep:
case PowerActionHibernate:
if (fdoData->HotplugInfo.MediaRemovable || fdoData->HotplugInfo.MediaHotplug){
/*
* We are suspending 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(&fdoExtension->MediaChangeCount);
}
break;
}
break;
}
default: {
DebugPrint((1, "(%p)\tIrp minor code = %#x\n",
Irp, irpStack->MinorFunction));
break;
}
}
if (irpStack->Parameters.Power.Type != DevicePowerState ||
irpStack->MinorFunction != IRP_MN_SET_POWER) {
DebugPrint((1, "(%p)\tSending to lower device\n", Irp));
goto ClasspPowerHandlerCleanup;
}
nextIrpStack = IoGetNextIrpStackLocation(Irp);
//
// already in exact same state, don't work to transition to it.
//
if(irpStack->Parameters.Power.State.DeviceState ==
fdoExtension->DevicePowerState) {
DebugPrint((1, "(%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)) {
DebugPrint((1, "(%p)\tAlready powered down to %x???\n",
Irp, fdoExtension->DevicePowerState));
fdoExtension->DevicePowerState =
irpStack->Parameters.Power.State.DeviceState;
goto ClasspPowerHandlerCleanup;
}
//
// or going into a hibernation state when we're in the hibernation path.
// If the device is spinning then we should leave it spinning - if it's not
// then the dump driver will start it up for us.
//
if((irpStack->Parameters.Power.State.DeviceState == PowerDeviceD3) &&
(irpStack->Parameters.Power.ShutdownType == PowerActionHibernate) &&
(commonExtension->HibernationPathCount != 0)) {
DebugPrint((1, "(%p)\tdoing nothing for hibernation request for "
"state %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)) {
DebugPrint((2, "(%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)) {
DebugPrint((2, "(%p)\tNot handling spindown to state %x\n",
Irp, fdoExtension->DevicePowerState));
fdoExtension->DevicePowerState =
irpStack->Parameters.Power.State.DeviceState;
goto ClasspPowerHandlerCleanup;
}
context = &(fdoExtension->PowerContext);
#if DBG
//
// Mark the context as in use. We should be synchronizing this but
// since it's just for debugging purposes we don't worry too much.
//
ASSERT(context->InUse == FALSE);
#endif
RtlZeroMemory(context, sizeof(CLASS_POWER_CONTEXT));
context->InUse = TRUE;
nextIrpStack->Parameters.Scsi.Srb = &(context->Srb);
nextIrpStack->MajorFunction = IRP_MJ_SCSI;
context->FinalStatus = STATUS_SUCCESS;
context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
context->Srb.OriginalRequest = Irp;
context->Srb.SrbFlags |= SRB_FLAGS_BYPASS_LOCKED_QUEUE
| SRB_FLAGS_NO_QUEUE_FREEZE;
context->Srb.Function = SRB_FUNCTION_LOCK_QUEUE;
context->Srb.SenseInfoBuffer =
commonExtension->PartitionZeroExtension->SenseData;
context->Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE;
context->RetryCount = MAXIMUM_RETRIES;
context->Options = Options;
context->DeviceObject = DeviceObject;
context->Irp = Irp;
if(irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0) {
ASSERT(Options.HandleSpinUp);
DebugPrint((2, "(%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 {
ASSERT(Options.HandleSpinDown);
fdoExtension->PowerDownInProgress = TRUE;
DebugPrint((2, "(%p)\tPowering down - locking queue\n", Irp));
PoSetPowerState(DeviceObject,
irpStack->Parameters.Power.Type,
irpStack->Parameters.Power.State);
context->Options.PowerDown = TRUE;
context->PowerChangeState.PowerDown2 = PowerDownDeviceInitial2;
context->CompletionRoutine = ClasspPowerDownCompletion;
}
//
// we are not dealing with port-allocated sense in these routines.
//
ASSERT(!TEST_FLAG(context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
ASSERT(!TEST_FLAG(context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
//
// we are always returning STATUS_PENDING, so we need to always
// set the irp as pending.
//
IoMarkIrpPending(Irp);
if(Options.LockQueue) {
//
// Send the lock irp down.
//
IoSetCompletionRoutine(Irp,
context->CompletionRoutine,
context,
TRUE,
TRUE,
TRUE);
IoCallDriver(lowerDevice, Irp);
} 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, Irp, context);
}
return STATUS_PENDING;
ClasspPowerHandlerCleanup:
ClassReleaseRemoveLock(DeviceObject, Irp);
DebugPrint((1, "(%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
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 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
//
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
--*/
NTSTATUS
ClassSpinDownPowerHandler(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
CLASS_POWER_OPTIONS options;
fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
//
// this will set all options to FALSE
//
RtlZeroMemory(&options, sizeof(CLASS_POWER_OPTIONS));
//
// 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;
}
DebugPrint((3, "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
ClassStopUnitPowerHandler(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
DebugPrint((0, "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
)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
PSCSI_REQUEST_BLOCK srb = &(Context->Srb);
LARGE_INTEGER dueTime;
DebugPrint((1, "(%p)\tDelaying retry by queueing DPC\n", Irp));
ASSERT(Context->Irp == Irp);
ASSERT(Context->DeviceObject == DeviceObject);
ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
//
// reset the retry interval
//
Context->RetryInterval = 0;
//
// Reset byte count of transfer in SRB Extension.
//
srb->DataTransferLength = 0;
//
// Zero SRB statuses.
//
srb->SrbStatus = srb->ScsiStatus = 0;
//
// Set up major SCSI function.
//
nextIrpStack->MajorFunction = IRP_MJ_SCSI;
//
// Save SRB address in next stack for port driver.
//
nextIrpStack->Parameters.Scsi.Srb = srb;
//
// Set the completion routine up again.
//
IoSetCompletionRoutine(Irp, Context->CompletionRoutine, Context,
TRUE, TRUE, TRUE);
if (Context->RetryInterval == 0) {
DebugPrint((2, "(%p)\tDelaying minimum time (.2 sec)\n", Irp));
dueTime.QuadPart = (LONGLONG)1000000 * 2;
} else {
DebugPrint((2, "(%p)\tDelaying %x seconds\n",
Irp, Context->RetryInterval));
dueTime.QuadPart = (LONGLONG)1000000 * 10 * Context->RetryInterval;
}
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
ClasspStartNextPowerIrpCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
if(Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
PoStartNextPowerIrp(Irp);
return STATUS_SUCCESS;
} // end ClasspStartNextPowerIrpCompletion()