5617 lines
166 KiB
C
5617 lines
166 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ncr53c9x.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the NCR 53c9x specific functions for the NT SCSI port
|
||
driver.
|
||
|
||
Author:
|
||
|
||
Jeff Havens (jhavens) 28-Feb-1991
|
||
|
||
Environment:
|
||
|
||
Kernel Mode only
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "miniport.h"
|
||
#include "scsi.h"
|
||
|
||
#include "ncr53c9x.h"
|
||
#include "mcadefs.h"
|
||
#include "string.h"
|
||
|
||
#include "jazzdef.h"
|
||
#include "jazzdma.h"
|
||
|
||
#if DBG
|
||
int NcrDebug;
|
||
#define NcrPrint(arg) ScsiDebugPrint arg
|
||
#else
|
||
#define NcrPrint(arg)
|
||
#endif
|
||
|
||
//
|
||
// Define SCSI Protocol Chip configuration parameters.
|
||
//
|
||
|
||
#define INITIATOR_BUS_ID 0x7
|
||
#define SELECT_TIMEOUT_FACTOR 33
|
||
#define SYNCHRONOUS_OFFSET 0x0f
|
||
#define ASYNCHRONOUS_OFFSET 0
|
||
#define ASYNCHRONOUS_PERIOD 0x05
|
||
#define RESET_STALL_TIME 25 // The minimum assertion time for
|
||
// a SCSI bus reset.
|
||
#define INTERRUPT_STALL_TIME 5 // Time to wait for the next interrupt.
|
||
|
||
|
||
//
|
||
// NCR 53c9x 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_PENDING_DATA_TRANSFER 0x0080
|
||
#define PD_PARITY_ERROR_LOGGED 0x0100
|
||
#define PD_EXPECTING_RESET_INTERRUPT 0x0200
|
||
#define PD_EXPECTING_QUEUE_TAG 0x0400
|
||
#define PD_TAGGED_SELECT 0x0800
|
||
#define PD_NCR_ADAPTER 0x1000
|
||
|
||
//
|
||
// 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_PENDING_DATA_TRANSFER | \
|
||
PD_PARITY_ERROR_LOGGED | \
|
||
PD_EXPECTING_QUEUE_TAG | \
|
||
PD_TAGGED_SELECT | \
|
||
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_PENDING_DATA_TRANSFER | \
|
||
PD_PARITY_ERROR_LOGGED | \
|
||
PD_EXPECTING_QUEUE_TAG | \
|
||
PD_TAGGED_SELECT | \
|
||
PD_DISCONNECT_EXPECTED \
|
||
)
|
||
|
||
//
|
||
// The largest SCSI bus message expected.
|
||
//
|
||
|
||
#define MESSAGE_BUFFER_SIZE 8
|
||
|
||
//
|
||
// 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,
|
||
AttemptingSelect,
|
||
CommandComplete,
|
||
CommandOut,
|
||
DataTransfer,
|
||
DisconnectExpected,
|
||
MessageAccepted,
|
||
MessageIn,
|
||
MessageOut,
|
||
Reselected
|
||
} ADAPTER_STATE, *PADAPTER_STATE;
|
||
|
||
//
|
||
// Define the types of chips this driver will support.
|
||
//
|
||
|
||
typedef enum _CHIP_TYPES {
|
||
Ncr53c90,
|
||
Ncr53c94,
|
||
Fas216,
|
||
Fas216Fast
|
||
}CHIP_TYPES, *PCHIP_TYPES;
|
||
|
||
//
|
||
// NCR 53c9x specific port driver logical unit flags.
|
||
//
|
||
|
||
#define PD_STATUS_VALID 0x0004
|
||
#define PD_DO_NOT_CHECK_TRANSFER_LENGTH 0x0008
|
||
#define PD_INITIATE_RECOVERY 0x0010
|
||
#define PD_QUEUED_COMMANDS_EXECUTING 0x0020
|
||
|
||
//
|
||
// 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_STATUS_VALID | \
|
||
PD_DO_NOT_CHECK_TRANSFER_LENGTH | \
|
||
PD_QUEUED_COMMANDS_EXECUTING | \
|
||
PD_INITIATE_RECOVERY \
|
||
)
|
||
|
||
//
|
||
// NCR 53c9x specific port driver SRB extension.
|
||
//
|
||
|
||
typedef struct _SRB_EXTENSION {
|
||
ULONG SrbExtensionFlags;
|
||
ULONG SavedDataPointer;
|
||
ULONG SavedDataLength;
|
||
ULONG MaximumTransferLength;
|
||
}SRB_EXTENSION, *PSRB_EXTENSION;
|
||
|
||
#define SRB_EXT(x) ((PSRB_EXTENSION)(x->SrbExtension))
|
||
|
||
//
|
||
// NCR 53c9x specific port driver logical unit extension.
|
||
//
|
||
|
||
typedef struct _SPECIFIC_LOGICAL_UNIT_EXTENSION {
|
||
USHORT LuFlags;
|
||
UCHAR RetryCount;
|
||
ULONG SavedDataPointer;
|
||
ULONG SavedDataLength;
|
||
PSCSI_REQUEST_BLOCK ActiveLuRequest;
|
||
PSCSI_REQUEST_BLOCK ActiveSendRequest;
|
||
}SPECIFIC_LOGICAL_UNIT_EXTENSION, *PSPECIFIC_LOGICAL_UNIT_EXTENSION;
|
||
|
||
//
|
||
// NCR 53c9x specific per target controller information.
|
||
//
|
||
|
||
typedef struct _SPECIFIC_TARGET_EXTENSION {
|
||
UCHAR TargetFlags;
|
||
UCHAR SynchronousPeriod;
|
||
UCHAR SynchronousOffset;
|
||
SCSI_CONFIGURATION3 Configuration3;
|
||
}SPECIFIC_TARGET_EXTENSION, *PSPECIFIC_TARGET_EXTENSION;
|
||
|
||
//
|
||
// Define target controller specific flags.
|
||
//
|
||
|
||
#define PD_SYNCHRONOUS_NEGOTIATION_DONE 0x0001
|
||
#define PD_DO_NOT_NEGOTIATE 0x0002
|
||
|
||
//
|
||
// NCR 53c9x specific port driver device object extension.
|
||
//
|
||
|
||
typedef struct _SPECIFIC_DEVICE_EXTENSION {
|
||
ULONG AdapterFlags;
|
||
ADAPTER_STATE AdapterState; // Current state of the adapter
|
||
PADAPTER_REGISTERS AdapterBase; // Address of the NCR 53c9x adapter
|
||
PSCSI_REGISTERS Adapter; // Address of the NCR 53c9x chip
|
||
PSCSI_REQUEST_BLOCK ActiveLuRequest;
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION ActiveLogicalUnit;
|
||
// Pointer to the acitive request.
|
||
PSCSI_REQUEST_BLOCK NextSrbRequest; // Pointer to the next SRB to process.
|
||
ULONG ActiveDataPointer; // SCSI bus active data pointer
|
||
ULONG ActiveDataLength; // The amount of data to be transferred.
|
||
LONG InterruptCount; // Count of interrupts since connection.
|
||
SPECIFIC_TARGET_EXTENSION TargetState[SCSI_MAXIMUM_TARGETS];
|
||
CHIP_TYPES ChipType; // Type or version of the chip.
|
||
SCSI_STATUS AdapterStatus; // Saved status register value
|
||
SCSI_SEQUENCE_STEP SequenceStep; // Saved sequence step register value
|
||
SCSI_INTERRUPT AdapterInterrupt; // Saved interrupt status register
|
||
SCSI_CONFIGURATION3 Configuration3;
|
||
UCHAR AdapterBusId; // This adapter's SCSI bus ID
|
||
UCHAR AdapterBusIdMask; // This adapter's SCSI bus ID bit mask
|
||
UCHAR ClockSpeed; // Chip clock speed in megahetrz.
|
||
BOOLEAN InterruptPending; // Interrupt pending indicator
|
||
UCHAR MessageBuffer[MESSAGE_BUFFER_SIZE]; // SCSI bus message buffer
|
||
UCHAR MessageCount; // Count of bytes in message buffer
|
||
UCHAR MessageSent; // Count of bytes sent to target
|
||
UCHAR TargetId; // Saved target id.
|
||
UCHAR Lun; // Saved lun id.
|
||
UCHAR ErrorCount;
|
||
} SPECIFIC_DEVICE_EXTENSION, *PSPECIFIC_DEVICE_EXTENSION;
|
||
|
||
|
||
//
|
||
// Define the synchrouns data transfer parameters structure.
|
||
//
|
||
|
||
typedef struct _SYNCHRONOUS_TYPE_PARAMETERS {
|
||
UCHAR MaximumPeriodCyles;
|
||
UCHAR SynchronousPeriodCyles;
|
||
UCHAR InitialRegisterValue;
|
||
}SYNCHRONOUS_TYPE_PARAMETERS, *PSYNCHRONOUS_TYPE_PARAMETERS;
|
||
|
||
//
|
||
// Define the table of synchronous transfer types.
|
||
//
|
||
|
||
const SYNCHRONOUS_TYPE_PARAMETERS SynchronousTransferTypes[] = {
|
||
{ 0, 0, 5},
|
||
{ 32, 5, 5},
|
||
{ 32, 8, 7},
|
||
{ 12, 4, 4}
|
||
};
|
||
|
||
//
|
||
// SCSI Protocol Chip Control read and write macros.
|
||
//
|
||
|
||
#ifdef SCSI_READ
|
||
#undef SCSI_READ
|
||
#undef SCSI_WRITE
|
||
#endif
|
||
|
||
#if defined(DECSTATION)
|
||
|
||
#define SCSI_READ(ChipAddr, Register) ScsiPortReadRegisterUchar(&((ChipAddr)->ReadRegisters.Register.Byte))
|
||
|
||
#define SCSI_WRITE(ChipAddr, Register, Value) ScsiPortWriteRegisterUchar(&((ChipAddr)->WriteRegisters.Register.Byte), (Value))
|
||
|
||
#else
|
||
|
||
#define SCSI_READ(ChipAddr, Register) ScsiPortReadPortUchar(&((ChipAddr)->ReadRegisters.Register))
|
||
#define SCSI_WRITE(ChipAddr, Register, Value) (ScsiPortWritePortUchar(&((ChipAddr)->WriteRegisters.Register), (Value)))
|
||
|
||
#endif
|
||
|
||
|
||
//
|
||
// Functions passed to the OS-specific port driver.
|
||
//
|
||
|
||
ULONG
|
||
NcrEisaFindAdapter(
|
||
IN PVOID ServiceContext,
|
||
IN PVOID Context,
|
||
IN PVOID BusInformation,
|
||
IN PCHAR ArgumentString,
|
||
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
||
OUT PBOOLEAN Again
|
||
);
|
||
|
||
ULONG
|
||
NcrMcaFindAdapter(
|
||
IN PVOID ServiceContext,
|
||
IN PVOID Context,
|
||
IN PVOID BusInformation,
|
||
IN PCHAR ArgumentString,
|
||
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
||
OUT PBOOLEAN Again
|
||
);
|
||
|
||
ULONG
|
||
NcrMipsFindAdapter(
|
||
IN PVOID ServiceContext,
|
||
IN PVOID Context,
|
||
IN PVOID BusInformation,
|
||
IN PCHAR ArgumentString,
|
||
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
||
OUT PBOOLEAN Again
|
||
);
|
||
|
||
BOOLEAN
|
||
NcrInitializeAdapter(
|
||
IN PVOID ServiceContext
|
||
);
|
||
|
||
BOOLEAN
|
||
NcrInterruptServiceRoutine(
|
||
IN PVOID ServiceContext
|
||
);
|
||
|
||
BOOLEAN
|
||
NcrResetScsiBus(
|
||
IN PVOID ServiceContext,
|
||
IN ULONG PathId
|
||
);
|
||
|
||
BOOLEAN
|
||
NcrStartIo(
|
||
IN PVOID ServiceContext,
|
||
IN PSCSI_REQUEST_BLOCK Srb
|
||
);
|
||
|
||
VOID
|
||
NcrStartDataTransfer(
|
||
IN PVOID ServiceContext
|
||
);
|
||
|
||
//
|
||
// NCR 53c9x internal mini-port driver functions.
|
||
//
|
||
|
||
VOID
|
||
NcrAcceptMessage(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN BOOLEAN SetAttention,
|
||
IN BOOLEAN SetSynchronousParameters
|
||
);
|
||
|
||
VOID
|
||
NcrCleanupAfterReset(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN BOOLEAN ExternalReset
|
||
);
|
||
|
||
VOID
|
||
NcrCompleteSendMessage(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN UCHAR SrbStatus
|
||
);
|
||
|
||
BOOLEAN
|
||
NcrDecodeSynchronousRequest(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN PSPECIFIC_TARGET_EXTENSION TargetState,
|
||
IN BOOLEAN ResponseExpected
|
||
);
|
||
|
||
VOID
|
||
NcrDumpState(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
|
||
);
|
||
|
||
BOOLEAN
|
||
NcrMessageDecode(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
|
||
);
|
||
|
||
VOID
|
||
NcrLogError(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN ULONG ErrorCode,
|
||
IN ULONG UniqueCode
|
||
);
|
||
|
||
VOID
|
||
NcrProcessRequestCompletion(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
|
||
);
|
||
|
||
VOID
|
||
NcrResetScsiBusInternal(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN ULONG PathId
|
||
);
|
||
|
||
VOID
|
||
NcrSelectTarget(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension
|
||
);
|
||
|
||
VOID
|
||
NcrSendMessage(
|
||
IN PSCSI_REQUEST_BLOCK Srb,
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension
|
||
);
|
||
|
||
VOID
|
||
NcrStartExecution(
|
||
PSCSI_REQUEST_BLOCK Srb,
|
||
PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION LuExtension
|
||
);
|
||
|
||
|
||
VOID
|
||
NcrAcceptMessage(
|
||
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_TARGET_EXTENSION targetState;
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
targetState = &DeviceExtension->TargetState[DeviceExtension->TargetId];
|
||
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
SynchronousPeriod,
|
||
targetState->SynchronousPeriod
|
||
);
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
SynchronousOffset,
|
||
targetState->SynchronousOffset
|
||
);
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
Configuration3,
|
||
*((PUCHAR) &targetState->Configuration3)
|
||
);
|
||
}
|
||
|
||
//
|
||
// Check to see if the attention signal needs to be set.
|
||
//
|
||
|
||
if (SetAttention) {
|
||
|
||
//
|
||
// This requests that the target enter the message-out phase.
|
||
//
|
||
|
||
SCSI_WRITE( DeviceExtension->Adapter, Command, SET_ATTENTION );
|
||
}
|
||
|
||
//
|
||
// Indicate to the adapter that the message-in phase may now be completed.
|
||
//
|
||
|
||
SCSI_WRITE(DeviceExtension->Adapter, Command, MESSAGE_ACCEPTED);
|
||
}
|
||
|
||
|
||
VOID
|
||
NcrCleanupAfterReset(
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
UCHAR pathId = 0;
|
||
UCHAR targetId;
|
||
UCHAR luId;
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension;
|
||
PSPECIFIC_TARGET_EXTENSION targetState;
|
||
|
||
//
|
||
// Check to see if a data transfer was in progress, if so, flush the DMA.
|
||
//
|
||
|
||
if (DeviceExtension->AdapterState == DataTransfer) {
|
||
|
||
SCSI_WRITE(DeviceExtension->Adapter, Command, FLUSH_FIFO);
|
||
ScsiPortFlushDma(DeviceExtension);
|
||
|
||
}
|
||
|
||
//
|
||
// if the current state is AttemptingSelect 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 request of the active requests
|
||
// are returned.
|
||
|
||
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->ActiveLuRequest != NULL) {
|
||
|
||
//
|
||
// Set the SrbStatus in the SRB, complete the request and
|
||
// clear the active pointers
|
||
//
|
||
|
||
luExtension = DeviceExtension->ActiveLogicalUnit;
|
||
|
||
DeviceExtension->ActiveLuRequest->SrbStatus =
|
||
SRB_STATUS_PHASE_SEQUENCE_FAILURE;
|
||
|
||
targetState = &DeviceExtension->TargetState[DeviceExtension->TargetId];
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
DeviceExtension->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)) {
|
||
|
||
//
|
||
// This target cannot negotiate properly. Set a flag to prevent
|
||
// further attempts and set the synchronous parameters to use
|
||
// asynchronous data transfer.
|
||
//
|
||
|
||
targetState->TargetFlags |= PD_DO_NOT_NEGOTIATE;
|
||
targetState->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
targetState->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
targetState->Configuration3.FastScsi = 0;
|
||
|
||
}
|
||
|
||
luExtension->ActiveLuRequest = NULL;
|
||
luExtension->RetryCount = 0;
|
||
DeviceExtension->ActiveLogicalUnit = NULL;
|
||
DeviceExtension->ActiveLuRequest = 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.
|
||
//
|
||
|
||
//
|
||
// Clear the synchronous negotiation flage for the target controller.
|
||
//
|
||
|
||
DeviceExtension->TargetState[targetId].TargetFlags &=
|
||
~PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
|
||
for (luId = 0; luId < SCSI_MAXIMUM_LOGICAL_UNITS; luId++) {
|
||
|
||
luExtension = ScsiPortGetLogicalUnit( DeviceExtension,
|
||
pathId,
|
||
targetId,
|
||
luId
|
||
);
|
||
|
||
if (luExtension == NULL) {
|
||
continue;
|
||
}
|
||
|
||
ScsiPortCompleteRequest(
|
||
DeviceExtension,
|
||
pathId,
|
||
targetId,
|
||
luId,
|
||
SRB_STATUS_BUS_RESET
|
||
);
|
||
|
||
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;
|
||
|
||
} /* for luId */
|
||
} /* for targetId */
|
||
|
||
//
|
||
// Clear the adapter flags and set the bus state to free.
|
||
//
|
||
|
||
DeviceExtension->AdapterState = BusFree;
|
||
DeviceExtension->AdapterFlags &= ~PD_ADAPTER_RESET_MASK;
|
||
|
||
}
|
||
|
||
VOID
|
||
NcrCompleteSendMessage(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN UCHAR 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 completeted 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;
|
||
PSCSI_REQUEST_BLOCK srbAbort;
|
||
UCHAR pathId = 0;
|
||
UCHAR targetId;
|
||
UCHAR 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.
|
||
//
|
||
|
||
srbAbort = ScsiPortGetSrb(
|
||
DeviceExtension,
|
||
srb->PathId,
|
||
srb->TargetId,
|
||
srb->Lun,
|
||
srb->QueueTag
|
||
);
|
||
|
||
if (srbAbort != srb->NextSrb) {
|
||
|
||
//
|
||
// If there is no request, then fail the abort.
|
||
//
|
||
|
||
SrbStatus = SRB_STATUS_ABORT_FAILED;
|
||
break;
|
||
}
|
||
|
||
srbAbort->SrbStatus = SRB_STATUS_ABORTED;
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
srbAbort
|
||
);
|
||
|
||
if (DeviceExtension->ActiveLuRequest == srbAbort) {
|
||
|
||
DeviceExtension->ActiveLuRequest = NULL;
|
||
}
|
||
|
||
luExtension->ActiveLuRequest = NULL;
|
||
luExtension->LuFlags &= ~PD_LU_COMPLETE_MASK;
|
||
luExtension->RetryCount = 0;
|
||
|
||
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;
|
||
DeviceExtension->TargetState[targetId].TargetFlags &=
|
||
~PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
|
||
for (luId = 0; luId < SCSI_MAXIMUM_LOGICAL_UNITS; luId) {
|
||
|
||
luExtension = ScsiPortGetLogicalUnit( DeviceExtension,
|
||
pathId,
|
||
targetId,
|
||
luId
|
||
);
|
||
|
||
if (luExtension == NULL) {
|
||
continue;
|
||
}
|
||
|
||
ScsiPortCompleteRequest(
|
||
DeviceExtension,
|
||
pathId,
|
||
targetId,
|
||
luId,
|
||
SRB_STATUS_BUS_RESET
|
||
);
|
||
|
||
luExtension->ActiveLuRequest = NULL;
|
||
luExtension->RetryCount = 0;
|
||
|
||
//
|
||
// Clear the necessary logical unit flags.
|
||
//
|
||
|
||
luExtension->LuFlags &= ~PD_LU_RESET_MASK;
|
||
|
||
} /* for luId */
|
||
|
||
/* TODO: Handle CLEAR QUEUE */
|
||
}
|
||
} 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.
|
||
//
|
||
|
||
srbAbort = ScsiPortGetSrb(
|
||
DeviceExtension,
|
||
srb->PathId,
|
||
srb->TargetId,
|
||
srb->Lun,
|
||
srb->QueueTag
|
||
);
|
||
|
||
if (srbAbort == srb->NextSrb) {
|
||
|
||
srbAbort->SrbStatus = SRB_STATUS_ABORTED;
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
srbAbort
|
||
);
|
||
|
||
luExtension->ActiveLuRequest = NULL;
|
||
luExtension->LuFlags &= ~PD_LU_COMPLETE_MASK;
|
||
luExtension->RetryCount = 0;
|
||
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Complete the actual send-message request.
|
||
//
|
||
|
||
srb->SrbStatus = SrbStatus;
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
srb
|
||
);
|
||
|
||
//
|
||
// Clear the active send request and PD_SEND_MESSAGE_REQUEST flag.
|
||
//
|
||
|
||
luExtension->RetryCount = 0;
|
||
luExtension->ActiveSendRequest = NULL;
|
||
DeviceExtension->AdapterFlags &= ~PD_SEND_MESSAGE_REQUEST;
|
||
}
|
||
|
||
BOOLEAN
|
||
NcrMessageDecode(
|
||
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 several 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;
|
||
PSPECIFIC_TARGET_EXTENSION targetState;
|
||
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;
|
||
srb = DeviceExtension->ActiveLuRequest;
|
||
targetState = &DeviceExtension->TargetState[DeviceExtension->TargetId];
|
||
|
||
//
|
||
// If a queue message is expected then it must be the first message byte.
|
||
//
|
||
|
||
if (DeviceExtension->AdapterFlags & PD_EXPECTING_QUEUE_TAG &&
|
||
DeviceExtension->MessageBuffer[0] != SRB_SIMPLE_TAG_REQUEST) {
|
||
|
||
NcrPrint((1, "NcrMessageDecode: Unexpected message recieved when que tag expected.\n"));
|
||
|
||
//
|
||
// The target did not reselect correctly Send a
|
||
// message reject of this message.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// 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 implie 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);
|
||
}
|
||
|
||
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;
|
||
targetState->TargetFlags |= 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;
|
||
targetState->TargetFlags |= PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
}
|
||
|
||
}
|
||
|
||
switch (DeviceExtension->MessageBuffer[0]) {
|
||
case SCSIMESS_COMMAND_COMPLETE:
|
||
|
||
//
|
||
// For better or worse the command is complete. Process request which
|
||
// set the SrbStatus and clean up the device and logical unit states.
|
||
//
|
||
|
||
NcrProcessRequestCompletion(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->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->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 < MESSAGE_BUFFER_SIZE &&
|
||
DeviceExtension->MessageCount < 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, so 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 NcrDecodeSynchronousMessage to decode the message and
|
||
// formulate a response if necessary.
|
||
// NcrDecodeSynchronousRequest will return FALSE if the
|
||
// message is not accepable and should be rejected.
|
||
//
|
||
|
||
if (!NcrDecodeSynchronousRequest(
|
||
DeviceExtension,
|
||
targetState,
|
||
(BOOLEAN)(!(savedAdapterFlags & PD_SYNCHRONOUS_TRANSFER_SENT))
|
||
)) {
|
||
|
||
//
|
||
// Indicate that a negotiation has been done in the logical
|
||
// unit and clear the negotiation flags.
|
||
//
|
||
|
||
targetState->TargetFlags |= 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, NcrDecodeSynchronousRequest 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;
|
||
targetState->TargetFlags |= 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.
|
||
//
|
||
|
||
SRB_EXT(srb)->SrbExtensionFlags |= 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);
|
||
}
|
||
|
||
NcrProcessRequestCompletion(DeviceExtension);
|
||
|
||
DeviceExtension->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);
|
||
}
|
||
|
||
NcrProcessRequestCompletion(DeviceExtension);
|
||
|
||
DeviceExtension->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.
|
||
//
|
||
|
||
/* TODO: Handle message reject correctly. */
|
||
if (DeviceExtension->AdapterFlags & PD_SEND_MESSAGE_REQUEST) {
|
||
|
||
//
|
||
// Complete the request with message rejected status.
|
||
//
|
||
|
||
NcrCompleteSendMessage(
|
||
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.
|
||
//
|
||
|
||
targetState->TargetFlags |= PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
targetState->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
targetState->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
targetState->Configuration3.FastScsi = 0;
|
||
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.
|
||
//
|
||
|
||
targetState->TargetFlags |= PD_DO_NOT_NEGOTIATE;
|
||
|
||
}
|
||
|
||
DeviceExtension->AdapterState = MessageAccepted;
|
||
DeviceExtension->MessageCount = 0;
|
||
return(FALSE);
|
||
|
||
case SCSIMESS_SIMPLE_QUEUE_TAG:
|
||
case SCSIMESS_ORDERED_QUEUE_TAG:
|
||
case SCSIMESS_HEAD_OF_QUEUE_TAG:
|
||
|
||
//
|
||
// A queue tag message was recieve. If this is the first byte just
|
||
// accept the message and wait for the next one.
|
||
//
|
||
|
||
if (DeviceExtension->MessageCount < 2) {
|
||
|
||
DeviceExtension->AdapterState = MessageAccepted;
|
||
return(FALSE);
|
||
|
||
}
|
||
|
||
//
|
||
// Make sure that a queue tag message is expected.
|
||
//
|
||
|
||
if (!(DeviceExtension->AdapterFlags & PD_EXPECTING_QUEUE_TAG) ||
|
||
luExtension == NULL) {
|
||
|
||
NcrPrint((1, "NcrMessageDecode: Unexpected queue tag message recieved\n"));
|
||
|
||
//
|
||
// Something is messed up. Reject the message.
|
||
//
|
||
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
NcrLogError( DeviceExtension, SP_PROTOCOL_ERROR, 17);
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
//
|
||
// The second byte contains the tag used to locate the srb.
|
||
//
|
||
|
||
srb = ScsiPortGetSrb(
|
||
DeviceExtension,
|
||
0,
|
||
DeviceExtension->TargetId,
|
||
DeviceExtension->Lun,
|
||
DeviceExtension->MessageBuffer[1]
|
||
);
|
||
|
||
if (srb == NULL) {
|
||
|
||
NcrPrint((1, "NcrMessageDecode: Invalid queue tag recieved\n"));
|
||
|
||
//
|
||
// Something is messed up. Reject the message.
|
||
//
|
||
|
||
DeviceExtension->AdapterFlags &= ~PD_EXPECTING_QUEUE_TAG;
|
||
DeviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED;
|
||
DeviceExtension->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_MESSAGE_REJECT;
|
||
DeviceExtension->AdapterState = MessageOut;
|
||
|
||
NcrLogError( DeviceExtension, SP_PROTOCOL_ERROR, 16);
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
//
|
||
// Everthing is ok. Set up the device extension and accept the message.
|
||
// Restore the data pointers.
|
||
//
|
||
|
||
DeviceExtension->ActiveLuRequest = srb;
|
||
DeviceExtension->ActiveDataPointer = SRB_EXT(srb)->SavedDataPointer;
|
||
DeviceExtension->ActiveDataLength = SRB_EXT(srb)->SavedDataLength;
|
||
DeviceExtension->AdapterFlags &= ~PD_EXPECTING_QUEUE_TAG;
|
||
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 = SRB_EXT(srb)->SavedDataPointer;
|
||
DeviceExtension->ActiveDataLength = SRB_EXT(srb)->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.
|
||
//
|
||
|
||
SRB_EXT(srb)->SavedDataPointer = DeviceExtension->ActiveDataPointer;
|
||
SRB_EXT(srb)->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
|
||
NcrDecodeSynchronousRequest(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
PSPECIFIC_TARGET_EXTENSION TargetState,
|
||
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 NCR 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.
|
||
|
||
TargetState - Supplies a pointer to the target controller's state.
|
||
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;
|
||
CHIP_TYPES chipType;
|
||
LONG period;
|
||
ULONG localPeriod;
|
||
ULONG step;
|
||
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.
|
||
//
|
||
|
||
TargetState->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
TargetState->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
TargetState->Configuration3.FastScsi = 0;
|
||
return(FALSE);
|
||
}
|
||
|
||
extendedMessage->ExtendedArguments.Synchronous.ReqAckOffset = SYNCHRONOUS_OFFSET;
|
||
TargetState->SynchronousOffset = SYNCHRONOUS_OFFSET;
|
||
|
||
} else {
|
||
|
||
TargetState->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) {
|
||
TargetState->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
TargetState->Configuration3.FastScsi = 0;
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// Calculate the period in nanoseconds from the message.
|
||
//
|
||
|
||
period = extendedMessage->ExtendedArguments.Synchronous.TransferPeriod;
|
||
|
||
NcrPrint((1, "NcrDecodeSynchronousRequest: Requested period %d, ", period));
|
||
|
||
//
|
||
// If the chip supports fast SCSI and the requested period is faster than
|
||
// 200 ns then assume fast SCSI.
|
||
//
|
||
|
||
if (DeviceExtension->ChipType == Fas216 && period < 200 / 4) {
|
||
|
||
chipType = Fas216Fast;
|
||
|
||
//
|
||
// Set the fast SCSI bit in the configuration register.
|
||
//
|
||
|
||
TargetState->Configuration3.FastScsi = 1;
|
||
|
||
} else {
|
||
chipType = DeviceExtension->ChipType;
|
||
}
|
||
|
||
//
|
||
// The initial sychronous transfer period is:
|
||
//
|
||
// SynchronousPeriodCyles * 1000
|
||
// -----------------------------
|
||
// ClockSpeed * 4
|
||
//
|
||
// Note the result of the divide by four must be rounded up.
|
||
//
|
||
|
||
localPeriod = ((SynchronousTransferTypes[chipType].SynchronousPeriodCyles
|
||
* 1000) / DeviceExtension->ClockSpeed + 3) / 4;
|
||
|
||
//
|
||
// 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 ((ULONG) period < localPeriod ) {
|
||
|
||
if (!ResponseExpected) {
|
||
|
||
//
|
||
// The negotiation failed for some reason; fall back to
|
||
// asynchronous data transfer.
|
||
//
|
||
|
||
TargetState->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
TargetState->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
TargetState->Configuration3.FastScsi = 0;
|
||
NcrPrint((1, "Too fast. Local period %d\n", localPeriod));
|
||
return(FALSE);
|
||
}
|
||
|
||
extendedMessage->ExtendedArguments.Synchronous.TransferPeriod =
|
||
(UCHAR) localPeriod;
|
||
period = localPeriod;
|
||
}
|
||
|
||
//
|
||
// The synchronous transfer cycle count is calculated by:
|
||
//
|
||
// (RequestedPeriod - BasePeriod) * 1000
|
||
// ------------------------------------- + InitialRegisterValue
|
||
// ClockSpeed * 4
|
||
//
|
||
// Note the divide must be rounded up.
|
||
//
|
||
|
||
step = (1000 / 4) / DeviceExtension->ClockSpeed;
|
||
period -= localPeriod;
|
||
for (i = SynchronousTransferTypes[chipType].InitialRegisterValue;
|
||
i < SynchronousTransferTypes[chipType].MaximumPeriodCyles;
|
||
i++) {
|
||
|
||
if (period <= 0) {
|
||
break;
|
||
}
|
||
|
||
period -= step;
|
||
localPeriod += step;
|
||
}
|
||
|
||
NcrPrint((1, "Local period: %d, Register value: %d\n", localPeriod, i));
|
||
|
||
if (i >= SynchronousTransferTypes[chipType].MaximumPeriodCyles) {
|
||
|
||
//
|
||
// The requested transfer period is too long for the SCSI protocol
|
||
// chip. Fall back to synchronous and reject the request.
|
||
//
|
||
|
||
TargetState->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
TargetState->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
TargetState->Configuration3.FastScsi = 0;
|
||
|
||
return(FALSE);
|
||
}
|
||
|
||
TargetState->SynchronousPeriod = (UCHAR) i;
|
||
|
||
//
|
||
// If no response was expected then the negotation has completed
|
||
// successfully. Set the synchronous data transfer parameter registers
|
||
// to the new values. These must be set before a data transfer
|
||
// is started.
|
||
//
|
||
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
SynchronousPeriod,
|
||
TargetState->SynchronousPeriod
|
||
);
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
SynchronousOffset,
|
||
TargetState->SynchronousOffset
|
||
);
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
Configuration3,
|
||
*((PUCHAR) &TargetState->Configuration3)
|
||
);
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
VOID
|
||
NcrDumpState(
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
NcrPrint((0, "NcrDumpState: Specific device extension: %8x; Active Logical Unit: %8x;\n",
|
||
DeviceExtension,
|
||
DeviceExtension->ActiveLogicalUnit
|
||
));
|
||
NcrPrint((0, "NcrDumpState: Adapter Status: %2x; Adapter Interrupt: %2x; Adapter Step: %2x;\n",
|
||
*((PUCHAR) &DeviceExtension->AdapterStatus),
|
||
*((PUCHAR) &DeviceExtension->AdapterInterrupt),
|
||
*((PUCHAR) &DeviceExtension->SequenceStep)
|
||
));
|
||
NcrPrint((0, "NcrDumpState: Adapter flags: %4x; Adapter state: %d;\n",
|
||
DeviceExtension->AdapterFlags,
|
||
DeviceExtension->AdapterState
|
||
));
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NcrInitializeAdapter(
|
||
IN PVOID ServiceContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function initializes the NCR 53c9x SCSI host adpater and protocol
|
||
chip. This function must be called before any other operations are
|
||
performed. 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.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext;
|
||
UCHAR dataByte;
|
||
|
||
//
|
||
// Clear the adapter flags, but preserve the NCR adapter flag.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags =
|
||
deviceExtension->AdapterFlags & PD_NCR_ADAPTER;
|
||
|
||
//
|
||
// Initialize the NCR 53c9x SCSI protocol chip.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, RESET_SCSI_CHIP );
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, NO_OPERATION_DMA );
|
||
|
||
//
|
||
// Set the configuration register for slow cable mode, parity enable,
|
||
// and allow reset interrupts, also set the host adapter SCSI bus id.
|
||
// Configuration registers 2 and 3 are cleared by the chip reset and
|
||
// do not need to be changed.
|
||
//
|
||
|
||
dataByte = deviceExtension->AdapterBusId;
|
||
((PSCSI_CONFIGURATION1)(&dataByte))->ParityEnable = 1;
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Configuration1, dataByte);
|
||
|
||
//
|
||
// Configuration registers 2 and 3 are cleared by a chip reset and do
|
||
// need to be initialized. Note these registers do not exist on the
|
||
// Ncr53c90, but the writes will do no harm. Set configuration register 3
|
||
// with the value determined by the find adapter routine.
|
||
//
|
||
|
||
SCSI_WRITE(
|
||
deviceExtension->Adapter,
|
||
Configuration3,
|
||
*((PUCHAR)&deviceExtension->Configuration3)
|
||
);
|
||
|
||
//
|
||
// Enable the SCSI-2 features.
|
||
//
|
||
|
||
dataByte = 0;
|
||
((PSCSI_CONFIGURATION2)(&dataByte))->Scsi2 = 1;
|
||
((PSCSI_CONFIGURATION2)(&dataByte))->EnablePhaseLatch = 1;
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Configuration2, dataByte);
|
||
|
||
//
|
||
// Set the clock conversion register. The clock convertion factor is the
|
||
// clock speed divided by 5 rounded up. Only the low three bits are used.
|
||
//
|
||
|
||
dataByte = (deviceExtension->ClockSpeed + 4) / 5;
|
||
SCSI_WRITE(
|
||
deviceExtension->Adapter,
|
||
ClockConversionFactor,
|
||
(UCHAR)(dataByte & 0x07)
|
||
);
|
||
|
||
//
|
||
// Set the SelectTimeOut Register to 250ms. This value is based on the
|
||
// clock conversion factor and the clock speed.
|
||
//
|
||
|
||
dataByte = SELECT_TIMEOUT_FACTOR * deviceExtension->ClockSpeed / dataByte;
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, SelectTimeOut, dataByte);
|
||
|
||
//
|
||
// NOTE: Reselection does not need to be enabled until a request is sent
|
||
// to a target. The process of sending a target a request will cause a
|
||
// disconnect interrupt so that an ENABLE_SELECTION_RESELECTION request
|
||
// will be performed.
|
||
//
|
||
|
||
if (deviceExtension->AdapterFlags & PD_NCR_ADAPTER) {
|
||
|
||
//
|
||
// Enable Adapter Interrupts
|
||
//
|
||
|
||
dataByte = SCSI_READ(deviceExtension->AdapterBase, OptionSelect1);
|
||
((PPOS_DATA_1)(&dataByte))->InterruptEnable = 1;
|
||
SCSI_WRITE(deviceExtension->AdapterBase, OptionSelect1, dataByte);
|
||
|
||
}
|
||
|
||
return( TRUE );
|
||
}
|
||
|
||
BOOLEAN
|
||
NcrInterruptServiceRoutine(
|
||
PVOID ServiceContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the interrupt service routine for the NCR 53c9x SCSI
|
||
host adapter. 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.
|
||
|
||
Check to see if the bus disconnected.
|
||
|
||
Check that the previous function completed normally.
|
||
|
||
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;
|
||
LONG i;
|
||
SCSI_DMA_STATUS DmaStatus;
|
||
BOOLEAN setAttention;
|
||
|
||
/* POWERFAIL */
|
||
|
||
//
|
||
// Make sure there is really an interrupt before reading the other
|
||
// registers, particularly, the interrupt register.
|
||
//
|
||
|
||
if (deviceExtension->AdapterFlags & PD_NCR_ADAPTER) {
|
||
|
||
*((PUCHAR) &DmaStatus) = SCSI_READ( deviceExtension->AdapterBase, DmaStatus );
|
||
if (DmaStatus.Interrupt != deviceExtension->InterruptPending ) {
|
||
return(FALSE);
|
||
}
|
||
|
||
*((PUCHAR) &deviceExtension->AdapterStatus) = SCSI_READ ( deviceExtension->Adapter, ScsiStatus );
|
||
|
||
} else {
|
||
|
||
*((PUCHAR) &deviceExtension->AdapterStatus) = SCSI_READ ( deviceExtension->Adapter, ScsiStatus );
|
||
if (!deviceExtension->AdapterStatus.Interrupt) {
|
||
return(FALSE);
|
||
}
|
||
}
|
||
|
||
NextInterrupt:
|
||
|
||
//
|
||
// Get the current chip state which includes the status register, the
|
||
// sequence step register and the interrupt register. These registers are
|
||
// frozen until the interrupt register is read.
|
||
//
|
||
|
||
*((PUCHAR) &deviceExtension->SequenceStep) = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
SequenceStep
|
||
);
|
||
//
|
||
// This read will dismiss the interrupt.
|
||
//
|
||
|
||
*((PUCHAR) &deviceExtension->AdapterInterrupt) = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
ScsiInterrupt
|
||
);
|
||
|
||
#if DBG
|
||
if (!deviceExtension->AdapterInterrupt.Disconnect && NcrDebug) {
|
||
NcrPrint((0, "NcrInterrupt: Adapter Status: %2x; Adapter Interrupt: %2x; Adapter Step: %2x;\n",
|
||
*((PUCHAR) &deviceExtension->AdapterStatus),
|
||
*((PUCHAR) &deviceExtension->AdapterInterrupt),
|
||
*((PUCHAR) &deviceExtension->SequenceStep)
|
||
));
|
||
}
|
||
#endif
|
||
|
||
deviceExtension->InterruptCount++;
|
||
|
||
if (deviceExtension->AdapterInterrupt.IllegalCommand) {
|
||
NcrPrint((1, "NcrInterrupt: IllegalCommand\n" ));
|
||
|
||
#if DBG
|
||
if ( NcrDebug != 0) {
|
||
NcrDumpState(deviceExtension);
|
||
}
|
||
#endif
|
||
|
||
if (deviceExtension->AdapterState == AttemptingSelect ||
|
||
deviceExtension->AdapterState == Reselected) {
|
||
|
||
//
|
||
// If an IllegalCommand interrupt has occurred and a select
|
||
// is being attempted, flush the FIFO and exit. This occurs
|
||
// when the fifo is being filled for a new command at the
|
||
// same time time a reselection occurs.
|
||
//
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, FLUSH_FIFO);
|
||
|
||
} else {
|
||
|
||
//
|
||
// An illegal command occured at an unexpected time. Reset the
|
||
// bus and log an error.
|
||
//
|
||
|
||
NcrResetScsiBusInternal(deviceExtension, 0);
|
||
NcrInitializeAdapter(deviceExtension);
|
||
|
||
#ifdef MIPS
|
||
//
|
||
// There is a chip bug with the Emulex 216 part which causes
|
||
// illegal commands interrupts to be generated. This problem
|
||
// can be prevented on the mips systems by setting a bit in the
|
||
// DMA controller to provide better DMA service to the adapter.
|
||
//
|
||
|
||
if (deviceExtension->ErrorCount++ == 1) {
|
||
|
||
//
|
||
// Clear on board DMA
|
||
//
|
||
|
||
i = ScsiPortReadRegisterUlong(
|
||
(PULONG) &DMA_CONTROL->Channel[SCSI_CHANNEL].Enable.Long
|
||
);
|
||
|
||
((PDMA_CHANNEL_ENABLE) &i)->ChannelEnable = 0;
|
||
ScsiPortWriteRegisterUlong(
|
||
(PULONG) &DMA_CONTROL->Channel[SCSI_CHANNEL].Enable.Long,
|
||
i
|
||
);
|
||
|
||
|
||
//
|
||
// Enable brust mode in the DMA controller.
|
||
//
|
||
|
||
i = ScsiPortReadRegisterUlong(
|
||
(PULONG) &DMA_CONTROL->Channel[SCSI_CHANNEL].Mode.Long
|
||
);
|
||
|
||
|
||
((PDMA_CHANNEL_MODE) &i)->BurstMode = 1;
|
||
|
||
ScsiPortWriteRegisterUlong(
|
||
(PULONG) &DMA_CONTROL->Channel[SCSI_CHANNEL].Mode.Long,
|
||
i
|
||
);
|
||
|
||
NcrLogError(deviceExtension, SP_BAD_FW_WARNING, 15);
|
||
|
||
}
|
||
#endif
|
||
NcrLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 14);
|
||
|
||
}
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// Check for major errors these should never occur.
|
||
//
|
||
|
||
if ( deviceExtension->AdapterInterrupt.Selected ||
|
||
deviceExtension->AdapterInterrupt.SelectedWithAttention ||
|
||
deviceExtension->AdapterStatus.GrossError ||
|
||
deviceExtension->InterruptCount > MAX_INTERRUPT_COUNT) {
|
||
|
||
//
|
||
// Things are really messed up. Reset the bus, the chip and
|
||
// bail out.
|
||
//
|
||
|
||
NcrPrint((0,
|
||
"NcrInterruptServiceRoutine: Unexpected error. Interrupt Count=%d\n",
|
||
deviceExtension->InterruptCount
|
||
));
|
||
|
||
NcrDumpState(deviceExtension);
|
||
|
||
NcrResetScsiBusInternal(deviceExtension, 0);
|
||
NcrInitializeAdapter(deviceExtension);
|
||
|
||
NcrLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 1);
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// Check for a bus reset.
|
||
//
|
||
|
||
if (deviceExtension->AdapterInterrupt.ScsiReset) {
|
||
|
||
//
|
||
// Check if this was an expected reset.
|
||
//
|
||
|
||
if (!(deviceExtension->AdapterFlags & PD_EXPECTING_RESET_INTERRUPT)) {
|
||
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: SCSI bus reset detected\n"));
|
||
|
||
//
|
||
// Cleanup the logical units and notify the port driver,
|
||
// then return.
|
||
//
|
||
|
||
NcrCleanupAfterReset(deviceExtension, TRUE);
|
||
ScsiPortNotification(
|
||
ResetDetected,
|
||
deviceExtension,
|
||
NULL
|
||
);
|
||
|
||
} else {
|
||
deviceExtension->AdapterFlags &= ~PD_EXPECTING_RESET_INTERRUPT;
|
||
}
|
||
|
||
//
|
||
// Stall for a short time. This allows interrupt to clear.
|
||
//
|
||
|
||
ScsiPortStallExecution(INTERRUPT_STALL_TIME);
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, FLUSH_FIFO);
|
||
|
||
//
|
||
// Note that this should only happen in firmware where the interrupts
|
||
// are polled.
|
||
//
|
||
|
||
if (deviceExtension->AdapterFlags & PD_PENDING_START_IO) {
|
||
|
||
//
|
||
// Call NcrStartIo to start the pending request.
|
||
// Note that NcrStartIo is idempotent when called with
|
||
// the same arguments.
|
||
//
|
||
|
||
NcrStartIo(
|
||
deviceExtension,
|
||
deviceExtension->NextSrbRequest
|
||
);
|
||
|
||
}
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// Check for parity errors.
|
||
//
|
||
|
||
if (deviceExtension->AdapterStatus.ParityError) {
|
||
|
||
//
|
||
// The SCSI protocol chip has set 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 or something else. If the status
|
||
// is currently message-in then send-message PARITY ERROR;
|
||
// otherwise, send INITIATOR DETECTED ERROR.
|
||
//
|
||
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: Parity error detected.\n"));
|
||
NcrDumpState(deviceExtension);
|
||
|
||
//
|
||
// If the current phase is MESSAGE_IN then special handling is requred.
|
||
//
|
||
|
||
if (deviceExtension->AdapterStatus.Phase == MESSAGE_IN) {
|
||
|
||
//
|
||
// If the current state is CommandComplete, then the fifo contains
|
||
// a good status byte. Save the status byte before handling the
|
||
// message parity error.
|
||
//
|
||
|
||
if (deviceExtension->AdapterState == CommandComplete) {
|
||
|
||
srb = deviceExtension->ActiveLuRequest;
|
||
|
||
srb->ScsiStatus = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
Fifo
|
||
);
|
||
|
||
SRB_EXT(srb)->SrbExtensionFlags |= PD_STATUS_VALID;
|
||
|
||
}
|
||
|
||
//
|
||
// Set the message to indicate a message parity error, flush the
|
||
// fifo and accept the message.
|
||
//
|
||
|
||
deviceExtension->MessageBuffer[0] = SCSIMESS_MESS_PARITY_ERROR;
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, FLUSH_FIFO);
|
||
NcrAcceptMessage(deviceExtension, TRUE, TRUE);
|
||
|
||
//
|
||
// Since the message which was in the fifo is no good. Clear the
|
||
// function complete interrupt which indicates that a message byte
|
||
// has been recieved. If this is a reselection, then this will
|
||
// a bus reset to occur. This cause is not handled well in this
|
||
// code, because it is not setup to deal with a target id and no
|
||
// logical unit.
|
||
//
|
||
|
||
deviceExtension->AdapterInterrupt.FunctionComplete = FALSE;
|
||
|
||
} else {
|
||
|
||
deviceExtension->MessageBuffer[0] = SCSIMESS_INIT_DETECTED_ERROR;
|
||
|
||
}
|
||
|
||
deviceExtension->MessageCount = 1;
|
||
deviceExtension->MessageSent = 0;
|
||
deviceExtension->AdapterState = MessageOut;
|
||
deviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID;
|
||
|
||
if (!(deviceExtension->AdapterFlags & PD_PARITY_ERROR_LOGGED)) {
|
||
NcrLogError(deviceExtension, SP_BUS_PARITY_ERROR, 2);
|
||
deviceExtension->AdapterFlags |= PD_PARITY_ERROR_LOGGED;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Check for bus disconnection. If this was expected, then the next request
|
||
// can be processed. If a selection was being attempted, then perhaps the
|
||
// logical unit is not there or has gone away. Otherwise, this is an
|
||
// unexpected disconnect and should be reported as an error.
|
||
//
|
||
|
||
if (deviceExtension->AdapterInterrupt.Disconnect) {
|
||
|
||
srb = deviceExtension->NextSrbRequest;
|
||
|
||
//
|
||
// Check for an unexpected disconnect. This occurs if the state is
|
||
// not ExpectingDisconnect and a selection did not fail. A selection
|
||
// failure is indicated by state of AttemptingSelect and a sequence
|
||
// step of 0.
|
||
//
|
||
|
||
if (deviceExtension->AdapterState == AttemptingSelect &&
|
||
deviceExtension->SequenceStep.Step == 0) {
|
||
|
||
//
|
||
// The target selection failed. Log the error. If the retry
|
||
// count is not exceeded then retry the selection; otherwise
|
||
// fail the request.
|
||
//
|
||
|
||
luExtension = ScsiPortGetLogicalUnit(
|
||
deviceExtension,
|
||
srb->PathId,
|
||
srb->TargetId,
|
||
srb->Lun
|
||
);
|
||
|
||
if (luExtension->RetryCount++ >= RETRY_SELECTION_LIMIT) {
|
||
|
||
//
|
||
// Clear the Active request in the logical unit.
|
||
//
|
||
|
||
luExtension->RetryCount = 0;
|
||
|
||
if (deviceExtension->AdapterFlags & PD_SEND_MESSAGE_REQUEST) {
|
||
|
||
//
|
||
// Process the completion of the send message request.
|
||
// Set the ActiveLogicalUnit for NcrCompleteSendMessage.
|
||
// ActiveLogicalUnit is cleared after it returns.
|
||
//
|
||
|
||
deviceExtension->ActiveLogicalUnit = luExtension;
|
||
|
||
NcrCompleteSendMessage(
|
||
deviceExtension,
|
||
SRB_STATUS_SELECTION_TIMEOUT
|
||
);
|
||
|
||
deviceExtension->ActiveLogicalUnit = NULL;
|
||
|
||
} else {
|
||
|
||
srb->SrbStatus = SRB_STATUS_SELECTION_TIMEOUT;
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
deviceExtension,
|
||
srb
|
||
);
|
||
|
||
luExtension->ActiveLuRequest = NULL;
|
||
}
|
||
|
||
deviceExtension->NextSrbRequest = NULL;
|
||
deviceExtension->AdapterFlags &= ~PD_PENDING_START_IO;
|
||
|
||
ScsiPortNotification(
|
||
NextRequest,
|
||
deviceExtension,
|
||
NULL
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// If the request needs to be retried, it will be automatically
|
||
// because the PD_PENDING_START_IO flag is still set, and the
|
||
// following code will cause it to be restarted.
|
||
//
|
||
|
||
//
|
||
// The chip leaves some of the command in the FIFO, so clear the
|
||
// FIFO so there is no garbage left in it.
|
||
//
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, FLUSH_FIFO);
|
||
|
||
} else if ( deviceExtension->AdapterState == DisconnectExpected ||
|
||
deviceExtension->AdapterFlags & PD_DISCONNECT_EXPECTED) {
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
NcrCompleteSendMessage( deviceExtension,
|
||
SRB_STATUS_SUCCESS
|
||
);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// The disconnect was unexpected treat it as an error.
|
||
// Check to see if a data transfer was in progress, if so flush
|
||
// the DMA.
|
||
//
|
||
|
||
if (deviceExtension->AdapterState == DataTransfer) {
|
||
ScsiPortFlushDma(deviceExtension);
|
||
}
|
||
|
||
//
|
||
// NOTE: If the state is AttemptingSelect, then ActiveLogicalUnit
|
||
// is NULL!
|
||
//
|
||
|
||
//
|
||
// The chip leaves some of the command in the FIFO, so clear the
|
||
// FIFO so there is not garbage left in it.
|
||
//
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, FLUSH_FIFO);
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: Unexpected bus disconnect\n"));
|
||
|
||
NcrLogError(deviceExtension, SP_UNEXPECTED_DISCONNECT, 3);
|
||
}
|
||
|
||
//
|
||
// 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->ActiveLuRequest = NULL;
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, ENABLE_SELECTION_RESELECTION);
|
||
|
||
#if DBG
|
||
if (NcrDebug) {
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: DisconnectComplete.\n"));
|
||
}
|
||
#endif
|
||
|
||
if (deviceExtension->AdapterFlags & PD_PENDING_START_IO) {
|
||
|
||
ASSERT(deviceExtension->NextSrbRequest->SrbExtension != NULL);
|
||
|
||
//
|
||
// Check that the next request is still active. This should not
|
||
// be necessary, but it appears there is a hole somewhere.
|
||
//
|
||
|
||
srb = deviceExtension->NextSrbRequest;
|
||
srb = ScsiPortGetSrb(
|
||
deviceExtension,
|
||
srb->PathId,
|
||
srb->TargetId,
|
||
srb->Lun,
|
||
srb->QueueTag
|
||
);
|
||
|
||
ASSERT(srb == deviceExtension->NextSrbRequest ||
|
||
deviceExtension->NextSrbRequest->Function != SRB_FUNCTION_EXECUTE_SCSI);
|
||
|
||
if (srb != deviceExtension->NextSrbRequest &&
|
||
deviceExtension->NextSrbRequest->Function == SRB_FUNCTION_EXECUTE_SCSI) {
|
||
|
||
NcrPrint((1, "NcrInterruptServiceRoutine: Found in active SRB in next request field.\n"));
|
||
NcrDumpState(deviceExtension);
|
||
|
||
//
|
||
// Dump it on the floor.
|
||
//
|
||
|
||
deviceExtension->NextSrbRequest = NULL;
|
||
deviceExtension->AdapterFlags &= ~PD_PENDING_START_IO;
|
||
|
||
NcrLogError(deviceExtension, SP_INTERNAL_ADAPTER_ERROR, 18);
|
||
|
||
ScsiPortNotification(
|
||
NextRequest,
|
||
deviceExtension,
|
||
NULL
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Call NcrStartIo to start the pending request.
|
||
// Note that NcrStartIo is idempotent when called with
|
||
// the same arguments.
|
||
//
|
||
|
||
NcrStartIo(
|
||
deviceExtension,
|
||
deviceExtension->NextSrbRequest
|
||
);
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Check for a reselection interrupt.
|
||
//
|
||
|
||
if (deviceExtension->AdapterInterrupt.Reselected) {
|
||
UCHAR targetId;
|
||
UCHAR luId;
|
||
|
||
//
|
||
// The usual case is not to set attention so initialize the
|
||
// varible to FALSE.
|
||
//
|
||
|
||
setAttention = FALSE;
|
||
|
||
//
|
||
// If the FunctionComplete interrupt is not set then the target did
|
||
// not send an IDENTFY message. This is a fatal protocol violation.
|
||
// Reset the bus to get rid of this target.
|
||
//
|
||
|
||
if (!deviceExtension->AdapterInterrupt.FunctionComplete) {
|
||
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: Reselection Failed.\n"));
|
||
NcrDumpState(deviceExtension);
|
||
|
||
NcrResetScsiBusInternal(deviceExtension, 0);
|
||
NcrInitializeAdapter(deviceExtension);
|
||
|
||
NcrLogError(deviceExtension, SP_PROTOCOL_ERROR, 4);
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// The target Id and the logical unit id are in the FIFO. Use them to
|
||
// get the connected active logical unit.
|
||
//
|
||
|
||
luId = SCSI_READ(deviceExtension->Adapter, Fifo);
|
||
|
||
//
|
||
// The select id has two bits set. One is the SCSI bus id of the
|
||
// initiator and the other is the reselecting target id. The initiator
|
||
// id must be stripped and the remaining bit converted to a bit number
|
||
// to get the target id.
|
||
//
|
||
|
||
luId &= ~deviceExtension->AdapterBusIdMask;
|
||
WHICH_BIT(luId, targetId);
|
||
|
||
luId = SCSI_READ(deviceExtension->Adapter, Fifo);
|
||
|
||
//
|
||
// The logical unit id is stored in the low-order 3 bits of the
|
||
// IDENTIFY message, so the upper bits must be stripped off the
|
||
// byte read from the FIFO to get the logical unit number.
|
||
//
|
||
|
||
luId &= SCSI_MAXIMUM_LOGICAL_UNITS - 1;
|
||
|
||
luExtension = ScsiPortGetLogicalUnit( deviceExtension,
|
||
0,
|
||
targetId,
|
||
luId
|
||
);
|
||
|
||
//
|
||
// Check to that this is a valid logical unit.
|
||
//
|
||
|
||
if (luExtension == NULL) {
|
||
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: Reselection Failed.\n"));
|
||
NcrDumpState(deviceExtension);
|
||
|
||
|
||
ScsiPortLogError(
|
||
deviceExtension, // HwDeviceExtension,
|
||
NULL, // Srb
|
||
0, // PathId,
|
||
targetId, // TargetId,
|
||
luId, // Lun,
|
||
SP_INVALID_RESELECTION, // ErrorCode,
|
||
4 // UniqueId
|
||
);
|
||
|
||
//
|
||
// 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;
|
||
|
||
setAttention = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Everything looks ok.
|
||
//
|
||
|
||
//
|
||
// A reselection has been completed. Set the active logical
|
||
// unit, restore the active data pointer, and set the state.
|
||
// In addition, any adpater flags set by a pending select
|
||
// must be cleared using the disconnect mask.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags &= ~PD_ADAPTER_DISCONNECT_MASK;
|
||
deviceExtension->ActiveLogicalUnit = luExtension;
|
||
deviceExtension->AdapterState = Reselected;
|
||
deviceExtension->MessageCount = 0;
|
||
|
||
srb = luExtension->ActiveLuRequest;
|
||
deviceExtension->ActiveLuRequest = srb;
|
||
|
||
if (srb == NULL) {
|
||
|
||
//
|
||
// This must be a reconnect for a tagged request.
|
||
// Indicate a queue tag message is expected next and save
|
||
// the target and logical unit ids.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags |= PD_EXPECTING_QUEUE_TAG;
|
||
deviceExtension->Lun = luId;
|
||
} else {
|
||
|
||
deviceExtension->ActiveDataPointer = SRB_EXT(srb)->SavedDataPointer;
|
||
deviceExtension->ActiveDataLength = SRB_EXT(srb)->SavedDataLength;
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// The bus is waiting for the message to be accepted. The attention
|
||
// signal will be set if this is not a valid reselection. Finally,
|
||
// the synchronous data tranfer parameters need to be set in case a
|
||
// data transfer is done.
|
||
//
|
||
|
||
deviceExtension->TargetId = targetId;
|
||
NcrAcceptMessage(deviceExtension, setAttention, TRUE);
|
||
deviceExtension->InterruptCount = 0;
|
||
|
||
} else if (deviceExtension->AdapterInterrupt.FunctionComplete) {
|
||
|
||
//
|
||
// Check for function complete interrupt if there was not a reselected
|
||
// interrupt. The function complete interrupt has already been checked
|
||
// in the previous case.
|
||
//
|
||
// The function complete interrupt occurs after the following cases:
|
||
// A select succeeded
|
||
// A message byte has been read
|
||
// A status byte and message byte have been read when in the
|
||
// command complete state.
|
||
// A reselection (handled above)
|
||
//
|
||
// Switch on the state current state of the bus to determine what
|
||
// action should be taken now the function has completed.
|
||
//
|
||
|
||
switch (deviceExtension->AdapterState) {
|
||
case AttemptingSelect:
|
||
|
||
//
|
||
// The target was successfully selected. Set the active
|
||
// logical unit field, clear the next logical unit, and
|
||
// notify the OS-dependent driver that a new request can
|
||
// be accepted. The state is set to MessageOut since is
|
||
// the next thing done after a selection.
|
||
//
|
||
|
||
deviceExtension->ActiveLogicalUnit = ScsiPortGetLogicalUnit(
|
||
deviceExtension,
|
||
deviceExtension->NextSrbRequest->PathId,
|
||
deviceExtension->NextSrbRequest->TargetId,
|
||
deviceExtension->NextSrbRequest->Lun
|
||
);
|
||
|
||
srb = deviceExtension->NextSrbRequest;
|
||
deviceExtension->ActiveLuRequest = srb;
|
||
|
||
//
|
||
// Restore the data pointers.
|
||
//
|
||
|
||
deviceExtension->ActiveDataPointer = SRB_EXT(srb)->SavedDataPointer;
|
||
deviceExtension->ActiveDataLength = SRB_EXT(srb)->SavedDataLength;
|
||
|
||
//
|
||
// The next request has now become the active request.
|
||
// Clear the state associated with the next request and ask for
|
||
// another one to start.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags &= ~PD_PENDING_START_IO;
|
||
deviceExtension->NextSrbRequest = NULL;
|
||
deviceExtension->AdapterState = MessageOut;
|
||
|
||
//
|
||
// If this was a tagged request then indicate that the next
|
||
// request for this lu may be sent.
|
||
//
|
||
|
||
if (deviceExtension->AdapterFlags & PD_TAGGED_SELECT) {
|
||
|
||
ScsiPortNotification(
|
||
NextLuRequest,
|
||
deviceExtension,
|
||
srb->PathId,
|
||
srb->TargetId,
|
||
srb->Lun
|
||
);
|
||
|
||
} else {
|
||
|
||
ScsiPortNotification(
|
||
NextRequest,
|
||
deviceExtension,
|
||
NULL
|
||
);
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
case CommandComplete:
|
||
|
||
//
|
||
// The FIFO contains the status byte and a message byte. Save the
|
||
// status byte and set the state to look like MessageIn, then fall
|
||
// through to the message-in state.
|
||
//
|
||
|
||
srb = deviceExtension->ActiveLuRequest;
|
||
|
||
ASSERT(deviceExtension->NextSrbRequest != srb);
|
||
|
||
srb->ScsiStatus = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
Fifo
|
||
);
|
||
|
||
SRB_EXT(srb)->SrbExtensionFlags |= PD_STATUS_VALID;
|
||
|
||
deviceExtension->AdapterState = MessageIn;
|
||
deviceExtension->MessageCount = 0;
|
||
deviceExtension->AdapterFlags &= ~PD_MESSAGE_OUT_VALID;
|
||
|
||
//
|
||
// Fall through and process the message byte in the FIFO.
|
||
//
|
||
|
||
case MessageIn:
|
||
|
||
//
|
||
// A message byte has been received. Store it in the message buffer
|
||
// and 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.
|
||
//
|
||
|
||
deviceExtension->MessageBuffer[deviceExtension->MessageCount++] =
|
||
SCSI_READ( deviceExtension->Adapter, Fifo );
|
||
|
||
if (NcrMessageDecode( deviceExtension )) {
|
||
|
||
//
|
||
// NcrMessageDecode 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 by NcrMessageDecode. All that needs
|
||
// to be done here is 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.
|
||
//
|
||
|
||
NcrAcceptMessage( deviceExtension, setAttention, FALSE);
|
||
break;
|
||
|
||
default:
|
||
|
||
//
|
||
// A function complete should not occur while in any other states.
|
||
//
|
||
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: Unexpected function complete interrupt.\n"));
|
||
NcrDumpState(deviceExtension);
|
||
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Check for a bus service interrupt. This interrupt indicates the target
|
||
// is requesting some form of bus transfer. The bus transfer type is
|
||
// determined by the bus phase.
|
||
//
|
||
|
||
if (deviceExtension->AdapterInterrupt.BusService) {
|
||
|
||
luExtension = deviceExtension->ActiveLogicalUnit;
|
||
|
||
if (luExtension == NULL) {
|
||
|
||
//
|
||
// There should never be an bus service interrupt without an
|
||
// active locgial unit. The bus or the chip is really messed up.
|
||
// Reset the bus and return.
|
||
//
|
||
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: Unexpected Bus service interrupt.\n"));
|
||
NcrDumpState(deviceExtension);
|
||
|
||
NcrResetScsiBusInternal(deviceExtension, 0);
|
||
NcrInitializeAdapter(deviceExtension);
|
||
|
||
NcrLogError(deviceExtension, SP_PROTOCOL_ERROR, 6);
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
srb = deviceExtension->ActiveLuRequest;
|
||
|
||
//
|
||
// If there is no current srb request then the bus service interrupt
|
||
// must be a message in with a tag.
|
||
//
|
||
|
||
if (deviceExtension->AdapterFlags & PD_EXPECTING_QUEUE_TAG &&
|
||
deviceExtension->AdapterStatus.Phase != MESSAGE_IN ) {
|
||
|
||
//
|
||
// A bus service interrupt occured when a queue tag message
|
||
// was exepected. Is a protocol error by the target reset the
|
||
// bus.
|
||
//
|
||
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: Unexpected Bus service interrupt when queue tag expected.\n"));
|
||
NcrDumpState(deviceExtension);
|
||
|
||
NcrResetScsiBusInternal(deviceExtension, 0);
|
||
NcrInitializeAdapter(deviceExtension);
|
||
|
||
NcrLogError(deviceExtension, SP_PROTOCOL_ERROR, 13);
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
//
|
||
// The bus is changing phases or needs more data. Generally, the target
|
||
// can change bus phase at any time: in particular, in the middle of
|
||
// a data transfer. The initiator must be able to restart a transfer
|
||
// where it left off. To do this it must know how much data was
|
||
// transferred. If the previous state was a data transfer, then the
|
||
// amount of data transferred needs to be determined, saved and
|
||
// the DMA flushed.
|
||
//
|
||
|
||
if (deviceExtension->AdapterState == DataTransfer) {
|
||
SCSI_FIFO_FLAGS fifoFlags;
|
||
|
||
//
|
||
// Figure out how many bytes have been transferred based on the
|
||
// original transfer count stored in the ActiveLengthField,
|
||
// SCSI protocol chip transfer counters, and
|
||
// the number of bytes in the FIFO. The normal case is when all
|
||
// the bytes have been transferred so check for that using the
|
||
// TerminalCount bit in the status field.
|
||
//
|
||
|
||
i = 0;
|
||
|
||
if (!deviceExtension->AdapterStatus.TerminalCount) {
|
||
|
||
//
|
||
// Read bits 23-16 if this chip has that register.
|
||
//
|
||
|
||
if (deviceExtension->ChipType == Fas216) {
|
||
|
||
i = (SCSI_READ(deviceExtension->Adapter,
|
||
TransferCountPage
|
||
)) << 16;
|
||
|
||
}
|
||
|
||
//
|
||
// Read the current value of the tranfer count registers;
|
||
//
|
||
|
||
i |= (SCSI_READ(deviceExtension->Adapter, TransferCountHigh)) << 8;
|
||
i |= SCSI_READ(deviceExtension->Adapter, TransferCountLow );
|
||
|
||
//
|
||
// A value of zero in i and TerminalCount clear indicates
|
||
// that the transfer length was 64K and that no bytes were
|
||
// transferred. Set i to 64K.
|
||
//
|
||
|
||
if (i == 0) {
|
||
i = 0x10000;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If this is a write then there may still be some bytes in the
|
||
// FIFO which have yet to be transferred to the target.
|
||
//
|
||
|
||
if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
|
||
*((PUCHAR) &fifoFlags) = SCSI_READ(deviceExtension->Adapter,
|
||
FifoFlags
|
||
);
|
||
i += fifoFlags.ByteCount;
|
||
|
||
|
||
if (i == 1 && deviceExtension->ChipType == Fas216) {
|
||
|
||
//
|
||
// This is a chip bug. If the bus state is still data
|
||
// out then tell the chip to transfer one more byte.
|
||
//
|
||
|
||
NcrPrint((1, "NcrInterruptServiceRoutine: One byte left!\n"));
|
||
|
||
//
|
||
// Set the transfer count.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter,
|
||
TransferCountLow,
|
||
1
|
||
);
|
||
SCSI_WRITE( deviceExtension->Adapter,
|
||
TransferCountHigh,
|
||
0
|
||
);
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter,
|
||
TransferCountPage,
|
||
0
|
||
);
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, TRANSFER_INFORMATION);
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// The chip leaves some data in the FIFO, so clear the
|
||
// FIFO so there is not garbage left in it.
|
||
//
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, FLUSH_FIFO);
|
||
}
|
||
|
||
//
|
||
// i now contains the number of bytes to be transferred.
|
||
// Check to see if this the maximum that has be transferred so far,
|
||
// and update the active data pointer and the active length.
|
||
//
|
||
|
||
if (srb->DataTransferLength - i >
|
||
SRB_EXT(srb)->MaximumTransferLength) {
|
||
SRB_EXT(srb)->MaximumTransferLength = srb->DataTransferLength -
|
||
i;
|
||
}
|
||
|
||
deviceExtension->ActiveDataPointer +=
|
||
deviceExtension->ActiveDataLength - i;
|
||
deviceExtension->ActiveDataLength = i;
|
||
|
||
//
|
||
// Flush the DMA to ensure all the bytes are transferred.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags &= ~PD_PENDING_DATA_TRANSFER;
|
||
ScsiPortFlushDma(deviceExtension);
|
||
|
||
} else if (deviceExtension->AdapterState == DisconnectExpected) {
|
||
|
||
//
|
||
// This is an error; however, some contollers attempt to read more
|
||
// message bytes even after a message indicating a disconnect.
|
||
// If the request is for a message transfer and extra bytes
|
||
// are expected, then allow the transfer; otherwise, reset the bus.
|
||
//
|
||
|
||
if (!(deviceExtension->AdapterFlags & PD_POSSIBLE_EXTRA_MESSAGE_OUT)
|
||
|| (deviceExtension->AdapterStatus.Phase != MESSAGE_OUT &&
|
||
deviceExtension->AdapterStatus.Phase != 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.
|
||
//
|
||
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: Bus request while disconnect expected.\n"));
|
||
NcrDumpState(deviceExtension);
|
||
|
||
NcrResetScsiBusInternal(deviceExtension, 0);
|
||
NcrInitializeAdapter(deviceExtension);
|
||
|
||
NcrLogError(deviceExtension, SP_PROTOCOL_ERROR, 7);
|
||
|
||
return(TRUE);
|
||
} else {
|
||
|
||
//
|
||
// Make sure the disconnect-expected flag is set.
|
||
//
|
||
|
||
deviceExtension->AdapterFlags |= PD_DISCONNECT_EXPECTED;
|
||
}
|
||
} else if (deviceExtension->AdapterState == 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 |
|
||
PD_SYNCHRONOUS_TRANSFER_SENT | PD_SYNCHRONOUS_RESPONSE_SENT) &&
|
||
deviceExtension->AdapterStatus.Phase != MESSAGE_OUT &&
|
||
deviceExtension->AdapterStatus.Phase != MESSAGE_IN) {
|
||
|
||
if (deviceExtension->AdapterFlags & PD_DISCONNECT_EXPECTED) {
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: Bus request while disconnect expected after message-out.\n"));
|
||
NcrDumpState(deviceExtension);
|
||
|
||
NcrResetScsiBusInternal(deviceExtension, 0);
|
||
NcrInitializeAdapter(deviceExtension);
|
||
|
||
NcrLogError(deviceExtension, SP_PROTOCOL_ERROR, 8);
|
||
|
||
return(TRUE);
|
||
|
||
} else if (deviceExtension->AdapterFlags &
|
||
PD_SYNCHRONOUS_TRANSFER_SENT) {
|
||
|
||
//
|
||
// The controller ignored the synchronous transfer message.
|
||
// Treat it as a rejection and clear the necessary state.
|
||
//
|
||
|
||
deviceExtension->TargetState[deviceExtension->TargetId]
|
||
.TargetFlags |=
|
||
PD_SYNCHRONOUS_NEGOTIATION_DONE | PD_DO_NOT_NEGOTIATE;
|
||
deviceExtension->AdapterFlags &=
|
||
~(PD_SYNCHRONOUS_RESPONSE_SENT |
|
||
PD_SYNCHRONOUS_TRANSFER_SENT);
|
||
} else if (deviceExtension->AdapterFlags &
|
||
PD_SYNCHRONOUS_RESPONSE_SENT) {
|
||
|
||
//
|
||
// The target controller accepted the negotiation. Set
|
||
// the done flag in the logical unit and clear the
|
||
// negotiation flags in the adapter.
|
||
//
|
||
|
||
deviceExtension->TargetState[deviceExtension->TargetId].TargetFlags |=
|
||
PD_SYNCHRONOUS_NEGOTIATION_DONE | PD_DO_NOT_NEGOTIATE;
|
||
deviceExtension->AdapterFlags &=
|
||
~(PD_SYNCHRONOUS_RESPONSE_SENT |
|
||
PD_SYNCHRONOUS_TRANSFER_SENT);
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the bus phase is not DATA_IN then the FIFO may need to be
|
||
// flushed. The FIFO cannot be flushed while the bus is in the
|
||
// DATA_IN phase because the FIFO already has data bytes in it.
|
||
// The only case where a target can legally switch phases while
|
||
// there are message bytes in the FIFO to the MESSAGE_OUT bus
|
||
// phase. If the target leaves message bytes and attempts to
|
||
// goto a DATA_IN phase, then the transfer will appear to overrun
|
||
// and be detected as an error.
|
||
//
|
||
|
||
if (deviceExtension->AdapterStatus.Phase != DATA_IN) {
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, FLUSH_FIFO);
|
||
}
|
||
|
||
//
|
||
// Decode the current bus phase.
|
||
//
|
||
|
||
switch (deviceExtension->AdapterStatus.Phase) {
|
||
|
||
case COMMAND_OUT:
|
||
|
||
//
|
||
// Fill the FIFO with the commnad and tell the SCSI protocol chip
|
||
// to go.
|
||
//
|
||
|
||
for (i = 0; i < srb->CdbLength; i++) {
|
||
SCSI_WRITE( deviceExtension->Adapter,
|
||
Fifo,
|
||
srb->Cdb[i]
|
||
);
|
||
}
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter,
|
||
Command,
|
||
TRANSFER_INFORMATION
|
||
);
|
||
|
||
deviceExtension->AdapterState = CommandOut;
|
||
|
||
break;
|
||
|
||
case STATUS_IN:
|
||
|
||
//
|
||
// Setup of the SCSI protocol chip to read in the status and the
|
||
// following message byte, and set the adapter state.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, COMMAND_COMPLETE );
|
||
deviceExtension->AdapterState = CommandComplete;
|
||
|
||
break;
|
||
|
||
case MESSAGE_OUT:
|
||
|
||
//
|
||
// The target is requesting a message-out. There are three
|
||
// possible cases. First, the target is improperly requesting
|
||
// a message. Second, a message has been sent, but the target
|
||
// could not read it properly. Third, a message has been
|
||
// partially sent and the target is requesting the remainder
|
||
// of the message.
|
||
//
|
||
// The first 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;
|
||
NcrLogError(deviceExtension, SP_PROTOCOL_ERROR, 9);
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: Unexpected message-out request\n"));
|
||
NcrDumpState(deviceExtension);
|
||
|
||
}
|
||
|
||
deviceExtension->MessageCount = 1;
|
||
deviceExtension->MessageSent = 0;
|
||
deviceExtension->AdapterState = MessageOut;
|
||
|
||
}
|
||
|
||
//
|
||
// The second case is indicated when MessageCount and MessageSent
|
||
// are equal and nonzero.
|
||
//
|
||
|
||
if (deviceExtension->MessageCount == deviceExtension->MessageSent){
|
||
|
||
//
|
||
// The message needs to be resent, so set ATN, clear MessageSent
|
||
// and fall through to the next case.
|
||
//
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, SET_ATTENTION);
|
||
deviceExtension->MessageSent = 0;
|
||
}
|
||
|
||
if (deviceExtension->MessageCount != deviceExtension->MessageSent){
|
||
|
||
//
|
||
// The ATTENTION signal needs to be set if the current state
|
||
// is not MessageOut.
|
||
//
|
||
|
||
if (deviceExtension->AdapterState != MessageOut) {
|
||
|
||
SCSI_WRITE(
|
||
deviceExtension->Adapter,
|
||
Command,
|
||
SET_ATTENTION
|
||
);
|
||
}
|
||
|
||
//
|
||
// There is more message to send. Fill the FIFO with the
|
||
// message and tell the SCSI protocol chip to transfer the
|
||
// message.
|
||
//
|
||
|
||
for (;
|
||
deviceExtension->MessageSent <
|
||
deviceExtension->MessageCount;
|
||
deviceExtension->MessageSent++ ) {
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter,
|
||
Fifo,
|
||
deviceExtension->
|
||
MessageBuffer[deviceExtension->MessageSent]
|
||
);
|
||
|
||
}
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter,
|
||
Command,
|
||
TRANSFER_INFORMATION
|
||
);
|
||
|
||
}
|
||
|
||
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;
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter,
|
||
Command,
|
||
TRANSFER_INFORMATION
|
||
);
|
||
|
||
break;
|
||
|
||
case DATA_OUT:
|
||
case DATA_IN:
|
||
|
||
//
|
||
// Check that the transfer direction is ok, setup the DMA, set
|
||
// the synchronous transfer parameter, and tell the chip to go.
|
||
// Also check that there is still data to be transferred.
|
||
//
|
||
|
||
if ((!(srb->SrbFlags & SRB_FLAGS_DATA_IN) &&
|
||
deviceExtension->AdapterStatus.Phase == DATA_IN) ||
|
||
|
||
(!(srb->SrbFlags & SRB_FLAGS_DATA_OUT) &&
|
||
deviceExtension->AdapterStatus.Phase == DATA_OUT) ||
|
||
|
||
deviceExtension->ActiveDataLength == 0 ) {
|
||
|
||
//
|
||
// The data direction is incorrect. Reset the bus to clear
|
||
// things up.
|
||
//
|
||
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: Illegal transfer direction.\n"));
|
||
NcrDumpState(deviceExtension);
|
||
|
||
NcrResetScsiBusInternal(deviceExtension, 0);
|
||
NcrInitializeAdapter(deviceExtension);
|
||
|
||
NcrLogError(deviceExtension, SP_PROTOCOL_ERROR, 10);
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//
|
||
// Set the transfer count.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter,
|
||
TransferCountLow,
|
||
(UCHAR) deviceExtension->ActiveDataLength
|
||
);
|
||
SCSI_WRITE( deviceExtension->Adapter,
|
||
TransferCountHigh,
|
||
(UCHAR) (deviceExtension->ActiveDataLength >> 8)
|
||
);
|
||
|
||
//
|
||
// Write bits 23-16 if this chip has that register.
|
||
//
|
||
|
||
if (deviceExtension->ChipType == Fas216) {
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter,
|
||
TransferCountPage,
|
||
(UCHAR) (deviceExtension->ActiveDataLength >> 16)
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Clear the extra data transfer flags correctly.
|
||
//
|
||
|
||
if (deviceExtension->AdapterStatus.Phase == DATA_IN) {
|
||
srb->SrbFlags &= ~SRB_FLAGS_DATA_OUT;
|
||
} else {
|
||
srb->SrbFlags &= ~SRB_FLAGS_DATA_IN;
|
||
}
|
||
|
||
//
|
||
// Set up the DMA controller.
|
||
//
|
||
|
||
deviceExtension->AdapterState = DataTransfer;
|
||
deviceExtension->AdapterFlags |= PD_PENDING_DATA_TRANSFER;
|
||
|
||
ScsiPortIoMapTransfer( deviceExtension,
|
||
srb,
|
||
(PVOID) deviceExtension->ActiveDataPointer,
|
||
deviceExtension->ActiveDataLength
|
||
);
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
//
|
||
// This phase is illegal and indicates a serious error. Reset the
|
||
// bus to clear the problem.
|
||
//
|
||
|
||
NcrPrint((0, "NcrInterruptServiceRoutine: Illegal bus state detected.\n"));
|
||
NcrDumpState(deviceExtension);
|
||
|
||
NcrResetScsiBusInternal(deviceExtension, 0);
|
||
NcrInitializeAdapter(deviceExtension);
|
||
|
||
NcrLogError(deviceExtension, SP_PROTOCOL_ERROR, 11);
|
||
|
||
return(TRUE);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Stall for a short time. This allows interrupt to clear and gives new
|
||
// interrupts a chance to fire.
|
||
//
|
||
|
||
ScsiPortStallExecution(INTERRUPT_STALL_TIME);
|
||
|
||
//
|
||
// Make sure there is really an interrupt before reading the other
|
||
// registers, particularly, the interrupt register.
|
||
//
|
||
|
||
if (deviceExtension->AdapterFlags & PD_NCR_ADAPTER) {
|
||
|
||
*((PUCHAR) &DmaStatus) = SCSI_READ( deviceExtension->AdapterBase, DmaStatus );
|
||
|
||
if (DmaStatus.Interrupt == deviceExtension->InterruptPending) {
|
||
*((PUCHAR) &deviceExtension->AdapterStatus) = SCSI_READ( deviceExtension->Adapter, ScsiStatus );
|
||
deviceExtension->InterruptCount++;
|
||
goto NextInterrupt;
|
||
|
||
}
|
||
} else {
|
||
|
||
*((PUCHAR) &deviceExtension->AdapterStatus) = SCSI_READ ( deviceExtension->Adapter, ScsiStatus );
|
||
|
||
if (deviceExtension->AdapterStatus.Interrupt) {
|
||
deviceExtension->InterruptCount++;
|
||
goto NextInterrupt;
|
||
|
||
}
|
||
}
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
VOID
|
||
NcrLogError(
|
||
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->ActiveLuRequest != NULL) {
|
||
|
||
srb = DeviceExtension->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->AdapterBusId, // 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
|
||
NcrProcessRequestCompletion(
|
||
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 on to which the completing target controller is connected.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSPECIFIC_LOGICAL_UNIT_EXTENSION luExtension;
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
|
||
luExtension = DeviceExtension->ActiveLogicalUnit;
|
||
srb = DeviceExtension->ActiveLuRequest;
|
||
|
||
ASSERT(DeviceExtension->NextSrbRequest != srb);
|
||
|
||
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;
|
||
|
||
//
|
||
// Indicate that a INITIATE RECOVERY message was received. This
|
||
// indicates to the class driver that it must send a TERMINATE
|
||
// RECOVERY message before the logical unit will resume normal
|
||
// operation.
|
||
//
|
||
|
||
if (SRB_EXT(srb)->SrbExtensionFlags & PD_INITIATE_RECOVERY) {
|
||
|
||
//
|
||
// Modify the SrbStatus.
|
||
//
|
||
|
||
srb->SrbStatus = SRB_STATUS_ERROR_RECOVERY;
|
||
}
|
||
|
||
//
|
||
// 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) {
|
||
|
||
DeviceExtension->TargetState[srb->TargetId].TargetFlags
|
||
&= ~PD_SYNCHRONOUS_NEGOTIATION_DONE;
|
||
|
||
}
|
||
|
||
//
|
||
// If there is a pending request for this logical unit then return
|
||
// that request with a busy status. This situation only occurs when
|
||
// command queuing is enabled an a command pending for a logical unit
|
||
// at the same time that an error has occured. This may be a BUSY,
|
||
// QUEUE FULL or CHECK CONDITION. The important case is CHECK CONDITION
|
||
// because a contingatent aligance condition has be established and the
|
||
// port driver needs a chance to send a Reqeust Sense before the
|
||
// pending command is started.
|
||
//
|
||
|
||
if (DeviceExtension->AdapterFlags & PD_PENDING_START_IO &&
|
||
DeviceExtension->NextSrbRequest->PathId == srb->PathId &&
|
||
DeviceExtension->NextSrbRequest->TargetId == srb->TargetId &&
|
||
DeviceExtension->NextSrbRequest->Lun == srb->Lun) {
|
||
|
||
NcrPrint((1, "NcrProcessRequestCompletion: Failing request with busy status due to check condition\n"));
|
||
DeviceExtension->NextSrbRequest->SrbStatus =
|
||
SCSISTAT_CHECK_CONDITION;
|
||
|
||
DeviceExtension->NextSrbRequest->ScsiStatus = SCSISTAT_BUSY;
|
||
|
||
ScsiPortNotification(
|
||
RequestComplete,
|
||
DeviceExtension,
|
||
DeviceExtension->NextSrbRequest
|
||
);
|
||
|
||
//
|
||
// Make sure the request is not sitting in the logical unit.
|
||
//
|
||
|
||
if (DeviceExtension->NextSrbRequest == luExtension->ActiveLuRequest) {
|
||
|
||
luExtension->ActiveLuRequest = NULL;
|
||
|
||
} else if (DeviceExtension->NextSrbRequest ==
|
||
luExtension->ActiveSendRequest) {
|
||
|
||
luExtension->ActiveSendRequest = NULL;
|
||
}
|
||
|
||
DeviceExtension->NextSrbRequest = NULL;
|
||
DeviceExtension->AdapterFlags &= ~PD_PENDING_START_IO;
|
||
|
||
ScsiPortNotification(
|
||
NextRequest,
|
||
DeviceExtension,
|
||
NULL
|
||
);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Everything looks correct so far.
|
||
//
|
||
|
||
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
||
|
||
//
|
||
// Make sure that status is valid.
|
||
//
|
||
|
||
if (!(SRB_EXT(srb)->SrbExtensionFlags & PD_STATUS_VALID)) {
|
||
|
||
//
|
||
// The status byte is not valid.
|
||
//
|
||
|
||
srb->SrbStatus = SRB_STATUS_PHASE_SEQUENCE_FAILURE;
|
||
|
||
//
|
||
// Log the error.
|
||
//
|
||
|
||
NcrLogError(DeviceExtension, SP_PROTOCOL_ERROR, 12);
|
||
|
||
}
|
||
|
||
|
||
}
|
||
|
||
//
|
||
// Check that data was transferred to the end of the buffer.
|
||
//
|
||
|
||
if ( SRB_EXT(srb)->MaximumTransferLength != srb->DataTransferLength ){
|
||
|
||
//
|
||
// The entire buffer was not transferred. Update the length
|
||
// and update the status code.
|
||
//
|
||
|
||
if (srb->SrbStatus == SRB_STATUS_SUCCESS) {
|
||
|
||
NcrPrint((1, "NcrProcessRequestCompletion: Short transfer, Actual: %x; Expected: %x;\n",
|
||
SRB_EXT(srb)->MaximumTransferLength,
|
||
srb->DataTransferLength
|
||
));
|
||
|
||
//
|
||
// If no data was transferred then indicated this was a
|
||
// protocol error rather than a data under/over run.
|
||
//
|
||
|
||
if (srb->DataTransferLength == 0) {
|
||
srb->SrbStatus = SRB_STATUS_PHASE_SEQUENCE_FAILURE;
|
||
} else {
|
||
srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
|
||
}
|
||
|
||
srb->DataTransferLength = SRB_EXT(srb)->MaximumTransferLength;
|
||
} else {
|
||
|
||
//
|
||
// Update the length if a check condition was returned.
|
||
//
|
||
|
||
if (srb->ScsiStatus == SCSISTAT_CHECK_CONDITION) {
|
||
srb->DataTransferLength = SRB_EXT(srb)->MaximumTransferLength;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (srb->SrbStatus != SRB_STATUS_SUCCESS) {
|
||
NcrPrint((1, "NcrProcessRequestCompletion: Request failed. ScsiStatus: %x, SrbStatus: %x\n",
|
||
srb->ScsiStatus,
|
||
srb->SrbStatus
|
||
));
|
||
}
|
||
|
||
//
|
||
// Clear the request but not the ActiveLogicalUnit since the target has
|
||
// not disconnected from the SCSI bus yet.
|
||
//
|
||
|
||
DeviceExtension->ActiveLuRequest = NULL;
|
||
luExtension->ActiveLuRequest = NULL;
|
||
luExtension->RetryCount = 0;
|
||
luExtension->LuFlags &= ~PD_LU_COMPLETE_MASK;
|
||
}
|
||
|
||
BOOLEAN
|
||
NcrResetScsiBus(
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Indicating the reset is complete.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext;
|
||
|
||
NcrPrint((1, "NcrResetScsiBus: Resetting the SCSI bus.\n"));
|
||
|
||
//
|
||
// The bus should be reset regardless of what is occurring on the bus or in
|
||
// the chip. The reset SCSI bus command executes immediately.
|
||
//
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, RESET_SCSI_BUS);
|
||
|
||
//
|
||
// Delay the minimum assertion time for a SCSI bus reset to make sure a
|
||
// valid reset signal is sent.
|
||
//
|
||
|
||
ScsiPortStallExecution( RESET_STALL_TIME );
|
||
|
||
NcrCleanupAfterReset(deviceExtension, FALSE);
|
||
deviceExtension->AdapterFlags |= PD_EXPECTING_RESET_INTERRUPT;
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
VOID
|
||
NcrResetScsiBusInternal(
|
||
IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
|
||
IN ULONG PathId
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function resets the SCSI bus and notifies the port driver.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - Supplies a pointer to the specific device extension.
|
||
|
||
PathId - Supplies the path id of the bus.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
|
||
ScsiPortNotification(
|
||
ResetDetected,
|
||
DeviceExtension,
|
||
NULL
|
||
);
|
||
|
||
NcrResetScsiBus(DeviceExtension, 0);
|
||
}
|
||
|
||
VOID
|
||
NcrSelectTarget(
|
||
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
|
||
|
||
--*/
|
||
|
||
{
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
PSPECIFIC_TARGET_EXTENSION targetState;
|
||
SCSI_DMA_STATUS DmaStatus;
|
||
LONG i;
|
||
|
||
srb = DeviceExtension->NextSrbRequest;
|
||
|
||
targetState = &DeviceExtension->TargetState[srb->TargetId];
|
||
|
||
#if DBG
|
||
if (NcrDebug) {
|
||
NcrPrint((0, "NcrSelectTarget: 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 FIFO with the IDENTIFY message and the CDB
|
||
// setting the command register
|
||
//
|
||
// If the chip is not interrupting, then set up for selection. If the
|
||
// chip is interrupting then return. The interrupt will process the
|
||
// request. Note that if we get reselected after this point the chip
|
||
// will ignore the bytes written until the interrupt register is read.
|
||
// The commands that handle a message and a CDB can only be used if the
|
||
// message is one byte or 3 bytes long; otherwise only a one-byte message
|
||
// is transferred on the select and the remaining bytes are handled in the
|
||
// interrupt routine.
|
||
//
|
||
|
||
if (DeviceExtension->AdapterFlags & PD_NCR_ADAPTER) {
|
||
|
||
*((PUCHAR) &DmaStatus) = SCSI_READ( DeviceExtension->AdapterBase, DmaStatus );
|
||
if (DmaStatus.Interrupt == DeviceExtension->InterruptPending) {
|
||
return;
|
||
}
|
||
|
||
} else {
|
||
|
||
*((PUCHAR) &DeviceExtension->AdapterStatus) = SCSI_READ( DeviceExtension->Adapter, ScsiStatus );
|
||
if (DeviceExtension->AdapterStatus.Interrupt) {
|
||
return;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Set the destination ID. Put the first byte of the message-in
|
||
// the fifo and set the command to select with ATN. This command
|
||
// selects the target, sends one message byte and interrupts. The
|
||
// ATN line remains set. The succeeding bytes are loaded into the
|
||
// FIFO and sent to the target by the interrupt service routine.
|
||
//
|
||
|
||
SCSI_WRITE(DeviceExtension->Adapter, DestinationId, srb->TargetId);
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
Fifo,
|
||
DeviceExtension->MessageBuffer[DeviceExtension->MessageSent++]
|
||
);
|
||
|
||
//
|
||
// Set the synchronous data transfer parameter registers in case a
|
||
// data transfer is done. These must be set before a data transfer
|
||
// is started.
|
||
//
|
||
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
SynchronousPeriod,
|
||
targetState->SynchronousPeriod
|
||
);
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
SynchronousOffset,
|
||
targetState->SynchronousOffset
|
||
);
|
||
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
Configuration3,
|
||
*((PCHAR) &targetState->Configuration3)
|
||
);
|
||
|
||
|
||
//
|
||
// 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 fifo; otherwise, just
|
||
// load the first byte of the message.
|
||
//
|
||
|
||
if (srb->Function == SRB_FUNCTION_EXECUTE_SCSI &&
|
||
(DeviceExtension->MessageCount == 1 ||
|
||
DeviceExtension->MessageCount == 3)) {
|
||
|
||
//
|
||
// Copy the entire message and Cdb into the fifo.
|
||
//
|
||
|
||
for (;
|
||
DeviceExtension->MessageSent <
|
||
DeviceExtension->MessageCount;
|
||
DeviceExtension->MessageSent++ ) {
|
||
|
||
SCSI_WRITE( DeviceExtension->Adapter,
|
||
Fifo,
|
||
DeviceExtension->
|
||
MessageBuffer[DeviceExtension->MessageSent]
|
||
);
|
||
|
||
}
|
||
|
||
for (i = 0; i < srb->CdbLength; i++) {
|
||
SCSI_WRITE(DeviceExtension->Adapter,
|
||
Fifo,
|
||
srb->Cdb[i]
|
||
);
|
||
}
|
||
|
||
if (DeviceExtension->MessageCount == 1) {
|
||
|
||
//
|
||
// One message byte so use select with attention which uses one
|
||
// message byte.
|
||
//
|
||
|
||
SCSI_WRITE(
|
||
DeviceExtension->Adapter,
|
||
Command,
|
||
SELECT_WITH_ATTENTION
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Three byte message, so use the select with attention which uses
|
||
// three byte messages.
|
||
//
|
||
|
||
SCSI_WRITE(
|
||
DeviceExtension->Adapter,
|
||
Command,
|
||
SELECT_WITH_ATTENTION3
|
||
);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Only the first byte of the message can be sent so select with
|
||
// ATTENTION and the target will request the rest.
|
||
//
|
||
|
||
SCSI_WRITE(
|
||
DeviceExtension->Adapter,
|
||
Command,
|
||
SELECT_WITH_ATTENTION_STOP
|
||
);
|
||
|
||
}
|
||
|
||
/* Powerfail release */
|
||
|
||
//
|
||
// Set the device state to message-out and indicate that a message
|
||
// is being sent.
|
||
//
|
||
|
||
DeviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID;
|
||
DeviceExtension->AdapterState = AttemptingSelect;
|
||
DeviceExtension->InterruptCount = 0;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
NcrSendMessage(
|
||
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 INDENTIFY message prefixed
|
||
to 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;
|
||
BOOLEAN useTag;
|
||
UCHAR message;
|
||
|
||
impliesDisconnect = FALSE;
|
||
useTag = FALSE;
|
||
|
||
//
|
||
// Decode the type of message.
|
||
//
|
||
|
||
switch (Srb->Function) {
|
||
|
||
case SRB_FUNCTION_TERMINATE_IO:
|
||
case SRB_FUNCTION_ABORT_COMMAND:
|
||
|
||
//
|
||
// Verify that the request is being processed by the logical unit.
|
||
//
|
||
|
||
linkedSrb = ScsiPortGetSrb(
|
||
DeviceExtension,
|
||
Srb->PathId,
|
||
Srb->TargetId,
|
||
Srb->Lun,
|
||
Srb->QueueTag
|
||
);
|
||
|
||
if (linkedSrb != Srb->NextSrb) {
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND) {
|
||
impliesDisconnect = TRUE;
|
||
message = SCSIMESS_ABORT;
|
||
} else {
|
||
message = SCSIMESS_TERMINATE_IO_PROCESS;
|
||
impliesDisconnect = FALSE;
|
||
}
|
||
|
||
//
|
||
// Use a tagged message if the original request was tagged.
|
||
//
|
||
|
||
useTag = linkedSrb->SrbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE ?
|
||
TRUE : FALSE;
|
||
|
||
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 SRB_FUNCTION_RELEASE_RECOVERY:
|
||
|
||
//
|
||
// These messages require an IDENTIFY message and imply a disconnect.
|
||
//
|
||
|
||
impliesDisconnect = TRUE;
|
||
message = SCSIMESS_RELEASE_RECOVERY;
|
||
break;
|
||
|
||
case SCSIMESS_CLEAR_QUEUE:
|
||
|
||
//
|
||
// These messages require an IDENTIFY message and imply a disconnect.
|
||
//
|
||
|
||
message = SCSIMESS_CLEAR_QUEUE;
|
||
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;
|
||
|
||
if (useTag && Srb->QueueTag != SP_UNTAGGED) {
|
||
DeviceExtension->MessageBuffer[DeviceExtension->MessageCount++] = SCSIMESS_SIMPLE_QUEUE_TAG;
|
||
DeviceExtension->MessageBuffer[DeviceExtension->MessageCount++] = Srb->QueueTag;
|
||
|
||
if (message == SCSIMESS_ABORT) {
|
||
message = SCSIMESS_ABORT_WITH_TAG;
|
||
}
|
||
}
|
||
|
||
DeviceExtension->MessageBuffer[DeviceExtension->MessageCount++] = message;
|
||
|
||
//
|
||
// Attempt to select the target and update the adapter flags.
|
||
//
|
||
|
||
NcrSelectTarget( DeviceExtension, LuExtension );
|
||
|
||
DeviceExtension->AdapterFlags |= impliesDisconnect ?
|
||
PD_DISCONNECT_EXPECTED | PD_SEND_MESSAGE_REQUEST
|
||
: PD_SEND_MESSAGE_REQUEST;
|
||
|
||
}
|
||
|
||
VOID
|
||
NcrStartExecution(
|
||
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 NCR 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 requst.
|
||
|
||
Notes:
|
||
|
||
This routine must be synchronized with the interrupt routine.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSCSI_EXTENDED_MESSAGE extendedMessage;
|
||
CHIP_TYPES chipType;
|
||
PSPECIFIC_TARGET_EXTENSION targetState;
|
||
|
||
//
|
||
// Save away the parameters in case nothing can be done now.
|
||
//
|
||
|
||
SRB_EXT(Srb)->SavedDataPointer = (ULONG) Srb->DataBuffer;
|
||
SRB_EXT(Srb)->SavedDataLength = Srb->DataTransferLength;
|
||
SRB_EXT(Srb)->SrbExtensionFlags = 0;
|
||
SRB_EXT(Srb)->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->MessageCount = 1;
|
||
DeviceExtension->MessageSent = 0;
|
||
DeviceExtension->AdapterFlags |= PD_MESSAGE_OUT_VALID;
|
||
|
||
DeviceExtension->MessageBuffer[0] = SCSIMESS_IDENTIFY | Srb->Lun;
|
||
|
||
DeviceExtension->TargetId = Srb->TargetId;
|
||
targetState = &DeviceExtension->TargetState[Srb->TargetId];
|
||
|
||
//
|
||
// Check to see if disconnect is allowed. If not then don't do tagged
|
||
// queuing either.
|
||
//
|
||
|
||
if (!(Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT)) {
|
||
|
||
//
|
||
// Enable disconnects in the message.
|
||
//
|
||
|
||
DeviceExtension->MessageBuffer[0] |= SCSIMESS_IDENTIFY_WITH_DISCON;
|
||
|
||
//
|
||
// If this is a tagged command then create a tagged message.
|
||
//
|
||
|
||
if (Srb->SrbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE) {
|
||
|
||
//
|
||
// The queue tag message is two bytes the first is the queue action
|
||
// and the second is the queue tag.
|
||
//
|
||
|
||
DeviceExtension->MessageBuffer[1] = Srb->QueueAction;
|
||
DeviceExtension->MessageBuffer[2] = Srb->QueueTag;
|
||
DeviceExtension->MessageCount += 2;
|
||
DeviceExtension->AdapterFlags |= PD_TAGGED_SELECT;
|
||
|
||
} else {
|
||
LuExtension->ActiveLuRequest = Srb;
|
||
}
|
||
|
||
} else {
|
||
LuExtension->ActiveLuRequest = Srb;
|
||
}
|
||
|
||
//
|
||
// Check to see if synchronous negotiation is necessary.
|
||
//
|
||
|
||
if (!(targetState->TargetFlags &
|
||
(PD_SYNCHRONOUS_NEGOTIATION_DONE | PD_DO_NOT_NEGOTIATE))) {
|
||
|
||
//
|
||
// Initialize the synchronous transfer register values to an
|
||
// asynchronous transfer, which is what will be used if anything
|
||
// goes wrong with the negotiation.
|
||
//
|
||
|
||
targetState->SynchronousOffset = ASYNCHRONOUS_OFFSET;
|
||
targetState->SynchronousPeriod = ASYNCHRONOUS_PERIOD;
|
||
targetState->Configuration3 = DeviceExtension->Configuration3;
|
||
|
||
if (Srb->SrbFlags & SRB_FLAGS_DISABLE_SYNCH_TRANSFER) {
|
||
|
||
//
|
||
// Synchronous transfers are disabled by the SRB.
|
||
//
|
||
|
||
NcrSelectTarget( DeviceExtension, LuExtension );
|
||
return;
|
||
}
|
||
|
||
if (DeviceExtension->ChipType == Fas216) {
|
||
|
||
//
|
||
// The Fas216 supports fast synchronous transfers.
|
||
//
|
||
|
||
chipType = Fas216Fast;
|
||
|
||
} else if (DeviceExtension->ChipType == Ncr53c90) {
|
||
|
||
//
|
||
// The 53c90 does not support synchronous transfers.
|
||
// Set the do not negotate flag in the logical unit structure.
|
||
//
|
||
|
||
targetState->TargetFlags |= PD_DO_NOT_NEGOTIATE;
|
||
NcrSelectTarget( DeviceExtension, LuExtension );
|
||
return;
|
||
|
||
} else {
|
||
|
||
chipType = DeviceExtension->ChipType;
|
||
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
|
||
//
|
||
// If this chips does not suport fast SCSI, just calculate the normal
|
||
// minimum transfer period; otherwise use the fast value.
|
||
//
|
||
|
||
//
|
||
// The initial sychronous transfer period is:
|
||
//
|
||
// SynchronousPeriodCyles * 1000
|
||
// -----------------------------
|
||
// ClockSpeed * 4
|
||
//
|
||
// Note the result of the divide by four must be rounded up.
|
||
//
|
||
|
||
extendedMessage->ExtendedArguments.Synchronous.TransferPeriod =
|
||
((SynchronousTransferTypes[chipType].SynchronousPeriodCyles * 1000) /
|
||
DeviceExtension->ClockSpeed + 3) / 4;
|
||
extendedMessage->ExtendedArguments.Synchronous.ReqAckOffset = SYNCHRONOUS_OFFSET;
|
||
|
||
//
|
||
// Attempt to select the target and update the adapter flags.
|
||
//
|
||
|
||
NcrSelectTarget( 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;
|
||
}
|
||
|
||
NcrSelectTarget( DeviceExtension, LuExtension );
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
NcrStartIo(
|
||
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 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:
|
||
|
||
//
|
||
// Determine the logical unit that this request is for.
|
||
//
|
||
|
||
luExtension = ScsiPortGetLogicalUnit(
|
||
deviceExtension,
|
||
Srb->PathId,
|
||
Srb->TargetId,
|
||
Srb->Lun
|
||
);
|
||
|
||
NcrStartExecution(
|
||
Srb,
|
||
deviceExtension,
|
||
luExtension
|
||
);
|
||
|
||
return(TRUE);
|
||
|
||
case SRB_FUNCTION_ABORT_COMMAND:
|
||
case SRB_FUNCTION_RESET_DEVICE:
|
||
case SRB_FUNCTION_TERMINATE_IO:
|
||
|
||
//
|
||
// Determine the logical unit that this request is for.
|
||
//
|
||
|
||
luExtension = ScsiPortGetLogicalUnit(
|
||
deviceExtension,
|
||
Srb->PathId,
|
||
Srb->TargetId,
|
||
Srb->Lun
|
||
);
|
||
|
||
NcrSendMessage(
|
||
Srb,
|
||
deviceExtension,
|
||
luExtension
|
||
);
|
||
|
||
return(TRUE);
|
||
|
||
case SRB_FUNCTION_RESET_BUS:
|
||
|
||
//
|
||
// There is no logical unit so just reset the bus.
|
||
//
|
||
|
||
NcrResetScsiBusInternal( deviceExtension, 0 );
|
||
return(TRUE);
|
||
|
||
default:
|
||
|
||
//
|
||
// 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);
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
NcrStartDataTransfer(
|
||
IN PVOID ServiceContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets up the scsi bus protocol chip to perform a data transfer.
|
||
It is called after the DMA has been initialized.
|
||
|
||
Arguments:
|
||
|
||
ServiceContext - Supplies a pointer to the specific device extension.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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;
|
||
|
||
}
|
||
|
||
//
|
||
// Set up the SCSI protocol chip for the data transfer with the
|
||
// command to start.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter,
|
||
Command,
|
||
TRANSFER_INFORMATION_DMA
|
||
);
|
||
|
||
deviceExtension->AdapterFlags &= ~PD_PENDING_DATA_TRANSFER;
|
||
|
||
}
|
||
|
||
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;
|
||
ULONG i;
|
||
ULONG Status1;
|
||
ULONG Status2;
|
||
INIT_DATA InitData;
|
||
|
||
ScsiDebugPrint(1,"\n\nNCR 53c9x SCSI MiniPort Driver\n");
|
||
|
||
//
|
||
// Zero out hardware initialization data structure.
|
||
//
|
||
|
||
for (i=0; i<sizeof(HW_INITIALIZATION_DATA); i++) {
|
||
((PUCHAR)&hwInitializationData)[i] = 0;
|
||
}
|
||
|
||
//
|
||
// Fill in the hardware initialization data structure.
|
||
//
|
||
|
||
hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
|
||
hwInitializationData.HwInitialize = NcrInitializeAdapter;
|
||
hwInitializationData.HwStartIo = NcrStartIo;
|
||
hwInitializationData.HwInterrupt = NcrInterruptServiceRoutine;
|
||
hwInitializationData.HwFindAdapter = NcrMcaFindAdapter;
|
||
hwInitializationData.HwResetBus = NcrResetScsiBus;
|
||
hwInitializationData.HwDmaStarted = NcrStartDataTransfer;
|
||
hwInitializationData.NumberOfAccessRanges = 1;
|
||
hwInitializationData.AdapterInterfaceType = MicroChannel;
|
||
hwInitializationData.DeviceExtensionSize = sizeof(SPECIFIC_DEVICE_EXTENSION);
|
||
hwInitializationData.SpecificLuExtensionSize =
|
||
sizeof(SPECIFIC_LOGICAL_UNIT_EXTENSION);
|
||
hwInitializationData.SrbExtensionSize = sizeof(SRB_EXTENSION);
|
||
|
||
//
|
||
// Initialize configuration information.
|
||
//
|
||
// The following adapter search order should be observed:
|
||
// 1. Onboard 53c94 scsi host adapter
|
||
// 2. Onboard 53c90 scsi host adapter
|
||
// 3. Plug-in 53c90 scsi host adapter
|
||
//
|
||
|
||
InitData.AdapterId = 0;
|
||
InitData.CardSlot = 7;
|
||
|
||
Status1 = ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &InitData);
|
||
|
||
//
|
||
// Look for internal mips adapter.
|
||
//
|
||
|
||
hwInitializationData.AdapterInterfaceType = Internal;
|
||
hwInitializationData.HwFindAdapter = NcrMipsFindAdapter;
|
||
|
||
Status2 = ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &InitData);
|
||
|
||
Status1 = Status2 < Status1 ? Status2 : Status1;
|
||
|
||
//
|
||
// Look for an Eisa adapter.
|
||
//
|
||
|
||
InitData.AdapterId = 0;
|
||
hwInitializationData.AdapterInterfaceType = Eisa;
|
||
hwInitializationData.HwFindAdapter = NcrEisaFindAdapter;
|
||
|
||
Status2 = ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &InitData);
|
||
|
||
return(Status2 < Status1 ? Status2 : Status1);
|
||
|
||
} // end PortInitialize()
|
||
|
||
ULONG
|
||
NcrMcaFindAdapter(
|
||
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. This
|
||
routine is temporary until the configuration manager supplies similar
|
||
informtion. It also fills in the capabilities structure.
|
||
|
||
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 initialazitaition.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext;
|
||
PINIT_DATA InitData = Context;
|
||
LONG Slot;
|
||
LONG i;
|
||
ULONG Status = SP_RETURN_NOT_FOUND;
|
||
|
||
//
|
||
// Determine if bus information array needs to be filled in.
|
||
//
|
||
|
||
if ( InitData->AdapterId == 0 ) {
|
||
|
||
//
|
||
// Fill in the POS data structure.
|
||
//
|
||
|
||
for ( Slot = 0; Slot < 8; Slot++ ) {
|
||
|
||
i = ScsiPortGetBusData ( deviceExtension,
|
||
Pos,
|
||
0,
|
||
Slot,
|
||
&InitData->PosData[Slot],
|
||
sizeof( POS_DATA )
|
||
);
|
||
|
||
//
|
||
// If less that the requested amount of data is returned, then
|
||
// insure that this adapter is ignored.
|
||
//
|
||
|
||
if ( i < (sizeof( POS_DATA ))) {
|
||
InitData->PosData[Slot].AdapterId = 0xffff;
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
for ( Slot = InitData->CardSlot; Slot >= 0; Slot-- ) {
|
||
if (( InitData->PosData[Slot].AdapterId == ONBOARD_C94_ADAPTER_ID ||
|
||
InitData->PosData[Slot].AdapterId == ONBOARD_C90_ADAPTER_ID ||
|
||
InitData->PosData[Slot].AdapterId == PLUGIN_C90_ADAPTER_ID ) &&
|
||
(*(PPOS_DATA_1)&InitData->PosData[Slot].OptionData1).AdapterEnable) {
|
||
InitData->CardSlot = Slot - 1;
|
||
Status = SP_RETURN_FOUND;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( Status == SP_RETURN_FOUND ) {
|
||
|
||
*Again = TRUE;
|
||
|
||
deviceExtension->AdapterBase = (PVOID) AdapterBaseAddress[
|
||
(*(PPOS_DATA_1) &InitData->PosData[Slot].OptionData1).IoAddressSelects];
|
||
deviceExtension->Adapter = (PSCSI_REGISTERS)
|
||
((PCHAR) deviceExtension->AdapterBase + sizeof(ADAPTER_REGISTERS));
|
||
if ( InitData->PosData[Slot].AdapterId == ONBOARD_C94_ADAPTER_ID ) {
|
||
deviceExtension->ChipType = Ncr53c94;
|
||
|
||
// RDR This isn't filled in before use in the new code!!!
|
||
|
||
deviceExtension->AdapterBusId = AdapterScsiIdC94[
|
||
(*(PPOS_DATA_3) &InitData->PosData[Slot].OptionData3).HostIdSelects];
|
||
deviceExtension->InterruptPending = FALSE;
|
||
} else {
|
||
deviceExtension->ChipType = Ncr53c90;
|
||
deviceExtension->AdapterBusId = AdapterScsiIdC90[
|
||
(*(PPOS_DATA_4) &InitData->PosData[Slot].OptionData4).HostIdSelects];
|
||
deviceExtension->InterruptPending = TRUE;
|
||
}
|
||
|
||
deviceExtension->AdapterBusIdMask = 1 << deviceExtension->AdapterBusId;
|
||
|
||
//
|
||
// Set configuration information
|
||
//
|
||
|
||
ConfigInfo->NumberOfPhysicalBreaks = 16;
|
||
ConfigInfo->BusInterruptLevel = AdapterInterruptLevel[
|
||
(*(PPOS_DATA_1) &InitData->PosData[Slot].OptionData1).InterruptSelects];
|
||
ConfigInfo->InitiatorBusId[0] = deviceExtension->AdapterBusId;
|
||
ConfigInfo->DmaChannel = AdapterDmaLevel[
|
||
(*(PPOS_DATA_2) &InitData->PosData[Slot].OptionData2).DmaSelects];
|
||
ConfigInfo->DmaPort = (ULONG) deviceExtension->AdapterBase + (ULONG)
|
||
&((PADAPTER_WRITE_REGISTERS) 0)->DmaDecode;
|
||
if ( InitData->PosData[Slot].AdapterId == ONBOARD_C94_ADAPTER_ID ) {
|
||
ConfigInfo->DmaWidth = Width16Bits;
|
||
ConfigInfo->AlignmentMask = 1;
|
||
ConfigInfo->AdapterScansDown = TRUE;
|
||
ConfigInfo->TaggedQueuing = 1;
|
||
} else {
|
||
ConfigInfo->DmaWidth = Width8Bits;
|
||
}
|
||
|
||
ConfigInfo->DmaSpeed = Compatible;
|
||
ConfigInfo->MaximumTransferLength = 0xf000;
|
||
ConfigInfo->NumberOfBuses = 1;
|
||
|
||
//
|
||
// Fill in the access array information.
|
||
//
|
||
|
||
(*ConfigInfo->AccessRanges)[0].RangeStart =
|
||
ScsiPortConvertUlongToPhysicalAddress((ULONG) deviceExtension->AdapterBase);
|
||
(*ConfigInfo->AccessRanges)[0].RangeLength = sizeof(ADAPTER_REGISTERS) +
|
||
sizeof(SCSI_REGISTERS);
|
||
(*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
|
||
|
||
//
|
||
// Initialize hardware.
|
||
//
|
||
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, RESET_SCSI_CHIP);
|
||
SCSI_WRITE(deviceExtension->Adapter, Command, NO_OPERATION_DMA);
|
||
|
||
deviceExtension->ClockSpeed = 14; // set clock speed at 14 Mhz.
|
||
deviceExtension->AdapterFlags |= PD_NCR_ADAPTER;
|
||
|
||
ScsiDebugPrint(1, " ScsiId = %x\n", ConfigInfo->InitiatorBusId[0]);
|
||
ScsiDebugPrint(1, " IoBase = %2x\n", deviceExtension->Adapter);
|
||
ScsiDebugPrint(1, " Irq = %x\n", ConfigInfo->BusInterruptLevel);
|
||
ScsiDebugPrint(1, " Dma = %x\n", ConfigInfo->DmaChannel);
|
||
|
||
} else {
|
||
|
||
*Again = FALSE;
|
||
|
||
}
|
||
|
||
return(Status);
|
||
|
||
}
|
||
|
||
ULONG
|
||
NcrEisaFindAdapter(
|
||
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. This
|
||
routine is temporary until the configuration manager supplies similar
|
||
informtion. It also fills in the capabilities structure.
|
||
|
||
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 initialazitaition.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext;
|
||
PCM_EISA_FUNCTION_INFORMATION functionInformation;
|
||
PCM_EISA_SLOT_INFORMATION slotInformation;
|
||
UCHAR dataByte;
|
||
PUCHAR configurationRegister;
|
||
ULONG boardId;
|
||
ULONG numberOfFunctions;
|
||
ULONG slotNumber;
|
||
|
||
*Again = FALSE;
|
||
|
||
for (slotNumber = 0; slotNumber < 16; slotNumber++) {
|
||
|
||
boardId = ScsiPortGetBusData( deviceExtension,
|
||
EisaConfiguration,
|
||
ConfigInfo->SystemIoBusNumber,
|
||
slotNumber,
|
||
&slotInformation,
|
||
0);
|
||
|
||
if (boardId == 0 || slotInformation == NULL) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Calculate the actual number of functions returned.
|
||
//
|
||
|
||
numberOfFunctions = (boardId - sizeof(CM_EISA_SLOT_INFORMATION)) /
|
||
sizeof(CM_EISA_FUNCTION_INFORMATION);
|
||
|
||
if (numberOfFunctions > (ULONG) slotInformation->NumberFunctions) {
|
||
numberOfFunctions = slotInformation->NumberFunctions;
|
||
}
|
||
|
||
functionInformation = (PCM_EISA_FUNCTION_INFORMATION) (slotInformation + 1);
|
||
for (; 0 < numberOfFunctions; numberOfFunctions--, functionInformation++) {
|
||
|
||
if (!(functionInformation->FunctionFlags & EISA_FUNCTION_ENABLED) &&
|
||
!strcmp(functionInformation->TypeString, "MSD,CDROM1")) {
|
||
|
||
DebugPrint((1, "NcrEisaFindAdapter: Found type string. Function information: %lx\n", functionInformation));
|
||
ConfigInfo->DmaWidth = Width8Bits;
|
||
ConfigInfo->DmaSpeed = Compatible;
|
||
deviceExtension->ChipType = Ncr53c90;
|
||
goto found;
|
||
|
||
} else if (!(functionInformation->FunctionFlags & EISA_FUNCTION_ENABLED) &&
|
||
!strcmp(functionInformation->TypeString, "MSD,SCSI,C94")) {
|
||
|
||
DebugPrint((1, "NcrEisaFindAdapter: Found type string. Function information: %lx\n", functionInformation));
|
||
ConfigInfo->DmaWidth = Width16Bits;
|
||
ConfigInfo->AlignmentMask = 1;
|
||
ConfigInfo->DmaSpeed = TypeB;
|
||
ConfigInfo->MaximumTransferLength = 0x10000;
|
||
deviceExtension->ChipType = Ncr53c94;
|
||
goto found;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Determine if this is the correct Eisa board.
|
||
//
|
||
|
||
if (slotNumber >= 16) {
|
||
|
||
//
|
||
// The device was not found.
|
||
//
|
||
|
||
return(SP_RETURN_NOT_FOUND);
|
||
}
|
||
|
||
found:
|
||
|
||
ConfigInfo->BusInterruptVector = 0;
|
||
ConfigInfo->NumberOfBuses = 1;
|
||
|
||
//
|
||
// Get the SCSI bus Id from the configuration information if there
|
||
// is any.
|
||
//
|
||
|
||
if (ConfigInfo->InitiatorBusId[0] == (CCHAR) SP_UNINITIALIZED_VALUE) {
|
||
deviceExtension->AdapterBusId = INITIATOR_BUS_ID;
|
||
deviceExtension->AdapterBusIdMask = 1 << INITIATOR_BUS_ID;
|
||
ConfigInfo->InitiatorBusId[0] = INITIATOR_BUS_ID;
|
||
} else {
|
||
deviceExtension->AdapterBusId = ConfigInfo->InitiatorBusId[0];
|
||
deviceExtension->AdapterBusIdMask = 1 << ConfigInfo->InitiatorBusId[0];
|
||
}
|
||
|
||
configurationRegister = ScsiPortGetDeviceBase(
|
||
deviceExtension, // HwDeviceExtension
|
||
ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType
|
||
ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber
|
||
ScsiPortConvertUlongToPhysicalAddress(functionInformation->EisaPort->PortAddress),
|
||
functionInformation->EisaPort->Configuration.NumberPorts, // NumberOfBytes
|
||
TRUE // InIoSpace
|
||
);
|
||
|
||
if (configurationRegister == (PUCHAR) SP_UNINITIALIZED_VALUE) {
|
||
return(SP_RETURN_ERROR);
|
||
}
|
||
|
||
deviceExtension->ClockSpeed = 25; // Assume a 25 Mhz clock speed.
|
||
deviceExtension->Adapter = (PSCSI_REGISTERS) configurationRegister;
|
||
|
||
//
|
||
// The Emulex chip loads the TransferCountPage register with the chip id,
|
||
// if the EnablePhaseLatch is set and a NOP DMA command has been loaded.
|
||
//
|
||
|
||
if (deviceExtension->ChipType == Ncr53c94) {
|
||
|
||
//
|
||
// Now write to the command register with a reset.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, RESET_SCSI_CHIP );
|
||
|
||
//
|
||
// A NOP command is required by the FAS218 to
|
||
// load the TransferCountPage register with the chip Id.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, NO_OPERATION_DMA );
|
||
|
||
dataByte = SCSI_READ( deviceExtension->Adapter, TransferCountPage);
|
||
|
||
if (((PNCR_PART_CODE) &dataByte)->ChipFamily == EMULEX_FAS_216) {
|
||
|
||
deviceExtension->ChipType = Fas216;
|
||
} else if (((PNCR_PART_CODE) &dataByte)->ChipFamily == NCR_53c96) {
|
||
|
||
NcrPrint((1, "NcrFindAdapter: NCR 53c96 chip detected.\n"));
|
||
deviceExtension->ChipType = Fas216;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set the parameters according to the chip type.
|
||
//
|
||
|
||
switch (deviceExtension->ChipType) {
|
||
case Ncr53c94:
|
||
|
||
NcrPrint((1, "NcrFindAdapter: Ncr 53C94 chip detected.\n"));
|
||
ConfigInfo->TaggedQueuing = 1;
|
||
ConfigInfo->MaximumTransferLength = 0x10000;
|
||
break;
|
||
|
||
case Fas216:
|
||
NcrPrint((1, "NcrFindAdapter: Emulex FAS 216 chip detected.\n"));
|
||
deviceExtension->ClockSpeed = EMULEX_SCSI_CLOCK_SPEED;
|
||
ConfigInfo->TaggedQueuing = 1;
|
||
ConfigInfo->MaximumTransferLength = 0x1000000-0x1000;
|
||
deviceExtension->Configuration3.CheckIdMessage = 1;
|
||
break;
|
||
|
||
case Ncr53c90:
|
||
|
||
NcrPrint((1, "NcrFindAdapter: Ncr 53C90 chip detected.\n"));
|
||
ConfigInfo->MaximumTransferLength = 0x10000 - 0x1000;
|
||
break;
|
||
|
||
default:
|
||
*Again = FALSE;
|
||
return(SP_RETURN_BAD_CONFIG);
|
||
}
|
||
|
||
//
|
||
// If the clock speed is greater than 25 Mhz then set the fast clock
|
||
// bit in configuration register.
|
||
//
|
||
|
||
if (deviceExtension->ClockSpeed > 25) {
|
||
deviceExtension->Configuration3.FastClock = 1;
|
||
}
|
||
|
||
ConfigInfo->DmaChannel =
|
||
functionInformation->EisaDma->ConfigurationByte0.Channel;
|
||
ConfigInfo->BusInterruptLevel =
|
||
functionInformation->EisaIrq->ConfigurationByte.Interrupt;
|
||
ConfigInfo->InterruptMode =
|
||
functionInformation->EisaIrq->ConfigurationByte.LevelTriggered ?
|
||
LevelSensitive : Latched;
|
||
|
||
//
|
||
// Fill in the access array information.
|
||
//
|
||
|
||
(*ConfigInfo->AccessRanges)[0].RangeStart =
|
||
ScsiPortConvertUlongToPhysicalAddress(
|
||
functionInformation->EisaPort->PortAddress);
|
||
(*ConfigInfo->AccessRanges)[0].RangeLength =
|
||
functionInformation->EisaPort->Configuration.NumberPorts;
|
||
(*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
|
||
|
||
//
|
||
// Make sure the interrupt and DMA channel are properly configured.
|
||
//
|
||
|
||
if (ConfigInfo->DmaChannel == (UCHAR) ~0 ||
|
||
ConfigInfo->BusInterruptLevel == (UCHAR) ~0) {
|
||
|
||
return(SP_RETURN_BAD_CONFIG);
|
||
}
|
||
|
||
//
|
||
// Test for the interrupt bit in the status register. Some chips do not
|
||
// implement this bit; however, this driver assumes that it exists.
|
||
// The itnerrupt bit is tested for by reseting the chip, giving an
|
||
// illegal command, and checking for an gross error interrupt.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, RESET_SCSI_CHIP );
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, NO_OPERATION_DMA );
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, COMMAND_COMPLETE );
|
||
|
||
*((PUCHAR) &deviceExtension->AdapterStatus) = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
ScsiStatus
|
||
);
|
||
|
||
dataByte = SCSI_READ( deviceExtension->Adapter, ScsiInterrupt );
|
||
|
||
//
|
||
// Test for the interrupt.
|
||
//
|
||
|
||
if (!deviceExtension->AdapterStatus.Interrupt) {
|
||
|
||
NcrPrint((0, "\nNcrEisaFindAdapter: Ncr53c90 chip without status register interrupt detected.\n"));
|
||
NcrPrint((0, "NcrInterrupt: Adapter Status: %2x; Adapter Interrupt: %2x\n",
|
||
*((PUCHAR) &deviceExtension->AdapterStatus),
|
||
dataByte
|
||
));
|
||
|
||
ScsiPortFreeDeviceBase(deviceExtension, deviceExtension->Adapter);
|
||
return(SP_RETURN_BAD_CONFIG);
|
||
}
|
||
|
||
//
|
||
// Now write to the command register with a reset and a DMA nop.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, RESET_SCSI_CHIP );
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, NO_OPERATION_DMA );
|
||
|
||
return(SP_RETURN_FOUND);
|
||
|
||
}
|
||
|
||
ULONG
|
||
NcrMipsFindAdapter(
|
||
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 mapes
|
||
the SCSI protocol chip for access. This routine is temporary until
|
||
the configuration manager supplies similar informtion.
|
||
|
||
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 initialazitaition.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSPECIFIC_DEVICE_EXTENSION deviceExtension = ServiceContext;
|
||
UCHAR dataByte;
|
||
UCHAR commandSave;
|
||
|
||
if (ConfigInfo->DmaChannel == SP_UNINITIALIZED_VALUE
|
||
|| ConfigInfo->BusInterruptLevel == 0 ||
|
||
(*ConfigInfo->AccessRanges)[0].RangeLength == 0) {
|
||
return(SP_RETURN_NOT_FOUND);
|
||
}
|
||
|
||
ConfigInfo->NumberOfBuses = 1;
|
||
|
||
//
|
||
// Get the SCSI bus Id from the configuration information if there
|
||
// is any.
|
||
//
|
||
|
||
if (ConfigInfo->InitiatorBusId[0] == (CCHAR) SP_UNINITIALIZED_VALUE) {
|
||
deviceExtension->AdapterBusId = INITIATOR_BUS_ID;
|
||
deviceExtension->AdapterBusIdMask = 1 << INITIATOR_BUS_ID;
|
||
ConfigInfo->InitiatorBusId[0] = INITIATOR_BUS_ID;
|
||
} else {
|
||
deviceExtension->AdapterBusId = ConfigInfo->InitiatorBusId[0];
|
||
deviceExtension->AdapterBusIdMask = 1 << ConfigInfo->InitiatorBusId[0];
|
||
}
|
||
|
||
//
|
||
// Map the SCSI protocol chip into the virtual address space.
|
||
//
|
||
|
||
deviceExtension->Adapter = ScsiPortGetDeviceBase(
|
||
deviceExtension, // HwDeviceExtension
|
||
ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType
|
||
ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber
|
||
(*ConfigInfo->AccessRanges)[0].RangeStart,
|
||
(*ConfigInfo->AccessRanges)[0].RangeLength, // NumberOfBytes
|
||
FALSE // InIoSpace
|
||
);
|
||
|
||
if (deviceExtension->Adapter == NULL) {
|
||
NcrPrint((0, "\nScsiPortInitialize: Failed to map SCSI device registers into system space.\n"));
|
||
return(SP_RETURN_ERROR);
|
||
}
|
||
|
||
//
|
||
// Check the iterrupt register. If it is not equal zero then read it
|
||
// agian it should now be zero.
|
||
//
|
||
|
||
dataByte = SCSI_READ( deviceExtension->Adapter, ScsiInterrupt );
|
||
|
||
if (dataByte != 0) {
|
||
|
||
ScsiPortStallExecution(INTERRUPT_STALL_TIME);
|
||
dataByte = SCSI_READ( deviceExtension->Adapter, ScsiInterrupt );
|
||
|
||
if (dataByte != 0 ) {
|
||
NcrPrint((1, "NcrMipsFindAdapter: No Ncr53c9x chip found! Interrupt will not clear.\n"));
|
||
ScsiPortFreeDeviceBase(deviceExtension, deviceExtension->Adapter);
|
||
return(SP_RETURN_NOT_FOUND);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Test for the interrupt bit in the status register. Some chips do not
|
||
// implement this bit; however, this driver assumes that it exists.
|
||
// The itnerrupt bit is tested for by reseting the chip, giving an
|
||
// illegal command, and checking for an gross error interrupt.
|
||
//
|
||
|
||
commandSave = SCSI_READ(deviceExtension->Adapter, Command);
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, RESET_SCSI_CHIP );
|
||
ScsiPortStallExecution(1);
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, NO_OPERATION_DMA );
|
||
ScsiPortStallExecution(1);
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, COMMAND_COMPLETE );
|
||
ScsiPortStallExecution(1);
|
||
|
||
*((PUCHAR) &deviceExtension->AdapterStatus) = SCSI_READ(
|
||
deviceExtension->Adapter,
|
||
ScsiStatus
|
||
);
|
||
|
||
dataByte = SCSI_READ( deviceExtension->Adapter, ScsiInterrupt );
|
||
|
||
//
|
||
// Test for the interrupt.
|
||
//
|
||
|
||
if (!deviceExtension->AdapterStatus.Interrupt) {
|
||
|
||
NcrPrint((0, "\nNcrMipsFindAdapter: Ncr53c90 chip without status register interrupt detected.\n"));
|
||
NcrPrint((0, "NcrMipsFindAdapter: Adapter Status: %2x; Adapter Interrupt: %2x\n",
|
||
*((PUCHAR) &deviceExtension->AdapterStatus),
|
||
dataByte
|
||
));
|
||
|
||
//
|
||
// Restore the command register.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, commandSave );
|
||
|
||
ScsiPortFreeDeviceBase(deviceExtension, deviceExtension->Adapter);
|
||
return(SP_RETURN_NOT_FOUND);
|
||
}
|
||
|
||
//
|
||
// Initialize the NCR SCSI Chip.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, RESET_SCSI_CHIP );
|
||
|
||
//
|
||
// A NOP command is required to clear the chip reset command.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, NO_OPERATION_DMA );
|
||
|
||
//
|
||
// Determine the chip type.
|
||
//
|
||
|
||
//
|
||
// The Ncr53c90 is detected by the absense of configuration register 3.
|
||
// On the other to chips this register is zero after a reset and is read
|
||
// writable.
|
||
//
|
||
|
||
dataByte = SCSI_READ( deviceExtension->Adapter, Configuration2);
|
||
|
||
if (dataByte != 0) {
|
||
|
||
deviceExtension->ChipType = Ncr53c90;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Set the Scsi2 and EnablePhaseLatch of the configuration regisiter.
|
||
//
|
||
|
||
((PSCSI_CONFIGURATION2)(&dataByte))->Scsi2 = 1;
|
||
((PSCSI_CONFIGURATION2)(&dataByte))->EnablePhaseLatch = 1;
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Configuration2, dataByte);
|
||
|
||
//
|
||
// Read the register back if the value is not the same as was written,
|
||
// then this an Ncr53c90.
|
||
//
|
||
|
||
if (dataByte != SCSI_READ( deviceExtension->Adapter, Configuration2)) {
|
||
|
||
deviceExtension->ChipType = Ncr53c90;
|
||
|
||
} else {
|
||
|
||
deviceExtension->ChipType = Ncr53c94;
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// The Emulex chip loads the TransferCountPage register with the chip id,
|
||
// if the EnablePhaseLatch is set and a NOP DMA command has been loaded.
|
||
//
|
||
|
||
if (deviceExtension->ChipType == Ncr53c94) {
|
||
|
||
//
|
||
// A NOP command is required by the FAS218 to
|
||
// load the TransferCountPage register with the chip Id.
|
||
//
|
||
|
||
SCSI_WRITE( deviceExtension->Adapter, Command, NO_OPERATION_DMA );
|
||
|
||
dataByte = SCSI_READ( deviceExtension->Adapter, TransferCountPage);
|
||
|
||
if (((PNCR_PART_CODE) &dataByte)->ChipFamily == EMULEX_FAS_216) {
|
||
|
||
deviceExtension->ChipType = Fas216;
|
||
|
||
} else if (((PNCR_PART_CODE) &dataByte)->ChipFamily == NCR_53c96) {
|
||
|
||
NcrPrint((1, "NcrFindAdapter: NCR 53c96 chip detected.\n"));
|
||
deviceExtension->ChipType = Fas216;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set the parameters according to the chip type.
|
||
//
|
||
|
||
switch (deviceExtension->ChipType) {
|
||
case Ncr53c94:
|
||
|
||
NcrPrint((1, "NcrFindAdapter: Ncr 53C94 chip detected.\n"));
|
||
deviceExtension->ClockSpeed = NCR_SCSI_CLOCK_SPEED;
|
||
ConfigInfo->TaggedQueuing = 1;
|
||
ConfigInfo->MaximumTransferLength = 0x10000;
|
||
break;
|
||
|
||
case Fas216:
|
||
NcrPrint((1, "NcrFindAdapter: Emulex FAS 216 chip detected.\n"));
|
||
deviceExtension->ClockSpeed = EMULEX_SCSI_CLOCK_SPEED;
|
||
ConfigInfo->TaggedQueuing = 1;
|
||
ConfigInfo->MaximumTransferLength = 0x1000000-0x1000;
|
||
deviceExtension->Configuration3.CheckIdMessage = 1;
|
||
break;
|
||
|
||
case Ncr53c90:
|
||
|
||
NcrPrint((1, "NcrFindAdapter: Ncr 53C90 chip detected.\n"));
|
||
|
||
default:
|
||
*Again = FALSE;
|
||
return(SP_RETURN_BAD_CONFIG);
|
||
}
|
||
|
||
//
|
||
// If the clock speed is greater than 25 Mhz then set the fast clock
|
||
// bit in configuration register.
|
||
//
|
||
|
||
if (deviceExtension->ClockSpeed > 25) {
|
||
deviceExtension->Configuration3.FastClock = 1;
|
||
}
|
||
|
||
*Again = FALSE;
|
||
return(SP_RETURN_FOUND);
|
||
|
||
}
|
||
|