/*++

*******************************************************************************
* Copyright (c) 1994-96 Digi International Inc., All Rights Reserved          *
*                                                                             *
* This  software  contains  proprietary  and  confidential   information   of *
* Digi International  Inc.   By  accepting  transfer  of this copy, Recipient *
* agrees to retain this software in  confidence,  to  prevent  disclosure  to *
* others,  and  to make no  use of this software other than that for which it *
* was delivered.  This is an unpublished 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-96                               *
*                                                                             *
* 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 specific to Frame Relay for the DigiLan NDIS
	LAN miniport driver.

Author:

   Michael Zarns

--*/

#include "digilan.h"
#include "DsApi.h"

#include "wan_control.h"


///////////////////////////////////////////////////////////////////////////////
//
// Function Prototypes
//

NDIS_STATUS
FrOpenPll(
	PDIGILAN_LOGICAL_ADAPTER pAdapter,
	ULONG ProtocolId
	);

NDIS_STATUS
FrSend(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
	IN PNDIS_PACKET pNdisPacket
	);

VOID
FrSendComplete(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
	IN PVOID pControl,
	IN ULONG ControlLength,
	IN PNDIS_PACKET pNdisPacket,
	IN NDIS_STATUS IOStatus
	);

VOID
FrReceive(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
	IN PVOID pControl,
	IN PDIGILAN_FRAME_BUFFER pBuffer,
	IN ULONG DataLength
	);

VOID
FrHandleReceiveData (
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
	IN PVOID pControl,
	IN PDIGILAN_FRAME_BUFFER pBuffer,
	IN ULONG DataLength
	);

VOID
FrDeliverData(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,	
	IN PDIGILAN_FRAME_BUFFER    pBuffer,
	PDIGILAN_ETHERNET_FRAME     pFrame,	
	ULONG                       PayloadLength
	);

VOID
FrUpdateTimerFunction(   
	IN PVOID,
	IN PVOID,
	IN PVOID,
	IN PVOID
	);

VOID
FrUpdate(
    IN struct _NDIS_WORK_ITEM *  pWorkItem,
    IN PVOID  Context
);


///////////////////////////////////////////////////////////////////////////////
//
// Constants
//

// Name of SXB device to open (passed in the call to SXB_S_OPEN).
#define FR_DEVICE_NAME			"FR"

// Unicode Strings
#define IP_NAME					TEXT("TCPIP")
#define IPX_NAME				TEXT("IPX")
#define ETHERNET_NAME			TEXT("Ethernet")

#define DLCI_NAME				TEXT("DLCI")
#define BCMAX_NAME				TEXT("BcMax")
#define BCMIN_NAME				TEXT("BcMin")
#define BE_NAME					TEXT("Be")
#define CIR_NAME				TEXT("CIR")
#define CMP_NAME				TEXT("CMP")
#define FRF9_RETRY_COUNT		TEXT("ModeOneRRMax")
#define FRF9_RETRY_PERIOD		TEXT("ModeOneRRPeriod")

#define IPX_PACKET_TYPE_OFFSET 8
#define IPX_PACKET_TYPE_IPX	   4
#define IPX_PACKET_TYPE_RIP	   1
#define IPX_SOURCE_ADDR_OFFSET 25


typedef struct
{
	USHORT ProtocolId;	// RFC1490 Encapsulation protocol id
	USHORT Offset;			// Offset into ndis_packet where protocol data starts
} PROTOCOL_PARAMS;




NTSTATUS
FrInitialize(
	IN OUT PDIGILAN_LOGICAL_ADAPTER pAdapter,
	IN HANDLE hRedirectKey
	)
