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

   dxbrap.c

Abstract:

   Routines specific to the tigger chip.

Author:

   Richard Masoner

--*/

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


// Dense Firmware
#include "hou2mmap.h"
#include "mipsheader.hh"

// Io and Memory ranges requested by PCI adapters.  
// The RAP/Tigger PCI interface requests 2 PCI ranges: BAR1 points to local memeory on
// the Hercules, and BAR2 points to RAP internal registers.

#define RAP_PCI_MEM_RAM     0
#define RAP_PCI_MEM_TIGGER  1

// offsets in adapter RAM that might be handy

#define RAP_CPU_REGS_OFF			0x8400	// offset to CPU registers
#define RAP_CPU_INTP					(RAP_CPU_REGS_OFF + 0x04)
#define RAP_CPU_INTE					(RAP_CPU_REGS_OFF + 0x08)

#define RAP_DMAC_REGS_OFF			0xc000
#define RAP_DMAC_RXEN				(RAP_DMAC_REGS_OFF + 0x08)
#define RAP_DMAC_TXEN				(RAP_DMAC_REGS_OFF + 0x10)
#define RAP_DMAC_TXINTP				(RAP_DMAC_REGS_OFF + 0x30)
#define RAP_DMAC_RXINTP				(RAP_DMAC_REGS_OFF + 0x38)
#define RAP_DMAC_RXINTE				(RAP_DMAC_REGS_OFF + 0x18)
#define RAP_DMAC_TXINTE				(RAP_DMAC_REGS_OFF + 0x20)

#define RAP_MBOX_REGS_OFF			0x0000
#define RAP_MBOX_TXFIFO				(RAP_MBOX_REGS_OFF + 0x00)
#define RAP_MBOX_RXFIFO				(RAP_MBOX_REGS_OFF + 0x08)

#define RAP_MBOX_XPCI				(RAP_MBOX_REGS_OFF + 0x10)
#define MB_TX_EMPTY					(1 << 0)
#define MB_TX_FULL					(1 << 2)
#define MB_RX_EMPTY					(1 << 3)
#define MB_RX_FULL					(1 << 5)
#define MB_ENABLE_INT_TO_HOST		(1 << 12)

#define RAP_ID_OFF					0x8180	// device & vendor ID
#define RAP_REGACCEN_OFF			0x8160
#define RAP_RESETSTATE_OFF			0x840c
#define RAP_PROGCNTR_OFF			0x841c	// tigger program counter

// flags for use in tigger SYSRSTCtrl register 

#define RAP_SYSRSTCTL_OFF			0x8158	// reset control
#define RAP_SYSRSTCTL_WARMRESET	0x40
#define RAP_SYSRSTCTL_CPURESET	0x20


/* swap a 16 bit int */
STATIC USHORT
swap16(USHORT val)
{
	return ((val << 8) | (val >> 8));
}


/* swap a 32 bit int */
STATIC ULONG
swap32(ULONG val)
{
	ULONG d = (val << 16) | ( val >> 16);
	return ((d >> 8) & 0x00ff00ff) | ((d << 8) & 0xff00ff00);
}


/* Dump the Tigger Program Counter */
#define DUMPPC(P)		dxbRapDumpPC(P, __LINE__)
STATIC VOID
dxbRapDumpPC(IN PDXB_DEVICE_EXTENSION pExt, ULONG Line)
{
	ULONG Reg;

	/* dump the tigger program counter, for debug */
	Reg = READ_REGISTER_ULONG((PVOID)((PUCHAR) pExt->Hardware.Memory2.Virtual + RAP_PROGCNTR_OFF));
}


STATIC NTSTATUS
dxbRapGetGlobalCommonOffset( 
	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;

	*pOffset = 0;
	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;
}


