//============================================================================
// Copyright (C) Digi International, Inc. 1998.
//
// File: Upgrade.cpp
//
//
//		Digi specific helper functions for NT 5.0 OS upgrade.  
//	
//
//
// History:
//      V Raman  July-1-1997  Created.
//
//		Ruairi Meyler	11 Feb 98
//		Received from Microsoft.  
//		Renamed to CPP
//		Updated to use Answer File instead of a memory buffer.
//
//============================================================================


#include <windows.h>
#include <setupapi.h>
#include <tchar.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "upgrade.h"
#include "dgupgex.h"

#include "dxbtypes.h"
#include "mgmthlc.h"
#include "dxbreg.h"

extern NetUpgradeAddSectionPrototype       g_pfnNuAddSection;
extern NetUpgradeAddLineToSectionPrototype g_pfnNuAddLineToSection;



//
// New inf Key names
//

static const TCHAR c_ptszIsdnNumDChannels[]	= TEXT( "IsdnNumDChannels" ); 
static const TCHAR c_ptszIsdnSwitchType[]	= TEXT( "IsdnSwitchType" );
static const TCHAR c_ptszIsdnNumBChannels[]	= TEXT( "IsdnNumBChannels" );


//============================================================================
// Values for DIGI Datafire ISDN cards.
//
//
//============================================================================

// the different switch types supported by PRI
#define PRI_NATIONALITY_GERMAN				0x00000001
#define PRI_NATIONALITY_BRITISH				0x00000002
#define PRI_NATIONALITY_FRENCH				0x00000004
#define PRI_NATIONALITY_ETSI				0x00000008
#define PRI_NATIONALITY_ISDN_1				0x00000010
#define PRI_NATIONALITY_ISDN_2				0x00000020
#define PRI_NATIONALITY_ATT_41449			0x00000040
#define PRI_NATIONALITY_DMS_100				0x00000080

typedef struct _tagSwitchMap
{
	DWORD dwSwitchType;
	DWORD dwSwitchIndex;
}SWITCHMAP;

static SWITCHMAP g_SwitchesT1[] = 
{
	{PRI_NATIONALITY_ATT_41449,ISDN_SWITCH_ATT},   // AT&T 5ESS
	{PRI_NATIONALITY_ISDN_2,ISDN_SWITCH_NI2},		 // National ISDN-2
	{PRI_NATIONALITY_DMS_100,ISDN_SWITCH_NTI},	 // DMS-100
	{0,0}
};

static SWITCHMAP g_SwitchesE1[] =
{
	{PRI_NATIONALITY_ETSI,ISDN_SWITCH_DSS1},		// ETSI switch
	{0,0}
};



//
// registry keys for DIGI Datafire PRIME card.
//
#define VENDORSPECIFIC		_T("VendorSpecific")



static const TCHAR c_ptszNumberOfLines[]		= TEXT( "DigiNumPorts" );

static const TCHAR c_ptszLine[]					= TEXT( "Line" );
static const TCHAR c_ptszSwitchStyle[]			= TEXT( "SwitchStyle" );
static const TCHAR c_ptszLogicalTerminals[]		= TEXT( "LogicalTerminals" );
static const TCHAR c_ptszTerminalManagement[]	= TEXT( "TerminalManagement" );

static const TCHAR c_ptszLTerm[]				= TEXT( "LTerm" );
static const TCHAR c_ptszSPID[]					= TEXT( "SPID" );
static const TCHAR c_ptszAddress[]				= TEXT( "Address" );


#ifndef __cplusplus
//----------------------------------------------------------------------------
// Function:    DLLMAIN
//
// This is the DLL entrypoint handler. 
//----------------------------------------------------------------------------

BOOL
WINAPI
DLLMAIN(
    HINSTANCE hInstance,
    DWORD dwReason,
    PVOID pUnused
    ) 
{

    switch(dwReason) {
        case DLL_PROCESS_ATTACH:
        
            DisableThreadLibraryCalls(hInstance);

            break;
 
        case DLL_PROCESS_DETACH:

            break;

        default:

            break;
    }

    return TRUE;
}
#endif

