5947 lines
168 KiB
C
5947 lines
168 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
wd33c93.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the WD33C93-specific functions for the NT SCSI port
|
||
driver.
|
||
|
||
Author:
|
||
|
||
Jeff Havens (jhavens) 10-June-1991
|
||
|
||
Environment:
|
||
|
||
Kernel Mode only
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "miniport.h"
|
||
#include "scsi.h"
|
||
#include "wd33c93.h"
|
||
#include "vendor.h"
|
||
|
||
#if DBG
|
||
int WdDebug;
|
||
#define WdPrint(arg) ScsiDebugPrint arg
|
||
#else
|
||
#define WdPrint(arg)
|
||
#endif
|
||
|
||
#define ScsiPortGetNextLink(srb) srb->NextSrb
|
||
|
||
//
|
||
// Define SCSI Protocol Chip configuration parameters.
|
||
//
|
||
|
||
#define INITIATOR_BUS_ID 0x7
|
||
#define RESET_STALL_TIME 25 // The minimum assertion time for a SCSI bus reset.
|
||
#define RESET_DELAY_TIME 3 // Time in 250ms increments to delay after reset.
|
||
#define INTERRUPT_STALL_TIME 50 // Time to wait for the next interrupt.
|
||
#define INTERRUPT_CLEAR_TIME 10
|
||
#define DATA_BUS_READY_TIME 3000
|
||
|
||
|
||
//
|
||
// WD33C93-specific port driver device extension flags.
|
||
//
|
||
|
||
#define PD_SYNCHRONOUS_RESPONSE_SENT 0X0001
|
||
#define PD_SYNCHRONOUS_TRANSFER_SENT 0X0002
|
||
#define PD_PENDING_START_IO 0X0004
|
||
#define PD_MESSAGE_OUT_VALID 0X0008
|
||
#define PD_DISCONNECT_EXPECTED 0X0010
|
||
#define PD_SEND_MESSAGE_REQUEST 0X0020
|
||
#define PD_POSSIBLE_EXTRA_MESSAGE_OUT 0X0040
|
||
#define PD_DISCONNECT_INTERRUPT_ENABLED 0X0080
|
||
#define PD_DMA_ACTIVE 0X0100
|
||
#define PD_PARITY_ERROR 0X0200
|
||
#define PD_PENDING_DATA_TRANSFER 0X0400
|
||
|
||
//
|
||
// The following defines specify masks which are used to clear flags when
|
||
// specific events occur, such as reset or disconnect.
|
||
//
|
||
|
||
#define PD_ADAPTER_RESET_MASK ( PD_SYNCHRONOUS_TRANSFER_SENT | \
|
||
PD_PENDING_START_IO | \
|
||
PD_MESSAGE_OUT_VALID | \
|
||
PD_SEND_MESSAGE_REQUEST | \
|
||
PD_POSSIBLE_EXTRA_MESSAGE_OUT | \
|
||
PD_DMA_ACTIVE | \
|
||
PD_PARITY_ERROR | \
|
||
PD_PENDING_DATA_TRANSFER | \
|
||
PD_DISCONNECT_EXPECTED \
|
||
)
|
||
|
||
#define PD_ADAPTER_DISCONNECT_MASK ( PD_SYNCHRONOUS_TRANSFER_SENT | \
|
||
PD_MESSAGE_OUT_VALID | \
|
||
PD_SEND_MESSAGE_REQUEST | \
|
||
PD_POSSIBLE_EXTRA_MESSAGE_OUT | \
|
||
PD_PARITY_ERROR | \
|
||
PD_PENDING_DATA_TRANSFER | \
|
||
PD_DISCONNECT_EXPECTED \
|
||
)
|
||
|
||
//
|
||
// The largest SCSI bus message expected.
|
||
//
|
||
|
||
#define MESSAGE_BUFFER_SIZE 7
|
||
|
||
//
|
||
// Retry count limits.
|
||
//
|
||
|
||
#define RETRY_SELECTION_LIMIT 1
|
||
#define RETRY_ERROR_LIMIT 2
|
||
#define MAX_INTERRUPT_COUNT 64
|
||
|
||
//
|
||
// Bus and chip states.
|
||
//
|
||
|
||
typedef enum _ADAPTER_STATE {
|
||
BusFree,
|
||
Select,
|
||
SelectAndTransfer,
|
||
CommandOut,
|
||
DataTransfer,
|
||
DataTransferComplete,
|
||
DisconnectExpected,
|
||
MessageAccepted,
|
||
MessageIn,
|
||
MessageOut,
|
||
StatusIn
|
||
} ADAPTER_STATE, *PADAPTER_STATE;
|
||
|
||
//
|
||
// WD33C93-specific port driver logical unit flags.
|
||
//
|
||
|
||
#define PD_SYNCHRONOUS_NEGOTIATION_DONE 0X0001
|
||
#define PD_DO_NOT_NEGOTIATE 0X0002
|
||
#define PD_STATUS_VALID 0X0004
|
||
#define PD_DO_NOT_CHECK_TRANSFER_LENGTH 0X0008
|
||
#define PD_INITIATE_RECOVERY 0X0010
|
||
|
||
//
|
||
// The following defines specify masks which are used to clear flags when
|
||
// specific events occur, such as reset or command complete.
|
||
//
|
||
|
||
#define PD_LU_COMPLETE_MASK (PD_STATUS_VALID | \
|
||
PD_DO_NOT_CHECK_TRANSFER_LENGTH | \
|
||
PD_INITIATE_RECOVERY \
|
||
)
|
||
|
||
#define PD_LU_RESET_MASK (PD_SYNCHRONOUS_NEGOTIATION_DONE |\
|
||
PD_STATUS_VALID | \
|
||
PD_DO_NOT_CHECK_TRANSFER_LENGTH | \
|
||
PD_INITIATE_RECOVERY \
|
||
)
|
||
//
|
||
// WD33C93-specific port driver logical unit extension.
|
||
//
|
||
|
||
typedef struct _SPECIFIC_LOGICAL_UNIT_EXTENSION {
|
||
USHORT LuFlags;
|
||
UCHAR SynchronousPeriod;
|
||
UCHAR SynchronousOffset;
|
||
ULONG SavedDataPointer;
|
||
ULONG SavedDataLength;
|
||
ULONG MaximumTransferLength;
|
||
PSCSI_REQUEST_BLOCK ActiveLuRequest;
|
||
PSCSI_REQUEST_BLOCK ActiveSendRequest;
|
||
ULONG RetryCount;
|
||
UCHAR SavedCommandPhase;
|
||
}SPECIFIC_LOGICAL_UNIT_EXTENSION, *PSPECIFIC_LOGICAL_UNIT_EXTENSION;
|
||
|
||
//
|
||
// WD33C93-specific port driver device object extension.
|
||
//
|
||
|
||
typedef struct _SPECIFIC_DEVICE_EXTENSION {
|
||
ULONG AdapterFlags;
|
||
ADAPTER_STATE AdapterState; // Current state of the adapter
|
||
PCARD_REGISTERS Adapter; // Address of the WD33C93 card
|
||
AUXILIARY_STATUS AdapterStatus; // Saved status register value
|
||
SCSI_STATUS AdapterInterrupt; // Saved interrupt status register
|
||
UCHAR CommandPhase; // Saved command phase value
|
||
UCHAR InitiatorBusId; // This adapter's SCSI bus ID in bit mask form
|
||
UCHAR DmaCommand;
|
||
UCHAR DmaPhase;
|
||
UCHAR MessageCount; // Count of bytes in message buffer
|
||
UCHAR MessageSent; // Count of bytes sent to target
|
||
UCHAR RequestCount;
|
||
UCHAR DmaCode;
|
||
UCHAR IrqCode;
|
||
UCHAR MessageBuffer[MESSAGE_BUFFER_SIZE]; // SCSI bus message buffer
|
||
ULONG ActiveDataPointer; // SCSI bus active data pointer
|
||
ULONG ActiveDataLength; // The amount of data to be transferred.
|
||
LONG InterruptCount; // Count of interrupts since connection.
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION ActiveLogicalUnit;
|
||
|
||
// Pointer to the active request.
|
||
|
||
PSCSI_REQUEST_BLOCK NextSrbRequest;
|
||
// Pointer to the next SRB to process.
|
||
|
||
} SPECIFIC_DEVICE_EXTENSION, *PSPECIFIC_DEVICE_EXTENSION;
|
||
|
||
|
||
//
|
||
// Functions passed to the OS-specific port driver.
|
||
//
|
||
|
||
ULONG
|
||
WdFindAdapter(
|
||
IN PVOID ServiceContext,
|
||
IN PVOID Context,
|
||
IN PVOID BusInformation,
|
||
IN PCHAR ArgumentString,
|
||
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
||
OUT PBOOLEAN Again
|
||
);
|
||
|
||
BOOLEAN
|
||
WdInitializeAdapter(
|
||
IN PVOID ServiceContext
|
||
);
|
||
|
||
BOOLEAN
|
||
WdInterruptServiceRoutine(
|
||
IN PVOID ServiceContext
|
||
);
|
||
|
||
BOOLEAN
|
||
WdResetScsiBus(
|
||
IN PVOID ServiceContext,
|
||
IN ULONG PathId
|
||
);
|
||
|
||
VOID
|
||
WdSetupDma(
|
||
PVOID ServiceContext
|
||
);
|
||
|
||
BOOLEAN
|
||
WdStartIo(
|
||
IN PVOID ServiceContext,
|
||
IN PSCSI_REQUEST_BLOCK Srb
|
||
);
|
||
|
||
//
|
||
// WD33C93-specific internal mini-port driver functions.
|
||
//
|
||
|
||
VOID
|
||
WdAcceptMessage(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN BOOLEAN SetAttention,
|
||
IN BOOLEAN SetSynchronousParameters
|
||
);
|
||
|
||
VOID
|
||
WdCleanupAfterReset(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN BOOLEAN ExternalReset
|
||
);
|
||
|
||
VOID
|
||
WdCompleteSendMessage(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN ULONG SrbStatus
|
||
);
|
||
|
||
BOOLEAN
|
||
WdDecodeSynchronousRequest(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension,
|
||
IN BOOLEAN ResponseExpected
|
||
);
|
||
|
||
VOID
|
||
WdDumpState(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
|
||
);
|
||
|
||
BOOLEAN
|
||
WdIssueCommand(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN UCHAR CommandByte,
|
||
IN LONG TransferCount,
|
||
IN UCHAR CommandPhase
|
||
);
|
||
|
||
VOID
|
||
WdLogError(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN ULONG ErrorCode,
|
||
IN ULONG UniqueId
|
||
);
|
||
|
||
BOOLEAN
|
||
WdMessageDecode(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
|
||
);
|
||
|
||
VOID
|
||
WdProcessRequestCompletion(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
|
||
);
|
||
|
||
BOOLEAN
|
||
WdProcessReselection(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN UCHAR TargetId,
|
||
IN UCHAR LogicalUnitNumber
|
||
);
|
||
|
||
VOID
|
||
WdResetScsiBusInternal(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN ULONG PathId
|
||
);
|
||
|
||
VOID
|
||
WdSelectTarget(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension
|
||
);
|
||
|
||
VOID
|
||
WdSendMessage(
|
||
IN PSCSI_REQUEST_BLOCK Srb,
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension
|
||
);
|
||
|
||
VOID
|
||
WdStartExecution(
|
||
PSCSI_REQUEST_BLOCK Srb,
|
||
PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension
|
||
);
|
||
|
||
BOOLEAN
|
||
WdTransferInformation(
|
||
PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
PUCHAR BufferPointer,
|
||
ULONG Count,
|
||
BOOLEAN TransferToChip
|
||
);
|
||
|
||
ULONG
|
||
WdParseArgumentString(
|
||
IN PCHAR String,
|
||
IN PCHAR KeyWord
|
||
);
|
||
|
||
VOID
|
||
WdAcceptMessage(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN BOOLEAN SetAttention,
|
||
IN BOOLEAN SetSynchronousParameters
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure tells the adapter to accept a pending message on the SCSI
|
||
bus. Optionally, it will set the synchronous transfer parameters and the
|
||
attention signal.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies a pointer to the device extension.
|
||
|
||
SetAttention - Indicates the attention line on the SCSI bus should be set.
|
||
|
||
SetSynchronousParameters - Indicates the synchronous data transfer
|
||
parameters should be set.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension;
|
||
SCSI_SYNCHRONOUS scsiSynchronous;
|
||
|
||
/* Powerfail */
|
||
|
||
//
|
||
// Check to see if the synchonous data transfer parameters need to be set.
|
||
//
|
||
|
||
if (SetSynchronousParameters) {
|
||
|
||
//
|
||
// These must be set before a data transfer is started.
|
||
//
|
||
|
||
luExtension = DeviceExtension->ActiveLogicalUnit;
|
||
*((PUCHAR) &scsiSynchronous) = 0;
|
||
scsiSynchronous.SynchronousOffset = luExtension->SynchronousOffset;
|
||
scsiSynchronous.SynchronousPeriod = luExtension->SynchronousPeriod;
|
||
|
||
SCSI_WRITE(
|
||
DeviceExtension->Adapter,
|
||
Synchronous,
|
||
*((PUCHAR) &scsiSynchronous)
|
||
);
|
||
}
|
||
|
||
//
|
||
// Check to see if the attention signal needs to be set.
|
||
//
|
||
|
||
if (SetAttention) {
|
||
|
||
//
|
||
// This requests that the target enter the message-out phase.
|
||
//
|
||
|
||
WdIssueCommand(DeviceExtension, ASSERT_ATN, -1, 0);
|
||
}
|
||
|
||
//
|
||
// Indicate to the adapter that the message-in phase may now be completed.
|
||
//
|
||
|
||
WdIssueCommand(DeviceExtension, NEGATE_ACK, -1, 0);
|
||
}
|
||
|
||
|
||
VOID
|
||
WdCleanupAfterReset(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN BOOLEAN ExternalReset
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine cleans up the adapter-specific
|
||
and logical-unit-specific data structures. Any active requests are
|
||
completed and the synchronous negotiation flags are cleared.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies a pointer to device extension for the bus that
|
||
was reset.
|
||
|
||
ExternalReset - When set, indicates that the reset was generated by a
|
||
SCSI device other than this host adapter.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG pathId = 0;
|
||
LONG targetId;
|
||
LONG luId;
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension;
|
||
|
||
//
|
||
// Check to see if a data transfer was in progress, if so, flush the DMA.
|
||
//
|
||
|
||
if (DeviceExtension->AdapterFlags & PD_DMA_ACTIVE) {
|
||
CARD_DMA_TERMINATE( DeviceExtension );
|
||
ScsiPortFlushDma(DeviceExtension);
|
||
}
|
||
|
||
//
|
||
// if the current state is Select then SCSI port driver needs
|
||
// to be notified that new requests can be sent.
|
||
//
|
||
|
||
if (DeviceExtension->AdapterFlags & PD_PENDING_START_IO) {
|
||
|
||
//
|
||
// Ask for another request and clear the pending one. The pending
|
||
// request will be processed when the rest of the active requests
|
||
// are completed.
|
||
|
||
DeviceExtension->NextSrbRequest = NULL;
|
||
DeviceExtension->AdapterFlags &= ~PD_PENDING_START_IO;
|
||
|
||
ScsiPortNotification( NextRequest, DeviceExtension, NULL );
|
||
|
||
}
|
||
|
||
//
|
||
// If there was an active request, then complete it with
|
||
// SRB_STATUS_PHASE_SEQUENCE_FAILURE so the class driver will know not
|
||
// to retry it too many times.
|
||
//
|
||
|
||
if (DeviceExtension->ActiveLogicalUnit != NULL
|
||
&& DeviceExtension->ActiveLogicalUnit->ActiveLuRequest != NULL) {
|
||
|
||
//
|
||
// Set the SrbStatus in the SRB, complete the request and
|
||
// clear the active pointers
|
||
//
|
||
|
||
luExtension = DeviceExtension->ActiveLogicalUnit;
|
||
|
||
luExtension->ActiveLuRequest->SrbStatus =
|
||
SRB_STATUS_PHASE_SEQUENCE_FAILURE;
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
luExtension->ActiveLuRequest
|
||
);
|
||
|
||
//
|
||
// Check to see if there was a synchronous negotiation in progress. If
|
||
// there was then do not try to negotiate with this target again.
|
||
//
|
||
|
||
if (DeviceExtension->AdapterFlags & (PD_SYNCHRONOUS_RESPONSE_SENT |
|
||
PD_SYNCHRONOUS_TRANSFER_SENT | PD_POSSIBLE_EXTRA_MESSAGE_OUT)) {
|
||
|
||
//
|
||
// This target cannot negotiate properly. Set a flag to prevent
|
||
// further attempts and set the synchronous parameters to use
|
||
// asynchronous data transfer.
|
||
//
|
||
|
||
/* TODO: Consider propagating this flag to all the Lus on this target. */
|
||
luExtension->LuFlags |= PD_DO_NOT_NEGOTIATE;
|
||
luExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
luExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
|
||
}
|
||
|
||
luExtension->ActiveLuRequest = NULL;
|
||
luExtension->RetryCount = 0;
|
||
DeviceExtension->ActiveLogicalUnit = NULL;
|
||
|
||
}
|
||
|
||
//
|
||
// Clear the appropriate state flags as well as the next request.
|
||
// The request will actually be cleared when the logical units are processed.
|
||
// Note that it is not necessary to fail the request waiting to be started
|
||
// since it will be processed properly by the target controller, but it
|
||
// is cleared anyway.
|
||
//
|
||
|
||
for (targetId = 0; targetId < SCSI_MAXIMUM_TARGETS; targetId++) {
|
||
|
||
//
|
||
// Loop through each of the possible logical units for this target.
|
||
//
|
||
|
||
for (luId = 0; luId < SCSI_MAXIMUM_LOGICAL_UNITS; luId++) {
|
||
|
||
luExtension = ScsiPortGetLogicalUnit( DeviceExtension,
|
||
(UCHAR)pathId,
|
||
(UCHAR)targetId,
|
||
(UCHAR)luId
|
||
);
|
||
|
||
if (luExtension == NULL) {
|
||
continue;
|
||
}
|
||
|
||
if (luExtension->ActiveLuRequest != NULL) {
|
||
|
||
//
|
||
// Set the SrbStatus in the SRB, complete the request and
|
||
// clear the active pointers
|
||
//
|
||
|
||
luExtension->ActiveLuRequest->SrbStatus =
|
||
SRB_STATUS_BUS_RESET;
|
||
|
||
//
|
||
// Complete the request.
|
||
//
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
luExtension->ActiveLuRequest
|
||
);
|
||
|
||
luExtension->ActiveLuRequest = NULL;
|
||
|
||
}
|
||
|
||
if (luExtension->ActiveSendRequest != NULL) {
|
||
|
||
//
|
||
// Set the SrbStatus in the SRB, complete the request and
|
||
// clear the active pointers
|
||
//
|
||
|
||
luExtension->ActiveSendRequest->SrbStatus =
|
||
SRB_STATUS_BUS_RESET;
|
||
|
||
//
|
||
// Complete the request.
|
||
//
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
luExtension->ActiveSendRequest
|
||
);
|
||
|
||
luExtension->ActiveSendRequest = NULL;
|
||
|
||
}
|
||
|
||
//
|
||
// Clear the necessary logical unit flags.
|
||
//
|
||
|
||
luExtension->LuFlags &= ~PD_LU_RESET_MASK;
|
||
luExtension->RetryCount = 0;
|
||
luExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
luExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
|
||
} /* for luId */
|
||
} /* for targetId */
|
||
|
||
//
|
||
// Set the bus state to free and clear the adapter flags.
|
||
//
|
||
|
||
DeviceExtension->AdapterState = BusFree;
|
||
DeviceExtension->AdapterFlags &= ~PD_ADAPTER_RESET_MASK;
|
||
DeviceExtension->ActiveLogicalUnit = NULL;
|
||
|
||
}
|
||
|
||
VOID
|
||
WdCompleteSendMessage(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN ULONG SrbStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function does the cleanup necessary to complete a send-message request.
|
||
This includes completing any affected execute-I/O requests and cleaning
|
||
up the device extension state.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies a pointer to the device extension of the SCSI bus
|
||
adapter. The active logical unit is stored in ActiveLogicalUnit.
|
||
|
||
SrbStatus - Indicates the status that the request should be completed with
|
||
if the request did not complete normally, then any active execute
|
||
requests are not considered to have been affected.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension;
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
LONG targetId;
|
||
LONG luId;
|
||
|
||
luExtension = DeviceExtension->ActiveLogicalUnit;
|
||
srb = luExtension->ActiveSendRequest;
|
||
|
||
//
|
||
// Clean up any EXECUTE requests which may have been affected by this
|
||
// message.
|
||
//
|
||
|
||
if (SrbStatus == SRB_STATUS_SUCCESS) {
|
||
switch (srb->Function) {
|
||
case SRB_FUNCTION_ABORT_COMMAND:
|
||
|
||
//
|
||
// Make sure there is still a request to complete. If so complete
|
||
// it with an SRB_STATUS_ABORTED status.
|
||
//
|
||
|
||
if (luExtension->ActiveLuRequest == NULL) {
|
||
|
||
//
|
||
// If there is no request, then fail the abort.
|
||
//
|
||
|
||
SrbStatus = SRB_STATUS_ABORT_FAILED;
|
||
break;
|
||
}
|
||
|
||
luExtension->ActiveLuRequest->SrbStatus =
|
||
SRB_STATUS_ABORTED;
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
luExtension->ActiveLuRequest
|
||
);
|
||
|
||
luExtension->ActiveLuRequest = NULL;
|
||
luExtension->RetryCount = 0;
|
||
luExtension->LuFlags &= ~PD_LU_COMPLETE_MASK;
|
||
|
||
break;
|
||
|
||
case SRB_FUNCTION_RESET_DEVICE:
|
||
|
||
//
|
||
// Cycle through each of the possible logical units looking
|
||
// for requests which have been cleared by the target reset.
|
||
//
|
||
|
||
targetId = srb->TargetId;
|
||
|
||
for (luId = 0; luId < SCSI_MAXIMUM_LOGICAL_UNITS; luId) {
|
||
|
||
luExtension = ScsiPortGetLogicalUnit( DeviceExtension,
|
||
srb->PathId,
|
||
(UCHAR)targetId,
|
||
(UCHAR)luId
|
||
);
|
||
|
||
if (luExtension == NULL) {
|
||
continue;
|
||
}
|
||
|
||
if (luExtension->ActiveLuRequest != NULL) {
|
||
|
||
//
|
||
// Set the SrbStatus in the SRB, complete the
|
||
// request and clear the active pointers
|
||
//
|
||
|
||
luExtension->ActiveLuRequest->SrbStatus =
|
||
SRB_STATUS_BUS_RESET;
|
||
|
||
//
|
||
// Complete the request.
|
||
//
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
luExtension->ActiveLuRequest
|
||
);
|
||
|
||
luExtension->RetryCount = 0;
|
||
luExtension->ActiveLuRequest = NULL;
|
||
|
||
//
|
||
// Clear the necessary logical unit flags.
|
||
//
|
||
|
||
luExtension->LuFlags &= ~PD_LU_RESET_MASK;
|
||
}
|
||
} /* for luId */
|
||
|
||
/* TODO: Handle CLEAR QUEUE and ABORT WITH TAG */
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// If an abort request fails then complete target of the abort;
|
||
// otherwise the target of the ABORT may never be compileted.
|
||
//
|
||
|
||
if (srb->Function == SRB_FUNCTION_ABORT_COMMAND) {
|
||
|
||
//
|
||
// Make sure there is still a request to complete. If so
|
||
// it with an SRB_STATUS_ABORTED status.
|
||
//
|
||
|
||
if (luExtension->ActiveLuRequest != NULL) {
|
||
|
||
luExtension->ActiveLuRequest->SrbStatus =
|
||
SRB_STATUS_ABORTED;
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
luExtension->ActiveLuRequest
|
||
);
|
||
|
||
luExtension->ActiveLuRequest = NULL;
|
||
luExtension->RetryCount = 0;
|
||
luExtension->LuFlags &= ~PD_LU_COMPLETE_MASK;
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Complete the actual send-message request.
|
||
//
|
||
|
||
srb->SrbStatus = (UCHAR)SrbStatus;
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
srb
|
||
);
|
||
|
||
//
|
||
// Clear the active send request and PD_SEND_MESSAGE_REQUEST flag.
|
||
//
|
||
|
||
luExtension->ActiveSendRequest = NULL;
|
||
luExtension->RetryCount = 0;
|
||
DeviceExtension->AdapterFlags &= ~PD_SEND_MESSAGE_REQUEST;
|
||
}
|
||
|
||
BOOLEAN
|
||
WdIssueCommand(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN UCHAR CommandByte,
|
||
IN LONG TransferCount,
|
||
IN UCHAR CommandPhase
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function waits for the command buffer to become available and then
|
||
issues the requested command. The transfer count registers
|
||
and CommandPhase are optionally set.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies a pointer to the specific device extension.
|
||
|
||
CommandByte - Supplies the command byte to be written to the SCSI
|
||
protocol chip.
|
||
|
||
TransferCount - Supplies the value to load in transfer count register.
|
||
If -1 is supplied, then the transfer counter is not loaded.
|
||
|
||
CommandPhase - Supplies the value to load into the Command Phase register.
|
||
|
||
Return Value:
|
||
|
||
TRUE - If the command was written.
|
||
|
||
FALSE - If the command could not be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
AUXILIARY_STATUS auxiliaryStatus;
|
||
|
||
//
|
||
// First make sure the SCSI adapter chip is ready for a command.
|
||
//
|
||
|
||
*((PUCHAR) &auxiliaryStatus) = SCSI_READ_AUX(
|
||
DeviceExtension->Adapter,
|
||
AuxiliaryStatus
|
||
);
|
||
|
||
for (i = 0;
|
||
i < INTERRUPT_STALL_TIME && auxiliaryStatus.CommandInProgress;
|
||
i++) {
|
||
|
||
ScsiPortStallExecution(1);
|
||
|
||
*((PUCHAR) &auxiliaryStatus) = SCSI_READ_AUX(
|
||
DeviceExtension->Adapter,
|
||
AuxiliaryStatus
|
||
);
|
||
}
|
||
|
||
if (auxiliaryStatus.CommandInProgress) {
|
||
|
||
//
|
||
// The chip is messed up but there is nothing that can be done so
|
||
// just return false.
|
||
//
|
||
|
||
WdPrint((0, "WdIssueCommand: A command in progress timeout occured! Aux Status 0x%.2X\n", auxiliaryStatus));
|
||
WdDumpState(DeviceExtension);
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Set the transfer count if necessary.
|
||
//
|
||
|
||
if (TransferCount != -1) {
|
||
|
||
//
|
||
// Set up the SCSI protocol chip for the data transfer with the
|
||
// transfer length, regardless of the length.
|
||
//
|
||
|
||
SCSI_WRITE_TRANSFER_COUNT(DeviceExtension->Adapter, TransferCount);
|
||
|
||
}
|
||
|
||
if (CommandByte == SELECT_ATN_AND_TRANSFER ||
|
||
CommandByte == SELECT_AND_TRANSFER) {
|
||
|
||
//
|
||
// These commands use the command phase register, so set it to the
|
||
// requested value.
|
||
//
|
||
|
||
SCSI_WRITE(DeviceExtension->Adapter, CommandPhase, CommandPhase);
|
||
}
|
||
|
||
SCSI_WRITE(DeviceExtension->Adapter, Command, CommandByte);
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
WdMessageDecode(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function decodes the SCSI bus message-in the device extension message
|
||
buffer. After the message is decoded it decides what action to take in
|
||
response to the message. If an outgoing message needs to be sent, then
|
||
it is placed in the message buffer and true is returned. If the message
|
||
is acceptable, then the device state is set either to DisconnectExpected or
|
||
MessageAccepted and the MessageCount is reset to 0.
|
||
|
||
Some messages are made up of serveral bytes. This funtion will simply
|
||
return false when an incomplete message is detected, allowing the target
|
||
to send the rest of the message. The message count is left unchanged.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies a pointer to the specific device extension.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Returns true if there is a reponse message to be sent.
|
||
|
||
FALSE - If there is no response message.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension;
|
||
LONG offset;
|
||
LONG i;
|
||
ULONG savedAdapterFlags;
|
||
PSCSI_EXTENDED_MESSAGE extendedMessage;
|
||
|
||
//
|
||
// Note: the ActivelogicalUnit field could be invalid if the
|
||
// PD_DISCONNECT_EXPECTED flag is set, so luExtension cannot be used until
|
||
// this flag has been checked.
|
||
//
|
||
|
||
luExtension = DeviceExtension->ActiveLogicalUnit;
|
||
savedAdapterFlags = DeviceExtension->AdapterFlags;
|
||
|
||
//
|
||
// A number of special cases must be handled if a special message has
|
||
// just been sent. These special messages are synchronous negotiations
|
||
// or a messages which imply a disconnect. The special cases are:
|
||
//
|
||
// If a disconnect is expected because of a send-message request,
|
||
// then the only valid message-in is a MESSAGE REJECT; other messages
|
||
// are a protocol error and are rejected.
|
||
//
|
||
// If a synchronous negotiation response was just sent and the message
|
||
// in was not a MESSAGE REJECT, then the negotiation has been accepted.
|
||
//
|
||
// If a synchronous negotiation request was just sent, then valid responses
|
||
// are a MESSAGE REJECT or an extended synchronous message back.
|
||
//
|
||
|
||
if (DeviceExtension->AdapterFlags & (PD_SYNCHRONOUS_RESPONSE_SENT |
|
||
PD_DISCONNECT_EXPECTED | PD_SYNCHRONOUS_TRANSFER_SENT)) {
|
||
|
||
if (DeviceExtension->AdapterFlags & PD_DISCONNECT_EXPECTED &&
|
||
DeviceExtension->MessageBuffer[0] != SCSIMESS_MESSAGE_REJECT) {
|
||
|
||
//
|
||
// The target is not responding correctly to the message. Send a
|
||
// message reject of this message.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
|
||
return(TRUE);
|
||
} else {
|
||
srb = luExtension->ActiveLuRequest;
|
||
}
|
||
|
||
if (DeviceExtension->AdapterFlags & PD_SYNCHRONOUS_RESPONSE_SENT &&
|
||
DeviceExtension->MessageBuffer[0] != SCSIMESS_MESSAGE_REJECT) {
|
||
|
||
//
|
||
// The target did not reject our response so the synchronous
|
||
// transfer negotiation is done. Clear the adapter flags and
|
||
// set the logical unit flags indicating this. Continue processing
|
||
// the message which is unrelated to negotiation.
|
||
//
|
||
|
||
DeviceExtension->AdapterFlags &= ~PD_SYNCHRONOUS_RESPONSE_SENT;
|
||
luExtension->LuFlags |= PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
}
|
||
|
||
//
|
||
// Save the adapter flags for later use.
|
||
//
|
||
|
||
savedAdapterFlags = DeviceExtension->AdapterFlags;
|
||
|
||
if (DeviceExtension->AdapterFlags & PD_SYNCHRONOUS_TRANSFER_SENT ) {
|
||
|
||
//
|
||
// The target is sending a message after a synchronous transfer
|
||
// request was sent. Valid responses are a MESSAGE REJECT or an
|
||
// extended synchronous message; any other message negates the
|
||
// fact that a negotiation was started. However, since extended
|
||
// messages are multi-byte, it is difficult to determine what the
|
||
// incoming message is. So at this point, the fact that a
|
||
// sychronous transfer was sent will be saved and cleared from the
|
||
// AdapterFlags. If the message looks like a synchronous transfer
|
||
// request, then restore this fact back into the AdapterFlags. If
|
||
// the complete message is not the one expected, then opening
|
||
// negotiation will be forgotten. This is an error by the target,
|
||
// but minor so nothing will be done about it. Finally, to prevent
|
||
// this cycle from reoccurring on the next request indicate that
|
||
// the negotiation is done.
|
||
//
|
||
|
||
DeviceExtension->AdapterFlags &= ~PD_SYNCHRONOUS_TRANSFER_SENT;
|
||
luExtension->LuFlags |= PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
}
|
||
|
||
} else {
|
||
srb = luExtension->ActiveLuRequest;
|
||
}
|
||
|
||
switch (DeviceExtension->MessageBuffer[0]) {
|
||
case SCSIMESS_COMMAND_COMPLETE:
|
||
|
||
//
|
||
// For better or worse the command is complete. Process request which
|
||
// sets the SrbStatus and cleans up the device and logical unit states.
|
||
//
|
||
|
||
WdProcessRequestCompletion(DeviceExtension);
|
||
|
||
//
|
||
// Complete the request.
|
||
//
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
srb
|
||
);
|
||
|
||
//
|
||
// Everything is ok with the message so do not send one and set the
|
||
// state to DisconnectExpected.
|
||
//
|
||
|
||
DeviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED;
|
||
DeviceExtension->AdapterState = DisconnectExpected;
|
||
DeviceExtension->MessageCount = 0;
|
||
return(FALSE);
|
||
|
||
case SCSIMESS_DISCONNECT:
|
||
|
||
//
|
||
// The target wants to disconnect. Set the state to DisconnectExpected,
|
||
// and do not request a message-out.
|
||
//
|
||
|
||
DeviceExtension->AdapterState = DisconnectExpected;
|
||
DeviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED;
|
||
DeviceExtension->MessageCount = 0;
|
||
return(FALSE);
|
||
|
||
case SCSIMESS_EXTENDED_MESSAGE:
|
||
|
||
//
|
||
// The format of an extended message is:
|
||
// Extended Message Code
|
||
// Length of Message
|
||
// Extended Message Type
|
||
// .
|
||
// .
|
||
//
|
||
// Until the entire message has been read in, just keep getting bytes
|
||
// from the target, making sure that the message buffer is not
|
||
// overrun.
|
||
//
|
||
|
||
extendedMessage = (PSCSI_EXTENDED_MESSAGE)
|
||
DeviceExtension->MessageBuffer;
|
||
|
||
if (DeviceExtension->MessageCount < 2 ||
|
||
(DeviceExtension->MessageCount < (UCHAR) MESSAGE_BUFFER_SIZE &&
|
||
DeviceExtension->MessageCount < (UCHAR) (extendedMessage->MessageLength + 2))
|
||
) {
|
||
|
||
//
|
||
// Update the state and return; also restore the AdapterFlags.
|
||
//
|
||
|
||
DeviceExtension->AdapterFlags = savedAdapterFlags;
|
||
DeviceExtension->AdapterState = MessageAccepted;
|
||
return(FALSE);
|
||
|
||
}
|
||
|
||
//
|
||
// Make sure the length includes an extended op-code.
|
||
//
|
||
|
||
if (DeviceExtension->MessageCount < 3) {
|
||
|
||
//
|
||
// This is an illegal extended message. Send a MESSAGE_REJECT.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// Determine the extended message type.
|
||
//
|
||
|
||
switch (extendedMessage->MessageType) {
|
||
case SCSIMESS_MODIFY_DATA_POINTER:
|
||
|
||
//
|
||
// Verify the message length.
|
||
//
|
||
|
||
if (extendedMessage->MessageLength != SCSIMESS_MODIFY_DATA_LENGTH) {
|
||
|
||
//
|
||
// Reject the message.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// Calculate the modification to be added to the data pointer.
|
||
//
|
||
|
||
offset = 0;
|
||
for (i = 0; i < 4; i++) {
|
||
offset << 8;
|
||
offset += extendedMessage->ExtendedArguments.Modify.Modifier[i];
|
||
}
|
||
|
||
//
|
||
// Verify that the new data pointer is still within the range
|
||
// of the buffer.
|
||
//
|
||
|
||
if (DeviceExtension->ActiveDataLength - offset >
|
||
srb->DataTransferLength ||
|
||
((LONG) DeviceExtension->ActiveDataLength - offset) < 0 ) {
|
||
|
||
//
|
||
// The new pointer is not valid, so reject the message.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// Everything has checked out, so update the pointer.
|
||
//
|
||
|
||
DeviceExtension->ActiveDataPointer += offset;
|
||
DeviceExtension->ActiveDataLength -= offset;
|
||
|
||
//
|
||
// Everything is ok, so accept the message as is.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 0;
|
||
DeviceExtension->AdapterState = MessageAccepted;
|
||
return(FALSE);
|
||
|
||
case SCSIMESS_SYNCHRONOUS_DATA_REQ:
|
||
|
||
//
|
||
// A SYNCHRONOUS DATA TRANSFER REQUEST message was received.
|
||
// Make sure the length is correct.
|
||
//
|
||
|
||
if ( extendedMessage->MessageLength !=
|
||
SCSIMESS_SYNCH_DATA_LENGTH) {
|
||
|
||
//
|
||
// The length is invalid reject the message.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// If synchrouns negotiation has been disabled for this request,
|
||
// then reject any synchronous messages; however, when synchronous
|
||
// transfers are allowed then a new attempt can be made.
|
||
//
|
||
|
||
if (srb != NULL &&
|
||
!(savedAdapterFlags & PD_SYNCHRONOUS_TRANSFER_SENT) &&
|
||
srb->SrbFlags & SRB_FLAGS_DISABLE_SYNCH_TRANSFER) {
|
||
|
||
//
|
||
// Reject the synchronous transfer message since synchonrous
|
||
// transfers are not desired at this time.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
//
|
||
// Call WdDecodeSynchronousMessage to decode the message and
|
||
// formulate a response if necessary.
|
||
// WdDecodeSynchronousRequest will return FALSE if the
|
||
// message is not accepable and should be rejected.
|
||
//
|
||
|
||
if (!WdDecodeSynchronousRequest(
|
||
DeviceExtension,
|
||
luExtension,
|
||
(BOOLEAN) (!(savedAdapterFlags & PD_SYNCHRONOUS_TRANSFER_SENT))
|
||
)) {
|
||
|
||
//
|
||
// Indicate that a negotiation has been done in the logical
|
||
// unit and clear the negotiation flags.
|
||
//
|
||
|
||
luExtension->LuFlags |= PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
DeviceExtension->AdapterFlags &=
|
||
~(PD_SYNCHRONOUS_RESPONSE_SENT|
|
||
PD_SYNCHRONOUS_TRANSFER_SENT);
|
||
|
||
//
|
||
// The message was not acceptable so send a MESSAGE_REJECT.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// If a reponse was expected, then set the state for a message-out.
|
||
// Otherwise, WdDecodeSynchronousRequest has put a reponse
|
||
// in the message buffer to be returned to the target.
|
||
//
|
||
|
||
if (savedAdapterFlags & PD_SYNCHRONOUS_TRANSFER_SENT){
|
||
|
||
//
|
||
// We initiated the negotiation, so no response is necessary.
|
||
//
|
||
|
||
DeviceExtension->AdapterState = MessageAccepted;
|
||
DeviceExtension->AdapterFlags &= ~PD_SYNCHRONOUS_TRANSFER_SENT;
|
||
luExtension->LuFlags |= PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
DeviceExtension->MessageCount = 0;
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Set up the state to send the reponse. The message count is
|
||
// still correct.
|
||
//
|
||
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
DeviceExtension->AdapterFlags &= ~PD_SYNCHRONOUS_TRANSFER_SENT;
|
||
DeviceExtension->AdapterFlags |= PD_SYNCHRONOUS_RESPONSE_SENT;
|
||
return(TRUE);
|
||
|
||
case SCSIMESS_WIDE_DATA_REQUEST:
|
||
|
||
//
|
||
// A WIDE DATA TRANSFER REQUEST message was received.
|
||
// Make sure the length is correct.
|
||
//
|
||
|
||
if ( extendedMessage->MessageLength !=
|
||
SCSIMESS_WIDE_DATA_LENGTH) {
|
||
|
||
//
|
||
// The length is invalid reject the message.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// Since this SCSI protocol chip only supports 8 bits, return
|
||
// a width of 0 which indicates an 8-bit-wide transfers. The
|
||
// MessageCount is still correct for the message.
|
||
//
|
||
|
||
extendedMessage->ExtendedArguments.Wide.Width = 0;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
return(TRUE);
|
||
|
||
default:
|
||
|
||
//
|
||
// This is an unknown or illegal message, so send message REJECT.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
return(TRUE);
|
||
}
|
||
|
||
case SCSIMESS_INITIATE_RECOVERY:
|
||
|
||
//
|
||
// Save the fact that a INITIATE RECOVERY message was received.
|
||
//
|
||
|
||
luExtension->LuFlags |= PD_INITIATE_RECOVERY;
|
||
DeviceExtension->MessageCount = 0;
|
||
return(FALSE);
|
||
|
||
case SCSIMESS_LINK_CMD_COMP:
|
||
|
||
//
|
||
// A link command completed. Process the completion. Since the link
|
||
// FLAG was not set, do not call ScsiPortNotification. Get the next
|
||
// segment of the request and accept the message.
|
||
//
|
||
|
||
//
|
||
// Make sure that this is a linked command.
|
||
// Linked commands are not supported.
|
||
//
|
||
|
||
if (TRUE) {
|
||
|
||
//
|
||
// Something is messed up. Reject the message.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
return(TRUE);
|
||
}
|
||
|
||
WdProcessRequestCompletion(DeviceExtension);
|
||
|
||
luExtension->ActiveLuRequest = srb->NextSrb;
|
||
|
||
//
|
||
// Everything is ok with the message, so do not send one and set the
|
||
// state to MessageAccepted.
|
||
//
|
||
|
||
DeviceExtension->AdapterState = MessageAccepted;
|
||
DeviceExtension->MessageCount = 0;
|
||
return(FALSE);
|
||
|
||
case SCSIMESS_LINK_CMD_COMP_W_FLAG:
|
||
|
||
//
|
||
// A link command completed. Process the completion and get the next
|
||
// segment of the request. Since the link FLAG was set, call
|
||
// ScsiPortNotification to notify the class driver.
|
||
//
|
||
|
||
//
|
||
// Make sure that this is a linked command.
|
||
// Linked commands are not supported.
|
||
//
|
||
|
||
if (TRUE) {
|
||
|
||
//
|
||
// Something is messed up. Reject the message.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
return(TRUE);
|
||
}
|
||
|
||
WdProcessRequestCompletion(DeviceExtension);
|
||
|
||
luExtension->ActiveLuRequest = srb->NextSrb;
|
||
|
||
//
|
||
// Complete the request.
|
||
//
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
srb
|
||
);
|
||
|
||
//
|
||
// Everything is ok with the message, so do not send one and set the
|
||
// state to MessageAccepted.
|
||
//
|
||
|
||
DeviceExtension->AdapterState = MessageAccepted;
|
||
DeviceExtension->MessageCount = 0;
|
||
return(FALSE);
|
||
|
||
case SCSIMESS_MESSAGE_REJECT:
|
||
|
||
//
|
||
// The last message we sent was rejected. If this was a send
|
||
// message request, then set the proper status and complete the
|
||
// request. Set the state to message accepted.
|
||
//
|
||
|
||
if (DeviceExtension->AdapterFlags & PD_SEND_MESSAGE_REQUEST) {
|
||
|
||
//
|
||
// Complete the request with message rejected status.
|
||
//
|
||
|
||
WdCompleteSendMessage(
|
||
DeviceExtension,
|
||
SRB_STATUS_MESSAGE_REJECTED
|
||
);
|
||
}
|
||
|
||
//
|
||
// Check to see if a synchronous negotiation is in progress.
|
||
//
|
||
|
||
if (savedAdapterFlags & (PD_SYNCHRONOUS_RESPONSE_SENT|
|
||
PD_SYNCHRONOUS_TRANSFER_SENT)) {
|
||
|
||
//
|
||
// The negotiation failed so use asynchronous data transfers.
|
||
// Indicate that the negotiation has been attempted and set
|
||
// the transfer for asynchronous. Clear the negotiation flags.
|
||
//
|
||
|
||
luExtension->LuFlags |= PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
luExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
luExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
DeviceExtension->AdapterFlags &= ~(PD_SYNCHRONOUS_RESPONSE_SENT|
|
||
PD_SYNCHRONOUS_TRANSFER_SENT);
|
||
|
||
//
|
||
// Even though the negotiation appeared to go ok, there is no reason
|
||
// to try again, and some targets get messed up later, so do not try
|
||
// synchronous negotiation again.
|
||
//
|
||
|
||
/* TODO: Reconsider doing this. */
|
||
|
||
// luExtension->LuFlags |= PD_DO_NOT_NEGOTIATE;
|
||
|
||
}
|
||
|
||
DeviceExtension->AdapterState = MessageAccepted;
|
||
DeviceExtension->MessageCount = 0;
|
||
return(FALSE);
|
||
|
||
case SCSIMESS_RESTORE_POINTERS:
|
||
|
||
//
|
||
// Restore data pointer message. Just copy the saved data pointer
|
||
// and the length to the active data pointers.
|
||
//
|
||
|
||
DeviceExtension->ActiveDataPointer = luExtension->SavedDataPointer;
|
||
DeviceExtension->ActiveDataLength = luExtension->SavedDataLength;
|
||
DeviceExtension->AdapterState = MessageAccepted;
|
||
DeviceExtension->MessageCount = 0;
|
||
return(FALSE);
|
||
|
||
case SCSIMESS_SAVE_DATA_POINTER:
|
||
|
||
//
|
||
// SAVE DATA POINTER message request that the active data pointer and
|
||
// length be copied to the saved location.
|
||
//
|
||
|
||
luExtension->SavedDataPointer = DeviceExtension->ActiveDataPointer;
|
||
luExtension->SavedDataLength = DeviceExtension->ActiveDataLength;
|
||
DeviceExtension->AdapterState = MessageAccepted;
|
||
DeviceExtension->MessageCount = 0;
|
||
return(FALSE);
|
||
|
||
default:
|
||
|
||
//
|
||
// An unrecognized or unsupported message: send message reject.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
return(TRUE);
|
||
}
|
||
}
|
||
|
||
BOOLEAN
|
||
WdDecodeSynchronousRequest(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
OUT PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension,
|
||
IN BOOLEAN ResponseExpected
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function decodes the synchronous data transfer request message from
|
||
the target. It will update the synchronous message in the buffer and the
|
||
synchronous transfer parameters in the logical unit extension. These
|
||
parameters are specific for the WD 53C9X protocol chip. The updated
|
||
message in the device extension message buffer might be returned to the
|
||
target.
|
||
|
||
This function should be called before the final byte of the message is
|
||
accepted from the SCSI bus.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies a pointer to the adapter-specific device
|
||
extension.
|
||
|
||
LuExtension - Supplies a pointer to the logical unit's device extension.
|
||
The synchronous transfer fields are updated in this structure to
|
||
reflect the new parameter in the message.
|
||
|
||
ResponseExpected - When set, indicates that the target initiated the
|
||
negotiation and that it expects a response.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Returned if the request is acceptable.
|
||
|
||
FALSE - Returned if the request should be rejected and asynchronous
|
||
transfer should be used.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSCSI_EXTENDED_MESSAGE extendedMessage;
|
||
SCSI_SYNCHRONOUS scsiSynchronous;
|
||
LONG period;
|
||
LONG i;
|
||
|
||
|
||
extendedMessage = (PSCSI_EXTENDED_MESSAGE) DeviceExtension->MessageBuffer;
|
||
|
||
//
|
||
// Determine the transfer offset. It is the minimum of the SCSI protocol
|
||
// chip's maximum offset and the requested offset.
|
||
//
|
||
|
||
if (extendedMessage->ExtendedArguments.Synchronous.ReqAckOffset >
|
||
SYNCHRONOUS_OFFSET) {
|
||
|
||
if (!ResponseExpected) {
|
||
|
||
//
|
||
// The negotiation failed for some reason, fall back to
|
||
// asynchronous data transfer.
|
||
//
|
||
|
||
LuExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
LuExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
return(FALSE);
|
||
}
|
||
|
||
extendedMessage->ExtendedArguments.Synchronous.ReqAckOffset = SYNCHRONOUS_OFFSET;
|
||
LuExtension->SynchronousOffset = SYNCHRONOUS_OFFSET;
|
||
|
||
} else {
|
||
|
||
LuExtension->SynchronousOffset =
|
||
extendedMessage->ExtendedArguments.Synchronous.ReqAckOffset;
|
||
|
||
}
|
||
|
||
//
|
||
// If the offset requests asynchronous transfers then set the default
|
||
// period and return.
|
||
//
|
||
|
||
if (extendedMessage->ExtendedArguments.Synchronous.ReqAckOffset ==
|
||
ASYNCHRONOUS_OFFSET) {
|
||
LuExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// Check to see if the period is less than the SCSI protocol chip can
|
||
// use. If it is then update the message with our minimum and return.
|
||
//
|
||
|
||
if (extendedMessage->ExtendedArguments.Synchronous.TransferPeriod < SYNCHRONOUS_PERIOD) {
|
||
|
||
if (!ResponseExpected) {
|
||
|
||
//
|
||
// The negotiation failed for some reason, fall back to
|
||
// asynchronous data transfer.
|
||
//
|
||
|
||
LuExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
LuExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
return(FALSE);
|
||
}
|
||
|
||
extendedMessage->ExtendedArguments.Synchronous.TransferPeriod = SYNCHRONOUS_PERIOD;
|
||
}
|
||
|
||
//
|
||
// The synchronous period uses the following formula to calculate the
|
||
// transfer period returned in the message:
|
||
//
|
||
// (SynchronousPeriod - 2) * 1000 * ClockDivide
|
||
// ---------------------------------------------
|
||
// Clock speed in Mhz * 2 * 4
|
||
//
|
||
// The 4 is the divisor is because the message byte is in units of 4 ns.
|
||
// For the WD53c93 the Synchronous period will be calculated by:
|
||
//
|
||
// (MessagePeriod - SYNCHRONOUS_PERIOD)
|
||
// SynchrounousPeriod = ------------------------------------
|
||
// SYNCHRONOUS_PERIOD_STEP
|
||
//
|
||
// Note that this must be rounded up. Since the range of SynchronousPeriod
|
||
// is only 3-6 a simple loop will handle this calculation.
|
||
//
|
||
|
||
period = extendedMessage->ExtendedArguments.Synchronous.TransferPeriod -
|
||
SYNCHRONOUS_PERIOD;
|
||
|
||
for (i = 3; i < 7; i++) {
|
||
if (period <= 0) {
|
||
break;
|
||
}
|
||
period -= SYNCHRONOUS_PERIOD_STEP;
|
||
}
|
||
|
||
if (i >= 7) {
|
||
|
||
//
|
||
// The requested transfer period is too long for the SCSI protocol
|
||
// chip. Fall back to synchronous and reject the request.
|
||
//
|
||
|
||
LuExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
LuExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
return(FALSE);
|
||
} else {
|
||
LuExtension->SynchronousPeriod = (UCHAR)i;
|
||
}
|
||
|
||
//
|
||
// Set the synchronous data transfer parameter registers
|
||
// to the new values. These must be set before a data transfer
|
||
// is started. If a response message is received then the parameters
|
||
// must be reset.
|
||
//
|
||
|
||
/* Powerfail */
|
||
|
||
*((PCHAR) &scsiSynchronous) = 0;
|
||
scsiSynchronous.SynchronousPeriod = LuExtension->SynchronousPeriod;
|
||
scsiSynchronous.SynchronousOffset = LuExtension->SynchronousOffset;
|
||
|
||
SCSI_WRITE(
|
||
DeviceExtension->Adapter,
|
||
Synchronous,
|
||
*((PUCHAR) &scsiSynchronous)
|
||
);
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
VOID
|
||
WdDumpState(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function prints the interesting state information about the requested
|
||
SCSI bus adapter.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies a pointer to device extension for the SCSI
|
||
bus adapter that should be displayed.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
WdPrint((0, "WdDumpState: Specific device extension: 0x%.8X; Active Logical Unit: 0x%.8X;\n",
|
||
DeviceExtension,
|
||
DeviceExtension->ActiveLogicalUnit
|
||
));
|
||
WdPrint((0, "WdDumpState: Adapter Status: 0x%.2X; Adapter Interrupt: 0x%.2X; Command Phase: 0x%.2X;\n",
|
||
*((PUCHAR) &DeviceExtension->AdapterStatus),
|
||
*((PUCHAR) &DeviceExtension->AdapterInterrupt),
|
||
DeviceExtension->CommandPhase
|
||
));
|
||
WdPrint((0, "WdDumpState: Adapter flags: 0x%.4X; Adapter state: %d;\n",
|
||
DeviceExtension->AdapterFlags,
|
||
DeviceExtension->AdapterState
|
||
));
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
WdInitializeAdapter(
|
||
IN PVOID ServiceContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function initializes the WD SCSI adapter chip. This function must
|
||
be called before any other operations are performed on the chip. It should
|
||
also be called after a power failure. This function does not cause any
|
||
interrupts; however, after it completes interrupts can occur.
|
||
|
||
Arguments:
|
||
|
||
ServiceContext - Pointer to the specific device extension for this SCSI
|
||
bus.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Returns TRUE indicating that the initialization of the chip is
|
||
complete.
|
||
|
||
FALSE - Returns FALSE if the initialization failed.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext;
|
||
OWN_ID ownId;
|
||
SCSI_CONTROL control;
|
||
SCSI_STATUS interruptStatus;
|
||
AUXILIARY_STATUS auxiliaryStatus;
|
||
SOURCE_ID sourceId;
|
||
ULONG i;
|
||
|
||
//
|
||
// Initialize the card.
|
||
//
|
||
|
||
CARD_INITIALIZE(deviceExtension);
|
||
|
||
//
|
||
// If the SCSI protocol chip is interrupting, then clear the interrupt so
|
||
// that the reset command can be written to the chip.
|
||
//
|
||
|
||
*((PUCHAR) &auxiliaryStatus) = SCSI_READ_AUX(
|
||
deviceExtension->Adapter,
|
||
AuxiliaryStatus
|
||
);
|
||
|
||
if (auxiliaryStatus.Interrupt) {
|
||
|
||
//
|
||
// Read the status register to clear the interrupt.
|
||
//
|
||
|
||
*((PUCHAR) &interruptStatus) = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
Status
|
||
);
|
||
|
||
//
|
||
// Stall the required time to allow the interrupt to clear.
|
||
//
|
||
|
||
ScsiPortStallExecution(INTERRUPT_CLEAR_TIME);
|
||
}
|
||
|
||
//
|
||
// The OwnId register must be set when the SCSI protocol chip is reset.
|
||
// Initialize the ownId with the adapter's host ID, advanced features,
|
||
// and the correct clock frequency select. Note the CdbSize register is
|
||
// used as the OwnId register when a reset command is issued.
|
||
//
|
||
|
||
*((PUCHAR) &ownId) = 0;
|
||
ownId.InitiatorId = deviceExtension->InitiatorBusId;
|
||
ownId.AdvancedFeatures = 1;
|
||
ownId.FrequencySelect = CLOCK_CONVERSION_FACTOR;
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, CdbSize, *((PUCHAR) &ownId) );
|
||
|
||
//
|
||
// Issue a reset-chip command.
|
||
//
|
||
|
||
WdIssueCommand(deviceExtension, RESET_SCSI_CHIP, -1, 0);
|
||
|
||
//
|
||
// Wait for the reset to complete. A reset complete is indicated by an
|
||
// interrupt with none of the interrupt bits set. The PhaseState in the
|
||
// interruptStatus indicates whether the this chip supports advanced mode
|
||
// or not.
|
||
//
|
||
|
||
i = 0;
|
||
*((PUCHAR) &auxiliaryStatus) = SCSI_READ_AUX(
|
||
deviceExtension->Adapter,
|
||
AuxiliaryStatus
|
||
);
|
||
|
||
while (!auxiliaryStatus.Interrupt && i < INTERRUPT_STALL_TIME) {
|
||
|
||
ScsiPortStallExecution(1);
|
||
i++;
|
||
|
||
*((PUCHAR) &auxiliaryStatus) = SCSI_READ_AUX(
|
||
deviceExtension->Adapter,
|
||
AuxiliaryStatus
|
||
);
|
||
|
||
}
|
||
|
||
#if DBG
|
||
if (WdDebug) {
|
||
WdPrint((0, "WdInitializeAdapter: Interrupt stall time for reset = %d\n", i));
|
||
}
|
||
#endif
|
||
|
||
if (!auxiliaryStatus.Interrupt) {
|
||
|
||
//
|
||
// The SCSI protocol chip did not reset properly. Notify the OS port
|
||
// driver of the error.
|
||
//
|
||
|
||
WdPrint((0, "WdInitializeAdapter: SCSI chip reset failed. Aux Status: 0x%.2X.\n",
|
||
auxiliaryStatus
|
||
));
|
||
|
||
return(FALSE);
|
||
}
|
||
|
||
|
||
//
|
||
// Dismiss the reset interrupt, and
|
||
// verify that the SCSI protocol chip reset correctly and that the
|
||
// advanced features were enabled.
|
||
//
|
||
|
||
*((PUCHAR) &interruptStatus) = SCSI_READ(deviceExtension->Adapter, Status);
|
||
|
||
if (interruptStatus.PhaseState != RESET_STATUS &&
|
||
interruptStatus.PhaseState != RESET_WITH_ADVANCED) {
|
||
|
||
WdPrint((0, "WdInitializeAdapter: SCSI chip reset failed. Status: 0x%.2X.\n",
|
||
*((PUCHAR) &interruptStatus)
|
||
));
|
||
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Stall the required time to allow the interrupt to clear.
|
||
//
|
||
|
||
ScsiPortStallExecution(INTERRUPT_CLEAR_TIME);
|
||
|
||
//
|
||
// Initialize the control register for halt on parity error,
|
||
// intermediate disconnect interrupt, ending disconnect interrupt,
|
||
// and polled I/O mode.
|
||
//
|
||
|
||
*((PUCHAR) &control) = 0;
|
||
control.HaltOnParity = 1;
|
||
control.IntermediateDisconnectInt = 1;
|
||
control.EndingDisconnectInt = 1;
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Control, *((PUCHAR) &control));
|
||
|
||
//
|
||
// Set the SelectTimeOut Register to 250ms. This value does not need to
|
||
// be reinitialized for each selection.
|
||
//
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Timeout, SELECT_TIMEOUT_VALUE);
|
||
|
||
//
|
||
// Initialize the source register, in particular, enable reselection.
|
||
//
|
||
|
||
*((PUCHAR) &sourceId) = 0;
|
||
sourceId.EnableReselection = 1;
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, SourceId, *((PUCHAR) &sourceId));
|
||
|
||
return( TRUE );
|
||
}
|
||
|
||
BOOLEAN
|
||
WdInterruptServiceRoutine(
|
||
PVOID ServiceContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routine is the interrupt service routine for the WD 33C94 SCSI
|
||
protocol chip. It is the main SCSI protocol engine of the driver and
|
||
is driven by service requests from targets on the SCSI bus. This routine
|
||
also detects errors and performs error recovery. Generally, this routine
|
||
handles one interrupt per invokation.
|
||
|
||
The general flow of this routine is as follows:
|
||
|
||
Check for an interrupt.
|
||
|
||
Determine if there are any pending errors.
|
||
|
||
Determine what interrupt occurred.
|
||
|
||
Update the adapter state based on what has occurred.
|
||
|
||
Determine what the target wants to do next and program the chip
|
||
appropriately.
|
||
|
||
Check for the next interrupt.
|
||
|
||
Arguments:
|
||
|
||
ServiceContext - Supplies a pointer to the device extension for the
|
||
interrupting adapter.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Indicates that an interrupt was found.
|
||
|
||
FALSE - Indicates the device was not interrupting.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext;
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension;
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
BOOLEAN setAttention;
|
||
BOOLEAN waitForInterrupt;
|
||
SCSI_STATUS interruptStatus;
|
||
SOURCE_ID sourceId;
|
||
TARGET_LUN targetLun;
|
||
ULONG waitCount;
|
||
|
||
/* POWERFAIL */
|
||
|
||
//
|
||
// Get the current chip state which includes the auxiliary status
|
||
// register, the command phase register and the SCSI status register.
|
||
// These registers are frozen until the interrupt register is read.
|
||
//
|
||
|
||
*((PUCHAR) &deviceExtension->AdapterStatus) = SCSI_READ_AUX(
|
||
deviceExtension->Adapter,
|
||
AuxiliaryStatus
|
||
);
|
||
|
||
//
|
||
// Make sure there is really an interrupt before reading the other
|
||
// registers, particularly, the interrupt register.
|
||
//
|
||
|
||
if (!deviceExtension->AdapterStatus.Interrupt) {
|
||
return(FALSE);
|
||
}
|
||
|
||
NextInterrupt:
|
||
|
||
*((PUCHAR) &deviceExtension->CommandPhase) = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
CommandPhase
|
||
);
|
||
|
||
//
|
||
// This read will dismiss the interrupt.
|
||
//
|
||
|
||
*((PUCHAR) &interruptStatus) = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
Status
|
||
);
|
||
|
||
deviceExtension->AdapterInterrupt = interruptStatus;
|
||
|
||
//
|
||
// Intialize the logical unit extension pointer.
|
||
// Note that this may be NULL.
|
||
//
|
||
|
||
luExtension = deviceExtension->ActiveLogicalUnit;
|
||
|
||
if (luExtension != NULL) {
|
||
srb = luExtension->ActiveLuRequest;
|
||
} else {
|
||
srb = NULL;
|
||
}
|
||
|
||
//
|
||
// If data transfer is active,
|
||
// then update the active pointers.
|
||
// If a DMA data transfer is complete, then flush the DMA buffer.
|
||
//
|
||
|
||
if (deviceExtension->AdapterState == DataTransfer) {
|
||
ULONG transferCount;
|
||
|
||
//
|
||
// Get the number of bytes that didn't get transferred, if any.
|
||
//
|
||
|
||
SCSI_READ_TRANSFER_COUNT(deviceExtension->Adapter, transferCount);
|
||
|
||
//
|
||
// transferCount now contains the number of bytes that did not
|
||
// get transferred. Change it to the number of bytes that did get
|
||
// transferred.
|
||
//
|
||
|
||
transferCount = deviceExtension->ActiveDataLength - transferCount;
|
||
|
||
//
|
||
// Now figure out if anything remains to be transferred.
|
||
//
|
||
|
||
luExtension->MaximumTransferLength += transferCount;
|
||
deviceExtension->ActiveDataPointer += transferCount;
|
||
deviceExtension->ActiveDataLength = srb->DataTransferLength -
|
||
luExtension->MaximumTransferLength;
|
||
luExtension->SavedDataPointer = deviceExtension->ActiveDataPointer;
|
||
luExtension->SavedDataLength = deviceExtension->ActiveDataLength;
|
||
|
||
if (deviceExtension->AdapterFlags & PD_DMA_ACTIVE) {
|
||
|
||
//
|
||
// Flush the DMA buffer to ensure all the bytes are transferred.
|
||
//
|
||
|
||
ScsiPortFlushDma(deviceExtension);
|
||
|
||
//
|
||
// Shutdown DMA mode on the card.
|
||
//
|
||
|
||
CARD_DMA_TERMINATE( deviceExtension );
|
||
|
||
//
|
||
// Clear the DMA active flag.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags &= ~(PD_DMA_ACTIVE | PD_PENDING_DATA_TRANSFER);
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Stall after the interrupt has been dismissed.
|
||
// This must be done before any commands issued.
|
||
//
|
||
|
||
ScsiPortStallExecution(INTERRUPT_CLEAR_TIME);
|
||
|
||
#if DBG
|
||
if (WdDebug) {
|
||
WdPrint((0, "WdInterrupt: Adapter Status: 0x%.2X; Adapter Interrupt: %2x; Command Phase: 0x%.2X;\n",
|
||
*((PUCHAR) &deviceExtension->AdapterStatus),
|
||
*((PUCHAR) &interruptStatus),
|
||
*((PUCHAR) &deviceExtension->CommandPhase)
|
||
));
|
||
}
|
||
#endif
|
||
|
||
deviceExtension->InterruptCount++;
|
||
waitForInterrupt = FALSE;
|
||
|
||
//
|
||
// Check for major errors that should never occur.
|
||
//
|
||
|
||
if (deviceExtension->InterruptCount > MAX_INTERRUPT_COUNT) {
|
||
|
||
//
|
||
// Things are really messed up. Reset the bus, the chip and
|
||
// bail out.
|
||
//
|
||
|
||
WdPrint((0,
|
||
"WdInterruptServiceRoutine: Unexpected error. Interrupt Count=%d\n",
|
||
deviceExtension->InterruptCount
|
||
));
|
||
|
||
WdDumpState(deviceExtension);
|
||
|
||
WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 1);
|
||
|
||
WdResetScsiBusInternal(deviceExtension, 0);
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// Check to see if a subsequent request can be made pending.
|
||
// Logically this happens when a select occurs that causes an
|
||
// already "pending" request to become an "active" request. A
|
||
// select of this type has occured if and only if the adapter
|
||
// state is either Select or SelectAndTransfer (attempting to
|
||
// select) and the reason for the SCSI protocol chip interrupt
|
||
// is something other than one of the following interrupt and
|
||
// PhaseState values:
|
||
//
|
||
// AbortedPaused PAUSED_DURING_RESELECT
|
||
// AbortedPaused PAUSED_RESELECT_OR_SELECT
|
||
// ServiceRequired SERVICE_RESELECTED
|
||
// ServiceRequired SERVICE_RESELECTED_IDENTIFY
|
||
// Terminated TERMINATE_SELECT_TIMEOUT
|
||
//
|
||
|
||
switch (deviceExtension->AdapterState) {
|
||
case Select:
|
||
case SelectAndTransfer:
|
||
if (!interruptStatus.PhaseStateValid) {
|
||
if (interruptStatus.AbortedPaused) {
|
||
if ((interruptStatus.PhaseState == PAUSED_DURING_RESELECT) ||
|
||
(interruptStatus.PhaseState == PAUSED_RESELECT_OR_SELECT)) {
|
||
break;
|
||
}
|
||
} else if (interruptStatus.ServiceRequired) {
|
||
if ((interruptStatus.PhaseState == SERVICE_RESELECTED) ||
|
||
(interruptStatus.PhaseState == SERVICE_RESELECTED_IDENTIFY)) {
|
||
break;
|
||
}
|
||
} else if (interruptStatus.Terminated) {
|
||
if (interruptStatus.PhaseState == TERMINATE_SELECT_TIMEOUT) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// A Select has completed or a SelectAndTransfer has begun.
|
||
// Set the adapter state appropriately.
|
||
//
|
||
|
||
if (deviceExtension->AdapterState == Select) {
|
||
deviceExtension->AdapterState = MessageOut;
|
||
} else {
|
||
deviceExtension->AdapterState = CommandOut;
|
||
}
|
||
|
||
//
|
||
// The "pending" srb has now become the active srb.
|
||
// Clear the deviceExtension information associated with
|
||
// the no longer pending condition and ask for another srb
|
||
// (if any) to be made the pending srb.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags &= ~PD_PENDING_START_IO;
|
||
deviceExtension->NextSrbRequest = NULL;
|
||
|
||
ScsiPortNotification(
|
||
NextRequest,
|
||
deviceExtension,
|
||
NULL
|
||
);
|
||
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Check for a successful completion interrupt.
|
||
//
|
||
|
||
if (interruptStatus.CommandComplete &&
|
||
!interruptStatus.PhaseStateValid) {
|
||
|
||
//
|
||
// Determine what completed based on the state in the interrupt status.
|
||
//
|
||
|
||
switch (interruptStatus.PhaseState) {
|
||
case COMPLETE_SELECT:
|
||
|
||
//
|
||
// This case was handled above.
|
||
//
|
||
|
||
waitForInterrupt = 1;
|
||
break;
|
||
|
||
case COMPLETE_SELECT_AND_TRANS:
|
||
|
||
//
|
||
// A select-and-transfer command completed. This implies that
|
||
// everything went normally. In particular, all of the data was
|
||
// transferred, a SCSI bus status byte was received, the
|
||
// command complete message was accepted and the target has
|
||
// disconnected. Simulate the state change which would normally
|
||
// occur if these events were individual interrupts.
|
||
//
|
||
|
||
srb = luExtension->ActiveLuRequest;
|
||
|
||
//
|
||
// Get the status value and indicate it has been received. The
|
||
// SCSI status value is saved in the TargetLun register.
|
||
//
|
||
|
||
srb->ScsiStatus = SCSI_READ(deviceExtension->Adapter, TargetLun);
|
||
luExtension->LuFlags |= PD_STATUS_VALID;
|
||
|
||
//
|
||
// Simulate the COMMAND COMPLETE message.
|
||
//
|
||
|
||
deviceExtension->MessageCount = 0;
|
||
deviceExtension->MessageBuffer[0] = SCSIMESS_COMMAND_COMPLETE;
|
||
WdMessageDecode(deviceExtension);
|
||
|
||
//
|
||
// Indicate that the bus is free.
|
||
// Clean up the adapter state to indicate the bus is now free,
|
||
// stop the PhaseTimer, and start any pending request.
|
||
//
|
||
|
||
deviceExtension->AdapterState = BusFree;
|
||
deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK;
|
||
deviceExtension->ActiveLogicalUnit = NULL;
|
||
|
||
if (deviceExtension->AdapterFlags & PD_PENDING_START_IO) {
|
||
|
||
//
|
||
// Call WdStartIo to start the pending request.
|
||
// Note that WdStartIo is idempotent when called with
|
||
// the same arguments.
|
||
//
|
||
|
||
WdStartIo(
|
||
deviceExtension,
|
||
deviceExtension->NextSrbRequest
|
||
);
|
||
|
||
}
|
||
|
||
waitForInterrupt = TRUE;
|
||
break;
|
||
|
||
default:
|
||
|
||
//
|
||
// Things are really messed up. Reset the bus, the chip and
|
||
// bail out.
|
||
//
|
||
|
||
WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 3);
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Unexpected command complete state.\n"));
|
||
WdDumpState(deviceExtension);
|
||
|
||
|
||
WdResetScsiBusInternal(deviceExtension, 0);
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
} else if (interruptStatus.CommandComplete &&
|
||
interruptStatus.PhaseStateValid) {
|
||
|
||
//
|
||
// A transfer information command completed and the target is
|
||
// requesting another bus phase. Process the fact that the transfer
|
||
// completed based on the current state. The new request will be
|
||
// serviced later. Note that message-in transfers do not cause this
|
||
// type of interrupt, because the last byte of the transfer is not
|
||
// acknowleged by the SCSI protocol chip.
|
||
//
|
||
|
||
srb = luExtension->ActiveLuRequest;
|
||
|
||
//
|
||
// The following states are processed:
|
||
//
|
||
// CommandOut
|
||
// DataTransfer
|
||
// DataTransferComplete
|
||
// MessageOut
|
||
// StatusIn
|
||
//
|
||
//
|
||
|
||
switch (deviceExtension->AdapterState) {
|
||
case CommandOut:
|
||
case DataTransferComplete:
|
||
break;
|
||
|
||
case DataTransfer:
|
||
|
||
//
|
||
// A data transfer completed or is being suspended.
|
||
// If no longer in data transfer state,
|
||
// then change the state to say so.
|
||
//
|
||
|
||
switch (interruptStatus.PhaseState) {
|
||
case DATA_OUT:
|
||
case DATA_IN:
|
||
//
|
||
// The target device is still in data phase.
|
||
// This may be because an even length DMA transfer
|
||
// has just completed and an odd length byte still
|
||
// remains to be transferred. Check to see if this
|
||
// is the case: if so, stay in DataTransfer state
|
||
// to transfer the odd byte; else, we have a genuine
|
||
// data under run (our data transfer length is less
|
||
// than the target's expected data transfer length).
|
||
//
|
||
|
||
if (deviceExtension->ActiveDataLength == 1) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// else: fall through to next/default case
|
||
//
|
||
|
||
default:
|
||
deviceExtension->AdapterState = DataTransferComplete;
|
||
break;
|
||
}
|
||
|
||
break;
|
||
|
||
case MessageOut:
|
||
|
||
//
|
||
// The SCSI protocol chip indicates that the message has been sent;
|
||
// however, the target may need to reread the message or there
|
||
// may be more messages to send. This condition is indicated by a
|
||
// message-out bus phase; otherwise, the message has been accepted
|
||
// by the target. If message has been accepted then check to see
|
||
// if any special processing is necessary. Note that the driver
|
||
// state is set to MessageOut after the PD_DISCONNECT_EXPECTED is
|
||
// set, or after a selection. So it is only necessary to check for
|
||
// PD_DISCONNECT_EXPECTED when the driver state is currently in
|
||
// MessageOut.
|
||
//
|
||
|
||
if (deviceExtension->AdapterFlags & PD_DISCONNECT_EXPECTED &&
|
||
interruptStatus.PhaseState != MESSAGE_OUT &&
|
||
interruptStatus.PhaseState != MESSAGE_IN) {
|
||
|
||
//
|
||
// If a disconnect was expected and a bus service interrupt
|
||
// was detected, then a SCSI protocol error has been
|
||
// detected and the SCSI bus should be reset to clear the
|
||
// condition.
|
||
//
|
||
|
||
WdLogError(deviceExtension, SP_PROTOCOL_ERROR, 4);
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Bus request while disconnect expected after message-out.\n"));
|
||
WdDumpState(deviceExtension);
|
||
|
||
WdResetScsiBusInternal(deviceExtension, 0);
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
if (deviceExtension->AdapterFlags & PD_SYNCHRONOUS_TRANSFER_SENT &&
|
||
interruptStatus.PhaseState != MESSAGE_OUT &&
|
||
interruptStatus.PhaseState != MESSAGE_IN) {
|
||
|
||
//
|
||
// The controller ignored the synchronous transfer message.
|
||
// Treat it as a rejection and clear the necessary state.
|
||
//
|
||
|
||
deviceExtension->ActiveLogicalUnit->LuFlags |=
|
||
PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
deviceExtension->AdapterFlags &=
|
||
~(PD_SYNCHRONOUS_RESPONSE_SENT|
|
||
PD_SYNCHRONOUS_TRANSFER_SENT);
|
||
}
|
||
|
||
if (deviceExtension->AdapterFlags & PD_SYNCHRONOUS_RESPONSE_SENT &&
|
||
interruptStatus.PhaseState != MESSAGE_OUT &&
|
||
interruptStatus.PhaseState != MESSAGE_IN) {
|
||
|
||
//
|
||
// The target controller accepted the negotiation. Set
|
||
// the done flag in the logical unit and clear the
|
||
// negotiation flags in the adapter.
|
||
//
|
||
|
||
deviceExtension->ActiveLogicalUnit->LuFlags |=
|
||
PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
deviceExtension->AdapterFlags &=
|
||
~(PD_SYNCHRONOUS_RESPONSE_SENT|
|
||
PD_SYNCHRONOUS_TRANSFER_SENT);
|
||
|
||
}
|
||
|
||
//
|
||
// Finally, update the message sent count to indicate that all of
|
||
// the current message bytes have been sent.
|
||
//
|
||
|
||
deviceExtension->MessageSent = deviceExtension->MessageCount;
|
||
break;
|
||
|
||
case StatusIn:
|
||
|
||
//
|
||
// Get the status value and indicate it has been received. The
|
||
// SCSI status value is data register.
|
||
//
|
||
|
||
#if DBG
|
||
if (!deviceExtension->AdapterStatus.DataBufferReady) {
|
||
WdPrint((0, "WdInterruptServiceRoutine: Status in complete and data buffer not ready\n"));
|
||
WdDumpState(deviceExtension);
|
||
}
|
||
#endif
|
||
|
||
srb->ScsiStatus = SCSI_READ(deviceExtension->Adapter, Data);
|
||
luExtension->LuFlags |= PD_STATUS_VALID;
|
||
break;
|
||
|
||
default:
|
||
|
||
//
|
||
// A function complete should not occur while in any other states.
|
||
//
|
||
|
||
WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 5);
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Unexpected function complete interrupt.\n"));
|
||
WdDumpState(deviceExtension);
|
||
|
||
WdResetScsiBusInternal(deviceExtension, 0);
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Check for an Aborted or Paused interrupt.
|
||
//
|
||
|
||
} else if (interruptStatus.AbortedPaused &&
|
||
!interruptStatus.PhaseStateValid) {
|
||
|
||
//
|
||
// Determine the cause of this interrupt based on the state value.
|
||
//
|
||
|
||
switch (interruptStatus.PhaseState) {
|
||
case PAUSED_MESSAGE_IN_DONE:
|
||
|
||
//
|
||
// A message byte has been received.
|
||
// Call message decode to determine what to do. The message
|
||
// byte will either be accepted, or cause a message to be sent.
|
||
// A message-out is indicated to the target by setting the ATN
|
||
// line before sending the SCSI protocol chip the MESSAGE_ACCEPTED
|
||
// command.
|
||
//
|
||
// First determine if this is an IDENTIFY message for a reselect.
|
||
// If it is, then clear the disconnect expected flag and process
|
||
// the reselection. This block of code will respond the to message
|
||
// as necessary rather, than calling WdMessageDecode.
|
||
//
|
||
|
||
if (deviceExtension->MessageBuffer[0] & SCSIMESS_IDENTIFY &&
|
||
deviceExtension->AdapterFlags & PD_DISCONNECT_EXPECTED) {
|
||
|
||
//
|
||
// Process this message as a reselect.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags &= ~PD_DISCONNECT_EXPECTED;
|
||
*((PUCHAR) &targetLun) = deviceExtension->MessageBuffer[0];
|
||
|
||
//
|
||
// Read in the target ID from the source register.
|
||
//
|
||
|
||
*((PUCHAR) &sourceId) = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
SourceId
|
||
);
|
||
|
||
#if DBG
|
||
if (!sourceId.TargetIdValid || !targetLun.TargetLunValid) {
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Reselection data not valid.\n"));
|
||
WdPrint((0,
|
||
"WdInterruptServiceRoutine: Source ID: 0x%.2X; Target LUN: 0x%.2X;\n",
|
||
sourceId,
|
||
targetLun
|
||
));
|
||
}
|
||
|
||
#endif
|
||
|
||
WdProcessReselection(
|
||
deviceExtension,
|
||
(UCHAR)sourceId.TargetId,
|
||
(UCHAR)targetLun.LogicalUnitNumber
|
||
);
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
if (WdMessageDecode( deviceExtension )) {
|
||
|
||
//
|
||
// WdMessageDecode returns TRUE if there is a message to be
|
||
// sent out. This message will normally be a MESSAGE REJECT
|
||
// or a SYNCHRONOUS DATA TRANSFER REQUEST. In any case, the
|
||
// message has been set up by WdMessageDecode. All that needs
|
||
// to be done here is to set the ATN signal and set
|
||
// PD_MESSAGE_OUT_VALID in the adapter flags.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID;
|
||
setAttention = TRUE;
|
||
|
||
} else {
|
||
|
||
setAttention = FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// In either case, tell the SCSI protocol chip to acknowlege or
|
||
// accept the message. The synchronous data transfer parameters
|
||
// do not need to be set.
|
||
//
|
||
|
||
WdAcceptMessage( deviceExtension, setAttention, FALSE);
|
||
waitForInterrupt = TRUE;
|
||
break;
|
||
|
||
case PAUSED_SAVE_POINTER_MESSAGE:
|
||
|
||
//
|
||
// A SAVE DATA POINTERS message was received. Perform the
|
||
// requested operation. Since this is a select-and-transfer
|
||
// operation it can be restarted from here.
|
||
// The data pointers are saved by copying the active pointers to
|
||
// their saved location in the logical unit extension.
|
||
//
|
||
|
||
luExtension->SavedDataPointer = deviceExtension->ActiveDataPointer;
|
||
luExtension->SavedDataLength = deviceExtension->ActiveDataLength;
|
||
|
||
//
|
||
// Restart the select-and-transfer command where it left off. All
|
||
// of the registers are in the correct state, but
|
||
// clear the transfer count so no DMA attempts occur. Finally,
|
||
// return.
|
||
//
|
||
|
||
WdIssueCommand(
|
||
deviceExtension, // Device Extension.
|
||
SELECT_ATN_AND_TRANSFER, // Command to issue.
|
||
0, // New transfer count.
|
||
deviceExtension->CommandPhase // New CommandPhase.
|
||
);
|
||
|
||
return(TRUE);
|
||
|
||
case PAUSED_RESELECT_OR_SELECT:
|
||
case PAUSED_DURING_RESELECT:
|
||
|
||
//
|
||
// An abort during a selection or relection has occurred. The bus
|
||
// is free. Clean up the adapter state to indicate the bus
|
||
// is now free, and start any pending request.
|
||
//
|
||
|
||
deviceExtension->AdapterState = BusFree;
|
||
deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK;
|
||
deviceExtension->ActiveLogicalUnit = NULL;
|
||
|
||
if (deviceExtension->AdapterFlags & PD_PENDING_START_IO) {
|
||
|
||
//
|
||
// Call WdStartIo to start the pending request.
|
||
// Note that WdStartIo is idempotent when called with
|
||
// the same arguments.
|
||
//
|
||
|
||
WdStartIo(
|
||
deviceExtension,
|
||
deviceExtension->NextSrbRequest
|
||
);
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
case PAUSED_NEW_TARGET_RESELECT:
|
||
|
||
//
|
||
// A new or different target has reselected the SCSI protocol chip.
|
||
// First processs the implied disconnect of the previous target by
|
||
// clearing the flags and saving the command phase. Then process
|
||
// the new reselecting target.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK;
|
||
luExtension->SavedCommandPhase = deviceExtension->CommandPhase;
|
||
|
||
//
|
||
// Figure which target reselected. The target ID is in the source
|
||
// ID register and the logical unit number is in the target lun
|
||
// register.
|
||
//
|
||
|
||
*((PUCHAR) &sourceId) = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
SourceId
|
||
);
|
||
|
||
*((PUCHAR) &targetLun) = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
TargetLun
|
||
);
|
||
|
||
#if DBG
|
||
if (!sourceId.TargetIdValid || !targetLun.TargetLunValid) {
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Reselection data not valid.\n"));
|
||
WdPrint((0,
|
||
"WdInterruptServiceRoutine: Source ID: 0x%.2X; Target LUN: 0x%.2X;\n",
|
||
sourceId,
|
||
targetLun
|
||
));
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// Check that the message which was read in is a valid IDENTIFY
|
||
// message. If it is not reject the message.
|
||
//
|
||
|
||
if (!targetLun.TargetLunValid) {
|
||
|
||
//
|
||
// This is a bogus message and should be aborted.
|
||
// Send an abort message. Put the message in the buffer, set
|
||
// the state, indicate that a disconnect is expected after
|
||
// this, and set the attention signal.
|
||
//
|
||
|
||
deviceExtension->MessageBuffer[0] = SCSIMESS_ABORT;
|
||
deviceExtension->MessageCount = 1;
|
||
deviceExtension->MessageSent = 0;
|
||
deviceExtension->AdapterState = MessageOut;
|
||
deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK;
|
||
deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID |
|
||
PD_DISCONNECT_EXPECTED;
|
||
|
||
//
|
||
// The bus is waiting for the message to be accepted. The
|
||
// attention signal will be set since this is not a valid
|
||
// reselection. Finally, the synchronous data tranfer
|
||
// parameters need to be set in case a data transfer is done.
|
||
//
|
||
|
||
WdAcceptMessage(deviceExtension, TRUE, TRUE);
|
||
deviceExtension->InterruptCount = 0;
|
||
break;
|
||
|
||
}
|
||
|
||
WdProcessReselection(
|
||
deviceExtension,
|
||
(UCHAR)sourceId.TargetId,
|
||
(UCHAR)targetLun.LogicalUnitNumber
|
||
);
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
//
|
||
// A phased or abort interrupt should not occur with any other
|
||
// state values.
|
||
//
|
||
|
||
WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 6);
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Unexpected paused/aborted interrupt.\n"));
|
||
WdDumpState(deviceExtension);
|
||
WdResetScsiBusInternal(deviceExtension, 0);
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
//
|
||
// Look for a service required interrupt.
|
||
//
|
||
|
||
} else if (interruptStatus.ServiceRequired &&
|
||
!interruptStatus.PhaseStateValid) {
|
||
|
||
//
|
||
// A service-required interrupt has occurred. Determine what happened
|
||
// based on the PhaseState code.
|
||
//
|
||
|
||
switch (interruptStatus.PhaseState) {
|
||
case SERVICE_RESELECTED:
|
||
|
||
//
|
||
// A target reselected; however, the IDENTIFY message has not been
|
||
// received. Indicate that a disconnect is expected. This will
|
||
// only allow the target to perform a message-in or a message-out.
|
||
// When the IDENTIFY message is actually received then the disconnect
|
||
// expected flag will be cleared.
|
||
//
|
||
|
||
deviceExtension->MessageCount = 0;
|
||
deviceExtension->AdapterFlags &= ~PD_MESSAGE_OUT_VALID;
|
||
deviceExtension->AdapterState = MessageOut;
|
||
deviceExtension->InterruptCount = 0;
|
||
deviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED;
|
||
waitForInterrupt = TRUE;
|
||
|
||
break;
|
||
|
||
case SERVICE_RESELECTED_IDENTIFY:
|
||
|
||
//
|
||
// A target reselected, and an IDENTIFY message has been received.
|
||
// The target ID is in the SourceId register and the IDENTIFY
|
||
// message is in the Data register. Get these values and process
|
||
// the reselection. Note that the format of the identify message
|
||
// is identical to that of the TargetId register.
|
||
//
|
||
|
||
*((PUCHAR) &sourceId) = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
SourceId
|
||
);
|
||
|
||
*((PUCHAR) &targetLun) = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
Data
|
||
);
|
||
|
||
#if DBG
|
||
if (!sourceId.TargetIdValid || !targetLun.TargetLunValid) {
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Reselection data not valid.\n"));
|
||
WdPrint((0,
|
||
"WdInterruptServiceRoutine: Source ID: 0x%.2X; Target LUN: 0x%.2X;\n",
|
||
sourceId,
|
||
targetLun
|
||
));
|
||
}
|
||
|
||
#endif
|
||
//
|
||
// Check that the message which was read in is a valid IDENTIFY
|
||
// message. If it is not reject the message.
|
||
//
|
||
|
||
if (!targetLun.TargetLunValid) {
|
||
|
||
//
|
||
// This is a bogus message and should be aborted.
|
||
// Send an abort message. Put the message-in the buffer, set
|
||
// the state, indicate that a disconnect is expected after
|
||
// this, and set the attention signal.
|
||
//
|
||
|
||
deviceExtension->MessageBuffer[0] = SCSIMESS_ABORT;
|
||
deviceExtension->MessageCount = 1;
|
||
deviceExtension->MessageSent = 0;
|
||
deviceExtension->AdapterState = MessageOut;
|
||
deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK;
|
||
deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID |
|
||
PD_DISCONNECT_EXPECTED;
|
||
|
||
//
|
||
// The bus is waiting for the message to be accepted. The
|
||
// attention signal will be set since this is not a valid
|
||
// reselection. Finally, the synchronous data tranfer
|
||
// parameters need to be set in case a data transfer is done.
|
||
//
|
||
|
||
WdAcceptMessage(deviceExtension, TRUE, TRUE);
|
||
deviceExtension->InterruptCount = 0;
|
||
break;
|
||
|
||
}
|
||
|
||
WdProcessReselection(
|
||
deviceExtension,
|
||
(UCHAR)sourceId.TargetId,
|
||
(UCHAR)targetLun.LogicalUnitNumber
|
||
);
|
||
|
||
break;
|
||
|
||
case SERVICE_DISCONNECTED:
|
||
|
||
//
|
||
// A disconnect has occurred. Clean up the state and check for
|
||
// pending requests.
|
||
// Check to see if this was a send-message request which is
|
||
// completed when the disconnect occurs.
|
||
//
|
||
|
||
if (deviceExtension->AdapterFlags & PD_SEND_MESSAGE_REQUEST) {
|
||
|
||
//
|
||
// Complete the request.
|
||
//
|
||
|
||
WdCompleteSendMessage( deviceExtension,
|
||
SRB_STATUS_SUCCESS
|
||
);
|
||
}
|
||
|
||
//
|
||
// Save the command phase for the logical unit.
|
||
//
|
||
|
||
luExtension->SavedCommandPhase = deviceExtension->CommandPhase;
|
||
|
||
//
|
||
// If this disconnect was not expected or the command phase is not
|
||
// correct for a legal disconnect, then this is a unexpected
|
||
// disconnect.
|
||
//
|
||
|
||
if (deviceExtension->CommandPhase != PHASE_LEGAL_DISCONNECT &&
|
||
!(deviceExtension->AdapterFlags & PD_DISCONNECT_EXPECTED)) {
|
||
|
||
//
|
||
// An unexpected disconnect occurred; make sure this is not
|
||
// related to a synchronous message.
|
||
//
|
||
|
||
if (deviceExtension->AdapterFlags &
|
||
(PD_SYNCHRONOUS_RESPONSE_SENT | PD_SYNCHRONOUS_TRANSFER_SENT |
|
||
PD_POSSIBLE_EXTRA_MESSAGE_OUT)) {
|
||
|
||
//
|
||
// This target cannot negotiate properly. Set a flag to
|
||
// prevent further attempts and set the synchronous
|
||
// parameters to use asynchronous data transfer.
|
||
//
|
||
|
||
/* TODO: Consider propagating this flag to all the Lus on this target. */
|
||
luExtension->LuFlags |= PD_DO_NOT_NEGOTIATE;
|
||
luExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
luExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
|
||
}
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Unexpected bus disconnect\n"));
|
||
WdLogError(deviceExtension, SP_UNEXPECTED_DISCONNECT, 7);
|
||
|
||
}
|
||
|
||
//
|
||
// Clean up the adapter state to indicate the bus is now free, enable
|
||
// reselection, and start any pending request.
|
||
//
|
||
|
||
deviceExtension->AdapterState = BusFree;
|
||
deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK;
|
||
deviceExtension->ActiveLogicalUnit = NULL;
|
||
|
||
#if DBG
|
||
if (WdDebug) {
|
||
WdPrint((0, "WdInterruptServiceRoutine: DisconnectComplete.\n"));
|
||
}
|
||
#endif
|
||
|
||
if (deviceExtension->AdapterFlags & PD_PENDING_START_IO) {
|
||
|
||
//
|
||
// Call WdStartIo to start the pending request.
|
||
// Note that WdStartIo is idempotent when called with
|
||
// the same arguments.
|
||
//
|
||
|
||
WdStartIo(
|
||
deviceExtension,
|
||
deviceExtension->NextSrbRequest
|
||
);
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
//
|
||
// This interrupt should not occur with any other
|
||
// state values.
|
||
//
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Unexpected service required interrupt.\n"));
|
||
WdDumpState(deviceExtension);
|
||
WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 8);
|
||
|
||
WdResetScsiBusInternal(deviceExtension, 0);
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
//
|
||
// Look for a terminated interrupt.
|
||
//
|
||
|
||
} else if (interruptStatus.Terminated &&
|
||
!interruptStatus.PhaseStateValid) {
|
||
|
||
//
|
||
// A terminated interrupt occurred. Decode the state information to
|
||
// determine why this happened.
|
||
//
|
||
|
||
switch (interruptStatus.PhaseState) {
|
||
case TERMINATE_INVALID_COMMAND:
|
||
|
||
//
|
||
// The chip detected an invalid command. This may occur during
|
||
// normal operation if a select is attempted at the same time that
|
||
// a reselect occurred.
|
||
//
|
||
|
||
#if DBG
|
||
WdPrint((0, "WdInterruptServiceRoutine: Invalid command interrupt occurred\n"));
|
||
WdDumpState(deviceExtension);
|
||
#endif
|
||
|
||
WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 9);
|
||
|
||
//
|
||
// Things appear to be messed up. Reset the bus and the chip.
|
||
//
|
||
|
||
WdResetScsiBusInternal(deviceExtension, 0);
|
||
|
||
return(TRUE);
|
||
break;
|
||
|
||
case TERMINATE_UNEXPECTED_DISC:
|
||
|
||
//
|
||
// An unexpected disconnect occurred; make sure this is not
|
||
// related to a synchronous message.
|
||
//
|
||
|
||
if (deviceExtension->AdapterFlags &
|
||
(PD_SYNCHRONOUS_RESPONSE_SENT | PD_SYNCHRONOUS_TRANSFER_SENT |
|
||
PD_POSSIBLE_EXTRA_MESSAGE_OUT)) {
|
||
|
||
//
|
||
// This target cannot negotiate properly. Set a flag to
|
||
// prevent further attempts and set the synchronous
|
||
// parameters to use asynchronous data transfer.
|
||
//
|
||
|
||
/* TODO: Consider propagating this flag to all the Lus on this target. */
|
||
luExtension->LuFlags |= PD_DO_NOT_NEGOTIATE;
|
||
luExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
luExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
|
||
}
|
||
|
||
//
|
||
// An unexpected disconnect has occurred. Log the error. It is
|
||
// not clear if the device will respond again, so let the time-out
|
||
// code clean up the request if necessary.
|
||
//
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Unexpected bus disconnect\n"));
|
||
WdLogError(deviceExtension, SP_UNEXPECTED_DISCONNECT, 10);
|
||
|
||
|
||
|
||
//
|
||
// Clean up the adapter state to indicate the bus is now free,
|
||
// and start any pending request.
|
||
//
|
||
|
||
deviceExtension->AdapterState = BusFree;
|
||
deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK;
|
||
deviceExtension->ActiveLogicalUnit = NULL;
|
||
|
||
#if DBG
|
||
if (WdDebug) {
|
||
WdPrint((0, "WdInterruptServiceRoutine: DisconnectComplete.\n"));
|
||
}
|
||
#endif
|
||
|
||
if (deviceExtension->AdapterFlags & PD_PENDING_START_IO) {
|
||
|
||
//
|
||
// Call WdStartIo to start the pending request.
|
||
// Note that WdStartIo is idempotent when called with
|
||
// the same arguments.
|
||
//
|
||
|
||
WdStartIo(
|
||
deviceExtension,
|
||
deviceExtension->NextSrbRequest
|
||
);
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
case TERMINATE_SELECT_TIMEOUT:
|
||
|
||
//
|
||
// The target selection failed. Log the error. If the retry
|
||
// count is not exceeded then retry the selection; otherwise
|
||
// fail the request.
|
||
//
|
||
|
||
if (luExtension->RetryCount++ >= RETRY_SELECTION_LIMIT) {
|
||
|
||
//
|
||
// Clear the Active request in the logical unit.
|
||
//
|
||
|
||
if (deviceExtension->AdapterFlags & PD_SEND_MESSAGE_REQUEST) {
|
||
luExtension->ActiveSendRequest = NULL;
|
||
} else {
|
||
luExtension->ActiveLuRequest = NULL;
|
||
}
|
||
|
||
luExtension->RetryCount = 0;
|
||
deviceExtension->NextSrbRequest->SrbStatus =
|
||
SRB_STATUS_SELECTION_TIMEOUT;
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
deviceExtension,
|
||
deviceExtension->NextSrbRequest
|
||
);
|
||
|
||
deviceExtension->NextSrbRequest = NULL;
|
||
deviceExtension->AdapterFlags &= ~PD_PENDING_START_IO;
|
||
|
||
ScsiPortNotification(
|
||
NextRequest,
|
||
deviceExtension,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
//
|
||
// Clean up the adapter state to indicate the bus is now free,
|
||
// and start any pending request.
|
||
//
|
||
|
||
deviceExtension->AdapterState = BusFree;
|
||
deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK;
|
||
deviceExtension->ActiveLogicalUnit = NULL;
|
||
|
||
#if DBG
|
||
if (WdDebug) {
|
||
WdPrint((0, "WdInterruptServiceRoutine: DisconnectComplete.\n"));
|
||
}
|
||
#endif
|
||
|
||
if (deviceExtension->AdapterFlags & PD_PENDING_START_IO) {
|
||
|
||
//
|
||
// Call WdStartIo to start the pending request.
|
||
// Note that WdStartIo is idempotent when called with
|
||
// the same arguments.
|
||
//
|
||
|
||
WdStartIo(
|
||
deviceExtension,
|
||
deviceExtension->NextSrbRequest
|
||
);
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
case TERMINATE_PARITY_NO_ATN:
|
||
case TERMINATE_PARITY_STATUS_IN:
|
||
case TERMINATE_PARITY_WITH_ATN:
|
||
|
||
//
|
||
// The SCSI protocol chip has set not ATN; we expect the target to
|
||
// go into message-out so that a error message can be sent and the
|
||
// operation retried. After the error has been noted, continue
|
||
// processing the interrupt. The message sent depends on whether a
|
||
// message was being received: if the adapter
|
||
// state is currently message-in then send-message PARITY ERROR;
|
||
// otherwise, send INITIATOR DETECTED ERROR.
|
||
//
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Parity error detected.\n"));
|
||
WdDumpState(deviceExtension);
|
||
|
||
if (!(deviceExtension->AdapterFlags & PD_PARITY_ERROR)) {
|
||
|
||
//
|
||
// Only log one parity error per request.
|
||
//
|
||
|
||
WdLogError(deviceExtension, SP_BUS_PARITY_ERROR, 11);
|
||
}
|
||
|
||
//
|
||
// If the ATN single has not been set then set it and clear the ACK
|
||
// signal.
|
||
//
|
||
|
||
if (!(interruptStatus.PhaseState == TERMINATE_PARITY_WITH_ATN)) {
|
||
|
||
//
|
||
// The ATN signal must be set.
|
||
//
|
||
|
||
WdAcceptMessage(deviceExtension, TRUE, FALSE);
|
||
|
||
} else {
|
||
|
||
//
|
||
// ATN is already set so just clear ACK.
|
||
//
|
||
|
||
WdAcceptMessage(deviceExtension, FALSE, FALSE);
|
||
|
||
}
|
||
|
||
|
||
deviceExtension->MessageBuffer[0] =
|
||
deviceExtension->AdapterState == MessageIn ?
|
||
SCSIMESS_MESS_PARITY_ERROR : SCSIMESS_INIT_DETECTED_ERROR;
|
||
deviceExtension->MessageCount = 1;
|
||
deviceExtension->MessageSent = 0;
|
||
deviceExtension->AdapterState = MessageOut;
|
||
deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID | PD_PARITY_ERROR;
|
||
|
||
break;
|
||
|
||
case TERMINATE_NEW_TRAGET_NO_ID:
|
||
|
||
//
|
||
// First processs the implied disconnect of the previous target by
|
||
// clearing the flags and saving the command phase. Then process
|
||
// the new reselecting target.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK;
|
||
luExtension->SavedCommandPhase = deviceExtension->CommandPhase;
|
||
|
||
//
|
||
// A new target as reselected; however, the IDENTIFY message has
|
||
// not been received yet. The target Id is in the destination
|
||
// register. Indicate that a disconnect is expected. This will
|
||
// only allow the target to perform a message-in or the message-out.
|
||
// When the IDENTIFY message is actually received then the disconnect
|
||
// expected flag will be cleared.
|
||
//
|
||
|
||
deviceExtension->MessageCount = 0;
|
||
deviceExtension->AdapterFlags &= ~PD_MESSAGE_OUT_VALID;
|
||
deviceExtension->AdapterState = MessageOut;
|
||
deviceExtension->InterruptCount = 0;
|
||
deviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED;
|
||
waitForInterrupt = TRUE;
|
||
|
||
break;
|
||
|
||
|
||
default:
|
||
|
||
//
|
||
// This interrupt should not occur with any other
|
||
// state values.
|
||
//
|
||
|
||
WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 12);
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Unexpected terminated interrupt.\n"));
|
||
WdDumpState(deviceExtension);
|
||
WdResetScsiBusInternal(deviceExtension, 0);
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
} else if (interruptStatus.Terminated &&
|
||
interruptStatus.PhaseStateValid) {
|
||
|
||
//
|
||
// A select-and-transfer command was halted because of an unexpected
|
||
// phase or because a simple transfer command was not completed because
|
||
// the target switched to a new phase.
|
||
// If this was part of a synchronous negotiation and the phase is
|
||
// message-out, then the target do not read all of the message bytes
|
||
// may later request extra bytes.
|
||
//
|
||
|
||
if (deviceExtension->AdapterState == MessageOut) {
|
||
|
||
ULONG count;
|
||
|
||
//
|
||
// Read the transfer counter to determine how many message bytes
|
||
// have been sent to the target and update the message sent count.
|
||
// This is necessary in case the message out needs to be restarted.
|
||
//
|
||
|
||
SCSI_READ_TRANSFER_COUNT(deviceExtension->Adapter, count);
|
||
|
||
deviceExtension->MessageSent = (UCHAR)(deviceExtension->MessageCount -
|
||
count);
|
||
|
||
if (deviceExtension->AdapterFlags & (PD_SYNCHRONOUS_TRANSFER_SENT |
|
||
PD_SYNCHRONOUS_RESPONSE_SENT)) {
|
||
|
||
//
|
||
// The target do not read all of the message bytes as it should
|
||
// have. This is not a problem except that some targets will
|
||
// come back later and try to read more bytes. Indicate that
|
||
// this is ok.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags |= PD_POSSIBLE_EXTRA_MESSAGE_OUT;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Based on the command phase determine if any data needs to be saved.
|
||
// The important cases are:
|
||
//
|
||
// Status byte read in.
|
||
// Command complete message read in.
|
||
//
|
||
|
||
switch (deviceExtension->CommandPhase) {
|
||
case PHASE_STATUS_RECEIVED:
|
||
|
||
//
|
||
// Get the status value and indicate it has been received. The
|
||
// SCSI status value is TargetLun.
|
||
//
|
||
|
||
srb->ScsiStatus = SCSI_READ(deviceExtension->Adapter, TargetLun);
|
||
luExtension->LuFlags |= PD_STATUS_VALID;
|
||
break;
|
||
|
||
case PHASE_COMPLETE_RECEIVED:
|
||
|
||
//
|
||
// The request is almost complete. A status has been received and
|
||
// the COMMAND COMPLETE message has been received; however, the
|
||
// target has not disconnected yet.
|
||
//
|
||
|
||
srb = luExtension->ActiveLuRequest;
|
||
|
||
//
|
||
// Get the status value and indicate it has been received. The
|
||
// SCSI status value is saved the the TargetLun register.
|
||
//
|
||
|
||
srb->ScsiStatus = SCSI_READ(deviceExtension->Adapter, TargetLun);
|
||
luExtension->LuFlags |= PD_STATUS_VALID;
|
||
|
||
//
|
||
// Simulate the COMMAND COMPLETE message.
|
||
//
|
||
|
||
deviceExtension->MessageCount = 1;
|
||
deviceExtension->MessageBuffer[0] = SCSIMESS_COMMAND_COMPLETE;
|
||
WdMessageDecode(deviceExtension);
|
||
|
||
//
|
||
// The next thing which should occur is a disconnect; however, this
|
||
// point is reached when the target makes a new request after the
|
||
// COMMAND COMPLETE message. WdMessageDecode has set the
|
||
// AdapterState correct, so try and process the target's request.
|
||
// This usually occurs with screwy targets which are messed up by
|
||
// synchronous negotiation messages.
|
||
//
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Check for a reset interrupt. This is indicated by an interrupt with
|
||
// no interrupt bits set.
|
||
//
|
||
|
||
} else if (!interruptStatus.PhaseStateValid) {
|
||
|
||
//
|
||
// The SCSI protocol chip was reset.
|
||
//
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: The SCSI protocol chip was reset.\n"));
|
||
WdDumpState(deviceExtension);
|
||
|
||
if (interruptStatus.PhaseState != RESET_WITH_ADVANCED &&
|
||
interruptStatus.PhaseState != RESET_STATUS) {
|
||
WdPrint((0, "WdInterruptServiceRoutine: SCSI chip reset failed. Status: 0x%.2X.\n",
|
||
*((PUCHAR) &interruptStatus)
|
||
));
|
||
|
||
WdLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 13);
|
||
|
||
}
|
||
|
||
if (interruptStatus.PhaseState == RESET_STATUS ) {
|
||
|
||
OWN_ID ownId;
|
||
|
||
//
|
||
// The OwnId register must be set when the SCSI protocol chip is reset.
|
||
// Initialize the ownId with the adapter's host ID, advanced features,
|
||
// and the correct clock frequency select. Note the CdbSize register is
|
||
// used as the OwnId register when a reset command is issued.
|
||
//
|
||
|
||
*((PUCHAR) &ownId) = 0;
|
||
ownId.InitiatorId = deviceExtension->InitiatorBusId;
|
||
ownId.AdvancedFeatures = 1;
|
||
ownId.FrequencySelect = CLOCK_CONVERSION_FACTOR;
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, CdbSize, *((PUCHAR) &ownId) );
|
||
|
||
//
|
||
// Issue a reset-chip command.
|
||
//
|
||
|
||
WdIssueCommand(deviceExtension, RESET_SCSI_CHIP, -1, 0);
|
||
|
||
return(TRUE);
|
||
|
||
} else {
|
||
|
||
SCSI_CONTROL control;
|
||
|
||
//
|
||
// Clean up the logical units and notify the port driver,
|
||
// then return.
|
||
//
|
||
|
||
WdCleanupAfterReset(deviceExtension, TRUE);
|
||
ScsiPortNotification(
|
||
ResetDetected,
|
||
deviceExtension,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Set the control register for halt on parity error, halt on ATN, ending
|
||
// disconnect interrupt, and normal DMA mode.
|
||
//
|
||
|
||
*((PUCHAR) &control) = 0;
|
||
control.HaltOnParity = 1;
|
||
control.HaltOnAtn = 1;
|
||
control.IntermediateDisconnectInt = 1;
|
||
control.EndingDisconnectInt = 1;
|
||
control.DmaModeSelect = CARD_DMA_MODE;
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Control, *((PUCHAR) &control));
|
||
|
||
//
|
||
// Set the SelectTimeOut Register to 250ms. This value does not need to
|
||
// be reinitialized for each selection.
|
||
//
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Timeout, SELECT_TIMEOUT_VALUE);
|
||
|
||
//
|
||
// Initialize the source register, in particular, enable reselection.
|
||
//
|
||
|
||
*((PUCHAR) &sourceId) = 0;
|
||
sourceId.EnableReselection = 1;
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, SourceId, *((PUCHAR) &sourceId));
|
||
|
||
//
|
||
// DO NOT REMOVE THE FOLLOWING SCSI_WRITE( )!
|
||
// It may look superfluous, but it is not.
|
||
// It is here to keep another driver in the system that
|
||
// is erroneously outputting to I/O port address 0x361
|
||
// during setup from triggering an unexpected interrupt
|
||
// from the Maynard card and hence crashing this driver.
|
||
//
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Data, 0);
|
||
|
||
deviceExtension->AdapterState = BusFree;
|
||
deviceExtension->ActiveLogicalUnit = NULL;
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Check for to see if the traget is
|
||
// is requesting some form of bus-transfer. The bus transfer type is
|
||
// determined by the bus phase. This is indicated by the
|
||
// StatusPhaseValid
|
||
//
|
||
|
||
if (interruptStatus.PhaseStateValid) {
|
||
|
||
//
|
||
// The bus is changing phases or needs more data.
|
||
//
|
||
|
||
if (deviceExtension->AdapterState == MessageOut) {
|
||
|
||
//
|
||
// The adapter state indicates that a message has been
|
||
// sent. The target may need to reread it or there may
|
||
// be more messages to send: this condition is indicated
|
||
// by a message-out bus phase. Otherwise, the message has
|
||
// been accepted by the target. Note that the driver state
|
||
// is set to MessageOut after PD_DISCONNECT_EXPECTED is set,
|
||
// or after a selection. So it is only necessary to check
|
||
// for PD_DISCONNECT_EXPECTED when the driver state is
|
||
// MessageOut.
|
||
//
|
||
|
||
if ((deviceExtension->AdapterFlags & PD_DISCONNECT_EXPECTED) &&
|
||
(interruptStatus.PhaseState != MESSAGE_OUT) &&
|
||
(interruptStatus.PhaseState != MESSAGE_IN)) {
|
||
|
||
//
|
||
// If a disconnect was expected and a bus service
|
||
// interrupt was detected, then a SCSI protocol error
|
||
// has been detected and the SCSI bus should be reset
|
||
// to clear the condition.
|
||
//
|
||
|
||
WdLogError(deviceExtension, SP_PROTOCOL_ERROR, 14);
|
||
WdPrint((0, "WdInterruptServiceRoutine: Bus request while disconnect expected after message-out.\n"));
|
||
WdDumpState(deviceExtension);
|
||
|
||
WdResetScsiBusInternal(deviceExtension, 0);
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Decode the current bus phase.
|
||
//
|
||
|
||
switch (interruptStatus.PhaseState) {
|
||
|
||
case COMMAND_OUT:
|
||
|
||
//
|
||
// Transfer the SCSI command block to the chip.
|
||
//
|
||
|
||
deviceExtension->AdapterState = CommandOut;
|
||
|
||
WdTransferInformation(
|
||
deviceExtension,
|
||
srb->Cdb,
|
||
srb->CdbLength,
|
||
TRUE
|
||
);
|
||
|
||
break;
|
||
|
||
case STATUS_IN:
|
||
|
||
//
|
||
// Setup of the SCSI protocol chip to read in the status, read the
|
||
// following message byte, and wait for the final disconnect and
|
||
// set the adapter state.
|
||
//
|
||
|
||
deviceExtension->AdapterState = MessageAccepted;
|
||
|
||
|
||
//
|
||
// Clear the transfer counter registers. This is necessary for the
|
||
// chip to really believe that the transfer has completed.
|
||
// Set the CommandPhase register to resume with a status-in phase,
|
||
// and command the SCSI protocol chip to resume a
|
||
// select-and-transfer command.
|
||
//
|
||
|
||
WdIssueCommand(
|
||
deviceExtension, // Device Extension.
|
||
SELECT_ATN_AND_TRANSFER, // Command to issue.
|
||
0, // New transfer count.
|
||
PHASE_DATA_TRANSFER_DONE // New CommandPhase.
|
||
);
|
||
|
||
break;
|
||
|
||
case MESSAGE_OUT:
|
||
|
||
//
|
||
// The target is requesting a message-out. There are four
|
||
// possible cases:
|
||
//
|
||
// 1. ATN has been asserted (because of a data under
|
||
// run condition) to force the target out of data
|
||
// transfer phase and into message out phase.
|
||
//
|
||
// 2. The target is improperly requesting a message.
|
||
//
|
||
// 3. A message has been sent, but the target could not
|
||
// read it properly.
|
||
//
|
||
// 4. It is a "normal" message out: all or the remainder
|
||
// of a message is ready and waiting to be sent.
|
||
//
|
||
|
||
//
|
||
// The first case is indicated when the adapter state is
|
||
// DataTransferComplete.
|
||
//
|
||
|
||
if (deviceExtension->AdapterState == DataTransferComplete) {
|
||
|
||
//
|
||
// The target was trying to go into, or stay in,
|
||
// data transfer phase, but we did not expect it
|
||
// to do so. Complete the request here and now
|
||
// and send an abort message to tell the target
|
||
// to abort the transfer.
|
||
//
|
||
|
||
srb->ScsiStatus = SCSISTAT_GOOD;
|
||
luExtension->LuFlags &= ~PD_STATUS_VALID;
|
||
WdProcessRequestCompletion(deviceExtension);
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
deviceExtension,
|
||
srb
|
||
);
|
||
|
||
deviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED;
|
||
deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID;
|
||
deviceExtension->MessageBuffer[0] = SCSIMESS_ABORT;
|
||
deviceExtension->MessageCount = 1;
|
||
deviceExtension->MessageSent = 0;
|
||
}
|
||
|
||
//
|
||
// The second case is indicated when the MessageCount is
|
||
// zero or the message-out flag is not set.
|
||
//
|
||
|
||
if ( deviceExtension->MessageCount == 0 ||
|
||
!(deviceExtension->AdapterFlags & PD_MESSAGE_OUT_VALID)
|
||
) {
|
||
|
||
//
|
||
// If extra message-outs are possible then just send a NOP
|
||
// message.
|
||
|
||
if (deviceExtension->AdapterFlags &
|
||
PD_POSSIBLE_EXTRA_MESSAGE_OUT) {
|
||
|
||
//
|
||
// Set the message to NOP and clear the extra message
|
||
// flag. This is a hack for controllers that do not
|
||
// properly read the entire message.
|
||
//
|
||
|
||
deviceExtension->MessageBuffer[0] = SCSIMESS_NO_OPERATION;
|
||
deviceExtension->AdapterFlags &=
|
||
~PD_POSSIBLE_EXTRA_MESSAGE_OUT;
|
||
} else {
|
||
|
||
//
|
||
// Send an INITIATOR DETECTED ERROR message.
|
||
//
|
||
|
||
deviceExtension->MessageBuffer[0] =
|
||
SCSIMESS_INIT_DETECTED_ERROR;
|
||
|
||
WdLogError(deviceExtension, SP_PROTOCOL_ERROR, 15);
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Unexpected message-out request\n"));
|
||
WdDumpState(deviceExtension);
|
||
|
||
}
|
||
|
||
deviceExtension->AdapterState = MessageOut;
|
||
deviceExtension->MessageCount = 1;
|
||
deviceExtension->MessageSent = 0;
|
||
|
||
}
|
||
|
||
//
|
||
// The third case is indicated when MessageCount and MessageSent
|
||
// are equal and nonzero (note: MessageCount can't be zero at
|
||
// this point because of the first or second case above).
|
||
//
|
||
|
||
if (deviceExtension->MessageCount == deviceExtension->MessageSent){
|
||
|
||
//
|
||
// The message needs to be re-sent, so clear MessageSent
|
||
// and fall through to the next case.
|
||
//
|
||
|
||
deviceExtension->MessageSent = 0;
|
||
}
|
||
|
||
//
|
||
// The fourth case and/or fallout from the cases above is
|
||
// taken care of by default hereinafter.
|
||
//
|
||
|
||
//
|
||
// Clear the parity error flag.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags &= ~PD_PARITY_ERROR;
|
||
|
||
//
|
||
// Tell the SCSI protocol chip to "go" and
|
||
// transfer the message to the data register.
|
||
//
|
||
|
||
deviceExtension->AdapterState = MessageOut;
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, ASSERT_ATN);
|
||
WdTransferInformation(
|
||
deviceExtension,
|
||
&deviceExtension->MessageBuffer[deviceExtension->MessageSent],
|
||
deviceExtension->MessageCount - deviceExtension->MessageSent,
|
||
TRUE
|
||
);
|
||
|
||
break;
|
||
|
||
case MESSAGE_IN:
|
||
|
||
//
|
||
// If this is the first byte of the message then initialize
|
||
// MessageCount and the adapter state. The message buffer
|
||
// cannot overflow because the message decode function will
|
||
// take care of the message before the buffer is full.
|
||
// The SCSI protocol chip will interrupt for each message
|
||
// byte.
|
||
//
|
||
|
||
if ( deviceExtension->AdapterState != MessageIn &&
|
||
deviceExtension->AdapterState != MessageAccepted ) {
|
||
|
||
deviceExtension->AdapterFlags &= ~PD_MESSAGE_OUT_VALID;
|
||
deviceExtension->MessageCount = 0;
|
||
}
|
||
|
||
deviceExtension->AdapterState = MessageIn;
|
||
waitForInterrupt = TRUE;
|
||
|
||
//
|
||
// Set the transfer counter registers to one byte and write
|
||
// the command register.
|
||
//
|
||
|
||
WdTransferInformation(
|
||
deviceExtension,
|
||
&deviceExtension->MessageBuffer[deviceExtension->MessageCount++],
|
||
1,
|
||
FALSE
|
||
);
|
||
|
||
break;
|
||
|
||
case DATA_OUT:
|
||
case DATA_IN:
|
||
|
||
if (deviceExtension->AdapterState == CommandOut) {
|
||
//
|
||
// Check that the transfer direction is ok.
|
||
//
|
||
|
||
if ((!(srb->SrbFlags & SRB_FLAGS_DATA_IN) &&
|
||
(interruptStatus.PhaseState == DATA_IN)) ||
|
||
|
||
(!(srb->SrbFlags & SRB_FLAGS_DATA_OUT) &&
|
||
(interruptStatus.PhaseState == DATA_OUT))) {
|
||
|
||
//
|
||
// The data direction is incorrect.
|
||
// Reset the bus to clear things up.
|
||
//
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Illegal transfer direction.\n"));
|
||
WdDumpState(deviceExtension);
|
||
|
||
WdLogError(deviceExtension, SP_PROTOCOL_ERROR, 16);
|
||
|
||
WdResetScsiBusInternal(deviceExtension, 0);
|
||
return(TRUE);
|
||
}
|
||
}
|
||
|
||
if (deviceExtension->ActiveDataLength == 0) {
|
||
WdPrint((0, "WdInterruptServiceRoutine: Data underrun!\n"));
|
||
|
||
//
|
||
// We have a data under run condition!
|
||
// The target is trying to go into, or stay in,
|
||
// data transfer phase, but we did not expect it
|
||
// to do so. Set ATN to force the target out of
|
||
// data phase and into message out phase in order
|
||
// to tell the target to abort the data transfer.
|
||
//
|
||
|
||
deviceExtension->InterruptCount--;
|
||
deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID;
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, ASSERT_ATN);
|
||
WdTransferInformation(
|
||
deviceExtension,
|
||
&deviceExtension->MessageBuffer[MESSAGE_BUFFER_SIZE-1],
|
||
1,
|
||
(BOOLEAN)((interruptStatus.PhaseState == DATA_OUT)? TRUE : FALSE)
|
||
);
|
||
waitForInterrupt = TRUE;
|
||
|
||
} else if (deviceExtension->ActiveDataLength == 1) {
|
||
|
||
//
|
||
// We have a final (or only), odd length byte to transfer.
|
||
//
|
||
|
||
deviceExtension->AdapterState = DataTransfer;
|
||
WdTransferInformation(
|
||
deviceExtension,
|
||
(PUCHAR)deviceExtension->ActiveDataPointer,
|
||
1,
|
||
(BOOLEAN) ((interruptStatus.PhaseState == DATA_OUT)? TRUE : FALSE)
|
||
);
|
||
|
||
waitForInterrupt = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Setup a DMA data transfer.
|
||
//
|
||
|
||
deviceExtension->DmaPhase = 0;
|
||
deviceExtension->DmaCommand = TRANSFER_INFORMATION;
|
||
deviceExtension->AdapterState = DataTransfer;
|
||
deviceExtension->AdapterFlags |= PD_DMA_ACTIVE |
|
||
PD_PENDING_DATA_TRANSFER;
|
||
deviceExtension->ActiveDataLength &= ~((ULONG)1);
|
||
|
||
ScsiPortIoMapTransfer(
|
||
deviceExtension,
|
||
srb,
|
||
(PVOID) deviceExtension->ActiveDataPointer,
|
||
deviceExtension->ActiveDataLength
|
||
);
|
||
}
|
||
return(TRUE);
|
||
break;
|
||
|
||
default:
|
||
|
||
//
|
||
// This phase is illegal and indicates a serious error. Reset the
|
||
// bus to clear the problem.
|
||
//
|
||
|
||
WdLogError(deviceExtension, SP_PROTOCOL_ERROR, 17);
|
||
|
||
WdPrint((0, "WdInterruptServiceRoutine: Illegal bus state detected.\n"));
|
||
WdDumpState(deviceExtension);
|
||
|
||
WdResetScsiBusInternal(deviceExtension, 0);
|
||
|
||
return(TRUE);
|
||
}
|
||
}
|
||
|
||
//
|
||
// If an interrupt is expected, then wait a short time for it.
|
||
//
|
||
|
||
if (waitForInterrupt) {
|
||
|
||
for (waitCount = 0; waitCount < INTERRUPT_STALL_TIME; waitCount++) {
|
||
|
||
ScsiPortStallExecution(1);
|
||
|
||
//
|
||
// Read the auxilary status register to determine if the there is
|
||
// an interrupt.
|
||
//
|
||
|
||
*((PUCHAR) &deviceExtension->AdapterStatus) = SCSI_READ_AUX(
|
||
deviceExtension->Adapter,
|
||
AuxiliaryStatus
|
||
);
|
||
|
||
//
|
||
// If there is an interrupt, then start to process it.
|
||
//
|
||
|
||
if (deviceExtension->AdapterStatus.Interrupt) {
|
||
goto NextInterrupt;
|
||
}
|
||
}
|
||
}
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
VOID
|
||
WdLogError(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN ULONG ErrorCode,
|
||
IN ULONG UniqueId
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine logs an error.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies a pointer to the device extension for the
|
||
port adapter to which the completing target controller is connected.
|
||
|
||
ErrorCode - Supplies the error code to log with the error.
|
||
|
||
UniqueId - Supplies the unique error identifier.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
|
||
//
|
||
// Look for a current request in the device extension.
|
||
//
|
||
|
||
if (DeviceExtension->ActiveLogicalUnit != NULL) {
|
||
|
||
if (DeviceExtension->ActiveLogicalUnit->ActiveLuRequest != NULL) {
|
||
|
||
srb = DeviceExtension->ActiveLogicalUnit->ActiveLuRequest;
|
||
|
||
} else {
|
||
|
||
srb = DeviceExtension->ActiveLogicalUnit->ActiveSendRequest;
|
||
|
||
}
|
||
} else {
|
||
|
||
srb = DeviceExtension->NextSrbRequest;
|
||
|
||
}
|
||
|
||
//
|
||
// If the srb is NULL, then log the error against the host adapter address.
|
||
//
|
||
|
||
if (srb == NULL) {
|
||
|
||
ScsiPortLogError(
|
||
DeviceExtension, // HwDeviceExtension,
|
||
NULL, // Srb
|
||
0, // PathId,
|
||
DeviceExtension->InitiatorBusId, // TargetId,
|
||
0, // Lun,
|
||
ErrorCode, // ErrorCode,
|
||
UniqueId // UniqueId
|
||
);
|
||
|
||
} else {
|
||
|
||
ScsiPortLogError(
|
||
DeviceExtension, // HwDeviceExtension,
|
||
srb, // Srb
|
||
srb->PathId, // PathId,
|
||
srb->TargetId, // TargetId,
|
||
srb->Lun, // Lun,
|
||
ErrorCode, // ErrorCode,
|
||
UniqueId // UniqueId
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
WdProcessRequestCompletion(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does all of checking and state updating necessary when a
|
||
request terminates normally. It determines what the SrbStatus
|
||
should be and updates the state in the DeviceExtension, the
|
||
logicalUnitExtension and the srb.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies a pointer to the device extension for the
|
||
port adapter to which the completing target controller is connected.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension;
|
||
|
||
luExtension = DeviceExtension->ActiveLogicalUnit;
|
||
srb = luExtension->ActiveLuRequest;
|
||
|
||
if ( srb->ScsiStatus != SCSISTAT_GOOD &&
|
||
srb->ScsiStatus != SCSISTAT_CONDITION_MET &&
|
||
srb->ScsiStatus != SCSISTAT_INTERMEDIATE &&
|
||
srb->ScsiStatus != SCSISTAT_INTERMEDIATE_COND_MET ) {
|
||
|
||
//
|
||
// Indicate an abnormal status code.
|
||
//
|
||
|
||
srb->SrbStatus = SRB_STATUS_ERROR;
|
||
|
||
//
|
||
// Add in the INITIATE RECOVERY flag if it was received. This
|
||
// indicates to the class driver that it must send a TERMINATE
|
||
// RECOVERY message before the logical unit will resume normal
|
||
// operation.
|
||
//
|
||
|
||
#ifdef SRB_INITIATE_RECOVERY
|
||
if (DeviceExtension->ActiveLogicalUnit->LuFlags &
|
||
PD_INITIATE_RECOVERY) {
|
||
|
||
//
|
||
// Modify the SrbStatus.
|
||
//
|
||
|
||
srb->SrbStatus |= SRB_INITIATE_RECOVERY;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// If this is a check condition, then clear the synchronous negotiation
|
||
// done flag. This is done in case the controller was power cycled.
|
||
//
|
||
|
||
if (srb->ScsiStatus == SCSISTAT_CHECK_CONDITION) {
|
||
|
||
luExtension->LuFlags &= ~PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Everything looks correct so far.
|
||
//
|
||
|
||
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
||
|
||
//
|
||
// Make sure that status is valid.
|
||
//
|
||
|
||
if (!(luExtension->LuFlags & PD_STATUS_VALID)) {
|
||
|
||
//
|
||
// The status byte is not valid.
|
||
//
|
||
|
||
srb->SrbStatus = SRB_STATUS_PHASE_SEQUENCE_FAILURE;
|
||
|
||
//
|
||
// Log the error.
|
||
//
|
||
|
||
WdLogError(DeviceExtension, SP_PROTOCOL_ERROR, 20);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Check that data was transferred to the end of the buffer.
|
||
//
|
||
|
||
if ( luExtension->MaximumTransferLength != srb->DataTransferLength ){
|
||
|
||
//
|
||
// The entire buffer was not transferred. Update the length
|
||
// and update the status code.
|
||
//
|
||
|
||
#if DBG
|
||
if (srb->SrbStatus == SRB_STATUS_SUCCESS) {
|
||
|
||
WdPrint((1, "WdProcessRequestCompletion: Short transfer, Actual: %lu; Expected: %lu;\n",
|
||
luExtension->MaximumTransferLength,
|
||
srb->DataTransferLength
|
||
));
|
||
|
||
}
|
||
#endif
|
||
srb->DataTransferLength = luExtension->MaximumTransferLength;
|
||
|
||
//
|
||
// If the request was ok upto this point then over write the status
|
||
// with data overrun.
|
||
//
|
||
|
||
if (srb->SrbStatus == SRB_STATUS_SUCCESS) {
|
||
|
||
srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
|
||
|
||
}
|
||
}
|
||
|
||
#if DBG
|
||
if (srb->SrbStatus != SRB_STATUS_SUCCESS) {
|
||
WdPrint((1, "WdProcessRequestCompletion: Request failed. ScsiStatus: 0x%.2X, SrbStatus: 0x%.2X\n",
|
||
srb->ScsiStatus,
|
||
srb->SrbStatus
|
||
));
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Clear the request but not the ActiveLogicalUnit since the target has
|
||
// not disconnected from the SCSI bus yet.
|
||
//
|
||
|
||
luExtension->ActiveLuRequest = NULL;
|
||
luExtension->RetryCount = 0;
|
||
luExtension->LuFlags &= ~PD_LU_COMPLETE_MASK;
|
||
}
|
||
|
||
BOOLEAN
|
||
WdProcessReselection(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN UCHAR TargetId,
|
||
IN UCHAR LogicalUnitNumber
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function updates the device extension and the logical unit extension
|
||
when target reselects. If necessary, the DMA is set up. This routine
|
||
should be called before the identify message has been accepted. The SCSI
|
||
protocol chip will be restarted with either a select-and-transfer command
|
||
or a message accept.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies a pointer to the specific device extension.
|
||
|
||
TargetId - Supplies the target id of the reselecting target.
|
||
|
||
LogicalUnitNumber - Supplies the logical unit number of the reselecting
|
||
target.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Returned if the reselection is valid.
|
||
|
||
FALSE - Returned if the reselection is invalid and ATN should be set.
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG pathId = 0;
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension;
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
DESTINATION_ID destinationId;
|
||
SCSI_SYNCHRONOUS scsiSynchronous;
|
||
|
||
|
||
DeviceExtension->InterruptCount = 0;
|
||
|
||
//
|
||
// Get the specific logical unit extension.
|
||
//
|
||
|
||
luExtension = ScsiPortGetLogicalUnit(
|
||
DeviceExtension,
|
||
(UCHAR)pathId,
|
||
TargetId,
|
||
LogicalUnitNumber
|
||
);
|
||
|
||
DeviceExtension->ActiveLogicalUnit = luExtension;
|
||
|
||
if (!luExtension || !luExtension->ActiveLuRequest) {
|
||
|
||
ScsiPortLogError(
|
||
DeviceExtension, // HwDeviceExtension,
|
||
NULL, // Srb
|
||
(UCHAR)pathId, // PathId,
|
||
TargetId, // TargetId,
|
||
LogicalUnitNumber, // Lun,
|
||
SP_INVALID_RESELECTION, // ErrorCode,
|
||
18 // UniqueId
|
||
);
|
||
|
||
|
||
WdPrint((0, "WdProcessReselection: Reselection Failed.\n"));
|
||
WdDumpState(DeviceExtension);
|
||
|
||
//
|
||
// Send an abort message. Put the message-in the buffer, set the
|
||
// state, indicate that a disconnect is expected after this, and
|
||
// set the attention signal.
|
||
//
|
||
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_ABORT;
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
DeviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK;
|
||
DeviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID |
|
||
PD_DISCONNECT_EXPECTED;
|
||
|
||
|
||
//
|
||
// The target and logical unit specified are not valid. A
|
||
// MESSAGE REJECT message has been set up. Set ATN and accept the
|
||
// message.
|
||
//
|
||
|
||
WdAcceptMessage(DeviceExtension, TRUE, FALSE);
|
||
|
||
return(FALSE);
|
||
|
||
}
|
||
|
||
srb = luExtension->ActiveLuRequest;
|
||
|
||
//
|
||
// A reselection has been completed. Set the active logical unit,
|
||
// restore the active data pointer, set the state.
|
||
// In addition, any adapter flags set by a pending select must be
|
||
// cleared using the disconnect mask.
|
||
//
|
||
|
||
DeviceExtension->ActiveDataPointer = luExtension->SavedDataPointer;
|
||
DeviceExtension->ActiveDataLength = luExtension->SavedDataLength;
|
||
DeviceExtension->AdapterState = MessageAccepted;
|
||
DeviceExtension->MessageCount = 0;
|
||
|
||
//
|
||
// Determine if the DMA needs to be done then set the synchronous transfer
|
||
// register.
|
||
//
|
||
|
||
if (luExtension->SavedDataLength) {
|
||
|
||
//
|
||
// Set the synchronous data transfer parameter registers in case a
|
||
// data transfer will be done. These must be set before a data transfer
|
||
// is started.
|
||
//
|
||
|
||
*((PUCHAR) &scsiSynchronous) = 0;
|
||
scsiSynchronous.SynchronousOffset = luExtension->SynchronousOffset;
|
||
scsiSynchronous.SynchronousPeriod = luExtension->SynchronousPeriod;
|
||
|
||
SCSI_WRITE(
|
||
DeviceExtension->Adapter,
|
||
Synchronous,
|
||
*((PUCHAR) &scsiSynchronous)
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Set the destination id register; this register will allow the target to
|
||
// reselect later without an interrupt, and tell the SCSI protocol chip the
|
||
// direction of the data transfer.
|
||
//
|
||
|
||
*((PUCHAR) &destinationId) = 0;
|
||
destinationId.TargetId = TargetId;
|
||
destinationId.DataDirection = srb->SrbFlags & SRB_FLAGS_DATA_OUT ? 0 : 1;
|
||
|
||
SCSI_WRITE( DeviceExtension->Adapter, DestinationId, *((PUCHAR) &destinationId));
|
||
|
||
//
|
||
// Its not clear what state the target is in so just handle
|
||
// the rest of this request one phase at a time.
|
||
// Clear the saved comand phase.
|
||
//
|
||
|
||
luExtension->SavedCommandPhase = 0;
|
||
|
||
//
|
||
// Accept the IDENTIFY message and wait for the next interrupt.
|
||
// Note that WdProcessReselect set the synchronous transfer
|
||
// registers if the DMA was set up.
|
||
//
|
||
|
||
WdAcceptMessage(DeviceExtension, FALSE, FALSE);
|
||
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
WdResetScsiBus(
|
||
IN PVOID ServiceContext,
|
||
IN ULONG PathId
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function resets the SCSI bus and calls the reset cleanup function.
|
||
|
||
Arguments:
|
||
|
||
ServiceContext - Supplies a pointer to the specific device extension.
|
||
|
||
PathId - Supplies the path id of the bus to be reset.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Indicating that the reset has completed.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext;
|
||
|
||
WdPrint((0, "WdResetScsiBus: Resetting the SCSI bus.\n"));
|
||
|
||
//
|
||
// Tell the chip to disconnect from the bus.
|
||
//
|
||
|
||
WdIssueCommand(deviceExtension, DISCONNECT_FROM_BUS, -1, 0);
|
||
|
||
//
|
||
// Reset the SCSI bus.
|
||
//
|
||
|
||
SCSI_RESET_BUS(deviceExtension);
|
||
|
||
//
|
||
// Reset the adapter.
|
||
//
|
||
|
||
WdInitializeAdapter(deviceExtension);
|
||
|
||
WdCleanupAfterReset(deviceExtension, FALSE);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
VOID
|
||
WdResetScsiBusInternal(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN ULONG PathId
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function resets the SCSI bus, notifies the port driver of the reset
|
||
and calls the reset cleanup function.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies a pointer to the specific device extension.
|
||
|
||
PathId - Supplies the path id of the bus to be reset.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
ScsiPortNotification(
|
||
ResetDetected,
|
||
DeviceExtension,
|
||
NULL
|
||
);
|
||
|
||
WdResetScsiBus(DeviceExtension, 0);
|
||
}
|
||
|
||
|
||
VOID
|
||
WdSelectTarget(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets up the hardware to select a target. If a valid message
|
||
is in the message buffer, it will be sent to the target. If the request
|
||
includes a SCSI command descriptor block, it will also be passed to the
|
||
target.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies the device extension for this HBA adapter.
|
||
|
||
LuExtension - Supplies the logical unit extension for the target being
|
||
selected.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
AUXILIARY_STATUS auxiliaryStatus;
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
TARGET_LUN targetLun;
|
||
DESTINATION_ID destinationId;
|
||
SCSI_SYNCHRONOUS scsiSynchronous;
|
||
ULONG i;
|
||
|
||
srb = DeviceExtension->NextSrbRequest;
|
||
DeviceExtension->ActiveLogicalUnit = LuExtension;
|
||
|
||
#if DBG
|
||
if (WdDebug) {
|
||
WdPrint((0, "WdSelectTarget: Attempting target select.\n"));
|
||
}
|
||
#endif
|
||
/* Powerfail Start */
|
||
|
||
//
|
||
// Set up the SCSI protocol chip to select the target, transfer the
|
||
// IDENTIFY message and the CDB. This can be done by following steps:
|
||
//
|
||
// setting the destination register,
|
||
// filling the registers with the IDENTIFY message and the CDB
|
||
// setting the command register
|
||
//
|
||
// Read the auxiliary status.
|
||
//
|
||
|
||
*((PUCHAR) &auxiliaryStatus) = SCSI_READ_AUX(DeviceExtension->Adapter,
|
||
AuxiliaryStatus
|
||
);
|
||
|
||
//
|
||
// If the SCSI protocol chip is interrupting or busy, just return.
|
||
//
|
||
|
||
if (auxiliaryStatus.Interrupt || auxiliaryStatus.ChipBusy) {
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
//
|
||
// Ensure that the data transfer count is zero if no transfer is expected.
|
||
//
|
||
|
||
if (!(srb->SrbFlags & (SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT))) {
|
||
|
||
//
|
||
// Clear the srb DataTransferLength and the SavedDataLength so chip
|
||
// won't transfer data if requested.
|
||
//
|
||
|
||
srb->DataTransferLength = 0;
|
||
LuExtension->SavedDataLength = 0;
|
||
}
|
||
|
||
//
|
||
// Set the destination ID, data direction and the target logical unit
|
||
// number. Note that setting the data direction flag indicates the
|
||
// transfer is in. The data direction flags will only be set if the
|
||
// transfer is really in.
|
||
//
|
||
|
||
*((PUCHAR) &destinationId) = 0;
|
||
destinationId.TargetId = srb->TargetId;
|
||
destinationId.DataDirection = srb->SrbFlags & SRB_FLAGS_DATA_OUT ? 0 : 1;
|
||
SCSI_WRITE(
|
||
DeviceExtension->Adapter,
|
||
DestinationId,
|
||
*((PUCHAR) &destinationId)
|
||
);
|
||
|
||
//
|
||
// Set the synchronous data transfer parameter registers in case a
|
||
// data transfer is done. These must be set before a data transfer
|
||
// is started.
|
||
//
|
||
|
||
*((PUCHAR) &scsiSynchronous) = 0;
|
||
scsiSynchronous.SynchronousOffset = LuExtension->SynchronousOffset;
|
||
scsiSynchronous.SynchronousPeriod = LuExtension->SynchronousPeriod;
|
||
|
||
SCSI_WRITE(
|
||
DeviceExtension->Adapter,
|
||
Synchronous,
|
||
*((PUCHAR) &scsiSynchronous)
|
||
);
|
||
|
||
//
|
||
// Determine if this srb has a Cdb with it and whether the message is
|
||
// such that the message and the Cdb can be loaded into the registers;
|
||
// otherwise, just select the target with ATN.
|
||
//
|
||
|
||
if ((srb->Function == SRB_FUNCTION_EXECUTE_SCSI) &&
|
||
(DeviceExtension->MessageCount == 1)) {
|
||
|
||
//
|
||
// Update the message-sent count to indicate that the IDENTIFY
|
||
// message has been sent.
|
||
//
|
||
|
||
DeviceExtension->MessageSent++;
|
||
|
||
//
|
||
// Initialize the target logical unit register.
|
||
//
|
||
|
||
*((PUCHAR) &targetLun) = 0;
|
||
targetLun.LogicalUnitNumber = srb->Lun;
|
||
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
TargetLun,
|
||
*((PUCHAR) &targetLun)
|
||
);
|
||
|
||
//
|
||
// Copy the CDB into the registers.
|
||
//
|
||
|
||
for (i = 0; i < srb->CdbLength; i++) {
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
Cdb[i],
|
||
srb->Cdb[i]
|
||
);
|
||
}
|
||
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
CdbSize,
|
||
srb->CdbLength
|
||
);
|
||
|
||
//
|
||
// Initialize the CommandPhase in the logical unit.
|
||
//
|
||
|
||
LuExtension->SavedCommandPhase = 0;
|
||
|
||
//
|
||
// Since the chip may automatically start a data transfer after the
|
||
// selection, restore the data pointers.
|
||
//
|
||
|
||
DeviceExtension->ActiveDataPointer = LuExtension->SavedDataPointer;
|
||
DeviceExtension->ActiveDataLength = LuExtension->SavedDataLength;
|
||
|
||
//
|
||
// Set the adapter state for subsequent processing.
|
||
//
|
||
|
||
DeviceExtension->AdapterState = SelectAndTransfer;
|
||
|
||
//
|
||
// Issue select-with-ATN-and-transfer command: the transfer
|
||
// count is set to zero in order to cause an interrupt to occur
|
||
// if/when the target requests a data transfer phase.
|
||
//
|
||
|
||
WdIssueCommand(
|
||
DeviceExtension, // Device Extension.
|
||
SELECT_ATN_AND_TRANSFER, // Command to issue.
|
||
0, // New transfer count.
|
||
0 // New CommandPhase.
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Set the adapter state for subsequent processing.
|
||
//
|
||
|
||
DeviceExtension->AdapterState = Select;
|
||
|
||
//
|
||
// Select the target with ATN
|
||
//
|
||
|
||
WdIssueCommand(DeviceExtension, SELECT_WITH_ATN, -1, 0);
|
||
|
||
}
|
||
|
||
/* Powerfail release */
|
||
|
||
//
|
||
// Indicate that a message out can be or is being sent.
|
||
// Start the phase timer.
|
||
//
|
||
|
||
DeviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID;
|
||
DeviceExtension->InterruptCount = 0;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
WdSendMessage(
|
||
PSCSI_REQUEST_BLOCK Srb,
|
||
PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine attempts to send the indicated message to the target
|
||
controller. There are three classes of messages:
|
||
Those which terminate a specific request and end in bus free.
|
||
Those which apply to a specific request and then proceed.
|
||
Those which end in bus free.
|
||
|
||
For those messages that apply to a specific request, check to see that
|
||
the request is currently being processed and an IDENTIFY message prefixed
|
||
the message.
|
||
|
||
It is possible that the destination logical unit is the active logical unit;
|
||
however, it would difficult to jump in and send the requested message, so
|
||
just wait for the bus to become free.
|
||
|
||
In the case where the target is not currently active, then set up the SCSI
|
||
protocol chip to select the target controller and send the message.
|
||
|
||
Arguments:
|
||
|
||
Srb - Supplies the request to be started.
|
||
|
||
DeviceExtension - Supplies the extended device extension for this SCSI bus.
|
||
|
||
LuExtension - Supplies the logical unit extension for this request.
|
||
|
||
Notes:
|
||
|
||
This routine must be synchronized with the interrupt routine.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PSCSI_REQUEST_BLOCK linkedSrb;
|
||
BOOLEAN impliesDisconnect;
|
||
UCHAR message;
|
||
|
||
impliesDisconnect = FALSE;
|
||
|
||
//
|
||
// Decode the type of message.
|
||
//
|
||
|
||
switch (Srb->Function) {
|
||
|
||
// case SCSIMESS_ABORT_WITH_TAG:
|
||
// case SCSIMESS_TERMINATE_IO_PROCESS:
|
||
|
||
/* TODO: Handle the previous two cases. */
|
||
case SRB_FUNCTION_ABORT_COMMAND:
|
||
|
||
//
|
||
// Verify that the request is being processed by the logical unit.
|
||
//
|
||
|
||
linkedSrb = ScsiPortGetNextLink( Srb );
|
||
if (linkedSrb != LuExtension->ActiveLuRequest) {
|
||
|
||
//
|
||
// The specified request is not here. Complete the request
|
||
// without error.
|
||
//
|
||
|
||
Srb->SrbStatus = SRB_STATUS_ABORT_FAILED;
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
Srb
|
||
);
|
||
|
||
ScsiPortNotification(
|
||
NextRequest,
|
||
DeviceExtension,
|
||
NULL
|
||
);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
message = SCSIMESS_ABORT;
|
||
impliesDisconnect = TRUE;
|
||
break;
|
||
|
||
case SRB_FUNCTION_RESET_DEVICE:
|
||
|
||
//
|
||
// Because of the way the chip works it is easiest to send an IDENTIFY
|
||
// message along with the BUS DEVICE RESET message. That is because
|
||
// there is no way to select a target with ATN and send one message
|
||
// byte. This IDENTIFY message is not necessary for the SCSI protocol,
|
||
// but it is legal and should not cause any problem.
|
||
//
|
||
|
||
message = SCSIMESS_BUS_DEVICE_RESET;
|
||
impliesDisconnect = TRUE;
|
||
break;
|
||
|
||
/* case SCSIMESS_RELEASE_RECOVERY:
|
||
case SCSIMESS_CLEAR_QUEUE:
|
||
|
||
//
|
||
// These messages require an IDENTIFY message and imply a disconnect.
|
||
//
|
||
|
||
impliesDisconnect = TRUE;
|
||
break;
|
||
*/
|
||
default:
|
||
|
||
//
|
||
// This is an unsupported message request. Fail the request.
|
||
//
|
||
|
||
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
Srb
|
||
);
|
||
|
||
ScsiPortNotification(
|
||
NextRequest,
|
||
DeviceExtension,
|
||
NULL
|
||
);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
//
|
||
// Save away the parameters in case nothing can be done now.
|
||
//
|
||
|
||
DeviceExtension->NextSrbRequest = Srb;
|
||
DeviceExtension->AdapterFlags |= PD_PENDING_START_IO;
|
||
LuExtension->ActiveSendRequest = Srb;
|
||
|
||
//
|
||
// Check to see if the bus is free. If it is not, then return. Since
|
||
// the request parameters have been saved, indicate that the request has
|
||
// been accepted. The request will be processed when the bus becomes free.
|
||
//
|
||
|
||
if (DeviceExtension->AdapterState != BusFree) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Create the identify command and copy the message to the buffer.
|
||
//
|
||
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_IDENTIFY_WITH_DISCON |
|
||
Srb->Lun;
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
|
||
DeviceExtension->MessageBuffer[DeviceExtension->MessageCount++] = message;
|
||
|
||
//
|
||
// Attempt to select the target and update the adapter flags.
|
||
//
|
||
|
||
WdSelectTarget( DeviceExtension, LuExtension );
|
||
|
||
DeviceExtension->AdapterFlags |= impliesDisconnect ?
|
||
PD_DISCONNECT_EXPECTED | PD_SEND_MESSAGE_REQUEST
|
||
: PD_SEND_MESSAGE_REQUEST;
|
||
|
||
}
|
||
|
||
VOID
|
||
WdSetupDma(
|
||
PVOID ServiceContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function performs all of the operations necessary set up the SCSI
|
||
protocol card and chip for a data transfer. This function should be called
|
||
after the last access to chip. In particular, the transfer count is set and
|
||
the transfer is mapped. If the active transfer count is zero, then the
|
||
DMA is not started.
|
||
|
||
Arguments:
|
||
|
||
ServiceContext - Supplies the extended device extension for this SCSI bus.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension;
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext;
|
||
|
||
//
|
||
// If the data tranfer is no longer expected then ignore the notification.
|
||
//
|
||
|
||
if (!(deviceExtension->AdapterFlags & PD_PENDING_DATA_TRANSFER)) {
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
luExtension = deviceExtension->ActiveLogicalUnit;
|
||
srb = luExtension->ActiveLuRequest;
|
||
|
||
//
|
||
// Setup the SCSI host adapter card.
|
||
//
|
||
|
||
CARD_DMA_INITIATE( deviceExtension, srb->SrbFlags & SRB_FLAGS_DATA_IN );
|
||
|
||
//
|
||
// Tell the chip to transfer the data.
|
||
//
|
||
|
||
WdIssueCommand(
|
||
deviceExtension, // Device Extension.
|
||
deviceExtension->DmaCommand, // Command to issue.
|
||
deviceExtension->ActiveDataLength, // New transfer count.
|
||
deviceExtension->DmaPhase // New CommandPhase.
|
||
);
|
||
|
||
deviceExtension->AdapterFlags &= ~PD_PENDING_DATA_TRANSFER;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
WdStartExecution(
|
||
PSCSI_REQUEST_BLOCK Srb,
|
||
PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure sets up the chip to select the target and notify it that
|
||
a request is available. For the WD chip, the chip is set up to select,
|
||
send the IDENTIFY message and send the command data block. A check is
|
||
made to determine if synchronous negotiation is necessary.
|
||
|
||
Arguments:
|
||
|
||
Srb - Supplies the request to be started.
|
||
|
||
DeviceExtension - Supplies the extended device extension for this SCSI bus.
|
||
|
||
LuExtension - Supplies the logical unit extension for this request.
|
||
|
||
Notes:
|
||
|
||
This routine must be synchronized with the interrupt routine.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSCSI_EXTENDED_MESSAGE extendedMessage;
|
||
|
||
//
|
||
// Save away the parameters in case nothing can be done now.
|
||
//
|
||
|
||
LuExtension->ActiveLuRequest = Srb;
|
||
LuExtension->SavedDataPointer = (ULONG) Srb->DataBuffer;
|
||
LuExtension->SavedDataLength = Srb->DataTransferLength;
|
||
LuExtension->MaximumTransferLength = 0;
|
||
DeviceExtension->NextSrbRequest = Srb;
|
||
DeviceExtension->AdapterFlags |= PD_PENDING_START_IO;
|
||
|
||
//
|
||
// Check to see if the bus is free. If it is not, then return. Since
|
||
// the request parameters have been saved, indicate that the request has
|
||
// been accepted. The request will be processed when the bus becomes free.
|
||
//
|
||
|
||
if (DeviceExtension->AdapterState != BusFree) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Create the identify command.
|
||
//
|
||
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_IDENTIFY_WITH_DISCON | Srb->Lun;
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID;
|
||
|
||
//
|
||
// Set the active data length and pointer in the specific device
|
||
// extension. These are used to program the DMA.
|
||
//
|
||
|
||
DeviceExtension->ActiveDataPointer = LuExtension->SavedDataPointer;
|
||
DeviceExtension->ActiveDataLength = LuExtension->SavedDataLength;
|
||
|
||
//
|
||
// Check to see if synchronous negotiation is necessary.
|
||
//
|
||
|
||
if (!(LuExtension->LuFlags &
|
||
(PD_SYNCHRONOUS_NEGOTIATION_DONE | PD_DO_NOT_NEGOTIATE)) &&
|
||
!(Srb->SrbFlags & SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) {
|
||
|
||
//
|
||
// Initialize the synchronous transfer register values to an
|
||
// asynchronous transfer, which is what will be used if anything
|
||
// goes wrong with the negotiation.
|
||
//
|
||
|
||
LuExtension->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
LuExtension->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
|
||
//
|
||
// Create the synchronous data transfer request message.
|
||
// The format of the message is:
|
||
//
|
||
// EXTENDED_MESSAGE op-code
|
||
// Length of message
|
||
// Synchronous transfer data request op-code
|
||
// Our Transfer period
|
||
// Our REQ/ACK offset
|
||
//
|
||
// The message is placed after the IDENTIFY message.
|
||
//
|
||
|
||
extendedMessage = (PSCSI_EXTENDED_MESSAGE)
|
||
&DeviceExtension->MessageBuffer[DeviceExtension->MessageCount];
|
||
DeviceExtension->MessageCount += 2 + SCSIMESS_SYNCH_DATA_LENGTH;
|
||
|
||
extendedMessage->InitialMessageCode = SCSIMESS_EXTENDED_MESSAGE;
|
||
extendedMessage->MessageLength = SCSIMESS_SYNCH_DATA_LENGTH;
|
||
extendedMessage->MessageType = SCSIMESS_SYNCHRONOUS_DATA_REQ;
|
||
extendedMessage->ExtendedArguments.Synchronous.TransferPeriod = SYNCHRONOUS_PERIOD;
|
||
extendedMessage->ExtendedArguments.Synchronous.ReqAckOffset = SYNCHRONOUS_OFFSET;
|
||
|
||
//
|
||
// Attempt to select the target and update the adapter flags.
|
||
//
|
||
|
||
WdSelectTarget( DeviceExtension, LuExtension );
|
||
|
||
//
|
||
// Many controllers reject the first byte of a synchronous
|
||
// negotiation message. Since this is a multibyte message the
|
||
// ATN signal remains set after the first byte is sent. Some
|
||
// controllers remember this attempt to do a message-out
|
||
// later. Setting the PD_POSSIBLE_EXTRA_MESSAGE_OUT flag allows
|
||
// this extra message transfer to occur without error.
|
||
//
|
||
|
||
DeviceExtension->AdapterFlags |= PD_POSSIBLE_EXTRA_MESSAGE_OUT |
|
||
PD_SYNCHRONOUS_TRANSFER_SENT;
|
||
|
||
return;
|
||
}
|
||
|
||
WdSelectTarget( DeviceExtension, LuExtension );
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
WdStartIo(
|
||
IN PVOID ServiceContext,
|
||
IN PSCSI_REQUEST_BLOCK Srb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used by the OS dependent port driver to pass requests to
|
||
the HBA-dependent driver. This function begins the execution of the request.
|
||
Requests to reset the SCSI bus are handled immediately. Requests to send
|
||
a message or start a SCSI command are handled when the bus is free.
|
||
|
||
Arguments:
|
||
|
||
ServiceContext - Supplies the device extension for the SCSI bus adapter.
|
||
|
||
Srb - Supplies the SCSI request block to be started.
|
||
|
||
Return Value:
|
||
|
||
TRUE - If the request can be accepted at this time.
|
||
|
||
FALSE - If the request must be submitted later.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext;
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension;
|
||
|
||
switch (Srb->Function) {
|
||
case SRB_FUNCTION_EXECUTE_SCSI:
|
||
|
||
WdPrint((0, "WdStartIo: execute scsi\n" ));
|
||
|
||
//
|
||
// Determine the logical unit that this request is for.
|
||
//
|
||
|
||
luExtension = ScsiPortGetLogicalUnit(
|
||
deviceExtension,
|
||
Srb->PathId,
|
||
Srb->TargetId,
|
||
Srb->Lun
|
||
);
|
||
WdStartExecution(
|
||
Srb,
|
||
deviceExtension,
|
||
luExtension
|
||
);
|
||
|
||
return(TRUE);
|
||
|
||
case SRB_FUNCTION_ABORT_COMMAND:
|
||
case SRB_FUNCTION_RESET_DEVICE:
|
||
case SRB_FUNCTION_TERMINATE_IO:
|
||
|
||
WdPrint((0, "WdStartIo: abort/reset/terminate\n" ));
|
||
|
||
//
|
||
//
|
||
// Determine the logical unit that this request is for.
|
||
//
|
||
|
||
luExtension = ScsiPortGetLogicalUnit(
|
||
deviceExtension,
|
||
Srb->PathId,
|
||
Srb->TargetId,
|
||
Srb->Lun
|
||
);
|
||
WdSendMessage(
|
||
Srb,
|
||
deviceExtension,
|
||
luExtension
|
||
);
|
||
|
||
return(TRUE);
|
||
|
||
case SRB_FUNCTION_RESET_BUS:
|
||
|
||
WdPrint((0, "WdStartIo: reset bus\n" ));
|
||
|
||
//
|
||
// There is no logical unit so just reset the bus.
|
||
//
|
||
|
||
WdResetScsiBus( deviceExtension, 0 );
|
||
return(TRUE);
|
||
|
||
default:
|
||
|
||
WdPrint((0, "WdStartIo: default case -- bad function\n" ));
|
||
|
||
//
|
||
// Unknown function code in the request.
|
||
// Complete the request with an error and ask for the next request.
|
||
//
|
||
|
||
Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION;
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
deviceExtension,
|
||
Srb
|
||
);
|
||
|
||
ScsiPortNotification(
|
||
NextRequest,
|
||
deviceExtension,
|
||
NULL
|
||
);
|
||
|
||
return(TRUE);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
WdTransferInformation(
|
||
PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
PUCHAR BufferPointer,
|
||
ULONG Count,
|
||
BOOLEAN TransferToChip
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This fucntion transfers data between memory and the SCSI protocol chip's
|
||
data registers. It is only used for non-data transfers. This operation
|
||
is done synchronously; however, since the chip has a 12-byte FIFO and this
|
||
function is used for small transfers, it will execute quickly.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies the device Extension for the SCSI bus adapter.
|
||
|
||
BufferPointer - Supplies a pointer to the data to be copied.
|
||
|
||
Count - Supplies the number of bytes to be transferred.
|
||
|
||
TransferToChip - Indicates the direction of the transfer.
|
||
|
||
Return Value:
|
||
|
||
Returns the number of bytes actually transferred.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
ULONG j;
|
||
AUXILIARY_STATUS scsiStatus;
|
||
|
||
//
|
||
// Issue the transfer information command to the SCSI protocol chip.
|
||
//
|
||
|
||
WdIssueCommand(DeviceExtension, TRANSFER_INFORMATION, Count, 0);
|
||
|
||
for (i = 0; i < Count; i++ ) {
|
||
|
||
//
|
||
// Determine if the data buffer is ready.
|
||
//
|
||
|
||
*((PUCHAR) &scsiStatus) = SCSI_READ_AUX(
|
||
DeviceExtension->Adapter,
|
||
AuxiliaryStatus
|
||
);
|
||
|
||
j = 0;
|
||
|
||
while (!scsiStatus.DataBufferReady && j < DATA_BUS_READY_TIME) {
|
||
|
||
if (scsiStatus.Interrupt) {
|
||
|
||
//
|
||
// The SCSI protocol chip is interrupting so no more data
|
||
// will be transferred.
|
||
//
|
||
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Wait for the data buffer to become free.
|
||
//
|
||
|
||
ScsiPortStallExecution( 1 );
|
||
j++;
|
||
|
||
*((PUCHAR) &scsiStatus) = SCSI_READ_AUX(
|
||
DeviceExtension->Adapter,
|
||
AuxiliaryStatus
|
||
);
|
||
|
||
}
|
||
|
||
if (!scsiStatus.DataBufferReady) {
|
||
|
||
//
|
||
// The data buffer did not become free in time. Something is hung.
|
||
// Reset the controller and the bus, finally return FALSE.
|
||
//
|
||
|
||
WdLogError(DeviceExtension, SP_BUS_TIME_OUT, 21);
|
||
|
||
WdPrint((0,
|
||
"WdTransferInformation: The data buffer ready flag timed out. Transfer count = %lu.\n",
|
||
Count
|
||
));
|
||
|
||
WdDumpState(DeviceExtension);
|
||
WdResetScsiBusInternal(DeviceExtension, 0);
|
||
|
||
return(FALSE);
|
||
|
||
|
||
}
|
||
|
||
#if DBG
|
||
if (WdDebug && j > 1) {
|
||
WdPrint((0, "WdTransferInformation Data wait time: %d, Status: 0x%.2X\n",
|
||
j, DeviceExtension->AdapterInterrupt));
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Move the data to the data register.
|
||
//
|
||
|
||
if (TransferToChip) {
|
||
|
||
SCSI_WRITE(DeviceExtension->Adapter, Data, *BufferPointer);
|
||
BufferPointer++;
|
||
|
||
} else {
|
||
|
||
*BufferPointer = SCSI_READ(DeviceExtension->Adapter, Data);
|
||
BufferPointer++;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
ULONG
|
||
DriverEntry (
|
||
IN PVOID DriverObject,
|
||
IN PVOID Argument2
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Driver Object is passed to ScsiPortInitialize()
|
||
|
||
Return Value:
|
||
|
||
Status from ScsiPortInitialize()
|
||
|
||
--*/
|
||
|
||
{
|
||
HW_INITIALIZATION_DATA hwInitializationData;
|
||
PUCHAR BufferPointer;
|
||
ULONG i;
|
||
|
||
WdPrint((1,"\nSCSI WD33C93A Miniport Driver\n"));
|
||
|
||
//
|
||
// Initialize the buffer to zero.
|
||
//
|
||
|
||
BufferPointer = (PUCHAR) &hwInitializationData;
|
||
for (i = 0; i < sizeof(hwInitializationData); i++) {
|
||
*BufferPointer++ = 0;
|
||
}
|
||
|
||
hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
|
||
|
||
hwInitializationData.HwInitialize = WdInitializeAdapter;
|
||
hwInitializationData.HwStartIo = WdStartIo;
|
||
hwInitializationData.HwInterrupt = WdInterruptServiceRoutine;
|
||
hwInitializationData.HwFindAdapter = WdFindAdapter;
|
||
hwInitializationData.HwResetBus = WdResetScsiBus;
|
||
hwInitializationData.HwDmaStarted = WdSetupDma;
|
||
hwInitializationData.AdapterInterfaceType = SCSI_BUS_INTERFACE;
|
||
hwInitializationData.NumberOfAccessRanges = 1;
|
||
hwInitializationData.DeviceExtensionSize = sizeof(SPECIFIC_DEVICE_EXTENSION);
|
||
hwInitializationData.MapBuffers = TRUE;
|
||
hwInitializationData.SpecificLuExtensionSize =
|
||
sizeof(SPECIFIC_LOGICAL_UNIT_EXTENSION);
|
||
|
||
return ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, NULL);
|
||
|
||
} // end DriverEntry()
|
||
|
||
ULONG
|
||
WdFindAdapter(
|
||
IN PVOID ServiceContext,
|
||
IN PVOID Context,
|
||
IN PVOID BusInformation,
|
||
IN PCHAR ArgumentString,
|
||
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
||
OUT PBOOLEAN Again
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function fills in the configuration information structure and maps
|
||
the SCSI protocol chip for access. This routine is temporary until
|
||
the configuration manager supplies similar information.
|
||
|
||
Arguments:
|
||
|
||
ServiceContext - Supplies a pointer to the device extension.
|
||
|
||
Context - Unused.
|
||
|
||
BusInformation - Unused.
|
||
|
||
ArgumentString - Unused.
|
||
|
||
ConfigInfo - Pointer to the configuration information structure to be
|
||
filled in.
|
||
|
||
Again - Returns back a request to call this function again.
|
||
|
||
Return Value:
|
||
|
||
Returns a status value for the initialization.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext;
|
||
AUXILIARY_STATUS scsiStatus;
|
||
ULONG physicalBase;
|
||
SCSI_PHYSICAL_ADDRESS base;
|
||
UCHAR dataByte;
|
||
BOOLEAN found = FALSE;
|
||
BOOLEAN baseSupplied;
|
||
|
||
*Again = FALSE;
|
||
|
||
//
|
||
// Parse any supplied argument string.
|
||
//
|
||
|
||
if (ArgumentString != NULL) {
|
||
|
||
//
|
||
// Look for a base parameter.
|
||
//
|
||
|
||
physicalBase = WdParseArgumentString(ArgumentString, "BASE");
|
||
|
||
if (physicalBase != 0) {
|
||
|
||
base = ScsiPortConvertUlongToPhysicalAddress(physicalBase);
|
||
(*ConfigInfo->AccessRanges)[0].RangeStart = base;
|
||
}
|
||
|
||
//
|
||
// Look for interrupt level.
|
||
//
|
||
|
||
physicalBase = WdParseArgumentString(ArgumentString, "IRQ");
|
||
|
||
if (physicalBase != 0) {
|
||
|
||
ConfigInfo->BusInterruptVector = SCSI_VECTOR;
|
||
ConfigInfo->BusInterruptLevel = physicalBase;
|
||
|
||
}
|
||
|
||
//
|
||
// Look for interrupt level.
|
||
//
|
||
|
||
physicalBase = WdParseArgumentString(ArgumentString, "DMA");
|
||
|
||
if (physicalBase != 0) {
|
||
|
||
ConfigInfo->DmaChannel = physicalBase;
|
||
|
||
}
|
||
|
||
|
||
}
|
||
|
||
physicalBase = ScsiPortConvertPhysicalAddressToUlong(
|
||
(*ConfigInfo->AccessRanges)[0].RangeStart);
|
||
if (physicalBase != 0) {
|
||
|
||
baseSupplied = TRUE;
|
||
|
||
} else {
|
||
|
||
baseSupplied = FALSE;
|
||
physicalBase = SCSI_PHYSICAL_BASE;
|
||
}
|
||
|
||
//
|
||
// Get the system physical address for this card. The card uses I/O space.
|
||
//
|
||
|
||
base = ScsiPortConvertUlongToPhysicalAddress(physicalBase);
|
||
deviceExtension->Adapter = ScsiPortGetDeviceBase(
|
||
deviceExtension, // HwDeviceExtension
|
||
ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType
|
||
ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber
|
||
base,
|
||
sizeof(CARD_REGISTERS), // NumberOfBytes
|
||
TRUE // InIoSpace
|
||
);
|
||
|
||
if (deviceExtension->Adapter == NULL) {
|
||
WdPrint((0, "\nScsiPortInitialize: Failed to map SCSI device registers into system space.\n"));
|
||
return(SP_RETURN_ERROR);
|
||
}
|
||
|
||
//
|
||
// Determine if the card is really installed in the system. First read the
|
||
// status register.
|
||
//
|
||
|
||
*((PUCHAR) &scsiStatus) = SCSI_READ_AUX(
|
||
deviceExtension->Adapter,
|
||
AuxiliaryStatus
|
||
);
|
||
|
||
//
|
||
// Make sure the reserved bits are zero.
|
||
//
|
||
|
||
if (scsiStatus.Reserved != 0) {
|
||
|
||
//
|
||
// The card is not here so clean up.
|
||
//
|
||
|
||
ScsiPortFreeDeviceBase(
|
||
deviceExtension,
|
||
deviceExtension->Adapter
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Write and read the command phase register. This is not a good thing to
|
||
// do for card detection, but there does not appear to be any other way.
|
||
//
|
||
|
||
dataByte = PHASE_STATUS_STARTED;
|
||
SCSI_WRITE(deviceExtension->Adapter, CommandPhase, dataByte);
|
||
dataByte = SCSI_READ(deviceExtension->Adapter, CommandPhase);
|
||
if (dataByte != PHASE_STATUS_STARTED) {
|
||
|
||
//
|
||
// This is not our card so clean up.
|
||
//
|
||
|
||
ScsiPortFreeDeviceBase(
|
||
deviceExtension,
|
||
deviceExtension->Adapter
|
||
);
|
||
|
||
} else {
|
||
|
||
found = TRUE;
|
||
|
||
}
|
||
}
|
||
|
||
|
||
if (!found && !baseSupplied) {
|
||
|
||
#ifndef SCSI_SECOND_PHYSICAL_BASE
|
||
|
||
return(SP_RETURN_NOT_FOUND);
|
||
|
||
#else
|
||
//
|
||
// Try an alternet address.
|
||
// Get the system physical address for this card. The card uses I/O space.
|
||
//
|
||
|
||
base = ScsiPortConvertUlongToPhysicalAddress(SCSI_SECOND_PHYSICAL_BASE);
|
||
deviceExtension->Adapter = ScsiPortGetDeviceBase(
|
||
deviceExtension, // HwDeviceExtension
|
||
ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType
|
||
ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber
|
||
base,
|
||
sizeof(CARD_REGISTERS), // NumberOfBytes
|
||
TRUE // InIoSpace
|
||
);
|
||
|
||
if (deviceExtension->Adapter == NULL) {
|
||
WdPrint((0, "\nScsiPortInitialize: Failed to map SCSI device registers into system space.\n"));
|
||
return(SP_RETURN_ERROR);
|
||
}
|
||
|
||
//
|
||
// Determine if the card is really installed in the system. First read the
|
||
// status register.
|
||
//
|
||
|
||
*((PUCHAR) &scsiStatus) = SCSI_READ_AUX(
|
||
deviceExtension->Adapter,
|
||
AuxiliaryStatus
|
||
);
|
||
|
||
//
|
||
// Make sure the reserved bits are zero.
|
||
//
|
||
|
||
if (scsiStatus.Reserved != 0) {
|
||
|
||
//
|
||
// The card is not here so clean up.
|
||
//
|
||
|
||
ScsiPortFreeDeviceBase(
|
||
deviceExtension,
|
||
deviceExtension->Adapter
|
||
);
|
||
|
||
return(SP_RETURN_NOT_FOUND);
|
||
|
||
}
|
||
|
||
//
|
||
// Write and read the command phase register. This is not a good thing to
|
||
// do for card detection, but there does not appear to be any other way.
|
||
//
|
||
|
||
dataByte = PHASE_STATUS_STARTED;
|
||
SCSI_WRITE(deviceExtension->Adapter, CommandPhase, dataByte);
|
||
dataByte = SCSI_READ(deviceExtension->Adapter, CommandPhase);
|
||
if (dataByte != PHASE_STATUS_STARTED) {
|
||
|
||
//
|
||
// This is not our card so clean up.
|
||
//
|
||
|
||
ScsiPortFreeDeviceBase(
|
||
deviceExtension,
|
||
deviceExtension->Adapter
|
||
);
|
||
|
||
return(SP_RETURN_NOT_FOUND);
|
||
|
||
}
|
||
|
||
found = TRUE;
|
||
#endif
|
||
}
|
||
|
||
if (!found) {
|
||
return(SP_RETURN_NOT_FOUND);
|
||
}
|
||
|
||
//
|
||
// Set the defaults for any uninitialized values.
|
||
//
|
||
|
||
if (ConfigInfo->DmaChannel == SP_UNINITIALIZED_VALUE) {
|
||
|
||
ConfigInfo->DmaChannel = CARD_DMA_REQUEST;
|
||
}
|
||
|
||
if (ConfigInfo->BusInterruptLevel == 0 &&
|
||
ConfigInfo->BusInterruptVector == 0) {
|
||
|
||
ConfigInfo->BusInterruptLevel = SCSI_LEVEL;
|
||
ConfigInfo->BusInterruptVector = SCSI_VECTOR;
|
||
|
||
}
|
||
|
||
//
|
||
// Get the adapter object for this card.
|
||
//
|
||
|
||
ConfigInfo->MaximumTransferLength = 0x1000000;
|
||
ConfigInfo->DmaWidth = CARD_DMA_WIDTH;
|
||
ConfigInfo->DmaSpeed = CARD_DMA_SPEED;
|
||
ConfigInfo->NumberOfBuses = 1;
|
||
|
||
//
|
||
// Translate the DMA Channel values.
|
||
//
|
||
|
||
switch (ConfigInfo->DmaChannel) {
|
||
case 5:
|
||
deviceExtension->DmaCode = 1;
|
||
break;
|
||
|
||
case 6:
|
||
deviceExtension->DmaCode = 2;
|
||
break;
|
||
|
||
case 7:
|
||
deviceExtension->DmaCode = 3;
|
||
break;
|
||
|
||
default:
|
||
return(SP_RETURN_BAD_CONFIG);
|
||
}
|
||
|
||
//
|
||
// Translate interrupt level values.
|
||
//
|
||
|
||
switch (ConfigInfo->BusInterruptLevel) {
|
||
case 3:
|
||
deviceExtension->IrqCode = 1;
|
||
break;
|
||
|
||
case 4:
|
||
deviceExtension->IrqCode = 2;
|
||
break;
|
||
|
||
case 5:
|
||
deviceExtension->IrqCode = 3;
|
||
break;
|
||
|
||
case 10:
|
||
deviceExtension->IrqCode = 4;
|
||
break;
|
||
|
||
case 11:
|
||
deviceExtension->IrqCode = 5;
|
||
break;
|
||
|
||
case 12:
|
||
deviceExtension->IrqCode = 6;
|
||
break;
|
||
|
||
case 15:
|
||
deviceExtension->IrqCode = 7;
|
||
break;
|
||
|
||
default:
|
||
return(SP_RETURN_BAD_CONFIG);
|
||
|
||
}
|
||
|
||
//
|
||
// Get the SCSI bus Id from the configuration information if there
|
||
// is any.
|
||
//
|
||
|
||
if (ConfigInfo->InitiatorBusId[0] == (CCHAR) SP_UNINITIALIZED_VALUE) {
|
||
ConfigInfo->InitiatorBusId[0] = INITIATOR_BUS_ID;
|
||
deviceExtension->InitiatorBusId = INITIATOR_BUS_ID;
|
||
} else {
|
||
deviceExtension->InitiatorBusId = ConfigInfo->InitiatorBusId[0];
|
||
}
|
||
|
||
//
|
||
// Fill in the access array information.
|
||
//
|
||
|
||
(*ConfigInfo->AccessRanges)[0].RangeStart = base;
|
||
(*ConfigInfo->AccessRanges)[0].RangeLength = sizeof(CARD_REGISTERS);
|
||
(*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
|
||
|
||
return(SP_RETURN_FOUND);
|
||
}
|
||
|
||
ULONG
|
||
WdParseArgumentString(
|
||
IN PCHAR String,
|
||
IN PCHAR KeyWord
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will parse the string for a match on the keyword, then
|
||
calculate the value for the keyword and return it to the caller.
|
||
|
||
Arguments:
|
||
|
||
String - The ASCII string to parse.
|
||
KeyWord - The keyword for the value desired.
|
||
|
||
Return Values:
|
||
|
||
Zero if value not found
|
||
Value converted from ASCII to binary.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCHAR cptr;
|
||
PCHAR kptr;
|
||
ULONG value;
|
||
ULONG stringLength = 0;
|
||
ULONG keyWordLength = 0;
|
||
ULONG index;
|
||
|
||
//
|
||
// Calculate the string length and lower case all characters.
|
||
//
|
||
cptr = String;
|
||
while (*cptr) {
|
||
|
||
if (*cptr >= 'A' && *cptr <= 'Z') {
|
||
*cptr = *cptr + ('a' - 'A');
|
||
}
|
||
cptr++;
|
||
stringLength++;
|
||
}
|
||
|
||
//
|
||
// Calculate the keyword length and lower case all characters.
|
||
//
|
||
cptr = KeyWord;
|
||
while (*cptr) {
|
||
|
||
if (*cptr >= 'A' && *cptr <= 'Z') {
|
||
*cptr = *cptr + ('a' - 'A');
|
||
}
|
||
cptr++;
|
||
keyWordLength++;
|
||
}
|
||
|
||
if (keyWordLength > stringLength) {
|
||
|
||
//
|
||
// Can't possibly have a match.
|
||
//
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// Now setup and start the compare.
|
||
//
|
||
cptr = String;
|
||
|
||
ContinueSearch:
|
||
//
|
||
// The input string may start with white space. Skip it.
|
||
//
|
||
while (*cptr == ' ' || *cptr == '\t') {
|
||
cptr++;
|
||
}
|
||
|
||
if (*cptr == '\0') {
|
||
|
||
//
|
||
// end of string.
|
||
//
|
||
return 0;
|
||
}
|
||
|
||
kptr = KeyWord;
|
||
while (*cptr++ == *kptr++) {
|
||
|
||
if (*(cptr - 1) == '\0') {
|
||
|
||
//
|
||
// end of string
|
||
//
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
if (*(kptr - 1) == '\0') {
|
||
|
||
//
|
||
// May have a match backup and check for blank or equals.
|
||
//
|
||
|
||
cptr--;
|
||
while (*cptr == ' ' || *cptr == '\t') {
|
||
cptr++;
|
||
}
|
||
|
||
//
|
||
// Found a match. Make sure there is an equals.
|
||
//
|
||
if (*cptr != '=') {
|
||
|
||
//
|
||
// Not a match so move to the next semicolon.
|
||
//
|
||
while (*cptr) {
|
||
if (*cptr++ == ';') {
|
||
goto ContinueSearch;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
// Skip the equals sign.
|
||
//
|
||
cptr++;
|
||
|
||
//
|
||
// Skip white space.
|
||
//
|
||
while ((*cptr == ' ') || (*cptr == '\t')) {
|
||
cptr++;
|
||
}
|
||
|
||
if (*cptr == '\0') {
|
||
|
||
//
|
||
// Early end of string, return not found
|
||
//
|
||
return 0;
|
||
}
|
||
|
||
if (*cptr == ';') {
|
||
|
||
//
|
||
// This isn't it either.
|
||
//
|
||
cptr++;
|
||
goto ContinueSearch;
|
||
}
|
||
|
||
value = 0;
|
||
if ((*cptr == '0') && (*(cptr + 1) == 'x')) {
|
||
|
||
//
|
||
// Value is in Hex. Skip the "0x"
|
||
//
|
||
cptr += 2;
|
||
for (index = 0; *(cptr + index); index++) {
|
||
|
||
if (*(cptr + index) == ' ' ||
|
||
*(cptr + index) == '\t' ||
|
||
*(cptr + index) == ';') {
|
||
break;
|
||
}
|
||
|
||
if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) {
|
||
value = (16 * value) + (*(cptr + index) - '0');
|
||
} else {
|
||
if ((*(cptr + index) >= 'a') && (*(cptr + index) <= 'f')) {
|
||
value = (16 * value) + (*(cptr + index) - 'a' + 10);
|
||
} else {
|
||
|
||
//
|
||
// Syntax error, return not found.
|
||
//
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Value is in Decimal.
|
||
//
|
||
for (index = 0; *(cptr + index); index++) {
|
||
|
||
if (*(cptr + index) == ' ' ||
|
||
*(cptr + index) == '\t' ||
|
||
*(cptr + index) == ';') {
|
||
break;
|
||
}
|
||
|
||
if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) {
|
||
value = (10 * value) + (*(cptr + index) - '0');
|
||
} else {
|
||
|
||
//
|
||
// Syntax error return not found.
|
||
//
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
return value;
|
||
} else {
|
||
|
||
//
|
||
// Not a match check for ';' to continue search.
|
||
//
|
||
while (*cptr) {
|
||
if (*cptr++ == ';') {
|
||
goto ContinueSearch;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
}
|
||
|