/*++
 * Copyright (c) 1990-96 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
 *
 *
 * Last Update: $Date: 10/22/97 11:56a $
 *
 * $Header: /Components/Windows/NT/Sync/Build/Mp570/mpinit.c 7     10/22/97 11:56a Darinh $
 *
 * Revision History:
 * $Log: /Components/Windows/NT/Sync/Build/Mp570/mpinit.c $
 * 
 * 7     10/22/97 11:56a Darinh
 * Changes to make multiple board installations work better.
 * 
 * 5     9/08/97 3:45p Darinh
 * A variety of changes, including Data General fixes are included.
 * 
 * 4     7/15/97 1:05p Darinh
 * Added DigiLogError statements and message file, altered code to function
 * in DStreams environment and SNA environment with a conditional
 * compilation.
 * 
 * 3     6/06/97 1:48p Darinh
 * Everything seems functional, but more testing is required.
 * 
 * 2     4/08/97 4:55p Darinh
 * Changes made to correspond to the new DigiUtil library.  It
 * successfully transmits data!  I don't know about receiving data yet.
 * 
 * 1     4/02/97 10:08a Darinh
 * Initial checkin; ISA cards initialize, but do not pass data.
 * 
--*/

#include "digilib.h"

PDRIVER_OBJECT		pGlobalDriverObject;

// NT5 Builds must use the DLL interface
#if !NT5_BUILD		// Defined in Digilib.h

#include <ntddk.h>
#include "def570.h"
#include "def64570.h"
#include "board.h"
#include "proto.h"
#include "mp_api.h"
#include "mpisr.h"
#include "hw570.h"
#include "hwutils.h"
#include "io.h"
#include "dsmp.h"
#include "hwsca.h"
#include "debug.h"
#include "digilog.h"
#include "dxbreg.h"


BOOLEAN				DigiDriverInitialized = FALSE;
KSPIN_LOCK          gControllerListSpinLock;
LIST_ENTRY          gControllerList;

#define	MAX_MULTISZ_LENGTH		256


// The SNA Driver inteface id for the old SYNC570 MS SNA Server SNADA Inteface.
// It expects the registry to be configured with the old-style INF SNA Server
// Installation/

#if SNA_DRIVER

//
//  Globals
//
CONFIGDATA	CardCfgArray[16];

NTSTATUS
fnRetrieveDeviceConfigInfo (
	IN PUNICODE_STRING	RegistryPath,
	IN OUT PUCHAR		BoardConfig,
	IN OUT PULONG		ConfigEntries);

ULONG
CountInstalledCards (
	IN PUNICODE_STRING	RegistryPath);

NTSTATUS
FindCardInfo (
	IN UCHAR		CardNumber,
	IN UCHAR		PortNumber,
	OUT PCONFIGDATA	FoundConfigData);

VOID
PrintConfig (
	IN PCONFIGDATA	pConfigData);

LONG 
DuStringToWideString (
	IN PSTR		pSource,
	OUT PWSTR * pDestination,
	IN BOOLEAN	bAllocateDest);

#endif // SNA_DRIVER


VOID
RetrievePortConfig (
	IN PCONTROLLER_EXT	pControllerExt,
	IN UCHAR			CardNumber);

VOID
RetrievePortCount (
	IN PCONTROLLER_EXT	pControllerExt);


// Debug Externs
extern ULONG gDebugBreak;



#pragma alloc_text( INIT, DriverEntry )
NTSTATUS
DriverEntry (
	IN	PDRIVER_OBJECT		pDriverObject,
	IN	PUNICODE_STRING		RegistryPath)

/*++

Routine Description:

	Initial Entry Point for this driver.

Purpose :

	This is the Device Driver entry point when NT loads this driver. This
	is the first function NT will call.
	This function will perform the following tasks:
		1.	Setup Debugging configuration
		2.	Call DigiFindControllers to init all configured Sync/570 cards
		3.	Register Driver routines.
			- fnClose(), fnOpen(), fnDevIoctl(), fnUnload

Inputs  :

	pDriverObject = Pointer to Driver Object
	RegistryPath = Pointer to Registry path (in unicode string)

Outputs :

	None

Returns :

	STATUS_SUCCESS
	STATUS_UNSUCCESSFUL

--*/
{
	NTSTATUS		Status = STATUS_CANCELLED;
	UNICODE_STRING	path;
	WCHAR			pathBuffer[257];

	// Set up the debug globals
	DigiDebugInit( RegistryPath,
		(DIGIERRORS|DIGIINIT),
		 DEBUGMODE_ANY
		 );

    //
    //  Set up global variavles
    //
	pGlobalDriverObject = pDriverObject;
    KeInitializeSpinLock(&gControllerListSpinLock);
    InitializeListHead(&gControllerList);

	//
	// Read the registry to determine some initial information.
	//
	DigiInitMem ('075D');

	//  Setup the path, why can't we pass in RegistryPath????
	RtlZeroMemory (pathBuffer, sizeof (pathBuffer));
	path.Buffer = pathBuffer;
	path.Length = RegistryPath->Length;
	RtlMoveMemory (path.Buffer, RegistryPath->Buffer, RegistryPath->Length);

#if DBG
	if (gDebugBreak)
	{
		DbgBreakPoint();
	}
#endif // DBG

	DigiDump (DIGIINIT, ("570 MP: Entering DriverEntry()\n"));
	DigiDump (DIGIINIT, ("  RegistryPath = %wZ\n", RegistryPath));

	//
	//  Read the Registry to get info for installed Sync 570 cards, then
	//  proceed by mapping in their resources, and starting them.
	//
	Status = DigiFindControllers (pDriverObject, RegistryPath);

	//
	//  If things were successful, register with the I/O Subsystem.  Else,
	//  cleanup and exit.
	//
	if (NT_SUCCESS (Status))
	{
		pDriverObject->MajorFunction[IRP_MJ_CLOSE] = fnClose;
		pDriverObject->MajorFunction[IRP_MJ_CREATE] = fnOpen;
		pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = fnDevIoctl;
		pDriverObject->DriverUnload = fnUnload;
	}
	else
	{
		DigiDump (DIGIERRORS, ("*** Device Initialization Error\n"));
	}

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

	return (Status);
}



#pragma alloc_text( INIT, DigiFindControllers )

#if (!SNA_DRIVER)

NTSTATUS
DigiFindControllers (
	IN PDRIVER_OBJECT	pDriverObject,
	IN PUNICODE_STRING	RegistryPath)
