/*++
*******************************************************************************
* Copyright (c) 1990-2000 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:

   dxbepc.c

Abstract:

   This module contains routines common to the Digi EPC-based sync hardware.  This 
   includes all Isa/Pci/Mca SyncPort and Isa/Pci DataFire PRIme adapters.

Author:

   Michael Zarns

--*/

#include "digidxb.h"
#pragma hdrstop
#include "il3_sm.h"

#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"

// Io and Memory ranges requested by PCI adapters.  The PLX PCI Interface chip that
// these cards use requests 3 ranges: PLX mapped into memory, PLX mapped into IO, and
// our shared memory ("RAM").

#define EPC_PCI_PLX_MEM  0
#define EPC_PCI_PLX_IO   1
#define EPC_PCI_DUALPORT 2

// Control bits are located in IO space on ISA cards.  This space is memory mapped on 
// PCI cards at location MemLength/2.


// Reset bit is located in byte 0 of IO space.
#define EPC_ASSERT_RESET				0x04
#define EPC_RELEASE_RESET				0x00

// Memory Enable bit is located in byte 1 of IO space.
#define EPC_MEMORY_ENABLE				0x80
#define EPC_MEMORY_DISABLE				0x00

// EPC Bios constants
#define EPC_BIOS_DOWNLOAD_OFFSET		0x1000
#define EPC_BIOS_STATUS_OFFSET		0x0C00
#define EPC_BIOS_STATUS_OK				'DG'

// EPC Fep constants
#define EPC_FEP_DOWNLOAD_OFFSET		0x10000

// DxbV1/Epc constants
#define EPC_DXB_CMD_OFFSET				0x0100
#define EPC_DXB_EVENT_OFFSET			(EPC_DXB_CMD_OFFSET + sizeof (DXB_CMD))


STATIC NTSTATUS
dxbEpcGetIsaResources( 
	IN OUT PDXB_DEVICE_EXTENSION pExt,
	IN HANDLE                    hKey,
	IN PDIGI_RESOURCE_LIST       pResources,
	IN ULONG                     DefaultMemoryAddress,
	IN ULONG                     DefaultMemoryLength,
	IN ULONG                     DefaultIoAddress,
	IN ULONG                     DefaultIoLength )
/*++

Description:

	Read Io and Memory settings from registry.  Report the resources and map them in.

Parameters:

	pExt
		Device extension for the adapter

	hKey
		Registry key in which to find resource settings

	pResources
		Resources assigned by NT5 PNP.  Otherwise use following defaults in NT4.

	Default*
		Default values to use if they cannot be read from the registry.

Return Value:

	STATUS_SUCCESS or failure status.

--*/
{
	NTSTATUS Status;
	BOOLEAN fErrorConflict = FALSE;

#if NT5_BUILD
	ULONG i;
	ASSERT( pResources->BusType == Isa );

	// Grab Io and Memory resource assignments out of Resources list.  
	for ( i = 0, Status = STATUS_UNSUCCESSFUL; i < sizeof pResources->Io / sizeof pResources->Io[0]; i++ )
	{
		// Make sure addr and length are non-zero
		if ( pResources->Io[i].BaseAddressTranslated.LowPart && pResources->Io[i].Length )
		{
			// Map in Io or Memory as appropriate
			switch ( pResources->Io[i].RawType )
			{
				case CmResourceTypePort:
					Status = dxbMapMemory( &pResources->Io[i], &pExt->Hardware.Io );
					break;
				case CmResourceTypeMemory:
					Status = dxbMapMemory( &pResources->Io[i], &pExt->Hardware.Memory );
					break;

				default:
					DigiDump( DIGIWARN, ( "Dxb!GetIsaResources: Unknown AddressSpace, 0x%x!\n", pResources->Io[i].AddressSpace ) );
					break;
			}
		}
	}
	if ( !pExt->Hardware.Io.Virtual || !pExt->Hardware.Io.Length || 
	     !pExt->Hardware.Memory.Virtual || !pExt->Hardware.Memory.Length )
	{
		DigiDump( DIGIERROR, ( "*** Dxb!GetIsaResources: Resources not assigned!\n"
		                       "       Memory 0x%08x, cb 0x%x\n"
		                       "       Io     0x%08x, cb 0x%x\n",
		                       pExt->Hardware.Memory.Virtual, pExt->Hardware.Memory.Length,
		                       pExt->Hardware.Io.Virtual, pExt->Hardware.Io.Length ) );
		Status = STATUS_UNSUCCESSFUL;
	}
#else
	DigiRegQueryDword( hKey, DXB_REGVAL_MEMORY_ADDRESS, &pExt->Hardware.Memory.Physical.LowPart,
	                   DigiRegFlagOptional, DefaultMemoryAddress );

	DigiRegQueryDword( hKey, DXB_REGVAL_MEMORY_LENGTH, &pExt->Hardware.Memory.Length,
	                   DigiRegFlagOptional, DefaultMemoryLength );

	DigiRegQueryDword( hKey, DXB_REGVAL_IO_ADDRESS, &pExt->Hardware.Io.Physical.LowPart,
	                   DigiRegFlagOptional, DefaultIoAddress );

	DigiRegQueryDword( hKey, DXB_REGVAL_IO_LENGTH, &pExt->Hardware.Io.Length,
	                   DigiRegFlagOptional, DefaultIoLength );

	Status = DigiReportResources( gpDriverObject, 
	                              pExt->Hardware.BusType, pExt->Hardware.BusNumber,
	                              &pExt->Hardware.Io.Physical, pExt->Hardware.Io.Length,
	                              &pExt->Hardware.Memory.Physical, pExt->Hardware.Memory.Length,
	                              0, 0, 0, Latched, &fErrorConflict, &pExt->hReportedResources );

	if ( NT_SUCCESS( Status ) && fErrorConflict )
		Status = STATUS_INSUFFICIENT_RESOURCES;

	if ( NT_SUCCESS( Status ) )
		Status = DigiMapAddress( &pExt->Hardware.Memory.Virtual, &pExt->Hardware.Memory.fUnmap,
		                         pExt->Hardware.BusType, pExt->Hardware.BusNumber, pExt->Hardware.Memory.Physical,
		                         pExt->Hardware.Memory.Length, CM_RESOURCE_PORT_MEMORY );

	if ( NT_SUCCESS( Status ) )
		Status = DigiMapAddress( &pExt->Hardware.Io.Virtual, &pExt->Hardware.Io.fUnmap,
		                         pExt->Hardware.BusType, pExt->Hardware.BusNumber, pExt->Hardware.Io.Physical,
		                         pExt->Hardware.Io.Length, CM_RESOURCE_PORT_IO );
#endif

	pExt->Hardware.Memory.cbWindow = pExt->Hardware.Memory.Length;

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

	DigiDump( DIGIINIT, ( "DXB: Adapter %S resources:\n"
	                    "   Memory: Physical 0x%08x, Virtual 0x%08x, Length 0x%08x\n"
	                    "   Io:     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.Io.Physical.LowPart,
	                    pExt->Hardware.Io.Virtual, 
	                    pExt->Hardware.Io.Length ) );
	return Status;
}


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

