/*++

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 "fwp.h"
#include "ntos.h"
#include "scsi.h"

#include "ncr53c9x.h"
#include "mcadefs.h"
#include "string.h"

#include "jazzdef.h"
#include "jazzdma.h"

PVOID UnknownIoSpace = (PVOID) 0xE000D600;

#if DBG
int NcrDebug;
#define NcrPrint(arg) ScsiDebugPrint arg
#else
#define NcrPrint(arg) ScsiDebugPrint arg
#define ScsiDebugPrint(a,b,...)
#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 */

    
    // Poke unknown MMIO
    WRITE_REGISTER_UCHAR(UnknownIoSpace, 0);
    //
    // 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
            );

        
            // Poke unknown MMIO
            WRITE_REGISTER_UCHAR(UnknownIoSpace, 0);
        //
        // 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:

        
            // Poke unknown MMIO
            WRITE_REGISTER_UCHAR(UnknownIoSpace, 0);
        //
        // 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,
                !(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,
        (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);
        }

	
            // Poke unknown MMIO
            WRITE_REGISTER_UCHAR(UnknownIoSpace, 1);
        //
        // 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.
            //
            
            // Poke unknown MMIO
            WRITE_REGISTER_UCHAR(UnknownIoSpace, 1);

            //
            // 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:
            // Poke unknown MMIO
            WRITE_REGISTER_UCHAR(UnknownIoSpace, 1);
            //
            // 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) {

            ScsiDebugPrint(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->DmaChannel = 0;
    if (ConfigInfo->BusInterruptLevel == 0) {
        ConfigInfo->BusInterruptVector = 22;
        ConfigInfo->BusInterruptLevel = 4;
    }

    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.
    //

    SCSI_PHYSICAL_ADDRESS PhysAddr;
    PhysAddr.LowPart = 0x8000D600;
    PhysAddr.HighPart = 0;

    UnknownIoSpace = ScsiPortGetDeviceBase(
        deviceExtension,                      // HwDeviceExtension
        ConfigInfo->AdapterInterfaceType,     // AdapterInterfaceType
        ConfigInfo->SystemIoBusNumber,        // SystemIoBusNumber
        PhysAddr,
        sizeof(UCHAR),   // NumberOfBytes
        FALSE                                         // InIoSpace
        );

    PhysAddr.LowPart = SCSI_PHYSICAL_BASE;

    deviceExtension->Adapter = ScsiPortGetDeviceBase(
        deviceExtension,                      // HwDeviceExtension
        ConfigInfo->AdapterInterfaceType,     // AdapterInterfaceType
        ConfigInfo->SystemIoBusNumber,        // SystemIoBusNumber
        PhysAddr,
        sizeof(SCSI_REGISTERS),   // NumberOfBytes
        FALSE                                         // InIoSpace
        );

    if (deviceExtension->Adapter == NULL) {
        ScsiDebugPrint(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);

}