/*++

Routine Description:

	Initializes each controller/adapter configured in the registry.

Purpose:

	This routine is called at init time by Driver Entry, to start all
	Sync/570 cards that are configured in the system.  The Registry
	path passed in will point to ..\CurrentControlSet\Services\Dg570mp.
		1.	Parse the $RegistryPath\Linkage\Route key
		2.	Create a controller object for each adapter.
		3.	Call DigiInitializeAdapter() to get resource information.
		4.	Create a device object and corresponding device extension for
			each line on the controller.
		5.	Create a Symbolic Link for each device object for Win32 app access.

Arguments:

	pDriverObject - Pointer to this drivers unique driver object.

	RegistryPath - Pointer to this drivers registry path, which contains
		configuration information.

Return Value:

	STATUS_SUCCESS - No errors found initializing the controllers.

--*/
{
	PDEVICE_OBJECT			pDeviceObject;
	PCONTROLLER_OBJECT		pControllerObj;
	PDEVICE_EXT				pDeviceExt;
	PWSTR					RouteName = L"Route";
	PWSTR					LinkageString = L"Linkage";
	ULONG					RouteStorage[MAX_MULTISZ_LENGTH];
	PKEY_VALUE_FULL_INFORMATION	RouteValue =
		(PKEY_VALUE_FULL_INFORMATION) RouteStorage;
	OBJECT_ATTRIBUTES		LinkageAttributes;
	HANDLE					LinkageHandle;
	UNICODE_STRING			LinkagePath, AdapterPath, Route;
	PWSTR					CurRouteChar;
	ULONG					BytesWritten;
	UCHAR					LineNumber;
	ULONG					ControllersStarted = 0;
	BOOLEAN					DigiDriverInitialized = FALSE;
	NTSTATUS				Status = STATUS_CANCELLED;
	NTSTATUS				RegistryStatus = STATUS_CANCELLED;
	NTSTATUS				ControllerStatus = STATUS_CANCELLED;
	PWCHAR					RouteSrc, RouteDest;
	PWSTR					ServicesString =
		L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\";
	PCONTROLLER_EXT			pControllerExt;
	WCHAR					PortNumber[2];
	WCHAR					AdapterBuffer[32];
	WCHAR					NTDeviceNameBuffer[32];
	WCHAR					SymbolicLinkBuffer[32];
	UNICODE_STRING			AdapterName, NTDeviceName, SymbolicLinkName;
	LINKCHAR				LinkChars = {DEFAULT_MAX_MAXDATA, 56000, 0, 0, 
								LO_NRZI | LO_HDLC | LO_RSTAT, 0};
	ULONG					RealBaudRate[] = { 0, 2400, 4800, 9600, 19200,
								38400, 56000, 64000, 128000, 256000, 512000,
								1024000, 1536000, 2048000 };
	PLIST_ENTRY				pControllerListEntry;


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


	// Alloc space for adapter instance registry path
	AdapterPath.Length = 0;
	AdapterPath.MaximumLength = RegistryPath->Length + (sizeof (WCHAR) * 257);
	AdapterPath.Buffer = DigiAllocMem (PagedPool, AdapterPath.MaximumLength);

	// Alloc space for the Driver Linkage registry path
	LinkagePath.Length = 0;
	LinkagePath.MaximumLength = RegistryPath->Length + (sizeof (WCHAR) * 257);
	LinkagePath.Buffer = DigiAllocMem (PagedPool, LinkagePath.MaximumLength);

	if (!LinkagePath.Buffer || !AdapterPath.Buffer)
	{
		DigiDump (DIGIERRORS,
			("*** Could not allocate string for path to adapter for %wZ\n",
			RegistryPath));

		DigiLogError (pDriverObject,
			MP570_ALLOC_FAILED,
			16,
			NULL, 0,
			NULL, 0,
			NULL, 0);

		goto DigiFindControllersExit;
	}

	//
	// Build Linkage path, i.e. \Registry\...\Dg570mp\Linkage
	//

	RtlZeroMemory (LinkagePath.Buffer, LinkagePath.MaximumLength);
	RtlAppendUnicodeStringToString (&LinkagePath, RegistryPath);
	RtlAppendUnicodeToString (&LinkagePath, L"\\");
	RtlAppendUnicodeToString (&LinkagePath, LinkageString);
	DigiDump (DIGIINIT, ("  LinkagePath = %wZ\n", &LinkagePath));

	InitializeObjectAttributes (&LinkageAttributes,
		&LinkagePath,
		OBJ_CASE_INSENSITIVE,
		NULL, NULL);

	if (!NT_SUCCESS (RegistryStatus = ZwOpenKey (&LinkageHandle,
			MAXIMUM_ALLOWED,
			&LinkageAttributes)))
	{
		DigiDump (DIGIERRORS, ("*** Could not open the driver's linkage key %wZ\n",
			&LinkagePath));

		DigiLogError (pDriverObject,
			MP570_UNABLE_TO_OPEN_KEY,
			17,
			NULL, 0,
			LinkagePath.Buffer, LinkagePath.Length + sizeof (WCHAR),
			NULL, 0);

		LinkageHandle = NULL;

		goto DigiFindControllersExit;
	}

	//
	// Get Route value from driver Linkage key
	//

	RtlInitUnicodeString (&Route, RouteName);
	RegistryStatus = ZwQueryValueKey (LinkageHandle,
		&Route,
		KeyValueFullInformation,
		RouteValue,
		MAX_MULTISZ_LENGTH * sizeof (ULONG),
		&BytesWritten);
	ZwClose (LinkageHandle);

	if ((RegistryStatus != STATUS_SUCCESS) ||
		(RouteValue->DataOffset == -1) ||
		(RouteValue->DataLength == 0))
	{
		DigiLogError (pDriverObject,
			MP570_REGISTRY_VALUE_NOT_FOUND,
			18,
			NULL, 0,
			Route.Buffer, Route.Length + sizeof (WCHAR),
			NULL, 0);

		goto DigiFindControllersExit;
	}

	CurRouteChar = (PWCHAR) ((PUCHAR) RouteValue + RouteValue->DataOffset);

	while ((*CurRouteChar != 0))
	{
		AdapterName.MaximumLength = sizeof (AdapterBuffer);
		AdapterName.Buffer = AdapterBuffer;
		RtlZeroMemory (AdapterName.Buffer, AdapterName.MaximumLength);
		DigiDump (DIGIINIT, ("  CurRouteChar = %ws\n", CurRouteChar));

		// Get current route entry
		RouteSrc = CurRouteChar;

		// Advance to next route entry, for next iteration thru this loop
		CurRouteChar += wcslen (CurRouteChar) + 1;

		// Extract the adapter name, i.e. Dg570mp2, from the Route multistring

		RouteSrc++;				   // Advance past opening quote
		RouteDest = AdapterName.Buffer;
		while ((*RouteSrc != *(PWSTR) L"\""))
			*RouteDest++ = *RouteSrc++;	// copy chars up to closing quote
		AdapterName.Length = wcslen (AdapterName.Buffer) * sizeof(WCHAR);
		DigiDump (DIGIINIT, ("  AdapterName = %wZ\n", &AdapterName));

		//
		// Build the adapter registry path
		// i.e. \Registry\Machine\System\CurrentControlSet\Services\Dg570mp2
		//

		AdapterPath.Length = 0;
		RtlZeroMemory (AdapterPath.Buffer, AdapterPath.MaximumLength);
		RtlAppendUnicodeToString (&AdapterPath, ServicesString);
		RtlAppendUnicodeStringToString (&AdapterPath, &AdapterName);
		DigiDump (DIGIINIT, ("  AdapterPath = %wZ\n", &AdapterPath));

		// Create the full NT namespace name for this device, i.e.
		// \Device\Dg570mp2

		NTDeviceName.Buffer = NTDeviceNameBuffer;
		NTDeviceName.MaximumLength = sizeof (NTDeviceNameBuffer);
		NTDeviceName.Length = 0;
		RtlAppendUnicodeToString (&NTDeviceName, L"\\Device\\");
		RtlAppendUnicodeStringToString (&NTDeviceName, &AdapterName);

		//
		//  Create a controller object for each real adapter
		//
		pControllerObj = IoCreateController (sizeof (CONTROLLER_EXT));
		if (pControllerObj == NULL)
		{
			DigiDump (DIGIERRORS, ("*** IoCreateController failed!\n"));
			Status = STATUS_UNSUCCESSFUL;

			DigiLogError (pDriverObject,
				MP570_CREATE_CONTROLLER_FAILED,
				19,
				NULL, 0,
				NULL, 0,
				NULL, 0);

			continue;
		}

		DigiDump (DIGIINIT, ("  ControllerObject = 0x%x\n", pControllerObj));
		pControllerExt = pControllerObj->ControllerExtension;
		pControllerExt->pControllerObj = pControllerObj;
		DigiDump (DIGIINIT, ("  ControllerExt = 0x%x\n", pControllerExt));

		// Setup the adapter name information for Error Logging
		pControllerExt->Name.Buffer = (PVOID) &pControllerExt->NameBuffer;
		pControllerExt->Name.MaximumLength = sizeof (pControllerExt->NameBuffer);
		RtlCopyUnicodeString (&pControllerExt->Name, &AdapterName);


		// Read config from registry and initialize adapter
		Status = DigiInitializeAdapter (pDriverObject,
			pControllerExt,
			RegistryPath,
			&AdapterName,
			&AdapterPath);

		if (!NT_SUCCESS (Status))
		{
			IoFreeController (pControllerObj);
			continue;
		}
		
		// Insert the Controller in the global Controller List
		pControllerListEntry = ExInterlockedInsertHeadList(&gControllerList,
									&pControllerExt->ListElement,
									&gControllerListSpinLock);
		ControllersStarted++;

		//  Setup some known offsets
		if (PCIBus == pControllerExt->BusType)
		{
			pControllerExt->pDigiDcdIntReg =
				pControllerExt->VirtualIO + REG570PCI_DCD_INT_STATUS;
		}
		else
		{
			pControllerExt->pDigiDcdIntReg =
				pControllerExt->VirtualIO + REG570_DCD_INT_STATUS;
			pControllerExt->pDigiMemScaReg =
				pControllerExt->VirtualIO + REG570_MEM_SCA;
		}

		//  Reset the SCA chip(s)
		fnResetScas (pControllerExt);

		//  Get the interface information for this card (needs to be called
		//  after fnResetScas()).
		GetInterfaceId (pControllerExt);

		Status = fnZeroCardMemory (pControllerExt);

		if (!NT_SUCCESS (Status))
		{
			ExInterlockedRemoveHeadList(&gControllerList,
										&gControllerListSpinLock
									   );
			fnUnloadController(pControllerExt,pDriverObject);
			ControllersStarted--;
			continue;
		}


#define	FILE_DEVICE_570_ADAPTER		0x00002020

		//
		// The name of the Device should be \Device\Dg570mp2[1234]
		// so append the letter to the end of the adapter name
		//
		for (LineNumber = 0; LineNumber < pControllerExt->AdapterLines; LineNumber++)
		{
			PortNumber[0] = '0' + LineNumber;
			PortNumber[1] = 0;

			// Create the full NT namespace name for this device, i.e.
			// \Device\Dg570mp21

			RtlZeroMemory (NTDeviceNameBuffer, sizeof (NTDeviceNameBuffer));
			NTDeviceName.Buffer = NTDeviceNameBuffer;
			NTDeviceName.MaximumLength = sizeof (NTDeviceNameBuffer);
			NTDeviceName.Length = 0;
			RtlAppendUnicodeToString (&NTDeviceName, L"\\Device\\");
			RtlAppendUnicodeStringToString (&NTDeviceName, &AdapterName);
			RtlAppendUnicodeToString (&NTDeviceName, (PWSTR) &PortNumber);

			DigiDump (DIGIINIT, ("  PortName = %wZ\n", &NTDeviceName));

			Status = IoCreateDevice (pDriverObject,
				sizeof (DEVICE_EXT),
				&NTDeviceName,
				FILE_DEVICE_570_ADAPTER,
				0,
				FALSE,
				&pDeviceObject);

			if (!NT_SUCCESS (Status))
			{
				DigiLogError (pDriverObject,
					MP570_CREATE_DEVICE_FAILED,
					20,
					NULL, 0,
					NULL, 0,
					NULL, 0);

				continue;
			}

			DigiDump (DIGIINIT, ("  DeviceObject = 0x%x\n", pDeviceObject));

			//  Save a pointer to the device object in the controller extension
			pControllerExt->pDeviceObject[LineNumber] = pDeviceObject;

			//  Save a pointer to the device object's extension
			pControllerExt->pDeviceExt[LineNumber] = pDeviceObject->DeviceExtension;

			//
			// Init Device extension
			//
			pDeviceExt = pDeviceObject->DeviceExtension;
			RtlZeroMemory (pDeviceExt, sizeof (DEVICE_EXT));

			pDeviceExt->Name.Buffer = (PVOID) &pDeviceExt->NameBuffer;
			pDeviceExt->Name.MaximumLength = sizeof (pDeviceExt->NameBuffer);
			RtlCopyUnicodeString (&pDeviceExt->Name, &AdapterName);

			pDeviceObject->Flags |= DO_BUFFERED_IO;

			//  Setup the DPC routine associated with the ISR
			IoInitializeDpcRequest (pDeviceObject, fnDPCRoutine);

			//  Save a pointer to this line's parent controller object
			pDeviceExt->pControllerExt = pControllerExt;
			pDeviceExt->pControllerObj = pControllerObj;

			pDeviceExt->PortNumber = LineNumber;

			//  Get the Port Type for this DeviceObject
			GetPortType (pDeviceExt);

			//  Point to the SCA registers
			if (PCIBus == pControllerExt->BusType)
			{
				//  Ports 1 & 2
				if ((LineNumber == 0) || (LineNumber == 1))
				{
					pDeviceExt->pSca = (PSCA_REGS) pControllerExt->VirtualSca;
				}
				//  Ports 3 & 4
				else
				{
					pDeviceExt->pSca = (PSCA_REGS) (pControllerExt->VirtualSca + 0x400);
				}
			}
			else
			{
				//  ISA card
				pDeviceExt->pSca = (PSCA_REGS) pControllerExt->VirtualSca;
			}


			//  Setup register offsets
			fnSetupPortRelatedRegisters (pDeviceExt);

			//
			//  Use the defaults set above as the default
			//
			fnSetLinkConfig (pDeviceExt, &LinkChars);

			//  Setup DMA Descriptor Chains for Rx and Tx channels
			fnSetupDmaChain (pDeviceExt);

			//  Initialize the common registers in the SCA (only once)
			if (LineNumber == 0)
			{
				fnInitCommonScaRegs (pDeviceExt);
//				fnPrintScaRegisters (pDeviceExt);
			}

			//  Setup Timeout values based on line speed
			fnSetupTimerValues (pDeviceExt);

			//  Setup SCA interrupt vector that uses a modified vector
			fnSetupScaIMVR (pDeviceExt);

			// Create a symbolic link to allow win32 apps access to this device
			SymbolicLinkName.Buffer = SymbolicLinkBuffer;
			SymbolicLinkName.MaximumLength = sizeof (SymbolicLinkBuffer);
			SymbolicLinkName.Length = 0;
			RtlZeroMemory (SymbolicLinkName.Buffer,
				SymbolicLinkName.MaximumLength);

			RtlAppendUnicodeToString (&SymbolicLinkName, L"\\DosDevices\\");
			RtlAppendUnicodeStringToString (&SymbolicLinkName, &AdapterName);
			RtlAppendUnicodeToString (&SymbolicLinkName, (PWSTR) &PortNumber);

			Status = IoCreateSymbolicLink (&SymbolicLinkName, &NTDeviceName);
			if (!NT_SUCCESS (Status))
			{
				DigiDump (DIGIERRORS,
					("*** Couldn't create the symbolic link from %wZ to device %wZ\n",
					&SymbolicLinkName,
					&NTDeviceName));

				DigiLogError (pDeviceObject,
					MP570_NO_SYMLINK_CREATED,
					21,
					NULL, 0,
					NTDeviceName.Buffer,
					NTDeviceName.Length + sizeof (WCHAR),
					SymbolicLinkName.Buffer,
					SymbolicLinkName.Length + sizeof (WCHAR)
					);
				continue;
			}

			DigiDump (DIGIINIT, ("  SymbolicLinkName = %wZ created.\n", 
				&SymbolicLinkName));

			// Store the symbolic link name in the device extension
			pDeviceExt->SymbolicLinkName.Buffer = pDeviceExt->SymLinkNameBuffer;

			pDeviceExt->SymbolicLinkName.Length = 
				SymbolicLinkName.Length;
			pDeviceExt->SymbolicLinkName.MaximumLength =
				sizeof (pDeviceExt->SymLinkNameBuffer);
			RtlZeroMemory (pDeviceExt->SymbolicLinkName.Buffer,
				pDeviceExt->SymbolicLinkName.MaximumLength);
			
			pDeviceExt->SymbolicLinkNameCreated = TRUE;

			RtlCopyUnicodeString (&pDeviceExt->SymbolicLinkName,
				&SymbolicLinkName);
		}

		if (PCIBus == pControllerExt->BusType)
		{
			Status = PciConnectToInterrupt (pControllerExt);
		}
		else
		{
			Status = IsaConnectToInterrupt (pControllerExt);
		}

		// If connecting to the interrupt failed, then unload the controller
		if (!(NT_SUCCESS (Status))) {

			ExInterlockedRemoveHeadList(&gControllerList,
										&gControllerListSpinLock
									   );
			fnUnloadController(pControllerExt,pDriverObject);
			ControllersStarted--;

		}

	}

DigiFindControllersExit:

	// Cleanup

	if (AdapterPath.Buffer)
		DigiFreeMem (AdapterPath.Buffer);

	if (LinkagePath.Buffer)
		DigiFreeMem (LinkagePath.Buffer);

	if (ControllersStarted)
	{
		DigiDriverInitialized = TRUE;
		Status = STATUS_SUCCESS;
	}
	else
	{
		DigiDump (DIGIERRORS, ("*** No Controllers were initialized!\n"));
		Status = STATUS_UNSUCCESSFUL;
	}

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

	return (Status);

}	// end DigiFindControllers

