/*++

*******************************************************************************
* 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 ));
    }

    //SDB:
#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 ) );
    }
}


NTSTATUS
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 ) );
    }
    else 
    {
        // BUGBUG: SDB: I don't know the meaning of the linestatus member,
        // so I found that bit 0 is set if the line is down otherwise it's
        // reset. This leads to the following code: ( Ergo: We need to
        // check if that's correct )

        if ( LineStatus.wan_linestatus_linestatus & 0x01 )
        {           
            NewState = LINK_INACTIVE;
        }
        else
        {
            NewState = LINK_ACTIVE;         
        }

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

        if ( OldState != NewState )
        {
            NDIS_STATUS NdisStatus;

#if DBG
            //SDB:
            DbgPrint("*WAN link state = 0x%x\n", LineStatus.wan_linestatus_linestatus );
#endif
        
            NdisStatus = ( NewState == LINK_ACTIVE ) ? NDIS_STATUS_MEDIA_CONNECT : 
                NDIS_STATUS_MEDIA_DISCONNECT ;

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

    return Status;
}


//
//
//
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_MULTICAST | NDIS_PACKET_TYPE_ALL_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, 1000 );

}
#endif

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

    ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );

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

    if (!StatusBoolean) {
        // timer was either running or never started - hang around for
        // a bit just in case it was running...
        DigiDump( DIGIINIT, ("*** FrStopUpdate -  cant stop update timer\n") );
        NdisMSleep(500000); // 0.5s in microseconds
    }

    // and wait for pending work items to drain
    while (pAdapter->WorkItemPending)
    {
        NdisMSleep(100000); // 0.1s in microseconds
    }

    // 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;

    // Stratus bugfix - If we've received a halt notification, bail without
    // doing anything.

    NdisAcquireSpinLock( &pAdapter->Lock );
    if (pAdapter->HaltReceived)
    {
        NdisReleaseSpinLock( &pAdapter->Lock );
        return;
    }
    else {
        pAdapter->WorkItemPending = TRUE;
    }

    NdisReleaseSpinLock( &pAdapter->Lock );

    // 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 so we can try again in a bit
        // NOTE: timer is rescheduled from work item normally
        // Re-schedule timer:
        NdisSetTimer(
            &pAdapter->UpdateTimer,
            1000 // 1 second
            );
    }
}
#endif

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

    // Things to update (bear in mind adapter could have disappeared
    // due to surprise removal):

    if (pAdapter)
    {
        if (!pAdapter->HaltReceived)
            Status = FrUpdateLinkState( pAdapter );

        // Comment in for debug: FrShowStatistics( pAdapter );

        // Finally flag that we're done with our current work item.
        pAdapter->WorkItemPending = FALSE;

        // and reschedule timer unless state update failed (which
        // indicates adapter is gone)
        if (NT_SUCCESS(Status))
        {
            NdisSetTimer(&pAdapter->UpdateTimer, 1000 /* 1 second */);
        }
    }                             
}
#endif
