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

   dxbinit.c

Abstract:

   Initialization routines to start adapters.

Author:

   Michael Zarns

--*/
#include "digidxb.h"

typedef struct ADAPTER_OPTION
{
	PCWSTR             Name;			// Adapter Option name string
	PDXB_ADAPTER_START StartRoutine;	// Adapter Startup routine

} ADAPTER_OPTION, *PADAPTER_OPTION;


// Table of adapter startup routines
static ADAPTER_OPTION Options[] = 
{
	{ DXB_REGVAL_OPTION_ENUM_SYNCPORT,        dxbEpcStartAdapterSyncport },
	{ DXB_REGVAL_OPTION_ENUM_PRIME,           dxbEpcStartAdapterPrime    },
	{ DXB_REGVAL_OPTION_ENUM_CLYDE,           dxbClydeStartNonCsuAdapter },
	{ DXB_REGVAL_OPTION_ENUM_HOUSTON,         dxbHlcStartAdapterDense    },
	{ DXB_REGVAL_OPTION_ENUM_LONDON,          dxbHlcStartAdapterDense    },
	{ DXB_REGVAL_OPTION_ENUM_CHASKA,          dxbHlcStartAdapterDense    },
	{ DXB_REGVAL_OPTION_ENUM_ACCELEPORT_2000, dxbRapStartAdapterTigger   }
};

#define OptionCount		( sizeof Options / sizeof Options[0] )

