/*++
*******************************************************************************
* Copyright (c) 1990-99 Digi International Inc., All Rights Reserved          *
*                                                                             *
* This software contains proprietary and confidential information of Digi     *
* International Inc.  By accepting transfer of this copy, Recipient agrees    *
* to retain this software in confidence, to prevent disclosure to others,     *
* and to make no use of this software other than that for which it was        *
* delivered.  This is an unpublished copyrighted work of Digi International   *
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly   *
* prohibited.                                                                                                           *
*                                                                                                                                                       *
* Restricted Rights Legend                                                                                                      *
*                                                                                                                                                               *
* Use, duplication, or disclosure by the Government is subject to                               *
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in                   *
* Technical Data and Computer Software clause at DFARS 252.227-7031 or                  *
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -                    *
* Restricted Rights at 48 CFR 52.227-19, as applicable.                                                 *
*                                                                                                                                                               *
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343                    *
*                                                                                                                                                       *
*******************************************************************************

Module Name:

   dxbclyde.c

Abstract:

   This module contains routines specific to the Digi Clydesdale sync hardware.  
   Clydesdale is basically a PCI, cost-reduced, performance-enhanced, more scalable
   version of the EPC-based SyncPort and DataFire PRIme adapter.

Author:

   Michael Zarns

--*/

#include "digidxb.h"
#include "sradxb.h"
#pragma hdrstop
#if 0
#include "alloc.h"
#else
#undef NCLASS           /* We don't want to clash with the system's NCLASS */
#define NCLASS    10
struct clinit
{
    int32 ci_count;
    int32 ci_size;
};
struct alloc
{
    int32 n_qpairs;
    int32 n_streams;
    int32 n_mblocks;
    int32 n_classes;
    int32 n_bcalls;

    int32 n_putwait;
    int32 n_nrdy;
    int32 n_timeout;

    int32 n_loscale;
    int32 n_medscale;
    struct clinit n_classinfo[NCLASS];
};
#endif
#include "dxbshare.h"

// #define IGNORE_FEP_FAILURE

// PCI_COMMON_CONFIG.u.type0.BaseAddresses[] indices for the various IO and memory
// ranges requested by the clydesdale adapter.  This card requests 4 ranges: 
// PLX mapped into memory, PLX mapped into IO, shared memory (i.e. "dualport") 
// and control/flash memory.

#define CLYDE_PCI_PLX_MEM   0
#define CLYDE_PCI_PLX_IO    1
#define CLYDE_PCI_DUALPORT  2
#define CLYDE_PCI_CONTROL   3


// Define bitfields in the Clydesdale control (CSR) register, located at offset 0 in 
// control space.
#define CLYDE_CSR_IPEND                         0x01
#define CLYDE_CSR_ICLR                          CLYDE_CSR_IPEND
#define CLYDE_CSR_860_HRESET                            0x02
#define CLYDE_CSR_860_SRESET                    0x04
#define CLYDE_CSR_860_INT                       0x08            
#define CLYDE_CSR_603_RESET                             0x10
#define CLYDE_CSR_603_INT                                       0x20
#define CLYDE_CSR_RSVD                          0xC0

#define CLYDE_ASSERT_RESET                                      0xEB
#define CLYDE_DEASSERT_RESET                            0xEF
#define CLYDE_RESET_DONE                                        'ENOD'
#define CLYDE_RESET_DONE_OFFSET                 0x40


// EPC Fep constants
#define CLYDE_FEP_DOWNLOAD_OFFSET               0x4000

// DxbV1/Clydesdale onboard Queue descriptors
#define CLYDE_DXB_CMD_OFFSET                            0x2000
#define CLYDE_DXB_EVENT_OFFSET                  0x2100


/*--------------------------------------------------------------------------------------------*/
/* -- Implementation: ------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------*/

// Stratus 29/3/02

VOID
dxbClydeCheckHardware(
    IN PKDPC Dpc,
    IN PVOID DeferredContext,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2
    )
{
    PDXB_DEVICE_EXTENSION pExt = (PDXB_DEVICE_EXTENSION)DeferredContext;
    LARGE_INTEGER WaitTime;
    ULONG ReadWord;

    if (!pExt->bShutdown && !pExt->bAdapterFailed)
    {
        // This check reads the first byte of the card serial number (which
        // should always be 'V'. If not, either we've got a corrupted adapter,
        // or it's been removed...

        ReadWord = READ_REGISTER_UCHAR(
            (PVOID)((PUCHAR)pExt->Hardware.Memory.Virtual + 0x64));

        if ( ReadWord != 'V' 
#ifdef STRATUS_HAL
             && HalGetSlotStatus(pExt->Hardware.BusNumber,pExt->Hardware.SlotNumber,NULL) != H_ONLINE
#endif
            )
        {
            // hardware is gone
            SRAPRINT(DBGLVL_ALWAYS,("dxbClydeCheckHardware: adapter shoot/remove detected!\n"));
            pExt->bAdapterFailed = TRUE;
        }
    }

    if (pExt->bShutdown || pExt->bAdapterFailed)
    {
        // system being shutdown or hardware non-functional - cancel 
        // any outstanding requests
        DxbCancelAdapterIo( pExt, STATUS_IO_DEVICE_ERROR, TRUE/* all */);
    }

    // Schedule us to run again in 2s - do this so we can cancel
    // any future IO that gets initiated
    if (pExt->bTimerInitialized)
    {
        WaitTime.QuadPart = 2000 * ONE_MILLISECOND;
        KeSetTimer(&pExt->HardwareCheckTimer,
                   WaitTime,
                   &pExt->HardwareCheckDpc);
    }
}

