/*++

*******************************************************************************
* 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:

   x25.c

Abstract:

   This module contains routines specific to X.25 for the DigiLan NDIS
        LAN miniport driver.

Author:

   Michael Zarns

--*/

#include "digilan.h"

// Spider Streams specific include files
#include "ll_proto.h"
#include "x25_proto.h"
#include "x25_control.h"


///////////////////////////////////////////////////////////////////////////////
//
// Exported Function Declarations
//

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

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

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

///////////////////////////////////////////////////////////////////////////////
//
// Internal Function Declarations (internal to X25)
//

VOID 
X25Attach( 
        IN PKDPC Dpc,
        IN PVOID DeferredContext,
        IN PVOID SystemContext1,
        IN PVOID SystemContext2 
        );

VOID
X25OnData(
        IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
        IN PDIGILAN_FRAME_BUFFER pBuffer,
        IN ULONG DataLength,
        IN BOOLEAN bMore
        );

VOID
X25OnPvcAttach(
        IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
        IN PDIGILAN_FRAME_BUFFER pBuffer,
        IN ULONG ResultCode
        );

VOID
X25OnPvcDetach(
        IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
        IN PDIGILAN_FRAME_BUFFER pBuffer,
        IN ULONG Reason
        );

VOID
X25OnReset(
        IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
        IN PDIGILAN_FRAME_BUFFER pBuffer,
        IN ULONG Cause,
        IN ULONG Diag
        );


#define DBG_ENTER( RoutineName )                        \
        DigiDump( DIGIFLOW, ("Enter "RoutineName"\n") );

#define DBG_LEAVE( RoutineName, ReturnVal )             \
{                                                                                                                                                 \
        DigiDump( DIGIFLOW, ("Leave "RoutineName"\n" ) );         \
}

#define DBG_LEAVE_NOVAL( RoutineName )                  \
{                                                                                                                                                 \
        DigiDump( DIGIFLOW, ("Leave "RoutineName"\n" ) );         \
}


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

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

// Registry Value Names (Unicode Strings)
#define REGKEY_CHANNEL                          TEXT("Channel")
#define REGKEY_LOCAL_PACKET                     TEXT("LOCDEFPKTSIZE")
#define REGKEY_REMOTE_PACKET            TEXT("REMDEFPKTSIZE")
#define REGKEY_LOCAL_WINDOW                     TEXT("LOCDEFWSIZE")
#define REGKEY_REMOTE_WINDOW            TEXT("REMDEFWSIZE")
#define REGKEY_NDSU_LIMIT                       TEXT("NdsuLimit")


///////////////////////////////////////////////////////////////////////////////
//
// PVC State Machine
//
// X25 PVCs are not permanently connected, and behave according to the following
// state machine.  We use this state machine to determine whether we can send
// or receive data on this PVC.
//

//
// These are the possible states
//
typedef enum 
{
    STATE_UNKNOWN = 0,
    STATE_DETACHED,
    STATE_ATTACHING,
    STATE_ATTACHED,
    STATE_MAX
} PVC_STATE;

//
// These are the possible inputs
//
typedef enum
{
    ACTION_SENT_ATTACH = 0,
    ACTION_RECEIVED_ATTACH_ACK,
    ACTION_RECEIVED_ATTACH_NAK,
    ACTION_RECEIVED_DETACH,
    ACTION_MAX
} PVC_ACTION;

//
// Given a current state and an input, this table defines the resultant state.
//
PVC_STATE StateTable[ STATE_MAX ][ ACTION_MAX ] = 
{  //                      SENT_ATTACH,     ATTACH_ACK,     ATTACK_NAK,     RECEIVED_DETACH
    /* STATE_UNKNOWN   */ { STATE_ATTACHING, STATE_UNKNOWN,  STATE_UNKNOWN,  STATE_UNKNOWN  },
    /* STATE_DETACHED  */ { STATE_ATTACHING, STATE_UNKNOWN,  STATE_UNKNOWN,  STATE_UNKNOWN  },
    /* STATE_ATTACHING */ { STATE_UNKNOWN,   STATE_ATTACHED, STATE_DETACHED, STATE_UNKNOWN  },
    /* STATE_ATTACHED  */ { STATE_UNKNOWN,   STATE_UNKNOWN,  STATE_UNKNOWN,  STATE_DETACHED },
};

