/*++
*******************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved				*
* 																										*
* This software contains proprietary and confidential information of Digi		*
* International Inc.  By accepting transfer of this copy, Recipient agrees		*
* to retain this software in confidence, to prevent disclosure to others,		*
* and to make no use of this software other than that for which it was			*
* delivered.  This is an unpublished copyrighted work of Digi International	*
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	*
* prohibited.																						*
* 																										*
* Restricted Rights Legend																		*
*																										*
* Use, duplication, or disclosure by the Government is subject to					*
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in			*
* Technical Data and Computer Software clause at DFARS 252.227-7031 or			*
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -				*
* Restricted Rights at 48 CFR 52.227-19, as applicable.								*
*																										*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343				*
*																										*
*******************************************************************************

Module Name:

   dxbpri.c

Abstract:

   DataFire PRIme Isdn layer initialization routines.

Author:

   Michael Zarns

--*/
#include "digidxb.h"
#pragma hdrstop
#include "isdn_proto.h"
#include "il3_config.h"
#include "lmi.h"
#include "lmi_errno.h"
#include "lmsi.h"
#include "lapd_control.h"
#include "il3_sm.h"
#include "il3_user.h"

//////////////////////////////////////////////////////////////////////////////////////
// Stolen from $/Working Source/PRI/PriConfig/pridefs.h.  Origin unknown!
//
//
// 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 NTSTATUS
dxbPriSetSingle( 
	HANDLE hStream,
	UCHAR  Attribute,
	PVOID  pPrimaryKey,
	ULONG  cbPrimary,
	PVOID  pData,
	ULONG  cbData )
{
	NTSTATUS Status;
	avl_t *pAvl;
	ULONG const cb = (ULONG)( FIXED_ID_SIZE + (2 * FIXED_AVL_SIZE) + dxbAlign32(cbPrimary) + dxbAlign32(cbData) );
	inst_t * const pInstance = DigiAllocMem( NonPagedPool, cb );

	if ( !pInstance )
	{
		Status = STATUS_INSUFFICIENT_RESOURCES;
		goto Exit;
	}

	RtlZeroMemory( pInstance, cb );
	pInstance->i_class = IL3;
	pInstance->count = 2;	// Appending two items: Primary Key and data block
	pAvl = &pInstance->list[0];

	// Append Primary key
	{
		pAvl->att = AVL_OK;
		pAvl->len = (ULONG)dxbAlign32( cbPrimary );
		RtlCopyMemory( &pAvl->val[0], pPrimaryKey, cbPrimary );
		pAvl = (PVOID)( (PUCHAR)pAvl + FIXED_AVL_SIZE + pAvl->len );
		ASSERT( dxbAlign32( (ULONG_PTR)pAvl ) == (ULONG_PTR)pAvl );
	}

	// Append Data block
	{
		pAvl->att = Attribute;
		pAvl->len = (ULONG)dxbAlign32( cbData );
		RtlCopyMemory( &pAvl->val[0], pData, cbData );
		pAvl = (PVOID)( (PUCHAR)pAvl + FIXED_AVL_SIZE + pAvl->len );
		ASSERT( dxbAlign32( (ULONG_PTR)pAvl ) == (ULONG_PTR)pAvl );
	}

	ASSERT( (ULONG)( (ULONG_PTR)pAvl - (ULONG_PTR)pInstance ) == cb );

	// Send down the request
	{
		ULONG Error;
		Status = DsIoctl( hStream, LMSI_SET, pInstance, cb, &Error );
		if ( NT_ERROR( Status ) )
		{
			SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                                  ( "DXB!PriSetSingle: DsIoctl failed, Status 0x%x, Error 0x%x\n", Status, Error ) );
		}
	}

Exit:
	if ( pInstance )
		DigiFreeMem( pInstance );
	return Status;
}


STATIC INLINE NTSTATUS
dxbPriSetSingleUlong(
	HANDLE hStream,
	UCHAR  Attribute,
	UCHAR  PrimaryKey,
	ULONG  Data )
{
	NTSTATUS Status = dxbPriSetSingle( hStream, Attribute, &PrimaryKey, sizeof PrimaryKey, &Data, sizeof Data );
	if ( NT_ERROR( Status ) )
	{
		SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                          ( "DXB!Il3SetSingleUlong (0x%x) failed, status 0x%x\n", PrimaryKey, Status ) );
	}

	return Status;
}


