/* File: DxbRegHndlr.cpp */

#include <windows.h>
#include <winbase.h>
#include <windef.h>
#include <string.h>

#include "dxbtypes.h"				// Common types used for cross platform development
#include "macros.h"
#include "buildver.h"
#include "DxbMgtInternal.h"
#include "DxbReg.h"					// Common header used by config, driver and this
#include "MgmtShim.h"
#include "mgmthlc.h"
#include "DxbRegHndlr.h"
#include "triglbl.h"

#if defined NT5
#include <setupapi.h>
#include <stdio.h>
#include "..\inc\pnpfilesandids.h"
#endif // #if defined NT5

#if !NT5_BUILD && !_M_IA64
// NT5 and newer DDKs define these types.  They float (at compile time) from 32 to 64
// bit width depending on the size of pointers in the system being compiled for.
// Define it for NT4.
typedef DWORD        DWORD_PTR, *PDWORD_PTR;
#endif

static REG_DESCRIPTOR_T regDescriptors[MAX_BOARDS] = {0};
static DWORD regIndexLookup[MAX_BOARDS] = {0};
static BOOL gfInitialized = FALSE;
static DWORD gnBoards = 0;
static BOOL gfSorted = FALSE;

#if !defined NT5
static inline BOOL
GetNetworkCardsKey(HKEY * const pCardsKeyOut,
						 DWORD * const pcSubKeysOut)
{
	BOOL Success = FALSE;
	LONG Result;
	HKEY hkCardsKey = NULL;
	DWORD cSubKeys;

	TCHAR NetworkCardsPath[] = NETCARDS_REG_PATH;

	/////////////////////////////////////////////////////////////////
	// HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards
	// For NT4, this key contains one sub-key per installed network adapter
	// Dxb adapter keys have the following values:
	// Manufacturer: REG_SZ: Digi
	// ProductName: REG_SZ: DigiDxb
	// ServiceName: REG_SZ: DigiDxbN

	// NOTE: NOTE: Registry path is NT4 specific, requires update for NT5
	Result = RegOpenKeyEx(	HKEY_LOCAL_MACHINE,
									&NetworkCardsPath[0],
									NULL,
									KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE,
									&hkCardsKey);

	if (ERROR_SUCCESS != Result)
	{
		// NOTE: NOTE: For NT5, will need to check for 'not found' error, then check for NT5 specific path
		DebugMessage2("Couldn't open key %s\nError:0x%x\n", &NetworkCardsPath[0], Result);
		goto Exit;
	}

	Result = RegQueryInfoKey (	hkCardsKey,
										NULL,NULL,NULL,
										&cSubKeys,
										NULL,NULL,NULL,NULL,NULL,NULL,NULL);

	if (ERROR_SUCCESS != Result)
	{
		DebugMessage2("Couldn't query key info for %s\nError:0x%x\n", &NetworkCardsPath[0], Result);
		goto Exit;
	}

	*pcSubKeysOut = cSubKeys;
	*pCardsKeyOut = hkCardsKey;
	Success = TRUE;
Exit:
	return Success;
}
#endif // #if !defined NT5

static inline BOOL
VerifyBoardNo(DWORD nBoard)
{
	return ( gfSorted && gfInitialized && (nBoard <= gnBoards) && (nBoard > 0));
}

static inline BOOL
VerifyRegDescriptorIndex(DWORD nIndex)
{
	return ((nIndex < gnBoards) && (nIndex >= 0));
}

static inline void
Cleanup(void)
{
	// cleans up the globals
	ZeroMemory(&regDescriptors[0], sizeof regDescriptors);
	ZeroMemory(&regIndexLookup[0], sizeof regIndexLookup);
	gfInitialized = FALSE;
	gnBoards = 0;
}

#define REGKEY_SEPERATOR TEXT("\\")

#define 	ADD_SEPERATOR(pbuf) {strcat(pbuf,"\\");}

#if defined NT5
#define BUILD_PARAMETERS_KEY_NAME(rd_index,pbuf) {		\
	pbuf[0]=0;												\
	strcat(pbuf, &(regDescriptors[rd_index].RegPath[0]));	\
}
#else // #if defined NT5
#define BUILD_PARAMETERS_KEY_NAME(rd_index,pbuf) {		\
	pbuf[0]=0;												\
	strcat(pbuf,DXB_REGKEY_BASE_BOARD_INSTANCE);	\
	ADD_SEPERATOR(pbuf);									\
	strcat(pbuf, &(regDescriptors[rd_index].DosDeviceName[0]));	\
	ADD_SEPERATOR(pbuf);									\
	strcat(pbuf, DXB_REGKEY_PARAMETERS);			\
}
#endif // #if defined NT5

// NOTE: NOTE: These may require ASYN or ISDN as part of the path. Other protocols could be added as well
#define BUILD_PORT_KEY_NAME(rd_index,prt,pbuf) {	\
	BUILD_PARAMETERS_KEY_NAME(rd_index,pbuf);			\
	ADD_SEPERATOR(pbuf);										\
	_itoa(prt-1, &pbuf[strlen(pbuf)], 10 );			\
}

#define BUILD_CHANNEL_KEY_NAME(rd_index,prt,chan,pbuf)	{	\
	BUILD_PORT_KEY_NAME(rd_index,prt,pbuf);						\
	ADD_SEPERATOR(pbuf);													\
	_itoa(chan-1, &pbuf[strlen(pbuf)], 10 );						\
}

#define ADD_SUBKEY_NAME(pszSubKeyName,szKeyName) {	\
	ADD_SEPERATOR(szKeyName);								\
	strcat(szKeyName, pszSubKeyName);					\
}

#if defined NT5
#define BUILD_ASYNC_CHAN_KEY_NAME(rd_index, port, chan, pbuf) {		\
	BUILD_PARAMETERS_KEY_NAME(rd_index,pbuf);			\
	ADD_SEPERATOR(pbuf);									\
	strcat(pbuf, DXB_REGKEY_ASYN);					\
	ADD_SEPERATOR(pbuf);									\
	_itoa(port-1, &pbuf[strlen(pbuf)], 10 );		\
	ADD_SEPERATOR(pbuf);									\
	_itoa(chan-1, &pbuf[strlen(pbuf)], 10 );		\
}
#else // #if defined NT5
#define BUILD_ASYNC_CHAN_KEY_NAME(rd_index, port, chan, pbuf) {		\
	pbuf[0]=0;												\
	strcat(pbuf,DXB_REGKEY_BASE_BOARD_INSTANCE);	\
	ADD_SEPERATOR(pbuf);									\
	strcat(pbuf, &(regDescriptors[rd_index].DosDeviceName[0]));	\
	ADD_SEPERATOR(pbuf);									\
	strcat(pbuf, DXB_REGKEY_PARAMETERS);			\
	ADD_SEPERATOR(pbuf);									\
	strcat(pbuf, DXB_REGKEY_ASYN);					\
	ADD_SEPERATOR(pbuf);									\
	_itoa(port-1, &pbuf[strlen(pbuf)], 10 );		\
	ADD_SEPERATOR(pbuf);									\
	_itoa(chan-1, &pbuf[strlen(pbuf)], 10 );		\
}

static inline BOOL
OpenAdapterParameterKey(	DWORD const regDescriptorIndex,
									HKEY * const phkParamsKey)
{
	// Can be used to open for query and or writing
	DWORD Result;
	BOOL Success;
	char szKeyName[MAX_PATH];
	HKEY hkReturn = NULL;

	if (!VerifyRegDescriptorIndex(regDescriptorIndex))
	{
		Success = FALSE;
		goto Exit;
	}

	BUILD_PARAMETERS_KEY_NAME(regDescriptorIndex,szKeyName);	// Hard coded pathing

	Result = RegOpenKeyEx(	HKEY_LOCAL_MACHINE,
									&szKeyName[0],
									NULL,
									KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE | KEY_WRITE,
									&hkReturn);

	if (ERROR_SUCCESS != Result)
	{
		DebugMessage2("Couldn't open key:%s, returned error:0x%x\n", szKeyName, Result);
		Success = FALSE;
		goto Exit;
	}

	*phkParamsKey = hkReturn;
	Success = TRUE;
Exit:
	return Success;
}