#else

NTSTATUS
DigiFindControllers (
	IN PDRIVER_OBJECT	pDriverObject,
	IN PUNICODE_STRING	RegistryPath)
/*++

Routine Description:

	Initializes each controller/adapter configured in the registry.

Purpose:

	This routine is called at init time by Driver Entry, to start all
	Sync/570 cards that are configured in the system.  The Registry
	path passed in will point to ..\CurrentControlSet\Services\DigiSync.
		1.	Create a controller object for each adapter.
		2.	Call DigiInitializeAdapter() to get resource information.
		3.	Create a device object and corresponding device extension for
			each line on the controller.
		4.	Create a Symbolic Link for each device object for Win32 app access.

Arguments:

	pDriverObject - Pointer to this drivers unique driver object.

	RegistryPath - Pointer to this drivers registry path, which contains
		configuration information.

Return Value:

	STATUS_SUCCESS - No errors found initializing the controllers.

--*/
{
	PDEVICE_OBJECT			pDeviceObject;
	PCONTROLLER_OBJECT		pControllerObj;
	PDEVICE_EXT				pDeviceExt;
	UNICODE_STRING			DeviceNameWStr;
	UNICODE_STRING			AdapterPath;
	ULONG					BytesWritten;
	UCHAR					LineNumber;
	UCHAR					char_cnt = 0;
	ULONG					ControllersStarted = 0;
	BOOLEAN					DigiDriverInitialized = FALSE;
	NTSTATUS				Status = STATUS_CANCELLED;
	NTSTATUS				RegistryStatus = STATUS_CANCELLED;
	NTSTATUS				ControllerStatus = STATUS_CANCELLED;
	PCONTROLLER_EXT			pControllerExt;
	WCHAR					AdapterBuffer[32];
	WCHAR					NTDeviceNameBuffer[32];
	WCHAR					SymbolicLinkBuffer[32];
	WCHAR					DeviceNameWStrBuffer[32];
	PSHORT					p;
	USHORT					DeviceLength, DosDeviceLength, CopyLength;
	ULONG					InstalledCards = 0, CardNumber = 0;
	ULONG					ConfigEntries = 0;
	UNICODE_STRING			AdapterName, NTDeviceName, SymbolicLinkName;
	LINKCHAR				LinkChars = {DEFAULT_MAX_MAXDATA, 64000, 0, 0, 
								LO_NRZI | LO_HDLC | LO_RSTAT, 0};
	ULONG					RealBaudRate[] = { 0, 2400, 4800, 9600, 19200,
								38400, 56000, 64000, 128000, 256000, 512000,
								1024000, 1536000, 2048000 };
	PLIST_ENTRY				pControllerListEntry;



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


	// Alloc space for adapter instance registry path
	AdapterPath.Length = 0;
	AdapterPath.MaximumLength = RegistryPath->Length + (sizeof (WCHAR) * 257);
	AdapterPath.Buffer = DigiAllocMem (PagedPool, AdapterPath.MaximumLength);
	RtlCopyMemory (AdapterPath.Buffer, RegistryPath->Buffer,
		RegistryPath->Length + 2);
	AdapterPath.Length = RegistryPath->Length;
	DigiDump (DIGIINIT, ("  AdapterPath = %wZ\n", &AdapterPath));


	//  Init AdapterName unicode string
	AdapterName.MaximumLength = sizeof (AdapterBuffer);
	RtlZeroMemory (AdapterBuffer, sizeof (AdapterBuffer));
	AdapterName.Buffer = AdapterBuffer;

	//  Extract the Adapter Path name from the Registry Path
	p = (PSHORT) &(RegistryPath->Buffer[wcslen(RegistryPath->Buffer) - 1]);
	while (*p != '\\\0')
	{
		char_cnt += 2;
		--p;
	}

	//  Move ahead, past the backslash
	++p;

	RtlCopyMemory (AdapterName.Buffer, p, char_cnt);
	AdapterName.Length = wcslen (AdapterName.Buffer) * sizeof(WCHAR);
	DigiDump (DIGIINIT, ("  AdapterName = %wZ\n", &AdapterName));

	//
	//  Read the card config information for all installed ports
	//
	RtlZeroMemory (CardCfgArray, sizeof (CardCfgArray));
	Status = fnRetrieveDeviceConfigInfo (RegistryPath,
		(PUCHAR)CardCfgArray, &ConfigEntries);
	if (!NT_SUCCESS (Status))
	{
		DigiDump (DIGIERRORS, ("  *** ERROR *** Device Config Info error!\n"));
		goto DigiFindControllersExit;
	}

	InstalledCards = CountInstalledCards (RegistryPath);
	for (CardNumber = 0; CardNumber < InstalledCards; CardNumber++)
	{
		//
		//  Create a controller object for each real adapter
		//
		pControllerObj = IoCreateController (sizeof (CONTROLLER_EXT));
		if (pControllerObj == NULL)
		{
			DigiDump (DIGIERRORS, ("*** IoCreateController failed!\n"));
			Status = STATUS_UNSUCCESSFUL;
			continue;
		}

		DigiDump (DIGIINIT, ("  ControllerObject = 0x%x\n", pControllerObj));

		pControllerExt = pControllerObj->ControllerExtension;
		pControllerExt->pControllerObj = pControllerObj;

		DigiDump (DIGIINIT, ("  ControllerExt = 0x%x\n", pControllerExt));

		// Setup the adapter name information for Error Logging
		pControllerExt->Name.Buffer = (PVOID) &pControllerExt->NameBuffer;
		pControllerExt->Name.MaximumLength = sizeof (pControllerExt->NameBuffer);
		RtlCopyUnicodeString (&pControllerExt->Name, &AdapterName);

		// Read config from registry and initialize adapter
		Status = DigiInitializeAdapter (pDriverObject,
			pControllerExt,
			RegistryPath,
			&AdapterName,
			&AdapterPath);

		if (NT_SUCCESS (Status))
		{
			// Insert the Controller in the global Controller List
			pControllerListEntry = ExInterlockedInsertHeadList(&gControllerList,
										&pControllerExt->ListElement,
										&gControllerListSpinLock);
			ControllersStarted++;
		}
		else
		{
			IoFreeController (pControllerObj);
			continue;
		}

		//  Initialize the board level spinlock
		KeInitializeSpinLock (&pControllerExt->MemoryLock);

		//  Setup some known offsets
		if (PCIBus == pControllerExt->BusType)
		{
			pControllerExt->pDigiDcdIntReg =
				pControllerExt->VirtualIO + REG570PCI_DCD_INT_STATUS;
		}
		else
		{
			pControllerExt->pDigiDcdIntReg =
				pControllerExt->VirtualIO + REG570_DCD_INT_STATUS;
			pControllerExt->pDigiMemScaReg =
				pControllerExt->VirtualIO + REG570_MEM_SCA;
		}

		//  Reset the SCA chip(s)
		fnResetScas (pControllerExt);

		//  Get the interface information for this card (needs to be called
		//  after fnResetScas()).
		GetInterfaceId (pControllerExt);

		//  Clear card memory of misc junk.
		Status = fnZeroCardMemory (pControllerExt);

		if (!NT_SUCCESS (Status))
		{
			ExInterlockedRemoveHeadList(&gControllerList,
										&gControllerListSpinLock
									   );
			fnUnloadController(pControllerExt,pDriverObject);
			ControllersStarted--;
			continue;
		}


#define	FILE_DEVICE_570_ADAPTER		0x00002020

		//
		// The name of the Device should be read from the CardCfg
		//
		for (LineNumber = 0; LineNumber < pControllerExt->AdapterLines; LineNumber++)
		{
			// Copy from the pControllerExt->DeviceName
			RtlZeroMemory (NTDeviceNameBuffer, sizeof (NTDeviceNameBuffer));
			NTDeviceName.Buffer = NTDeviceNameBuffer;
			NTDeviceName.MaximumLength = sizeof (NTDeviceNameBuffer);
			NTDeviceName.Length = 0;
			RtlZeroMemory (DeviceNameWStrBuffer, sizeof (DeviceNameWStrBuffer));
			DeviceNameWStr.Buffer = DeviceNameWStrBuffer;
			DeviceNameWStr.MaximumLength = sizeof (DeviceNameWStrBuffer);
			DeviceNameWStr.Length = 0;
			DuStringToWideString (&pControllerExt->DeviceName[LineNumber][0],
				(PWSTR *) &(DeviceNameWStr.Buffer), FALSE);
			DeviceNameWStr.Length = wcslen (DeviceNameWStr.Buffer) * sizeof(WCHAR);
			RtlAppendUnicodeStringToString (&NTDeviceName, &DeviceNameWStr);

			DigiDump (DIGIINIT, ("  PortName = %wZ\n", &NTDeviceName));

			Status = IoCreateDevice (pDriverObject,
				sizeof (DEVICE_EXT),
				&NTDeviceName,
				FILE_DEVICE_570_ADAPTER,
				0,
				FALSE,
				&pDeviceObject);

			if (!NT_SUCCESS (Status))
			{
				DigiDump (DIGIERRORS, ("  IoCreateDeviceFailed! <status: 0x%x>\n",
					Status));
				continue;
			}

			DigiDump (DIGIINIT, ("  DeviceObject = 0x%x\n", pDeviceObject));

			//  Save a pointer to the device object in the controller extension
			pControllerExt->pDeviceObject[LineNumber] = pDeviceObject;

			//  Save a pointer to the device object's extension
			pControllerExt->pDeviceExt[LineNumber] = pDeviceObject->DeviceExtension;

			//
			// Init Device extension
			//
			pDeviceExt = pDeviceObject->DeviceExtension;
			RtlZeroMemory (pDeviceExt, sizeof (DEVICE_EXT));

			pDeviceExt->Name.Buffer = (PVOID) &pDeviceExt->NameBuffer;
			pDeviceExt->Name.MaximumLength = sizeof (pDeviceExt->NameBuffer);
			RtlCopyUnicodeString (&pDeviceExt->Name, &AdapterName);

			pDeviceObject->Flags |= DO_BUFFERED_IO;

			//  Setup the DPC routine associated with the ISR
			IoInitializeDpcRequest (pDeviceObject, fnDPCRoutine);

			//  Save a pointer to this line's parent controller object
			pDeviceExt->pControllerExt = pControllerExt;
			pDeviceExt->pControllerObj = pControllerObj;

			pDeviceExt->PortNumber = LineNumber;

			//  Get the Port Type for this DeviceObject
			GetPortType (pDeviceExt);

			//  This is an SNA configuration, we will already have
			//  the line speed information, set it here.
			pDeviceExt->LineSpeed = pControllerExt->SNALineSpeed[LineNumber];

			//  Convert the LineSpeed code to a real baud rate
			pDeviceExt->Config.BaudRate = RealBaudRate[pDeviceExt->LineSpeed];

			//  Are we using internal clocking?
			pDeviceExt->InternalClocking = pControllerExt->InternalClocking[LineNumber];

			//  Point to the SCA registers
			if (PCIBus == pControllerExt->BusType)
			{
				//  Ports 1 & 2
				if ((LineNumber == 0) || (LineNumber == 1))
				{
					pDeviceExt->pSca = (PSCA_REGS) pControllerExt->VirtualSca;
				}
				//  Ports 3 & 4
				else
				{
					pDeviceExt->pSca = (PSCA_REGS) (pControllerExt->VirtualSca + 0x400);
				}
			}
			else
			{
				//  ISA card
				pDeviceExt->pSca = (PSCA_REGS) pControllerExt->VirtualSca;
			}

			//  Setup register offsets
			fnSetupPortRelatedRegisters (pDeviceExt);

			//
			//  Use the defaults set above as the default
			//
			fnSetLinkConfig (pDeviceExt, &LinkChars);

			//  Setup DMA Descriptor Chains for Rx and Tx channels
			fnSetupDmaChain (pDeviceExt);

			//  Initialize the common registers in the SCA (only once)
			if (LineNumber == 0)
			{
				fnInitCommonScaRegs (pDeviceExt);
//				fnPrintScaRegisters (pDeviceExt);
			}

			//  Setup Timeout values based on line speed
			fnSetupTimerValues (pDeviceExt);

			//  Setup SCA interrupt vector that uses a modified vector
			fnSetupScaIMVR (pDeviceExt);

			// Create a symbolic link to allow win32 apps access to this device
			SymbolicLinkName.Buffer = SymbolicLinkBuffer;
			SymbolicLinkName.MaximumLength = sizeof (SymbolicLinkBuffer);
			SymbolicLinkName.Length = 0;
			RtlZeroMemory (SymbolicLinkName.Buffer,
				SymbolicLinkName.MaximumLength);

			//  Create the name \DosDevices\DIGIDO_1
			RtlAppendUnicodeToString (&SymbolicLinkName, L"\\DosDevices\\");
			DeviceLength = wcslen (L"\\Device\\") * sizeof(WCHAR);
			CopyLength = (wcslen (DeviceNameWStr.Buffer) * sizeof(WCHAR)) - DeviceLength;
			DosDeviceLength = wcslen (SymbolicLinkName.Buffer) * sizeof(WCHAR);
			RtlCopyMemory (&(SymbolicLinkName.Buffer[DosDeviceLength/2]),
				&(DeviceNameWStr.Buffer[DeviceLength/2]),
				CopyLength);
			SymbolicLinkName.Length += CopyLength;

			Status = IoCreateSymbolicLink (&SymbolicLinkName, &NTDeviceName);
			if (!NT_SUCCESS (Status))
			{
				DigiDump (DIGIERRORS,
					("*** Couldn't create the symbolic link from %wZ to device %wZ\n",
					&SymbolicLinkName,
					&NTDeviceName));

				DigiLogError (
					pDeviceObject,
					MP570_NO_SYMLINK_CREATED,
					22,
					NULL, 0,
					NTDeviceName.Buffer,
					NTDeviceName.Length + sizeof (WCHAR),
					SymbolicLinkName.Buffer,
					SymbolicLinkName.Length + sizeof (WCHAR)
					);

				continue;
			}

			DigiDump (DIGIINIT, ("  SymbolicLinkName = %wZ created.\n", 
				&SymbolicLinkName));

			// Store the symbolic link name in the device extension
			pDeviceExt->SymbolicLinkName.Buffer = pDeviceExt->SymLinkNameBuffer;

			pDeviceExt->SymbolicLinkName.Length = 0;
			pDeviceExt->SymbolicLinkName.MaximumLength =
				sizeof (pDeviceExt->SymLinkNameBuffer);
			RtlZeroMemory (pDeviceExt->SymbolicLinkName.Buffer,
				pDeviceExt->SymbolicLinkName.MaximumLength);

			pDeviceExt->SymbolicLinkNameCreated = TRUE;

			RtlCopyUnicodeString (&pDeviceExt->SymbolicLinkName,
				&SymbolicLinkName);
		}

		if (PCIBus == pControllerExt->BusType)
		{
			Status = PciConnectToInterrupt (pControllerExt);
		}
		else
		{
			Status = IsaConnectToInterrupt (pControllerExt);
		}

		// If connecting to the interrupt failed, then unload the controller
		if (!(NT_SUCCESS (Status))) {

			ExInterlockedRemoveHeadList(&gControllerList,
										&gControllerListSpinLock
									   );
			fnUnloadController(pControllerExt,pDriverObject);
			ControllersStarted--;

		}

	}

DigiFindControllersExit:

	// Cleanup

	if (AdapterPath.Buffer)
		DigiFreeMem (AdapterPath.Buffer);

	if (ControllersStarted)
	{
		DigiDriverInitialized = TRUE;
		Status = STATUS_SUCCESS;
	}
	else
	{
		DigiDump (DIGIERRORS, ("*** No Controllers were initialized!\n"));
		Status = STATUS_UNSUCCESSFUL;
	}

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

	return (Status);

}	// end DigiFindControllers