VOID
dxbAdapterFreeResources(
	PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Unmap Io and Memory, unreport reported resources.

Parameters:

	pExt
		Device extension defining the adapter 

Return Value:

	None

--*/
{
	if ( pExt->Hardware.Memory.fUnmap )
	{
		pExt->Hardware.Memory.fUnmap = FALSE;
		DigiUnmapAddress( pExt->Hardware.Memory.Virtual, pExt->Hardware.Memory.Length );
	}

	if ( pExt->Hardware.Memory2.fUnmap )
	{
		pExt->Hardware.Memory.fUnmap = FALSE;
		DigiUnmapAddress( pExt->Hardware.Memory2.Virtual, pExt->Hardware.Memory2.Length );
	}

	if ( pExt->Hardware.Io.fUnmap )
	{
		pExt->Hardware.Memory.fUnmap = FALSE;
		DigiUnmapAddress( pExt->Hardware.Io.Virtual, pExt->Hardware.Io.Length );
	}

	if ( pExt->Hardware.PciInterfaceChip.fUnmap )
	{
		pExt->Hardware.Memory.fUnmap = FALSE;
		DigiUnmapAddress( pExt->Hardware.PciInterfaceChip.Virtual, pExt->Hardware.PciInterfaceChip.Length );
	}

#if !NT5_BUILD
	if ( pExt->hReportedResources != NULL ) 
	{
		(void)DigiUnreportResources( gpDriverObject, pExt->hReportedResources );
		pExt->hReportedResources = NULL;
	}
#endif
}


#if !NT5_BUILD
STATIC NTSTATUS
dxbStartAdapter(
	IN HANDLE                 hKeyParams,
	IN PUNICODE_STRING        pName,
	OUT PDXB_DEVICE_EXTENSION *ppExt )
/*++

Description:

	Create device object for the adapter, initialize the device extension.  Then gather
	adapter and bus settings from registry and initialize the adapter.  If successful,
	add the device extension to the global list of device extensions.

Parameters:

	hKeyParams
		Handle to open registry "Parameters" key containing the adapter settings.  

	pName
		String defining the adapter name == device object name == symbolic link name.

	ppExt
		Output pointer to Device Extension if adapter successfully started.

Return Value:

	STATUS_SUCCESS if adapter started successfully.

--*/
{
	NTSTATUS Status;
	PDEVICE_OBJECT pDevice = NULL;
	PDXB_DEVICE_EXTENSION pExt = NULL;
	UNICODE_STRING DeviceName = {0};
	UNICODE_STRING Option = {0};
	DIGI_RESOURCE_LIST Resources = {0};

	DigiDump( DIGIINITLOUD, ( "DXB!dxbStartAdapter, adapter %S\n", pName->Buffer ) );

	RtlInitUnicodeString( &DeviceName, NULL );
	Status = DigiStrDeviceName( &DeviceName, pName->Buffer );
	if ( NT_SUCCESS( Status ) )
	{
		Status = IoCreateDevice( gpDriverObject, sizeof *pExt, &DeviceName, 
		                         FILE_DEVICE_DS, 0, FALSE, &pDevice );
	}

	if ( NT_ERROR( Status ) )
		goto Exit;

	// Initialize device extension
	pExt = pDevice->DeviceExtension;
	RtlZeroMemory( pExt, sizeof *pExt );
	pExt->Type = DXB_TYPE_DEVICE_EXT;

	{
		pExt->Name.Buffer = (PVOID)&pExt->NameBuffer;
		pExt->Name.MaximumLength = sizeof pExt->NameBuffer;
		RtlCopyUnicodeString( &pExt->Name, pName );
	}

	DigiInitializeSpinLock( &pExt->DxbLock );
	pExt->pDeviceObject = pDevice;

	InitializeListHead( &pExt->StreamList );
	DigiInitializeSpinLock( &pExt->WindowLock );

	dxbQueueInitializeEventDpcTimer( pExt );
	dxbQueueInitializeCmdWorkQueue( pExt );
	dxbQueueInitializeCreditWorkQueue( pExt );

	{
		ULONG i;
		for ( i = 0; i < sizeof pExt->Channels / sizeof pExt->Channels[0]; i++ )
			DigiInitializeSpinLock( &pExt->Channels[i].DxbLock );
	}

	Status = DigiRegQueryDword( hKeyParams, DXB_REGVAL_BUS_TYPE, (PVOID)&pExt->Hardware.BusType, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKeyParams, DXB_REGVAL_BUS_NUMBER, &pExt->Hardware.BusNumber, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKeyParams, DXB_REGVAL_SLOT_NUMBER, &pExt->Hardware.SlotNumber, DigiRegFlagRequired, 0 );

	// TODO PATTON: Patton PCI ID
	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKeyParams, DXB_REGVAL_VENDOR_ID, &pExt->Hardware.PciVendorId, DigiRegFlagOptional, DIGI_PCI_VENDORID );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKeyParams, DXB_REGVAL_DEVICE_ID, &pExt->Hardware.PciId, DigiRegFlagRequired, 0 );

	// TODO PATTON: Patton PCI ID
	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKeyParams, DXB_REGVAL_SUBVENDOR_ID, &pExt->Hardware.PciSubVendorId, DigiRegFlagOptional, DIGI_PCI_SUBVENDORID );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKeyParams, DXB_REGVAL_SUBDEVICE_ID, &pExt->Hardware.PciSubSystemId, DigiRegFlagOptional, 0 );

	if ( NT_SUCCESS( Status ) && pExt->Hardware.BusType == PCIBus )
	{
		Status = dxbPciVerifyAdapter( pExt->Hardware.BusNumber, pExt->Hardware.SlotNumber, pExt->Hardware.PciVendorId, pExt->Hardware.PciId,
		                              pExt->Hardware.PciSubVendorId, pExt->Hardware.PciSubSystemId );
		if ( NT_ERROR( Status ) )
			dxbLogAdapterNotFound( &pExt->Name );
	}

	if ( NT_SUCCESS( Status ) )
	{
		if ( pExt->Hardware.BusType == PCIBus )
			Status = DigiAssignPciSlotResources( gpDriverObject, &gDriverRegPath, pExt->Hardware.BusNumber, pExt->Hardware.SlotNumber, 
			                                     &pExt->hReportedResources, &Resources );
		else
		{
#if 0
			Status = DigiAssignIsaResources();
#else
			DigiDump( DIGIERROR, ( "*** DXB ERROR - assigning ISA Resources not implemented!!!  ***\n" ) );
			Status = STATUS_UNSUCCESSFUL;
#endif
		}
	}

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQuerySz( hKeyParams, DXB_REGVAL_OPTION, &Option, DigiRegFlagRequired, NULL );

	if ( NT_SUCCESS( Status ) )
	{
		// Find and call the adapter startup routine based on the Option found.
		ULONG i;
		Status = STATUS_UNSUCCESSFUL;

		for ( i = 0; i < OptionCount; i++ )
		{
			UNICODE_STRING OptionName;
			RtlInitUnicodeString( &OptionName, Options[i].Name );
			if ( RtlEqualUnicodeString( &Option, &OptionName, TRUE ) )
			{
				Status = Options[i].StartRoutine( pExt, &OptionName, hKeyParams, &Resources );
				break;
			}
		}
	}

	// Expose this adapter/device to usermode apps via symbolic link
	if ( NT_SUCCESS( Status ) )
	{
		UNICODE_STRING SymbolicName;
		Status = DigiStrSymbolicName( &SymbolicName, pExt->Name.Buffer );

		if ( NT_SUCCESS( Status ) )
		{
			(void)IoCreateSymbolicLink( &SymbolicName, &DeviceName );
			DigiStrFreeUnicodeString( &SymbolicName );
		}
	}

	if ( NT_SUCCESS( Status ) )
	{
		*ppExt = pExt;
		IoRegisterShutdownNotification( pExt->pDeviceObject );
	}

Exit:

	if ( NT_ERROR( Status ) && pDevice != NULL )
		IoDeleteDevice( pDevice );

	if ( Option.Buffer )
		DigiStrFreeUnicodeString( &Option );

	if ( DeviceName.Buffer )
		DigiStrFreeUnicodeString( &DeviceName );

	DigiDump( DIGIINITLOUD,  ( "DXB!dxbStartAdapter: Leave, Status 0x%x\n", Status ) );
	return Status;
}


NTSTATUS
dxbStartAllAdapters(
	HANDLE hKeyDriver,
	HANDLE hKeyServices )