static inline BOOL
OpenAdapterKey(DWORD const regDescriptorIndex,
						DWORD const PortNumber,
						DWORD const ChannelNumber,
						TCHAR const * const pszSubKeyName,
						HKEY * const phkParamKey)
{
	// Can be used to open for query and or writing
	DWORD Result;
	BOOL Success;
	char szKeyName[MAX_PATH] = {0};
	HKEY hkReturn = NULL;

	if (!VerifyRegDescriptorIndex(regDescriptorIndex))
	{
		Success = FALSE;
		goto Exit;
	}

	// zero is invalid/ignored, key on this to build path
	if ( (0 == PortNumber) && (0 == ChannelNumber) )
	{
		BUILD_PARAMETERS_KEY_NAME(regDescriptorIndex,szKeyName);
	}

	if ( (0 != PortNumber) && (0 == ChannelNumber) )
	{
		BUILD_PORT_KEY_NAME(regDescriptorIndex,PortNumber,szKeyName);
	}

	if ( (0 != PortNumber) && (0 != ChannelNumber) )
	{
		BUILD_CHANNEL_KEY_NAME(regDescriptorIndex,PortNumber,ChannelNumber,szKeyName);
	}

	if ( (0 == PortNumber) && (0 != ChannelNumber) )
	{
		// Error, must have port if channel is addressed
		DebugMessage1("Port zero, channel nonzero:%d, must have valid port for channel addressing\n", ChannelNumber);
		Success = FALSE;
		goto Exit;
	}

	if ((NULL != pszSubKeyName) && (0 != strlen(pszSubKeyName)))
	{
		ADD_SUBKEY_NAME(pszSubKeyName,szKeyName);
	}

	{
		DWORD Disposition;
		Result = RegCreateKeyEx(	HKEY_LOCAL_MACHINE,
											&szKeyName[0],
											NULL,
											NULL,
											REG_OPTION_NON_VOLATILE,
											KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE | KEY_WRITE,
											NULL,
											&hkReturn,
											&Disposition);

		if (ERROR_SUCCESS != Result)
		{
			DebugMessage3("Couldn't create key:%s, returned error:0x%x, Disposition: %x\n", szKeyName, Result, Disposition);
			Success = FALSE;
			goto Exit;
		}
	}

	*phkParamKey = hkReturn;
	Success = TRUE;
Exit:
	return Success;
}
#endif // #if defined NT5

static inline BOOL
CheckRegSzValue(HKEY const hkCheck,
					 TCHAR const * const pszValueName,
					 TCHAR const * const pszValue)
{
	BOOL Success = FALSE;
	unsigned char szRegValue[MAX_PATH];
	DWORD Type = REG_SZ;
	DWORD cbData = sizeof szRegValue;

	LONG Result = RegQueryValueEx(	hkCheck,
												pszValueName,
												NULL,
												&Type,
												&szRegValue[0],
												&cbData);
	if (ERROR_SUCCESS != Result)
	{
		DebugMessage2("Couldn't query value: %s\nError:0x%x\n", &pszValueName[0], Result);
		goto Exit;
	}

	if (0 != strcmp(pszValue, (TCHAR *)(&szRegValue[0])) )
		goto Exit;	// Not an error... just not the value we're interested in

	Success = TRUE;
Exit:
	return Success;
}

static inline BOOL
GetRegSzValue(HKEY const hkKey,
				  TCHAR const * const pszValueName,
				  TCHAR * const pszValue,
				  DWORD const cbMaxValue)
{
	BOOL Success = FALSE;

	unsigned char szRegValue[MAX_PATH];
	DWORD Type = REG_SZ;
	DWORD cbData = sizeof szRegValue;
	LONG Result = RegQueryValueEx(	hkKey,
												pszValueName,
												NULL,
												&Type,
												&szRegValue[0],
												(unsigned long *) &cbData);

   if (ERROR_SUCCESS != Result)
	{
		DebugMessage2("Couldn't query value: %s, error:0x%x\n", &pszValueName[0], Result);
		goto Exit;
	}

   if (cbData > cbMaxValue)
	{
		DebugMessage2("Value size too large, have: %d, need: %d\n", cbMaxValue, cbData);
		goto Exit;
	}

   memcpy(&pszValue[0], &szRegValue[0], cbData);

	Success = TRUE;
Exit:
	return Success;
}

static inline BOOL
SetRegSzValue(HKEY hkKey,
				  TCHAR const * const pszValueName,
				  TCHAR const * const pszValue)
{
	BOOL Success = FALSE;
	DWORD Type = REG_SZ;
	DWORD_PTR cbData = strlen(pszValue)+1;

	LONG Result = RegSetValueEx(	hkKey,
											pszValueName,
											NULL,
											Type,
											(unsigned char *)(pszValue),
											(unsigned long) cbData);
	if (ERROR_SUCCESS != Result)
	{
		DebugMessage3("Couldn't set string value: %s to %s, error:0x%x\n", &pszValueName[0], pszValue, Result);
		goto Exit;
	}

	Success = TRUE;
Exit:
	return Success;
}

static inline BOOL
GetRegDwordValue(HKEY const hkKey,
					  TCHAR const * const pszValueName,
					  DWORD * const pValue)
{
	BOOL Success = FALSE;
	DWORD nRegValue;
	DWORD Type = REG_DWORD;
	DWORD cbData = sizeof nRegValue;

	LONG Result = RegQueryValueEx(	hkKey,
												pszValueName,
												NULL,
												&Type,
												(unsigned char *)(&nRegValue),
												&cbData);
	if (ERROR_SUCCESS != Result)
	{
		DebugMessage2("Couldn't query value: %s  error:0x%x\n", &pszValueName[0], Result);
		goto Exit;
	}

	if (REG_DWORD != Type)
	{
		DebugMessage1("Query returned incorrect value type: %d\n", Type);
		goto Exit;
	}

	if (sizeof nRegValue != cbData)
	{
		DebugMessage2("Value size incorrect, expected: %d, got: %d\n", sizeof nRegValue, cbData);
		goto Exit;
	}

	*pValue = nRegValue;

	Success = TRUE;
Exit:
	return Success;
}

static inline BOOL
SetRegDwordValue(HKEY const hkKey,
					  TCHAR const * const pszValueName,
					  DWORD const Value)
{
	BOOL Success = FALSE;
	DWORD Type = REG_DWORD;
	DWORD cbData = sizeof Value;

	LONG Result = RegSetValueEx(	hkKey,
											pszValueName,
											NULL,
											Type,
											(unsigned char *)(&Value),
											cbData);
	if (ERROR_SUCCESS != Result)
	{
		DebugMessage3("Couldn't set value:%s to %d error:0x%x\n", &pszValueName[0], Value, Result);
		goto Exit;
	}

	Success = TRUE;
Exit:
	return Success;
}

static inline BOOL
GetRegBinValue(	HKEY const hkKey,
				TCHAR const * const pszValueName,
				TCHAR * const pValue,
				DWORD const cbMaxValue)
{
	BOOL Success = FALSE;

	unsigned char RegValue[MAX_PATH];
	DWORD Type = REG_BINARY;
	DWORD cbData = sizeof RegValue;

	LONG Result = RegQueryValueEx(	hkKey,
									pszValueName,
									NULL,
									&Type,
									&RegValue[0],
									&cbData);
	if (ERROR_SUCCESS != Result)
	{
		DebugMessage2("Couldn't query binary value: %s, error:0x%x\n", &pszValueName[0], Result);
		goto Exit;
	}

	if (cbData > cbMaxValue)
	{
		DebugMessage2("Value size too large, have: %d, need: %d\n", cbMaxValue, cbData);
		goto Exit;
	}

	memcpy(&pValue[0], &RegValue[0], cbData);

	Success = TRUE;
Exit:
	return Success;
}

