/*++

*******************************************************************************
* Copyright (c) 1994-95 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 copyright work  Digi   International *
* Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is *
* strictly prohibited.                                                        *
*                                                                             *
* Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc. *
* Confidential  when   combined   with   the   aggregated  modules  for  this *
* product) OBJECT CODE ONLY SOURCE MATERIALS                                  *
*                                                                             *
* (C) COPYRIGHT Digi International Inc. 1994-95                               *
*                                                                             *
* All Rights Reserved                                                         *
*                                                                             *
* US   Government   Users   Restricted   Rights   -   Use,   duplication   or *
* disclosure  restricted by GSA ADP Schedule Contract with Digi International *
* Inc.                                                                        *
*                                                                             *
*******************************************************************************

Module Name:

   init.c

Abstract:

   This module contains routines to initialize virtual circuits (PVCs).

Author:

   Michael Zarns

Revision History:

   $Log: /Components/Windows/NT/Sync/Build/Lan/Src/INIT.C $
 * 
 * 9     6/19/97 12:05p Mikez
 * Mods to support new SNID based firmware api for Frame Relay.
 * 
 * 8     4/28/97 1:55p Mikez
 * Updated to use DsApi library.
 * 
 * 7     12/06/96 2:27p Mikez
 * Modified to support new SXB paradigm of "one NT device per adapter"
 * rather than the older "one NT device per port".
 * 
 * 6     10/03/96 10:13a Mikez
 * Pulled Sxb interface code out of this driver and use newly created Sxb
 * interface wrapper library, SxbWrap.
 * 
 * 5     9/26/96 11:29a Mikez
 * Added code to reschedule a GetMsg if one could not be allocated
 * immediately.
 * 
 * 4     9/26/96 8:23a Mikez
 * Moved all Sxb interface routines into two source files - msg.c and
 * irp.c.  
 * 
 * 3     9/19/96 9:23a Mikez
 * Added X25 PVC support.
 * 
 * 2     8/13/96 10:48a Mikez
 * Fully abstracted Frame Relay code into three routines:  FrSend,
 * FrReceive and FrInit, and one AdapterObject field: RxControlLength.
 * This revision works and support Frame Relay only.
 * 
 * 1     3/11/96 3:16p Mikez
 * Revision 1.3  1995/11/08  10:22:57  mikez
 * Line registry key was duplicating info held in Port key.  Now use Port
 * key and translate from alpha characters A and B into integers 1 and 2.
 * 
 * Revision 1.2  1995/08/23  07:47:38  mikez
 * Fixed IP routing by adding ARP spoofing.  Added IPX routing support.
 * 
 * Revision 1.1  1995/08/16  12:40:09  mikez
 * Initial revision
 * 

--*/

#include "digilan.h"
#include "DsApi.h"
#include "wan_control.h"

//
// Local Prototypes:
//

void
DigiSetClock(
    IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
    IN NDIS_HANDLE hkParams
    );

//
// Global Data
//

extern DIGILAN_DRIVER_OBJECT DigiLanDriverObject;


//
// Constants
//

//
// Registry Keys found in 
// \Registry\Machine\System\CurrentControlSet\Services\DigiLanXX\Parameters
//

#define SERVICE_NAME			NDIS_STRING_CONST("ServiceName")
#define PROTOCOL_NAME			TEXT("Protocol")
#define PROTOCOL_NAME_X25		NDIS_STRING_CONST("X25")
#define PROTOCOL_NAME_FR		NDIS_STRING_CONST("FR")




NTSTATUS
DigiLanInitialize(
	OUT PNDIS_STATUS OpenErrorStatus, 
	OUT PUINT SelectedMediumIndex, 
	IN PNDIS_MEDIUM MediumArray,
	IN UINT MediumArraySize, 
	IN NDIS_HANDLE MiniportAdapterHandle, 
	IN NDIS_HANDLE WrapperConfigurationContext
	)