STATIC VOID
dxbRapDumpGlobalCommon(
	IN PDXB_DEVICE_EXTENSION pExt)	// ptr to card structure
{
	UCHAR Reg;
	ULONG Offset;
	int i;

	/*
	global_common looks like this:

	u_int32_t 	magic;
	u_int32_t 	status;
	u_int32_t 	revision;
	u_int32_t 	heartbeat;
	u_int32_t 	bigendian_board;
	u_int32_t 	pad1[5];
	RWDATA		rw;
	*/
	NTSTATUS Status = dxbRapGetGlobalCommonOffset( pExt, &Offset, TRUE );
	if ( NT_SUCCESS( Status ) )
	{
		// read magic, status, revision, heartbeat, and bigendian_board
		for (i = 0; i < 5*4; i++)
		{
			Reg = READ_REGISTER_UCHAR(
				(PVOID)((PUCHAR) pExt->Hardware.Memory.Virtual + Offset + (i)));
		}
	}
	return;
}


STATIC NTSTATUS
dxbRapAssertReset(
	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;
	UCHAR Reg;

	Status = STATUS_SUCCESS;

	Reg = READ_REGISTER_UCHAR(
		(PVOID)((PUCHAR) pExt->Hardware.Memory2.Virtual + RAP_SYSRSTCTL_OFF));

	// set warm reset and CPU reset bits.
	// Reset  is cleared later.

	Reg = (RAP_SYSRSTCTL_WARMRESET | RAP_SYSRSTCTL_CPURESET);

	WRITE_REGISTER_UCHAR(
		(PVOID)((PUCHAR)pExt->Hardware.Memory2.Virtual + 0x815b), Reg);

	Reg = READ_REGISTER_UCHAR(
		(PVOID)((PUCHAR) pExt->Hardware.Memory2.Virtual + RAP_SYSRSTCTL_OFF));

	return Status;
}