NTSTATUS
dxbClydeStartHardwareCheck( IN PDXB_DEVICE_EXTENSION pExt )
{
    LARGE_INTEGER   WaitTime;

    if (!pExt->bTimerInitialized)
    {
        KeInitializeTimer(&pExt->HardwareCheckTimer);
        KeInitializeDpc(&pExt->HardwareCheckDpc,dxbClydeCheckHardware,(PVOID)pExt);
        pExt->bTimerInitialized = TRUE;
    }
        
    WaitTime.QuadPart = 2000 * ONE_MILLISECOND;
    if (KeSetTimer(&pExt->HardwareCheckTimer,
                   WaitTime,
                   &pExt->HardwareCheckDpc))
        return STATUS_SUCCESS;
    else
        return STATUS_UNSUCCESSFUL;
}

NTSTATUS
dxbClydeStopHardwareCheck(IN PDXB_DEVICE_EXTENSION pExt)
{
    LARGE_INTEGER Timeout;

    if (pExt->bTimerInitialized) {
        pExt->bTimerInitialized = FALSE;
        KeCancelTimer(&pExt->HardwareCheckTimer);
        // Wait a second for the timer to finish 
        Timeout.QuadPart = 500 * ONE_MILLISECOND;
        KeDelayExecutionThread(KernelMode,FALSE,&Timeout);
    }

    return STATUS_SUCCESS;
}

/* --------------------------------------------- */
/* -- dxbClydeEnablePci21                        */
/* Enables the PCI 2.1 mode on the PLX 2080 chip */
/* --------------------------------------------- */
void
dxbClydeEnablePci21(
        PDXB_DEVICE_EXTENSION pExt 
        )
{       
    ULONG Value;

    // Make a read register, modify and write cycle so we 
    // don't destroy any data in the register:
        
#define PLX9080_MARBR_REG 0xAC
    Value = READ_REGISTER_ULONG( (PVOID)((PCHAR)pExt->Hardware.PciInterfaceChip.Virtual+PLX9080_MARBR_REG) );
    Value |= ( 1 << 24 ); // Set PCI 2.1 enable bit
    WRITE_REGISTER_ULONG( (PVOID)((PCHAR)pExt->Hardware.PciInterfaceChip.Virtual+PLX9080_MARBR_REG), Value );
}

NTSTATUS
dxbClydeGetResources(
        IN OUT PDXB_DEVICE_EXTENSION pExt,
        IN PDIGI_RESOURCE_LIST       pResources )
/*++

Description:

        Map in memory resources.

Parameters:

        pExt
                Device extension for the adapter

        pResources
                Digi resource list defining PCI assigned resources for this adapter.

Return Value:

        STATUS_SUCCESS or failure status.

--*/
{
    NTSTATUS Status;
    Status = dxbMapMemory( &pResources->Io[CLYDE_PCI_DUALPORT], &pExt->Hardware.Memory );

    if ( NT_SUCCESS( Status ) )
        Status = dxbMapMemory( &pResources->Io[CLYDE_PCI_CONTROL], &pExt->Hardware.Memory2 );

    if ( NT_SUCCESS( Status ) )
        Status = dxbMapMemory( &pResources->Io[CLYDE_PCI_PLX_MEM], &pExt->Hardware.PciInterfaceChip );

    if ( NT_ERROR( Status ) )
    {
        dxbLogResourceConflict( &pExt->Name );
        dxbAdapterFreeResources( pExt );
        goto Exit;
    }

    DigiDump( DIGIINIT, ( "DXB: PCI Adapter %S resources:\n"
                          "   Memory:  Physical 0x%08x, Virtual 0x%08x, Length 0x%08x\n"
                          "   Control: Physical 0x%08x, Virtual 0x%08x, Length 0x%08x\n"
                          "   PlxMem:  Physical 0x%08x, Virtual 0x%08x, Length 0x%08x\n"
                          "   PlxIo:   Physical 0x%08x, Virtual 0x%08x, Length 0x%08x\n",
                          pExt->Name.Buffer, 
                          pExt->Hardware.Memory.Physical.LowPart,
                          pExt->Hardware.Memory.Virtual, 
                          pExt->Hardware.Memory.Length, 
                          pExt->Hardware.Memory2.Physical.LowPart,
                          pExt->Hardware.Memory2.Virtual, 
                          pExt->Hardware.Memory2.Length, 
                          pExt->Hardware.PciInterfaceChip.Physical.LowPart,
                          pExt->Hardware.PciInterfaceChip.Virtual, 
                          pExt->Hardware.PciInterfaceChip.Length, 
                          pResources->Io[CLYDE_PCI_PLX_IO].BaseAddress.LowPart, NULL, 
                          pResources->Io[CLYDE_PCI_PLX_IO].Length ) );
 Exit:
    return Status;
}