#ifdef SORT_BY_INSTANCE
static inline BOOL
MakeLookupList(void)
{
	BOOL Success = FALSE;
	DWORD nBoardCount;
	DWORD nIndex;
	DWORD regDescriptorIndex = 0;
	DWORD nLargest = 0;
	TCHAR * pszName;

	gfSorted = FALSE;

	// Convert the ASCII to int, find the largest instance number...
	for (nBoardCount = 0; nBoardCount < gnBoards; nBoardCount++)
	{
		TCHAR szNumber[MAX_DOS_DEVICE_NAME];
		DWORD nNumberIndex;
		DWORD nNumber;
		DWORD Length;
		DWORD nCount;

		pszName = &regDescriptors[nBoardCount].DosDeviceName[0];
		Length = strlen(pszName)+1;
		nNumberIndex = 0;
		for (nCount = 0; nCount < Length; nCount++)
		{
			// Assumes the numbers aren't 'split'
			if ( (pszName[nCount] >= '0') && (pszName[nCount] <= '9') )
			{
				szNumber[nNumberIndex] = pszName[nCount];
				nNumberIndex++;
			}
		}
		szNumber[nNumberIndex] = 0;	// NULL terminate
		nNumber = atoi(szNumber);
		regDescriptors[nBoardCount].InstanceNo = nNumber;

		DebugMessage1("Found Instance Number %d\n", nNumber);

		if (nLargest < nNumber)
			nLargest = nNumber;
	}

	DWORD nLastInstance = 0;
	DWORD nInstanceCandidate;
	// Bubble sort the lookup table, Assumes unique values... (n*n complexity, n is small)
	for (nIndex = 0; nIndex < gnBoards; nIndex++)
	{
		nInstanceCandidate = nLargest;	// Derived above
		for (nBoardCount = 0; nBoardCount < gnBoards; nBoardCount++)
		{
			// Find the smallest instance number that's greater than the last value
			if (	(regDescriptors[nBoardCount].InstanceNo > nLastInstance) &&
					(regDescriptors[nBoardCount].InstanceNo <= nInstanceCandidate)
				)
			{
				nInstanceCandidate = regDescriptors[nBoardCount].InstanceNo;	// for iteration only
				regDescriptorIndex = nBoardCount;
			}
		}

		regDescriptors[regDescriptorIndex].BoardInfo.BoardNo = nIndex + 1;
		regIndexLookup[nIndex] = regDescriptorIndex;
		nLastInstance = nInstanceCandidate;

		DebugMessage3("BoardNo: %d, Lookup Index: %d, Last Instance: %d\n", regDescriptors[regDescriptorIndex].BoardNo, regIndexLookup[nIndex], nLastInstance);
	}

	// Sanity check.... We should have a non-zero BoardNo in every instance
	for (nBoardCount = 0; nBoardCount < gnBoards; nBoardCount++)
	{
		if (0 == regDescriptors[nBoardCount].BoardNo)
		{
			DebugMessage0("Found BoardNo value of zero after generating list\n");
			goto Exit;
		}
	}

	Success = TRUE;
	gfSorted = TRUE;

Exit:
	return Success;
}

#else // #ifdef SORT_BY_INSTANCE
// Sort by BrdId
static inline BOOL
MakeLookupList(void)
{
	BOOL Success = FALSE;
	DWORD nIndex, nOutsideIndex, nInsideIndex;

	DWORD nLargest = 0;
	DWORD nCurrent = 0;
	DWORD nCandidate = 0;
	DWORD regDescriptorIndex = 0;

	gfSorted = FALSE;	// We're not sorted if we get here

	if (0 == gnBoards)
	{
		goto Exit;
	}

	// Create the lookup table, these indexes will then be sorted
	for (nIndex = 0; nIndex < gnBoards; nIndex++)
	{
		regIndexLookup[nIndex] = nIndex;
	}
	// Bubbles...
	if (1 != gnBoards) { // Nothing to sort
		for (nOutsideIndex = 0; nOutsideIndex < gnBoards; nOutsideIndex++)
		{
			for (nInsideIndex = 1; nInsideIndex < gnBoards; nInsideIndex++)
			{
				if (	regDescriptors[regIndexLookup[nInsideIndex - 1]].BoardInfo.BrdId >
						regDescriptors[regIndexLookup[nInsideIndex]].BoardInfo.BrdId )
				{
					// Swap the lookup indexes...
					nCandidate = regIndexLookup[nInsideIndex - 1];
					regIndexLookup[nInsideIndex - 1] = regIndexLookup[nInsideIndex];
					regIndexLookup[nInsideIndex] = nCandidate;
				}
			}
		}
	}

	// Now fill in the sorted board numbers...
	for (nIndex = 0; nIndex < gnBoards; nIndex++)
	{
		regDescriptors[regIndexLookup[nIndex]].BoardInfo.BoardNo = nIndex + 1;
	}

	// Debug
	DebugMessage0("regDescriptors List:\n");
	for (nIndex = 0; nIndex < gnBoards; nIndex++)
	{
		DebugMessage6("DosDeviceName: %s, BoardNo:%d, BrdId:%d, BoardType:%d, TotalChannels:%d, TotalPorts:%d\n",
							&regDescriptors[nIndex].DosDeviceName[0],
							regDescriptors[nIndex].BoardInfo.BoardNo,
							regDescriptors[nIndex].BoardInfo.BrdId,
							regDescriptors[nIndex].BoardInfo.BoardType,
							regDescriptors[nIndex].BoardInfo.TotalChannels,
							regDescriptors[nIndex].BoardInfo.TotalPorts);
	}

	Success = TRUE;
	gfSorted = TRUE;

Exit:
	return Success;
}
#endif // #ifdef SORT_BY_INSTANCE