/*++

Description:

	Walk the Linkage list of adapters, initializing each adapter.

Parameters:

	hKeyDriver
		Handle to open registry key for the driver.

	hKeyServices
		Handle to open registry Services key.  The adapter keys are Subkeys of this key.

Return Value:

	STATUS_SUCCESS if any adapters started successfully.

--*/
{
	HANDLE hKeyLinkage;
	NTSTATUS Status;
	UNICODE_STRING Route;
	UNICODE_STRING RouteEntry;
	ULONG i;

	DigiDump( DIGIINITLOUD, ( "DXB!DxbStartAllAdapters\n") );
	RtlInitUnicodeString( &Route, NULL );

	Status = DigiRegOpenSubkey( &hKeyLinkage, hKeyDriver, L"Linkage" );
	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryMultiSz( hKeyLinkage, L"Route", &Route, DigiRegFlagRequired, NULL );

	if NT_ERROR( Status )
		goto Exit;

	i = 0;
	while ( DigiStrExtractMultiSzEntry( &RouteEntry, &Route, i++ ) )
	{
		HANDLE hKeyAdapter = NULL;
		HANDLE hKeyParams  = NULL;
		PDXB_DEVICE_EXTENSION pExt = NULL;
		UNICODE_STRING Adapter;

		// Route entries are adapter names enclosed in quotes.  Strip off the quotes and
		// Open the registry key for the adapter.
		Status = DigiStrAllocUnicodeString( &Adapter, &RouteEntry.Buffer[1], NULL, NULL );
		if ( NT_SUCCESS( Status ) )
		{
			Adapter.Buffer[Adapter.Length/sizeof(WCHAR)-1] = '\0';
			Adapter.Length -= sizeof (WCHAR);

			DigiDump( DIGIINIT, ( "Dxb: Initializing adapter %S\n", Adapter.Buffer ) );
			Status = DigiRegOpenSubkey( &hKeyAdapter, hKeyServices, Adapter.Buffer );
		}

		if ( NT_SUCCESS( Status ) )
			Status = DigiRegOpenSubkey( &hKeyParams, hKeyAdapter, DXB_REGKEY_PARAMETERS );

		if ( NT_SUCCESS( Status ) )
			Status = dxbStartAdapter( hKeyParams, &Adapter, &pExt );

		if ( NT_SUCCESS( Status ) )
			InsertTailList( &gDeviceExtList, &pExt->ExtListEntry );

		if ( hKeyParams )
			DigiRegCloseKey( hKeyParams );

		if ( hKeyAdapter )
			DigiRegCloseKey( hKeyAdapter );

		DigiStrFreeUnicodeString( &Adapter );
	}
	
Exit:
	if ( Route.Buffer )
		DigiRegFreeString( &Route );

	Status = IsListEmpty( &gDeviceExtList ) ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS;
	DigiDump( DIGIINITLOUD, ( "DXB!DxbStartAllAdapters: Leave, Status 0x%x\n", Status) );
	return Status;
}
#endif


NTSTATUS
dxbDownload(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN HANDLE                hKeyParams,
	IN PCWSTR                pRegValFilename,
	IN PCWSTR                pDefaultFilename,
	IN ULONG                 AdapterOffset )
/*++

Description:

	This routine grabs the filename from the registry and downloads the image.

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.

	AdapterOffset
		Offset into adapter memory window at which to begin writing the firmware image.

Return Value:

	STATUS_SUCCESS if successfully downloaded the image, otherwise error. 

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

	DigiDump( DIGIINITLOUD, 
	          ( "DXB!dxbDownload: pExt 0x%x, file registry value %S\n", 
	          pExt, pRegValFilename ) );

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

	DigiOpenFile( &Status, &hFile, &cbFile, &Filename, PhysicalZero );
	if ( NT_ERROR( Status ) )
    {
		dxbLogFileNotFound( &Filename );
        goto Exit;
    }

    DigiMapFile( &Status, &pFileImage, hFile );
	if ( NT_ERROR( Status ) )
	{
		dxbLogFileNotFound( &Filename );
		goto Exit;
	}
		
	DxbAdapterWrite( pExt, pFileImage, AdapterOffset, cbFile );
	Status = STATUS_SUCCESS;

#if DBG
{
	PUCHAR pVerify = NULL;
	ULONG i;
	pVerify = DigiAllocMem( NonPagedPool, cbFile );
	if ( pVerify == NULL )
		goto DbgExit;

	DxbAdapterRead( pExt, pVerify, AdapterOffset, cbFile );
	for ( i = 0; i < cbFile; i++ )
	{
		if ( pVerify[i] != pFileImage[i] )
		{
			DigiDump( DIGIERROR, 
			          ( "DXB: Download, image corruption at offset %d\n"
			          "      Found 0x%x, should be 0x%x!\n", 
			          i, pVerify[i], pFileImage[i] ) );
			Status = STATUS_UNSUCCESSFUL;
			break;
		}
	}
DbgExit:
	if ( pVerify != NULL )
		DigiFreeMem( pVerify );
}
#endif

Exit:
	if ( pFileImage != NULL )
		DigiUnmapFile( hFile );

    if ( hFile != NULL )
		DigiCloseFile( hFile );

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

	return Status;	
}