/*++

Description:

	This routine is called by DigiLanInitialize to init a Frame Relay PVC.

Parameters:

	PDIGILAN_LOGICAL_ADAPTER pAdapter
		The virtual adapter exposed to NDIS which describes the PVC to be 
		initialized.

	NDIS_HANDLE RegConfigHandle
		Handle to our registry key used in calls when reading registry params.

Return Value:

	NDIS_STATUS_SUCCESS,	or some error value.

--*/
{	

	FRSCONF_InsertPLL_t InsertPll;
	NDIS_STATUS Status;
	ULONG Error;
	ULONG Temp;

	// 
	// Gather settings from the registry
	//

	//
	// Get the DLCI from the registry
	//
	Status = DigiRegQueryDword( hRedirectKey,
								DLCI_NAME,
								&pAdapter->SyncProtocol.Private.FR.Dlci,
								DigiRegFlagRequired,
								0 );
		
	if( Status != NDIS_STATUS_SUCCESS )
		goto FrInitializeExit;

	//
	// Check each network protocol for encapsulation method
	//
	Status = DigiRegQueryDword( hRedirectKey,
								IP_NAME,
								&Temp,
								DigiRegFlagRequired,
								0 );
	
	if ( ( NDIS_STATUS_SUCCESS == Status ) && ( Temp < PROTOCOL_SUPPORT_MAX ) )
		pAdapter->LanProtocol.Ip = Temp;	// Enable
	else
		pAdapter->LanProtocol.Ip = 0;		// Disable

	Status = DigiRegQueryDword( hRedirectKey,
								IPX_NAME,
								&Temp,
								DigiRegFlagRequired,
								0 );
	
	if ( ( NDIS_STATUS_SUCCESS == Status ) && ( Temp < PROTOCOL_SUPPORT_MAX ) )
		pAdapter->LanProtocol.Ipx = Temp;	// Enable
	else
		pAdapter->LanProtocol.Ipx = 0;		// Disable

	//
	// Netbeui, and other non-routable protocols can only be bridged, so check 
	// to see if bridging is enabled for "other" (i.e. non-routable) protocols.
	// If so, enable ethernet bridging.
	//
	Status = DigiRegQueryDword( hRedirectKey,
								ETHERNET_NAME,
								&Temp,
								DigiRegFlagRequired,
								0 );
	
	if ( ( Status == NDIS_STATUS_SUCCESS ) && 
		( Temp == PROTOCOL_SUPPORT_BRIDGE || Temp == PROTOCOL_SUPPORT_BRIDGE_FCS ) )
		pAdapter->LanProtocol.Ethernet = Temp;	// Enable
	else
		pAdapter->LanProtocol.Ethernet = 0;		// Disable

	//
	// Initialize the Frame Relay config structure for this PVC
	//
	NdisZeroMemory( &InsertPll, sizeof( FRSCONF_InsertPLL_t ) );

	NdisMoveMemory( 
		&InsertPll.pllconfig.frs_conf_name, 
		pAdapter->Name, 
		strlen( pAdapter->Name ) < FRS_PLL_NAME_SIZE ? 
			strlen( pAdapter->Name ) : ( FRS_PLL_NAME_SIZE - 1 ) );

//------------------------------------------------------------ MLZ 7/9/97 {
#ifndef CLYDESDALE
	InsertPll.link = InsertPll.pllconfig.frs_pll_ndx = 
		(USHORT)( pAdapter->Snid - DXB_REGVAL_LINE_SNID_ENUM_FIRST + 1 );
#else
	InsertPll.snid = pAdapter->Snid;
#endif
//------------------------------------------------------------ MLZ 7/9/97 }
	InsertPll.dlci = InsertPll.pllconfig.frs_pll_dlci = 
		(USHORT)pAdapter->SyncProtocol.Private.FR.Dlci;
	
	InsertPll.pllconfig.frs_pll_state = 1;

	Status = DigiRegQueryDword( hRedirectKey,
								BCMAX_NAME,
								&Temp,
								DigiRegFlagRequired,
								0 );

	if( Status != NDIS_STATUS_SUCCESS )
		goto FrInitializeExit;

	InsertPll.pllconfig.frs_pll_BcMax = Temp;

	Status = DigiRegQueryDword( hRedirectKey,
								BCMIN_NAME,
								&Temp,
								DigiRegFlagRequired,
								0 );

	if( Status != NDIS_STATUS_SUCCESS )
		goto FrInitializeExit;

	InsertPll.pllconfig.frs_pll_BcMin = Temp;

	Status = DigiRegQueryDword( hRedirectKey,
								BE_NAME,
								&Temp,
								DigiRegFlagRequired,
								0 );

	if( Status != NDIS_STATUS_SUCCESS )
		goto FrInitializeExit;

	InsertPll.pllconfig.frs_pll_Be = Temp;

	Status = DigiRegQueryDword( hRedirectKey,
								CIR_NAME,
								&Temp,
								DigiRegFlagRequired,
								0 );

	if( Status != NDIS_STATUS_SUCCESS )
		goto FrInitializeExit;

	InsertPll.pllconfig.frs_pll_CIR = Temp;
	pAdapter->Speed = Temp; // Use this as the speed statistic

	Status = DigiRegQueryDword( hRedirectKey,
								CMP_NAME,
								&Temp,
								DigiRegFlagRequired,
								0 );

	if( Status != NDIS_STATUS_SUCCESS )
		goto FrInitializeExit;

	InsertPll.pllconfig.frs_pll_CMP = (USHORT) Temp;

	//
	// FRF.9 Compression Parameters - optional
	//

	Status = DigiRegQueryDword( hRedirectKey,
								FRF9_RETRY_COUNT,
								&Temp,
								DigiRegFlagRequired,
								0 );

	if( Status == NDIS_STATUS_SUCCESS )
		InsertPll.pllconfig.frs_pll_FRF9RetryCount = (UCHAR) Temp;

	Status = DigiRegQueryDword( hRedirectKey,
								FRF9_RETRY_PERIOD,
								&Temp,
								DigiRegFlagRequired,
								0 );

	if( Status == NDIS_STATUS_SUCCESS )
		InsertPll.pllconfig.frs_pll_FRF9RetryPeriod = (UCHAR) Temp;

	// Open the stream first
	Status = DsOpen( pAdapter->hDevice, FR_DEVICE_NAME, pAdapter, NULL, 0,
	                 &pAdapter->hStream, &Error );

	if ( Status != NDIS_STATUS_SUCCESS || Error )
	{
		DigiDump( (DIGIERRORS|DIGIINIT), 
		          ("*** FrInitialize: DsOpen failed, status 0x%x, error 0x%x!\n", 
		          Status, Error ) );
		goto FrInitializeExit;
	}

	// Send down the PVC configuration via FRSCONF_InsertPLL
//------------------------------------------------------------ MLZ 7/9/97 {
#ifndef CLYDESDALE
	DigiDump( DIGIINIT, ("   FRI_FRSCONF_InsertPLL on line 0x%x\n"
	          "      link = 0x%x\n"
	          "      dlci = %d (0x%x)\n"
	          "      frs_pll_ndx = %d\n"
	          "      frs_pll_link  = %d\n"
	          "      frs_pll_next  = %d\n"
	          "      frs_pll_dlci  = %d\n"
	          "      frs_pll_BcMax = 0x%x\n"
	          "      frs_pll_BcMin = 0x%x\n"
	          "      frs_pll_Be    = 0x%x\n"
	          "      frs_pll_CIR   = 0x%x\n"
	          "      frs_pll_CMP   = 0x%x\n"
	          "      frs_pll_fwd_node = %d\n"
	          "      frs_pll_fwd_link = %d\n"
	          "      frs_pll_fwd_dlci = %d\n"
	          "      frs_pll_state = %d\n"
	          "      frs_pll_FRF9RetryPeriod = %d\n"
	          "      frs_pll_FRF9RetryCount = %d\n"
	          "      frs_pll_FRF9ModesWanted = %d\n"
	          "      frs_pll_FRF9ContextMax = %d\n"
	          "      pll_extent = %d\n",
	          pAdapter->Snid,
	          InsertPll.link,
	          InsertPll.dlci,
	          InsertPll.dlci,
	          InsertPll.pllconfig.frs_pll_ndx,
	          InsertPll.pllconfig.frs_pll_link,
	          InsertPll.pllconfig.frs_pll_next,
	          InsertPll.pllconfig.frs_pll_dlci,
	          InsertPll.pllconfig.frs_pll_BcMax,
	          InsertPll.pllconfig.frs_pll_BcMin,
	          InsertPll.pllconfig.frs_pll_Be,
	          InsertPll.pllconfig.frs_pll_CIR,
	          InsertPll.pllconfig.frs_pll_CMP,
	          InsertPll.pllconfig.frs_pll_fwd_node,
	          InsertPll.pllconfig.frs_pll_fwd_link,
	          InsertPll.pllconfig.frs_pll_fwd_dlci,
	          InsertPll.pllconfig.frs_pll_state,
	          InsertPll.pllconfig.frs_pll_FRF9RetryPeriod,
	          InsertPll.pllconfig.frs_pll_FRF9RetryCount,
	          InsertPll.pllconfig.frs_pll_FRF9ModesWanted,
	          InsertPll.pllconfig.frs_pll_FRF9ContextMax,
	          InsertPll.pllconfig.pll_extent ) );
#else
	DigiDump( DIGIINIT, ("   FRI_FRSCONF_InsertPLL on line 0x%x\n"
	          "      snid = 0x%x\n"
	          "      dlci = %d (0x%x)\n"
	          "      frs_pll_ndx = %d\n"
	          "      frs_pll_link  = %d\n"
	          "      frs_pll_next  = %d\n"
	          "      frs_pll_dlci  = %d\n"
	          "      frs_pll_BcMax = 0x%x\n"
	          "      frs_pll_BcMin = 0x%x\n"
	          "      frs_pll_Be    = 0x%x\n"
	          "      frs_pll_CIR   = 0x%x\n"
	          "      frs_pll_CMP   = 0x%x\n"
	          "      frs_pll_fwd_node = %d\n"
	          "      frs_pll_fwd_link = %d\n"
	          "      frs_pll_fwd_dlci = %d\n"
	          "      frs_pll_state = %d\n"
	          "      frs_pll_FRF9RetryPeriod = %d\n"
	          "      frs_pll_FRF9RetryCount = %d\n"
	          "      frs_pll_FRF9ModesWanted = %d\n"
	          "      frs_pll_FRF9ContextMax = %d\n",
	          pAdapter->Snid,
	          InsertPll.snid,
	          InsertPll.dlci,
	          InsertPll.dlci,
	          InsertPll.pllconfig.frs_pll_ndx,
	          InsertPll.pllconfig.frs_pll_link,
	          InsertPll.pllconfig.frs_pll_next,
	          InsertPll.pllconfig.frs_pll_dlci,
	          InsertPll.pllconfig.frs_pll_BcMax,
	          InsertPll.pllconfig.frs_pll_BcMin,
	          InsertPll.pllconfig.frs_pll_Be,
	          InsertPll.pllconfig.frs_pll_CIR,
	          InsertPll.pllconfig.frs_pll_CMP,
	          InsertPll.pllconfig.frs_pll_fwd_node,
	          InsertPll.pllconfig.frs_pll_fwd_link,
	          InsertPll.pllconfig.frs_pll_fwd_dlci,
	          InsertPll.pllconfig.frs_pll_state,
	          InsertPll.pllconfig.frs_pll_FRF9RetryPeriod,
	          InsertPll.pllconfig.frs_pll_FRF9RetryCount,
	          InsertPll.pllconfig.frs_pll_FRF9ModesWanted,
	          InsertPll.pllconfig.frs_pll_FRF9ContextMax ) );
#endif
//------------------------------------------------------------ MLZ 7/9/97 }

	Status = DlwIoctl( pAdapter, 
	                   FRI_FRSCONF_InsertPLL, 
	                   (PVOID)&InsertPll, 
	                   sizeof( InsertPll ),
	                   &Error );

	if ( !NT_SUCCESS( Status ) || Error )
	{
		DigiDump( (DIGIERRORS|DIGIINIT), 
		          ("*** FrInitialize: FRSCONF_InsertPLL failed!\n"
		          "      Status = 0x%x\n"
		          "      Error = 0x%x\n",
		          Status,
		          Error) );

		goto FrInitializeExit;
	}

	//
	// Attempt to open the PVC (PLL) once for each protocol (IP, IPX, 802.3) 
	// via ENCAP_OpenPLL.
	//

	if ( pAdapter->LanProtocol.Ip == PROTOCOL_SUPPORT_ROUTE )
	{
		Status = FrOpenPll( pAdapter, PID_IP );

		if ( NDIS_STATUS_SUCCESS != Status )
			goto FrInitializeExit;
	}

	if ( pAdapter->LanProtocol.Ip == PROTOCOL_SUPPORT_CICSO_ROUTED )
	{
		Status = FrOpenPll( pAdapter, PID_CISCO_IP );

		if ( NDIS_STATUS_SUCCESS != Status )
			goto FrInitializeExit;
	}

	if ( pAdapter->LanProtocol.Ipx == PROTOCOL_SUPPORT_ROUTE )
	{
		Status = FrOpenPll( pAdapter, PID_IPX );

		if ( NDIS_STATUS_SUCCESS != Status )
			goto FrInitializeExit;
	}

	if ( PROTOCOL_SUPPORT_BRIDGE == pAdapter->LanProtocol.Ethernet ||
		  PROTOCOL_SUPPORT_BRIDGE == pAdapter->LanProtocol.Ip ||
		  PROTOCOL_SUPPORT_BRIDGE == pAdapter->LanProtocol.Ipx )
	{
		Status = FrOpenPll( pAdapter, PID_ETHERNET );

		if ( NDIS_STATUS_SUCCESS != Status )
			goto FrInitializeExit;
	}

	if ( PROTOCOL_SUPPORT_BRIDGE_FCS == pAdapter->LanProtocol.Ethernet ||
		  PROTOCOL_SUPPORT_BRIDGE_FCS == pAdapter->LanProtocol.Ip ||
		  PROTOCOL_SUPPORT_BRIDGE_FCS == pAdapter->LanProtocol.Ipx )
	{
		Status = FrOpenPll( pAdapter, PID_ETHERNET_FCS );

		if ( NDIS_STATUS_SUCCESS != Status )
			goto FrInitializeExit;
	}

	Status = NDIS_STATUS_SUCCESS;

	pAdapter->SyncProtocol.Id = PROTOCOL_FR;
	pAdapter->SyncProtocol.RxControlLength = sizeof( FRP_FRSFR_RcvData_t );
	pAdapter->SyncProtocol.Send = FrSend;
	pAdapter->SyncProtocol.SendComplete = FrSendComplete;
	pAdapter->SyncProtocol.Receive = FrReceive;	

	pAdapter->SyncProtocol.Private.FR.LinkState = LINK_ACTIVE;

FrInitializeExit:;
	return( Status );
}