#if !defined NT5
inline BOOL
GetBoardInfo(DWORD const RegDescriptorIndex,
					 void * const pBuf)
{
	BOOL	Success;
	DWORD Bus = 0;
	DWORD	Slot = 0;
	DWORD	PciId = 0;
	DWORD	PciSubId = 0;
	DWORD	Ports = 0;
	DWORD	Channels = 0;
	DWORD Type = 0;
	HKEY	hkParamsKey = NULL;

	mgmt_board_info_t * pMgmtBoardInfo = (mgmt_board_info_t *)pBuf;

	// Get the params we're interested in
	if (	!OpenAdapterParameterKey(RegDescriptorIndex, &hkParamsKey) ||
			!GetRegDwordValue(hkParamsKey, DXB_REGVAL_BUS_NUMBER, &Bus) ||
			!GetRegDwordValue(hkParamsKey, DXB_REGVAL_SLOT_NUMBER, &Slot) ||
			!GetRegDwordValue(hkParamsKey, DXB_REGVAL_PCI_ID, &PciId) ||
			!GetRegDwordValue(hkParamsKey, DXB_REGVAL_SUBDEVICE_ID, &PciSubId)
		)
	{
		Success = FALSE;
		goto Exit;
	}

	switch(PciId)
	{
	default:
		DebugMessage1("Invalid Board ID: %d\n", PciId);
		Success = FALSE;
		goto Exit;

	// Currently defined but unsupported types
	case DIGI_PCI_DEVICEID_SYNCPORT:
	case DIGI_PCI_DEVICEID_PRIME:
	case DIGI_PCI_DEVICEID_CLYDE_NON_CSU_603_4:
	case DIGI_PCI_DEVICEID_CLYDE_NON_CSU_603_2:
	case DIGI_PCI_DEVICEID_CLYDE_NON_CSU_4:
	case DIGI_PCI_DEVICEID_CLYDE_NON_CSU_2:
		DebugMessage1("Unsupported Board ID: %d\n", PciId);
		Success = FALSE;
		goto Exit;

	case IBM_PCI_DEVICEID_LONDON:
	case DIGI_PCI_DEVICEID_LONDON:
		{
			switch(PciSubId)
			{
			default:
				DebugMessage1("Unsupported sub ID: %d\n", PciSubId);
				Success = FALSE;
				goto Exit;

			case	IBM_PCI_SUBDEVICEID_LONDON_ST:
			case	DIGI_PCI_SUBDEVICEID_LONDON_ST:
				Type = ADAPTER_LON_ST;

				Ports = 4;
				Channels = 2 * Ports;
				break;

			case	IBM_PCI_SUBDEVICEID_LONDON_U:
			case	DIGI_PCI_SUBDEVICEID_LONDON_U:
				Type = ADAPTER_LON_U;
				Ports = 4;
				Channels = 2 * Ports;
				break;
			}
		}
		break;

	case IBM_PCI_DEVICEID_CHASKA:
	case DIGI_PCI_DEVICEID_CHASKA:
		{
			switch(PciSubId)
			{
			default:
				DebugMessage1("Unsupported sub ID: %d\n", PciSubId);
				Success = FALSE;
				goto Exit;

			case	IBM_PCI_SUBDEVICEID_CHASKA_40:
			case	DIGI_PCI_SUBDEVICEID_CHASKA_40:
				Type = ADAPTER_CHASKA40;
				Ports = 4;
				break;
			case	IBM_PCI_SUBDEVICEID_CHASKA_41:
			case	DIGI_PCI_SUBDEVICEID_CHASKA_41:
				Type = ADAPTER_CHASKA41;
				Ports = 4;
				break;
			case	IBM_PCI_SUBDEVICEID_CHASKA_42:
			case	DIGI_PCI_SUBDEVICEID_CHASKA_42:
				Type = ADAPTER_CHASKA42;
				Ports = 4;
				break;
			case	IBM_PCI_SUBDEVICEID_CHASKA_43:
			case	DIGI_PCI_SUBDEVICEID_CHASKA_43:
				Type = ADAPTER_CHASKA43;
				Ports = 4;
				break;
			case	IBM_PCI_SUBDEVICEID_CHASKA_44:
			case	DIGI_PCI_SUBDEVICEID_CHASKA_44:
				Type = ADAPTER_CHASKA44;
				Ports = 4;
				break;
			case	IBM_PCI_SUBDEVICEID_CHASKA_45:
			case	DIGI_PCI_SUBDEVICEID_CHASKA_45:
				Type = ADAPTER_CHASKA45;
				Ports = 4;
				break;

			case	IBM_PCI_SUBDEVICEID_CHASKA_80:
			case	DIGI_PCI_SUBDEVICEID_CHASKA_80:
				Type = ADAPTER_CHASKA80;
				Ports = 8;
				break;
			case	IBM_PCI_SUBDEVICEID_CHASKA_81:
			case	DIGI_PCI_SUBDEVICEID_CHASKA_81:
				Type = ADAPTER_CHASKA81;
				Ports = 8;
				break;
			case	IBM_PCI_SUBDEVICEID_CHASKA_82:
			case	DIGI_PCI_SUBDEVICEID_CHASKA_82:
				Type = ADAPTER_CHASKA82;
				Ports = 8;
				break;
			case	IBM_PCI_SUBDEVICEID_CHASKA_83:
			case	DIGI_PCI_SUBDEVICEID_CHASKA_83:
				Type = ADAPTER_CHASKA83;
				Ports = 8;
				break;
			case	IBM_PCI_SUBDEVICEID_CHASKA_84:
			case	DIGI_PCI_SUBDEVICEID_CHASKA_84:
				Type = ADAPTER_CHASKA84;
				Ports = 8;
				break;
			case	IBM_PCI_SUBDEVICEID_CHASKA_85:
			case	DIGI_PCI_SUBDEVICEID_CHASKA_85:
				Type = ADAPTER_CHASKA85;
				Ports = 8;
				break;
			}
			// For Chaska the channels are equal to the number of ports
			Channels = Ports;
		}
		break;

	case IBM_PCI_DEVICEID_HOUSTON:
	case 0x2A:	// NOTE: NOTE: Allowing old Houston PCI ID
	case DIGI_PCI_DEVICEID_HOUSTON:
		{
			switch(PciSubId)
			{
			default:
				DebugMessage1("Unsupported sub ID: %d\n", PciSubId);
				Success = FALSE;
				goto Exit;

			case	IBM_PCI_SUBDEVICEID_HOUSTON_T1_SINGLE:
			case	DIGI_PCI_SUBDEVICEID_HOUSTON_T1_SINGLE:
				Ports = 1;
				Channels = 24 * Ports;
				Type = ADAPTER_HOU24;
				break;

			case	IBM_PCI_SUBDEVICEID_HOUSTON_T1_DUAL:
			case	DIGI_PCI_SUBDEVICEID_HOUSTON_T1_DUAL:
				Ports = 2;
				Channels = 24 * Ports;
				Type = ADAPTER_HOU48;
				break;

			case	IBM_PCI_SUBDEVICEID_HOUSTON_E1_SINGLE:
			case	DIGI_PCI_SUBDEVICEID_HOUSTON_E1_SINGLE:
				Ports = 1;
				Channels = 30 * Ports;
				Type = ADAPTER_HOU30;
				break;

			case	IBM_PCI_SUBDEVICEID_HOUSTON_E1_DUAL:
			case	DIGI_PCI_SUBDEVICEID_HOUSTON_E1_DUAL:
				Ports = 2;
				Channels = 30 * Ports;
				Type = ADAPTER_HOU60;
				break;
			}
		}
		break;

	// NOTE: NOTE: These values are from pci_ids.h NOT dxbreg.h
	case DIGI_CPCIHOUSTON_DID:
		{
			switch(PciSubId)
			{
			default:
				DebugMessage1("Unsupported sub ID: %d\n", PciSubId);
				Success = FALSE;
				goto Exit;

			case	DIGI_CPCIHOUSTON24_SSID:
				Ports = 1;
				Channels = 24 * Ports;
				Type = ADAPTER_HOU24cpci;
				break;

			case	DIGI_CPCIHOUSTON48_SSID:
				Ports = 2;
				Channels = 24 * Ports;
				Type = ADAPTER_HOU48cpci;
				break;

			case	DIGI_CPCIHOUSTON30_SSID:
				Ports = 1;
				Channels = 30 * Ports;
				Type = ADAPTER_HOU30cpci;
				break;

			case	DIGI_CPCIHOUSTON60_SSID:
				Ports = 2;
				Channels = 30 * Ports;
				Type = ADAPTER_HOU60cpci;
				break;
			}
		}
		break;

	case DIGI_PCI_DEVICEID_ACCELEPORT_2000:
		{
			switch(PciSubId)
			{
			default:
				DebugMessage1("Unsupported sub ID: %d\n", PciSubId);
				Success = FALSE;
				goto Exit;

			case	DIGI_PCI_SUBDEVICEID_ACCELEPORT_2R_2000:
				Type = ADAPTER_AP2K2;
				Ports = 1;
				Channels = 2;
				break;

			case	DIGI_PCI_SUBDEVICEID_ACCELEPORT_4R_2000:
				Type = ADAPTER_AP2K4;
				Ports = 1;
				Channels = 4;
				break;

			case	DIGI_PCI_SUBDEVICEID_ACCELEPORT_8R_2000:
				Type = ADAPTER_AP2K8;
				Ports = 1;
				Channels = 8;
				break;

			case	DIGI_PCI_SUBDEVICEID_ACCELEPORT_16R_2000:
				Type = ADAPTER_AP2K16;
				Ports = 1;
				Channels = 16;
				break;
			}
		}
		break;

	case STRATUS_PCI_DEVICEID_ACCELEPORT_2000:
		{
			switch(PciSubId)
			{
			default:
				DebugMessage1("Unsupported sub ID: %d\n", PciSubId);
				Success = FALSE;
				goto Exit;

			case	STRATUS_PCI_SUBDEVICEID_ACCELEPORT_8R_2000:
				Type = ADAPTER_AP2K8;
				Ports = 1;
				Channels = 8;
				break;
			}
		}
		break;
	}

	// pMgmtBoardInfo->BoardNo = BoardNumber;	// This will be corrected when sorted
	pMgmtBoardInfo->BrdId = MAKELONG(Slot, Bus);				// (PCI Bus number << 16) | (PCI Slot number)
	pMgmtBoardInfo->BoardType = Type;		// FEP Style board type
	pMgmtBoardInfo->TotalChannels = Channels;
	pMgmtBoardInfo->TotalPorts = Ports;

	Success = TRUE;
Exit:
	if (NULL != hkParamsKey)
		RegCloseKey(hkParamsKey);
	return Success;
}
#endif // #if !defined NT5

