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

   dxbhlc.c

Abstract:

   Routines specific to the Houston, London, Chaska series of hardware.

Author:

   Michael Zarns

--*/
#include "digidxb.h"
#pragma hdrstop // Stop precomiled header generation

#define GOOFY_FIRMWARE_NEEDS_DCHAN

// Dense Firmware
#include "mgmthlc.h"		// Modified on: 8-15-00
#include "hou2mmap.h"
#include "mipsheader.hh"
#include "64011.h"

// Io and Memory ranges requested by PCI adapters.  The Galileo PCI Interface chip that
// these cards use requests 3 ranges: Galileo registers, DSP GPP, and our shared 
// memory ("RAM").
#define HLC_PCI_MEM_RAM     0
#define HLC_PCI_MEM_DSPGPP  1
#define HLC_PCI_MEM_GALILEO 2

typedef enum HLC_IFACE
{
	IFACE_T1,
	IFACE_E1
} HLC_IFACE;

// Use the HLC_PCI_ADAPTER_MAP struct to map PCI identifiers to adapter types
// including T1 vs E1 and number of lines
typedef struct HLC_PCI_ADAPTER_MAP
{
	IN ULONG VendorId;
	IN ULONG DeviceId;
	IN ULONG SubVendorId;
	IN ULONG SubSystemId;
	OUT HLC_IFACE Interface;
	OUT ULONG nLines;  
} HLC_PCI_ADAPTER_MAP;

static const HLC_PCI_ADAPTER_MAP HoustonPciMap[] = 
{
	// New PCI ID Scheme
	{ DIGI_PCI_VENDORID, DIGI_PCI_DEVICEID_HOUSTON_T1_SINGLE, DIGI_PCI_VENDORID, DIGI_PCI_SUBDEVICEID_HOUSTON_T1_SINGLE, IFACE_T1, 1 },	// T1 Single
	{ DIGI_PCI_VENDORID, DIGI_PCI_DEVICEID_HOUSTON_T1_DUAL,   DIGI_PCI_VENDORID, DIGI_PCI_SUBDEVICEID_HOUSTON_T1_DUAL,   IFACE_T1, 2 },	// T1 Dual
	{ DIGI_PCI_VENDORID, DIGI_PCI_DEVICEID_HOUSTON_E1_SINGLE, DIGI_PCI_VENDORID, DIGI_PCI_SUBDEVICEID_HOUSTON_E1_SINGLE, IFACE_E1, 1 },	// E1 Single
	{ DIGI_PCI_VENDORID, DIGI_PCI_DEVICEID_HOUSTON_E1_DUAL,   DIGI_PCI_VENDORID, DIGI_PCI_SUBDEVICEID_HOUSTON_E1_DUAL,   IFACE_E1, 2 },	// E1 Dual

	// Patton PCI ID Scheme
	{ PATTON_PCI_VENDORID, DIGI_PCI_DEVICEID_HOUSTON_T1_SINGLE, PATTON_PCI_VENDORID, DIGI_PCI_SUBDEVICEID_HOUSTON_T1_SINGLE, IFACE_T1, 1 },	// T1 Single
	{ PATTON_PCI_VENDORID, DIGI_PCI_DEVICEID_HOUSTON_T1_DUAL,   PATTON_PCI_VENDORID, DIGI_PCI_SUBDEVICEID_HOUSTON_T1_DUAL,   IFACE_T1, 2 },	// T1 Dual
	{ PATTON_PCI_VENDORID, DIGI_PCI_DEVICEID_HOUSTON_E1_SINGLE, PATTON_PCI_VENDORID, DIGI_PCI_SUBDEVICEID_HOUSTON_E1_SINGLE, IFACE_E1, 1 },	// E1 Single
	{ PATTON_PCI_VENDORID, DIGI_PCI_DEVICEID_HOUSTON_E1_DUAL,   PATTON_PCI_VENDORID, DIGI_PCI_SUBDEVICEID_HOUSTON_E1_DUAL,   IFACE_E1, 2 },	// E1 Dual

	// IBM PCI ID Scheme
	{ IBM_PCI_VENDORID, IBM_PCI_DEVICEID_HOUSTON_T1_SINGLE, IBM_PCI_VENDORID, IBM_PCI_SUBDEVICEID_HOUSTON_T1_SINGLE, IFACE_T1, 1 },	// T1 Single
	{ IBM_PCI_VENDORID, IBM_PCI_DEVICEID_HOUSTON_T1_DUAL,   IBM_PCI_VENDORID, IBM_PCI_SUBDEVICEID_HOUSTON_T1_DUAL,   IFACE_T1, 2 },	// T1 Dual
	{ IBM_PCI_VENDORID, IBM_PCI_DEVICEID_HOUSTON_E1_SINGLE, IBM_PCI_VENDORID, IBM_PCI_SUBDEVICEID_HOUSTON_E1_SINGLE, IFACE_E1, 1 },	// E1 Single
	{ IBM_PCI_VENDORID, IBM_PCI_DEVICEID_HOUSTON_E1_DUAL,   IBM_PCI_VENDORID, IBM_PCI_SUBDEVICEID_HOUSTON_E1_DUAL,   IFACE_E1, 2 },	// E1 Dual

	// Old PCI ID Scheme
	{ DIGI_PCI_VENDORID, 0x1d, DIGI_PCI_VENDORID, 0x4146, IFACE_T1, 1 },	// T1 Single
	{ DIGI_PCI_VENDORID, 0x2a, DIGI_PCI_VENDORID, 0x4146, IFACE_T1, 2 },	// T1 Dual
	{ DIGI_PCI_VENDORID, 0x2b, DIGI_PCI_VENDORID, 0x4146, IFACE_E1, 1 },	// E1 Single
	{ DIGI_PCI_VENDORID, 0x2c, DIGI_PCI_VENDORID, 0x4146, IFACE_E1, 2 },	// E1 Dual
};


NTSTATUS
DxbHlcWaitFor( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 AdapterOffset,
	IN ULONG                 Wanted,
	IN ULONG                 Milliseconds )
{
	NTSTATUS Status;
	ULONG i;
	LARGE_INTEGER Timeout;
	ULONG Count = 1 + Milliseconds / 20;	// minimum of one.
	Timeout.QuadPart = 20 * ONE_MILLISECOND;

	Status = STATUS_UNSUCCESSFUL;
	for ( i = 0; i < Count; i++ )
	{
		if ( DxbAdapterReadDword( pExt, AdapterOffset ) == Wanted )
		{
			Status = STATUS_SUCCESS;
			break;
		}
		(void)KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
	}
	return Status;
}


STATIC NTSTATUS
dxbHlcGetGlobalCommonOffset( 
	IN PDXB_DEVICE_EXTENSION pExt,
	OUT PULONG               pOffset,
	IN BOOLEAN               fNoDelay )
/*++

Description:

	Read the offset of the GLOBAL_COMMON struct from the card.  

Parameters:

	pExt
		Device extension defining the adapter

	pOffset
		Output Offset value, if successful.

	fNoDelay
		Return after only 1 test.

Return Value:

	STATUS_SUCCESS if pOffset was set correctly, or error.

--*/
{
	LARGE_INTEGER Timeout;
	ULONG i;
	ULONG Count = fNoDelay ? 1 : 50;
	NTSTATUS Status = STATUS_UNSUCCESSFUL;
	Timeout.QuadPart = 20 * ONE_MILLISECOND;

	for ( i = 0; i < Count; i++ )
	{
		ULONG Offset = DxbAdapterReadDword( pExt, GLOBAL_DATA_OFFSET );
		if ( Offset > 0 && 
		     Offset < pExt->Hardware.Memory.Length - sizeof ( GLOBAL_COMMON ) )
		{
			*pOffset = Offset;
			Status = STATUS_SUCCESS;
			break;
		}
			
		(void)KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
	}

	return Status;
}