Description:

	Read slot number from registry.  Report and map in 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[EPC_PCI_DUALPORT], &pExt->Hardware.Memory );
	pExt->Hardware.Memory.cbWindow = pExt->Hardware.Memory.Length / 2;

	if ( NT_SUCCESS( Status ) )
		Status = dxbMapMemory( &pResources->Io[EPC_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"
	                    "   PlxMem: 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.PciInterfaceChip.Physical.LowPart,
	                    pExt->Hardware.PciInterfaceChip.Virtual, 
	                    pExt->Hardware.PciInterfaceChip.Length ) );
Exit:
	return Status;
}


STATIC INLINE PUCHAR
dxbEpcIsaIoBase( 
	PDXB_DEVICE_EXTENSION pExt )
{
	return (PVOID)pExt->Hardware.Io.Virtual;
}


// Actual dual-port memory length is half the length of memory mapped in by the 
// PCI adapter; the first half.  Control Registers are mapped in the second half of
// mapped memory at (MemoryLength/2).

STATIC INLINE ULONG
dxbEpcPciRegsOffset( PDXB_DEVICE_EXTENSION pExt )
{
	return pExt->Hardware.Memory.cbWindow;
}


STATIC INLINE NTSTATUS
dxbEpcAssertResetIsa(
	PDXB_DEVICE_EXTENSION pExt )
{
	ULONG i;
	NTSTATUS Status;
	LARGE_INTEGER Timeout;

	ASSERT( pExt->Hardware.Io.Virtual != NULL );
	WRITE_PORT_UCHAR( dxbEpcIsaIoBase( pExt ), EPC_ASSERT_RESET );

	// Wait for confirmation of reset
	Timeout.QuadPart = 50 * ONE_MILLISECOND;

	for ( i = 0, Status = STATUS_UNSUCCESSFUL; i < 20; i++ )
	{
		if ( READ_PORT_UCHAR( dxbEpcIsaIoBase( pExt ) ) & EPC_ASSERT_RESET )
		{
			Status = STATUS_SUCCESS;
			break;
		}
		(void)KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
	}

	if ( NT_ERROR( Status ) )
		dxbLogAdapterResetFailed( &pExt->Name );

	return Status;
}


STATIC INLINE NTSTATUS
dxbEpcAssertResetPci(
	PDXB_DEVICE_EXTENSION pExt )
{
	ULONG i;
	NTSTATUS Status;
	LARGE_INTEGER Timeout;
	ULONG ResetOffset = dxbEpcPciRegsOffset(pExt);

	ASSERT( pExt->Hardware.Memory.Virtual != NULL );
	DxbAdapterWriteByte( pExt, ResetOffset, EPC_ASSERT_RESET );

	// Wait for confirmation of reset
	Timeout.QuadPart = 50 * ONE_MILLISECOND;

	for ( i = 0, Status = STATUS_UNSUCCESSFUL; i < 20; i++ )
	{
		if ( DxbAdapterReadByte( pExt, ResetOffset ) & EPC_ASSERT_RESET )
		{
			Status = STATUS_SUCCESS;
			break;
		}
		(void)KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
	}

	if ( NT_ERROR( Status ) )
		dxbLogAdapterResetFailed( &pExt->Name );

	return Status;
}


