/*++
*******************************************************************************
* 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"
#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: ------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------*/


/* --------------------------------------------- */
/* -- 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 );

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

		DigiDump( DIGIINIT, ( "Wait for Assert confirmation...\n" ) );
		for ( i = 0; i < COUNT; i++ )
		{
			DigiDump( DIGIINIT, ( "." ) );
			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
	}
	DigiDump( DIGIINIT, ( "\n" ) );
	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

	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;

	// 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 );
		TIME Timeout;
		ULONG i;		


		// 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->bShutdown != TRUE ) &&
	     ( 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 );
}


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;
	}

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;
	}

	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 );

   if ( NT_SUCCESS( Status ) )
		Status = dxbClydeStartFirmware( pExt, hKeyParams );

	// 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;
			}
#ifdef 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;
}