///////////////////////////////////////////////////////////////////////////////
//
// X25 Messages (Defines the NLI or Network Layer Interface)
//
// This union includes all possible X.25 messages sent to / received from 
// the X.25 protocol stack.  Since GetMsg's are generic and any message could be
// returned, we alloc a buffer the size of this union to be passed down.
//

typedef union
{
    struct xhdrf      Header;         // basic header, common across all primitives
    struct xcallf     Connect;        // Connect Request/Indication
    struct xccnff     ConnectConfirm; // Connect Response/Confirmation
    struct xdataf     Data;           // Data Request/Indication
    struct xdatacf    DataAck;        // Data Ack Request/Indication, for D-bit ack's
    struct xedataf    EData;          // Expedited Data Request/Indication
    struct xedatacf   EDataAck;       // Expedited Data Ack Response/Confirm
    struct xrstf      Reset;          // Reset Request/Indication
    struct xrscf      ResetConfirm;   // Reset Response/Confirm
    struct xdiscf     Disconnect;     // Disconnect Request/Indication
    struct xdcnff     DiscConfirm;    // Disconnect Confirm
    struct xabortf    Abort;          // Abort Indication
    struct xlistenf   ListenRequest;  // Listen Request/Confirm
    struct xcanlisf   ListenCancel;   // Listen Cancel Request/Confirm
    struct pvcattf    PvcAttach;      // PVC Attach Request
    struct pvcdetf    PvcDetach;      // PVC Detach Request/Indication
} X25_PRIMITIVES, *PX25_PRIMITIVES;




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

Description:

        This routine is called by DigiLanInitialize to initialize a single X.25 PVC
        defined by the virtual adapter at registry key RegConfigHandle.

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 to when reading registry params.

Return Value:

        NDIS_STATUS_SUCCESS,    or some error value.