// write data to the SIF file in a certain format
// HKR, Parameters\<Line#=[0,1,..N]>\<subkey=[DS1,DCHAN]>,<value name>,REG_DWORD,<value data>
static void WriteToSIFFile(LPCTSTR pszSection,int nLine,LPCTSTR pszKey,LPCTSTR pszVal,DWORD dwVal)
{
	TCHAR szBuffer[MAX_PATH];
	wsprintf(szBuffer,TEXT( "HKR, %s\\%d\\%s,%s, %#.8lx, %d"),
		DXB_REGKEY_PARAMETERS,nLine,pszKey,pszVal,FLG_ADDREG_TYPE_DWORD,dwVal);
		// write this line to our section within the Answer.sif file
    g_pfnNuAddLineToSection(pszSection, szBuffer);
#ifdef _DEBUG
	OutputDebugString(szBuffer);OutputDebugString(_T("\n"));
#endif
	
}

// 
static BOOL ReadDwordValue(HKEY hkey,LPCTSTR pszValName,DWORD* pdwVal)
{
	DWORD dwSize = sizeof(DWORD),dwType = 0;
	BOOL bSuccess = FALSE;
	if (RegQueryValueEx(hkey,pszValName,NULL,&dwType,(PBYTE)pdwVal,&dwSize) == ERROR_SUCCESS)
		bSuccess = TRUE;
	return bSuccess;
}



static void ShowError(LPCTSTR pszErrMsg)
{
	assert(FALSE);
	OutputDebugString(pszErrMsg);
}


// ISDN values being read
#define ISDN_NATIONALITY	_T("Nationality")
#define ISDN_TYPEOFADDRESS	_T("Type_of_address")
#define ISDN_TYPEOFSUBADDRESS _T("Type_of_sub_address")
#define ISDN_NUMBERINGPLAN	_T("Numbering_plan")
// address type

#define	PRI_ADDR_TYPE_UNKNOWN			0
#define	PRI_ADDR_TYPE_INTERNATIONAL	1
#define	PRI_ADDR_TYPE_NATIONAL			2
#define	PRI_ADDR_TYPE_NETWORK			3
#define	PRI_ADDR_TYPE_SUBSCRIBER		4
#define	PRI_ADDR_TYPE_ABBREVIATED		6

// sub address type

#define PRI_SUBADDR_TYPE_NSAP			0
#define PRI_SUBADDR_TYPE_USER			2	

// numbering plan

#define PRI_NUMBERING_PLAN_UNKNOWN		0
#define PRI_NUMBERING_PLAN_ISDN			1
#define PRI_NUMBERING_PLAN_DATA			3
#define PRI_NUMBERING_PLAN_TELEX		4
#define PRI_NUMBERING_PLAN_NATIONAL		8
#define PRI_NUMBERING_PLAN_PRIVATE		9