/*++

Description:

	This routine is called (by NDIS wrapper) once for each adapter installed.  We 
	break from NDIS conventions slightly in that we consider each Frame Relay 
	virtual connection (VC) to be a separate adapter, so that a single physical 
	Frame Relay adapter will support multiple virtual adapters.  Thus an EPC with 
	two physical lines may have some number of VC's on each line, and each VC will 
	be registered as a seperate network adapter.

Parameters:

	PNDIS_STATUS OpenErrorStatus
		A status value specifying information about the error if the return value is 
		NDIS_STATUS_OPEN_ERROR. Currently, the only values defined match those 
		specified as Open Error Codes in Appendix B of the IBM Local Area Network 
		Technical Reference.

	PUINT SelectedMediumIndex
		An index to MediumArray that specifies the medium type of the miniport driver.

	UINT MediumArray
		An array of medium types supported by the NDIS wrapper.

	NDIS_HANDLE MediumArraySize
		The number of elements in MediumArray.

	NDIS_HANDLE MiniportAdapterHandle
		The handle identifying the miniport driver assigned by the wrapper. The 
		miniport driver must supply this handle as a parameter in subsequent calls 
		to the NDIS wrapper.

	NDIS_HANDLE WrapperConfigurationContext
		The handle used for calls to NdisOpenConfiguration and the NdisImmediateReadXxx
		and NdisImmediateWriteXxx functions.

Return Value:

	NDIS_STATUS_SUCCESS,
	NDIS_STATUS_PENDING, or some error value.

--*/
{
	PDIGILAN_LOGICAL_ADAPTER pAdapter = NULL;
	NDIS_HANDLE MyConfigHandle;
	NDIS_STATUS Status = NDIS_STATUS_SUCCESS;

	NDIS_STRING ServiceNameStr = SERVICE_NAME;
	NDIS_STRING RegPathStr = NDIS_STRING_CONST("RegPath");

	ULONG		Snid;
	UNICODE_STRING ProtocolName = {0};
	UNICODE_STRING ProtocolNameX25 = PROTOCOL_NAME_X25;
	UNICODE_STRING ProtocolNameFr = PROTOCOL_NAME_FR;
	const unsigned char BroadCastAddress[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

	ULONG i;

	// SDB:
	HANDLE hSdbKey;	
	UNICODE_STRING	SdbPath = {0};

	//
	// The network address to assign to this VC.
	//
	PVOID NetworkAddress = NULL;

	//
	// The number of bytes in the network address.  It should be
	// DIGILAN_LENGTH_OF_ADDRESS.
	//
	ULONG Length;

#if NT5_BUILD
	NDIS_STRING Name;
#endif

	CHAR			RegPathName[256];
	UNICODE_STRING	RegPath = {0};
	UNICODE_STRING	FullRedirectPath;
	HANDLE			hRedirectKey = NULL;
	NDIS_HANDLE		hkParams = NULL;

	DigiDump( DIGIFLOW, ("Enter DigiLanInitialize\n") );

	DigiDump( 
		DIGIINIT, 
		("DigiLanInitialize: MiniportAdapterHandle = 0x%x\n",
		MiniportAdapterHandle
		) );

	//
	// Search for correct medium.
	//

	while( MediumArraySize > 0 )
	{
		MediumArraySize--;

		if ( MediumArray[ MediumArraySize ] == NdisMedium802_3 )
			break;
	}

	if ( MediumArray[ MediumArraySize ] != NdisMedium802_3 )
	{
		DigiDump( DIGIERRORS, ( "*** DigiLan: Unsupported media type!\n" ) );

		return(NDIS_STATUS_UNSUPPORTED_MEDIA);
	}

	*SelectedMediumIndex = MediumArraySize;

	//
	// Create and initialize an adapter object
	//

	pAdapter = DigiAllocMem( NonPagedPool,
										sizeof( DIGILAN_LOGICAL_ADAPTER )
										);

	if( pAdapter == NULL )
		return( NDIS_STATUS_RESOURCES );

	//
	// Clear out the adapter object, which sets all default values to FALSE or NULL.
	//
	NdisZeroMemory( pAdapter, sizeof(DIGILAN_LOGICAL_ADAPTER) );

	pAdapter->Type = DIGI_TYPE_ADAPTER_OBJECT;
	pAdapter->bSpoof = FALSE;

	// Stratus bugfix - for proper handling of NDIS work item, init a couple of flags to 
	// enable us to track exactly what we're currently doing.

	pAdapter->HaltReceived = FALSE;
	pAdapter->WorkItemPending = FALSE;

	//
	// Open the configuration space.
	//
	NdisOpenConfiguration( &Status,
	                       &MyConfigHandle,
	                       WrapperConfigurationContext );

	if( Status != NDIS_STATUS_SUCCESS )
	{
	   DigiDump( DIGIERRORS, ("*** DigiLan: NdisOpenConfiguration failed!\n") );
		goto DigiLanInitializeExit;
	}



#if NT5_BUILD
	RtlInitUnicodeString( &Name, DXB_REGKEY_PARAMETERS );
	NdisOpenConfigurationKeyByName( &Status, MyConfigHandle, &Name, &hkParams );
#else
	hkParams = MyConfigHandle;
#endif
	
	//
	// Get Registry Path ( == RegPath ).
	//
	// In NT5 this won't be here until post processing, in this case we need to spoof
	// the DigiLan startup

	Status = DigiReadConfiguration( hkParams,
									&RegPathStr,
									NdisParameterString,
									&RegPath,
									RegPathName );

	DigiDump( DIGIINIT, ("DigiLan: RegPath = %ls\n",RegPath.Buffer) );

#if NT5_BUILD
	if( Status != NDIS_STATUS_SUCCESS )
	{
	   	pAdapter->bSpoof = TRUE;	   
		pAdapter->MiniportAdapterHandle = MiniportAdapterHandle;
		NdisMSetAttributesEx( pAdapter->MiniportAdapterHandle, (NDIS_HANDLE)pAdapter, 0, NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT | NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER  , NdisInterfaceIsa );
		Status = NDIS_STATUS_SUCCESS;
		DigiDump( DIGIINIT, ("DigiLan: Spoofing the Startup!\n") );
		goto DigiLanInitializeExit;
	}
#else
	if( Status != NDIS_STATUS_SUCCESS )
		goto DigiLanInitializeExit;
#endif

	DigiDump( DIGIINIT, ("DigiLan: No Spoof - Ready to Proceed!\n") );

	//
	// Get the string which indicates our virtual adapter name ( == ServiceName ).
	//
	Status = DigiReadConfiguration(	hkParams,
									  &ServiceNameStr,
									  NdisParameterString,
									  &pAdapter->UName,
									  &pAdapter->Name );

	if( Status != NDIS_STATUS_SUCCESS )
		goto DigiLanInitializeExit;

	Status = DigiStrAllocUnicodeString( &FullRedirectPath, DXB_REDIRECTPATH_ROOT, RegPath.Buffer, NULL );

	if( Status != NDIS_STATUS_SUCCESS )
		goto DigiLanInitializeExit;

	Status = DigiRegOpenKey( &hRedirectKey, FullRedirectPath.Buffer );

	DigiStrFreeUnicodeString( &FullRedirectPath );

	if( Status != NDIS_STATUS_SUCCESS )
		goto DigiLanInitializeExit;

	//
	// Read network address from Registry
	//
	DigiDump( DIGIINIT, ("DigiLan: Get The Network Address!\n") );

	//
	// For NT5 we find the Network Address under the Adapter Parameters.  We must
	// move it under the NDIS Adapter Parameters so NdisReadNetworkAddress
	// will work.
	// 
	//    Other Options would be to let:
	//			1. the Cointaller do it, but we want to remove the parameter when we
	//             are done with it, so we only have one registry entry.  Also the
	//             coinstaller is not protocol specific, and all protocols do not
	//             require this logic.
	//          2. Write our own routine to replace NdisReadNetworkAddress.  Why
	//			   reinvent the wheel.
	//
	// We really didn't like any of the options, but we had to choose one.
	//

#if NT5_BUILD
	{

	UNICODE_STRING	ucNetworkAddressKey;
	NDIS_CONFIGURATION_PARAMETER ConfigParam;

#define DXB_REGVAL_NETADDR TEXT("NetworkAddress")

	// Get the name of the device object to open for this connection
	Status = DigiRegQuerySz( hRedirectKey, DXB_REGVAL_NETADDR, &ConfigParam.ParameterData.StringData, DigiRegFlagRequired, NULL );


	if( Status != NDIS_STATUS_SUCCESS )
		goto DigiLanInitializeExit;

	RtlInitUnicodeString( &ucNetworkAddressKey, DXB_REGVAL_NETADDR );
	ConfigParam.ParameterType = NdisParameterString;
	NdisWriteConfiguration(  &Status,
							 hkParams,
							 &ucNetworkAddressKey,
							 &ConfigParam);

	DigiRegFreeString( &ConfigParam.ParameterData.StringData);

	if( Status != NDIS_STATUS_SUCCESS )
	goto DigiLanInitializeExit;

	}

#endif

	// "Permanent" network address
	NdisReadNetworkAddress( &Status,
	                        &NetworkAddress,
	                        &Length,
	                        hkParams );

	NdisMoveMemory( pAdapter->PermanentNetworkAddress,
				   NetworkAddress,
				   DIGILAN_LENGTH_OF_ADDRESS );

	// Current network address:
	NdisReadNetworkAddress( &Status,
	                        &NetworkAddress,
	                        &Length,
	                        MyConfigHandle );

	if ( Status == NDIS_STATUS_SUCCESS )
	{
		NdisMoveMemory( pAdapter->CurrentNetworkAddress,
					   NetworkAddress,
					   DIGILAN_LENGTH_OF_ADDRESS );

	}
	else
	{
		NdisMoveMemory( pAdapter->CurrentNetworkAddress,
					   pAdapter->PermanentNetworkAddress,
					   DIGILAN_LENGTH_OF_ADDRESS );

		Status = NDIS_STATUS_SUCCESS;
	}

	if ( ( Status != NDIS_STATUS_SUCCESS ) || 
		  ( Length != DIGILAN_LENGTH_OF_ADDRESS ) )
	{
	   DigiDump( DIGIERRORS, ("*** DigiLan: NdisReadNetworkAddress failed!\n") );
		Status = NDIS_STATUS_FAILURE;
		goto DigiLanInitializeExit;
	}		

	//
	// Store it
	//

	DigiDump( DIGIINIT, ("   Using NetworkAddress = ") );

	for( i = 0; i < DIGILAN_LENGTH_OF_ADDRESS-1; i++ )
	{
	  DigiDump( DIGIINIT, ("%x-", *(((PUCHAR)NetworkAddress) + i)) );
	}

	DigiDump( DIGIINIT, ("%x\n", *(((PUCHAR)NetworkAddress) + i)) );
	DigiDump( DIGIINIT, ("                          ") );

	for( i = 0; i < DIGILAN_LENGTH_OF_ADDRESS-1; i++ )
	{
	  DigiDump( DIGIINIT, ("%c-", *(((PUCHAR)NetworkAddress) + i)) );
	}
	DigiDump( DIGIINIT, ("%c\n", *(((PUCHAR)NetworkAddress) + i)) );

	//
	// Arbitrarily assign the remote network address to be NetworkAddress + 1
	//

	NdisMoveMemory( pAdapter->RemoteNetworkAddress,
				   NetworkAddress,
				   DIGILAN_LENGTH_OF_ADDRESS );

	pAdapter->RemoteNetworkAddress[ DIGILAN_LENGTH_OF_ADDRESS - 1 ] += 1;

	//
	// Preset broadcast address:
	//
	NdisMoveMemory( pAdapter->BroadCastAddress,
				   BroadCastAddress,
				   DIGILAN_LENGTH_OF_ADDRESS );


	// Get the name of the device object to open for this connection
	Status = DigiRegQuerySz( hRedirectKey, DXB_REGVAL_DEVICE, &pAdapter->UDeviceName, DigiRegFlagRequired, NULL );

	if( Status != NDIS_STATUS_SUCCESS )
		goto DigiLanInitializeExit;

	DigiDump( DIGIINIT, ( "   DeviceName = %wZ\n", &pAdapter->UDeviceName ) );

	// Get the Snid (Line identifier) for the physical line for this connection
	Status = DigiRegQueryDword( hRedirectKey, DXB_REGVAL_LINE_SNID, &Snid, DigiRegFlagRequired, 0 );
		
	if( Status != NDIS_STATUS_SUCCESS )
		goto DigiLanInitializeExit;

	// The Snid key has a value of 1 for line1, 2 for line2, etc.
	pAdapter->Snid = Snid;

	Status = DsOpenDevice( &pAdapter->UDeviceName, &pAdapter->hDevice );
	if( Status != NDIS_STATUS_SUCCESS )
		goto DigiLanInitializeExit;

	DigiAdapterContextQueueInit( pAdapter, MiniportAdapterHandle );
	NdisAllocateSpinLock( &pAdapter->Lock );

	//
	// Get protocol type (X.25 or FrameRelay)
	//
	Status = DigiRegQuerySz( hRedirectKey, PROTOCOL_NAME, &ProtocolName, DigiRegFlagRequired, NULL );
		
	if( Status != NDIS_STATUS_SUCCESS )
		goto DigiLanInitializeExit;

	if ( !RtlCompareUnicodeString( &ProtocolName, &ProtocolNameX25, FALSE ) )
		pAdapter->SyncProtocol.Id = PROTOCOL_X25;
	else if ( !RtlCompareUnicodeString( &ProtocolName, &ProtocolNameFr, FALSE ) )
		pAdapter->SyncProtocol.Id = PROTOCOL_FR;
	else
	{
		Status = NDIS_STATUS_FAILURE;
		goto DigiLanInitializeExit;
	}

	// Before dereferencing from the table, make sure it's the correct size!
	ASSERT( PROTOCOL_MAX == sizeof( gProtocolInitTable ) / sizeof( PROTOCOL_INIT_ROUTINE ) );

	//
	// Initialize the protocol.  These routines will gather any needed info
	// from the registry and initialize the firmware for this PVC.
	//

	if ( pAdapter->SyncProtocol.Id >= PROTOCOL_MAX )
	{
		Status = NDIS_STATUS_FAILURE;
		goto DigiLanInitializeExit;
	}

	Status = 
		gProtocolInitTable[ pAdapter->SyncProtocol.Id ]( pAdapter, hRedirectKey );

	if ( !NT_SUCCESS( Status ) )
		goto DigiLanInitializeExit;

	//
	// Finish initializing the Adapter Object
	//

	pAdapter->MiniportAdapterHandle = MiniportAdapterHandle;

	NdisAcquireSpinLock( &DigiLanDriverObject.AdapterListLock  );
	InsertTailList( &DigiLanDriverObject.AdapterList, &pAdapter->ListEntry );
	NdisReleaseSpinLock( &DigiLanDriverObject.AdapterListLock  );

	//
	// This call is necessary to pass our MiniportAdapterContext (a pointer to our 
	// pAdapter object) back to Ndis so that future calls (i.e. MiniportSend) will
	// have the give us the correct context.
	//
	NdisMSetAttributesEx( pAdapter->MiniportAdapterHandle, (NDIS_HANDLE)pAdapter, 0, NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT | NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER  , NdisInterfaceIsa );

	// The DigiLanGetMsgTimeout routine is only used when a GetMsg failed due to 
	// resource limitations and needs to be rescheduled later.
	KeInitializeTimer( &pAdapter->GetMsgTimer );
	KeInitializeDpc( &pAdapter->GetMsgDpc, DlwGetMsgTimeout, pAdapter );
		
	//
	// The PVC is now configured and we are ready to transfer data.  Start the 
	// GetMsg processing.
	//

	DlwGetMsg( pAdapter );
	// Windxb-217: Back out call to DigiSetClock that was causing hang on bringup
    //DigiSetClock ( pAdapter, hkParams );

    if ( pAdapter->SyncProtocol.Id == PROTOCOL_FR )
    {
        // Start update process:
	    FrStartUpdate( pAdapter );
    }

DigiLanInitializeExit:

	// Free Strings
	if ( ProtocolName.Buffer )
		DigiRegFreeString( &ProtocolName);

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

#if NT5_BUILD
	if ( hkParams )
		NdisCloseConfiguration( hkParams );
#endif
	if ( hRedirectKey )
		DigiRegCloseKey( hRedirectKey );

	// SDB:
	// if ( hSdbKey ) DigiRegCloseKey( hSdbKey );


	NdisCloseConfiguration( MyConfigHandle );

	DigiDump( DIGIINIT, ("DigiLan: Leave Initialize - Status = %08x!\n",Status) );

	return( Status );
}


NDIS_STATUS
DigiReadConfiguration(
	IN NDIS_HANDLE ConfigurationHandle,
	IN PNDIS_STRING Keyword,
	IN NDIS_PARAMETER_TYPE ParameterType,
	OUT PUNICODE_STRING pwOutString OPTIONAL,
	OUT PVOID pOut OPTIONAL
	)
/*++

Description:

	This routine reads a configuration parameter from the registry.  
	NdisParameterInteger and NdisParameterString are currently supported.  Integer
	values will be written to (PULONG) pOut; string values will be written to pOut
	and optionally a unicode version of the string will be written to pwOutString.

Parameters:

	NDIS_HANDLE ConfigurationHandle
		Specifies the handle that the NDIS interface library associates with a 
		particular database open.

	PNDIS_STRING Keyword
		Points to the keyword.

	NDIS_PARAMETER_TYPE ParameterType
		Specifies the parameter type (NDIS_PARAMETER_TYPE).

	PUNICODE_STRING pwOutString
		Optional pointer to location to write a unicode version of the string (only if
		ParameterType is NdisParameterString).  

	PVOID pOut
		Pointer to location to write parameter.  Currently, only NdisParameterInteger
		and NdisParameterString are supported.

Return Value:

	NDIS_STATUS_FAILURE
	NDIS_STATUS_RESOURCES
	NDIS_STATUS_SUCCESS

--*/
{
	NDIS_STATUS Status;

	PNDIS_CONFIGURATION_PARAMETER ConfigParam;

	ANSI_STRING AnsiString;

	//
	// Get parameter from the registry
	//
	NdisReadConfiguration(	
		&Status,
		&ConfigParam,
		ConfigurationHandle,
		Keyword,
		ParameterType
		);

	if( Status != NDIS_STATUS_SUCCESS )
	{
		DigiDump( 
			DIGIINIT, 
			("*** DigiLan: Unable to find %wZ registry key! ***\n", 
			Keyword) );

		return( NDIS_STATUS_FAILURE );
	}


	//
	// Save parameter
	//

	if ( ParameterType == NdisParameterInteger )
	{
		DigiDump( 
			DIGIINIT, 
			( "   %wZ = %d\n", Keyword, ConfigParam->ParameterData.IntegerData )
			);

		*(PULONG)pOut = ConfigParam->ParameterData.IntegerData;

	}
	else
	{
		ASSERT( ParameterType == NdisParameterString );

		DigiDump( 
			DIGIINIT, 
			("   %wZ = %wZ\n", 
			Keyword,
			&(ConfigParam->ParameterData.StringData))
			);

		//
		// Save the unicode version of the string parameter
		//

		if ( pwOutString )
		{
			NdisMoveMemory( 
				pwOutString,
				&(ConfigParam->ParameterData.StringData),
				sizeof(UNICODE_STRING) );

			pwOutString->Buffer = 
				DigiAllocMem( 
					NonPagedPool,
					pwOutString->MaximumLength
					);

			if( pwOutString->Buffer == NULL )
				return ( NDIS_STATUS_FAILURE );

			NdisZeroMemory( pwOutString->Buffer,
								 pwOutString->MaximumLength );

			NdisMoveMemory( pwOutString->Buffer,
								 ConfigParam->ParameterData.StringData.Buffer,
								 pwOutString->Length );
		}
			
		//
		// Save an ascii version of the string parameter
		//

		if ( pOut )
		{
			RtlUnicodeStringToAnsiString( 
				&AnsiString,
				&(ConfigParam->ParameterData.StringData),
				TRUE );

			strcpy( pOut, AnsiString.Buffer );

			RtlFreeAnsiString( &AnsiString );
		}
	}

	return( NDIS_STATUS_SUCCESS );
}

//
// Support for setclock functionality
//

void
DigiSetClock(
    IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
    IN NDIS_HANDLE hkParams
    )
{
    NDIS_STATUS NtStatus;
    ULONG ReturnCode;
    ULONG ClockSettings = 0;
    NDIS_STRING ClockSettingsName = NDIS_STRING_CONST("CLK");
    wan_clkioc_t ClkSet;

    // Open WAN Statistic stream:
    NtStatus = DsOpen( pAdapter->hDevice, "WANS", pAdapter, NULL, 0, 
                        &pAdapter->hWans, &ReturnCode);

    if ( ( NtStatus != NDIS_STATUS_SUCCESS ) || ReturnCode )
    {
        DigiDump ( DIGIERRORS | DIGIINIT, 
                    ("DigiSetClock: failed to open WANS Stream (0x%x 0x%x)\n",
                    NtStatus, ReturnCode ));
        return;
    }

    if ( !NT_SUCCESS( NtStatus ) )
    {
        // The user did not specify the optional clock settings override key, so
        // don't be alarmed and simpley clean up:
        goto Exit;
    }

    //
    // Send command down to the firmware
    //

    // Possible values:
    //
    // SET_TCLOK_ODD    0x00010000
    // SET_TCLOK_EVEN   0x00020000
    // SET_TCLOK_BRG    0x00040000
    // SET_PHASE        0x00080000
    // SET_RCLOK_ODD    0x00001000
    // SET_RCLOK_EVEN   0x00002000
    // SET_RCLOK_BRG    0x00004000

    ClkSet.clockbits = 0;
    ClkSet.clockbits |= ClockSettings;
    ClkSet.cmd = SETCLOCK;
    ClkSet.snid = pAdapter->Snid;

    NtStatus = DsIoctl ( pAdapter->hWans,
                        SETCLOCK,
                        (PUCHAR)&ClkSet,
                        sizeof ( wan_clkioc_t ),
                        &ReturnCode );

    if ( !NT_SUCCESS (NtStatus) || ReturnCode )
    {
        DigiDump ( DIGIERRORS, ("*** DigiLan: failed to override clock settings (0x%x 0x%x)\n",NtStatus, ReturnCode));
        goto Exit;
    }

    DigiDump ( DIGIINIT, ("DigiLan: override clock settings with 0x%x\n", ClockSettings ) );

Exit:

    DsClose (pAdapter->hWans);
}