NDIS_STATUS
FrOpenPll(
	PDIGILAN_LOGICAL_ADAPTER pAdapter,
	ULONG ProtocolId
	)
/*++

Description:

	This routine sends an OpenPll ioctl down to the adapter.  This needs to be
	done once for each protocol which will be encapsulated via RFC1490.  Currently,
	we support (in the driver) IP, IPX, and 802.3 ethernet.

Parameters:

	pAdapter
		Address of the adapter object

	ProtocolId
		The PID_xxxx identification for the protocol to be opened.

Return Value:

	NDIS_STATUS_SUCCESS, or NDIS_STATUS_FAILURE if error

--*/
{
	ENCAP_OpenPLL_t OpenPll;
	ULONG ReturnCode;
	NDIS_STATUS Status;

	NdisZeroMemory( &OpenPll, sizeof( ENCAP_OpenPLL_t ) );

//------------------------------------------------------------ MLZ 7/9/97 {
#ifndef CLYDESDALE
	OpenPll.link = (USHORT)( pAdapter->Snid - DXB_REGVAL_LINE_SNID_ENUM_FIRST + 1 );
#else
	OpenPll.snid = pAdapter->Snid;
#endif
//------------------------------------------------------------ MLZ 7/9/97 }
	OpenPll.dlci = (USHORT) pAdapter->SyncProtocol.Private.FR.Dlci;

	OpenPll.protocol_id = (USHORT)ProtocolId;

	switch ( OpenPll.protocol_id )
	{
		case PID_IP:

			DigiDump( DIGIINIT, ("   Attempting to open Pll for IP... \n") );
			break;

		case PID_CISCO_IP:

			DigiDump( DIGIINIT, ("   Attempting to open Pll for Cisco IP... \n") );
			break;

		case PID_IPX:

			DigiDump( DIGIINIT, ("   Attempting to open Pll for IPX... \n") );
			break;

		case PID_ETHERNET:

			DigiDump( DIGIINIT, ("   Attempting to open Pll for 802.3... \n") );
			break;

		case PID_ETHERNET_FCS:

			DigiDump( DIGIINIT, ("   Attempting to open Pll for 802.3 w/FCS... \n") );
			break;

		default:

			DigiDump( 
				(DIGIERRORS|DIGIINIT),
				("*** FrOpenPll: Invalid protocol: 0x%x!\n", ProtocolId ) );

			return( NDIS_STATUS_FAILURE );
	}

//------------------------------------------------------------ MLZ 7/9/97 {
#ifndef CLYDESDALE
	DigiDump( DIGIINIT, ("   FRI_ENCAP_OpenPLL on line 0x%x\n"
	                     "      link = 0x%x\n"
	                     "      dlci = %d (0x%x)\n"
	                     "      pll_handle  = 0x%x\n"
	                     "      protocol_id = 0x%x\n"
	                     "      employ_STAC    = %d\n"
	                     "      STAC_perf_mode = %d\n"
	                     "      STAC_perf_parm = %d\n",
	                     pAdapter->Snid,
	                     OpenPll.link,
	                     OpenPll.dlci,
	                     OpenPll.dlci,
	                     OpenPll.pll_handle,
	                     OpenPll.protocol_id,
	                     OpenPll.padding) );
#else
	DigiDump( DIGIINIT, ("   FRI_ENCAP_OpenPLL on line 0x%x\n"
	                     "      snid = 0x%x\n"
	                     "      dlci = %d (0x%x)\n"
	                     "      pll_handle  = 0x%x\n"
	                     "      protocol_id = 0x%x\n"
	                     "      employ_STAC    = %d\n"
	                     "      STAC_perf_mode = %d\n"
	                     "      STAC_perf_parm = %d\n",
	                     pAdapter->Snid,
	                     OpenPll.snid,
	                     OpenPll.dlci,
	                     OpenPll.dlci,
	                     OpenPll.pll_handle,
	                     OpenPll.protocol_id ) );
#endif
//------------------------------------------------------------ MLZ 7/9/97 }

	Status = DlwIoctl( pAdapter,
	                   FRI_ENCAP_OpenPLL,
	                   (PVOID)&OpenPll,
	                   sizeof( ENCAP_OpenPLL_t ),
	                   &ReturnCode );

	if ( !NT_SUCCESS( Status ) || ReturnCode )
	{
		DigiDump( (DIGIERRORS|DIGIINIT), 
		          ("*** FrOpenPll: ENCAP_OpenPLL failed!\n"
		          "      ProtocolId = 0x%x\n"
		          "      Status     = 0x%x\n"
		          "      ReturnCode = 0x%x\n",
		          ProtocolId,
		          Status,
		          ReturnCode ) );

		return( Status );
	}

	pAdapter->SyncProtocol.Private.FR.PllHandle = OpenPll.pll_handle;

	DigiDump( DIGIINIT, 
	          ("   ENCAP_OpenPLL Success!  pll_handle = 0x%x\n", 
	          pAdapter->SyncProtocol.Private.FR.PllHandle ) );

	return( NDIS_STATUS_SUCCESS );
}



