1614 lines
49 KiB
C
1614 lines
49 KiB
C
/*++
|
||
|
||
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()
|