NTSTATUS
DxbHlcWaitForRunning( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN ULONG                 Magic,
	IN ULONG                 Milliseconds )
{
	NTSTATUS Status;
	ULONG i;
	ULONG Offset;
	LARGE_INTEGER Timeout;
	ULONG Count = 1 + Milliseconds / 20;	// minimum of one.
	Timeout.QuadPart = 20 * ONE_MILLISECOND;

	Status = STATUS_UNSUCCESSFUL;
	for ( i = 0; i < Count; i++ )
	{
		NTSTATUS LocalStatus = dxbHlcGetGlobalCommonOffset( pExt, &Offset, TRUE );

		DigiDump( DIGIINIT, ( "." ) );

		if ( NT_ERROR( LocalStatus ) )
			continue;

		if ( DxbAdapterReadDword( pExt, Offset + FIELD_OFFSET( GLOBAL_COMMON, magic ) ) == Magic &&
		     DxbAdapterReadDword( pExt, Offset + FIELD_OFFSET( GLOBAL_COMMON, status ) ) >= STATUS_RUN )
		{
			Status = STATUS_SUCCESS;
			break;
		}
		(void)KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
	}

	return Status;
}


STATIC NTSTATUS
dxbHlcAssertReset(
	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;
	ULONG ResetOffset, CommonOffset;

	Status = dxbHlcGetGlobalCommonOffset( pExt, &CommonOffset, FALSE );
	if ( NT_ERROR( Status ) )
		goto Exit;

	ResetOffset = CommonOffset + FIELD_OFFSET( GLOBAL_COMMON, rw.rw_req );
	DxbAdapterWriteDword( pExt, ResetOffset, RW_RESET );

	Status = DxbHlcWaitFor( pExt, ResetOffset, RW_IDLE, 4000 );

Exit:

	return Status;
}