STATIC NTSTATUS
dxbRapVerifyPost(
	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, 10000 );

	if ( NT_SUCCESS( Status ) )
	{
		Status = dxbRapGetGlobalCommonOffset( 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
dxbRapPostErrors(
	PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Verifies the error count provided by the POST.  If non-zero, fail to load the 
	board.

Parameters:

	pExt
		Device extension for the adapter

Return Value:

	STATUS_SUCCESS or error

--*/
{
	NTSTATUS Status;
	ULONG CommonOffset;
	ULONG ErrorOffset;
	ULONG nErrors;

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


STATIC ULONG 
dxbRapCrc32(
	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
dxbRapVerifyCrc( 
	IN PUCHAR pBuffer, 
	IN ULONG  cbBuf )
{
	ULONG Original = swap32(((struct digiheader *)pBuffer)->checksum);
	ULONG Checksum1 = dxbRapCrc32( 0xFFFFFFFF, pBuffer, FIELD_OFFSET( struct digiheader, checksum ) );
	ULONG Checksum2 = dxbRapCrc32( 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 Hercules adapters!
#endif

#define HLC_MASTER_FLAT_PAGE_SIZE		0x20000
#define HLC_MASTER_FLAT_PAGE_COUNT		1


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

Description:

	Releases resources acquired by InitBusmasterQueues.

Parameters:

	pExt
		Device extension defining the adapter 

Return Value:

	None

--*/
{
	if ( pExt->pDmaAdapter != NULL )
	{
		ULONG i;
		for ( i = 0; i < MAX_PAGES; i++ )
		{
			if ( pExt->DxbCmdQ.Mode.Master.VirtualPages[i] != NULL )
			{
#if NT5_BUILD
				pExt->pDmaAdapter->DmaOperations->FreeCommonBuffer( pExt->pDmaAdapter, pExt->DxbCmdQ.cbPage, 
					pExt->DxbCmdQ.Mode.Master.PhysicalPages[i], 
					pExt->DxbCmdQ.Mode.Master.VirtualPages[i], FALSE );
#else
				HalFreeCommonBuffer( pExt->pDmaAdapter, pExt->DxbCmdQ.cbPage, 
					pExt->DxbCmdQ.Mode.Master.PhysicalPages[i], 
					pExt->DxbCmdQ.Mode.Master.VirtualPages[i], FALSE );
#endif
				pExt->DxbCmdQ.Mode.Master.VirtualPages[i] = NULL;
			}
			if ( pExt->DxbEventQ.Mode.Master.VirtualPages[i] != NULL )
			{
#if NT5_BUILD
				pExt->pDmaAdapter->DmaOperations->FreeCommonBuffer( pExt->pDmaAdapter, pExt->DxbEventQ.cbPage, 
					pExt->DxbEventQ.Mode.Master.PhysicalPages[i], 
					pExt->DxbEventQ.Mode.Master.VirtualPages[i], FALSE );
#else
				HalFreeCommonBuffer( pExt->pDmaAdapter, pExt->DxbEventQ.cbPage, 
					pExt->DxbEventQ.Mode.Master.PhysicalPages[i], 
					pExt->DxbEventQ.Mode.Master.VirtualPages[i], FALSE );
#endif
				pExt->DxbEventQ.Mode.Master.VirtualPages[i] = NULL;
			}
		}

		DxbCleanupGlobalHostData( pExt );
		pExt->pDmaAdapter = NULL;
	}
}


STATIC NTSTATUS
dxbAllocBusmasterBuffers( 
	IN PADAPTER_OBJECT    pDmaAdapter, 
	IN PDXB_MESSAGE_QUEUE pDesc, 
	IN ULONG              nBuffers, 
	IN ULONG              cbBuffer )
{
	NTSTATUS Status;
	ULONG i;
	DigiDump( DIGIINIT,  ( "   cbPage 0x%08x, nPages 0x%x\n", cbBuffer, nBuffers ) );

	pDesc->cbPage = cbBuffer;
	pDesc->cbTotal = nBuffers * cbBuffer;
	pDesc->Mode.Master.nPages = nBuffers;

	for ( i = 0; i < nBuffers; i++ )
	{
		PULONG pDword;
		pDesc->Mode.Master.VirtualPages[i] = 
#if NT5_BUILD
			pDmaAdapter->DmaOperations->AllocateCommonBuffer( pDmaAdapter, pDesc->cbPage, &pDesc->Mode.Master.PhysicalPages[i], FALSE );
#else
			HalAllocateCommonBuffer( pDmaAdapter, pDesc->cbPage, &pDesc->Mode.Master.PhysicalPages[i], FALSE );
#endif
		if ( pDesc->Mode.Master.VirtualPages[i] == NULL || pDesc->Mode.Master.PhysicalPages[i].QuadPart == 0 )
		{
			Status = STATUS_INSUFFICIENT_RESOURCES;
			goto Exit;
		}

		pDword = pDesc->Mode.Master.VirtualPages[i];
		pDword[0] = MAGIC_HOSTBUF;
		pDword[1] = i;

#if DBG
		{
			PCHAR const pChar = (PVOID)pDword;
			DigiDump( DIGIINIT,  ( "      Page %d, Virtual 0x%08x, Physical 0x%08x\n", i, 
				pDesc->Mode.Master.VirtualPages[i], pDesc->Mode.Master.PhysicalPages[i] ) );
		}
#endif

	}

	Status = STATUS_SUCCESS;

Exit:
	return Status;
}


STATIC NTSTATUS
dxbInitBusmasterQueues(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN ULONG                 nBuffers, 
	IN ULONG                 cbBuffer )
/*++

Description:

	Alloc queue buffers for busmaster, along with the required system Dma Adapter 
	object.

Parameters:

	pExt
		Device extension defining the adapter 

	nBuffers
		Count of buffers to alloc

	cbBuffer
		Count of bytes per buffer

Return Value:

	STATUS_SUCCESS or error.

--*/
{
	NTSTATUS Status;
	DEVICE_DESCRIPTION Device = {0};
	ULONG nMapRegisters;

	DigiDump( DIGIINIT,  ( "DXB: Busmaster mode setup:\n" ) );

	Device.Version = DEVICE_DESCRIPTION_VERSION;
	Device.Master = TRUE;
	Device.ScatterGather = FALSE;
	Device.DemandMode = FALSE;
	Device.AutoInitialize = FALSE;
	Device.Dma32BitAddresses = TRUE;
	Device.IgnoreCount = FALSE;
	Device.BusNumber = pExt->Hardware.BusNumber;
	Device.DmaWidth = Width32Bits;
	Device.DmaSpeed = Compatible;
	Device.MaximumLength = pExt->DxbEventQ.cbPage = cbBuffer;
#if NT5_BUILD
	pExt->pDmaAdapter = IoGetDmaAdapter( pExt->pPdo, &Device, &nMapRegisters );
#else
	pExt->pDmaAdapter = HalGetAdapter( &Device, &nMapRegisters );
#endif
	if ( pExt->pDmaAdapter == NULL )
	{
		Status = STATUS_INSUFFICIENT_RESOURCES;
		goto Exit;
	}

	DigiDump( DIGIINIT,  ( "   Command Queue\n" ) );
	Status = dxbAllocBusmasterBuffers( pExt->pDmaAdapter, &pExt->DxbCmdQ, nBuffers, cbBuffer );
	if ( NT_ERROR( Status ) )
		goto Exit;

	DigiDump( DIGIINIT,  ( "   Event Queue\n" ) );
	Status = dxbAllocBusmasterBuffers( pExt->pDmaAdapter, &pExt->DxbEventQ, nBuffers, cbBuffer );
	if ( NT_ERROR( Status ) )
		goto Exit;

	Status = DxbAllocGlobalHostData( pExt, GLOBAL_HOST_OFFSET );

Exit:
	if ( NT_ERROR( Status ) )
		dxbCleanupBusmasterQueues( pExt );

	return Status;
}


STATIC NTSTATUS
dxbRapDownloadFep(
	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!dxbRapDownloadFep: 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 = dxbRapVerifyCrc( pFileImage, cbFile );

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

#define HLC_SEGMENT_SIZE 4096 
		AdapterOffset = swap16(Header.load_segment) * HLC_SEGMENT_SIZE;

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

		DxbAdapterWrite( pExt, pFileImage, AdapterOffset, cbFile );

		{
			// Read downloaded image from adapter and see if it matches
			// what we sent.
			PUCHAR pVerify = DigiAllocMem( NonPagedPool, cbFile );
			if ( pVerify != NULL )
			{
				DxbAdapterRead( pExt, pVerify, AdapterOffset, cbFile );
				Status = dxbRapVerifyCrc( pVerify, cbFile );
				DigiFreeMem( pVerify );
			}
			else
			{
				// Couldn't alloc space to verify.  Assume image is ok.
				Status = STATUS_SUCCESS;
			}
		}

		if ( NT_ERROR( Status ) )
			goto Exit;

		// calculate MIPS start code address
		ExecOffset = AdapterOffset + swap16(Header.exec_start);

		if ( ExecOffset >= swap16(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
dxbRapStartFep( 
	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 = dxbRapDownloadFep( pExt, hKeyParams, DXB_REGVAL_FEP, 
		DXB_REGVAL_FEP_ENUM_ACCELEPORT_2000, &ExecOffset );
	if ( NT_ERROR( Status ) )
		goto Exit;

	DigiDump( DIGIINIT, ( "DXB: Fep downloaded successfully\n" ) );

	/* tell firmware that this is a little endian host */
	DxbAdapterWriteDword( pExt, GLOBAL_ENDIAN_OFFSET, 0 );

	/* write MIPS instructions to jump-start the FEP */

	{
		PCHAR pMem;
		ULONG handler = 0xa0000000 + ExecOffset;
		ULONG hihandler = handler >> 16;
		ULONG lohandler = handler & 0xffff;

		/* point to base exception address */
		pMem = (PCHAR) pExt->Hardware.Memory.Virtual + 0;

		WRITE_REGISTER_ULONG( (PVOID)( (PUCHAR)pMem + 0), 
			swap32(0x3c1a0000 | hihandler)		// lui $k0 hi-half word
			);
		WRITE_REGISTER_ULONG( (PVOID)( (PUCHAR)pMem + 4), 
			swap32(0x375a0000 | lohandler)		// ori $k0 lo-half word
			);
		WRITE_REGISTER_ULONG( (PVOID)( (PUCHAR)pMem + 8), 
			swap32(0x03400008)						// jr $k0
			);
		WRITE_REGISTER_ULONG( (PVOID)( (PUCHAR)pMem +12), 
			swap32(0)									// nop
			);
		WRITE_REGISTER_ULONG( (PVOID)( (PUCHAR)pMem +16), 
			swap32(0)									// 0 (cpu number
			);
	}

	/* clear the reset bit */
	{
		UCHAR Reg;

		Reg = READ_REGISTER_UCHAR( (PVOID)((PUCHAR) pExt->Hardware.Memory2.Virtual + RAP_SYSRSTCTL_OFF) );

		// clear the CPU_Reset bit in Sys_RST_Ctrl
		WRITE_REGISTER_UCHAR( (PVOID)((PUCHAR)pExt->Hardware.Memory2.Virtual + 0x815b), 0 );

		Reg = READ_REGISTER_UCHAR( (PVOID)((PUCHAR) pExt->Hardware.Memory2.Virtual + RAP_SYSRSTCTL_OFF) );
	}

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

	DigiDump( DIGIINIT, ( "DXB: Waiting for fep to start" ) );

	if ( NT_SUCCESS( Status ) )
		Status = DxbHlcWaitForRunning( pExt, MAGIC_FEP, 10000 );

	DigiDump( DIGIINIT, ( "\n" ) );

	if ( NT_SUCCESS( Status ) )
	{
		Status = dxbRapGetGlobalCommonOffset( 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 = dxbRapGetGlobalCommonOffset( 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
dxbRapStopAdapter( 
	IN PDXB_DEVICE_EXTENSION pExt )
{
	if ( pExt->hDsDevice )
	{
		if ( pExt->ControlStreams.Fr )
			(void)DsClose( pExt->ControlStreams.Fr );
		if ( pExt->ControlStreams.Isdn )
			(void)DsClose( pExt->ControlStreams.Isdn );
		if ( pExt->ControlStreams.X25 )
			(void)DsClose( pExt->ControlStreams.X25 );
		if ( pExt->ControlStreams.Lapb )
			(void)DsClose( pExt->ControlStreams.Lapb );

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

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

		(void)dxbRapAssertReset( pExt );
//#if !NT5_BUILD || ENABLE_NT5_INTERRUPTS
		if ( pExt->Hardware.Irq.Period > 0 )
		{
			dxbDisconnectInterrupt( pExt );
			pExt->bShutdown = TRUE;
			dxbQueueStopEventDpcTimer( pExt );
		}
		else
//#endif      
		{
			dxbQueueStopEventDpcTimer( pExt );
		}

		dxbAdapterFreeResources( pExt );
	}

	dxbCleanupBusmasterQueues( pExt );
}


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

Description:

	Map in memory resources.

Parameters:

	pExt
		Device extension for the adapter

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

Return Value:

	STATUS_SUCCESS or failure status.

--*/
{
	// First, map in the PCI Interface Chip
	NTSTATUS Status;

	// The length given for BAR2 (tigger PCI registers) by tigger in RAP2
	// is incorrect
	pResources->Io[RAP_PCI_MEM_TIGGER].Length = 0xd000;

	/* shared memory space */
	Status = dxbMapMemory( &pResources->Io[RAP_PCI_MEM_RAM], &pExt->Hardware.Memory );		

	/* tigger registers */
	if ( NT_SUCCESS( Status ) )
		Status = dxbMapMemory( &pResources->Io[RAP_PCI_MEM_TIGGER], &pExt->Hardware.Memory2 );		

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

	DigiDump( DIGIINIT,  ( "DXB: Hercules adapter %S resources:\n"
		"   Memory:  Physical 0x%08x, Virtual 0x%08x, Length 0x%08x\n"
		"   Tigger:  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 ) );

Exit:
	return Status;
}


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

Description:

	Startup a tigger 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;

	/* reset the adapter */
	Status = dxbRapAssertReset( pExt );

#if 0 // post doesn't work on Tigger yet
		// Verify that POST is running.  If not, try to start it up
	Status = dxbRapVerifyPost( pExt );

	if ( NT_ERROR( Status ) )
	{
		// Post isn't ready.  Reset adapter and try again.
		Status = dxbRapAssertReset( pExt );
		if ( NT_SUCCESS( Status ) )
			Status = dxbRapVerifyPost( pExt );
	}

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

	// Check error count returned by post.  If non-zero, fail
	Status = dxbRapPostErrors( pExt );

	if ( NT_ERROR( Status ) )
	{
		Status = STATUS_UNSUCCESSFUL;
		dxbLogPostErrors( &pExt->Name );
		goto Exit;
	}
#endif

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

	dxbQueueInitAccessMethods( pExt, DXB_BUSMODE_MASTER_FLAT );
	DigiInitializeSpinLock( &pExt->DxbCmdQ.Lock );
	DigiInitializeSpinLock( &pExt->DxbEventQ.Lock );
	Status = dxbInitBusmasterQueues(	pExt, HLC_MASTER_FLAT_PAGE_COUNT, HLC_MASTER_FLAT_PAGE_SIZE );
	pExt->DxbBusMode = DXB_BUSMODE_MASTER_FLAT;

	if ( NT_ERROR( Status ) )
	{
		// Try one more time, using "windowed" mode
		dxbQueueInitAccessMethods( pExt, DXB_BUSMODE_MASTER_WINDOWED );
		Status = dxbInitBusmasterQueues(	pExt, HLC_MASTER_WINDOWED_PAGE_COUNT, HLC_MASTER_WINDOWED_PAGE_SIZE );
		pExt->DxbBusMode = DXB_BUSMODE_MASTER_WINDOWED;
	}

	if ( NT_ERROR( Status ) )
		goto Exit;

//#if !NT5_BUILD || ENABLE_NT5_INTERRUPTS
// TODO: The dxbConnectInterrupt needs to be rewritten for W2K. It's using obsolete functions...
	if ( pExt->Hardware.Irq.Period > 0 )
	{
		// The Hercules adapter can be either polled or interrupt driven.
		// If the adpater is polled, then the polling is done through a DPC
		// scheduled through a timer interrupt 100 times per second.
		// If the adapter is interrupt driven, then we set that up instead.
		dxbConnectInterrupt( pExt );
	}
	else
//#endif   
	{
		// Start polling the adapter event queue
		dxbQueueStartEventDpcTimer( pExt );
	}

	DigiDump( DIGIINIT, ( "DXB!RapStartFirmware: 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 = dxbRapStartFep( 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;

	Status = STATUS_SUCCESS;

Exit:
	return Status;
}


/*
	dxbRapClearIrq()

	To clear the interrupt, we read the mailbox register.
*/
BOOLEAN
dxbRapClearInterrupt( IN PDXB_DEVICE_EXTENSION pExt )
{
	BOOLEAN fOurInterrupt = FALSE;

	ASSERT( pExt != NULL );

	if ( pExt != NULL )
	{
		volatile ULONG xpci = swap32( READ_REGISTER_ULONG( (PVOID)( (PUCHAR) pExt->Hardware.Memory2.Virtual + RAP_MBOX_XPCI ) ) );

		while (( xpci & MB_TX_EMPTY ) == 0 )
		{
			// Read the Tx FIFO until the PCI control register says it's empty
			volatile ULONG txfifo = swap32( READ_REGISTER_ULONG( (PVOID)( (PUCHAR) pExt->Hardware.Memory2.Virtual + RAP_MBOX_TXFIFO ) ) );

			// Indicate to the caller that this board actually generated an interrupt
			fOurInterrupt = TRUE;

			xpci = swap32( READ_REGISTER_ULONG( (PVOID)( (PUCHAR) pExt->Hardware.Memory2.Virtual + RAP_MBOX_XPCI ) ) );
		}
	}

	return fOurInterrupt;
}


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

Description:

	Startup a Hercules 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 = dxbRapStopAdapter;
	pExt->AssertReset = dxbRapAssertReset;
	pExt->ClearInterrupt = dxbRapClearInterrupt;

	{
		ULONG fUseInterrupts;
		DigiRegQueryDword( hKeyParams, DXB_REGVAL_USE_INTERRUPTS, &fUseInterrupts, DigiRegFlagOptional, DXB_REGVAL_USE_INTERRUPTS_DEFAULT );

		DigiDump( DIGIINIT,  ( "DXB!dxbRapStartAdapterTigger\n" ) );
//#if !NT5_BUILD || ENABLE_NT5_INTERRUPTS
		if (fUseInterrupts)
		{
#define DXB_REGVAL_INTERRUPTPERIOD				TEXT( "InterruptPeriod" )
#define DXB_REGVAL_INTERRUPTPERIOD_TYPE		REG_DWORD
#define DXB_REGVAL_INTERRUPTPERIOD_DEFAULT	1
#define DXB_REGVAL_INTERRUPTPERIOD_MAXIMUM	20

			ULONG InterruptPeriod;
			DigiRegQueryDword( hKeyParams, DXB_REGVAL_INTERRUPTPERIOD, &InterruptPeriod, DigiRegFlagOptional, DXB_REGVAL_INTERRUPTPERIOD_DEFAULT );

			if ((InterruptPeriod == 0) || (InterruptPeriod > DXB_REGVAL_INTERRUPTPERIOD_MAXIMUM))
			{
				DigiDump( DIGIWARN,  ( "DXB: Invalid InterruptPeriod value %d read for from registry; resetting to default\n",
					InterruptPeriod ) );

				InterruptPeriod = DXB_REGVAL_INTERRUPTPERIOD_DEFAULT;
			}

			DigiDump( DIGIINIT,  ( "DXB: Starting adapter using interrupts (a.k.a low latency)\n"
				"   Mode:  LevelSensitive\n"
				"   Shared:  TRUE\n"
				"   Period:  %d\n", 
				InterruptPeriod ) );

#if !NT5_BUILD
			pExt->Hardware.Irq.Mode = LevelSensitive;
#else
			pExt->Hardware.Irq.Mode = pResources->Irq.Mode;
			pExt->Hardware.Irq.Vector = pResources->Irq.Vector;
			pExt->Hardware.Irq.Irql = (UCHAR)pResources->Irq.Level;
			pExt->Hardware.Irq.Affinity = pResources->Irq.Affinity;
#endif
			pExt->Hardware.Irq.fShared = TRUE;
			pExt->Hardware.Irq.Period = InterruptPeriod;
		}
		else
//#endif      
		{
			DigiDump( DIGIINIT,  ( "DXB: Starting adapter in polling mode\n" ) );

			pExt->Hardware.Irq.Mode = 0;
			pExt->Hardware.Irq.fShared = FALSE;
			pExt->Hardware.Irq.Period = 0;
		}
	}

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

	DxbSlaveSetAdapterFlatMemory( pExt );

	if ( NT_SUCCESS( Status ) )
	{
		Status = dxbRapStartFirmware( pExt, hKeyParams );	/* AKA "triboot" */

	}

Exit:

	if ( NT_ERROR( Status ) )
		dxbRapStopAdapter( pExt );

	return Status;
}