#endif // 0

#pragma alloc_text( PAGE, DigiInitializeAdapter )



NTSTATUS 
DigiInitializeAdapter (
	IN PDRIVER_OBJECT	pDriverObject,
	IN PCONTROLLER_EXT	pControllerExt,
	IN PUNICODE_STRING	RegistryPath,
	IN PUNICODE_STRING	AdapterName,
	IN PUNICODE_STRING	AdapterPath)
/*++

Routine Description:

	Read the parameters for each adapter configured in the registry.

Purpose:

	This routine is called at init time by DigiFindControllers().  It
	queries the adapter resource information needed from the registry, and
	saves that information in the appropriate Controller extension.  This
	routine is called once for each adapter in the system.
		1.	Query resource information for the given adapter instance.
		2.	Calls the appropriate adapter initialization routine,
			Setup<BusType>Adapter (i.e. SetupPciAdapter).

Parameters:

	pDriverObject
		Address of the Driver object.

	pDeviceObject
		Address of the Device object.

	pControllerExt
		Address of the Controller extension

	RegistryPath
		Pointer to the registry path (passed into the DriverEntry)

	AdapterName
		Unicode string with the name of the adapter instance (i.e. Dg570mp2)

	AdapterPath
		Pointer to this controller's entry in the registry

Return Value:

	STATUS_SUCCESS - Controller found, and initialized successfully.

--*/
{
	PWSTR			ErrString = NULL;
	CONFIGDATA		ConfigStruct;
	UCHAR			Buf[(CONFIGDATA_SIZE * 8)];
	NTSTATUS		Status = STATUS_SUCCESS;
	UCHAR			i = 0, ConfigEntries = 0;
	static UCHAR	CardNumber = 0;
	HANDLE			hKeyAdapterParameters = NULL;
	UNICODE_STRING	ucParametersPath;	


	DigiDump (DIGIFLOW, ("Entering DigiInitializeAdapter ()\n"));

	// Obtain a UNICODE STRING long enough to add the Paramters Key to 
	// the input registry path.
	Status = DigiStrAllocUnicodeString ( &ucParametersPath,
									     AdapterPath->Buffer,
						                 L"\\",
									     DXB_REGKEY_PARAMETERS);
	
	if (Status != STATUS_SUCCESS)
	{
		DigiDump(
			DIGIINIT,
			("MPINIT: Couldn't Allocate the Parameters String \n")
			);

		DigiLogError( pDriverObject,
					  MP570_INSUFFICIENT_RESOURCES,
					  22,
					  NULL,0,
					  NULL,0,
					  NULL,0 );

		return Status;
	}
	
	//
	// Open the Parameters Registry Key
	//
	Status = DigiRegOpenKey( &hKeyAdapterParameters, ucParametersPath.Buffer );
	if ( NT_ERROR( Status ) ) {

		// error processing
		DigiDump( DIGIERRORS,
			      ( "MPINIT: Couldn't open the adapter %wZ \n", ucParametersPath ) );

		DigiLogError( pDriverObject,
					  MP570_UNABLE_TO_OPEN_KEY,
					  23,
					  NULL,0,
					  ucParametersPath.Buffer, ucParametersPath.Length + sizeof( WCHAR ),
					  NULL,0 );

		DigiStrFreeUnicodeString( &ucParametersPath );
		return Status;
	}

	//
	//  The following registry entries are NOT required
	//

#if (!SNA_DRIVER)

	(void)DigiRegQueryDword( hKeyAdapterParameters,
							 DXB_REGVAL_MEMORY_ADDRESS,
							 &pControllerExt->PhysWindowAddress.LowPart,
							 DigiRegFlagOptional,
							 0 );

	(void)DigiRegQueryDword( hKeyAdapterParameters,
							 DXB_REGVAL_IO_ADDRESS,
							 &pControllerExt->PhysIOPort.LowPart,
							 DigiRegFlagOptional,
							 0 );

	(void)DigiRegQueryDword( hKeyAdapterParameters,
							 DXB_REGVAL_INTERRUPT,
							 &pControllerExt->PhysInterruptNumber.LowPart,
							 DigiRegFlagOptional,
							 0 );

	(void)DigiRegQueryDword( hKeyAdapterParameters,
							 DXB_REGVAL_MEMORY_LENGTH,
							 &pControllerExt->WindowSize,
							 DigiRegFlagOptional,
							 0 );

	(void)DigiRegQueryDword( hKeyAdapterParameters,
							 DXB_REGVAL_SLOT_NUMBER,
							 &pControllerExt->SlotNumber,
							 DigiRegFlagOptional,
							 0 );

#endif // !SNA_DRIVER

#if SNA_DRIVER
	//
	//  We need to find the hardware parameters for this card installation,
	//  in order to initialize the card, at least 1 port on the card must
	//  be configured, we will search the config array for a configured
	//  port for the current adapter.
	//

	//  Verify we have some adapter configuration information
	if ((NT_SUCCESS (FindCardInfo (CardNumber, 1, &ConfigStruct))) ||
		(NT_SUCCESS (FindCardInfo (CardNumber, 2, &ConfigStruct))) ||
		(NT_SUCCESS (FindCardInfo (CardNumber, 3, &ConfigStruct))) ||
		(NT_SUCCESS (FindCardInfo (CardNumber, 4, &ConfigStruct))))
	{
		PrintConfig (&ConfigStruct);

		//  ISA Card config string
		if (ConfigStruct.BaseIoAddress >= 0x100)
		{
			pControllerExt->PhysIOPort.LowPart = ConfigStruct.BaseIoAddress;
			pControllerExt->PhysWindowAddress.LowPart = ConfigStruct.WindowAddress;
			pControllerExt->PhysInterruptNumber.LowPart = ConfigStruct.Irql;
			pControllerExt->BusType = Isa;
		}
		//  PCI Card config string
		else
		{
			pControllerExt->BusNumber = ConfigStruct.BaseIoAddress;
			pControllerExt->SlotNumber = ConfigStruct.WindowAddress;
			//  we don't care about the PCI card function number
			pControllerExt->BusType = PCIBus;
		}
	}
	else
	{
		DigiDump (DIGIERRORS, ("*** ERROR *** Card %d info not available!\n",
			CardNumber));
		Status = STATUS_UNSUCCESSFUL;
		goto DigiInitControllerExit;
	}

#else

	//
	//  The following registry entries are required
	//

	Status = DigiRegQueryDword( hKeyAdapterParameters,
								DXB_REGVAL_BUS_TYPE,
								&pControllerExt->BusType,
								DigiRegFlagRequired,
								0 );
	if (!NT_SUCCESS (Status))
	{
		ErrString = DXB_REGVAL_BUS_TYPE;
		goto DigiInitControllerExit;
	}


	Status = DigiRegQueryDword( hKeyAdapterParameters,
							DXB_REGVAL_BUS_NUMBER,
							&pControllerExt->BusNumber,
							DigiRegFlagRequired,
							0 );
	if (!NT_SUCCESS (Status))
	{
		ErrString = DXB_REGVAL_BUS_NUMBER;
		goto DigiInitControllerExit;
	}

	Status = DigiRegQueryDword( hKeyAdapterParameters,
						DXB_REGVAL_NUMBEROFLINES,
						&pControllerExt->AdapterLines,
						DigiRegFlagRequired,
						0 );
	if (!NT_SUCCESS (Status))
	{
		ErrString = DXB_REGVAL_NUMBEROFLINES;
		goto DigiInitControllerExit;
	}

#endif // SNA_DRIVER

	//
	//  Now we will setup the hardware specific stuff for the current adapter
	//
	switch (pControllerExt->BusType)
	{
	case PCIBus:
		Status = SetupPciAdapter (RegistryPath,
								  pDriverObject,
								  pControllerExt);
		break;

	case Isa:
	case Eisa:
		Status = SetupIsaAdapter (pDriverObject,
								  pControllerExt);
		break;

	default:
		ASSERT (0 && "*** Unsupported BusType! ***\n");
		Status = STATUS_UNSUCCESSFUL;
		break;
	}

	if (Status != STATUS_SUCCESS)
	{
		DigiDump (DIGIERRORS,
			("*** Could not acquire resources for controller.  (%s:%d)\n",
				__FILE__, __LINE__));

		DigiLogError (pDriverObject,
			MP570_CONTROLLER_FAILED_INITIALIZATION,
			24,
			NULL, 0,
			pControllerExt->Name.Buffer,
			pControllerExt->Name.Length + sizeof (WCHAR),
			NULL, 0);

		goto DigiInitControllerExit;
	}

	//
	//  Retrieve hardware port count.
	//
	RetrievePortCount (pControllerExt);

	//
	//  Now, retrieve and save port information (port count
	//  from RetrievePortCount() needed for this call).
	//
	RetrievePortConfig (pControllerExt, CardNumber);

	Status = STATUS_SUCCESS;

DigiInitControllerExit:

	// This card is done
	++CardNumber ;

	if (ErrString)
	{
		Status = STATUS_UNSUCCESSFUL;

		DigiLogError (pDriverObject,
			MP570_REGISTRY_VALUE_NOT_FOUND,
			25,
			NULL, 0,
			ErrString, wcslen (ErrString) * sizeof(WCHAR),
			NULL, 0);
	}

	if (!NT_SUCCESS (Status))
	{
		if (pControllerExt)
		{
			// We're not using hardware resources anymore.  Unreport them.
			if (pControllerExt->hReportedResources)
			{
				DigiUnreportResources (pDriverObject,
					pControllerExt->hReportedResources);
			}

		}
	}

	// Clean up
	DigiRegCloseKey( hKeyAdapterParameters );
	DigiStrFreeUnicodeString( &ucParametersPath );

	DigiDump (DIGIFLOW, ("Exiting DigiInitializeAdapter ()\n"));

	return (Status);

} // end DigiInitializeAdapter