--*/
{
    NDIS_STATUS Status;

    DBG_ENTER( "X25Initialize" );

    //
    // Gather PVC settings from registry
    //
    Status = DigiRegQueryDword( hRedirectKey,
                                REGKEY_CHANNEL,
                                &pAdapter->SyncProtocol.Private.X25.Channel,
                                DigiRegFlagRequired,
                                0 );
                
    if( Status == NDIS_STATUS_SUCCESS )

        Status = DigiRegQueryDword( hRedirectKey,
                                    REGKEY_LOCAL_PACKET,
                                    &pAdapter->SyncProtocol.Private.X25.LocalPacket,
                                    DigiRegFlagRequired,
                                    0 );
                
    if( Status == NDIS_STATUS_SUCCESS )

        Status = DigiRegQueryDword( hRedirectKey,
                                    REGKEY_REMOTE_PACKET,
                                    &pAdapter->SyncProtocol.Private.X25.RemotePacket,
                                    DigiRegFlagRequired,
                                    0 );
                
    if( Status == NDIS_STATUS_SUCCESS )

        Status = DigiRegQueryDword( hRedirectKey,
                                    REGKEY_LOCAL_WINDOW,
                                    &pAdapter->SyncProtocol.Private.X25.LocalWindow,
                                    DigiRegFlagRequired,
                                    0 );
                
    if( Status == NDIS_STATUS_SUCCESS )
                
        Status = DigiRegQueryDword( hRedirectKey,
                                    REGKEY_REMOTE_WINDOW,
                                    &pAdapter->SyncProtocol.Private.X25.RemoteWindow,
                                    DigiRegFlagRequired,
                                    0 );

    if( Status == NDIS_STATUS_SUCCESS )
    {
        ULONG Speed;
        // Optional, informational only
        (void) DigiRegQueryDword( hRedirectKey,
                                  DXB_REGVAL_LINESPEED,
                                  &Speed,
                                  DigiRegFlagOptional,
                                  0 );
        pAdapter->Speed = Speed;
    }

    if( Status != NDIS_STATUS_SUCCESS )
        goto Exit;

/*

Status = DigiRegQueryDword( hRedirectKey,
REGKEY_NDSU_LIMIT,
&pAdapter->SyncProtocol.Private.X25.NdsuLimit,
DigiRegFlagRequired,
0 );
                
if( Status != NDIS_STATUS_SUCCESS )
goto Exit;
*/

    // Open an X25 stream
    DigiDump( DIGIINIT, ("   Sending Open request on Snid 0x%x\n", pAdapter->Snid) );
    Status = DlwOpen( pAdapter, X25_DEVICE_NAME );
    if( Status != NDIS_STATUS_SUCCESS )
        goto Exit;

    {
        // Send down the PVC configuration
        struct pvcconff PvcConfig = { 0 };
        ULONG ReturnCode;
        PvcConfig.sn_id = pAdapter->Snid;
        PvcConfig.lci = (USHORT)pAdapter->SyncProtocol.Private.X25.Channel;
        PvcConfig.locpacket = (UCHAR)pAdapter->SyncProtocol.Private.X25.LocalPacket;
        PvcConfig.rempacket = (UCHAR)pAdapter->SyncProtocol.Private.X25.RemotePacket;
        PvcConfig.locwsize = (UCHAR)pAdapter->SyncProtocol.Private.X25.LocalWindow;
        PvcConfig.remwsize = (UCHAR)pAdapter->SyncProtocol.Private.X25.RemoteWindow;

        Status = DlwIoctl( pAdapter, N_putpvcmap, 
                           (PVOID)&PvcConfig, sizeof PvcConfig, 
                           &ReturnCode );
    }

    if ( !NT_SUCCESS( Status ) )
        goto Exit;

    // Initialize state variables.  We can only transmit data while in the 
    // PVC_STATE_ATTACHED state.  
    KeInitializeSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock );
    KeInitializeTimer( &pAdapter->SyncProtocol.Private.X25.AttachTimer );
    KeInitializeDpc( &pAdapter->SyncProtocol.Private.X25.AttachDpc, X25Attach, pAdapter );

    pAdapter->SyncProtocol.Id = PROTOCOL_X25;
    pAdapter->SyncProtocol.RxControlLength = sizeof( X25_PRIMITIVES );
    pAdapter->SyncProtocol.Send = X25Send;
    pAdapter->SyncProtocol.SendComplete = X25SendComplete;
    pAdapter->SyncProtocol.Receive = X25Receive;

    pAdapter->SyncProtocol.Private.X25.State = STATE_DETACHED;

    // Init More-bit data buffer concatenation spinlock
    KeInitializeSpinLock( &pAdapter->SyncProtocol.Private.X25.MoreLock );

    // Attempt to attach.  This is an async request, sometime later, we'll get
    // an indication back up (in a GetMsg) indicating whether the attach was
    // successful.
    X25Attach( NULL, pAdapter, NULL, NULL );

    Status = NDIS_STATUS_SUCCESS;

 Exit:
    DBG_LEAVE( "X25Initialize", Status );
    return Status;
}


VOID
X25StopAttach(
    IN PDIGILAN_LOGICAL_ADAPTER pAdapter
    )
{
    LARGE_INTEGER Delay;

    Delay.QuadPart = 100 * ONE_MILLISECOND;

    if (!KeCancelTimer(&pAdapter->SyncProtocol.Private.X25.AttachTimer)) {
        // timer wasn't in queue so might be running; wait a bit just
        // in case so that we are sure it has started running
        KeDelayExecutionThread(KernelMode,FALSE,&Delay);
    }

    // Now, assuming timer routine might be running - wait until it is
    // done
    while (pAdapter->X25AttachPending) {
        KeDelayExecutionThread(KernelMode,FALSE,&Delay);
    }
}