NDIS_STATUS
FrSend(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
	IN PNDIS_PACKET pNdisPacket
	)
/*++

Description:

	This routine initializes a PutMsg struct for Frame Relay.  The calling 
	routine is responsible for freeing the PutMsg.

Parameters:

	pAdapter
		Defines the PVC on which this packet should be sent.

	pNdisPacket
		The packet to be sent.

Return Value:

	STATUS_SUCCESS, if successful, otherwise an error.

--*/
{
static PROTOCOL_PARAMS IpTable[] =
{
	{ PID_UNKNOWN, 0 },							// PROTOCOL_SUPPORT_UNKNOWN
	{ PID_IP, ETHERNET_MAC_HEADER_SIZE },		// PROTOCOL_SUPPORT_ROUTE
	{ PID_ETHERNET, 0 },						// PROTOCOL_SUPPORT_BRIDGE
	{ PID_ETHERNET_FCS, 0 },					// PROTOCOL_SUPPORT_BRIDGE_FCS
	{ PID_CISCO_IP, ETHERNET_MAC_HEADER_SIZE }, // PROTOCOL_SUPPORT_CICSO_ROUTED
};

static PROTOCOL_PARAMS IpxTable[] =
{
	{ PID_UNKNOWN, 0 },							// PROTOCOL_SUPPORT_UNKNOWN
	{ PID_IPX, ETHERNET_MAC_HEADER_SIZE+ETHERNET_LLC_HEADER_SIZE }, // ROUTE
	{ PID_ETHERNET, 0 },						// PROTOCOL_SUPPORT_BRIDGE
	{ PID_ETHERNET_FCS, 0 },					// PROTOCOL_SUPPORT_BRIDGE_FCS
	{ PID_UNKNOWN, 0 },							// PROTOCOL_SUPPORT_CICSO_ROUTED
};

//#define SPOOF_ARP_FOR_BRIDGING

static PROTOCOL_PARAMS ArpTable[] =
{
	{ PID_UNKNOWN, 0 },                    // PROTOCOL_SUPPORT_UNKNOWN
	{ PID_UNKNOWN, 0 },                    // PROTOCOL_SUPPORT_ROUTE
#ifdef SPOOF_ARP_FOR_BRIDGING
 	{ PID_UNKNOWN, 0 },                    // PROTOCOL_SUPPORT_BRIDGE
	{ PID_UNKNOWN, 0 },                    // PROTOCOL_SUPPORT_BRIDGE_FCS
#else
 	{ PID_ETHERNET, 0 },                   // PROTOCOL_SUPPORT_BRIDGE
	{ PID_ETHERNET_FCS, 0 },               // PROTOCOL_SUPPORT_BRIDGE_FCS
#endif
	{ PID_UNKNOWN, 0 },                    // PROTOCOL_SUPPORT_CICSO_ROUTED
};

static PROTOCOL_PARAMS EthTable[] =
{
	{ PID_UNKNOWN, 0 },						// PROTOCOL_SUPPORT_UNKNOWN
	{ PID_UNKNOWN, 0 },                    // PROTOCOL_SUPPORT_ROUTE
	{ PID_ETHERNET, 0 },                   // PROTOCOL_SUPPORT_BRIDGE
	{ PID_ETHERNET_FCS, 0 },               // PROTOCOL_SUPPORT_BRIDGE_FCS
	{ PID_UNKNOWN, 0 },                    // PROTOCOL_SUPPORT_CICSO_ROUTED
};

	NDIS_STATUS Status;
	ULONG ProtocolId;
	ULONG Offset;
	ULONG PacketLength;
	PACKET_TYPE PacketType;

	FRP_SendData_t SendData;

	ENTER( "FrSend" );

	PacketType = DigiLanGetPacketType( pNdisPacket );

	switch ( PacketType )
	{
		case PACKET_TYPE_IP:

			ProtocolId = IpTable[ pAdapter->LanProtocol.Ip ].ProtocolId;
			Offset     = IpTable[ pAdapter->LanProtocol.Ip ].Offset;
			break;

		case PACKET_TYPE_IPX:

			ProtocolId = IpxTable[ pAdapter->LanProtocol.Ipx ].ProtocolId;
			Offset     = IpxTable[ pAdapter->LanProtocol.Ipx ].Offset;
			break;

		case PACKET_TYPE_ARP:

			ProtocolId = ArpTable[ pAdapter->LanProtocol.Ip ].ProtocolId;
			Offset     = ArpTable[ pAdapter->LanProtocol.Ip ].Offset;

			if ( ProtocolId == PID_UNKNOWN && pAdapter->LanProtocol.Ip )
			{
				DigiLanSpoofArpReply( pAdapter, pNdisPacket );

				NdisAcquireSpinLock( &pAdapter->Lock );
				pAdapter->PacketsSent++;
				NdisReleaseSpinLock( &pAdapter->Lock );

				LEAVE( "FrSend", NDIS_STATUS_SUCCESS );
			}
			break;

		case PACKET_TYPE_UNKNOWN:
			
			ProtocolId = EthTable[ pAdapter->LanProtocol.Ethernet ].ProtocolId;
			Offset     = EthTable[ pAdapter->LanProtocol.Ethernet ].Offset;
			break;

		default:
			ProtocolId = PID_UNKNOWN;			
			Offset     = 0;			
			break;
	}

	if ( ProtocolId == PID_UNKNOWN )
	{
		DigiDump( DIGIERRORS,("***FrSend: invalid protocol ID ( PacketType = 0x%x)\n", PacketType ));
		// Unsupported protocol or encapsulation method, fail the send request
		LEAVE( "FrSend", NDIS_STATUS_SUCCESS );
	}

	NdisQueryPacket( pNdisPacket, NULL, NULL, NULL, &PacketLength );

	if ( !PacketLength )
	{
		DigiDump( DIGIERRORS,("***FrSend: invalid packet length 0\n"));
		// Zero length packet is valid.  Don't send, just complete the request
		LEAVE( "FrSend", NDIS_STATUS_SUCCESS );
	}

	//
	// At this point, we've got a packet we want to send.  Build the control 
	// segment for the PutMsg, and do the PutMsg.
	//

	NdisZeroMemory( &SendData, sizeof( SendData ) );

	SendData.command     = FRP_ENCAP_SendData;
	SendData.ccb_handle  = (USHORT)pAdapter->SyncProtocol.Private.FR.PllHandle;
	SendData.protocol_id = (USHORT)ProtocolId;

	Status = DlwPutMsg( pAdapter,
	                    (PVOID)&SendData,
	                    sizeof( SendData ),
	                    pNdisPacket,
	                    Offset,
	                    PacketLength - Offset );

	// SDB:
	if ( NT_SUCCESS(Status) )
	{
		pAdapter->SendPacketsScheduled++;
	}
	else
	{
		DigiDump( DIGIERRORS,("***FrSend: failed to put msg (0x%x)\n", Status ));
	}

#if 0
	DbgPrint("FrSend: %d %d (0x%x)\n",
		pAdapter->SendPacketsScheduled,
		pAdapter->PacketsSent,
		Status);
#endif

	return( Status );
}