#if (SNA_DRIVER)


NTSTATUS
fnRetrieveDeviceConfigInfo (
	IN PUNICODE_STRING	RegistryPath,
	IN OUT PUCHAR		BoardConfig,
	IN OUT PULONG		ConfigEntries)
/*++

Description:

	Retrieve the Card configuration binary information from the Registry:
	HKLM\System\CurrentControlSet\Services\DigiSync\Parameters\CardCfg

Parameters:

***	PUNICODE_STRING RegistryPath
		Registry path from Driver Entry

	PUCHAR BoardConfig
		Destination board configuration array

	PULONG ConfigEntries
		The number of board config entries.

Return Value:

	Value returned from RtlQueryRegistryValues.

--*/
{
	UNICODE_STRING		ParametersPath;
	STRING				ValueName;
	UNICODE_STRING		UnicodeValueName;
	HANDLE				ParametersKey;
	OBJECT_ATTRIBUTES	ParamAttributes;
	ULONG				RetInfoLen;
	PUCHAR				RetInfo;
	ULONG				DataOffset;
	ULONG				NumConfigEntries = 0;
	int					i;
	STRING				Para;
	UNICODE_STRING		UnicodePara;
	NTSTATUS			Status;
	PCONFIGDATA			pConfigDataArray = NULL;
#define		INFO_SIZE	0x1000


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

	//
	// Initialize and allocate memory for the Registry path to retrieve
	// the line configuration information
	//
	RtlInitUnicodeString (&ParametersPath, NULL);
	ParametersPath.MaximumLength = RegistryPath->Length + sizeof (L"\\Parameters") + 9;
	ParametersPath.Buffer = ExAllocatePool (PagedPool, ParametersPath.MaximumLength);

	if (ParametersPath.Buffer)
	{
		// Zero out the buffer and copy the RegistryPath into it
		RtlZeroMemory (ParametersPath.Buffer, ParametersPath.MaximumLength);
		RtlAppendUnicodeStringToString (&ParametersPath, RegistryPath);

		// Format the path to point to the Parameters key
		RtlInitString (&Para, (PCSZ) "\\Parameters");
		RtlAnsiStringToUnicodeString (&UnicodePara, &Para, TRUE);
		RtlAppendUnicodeStringToString (&ParametersPath, &UnicodePara);
		RtlFreeUnicodeString (&UnicodePara);

		// Initialize the object attributes
		InitializeObjectAttributes (&ParamAttributes,
			&ParametersPath,
			OBJ_CASE_INSENSITIVE,
			NULL,
			NULL);

		// Open the key and retrieve the line configuration data
		Status = ZwOpenKey (&ParametersKey, KEY_QUERY_VALUE, &ParamAttributes);
		if (NT_SUCCESS (Status))
		{
			RtlInitString (&ValueName, (PCSZ) "CardCfg");
			RtlAnsiStringToUnicodeString (&UnicodeValueName, &ValueName, TRUE);
			RetInfo = ExAllocatePool (PagedPool, INFO_SIZE);

			// Retrieve the line configuartion data
			Status = ZwQueryValueKey (ParametersKey,
				&UnicodeValueName,
				KeyValueFullInformation,
				RetInfo,
				INFO_SIZE,
				&RetInfoLen);

			if (NT_SUCCESS (Status))
			{
				// Check for type and the size
				if ((((PKEY_VALUE_BASIC_INFORMATION) RetInfo)->Type == REG_BINARY) &&
					!(((PKEY_VALUE_FULL_INFORMATION) RetInfo)->DataLength
						% sizeof (CONFIGDATA)))
				{
					DataOffset = ((PKEY_VALUE_FULL_INFORMATION) RetInfo)->DataOffset;
					if (DataOffset != -1)
					{
						// Retrieve the number of line configuration
						NumConfigEntries = ((PKEY_VALUE_FULL_INFORMATION)
							RetInfo)->DataLength / sizeof (CONFIGDATA);
						RtlMoveMemory (BoardConfig,
							(RetInfo + DataOffset),
							sizeof (CONFIGDATA) * NumConfigEntries);
					}
				}
				else
				{
					DigiDump (DIGIERRORS, ("Type or DataLength is wrong \n"));
				}

				RtlFreeUnicodeString (&UnicodeValueName);
			}
			else
			{
				DigiDump (DIGIERRORS, ("ZwQueryValueKey() error \n"));
			}
			ExFreePool (RetInfo);
			ZwClose (ParametersKey);
		}
		else
		{
			DigiDump (DIGIERRORS, ("ZwOpenKey() error\n"));
		}
		ExFreePool (ParametersPath.Buffer);
	}
	else
	{
		DigiDump (DIGIERRORS, ("ParametersPath memory allocation error\n"));
	}

	*ConfigEntries = NumConfigEntries;

	DigiDump (DIGIINIT, ("Exiting fnRetrieveDeviceConfigInfo() %d\n",NumConfigEntries));
	return (Status);
}