#if defined NT5
static BOOL
IsDenseHwId(TCHAR * szId)
{
   BOOL Result = FALSE;

#if defined(ACCELEPORT_RAS_BUILD) || defined(DATAFIRE_RAS_BUILD)
   DWORD index;

   TCHAR * Ids[] =
   {
      WANLINKS_ID_LIST
   };

   for (index = 0; index < ElementCount(Ids); index++)
   {
      if (0 == strcmp(szId, Ids[index]))
      {
         Result = TRUE;
         break;
      }
   }
#endif

   return Result;
}

static BOOL
IsAxpHwId(TCHAR * szId)
{
   BOOL Result = FALSE;

#if defined(ACCELEPORT_XP_BUILD)
   DWORD index;

   TCHAR * Ids[] =
   {
      WANLINKS_ID_LIST
   };

   for (index = 0; index < ElementCount(Ids); index++)
   {
      if (0 == strcmp(szId, Ids[index]))
      {
         Result = TRUE;
         break;
      }
   }
#endif

   return Result;
}

inline BOOL
GetPersonality(TCHAR * const szHwId, // IN
               DWORD * const nPciId,    // OUT
               DWORD * const nPciSubId, // OUT
               DWORD * const nPorts,    // OUT
               DWORD * const nChans,    // OUT
               DWORD * const nType)     // OUT
{
   // This is the all knowing adapter data lookup table for adapters supported by the MGMT DLL
   static const struct
   {
      TCHAR * szHwId;
      DWORD nPciId;
      DWORD nPciSubId;
      DWORD nPorts;
      DWORD nChans;
      DWORD nType;
   }
   aAdapterData[] =
   {
      //PNP HW ID        PCI ID                            PCI Sub ID                                P  Ch  Type
#if defined(DATAFIRE_RAS_BUILD)
#if defined(DIGI_BUILD)
       // Digi DataFire RAS PTE (Houston, T1, E1)
      { PNPHWID_DFR_30,  DIGI_PCI_DEVICEID_HOUSTON,        DIGI_PCI_SUBDEVICEID_HOUSTON_E1_SINGLE,   1, 30, ADAPTER_HOU30},
      { PNPHWID_DFR_60,  DIGI_PCI_DEVICEID_HOUSTON,        DIGI_PCI_SUBDEVICEID_HOUSTON_E1_DUAL,     2, 60, ADAPTER_HOU60},
      { PNPHWID_DFR_24,  DIGI_PCI_DEVICEID_HOUSTON,        DIGI_PCI_SUBDEVICEID_HOUSTON_T1_SINGLE,   1, 24, ADAPTER_HOU24},
      { PNPHWID_DFR_48,  DIGI_PCI_DEVICEID_HOUSTON,        DIGI_PCI_SUBDEVICEID_HOUSTON_T1_DUAL,     2, 48, ADAPTER_HOU48},
      { PNPHWID_DFR_30c, DIGI_CPCI_DEVICEID_HOUSTON,       DIGI_CPCIHOUSTON30_SSID,                  1, 30, ADAPTER_HOU30cpci},
      { PNPHWID_DFR_60c, DIGI_CPCI_DEVICEID_HOUSTON,       DIGI_CPCIHOUSTON60_SSID,                  2, 60, ADAPTER_HOU60cpci},
      { PNPHWID_DFR_24c, DIGI_CPCI_DEVICEID_HOUSTON,       DIGI_CPCIHOUSTON24_SSID,                  1, 24, ADAPTER_HOU24cpci},
      { PNPHWID_DFR_48c, DIGI_CPCI_DEVICEID_HOUSTON,       DIGI_CPCIHOUSTON48_SSID,                  2, 48, ADAPTER_HOU48cpci},

      // Digi DataFire RAS 4BST (London, BRI)
      { PNPHWID_DFR_4BU, DIGI_PCI_DEVICEID_LONDON,         DIGI_PCI_SUBDEVICEID_LONDON_U,            4,  8, ADAPTER_LON_U},
      { PNPHWID_DFR_4BST,DIGI_PCI_DEVICEID_LONDON,         DIGI_PCI_SUBDEVICEID_LONDON_ST,           4,  8, ADAPTER_LON_ST},
#endif // if defined(DIGI_BUILD)
#if defined(PATTON_BUILD)
      // Patton DataFire RAS PTE (Houston, T1, E1)
      { PNPHWID_PA_DFR_30,  DIGI_PCI_DEVICEID_HOUSTON,        DIGI_PCI_SUBDEVICEID_HOUSTON_E1_SINGLE,   1, 30, ADAPTER_HOU30},
      { PNPHWID_PA_DFR_60,  DIGI_PCI_DEVICEID_HOUSTON,        DIGI_PCI_SUBDEVICEID_HOUSTON_E1_DUAL,     2, 60, ADAPTER_HOU60},
      { PNPHWID_PA_DFR_24,  DIGI_PCI_DEVICEID_HOUSTON,        DIGI_PCI_SUBDEVICEID_HOUSTON_T1_SINGLE,   1, 24, ADAPTER_HOU24},
      { PNPHWID_PA_DFR_48,  DIGI_PCI_DEVICEID_HOUSTON,        DIGI_PCI_SUBDEVICEID_HOUSTON_T1_DUAL,     2, 48, ADAPTER_HOU48},
      { PNPHWID_PA_DFR_30c, DIGI_CPCI_DEVICEID_HOUSTON,       DIGI_CPCIHOUSTON30_SSID,                  1, 30, ADAPTER_HOU30cpci},
      { PNPHWID_PA_DFR_60c, DIGI_CPCI_DEVICEID_HOUSTON,       DIGI_CPCIHOUSTON60_SSID,                  2, 60, ADAPTER_HOU60cpci},
      { PNPHWID_PA_DFR_24c, DIGI_CPCI_DEVICEID_HOUSTON,       DIGI_CPCIHOUSTON24_SSID,                  1, 24, ADAPTER_HOU24cpci},
      { PNPHWID_PA_DFR_48c, DIGI_CPCI_DEVICEID_HOUSTON,       DIGI_CPCIHOUSTON48_SSID,                  2, 48, ADAPTER_HOU48cpci},

      // Patton DataFire RAS 4BST (London, BRI)
      { PNPHWID_PA_DFR_4BU, DIGI_PCI_DEVICEID_LONDON,         DIGI_PCI_SUBDEVICEID_LONDON_U,            4,  8, ADAPTER_LON_U},
      { PNPHWID_PA_DFR_4BST,DIGI_PCI_DEVICEID_LONDON,         DIGI_PCI_SUBDEVICEID_LONDON_ST,           4,  8, ADAPTER_LON_ST},
#endif // if defined(PATTON_BUILD)
#endif // if defined(DATAFIRE_RAS_BUILD)

#if defined(ACCELEPORT_RAS_BUILD)
      // Digi Acceleport RAS (Chaska, POTS)
      { PNPHWID_AR_8_0,  DIGI_PCI_DEVICEID_CHASKA,         DIGI_PCI_SUBDEVICEID_CHASKA_80,           8,  8, ADAPTER_CHASKA80},
      { PNPHWID_AR_4_0,  DIGI_PCI_DEVICEID_CHASKA,         DIGI_PCI_SUBDEVICEID_CHASKA_40,           4,  4, ADAPTER_CHASKA40},
      { PNPHWID_AR_8_1,  DIGI_PCI_DEVICEID_CHASKA,         DIGI_PCI_SUBDEVICEID_CHASKA_81,           8,  8, ADAPTER_CHASKA81},
      { PNPHWID_AR_4_1,  DIGI_PCI_DEVICEID_CHASKA,         DIGI_PCI_SUBDEVICEID_CHASKA_41,           4,  4, ADAPTER_CHASKA41},
      { PNPHWID_AR_8_2,  DIGI_PCI_DEVICEID_CHASKA,         DIGI_PCI_SUBDEVICEID_CHASKA_82,           8,  8, ADAPTER_CHASKA82},
      { PNPHWID_AR_4_2,  DIGI_PCI_DEVICEID_CHASKA,         DIGI_PCI_SUBDEVICEID_CHASKA_42,           4,  4, ADAPTER_CHASKA42},
      { PNPHWID_AR_8_3,  DIGI_PCI_DEVICEID_CHASKA,         DIGI_PCI_SUBDEVICEID_CHASKA_83,           8,  8, ADAPTER_CHASKA83},
      { PNPHWID_AR_4_3,  DIGI_PCI_DEVICEID_CHASKA,         DIGI_PCI_SUBDEVICEID_CHASKA_43,           4,  4, ADAPTER_CHASKA43},
      { PNPHWID_AR_8_4,  DIGI_PCI_DEVICEID_CHASKA,         DIGI_PCI_SUBDEVICEID_CHASKA_84,           8,  8, ADAPTER_CHASKA84},
      { PNPHWID_AR_4_4,  DIGI_PCI_DEVICEID_CHASKA,         DIGI_PCI_SUBDEVICEID_CHASKA_44,           4,  4, ADAPTER_CHASKA44},
      { PNPHWID_AR_8_5,  DIGI_PCI_DEVICEID_CHASKA,         DIGI_PCI_SUBDEVICEID_CHASKA_85,           8,  8, ADAPTER_CHASKA85},
      { PNPHWID_AR_4_5,  DIGI_PCI_DEVICEID_CHASKA,         DIGI_PCI_SUBDEVICEID_CHASKA_45,           4,  4, ADAPTER_CHASKA45},
#endif

#if defined(ACCELEPORT_XP_BUILD)
      // Digi Acceleport Xp
      { PNPHWID_AXP_2,   DIGI_PCI_DEVICEID_ACCELEPORT_2000,DIGI_PCI_SUBDEVICEID_ACCELEPORT_2R_2000,  2,  2, ADAPTER_AP2K2},
      { PNPHWID_AXP_4,   DIGI_PCI_DEVICEID_ACCELEPORT_2000,DIGI_PCI_SUBDEVICEID_ACCELEPORT_4R_2000,  4,  4, ADAPTER_AP2K4},
      { PNPHWID_AXP_8,   DIGI_PCI_DEVICEID_ACCELEPORT_2000,DIGI_PCI_SUBDEVICEID_ACCELEPORT_8R_2000,  8,  8, ADAPTER_AP2K8},
      { PNPHWID_AXP_16,  DIGI_PCI_DEVICEID_ACCELEPORT_2000,DIGI_PCI_SUBDEVICEID_ACCELEPORT_16R_2000,16, 16, ADAPTER_AP2K16},

      // Stratus AccelePort Xp
      { STRATUS_PNPHWID_AXP_8,   STRATUS_PCI_DEVICEID_ACCELEPORT_2000,STRATUS_PCI_SUBDEVICEID_ACCELEPORT_8R_2000,  8,  8, ADAPTER_AP2K8},
#endif

#if defined(DATAFIRE_SYNC_BUILD)
      { TEXT(""), 0, 0, 0, 0, 0},
#endif

#if defined(TOOLS_BUILD)
      { TEXT(""), 0, 0, 0, 0, 0},
#endif

   };

   DWORD index;
   BOOL Result = FALSE;

   for (index = 0; index < ElementCount(aAdapterData); index++)
   {
      if (0 == strcmp(szHwId, aAdapterData[index].szHwId))
      {
         *nPciId = aAdapterData[index].nPciId;
         *nPciSubId = aAdapterData[index].nPciSubId;
         *nPorts = aAdapterData[index].nPorts;
         *nChans = aAdapterData[index].nChans;
         *nType = aAdapterData[index].nType;
         Result = TRUE;
         break;
      }
   }
   return Result;
}