STATIC INLINE NTSTATUS
dxbPriSetSingleUshort(
	HANDLE hStream,
	UCHAR  Attribute,
	UCHAR  PrimaryKey,
	USHORT Data )
{
	NTSTATUS Status = dxbPriSetSingle( hStream, Attribute, &PrimaryKey, sizeof PrimaryKey, &Data, sizeof Data );
	if ( NT_ERROR( Status ) )
	{
		SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                          ( "DXB!Il3SetSingleUshort (0x%x) failed, status 0x%x\n", PrimaryKey, Status ) );
	}

	return Status;
}


STATIC INLINE NTSTATUS
dxbPriSetSingleUchar(
	HANDLE hStream,
	UCHAR  Attribute,
	UCHAR  PrimaryKey,
	UCHAR  Data )
{
	NTSTATUS Status = dxbPriSetSingle( hStream, Attribute, &PrimaryKey, sizeof PrimaryKey, &Data, sizeof Data );
	if ( NT_ERROR( Status ) )
	{
		SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                          ( "DXB!Il3SetSingleUchar (0x%x) failed, status 0x%x\n", PrimaryKey, Status ) );
	}

	return Status;
}


STATIC INLINE NTSTATUS
dxbPriSetSingleTimer(
	HANDLE hStream,
	UCHAR  Attribute,
	PVOID  pPrimary,
	ULONG  cbPrimary,
	ULONG  Data )
{
	NTSTATUS Status = dxbPriSetSingle( hStream, Attribute, pPrimary, cbPrimary, &Data, sizeof Data );
	if ( NT_ERROR( Status ) )
	{
		SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                          ( "DXB!Il3SetSingleTimer (%s) failed, status 0x%x\n", pPrimary, Status ) );
	}

	return Status;
}


NTSTATUS
dxbPriIl3SetSnid( 
	HANDLE hStream,
	ULONG  iPort,
	ULONG  Snid,
	ULONG  Type,
	ULONG  Channel,
	ULONG  MuxId )
{
	NTSTATUS Status;
	il3_snchar_t SetSnid = {0};
	SetSnid.snid        = Snid;
	SetSnid.channeltype = (UCHAR)Type;
	SetSnid.channelno   = (UCHAR)Channel;
	SetSnid.controller  = (UCHAR)( iPort + 1 ) * 3;

	Status = dxbPriSetSingle( hStream, IL3_SNCHAR, &MuxId, sizeof MuxId, &SetSnid, sizeof SetSnid );
	if ( NT_ERROR( Status ) )
	{
		SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                          ( "DXB!Il3SetSnid failed, status 0x%x\n", Status ) );
	}

	return Status;
}


NTSTATUS
dxbPriGetDChanSettings( 
	IN HANDLE hKeyPort,
	OUT PULONG pMode,
	OUT PULONG pAddressType,
	OUT PULONG pNumberingPlan,
	OUT PULONG pSubaddressType )
{
	ULONG Mode;
	ULONG AddressType;
	ULONG NumberingPlan;
	ULONG SubaddressType;
	HANDLE hkDChan;

	NTSTATUS Status = DigiRegOpenSubkey( &hkDChan, hKeyPort, DXB_REGKEY_DCHAN );
	if ( NT_SUCCESS( Status ) )
	{
		Status = DigiRegQueryDword( hkDChan, DXB_REGVAL_DCHAN_MODE, &Mode, DigiRegFlagRequired, 0 );

		if ( NT_SUCCESS( Status ) )
			Status = DigiRegQueryDword( hkDChan, DXB_REGVAL_DCHAN_ADDRESS_TYPE, &AddressType, DigiRegFlagRequired, 0 );

		if ( NT_SUCCESS( Status ) )
			Status = DigiRegQueryDword( hkDChan, DXB_REGVAL_DCHAN_NUMBERING_PLAN, &NumberingPlan, DigiRegFlagRequired, 0 );

		if ( NT_SUCCESS( Status ) )
			Status = DigiRegQueryDword( hkDChan, DXB_REGVAL_DCHAN_SUBADDRESS_TYPE, &SubaddressType, DigiRegFlagRequired, 0 );

		DigiRegCloseKey( hkDChan );
	}

	if ( NT_SUCCESS( Status ) )
	{
		if (pMode)
			*pMode = Mode;
		if (pAddressType)
			*pAddressType = AddressType;
		if (pNumberingPlan)
			*pNumberingPlan = NumberingPlan;
		if (pSubaddressType)
			*pSubaddressType = SubaddressType;
	}
	return Status;
}