#endif // !SNA_DRIVER

#if SNA_DRIVER

VOID
PrintConfig (
	IN PCONFIGDATA	pConfigData)
{
	DigiDump (DIGIVERYLOUD, ("Config Information:\n"
		"  DisplayName           : %s\n"
		"  DeviceName            : %s\n"
		"  BusNum/BaseIoAddress  : 0x%x\n"
		"  SlotNum/WindowAddress : 0x%x\n"
		"  FuncNum/Interrupt     : 0x%x\n"
		"  PortNumber            : 0x%x\n"
		"  LineSpeed             : 0x%x\n"
		"  Internal Clock        : 0x%x\n\n",
		pConfigData->DisplayName,
		pConfigData->DeviceName,
		pConfigData->BaseIoAddress,
		pConfigData->WindowAddress,
		pConfigData->Irql,
		pConfigData->PortNumber,
		pConfigData->LineSpeed,
		pConfigData->InternalClock));
}


NTSTATUS
FindCardInfo (
	IN UCHAR		CardNumber,
	IN UCHAR		PortNumber,
	OUT PCONFIGDATA	FoundConfigData)
{
	UCHAR		Count = 0;
	BOOLEAN		Done = FALSE, Found = FALSE;
	NTSTATUS	Status = STATUS_UNSUCCESSFUL;
	PCONFIGDATA	pConfigData;
	ULONG		WindowAddressPlusBaseIoAddress[16];
	ULONG		CurrentWindowAddressPlusBaseIoAddress;
	UCHAR		i, j = 0;
	UCHAR		Default_DeviceName[DEV_NAME_LEN] = "\\Device\\DG570_";


	DigiDump (DIGIFLOW, ("<FindCardInfo> Searching for Card #%d, Port #%d\n",
		CardNumber, PortNumber));

	RtlZeroMemory (WindowAddressPlusBaseIoAddress, sizeof (WindowAddressPlusBaseIoAddress));
	RtlZeroMemory (FoundConfigData, sizeof (CONFIGDATA));

	//  Setup a default device name
	Default_DeviceName[strlen (Default_DeviceName)]	= '0' + CardNumber;
	Default_DeviceName[strlen (Default_DeviceName)] = '0' + PortNumber;
	RtlCopyMemory (FoundConfigData->DeviceName, Default_DeviceName,
		strlen (Default_DeviceName));

	//  Read all of the cards' WindowAddresses
	pConfigData = &CardCfgArray[0];
	while ((pConfigData->WindowAddress != 0) && (Count < 16))
	{
		CurrentWindowAddressPlusBaseIoAddress = pConfigData->WindowAddress + 
											    pConfigData->BaseIoAddress;
		//  First time through the loop
		if (Count == 0)
		{
			WindowAddressPlusBaseIoAddress[Count++] = CurrentWindowAddressPlusBaseIoAddress;
		}
		else
		{
			Found = FALSE;
			for (i = 0; (i < Count) && (!Found); i++)
			{
				if (WindowAddressPlusBaseIoAddress[i] == CurrentWindowAddressPlusBaseIoAddress)
				{
					Found = TRUE;
				}
			}

			if (!Found)
			{
				WindowAddressPlusBaseIoAddress[Count++] = CurrentWindowAddressPlusBaseIoAddress;
			}
		}
		pConfigData++;
	}


	Done = FALSE;
	Count = 0;
	j = 0;
	pConfigData = &CardCfgArray[0];
	while ((pConfigData->WindowAddress != 0) && (Count < 16) && (!Done))
	{
		++Count;

		CurrentWindowAddressPlusBaseIoAddress = pConfigData->WindowAddress + 
											    pConfigData->BaseIoAddress;

		//  We found the desired information
		if ((WindowAddressPlusBaseIoAddress[CardNumber] == CurrentWindowAddressPlusBaseIoAddress) &&
			(PortNumber == pConfigData->PortNumber))
		{
			Done = TRUE;
			Status = STATUS_SUCCESS;
			RtlCopyMemory (FoundConfigData, pConfigData, sizeof (CONFIGDATA));
		}

		pConfigData++;
	}

	DigiDump (DIGIFLOW, ("<FindCardInfo> Exiting (Status 0x%x)\n", Status));
	return (Status);
}