STATIC INLINE NTSTATUS
dxbClydeAssertReset(
        PDXB_DEVICE_EXTENSION pExt )
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;


    WRITE_REGISTER_ULONG( (PVOID)((PCHAR)pExt->Hardware.PciInterfaceChip.Virtual+CLYDE_RESET_DONE_OFFSET), 0 );
    DigiDump( DIGIINIT, ( "SRADXB: Resetting Sync adapter...\n" ) );

    // Assert reset for minimum 1 millisecond.
    WRITE_REGISTER_UCHAR( pExt->Hardware.Memory2.Virtual, CLYDE_ASSERT_RESET );
    {
        LARGE_INTEGER Delay;
        Delay.QuadPart = ONE_MILLISECOND;
        (void)KeDelayExecutionThread( KernelMode, FALSE, &Delay );
    }
    WRITE_REGISTER_UCHAR( pExt->Hardware.Memory2.Virtual, CLYDE_DEASSERT_RESET );
        
    // Wait for confirmation
    {
        ULONG i;
        LARGE_INTEGER Timeout;
#define TIMEOUT 7000
#define DELAY   20
#define COUNT   (TIMEOUT/DELAY)
        Timeout.QuadPart = DELAY * ONE_MILLISECOND;

        for ( i = 0; i < COUNT; i++ )
        {
            if ( READ_REGISTER_ULONG( (PVOID)( (PCHAR)pExt->Hardware.PciInterfaceChip.Virtual+CLYDE_RESET_DONE_OFFSET ) ) == CLYDE_RESET_DONE )
            {
                Status = STATUS_SUCCESS;
                break;
            }
            (void)KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
        }
#undef TIMEOUT
#undef DELAY
#undef COUNT
    }
    return Status;
}


STATIC INLINE NTSTATUS 
dxbClydeTestMemory( 
        PDXB_DEVICE_EXTENSION pExt )
{
#define DWORD_TEST_1 0xA55A3CC3
#define DWORD_TEST_2 0x5AA5C33C

    NTSTATUS Status;
    ULONG Result1, Result2;
    ULONG iFirst = 0;
    ULONG iLast = pExt->Hardware.Memory.cbWindow - sizeof(ULONG);

    DxbAdapterReadDword(pExt, iFirst);

#if 0
    DigiDump( DIGIINIT, 
              ( "Memory Test:  FirstDword       LastDword\n"
                "   Before:    0x%08x       0x%08x\n"
                "   Write:     0x%08x       0x%08x\n",
                DxbAdapterReadDword( pExt, iFirst ), DxbAdapterReadDword( pExt, iLast ),
                DWORD_TEST_1, DWORD_TEST_2 ) );
#endif

    DxbAdapterWriteDword( pExt, iFirst, DWORD_TEST_1 );
    DxbAdapterWriteDword( pExt, iLast, DWORD_TEST_2 );

    Result1 = DxbAdapterReadDword( pExt, iFirst );
    Result2 = DxbAdapterReadDword( pExt, iLast );

#if 0
    DigiDump( DIGIINIT, ( "After:        0x%08x       0x%08x\n", Result1, Result2 ) );
#endif

    if ( Result1 != DWORD_TEST_1 || Result2 != DWORD_TEST_2 )
    {
        Status = STATUS_UNSUCCESSFUL;
        dxbLogMemoryFailure( &pExt->Name );
    }
    else
        Status = STATUS_SUCCESS;

    return Status;
}


STATIC INLINE NTSTATUS
dxbClydeSetupMemory( 
        PDXB_DEVICE_EXTENSION pExt )
{
    ASSERT( pExt->Hardware.Memory.Length != 0 );

    if ( pExt->Hardware.Memory.Length != 0 )
    {
        pExt->Hardware.Memory.cbWindow = pExt->Hardware.Memory.Length;
        return STATUS_SUCCESS;
    }

    return STATUS_UNSUCCESSFUL;
}


STATIC VOID
dxbClydeGetQueueDescriptor( 
        IN PDXB_DEVICE_EXTENSION pExt,
        IN ULONG                 AdapterOffset,
        OUT PDXB_MESSAGE_QUEUE   pDesc )
