/*++

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

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

		// Retry in 20 ms
		Timeout.QuadPart = -( 20 * 10000 );
		KeSetTimer( &pAdapter->SyncProtocol.Private.X25.AttachTimer, Timeout, 
		            &pAdapter->SyncProtocol.Private.X25.AttachDpc );
	}
		
	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 )
	{
		// Retry in 20 ms:
		LARGE_INTEGER Timeout;
		Timeout.QuadPart = -( 20 * 10000 );		

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