ULONG
CountInstalledCards (
	IN PUNICODE_STRING	RegistryPath)
/*++

Description:

	Retrieve the Card configuration binary information from the Registry,
	then parse through it to count the number of cards installed in the
	system that have at least one port configured.  This is only done for
	SNA installations.

	It is the combination of the WindowAddress and the BaseIOAddress that define
	a card:

			PCI:  WindowAddress = SlotNumber
				  BaseIOAddress = BusNumber
			      
			ISA:  WindowAddress = Memory Base
				  BaseIOAddress = Base I/O Port

Parameters:

	PUNICODE_STRING RegistryPath
		Registry path from Driver Entry

Return Value:

	The count of installed cards.

--*/
{
	BOOLEAN		Found = FALSE;
	UCHAR		i, Count = 0;
	CONFIGDATA	ConfigDataArray[16];
	PCONFIGDATA	pConfigData;
	ULONG		ConfigEntries = 0, InstalledCards = 0;
	ULONG		WindowAddressPlusBaseIoAddress[16];
	ULONG		CurrentWindowAddressPlusBaseIoAddress;
	NTSTATUS	Status;


	DigiDump (DIGIFLOW, ("<CountInstalledCards> Entering\n"));

	RtlZeroMemory ((PUCHAR) ConfigDataArray, sizeof (ConfigDataArray));
	RtlZeroMemory (WindowAddressPlusBaseIoAddress, sizeof (WindowAddressPlusBaseIoAddress));

	Status = fnRetrieveDeviceConfigInfo (RegistryPath,
		(PUCHAR)ConfigDataArray, &ConfigEntries);
	if (NT_SUCCESS (Status))
	{
		if (ConfigEntries > (sizeof (ConfigDataArray)/sizeof (CONFIGDATA)))
		{
			DigiDump (DIGIERRORS,
				("<CountInstalledCards> *** ERROR *** Partial Registry info only\n"));
			return (InstalledCards);
		}
	}

	//  Read all of the cards' WindowAddresses
	pConfigData = &CardCfgArray[0];
	while ((pConfigData->WindowAddress != 0) && (Count < 16))
	{
		CurrentWindowAddressPlusBaseIoAddress = pConfigData->WindowAddress + 
											    pConfigData->BaseIoAddress;
		if (Count == 0)
		{
			// Track distinct addresses
			WindowAddressPlusBaseIoAddress[Count++] = CurrentWindowAddressPlusBaseIoAddress;
			++InstalledCards;
		}
		else
		{
			Found = FALSE;
			for (i = 0; (i < Count) && (!Found); i++)
			{
				if (WindowAddressPlusBaseIoAddress[i] == CurrentWindowAddressPlusBaseIoAddress)
				{
					Found = TRUE;
				}
			}

			if (!Found)
			{
				WindowAddressPlusBaseIoAddress[Count++] = CurrentWindowAddressPlusBaseIoAddress;
				++InstalledCards;
			}
		}

		pConfigData++;
	}


	DigiDump (DIGIFLOW, ("<CountInstalledCards> Exiting, (InstalledCards: %d)\n",
		InstalledCards));

	return (InstalledCards);
}