VOID 
X25Attach( 
        IN PKDPC Dpc,
        IN PVOID DeferredContext,
        IN PVOID SystemContext1,
        IN PVOID SystemContext2 
        )
/*++

Description:

        This routine sends down a PVC_ATTACH message attempting to re-attach to
        the PVC.  If unsuccessful, automatically reschedules another attempt later.

Parameters:

        DeferredContext
                This is a pointer to our Adapter object for this PVC.

--*/
{
    NDIS_STATUS Status;
    KIRQL OldIrql;

    PDIGILAN_LOGICAL_ADAPTER pAdapter = DeferredContext;

    DBG_ENTER( "X25Attach" );

    NdisAcquireSpinLock( &pAdapter->Lock );
    if (pAdapter->HaltReceived)
    {
        DBG_LEAVE_NOVAL( "X25Attach" );
        NdisReleaseSpinLock( &pAdapter->Lock );
        return;
    }
    else
        pAdapter->X25AttachPending = TRUE;

    NdisReleaseSpinLock( &pAdapter->Lock );

    KeAcquireSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock, &OldIrql );
    ASSERT( pAdapter->SyncProtocol.Private.X25.State == STATE_DETACHED );
    pAdapter->SyncProtocol.Private.X25.State = STATE_ATTACHING;
    KeReleaseSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock, OldIrql );

    {
        struct pvcattf PvcAttach = { 0 };
        PvcAttach.xl_type = XL_CTL;
        PvcAttach.xl_command = N_PVC_ATTACH;
        PvcAttach.lci = (USHORT)pAdapter->SyncProtocol.Private.X25.Channel;
        PvcAttach.sn_id = pAdapter->Snid;

        Status = DlwPutMsg( pAdapter,
                            (PVOID)&PvcAttach,
                            sizeof( PvcAttach ),
                            NULL, 0, 0 );
    }

    if ( !NT_SUCCESS( Status ) )
    {
        LARGE_INTEGER Timeout;
        KeAcquireSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock, &OldIrql );
        ASSERT( pAdapter->SyncProtocol.Private.X25.State == STATE_ATTACHING );
        pAdapter->SyncProtocol.Private.X25.State = STATE_DETACHED;
        KeReleaseSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock, OldIrql );

        if (!pAdapter->HaltReceived)
        {
            // Retry in 1s
            Timeout.QuadPart = 1 * 1000 * ONE_MILLISECOND;
            KeSetTimer( &pAdapter->SyncProtocol.Private.X25.AttachTimer, Timeout, 
                        &pAdapter->SyncProtocol.Private.X25.AttachDpc );
        }
    }

    pAdapter->X25AttachPending = FALSE;

    DBG_LEAVE_NOVAL( "X25Attach" );
    return;
}


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

Description:

        This routine initializes a PutMsg packet for X.25.   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.

--*/
{
    NDIS_STATUS Status;
    ULONG PacketLength;
    ULONG PacketType;
    KIRQL OldIrql;
    PVC_STATE State;

    DBG_ENTER( "X25Send" );

    KeAcquireSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock, &OldIrql );
    State = pAdapter->SyncProtocol.Private.X25.State;
    KeReleaseSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock, OldIrql );

    if ( State != STATE_ATTACHED )
    {
        // Since the PVC is not attached, the firmware will just drop any tx data
        // on the floor.  Don't bother sending it down.
        NdisAcquireSpinLock( &pAdapter->Lock );
        pAdapter->SendPacketsDropped++;
        NdisReleaseSpinLock( &pAdapter->Lock );
        Status = NDIS_STATUS_SUCCESS;
        goto Done;
    };

    Status = NDIS_STATUS_SUCCESS;
    PacketType = DigiLanGetPacketType( pNdisPacket );

    switch ( PacketType )
    {
    case PACKET_TYPE_IP:

        NdisQueryPacket( pNdisPacket, NULL, NULL, NULL, &PacketLength );
        if ( !PacketLength )
        {
            ASSERT( 0 && "*** X25Send: Zero length send packet! ***\n" );
            Status = NDIS_STATUS_FAILURE;
            goto Done;
        }

        {
            struct xdataf SendData = { 0 };
            SendData.xl_type = XL_DAT;
            SendData.xl_command = N_Data;
//                              SendData.More = 0;
//                              SendData.setDbit = 0;
//                              SendData.setQbit = 0;

            Status = DlwPutMsg( pAdapter, 
                                (PVOID)&SendData,
                                sizeof( SendData ),
                                pNdisPacket,
                                ETHERNET_MAC_HEADER_SIZE,
                                PacketLength - ETHERNET_MAC_HEADER_SIZE );
        }
                        
        break;

    case PACKET_TYPE_ARP:

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

        break;

    default:
        Status = NDIS_STATUS_SUCCESS;
        goto Done;
    }

 Done:
    DBG_LEAVE( "X25Send", Status );
    return Status;
}


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