static void ProcessISDNKey(LPCTSTR szSectionName,HKEY hLineKey,int nLine)
{	
	
	HKEY hSubKey;
	DWORD dwVal=0;

	if (RegOpenKeyEx(hLineKey,_T("ISDN"),0,KEY_READ,&hSubKey) != ERROR_SUCCESS)
	{
		ShowError(_T("Error opening ISDN key"));
		return;
	}

	if (ReadDwordValue(hSubKey,ISDN_NUMBERINGPLAN,&dwVal))
	{
		switch(dwVal)
		{
		case PRI_NUMBERING_PLAN_UNKNOWN: dwVal = DCHAN_NUMBERING_PLAN_UNKNOWN;break;
		case PRI_NUMBERING_PLAN_ISDN:   dwVal = DCHAN_NUMBERING_PLAN_ISDN;break;
		case PRI_NUMBERING_PLAN_DATA:	dwVal = DCHAN_NUMBERING_PLAN_DATA; break;
		case PRI_NUMBERING_PLAN_TELEX:	dwVal = DCHAN_NUMBERING_PLAN_TELEX;	break;
		case PRI_NUMBERING_PLAN_NATIONAL: dwVal = DCHAN_NUMBERING_PLAN_NATIONAL;break;
		case PRI_NUMBERING_PLAN_PRIVATE:  dwVal = DCHAN_NUMBERING_PLAN_PRIVATE;break;
		}	

		WriteToSIFFile(szSectionName,nLine,DXB_REGKEY_DCHAN,DXB_REGVAL_DCHAN_NUMBERING_PLAN,dwVal);
	}

	
	if (ReadDwordValue(hSubKey,ISDN_TYPEOFADDRESS,&dwVal))
	{
		switch(dwVal)
		{
		case PRI_ADDR_TYPE_UNKNOWN:	dwVal = DCHAN_ADDRESS_TYPE_UNKNOWN;break;
		case PRI_ADDR_TYPE_INTERNATIONAL: dwVal = DCHAN_ADDRESS_TYPE_INTL;break;
		case PRI_ADDR_TYPE_NATIONAL:	dwVal = DCHAN_ADDRESS_TYPE_NATIONAL;break;
		case PRI_ADDR_TYPE_NETWORK:	dwVal = DCHAN_ADDRESS_TYPE_NETWORK;break;
		case PRI_ADDR_TYPE_SUBSCRIBER:	dwVal = DCHAN_ADDRESS_TYPE_SUBSCR;break;
		case PRI_ADDR_TYPE_ABBREVIATED:	dwVal = DCHAN_ADDRESS_TYPE_ABBREV;break;
	
		}
		WriteToSIFFile(szSectionName,nLine,DXB_REGKEY_DCHAN,DXB_REGVAL_DCHAN_ADDRESS_TYPE,dwVal);
	}

	if (ReadDwordValue(hSubKey, ISDN_TYPEOFSUBADDRESS, &dwVal))
	{
		switch(dwVal)
		{
		case PRI_SUBADDR_TYPE_NSAP: dwVal = DCHAN_SUBADDRESS_TYPE_NSAP;break;
		case PRI_SUBADDR_TYPE_USER: dwVal = DCHAN_SUBADDRESS_TYPE_USER;break;
		}
		WriteToSIFFile(szSectionName,nLine,DXB_REGKEY_DCHAN,DXB_REGVAL_DCHAN_SUBADDRESS_TYPE,dwVal);
	}

	// read our switch type value and translate it to NT5 switch value
	if (ReadDwordValue(hSubKey,ISDN_NATIONALITY,&dwVal))
	{
		// search in the T1 & E1 arrays for a matching switch value
		DWORD dwPRISwitch;
		SWITCHMAP* pSwitchMap;
		BOOL bFound = FALSE;
		for(int i=0;i<2 && !bFound;i++)
		{
			if (i == 0)
			{
				pSwitchMap = &g_SwitchesT1[0];
				dwPRISwitch = ISDN_SWITCH_ATT;
			}
			else
			{
				pSwitchMap = &g_SwitchesE1[0];
				dwPRISwitch = ISDN_SWITCH_DSS1;
			}

			do 
			{
				if (pSwitchMap->dwSwitchType == dwVal)
				{
					bFound = TRUE;
					dwPRISwitch = pSwitchMap->dwSwitchIndex;
				}
				pSwitchMap++;
			}while(!bFound && pSwitchMap->dwSwitchType);
		}// for

		if (!bFound)
		{
			ShowError(_T("Switch type value not found \n.Defaults T1=AT&T5ESS, E1=ETSI"));
		}

		TCHAR szBuffer[MAX_PATH];
		wsprintf(szBuffer,TEXT( "HKR, , %s, %#.8lx, %d" ),
			TEXT("IsdnSwitchType"),	FLG_ADDREG_TYPE_DWORD, dwPRISwitch);

		g_pfnNuAddLineToSection(szSectionName, szBuffer);
		#ifdef _DEBUG
		OutputDebugString(szBuffer);OutputDebugString(_T("\n"));
		#endif

	}

	RegCloseKey(hSubKey);

}

// CSUDSU Options
#define CSUDSU_CLKSOURCE	_T("ClockSourceLocal")
#define CSUDSU_MODE			_T("Mode")
#define CSUDSU_OPTIONS		_T("Options")

	// bits 0-1: line build out