VOID
FrReceive(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
	IN PVOID pControl,
	IN PDIGILAN_FRAME_BUFFER pBuffer,
	IN ULONG DataLength
	)
/*++

Description:

	Processes the received packet, and inserts queues it to be sent up to ndis.

Parameters:

	pAdapter
		Describes the Sxb VC and NDIS logical adapter on which this packet is
		being received.

	pControl
		GetMsg Control data struct.  The Frame Relay stack fills this out to 
		indicate the packet type.

	pBuffer
		Address of frame buffer in which the received frame has been placed.  The
		actual packet data starts at pBuffer->Data.

	DataLength
		Number of bytes in the received packet.  This is the number of bytes
		starting at pBuffer->Data.

--*/
{	
	FRP_FRSFR_RcvData_t *pFrCtrl;	

	ENTER( "FrReceive" );

	pFrCtrl = pControl;

	switch ( pFrCtrl->command )
	{	
		case FRP_FRSFR_RcvData:
			FrHandleReceiveData( pAdapter, pControl, pBuffer, DataLength );
			// don't free buffer as it is needed for further processing in the stack
			break;

		default:
			
			//
			// Sxb returns failed PutMsg packets with a command value of FR_FAIL 
			// (thus it's really a FRP_FRSFR_SendData_t structure) - increment 
			// statistics counter, then dump the data!
			//

			NdisAcquireSpinLock( &pAdapter->Lock );
			pAdapter->SendPacketsDropped++;
			NdisReleaseSpinLock( &pAdapter->Lock );

			DigiDump( DIGITX|DIGIRX|DIGIWARN,
					  ("*** FrReceive: FR_FAIL received!\n"
						 "       pAdapter = 0x%x\n"
						 "       command  = 0x%x\n"
						 "       error    = 0x%x\n",
					  pAdapter,
					  pFrCtrl->command,
					  ((FRP_SendData_t *)(pFrCtrl))->error ) );	
			
			DigiFreeMem( pBuffer );	
			break;
	}

	LEAVE_NOVAL( "FrReceive" );
}