Description:

        Handles the completion of a sent frame.  X25 sends two types of messages 
        (PutMsg's): regular data messages for IP traffic and PVC_ATTACH messages to
        attach to (start up) a PVC.  IP traffic has an associated NdisPacket on 
        which we send an NdisSendComplete indication.  PVC_ATTACH messages should
        not be indicated to Ndis.

Parameters:

        pAdapter
                Describes the X25 PVC 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.

Return Value:

        None

--*/
{
    DBG_ENTER( "X25SendComplete" );
        
    if ( pNdisPacket )
    {
        // This must be a data packet.  Indicate the send complete up to Ndis
        ASSERT( ControlLength == sizeof( struct xdataf ) );
        DigiQueueSendComplete( pAdapter, pNdisPacket, IOStatus );
        NdisAcquireSpinLock( &pAdapter->Lock );

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

        NdisReleaseSpinLock( &pAdapter->Lock );
    }
    else
    {
        // This was a PVC_ATTACH request.  
        ASSERT( ControlLength == sizeof( struct pvcattf ) );
    }

    DBG_LEAVE_NOVAL( "X25SendComplete" );
    return;
}


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

Description:

        Processes the received message.  If it's IP data, 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 X.25 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.

Return Value:

        None

--*/
{
    X25_PRIMITIVES *pMsg = pControl;
    DBG_ENTER( "X25Receive" );

    switch ( pMsg->Header.xl_command )
    {
    case N_Data:

        ASSERT( pMsg->Data.xl_type == XL_DAT );
        // BUGBUG Dbit and Qbit support is not implemented yet!
        ASSERT( !pMsg->Data.setDbit && !pMsg->Data.setQbit );
        X25OnData( pAdapter, pBuffer, DataLength, (BOOLEAN)pMsg->Data.More );

        break;

    case N_PVC_ATTACH:

        ASSERT( pMsg->PvcAttach.xl_type == XL_CTL );
        X25OnPvcAttach( pAdapter, pBuffer, pMsg->PvcAttach.result_code );
        break;

    case N_PVC_DETACH:

        ASSERT( pMsg->PvcDetach.xl_type == XL_CTL );
        X25OnPvcDetach( pAdapter, pBuffer, pMsg->PvcDetach.reason_code );
        break;

    case N_RI:

        ASSERT( pMsg->Reset.xl_type == XL_CTL );
        X25OnReset( pAdapter, pBuffer, pMsg->Reset.cause, pMsg->Reset.diag );
        break;

    default:

        DigiDump( DIGIERRORS|DIGIX25MSG, 
                  ("*** DigiLan: Unexpected X25 message received:"
                   "  xl_type 0x%x, xl_command 0x%x ***\n",
                   pMsg->Header.xl_type,
                   pMsg->Header.xl_command ) );

        if ( pBuffer )
            DigiFreeMem( pBuffer );
        break;
    }
    DBG_LEAVE_NOVAL( "X25Receive" );
    return;
}



VOID
X25OnData(
        IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
        IN PDIGILAN_FRAME_BUFFER pBuffer,
        IN ULONG DataLength,
        IN BOOLEAN bMore
        )
/*++

Description:

        Called by X25Receive to process a received data packet (i.e. IP data).

Parameters:

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

        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.

        bMore
                Indicates whether the More-bit was set in the X25 packet.

Return Value:

        None

--*/
{
    NDIS_STATUS Status;
    KIRQL OldIrql;

    DBG_ENTER( "X25OnData" );

#if DBG
    KeAcquireSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock, &OldIrql );
    ASSERT( pAdapter->SyncProtocol.Private.X25.State == STATE_ATTACHED );
    KeReleaseSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock, OldIrql );