STATIC NTSTATUS
dxbHlcVerifyPost(
	PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Verifies the result status of the hardware POST.

Parameters:

	pExt
		Device extension for the adapter

Return Value:

	STATUS_SUCCESS or error

--*/
{
	NTSTATUS Status;
	ULONG CommonOffset;
	ULONG MagicOffset;

	DigiDump( DIGIINIT, ( "DXB: Waiting for Post to start" ) );
	Status = DxbHlcWaitForRunning( pExt, MAGIC_POST, 30000 );
	if ( NT_SUCCESS( Status ) )
	{
		Status = dxbHlcGetGlobalCommonOffset( pExt, &CommonOffset, FALSE );
		if ( NT_ERROR( Status ) )
			goto Exit;

		MagicOffset = CommonOffset + FIELD_OFFSET( GLOBAL_COMMON, magic );
		Status = DxbHlcWaitFor( pExt, MagicOffset, MAGIC_POST, 20 );
	}

Exit:
	DigiDump( DIGIINIT, ( "\n" ) );
	return Status;
}


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

Description:

	Verifies the error count provided by the POST.
	   If non-zero, fail...
           Special case when IgnorePostErrors flag is set in Registry
	       Note that for this special case the POST still must be running
	Serializes the POST errors and the POST error summary in the Registry

Parameters:

	pExt
		Device extension for the adapter

Return Value:

	STATUS_SUCCESS or error

--*/
{
	NTSTATUS Status;
	ULONG CommonOffset;
	ULONG ErrorOffset;
	ULONG ErrorReportOffset;
	ULONG nErrors = 0;
	UCHAR ErrorBuf[(sizeof(POST_ERROR_REPORT) * POST_MAX_ENTRIES)] = {0};
	ULONG fIgnorePostErrors = 0;

	// Find out if we're in special production mode, default is not
	(void) DigiRegQueryDword( hKeyParams, DXB_REGVAL_IGNORE_POST_ERRORS, &fIgnorePostErrors, DigiRegFlagOptional, 0 );

	Status = DxbHlcWaitForRunning( pExt, MAGIC_POST, 30000 );
	if ( NT_SUCCESS( Status ) )
	{
		Status = dxbHlcGetGlobalCommonOffset( pExt, &CommonOffset, FALSE );
		if ( NT_SUCCESS( Status ) )
		{
			ErrorOffset = CommonOffset + FIELD_OFFSET( GLOBAL_POST_DATA, summary.error_count );
			nErrors = DxbAdapterReadDword( pExt, ErrorOffset );
			if ( nErrors )
			{
				Status = (0 != fIgnorePostErrors) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
				DigiDump( DIGIERROR, ( "*** Dxb!HlcPostErrors: Post errors found, count 0x%x! ***\n", nErrors ) );
			}
			else
			{
				Status = STATUS_SUCCESS;
			}

			// Get the POST Error Report buffer
			ErrorOffset = CommonOffset + FIELD_OFFSET( GLOBAL_POST_DATA, summary.error_list_offset );
			ErrorReportOffset = DxbAdapterReadDword( pExt, ErrorOffset );
			if (ErrorReportOffset) {
				DxbAdapterRead( pExt, ErrorBuf, ErrorReportOffset, sizeof ErrorBuf );
			}
		}
	}

	// Always write these values, default is initialized to zeroes
	if ( NT_ERROR( DigiRegWriteDword( hKeyParams, DXB_REGVAL_POST_ERRORS, nErrors)) )	{
		DigiDump( DIGIERROR, ( "*** Dxb!dxbHlcPostErrors: Error writing PostErrors value!\n" ) );
		// Non-fatal
	}
	if ( NT_ERROR( DigiRegWriteBinary( hKeyParams, DXB_REGVAL_POST_ERROR_REPORT, ErrorBuf, sizeof ErrorBuf ) ) )
	{
		DigiDump( DIGIERROR, ( "*** Dxb!dxbHlcPostErrors: Error writing PostErrorReport value!\n" ) );
		// Non-fatal
	}

	DigiDump( DIGIINIT, ( "\n" ) );
	return Status;
}


STATIC ULONG 
dxbHlcCrc32(
	IN ULONG const CurrentCrc,
	IN UCHAR const *address, /* this must be unsigned to work */
	IN ULONG const length )
/*++

Description:

	Calculate a CRC on the block of UNSIGNED bytes for the length given.
	The CRC polynomial is 0x04c11db7.

	Swiped from digi mipsbin source.

Parameters:

	pData
		Data block to CRC

	cbData
		Length in bytes of the data block.

Return Value:

	CRC for the data

--*/
{
	ULONG crc_table[256];

	/* build a CRC table */
	{
		ULONG crc_temp;
		ULONG index;
		ULONG index2;
		for (index = 0; index < 256; index++) 
		{
			crc_temp = index << 24;
			for (index2 = 0; index2 < 8; index2++)
			{
				if ((crc_temp & 0x80000000) != 0)
					crc_temp = ((crc_temp << 1) ^ 0x04c11db7);
				else
					crc_temp = (crc_temp << 1);
			}
			crc_table[index] = crc_temp;
		}
	}

	/* use the table to calculate the CRC for the data */
	{
		ULONG len;
		ULONG Crc = CurrentCrc;

		for (len = length; len > 0; len--) 
		{
			ULONG i, j;
			i = (ULONG) *address++;
			i <<= 24;
			Crc ^= i;
			i = Crc;
			i >>= 24;
			j = crc_table[i];
			Crc <<= 8;
			Crc ^= j;
		}
		return Crc;
	}
}


STATIC NTSTATUS
dxbHlcVerifyCrc( 
	IN PUCHAR pBuffer,
	IN ULONG  cbBuf )
{
	ULONG Original = ((struct digiheader *)pBuffer)->checksum;
	ULONG Checksum1 = dxbHlcCrc32( 0xFFFFFFFF, pBuffer, FIELD_OFFSET( struct digiheader, checksum ) );
	ULONG Checksum2 = dxbHlcCrc32( Checksum1, pBuffer + sizeof (struct digiheader), cbBuf - sizeof (struct digiheader) );

	return Checksum2 == Original ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
}
	


#define HLC_DXB_QUEUE_SIZE					0x20000

#define HLC_MASTER_WINDOWED_PAGE_SIZE	0x1000
#define HLC_MASTER_WINDOWED_PAGE_COUNT	32

#if MAX_PAGES < HLC_MASTER_WINDOWED_PAGE_COUNT || MAX_PAGES < HLC_MASTER_WINDOWED_PAGE_COUNT
#error Device Ext has insufficient sized Master.apPages arrays for HLC adapters!
#endif

#define HLC_MASTER_FLAT_PAGE_SIZE		0x20000
#define HLC_MASTER_FLAT_PAGE_COUNT		1


STATIC NTSTATUS
dxbHlcDownloadFep(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN HANDLE                hKeyParams,
	IN PCWSTR                pRegValFilename,
	IN PCWSTR                pDefaultFilename, 
	OUT PULONG               pExecOffset )
/*++

Description:

	This routine grabs the filename from the registry, crc checks the file, and 
	downloads the image to the adapter.

Parameters:

	pExt
		Device extension defining the adapter to start

	hKeyParams
		Handle to open registry key in which to read the filename

	pRegValFilename
		Name of the registry value to read to find the filename to be downloaded

	pDefaultFilename
		The default name is used if the value cannot be read from the registry.

	ExecOffset
		Output value defining adapter offset to start execution of fep, if successful.

Return Value:

	STATUS_SUCCESS if successfully downloaded the image, otherwise error. 

--*/
{
	UNICODE_STRING Filename;
	HANDLE hFile = NULL;
	ULONG cbFile;
	PUCHAR pFileImage;
	NTSTATUS Status;
	LARGE_INTEGER PhysicalZero = {0};

	DigiDump( DIGIINITLOUD, ( "DXB!dxbHlcDownloadFep: pExt 0x%x\n", pExt ) );

	RtlInitUnicodeString( &Filename, NULL );
	DigiRegQuerySz( hKeyParams, pRegValFilename, &Filename, DigiRegFlagOptional, pDefaultFilename );

	DigiOpenFile( &Status, &hFile, &cbFile, &Filename, PhysicalZero );
	if ( NT_SUCCESS( Status ) )
		DigiMapFile( &Status, &pFileImage, hFile );

	if ( NT_ERROR( Status ) )
	{
		dxbLogFileNotFound( &Filename );
		goto Exit;
	}
		
	// Verify and download fep.
	{
		struct digiheader Header;
		USHORT Type;
		ULONG AdapterOffset;
		LONG  ExecOffset;

		Header = *(struct digiheader *)pFileImage;

		Type = *(PUSHORT)Header.type;
		if ( Type != 'OH' )
		{
			dxbLogBadFepImage( &pExt->Name, &Filename, Type );
			Status = STATUS_UNSUCCESSFUL;
			goto Exit;
		}

		Status = dxbHlcVerifyCrc( pFileImage, cbFile );
		if ( NT_ERROR( Status ) )
		{
			dxbLogBadFepImage( &pExt->Name, &Filename, Type );
			goto Exit;
		}

#define HLC_SEGMENT_SIZE 4096 
		AdapterOffset = Header.load_segment * HLC_SEGMENT_SIZE;

		if ( AdapterOffset + Header.length * HLC_SEGMENT_SIZE > pExt->Hardware.Memory.Length )
		{
			dxbLogBadFepImage( &pExt->Name, &Filename, Type );
			Status = STATUS_UNSUCCESSFUL;
			goto Exit;
		}

		DxbAdapterWrite( pExt, pFileImage, AdapterOffset, cbFile );

		{
			// Attempt to verify downloaded image
			PUCHAR pVerify = DigiAllocMem( NonPagedPool, cbFile );
			if ( pVerify != NULL )
			{
				DxbAdapterRead( pExt, pVerify, AdapterOffset, cbFile );
				Status = dxbHlcVerifyCrc( pVerify, cbFile );
				DigiFreeMem( pVerify );
			}
			else
			{
				// Couldn't alloc space to verify.  Assume image is ok.
				Status = STATUS_SUCCESS;
			}
		}

		if ( NT_ERROR( Status ) )
			goto Exit;

		ExecOffset = AdapterOffset + Header.exec_start;
		if ( ExecOffset >= Header.length * HLC_SEGMENT_SIZE )
		{
			dxbLogBadFepImage( &pExt->Name, &Filename, Type );
			Status = STATUS_UNSUCCESSFUL;
			goto Exit;
		}
		*pExecOffset = ExecOffset;
		Status = STATUS_SUCCESS;
	}

Exit:

	if ( hFile != NULL )
	{
		DigiUnmapFile( hFile );
		DigiCloseFile( hFile );
	}

	if ( Filename.Buffer )
		DigiRegFreeString( &Filename );

	return Status;	
}


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

Description:

	Read the fep filename from registry, map in file, CRC the file, decode the header,
	download image to adapter, CRC the image, start the fep and wait for confirmation.

Parameters:

	pExt
		Device extension for the adapter

Return Value:

	STATUS_SUCCESS or error

--*/
{
	NTSTATUS Status;
	ULONG CommonOffset, ExecOffset;

	Status = dxbHlcDownloadFep( pExt, hKeyParams, DXB_REGVAL_FEP, 
	                            DXB_REGVAL_FEP_ENUM_HOUSTON, &ExecOffset );
	if ( NT_ERROR( Status ) )
		goto Exit;

	DigiDump( DIGIINIT, ( "DXB: Fep downloaded successfully\n" ) );
	DxbAdapterWriteDword( pExt, GLOBAL_ENDIAN_OFFSET, 0 );
	Status = dxbHlcGetGlobalCommonOffset( pExt, &CommonOffset, FALSE );
	if ( NT_ERROR( Status ) )
		goto Exit;
										 
	DxbAdapterWriteDword( pExt, CommonOffset + FIELD_OFFSET(GLOBAL_COMMON, rw.rw_addr), ExecOffset | 0x80000000 );
	DxbAdapterWriteDword( pExt, CommonOffset + FIELD_OFFSET(GLOBAL_COMMON, rw.rw_req ), RW_EXEC );

	DigiDump( DIGIINIT, ( "DXB: Waiting for fep to start" ) );
	Status = DxbHlcWaitFor( pExt, CommonOffset + FIELD_OFFSET(GLOBAL_COMMON, rw.rw_req ), RW_IDLE, 2000 );
	if ( NT_SUCCESS( Status ) )
		Status = DxbHlcWaitForRunning( pExt, MAGIC_FEP, 10000 );
	DigiDump( DIGIINIT, ( "\n" ) );

	if ( NT_SUCCESS( Status ) )
	{
		Status = dxbHlcGetGlobalCommonOffset( pExt, &CommonOffset, FALSE );
		if ( NT_SUCCESS( Status ) )
		{
			DigiDump( DIGIINIT,  ( "DXB: Fep started.  Fep Panic offset 0x%x\n", 
			          CommonOffset + FIELD_OFFSET( GLOBAL_FEP_DATA, panic ) ) );
		}
	}
	else
	{
		NTSTATUS s;
		s = dxbHlcGetGlobalCommonOffset( pExt, &CommonOffset, FALSE );
		DigiDump( DIGIERROR, ( "DXB: Fep failed to start\n" ) );
		if ( NT_SUCCESS( s ) )
		{
			GLOBAL_FEP_DATA Data;
			DxbAdapterRead( pExt, &Data, CommonOffset, sizeof Data );
			DigiDump( DIGIERROR, ( "DXB: GlobalFepData at 0x%x\n"
			                       "   magic 0x%x\n"
			                       "   status 0x%x\n"
			                       "   revision 0x%x\n"
			                       "   panic offset 0x%x\n",
			                       CommonOffset,
			                       Data.common.magic,
			                       Data.common.status,
			                       Data.common.revision,
			                       Data.panic ) );
			if ( Data.common.magic == MAGIC_FEP && Data.panic )
			{
				ULONG Panic;
				DigiDump( DIGIERROR, ( "Panic:\n" ) );

				#define ADDR_MASK 0x0FFFFFFF
				Panic = Data.panic & ADDR_MASK;	// mask off high byte
				if ( Panic + sizeof Data.panic > pExt->Hardware.Memory.Length )
				{
					DigiDump( DIGIERROR, ( "   Invalid panic offset!\n" ) );
					goto Exit;
				}

				{
					ULONG OffsetErrorString = DxbAdapterReadDword( pExt, Panic ) & ADDR_MASK;
					if ( OffsetErrorString )
					{
						CHAR Buffer[256];
						DxbAdapterRead( pExt, Buffer, OffsetErrorString, sizeof Buffer );
						DigiDump( DIGIERROR, ( "   ErrorString 0x%x (%s)\n", OffsetErrorString, Buffer ) );
					}
				}

				{
					ULONG OffsetFileName = DxbAdapterReadDword( pExt, Panic + sizeof(ULONG) ) & ADDR_MASK;
					if ( OffsetFileName )
					{
						CHAR Buffer[256];
						DxbAdapterRead( pExt, Buffer, OffsetFileName, sizeof Buffer );
						DigiDump( DIGIERROR, ( "   Filename 0x%x (%s)\n", OffsetFileName, Buffer ) );
					}
				}

				{
					ULONG Linenumber = DxbAdapterReadDword( pExt, Panic + 2 * sizeof(ULONG) );
					DigiDump( DIGIERROR, ( "   Linenumber %d\n", Linenumber ) );
				}
			}
		}
	}

Exit:

	return Status;
}


STATIC VOID
dxbHlcStopAdapter( 
	IN PDXB_DEVICE_EXTENSION 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;
	}

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

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

	DxbCleanupAdapterQueues( pExt );
}