#define PRI_CSUDSU_OPTS_LINE_0DB			0x00000000
#define PRI_CSUDSU_OPTS_LINE_7_5DB			0x00000001
#define PRI_CSUDSU_OPTS_LINE_15DB			0x00000002
#define PRI_CSUDSU_OPTS_LINE_22_5DB			0x00000003
#define PRI_CSUDSU_OPTS_LINE_LBO_MASK		0x00000003

	// bit 2: gain

#define PRI_CSUDSU_OPTS_GAIN_HIGH			0x00000004
#define PRI_CSUDSU_OPTS_GAIN_LOW			0x00000000

#define PRI_CSUDSU_OPTS_SHORT_HAUL_DEFAULT	655 // 200 feet (short-haul distance)
#define PRI_CSUDSU_OPTS_LONG_HAUL_DEFAULT	(PRI_CSUDSU_OPTS_LINE_15DB \
										   | PRI_CSUDSU_OPTS_GAIN_LOW)

// short-haul distance ranges

#define PRI_SHORT_DISTANCE_1_133			133
#define PRI_SHORT_DISTANCE_134_266			266
#define PRI_SHORT_DISTANCE_267_399			399
#define PRI_SHORT_DISTANCE_400_533			533
#define PRI_SHORT_DISTANCE_534_655			655

// PRI Mode

	// bits 0-2: Interface mode

#define PRI_MODE_INTERFACE_T1			0x00000000
#define PRI_MODE_INTERFACE_E1			0x00000001
#define PRI_MODE_INTERFACE_MASK			0x00000007

	// bit 3: interface active

#define PRI_MODE_DI_ACTIVE				0x00000008
#define PRI_MODE_DI_INACTIVE			0x00000000
#define PRI_MODE_DI_MASK				0x00000008

	// bits 4-5: clock source

#define PRI_MODE_CLOCK_SOURCE_LINE		0x00000000
#define PRI_MODE_CLOCK_SOURCE_LOCAL		0x00000010
#define PRI_MODE_CLOCK_SOURCE_THROUGH	0x00000020
#define PRI_MODE_CLOCK_SOURCE_MASK		0x00000030

	// bits 6-8: line encoding

#define PRI_MODE_LINE_ENCODING_AMI		0x00000000
#define PRI_MODE_LINE_ENCODING_B8ZS		0x00000040
#define PRI_MODE_LINE_ENCODING_HDB3		0x00000080
#define PRI_MODE_LINE_ENCODING_MASK     0x000001C0

	// bits 9-11: Frame Format

#define PRI_MODE_FRAME_FORMAT_D4		0x00000000
#define PRI_MODE_FRAME_FORMAT_ESF		0x00000200
#define PRI_MODE_FRAME_FORMAT_CEPT		0x00000400
#define PRI_MODE_FRAME_FORMAT_NOCRC		0x00000600
#define PRI_MODE_FRAME_FORMAT_MASK		0x00000E00

	// bit 12: interface type

#define PRI_MODE_IFCE_TYPE_LONG_HAUL	0x00000000
#define PRI_MODE_IFCE_TYPE_SHORT_HAUL	0x00001000
#define PRI_MODE_IFCE_TYPE_HAUL_MASK	0x00001000

	// bits 13-14: jitter attenuator	

#define PRI_MODE_JITTER_ATTEN_AUTO		0x00000000
#define PRI_MODE_JITTER_ATTEN_XMIT		0x00002000
#define PRI_MODE_JITTER_ATTEN_RECV		0x00004000
#define PRI_MODE_JITTER_ATTEN_DISABLE	0x00006000
#define PRI_MODE_JITTER_ATTEN_MASK	    0x00006000

	// bits 15-18: signalling

#define PRI_MODE_SIGNALLING_NONE		0x00000000
#define PRI_MODE_SIGNALLING_CPE_GRND	0x00008000
#define PRI_MODE_SIGNALLING_E_AND_M		0x00018000
#define PRI_MODE_SIGNALLING_MASK        0x00078000

	// (bits 19-31 reserved)