VOID
FrSendComplete(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
	IN PVOID pControl,
	IN ULONG ControlLength,
	IN PNDIS_PACKET pNdisPacket,
	IN NDIS_STATUS IOStatus
	)
/*++

Description:

	

Parameters:

	pAdapter
		Describes the Sxb VC and NDIS logical adapter on which this packet is
		being received.

	pControl
		Data for the control portion of this message.

	ControlLength
		Length of control portion of this message.

	pNdisPacket
		Pointer to original NdisPacket when not a control message.

	IOStatus
		The Irp's returned IOStatus.Status field.  This is the value returned from
		DigiSxb.

--*/
{
	if ( pNdisPacket )
	{
		DigiQueueSendComplete( pAdapter,
		                       pNdisPacket,
		                       IOStatus );

		NdisAcquireSpinLock( &pAdapter->Lock );

		if ( NT_SUCCESS( IOStatus ) )
			pAdapter->PacketsSent++;
		else
			pAdapter->SendPacketsDropped++;

		NdisReleaseSpinLock( &pAdapter->Lock );
	}
	else
	{
		// This was our Arp Reply sent from DigiIncomingArpReply
		ASSERT( ControlLength == sizeof( FRP_SendData_t ) );
	}
}


VOID
FrUpdateLinkState(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter
	)
{
	NDIS_STATUS Status;
	ULONG ReturnCode;
	wan_linestatus_t LineStatus;
	ULONG OldState;
	ULONG NewState;


	OldState = pAdapter->SyncProtocol.Private.FR.LinkState;

	NdisZeroMemory( &LineStatus, sizeof( wan_linestatus_t ) );

	// -- preset snid as the dxb driver needs that information to find the corresponding 
	// communication channel
	LineStatus.wan_linestatus_snid = pAdapter->Snid;		

	Status = DsIoctl( pAdapter->hWans,
	                   W_LINESTATUS,
	                   (PUCHAR)&LineStatus,
	                   sizeof( wan_linestatus_t ),
	                   &ReturnCode );
	

	if ( !NT_SUCCESS( Status ) || ReturnCode )
	{
		DigiDump( (DIGIERRORS|DIGIINIT), 
		          ("*FrUpdateLinkState: failed!\n"		         
		          "  Status     = 0x%x\n"
		          "  ReturnCode = 0x%x\n",		        
		          Status,
		          ReturnCode ) );
	}

	NewState = ( LineStatus.wan_linestatus_cable_id == 0x0B ) ? LINK_INACTIVE : LINK_ACTIVE;

	// DbgPrint("cable ID: 0x%x\n", LineStatus.wan_linestatus_cable_id );	

	pAdapter->SyncProtocol.Private.FR.LinkState = NewState;

	if ( OldState != NewState )
	{
		NDIS_STATUS NdisStatus;

		NdisStatus = ( NewState == LINK_ACTIVE ) ? NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT ;

		DigiQueueIndicateStatus(
			pAdapter,
			NdisStatus,
			0,			
			0
			);
	}
}