NTSTATUS
dxbPriTranslateDChanSettingsToIl3( 
	IN HANDLE hKeyPort,
	OUT PULONG pExtra,
	OUT PULONG pAddressType,
	OUT PULONG pNumberingPlan,
	OUT PULONG pSubaddressType )
{
	ULONG HlcMode, HlcAddressType, HlcNumberingPlan, HlcSubaddressType;
	ULONG PriExtra, PriAddressType, PriNumberingPlan, PriSubaddressType;
	NTSTATUS Status = dxbPriGetDChanSettings( hKeyPort, &HlcMode, &HlcAddressType, &HlcNumberingPlan, &HlcSubaddressType );

	if ( NT_SUCCESS( Status ) )
	{
		switch ( HlcMode )
		{
				case DCHAN_NORMAL:		PriExtra = 0; 					break;
				case DCHAN_LOOPBACK_TE:	PriExtra = UO_LOOPBACK;		break;
				case DCHAN_LOOPBACK_NT:	PriExtra = UO_LOOPBACK;		break;
					break;

				default:
					SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                                                  ( "Dxb!PriTranslateDChanSettings: failure mapping Mode to Extra!\n" ) );
					Status = STATUS_UNSUCCESSFUL;
					break;
		}
	}

	if ( NT_SUCCESS( Status ) )
	{
		switch ( HlcAddressType )
		{
				case DCHAN_ADDRESS_TYPE_UNKNOWN:		PriAddressType = PRI_ADDR_TYPE_UNKNOWN; 			break;
				case DCHAN_ADDRESS_TYPE_INTL:			PriAddressType = PRI_ADDR_TYPE_INTERNATIONAL; 	break;
				case DCHAN_ADDRESS_TYPE_NATIONAL:	PriAddressType = PRI_ADDR_TYPE_NATIONAL; 			break;
				case DCHAN_ADDRESS_TYPE_NETWORK:		PriAddressType = PRI_ADDR_TYPE_NETWORK; 			break;
				case DCHAN_ADDRESS_TYPE_SUBSCR:		PriAddressType = PRI_ADDR_TYPE_SUBSCRIBER; 		break;
				case DCHAN_ADDRESS_TYPE_ABBREV:		PriAddressType = PRI_ADDR_TYPE_ABBREVIATED; 		break;
					break;

				default:
					SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                                                  ( "Dxb!PriTranslateDChanSettings: failure mapping AddressType!\n" ) );
					Status = STATUS_UNSUCCESSFUL;
					break;
		}
	}

	if ( NT_SUCCESS( Status ) )
	{
		switch ( HlcNumberingPlan )
		{
				case DCHAN_NUMBERING_PLAN_UNKNOWN:	PriNumberingPlan = PRI_NUMBERING_PLAN_UNKNOWN;	break;
				case DCHAN_NUMBERING_PLAN_ISDN:		PriNumberingPlan = PRI_NUMBERING_PLAN_ISDN;		break;
				case DCHAN_NUMBERING_PLAN_DATA:		PriNumberingPlan = PRI_NUMBERING_PLAN_DATA;		break;
				case DCHAN_NUMBERING_PLAN_TELEX:		PriNumberingPlan = PRI_NUMBERING_PLAN_TELEX;		break;
				case DCHAN_NUMBERING_PLAN_NATIONAL:	PriNumberingPlan = PRI_NUMBERING_PLAN_NATIONAL;	break;
				case DCHAN_NUMBERING_PLAN_PRIVATE:	PriNumberingPlan = PRI_NUMBERING_PLAN_PRIVATE;	break;
					break;

				default:
					SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                                                  ( "Dxb!PriTranslateDChanSettings: failure mapping Numbering Plan!\n" ) );
					Status = STATUS_UNSUCCESSFUL;
					break;
		}
	}

	if ( NT_SUCCESS( Status ) )
	{
		switch ( HlcSubaddressType )
		{
				case DCHAN_SUBADDRESS_TYPE_NSAP:	PriSubaddressType = PRI_SUBADDR_TYPE_NSAP;	break;
				case DCHAN_SUBADDRESS_TYPE_USER:	PriSubaddressType = PRI_SUBADDR_TYPE_USER;	break;
					break;

				default:
					SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                                                  ( "Dxb!PriTranslateDChanSettings: failure mapping SubaddressType!\n" ) );
					Status = STATUS_UNSUCCESSFUL;
					break;
		}
	}

	if ( NT_SUCCESS( Status ) )
	{
		if ( pExtra )
			*pExtra = PriExtra;
		if ( pAddressType )
			*pAddressType = PriAddressType;
		if ( pNumberingPlan )
			*pNumberingPlan = PriNumberingPlan;
		if ( pSubaddressType )
			*pSubaddressType = PriSubaddressType;
	}
	return Status;
}


NTSTATUS
dxbPriGetNationality( 
	HANDLE hKeyPort,
	PULONG pNationality )