#endif // SNA_DRIVER


VOID
RetrievePortConfig (
	IN PCONTROLLER_EXT	pControllerExt,
	IN UCHAR			CardNumber)
/*++

Description:

	Retrieve the Card configuration binary information from the Registry,
	then parse through it to save the configuration information for use
	later in the driver.  This is only done for SNA installations.

Parameters:

	PCONTROLLER_EXT
		Pointer to the Controller Extension

Return Value:

	None.

--*/
{
#if SNA_DRIVER

	CONFIGDATA		ConfigStruct;
	NTSTATUS		Status = STATUS_SUCCESS;
	UCHAR			i = 0;


	DigiDump (DIGIFLOW, ("<RetrievePortConfig> Entering\n"));


	for (i = 0; i < pControllerExt->AdapterLines; i++)
	{
		Status = FindCardInfo (CardNumber, (UCHAR)(i+1), &ConfigStruct);
		if (NT_SUCCESS (Status))
		{
			pControllerExt->InternalClocking[i] = (ConfigStruct.InternalClock)
				? TRUE : FALSE;

			pControllerExt->SNALineSpeed[i] = ConfigStruct.LineSpeed;
			RtlCopyMemory (&pControllerExt->DeviceName[i][0],
				ConfigStruct.DeviceName, sizeof (ConfigStruct.DeviceName));

			DigiDump (DIGIVERYLOUD, ("Found CardNumber %d, Name: %s\n",
				CardNumber, ConfigStruct.DeviceName));
		}
		else
		{
			//  Copy the default Device name
			RtlCopyMemory (&pControllerExt->DeviceName[i][0],
				ConfigStruct.DeviceName, sizeof (ConfigStruct.DeviceName));
		}
	}

	DigiDump (DIGIFLOW, ("<RetrievePortConfig> Exiting\n"));

#endif	// SNA_DRIVER
}


VOID
RetrievePortCount (
	IN PCONTROLLER_EXT	pControllerExt)
/*++

Description:

	Find out if the installed card is a 2-port or a 4-port version.  Save
	the port count in pControllerExt->AdapterLines.  This is only done for
	SNA installations.

Parameters:

	PCONTROLLER_EXT
		Pointer to the Controller Extension

Return Value:

	None.

--*/
{
#if SNA_DRIVER

	DigiDump (DIGIFLOW, ("<RetrievePortCount> Entering\n"));

	if (PCIBus == pControllerExt->BusType)
	{
		if (pControllerExt->DeviceID == DIGI_PCI_SYNC570I_2PORT_LIN) ||
		{
			pControllerExt->AdapterLines = 2;
		}
		else if (pControllerExt->DeviceID == DIGI_PCI_SYNC570I_4PORT_LIN)
		{
			pControllerExt->AdapterLines = 4;
		}
		else
		{
			pControllerExt->AdapterLines = 0;
		}
	}
	else
	{
		pControllerExt->AdapterLines = IO_IN (pControllerExt->VirtualIO + 5);
		if ((pControllerExt->AdapterLines != 2) &&
			(pControllerExt->AdapterLines != 4))
		{
			pControllerExt->AdapterLines = 0;
			DigiDump (DIGIERRORS, ("*** ERROR *** AdapterLines set to 0!!!\n"));
		}
	}

	DigiDump (DIGIFLOW, ("<RetrievePortCount> Exiting (%d ports)\n",
		pControllerExt->AdapterLines));

#endif	// SNA_DRIVER

}


LONG 
DuStringToWideString (
	IN PSTR		pSource,
	OUT PWSTR * pDestination,
	IN BOOLEAN	bAllocateDest)
/*++

Routine Description:
	This routine will take a normal string and convert it to a wide string.
	This simply means inserting a NULL byte between every character of the
	input string, with a double NULL at the end.  The destination buffer will
	be allocated by this function if bAllocateDest is TRUE.  It is the
	responsibility of the caller to free this buffer.

	Include: digistr.h; digistat.h

Arguments:
	pSource - Source string. Each character of this string is 1 byte.

	pDestination - Destination string. Each character of this string is 2 bytes.

	bAllocateDest - If TRUE, this routine will alocate the destination buffer.


Return Value:
	Status of operation.  Will be one of the following:
		SUCCESS, UNSUCCESSFUL
--*/
{
	//
	// Local Variables
	//
	PSTR    pCurrentSource = pSource;	// Pointer to current location within
										// source buffer
	PSTR    pCurrentDestination;	// Pointer to current location within
									// destination buffer

	//
	// Allocate memory, if requested.
	//
	if (bAllocateDest)
	{
		//
		// The length allocated is the length of the source string + 1 (because
		// strlen does not count the terminating NULL) and then times 2
		// (because each character in the source will require 2 bytes in
		// the destination).
		//
		*pDestination = DigiAllocMem (PagedPool, (strlen (pSource) + 1) * 2);
		if (*pDestination == NULL)
			return (STATUS_INSUFFICIENT_RESOURCES);
	}

	//
	// Initialize pCurrentDestination
	//
	pCurrentDestination = (PSTR) * pDestination;

	while (*pCurrentSource != '\0')
	{
		*pCurrentDestination++ = *pCurrentSource++;	// Copy source
		*pCurrentDestination++ = '\0';	// Copy NULL
	}

	//
	// Now add double NULL for string termination.
	//
	*pCurrentDestination++ = '\0';
	*pCurrentDestination = '\0';

	return (STATUS_SUCCESS);
}

#endif	// !NT5_BUILD