#endif

    KeAcquireSpinLock( &pAdapter->SyncProtocol.Private.X25.MoreLock, &OldIrql );
        
    //
    // Always stick the packet into our MoreBuffer, whether the more-bit is set
    // or not.
    // 
        
    if ( !pAdapter->SyncProtocol.Private.X25.pMoreBuffer )
    {
        //
        // Previous frame did *not* have the more bit set, so this is the first
        // of 1+ X.25 packets to define an IP packet.  Don't forget to fill in the 
        // ethernet header.
        //

        PDIGILAN_ETHERNET_FRAME pFrame;

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

        pAdapter->SyncProtocol.Private.X25.pMoreBuffer = pBuffer;
        pAdapter->SyncProtocol.Private.X25.pMoreFrame = pFrame;
        pAdapter->SyncProtocol.Private.X25.MoreOffset = DataLength + 
            ETHERNET_MAC_HEADER_SIZE;
    }
    else
    {
        //
        // Already got a portion of the IP packet.  Append this packet to it.
        //
                
        if ( DataLength + pAdapter->SyncProtocol.Private.X25.MoreOffset > 
             sizeof( DIGILAN_ETHERNET_FRAME ) )
        {
            // Too big, drop both this X25 packet and the MoreBuffer packet.
            DigiFreeMem( pAdapter->SyncProtocol.Private.X25.pMoreBuffer );
            pAdapter->SyncProtocol.Private.X25.pMoreBuffer = NULL;
            pAdapter->SyncProtocol.Private.X25.pMoreFrame = NULL;
            pAdapter->SyncProtocol.Private.X25.MoreOffset = 0;
            KeReleaseSpinLock( &pAdapter->SyncProtocol.Private.X25.MoreLock, OldIrql );

            NdisAcquireSpinLock( &pAdapter->Lock );
            pAdapter->RecvPacketsDropped++;
            NdisReleaseSpinLock( &pAdapter->Lock );
            goto Done;
        }
        
        //
        // Copy this x25 packet into the MoreBuffer, then free it.
        //

        NdisMoveMemory( (PUCHAR)pAdapter->SyncProtocol.Private.X25.pMoreFrame + 
                        pAdapter->SyncProtocol.Private.X25.MoreOffset,
                        pBuffer->Data,
                        DataLength );

        pAdapter->SyncProtocol.Private.X25.MoreOffset += DataLength;
        DigiFreeMem( pBuffer );
    }

    if ( !bMore )
    {
        //
        // No More-bit means we have a complete IP packet/frame.  Indicate it up.
        //

        ULONG PayloadLength;
        PDIGILAN_ETHERNET_FRAME pMoreFrame;
        PDIGILAN_FRAME_BUFFER pMoreBuffer;

        pMoreBuffer = pAdapter->SyncProtocol.Private.X25.pMoreBuffer;
        pMoreFrame = pAdapter->SyncProtocol.Private.X25.pMoreFrame;
        PayloadLength = pAdapter->SyncProtocol.Private.X25.MoreOffset - 
            ETHERNET_MAC_HEADER_SIZE;
                
        pAdapter->SyncProtocol.Private.X25.pMoreBuffer = NULL;
        pAdapter->SyncProtocol.Private.X25.pMoreFrame = NULL;
        pAdapter->SyncProtocol.Private.X25.MoreOffset = 0;

        KeReleaseSpinLock( &pAdapter->SyncProtocol.Private.X25.MoreLock, OldIrql );

        Status = DigiQueueIndicateReceive( pAdapter,
                                           pMoreBuffer,
                                           pMoreFrame,
                                           PayloadLength );

        if ( !NT_SUCCESS( Status ) )
        {
            // Couldn't queue up receive indication.  Drop packet on the floor.
            DigiDump( DIGIERRORS, 
                      ("*** X25Receive: Couldn't queue receive indication - dropping frame! ***\n") );

            DigiFreeMem( pBuffer );
            NdisAcquireSpinLock( &pAdapter->Lock );
            pAdapter->RecvPacketsDropped++;
            NdisReleaseSpinLock( &pAdapter->Lock );
        }
        else
        {
            NdisAcquireSpinLock( &pAdapter->Lock );
            pAdapter->PacketsReceived++;
            NdisReleaseSpinLock( &pAdapter->Lock );
        }
    }
    else
    {
        //
        // More bit set - wait...
        KeReleaseSpinLock( &pAdapter->SyncProtocol.Private.X25.MoreLock, OldIrql );
    }
 Done:
    DBG_LEAVE_NOVAL( "X25OnData" );
}