/*
Comments

	Read the DS1\Signalling value from registry and translate to PRIme Nationality.
*/
{
	HANDLE hkDs1 = NULL;

	NTSTATUS Status = DigiRegOpenSubkey( &hkDs1, hKeyPort, DXB_REGKEY_DS1 );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hkDs1, DXB_REGVAL_IL3_NATIONALITY, pNationality, DigiRegFlagRequired, 0 );

	// If we couldn't read Nationality, translate HLC-style Signalling --> Nationality.
	if ( NT_ERROR( Status ) )
	{
		ULONG Signalling;
		Status = DigiRegQueryDword( hkDs1, DXB_REGVAL_DS1_SIGNALLING, &Signalling, DigiRegFlagRequired, 0 );
		if ( NT_SUCCESS( Status ) )
		{
			switch ( Signalling )
			{
				case PRI_NI2:		*pNationality = NAT_NATIONAL_ISDN_2;	break;
				case PRI_5ESS:		*pNationality = NAT_ATT_41449;			break;
				case PRI_DMS100:	*pNationality = NAT_DMS100;				break;
				case PRI_ETSI:		*pNationality = NAT_ETSI;					break;
				case PRI_JAPAN:	*pNationality = NAT_NTT;					break;
					break;
				default:
					Status = STATUS_UNSUCCESSFUL;
					break;
			}
		}
	}

	if ( hkDs1 )
		DigiRegCloseKey( hkDs1 );

	return Status;
}


NTSTATUS
dxbPriIl3Tune( 
	IN HANDLE hStream,
	IN HANDLE hKeyPort,
	IN ULONG  iPort,
	IN ULONG  Snid )
{
	#define IL3_PRIMARY_RATE 1
	NTSTATUS Status = dxbPriSetSingleUlong( hStream, IL3_MACHINE, IL3_RATE, IL3_PRIMARY_RATE );

	if ( NT_SUCCESS( Status ) )
	{
		// Set the Switch type
		ULONG Nationality;
		Status = dxbPriGetNationality( hKeyPort, &Nationality );
		if ( NT_SUCCESS( Status ) )
			Status = dxbPriSetSingleUlong( hStream, IL3_MACHINE, IL3_NATIONALITY, Nationality );
	}

	if ( NT_SUCCESS( Status ) )
		Status = dxbPriSetSingleUchar( hStream, IL3_MACHINE, IL3_INFORMATION_TRANSFER_CAP_IN, 0x47 );

	if ( NT_SUCCESS( Status ) )
		Status = dxbPriSetSingleUchar( hStream, IL3_MACHINE, IL3_INFORMATION_TRANSFER_CAP_OUT, 0x2 );

	if ( NT_SUCCESS( Status ) )
		Status = dxbPriSetSingleUlong( hStream, IL3_MACHINE, IL3_USER_OPTIONS, UO_SELF_ID|UO_INIT_DL_UP );

	if ( NT_SUCCESS( Status ) )
	{
		ULONG Extra, AddressType, NumberingPlan, SubaddressType;
		Status = dxbPriTranslateDChanSettingsToIl3( hKeyPort, &Extra, &AddressType, &NumberingPlan, &SubaddressType );

		if ( NT_SUCCESS( Status ) )
			Status = dxbPriSetSingleUlong( hStream, IL3_MACHINE, IL3_USER_OPTIONS_EXTRA, Extra );

		if ( NT_SUCCESS( Status ) )
			Status = dxbPriSetSingleUlong( hStream, IL3_MACHINE, IL3_TYPE_OF_ADDRESS, AddressType );

		if ( NT_SUCCESS( Status ) )
			Status = dxbPriSetSingleUlong( hStream, IL3_MACHINE, IL3_NUMBERING_PLAN, NumberingPlan );

		if ( NT_SUCCESS( Status ) )
			Status = dxbPriSetSingleUlong( hStream, IL3_MACHINE, IL3_TYPE_OF_SUBADDRESS, SubaddressType );
	}

	if ( NT_SUCCESS( Status ) )
	{
		UCHAR Timer[8] = "T301\0\0\0";
		Status = dxbPriSetSingleTimer( hStream, IL3_TIMERS, &Timer, sizeof Timer, 0x960 );
	}

	if ( NT_SUCCESS( Status ) )
	{
		UCHAR Timer[8] = "T303\0\0\0";
		Status = dxbPriSetSingleTimer( hStream, IL3_TIMERS, &Timer, sizeof Timer, 0x28 );
	}

	if ( NT_SUCCESS( Status ) )
	{
		UCHAR Timer[8] = "T305\0\0\0";
		Status = dxbPriSetSingleTimer( hStream, IL3_TIMERS, &Timer, sizeof Timer, 0x12c );
	}

	if ( NT_SUCCESS( Status ) )
	{
		UCHAR Timer[8] = "T308\0\0\0";
		Status = dxbPriSetSingleTimer( hStream, IL3_TIMERS, &Timer, sizeof Timer, 0x28 );
	}

	if ( NT_SUCCESS( Status ) )
	{
		UCHAR Timer[8] = "T310\0\0\0";
		Status = dxbPriSetSingleTimer( hStream, IL3_TIMERS, &Timer, sizeof Timer, 0x4b0 );
	}

	if ( NT_SUCCESS( Status ) )
	{
		UCHAR Timer[8] = "T313\0\0\0";
		Status = dxbPriSetSingleTimer( hStream, IL3_TIMERS, &Timer, sizeof Timer, 0x28 );
	}

	if ( NT_SUCCESS( Status ) )
	{
		UCHAR Timer[8] = "T316\0\0\0";
		Status = dxbPriSetSingleTimer( hStream, IL3_TIMERS, &Timer, sizeof Timer, 0x4b0 );
	}

	if ( NT_SUCCESS( Status ) )
	{
		UCHAR Timer[8] = "T322\0\0\0";
		Status = dxbPriSetSingleTimer( hStream, IL3_TIMERS, &Timer, sizeof Timer, 0x28 );
	}

	if ( NT_SUCCESS( Status ) )
		Status = dxbPriSetSingleUshort( hStream, IL3_MACHINE, IL3_DATALINK_DELAY_ACK, 0x0 );

	SRAPRINT2(DIGIINIT, SRA_MED_LEVEL, 
                  ( "DXB!PriTune: Leave, Status 0x%x\n", Status ) );
	return Status;
}


