570 lines
14 KiB
C
570 lines
14 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (C) Microsoft Corporation, 1996 - 1999
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
power.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the routines for port driver power support
|
|||
|
|
|||
|
Authors:
|
|||
|
|
|||
|
Peter Wieland
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "port.h"
|
|||
|
|
|||
|
#define __FILE_ID__ 'enab'
|
|||
|
|
|||
|
typedef struct _REINIT_CONTEXT {
|
|||
|
IN PADAPTER_EXTENSION Adapter;
|
|||
|
IN BOOLEAN UseAdapterControl;
|
|||
|
OUT NTSTATUS Status;
|
|||
|
} REINIT_CONTEXT, *PREINIT_CONTEXT;
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SpReinitializeAdapter(
|
|||
|
IN PADAPTER_EXTENSION Adapter
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SpReinitializeAdapterSynchronized(
|
|||
|
IN PREINIT_CONTEXT Context
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SpShutdownAdapter(
|
|||
|
IN PADAPTER_EXTENSION Adapter
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SpShutdownAdapterSynchronized(
|
|||
|
IN PADAPTER_EXTENSION Adapter
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SpEnableDisableCompletionRoutine(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Context
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SpEnableDisableAdapter(
|
|||
|
IN PADAPTER_EXTENSION Adapter,
|
|||
|
IN BOOLEAN Enable
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will synchronously enable or disable the specified adapter.
|
|||
|
It should be called from the adapter's StartIo routine when a power
|
|||
|
irp is processed for the controller.
|
|||
|
|
|||
|
When the adapter is disabled the state of the adapter will be saved and
|
|||
|
the miniport will be shut-down. When the adapter is re-enabled the
|
|||
|
miniport will be reinitialized.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Adapter - the adapter to be [en|dis]abled.
|
|||
|
Enable - whether to enable or disable the adapter.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
status of the adapter enable/disable action.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG count;
|
|||
|
KIRQL oldIrql;
|
|||
|
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
|
|||
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
|||
|
|
|||
|
DebugPrint((1, "SpEnableDisableAdapter: Adapter %#p %s\n",
|
|||
|
Adapter, Enable ? "enable":"disable"));
|
|||
|
|
|||
|
if(Enable) {
|
|||
|
count = --Adapter->DisableCount;
|
|||
|
|
|||
|
DebugPrint((1, "SpEnableDisableAdapter: DisableCount is %d\n",
|
|||
|
count));
|
|||
|
|
|||
|
if(count == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// Re-initialize the adapter.
|
|||
|
//
|
|||
|
|
|||
|
status = SpReinitializeAdapter(Adapter);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
count = Adapter->DisableCount++;
|
|||
|
|
|||
|
DebugPrint((1, "SpEnableDisableAdapter: DisableCount was %d\n",
|
|||
|
count));
|
|||
|
|
|||
|
if(count == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// Shut-down the adapter.
|
|||
|
//
|
|||
|
|
|||
|
status = SpShutdownAdapter(Adapter);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
KeLowerIrql(oldIrql);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SpEnableDisableLogicalUnit(
|
|||
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|||
|
IN BOOLEAN Enable,
|
|||
|
IN PSP_ENABLE_DISABLE_COMPLETION_ROUTINE CompletionRoutine,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will enable the specified logical unit. An unlock queue
|
|||
|
request will be issued to the logical unit - if the lock count drops
|
|||
|
to zero then the device will be re-enabled and i/o processing can be
|
|||
|
restarted.
|
|||
|
|
|||
|
If STATUS_PENDING is returned then the completion routine will be run
|
|||
|
when the unlock has been processed. The routine will be called with
|
|||
|
the status of the unlock request (note that STATUS_SUCCESS does not
|
|||
|
necessarily indicate that the device is ready for use - just that the
|
|||
|
lock count has been decremented) and the specified context.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
LogicalUnit - the logical unit to be enabled.
|
|||
|
|
|||
|
Enable - whether the routine should enable or disable the logical unit
|
|||
|
|
|||
|
CompletionRoutine - the completion routine to be run when the unlock
|
|||
|
request has succeeded.
|
|||
|
|
|||
|
Context - arbitrary context value/pointer which will be passed into the
|
|||
|
enable completion routine.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_PENDING if the request to send an unlock succeeds and the
|
|||
|
completion routine will be called.
|
|||
|
|
|||
|
error if the attempt to send the irp fails. In this case the completion
|
|||
|
routine will not be called.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
USHORT srbSize;
|
|||
|
USHORT size;
|
|||
|
|
|||
|
PIRP irp;
|
|||
|
PSCSI_REQUEST_BLOCK srb;
|
|||
|
PIO_STACK_LOCATION nextStack;
|
|||
|
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
DebugPrint((4, "SpEnableDisableLun: Lun %#p %s context %#p\n",
|
|||
|
LogicalUnit, Enable ? "enable":"disable", Context));
|
|||
|
|
|||
|
srbSize = sizeof(SCSI_REQUEST_BLOCK);
|
|||
|
srbSize += sizeof(ULONG) - 1;
|
|||
|
srbSize &= ~(sizeof(ULONG) - 1);
|
|||
|
|
|||
|
size = srbSize + IoSizeOfIrp(LogicalUnit->DeviceObject->StackSize + 1);
|
|||
|
|
|||
|
srb = SpAllocatePool(NonPagedPool,
|
|||
|
size,
|
|||
|
SCSIPORT_TAG_ENABLE,
|
|||
|
LogicalUnit->DeviceObject->DriverObject);
|
|||
|
|
|||
|
if(srb == NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Already failed. Call the completion routine will the failure status
|
|||
|
// and let it clean up the request.
|
|||
|
//
|
|||
|
|
|||
|
DebugPrint((1, "SpEnableDisableLogicalUnit: failed to allocate SRB\n"));
|
|||
|
|
|||
|
if(CompletionRoutine != NULL) {
|
|||
|
CompletionRoutine(LogicalUnit->DeviceObject,
|
|||
|
STATUS_INSUFFICIENT_RESOURCES,
|
|||
|
Context);
|
|||
|
}
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
irp = (PIRP) (srb + 1);
|
|||
|
|
|||
|
IoInitializeIrp(irp,
|
|||
|
(USHORT) (size - srbSize),
|
|||
|
(CCHAR)(LogicalUnit->DeviceObject->StackSize + 1));
|
|||
|
|
|||
|
nextStack = IoGetNextIrpStackLocation(irp);
|
|||
|
|
|||
|
nextStack->Parameters.Others.Argument1 = LogicalUnit->DeviceObject;
|
|||
|
nextStack->Parameters.Others.Argument2 = CompletionRoutine;
|
|||
|
nextStack->Parameters.Others.Argument3 = Context;
|
|||
|
nextStack->Parameters.Others.Argument4 = 0; // retry count
|
|||
|
|
|||
|
IoSetNextIrpStackLocation(irp);
|
|||
|
|
|||
|
IoSetCompletionRoutine(irp,
|
|||
|
SpEnableDisableCompletionRoutine,
|
|||
|
srb,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE);
|
|||
|
|
|||
|
nextStack = IoGetNextIrpStackLocation(irp);
|
|||
|
|
|||
|
nextStack->MajorFunction = IRP_MJ_SCSI;
|
|||
|
nextStack->MinorFunction = 1;
|
|||
|
|
|||
|
nextStack->Parameters.Scsi.Srb = srb;
|
|||
|
|
|||
|
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
|
|||
|
|
|||
|
srb->Length = sizeof(SCSI_REQUEST_BLOCK);
|
|||
|
srb->OriginalRequest = irp;
|
|||
|
|
|||
|
srb->SrbFlags = SRB_FLAGS_BYPASS_LOCKED_QUEUE;
|
|||
|
|
|||
|
srb->Function = Enable ? SRB_FUNCTION_UNLOCK_QUEUE :
|
|||
|
SRB_FUNCTION_LOCK_QUEUE;
|
|||
|
|
|||
|
status = IoCallDriver(LogicalUnit->DeviceObject, irp);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SpEnableDisableCompletionRoutine(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PSCSI_REQUEST_BLOCK Srb
|
|||
|
)
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpStack;
|
|||
|
PSP_ENABLE_DISABLE_COMPLETION_ROUTINE routine;
|
|||
|
PVOID context;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
DebugPrint((4, "SpEnableDisableCompletionRoutine: irp %#p completed "
|
|||
|
"with status %#08lx\n",
|
|||
|
Irp, Irp->IoStatus.Status));
|
|||
|
|
|||
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
|
|||
|
routine = irpStack->Parameters.Others.Argument2;
|
|||
|
context = irpStack->Parameters.Others.Argument3;
|
|||
|
|
|||
|
DeviceObject = irpStack->Parameters.Others.Argument1;
|
|||
|
|
|||
|
status = Irp->IoStatus.Status;
|
|||
|
|
|||
|
//
|
|||
|
// Free the srb which will also release the irp.
|
|||
|
//
|
|||
|
|
|||
|
ExFreePool(Srb);
|
|||
|
|
|||
|
if(routine != NULL) {
|
|||
|
routine(DeviceObject, status, context);
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SpReinitializeAdapter(
|
|||
|
IN PADAPTER_EXTENSION Adapter
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will allow the miniport to restore any state or configuration
|
|||
|
information and then to restart it's adapter. Adapter interrupts will
|
|||
|
be re-enabled at the return of this routine and scsiport may begin issuing
|
|||
|
new requests to the miniport.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Adapter - the adapter to be re-initialized.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
status
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG oldDebug;
|
|||
|
|
|||
|
KIRQL oldIrql;
|
|||
|
|
|||
|
ULONG result;
|
|||
|
BOOLEAN again;
|
|||
|
|
|||
|
BOOLEAN useAdapterControl;
|
|||
|
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
UCHAR string[] = {'W', 'A', 'K', 'E', 'U', 'P', '\0'};
|
|||
|
|
|||
|
//
|
|||
|
// Give the miniport a chance to restore it's bus-data to a state which
|
|||
|
// allows the miniport to operate.
|
|||
|
//
|
|||
|
|
|||
|
if(SpIsAdapterControlTypeSupported(Adapter, ScsiRestartAdapter) == TRUE) {
|
|||
|
DebugPrint((1, "SpReinitializeAdapter: using AdapterControl\n"));
|
|||
|
useAdapterControl = TRUE;
|
|||
|
} else {
|
|||
|
DebugPrint((1, "SpReinitializeAdapter: using init routines\n"));
|
|||
|
useAdapterControl = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
oldIrql = KeRaiseIrqlToDpcLevel();
|
|||
|
|
|||
|
KeAcquireSpinLockAtDpcLevel(&(Adapter->SpinLock));
|
|||
|
|
|||
|
//
|
|||
|
// Since we may be reinitializing the miniport at DISPATCH_LEVEL we need
|
|||
|
// to set the miniport reinitializing flag for some of the SCSIPORT api's
|
|||
|
// to modify their behavior.
|
|||
|
//
|
|||
|
|
|||
|
SET_FLAG(Adapter->Flags, PD_MINIPORT_REINITIALIZING);
|
|||
|
|
|||
|
result = SP_RETURN_FOUND;
|
|||
|
|
|||
|
if(useAdapterControl == FALSE) {
|
|||
|
|
|||
|
result = Adapter->HwFindAdapter(Adapter->HwDeviceExtension,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
string,
|
|||
|
Adapter->PortConfig,
|
|||
|
&again);
|
|||
|
} else if(SpIsAdapterControlTypeSupported(Adapter,
|
|||
|
ScsiSetRunningConfig) == TRUE) {
|
|||
|
SCSI_ADAPTER_CONTROL_STATUS b;
|
|||
|
|
|||
|
b = SpCallAdapterControl(Adapter, ScsiSetRunningConfig, NULL);
|
|||
|
|
|||
|
ASSERT(b == ScsiAdapterControlSuccess);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if(result == SP_RETURN_FOUND) {
|
|||
|
|
|||
|
REINIT_CONTEXT context;
|
|||
|
|
|||
|
context.Adapter = Adapter;
|
|||
|
context.UseAdapterControl = useAdapterControl;
|
|||
|
|
|||
|
Adapter->SynchronizeExecution(Adapter->InterruptObject,
|
|||
|
SpReinitializeAdapterSynchronized,
|
|||
|
&context);
|
|||
|
|
|||
|
status = context.Status;
|
|||
|
} else {
|
|||
|
status = STATUS_DRIVER_INTERNAL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
if(NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// We had better be ready for another request by now.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortNotification(NextRequest,
|
|||
|
Adapter->HwDeviceExtension);
|
|||
|
|
|||
|
if (Adapter->InterruptData.InterruptFlags & PD_NOTIFICATION_REQUIRED) {
|
|||
|
|
|||
|
//
|
|||
|
// Request a completion DPC so that we clear out any existing
|
|||
|
// attempts to do things like reset the bus.
|
|||
|
//
|
|||
|
|
|||
|
SpRequestCompletionDpc(Adapter->DeviceObject);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
KeReleaseSpinLockFromDpcLevel(&(Adapter->SpinLock));
|
|||
|
|
|||
|
ASSERT(NT_SUCCESS(status));
|
|||
|
|
|||
|
KeLowerIrql(oldIrql);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SpReinitializeAdapterSynchronized(
|
|||
|
IN PREINIT_CONTEXT Context
|
|||
|
)
|
|||
|
{
|
|||
|
PADAPTER_EXTENSION adapter = Context->Adapter;
|
|||
|
BOOLEAN result;
|
|||
|
|
|||
|
DebugPrint((1, "SpReinitializeAdapterSynchronized: calling "
|
|||
|
"HwFindAdapter\n"));
|
|||
|
|
|||
|
if(Context->UseAdapterControl) {
|
|||
|
SCSI_ADAPTER_CONTROL_TYPE status;
|
|||
|
|
|||
|
status = SpCallAdapterControl(adapter, ScsiRestartAdapter, NULL);
|
|||
|
result = (status == ScsiAdapterControlSuccess);
|
|||
|
|
|||
|
} else {
|
|||
|
result = adapter->HwInitialize(adapter->HwDeviceExtension);
|
|||
|
}
|
|||
|
|
|||
|
if(result == TRUE) {
|
|||
|
Context->Status = STATUS_SUCCESS;
|
|||
|
} else {
|
|||
|
Context->Status = STATUS_ADAPTER_HARDWARE_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
DebugPrint((1, "SpReinitializeAdapterSynchronized: enabling interrupts\n"));
|
|||
|
|
|||
|
adapter->InterruptData.InterruptFlags &= ~PD_DISABLE_INTERRUPTS;
|
|||
|
|
|||
|
CLEAR_FLAG(adapter->Flags, PD_MINIPORT_REINITIALIZING);
|
|||
|
CLEAR_FLAG(adapter->Flags, PD_UNCACHED_EXTENSION_RETURNED);
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SpShutdownAdapter(
|
|||
|
IN PADAPTER_EXTENSION Adapter
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will shutdown the miniport and save away any state information
|
|||
|
necessary to restart it. Adapter interrupts will be disabled at the
|
|||
|
return of this routine - scsiport will not issue any new requests to the
|
|||
|
miniport until it has been reinitialized.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Adapter - the adapter to be shut down.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
status
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// Acquire the adapter spinlock and set state to indicate that a
|
|||
|
// shutdown is in progress. This will prevent us from starting
|
|||
|
// operations that shouldn't be started while shutting down.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireSpinLockAtDpcLevel(&Adapter->SpinLock);
|
|||
|
SET_FLAG(Adapter->Flags, PD_SHUTDOWN_IN_PROGRESS);
|
|||
|
KeReleaseSpinLockFromDpcLevel(&Adapter->SpinLock);
|
|||
|
|
|||
|
//
|
|||
|
// Cancel the miniport timer so that we don't call into it after we've
|
|||
|
// shut down.
|
|||
|
//
|
|||
|
|
|||
|
KeCancelTimer(&(Adapter->MiniPortTimer));
|
|||
|
|
|||
|
//
|
|||
|
// Currently we don't give the miniport any chance to save any sort of
|
|||
|
// state information. We just stop any i/o going into it and then do
|
|||
|
// a shutdown.
|
|||
|
//
|
|||
|
|
|||
|
Adapter->SynchronizeExecution(Adapter->InterruptObject,
|
|||
|
SpShutdownAdapterSynchronized,
|
|||
|
Adapter);
|
|||
|
|
|||
|
if(SpIsAdapterControlTypeSupported(Adapter, ScsiSetBootConfig)) {
|
|||
|
|
|||
|
//
|
|||
|
// Allow the miniport a chance to reset its PCI bus data before we
|
|||
|
// power it off.
|
|||
|
//
|
|||
|
|
|||
|
SpCallAdapterControl(Adapter, ScsiSetBootConfig, NULL);
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SpShutdownAdapterSynchronized(
|
|||
|
IN PADAPTER_EXTENSION Adapter
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine performs the ISR synchronized part of shutting down the
|
|||
|
miniport. This includes disabling the interrupt and request a shutdown
|
|||
|
of the miniport.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Adapter - the adapter to shut down.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
SpCallAdapterControl(Adapter, ScsiStopAdapter, NULL);
|
|||
|
DebugPrint((1, "SpShutdownAdapterSynchronized: Disabling interrupts\n"));
|
|||
|
Adapter->InterruptData.InterruptFlags |= PD_DISABLE_INTERRUPTS;
|
|||
|
CLEAR_FLAG(Adapter->Flags, PD_SHUTDOWN_IN_PROGRESS);
|
|||
|
return TRUE;
|
|||
|
}
|