BOOL
EnumerateSupportedAdapters(GUID * const pGuid, DWORD * const pBoardCount, REG_DESCRIPTOR_T * pRegDescriptors)
{
   TCHAR szDriver[128];
   TCHAR * szDriverInstance;
   TCHAR szHwId[MAX_PATH];
   SP_DEVINFO_DATA spd;
   DWORD dwDriverSize, dwHwIdSize, Result;
   DWORD index = 0;
   HDEVINFO hDeviceInfo = NULL;
   BOOL Success = FALSE;

   hDeviceInfo = SetupDiGetClassDevs(pGuid,NULL,NULL,DIGCF_PROFILE);
   if (hDeviceInfo != INVALID_HANDLE_VALUE)
   {
   	spd.cbSize = sizeof(SP_DEVINFO_DATA);

	   // start the enumeration process for the specified class
      index = 0;
	   while(SetupDiEnumDeviceInfo(hDeviceInfo,index,&spd))
	   {
		   // read the driver parameter for this device and get its instance id
		   if (SetupDiGetDeviceRegistryProperty(  hDeviceInfo,
                                                &spd,
                                                SPDRP_DRIVER,
                                                &dwDriverSize,
                                                (PBYTE)szDriver,
                                                sizeof(szDriver),
                                                NULL))
         {
            szDriverInstance = &szDriver[strlen(szDriver) - sizeof(TEXT("0000")) + sizeof(TCHAR)];
            // Now get the device property we really need, the PCI ID
		      if (SetupDiGetDeviceRegistryProperty(  hDeviceInfo,
                                                   &spd,
                                                   SPDRP_HARDWAREID ,
                                                   &dwHwIdSize,
                                                   (PBYTE)szHwId,
                                                   sizeof(szHwId),
                                                   NULL))
            {
               DWORD nPciId, nPciSubId, nPorts, nChans, nType;

               // HW Id has rev info too... Need to strip this off before the compare
               szHwId[sizeof(PNPHWID) - sizeof(TCHAR)] = NULL;

               // These two groups of PCI IDs are supported, save off the personality
               BOOL IsDense = IsDenseHwId(szHwId);
               BOOL IsAxp = IsAxpHwId(szHwId);
               if (IsDense || IsAxp)
               {
                  Success = TRUE; // Found at least one adapter

                  // DOS Device Name depends on hardware class
                  sprintf(&(regDescriptors[*pBoardCount].DosDeviceName[0]),WANLINKS_DXB_DEVICE_FORMAT_TEXT,szDriverInstance);

                  // Static values derived from a lookup table
                  if (GetPersonality(szHwId,&nPciId,&nPciSubId,&nPorts,&nChans,&nType))
                  {
                     regDescriptors[*pBoardCount].BoardInfo.BoardType = nType;
                     regDescriptors[*pBoardCount].BoardInfo.TotalChannels = nChans;
                     regDescriptors[*pBoardCount].BoardInfo.TotalPorts = nPorts;
                     regDescriptors[*pBoardCount].BoardInfo.BrdId = MAKELONG(nPciId,nPciSubId);
                  }

                  // Registry path is dynamic, config grabbed it already, save it off so we don't have to do this again
                  HKEY hkParamsKey = NULL;
		            HKEY hRootKey =  SetupDiOpenDevRegKey( hDeviceInfo,
                                                         &spd,
                                                         DICS_FLAG_GLOBAL,
                                                         0,
                                                         DIREG_DRV,
                                                         KEY_ALL_ACCESS);

		            if (hRootKey != (HKEY)INVALID_HANDLE_VALUE)
		            {
		               Result = RegOpenKeyEx(	hRootKey,
										               DXB_REGKEY_PARAMETERS,
										               NULL,
										               KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE,
										               &hkParamsKey);
		               if (ERROR_SUCCESS == Result)
		               {
      	               GetRegSzValue(hkParamsKey, DXB_REGVAL_REGPATH, &(regDescriptors[*pBoardCount].RegPath[0]), sizeof regDescriptors[*pBoardCount].RegPath /*MAX_PARAMS_KEY_NAME*/ );
			               RegCloseKey(hkParamsKey);
		               }
                     RegCloseKey(hRootKey);
                  }
                  *pBoardCount += 1; // Found another
               }
            }
         }
		   ++index;
	   }
   }
   return Success;
}
#endif // #if defined NT5