STATIC INLINE NTSTATUS
dxbEpcAssertReset( 
	PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Asserts the reset signal to the adapter to hold it in reset.

Parameters:

	pExt
		Device extension for the adapter.

Return Value:

	STATUS_SUCCESS if adapter successfully reset.  Or error status.

--*/
{
	NTSTATUS Status;
	ASSERT( pExt->Hardware.BusType == PCIBus ||
	        pExt->Hardware.BusType == Isa ||
	        pExt->Hardware.BusType == Eisa );
	switch ( pExt->Hardware.BusType )
	{
		case Isa:
		case Eisa:
			Status = dxbEpcAssertResetIsa( pExt );
			break;

		case PCIBus:
			Status = dxbEpcAssertResetPci( pExt );
			break;

		default:
			Status = STATUS_UNSUCCESSFUL;
			break;
	}
	return Status;
}


STATIC INLINE VOID
dxbEpcReleaseResetIsa(
	PDXB_DEVICE_EXTENSION pExt )
{
	WRITE_PORT_UCHAR( dxbEpcIsaIoBase( pExt ), EPC_RELEASE_RESET );
}


STATIC INLINE VOID
dxbEpcReleaseResetPci(
	PDXB_DEVICE_EXTENSION pExt )
{
	ULONG ResetOffset = dxbEpcPciRegsOffset(pExt);
	DxbAdapterWriteByte( pExt, ResetOffset, EPC_RELEASE_RESET );
}


STATIC INLINE VOID
dxbEpcReleaseReset( 
	PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Releases the reset signal to the adapter.

Parameters:

	pExt
		Device extension for the adapter.

Return Value:

	None

--*/
{
	ASSERT( pExt->Hardware.BusType == PCIBus ||
	        pExt->Hardware.BusType == Isa ||
	        pExt->Hardware.BusType == Eisa );
	switch ( pExt->Hardware.BusType )
	{
		case Isa:
		case Eisa:
			dxbEpcReleaseResetIsa( pExt );
			break;

		case PCIBus:
			dxbEpcReleaseResetPci( pExt );
			break;

		default:
			break;
	}
}


STATIC INLINE VOID
dxbEpcEnableWindow( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN USHORT                Window )
{
	DigiAcquireSpinLock( &pExt->WindowLock );
	WRITE_PORT_UCHAR( dxbEpcIsaIoBase( pExt ) + 1, (UCHAR)( EPC_MEMORY_ENABLE | Window ) );
}


STATIC INLINE VOID
dxbEpcDisableWindow( 
	IN PDXB_DEVICE_EXTENSION pExt )
{
	WRITE_PORT_UCHAR( dxbEpcIsaIoBase( pExt ) + 1, EPC_MEMORY_DISABLE );
	DigiReleaseSpinLock( &pExt->WindowLock );
}


STATIC INLINE NTSTATUS 
dxbEpcTestMemory( 
	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
dxbEpcSetupMemoryIsa( 
	PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Tell the isa adapter the memory address assigned to it.

   *	(io_port + 0):
   *	76543210
   *	||||||||-  Reserved
   *	|||||||--  Reserved
   *	||||||---  Host Adapter Reset
   *	|||||----- Host to Board Interrupt
   *	||||------ Reserved
   *
   *	(io_port + 1):
   *	76543210
   *	||||||||- Memory Select 0
   *	|||||||-- Memory Select 1
   *	||||||--- Memory Select 2
   *	|||||---- Memory Select 3
   *	||||----- Reserved
   *	|---------Memory Enable
   *	
   *	(io_port + 2):
   *	76543210
   *	||||||||- Interrupt Select 0
   *	|||||||-- Interrupt Select 1
   *	||||||--- Interrupt Select 2
   *	|||||---- Reserved
   *	|-------- Memory Location Bit 15
   *
   *	(io_port + 3):
   *	76543210
   *	||||||||- Memory Location Bit 16
   *	|||||||-- Memory Location Bit 17
   *	||||||--- Memory Location Bit 18
   *	|||||---- Memory Location Bit 19
   *	||||----- Memory Location Bit 20
   *	|||------ Memory Location Bit 21
   *	||------- Memory Location Bit 22
   *	|-------- Memory Location Bit 23

Parameters:

	pExt
		Device extension for the adapter

Return Value:

	STATUS_SUCCESS or failure status.

--*/
{
	NTSTATUS Status;
	USHORT Address;
	
	// Shared memory window size is same as physical mapped window
	ASSERT( pExt->Hardware.Memory.Length != 0 );
	if ( pExt->Hardware.Memory.Length == 0 )
	{
		dxbLogMemoryFailure( &pExt->Name );
		Status = STATUS_UNSUCCESSFUL;
		goto Exit;
	}

	pExt->Hardware.Memory.cbWindow = pExt->Hardware.Memory.Length;

	// Set memory address bits
	Address = (USHORT)( pExt->Hardware.Memory.Physical.LowPart >> 8 );

	WRITE_PORT_UCHAR( dxbEpcIsaIoBase( pExt ) + 2, (UCHAR)( Address & 0x00FF ) );
	WRITE_PORT_UCHAR( dxbEpcIsaIoBase( pExt ) + 3, (UCHAR)( Address / 0x100 ) );

	Status = dxbEpcTestMemory( pExt );

Exit:
	return Status;
}


STATIC INLINE NTSTATUS
dxbEpcSetupMemoryPci( 
	PDXB_DEVICE_EXTENSION pExt )
{
	NTSTATUS Status;
	ASSERT( pExt->Hardware.Memory.Length != 0 );
	if ( pExt->Hardware.Memory.Length != 0 )
	{
		pExt->Hardware.Memory.cbWindow = pExt->Hardware.Memory.Length / 2;
		Status = dxbEpcTestMemory( pExt );
	}
	else
		Status = STATUS_UNSUCCESSFUL;
	return Status;
}


STATIC VOID
dxbEpcGetQueueDescriptor( 
	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
dxbEpcStartBios( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN HANDLE hKeyParams,
	IN PCWSTR pDefBiosFile )
/*++

Description:

	Read the bios filename from registry, map in file, download image to the adapter,
	verify the download (DBG builds only), start the bios 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.

	pDefBiosFile
		Default Bios filename

Return Value:

	STATUS_SUCCESS or failure status

--*/
{
	NTSTATUS Status;
	Status = dxbDownload( pExt, hKeyParams, DXB_REGVAL_BIOS, 
	                      pDefBiosFile, EPC_BIOS_DOWNLOAD_OFFSET );
	
	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIERROR, ( "DXB: Bios download failed, adapter %S!\n", pExt->Name.Buffer ) );
		goto Exit;
	}

	{
		// Set adapter boot vector
		const ULONG BootVector[] = { 0x0BF00401, 0x00000000 };
		DxbAdapterWrite( pExt, BootVector, 0, sizeof BootVector );
	}

	// Clear post result area
	DxbAdapterWriteDword( pExt, EPC_BIOS_STATUS_OFFSET, 0 );

	{
		// Release reset and wait for bios to start
		LARGE_INTEGER Timeout;
		ULONG i;

		DigiDump( DIGIINIT, ( "DXB: Reset released, waiting for bios to start" ) );
		dxbEpcReleaseReset( pExt );
		Timeout.QuadPart = 100 * ONE_MILLISECOND;	// interval = 100 ms = 0.1 sec
		for ( Status = STATUS_UNSUCCESSFUL, i = 0; i < 300; i++ )	// max wait = 30 sec
		{
			USHORT Data;
			DigiDump( DIGIINIT, ( "." ) );
			DxbAdapterRead( pExt, &Data, EPC_BIOS_STATUS_OFFSET, sizeof Data );
			if ( Data == EPC_BIOS_STATUS_OK )
			{
				Status = STATUS_SUCCESS;
				break;
			}
			(void)KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
		}
		DigiDump( DIGIINIT, ( "\n" ) );
		if ( NT_ERROR( Status ) )
		{
			dxbLogBiosFailed( &pExt->Name );
			goto Exit;
		}
	}

Exit:
	return Status;
}


#define EPC_DXB_QUEUE_SIZE					0x8000

#define EPC_MASTER_WINDOWED_PAGE_SIZE	0x1000
#define EPC_MASTER_WINDOWED_PAGE_COUNT	32

#if EPC_MASTER_WINDOWED_PAGE_COUNT != CMD_PHYS_PAGES || EPC_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 EPC_MASTER_FLAT_PAGE_SIZE		0x8000
#define EPC_MASTER_FLAT_PAGE_COUNT		1

#define EPC_SLAVE_PAGE_SIZE				(EPC_MASTER_FLAT_PAGE_SIZE)
#define EPC_SLAVE_PAGE_COUNT				(EPC_MASTER_FLAT_PAGE_COUNT)

STATIC NTSTATUS
dxbEpcStartFep( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN HANDLE                hKeyParams,
	IN PCWSTR                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 fep filename can be found.

	pDefFepFile
		Default Fep filename

Return Value:

	STATUS_SUCCESS or failure status

--*/
{
	NTSTATUS Status;
	const ULONG OffsetXbusInit  = ROOTOFFSET_PRIME + FIELD_OFFSET( struct shared, xbus_init );
	DXB_BUS_MODE BusMode = DXB_BUSMODE_INVALID;

	Status = dxbDownload( pExt, hKeyParams, DXB_REGVAL_FEP, pDefFepFile, EPC_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 slave or busmaster queue access
	if ( pExt->Hardware.BusType == PCIBus )
	{
		Status = DxbInitAdapterQueues( pExt, DXB_BUSMODE_MASTER_FLAT, EPC_MASTER_FLAT_PAGE_SIZE, EPC_MASTER_FLAT_PAGE_COUNT );

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

		// On error, fallback to less efficient busmodes...
		if ( NT_ERROR( Status ) )
			Status = DxbInitAdapterQueues( pExt, DXB_BUSMODE_SLAVE, EPC_SLAVE_PAGE_SIZE, EPC_SLAVE_PAGE_COUNT );
	}
	else
		Status = DxbInitAdapterQueues( pExt, DXB_BUSMODE_SLAVE, EPC_SLAVE_PAGE_SIZE, EPC_SLAVE_PAGE_COUNT );

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

	// Tell adapter what busmode to use
	{
		struct xbus_init XbusInit = {0};

		switch ( pExt->DxbBusMode )
		{
			case DXB_BUSMODE_MASTER_FLAT:     XbusInit.bus_mode = BUS_MASTER_32K;  break;
			case DXB_BUSMODE_MASTER_WINDOWED: XbusInit.bus_mode = BUS_MASTER_4KX8; break;
			case DXB_BUSMODE_SLAVE:           XbusInit.bus_mode = BUS_SLAVE;       break;
			default:                          Status = STATUS_UNSUCCESSFUL;        goto Exit;
		}

		if ( pExt->Hardware.BusType == PCIBus )
		{
			// Default to newer optimized busmode for PRIme adapters.  Queue pointers are
			// located in PLX mailbox registers.  If set for Legacy mode, leave queue 
			// pointers in shared memory.
			ULONG UseLegacyBusMode;
			DigiRegQueryDword( hKeyParams, DXB_REGVAL_LEGACY_BUSMODE, &UseLegacyBusMode, DigiRegFlagOptional, 0 );
			if ( !UseLegacyBusMode )
				XbusInit.bus_mode |= BUS_USE_PLX_MAILBOXES;
		}

		DxbAdapterWrite( pExt, &XbusInit, OffsetXbusInit, sizeof XbusInit );
	}
	
	// Tell bios to start fep
	{
		LARGE_INTEGER Delay;
		const ULONG StartFepCommand[] = { 0x00000003, 0xBFC10004 };
		DxbAdapterWriteDword( pExt, 0x0C34, StartFepCommand[1] );
		DxbAdapterWriteDword( pExt, 0x0C30, StartFepCommand[0] );

		// Make sure he sees it?
		Delay.QuadPart = 20 * ONE_MILLISECOND;
		KeDelayExecutionThread( KernelMode, FALSE, &Delay );
	}

	// Initialize adapter queue descriptor blocks
	{
		// 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, EPC_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, EPC_DXB_EVENT_OFFSET, sizeof DxbEvent );
	}

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

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

		Status = STATUS_UNSUCCESSFUL;
		for ( i = 0; i < 100; 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;
	}

	// Fep creates queue descriptors defining queue size, location, etc.  Get the info.
	dxbEpcGetQueueDescriptor( pExt, EPC_DXB_CMD_OFFSET, &pExt->DxbCmdQ );
	dxbEpcGetQueueDescriptor( pExt, EPC_DXB_EVENT_OFFSET, &pExt->DxbEventQ );

	Status = STATUS_SUCCESS;

Exit:
	return Status;
}


STATIC NTSTATUS
dxbEpcVerifyAdapterIsa( 
	PVOID pIoBase )
/*++

Description:

	Tests the io port to see that it does indeed look like an ISA EPC-based card (which
	includes both SyncPort and DataFire PRIme).

Parameters:

	pIoBase
		Virtual address for the base io port of the adapter to be verified.  

Return Value:

	STATUS_SUCCESS if true.

--*/
{
	NTSTATUS Status;

	if ( READ_PORT_UCHAR( pIoBase ) == 0xFF )
	{
		DigiDump( DIGIERROR, ( "DXB: Adapter not present at IoBase 0x%x!\n", pIoBase ) );
		Status = STATUS_UNSUCCESSFUL;
		goto Exit;
	}

	Status = STATUS_SUCCESS;

Exit:
	return Status;
}


STATIC VOID
dxbEpcStopAdapter(
	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)dxbEpcAssertReset( pExt );
		dxbAdapterFreeResources( pExt );
		pExt->Hardware.Memory.Virtual = NULL;
	}

	if ( pExt->Hardware.BusType == PCIBus )
		DxbCleanupAdapterQueues( pExt );
}


STATIC NTSTATUS
dxbEpcStartAdapterIsa(
	IN PDXB_DEVICE_EXTENSION pExt,
	IN HANDLE                hKeyParams,
	IN PDIGI_RESOURCE_LIST   pResources,
	IN ULONG                 DefMemoryAddress,
	IN ULONG                 DefMemoryLength,
	IN ULONG                 DefIoAddress,
	IN ULONG                 DefIoLength,
	IN PCWSTR                DefBiosFile,
	IN PCWSTR                DefFepFile )
/*++

Description:

	Start an Epc-based ISA adapter to the point that Dxb is running and 
	ready for protocol stacks to be built.

Parameters:

	pExt
		Device extension defining the adapter to start

	hKeyParams
		Handle to opened registry key in which adapter params can be found.

	pResources
		Resources assigned by NT5 PNP.  Otherwise use following defaults in NT4.

	Defaults:
		DefMemoryAddress
		DefMemoryLength
		DefIoAddress
		DefIoLength
		DefBios
		DefFep

Return Value:

	STATUS_SUCCESS, or error

--*/
{
	NTSTATUS Status;

	DigiDump( DIGIINIT, ( "DXB: Enter dxbEpcStartAdapterIsa, adapter %S\n", pExt->Name.Buffer ) );
	
	ASSERT( pExt->Hardware.BusType == Isa || pExt->Hardware.BusType == Eisa );
	
	Status = dxbEpcGetIsaResources( pExt, hKeyParams, pResources, 
	                                DefMemoryAddress, DefMemoryLength, 
	                                DefIoAddress, DefIoLength );
	if ( NT_ERROR( Status ) )
	{
		dxbLogResourceConflict( &pExt->Name );
		goto Exit;
	}

	DxbSlaveSetAdapterWindowed( pExt, dxbEpcEnableWindow, dxbEpcDisableWindow );

	Status = dxbEpcVerifyAdapterIsa( pExt->Hardware.Io.Virtual );
	if ( NT_ERROR( Status ) )
	{
		dxbLogAdapterNotFound( &pExt->Name );
		goto Exit;
	}

	Status = dxbEpcAssertReset( pExt );
	if ( NT_ERROR( Status ) )
	{
		dxbLogAdapterResetFailed( &pExt->Name );
		goto Exit;
	}

	Status = dxbEpcSetupMemoryIsa( pExt );
	if ( NT_ERROR( Status ) )
	{
		dxbLogMemoryFailure( &pExt->Name );
		goto Exit;
	}

	Status = dxbEpcStartBios( pExt, hKeyParams, DefBiosFile );
	if ( NT_ERROR( Status ) )
	{
		dxbLogBiosFailed( &pExt->Name );
		goto Exit;
	}

	Status = dxbEpcStartFep( pExt, hKeyParams, DefFepFile );
	if ( NT_ERROR( Status ) )
	{
		dxbLogFepFailed( &pExt->Name );
		goto Exit;
	}

Exit:

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

	if ( NT_ERROR( Status ) )
		dxbEpcStopAdapter( pExt );

	return Status;
}


STATIC NTSTATUS
dxbEpcStartAdapterPci(
	IN PDXB_DEVICE_EXTENSION pExt,
	IN HANDLE                hKeyParams,
	IN PDIGI_RESOURCE_LIST   pResources,
	IN ULONG                 DeviceId,
	IN PCWSTR                DefBiosFile,
	IN PCWSTR                DefFepFile )
/*++

Description:

	Startup a Pci syncport 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

	DeviceId
		Expected PCI DeviceId for this adapter

Return Value:

	STATUS_SUCCESS or failure status.

--*/
{
	NTSTATUS Status;

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

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

	DxbSlaveSetAdapterFlatMemory( pExt );

	Status = dxbEpcAssertReset( pExt );
	if ( NT_ERROR( Status ) )
	{
		dxbLogAdapterResetFailed( &pExt->Name );
		goto Exit;
	}

	Status = dxbEpcSetupMemoryPci( pExt );
	if ( NT_ERROR( Status ) )
	{
		dxbLogMemoryFailure( &pExt->Name );
		goto Exit;
	}

	Status = dxbEpcStartBios( pExt, hKeyParams, DefBiosFile );
	if ( NT_ERROR( Status ) )
	{
		dxbLogBiosFailed( &pExt->Name );
		goto Exit;
	}

	Status = dxbEpcStartFep( pExt, hKeyParams, DefFepFile );
	if ( NT_ERROR( Status ) )
	{
		dxbLogFepFailed( &pExt->Name );
		goto Exit;
	}

Exit:

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

	if ( NT_ERROR( Status ) )
		dxbEpcStopAdapter( pExt );

	return Status;
}


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

Description:

	Startup a syncport adapter.  Calls the bus-specific 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

Return Value:

	STATUS_SUCCESS or failure status.

--*/
{
	NTSTATUS Status;

	dxbV1Init( pExt );
	pExt->StopAdapter = dxbEpcStopAdapter;
	pExt->AssertReset = dxbEpcAssertReset;

	switch ( pExt->Hardware.BusType )
	{
		case Isa:
		case Eisa:
			Status = dxbEpcStartAdapterIsa( pExt, hKeyParams, pResources,
			                                DXB_REGVAL_MEMORY_ADDRESS_ENUM_SYNCPORT,
			                                DXB_REGVAL_MEMORY_LENGTH_ENUM_SYNCPORT,
			                                DXB_REGVAL_IO_ADDRESS_ENUM_SYNCPORT,
			                                DXB_REGVAL_IO_LENGTH_ENUM_SYNCPORT,
			                                DXB_REGVAL_BIOS_ENUM_SYNCPORT_ISA,
			                                DXB_REGVAL_FEP_ENUM_SYNCPORT_ISA );
			break;

		case PCIBus:
			Status = dxbEpcStartAdapterPci( pExt, hKeyParams, pResources,
			                                DIGI_PCI_DEVICEID_SYNCPORT,
			                                DXB_REGVAL_BIOS_ENUM_SYNCPORT_PCI,
			                                DXB_REGVAL_FEP_ENUM_SYNCPORT_PCI );
			break;

		default:
			Status = STATUS_UNSUCCESSFUL;
			break;
	}

	if ( NT_ERROR( Status ) )
		goto Exit;

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

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

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

	{
		// Build protocol stacks for each physical line on the adapter
		UCHAR iPort = 0;
		HANDLE hKeyPort;
		NTSTATUS PortStatus;

		for ( iPort = 0, Status = STATUS_SUCCESS; 
		      NT_SUCCESS( dxbBuildOpenLineKey( &hKeyPort, hKeyParams, iPort ) ); 
		      iPort++ )
		{
			UNICODE_STRING Protocol;
			UNICODE_STRING Enum;

			RtlInitUnicodeString( &Protocol, NULL );
			(void)DigiRegQuerySz( hKeyPort, DXB_REGVAL_PROTOCOL, &Protocol, 
			                      DigiRegFlagOptional, DXB_REGVAL_PROTOCOL_DEFAULT );

			RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_FRAMERELAY );
			if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
			{
				PortStatus = dxbBuildFrameOverWan( pExt, hKeyPort, iPort );
				goto ProtocolDone;
			}

			RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_X25 );
			if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
			{
				PortStatus = dxbBuildX25OverWan( pExt, hKeyPort, iPort );
				goto ProtocolDone;
			}

			RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_NONE );
			if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
			{
				PortStatus = STATUS_SUCCESS;
				goto ProtocolDone;
			}
			else
			{
				PortStatus = STATUS_UNSUCCESSFUL;
				DigiDump( DIGIERROR, ( "DXB: Port %d Invalid protocol setting, %S!\n", iPort, Protocol.Buffer ) );
				goto ProtocolDone;
			}

ProtocolDone:
			DigiRegFreeString( &Protocol );
			DigiRegCloseKey( hKeyPort );
			Status = NT_ERROR( PortStatus ) ? PortStatus : Status;
		}
	}

Exit:

	if ( NT_ERROR( Status ) )
		dxbEpcStopAdapter( pExt );

	return Status;
}