NTSTATUS
dxbPriLapdSetSnid( 
	HANDLE hStream,
	HANDLE hKeyPort,
	ULONG  Snid,
	ULONG  MuxId )
{
	NTSTATUS Status;
	ULONG Error;
	ULONG Mode;
	lapd_snioc_t SetSnid = {0};

	SetSnid.l_type  = LAPD_SNID;
	// SetSnid.l_class = ;	// Read from registry, see below.
	SetSnid.l_snid  = Snid;
	SetSnid.l_index = MuxId;

	Status = dxbPriGetDChanSettings( hKeyPort, &Mode, NULL, NULL, NULL );
	if ( NT_SUCCESS( Status ) )
	{
		switch ( Mode )
		{
			case DCHAN_NORMAL:
			case DCHAN_LOOPBACK_TE:
				SetSnid.l_class = LC_LAPD_TE;
				break;

			case DCHAN_LOOPBACK_NT:
				SetSnid.l_class = LC_LAPD_NT;
				break;

			default:
				Status = STATUS_UNSUCCESSFUL;
				break;
		}
	}

	if ( NT_SUCCESS( Status ) )
		Status = DsIoctl( hStream, LAPD_SETSNID, &SetSnid, sizeof SetSnid, &Error );
	if ( NT_ERROR( Status ) )
	{
		SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                          ( "DXB!LapdSetSnid failed, Status 0x%x, Error 0x%x\n", Status, Error ) );
	}
	return Status;
}


NTSTATUS
dxbPriLapdTune( 
	IN HANDLE hStream,
	IN HANDLE hKeyPort,
	IN ULONG  iPort,
	IN ULONG  Snid )
{
	NTSTATUS Status;
	ULONG Error;
	lapd_tnioc_t Tune = {0};
	Tune.l_type = LAPD_TUNE;
	Tune.l_snid = Snid;
	Tune.l_tune.N200 = 0x0003;
	Tune.l_tune.N201 = 0x0104;
	Tune.l_tune.N202 = 0x0014;
	Tune.l_tune.T200 = 0x000a;
	Tune.l_tune.T202 = 0x0014;
	Tune.l_tune.T203 = 0x0064;
	Tune.l_tune.rem_busy_poll = 0x0003;
	Tune.l_tune.tx_window     = 0x0007;
	Tune.l_tune.ack_delay     = 0x03e8;
	Tune.l_tune.notack_max    = 0x0001;
	Tune.l_tune.lme_enabled   = 0x0000;
	Tune.l_tune.llconform     = 0x0004;
	Tune.l_tune.multiple_tei  = 0x0000;

	Status = DsIoctl( hStream, LAPD_SETTUNE, &Tune, sizeof Tune, &Error );
	if ( NT_ERROR( Status ) )
	{
		SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                          ( "DXB!PriLapdTune failed, Status 0x%x, Error 0x%x\n", Status, Error ) );
	}
	return Status;
}