STATIC NTSTATUS
dxbHlcFixupGalileoRegs(
	IN PVOID               pPciInterfaceChip,
	IN PDIGI_RESOURCE_LIST pResources )
/*++

Description:

	The DenseModem family of products uses a Galileo PCI interface chip which is 
	slightly broken.  It doesn't know how to decode memory ranges until we fixup a 
	few registers in Galileo memory space.

Parameters:

	pPciInterfaceChip
		Virtual address of the Galileo (Pci Interface chip) registers.

	pResourceList
		List of resources indirectly from PCI config space.

Return Value:

	None

--*/
{
	REG64011 * const pGalileo = pPciInterfaceChip;
	ULONG MemDecode, DspDecode;

	MemDecode = pResources->Io[HLC_PCI_MEM_RAM].BaseAddress.LowPart >> 20 & 0xff;

	WRITE_REGISTER_ULONG( &pGalileo->ras0_low_decode,  MemDecode );
	WRITE_REGISTER_ULONG( &pGalileo->ras0_high_decode, MemDecode + 3 );
	WRITE_REGISTER_ULONG( &pGalileo->ras1_low_decode,  MemDecode + 4 );
	WRITE_REGISTER_ULONG( &pGalileo->ras1_high_decode, MemDecode + 7 );

	DspDecode = pResources->Io[HLC_PCI_MEM_DSPGPP].BaseAddress.LowPart >> 20 & 0xff;
	WRITE_REGISTER_ULONG( &pGalileo->bootcs_low_decode,  DspDecode );
	WRITE_REGISTER_ULONG( &pGalileo->bootcs_high_decode, DspDecode + 1 );

	WRITE_REGISTER_ULONG( &pGalileo->cs3_low_decode,  DspDecode + 2 );
	WRITE_REGISTER_ULONG( &pGalileo->cs3_high_decode, DspDecode + 3 );

	return STATUS_SUCCESS;
}

STATIC NTSTATUS
dxbHoustoncPciFixupGalileoRegs(
	IN PVOID               pPciInterfaceChip,
	IN PDIGI_RESOURCE_LIST pResources )
/*++

Description:

	For the cPCI Houston, the mem map is 16M on one decode,
   take care of this special case here.

Parameters:

	pPciInterfaceChip
		Virtual address of the Galileo (Pci Interface chip) registers.

	pResourceList
		List of resources indirectly from PCI config space.

Return Value:

	None

--*/
{
	REG64011 * const pGalileo = pPciInterfaceChip;
	ULONG MemDecode, DspDecode;

	MemDecode = pResources->Io[HLC_PCI_MEM_RAM].BaseAddress.LowPart >> 20 & 0xff;

   /*
    * cpciHouston uses a single RAS to access all 16M
    */
	WRITE_REGISTER_ULONG( &pGalileo->ras0_low_decode,  MemDecode );
	WRITE_REGISTER_ULONG( &pGalileo->ras0_high_decode, MemDecode + 0x0f );
   /*
    * therefore, the second RAS is turned off by asking it
    * to decode a region above where we have real RAM
    */
	WRITE_REGISTER_ULONG( &pGalileo->ras1_low_decode,  MemDecode + 0x10 );
	WRITE_REGISTER_ULONG( &pGalileo->ras1_high_decode, MemDecode + 0x10 );

   DspDecode = pResources->Io[HLC_PCI_MEM_DSPGPP].BaseAddress.LowPart >> 20 & 0xff;
	WRITE_REGISTER_ULONG( &pGalileo->bootcs_low_decode,  DspDecode );
	WRITE_REGISTER_ULONG( &pGalileo->bootcs_high_decode, DspDecode + 1 );

	WRITE_REGISTER_ULONG( &pGalileo->cs3_low_decode,  DspDecode + 2 );
	WRITE_REGISTER_ULONG( &pGalileo->cs3_high_decode, DspDecode + 3 );

	return STATUS_SUCCESS;
}


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

Description:

	Map in memory resources.  Fixup broken Galileo chip problems by writing the
	"fine decode" address bits to the galileo registers.

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.

--*/
{
	// First, map in the Pci Interface Chip
	NTSTATUS Status = dxbMapMemory( &pResources->Io[HLC_PCI_MEM_GALILEO], &pExt->Hardware.PciInterfaceChip );

	// Then, write the fine decode to the gal registers.  
   if ( NT_SUCCESS( Status ) )
   {
      // cPCI Houston has its full 16M memory mapped onto one decode, take care of this special case here
      if (DIGI_CPCI_DEVICEID_HOUSTON == pExt->Hardware.PciId)
      {
   		Status = dxbHoustoncPciFixupGalileoRegs( pExt->Hardware.PciInterfaceChip.Virtual, pResources );
      }
      else
      {
   		Status = dxbHlcFixupGalileoRegs( pExt->Hardware.PciInterfaceChip.Virtual, pResources );
      }
   }
	
	if ( NT_SUCCESS( Status ) )
		Status = dxbMapMemory( &pResources->Io[HLC_PCI_MEM_RAM], &pExt->Hardware.Memory );		

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

	if ( NT_ERROR( Status ) )
	{
		dxbAdapterFreeResources( pExt );
		goto Exit;
	}

	DigiDump( DIGIINIT,  ( "DXB: HLC adapter %S resources:\n"
	                      "   Memory:  Physical 0x%08x, Virtual 0x%08x, Length 0x%08x\n"
	                      "   DspGpp:  Physical 0x%08x, Virtual 0x%08x, Length 0x%08x\n"
	                      "   Galileo: 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 ) );
Exit:
	return Status;
}


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

Description:

	Startup a dense (hlc) adapter.  Upon return, the caller may begin configuring the
	protocol stacks as appropriate for the specific adapter (Houston, Chaska, or 
	London).

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;

	// Verify that POST is running.  If not, try to start it up
	Status = dxbHlcVerifyPost( pExt );
	if ( NT_ERROR( Status ) )
	{
		// Post isn't ready.  Reset adapter and try again.
		Status = dxbHlcAssertReset( pExt );
		if ( NT_SUCCESS( Status ) )
			Status = dxbHlcVerifyPost( pExt );
	}
	
	if ( NT_ERROR( Status ) )
	{
		dxbLogPostFailed( &pExt->Name );
		goto Exit;
	}

	// Check error count returned by post.  If non-zero, fail
	Status = dxbHlcPostErrors( pExt, hKeyParams );
	if ( NT_ERROR( Status ) )
	{
		Status = STATUS_UNSUCCESSFUL;
		dxbLogPostErrors( &pExt->Name );
		goto Exit;
	}

	DigiDump( DIGIINIT, ( "DXB!HlcStartFirmware: Adapter %S, Post running\n", pExt->Name.Buffer ) );

	Status = DxbInitAdapterQueues( pExt, DXB_BUSMODE_MASTER_FLAT, HLC_MASTER_FLAT_PAGE_SIZE, HLC_MASTER_FLAT_PAGE_COUNT );

	if ( NT_ERROR( Status ) )	// Try one more time, using "windowed" mode
		Status = DxbInitAdapterQueues( pExt, DXB_BUSMODE_MASTER_WINDOWED, HLC_MASTER_WINDOWED_PAGE_SIZE, HLC_MASTER_WINDOWED_PAGE_COUNT );

	if ( NT_ERROR( Status ) )
		goto Exit;

	Status = DxbAllocGlobalHostData( pExt, GLOBAL_HOST_OFFSET );
	DigiDump( DIGIINIT, ( "DXB!HlcStartFirmware: Adapter %S, starting in %s mode\n", pExt->Name.Buffer, 
	                      pExt->DxbBusMode == DXB_BUSMODE_MASTER_FLAT     ? "Busmaster Flat" :
	                      pExt->DxbBusMode == DXB_BUSMODE_MASTER_WINDOWED ? "Busmaster Windowed" : "Unknown" ) );

	Status = dxbHlcStartFep( pExt, hKeyParams );

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

	// Startup adapter DPC routines
	Status = DsOpenDeviceInternal( pExt->pDeviceObject, &pExt->hDsDevice );
	if ( NT_ERROR( Status ) )
		goto Exit;

	// Read-side credit award processing and delayed command processing routines are 
	// called directly from EventDpc routine.  Don't start their timers here!
	//	dxbQueueStartCreditDpcTimer( pExt );
	//	dxbQueueStartCmdDpcTimer( pExt );

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

	Status = STATUS_SUCCESS;

Exit:

	return Status;
}


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

Comments

	Modem Nationality is global to the adapter, configured once but used for all lines.

--*/
{
	NTSTATUS Status;
	set_modem_nationality_request_t Nationality = {0};
	Status = DigiRegQueryDword( hKeyParams, DXB_REGVAL_MODEM_NATIONALITY, &Nationality.country_code, DigiRegFlagOptional, DXB_REGVAL_MODEM_NATIONALITY_DEFAULT );
	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIERROR, ( "Dxb!SetModemNationality: Error reading Nationality from registry, status 0x%x!\n", Status ) );
	}
	else
	{
		ULONG Error;
		DigiDump( DIGIINITLOUD, ( "DXB!SetModemNationality:\n"
		                          "   country_code 0x%x\n",
		                          Nationality.country_code,
		                          Nationality.country_code ) );
		Status = DsIoctl( hStream, SET_MODEM_NATIONALITY, &Nationality, sizeof Nationality, &Error );
		if ( NT_ERROR( Status ) )
		{
			DigiDump( DIGIERROR, ( "DXB!SetModemNationality: DsIoctl failed, Status 0x%x, Error 0x%x\n", Status, Error ) );
		}
	}
	return Status;		
}