//
//
//
VOID
FrHandleReceiveData
( 	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
	IN PVOID pControl,
	IN PDIGILAN_FRAME_BUFFER pBuffer,
	IN ULONG DataLength
)
{
	NDIS_STATUS Status;
	FRP_FRSFR_RcvData_t *pFrCtrl;
	PDIGILAN_ETHERNET_FRAME pFrame;	
	ULONG PayloadLength;
	BOOLEAN StatusBoolean;
	BOOLEAN IsDelivered;

	//
	// Good, it's a real receive data notification!
	// Stuff in MAC, LLC, and SNAP headers as necessary.  pBuffer has padding
	// before the data portion, so we can simply backfill the padding as
	// necessary to get a complete frame.
	//
	// Take a received Routed IP packet for example.  pBuffer looks like this:
	//
	// +------------------+--------------------------+
	// |  Padding         |   IP Packet              |
	// +------------------+--------------------------+
	//
	// We need to prepend a MAC header (14 bytes) onto it, so it looks like:
	//
	// +-----+------------+--------------------------+
	// | Pad | MAC header |   IP Packet              |
	// +-----+------------+--------------------------+
	//

	IsDelivered = FALSE;

	pFrCtrl = pControl;

	switch ( pFrCtrl->protocol_id )
	{
		case PID_IP:
		case PID_CISCO_IP:

			//
			// PID_IP or PID_CICSO_IP means that the received data is an IP packet.  Ndis expects us
			// to indicate up a full 802.3 ethernet frame, so we must stuff in a MAC
			// header before passing it up to Ndis.
			//
			// The MAC header looks like this:
			//
			// +-----------+-----------+-----------+
			// |  Dest     | Source    | EtherType |
			// | 6 bytes   | 6 bytes   | 2 bytes   |
			// +-----------+-----------+-----------+
			//

			pFrame = (PDIGILAN_ETHERNET_FRAME)(   
							(PUCHAR)( &pBuffer->Data ) - ETHERNET_MAC_HEADER_SIZE );

			// Make sure the previous assignment is valid!
			ASSERT( ( (PVOID)pFrame >= (PVOID)&pBuffer->Padding ) && 
					  ( (PVOID)pFrame <= (PVOID)&pBuffer->Data ) );

			//SDB:
			NdisMoveMemory( &pFrame->Header[ETHERNET_MAC_DEST_OFFSET],
			                pAdapter->CurrentNetworkAddress,
			                ETHERNET_MAC_ADDR_SIZE );

			NdisMoveMemory( &pFrame->Header[ETHERNET_MAC_SRC_OFFSET],
			                pAdapter->RemoteNetworkAddress,
			                ETHERNET_MAC_ADDR_SIZE );

			NdisMoveMemory( &pFrame->Header[ETHERNET_MAC_TYPE_OFFSET],
			                &EthernetTypeIP,
			                sizeof( EthernetTypeIP ) );

			PayloadLength = DataLength;

			FrDeliverData( pAdapter, pBuffer, pFrame, PayloadLength );
			IsDelivered = TRUE;

			break;

		case PID_ARP:

			DigiDump( DIGIWARN, ("*** DigiLan: ARP encapsulation not supported!\n") );
			IsDelivered = FALSE;  // No received frame to indicate to NDIS!
			break;

		case PID_IPX:

			//
			// PID_IPX means that the received data is an IPX packet.  Ndis expects 
			// us to indicate up a full 802.3 ethernet frame, so we must stuff in a 
			// MAC and LLC header before passing it up to Ndis.
			//
			// The header for IPX looks like this:
			//
			// |          MAC Header            |         LLC Header             |
			//
			// +----------+----------+----------+----------+----------+----------+
			// |  Dest    | Source   |  Length  |   DSAP   |   SSAP   | Category |
			// | 6 bytes  | 6 bytes  |  2 bytes |  1 byte  |  1 byte  |  1 byte  |
			// +----------+----------+----------+----------+----------+----------+
			//
			// Default values:
			//
			// |          |          |          |   0x0e	  |   0x0e   |   0x03   |
			//

			pFrame = (PDIGILAN_ETHERNET_FRAME)(   
						(PUCHAR)( &pBuffer->Data ) - 
						ETHERNET_MAC_HEADER_SIZE - 
						ETHERNET_LLC_HEADER_SIZE );

			// Since the remote's MAC addr is not available, try to sniff it from 
			// inside the IPX packet.  Fortunately, in IPX packets of type IPX or 
			// RIP the MAC addr is duplicated inside the IPX packet - we'll try to 
			// glean it from there.
			if ( !pAdapter->SyncProtocol.Private.FR.bSniffedRemoteNetworkAddress )
			{
				if ( pFrame->Payload[IPX_PACKET_TYPE_OFFSET] == IPX_PACKET_TYPE_IPX ||
				     pFrame->Payload[IPX_PACKET_TYPE_OFFSET] == IPX_PACKET_TYPE_RIP )
				{
					pAdapter->SyncProtocol.Private.FR.bSniffedRemoteNetworkAddress = TRUE;
					NdisMoveMemory( &pAdapter->RemoteNetworkAddress,
					                &pFrame->Payload[IPX_SOURCE_ADDR_OFFSET],
					                ETHERNET_MAC_ADDR_SIZE );
				}
			}

			//SDB:
			NdisMoveMemory( &pFrame->Header[ETHERNET_MAC_DEST_OFFSET],
			                pAdapter->CurrentNetworkAddress,
			                ETHERNET_MAC_ADDR_SIZE );

			NdisMoveMemory( &pFrame->Header[ETHERNET_MAC_SRC_OFFSET],
			                pAdapter->RemoteNetworkAddress, 
			                ETHERNET_MAC_ADDR_SIZE );

			PayloadLength = DataLength + ETHERNET_LLC_HEADER_SIZE;

			//
			// Stuff in the length; making sure we write in network byte order (high
			// order byte first).
			//

			pFrame->Header[ ETHERNET_MAC_TYPE_OFFSET ] = 
				(UCHAR)( ( PayloadLength & 0xFF00 ) >> 8 );

			pFrame->Header[ ETHERNET_MAC_TYPE_OFFSET + 1 ] = 
				(UCHAR)( PayloadLength & 0x00FF );

			NdisMoveMemory( &pFrame->Payload,
			                &EthernetTypeIpx,
			                sizeof( EthernetTypeIpx ) );

			FrDeliverData( pAdapter, pBuffer, pFrame, PayloadLength );
			IsDelivered = TRUE;

			break;

		case PID_ETHERNET:
		case PID_ETHERNET_FCS:


			pFrame = (PDIGILAN_ETHERNET_FRAME)( &pBuffer->Data );
			PayloadLength = DataLength - ETHERNET_MAC_HEADER_SIZE;


			//SDB:
			// -- Do packet filtering:		
			
			if ( pAdapter->PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS )
			{
				// Send all packet to NDIS regardless of address
				FrDeliverData( pAdapter, pBuffer, pFrame, PayloadLength );
				IsDelivered = TRUE;
				break;
			}
			
			if ( pAdapter->PacketFilter & NDIS_PACKET_TYPE_BROADCAST )
			{
				// Check for broadcast packet header:
				StatusBoolean = DigiCompareMemory(
									pAdapter->BroadCastAddress,
									pBuffer->Data,
									ETHERNET_MAC_ADDR_SIZE
									);

				if ( StatusBoolean == TRUE )
				{
					// It's a broadcast, so deliver it:
					FrDeliverData( pAdapter, pBuffer, pFrame, PayloadLength );
					IsDelivered = TRUE;
					break;
				}
			}
			
			if ( pAdapter->PacketFilter & NDIS_PACKET_TYPE_DIRECTED )
			{
				// Check if it is a packet directed to our current station address:

				StatusBoolean = DigiCompareMemory(
									pAdapter->CurrentNetworkAddress,
									pBuffer->Data,
									ETHERNET_MAC_ADDR_SIZE
									);

				if ( StatusBoolean == TRUE )
				{
					FrDeliverData( pAdapter, pBuffer, pFrame, PayloadLength );
					IsDelivered = TRUE;
					break;
				}
			}
			
			if ( pAdapter->PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST )
			{
				// We are supposed to indicate all multicast packets, 
				// so check if it is one:				
				// We do check if it is a multicast packet by verifying that the multicast bit
				// in the received ethernet address is set:
				if ( pBuffer->Data[ 0 ] & 1 )
				{
					FrDeliverData( pAdapter, pBuffer, pFrame, PayloadLength );
					IsDelivered = TRUE;
					break;
				}

			}

			if ( pAdapter->PacketFilter & NDIS_PACKET_TYPE_MULTICAST )
			{	
				// Check, if multicast addresses do match:

				unsigned int Cnt;
				char* pAddress;
				
				Cnt      = pAdapter->Multicast.Count;
				pAddress = pAdapter->Multicast.AddressList;

				while ( Cnt-- )
				{
					StatusBoolean = DigiCompareMemory(
										pAddress,
										pBuffer->Data,
										ETHERNET_MAC_ADDR_SIZE
										);
				
					if ( StatusBoolean == TRUE )
					{
						FrDeliverData( pAdapter, pBuffer, pFrame, PayloadLength );
						IsDelivered = TRUE;
						break;
					}

					// Try next address from list:
					pAddress += ETHERNET_MAC_ADDR_SIZE;
				}
			}
		
			break;

			//SDB: BUGBUG: The following code does not work after packet filtering
#if 0

#ifdef SPOOF_ARP_FOR_BRIDGING
// Insert our predefined MAC addresses into the packet

			//SDB:
			NdisMoveMemory( &pFrame->Header[ETHERNET_MAC_DEST_OFFSET],
			                pAdapter->CurrentNetworkAddress, 
			                ETHERNET_MAC_ADDR_SIZE );

			NdisMoveMemory( &pFrame->Header[ETHERNET_MAC_SRC_OFFSET],
			                pAdapter->RemoteNetworkAddress, 
			                ETHERNET_MAC_ADDR_SIZE );
#endif
			break;
#endif

		default:	

			DigiDump( DIGIERRORS,("*** DigiLan: unsupported encapsulation method!\n") );
			pFrame = NULL;							
			break;
	}

	if ( IsDelivered == FALSE )	
	{
		// Packet was not delivered, so put it in trash
		DigiFreeMem( pBuffer );
		LEAVE_NOVAL( "FrHandleReceiveData" );
	}
	
	LEAVE_NOVAL( "FrHandleReceiveData" );
}

