File:  [WindowsNT SDKs] / ntddk / src / scsi / fd8xx / fd8xx.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:31:12 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntddk-nov-1993, HEAD
Microsoft Windows NT Build 511 (DDK SDK) 11-01-1993

/*++

Copyright (c) 1991, 1992  Microsoft Corporation
Copyright (c) 1992  Future Domain Corporation

Module Name:

    fd8xx.c

Abstract:

    This is the miniport driver for the Future Domain TMC-8XX SCSI Adapters.

Environment:

    kernel mode only

Notes:

--*/

#include "miniport.h"
#include "stdarg.h"
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "fd8xx.h"      // includes scsi.h

//
// Logical Unit states.
//
typedef enum _LU_STATE {

    LS_UNDETERMINED,
    LS_ARBITRATE,
    LS_SELECT,
    LS_IDENTIFY,
    LS_MSG_SPECIAL,
    LS_COMMAND,
    LS_DATA,
    LS_DISCONNECTED,
    LS_ABORT,
    LS_STATUS,
    LS_MSG_IN,
    LS_COMPLETE

} LU_STATE, *PLU_STATE;

//
// Logical Unit extension.  The BOOLEANS in this structure will be initialized
// to FALSE by the port driver.  It will zero the structure when allocated.
// Therefore there is no explicit initialization of these fields.
//
typedef struct _SPECIFIC_LU_EXTENSION {

    LU_STATE            LuState;            // State information.
    ULONG               SavedDataPointer;   // Current data pointer.
    ULONG               SavedDataLength;    // Current data lenght.
    PSCSI_REQUEST_BLOCK ActiveLuRequest;    // Active Srb for this LUN.
    USHORT              OverRunCount;       // Initialized to zero, counts up
    UCHAR               AbortBeingAttempted;// Abort active on this LU.
    BOOLEAN             NoDisconnectActive; // No disconnect flag on SRB.
    BOOLEAN             HandShakeAllData;   // Dribbling drive fix.
    BOOLEAN             SixByteCDBActive;   // Potential tape access.

} SPECIFIC_LU_EXTENSION, *PSPECIFIC_LU_EXTENSION;

//
// Device extension
//
typedef struct _SPECIFIC_DEVICE_EXTENSION {

    PUCHAR              BaseAddress;        // Memory map address of adapter.
    ULONG               CurDataPointer;     // Current pointer for active LUN.
    ULONG               CurDataLength;      // Bytes left to xfer to this LUN.
    PSPECIFIC_LU_EXTENSION  SavedLu;        // Saved LUN during interrupts.
    PSPECIFIC_LU_EXTENSION  ActiveLu;       // Currently active LUN.
    UCHAR               PathId;             // Relates to SCSI bus.
    UCHAR               ControlRegister;    // Current val of fd8xx control reg.
    BOOLEAN             ExpectingInterrupt; // Bookkeeping for IRQ configuration.
    BOOLEAN             NotifiedConfigurationError; // Event is logged.
    USHORT              ContinueTimer;      // Indicator to continue polling
    USHORT              TimerCaughtInterrupt; // Number times timer caught IRQ
    UCHAR               InitiatorId;        // target id for host adapter
    BOOLEAN             ConfiguredWithoutInterrupts; // Intentially configured w/o IRQ

} SPECIFIC_DEVICE_EXTENSION, *PSPECIFIC_DEVICE_EXTENSION;


//
// Function declarations
//

ULONG
DriverEntry(
    IN PVOID DriverObject,
    IN PVOID Argument2
    );

ULONG
Fd8xxFindAdapter(
    IN PVOID                                Context,
    IN PVOID                                AdaptersFound,
    IN PVOID                                BusInformation,
    IN PCHAR                                ArgumentString,
    IN OUT PPORT_CONFIGURATION_INFORMATION  ConfigInfo,
    OUT PBOOLEAN                            Again
    );

BOOLEAN
Fd8xxInitialize(
    IN PVOID Context
    );

BOOLEAN
Fd8xxStartIo(
    IN PVOID               Context,
    IN PSCSI_REQUEST_BLOCK Srb
    );

VOID
Fd8xxDoReconnect(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    );

VOID
Fd8xxDpcRunPhase(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    );

VOID
Fd8xxTimer(
    IN PVOID Context
    );

BOOLEAN
Fd8xxInterrupt(
    IN PVOID Context
    );

BOOLEAN
Fd8xxResetBus(
    IN PVOID Context,
    IN ULONG PathId
    );

VOID
Fd8xxMoveMemoryUchar(
    PUCHAR Source,
    PUCHAR Dest,
    ULONG  Length
    );


#if DBG

//
// Globals and externals used for debugging.
//

//
// Fd8xxDebug affects which debug prints are enabled:
//
//      0x001    Arbitration and selection
//      0x002    Command, message out, and status
//      0x004    Data transfer
//      0x008    Message in and status
//      0x010    Miniport entry points and completion
//      0x020    Initialization and interrupt
//      0x040    StatusCheck and WaitForRequest
//      0x080    Control register manipulation
//      0x100    Completion
//      0x200    Handshake all bytes set.
//
ULONG Fd8xxDebug = 0x0000;

#define FdDebugPrint(MASK, ARGS)    \
        if (MASK & Fd8xxDebug) {    \
            ScsiDebugPrint ARGS;    \
        }

#else

#define FdDebugPrint(MASK, ARGS)

#endif


VOID
Fd8xxWriteControl(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
    IN UCHAR                      Value
    )

/*++

Routine Description:

    This routine sets the control register on the adapter and remembers
    the value set in the device extension.

Arguments:

    DeviceExtension - Device adapter context pointer.
    Value           - New value for the control register.

Return Value:

    None

--*/

{
    FdDebugPrint(0x80,
                 (0, "WriteControl: Device = %x, NewVal = %x\n",
                 DeviceExtension,
                 Value));

    DeviceExtension->ControlRegister = Value;

    FD8XX_SET_CONTROL(DeviceExtension->BaseAddress,
                      Value);
} // end Fd8xxWriteControl()


VOID
Fd8xxSetControl(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
    IN UCHAR                      Value
    )

/*++

Routine Description:

    This routine adds the control lines indicated to the current value
    for the adapter control register.

Arguments:

    DeviceExtension - Device adapter context pointer.
    Value           - The bits that are to be added (or'd in) to the
                      control register.

Return Value:

    None

--*/

{
    FdDebugPrint(0x80,
                 (0, "SetControl: Device = %x, Value = %x, NewVal = %x\n",
                 DeviceExtension,
                 Value,
                 DeviceExtension->ControlRegister | Value));

    DeviceExtension->ControlRegister |= Value;

    FD8XX_SET_CONTROL(DeviceExtension->BaseAddress,
                      DeviceExtension->ControlRegister);
} // end Fd8xxSetControl()


VOID
Fd8xxClearControl(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
    IN UCHAR                      Mask
    )

/*++

Routine Description:

    This routine masks out the bits for the control register that are
    passed in Mask and sets the adapter control register to the new value.

Arguments:

    DeviceExtension - Device adapter context pointer.
    Mask            - The bits that are to be reset to zero in the control
                      register.

Return Value:

    None

--*/

{
    FdDebugPrint(0x80,
                 (0, "ClearControl: Device = %x, Mask = %x, NewVal = %x\n",
                 DeviceExtension,
                 Mask,
                 DeviceExtension->ControlRegister & (~(Mask))));

    //
    // Negate Value to get a zero based mask.
    //
    Mask = ~(Mask);

    //
    // AND against the current contents of the control register.
    //
    DeviceExtension->ControlRegister &= Mask;

    FD8XX_SET_CONTROL(DeviceExtension->BaseAddress,
                      DeviceExtension->ControlRegister);
} // end Fd8xxClearControl()