STATIC NTSTATUS
SetDs1( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN HANDLE                hKeyParams,
	IN ULONG                 iPort,
	IN HANDLE                hStream )
{

	NTSTATUS Status;
	HANDLE hKey = NULL;
	set_ds1_config_request_t Ds1 = {0};

	Ds1.port = iPort;
	Status = DigiRegOpenSubkey( &hKey, hKeyParams, DXB_REGKEY_DS1 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DS1_CODING, &Ds1.config.coding, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DS1_FRAMING, &Ds1.config.framing, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DS1_IF_TYPE, &Ds1.config.if_type, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DS1_LONG_HAUL_LBO, &Ds1.config.long_haul_lbo, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DS1_SHORT_HAUL_LEN, &Ds1.config.short_haul_len, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DS1_SIGNALLING, &Ds1.config.signalling, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DS1_WINK_DIGITS, &Ds1.config.wink_digits, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DS1_CLOCKING, &Ds1.config.clocking, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DS1_JITTER_ATTEN, &Ds1.config.jitter_atten, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DS1_EQ_GAIN_LIMIT, &Ds1.config.eq_gain_limit, DigiRegFlagRequired, 0 );

 	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DS1_LEASED_MASK, &Ds1.config.leased_mask, DigiRegFlagRequired, 0 );

#ifdef NFAS_ON
#pragma message( "Compiling in NFAS support" )
 	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DS1_NFAS, &Ds1.config.nfas, DigiRegFlagOptional, NFAS_OFF );
#endif

#ifdef NFAS_MODIFY_BIT
#pragma message( "Hardcoding new and improved NFAS support off" )
	Ds1.config.nfas_master_port = NFAS_MODIFY_BIT;
#endif

	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIERROR, ( "Dxb!SetDs1: Error reading DS1 params from registry, status 0x%x!\n", Status ) );
	}

	if ( NT_SUCCESS( Status ) )
	{
		ULONG Error;
		DigiDump( DIGIINITLOUD, ( "DXB!SetDs1, Port 0x%x:\n"
		                          "   coding            0x%x\n"
		                          "   framing           0x%x\n"
		                          "   if_type           0x%x\n"
		                          "   long_haul_lbo     0x%x\n"
		                          "   short_haul_len    0x%x\n"
		                          "   signalling        0x%x\n"
		                          "   wink_digits       0x%x\n"
		                          "   clocking          0x%x\n"
		                          "   jitter_atten      0x%x\n"
		                          "   eq_gain_limit     0x%x\n"
		                          "   leased_mask       0x%x\n",
		                          Ds1.port,
		                          Ds1.config.coding,
		                          Ds1.config.framing,
		                          Ds1.config.if_type,
		                          Ds1.config.long_haul_lbo,
		                          Ds1.config.short_haul_len,
		                          Ds1.config.signalling,
		                          Ds1.config.wink_digits,
		                          Ds1.config.clocking,
		                          Ds1.config.jitter_atten,
		                          Ds1.config.eq_gain_limit,
		                          Ds1.config.leased_mask ) );
#ifdef NFAS_ON
		DigiDump( DIGIINITLOUD, ( "   nfas              0x%x\n", Ds1.config.nfas ) );
#endif
#ifdef NFAS_MODIFY_BIT
		DigiDump( DIGIINITLOUD, ( "   nfas_port         0x%x\n"
		                          "   nfas_interface_id 0x%x\n", 
		                          Ds1.config.nfas_master_port,
		                          Ds1.config.nfas_interface_id ) );
#endif

		Status = DsIoctl( hStream, SET_DS1_CONFIG, &Ds1, sizeof Ds1, &Error );
		if ( NT_ERROR( Status ) )
		{
			DigiDump( DIGIERROR, ( "DXB!SetDs1: DsIoctl failed, Status 0x%x, Error 0x%x\n", Status, Error ) );
		}
	}

	if ( hKey )
		DigiRegCloseKey( hKey );

	return Status;		
}


STATIC NTSTATUS
SetDChanExt( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN HANDLE                hKeyParams,
	IN ULONG                 iPort,
	IN HANDLE                hStream )
{

	NTSTATUS Status;
	HANDLE hKey = NULL;
	ULONG Error;
	set_dchan_ext_config_request_t DChan = {0};
	DChan.port = iPort;

	Status = DigiRegOpenSubkey( &hKey, hKeyParams, DXB_REGKEY_DCHAN );
	if ( NT_SUCCESS( Status ) )
	{
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_DCHAN_MODE, &DChan.config.mode, DigiRegFlagRequired, 0 );

		if ( NT_SUCCESS( Status ) ) 
			Status = DigiRegQueryDword( hKey, DXB_REGVAL_DCHAN_ADDRESS_TYPE, &DChan.config.address_type, DigiRegFlagRequired, 0 );

		if ( NT_SUCCESS( Status ) )
			Status = DigiRegQueryDword( hKey, DXB_REGVAL_DCHAN_NUMBERING_PLAN, &DChan.config.numbering_plan, DigiRegFlagRequired, 0 );

		if ( NT_SUCCESS( Status ) )
			Status = DigiRegQueryDword( hKey, DXB_REGVAL_DCHAN_SUBADDRESS_TYPE, &DChan.config.subaddress_type, DigiRegFlagRequired, 0 );

		if ( NT_ERROR( Status ) )
		{
			DigiDump( DIGIERROR, ( "Dxb!SetDChanExt: Error reading DChan params from registry, status 0x%x!\n", Status ) );
		}
		DigiRegCloseKey( hKey );
	}
	else
	{
		DigiDump( DIGIERROR, ( "Dxb!SetDChanExt: Error opening DChan registry key, status 0x%x!\n", Status ) );
	}

#ifdef GOOFY_FIRMWARE_NEEDS_DCHAN
	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIERROR, ( "Dxb!SetDChanExt: Overriding error reading from DChan registry key!\n" ) );
		DChan.config.mode = DCHAN_NORMAL;
		Status = STATUS_SUCCESS;
	}
#endif

	if ( NT_SUCCESS( Status ) )
	{
		DigiDump( DIGIINITLOUD, ( "DXB!SetDChanExt, Port 0x%x:\n"
		                          "   mode            0x%x\n"
		                          "   address_type    0x%x\n"
		                          "   numbering_plan  0x%x\n"
		                          "   subaddress_type 0x%x\n",
		                          DChan.port,
		                          DChan.config.mode,
		                          DChan.config.address_type,
		                          DChan.config.numbering_plan,
		                          DChan.config.subaddress_type ) );
		Status = DsIoctl( hStream, SET_DCHAN_EXT_CONFIG, &DChan, sizeof DChan, &Error );
		if ( NT_ERROR( Status ) )
		{
			DigiDump( DIGIERROR, ( "DXB!SetDChanExt: DsIoctl failed, Status 0x%x, Error 0x%x\n", Status, Error ) );
		}
	}

	return Status;		
}