#if NT5_BUILD
STATIC NTSTATUS
dxEpcTranslateIsdnWizardToPrime(
	PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Read in ISDN settings written by the NT5 ISDN Wizard and translate them into HLC 
	style ISDN params.  Write them back into Digi tree, leaving the ISDN Wiz settings 
	untouched.

Parameters:

	pExt
		Device extension for the adapter to be translated.

Return Value:

	STATUS_SUCCESS or error.

--*/
{
	NTSTATUS Status;
	ULONG nLines;
	ULONG WizSwitchType;
	HANDLE hkAdapter = NULL;
	HANDLE hkParameters = NULL;
	ULONG Nationality;

	// Open adapter registry key, i.e. CurrentControlSet\Class\{...}\0001.
	Status = IoOpenDeviceRegistryKey( pExt->pPdo, PLUGPLAY_REGKEY_DRIVER, MAXIMUM_ALLOWED, &hkAdapter );
	if ( NT_ERROR( Status ) )
		goto Exit;

	Status = DigiRegQueryDword( hkAdapter, DXB_REGVAL_ISDNWIZ_SWITCH_TYPE, &WizSwitchType, DigiRegFlagRequired, 0 );
	if ( NT_ERROR( Status ) )
	{
		// Ignore error.  IsdnWizard hasn't run yet, use default settings.
		Status = STATUS_SUCCESS;
		goto Exit;
	}

	// TODO: Define IsdnNumDChannels string in dxbreg.h
	Status = DigiRegQueryDword( hkAdapter, DXB_REGVAL_ISDNWIZ_NUM_DCHANNELS, &nLines, DigiRegFlagRequired, 0 );
	if ( NT_SUCCESS( Status ) )
	{
		switch ( WizSwitchType )
		{
			case ISDN_SWITCH_ATT:	Nationality = NAT_ATT_41449;			break;
			case ISDN_SWITCH_NTI:	Nationality = NAT_DMS100;				break;
			case ISDN_SWITCH_INS64:	Nationality = NAT_NTT;					break;
			case ISDN_SWITCH_1TR6:	Nationality = NAT_GERMAN;				break;
			case ISDN_SWITCH_DSS1:	Nationality = NAT_ETSI;					break;
			case ISDN_SWITCH_AUS:	Nationality = NAT_AUSTEL;				break;
			case ISDN_SWITCH_NI2:	Nationality = NAT_NATIONAL_ISDN_2;	break;
			default:			
				Status = STATUS_UNSUCCESSFUL;	break;
		}
	}

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegOpenSubkey( &hkParameters, hkAdapter, DXB_REGKEY_PARAMETERS );

	if ( NT_SUCCESS( Status ) )
	{
		UCHAR i;
		for ( i = 0; i < nLines; i++ )
		{
			HANDLE hkLine;
			Status = dxbBuildOpenLineKey( &hkLine, hkParameters, i );
			if ( NT_SUCCESS( Status ) )
			{
				HANDLE hkDs1;
				Status = DigiRegOpenSubkey( &hkDs1, hkLine, DXB_REGKEY_DS1 );
				if ( NT_SUCCESS( Status ) )
				{
					// Special case: this value overrides the standard HLC-style Signalling
					// value used for switch type.  Reason: Prime supports more switches 
					// than 
					Status = DigiRegWriteDword( hkDs1, DXB_REGVAL_IL3_NATIONALITY, Nationality );
					DigiRegCloseKey( hkDs1 );
				}
				DigiRegCloseKey( hkLine );
			}
			if ( NT_ERROR( Status ) )
			{
				DigiDump( DIGIERROR, ( "Dxb!TranslateIsdnWizardToPrime: Failure on line %d, Status 0x%x!\n", i, Status ) );
				break;
			}
		}
	}

Exit:

	if ( hkParameters )
		DigiRegCloseKey( hkParameters );

	if ( hkAdapter )
		DigiRegCloseKey( hkAdapter );

	return Status;
}
#endif


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

Description:

	Startup a Datafire PRIme adapter.  Calls the bus-specific 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

Return Value:

	STATUS_SUCCESS or failure status.

--*/
{
	NTSTATUS Status;

	pExt->StopAdapter = dxbEpcStopAdapter;
	pExt->AssertReset = dxbEpcAssertReset;

	// Reset these.  Otherwise, on return from Hibernate, they aren't initialized.
	pExt->DxbEventQ.QOffset = pExt->DxbCmdQ.QOffset = 0;

	switch ( pExt->Hardware.BusType )
	{
		case Isa:
		case Eisa:
			dxbV1Init( pExt );
			Status = dxbEpcStartAdapterIsa( pExt, hKeyParams, pResources,
			                                DXB_REGVAL_MEMORY_ADDRESS_ENUM_PRIME,
			                                DXB_REGVAL_MEMORY_LENGTH_ENUM_PRIME,
			                                DXB_REGVAL_IO_ADDRESS_ENUM_PRIME,
			                                DXB_REGVAL_IO_LENGTH_ENUM_PRIME,
			                                DXB_REGVAL_BIOS_ENUM_PRIME,
			                                DXB_REGVAL_FEP_ENUM_PRIME );
			break;

		case PCIBus:
		{
			// Default to newer optimized busmode for PRIme adapters.  Queue pointers are
			// located in PLX mailbox registers.  If set for Legacy mode, leave queue 
			// pointers in shared memory.
			ULONG LegacyBusMode = FALSE;
			DigiRegQueryDword( hKeyParams, DXB_REGVAL_LEGACY_BUSMODE, &LegacyBusMode, DigiRegFlagOptional, 0 );
			if ( LegacyBusMode )
				dxbV1Init( pExt );
			else
				dxbV1aInit( pExt );

			Status = dxbEpcStartAdapterPci( pExt, hKeyParams, pResources,
			                                DIGI_PCI_DEVICEID_PRIME,
			                                DXB_REGVAL_BIOS_ENUM_PRIME,
			                                DXB_REGVAL_FEP_ENUM_PRIME );
			break;
		}

		default:
			Status = STATUS_UNSUCCESSFUL;
			break;
	}

	if ( NT_ERROR( Status ) )
		goto Exit;

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

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

#if NT5_BUILD
	Status = dxEpcTranslateIsdnWizardToPrime( pExt );
#endif

	if ( NT_SUCCESS( Status ) )
	{
		// Build protocol stacks for each physical line on the adapter
		UCHAR iPort = 0;
		HANDLE hKeyPort;
		NTSTATUS PortStatus;

		for ( iPort = 0, Status = STATUS_SUCCESS; 
		      NT_SUCCESS( dxbBuildOpenLineKey( &hKeyPort, hKeyParams, iPort ) ); 
		      iPort++ )
		{
			UNICODE_STRING Protocol;
			UNICODE_STRING Enum;

			RtlInitUnicodeString( &Protocol, NULL );
			(void)DigiRegQuerySz( hKeyPort, DXB_REGVAL_PROTOCOL, &Protocol, 
			                      DigiRegFlagOptional, DXB_REGVAL_PROTOCOL_DEFAULT );

			RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_PRI );
			if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
			{
				PortStatus = dxbBuildPri( pExt, hKeyPort, iPort );
				goto ProtocolDone;
			}

			RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_E1 );
			if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
			{
				PortStatus = dxbBuildPri( pExt, hKeyPort, iPort );
				goto ProtocolDone;
			}

			RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_FRAMERELAY );
			if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
			{
				PortStatus = dxbBuildFrameOverIwan( pExt, hKeyPort, iPort );
				goto ProtocolDone;
			}

			RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_PPP );
			if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
			{
				PortStatus = dxbT1Tune( pExt, hKeyPort, iPort, 23 );
				goto ProtocolDone;
			}

			RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_NONE );
			if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
			{
				PortStatus = STATUS_SUCCESS;
				goto ProtocolDone;
			}
			else
			{
				PortStatus = STATUS_UNSUCCESSFUL;
				DigiDump( DIGIERROR, ( "DXB: Port %d Invalid protocol setting, %S!\n", iPort, Protocol.Buffer ) );
				goto ProtocolDone;
			}

ProtocolDone:
			DigiRegFreeString( &Protocol );
			DigiRegCloseKey( hKeyPort );
			Status = NT_ERROR( PortStatus ) ? PortStatus : Status;
		}
	}

Exit:

	if ( NT_ERROR( Status ) )
		dxbEpcStopAdapter( pExt );

	return Status;
}