VOID
X25OnPvcAttach(
        IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
        IN PDIGILAN_FRAME_BUFFER pBuffer,
        IN ULONG ResultCode
        )
/*++

Description:

        Called by X25Receive to process a received PVC_ATTACH acknowledge message.

Parameters:

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

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

        ResultCode
                This is the result_code returned in the message.  Zero if attach was 
                successful (ACK) or non-zero if unsuccessful (NAK).

Return Value:

        None

--*/
{
    KIRQL OldIrql;

#if DBG
    {
        LARGE_INTEGER LargeTime;
        TIME_FIELDS Time;

        KeQuerySystemTime( &LargeTime );
        RtlTimeToTimeFields( &LargeTime, &Time );

        DigiDump( DIGIX25MSG, 
                  ("X25OnPvcAttach: PVC_ATTACH %s on %s at %d:%d:%d.  Result code 0x%x\n",
                   ResultCode ? "NAK" : "Ack",
                   pAdapter->Name,
                   Time.Hour, Time.Minute, Time.Second,
                   ResultCode ) );
    }
#endif

    KeAcquireSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock, &OldIrql );

    ASSERT( pAdapter->SyncProtocol.Private.X25.State != STATE_UNKNOWN );

    pAdapter->SyncProtocol.Private.X25.State = ResultCode ? 
        StateTable[ pAdapter->SyncProtocol.Private.X25.State ][ ACTION_RECEIVED_ATTACH_NAK ] :
        StateTable[ pAdapter->SyncProtocol.Private.X25.State ][ ACTION_RECEIVED_ATTACH_ACK ];

    ASSERT( pAdapter->SyncProtocol.Private.X25.State != STATE_UNKNOWN );

    if ( pAdapter->SyncProtocol.Private.X25.State != STATE_ATTACHED )
    {
        LARGE_INTEGER Timeout;

        // Retry in 100ms
        Timeout.QuadPart = 100 * ONE_MILLISECOND;
        KeSetTimer( &pAdapter->SyncProtocol.Private.X25.AttachTimer, 
                    Timeout, 
                    &pAdapter->SyncProtocol.Private.X25.AttachDpc );
    }

    KeReleaseSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock, OldIrql );

    // Indicate to NDIS there is a change in Status in the VC
    if ( pAdapter->SyncProtocol.Private.X25.State == STATE_ATTACHED )
    {
        DigiQueueIndicateStatus( pAdapter, NDIS_STATUS_MEDIA_CONNECT, NULL, 0 );
    }
    else
    {
        DigiQueueIndicateStatus( pAdapter, NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0);
    }
        
    // Don't forget to free the buffer!
    ASSERT( pBuffer );
    DigiFreeMem( pBuffer );
}



VOID
X25OnPvcDetach(
        IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
        IN PDIGILAN_FRAME_BUFFER pBuffer,
        IN ULONG Reason
        )