// Used for E1R2.
STATIC NTSTATUS
SetE1R2( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN HANDLE                hKeyParams,
	IN ULONG                 iPort,
	IN HANDLE                hStream )
{

	NTSTATUS Status;
	HANDLE hKey = NULL;
	ULONG Error;
	set_ds1_e1r2_config_request_t E1R2 = {0};

	E1R2.port = iPort;						 
	Status = DigiRegOpenSubkey( &hKey, hKeyParams, DXB_REGKEY_E1R2 );
	
	if ( NT_SUCCESS( Status ) ) 
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_SWITCH_TYPE, &E1R2.config.r2_switch_type, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) ) 
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_METHOD, &E1R2.config.r2_method, DigiRegFlagRequired, 0 );

    if ( NT_SUCCESS( Status ) ) 
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_CD_BITS, &E1R2.config.r2_cd_bits, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_MIN_DNIS, &E1R2.config.min_dnis_digits, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_MAX_DNIS, &E1R2.config.max_dnis_digits, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) ) 
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_MIN_ANI, &E1R2.config.min_ani_digits, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_MAX_ANI, &E1R2.config.max_ani_digits, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_SEND_ANI, &E1R2.config.send_ani_digits, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_ANI_SIGNAL, &E1R2.config.ani_digit_signal, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) ) 
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_ANSWER_SIGNAL, &E1R2.config.answer_signal, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_END_DNIS_SIGNAL, &E1R2.config.end_dnis_signal, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_BUSY_SIGNAL, &E1R2.config.busy_signal, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_CONGESTION_SIGNAL, &E1R2.config.congestion_signal, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKey, DXB_REGVAL_E1R2_OUTGOING_TYPE, &E1R2.config.outgoing_type, DigiRegFlagRequired, 0 );

	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIERROR, ( "Dxb!SetE1R2: Error reading E1R2 params from registry, status 0x%x!\n", Status ) );
	}
	
	if ( NT_SUCCESS( Status ) )
	{
		DigiDump( DIGIINITLOUD, ( "DXB!SetE1R2,    Port 0x%x\n"
		                          "   r2_switch_type    0x%x\n"
		                          "   r2_cd_bits        0x%x\n"
		                          "   min_dnis_digits   0x%x\n"
		                          "   max_dnis_digits   0x%x\n"
		                          "   min_ani_digits    0x%x\n"
		                          "   max_ani_digits    0x%x\n"
		                          "   send_ani_digits   0x%x\n"
		                          "   ani_digit_signal  0x%x\n"
		                          "   answer_signal     0x%x\n"
		                          "   end_dnis_signal   0x%x\n"
		                          "   busy_signal       0x%x\n"
		                          "   congestion_signal 0x%x\n"
		                          "   outgoing_type     0x%x\n",
		                          E1R2.port,
		                          E1R2.config.r2_switch_type,
		                          E1R2.config.r2_cd_bits,
		                          E1R2.config.min_dnis_digits,
		                          E1R2.config.max_dnis_digits,
		                          E1R2.config.min_ani_digits,
		                          E1R2.config.max_ani_digits,
		                          E1R2.config.send_ani_digits,
		                          E1R2.config.ani_digit_signal,
		                          E1R2.config.answer_signal,
		                          E1R2.config.end_dnis_signal,
		                          E1R2.config.busy_signal,
		                          E1R2.config.congestion_signal,
		                          E1R2.config.outgoing_type ) );
		Status = DsIoctl( hStream, SET_DS1_E1R2_CONFIG, &E1R2, sizeof E1R2, &Error );
		if ( NT_ERROR( Status ) )
		{
			DigiDump( DIGIERROR, ( "DXB!SetE1R2: DsIoctl failed, Status 0x%x, Error 0x%x\n", Status, Error ) );
		}
	
	}

	if ( hKey )
		DigiRegCloseKey( hKey );
	
	return Status;		
}

STATIC INLINE NTSTATUS
GetRequiredAnsiString( 
	IN HANDLE hKey,
	IN PCWSTR Value,
	IN PCCHAR pBuf,
	IN USHORT cbBuf )
{
	UNICODE_STRING String = {0};
	NTSTATUS Status = DigiRegQuerySz( hKey, Value, &String, DigiRegFlagOptional, L"" );
	if ( NT_SUCCESS( Status ) )
	{
		ANSI_STRING Ansi;
		Ansi.MaximumLength = cbBuf;
		Ansi.Buffer = pBuf;
		Ansi.Length = 0;
		Status = RtlUnicodeStringToAnsiString( &Ansi, &String, FALSE );
		DigiRegFreeString( &String );
	}
	return Status;
}


STATIC NTSTATUS
SetBri( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN HANDLE                hKeyParams,
	IN ULONG                 iPort,
	IN HANDLE                hStream )
{

	NTSTATUS Status;
	HANDLE hKeyBri = NULL;
	set_bri_config_request_t Bri = {0};
	Bri.port = iPort;

	Status = DigiRegOpenSubkey( &hKeyBri, hKeyParams, DXB_REGKEY_BRI );
	if ( NT_SUCCESS( Status ) )
	{
		Status = DigiRegQueryDword( hKeyBri, DXB_REGVAL_BRI_SWITCH_TYPE, &Bri.config.switch_type, DigiRegFlagRequired, 0 );

		if ( NT_SUCCESS( Status ) )
			Status = DigiRegQueryDword( hKeyBri, DXB_REGVAL_BRI_AUTO_SPID, &Bri.config.auto_spid, DigiRegFlagRequired, 0 );

		if ( NT_SUCCESS( Status ) )
			Status = DigiRegQueryDword( hKeyBri, DXB_REGVAL_BRI_LEASED_MASK, &Bri.config.leased_mask, DigiRegFlagRequired, 0 );

		if ( NT_SUCCESS( Status ) )
		{
			UCHAR i;
			for ( i = 0; i < sizeof (Bri.config.directory) / sizeof (Bri.config.directory[0]); i++ )
			{
				HANDLE hKeyDirectory = NULL;
				NTSTATUS LocalStatus = dxbBuildOpenBriDirectoryKey( &hKeyDirectory, hKeyBri, i );
				if ( NT_SUCCESS( LocalStatus ) )
				{
					LocalStatus = GetRequiredAnsiString( hKeyDirectory, DXB_REGVAL_BRI_DIRECTORY_NUMBER, Bri.config.directory[i].number, sizeof (Bri.config.directory[i].number) );

					if ( NT_SUCCESS( LocalStatus ) )
						LocalStatus = GetRequiredAnsiString( hKeyDirectory, DXB_REGVAL_BRI_DIRECTORY_SUB_ADDRESS, Bri.config.directory[i].sub_address, sizeof (Bri.config.directory[i].sub_address) );

					if ( NT_SUCCESS( LocalStatus ) )
						Status = GetRequiredAnsiString( hKeyDirectory, DXB_REGVAL_BRI_DIRECTORY_SPID, Bri.config.directory[i].spid, sizeof (Bri.config.directory[i].spid) );

					DigiRegCloseKey( hKeyDirectory );
				}
			}
		}

#ifdef BRI_DIR_UPDATE
		if ( NT_SUCCESS( Status ) )
			Status = DigiRegQueryDword( hKeyBri, DXB_REGVAL_BRI_DIR_COUNT, &Bri.config.dir_count, DigiRegFlagRequired, 0 );

		if ( NT_SUCCESS( Status ) )
			Status = DigiRegQueryDword( hKeyBri, DXB_REGVAL_BRI_MSN, &Bri.config.msn, DigiRegFlagRequired, 0 );

		if ( NT_SUCCESS( Status ) )
			Status = DigiRegQueryDword( hKeyBri, DXB_REGVAL_BRI_AUTOALERT, &Bri.config.autoalert, DigiRegFlagRequired, 0 );
#endif

		DigiRegCloseKey( hKeyBri );
	}
	else
	{
		DigiDump( DIGIERROR, ( "Dxb!SetBri: Error opening Bri registry key, status 0x%x!\n", Status ) );
	}

	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIERROR, ( "DXB!SetBri: Error reading Bri params from registry, status 0x%x!\n", Status ) );
	}
	else
	{
		ULONG Error;
		DigiDump( DIGIINITLOUD, ( "DXB!SetBri, Port 0x%x:\n"
		                          "   switch_type 0x%x\n"
		                          "   auto_spid   0x%x\n"
		                          "   leased_mask 0x%x\n"
		                          "   directory:\n"
		                          "   0: number %s, sub_address %s, spid %s\n"
		                          "   1: number %s, sub_address %s, spid %s\n"
		                          "   2: number %s, sub_address %s, spid %s\n"
		                          "   3: number %s, sub_address %s, spid %s\n"
		                          "   4: number %s, sub_address %s, spid %s\n"
		                          "   5: number %s, sub_address %s, spid %s\n"
		                          "   6: number %s, sub_address %s, spid %s\n"
		                          "   7: number %s, sub_address %s, spid %s\n",
		                          Bri.port,
		                          Bri.config.switch_type,
		                          Bri.config.auto_spid,
		                          Bri.config.leased_mask,
		                          Bri.config.directory[0].number, Bri.config.directory[0].sub_address, Bri.config.directory[0].spid, 
		                          Bri.config.directory[1].number, Bri.config.directory[1].sub_address, Bri.config.directory[1].spid, 
		                          Bri.config.directory[2].number, Bri.config.directory[2].sub_address, Bri.config.directory[2].spid, 
		                          Bri.config.directory[3].number, Bri.config.directory[3].sub_address, Bri.config.directory[3].spid, 
		                          Bri.config.directory[4].number, Bri.config.directory[4].sub_address, Bri.config.directory[4].spid, 
		                          Bri.config.directory[5].number, Bri.config.directory[5].sub_address, Bri.config.directory[5].spid, 
		                          Bri.config.directory[6].number, Bri.config.directory[6].sub_address, Bri.config.directory[6].spid, 
		                          Bri.config.directory[7].number, Bri.config.directory[7].sub_address, Bri.config.directory[7].spid ) );
#ifdef BRI_DIR_UPDATE
		DigiDump( DIGIINITLOUD, ( "   dir_count 0x%x:\n"
		                          "   msn       0x%x\n"
		                          "   autoalert 0x%x\n",
		                          Bri.config.dir_count, Bri.config.msn, Bri.config.autoalert ) );
#endif
		Status = DsIoctl( hStream, SET_BRI_CONFIG, &Bri, sizeof Bri, &Error );
		if ( NT_ERROR( Status ) )
		{
			DigiDump( DIGIERROR, ( "DXB!SetBri: DsIoctl failed, Status 0x%x, Error 0x%x\n", Status, Error ) );
		}
	}

	return Status;		
}