/*++

Description:

        Read the queue descriptor from the adapter, fill in the DXB_MESSAGE_QUEUE struct in
        the device extension.

Parameters:

        pExt
                Device extension for the adapter

        AdapterOffset
                Adapter offset at which to find the queue descriptor

        pDesc
                Descriptor to be filled out.

Return Value:

        None

--*/
{
#define ADDRESS_MASK 0x00FFFFFF

    DXB_CMD DxbQueue;

    DxbAdapterRead( pExt, &DxbQueue, AdapterOffset, sizeof DxbQueue );

    // Q should be <= sizeof window
    ASSERT( DxbQueue.size <= pExt->Hardware.Memory.cbWindow );
    ASSERT( DxbQueue.size == pDesc->cbTotal );
    ASSERT( DxbQueue.in == 0 );
    ASSERT( DxbQueue.out == 0 )

        pDesc->Descriptor.QInOffset = AdapterOffset + FIELD_OFFSET( DXB_CMD, in );
    pDesc->Descriptor.QOutOffset = AdapterOffset + FIELD_OFFSET( DXB_CMD, out );

//      pDesc->QOffset = 0;                     // Already initialized to 0!
//      pDesc->cbTotal       = ;        // Filled in by InitAdapterBusMode!
//      pDesc->cbPage        = ;        // Filled in by InitAdapterBusMode!
//      pDesc->Master.nPages = ;        // Filled in by InitAdapterBusMode!

    if ( pExt->DxbBusMode == DXB_BUSMODE_SLAVE )
    {
        pDesc->Mode.Slave.Offset = DxbQueue.start & ADDRESS_MASK;
        DigiDump( DIGIINIT, ( "DXB: Queue: Start 0x%x, Size 0x%x, cbPage 0x%x, Window 0x%x\n",
                              pDesc->Mode.Slave.Offset, pDesc->cbTotal, pDesc->cbPage,
                              pDesc->Mode.Slave.Offset / pExt->Hardware.Memory.Length ) );
    }
}


STATIC NTSTATUS
dxbClydeStartBios( 
        IN PDXB_DEVICE_EXTENSION pExt,
        IN HANDLE hKeyParams,
        IN PWSTR  pDefBiosFile )
/*++

Description:

        Currently, simply waits up to 5 seconds for 603 and 860 heartbeats.  Futures: could 
        check for bios update and automatically flash it.

Parameters:

        pExt
                Device extension for the adapter

        hKeyParams
                Ignored for now.

        pDefBiosFile
                Ignored for now.

Return Value:

        STATUS_SUCCESS or failure status

--*/
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    ULONG i;
    LARGE_INTEGER Timeout;

    // Delay up to 5 seconds
#define TIMEOUT 5000
#define DELAY   20
#define COUNT   (TIMEOUT/DELAY)

    Timeout.QuadPart = DELAY * ONE_MILLISECOND;
    for ( i = 0; i < COUNT; i++ )
    {
#define OFFSET_860_HEARTBEAT 0x14
#define OFFSET_603_HEARTBEAT 0x24
        // TODO: Don't check for the 603 Heartbeat on non-populated boards
        if ( DxbAdapterReadDword( pExt, OFFSET_860_HEARTBEAT ) != 0  )
                     
        {
            Status = STATUS_SUCCESS;
            break;
        }
        (void)KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
    }
#undef OFFSET_860_HEARTBEAT
#undef OFFSET_603_HEARTBEAT
#undef TIMEOUT
#undef DELAY
#undef COUNT

// Added this delay per CurtM to help give Clyde more time to init memory.
#define MORE_DELAY
#ifdef MORE_DELAY
    {
        LARGE_INTEGER MoreDelay;
        MoreDelay.QuadPart = 100 * ONE_MILLISECOND;
        (void)KeDelayExecutionThread( KernelMode, FALSE, &MoreDelay );
    }
#endif
    if (Status == STATUS_SUCCESS)
    {
        DigiDump( DIGIINIT, ( "SRADXB: BIOS Started OK.\n") );
    }
    else
    {
        DigiDump( DIGIINIT, ( "SRADXB: BIOS Failed startup!\n") );
    }
    return Status;
}


#define CLYDE_DXB_QUEUE_SIZE                                    0x20000

#define CLYDE_MASTER_WINDOWED_PAGE_SIZE 0x1000
#define CLYDE_MASTER_WINDOWED_PAGE_COUNT        32

#if CLYDE_MASTER_WINDOWED_PAGE_COUNT != CMD_PHYS_PAGES || CLYDE_MASTER_WINDOWED_PAGE_COUNT != EVENT_PHYS_PAGES
#error Dxb Epc BusMaster page count error!
#endif

#if MAX_PAGES < CMD_PHYS_PAGES || MAX_PAGES < EVENT_PHYS_PAGES
#error Device Ext has insufficient sized Master.apPages arrays!
#endif

#define CLYDE_MASTER_FLAT_PAGE_SIZE                     0x20000
#define CLYDE_MASTER_FLAT_PAGE_COUNT            1


STATIC NTSTATUS
dxbClydeStartFep( 
        IN PDXB_DEVICE_EXTENSION pExt,
        IN HANDLE                hKeyParams,
        IN PWSTR                 pDefFepFile )