/*++

Description:

        Called by X25Receive to process a received PVC_DETACH indication/acknowledge message.

Parameters:

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

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

        Reason
                The reason_code value found in the PVC_DETACH message.

Return Value:

        None

--*/
{
    LARGE_INTEGER Timeout;
    KIRQL OldIrql;

#if DBG
    {
        LARGE_INTEGER LargeTime;
        TIME_FIELDS Time;

        KeQuerySystemTime( &LargeTime );
        RtlTimeToTimeFields( &LargeTime, &Time );

        DigiDump( DIGIX25MSG, 
                  ("X25OnPvcDetach: PVC_DETACH on %s at %d:%d:%d.  Reason code 0x%x\n",
                   pAdapter->Name,
                   Time.Hour, Time.Minute, Time.Second,
                   Reason ) );
    }
#endif

    KeAcquireSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock, &OldIrql );

    pAdapter->SyncProtocol.Private.X25.State = 
        StateTable[ pAdapter->SyncProtocol.Private.X25.State ][ ACTION_RECEIVED_DETACH ];
    ASSERT( pAdapter->SyncProtocol.Private.X25.State != STATE_UNKNOWN );

    KeReleaseSpinLock( &pAdapter->SyncProtocol.Private.X25.StateLock, OldIrql );

    // Retry in 20 ms
    Timeout.QuadPart = -( 20 * 10000 );

    KeSetTimer( &pAdapter->SyncProtocol.Private.X25.AttachTimer, 
                Timeout, 
                &pAdapter->SyncProtocol.Private.X25.AttachDpc );

    // Indicate to NDIS there is a change in Status in the VC
    {
        DigiQueueIndicateStatus( pAdapter, NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0 );
    }
        
    // Don't forget to free the buffer!
    ASSERT( pBuffer );
    DigiFreeMem( pBuffer );
}



VOID
X25OnReset(
        IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
        IN PDIGILAN_FRAME_BUFFER pBuffer,
        IN ULONG Cause,
        IN ULONG Diag
        )
/*++

Description:

        Called by X25Receive to process a received reset indication (N_RI) message.

Parameters:

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

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

        Cause
                X25 cause code found in reset packet.

        Diag
                X25 diag code found in reset packet

Return Value:

        None

--*/
{
    KIRQL OldIrql;

#if DBG
    {
        LARGE_INTEGER LargeTime;
        TIME_FIELDS Time;

        KeQuerySystemTime( &LargeTime );
        RtlTimeToTimeFields( &LargeTime, &Time );

        DigiDump( DIGIX25MSG, 
                  ("X25OnReset: Reset Indicated on %s at %d:%d:%d.  Cause 0x%x, Diag 0x%x\n",
                   pAdapter->Name,
                   Time.Hour, Time.Minute, Time.Second,
                   Cause,
                   Diag ) );
    }
#endif

    // 
    // Clear our cumulative "More" buffer, since a reset invalidates any old data.
    //

    KeAcquireSpinLock( &pAdapter->SyncProtocol.Private.X25.MoreLock, &OldIrql );

    if ( pAdapter->SyncProtocol.Private.X25.pMoreBuffer )
    {
        DigiFreeMem( pAdapter->SyncProtocol.Private.X25.pMoreBuffer );
        pAdapter->SyncProtocol.Private.X25.pMoreBuffer = NULL;
        pAdapter->SyncProtocol.Private.X25.pMoreFrame = NULL;
        pAdapter->SyncProtocol.Private.X25.MoreOffset = 0;
    }
    else
    {
        ASSERT( pAdapter->SyncProtocol.Private.X25.pMoreFrame == NULL );
        ASSERT( pAdapter->SyncProtocol.Private.X25.MoreOffset == 0 );
    }

    KeReleaseSpinLock( &pAdapter->SyncProtocol.Private.X25.MoreLock, OldIrql );

    // Don't forget to free the buffer!
    ASSERT( pBuffer );
    DigiFreeMem( pBuffer );
}