#if NT5_BUILD

STATIC NTSTATUS
dxbHlcTranslateIsdnWizardToHouston(
	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 HLC 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 Signalling;

	// 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:	Signalling = PRI_5ESS;	break;
			case ISDN_SWITCH_NTI:	Signalling = PRI_DMS100;break;
			case ISDN_SWITCH_DSS1:	Signalling = PRI_ETSI;	break;
			case ISDN_SWITCH_NI2:	Signalling = PRI_NI2;	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 ) )
				{
					Status = DigiRegWriteDword( hkDs1, DXB_REGVAL_DS1_SIGNALLING, Signalling );
					DigiRegCloseKey( hkDs1 );
				}
				DigiRegCloseKey( hkLine );
			}
			if ( NT_ERROR( Status ) )
			{
				DigiDump( DIGIERROR, ( "Dxb!TranslateIsdnWizardToHouston: Failure on line %d, Status 0x%x!\n", i, Status ) );
				break;
			}
		}
	}

Exit:

	if ( hkParameters )
		DigiRegCloseKey( hkParameters );

	if ( hkAdapter )
		DigiRegCloseKey( hkAdapter );

	return Status;
}


STATIC NTSTATUS
dxbRegCopySz( 
	HANDLE hkSource,
	PCWSTR pwszSource,
	HANDLE hkDest,
	PCWSTR pwszDest )
/*++

Description:

	Copy a string entry from one registry value to another.  Specify the source key and
	value name, and destination key and value.

Parameters:

	hkSource
		Handle to previously opened registry key

	pwszSource
		Value name to copy from

	hkDest
		Handle to previously opened registry key

	pwszDest
		Value name to copy to

Return Value:

	STATUS_SUCCESS or error.

--*/
{
	UNICODE_STRING string;
	NTSTATUS Status = DigiRegQuerySz( hkSource, pwszSource, &string, DigiRegFlagRequired, NULL );
	if ( NT_SUCCESS( Status ) )
	{
		Status = DigiRegWriteSz( hkDest, pwszDest, string.Buffer );
		DigiRegFreeString( &string );
	}
	return Status;
}


STATIC NTSTATUS
dxbHlcTranslateBriLine(
	HANDLE hkWiz,
	HANDLE hkDigi,
	ULONG  SwitchType )
/*++

Description:

	Read ISDN settings written by the NT5 ISDN Wizard for this line and translate them 
	into HLC style ISDN params.  Write them back into HLC tree, leaving the ISDN Wiz 
	settings untouched.  Wizard settings (source) are in [instance]\[line],
	Digi settings (destination) are [instance]\Parameters\[line]\BRI.

Parameters:

	hkWiz
		Handle to previously opened isdn wizard line regkey. 
		e.g. ...\[NetClass]\0006\0

	hkDigi
		Handle to previously opened digi isdn line regkey
		e.g. ...\[NetClass]\0006\Parameters\0

	SwitchType
		HLC BRI_xxxx style switchtype for this line

Return Value:

	STATUS_SUCCESS or error.

--*/
{
	NTSTATUS Status;
	ULONG nChannels;
	UNICODE_STRING AllMSN = {0};
	PCWSTR ThisMSN = NULL;
	BOOLEAN fUseMSN = FALSE;
	HANDLE hkBri = NULL;
	UCHAR i;

	ASSERT( hkWiz );
	ASSERT( hkDigi );

	// Write line params
	Status = DigiRegOpenSubkey( &hkBri, hkDigi, DXB_REGKEY_BRI );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegWriteDword( hkBri, DXB_REGVAL_BRI_SWITCH_TYPE, SwitchType );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegWriteDword( hkBri, DXB_REGVAL_BRI_DIR_COUNT, BRI_DIR_UPDATE|MSN_MAX );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryMultiSz( hkWiz, DXB_REGVAL_ISDNWIZ_MSN, &AllMSN, DigiRegFlagOptional, L"" );

	if ( NT_SUCCESS(Status) )
	{
		if ( AllMSN.Buffer[0] && AllMSN.Length )
		{
			// Use MSN
			fUseMSN = TRUE;
			ThisMSN = AllMSN.Buffer;
			Status = DigiRegWriteDword( hkBri, DXB_REGVAL_BRI_MSN, BRI_MSN_ON );
		}
		else
		{
			fUseMSN = FALSE;
			Status = DigiRegWriteDword( hkBri, DXB_REGVAL_BRI_MSN, BRI_MSN_OFF );
		}
	}

	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIERROR, ( "*** Dxb!HlcTranslateBriLine: Error writing line params, status 0x%x!\n", Status ) );
		goto Exit;
	}

	if ( fUseMSN )
	{
		// Write channel specific params
		for ( i = 0; i < MSN_MAX; i++ )
		{
			HANDLE hkWizChan = NULL;
			HANDLE hkDigiChan = NULL;
			UNICODE_STRING Temp;
			RtlInitUnicodeString( &Temp, ThisMSN );

			Status = dxbBuildOpenLineKey( &hkDigiChan, hkBri, i );

			if ( NT_ERROR( Status ) )
				goto ChanCleanup;

			Status = DigiRegWriteSz( hkDigiChan, DXB_REGVAL_BRI_DIRECTORY_NUMBER, ThisMSN );

			if ( NT_SUCCESS( Status ) )
				Status = DigiRegWriteSz( hkDigiChan, DXB_REGVAL_BRI_DIRECTORY_SPID, L"" );

			if ( NT_SUCCESS( Status ) )
				Status = DigiRegWriteSz( hkDigiChan, DXB_REGVAL_BRI_DIRECTORY_SUB_ADDRESS, L"" );

			// Step over terminating WCHAR(00) to next MSN
            if (Temp.Length != 0)
                ThisMSN += (Temp.Length + sizeof (WCHAR)) / sizeof Temp.Buffer[0];

		ChanCleanup:
			if ( hkDigiChan )
				DigiRegCloseKey( hkDigiChan );

			if ( NT_ERROR( Status ) )
				break;
		}
	}
	else
	{
		// Write channel specific params
		#define BRI_BCHANNELS_PER_LINE 2
		for ( i = 0; i < BRI_BCHANNELS_PER_LINE; i++ )
		{
			HANDLE hkWizChan = NULL;
			HANDLE hkDigiChan = NULL;

			Status = dxbBuildOpenLineKey( &hkWizChan, hkWiz, i );

			if ( NT_SUCCESS( Status ) )
				Status = dxbBuildOpenLineKey( &hkDigiChan, hkBri, i );

			if ( NT_SUCCESS( Status ) )
				Status = dxbRegCopySz( hkWizChan, DXB_REGVAL_ISDNWIZ_NUMBER, hkDigiChan, DXB_REGVAL_BRI_DIRECTORY_NUMBER );

			if ( NT_SUCCESS( Status ) )
				Status = dxbRegCopySz( hkWizChan, DXB_REGVAL_ISDNWIZ_SUBADDRESS, hkDigiChan, DXB_REGVAL_BRI_DIRECTORY_SUB_ADDRESS );

			if ( NT_SUCCESS( Status ) )
				Status = dxbRegCopySz( hkWizChan, DXB_REGVAL_ISDNWIZ_SPID, hkDigiChan, DXB_REGVAL_BRI_DIRECTORY_SPID );

			if ( hkWizChan )
				DigiRegCloseKey( hkWizChan );

			if ( hkDigiChan )
				DigiRegCloseKey( hkDigiChan );

			if ( NT_ERROR( Status ) )
				break;
		}
	}