BOOLEAN
Fd8xxWaitForRequestLine(
    PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    Spin checking the status of the FD8XX adapter until it indicates that
    the SCSI REQUEST line is high.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    TRUE    - indicates that the SCSI REQUEST line was asserted in time.
    FALSE   - indicates timeout occurred while waiting for the SCSI REQUEST
              line.

--*/

{
    PUCHAR  baseAddress = DeviceExtension->BaseAddress;
    ULONG   spinCount   = REQUEST_SPIN_WAIT;

    FdDebugPrint(0x40, (0, "FdWaitForReq: "));

    do {

        //
        // Check if TWO of the status register reads are identical.  This is
        // to validate that there was no "glitch" that caused some line to
        // temporarily go high.
        //
        while (FD8XX_READ_STATUS(baseAddress) !=
               FD8XX_READ_ALTERNATE_STATUS(baseAddress, 1)) {

            ScsiPortStallExecution(1);
        }

        if (FD8XX_READ_STATUS(baseAddress) & S_REQUEST) {

            FdDebugPrint(0x40, (0, "Got REQUEST\n"));
            return TRUE;
        }

        ScsiPortStallExecution(1);

    } while (spinCount--);

    FdDebugPrint(0x40, (0, "TIMEOUT\n"));

    if (DeviceExtension->ActiveLu) {

        ScsiPortLogError(DeviceExtension,
                         DeviceExtension->ActiveLu->ActiveLuRequest,
                         DeviceExtension->ActiveLu->ActiveLuRequest->PathId,
                         DeviceExtension->ActiveLu->ActiveLuRequest->TargetId,
                         DeviceExtension->ActiveLu->ActiveLuRequest->Lun,
                         SP_REQUEST_TIMEOUT,
                         1);
    }

    return FALSE;
} // end Fd8xxWaitForRequestLine()


BOOLEAN
Fd8xxStatusCheck(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
    IN UCHAR Mask,
    IN UCHAR Compare,
    IN ULONG SpinMax
    )

/*++

Routine Description:

    Spin checking the status of the FD8XX adapter until it matches
    the desired value.

Arguments:

    DeviceExtension - Device adapter context pointer.
    Mask            - Bits to mask out of the value returned by adapter
                      status register.
    Compare         - Desired result of status register after is is "Mask"ed.
    SpinMax         - Number of times to test adapter's status register.

Return Value:

    TRUE indicates desired value was returned by the adapter status register.
    FALSE indicates timeout occurred and desired value was not returned.

--*/

{
    PUCHAR  baseAddress = DeviceExtension->BaseAddress;

    FdDebugPrint(0x40, (0, "FdStatusCheck: "));

    FdDebugPrint(0x40,
                  (0, "Dev=%x Status=%x Mask=%x Compare=%x ",
                  DeviceExtension,
                  FD8XX_READ_STATUS(baseAddress),
                  Mask,
                  Compare));

    do {

        //
        // Check if TWO of the status register reads are identical.  This is
        // to validate that there was no "glitch" that caused some line to
        // temporarily go high.
        //
        while (FD8XX_READ_STATUS(baseAddress) !=
               FD8XX_READ_ALTERNATE_STATUS(baseAddress, 1)) {

            ScsiPortStallExecution(1);
        }

        if ((UCHAR)(FD8XX_READ_STATUS(baseAddress) & Mask) == Compare) {

            FdDebugPrint(0x40, (0, "Got Compare\n"));
            return TRUE;
        }

        ScsiPortStallExecution(1);

    } while (SpinMax--);

    FdDebugPrint(0x40,
                  (0, "TIMEOUT Status=%x\n",
                  FD8XX_READ_STATUS(baseAddress)));

    return FALSE;
} // end Fd8xxStatusCheck()


VOID
Fd8xxDetermineNextState(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    This routine determines the next logical state of this state machine
    based on the current SCSI bus phase.  It is typically called because
    from the current state, the next state can be one of a number of other
    states.  For example, after issuing the last command byte of COMMAND
    phase, the device may want to drive us into DATA_IN, DATA_OUT, STATUS,
    or MSG_IN (i.e., transfer data, report an error, or disconnect).

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None

--*/

{
    FdDebugPrint(0x01,
                  (0, "FdNextPhase: phase = %x ",
                  FD8XX_READ_PHASE(DeviceExtension->BaseAddress)));

    switch (FD8XX_READ_PHASE(DeviceExtension->BaseAddress)) {

    case BP_COMMAND:

        FdDebugPrint(0x01, (0, "LS_COMMAND\n"));
        DeviceExtension->ActiveLu->LuState = LS_COMMAND;
        break;

    case BP_DATA_IN:
    case BP_DATA_OUT:

        FdDebugPrint(0x01, (0, "LS_DATA\n"));
        DeviceExtension->ActiveLu->LuState = LS_DATA;
        break;

    case BP_MESSAGE_IN:

        FdDebugPrint(0x01, (0, "LS_MSG_IN\n"));
        DeviceExtension->ActiveLu->LuState = LS_MSG_IN;
        break;

    case BP_STATUS:

        FdDebugPrint(0x01, (0, "LS_STATUS\n"));
        DeviceExtension->ActiveLu->LuState = LS_STATUS;
        break;

    case BP_BUS_FREE:

        FdDebugPrint(0x01, (0, "LS_BUS_FREE\n"));

        if (DeviceExtension->ActiveLu->AbortBeingAttempted) {

            DeviceExtension->ActiveLu->ActiveLuRequest->SrbStatus =
                                        SRB_STATUS_SUCCESS;
        } else {

            DeviceExtension->ActiveLu->ActiveLuRequest->SrbStatus =
                                        SRB_STATUS_UNEXPECTED_BUS_FREE;
        }

        DeviceExtension->ActiveLu->LuState = LS_COMPLETE;
        break;

    case BP_MESSAGE_OUT:
    case BP_RESELECT:
    default:

        //
        // This will get handled in RunPhase.
        //
        FdDebugPrint(0x01, (0, "N/A\n"));
        break;
    }
} // end Fd8xxDetermineNextState()


VOID
Fd8xxArbitrate(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    Attempt to arbitrate for the SCSI bus.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None

--*/

{
    PUCHAR  baseAddress = DeviceExtension->BaseAddress;
    ULONG   attemptsRemaining = MAX_ARB_ATTEMPTS;

    FdDebugPrint(0x01, (0, "FdArbitrate: baseaddr = %x ", baseAddress));

    while (DeviceExtension->ActiveLu->LuState == LS_ARBITRATE) {

        if (FD8XX_READ_PHASE(baseAddress) == BP_BUS_FREE) {

            //
            // Tell the adapter to initiate arbitration for the SCSI bus
            // since it ia available.
            //
            Fd8xxClearControl(DeviceExtension,
                              C_BUS_ENABLE);
            Fd8xxSetControl(DeviceExtension,
                            C_PARITY_ENABLE);
            FD8XX_WRITE_DATA(baseAddress,
                            (1 << DeviceExtension->InitiatorId));
            Fd8xxSetControl(DeviceExtension,
                            C_ARBITRATION);
        }

        if (Fd8xxStatusCheck(DeviceExtension,
                             S_ARB_COMPLETE,
                             S_ARB_COMPLETE,
                             ARBITRATION_DELAY)) {

            FdDebugPrint(0x01,
                          (0, "SUCCESS on attempt #%x!\n",
                          (MAX_ARB_ATTEMPTS - attemptsRemaining)));

            //
            // Disable adapter interrupts so selection doesn't cause an
            // interrupt.  Then go to selection phase.
            //
            Fd8xxClearControl(DeviceExtension,
                              C_INT_ENABLE);
            DeviceExtension->ActiveLu->LuState = LS_SELECT;

        } else {

            FdDebugPrint(0x01,
                          (0, "FAILED! Status=%x ",
                          FD8XX_READ_STATUS(baseAddress)));

            Fd8xxClearControl(DeviceExtension,
                              C_ARBITRATION);

            if (FD8XX_READ_PHASE(baseAddress) & S_SELECT) {

                FdDebugPrint(0x01, (0, "Being re-selected!\n"));

                //
                // This will cause the interrupt routine to be called TWICE
                // for the same reason.  The first call will be from here.
                // The second call will be from the NT OS since it will
                // remember to call the interrupt routine due to the
                // adapter IRQ line (which is tied to the SCSI SELECT line)
                // going high.  The second call will be seen by the interrupt
                // routine as SPURRIOUS!
                //
                DeviceExtension->SavedLu = DeviceExtension->ActiveLu;
                DeviceExtension->ActiveLu = NULL;

                Fd8xxDoReconnect(DeviceExtension);

                if (DeviceExtension->SavedLu == NULL) {

                    //
                    // The bus was reset during the reconnect.
                    //
                    return;
                }

                DeviceExtension->ActiveLu = DeviceExtension->SavedLu;
                DeviceExtension->SavedLu = NULL;

                FdDebugPrint(0x01, (0, "Back to Arbitration "));

            } else if (!(attemptsRemaining--)) {

                FdDebugPrint(0x01, (0, "aborted.\n"));

                DeviceExtension->ActiveLu->LuState = LS_COMPLETE;
                DeviceExtension->ActiveLu->ActiveLuRequest->SrbStatus =
                                                        SRB_STATUS_TIMEOUT;
            }
        }
    }
} // end Fd8xxArbitrate()


VOID
Fd8xxSelect(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    Perform selection process on SCSI bus.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None

--*/

{
    FdDebugPrint(0x01,
                  (0, "FdSelect: Device = %x, Target = %x ",
                  DeviceExtension,
                  DeviceExtension->ActiveLu->ActiveLuRequest->TargetId));

    //
    // Raise Select line, keeping the BUSY line high.  Also, enable parity
    // and tell the adapter to drive the SCSI data lines to prepare for
    // outgoing ID bits.  Keep in mind that this also clears the
    // C_ARBITRATION bit.
    //
    Fd8xxWriteControl(DeviceExtension,
                      (C_SELECT | C_BUSY | C_BUS_ENABLE));
    Fd8xxSetControl(DeviceExtension,
                    C_PARITY_ENABLE);

    ScsiPortStallExecution(2);  // Delay 1200 nanoseconds (Bus-Settle + Bus-Clear).

    FD8XX_WRITE_DATA(DeviceExtension->BaseAddress,
                     (1 << DeviceExtension->InitiatorId) |
                     (1 << (DeviceExtension->ActiveLu->ActiveLuRequest->TargetId)));

    //
    // Raise attention to get to message out phase.
    //
    Fd8xxSetControl(DeviceExtension,
                    C_ATTENTION);

    ScsiPortStallExecution(1);  // Delay 90 nanoseconds (2 * Bus-Deskew).

    //
    // Clear BUSY
    //
    Fd8xxClearControl(DeviceExtension,
                      C_BUSY);

    ScsiPortStallExecution(1);  // Delay 400 nanoseconds (Bus-Settle).

    if (Fd8xxStatusCheck(DeviceExtension,
                         S_BUSY,
                         S_BUSY,
                         SELECTION_DELAY)) {

        ScsiPortStallExecution(1);  // Delay 90 nanoseconds (2 * Bus-Deskew).

        //
        // Selection is Ok.  Clear SELECT line.
        //
        Fd8xxClearControl(DeviceExtension,
                          C_SELECT);

        DeviceExtension->ActiveLu->LuState = LS_IDENTIFY;
        FdDebugPrint(0x01,
                      (0, "SELECT OK %x\n",
                      DeviceExtension->ActiveLu->ActiveLuRequest->TargetId));
        return;
    }

    //
    // Selection failed.  Force SCSI bus back to Bus-Free.
    //
    Fd8xxWriteControl(DeviceExtension,
                      C_PARITY_ENABLE);

    FdDebugPrint(0x01,
                  (0, "SELECT FAILED %x\n",
                  DeviceExtension->ActiveLu->ActiveLuRequest->TargetId));

    DeviceExtension->ActiveLu->LuState = LS_COMPLETE;
    DeviceExtension->ActiveLu->ActiveLuRequest->SrbStatus =
                                                SRB_STATUS_SELECTION_TIMEOUT;
} // end Fd8xxSelect()


VOID
Fd8xxSendIdentify(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    Send an identify message on the SCSI bus if the target will accept it.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None

--*/

{
    PUCHAR              baseAddress = DeviceExtension->BaseAddress;
    PSCSI_REQUEST_BLOCK srb = DeviceExtension->ActiveLu->ActiveLuRequest;

    FdDebugPrint(0x02,
                  (0, "FdSendIdentify: Device = %x, Lun = %x ",
                  DeviceExtension,
                  srb->Lun));

    if (Fd8xxWaitForRequestLine(DeviceExtension) == FALSE) {

        ScsiPortLogError(DeviceExtension,
                         DeviceExtension->ActiveLu->ActiveLuRequest,
                         DeviceExtension->ActiveLu->ActiveLuRequest->PathId,
                         DeviceExtension->ActiveLu->ActiveLuRequest->TargetId,
                         DeviceExtension->ActiveLu->ActiveLuRequest->Lun,
                         SP_REQUEST_TIMEOUT,
                         12);
        DeviceExtension->ActiveLu->LuState = LS_UNDETERMINED;
        return;
    }

    if (srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {

        Fd8xxWriteControl(DeviceExtension,
                          (C_PARITY_ENABLE | C_BUS_ENABLE));

        ScsiPortStallExecution(1);  // Delay 90 nanoseconds (2 * Bus-Deskew).

        //
        // The target may wish to negotiate synchronous with us, in which
        // case we will reject the message and go on to COMMAND phase.
        //
        DeviceExtension->ActiveLu->LuState = LS_UNDETERMINED;

    } else {

        //
        // We still have an ABORT or RESET message to send, so keep the
        // ATTENTION line high.
        //
        Fd8xxWriteControl(DeviceExtension,
                          (C_PARITY_ENABLE | C_BUS_ENABLE | C_ATTENTION));

        DeviceExtension->ActiveLu->LuState = LS_MSG_SPECIAL;
    }

    //
    // Some old SCSI-I devices want to skip MESSAGE OUT phase and go
    // directly to COMMAND phase.  We'll just let them do what they want.
    // If the drive is really stupid, the error should get picked up in
    // the phase to which it transitions, so just return and get on with
    // life.
    //
    if (FD8XX_READ_PHASE(baseAddress) == BP_MESSAGE_OUT) {

        FdDebugPrint(0x02, (0, "IDENTIFY sent.\n"));

        if (srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT) {

            DeviceExtension->ActiveLu->NoDisconnectActive = TRUE;
            FD8XX_WRITE_DATA(DeviceExtension->BaseAddress,
                             (SCSIMESS_IDENTIFY |
                             srb->Lun));
        } else {

            FD8XX_WRITE_DATA(DeviceExtension->BaseAddress,
                             (SCSIMESS_IDENTIFY_WITH_DISCON |
                             srb->Lun));
        }

    } else {

        FdDebugPrint(0x02,
                      (0, "IDENTIFY not sent %x\n",
                      FD8XX_READ_STATUS(DeviceExtension->BaseAddress)));

        Fd8xxClearControl(DeviceExtension, C_ATTENTION);
        ScsiPortStallExecution(1);  // Delay 90 nanoseconds (2 * Bus-Deskew).

        DeviceExtension->ActiveLu->LuState = LS_UNDETERMINED;
    }
} // end Fd8xxSendIdentify()


VOID
Fd8xxSendSpecialMessage(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    Send an ABORT or RESET message on the SCSI bus if the target will accept
    it.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None

--*/

{
    PUCHAR              baseAddress = DeviceExtension->BaseAddress;
    PSCSI_REQUEST_BLOCK srb = DeviceExtension->ActiveLu->ActiveLuRequest;

    FdDebugPrint(0x02,
                  (0, "FdSendSpecialMessage: Device = %x, Lun = %x ",
                  DeviceExtension,
                  srb->Lun));

    if (Fd8xxWaitForRequestLine(DeviceExtension) == FALSE) {

        ScsiPortLogError(DeviceExtension,
                         DeviceExtension->ActiveLu->ActiveLuRequest,
                         DeviceExtension->ActiveLu->ActiveLuRequest->PathId,
                         DeviceExtension->ActiveLu->ActiveLuRequest->TargetId,
                         DeviceExtension->ActiveLu->ActiveLuRequest->Lun,
                         SP_REQUEST_TIMEOUT,
                         13);
        DeviceExtension->ActiveLu->LuState = LS_UNDETERMINED;
        return;
    }

    Fd8xxClearControl(DeviceExtension,
                      C_ATTENTION);
    ScsiPortStallExecution(1);  // Delay 90 nanoseconds (2 * Bus-Deskew).

    if (srb->Function == SRB_FUNCTION_ABORT_COMMAND) {

        FdDebugPrint(0x02, (0, "ABORT\n"));
        FD8XX_WRITE_DATA(DeviceExtension->BaseAddress,
                         SCSIMESS_ABORT);
    } else if (srb->Function == SRB_FUNCTION_RESET_DEVICE) {

        FdDebugPrint(0x02, (0, "BDR\n"));
        FD8XX_WRITE_DATA(DeviceExtension->BaseAddress,
                         SCSIMESS_BUS_DEVICE_RESET);
    } else {

        ScsiPortLogError(DeviceExtension,
                         srb,
                         srb->PathId,
                         srb->TargetId,
                         srb->Lun,
                         SP_PROTOCOL_ERROR,
                         2);

        FdDebugPrint(0x02, (0, "NO-OP\n"));
        FD8XX_WRITE_DATA(DeviceExtension->BaseAddress,
                         SCSIMESS_NO_OPERATION);
    }

    DeviceExtension->ActiveLu->LuState = LS_UNDETERMINED;
} // end Fd8xxSendSpecialMessage()


VOID
Fd8xxSendCDB(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    Send the SCSI Command Descriptor Block (CDB) to the indicated target/lun.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None

--*/

{
    UCHAR   cdbLength = DeviceExtension->ActiveLu->ActiveLuRequest->CdbLength;
    PUCHAR  cdb = DeviceExtension->ActiveLu->ActiveLuRequest->Cdb;
    PUCHAR  baseAddress = DeviceExtension->BaseAddress;

    FdDebugPrint(0x02, (0, "SendCommand: "));

    Fd8xxSetControl(DeviceExtension, C_BUS_ENABLE);

    while ((cdbLength-- != 0) &&
           Fd8xxWaitForRequestLine(DeviceExtension) &&
           (FD8XX_READ_PHASE(baseAddress) == BP_COMMAND)) {

        FdDebugPrint(0x02, (0, "%x ", *cdb));

        FD8XX_WRITE_DATA(baseAddress,
                         *cdb++);
    }

    DeviceExtension->ActiveLu->LuState = LS_UNDETERMINED;

    //
    // Set up the running data pointer info for a possible data transfer.
    //
    DeviceExtension->CurDataPointer = DeviceExtension->ActiveLu->SavedDataPointer;
    DeviceExtension->CurDataLength = DeviceExtension->ActiveLu->SavedDataLength;

    FdDebugPrint(0x02, (0, "New phase=%x\n", FD8XX_READ_PHASE(baseAddress)));
} // end Fd8xxSendCDB()


VOID
Fd8xxCopyData(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    Copy data byte-for-byte to and from adapter.  This is used to copy data
    from the FD8XX controller to system memory and vice-versa.  Each
    iteration of the outer-most "while" loop will transfer MAX_BUFFER_LENGTH
    bytes (i.e., 512 bytes - this is the size of the TMC-950 memory-mapped
    SCSI data register), or any fraction thereof.

    In theory, one should be able to use the x86 REP MOVB instruction (or
    other equivalent) to "blast" 512 bytes of data to this register.  The
    REQ/ACK handshaking is done by the TMC-950 chip.  In practice, there is
    one problem.  The SCSI data register is "locked in" with the host bus
    (ISA).  This host bus cannot be tied up for longer than a memory refresh
    cycle (15 useconds, I think).  After completing a REQ/ACK cycle, the
    950 chip frees the bus, then locks it again waiting for the assertion
    SCSI REQ line by the target.  If REQ is not asserted within a memory
    refresh cycle, the 950 chip times out and frees the host bus so that a
    memory refresh can take place.  This puts the 950 chip one byte "ahead"
    of the target, thus screwing up the rest of the block transfer.

    Future Domain refers to this problem as the "dribbling drive" because
    the target delays the assertion of the SCSI REQ line for longer than
    usual (probably because the firmware did some maintenance task between
    bytes like filling or draining its internal FIFO).  SCSI places no
    limit on how long a target has to re-assert the SCSI REQ line, so we must
    provide a work-around.

    After a fair amount of research and experimentation, we have learned that
    "manually" handshaking the first x number of bytes, the last x number of
    bytes, or a combination of the two, solves the problem.  The determination
    of x in either case is purely trial-and-error and is based on device
    characteristics.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None

--*/

{
    PUCHAR  baseAddress = DeviceExtension->BaseAddress;
    UCHAR   byteBucket = 0;
    UCHAR   wantedState;

    register ULONG   moveCount;
    register ULONG   headCount;
    register ULONG   tailCount;

    if (Fd8xxWaitForRequestLine(DeviceExtension) == FALSE) {

        ScsiPortLogError(DeviceExtension,
                         DeviceExtension->ActiveLu->ActiveLuRequest,
                         DeviceExtension->ActiveLu->ActiveLuRequest->PathId,
                         DeviceExtension->ActiveLu->ActiveLuRequest->TargetId,
                         DeviceExtension->ActiveLu->ActiveLuRequest->Lun,
                         SP_REQUEST_TIMEOUT,
                         14);
        DeviceExtension->ActiveLu->LuState = LS_UNDETERMINED;
        return;
    }

    wantedState = FD8XX_READ_PHASE(baseAddress);

    FdDebugPrint(0x04,
                  (0, "CopyData: from %s for %x ",
                  (wantedState == BP_DATA_IN) ? "Target" : "Memory",
                  DeviceExtension->CurDataLength));

    if (wantedState == BP_DATA_IN) {

        //
        // The target will drive the SCSI data lines in DATA_IN phase.
        //
        Fd8xxClearControl(DeviceExtension, C_BUS_ENABLE);
    } else {

        //
        // We will drive the SCSI data lines in DATA_OUT phase.
        //
        Fd8xxSetControl(DeviceExtension, C_BUS_ENABLE);
    }

    while (FD8XX_READ_PHASE(baseAddress) == wantedState) {

        if (DeviceExtension->CurDataLength == 0) {

            //
            // Overrun condition.  Read (or write) bytes until
            // out of DATA phase.
            //
            FdDebugPrint(0x04, (0, "overrun "));

            while ((FD8XX_READ_PHASE(baseAddress) == wantedState) &&
                    Fd8xxWaitForRequestLine(DeviceExtension)) {

                FdDebugPrint(0x04, (0, "."));

                if (wantedState == BP_DATA_IN) {

                    //
                    // Throw the byte away since there's nowhere to put it.
                    //
                    byteBucket = FD8XX_READ_DATA(baseAddress);
                } else {

                    //
                    // Write a zero to the device.
                    //
                    FD8XX_WRITE_DATA(baseAddress, byteBucket);
                }
            }

            DeviceExtension->ActiveLu->ActiveLuRequest->SrbStatus =
                                                    SRB_STATUS_DATA_OVERRUN;
            DeviceExtension->ActiveLu->OverRunCount++;
            if (DeviceExtension->ActiveLu->OverRunCount >= 5) {
                FdDebugPrint(0x200, (0, "Fd8xx: Turned on handshake\n"));
                DeviceExtension->ActiveLu->HandShakeAllData = TRUE;
            }
            continue;
        }

        //
        // First, copy the first bytes (if any) with REQ/ACK handshaking...
        //
        // NOTE:  Some devices are REALLY slow when transferring inquiry
        // data.  So, we'll treat any transfer less than a block
        // as a SLOW transfer.  Also, all transfers performed when
        // the DISABLE_DISCONNECT bit was on in the SRB are done in
        // the slower, handshake per byte way.  Finally some devices
        // tend to "dribble" bytes and need to have a handshake all
        // the time.  These are found by a threshold of overruns being
        // encountered above.  The single OR on the HandShake and SixByte
        // booleans is on purpose.  This generates an or and conditional
        // jump where a double OR generates more code.
        //
        if (((DeviceExtension->ActiveLu->NoDisconnectActive) &&
             (DeviceExtension->ActiveLu->OverRunCount != 0)) ||
            (DeviceExtension->ActiveLu->HandShakeAllData |
             DeviceExtension->ActiveLu->SixByteCDBActive)) {

            headCount = DeviceExtension->CurDataLength;
        } else {
            if (MAX_BUFFER_LENGTH > DeviceExtension->CurDataLength) {
    
                headCount = DeviceExtension->CurDataLength;
            } else {
    
                if (DeviceExtension->CurDataLength & (MAX_BUFFER_LENGTH - 1)) {
    
                    //
                    // If the expected transfer is not a block multiple, do it
                    // all the slow way.  This will correct problems with NEC
                    // CD drives when doing NEC CD audio commands.
                    //
                    headCount = DeviceExtension->CurDataLength;
                } else {
                    headCount = min(MAX_HEAD_LENGTH,
                                    DeviceExtension->CurDataLength);
                }
            }
        }

        moveCount = 0;

        while (moveCount != headCount) {

            if (Fd8xxWaitForRequestLine(DeviceExtension) == FALSE) {
                ScsiPortLogError(DeviceExtension,
                                 DeviceExtension->ActiveLu->ActiveLuRequest,
                                 DeviceExtension->ActiveLu->ActiveLuRequest->PathId,
                                 DeviceExtension->ActiveLu->ActiveLuRequest->TargetId,
                                 DeviceExtension->ActiveLu->ActiveLuRequest->Lun,
                                 SP_REQUEST_TIMEOUT,
                                 11);
                break;
            }

            if (FD8XX_READ_PHASE(baseAddress) == BP_DATA_IN) {

                *((PUCHAR) DeviceExtension->CurDataPointer++) =
                                            FD8XX_READ_DATA(baseAddress);
            } else if (FD8XX_READ_PHASE(baseAddress) == BP_DATA_OUT) {

                FD8XX_WRITE_DATA(baseAddress,
                             *((PUCHAR) DeviceExtension->CurDataPointer++));
            } else {

                //
                // Account for the number of bytes moved.
                //
                DeviceExtension->CurDataLength -= moveCount;
                goto Fd8xxCopyData_CheckNextPhase;
            }
            moveCount++;
        }

        DeviceExtension->CurDataLength -= moveCount;

        if (DeviceExtension->CurDataLength == 0) {
            goto Fd8xxCopyData_CheckNextPhase;
        }

        //
        // Now, calculate the the middle chunk for transfer without hanshaking...
        //
        moveCount = min(
            (DeviceExtension->CurDataLength & (MAX_BUFFER_LENGTH - 1)),
            MAX_BUFFER_LENGTH);

        if (moveCount >= MAX_TAIL_LENGTH) {

            moveCount -= MAX_TAIL_LENGTH;
        }

        if (wantedState == BP_DATA_IN) {

            ScsiPortReadRegisterBufferUchar((baseAddress + READ_SCSI),
                                      ((PUCHAR) DeviceExtension->CurDataPointer),
                                      moveCount);
        } else {

            ScsiPortWriteRegisterBufferUchar((baseAddress + WRITE_SCSI),
                                       ((PUCHAR) DeviceExtension->CurDataPointer),
                                       moveCount);
        }

        DeviceExtension->CurDataPointer += moveCount;
        DeviceExtension->CurDataLength -= moveCount;

        //
        // Finally, copy the last bytes (if any) with REQ/ACK handshaking...
        //
        tailCount = min(MAX_TAIL_LENGTH, DeviceExtension->CurDataLength);
        moveCount = 0;

        while ((moveCount != tailCount) &&
               (FD8XX_READ_PHASE(baseAddress) != BP_BUS_FREE) &&
               Fd8xxWaitForRequestLine(DeviceExtension)) {

            if (FD8XX_READ_PHASE(baseAddress) == BP_DATA_IN) {

                *((PUCHAR) DeviceExtension->CurDataPointer++) =
                                            FD8XX_READ_DATA(baseAddress);
            } else if (FD8XX_READ_PHASE(baseAddress) == BP_DATA_OUT) {

                FD8XX_WRITE_DATA(baseAddress,
                             *((PUCHAR) DeviceExtension->CurDataPointer++));
            } else {

                DeviceExtension->CurDataLength -= moveCount;
                goto Fd8xxCopyData_CheckNextPhase;
            }
            moveCount++;
        }

        DeviceExtension->CurDataLength -= moveCount;

        if ((FD8XX_READ_PHASE(baseAddress) == BP_BUS_FREE) ||
            (Fd8xxWaitForRequestLine(DeviceExtension) == FALSE)) {

            DeviceExtension->ActiveLu->LuState = LS_UNDETERMINED;
            FdDebugPrint(0x04, (0, "NO REQUEST\n"));
            return;
        }
    }

Fd8xxCopyData_CheckNextPhase:

    if (FD8XX_READ_PHASE(baseAddress) == BP_MESSAGE_IN) {

        //
        // The device most likely wants to disconnect.
        //
        DeviceExtension->ActiveLu->LuState = LS_MSG_IN;

    } else if (FD8XX_READ_PHASE(baseAddress) == BP_STATUS) {

        if (DeviceExtension->CurDataLength != 0) {

            //
            // We have an underrun condition.  Update the count of bytes transferred.
            //
            FdDebugPrint(0x04, (0, "underrun "));
            DeviceExtension->ActiveLu->ActiveLuRequest->SrbStatus =
                                                    SRB_STATUS_DATA_OVERRUN;
            DeviceExtension->ActiveLu->ActiveLuRequest->DataTransferLength -=
                DeviceExtension->CurDataLength;
        } else if (FD8XX_READ_STATUS(baseAddress) & S_PARITY) {

            //
            // We have a parity error.
            //
            FdDebugPrint(0x04, (0, "Parity Error! "));
            DeviceExtension->ActiveLu->ActiveLuRequest->SrbStatus =
                                                    SRB_STATUS_PARITY_ERROR;
            ScsiPortLogError(DeviceExtension,
                             DeviceExtension->ActiveLu->ActiveLuRequest,
                             DeviceExtension->ActiveLu->ActiveLuRequest->PathId,
                             DeviceExtension->ActiveLu->ActiveLuRequest->TargetId,
                             DeviceExtension->ActiveLu->ActiveLuRequest->Lun,
                             SP_BUS_PARITY_ERROR,
                             3);
        }

        DeviceExtension->ActiveLu->LuState = LS_STATUS;

    } else {

        DeviceExtension->ActiveLu->LuState = LS_UNDETERMINED;
        FdDebugPrint(0x04, (0, "OTHER\n"));
        return;
    }

    FdDebugPrint(0x04, (0, "Done\n"));
} // end Fd8xxCopyData()


VOID
Fd8xxStatus(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    This routine will obtain the status from the target.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None

--*/

{
    UCHAR               status;
    UCHAR               srbStatus;
    PSCSI_REQUEST_BLOCK srb = DeviceExtension->ActiveLu->ActiveLuRequest;

    Fd8xxClearControl(DeviceExtension, C_BUS_ENABLE);

    if (Fd8xxWaitForRequestLine(DeviceExtension) == FALSE) {

        DeviceExtension->ActiveLu->LuState = LS_UNDETERMINED;
        ScsiPortLogError(DeviceExtension,
                         DeviceExtension->ActiveLu->ActiveLuRequest,
                         DeviceExtension->ActiveLu->ActiveLuRequest->PathId,
                         DeviceExtension->ActiveLu->ActiveLuRequest->TargetId,
                         DeviceExtension->ActiveLu->ActiveLuRequest->Lun,
                         SP_REQUEST_TIMEOUT,
                         17);
        return;
    }

    status = FD8XX_READ_DATA(DeviceExtension->BaseAddress);

    //
    // Save this away for the driver above.
    //
    srb->ScsiStatus = status;

    FdDebugPrint(0x02, (0, "FdStatus: Returned status byte=%x\n", status));

    switch (status) {

    case SCSISTAT_GOOD:
    case SCSISTAT_CONDITION_MET:
    case SCSISTAT_INTERMEDIATE:
    case SCSISTAT_INTERMEDIATE_COND_MET:

        srbStatus = SRB_STATUS_SUCCESS;
        break;

    case SCSISTAT_CHECK_CONDITION:
    case SCSISTAT_COMMAND_TERMINATED:

        srbStatus = SRB_STATUS_ERROR;
        break;

    case SCSISTAT_BUSY:
    case SCSISTAT_RESERVATION_CONFLICT:
    case SCSISTAT_QUEUE_FULL:
    default:

        srbStatus = SRB_STATUS_BUSY;
        break;
    }

    //
    // If some error condition already occurred (e.g., parity error), we'll
    // let that one take priority.
    //
    if (srb->SrbStatus == SRB_STATUS_PENDING)
        srb->SrbStatus = srbStatus;

    DeviceExtension->ActiveLu->LuState = LS_MSG_IN;
} // end Fd8xxStatus()


VOID
Fd8xxMessageIn(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    This routine will receive the message from the target.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None

--*/

{
    PSPECIFIC_LU_EXTENSION  luExtension = DeviceExtension->ActiveLu;
    PSCSI_REQUEST_BLOCK     srb = luExtension->ActiveLuRequest;
    PUCHAR                  baseAddress = DeviceExtension->BaseAddress;
    UCHAR                   msg;

    Fd8xxClearControl(DeviceExtension,
                      C_BUS_ENABLE);

    luExtension->LuState = LS_UNDETERMINED;

    if (Fd8xxWaitForRequestLine(DeviceExtension) == FALSE) {

        ScsiPortLogError(DeviceExtension,
                         DeviceExtension->ActiveLu->ActiveLuRequest,
                         DeviceExtension->ActiveLu->ActiveLuRequest->PathId,
                         DeviceExtension->ActiveLu->ActiveLuRequest->TargetId,
                         DeviceExtension->ActiveLu->ActiveLuRequest->Lun,
                         SP_REQUEST_TIMEOUT,
                         15);
        return;
    }

    msg = FD8XX_READ_DATA(baseAddress);

    FdDebugPrint(0x08, (0, "FdMessageIn: message=%x -- ", msg));

    if (msg == SCSIMESS_DISCONNECT) {

        FdDebugPrint(0x08, (0, "DISCONNECT\n"));

        luExtension->LuState = LS_DISCONNECTED;
        luExtension->SavedDataPointer = DeviceExtension->CurDataPointer;
        luExtension->SavedDataLength = DeviceExtension->CurDataLength;

        DeviceExtension->ActiveLu = NULL;

        if (DeviceExtension->ContinueTimer) {

            //
            // Still need the timer to insure interrupts.
            //
            DeviceExtension->ExpectingInterrupt = TRUE;
            ScsiPortNotification(RequestTimerCall,
                                 DeviceExtension,
                                 Fd8xxTimer,
                                 FD8xx_TIMER_VALUE);
        }

    } else if (msg == SCSIMESS_SAVE_DATA_POINTER) {

        FdDebugPrint(0x08, (0, "SAVE POINTERS\n"));

        luExtension->SavedDataPointer = DeviceExtension->CurDataPointer;
        luExtension->SavedDataLength = DeviceExtension->CurDataLength;

    } else if ((msg == SCSIMESS_COMMAND_COMPLETE) ||
               (msg == SCSIMESS_LINK_CMD_COMP) ||
               (msg == SCSIMESS_LINK_CMD_COMP_W_FLAG)) {

        FdDebugPrint(0x08, (0, "COMPLETION\n"));

        luExtension->LuState = LS_COMPLETE;

    } else if (msg == SCSIMESS_RESTORE_POINTERS) {

        FdDebugPrint(0x08, (0, "RESTORE POINTERS\n"));

        //
        // The Srb is already correct; just restore the adapter context
        // pointers.
        //
        DeviceExtension->CurDataPointer = luExtension->SavedDataPointer;
        DeviceExtension->CurDataLength = luExtension->SavedDataLength;

    } else if ((msg == SCSIMESS_NO_OPERATION) ||
               (msg == SCSIMESS_MESSAGE_REJECT)) {

        if (srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {

            //
            // We may be talking to a stupid device that cannot handle an
            // identify message.  If this is the case, there's no telling
            // what it'll do next.  Just let it take us where it wants and
            // hope we can recover.
            //
            FdDebugPrint(0x08, (0, "Identify "));

        } else {

            //
            // We were trying to tell the device to abort or reset and it
            // puked on the message.  It's probably some old device that
            // cannot support these messages.  So, just let the device take
            // us where it wants.  If it's to cammand phase, we'll error this
            // out since there are no command bytes are associated w/ these
            // messages.  Hopefully, it'll at least take us back to bus free.
            //
            srb->SrbStatus = SRB_STATUS_MESSAGE_REJECTED;
            FdDebugPrint(0x08, (0, "Abort/Reset "));
        }

        FdDebugPrint(0x08, (0, "REJECT\n"));

    } else if (msg == SCSIMESS_EXTENDED_MESSAGE) {

        UCHAR   xMsgType;
        UCHAR   xMsgLength;

        FdDebugPrint(0x08, (0, "EXTENDED "));

        if (Fd8xxStatusCheck(DeviceExtension,
                             BP_MESSAGE_IN,
                             BP_MESSAGE_IN,
                             REQUEST_SPIN_WAIT)) {

            xMsgLength = FD8XX_READ_DATA(baseAddress);
            FdDebugPrint(0x08, (0, "length=%x ", xMsgLength));

        } else {

            //
            // This better NEVER happen!  If it does, this device is
            // BRAINDEAD!!
            //
            goto Fd8xxMessageIn_PhaseSequenceFailure;
        }

        if (Fd8xxStatusCheck(DeviceExtension,
                             BP_MESSAGE_IN,
                             BP_MESSAGE_IN,
                             REQUEST_SPIN_WAIT)) {

            xMsgType = FD8XX_READ_DATA(baseAddress);
        } else {

            goto Fd8xxMessageIn_PhaseSequenceFailure;
        }

        xMsgLength--;    // Received one of the bytes.

        if (xMsgType == SCSIMESS_MODIFY_DATA_POINTER) {

            LONG offset = 0;

            FdDebugPrint(0x08, (0, "MODIFY "));
            while (xMsgLength-- != 0) {

                if (Fd8xxStatusCheck(DeviceExtension,
                                     BP_MESSAGE_IN,
                                     BP_MESSAGE_IN,
                                     REQUEST_SPIN_WAIT)) {

                    *(((PUCHAR) &(offset)) + (3 - xMsgLength)) =
                                                FD8XX_READ_DATA(baseAddress);
                } else {

                    goto Fd8xxMessageIn_PhaseSequenceFailure;
                }
            }

            //
            // All message bytes for this message were read.  We can now
            // go and update our pointers as indicated by the juse-read
            // message.  Keep in mind that the value of offset is a twos-
            // compliment value (i.e., negative).
            //
            FdDebugPrint(0x08, (0, "offset=%x\n", offset));
            DeviceExtension->CurDataPointer += offset;
            DeviceExtension->CurDataLength -= offset;

        } else {

            goto Fd8xxMessageIn_NotImplemented;
        }

    } else {

Fd8xxMessageIn_NotImplemented:

        FdDebugPrint(0x08, (0, "NOT IMPLEMENTED\n"));

        Fd8xxSetControl(DeviceExtension, C_ATTENTION);
        ScsiPortStallExecution(1);  // Delay 90 nanoseconds (2 * Bus-Deskew).

        while (Fd8xxWaitForRequestLine(DeviceExtension) &&
               (FD8XX_READ_PHASE(baseAddress) == BP_MESSAGE_IN)) {

            //
            // Ignore the rest of this message.
            //
            msg = FD8XX_READ_DATA(baseAddress);
        }

        if (FD8XX_READ_PHASE(baseAddress) == BP_MESSAGE_OUT) {

            Fd8xxSetControl(DeviceExtension, C_BUS_ENABLE);
            Fd8xxClearControl(DeviceExtension, C_ATTENTION);
            ScsiPortStallExecution(1);  // Delay 90 nanoseconds.
            FD8XX_WRITE_DATA(baseAddress, SCSIMESS_MESSAGE_REJECT);
        }

        Fd8xxClearControl(DeviceExtension, C_ATTENTION);
    }

    return;

Fd8xxMessageIn_PhaseSequenceFailure:

    srb->SrbStatus = SRB_STATUS_PHASE_SEQUENCE_FAILURE;
    ScsiPortLogError(DeviceExtension,
                     srb,
                     srb->PathId,
                     srb->TargetId,
                     srb->Lun,
                     SP_PROTOCOL_ERROR,
                     4);

    FdDebugPrint(0x08,
                (0,
                "PHS SEQ ERROR=%x\n",
                FD8XX_READ_DATA(baseAddress)));
    return;

} // end Fd8xxMessageIn()


VOID
Fd8xxNotifyCompletion(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    This routine will perform any clean up operations for the Srb
    and notify the ScsiPort driver of completion.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None

--*/

{
    PSPECIFIC_LU_EXTENSION     luExtension = DeviceExtension->ActiveLu;
    PSCSI_REQUEST_BLOCK        srb = luExtension->ActiveLuRequest;

    FdDebugPrint(0x110,
         (0, "FdComplete Dev = %x, Srb = %x, Srbstat = %x, Scsistat = %x\n",
             DeviceExtension,
             srb,
             srb->SrbStatus,
             srb->ScsiStatus));

    if (srb != NULL) {
        if (srb->SrbStatus == SRB_STATUS_PENDING) {
        
            srb->SrbStatus = SRB_STATUS_ERROR;
            ScsiPortLogError(DeviceExtension,
                    luExtension->ActiveLuRequest,
                    luExtension->ActiveLuRequest->PathId,
                    luExtension->ActiveLuRequest->TargetId,
                    luExtension->ActiveLuRequest->Lun,
                    SP_INTERNAL_ADAPTER_ERROR,
                    5);
        }
    }

    luExtension->ActiveLuRequest = NULL;
    luExtension->NoDisconnectActive = FALSE;
    DeviceExtension->ActiveLu = NULL;

    //
    // Call notification routine.
    //
    if (srb != NULL) {
        ScsiPortNotification(RequestComplete,
                             (PVOID) DeviceExtension,
                             srb);
    }

    if (!(luExtension->AbortBeingAttempted)) {

        //
        // Adapter ready for next request.
        //
        ScsiPortNotification(NextRequest,
                             DeviceExtension,
                             NULL);
    }

} // end Fd8xxNotifyCompletion()


VOID
Fd8xxRunPhase(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    This routine runs through the bus phases until some type of completion
    indication is received.  This can be when a message informing the host
    that the target will be disconnecting is received or when the SCSI bus
    goes to a free state.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None.

--*/

{
    PUCHAR  baseAddress = DeviceExtension->BaseAddress;
    ULONG   undetermined = 0;

    while (DeviceExtension->ActiveLu != NULL) {

        switch (DeviceExtension->ActiveLu->LuState) {

        case LS_UNDETERMINED:
            Fd8xxDetermineNextState(DeviceExtension);

            if (undetermined++ == REQUEST_SPIN_WAIT) {

                //
                // Some CD-ROM drives like to be SCSI bus hogs
                // and hold the SCSI bus in COMMAND phase while feching
                // data (they also conveniently skip message out phase
                // so you can't tell it that you allow disconnect)!!  So,
                // we better not time out if this happens.
                //
                if ((FD8XX_READ_PHASE(baseAddress) | S_REQUEST) !=
                                                            BP_COMMAND) {
                    FdDebugPrint(0x01, (0, "NO REQUEST\n"));

                    DeviceExtension->ActiveLu->LuState = LS_COMPLETE;
                    DeviceExtension->ActiveLu->ActiveLuRequest->SrbStatus =
                                            SRB_STATUS_PHASE_SEQUENCE_FAILURE;
    
                    ScsiPortLogError(DeviceExtension,
                            DeviceExtension->ActiveLu->ActiveLuRequest,
                            DeviceExtension->ActiveLu->ActiveLuRequest->PathId,
                            DeviceExtension->ActiveLu->ActiveLuRequest->TargetId,
                            DeviceExtension->ActiveLu->ActiveLuRequest->Lun,
                            SP_PROTOCOL_ERROR,
                            16);
                }
            }
            break;

        case LS_ARBITRATE:
            Fd8xxArbitrate(DeviceExtension);
            break;

        case LS_SELECT:
            Fd8xxSelect(DeviceExtension);
            break;

        case LS_IDENTIFY:
            Fd8xxSendIdentify(DeviceExtension);
            break;

        case LS_MSG_SPECIAL:
            Fd8xxSendSpecialMessage(DeviceExtension);
            break;

        case LS_COMMAND:
            Fd8xxSendCDB(DeviceExtension);
            break;

        case LS_DATA:
            Fd8xxCopyData(DeviceExtension);
            break;

        case LS_STATUS:
            Fd8xxStatus(DeviceExtension);
            break;

        case LS_MSG_IN:
            Fd8xxMessageIn(DeviceExtension);
            break;

        case LS_COMPLETE:
            Fd8xxNotifyCompletion(DeviceExtension);
            break;

        default:

            DeviceExtension->ActiveLu->LuState = LS_COMPLETE;
            DeviceExtension->ActiveLu->ActiveLuRequest->SrbStatus =
                                    SRB_STATUS_PHASE_SEQUENCE_FAILURE;

            ScsiPortLogError(DeviceExtension,
                    DeviceExtension->ActiveLu->ActiveLuRequest,
                    DeviceExtension->ActiveLu->ActiveLuRequest->PathId,
                    DeviceExtension->ActiveLu->ActiveLuRequest->TargetId,
                    DeviceExtension->ActiveLu->ActiveLuRequest->Lun,
                    SP_PROTOCOL_ERROR,
                    6);
            break;
        }
    }
} // end Fd8xxRunPhase()


VOID
Fd8xxReenableAdapterInterrupts(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )
/*++

Routine Description:

    This routine doesn't really do anything.  It is here to keep the
    SCSI port driver happy about what it can do with interrupts.
    The only time we should enable interrupts on selection is when we
    get a disconnect from a target.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None.

--*/

{
    //
    // Re-enable interrupts on selection.
    //
    Fd8xxSetControl(DeviceExtension, C_INT_ENABLE);

} // end Fd8xxReenableAdapterInterrupts()


VOID
Fd8xxDoReconnect(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    This routine handles reconnecting devices.  It basically plucks the
    target ID out of reselection phase, followed by the LUN out of
    message in phase (from the IDENTIFY message).  Then, after validating
    the target/lun combination, starts up the state machine again for
    the reconnecting device.

    This routine should be called with interrupts enabled.

Arguments:

    DeviceExtension - Device adapter context pointer.

Return Value:

    None.

--*/

{
    PUCHAR                  baseAddress = DeviceExtension->BaseAddress;
    PSPECIFIC_LU_EXTENSION  luExtension;
    USHORT                  busFreeTimeout;
    UCHAR                   target;         // bit value returned from target
    UCHAR                   targetID;       // numeric value of target ID.
    UCHAR                   lunID;          // numeric value of LUN ID.

    //
    // Wait for the target to drop busy, which is the TRUE beginning of
    // reselection phase.  If BUSY is not dropped within the indicated time,
    // abort the interrupt (and the reselect).
    //
    if (Fd8xxStatusCheck(DeviceExtension,
                         BP_RESELECT,
                         BP_RESELECT,
                         RESELECTION_WAIT) == FALSE) {

        FdDebugPrint(0x20,
                     (0, "BUSY not dropped: Status=%x\n",
                     FD8XX_READ_STATUS(baseAddress)));

        goto Fd8xxDoReconnect_InvalidReselection;
    }

    //
    // Delay a bit to allow for something close to a bus settle time
    // before reading the target id bits.
    //
    ScsiPortStallExecution(1);

    //
    // Try to figure out who is reselecting.
    //
    target = (FD8XX_READ_DATA(baseAddress) & ~(1 << DeviceExtension->InitiatorId));

    FdDebugPrint(0x20, (0, "Target data=%x ", target));

    //
    // Convert the bit oriented target into a number.
    //
    for (targetID = ((UCHAR) -1); target != 0; target >>= 1) {

        targetID++;
    }
    FdDebugPrint(0x20, (0, "Target Id=%x ", targetID));

    //
    // Answer the reselection by raising busy.  This should cause the target
    // to transition to MESSAGE IN phase so that we cen figure out which LUN
    // is associated with the reselecting target.
    //
    ScsiPortStallExecution(1);  // Delay 400 nanoseconds (Bus-Settle).
    Fd8xxSetControl(DeviceExtension, C_BUSY);

    if (Fd8xxStatusCheck(DeviceExtension,
                         BP_MESSAGE_IN,
                         BP_MESSAGE_IN,
                         REQUEST_SPIN_WAIT) == FALSE) {

        FdDebugPrint(0x20,
                     (0, "REQ not raised: Status=%x\n",
                     FD8XX_READ_STATUS(baseAddress)));

        goto Fd8xxDoReconnect_InvalidReselection;
    }

    //
    // The target should be driving BUSY now, so we should de-assert it in
    // control register (keep in mind that BUSY is or-tied).
    //
    Fd8xxClearControl(DeviceExtension, C_BUSY);

    lunID = FD8XX_READ_DATA(baseAddress) & (SCSI_MAXIMUM_LOGICAL_UNITS - 1);
    FdDebugPrint(0x20, (0, "Lun Id=%x ", lunID));

    if ((targetID >= SCSI_MAXIMUM_TARGETS) ||
        (lunID >= SCSI_MAXIMUM_LOGICAL_UNITS)) {

        FdDebugPrint(0x20,
                     (0, "BAD Target/Lun: %x/%x\n",
                     targetID, lunID));

        goto Fd8xxDoReconnect_InvalidReselection;
    }

    //
    // Re-enable adapter interrupts.  Don't do this if we're being called
    // by the arbitration routine, because they will never have been turned
    // off in the first place, and we'll only confuse the port driver.
    //
    if (DeviceExtension->SavedLu == NULL) {

        ScsiPortNotification(CallDisableInterrupts,
                             DeviceExtension,
                             Fd8xxReenableAdapterInterrupts);
    }

    luExtension = ScsiPortGetLogicalUnit(DeviceExtension,
                                         DeviceExtension->PathId,
                                         targetID,
                                         lunID);

    if (luExtension == NULL) {

        //
        // This is the pathological case.  This would indicate that when
        // the target ID bits were read there was a parity error or some
        // other problem that made it look like a reselection from a phantom
        // device.
        //

        ScsiPortLogError(DeviceExtension,
                         NULL,
                         0,
                         0,
                         0,
                         SP_INVALID_RESELECTION,
                         (0x07 << 16) | (targetID << 8) | lunID);
        Fd8xxResetBus(DeviceExtension, DeviceExtension->PathId);
        ScsiPortNotification(ResetDetected, DeviceExtension, NULL);
        return;
    }

    if (luExtension->ActiveLuRequest == NULL) {

        //
        // This reconnect is not expected.  There is no request for the
        // device.
        //
        ScsiPortLogError(DeviceExtension,
                         NULL,
                         0,
                         0,
                         0,
                         SP_INVALID_RESELECTION,
                         (0x17 << 16) | (targetID << 8) | lunID);
        Fd8xxResetBus(DeviceExtension, DeviceExtension->PathId);
        ScsiPortNotification(ResetDetected, DeviceExtension, NULL);
        return;
    }

    if ((luExtension->AbortBeingAttempted) &&
        (DeviceExtension->SavedLu != NULL)) {

        //
        // We're being called while trying to arbitrate for an abort.
        // The LUN we're trying to abort is reconnecting, so complete the
        // abort from here, and force the arbitration routine to exit.
        //
        DeviceExtension->SavedLu->LuState = LS_COMPLETE;
        DeviceExtension->SavedLu->ActiveLuRequest->SrbStatus = SRB_STATUS_SUCCESS;
        goto Fd8xxDoReconnect_AbortReconnect;
    }

    DeviceExtension->CurDataPointer = luExtension->SavedDataPointer;
    DeviceExtension->CurDataLength  = luExtension->SavedDataLength;
    DeviceExtension->ActiveLu = luExtension;

    luExtension->LuState = LS_UNDETERMINED;

    Fd8xxRunPhase(DeviceExtension);

    return;

Fd8xxDoReconnect_InvalidReselection:

    ScsiPortLogError(DeviceExtension,
                     NULL,
                     0,
                     0,
                     0,
                     SP_INVALID_RESELECTION,
                     (0x27 << 16) | (targetID << 8) | lunID);

    if (DeviceExtension->SavedLu == NULL) {

        ScsiPortNotification(CallDisableInterrupts,
                             DeviceExtension,
                             Fd8xxReenableAdapterInterrupts);
    }

Fd8xxDoReconnect_AbortReconnect:

    //
    // Attempt to send an abort message to the target.
    //
    Fd8xxSetControl(DeviceExtension, C_ATTENTION);
    ScsiPortStallExecution(1);  // Delay 90 nanoseconds (2 * Bus-Deskew).

    while (Fd8xxWaitForRequestLine(DeviceExtension) &&
           (FD8XX_READ_PHASE(baseAddress) != BP_MESSAGE_OUT)) {

        FD8XX_READ_DATA(baseAddress);
    }

    Fd8xxSetControl(DeviceExtension, C_BUS_ENABLE);
    Fd8xxClearControl(DeviceExtension, C_ATTENTION);
    ScsiPortStallExecution(1);  // Delay 90 nanoseconds.

    if (FD8XX_READ_PHASE(baseAddress) == BP_MESSAGE_OUT) {

        FD8XX_WRITE_DATA(baseAddress, SCSIMESS_ABORT);
    }

    Fd8xxClearControl(DeviceExtension, C_BUS_ENABLE);

    busFreeTimeout = 2048;
    while ((FD8XX_READ_PHASE(baseAddress) & S_BUSY) && (busFreeTimeout--)) {

        if (FD8XX_READ_PHASE(baseAddress) & S_REQUEST) {

            FD8XX_READ_DATA(baseAddress);
        } else {

            ScsiPortStallExecution(100);
        }
    }

    if (FD8XX_READ_PHASE(baseAddress) != BP_BUS_FREE) {

        //
        // Reset SCSI bus.  Drastic measures for drastic times...
        //
        Fd8xxResetBus(DeviceExtension, DeviceExtension->PathId);
        ScsiPortNotification(ResetDetected, DeviceExtension, NULL);
    }
} // end Fd8xxDoReconnect()


VOID
Fd8xxTimer(
    IN PVOID Context
    )

/*++

Routine Description:

    This routine is used to monitor interrupts from the Fd8xx adapter.
    The ScsiPort support for a timer is used when a disconnect message is
    received to schedule this routine as a timer for later execution.  If
    this routine is invoked before the interrupt and the adapter is attempting
    to select, then it is assumed that the adapter is on a different IRQ then
    expected and operation will continue in a "polling" mode.  If the actual
    interrupt occurs before this routine is called, then everything is ok
    and this routine need not reschedule itself.

    Operation when called is to see if the actual interrupt routine serviced
    the interrupt.  If so, then mark the device extension such that this
    routine is not rescheduled and return.  If the actual interrupt routine
    has not serviced the interrupt and the adapter is indicating that a
    reselection is being attempted, then call the interrupt routine to service
    the interrupt and continue scheduling this routine.  Lastly, if the
    interrupt routine has not serviced the interrupt and the adapter is not
    indicating a reselection, schedule another timer.

Arguments:

    Context - Device adapter context pointer.

Return Value

    TRUE indicates that the interrupt was from this Fd8xx adapter,
    FALSE indicates that this interrupt was NOT from us.

--*/

{
    PSPECIFIC_DEVICE_EXTENSION deviceExtension = Context;
    BOOLEAN                    restartTimer = TRUE;

    if (deviceExtension->ExpectingInterrupt == FALSE) {

        //
        // The interrupt routine got the interrupt.  Decrement the number
        // of times to continue scheduling a timer routine.
        //
        deviceExtension->ContinueTimer--;
        return;
    }

    if (Fd8xxStatusCheck(deviceExtension, S_SELECT, S_SELECT, 1)) {

        //
        // If the interrupt routine does not claim this interrupt for some
        // reason, restart the timer.  If it does claim the interrupt, shut
        // off the timer.
        //
        restartTimer = Fd8xxInterrupt(Context) == FALSE ? TRUE : FALSE;

        //
        // Now determine if an event log entry should be made to inform
        // the system administrator that there is a possible configuration
        // error on this driver.
        //
        if ((restartTimer == FALSE) &&
            (deviceExtension->NotifiedConfigurationError == FALSE)) {
            deviceExtension->TimerCaughtInterrupt--;
            if ((deviceExtension->TimerCaughtInterrupt == 0) &&
                (deviceExtension->ConfiguredWithoutInterrupts == FALSE)) {
                ScsiPortLogError(deviceExtension,
                                 NULL,
                                 0,
                                 0,
                                 0,
                                 SP_IRQ_NOT_RESPONDING,
                                 8);
                deviceExtension->NotifiedConfigurationError = TRUE;
            }
        }
    }

    if (restartTimer) {

        //
        // Not selected.  Set timer for another call.
        //
        ScsiPortNotification(RequestTimerCall,
                             deviceExtension,
                             Fd8xxTimer,
                             FD8xx_TIMER_VALUE);
    }
}


BOOLEAN
Fd8xxInterrupt(
    IN PVOID Context
    )

/*++

Routine Description:

    This routine handles the interrupts for the FD8XX.  The intention is to
    quickly determine the cause of the interrupt, clear the interrupt, and
    setup to process any SCSI command that may have been affected by the
    interrupt.

Arguments:

    Context - Device adapter context pointer.

Return Value:

    TRUE indicates that the interrupt was from this Fd8xx< adapter,
    FALSE indicates that this interrupt was NOT from us.

--*/

{
    PSPECIFIC_DEVICE_EXTENSION deviceExtension = Context;

    FdDebugPrint(0x20,
                  (0, "\nFdInterrupt: Device = %x ",
                  deviceExtension));

    if (Fd8xxStatusCheck(deviceExtension,
                         S_SELECT,
                         S_SELECT,
                         1) == FALSE) {

        //
        // Spurious interrupt or for some other device.
        //
        FdDebugPrint(0x20,
                      (0, "NOT Selected: Status = %x, Last CtrlReg = %x\n",
                      FD8XX_READ_STATUS(deviceExtension->BaseAddress),
                      deviceExtension->ControlRegister));
        return FALSE;

    } else {

        //
        // We are being reselected by a target.
        //
        FdDebugPrint(0x20,
                    (0, "Selected: Status = %x, Last CtrlReg = %x",
                    FD8XX_READ_STATUS(deviceExtension->BaseAddress),
                    deviceExtension->ControlRegister));

        deviceExtension->ExpectingInterrupt = FALSE;
        Fd8xxClearControl(deviceExtension, C_INT_ENABLE);

        //
        // Here's where the fun starts.  We tell the OS to call DoReconnect
        // with system interrupts enabled.  This way we're not transferring
        // gobs of data with the rest of the world blocked out.  We can be
        // friendly neighbors and do that sort of CPU hungry stuff while
        // letting others grab it when necessary.
        //
        ScsiPortNotification(CallEnableInterrupts,
                             deviceExtension,
                             Fd8xxDoReconnect);

        return TRUE;
    }
} // end Fd8xxInterrupt()


VOID
Fd8xxDpcRunPhase(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension
    )

/*++

Routine Description:

    This routine is called by the port driver with interrupts enabled.
    It calls the normal run phase routine of the 8xx driver to process
    the request.

Arguments:

    DeviceExtension - context pointer for the start or run phase.

Return Value:

    None.

--*/

{

    Fd8xxRunPhase(DeviceExtension);

    ScsiPortNotification(CallDisableInterrupts,
                         DeviceExtension,
                         Fd8xxReenableAdapterInterrupts);

    //
    // Adapter ready for next request.
    //
    ScsiPortNotification(NextRequest,
                         DeviceExtension,
                         NULL);
}


VOID
Fd8xxStartExecution(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
    IN PSPECIFIC_LU_EXTENSION     LuExtension,
    IN PSCSI_REQUEST_BLOCK        Srb
    )

/*++

Routine Description:

    This routine will start (and possibley complete) the execution of
    a SCSI request.

Arguments:

    DeviceExtension -   Device adapter context pointer.
    LuExtension -       The logical unit specific information.
    Srb -               The Srb command to execute.

Return Value:

    None.

--*/

{
    FdDebugPrint(0x10,
                  (0, "FdStartExecution Device = %x, LuExt = %x, Srb = %x ",
                  DeviceExtension,
                  LuExtension,
                  Srb));
    FdDebugPrint(0x10,
                  (0, "Target = %x, Lun = %x.\n",
                  Srb->TargetId,
                  Srb->Lun));

    //
    // Setup the context for this adapter.
    //
    DeviceExtension->ActiveLu = LuExtension;

    //
    // Turn off selection interrupt.
    //
    Fd8xxClearControl(DeviceExtension, C_INT_ENABLE);

    //
    // Check for potential tape access to insure handshake data transfer.
    // This is done here instead of in the deferred routine to save
    // two pointer loads (Srb and LuExtension).
    //
    if (Srb->CdbLength == 6) {
        LuExtension->SixByteCDBActive = TRUE;
    } else {
        LuExtension->SixByteCDBActive = FALSE;
    }

    //
    // Run the bus phase with other interrupts in the system enabled.
    //
    ScsiPortNotification(CallEnableInterrupts,
                         DeviceExtension,
                         Fd8xxDpcRunPhase);
} // end Fd8xxStartExecution()


VOID
Fd8xxAbort(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
    IN PSPECIFIC_LU_EXTENSION     LuExtension,
    IN PSCSI_REQUEST_BLOCK        Srb
    )

/*++

Routine Description:

    Attempt to abort a command on a SCSI target.

Arguments:

    DeviceExtension -   The adapter specific information.
    LuExtension     -   The specific target/logical unit information.
    Srb -               The Srb command to execute.

Return Value:

    None.

--*/

{
    PUCHAR              baseAddress = DeviceExtension->BaseAddress;
    PSCSI_REQUEST_BLOCK srbBeingAborted = LuExtension->ActiveLuRequest;

    FdDebugPrint(0x10, (0, "FdAbort: "));

    //
    // Make sure there is still a request to complete.  If so complete
    // it with an SRB_STATUS_ABORTED status.
    //
    if (srbBeingAborted == NULL) {

        //
        // If there is no request, then fail the abort.
        //
        Srb->SrbStatus = SRB_STATUS_ABORT_FAILED;

        FdDebugPrint(0x10, (0, "failed.\n"));

    } else {

        if (LuExtension->LuState == LS_DISCONNECTED) {

            FdDebugPrint(0x10, (0, "sending...\n"));

            //
            // Attempt to send an abort message SRB into the state machine.
            //
            LuExtension->LuState = LS_ARBITRATE;
            LuExtension->AbortBeingAttempted = TRUE;
            DeviceExtension->ActiveLu = LuExtension;
            Fd8xxArbitrate(DeviceExtension);

            if ((LuExtension->LuState == LS_SELECT) ||
                (LuExtension->LuState == LS_COMPLETE)) {

                //
                // Finish off the abort.
                //
                Fd8xxRunPhase(DeviceExtension);
                LuExtension->AbortBeingAttempted = FALSE;

                //
                // Now force a completion on the SRB that is being aborted.
                //
                LuExtension->LuState = LS_COMPLETE;
                srbBeingAborted->SrbStatus = SRB_STATUS_ABORTED;

                LuExtension->ActiveLuRequest = srbBeingAborted;
                DeviceExtension->ActiveLu = LuExtension;
                Fd8xxNotifyCompletion(DeviceExtension);
    
            } else {

                LuExtension->AbortBeingAttempted = FALSE;
                Srb->SrbStatus = SRB_STATUS_ABORT_FAILED;

                FdDebugPrint(0x10, (0, "FdAbort failed.\n"));
            }

        } else if (LuExtension->LuState != LS_COMPLETE) {

            //
            // The SCSI bus is most likely hung by this LUN, so reset it.
            //
            FdDebugPrint(0x10, (0, "still connected to SCSI bus.\n"));
            Fd8xxResetBus(DeviceExtension, Srb->PathId);
            ScsiPortNotification(ResetDetected, DeviceExtension, NULL);
        }
    }
} // end Fd8xxAbort()


BOOLEAN
Fd8xxResetBus(
    IN PVOID Context,
    IN ULONG PathId
    )

/*++

Routine Description:

    Reset Future Domain 8XX SCSI adapter (there is no action for this)
    and SCSI bus.

Arguments:

    Context - pointer to the device extension for the reset.
    PathId  - The bus for telling the port driver where the reset occurred.

Return Value:

    Nothing.

--*/

{
    PSPECIFIC_DEVICE_EXTENSION deviceExtension = Context;

    FdDebugPrint(0x10, (0, "FdResetBus: Reset Fd8xx and SCSI bus\n"));

    //
    // RESET SCSI bus.
    //
    Fd8xxSetControl(deviceExtension,
                    C_RESET);
    ScsiPortStallExecution(RESET_HOLD_TIME);
    Fd8xxClearControl(deviceExtension,
                      C_RESET);

    //
    // Complete all outstanding requests with SRB_STATUS_BUS_RESET.
    //
    ScsiPortCompleteRequest(deviceExtension,
                            (UCHAR) PathId,
                            (UCHAR) -1,
                            (UCHAR) -1,
                            SRB_STATUS_BUS_RESET);

    //
    // Find all luExtensions and zero them out.
    //
    if (deviceExtension->ActiveLu != NULL) {
        deviceExtension->ActiveLu->LuState = LS_COMPLETE;
        deviceExtension->ActiveLu->ActiveLuRequest = NULL;
        deviceExtension->ActiveLu->NoDisconnectActive = FALSE;
        deviceExtension->ActiveLu->AbortBeingAttempted = FALSE;
        deviceExtension->ActiveLu = NULL;
    }

    if (deviceExtension->SavedLu != NULL) {
        deviceExtension->SavedLu->LuState = LS_COMPLETE;
        deviceExtension->SavedLu->ActiveLuRequest = NULL;
        deviceExtension->SavedLu->NoDisconnectActive = FALSE;
        deviceExtension->SavedLu->AbortBeingAttempted = FALSE;
        deviceExtension->SavedLu = NULL;
    }

    return TRUE;
} // end Fd8xxResetBus()


BOOLEAN
Fd8xxStartIo(
    IN PVOID               Context,
    IN PSCSI_REQUEST_BLOCK Srb
    )

/*++

Routine Description:

    This routine is called from the SCSI port driver synchronized
    with the kernel with a request to be executed.

Arguments:
    Context -   The adapter specific information.
    Srb -       The Srb command to execute.

Return Value:

    TRUE

--*/

{
    PSPECIFIC_DEVICE_EXTENSION  deviceExtension = Context;
    PSPECIFIC_LU_EXTENSION      luExtension;

    FdDebugPrint(0x10,
                  (0, "\nFdStartIo: Device = %x, Srb = %x\n",
                  deviceExtension,
                  Srb));

    //
    // Determine the logical unit that this request is for.
    //
    deviceExtension->PathId = Srb->PathId;
    luExtension = ScsiPortGetLogicalUnit(deviceExtension,
                                         deviceExtension->PathId,
                                         Srb->TargetId,
                                         Srb->Lun);
    Srb->SrbStatus = SRB_STATUS_PENDING;

    switch (Srb->Function) {

    case SRB_FUNCTION_ABORT_COMMAND:

        FdDebugPrint(0x10, (0, "ABORT COMMAND.\n"));

        //
        // Abort request in progress.
        //
        // Fd8xxResetBus(deviceExtension, Srb->PathId);
        Fd8xxAbort(deviceExtension, luExtension, Srb);
        break;

    case SRB_FUNCTION_RESET_BUS:

        FdDebugPrint(0x10, (0, "RESET BUS.\n"));

        //
        // Reset Fd8xx and SCSI bus.
        //
        Fd8xxResetBus(deviceExtension, Srb->PathId);
        Srb->SrbStatus = SRB_STATUS_SUCCESS;
        break;

    case SRB_FUNCTION_EXECUTE_SCSI:

        FdDebugPrint(0x10, (0, "EXECUTE SCSI.\n"));

        //
        // Setup the context for this target/lun.
        //
        luExtension->ActiveLuRequest = Srb;
        luExtension->LuState = LS_ARBITRATE;
        luExtension->SavedDataPointer = (ULONG) Srb->DataBuffer;
        luExtension->SavedDataLength = Srb->DataTransferLength;

        //
        // Initiate a SCSI request.
        //
        Fd8xxStartExecution(deviceExtension, luExtension, Srb);
        return TRUE;

    default:

        Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
        ScsiPortNotification(RequestComplete,
                             (PVOID) deviceExtension,
                             Srb);
        break;

    }

    //
    // Adapter ready for next request.
    //
    ScsiPortNotification(NextRequest,
                         deviceExtension,
                         NULL);
    return TRUE;
} // end Fd8xxStartIo()


BOOLEAN
Fd8xxInitialize(
    IN PVOID Context
    )

/*++

Routine Description:

    Inititialize Fd8xx adapter.

Arguments:

    Context - Adapter object device extension.

Return Value:

    Status.

--*/

{
    PSPECIFIC_DEVICE_EXTENSION deviceExtension = Context;

    //
    // Reset Fd8xx and SCSI bus.
    //
    Fd8xxWriteControl(deviceExtension, C_RESET);
    ScsiPortStallExecution(RESET_HOLD_TIME);
    Fd8xxClearControl(deviceExtension, C_RESET);

    ScsiPortNotification(ResetDetected,
                         (PVOID) deviceExtension);

    Fd8xxClearControl(deviceExtension, C_INT_ENABLE);
    deviceExtension->ExpectingInterrupt = FALSE;
    deviceExtension->NotifiedConfigurationError = FALSE;
    deviceExtension->ContinueTimer = 5;
    deviceExtension->TimerCaughtInterrupt = 10;
    deviceExtension->ConfiguredWithoutInterrupts = FALSE;

    return TRUE;
} // end Fd8xx<Initialize()


BOOLEAN
Fd8xxCheckBaseAddress(
    IN PSPECIFIC_DEVICE_EXTENSION DeviceExtension,
    IN PUCHAR                     BaseAddress
    )

/*++

Routine Description:

    This routine will check to see if there is an adapter at the
    provided base address.  It does this by first reading and comparing
    all of the data area bytes on the adapter.  If these are all equal
    it then changes the first value and performs the compare again.
    All other data area values should change to be equal to the first
    value when the change is made.  If they are not equal any changes
    are restored.

Arguments:

    DeviceExtension - Necessary to attempt the reads.
    BaseAddress     - The address to attempt to use for the base of the
                      memory area

Return Value:

    TRUE    - There is an adapter present at the base address provided.
    FALSE   - There is no adapter present at the base address provided.

--*/

{
    UCHAR  testValue;
    UCHAR  oldValue;
    USHORT offset;

    oldValue = FD8XX_READ_DATA(BaseAddress);

    //
    // Perform a read test of the memory area of the adapter.
    //
    for (offset = 254; offset > 0; offset--) {

        testValue = FD8XX_READ_ALTERNATE_DATA(BaseAddress, offset);
        if (oldValue != testValue) {

            FdDebugPrint(0x20, (0, "Read test failed. "));
            return FALSE;
        }
    }

    FdDebugPrint(0x20, (0, "Read test passed. "));

    //
    // Card may be present in the system.  Perform a write test.
    // The write value is insured to be different than the previous
    // value by adding 1
    //
    oldValue++;
    Fd8xxWriteControl(DeviceExtension,
                      (C_BUS_ENABLE | C_PARITY_ENABLE));

    FD8XX_WRITE_DATA(BaseAddress, oldValue);

    for (offset = 254; offset > 0; offset--) {

        testValue = FD8XX_READ_ALTERNATE_DATA(BaseAddress, offset);

        if (oldValue != testValue) {

            oldValue--;    // Get the original value and restore it.
            FD8XX_WRITE_DATA(BaseAddress, oldValue);
            FdDebugPrint(0x20, (0, "Write test failed. "));
            return FALSE;
        }
    }

    FdDebugPrint(0x20, (0, "Write test passed. "));

    return TRUE;
} // end Fd8xxCheckBaseAddress()


ULONG
Fd8xxParseArgumentString(
    IN PCHAR String,
    IN PCHAR KeyWord
    )

/*++

Routine Description:

    This routine will parse the string for a match on the keyword, then
    calculate the value for the keyword and return it to the caller.

Arguments:

    String - The ASCII string to parse.
    KeyWord - The keyword for the value desired.

Return Values:

    Zero if value not found
    Value converted from ASCII to binary.

--*/

{
    PCHAR cptr;
    PCHAR kptr;
    ULONG value;
    ULONG stringLength = 0;
    ULONG keyWordLength = 0;
    ULONG index;

    //
    // Calculate the string length and lower case all characters.
    //
    cptr = String;
    while (*cptr) {

        if (*cptr >= 'A' && *cptr <= 'Z') {
            *cptr = *cptr + ('a' - 'A');
        }
        cptr++;
        stringLength++;
    }

    //
    // Calculate the keyword length and lower case all characters.
    //
    cptr = KeyWord;
    while (*cptr) {

        if (*cptr >= 'A' && *cptr <= 'Z') {
            *cptr = *cptr + ('a' - 'A');
        }
        cptr++;
        keyWordLength++;
    }

    if (keyWordLength > stringLength) {

        //
        // Can't possibly have a match.
        //
        return 0;
    }

    //
    // Now setup and start the compare.
    //
    cptr = String;

ContinueSearch:
    //
    // The input string may start with white space.  Skip it.
    //
    while (*cptr == ' ' || *cptr == '\t') {
        cptr++;
    }

    if (*cptr == '\0') {

        //
        // end of string.
        //
        return 0;
    }

    kptr = KeyWord;
    while (*cptr++ == *kptr++) {

        if (*(cptr - 1) == '\0') {

            //
            // end of string
            //
            return 0;
        }
    }

    if (*(kptr - 1) == '\0') {

        //
        // May have a match backup and check for blank or equals.
        //

        cptr--;
        while (*cptr == ' ' || *cptr == '\t') {
            cptr++;
        }

        //
        // Found a match.  Make sure there is an equals.
        //
        if (*cptr != '=') {

            //
            // Not a match so move to the next semicolon.
            //
            while (*cptr) {
                if (*cptr++ == ';') {
                    goto ContinueSearch;
                }
            }
            return 0;
        }

        //
        // Skip the equals sign.
        //
        cptr++;

        //
        // Skip white space.
        //
        while ((*cptr == ' ') || (*cptr == '\t')) {
            cptr++;
        }

        if (*cptr == '\0') {

            //
            // Early end of string, return not found
            //
            return 0;
        }

        if (*cptr == ';') {

            //
            // This isn't it either.
            //
            cptr++;
            goto ContinueSearch;
        }

        value = 0;
        if ((*cptr == '0') && (*(cptr + 1) == 'x')) {

            //
            // Value is in Hex.  Skip the "0x"
            //
            cptr += 2;
            for (index = 0; *(cptr + index); index++) {

                if (*(cptr + index) == ' ' ||
                    *(cptr + index) == '\t' ||
                    *(cptr + index) == ';') {
                     break;
                }

                if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) {
                    value = (16 * value) + (*(cptr + index) - '0');
                } else {
                    if ((*(cptr + index) >= 'a') && (*(cptr + index) <= 'f')) {
                        value = (16 * value) + (*(cptr + index) - 'a' + 10);
                    } else {
    
                        //
                        // Syntax error, return not found.
                        //
                        return 0;
                    }
                }
            }
        } else {

            //
            // Value is in Decimal.
            //
            for (index = 0; *(cptr + index); index++) {

                if (*(cptr + index) == ' ' ||
                    *(cptr + index) == '\t' ||
                    *(cptr + index) == ';') {
                     break;
                }

                if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) {
                    value = (10 * value) + (*(cptr + index) - '0');
                } else {

                    //
                    // Syntax error return not found.
                    //
                    return 0;
                }
            }
        }

        return value;
    } else {

        //
        // Not a match check for ';' to continue search.
        //
        while (*cptr) {
            if (*cptr++ == ';') {
                goto ContinueSearch;
            }
        }

        return 0;
    }
}


BOOLEAN
Fd8xxFindSignature(
    PUCHAR romAddress,
    PUCHAR p1RomBiosId)
    
/*++

Routine Description:

    This routine searches for a P2 ROM BIOS signature within the rom address
    provided.
    
Arguments:

    romAddress - Location to search
    p1RomBiosId - BIOS string to match
    
Return Values:

    TRUE if found
    FALSE otherwise
    
--*/

{
    USHORT  count = 64;         // Check first 64 bytes of ROM space.
    PUCHAR  ptr1 = romAddress;

    while (count--) {

        PUCHAR  ptr2 = ptr1++;
        PUCHAR  ptr3 = p1RomBiosId;
        UCHAR   c = ScsiPortReadRegisterUchar(ptr2);

        while (c == *ptr3++) {

            ptr2++;
            c = ScsiPortReadRegisterUchar(ptr2);

            if (*ptr3 == '\0')
                return TRUE;

            if (c == '\0')
                return FALSE;
        }
    }

    return FALSE;
}


BOOLEAN
Fd8xxVerifyPatriot1(
    PSPECIFIC_DEVICE_EXTENSION      DeviceExtension,
    PPORT_CONFIGURATION_INFORMATION ConfigInfo
    )
    
/*++

Routine Descriptions:

    This routine verifies the presence of a P2 ROM based adapter.
    
Arguments:

    DeviceExtension - call context.
    ConfigInfo      - system provided configuration information.

Return Value:

    TRUE if found
    FALSE otherwise
    
--*/

{
    UCHAR   p1RomBiosId[] = "IBM F1 BIOS";
    BOOLEAN retval;

    if (Fd8xxFindSignature(DeviceExtension->BaseAddress, p1RomBiosId)) {

        //
        // We have a patriot-1 adapter installed.
        //
        retval = TRUE;

    } else {

        retval = FALSE;
    }

    return retval;
}


ULONG
Fd8xxFindAdapter(
    IN PVOID                                Context,
    IN PVOID                                AdaptersFound,
    IN PVOID                                BusInformation,
    IN PCHAR                                ArgumentString,
    IN OUT PPORT_CONFIGURATION_INFORMATION  ConfigInfo,
    OUT PBOOLEAN                            Again
    )

/*++

Routine Description:

    This routine maps the memory area for the FD8XX adapter into
    the virtual address space for the system.  It then attempts to
    find the adapter and set the configuration information.  If
    the adapter is not present in the system (or cannot be found)
    this routine returnes an indication as such (i.e. FALSE).

Arguments:

    Context         - The device specific context for the call.
    AdaptersFound   - Passed through from the driver entry as additional
                      context for the call.
    BusInformation  - Unused.
    ArgumentString  - Points to the potential IRQ for this adapter.
    ConfigInfo      - Pointer to the configuration information structure to
                      be filled in.
    Again           - Returns back a request to call this function again.

Return Value:

    SP_RETURN_FOUND     - if an adapter is found.
    SP_RETURN_NOT_FOUND - if no adapter is found.

--*/

{
    USHORT                      curBaseAddress;
    USHORT                      adaptersFound;
    ULONG                       returnStatus = SP_RETURN_NOT_FOUND;
    PSPECIFIC_DEVICE_EXTENSION  deviceExtension = Context;
    ULONG baseAddresses[] =
    {
        0x000c8000,
        0x000ca000,
        0x000ce000,
        0x000de000,
        0x000e8000,
        0x000ea000,
        0
    };

     //
     // This must never be set to TRUE for this controller.  If we return
     // TRUE for this parameter, the SCSI port driver will not move to the
     // next entry in the registry for the next controller of this type.
     // Instead, it will call this routine again with the SAME registry
     // information (i.e., IRQ vector) causing the next adapter with a
     // DIFFERENT IRQ to fail to install.
     // 
    *Again = FALSE;

    curBaseAddress = (USHORT) (*((PULONG) AdaptersFound) >> 16);
    adaptersFound = (USHORT) (*((PULONG) AdaptersFound));

    if ((baseAddresses[curBaseAddress] == 0) ||
           (adaptersFound == MAX_ADAPTERS)) {

        return SP_RETURN_NOT_FOUND;
    }

    while ((baseAddresses[curBaseAddress] != 0) &&
           (adaptersFound != MAX_ADAPTERS) &&
           (returnStatus == SP_RETURN_NOT_FOUND)) {

        PUCHAR  baseAddress;

        FdDebugPrint(0x20,
                         (0, "Fd8xxFindAdapter: baseAddresses[%x] = %x, ",
                         curBaseAddress,
                         baseAddresses[curBaseAddress]));
        FdDebugPrint(0x20,
                         (0, "%x adapters found so far.\n",
                         adaptersFound));

        //
        // Map the TMC-950 chip into the virtual memory address space.
        // If ConfigInfo already has default information about this
        // controller, use it.  If not, then we derive our own.  This
        // is for Chicago compatibility.
        //
        if (ScsiPortConvertPhysicalAddressToUlong(
                (*ConfigInfo->AccessRanges)[0].RangeStart) != 0) {

            deviceExtension->BaseAddress = baseAddress =
                ScsiPortGetDeviceBase(
                    deviceExtension,
                    ConfigInfo->AdapterInterfaceType,
                    ConfigInfo->SystemIoBusNumber,
                    (*ConfigInfo->AccessRanges)[0].RangeStart,
                    (*ConfigInfo->AccessRanges)[0].RangeLength,
                    (BOOLEAN) !((*ConfigInfo->AccessRanges)[0].RangeInMemory));
        } else {

            deviceExtension->BaseAddress = baseAddress =
                ScsiPortGetDeviceBase(
                    deviceExtension,
                    ConfigInfo->AdapterInterfaceType,
                    ConfigInfo->SystemIoBusNumber,
                    ScsiPortConvertUlongToPhysicalAddress(baseAddresses[curBaseAddress]),
                    (FD8XX_ADDRESS_SIZE),     // NumberOfBytes
                    (BOOLEAN) FALSE);         // InIoSpace
        }
        FdDebugPrint(0x20,
                    (0, "MmMapIoSpace address = %x ",
                    baseAddress));

        //
        // Determine if the card is really installed in the system.
        //
        if (Fd8xxCheckBaseAddress(deviceExtension, baseAddress) == FALSE) {

            //
            // We did not find an adapter at this baseAddress.  Free the
            // mapped memory space and continue with the next address if
            // there is one.
            //
            returnStatus = SP_RETURN_NOT_FOUND;
            ScsiPortFreeDeviceBase(deviceExtension,
                                   (PVOID) baseAddress);

        } else {

            returnStatus = SP_RETURN_FOUND;

            //
            // If there was no previously configured value for the IRQ,
            // look at the other way to configure the value or use a default.
            //
            if (ConfigInfo->BusInterruptLevel == 0) {

                ConfigInfo->BusInterruptLevel = FD8XX_IDT_VECTOR;

                if (ArgumentString != NULL) {
                    ULONG irq = Fd8xxParseArgumentString(ArgumentString, "irq");

                    if (irq == 0) {

                        //
                        // Check for the old way.
                        //
                        irq = *((PULONG)ArgumentString);
                        if (irq > 15) {

                            //
                            // really was intended to be zero.
                            //
                            irq = 0;
                            deviceExtension->ConfiguredWithoutInterrupts = TRUE;
                        } else {
                            irq = FD8XX_IDT_VECTOR;
                        }
                    }

                    FdDebugPrint(0x20,
                                (0,
                                "IRQ %d passed in. ",
                                irq));
                    ConfigInfo->BusInterruptLevel = irq;
                } else {

                    FdDebugPrint(0x20, (0, "default IRQ. "));
                }
            } else {

                FdDebugPrint(0x20,
                            (0,
                            "IRQ previously set to %d. ",
                            ConfigInfo->BusInterruptLevel));
            }

            //
            // If the user has not specified a target ID, fill in a default.
            //
	    if (ConfigInfo->InitiatorBusId[0] == (UCHAR)SP_UNINITIALIZED_VALUE) {

                deviceExtension->InitiatorId = SCSI_INITIATOR_ID;
                ConfigInfo->InitiatorBusId[0] = SCSI_INITIATOR_ID;
            } else {

                deviceExtension->InitiatorId = ConfigInfo->InitiatorBusId[0];
                FdDebugPrint(0x20,
                            (0,
                            "Initiator ID set to %d ",
                            ConfigInfo->InitiatorBusId[0]));
            }

            //
            // Fill in the access array information only if there are no
            // default parameters already there.
            //
            if (ScsiPortConvertPhysicalAddressToUlong(
                    (*ConfigInfo->AccessRanges)[0].RangeStart) == 0) {

                //
                // There is no pre-assigned information in the ConfigInfo
                // structure derived by Chicago.  Thus, we can fill
                // our own derived information into the ConfigInfo structure.
                // Otherwise WE MUST NOT change the ConfigInfo structure, as
                // this will overwrite the Chicago-prestored information.
                //
                (*ConfigInfo->AccessRanges)[0].RangeStart =
                    ScsiPortConvertUlongToPhysicalAddress(baseAddresses[curBaseAddress]);
                (*ConfigInfo->AccessRanges)[0].RangeLength = FD8XX_ADDRESS_SIZE;
                (*ConfigInfo->AccessRanges)[0].RangeInMemory = TRUE;

                ConfigInfo->ScatterGather = FALSE;
                ConfigInfo->Master = FALSE;
                ConfigInfo->MaximumTransferLength = MAX_TRANSFER_LENGTH;
                ConfigInfo->NumberOfBuses = 1;

                if (Fd8xxVerifyPatriot1(deviceExtension, ConfigInfo)) {

                    //
                    // We have a Patriot-I adapter installed in the system
                    // which scans from Target ID 7 to Target ID 0.
                    //
                    ConfigInfo->AdapterScansDown = TRUE;
                }
            }

            adaptersFound++;
        }

        curBaseAddress++;

        FdDebugPrint(0x20, (0, "\n"));
    }

    *((PULONG) AdaptersFound) = ((ULONG) curBaseAddress) << 16;
    *((PULONG) AdaptersFound) += (ULONG) adaptersFound;

    return returnStatus;
} // end Fd8xxFindAdapter()


BOOLEAN
Fd8xxAdapterState(
    IN PVOID    DeviceExtension,
    IN PVOID    AdaptersFound,
    IN BOOLEAN  SaveState
    )

/*++

Routine Description:

    Saves/restores adapter's real-mode configuration state.

Arguments:

    DeviceExtension - Adapter object device extension.
    AdaptersFound   - Passed through from DriverEntry as additional
                      context for the call.
    SaveState       - TRUE = Save adapter state, FALSE = restore state.

Return Value:

    The spec did not intend for this routine to have a return value.
    Whoever did the header file just forgot to change the BOOLEAN to
    a VOID.  We will just return FALSE to shot the compiler up.

--*/

{
    return FALSE;
}


ULONG
DriverEntry(
    IN PVOID DriverObject,
    IN PVOID Argument2
    )

/*++

Routine Description:

    Driver initialization entry point for system.

Arguments:

    DriverObject - The driver specific object pointer
    Argument2    - not used.

Return Value:

    Status from ScsiPortInitialize()

--*/

{
    HW_INITIALIZATION_DATA  hwInitializationData;
    ULONG                   i;

    FdDebugPrint(0x20, (0, "\nSCSI Future Domain 8XX Miniport Driver\n"));

    //
    // Zero out the hwInitializationData structure.
    //
    for (i = 0; i < sizeof(HW_INITIALIZATION_DATA); i++) {

        *(((PUCHAR)&hwInitializationData + i)) = 0;
    }

    hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);

    //
    // Set entry points.
    //
    hwInitializationData.HwInitialize   = Fd8xxInitialize;
    hwInitializationData.HwStartIo      = Fd8xxStartIo;
    hwInitializationData.HwInterrupt    = Fd8xxInterrupt;
    hwInitializationData.HwFindAdapter  = Fd8xxFindAdapter;
    hwInitializationData.HwResetBus     = Fd8xxResetBus;
    hwInitializationData.HwAdapterState = Fd8xxAdapterState;

    //
    // Indicate need buffer mapping but not physical addresses.
    //
    hwInitializationData.MapBuffers             = TRUE;
    hwInitializationData.NeedPhysicalAddresses  = FALSE;
    hwInitializationData.NumberOfAccessRanges   = 1;

    //
    // Specify size of device extension.
    //
    hwInitializationData.DeviceExtensionSize = sizeof(SPECIFIC_DEVICE_EXTENSION);

    //
    // Specify size of logical unit extension.
    //
    hwInitializationData.SpecificLuExtensionSize = sizeof(SPECIFIC_LU_EXTENSION);

    //
    // The fourth parameter below (i.e., "i") will show up as the
    // "AdaptersFound" parameter when FindAdapter() is called.
    //

    FdDebugPrint(0x20, (0, "Trying ISA...\n"));
    hwInitializationData.AdapterInterfaceType = Isa;

    i = 0;
    return ScsiPortInitialize(DriverObject,
                              Argument2,
                              &hwInitializationData,
                              &(i));

} // end DriverEntry()

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.