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.
 
 
 
 
 
 

569 lines
14 KiB

/*++
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;
}