Exit:

	DigiRegFreeString( &AllMSN );		// Safe.  Checks buffer before freeing.

	if ( hkBri )
		DigiRegCloseKey( hkBri );

	return Status;
}


STATIC NTSTATUS
dxbHlcTranslateIsdnWizardToLondon(
	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 HLC 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 SwitchType;

	// 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:	SwitchType = BRI_5ESS;		break;
 			case ISDN_SWITCH_NI1:	SwitchType = BRI_NI1;		break;
			case ISDN_SWITCH_NTI:	SwitchType = BRI_DMS100;	break;
			case ISDN_SWITCH_INS64:	SwitchType = BRI_JAPAN;		break;
			case ISDN_SWITCH_DSS1:	SwitchType = BRI_ETSI;		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 hkWizLine = NULL;
			HANDLE hkDigiLine = NULL;

			// Isdn Wizard line key, e.g. [NetClass]\0001\0
			Status = dxbBuildOpenLineKey( &hkWizLine, hkAdapter, i );

			// Digi line key, e.g. [NetClass]\0001\Parameters\0
			if ( NT_SUCCESS( Status ) )
				Status = dxbBuildOpenLineKey( &hkDigiLine, hkParameters, i );

			if ( NT_SUCCESS( Status ) )
				Status = dxbHlcTranslateBriLine( hkWizLine, hkDigiLine, SwitchType );
	
			if ( hkDigiLine )
				DigiRegCloseKey( hkDigiLine );

			if ( hkWizLine )
				DigiRegCloseKey( hkWizLine );

			if ( NT_ERROR( Status ) )
			{
				DigiDump( DIGIERROR, ( "Dxb!TranslateIsdnWizardToLondon: Failure on line %d, Status 0x%x!\n", i, Status ) );
				break;
			}
		}
	}

Exit:

	if ( hkParameters )
		DigiRegCloseKey( hkParameters );

	if ( hkAdapter )
		DigiRegCloseKey( hkAdapter );

	return Status;
}
#endif


STATIC BOOLEAN
IsWideStringEqual( PCWSTR String1, PCWSTR String2 )
{
	UNICODE_STRING S1 = {0}, S2 = {0};
	RtlInitUnicodeString( &S1, String1 );
	RtlInitUnicodeString( &S2, String2 );
	return RtlEqualUnicodeString( &S1, &S2, TRUE );
}


STATIC INLINE BOOLEAN
IsHouston( PUNICODE_STRING OptionName )
{
	return IsWideStringEqual( OptionName->Buffer, DXB_REGVAL_OPTION_ENUM_HOUSTON );
}


STATIC BOOLEAN
IsLondon( PUNICODE_STRING OptionName )
{
	return IsWideStringEqual( OptionName->Buffer, DXB_REGVAL_OPTION_ENUM_LONDON );
}


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

Description:

	Startup a Houston adapter.  Assumes the BusNumber, BusType and SlotNumber and
	Option members of the Ext have been filled in from the registry.

Parameters:

	pExt
		Device extension defining the adapter

	pOption
		String defining the adapter type, as read from our registry key, Option.

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

	pResources
		Resource assignments (raw and translated) in digi resource format.

Return Value:

	STATUS_SUCCESS or failure status.

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

	dxbV2Init( pExt );
	pExt->StopAdapter = dxbHlcStopAdapter;
	pExt->AssertReset = dxbHlcAssertReset;

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

	DxbSlaveSetAdapterFlatMemory( pExt );

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

	// Optionally skip configuration of the board.  Used by mfg diags.
	if ( NT_SUCCESS( Status ) )
	{
		ULONG SkipConfig;
		PCWSTR SkipConfigValue = L"SkipConfig";
		(void)DigiRegQueryDword( hKeyParams, SkipConfigValue, &SkipConfig, DigiRegFlagOptional, FALSE );
		if ( SkipConfig )
			goto Exit;
	}

	// Configure the card
	if ( NT_SUCCESS( Status ) )
	{
		HANDLE hStreamMgmt;
		ULONG Error;
		Status = DsOpen( pExt->hDsDevice, "MGMT", NULL, NULL, 0, &hStreamMgmt, &Error );
		if ( NT_ERROR( Status ) || Error )
		{
			DigiDump( DIGIERROR, ( "Dxb!StartAdapterDense: Failed to open Mgmt device!\n" ) );
			goto Exit;
		}

#if NT5_BUILD
		// Grab Isdn Wizard settings and translate them to digi style settings
		if ( IsHouston( pOption ) )
			Status = dxbHlcTranslateIsdnWizardToHouston( pExt );
		else if ( IsLondon( pOption ) )
			Status = dxbHlcTranslateIsdnWizardToLondon( pExt );
#endif

		if ( NT_SUCCESS( Status ) )
			Status = SetModemNationality( pExt, hKeyParams, hStreamMgmt );

		// Read line configurations from the registry and send them down!
		if ( NT_SUCCESS( Status ) )
		{
			Status = DigiRegQueryDword( hKeyParams, DXB_REGVAL_NUMBEROFLINES, &nLines, DigiRegFlagRequired, 0 );
			if ( NT_SUCCESS( Status ) )
			{
				UCHAR i;
				// Houston and London require adapter specific configurations.  
				// Chaska does not.

				for ( i = 0; i < nLines; i++ )
				{
					HANDLE hKeyLine;
					// Get a Key to the Registry Path
					Status = dxbBuildOpenLineKey( &hKeyLine, hKeyParams, i );
					if ( NT_SUCCESS( Status ) )
					{
						if ( IsHouston( pOption ) )
						{
							UNICODE_STRING Protocol;
							UNICODE_STRING Enum;

							// Initialize the CSU/DSU.
							Status = SetDs1( pExt, hKeyLine, i+1, hStreamMgmt );
								
							if ( NT_SUCCESS( Status ) )
								Status = SetDChanExt( pExt, hKeyLine, i+1, hStreamMgmt );

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

							// Check the DXB_REGVAL_PROTOCOL for "E1R2." If this line is set for E1R2,
							// call SetE1R2. Build protocol stack accordingly.

							RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_E1R2 );
							if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
							{
								Status = SetE1R2( pExt, hKeyLine, i+1, hStreamMgmt );
								goto ProtocolDone;
							}

							ProtocolDone:
								DigiRegFreeString( &Protocol );
								DigiRegCloseKey( hKeyLine );

							if ( NT_ERROR( Status ) )
								break;
						}
						else if ( IsLondon( pOption ) )
						{
							Status = SetBri( pExt, hKeyLine, i+1, hStreamMgmt );

							if ( NT_SUCCESS( Status ) )
								Status = SetDChanExt( pExt, hKeyLine, i+1, hStreamMgmt );
						}
						DigiRegCloseKey( hKeyLine );
					}
					if ( NT_ERROR( Status ) )
						break;
				}
			}
		}
		DsClose( hStreamMgmt );
	}

Exit:

	if ( NT_ERROR( Status ) )
		dxbHlcStopAdapter( pExt );

	return Status;
}