static void ProcessCSUDSUKey(LPCTSTR szSectionName,HKEY hLineKey,int nLine)
{
	HKEY hSubKey;

	if (RegOpenKeyEx(hLineKey,_T("CSUDSU"),0,KEY_READ,&hSubKey) != ERROR_SUCCESS)
	{
		ShowError(_T("Error opening CSUDSU key"));
		return;
	}
	
	DWORD dwMode = 0,dwOptions = 0,dwClkSource = 0;
	if (ReadDwordValue(hSubKey,CSUDSU_CLKSOURCE,&dwClkSource) &&
		ReadDwordValue(hSubKey,CSUDSU_MODE, &dwMode) &&
		ReadDwordValue(hSubKey,CSUDSU_OPTIONS, &dwOptions))
	{
		// the clock source value is stored in the registry as shifted down by 1.
		// so scale it back up to match our masks. The masks could have been changed
		// but then the shift logic would have been out of sight (out of mind)
		dwClkSource <<= 1;
		switch(dwClkSource)
		{
		case PRI_MODE_CLOCK_SOURCE_LINE: dwClkSource = TIMING_LOOP;break;
		case PRI_MODE_CLOCK_SOURCE_LOCAL: dwClkSource = TIMING_LOCAL;break;
		default: dwClkSource = TIMING_LOOP;break;
		}
		
		WriteToSIFFile(szSectionName,nLine,DXB_REGKEY_DS1,DXB_REGVAL_DS1_CLOCKING,dwClkSource);

		BOOL bT1Switch =  (dwMode & PRI_MODE_INTERFACE_E1)?FALSE:TRUE;
		DWORD dwEncoding = 0,dwFraming = 0;
		if (bT1Switch)
		{

			dwEncoding = LINE_CODE_B8ZS;	// always. AMI is ignored
			if (dwMode & PRI_MODE_FRAME_FORMAT_ESF)
				dwFraming = FRAME_ESF;
			else
				dwFraming = FRAME_D4;
		}
		else
		{
			dwEncoding = LINE_CODE_HDB3;

			if (dwMode & PRI_MODE_FRAME_FORMAT_NOCRC)
				dwFraming = FRAME_CEPT_WO_CRC4;
			else
				dwFraming = FRAME_CEPT_CRC4;

		}

		WriteToSIFFile(szSectionName,nLine,DXB_REGKEY_DS1,DXB_REGVAL_DS1_FRAMING,dwFraming);
		WriteToSIFFile(szSectionName,nLine,DXB_REGKEY_DS1,DXB_REGVAL_DS1_CODING,dwEncoding);

		DWORD dwHaulVal = (dwMode & PRI_MODE_IFCE_TYPE_SHORT_HAUL)?SHORT_HAUL:LONG_HAUL;
		WriteToSIFFile(szSectionName,nLine,DXB_REGKEY_DS1,DXB_REGVAL_DS1_IF_TYPE,dwHaulVal);

		if (dwHaulVal == SHORT_HAUL)
		{
			// dwOptions is a short haul line length;
			DWORD dwLength;
			switch(dwOptions)
			{
			case PRI_SHORT_DISTANCE_1_133: dwLength = SH_0_133;break;
			case PRI_SHORT_DISTANCE_134_266: dwLength = SH_133_266;break;
			case PRI_SHORT_DISTANCE_267_399: dwLength = SH_266_399;break;
			case PRI_SHORT_DISTANCE_400_533: dwLength = SH_399_533;break;
			case PRI_SHORT_DISTANCE_534_655: dwLength = SH_533_655;break;
			default: dwLength = SH_0_133;break;
			}
			WriteToSIFFile(szSectionName,nLine,DXB_REGKEY_DS1,DXB_REGVAL_DS1_SHORT_HAUL_LEN,dwLength);
		}
		else
		{
			// dwOptions is a combination of gain & line build out
			DWORD dwGain = (dwOptions & PRI_CSUDSU_OPTS_GAIN_HIGH)?EGL_HIGH:EGL_LOW;
			WriteToSIFFile(szSectionName,nLine,DXB_REGKEY_DS1,DXB_REGVAL_DS1_EQ_GAIN_LIMIT,dwGain);

			DWORD dwLBO;
			DWORD dwPRILBO = dwOptions & PRI_CSUDSU_OPTS_LINE_LBO_MASK;
			switch(dwPRILBO)
			{
			case PRI_CSUDSU_OPTS_LINE_0DB: dwLBO = LBO_00_0_DB;break;
			case PRI_CSUDSU_OPTS_LINE_7_5DB: dwLBO = LBO_07_5_DB;break;
			case PRI_CSUDSU_OPTS_LINE_15DB: dwLBO = LBO_15_0_DB;break;
			case PRI_CSUDSU_OPTS_LINE_22_5DB: dwLBO = LBO_22_5_DB;break;
			default: dwLBO = LBO_15_0_DB;break;
			}
			WriteToSIFFile(szSectionName,nLine,DXB_REGKEY_DS1,DXB_REGVAL_DS1_LONG_HAUL_LBO,dwLBO);

			
		}



		
		DWORD dwJitter = dwMode & PRI_MODE_JITTER_ATTEN_MASK;
		switch(dwJitter)
		{
		case PRI_MODE_JITTER_ATTEN_AUTO: dwJitter = JITTER_AUTO;break;
		case PRI_MODE_JITTER_ATTEN_XMIT: dwJitter = JITTER_TX;break;
		case PRI_MODE_JITTER_ATTEN_RECV: dwJitter = JITTER_RX;break;
		case PRI_MODE_JITTER_ATTEN_DISABLE:
		default: dwJitter = JITTER_NONE;break;
		}

		WriteToSIFFile(szSectionName,nLine,DXB_REGKEY_DS1,DXB_REGVAL_DS1_JITTER_ATTEN,dwJitter);
		
		
		
	}

	RegCloseKey(hSubKey);
}
//----------------------------------------------------------------------------
// Function:        NetWriteDigiIsdnRegistry
//
//  reads the registry layout of DIGI PCIMAC card and write the key 
//  values that need to be updated in the 5.0 setup.
//----------------------------------------------------------------------------