/*++

Description:

        Read the fep filename from registry, map in file, download image to the adapter,
        verify the download (DBG builds only), start the fep and wait for confirmation.

Parameters:

        pExt
                Device extension for the adapter

        hKeyParams
                Handle to opened registry key in which the bios filename can be found.

        pDefFepFile
                Default Fep filename

Return Value:

        STATUS_SUCCESS or failure status

--*/
{
    NTSTATUS Status;
    const ULONG OffsetXbusInit  = ROOTOFFSET_CLYDE + FIELD_OFFSET( struct shared, xbus_init );
    ULONG UseDxbV2;
    TIME Timeout;

    // Newer clydesdale firmware operates in Dxb V2 mode.  Optionally support either V1 or V2
    (void)DigiRegQueryDword( hKeyParams, DXB_REGVAL_DXBVERSION2, &UseDxbV2, DigiRegFlagOptional, 0 );
    if ( UseDxbV2 )
    {
        DigiDump( DIGIINIT, ( "DXB: Adapter %S running DXB version 2\n", pExt->Name.Buffer ) );
        dxbV2Init( pExt );
    }
    else
        dxbV1Init( pExt );

    Status = dxbDownload( pExt, hKeyParams, DXB_REGVAL_FEP, pDefFepFile, CLYDE_FEP_DOWNLOAD_OFFSET );
    if ( NT_ERROR( Status ) )
    {
        DigiDump( DIGIERROR, ( "DXB: Fep download failed, adapter %S!\n", pExt->Name.Buffer ) );
        goto Exit;
    }

    // Configure adapter busmode for busmaster queue access
    Status = DxbInitAdapterQueues( pExt, DXB_BUSMODE_MASTER_FLAT, CLYDE_MASTER_FLAT_PAGE_SIZE, CLYDE_MASTER_FLAT_PAGE_COUNT );

    // On error, fallback to less efficient busmodes...
    if ( NT_ERROR( Status ) )
        Status = DxbInitAdapterQueues( pExt, DXB_BUSMODE_MASTER_WINDOWED, CLYDE_MASTER_WINDOWED_PAGE_SIZE, CLYDE_MASTER_WINDOWED_PAGE_COUNT );

    if ( NT_ERROR(Status) && !UseDxbV2 )
        Status = DxbInitAdapterQueues( pExt, DXB_BUSMODE_SLAVE, CLYDE_DXB_QUEUE_SIZE, 1 );

    if ( NT_ERROR( Status ) )
    {
        DigiDump( DIGIERROR, ( "DXB: Adapter %S, Dxb BusMode failed!\n", pExt->Name.Buffer) );
    }

    if ( NT_ERROR( Status ) )
        goto Exit;

#pragma message( "Remind Milt to add the global host offset and V2=0x5 for clydesdale to firmware headers!!!" )

    // Tell adapter what busmode to use
    {
        struct xbus_init XbusInit = {0};
        if ( UseDxbV2 )
        {
            XbusInit.bus_mode = 5;      
        }
        else
        {
            switch ( pExt->DxbBusMode )
            {
            case DXB_BUSMODE_MASTER_FLAT:     XbusInit.bus_mode = BUS_MASTER_128K;  break;
            case DXB_BUSMODE_MASTER_WINDOWED: XbusInit.bus_mode = BUS_MASTER_4KX32; break;
            case DXB_BUSMODE_SLAVE:           XbusInit.bus_mode = BUS_SLAVE;        break;
            default:                          Status = STATUS_UNSUCCESSFUL;         goto Exit;
            }
        }               
        DxbAdapterWrite( pExt, &XbusInit, OffsetXbusInit, sizeof XbusInit );
    }
                
    // Tell adapter about our busmaster buffers
    if ( UseDxbV2 )
    {
        Status = DxbAllocGlobalHostData( pExt, 0x2000 );  // TODO: Get Milt to define this offset in a firmware header!
    }
    else
    {
        {
            // Command Queue
            DXB_CMD DxbCmd = {0};
            ULONG i;
            for ( i = 0; i < pExt->DxbCmdQ.Mode.Master.nPages; i++ )
                DxbCmd.phys_addrs[i] = (PVOID)pExt->DxbCmdQ.Mode.Master.PhysicalPages[i].LowPart;
            DxbAdapterWrite( pExt, &DxbCmd, CLYDE_DXB_CMD_OFFSET, sizeof DxbCmd );
        }
        {
            // Event Queue
            DXB_EVENT DxbEvent = {0};
            ULONG i;
            for ( i = 0; i < pExt->DxbEventQ.Mode.Master.nPages; i++ )
                DxbEvent.phys_addrs[i] = (PVOID)pExt->DxbEventQ.Mode.Master.PhysicalPages[i].LowPart;
            DxbAdapterWrite( pExt, &DxbEvent, CLYDE_DXB_EVENT_OFFSET, sizeof DxbEvent );
        }
    }

    // Tell bios to start fep
    {
#define CLYDE_FEP_START_ADDRESS 0x18
#define CLYDE_FEP_GO            0x1C
        DxbAdapterWriteDword( pExt, CLYDE_FEP_START_ADDRESS, CLYDE_FEP_DOWNLOAD_OFFSET );
        DxbAdapterWriteByte( pExt, CLYDE_FEP_GO, 1 );
    }

    // Tell the fep to begin initialization and wait for confirmation.
    // i.e. Set Control, then wait for Response.
    {
        const ULONG OffsetControl   = OffsetXbusInit + FIELD_OFFSET( struct xbus_init, board_ctl );
        const ULONG OffsetResponse  = OffsetXbusInit + FIELD_OFFSET( struct xbus_init, board_rsp );
        ULONG i, attempts;

        // The Fep spins at startup waiting for Control to go non-zero before continuing 
        // with initialization.  Set it.
                
        DxbAdapterWriteByte( pExt, OffsetControl,  1 );
                
        // Wait one second, to give the firmware time to start...
        Timeout.QuadPart = 1000 * ONE_MILLISECOND;
        
        KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
                
        // The previous DxbAdapterWriteByte results in a reset of the PCI 2.1 bit. 
        // Maybe this is another PLX bug. Therefore we need to set it again...:
        dxbClydeEnablePci21( pExt );


        DigiDump( DIGIINIT, ( "DXB: Waiting for fep to start" ) );
        Timeout.QuadPart = 100 * ONE_MILLISECOND;

        Status = STATUS_UNSUCCESSFUL;
        for ( i = 0; i < 200; i++ )
        {
            UCHAR Confirm = DxbAdapterReadByte( pExt, OffsetResponse );
            DigiDump( DIGIINIT, ( "." ) );
            if ( Confirm != 0 )
            {
                Status = STATUS_SUCCESS;
                break;
            }
            (void)KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
        }
        DigiDump( DIGIINIT, ( "\n" ) );

        if ( NT_ERROR( Status ) )
            goto Exit;

    }

    if ( !UseDxbV2 )
    {
        // Fep creates queue descriptors defining queue size, location, etc.  Get the info.
        dxbClydeGetQueueDescriptor( pExt, CLYDE_DXB_CMD_OFFSET, &pExt->DxbCmdQ );
        dxbClydeGetQueueDescriptor( pExt, CLYDE_DXB_EVENT_OFFSET, &pExt->DxbEventQ );
    }

    Status = STATUS_SUCCESS;

 Exit:
    return Status;
}