BOOL
regEnumerateBoards(DWORD * const pnBoards)
{
#if defined NT5
#ifndef MPSCLASS
#define MPSCLASS	TEXT("MultiPortSerial")
#endif
#ifndef MODEMCLASS
#define MODEMCLASS	TEXT("Modem")
#endif
#ifndef NETCLASS
#define NETCLASS	TEXT("Net")
#endif
#ifndef PORTCLASS
#define PORTCLASS	TEXT("Ports")
#endif

	HKEY hkNetworkCards = NULL;

   if (gfInitialized)
		Cleanup();			// Allow re-init

   {
      // We need to use setup APIs to find the adapters...
      GUID guid;
		DWORD dwGuidSize;
   	gnBoards = 0;

      // get the GUID for the NET class device
	   if (SetupDiClassGuidsFromName(NETCLASS,&guid,1,&dwGuidSize))
         EnumerateSupportedAdapters(&guid, &gnBoards, &regDescriptors[0]);

      // get the GUID for the MPS class device
	   if (SetupDiClassGuidsFromName(MPSCLASS,&guid,1,&dwGuidSize))
         EnumerateSupportedAdapters(&guid, &gnBoards, &regDescriptors[0]);
   }

#else // #if defined NT5
	HKEY hkNetworkCards = NULL;
	LONG Result;
	DWORD	cSubKeys;		// How many keys we need to interrogate
	DWORD	SubKeyIndex;	// Loop iterator
	DWORD BoardCount;			// Loop iterator

	if (gfInitialized)
		Cleanup();			// Allow re-init

	if (!GetNetworkCardsKey(&hkNetworkCards, &cSubKeys))
		goto Exit;

	// Determine which of these are DXB Instances, ignore the rest
	// Fill in the DOS Device Name in the regDescriptors array
	gnBoards = 0;
	for (SubKeyIndex = 0; SubKeyIndex < cSubKeys; SubKeyIndex++)
	{
		TCHAR	pSubKeyName[MAX_PATH];
		DWORD	cbSubKeyNameMaxLen = MAX_PATH;
		HKEY hkCardKey = NULL;
		BOOL fSuccess;

		Result = RegEnumKey(	hkNetworkCards,
									SubKeyIndex,
									&pSubKeyName[0],
									cbSubKeyNameMaxLen);
		if (ERROR_SUCCESS != Result)
		{
			DebugMessage1("Couldn't enumerate Network Card key info, error:0x%x\n", Result);
			goto Exit;
		}

		Result = RegOpenKeyEx(	hkNetworkCards,
										&pSubKeyName[0],
										NULL,
										KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE,
										&hkCardKey);
		if (ERROR_SUCCESS != Result)
		{
			DebugMessage2("Couldn't open key %s\nError:0x%x\n", &pSubKeyName[0], Result);
			goto Exit;
		}
		if(	!CheckRegSzValue(hkCardKey, MFG_NAME, DGVER_COMPANY_NAME_NICK_TEXT) ||
				!CheckRegSzValue(hkCardKey, PROD_NAME, WANLINKS_DXB_NAME_TEXT)
			)
		{
			RegCloseKey(hkCardKey);	// Not a DXB board instance
			continue;
		}

		// Put it into the target buffer...
		fSuccess = GetRegSzValue(hkCardKey, "ServiceName", &(regDescriptors[gnBoards].DosDeviceName[0]), MAX_DOS_DEVICE_NAME );

		(void) RegCloseKey(hkCardKey);

		if (!fSuccess)
			goto Exit;

		// Found another...
		gnBoards++;
	}
#endif // #if defined NT5

	if (0 == gnBoards)
	{
		DebugMessage0("Couldn't find any boards\n");
		goto Exit;
	}

#if !defined NT5
   // board info is derived when the board is found for W2K
   for (BoardCount = 0; BoardCount < gnBoards; BoardCount++)
	{
		(void)GetBoardInfo(BoardCount, &regDescriptors[BoardCount].BoardInfo);
	}
#endif // #if defined NT5

	// Generate a sorted table of indexes and board numbers
	if (!MakeLookupList())
		goto Exit;

	gfInitialized = TRUE;
Exit:
	if (!gfInitialized)
		Cleanup();

	if (NULL != hkNetworkCards)
		RegCloseKey(hkNetworkCards);

	*pnBoards = gnBoards;	// We're sure now

	return gfInitialized;
}

BOOL
regGetDosDeviceName(DWORD const nBoard,
						  TCHAR * const pOutBuffer,
						  DWORD const cbBufferMax)
{
	BOOL Success = FALSE;
	TCHAR * pName;
	DWORD_PTR cbName;

	if (!VerifyBoardNo(nBoard))
		goto Exit;

	// Perform the 'lookup'
	pName = &(regDescriptors[regIndexLookup[nBoard-1]].DosDeviceName[0]);
	cbName = strlen(pName);
	if (cbName > cbBufferMax)
	{
		DebugMessage2("Name too large in regGetDosDeviceName(), have: %d, need: %d\n", cbBufferMax, cbName);
		goto Exit;
	}
	// Fill the output param
	memcpy(pOutBuffer, pName, cbName);
	pOutBuffer[cbName] = 0;	// NULL term
	DebugMessage2("DOS Device Name: %s, BoardNo: %d \n", &pOutBuffer[0], nBoard);
	Success = TRUE;
Exit:
	return Success;
}

BOOL
regGetComName(uint32	const BoardNumber,
				  uint32 const PortNumber,
				  uint32 const ChannelNumber,
				  char * const szComName,
				  size_t const cbComName)
{
	BOOL Success = FALSE;
	DWORD Result, regDescriptorIndex;
	char szName[MAX_PATH] = {0};
	HKEY hkReturn = NULL;

	if (!VerifyBoardNo(BoardNumber))
		goto Exit;

	regDescriptorIndex = regIndexLookup[BoardNumber-1];	// Perform the lookup
	if (!VerifyRegDescriptorIndex(regDescriptorIndex))
		goto Exit;

	// Should look like: ....\DigiDxbX\Parameters\ASYNC\Y\Z
	// Where X = instance, Y = Port, Z = Channel
	BUILD_ASYNC_CHAN_KEY_NAME(regDescriptorIndex, PortNumber, ChannelNumber, szName);

	Result = RegOpenKeyEx(	HKEY_LOCAL_MACHINE,
									&szName[0],
									NULL,
									KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE | KEY_WRITE,
									&hkReturn);
	if (ERROR_SUCCESS != Result)
	{
		DebugMessage2("Couldn't open key:%s, returned error:0x%x\n", szName, Result);
		goto Exit;
	}

#if defined NT5
   // Get the ComNumber and format a string...
#define DIGI_DXB_COM_NAME_FORMAT TEXT("COM%d")
   DWORD ComNumber;
   if (!GetRegDwordValue(hkReturn,DXB_REGVAL_COM_NUMBER,&ComNumber))
   {
		DebugMessage0("Couldn't get comm value\n");
		goto Exit;
   }
   sprintf(&szName[0],DIGI_DXB_COM_NAME_FORMAT,ComNumber);
#else // #if defined NT5

   // Done with szName, re-use it here
	if (!GetRegSzValue(hkReturn, DXB_REGVAL_ASYN_DOSDEVICES, &szName[0], sizeof szName ))
	{
		DebugMessage1("Couldn't get comm name value, returned error:0x%x\n", Result);
		goto Exit;
	}
#endif // #if defined NT5

	if (strlen(&szName[0]) > cbComName)
	{
		DebugMessage0("ComName is too large for provided buffer\n");
		goto Exit;
	}

	strcpy(&szComName[0], szName);

	Success = TRUE;
Exit:
	return Success;
}

#if !defined NT5
#define VALIDATE_AND_SET_DWORD_VALUE(hk,name,value) {if (0 != value){if(!SetRegDwordValue(hk, name, value))goto Exit;}}

