diff options
Diffstat (limited to 'private/ntos/miniport/ncr53c94/ncr53c9x.c')
-rw-r--r-- | private/ntos/miniport/ncr53c94/ncr53c9x.c | 5616 |
1 files changed, 5616 insertions, 0 deletions
diff --git a/private/ntos/miniport/ncr53c94/ncr53c9x.c b/private/ntos/miniport/ncr53c94/ncr53c9x.c new file mode 100644 index 000000000..a336c5a1f --- /dev/null +++ b/private/ntos/miniport/ncr53c94/ncr53c9x.c @@ -0,0 +1,5616 @@ +/*++ + +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); + +} + |