STATIC VOID
dxbClydeStopAdapter(
        IN PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

        Stop an EPC-based adapter.   Reset the card, release resources.

Parameters:

        pExt
                Device extension defining adapter

Return Value:

        None

--*/
{
    if ( pExt->hDsDevice )
    {
        if ( pExt->ControlStreams.Fr )
        {
            (void)DsClose( pExt->ControlStreams.Fr );
            pExt->ControlStreams.Fr = NULL;
        }
        if ( pExt->ControlStreams.Isdn )
        {
            (void)DsClose( pExt->ControlStreams.Isdn );
            pExt->ControlStreams.Isdn = NULL;
        }
        if ( pExt->ControlStreams.X25 )
        {
            (void)DsClose( pExt->ControlStreams.X25 );
            pExt->ControlStreams.X25 = NULL;
        }
        if ( pExt->ControlStreams.Lapb )
        {
            (void)DsClose( pExt->ControlStreams.Lapb );
            pExt->ControlStreams.Lapb = NULL;
        }

        (void)DsCloseDevice( pExt->hDsDevice );
        pExt->hDsDevice = NULL;
    }

    if ( pExt->Hardware.Memory.Virtual )
    {
        dxbQueueStopEventDpcTimer( pExt );
        dxbQueueStopCmdDpcTimer( pExt );
        dxbQueueStopCreditDpcTimer( pExt );

        (void)dxbClydeAssertReset( pExt );
        dxbAdapterFreeResources( pExt );
        pExt->Hardware.Memory.Virtual = NULL;
    }

    DxbCleanupGlobalHostData( pExt );
    DxbCleanupAdapterQueues( pExt );
    if ( pExt->hDsDevice )
    {
        if ( pExt->ControlStreams.Fr )
        {
            (void)DsClose( pExt->ControlStreams.Fr );
            pExt->ControlStreams.Fr = NULL;
        }
        if ( pExt->ControlStreams.Isdn )
        {
            (void)DsClose( pExt->ControlStreams.Isdn );
            pExt->ControlStreams.Isdn = NULL;
        }
        if ( pExt->ControlStreams.X25 )
        {
            (void)DsClose( pExt->ControlStreams.X25 );
            pExt->ControlStreams.X25 = NULL;
        }
        if ( pExt->ControlStreams.Lapb )
        {
            (void)DsClose( pExt->ControlStreams.Lapb );
            pExt->ControlStreams.Lapb = NULL;
        }

        (void)DsCloseDevice( pExt->hDsDevice );
        pExt->hDsDevice = NULL;
    }

    // Note that the call to dxbClydeStopHardwareCheck is made as late as possible
    // (i.e after the calls to dxbStopxxxTimer() fns) in order to avoid the possibility 
    // of Irp's not getting cancelled properly as a result of stopping too early !

    if ( pExt->Hardware.Memory.Virtual )
    {
        dxbQueueStopEventDpcTimer( pExt );
        dxbQueueStopCmdDpcTimer( pExt );
        dxbQueueStopCreditDpcTimer( pExt );

        (void)dxbClydeAssertReset( pExt );
        
        dxbClydeStopHardwareCheck( pExt );
        dxbAdapterFreeResources( pExt );

        pExt->Hardware.Memory.Virtual = NULL;
    }
    else
    {
        dxbClydeStopHardwareCheck( pExt );
    }

    DxbCleanupGlobalHostData( pExt );
    DxbCleanupAdapterQueues( pExt );
}


STATIC NTSTATUS
dxbClydeStartFirmware(
        IN PDXB_DEVICE_EXTENSION pExt,
        IN HANDLE                hKeyParams )
/*++

Description:

        Startup the adapter.  Upon successful return, the card should be running
        Dxb, ready for protocol stacks to be built.

Parameters:

        pExt
                Device extension defining the adapter

        hKeyParams
                Handle to opened registry "Parameters" key containing adapter params

Return Value:

        STATUS_SUCCESS or failure status.

--*/
{
    NTSTATUS Status;


    DigiDump( DIGIINIT, ( "DXB: Enter dxbClydeStartFirmware, adapter %S\n", pExt->Name.Buffer ) );
        
    ASSERT( pExt->Hardware.BusType == PCIBus );

    Status = dxbClydeAssertReset( pExt );
    if ( NT_ERROR( Status ) )
    {
        dxbLogAdapterResetFailed( &pExt->Name );
        goto Exit;
    }
        
    Status = dxbClydeStartBios( pExt, hKeyParams, NULL );
    if ( NT_ERROR( Status ) )
    {
        dxbLogBiosFailed( &pExt->Name );
        goto Exit;
    }
    Status = dxbClydeStartFep( pExt, hKeyParams, DXB_REGVAL_FEP_ENUM_CLYDE );
    if ( NT_ERROR( Status ) )
    {
        dxbLogFepFailed( &pExt->Name );
        goto Exit;
    }

    dxbClydeStartHardwareCheck( pExt );
 
 Exit:

    DigiDump( DIGIINIT, ( "DXB: Leave dxbClydeStartFirmware, Status 0x%x\n", Status ) );
    return Status;
}


NTSTATUS
dxbClydeStartNonCsuAdapter(
        IN PDXB_DEVICE_EXTENSION pExt, 
        IN PUNICODE_STRING       pOption,
        IN HANDLE                hKeyParams,
        IN PDIGI_RESOURCE_LIST   pResources )
/*++

Description:

        Startup the adapter and build protocol stacks as necessary.  Calls the startup 
        code, then builds protocol stacks on the adapter.

Parameters:

        pExt
                Device extension defining the adapter

        hKeyParams
                Handle to opened registry "Parameters" key containing adapter params

        pResources
                DigiLib resources list for this adapter.

Return Value:

        STATUS_SUCCESS or failure status.

--*/
{
    NTSTATUS Status;
    ASSERT( pExt->Hardware.BusType == PCIBus );

//    dxbV1Init( pExt ); // Moved to StartFep which checks for v1 or v2
    pExt->StopAdapter = dxbClydeStopAdapter;
    pExt->AssertReset = dxbClydeAssertReset;

    Status = dxbClydeGetResources( pExt, pResources );
    if ( NT_ERROR( Status ) )
    {
        dxbLogResourceConflict( &pExt->Name );
        goto Exit;
    }

#ifdef STRATUS_DEVICE
    /*
      DigiDump( DIGIINIT, ( "DXB: IRP_MN_START_DEVICE PCI SYNC Adapter %S resources in Bus %d and Slot %d:\n"
      "   Memory:  Physical 0x%08x, Virtual 0x%08x, Length 0x%08x\n"
      "   Control: Physical 0x%08x, Virtual 0x%08x, Length 0x%08x\n"
      "   PlxMem -- Sync Only:  Physical 0x%08x, Virtual 0x%08x, Length 0x%08x\n"
      "   Vendor ID: 0x%08x \n",
      pExt->Name.Buffer, 
      pExt->Hardware.BusNumber,  // Need to scan for this 
      pExt->Hardware.SlotNumber, // Also need to scan for this
      pExt->Hardware.Memory.Physical.LowPart,
      pExt->Hardware.Memory.Virtual, 
      pExt->Hardware.Memory.Length, 
      pExt->Hardware.Memory2.Physical.LowPart,
      pExt->Hardware.Memory2.Virtual, 
      pExt->Hardware.Memory2.Length,
      pExt->Hardware.PciInterfaceChip.Physical.LowPart, // Bar0 for Sync
      pExt->Hardware.PciInterfaceChip.Virtual, 
      pExt->Hardware.PciInterfaceChip.Length,
      pExt->Hardware.PciVendorId));  */

    pExt->uBar0Sync =  (ULONG)pExt->Hardware.PciInterfaceChip.Physical.LowPart;
    pExt->bAsyncDevice = FALSE;

    Status = FindPciSlotEIOVA(pExt);
    if ( NT_SUCCESS( Status ) )  {
                   
        DigiDump(DIGIINIT, ("Found DXB Adapter on Bus %d and Slot%d\n",
                            pExt->Hardware.BusNumber, pExt->Hardware.SlotNumber));

    }
    else
        ;
#endif

    DxbSlaveSetAdapterFlatMemory( pExt );

    // NOTE: NOTE:
    // For PLX 9054 (used on Sync2000 cPCI), a D3 to D0 transition clears the PLX config registers
    // Workaround: Assert the EEPROM reload bit prior to any PLX accesses after D3 to D0 transition
    // Note that this is the only opportunity the driver has to uniquely apply this to Sync2000
    // as such, the reload will be applied any time we start the driver
    // Currently the driver implements a workaround, in the future this should be fixed in the
    // board's BIOS or the PLX itself, this code will need to be revisited at that time
    {
        // Adapters that require the reload set this via the inf...
        ULONG bPlxReloadFlag = 0;
        (void)DigiRegQueryDword( hKeyParams, DXB_REGVAL_PLX_RELOAD_FLAG, &bPlxReloadFlag, DigiRegFlagOptional, DXB_REGVAL_PLX_RELOAD_FLAG_DEFAULT );
        if ( bPlxReloadFlag )
        {
            // Offset to the Serial EEPROM control register
#define PLX9054_SERIAL_EEPROM_CTL_REG 0x6c
            // This value represents the reset state of the register modified by setting the reload bit (0x20000000)
#define PLX9054_SERIAL_EEPROM_CTL_VAL 0x3001767E
#define EEPROM_RELOAD_DELAY   1000

            LARGE_INTEGER Timeout;
            Timeout.QuadPart = EEPROM_RELOAD_DELAY * ONE_MILLISECOND;
            WRITE_REGISTER_ULONG( (PVOID)((PCHAR)pExt->Hardware.PciInterfaceChip.Virtual+PLX9054_SERIAL_EEPROM_CTL_REG), PLX9054_SERIAL_EEPROM_CTL_VAL );
            (void)KeDelayExecutionThread( KernelMode, FALSE, &Timeout ); // Allow the config regs to be reloaded
        }
    }   

    // Enable PCI 2.1 mode on the PLX chip because Stratus reported a problem with the 
    // PLX chip causing the PCI bus to hang
    dxbClydeEnablePci21( pExt );

    // Assuming successful firmware start, we start our Stratus miniport.
    if ( NT_SUCCESS( Status ) )
    {
        Status = dxbClydeStartFirmware( pExt, hKeyParams );
        if ( NT_SUCCESS (Status) )
        {
            Status = SraMiniportInitialize(pExt);
        }
    }

    // Optionally allow driver to continue running even if fep fail to start
    if ( NT_ERROR( Status ) )
    {
        // If ignoring errors, don't startup dpc or try to init stacks!
        ULONG bIgnoreFailure = FALSE;
        (void)DigiRegQueryDword( hKeyParams, DXB_REGVAL_IGNORE_FEP_FAILURE, &bIgnoreFailure, DigiRegFlagOptional, FALSE );
        if ( bIgnoreFailure )
            Status = STATUS_SUCCESS;
        goto Exit;
    }

    Status = DsOpenDeviceInternal( pExt->pDeviceObject, &pExt->hDsDevice );
    if ( NT_ERROR( Status ) )
        goto Exit;

    // Start handling events from the adapter
    dxbQueueStartEventDpcTimer( pExt );

    // WAN module requires initialization before building protocol stacks
    Status = dxbWanInit( pExt );
    if ( NT_ERROR( Status ) )
        goto Exit;

    // Build protocol stacks for each configured line on the adapter
    {
        ULONG nLines;
        UCHAR i;

        Status = DigiRegQueryDword( hKeyParams, DXB_REGVAL_NUMBEROFLINES, &nLines, DigiRegFlagRequired, 0 );
        if ( NT_ERROR( Status ) )
            goto Exit;

        for ( i = 0; i < nLines; i++ )
        {
            HANDLE hKeyLine;
            Status = dxbBuildOpenLineKey( &hKeyLine, hKeyParams, i );
            if ( NT_SUCCESS( Status ) )
            {
                UNICODE_STRING Protocol;
                UNICODE_STRING Enum;

                // Get Protocol setting
                RtlInitUnicodeString( &Protocol, NULL );
                (void)DigiRegQuerySz( hKeyLine, DXB_REGVAL_PROTOCOL, &Protocol, 
                                      DigiRegFlagOptional, DXB_REGVAL_PROTOCOL_DEFAULT );

                // Build protocol stack accordingly

                RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_FRAMERELAY );
                if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
                {
                    Status = dxbBuildFrameOverWan( pExt, hKeyLine, i );
                    goto ProtocolDone;
                }

                RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_X25 );
                if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
                {
                    Status = dxbBuildX25OverWan( pExt, hKeyLine, i );
                    goto ProtocolDone;
                }

                RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_SNA );
                if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
                {
                    // No configuration necessary for SNA
                    Status = STATUS_SUCCESS;
                    goto ProtocolDone;
                }

                RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_PPP );
                if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
                {
                    // No configuration necessary for PPP
                    Status = STATUS_SUCCESS;
                    goto ProtocolDone;
                }


                RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_NONE );
                if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
                {
                    // No configuration necessary if no protocol
                    Status = STATUS_SUCCESS;
                    goto ProtocolDone;
                }

                // Didn't like the protocol setting, bail!
                Status = STATUS_UNSUCCESSFUL;
                DigiDump( DIGIERROR, ( "DXB: Port %d Invalid protocol setting, %S!\n", i, Protocol.Buffer ) );

            ProtocolDone:
                DigiRegFreeString( &Protocol );
                DigiRegCloseKey( hKeyLine );
                if ( NT_ERROR( Status ) )
                    break;
            }
#if NT5_BUILD
            else
            {
                // In W2K configurations, the lines are not set until the GUI is instantiates the lines,
                // however, DXB is started prior to configuration.  No configuration is a SUCCESS in W2K.
                Status = STATUS_SUCCESS;
                break;
            }
        }
#endif

    }

 Exit:

    if ( NT_ERROR( Status ) )
        dxbClydeStopAdapter( pExt );
    return Status;
}