BOOL
regSerialize(uint32	BoardNumber,	/* 1 to total boards or ignored if not applicable */
				 uint16	Command,			/* obvious */
				 void		*mpkt,			/* msg packet.  This is the data portion of the ioctl. */
				 uint16	mpktlen,			/* need to know its length */
				 void		*rpkt,			/* response packet.  This is the data portion of the ioctl response */
				 uint16	rpktlen)			/* I hate access violations. */
{
	uint32 port = 0;
	uint32 channel = 0;
	BOOL Success = FALSE;
	HKEY hkParam = NULL;
	DWORD regDescriptorIndex;


	// All values that are serialized must have a board number
	if (!VerifyBoardNo(BoardNumber))
	{
		DebugMessage1("Invalid attempt to serialize for board number:%d\n", BoardNumber);
	}

	regDescriptorIndex = regIndexLookup[BoardNumber-1];


	// The shim has full knowledge of the following serialized data
	switch(Command)
	{
	default:
		DebugMessage1("Serialize attempt for unsupported command:0x%x\n", Command);
		break;

	case SET_MODEM_NATIONALITY:
	{
		// NOTE: NOTE: There is only one 32 bit parameter for this command...
		uint32 * pCountryCode = &((set_modem_nationality_request_t *)mpkt)->country_code;
		port = 0; // No port for this command
		channel = 0;	// No channel for this command

		if (sizeof (uint32) != mpktlen) {
			DebugMessage2("Bad message packet size:%d, expected:%d\n", mpktlen, sizeof (uint32) );
			goto Exit;
		}

	 	if (!OpenAdapterKey(	regDescriptorIndex, port, channel, DXB_REGVAL_MODEM_NATIONALITY, &hkParam)) {
			DebugMessage3("Couldn't open registry key: %s for board:%d, port:%d\n", DXB_REGVAL_MODEM_NATIONALITY, BoardNumber, port);
			goto Exit;
		}

		// Complete 8/27
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_MODEM_NATIONALITY, *pCountryCode);

		Success = TRUE;
		break;
	}
	case SET_DS1_CONFIG:
	{
		ds1_config_t * pds1_config = &((set_ds1_config_request_t *)mpkt)->config;
		port = ((set_ds1_config_request_t *)mpkt)->port;
		channel = 0;	// No channel for this command

		if (sizeof (set_ds1_config_request_t) != mpktlen) {
			DebugMessage2("Bad message packet size:%d, expected:%d\n", mpktlen, sizeof (set_ds1_config_request_t) );
			goto Exit;
		}

	 	if (!OpenAdapterKey(	regDescriptorIndex, port, channel, DXB_REGKEY_DS1, &hkParam)) {
			DebugMessage3("Couldn't open registry key: %s for board:%d, port:%d\n", DXB_REGKEY_DS1, BoardNumber, port);
			goto Exit;
		}

		// Complete 8/27
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_CODING, pds1_config->coding);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_FRAMING, pds1_config->framing);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_IF_TYPE, pds1_config->if_type);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_LONG_HAUL_LBO, pds1_config->long_haul_lbo);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_SHORT_HAUL_LEN, pds1_config->short_haul_len);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_SIGNALLING, pds1_config->signalling);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_WINK_DIGITS, pds1_config->wink_digits);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_CLOCKING, pds1_config->clocking);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_JITTER_ATTEN, pds1_config->jitter_atten);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_EQ_GAIN_LIMIT, pds1_config->eq_gain_limit);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_LEASED_MASK, pds1_config->leased_mask);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_NFAS_MASTER_PORT, pds1_config->nfas_master_port);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DS1_NFAS_INTERFACE_ID, pds1_config->nfas_interface_id);

		Success = TRUE;
		break;
	}

	case SET_BRI_CONFIG:
	{
		bri_config_t * pbri_config = &((set_bri_config_request_t *)mpkt)->config;
		port = ((set_bri_config_request_t *)mpkt)->port;
		channel = 0;	// not needed for this command

		if (sizeof (set_bri_config_request_t) != mpktlen) {
			DebugMessage2("Bad message packet size:%d, expected:%d\n", mpktlen, sizeof (set_bri_config_request_t) );
			goto Exit;
		}

		if (!OpenAdapterKey(	regDescriptorIndex, port, channel, DXB_REGKEY_BRI, &hkParam)) {
			DebugMessage3("Couldn't open registry key: %s for board:%d, port:%d\n", DXB_REGKEY_BRI, BoardNumber, port);
			goto Exit;
		}

		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_BRI_SWITCH_TYPE, pbri_config->switch_type);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_BRI_AUTO_SPID, pbri_config->auto_spid);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_BRI_LEASED_MASK, pbri_config->leased_mask);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_BRI_DIR_COUNT, pbri_config->dir_count);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_BRI_MSN, pbri_config->msn);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_BRI_AUTOALERT, pbri_config->autoalert);

		DebugMessage0("NOTE: NOTE: Serialization SET_BRI_CONFIG not fully implemented! \"directory\" member not serialized\n");
		Success = TRUE;
		break;
	}

	case SET_SERIAL_CONFIG:
		DebugMessage0("Warning: Serialization of SET_SERIAL_CONFIG not implemented!\n");
		Success = TRUE;
		break;

	case SET_DCHAN_EXT_CONFIG:
	{
		dchan_ext_config_t * pdchan_ext_config = &((set_dchan_ext_config_request_t *)mpkt)->config;
		port = ((set_dchan_ext_config_request_t *)mpkt)->port;
		if (sizeof (set_dchan_ext_config_request_t) != mpktlen) {
			DebugMessage2("Bad message packet size:%d, expected:%d\n", mpktlen, sizeof (set_dchan_ext_config_request_t) );
			goto Exit;
		}

		if (!OpenAdapterKey(	regDescriptorIndex, port, channel, DXB_REGKEY_DCHAN, &hkParam)) {
			DebugMessage3("Couldn't open registry key: %s for board:%d, port:%d\n", DXB_REGKEY_DCHAN, BoardNumber, port);
			goto Exit;
		}
		// Complete 8/27
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DCHAN_MODE, pdchan_ext_config->mode);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DCHAN_ADDRESS_TYPE, pdchan_ext_config->address_type);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DCHAN_NUMBERING_PLAN, pdchan_ext_config->numbering_plan);
		VALIDATE_AND_SET_DWORD_VALUE(hkParam, DXB_REGVAL_DCHAN_SUBADDRESS_TYPE, pdchan_ext_config->subaddress_type);

		Success = TRUE;
		break;
	}

	}

Exit:
	if (NULL != hkParam)
		RegCloseKey(hkParam);

	return Success;
}
#endif // #if !defined NT5

BOOL
regGetBoardInfo(DWORD const BoardNumber,
					 void * const pBuf)
{
	BOOL	Success = FALSE;
	DWORD regDescriptorIndex;
	mgmt_board_info_t * pMgmtBoardInfo = (mgmt_board_info_t *)pBuf;

	if (!VerifyBoardNo(BoardNumber))
	{
		goto Exit;
	}

	regDescriptorIndex = regIndexLookup[BoardNumber-1];	// Perform the lookup
	pMgmtBoardInfo->BoardNo = regDescriptors[regDescriptorIndex].BoardInfo.BoardNo;
	pMgmtBoardInfo->BrdId = regDescriptors[regDescriptorIndex].BoardInfo.BrdId;
	pMgmtBoardInfo->BoardType = regDescriptors[regDescriptorIndex].BoardInfo.BoardType;
	pMgmtBoardInfo->TotalChannels = regDescriptors[regDescriptorIndex].BoardInfo.TotalChannels;
	pMgmtBoardInfo->TotalPorts = regDescriptors[regDescriptorIndex].BoardInfo.TotalPorts;

	DebugMessage6("regGetBoardInfo() - Requested Board Number: %d\nBoardNo:%d, BrdId:%d, BoardType:%d, TotalChannels:%d, TotalPorts:%d\n",BoardNumber,pMgmtBoardInfo->BoardNo,pMgmtBoardInfo->BrdId,pMgmtBoardInfo->BoardType,pMgmtBoardInfo->TotalChannels,pMgmtBoardInfo->TotalPorts);

	Success = TRUE;
Exit:
	return Success;
}