DWORD
NetWriteDigiIsdnRegistry(
    const       HKEY                hKeyInstanceParameters,
    const       LPCTSTR             szPreNT5IndId,
    const       LPCTSTR             szNT5InfId,
    const       LPCTSTR             szSectionName
    )
{

    DWORD       dwInd = 0, dwErr = ERROR_SUCCESS;
    DWORD       dwMaxKeyLen = 0, dwMaxValueLen = 0, dwType = 0, dwSize = 0,
                dwNumSubKeys = 0;

    DWORD       dwValue = 0, dwNumDChannels = 0, dwNumLogTerm = 0;

    TCHAR       szSubKey[ MAX_PATH ];

    

        //
        // Find number of D channels
        //

    dwSize = sizeof( DWORD );

        dwErr = RegQueryValueEx(
                    hKeyInstanceParameters,
                    c_ptszNumberOfLines,
                    NULL,
                    &dwType,
                    (PBYTE) &dwNumDChannels,
                    &dwSize
                    );

        if ( dwErr != ERROR_SUCCESS )
        {
			ShowError(_T("Error reading # of lines.. Exiting\n"));
            return dwErr;
        }

        //
        // for each D channel, read the params, translate to DS1 settings
		// and write them in the answer file

        for ( dwInd = 0; dwInd < dwNumDChannels; dwInd++ )
        {
			           
			// get set to read the subkey where all relevant params are located
			// Open the Port[A|B|...|Z] subkey. each indicates a separate line
            wsprintf( szSubKey, TEXT( "Port%c" ), (char)('A'+dwInd) );
			HKEY hLineKey;
			dwErr = RegOpenKeyEx(hKeyInstanceParameters,szSubKey,0,KEY_READ,&hLineKey);
			if (dwErr != ERROR_SUCCESS)
			{
				TCHAR sztemp[64];
				wsprintf(sztemp,_T("Error opening key %s\n"),szSubKey);
				ShowError(sztemp);
				continue;
			}

			ProcessISDNKey(szSectionName,hLineKey,dwInd);
			ProcessCSUDSUKey(szSectionName,hLineKey,dwInd);
			RegCloseKey(hLineKey);
		}

 
    return dwErr;
}