//
//
//
VOID
FrDeliverData(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,	
	IN PDIGILAN_FRAME_BUFFER    pBuffer,
	PDIGILAN_ETHERNET_FRAME     pFrame,	
	ULONG                       PayloadLength
	)
{
	NDIS_STATUS Status;

	// Insert a new IndicateReceive entry in the adapter context queue.
	Status = DigiQueueIndicateReceive( pAdapter,
	                                   pBuffer,
	                                   pFrame,
	                                   PayloadLength );

	if ( NT_SUCCESS( Status ) )
	{
		NdisAcquireSpinLock( &pAdapter->Lock );
		pAdapter->PacketsReceived++;
		NdisReleaseSpinLock( &pAdapter->Lock );
	}
	else
	{
		// Couldn't queue up receive indication.  Drop packet on the floor.
		DigiDump( DIGIERRORS, 
			("*** FrHandleDeliverData: Couldn't queue receive indication - dropping frame! ***\n") );

		DigiFreeMem( pBuffer );
		NdisAcquireSpinLock( &pAdapter->Lock );
		pAdapter->RecvPacketsDropped++;
		NdisReleaseSpinLock( &pAdapter->Lock );
	}
	LEAVE_NOVAL( "FrDeliverData" );
}

#if DBG
//
//
//
void
FrShowStatistics(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter
	)
{
	DbgPrint("Statistics: TX( %d %d %d ) RX( %d %d )\n",		
		pAdapter->SendPacketsScheduled,
		pAdapter->PacketsSent,
		pAdapter->SendPacketsDropped,
		pAdapter->PacketsReceived,
		pAdapter->RecvPacketsDropped
		);
}
#endif

#if NT5_BUILD
//
//
//
void
FrStartUpdate(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter
	)
{

	NDIS_STATUS   NdisStatus;
	ULONG         Error;


	// Open WAN statistic stream:
	NdisStatus = DsOpen( pAdapter->hDevice, "WANS", pAdapter, NULL, 0,
	                 &pAdapter->hWans, &Error );

	if ( ( NdisStatus != NDIS_STATUS_SUCCESS ) || Error )
	{
		DigiDump( (DIGIERRORS|DIGIINIT), 
		          ("*FrStartUpdate: DsOpen failed for WAN statistic , status 0x%x, error 0x%x!\n", 
		          NdisStatus, Error ) );
		return;
	}

	NdisInitializeTimer(
	    &pAdapter->UpdateTimer,
	    FrUpdateTimerFunction,
        pAdapter
        );


	NdisInitializeWorkItem(
	    &pAdapter->UpdateWorkItem,
	    FrUpdate,
	    pAdapter
        );

	// Initial trigger the update mechanism:
	NdisSetTimer( &pAdapter->UpdateTimer, 100 );
}
#endif

#if NT5_BUILD
//
//
//
void
FrStopUpdate(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter
	)
{	
	BOOLEAN StatusBoolean;

	ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );

	NdisCancelTimer(
		&pAdapter->UpdateTimer,
		&StatusBoolean
        );

	// SDB TODO: make sure that the update function has finished

	// Close the statistic link:
	DsClose( pAdapter->hWans );
}
#endif

#if NT5_BUILD
//
//
//
VOID
FrUpdateTimerFunction
(   
	IN PVOID  SystemSpecific1,
	IN PVOID  _Context,
	IN PVOID  SystemSpecific2,
	IN PVOID  SystemSpecific3
)
{
	PDIGILAN_LOGICAL_ADAPTER pAdapter;
	NDIS_STATUS NdisStatus;

	pAdapter = (PDIGILAN_LOGICAL_ADAPTER)_Context;

	// Schedule our work item:
	NdisStatus = NdisScheduleWorkItem( &pAdapter->UpdateWorkItem );

	if ( NdisStatus != NDIS_STATUS_SUCCESS )
	{
		DigiDump( (DIGIERRORS), 
		          ("*FrUpdateTimerFunction: failed to scheule work item (0x%x)\n", 
		          NdisStatus) );
		// Although the item could not be scheduled we nevertheless re-schedule the timer:
	}

	// Re-schedule timer:
	NdisSetTimer(
	    &pAdapter->UpdateTimer,
	    1000 // 1 second
	    );
}
#endif

#if NT5_BUILD
//
//
//
VOID
FrUpdate(
    IN struct _NDIS_WORK_ITEM *  pWorkItem,
    IN PVOID  _pContext
)
{
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter = _pContext;	

	// Things to update:

	FrUpdateLinkState( pAdapter );
	
	// Comment in for debug: FrShowStatistics( pAdapter );
}
#endif