/*++

*****************************************************************************
*                                                                           *
*  This software contains proprietary and confiential 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 copyrighted work of Digi International Inc.       *
*  Except as permitted by federal law, 17 USC 117, copying is strictly      *
*  prohibited.                                                              *
*                                                                           *
*****************************************************************************

Module Name:

   pad.c

Abstract:

   This module provides needed functionality for X.25 PAD connections.

   It will do crc generation and checking, transparency checking, and
   any thing else necessary to support PAD clients.

Revision History:

   $Log: /Components/Windows/NT/Sync/Wan/PAD.C $
 * 
 * 6     4/28/97 1:55p Mikez
 * Updated to use DsApi library.  Uses IoBuildDeviceIoControlRequest to
 * alloc Irps causing two problems.  Because ioctls are METHOD_BUFFERED, a
 * systemBuffer is alloc'd, but, because the completionroutines return
 * STATUS_MORE_PROCESSING_REQUIRED, never gets freed.  Also, SystemBuffer
 * is not copied to OutputBuffer until after completionRoutine returns
 * STATUS_SUCCESS.  Need to move from METHOD_BUFFER or from using
 * IoBuildDeviceIoControlRequest.
 * 
 * 5     3/03/97 9:23a Mikez
 * Updated to use async completions on MakeCall, Drop and Answer TAPI OID
 * requests.
 * 
 * 4     1/28/97 10:26a Mikez
 * Removed downlevel ras support.
 * 
 * 3     12/06/96 2:28p Mikez
 * Modified to support new SXB paridigm of "one NT device per adapter"
 * rather than the older "one NT device per port".
 * 
 * 2     10/28/96 1:25p Mikez
 * Updated to support latest firmware release, v1.1.2b.
 * 
 * 1     3/11/96 5:06p Mikez
 * Revision 1.4  1995/10/06  06:59:59  Craig_Harrison
 * Version 1.0.1.2 
 * 
 * Revision 1.3  1995/09/15  00:00:00  Craig_Harrison
 * First beta release
 * 
 * Revision 1.1  1995/06/01  10:19:06  rik
 * Initial revision
 * 

--*/
#include <ntddk.h>
#include <ntddndis.h>
#include <ndis.h>
#include <ndiswan.h>

#include "DsApi.h"
#include "digix25.h"
#include "digix25p.h"
#include "digifile.h"
#include <digialloc.h>

#include "framing.h"
#include "crc.h"
#include "compress.h"

//
// Local Prototypes
//
BOOLEAN RecvAsyncTransparency( PDIGIWAN_CALL_OBJECT CallObject );

BOOLEAN DetectFramingType( PDIGIWAN_CALL_OBJECT CallObject,
                           PUCHAR FrameStart );

VOID ReadPPPFrame( PDIGIWAN_CALL_OBJECT CallObject,
                   PUCHAR ByteBuffer,
                   ULONG Length );

ULONG PADThroughputClassTable[] = {
   64000, 64000, 64000, 75, 150, 300, 600, 1200,
   2400, 4800, 9600, 19200, 48000, 64000, 64000, 64000 };

//
// We create a static table for a 32-bit ACCM lookup mask.
//
ULONG ACCM32Lookup[] =
{
   0x00000001, 0x00000002, 0x00000004, 0x00000008,
   0x00000010, 0x00000020, 0x00000040, 0x00000080,
   0x00000100, 0x00000200, 0x00000400, 0x00000800,
   0x00001000, 0x00002000, 0x00004000, 0x00008000,
   0x00010000, 0x00020000, 0x00040000, 0x00080000,
   0x00100000, 0x00200000, 0x00400000, 0x00800000,
   0x01000000, 0x02000000, 0x04000000, 0x08000000,
   0x10000000, 0x20000000, 0x40000000, 0x80000000
};


BOOLEAN RecvAsyncTransparency( PDIGIWAN_CALL_OBJECT CallObject )
/*++

Routine Description:

   The following description was taken from RFC 1662.  In my implementation,
   I know the CallObject->RxBuffer should have a frame which I do
   Transparency checking on.  I make the modification directly within the
   CallObject->RxBuffer, so when we return, the buffer may not be
   exactly the same.

   An octet stuffing procedure is used.  The Control Escape octet is
   defined as binary 01111101 (hexadecimal 0x7d), most significant bit
   first.

   Receiving implementations MUST correctly process all Control Escape
   sequences.

   On reception, prior to FCS computation, each octet with value less
   than hexadecimal 0x20 is checked.  If it is flagged in the receiving
   ACCM, it is simply removed (it may have been inserted by intervening
   data communications equipment).  Each Control Escape octet is also
   removed, and the following octet is exclusive-or'd with hexadecimal
   0x20, unless it is the Flag Sequence (which aborts a frame).

   A few examples may make this more clear.  Escaped data is transmitted
   on the link as follows:

      0x7e is encoded as 0x7d, 0x5e.    (Flag Sequence)
      0x7d is encoded as 0x7d, 0x5d.    (Control Escape)
      0x03 is encoded as 0x7d, 0x23.    (ETX)

   Some modems with software flow control may intercept outgoing DC1 and
   DC3 ignoring the 8th (parity) bit.  This data would be transmitted on
   the link as follows:

      0x11 is encoded as 0x7d, 0x31.    (XON)
      0x13 is encoded as 0x7d, 0x33.    (XOFF)
      0x91 is encoded as 0x7d, 0xb1.    (XON with parity set)
      0x93 is encoded as 0x7d, 0xb3.    (XOFF with parity set)


Arguments:

    CallObject - pointer to the instance of the call which contains the
                 the buffer.

Return Value:

    TRUE - No errors were encounter appling Transparency to the
           CallObject->RxBuffer.

    FALSE - An error was found while appling Transparency to the
            CallObject->RxBuffer.

--*/
{
   PUCHAR CurrentLocation=CallObject->RxBuffer;
   PUCHAR TransLocation=CallObject->RxBuffer;
   PUCHAR RxBuffer=CallObject->RxBuffer;
   ULONG Length=CallObject->RxOffset;

#if DBG
	if ( CallObject->SpecialFlags & CALL_OBJ_PPP_FRAMING )
	{
	   ASSERT( CallObject->RxBuffer[0] == 0x7E );
	   ASSERT( CallObject->RxBuffer[Length-1] == 0x7E );
	}
	else
	{
		DigiDump( DIGIERRORS, ("*** WAN: Invalid Framing Setting ***!\n") );
		DbgBreakPoint();
	}
#endif

   // Increment past Flag Sequence.
   CurrentLocation++;

   // Decrement so we don't process closing Flag Sequence.
   Length--;

   while( CurrentLocation < (RxBuffer+Length) )
   {
      if( *CurrentLocation < 0x20 )
      {
         // We need to determine if this character was flagged in the
         // receive ACCM.  If it is, then just discard it, if it isn't
         // then we just put it into the frame.
         if( ACCM32Lookup[*CurrentLocation] & CallObject->RecvACCM )
         {
            // Okay, its cool to put this in the data stream.
            *TransLocation++ = *CurrentLocation++;
         }
         else
         {
            // Just drop on the floor.
            CurrentLocation++;
         }

      }
      else if( *CurrentLocation == 0x7D )
      {
         if( CurrentLocation[1] == 0x7E )
         {
            // We have a transmit abort situation.  Just return indicating
            // a valid frame was not found.
            return( FALSE );
         }

         CurrentLocation++;

         // Apply reverse transparency.
         *TransLocation++ = *CurrentLocation++ ^ 0x20;
      }
      else
         *TransLocation++ = *CurrentLocation++;
   }

   // Fixup the RxOffset value.
   CallObject->RxOffset = (ULONG)(TransLocation - RxBuffer);
   return( TRUE );

}  // end RecvAsynTransparency



VOID SendAsyncTransparency( PDIGIWAN_CALL_OBJECT CallObject,
                            PUCHAR OriginalBuffer,
                            ULONG  OriginalLength,
                            PUCHAR NewBuffer,
                            PULONG NewLength,
                            ULONG Type )
/*++

Routine Description:

   The following description was taken from RFC 1662.  In my implementation,
   I know the CallObject->RxBuffer should have a frame which I do
   Transparency checking on.  I make the modification directly within the
   CallObject->RxBuffer, so when we return, the buffer may not be
   exactly the same.

   An octet stuffing procedure is used.  The Control Escape octet is
   defined as binary 01111101 (hexadecimal 0x7d), most significant bit
   first.

   As a minimum, sending implementations MUST escape the Flag Sequence
   and Control Escape octets.

   After FCS computation, the transmitter examines the entire frame
   between the two Flag Sequences.  Each Flag Sequence, Control Escape
   octet, and any octet which is flagged in the sending Async-Control-
   Character-Map (ACCM), is replaced by a two octet sequence consisting
   of the Control Escape octet followed by the original octet
   exclusive-or'd with hexadecimal 0x20.

      This is bit 5 complemented, where the bit positions are numbered
      76543210 (the 6th bit as used in ISO numbered 87654321 -- BEWARE
      when comparing documents).

   A few examples may make this more clear.  Escaped data is transmitted
   on the link as follows:

      0x7e is encoded as 0x7d, 0x5e.    (Flag Sequence)
      0x7d is encoded as 0x7d, 0x5d.    (Control Escape)
      0x03 is encoded as 0x7d, 0x23.    (ETX)

   Some modems with software flow control may intercept outgoing DC1 and
   DC3 ignoring the 8th (parity) bit.  This data would be transmitted on
   the link as follows:

      0x11 is encoded as 0x7d, 0x31.    (XON)
      0x13 is encoded as 0x7d, 0x33.    (XOFF)
      0x91 is encoded as 0x7d, 0xb1.    (XON with parity set)
      0x93 is encoded as 0x7d, 0xb3.    (XOFF with parity set)


Arguments:

   OriginalBuffer - Pointer to the buffer which need transmit transparency
                    done on it.

   OriginalLength - Number of bytes pointed to by OriginalBuffer.

   NewBuffer - pointer to the buffer which will receive the new frame
               with transparency applied.

   NewLength - pointer to store the length of the new buffer.

   Type - Flag indicating varitions to RFC 1662.  In the case of RAS framing,
          the above description if followed except 0x7d is NOT
          escaped, which is the case in PPP framing.
            

Return Value:

    TRUE - No errors were encounter appling Transparency to the
           CallObject->RxBuffer.

    FALSE - An error was found while appling Transparency to the
            CallObject->RxBuffer.

--*/
{
   ULONG i;
   ULONG TempLength;

   TempLength = 0;

   for( i = 0; i < OriginalLength; i++ )
   {
      if( ((OriginalBuffer[i] < 0x20) &&
          (ACCM32Lookup[OriginalBuffer[i]] & CallObject->SendACCM)) ||
          (OriginalBuffer[i] == 0x7d) ||
          ((OriginalBuffer[i] == 0x7e) &&
            ((Type & PPP_TRANSPARENCY) == PPP_TRANSPARENCY)) )
      {
         NewBuffer[TempLength++] = 0x7d;
         NewBuffer[TempLength++] = OriginalBuffer[i] ^ 0x20;
      }
      else
      {
         NewBuffer[TempLength++] = OriginalBuffer[i];
      }
   }

   *NewLength = TempLength-1;
}  // end SendAsyncTransparency


VOID QueuePadByteStream( PDIGIWAN_CALL_OBJECT CallObject,
                         PUCHAR ByteBuffer,
                         ULONG Length )
/*++

Routine Description:

   We take the new data received and process it from a byte stream into
   frames.

Arguments:

    CallObject - pointer to the call instance information.

    ByteBuffer - pointer to the buffer which needs processing.

    Length - byte length of ByteBuffer;

--*/
{
   if( (CallObject->SpecialFlags & CALL_OBJ_FRAME_MASK) == CALL_OBJ_UNKNOWN_FRAMING )
   {
      //
      // We don't know what type of framing we are using so we sniff the frame
      // and try to figure it out.
      //
      if( Length < 3 )  // DetectFramingType expects min. 3 bytes for sniffing.
         return;

      if( !DetectFramingType( CallObject, ByteBuffer ) )
         return;
   }

   //
   // Okay, we know what find of framing for sure.  Lets start turning that
   // byte stream in to a frame stream.
   //
   if( CallObject->SpecialFlags & CALL_OBJ_PPP_FRAMING )
   {
      ReadPPPFrame( CallObject,
                    ByteBuffer,
                    Length );
   }
   else
   {
      ASSERT( FALSE && "Bad framing type even though we knew it a few lines ago!" );
   }

}  // end QueuePadByteStream



BOOLEAN DetectFramingType( PDIGIWAN_CALL_OBJECT CallObject,
                           PUCHAR FrameStart )
/*++

Routine Description:

   We try to determine what type of framing is being sent across
   the media.  Is it a downlevel client using RAS framing, or is it
   a PPP client???

   Note:  The following documents RAS framing.  For an explaination of
          PPP framing, see RFC 1662.

Arguments:

   CallObject - pointer to the call instance information.

   FrameStart - pointer to the buffer which we are suppose to sniff.
                Note:  I assume there are at least 2 bytes in the FrameStart
                       buffer which I can look at!

Return Value:

   TRUE - We were able to detect type type of framing.

   FALSE - We were unable to determine the type of framing.

--*/
{
   ASSERT( (CallObject->SpecialFlags & CALL_OBJ_FRAME_MASK)==CALL_OBJ_UNKNOWN_FRAMING );

   if( (FrameStart[0] == PPP_FLAG && FrameStart[1] == PPP_ADDRESS) ||
       (FrameStart[0] == PPP_ADDRESS && FrameStart[1] == PPP_ESCAPE &&
        FrameStart[2] == 0x23) )
   {
      CallObject->SendFramingBits = CallObject->RecvFramingBits = PPP_FRAMING;
      CallObject->SpecialFlags |= CALL_OBJ_PPP_FRAMING;
      return( TRUE );
   }

   return( FALSE );
}  // end DetectFramingType



VOID AssembleRASFrame( PDIGIWAN_CALL_OBJECT CallObject,
                       PNDIS_WAN_PACKET Packet,
                       PNDIS_WAN_PACKET OriginalPacket )
/*++

Routine Description:

   Given a packet to send from NdisWan, we put it on the wire using
   RAS framing for the downlevel async RAS client.  I know that
   the packets current buffer has enough preamble and postamble for
   the RAS framing info.

Arguments:

   CallObject - pointer to the call instance information.

   Packet - pointer to a new ndiswan packet which will contain the modified
            packet sent on the media.  We use OriginalPacket to create this
            modified packet.

   OriginalPacket - pointer to the original ndiswan packet given to us
                    by ndiswan.

Return Value:

   None.

--*/
{
   PUCHAR StartOfFrame, EndOfFrame;
   USHORT CalcCRC;

   if( CallObject->SendFeatureBits & XON_XOFF_SUPPORTED )
   {
      //
      // Note:  We put the Header and closing flag onto the original
      //        buffer because we know there is enough room, so we
      //        can calculate the correct CRC.
      //
      StartOfFrame = OriginalPacket->CurrentBuffer - 3;
      EndOfFrame = OriginalPacket->CurrentBuffer + OriginalPacket->CurrentLength;
      
      StartOfFrame[0] = (SOH_DEST | SOH_ESCAPE);
      StartOfFrame[1] = (UCHAR)((Packet->CurrentLength) >> 8);
      StartOfFrame[2] = (UCHAR)(Packet->CurrentLength);
      
      EndOfFrame[0] = ETX;
      
      //
      // Calculate CRC.  RAS framing is wierd in that you need to include
      // the closing flag in the CRC calculation, but don't include the
      // opening flag.
      //
      CalcCRC = RASCalcCRC( StartOfFrame,
                            (USHORT)(Packet->CurrentLength + 4) );
      
      //
      // Okay, now readust the start pointer so we do the correct thing
      //
      StartOfFrame = Packet->CurrentBuffer - 4;
      StartOfFrame[0] = (SYN | 0x20);
      StartOfFrame[1] = (SOH_DEST | SOH_ESCAPE);

      //
      // Calculate length of frame using two 7 bit bytes with the top bit
      // OR'd in to avoid it being a control char.
      //
      StartOfFrame[2] = (UCHAR)((((Packet->CurrentLength) >> 7) & 0x7f) | 0x80);
      StartOfFrame[3] = (UCHAR)(((Packet->CurrentLength) & 0x7f) | 0x80);
      
      
      SendAsyncTransparency( CallObject,
                             OriginalPacket->CurrentBuffer,
                             OriginalPacket->CurrentLength + 4,
                             &Packet->CurrentBuffer[4],
                             &Packet->CurrentLength,
                             RAS_TRANSPARENCY );
      
      Packet->CurrentLength++;   // Add the opening flag.
      
      StartOfFrame[Packet->CurrentLength] = (UCHAR)(CalcCRC);
      EndOfFrame[Packet->CurrentLength+1] = (UCHAR)(CalcCRC >> 8);
      Packet->CurrentLength += 2;   // Add the 16-bit CRC.
      
      //
      // Now, adjust the buffer pointer and the length to include the
      // framing we just did.
      //
      Packet->CurrentBuffer = StartOfFrame;
   }
   else
   {
      StartOfFrame = OriginalPacket->CurrentBuffer - 4;
      EndOfFrame = OriginalPacket->CurrentBuffer + OriginalPacket->CurrentLength;
      
      StartOfFrame[0] = SYN;
      StartOfFrame[1] = SOH_DEST;
      StartOfFrame[2] = (UCHAR)((Packet->CurrentLength) >> 8);
      StartOfFrame[3] = (UCHAR)(Packet->CurrentLength);
      
      EndOfFrame[0] = ETX;

      CalcCRC = RASCalcCRC( StartOfFrame + 1,
                            (USHORT)(Packet->CurrentLength + 4) );

      EndOfFrame[1] = (UCHAR)(CalcCRC);
      EndOfFrame[2] = (UCHAR)(CalcCRC >> 8);
      
   }

}  // end AssembleRASFrame



VOID AssemblePPPFrame( PDIGIWAN_CALL_OBJECT CallObject,
                       PNDIS_WAN_PACKET Packet,
                       PNDIS_WAN_PACKET OriginalPacket )
/*++

Routine Description:

   This routine is called to create an appropriate PPP frame for an
   async. PAD client.

Arguments:

   CallObject - pointer to the call instance information.

   Packet - pointer to a new ndiswan packet which will contain the modified
            packet sent on the media.  We use OriginalPacket to create this
            modified packet.

   OriginalPacket - pointer to the original ndiswan packet given to us
                    by ndiswan.

Return Value:

   None.

--*/
{
   USHORT FrameFCS;

   //
   // Okay, we have the Wan packet fixed up.  Now apply FCS and
   // transparency to the frame.
   //

   FrameFCS = pppfcs16( OriginalPacket->CurrentBuffer,
                        OriginalPacket->CurrentLength );

   FrameFCS ^= 0xffff;             /* complement */


   //
   // Now, run the buffer through transparency.  We put an opening
   // flag sequence number at the beginning of the frame and pass
   // on byte beyond that to the conversion routine.
   //
   Packet->CurrentBuffer[0] = 0x7E;

   //
   // We add the FCS to end of the buffer.  I know this space is there
   // because I asked for tailpadding sufficient for a 16-bit FCS.
   //
   // Note: I do this because the FCS must be included in the
   //       transparency checking where the FCS has 8-bit values which
   //       are less than 0x20 for ACCM.

   OriginalPacket->CurrentBuffer[OriginalPacket->CurrentLength] =
                                                      (FrameFCS & 0x00ff);

   OriginalPacket->CurrentBuffer[OriginalPacket->CurrentLength+1] =
                                                ((FrameFCS >> 8) & 0x00ff);

   SendAsyncTransparency( CallObject,
                          OriginalPacket->CurrentBuffer,
                          OriginalPacket->CurrentLength + sizeof(USHORT),
                          &Packet->CurrentBuffer[1],
                          &Packet->CurrentLength,
                          PPP_TRANSPARENCY );

   Packet->CurrentLength++;   // Add the opening flag sequence.

   //
   // Add one byte for the closing flag sequence.
   //
   Packet->CurrentLength++;   // Add the closingflag sequence.
   Packet->CurrentBuffer[Packet->CurrentLength] = 0x7e;
   Packet->CurrentLength++;

	ASSERT( Packet->CurrentLength < 
	        (ULONG)( Packet->EndBuffer - Packet->CurrentBuffer ) );
}  // end AssemblePPPFrame



VOID ReadPPPFrame( PDIGIWAN_CALL_OBJECT CallObject,
                   PUCHAR ByteBuffer,
                   ULONG Length )
/*++

Routine Description:

   This routine will assemble a PPP frame from data being received.
   I use the CallObject->RxBuffer to store a partially received
   frame.

Arguments:

   CallObject - pointer to the call instance information.

   ByteBuffer - pointer to the newly received data.

   Length - size of ByteBuffer.

Return Value:

   None.

--*/
{
   PUCHAR CurrentLocation=ByteBuffer;
	PUCHAR BufferEnd = ByteBuffer+Length;
   BOOLEAN ValidFrame;
	BOOLEAN bEndOfFrame;
	USHORT CalculatedFCS;

BeginNewFrame:;

	// Do we have an RxBuffer?  If not, alloc one.

	if ( !CallObject->RxBuffer )
	{
		CallObject->RxBuffer = DigiAllocMem( NonPagedPool,
		                                     CallObject->CurrentMaxMtuSize );
		CallObject->RxOffset = 0;

		if ( !CallObject->RxBuffer )
		{
	   	// Failed!  Drop packet.
	   	CallObject->MtuOverrun++;
	   	AdapterContextWanIndicateFragment( CallObject, WAN_ERROR_BUFFEROVERRUN );
	      DigiDump( DIGIWANERR, 
	                ("ReadPPPFrame: Couldn't alloc buffer.  Dropping data!\n") );
			return;
		}
	}

   if( CallObject->RxOffset == 0 )
   {
		// Find the starting flag
      while( CurrentLocation < BufferEnd && *CurrentLocation != PPP_FLAG )
         CurrentLocation++;

      if( CurrentLocation == BufferEnd )
         // Didn't find any flags
         return;

      CallObject->RxBuffer[CallObject->RxOffset++] = *CurrentLocation++;
   }
   else
      ASSERT( CallObject->RxBuffer[0] == PPP_FLAG );

   if ( CallObject->RxOffset == 1 )
   {
	   // Found opening flag.  There may be multiple flag chars.  Scan past all 
	   // subsequent flag bytes.
	   while( CurrentLocation < BufferEnd && *CurrentLocation == PPP_FLAG )
	      CurrentLocation++;
   }

#if DBG
	if ( CallObject->RxOffset > 1 )
	{
		DigiDump( DIGIDIAG3, 
					 ( "Concatenating rx packet; RxOffset 0x%x\n", 
					 CallObject->RxOffset ) );
	}
#endif

   //
   // Scan for closing flags.  Once found, process frame.
   //

	bEndOfFrame = FALSE;
//------------------------------------------------------------ MLZ 12/11/97 {
#if 0
	while( CurrentLocation < BufferEnd )
	{
		if ( CallObject->RxOffset >= CallObject->CurrentMaxMtuSize )
		{
			// Ran out of room.  Frame too large, drop it!
			DigiDump( DIGIERRORS, ("*** Error: ReadPPPFrame buffer overflow! ***\n" ) );
			CallObject->FramesDropped++;
			CallObject->RxOffset = 0;

		   // Indicate framing error to NdisWan
		   AdapterContextWanIndicateFragment( CallObject, WAN_ERROR_ALIGNMENT );
			goto BeginNewFrame;
		}

		if ( *CurrentLocation == PPP_FLAG )
		{
			// Found closing flag.  Copy it, but don't increment CurrentLocation.  The
			// closing flag at CurrentLocation may be re-used as the opening flag of
			// the next frame.
			CallObject->RxBuffer[CallObject->RxOffset++] = *CurrentLocation;
			bEndOfFrame = TRUE;
			break;
		}

	   // Not found yet, try again...
	   CallObject->RxBuffer[CallObject->RxOffset++] = *CurrentLocation++;
	}

	if ( !bEndOfFrame )
		// No closing flag.  Wait for more bytes to complete this frame.
		return;
#else
	while( CurrentLocation < BufferEnd && 
	       CallObject->RxOffset < CallObject->CurrentMaxMtuSize )
	{
	   CallObject->RxBuffer[CallObject->RxOffset++] = *CurrentLocation;

		if ( *CurrentLocation != PPP_FLAG )
		{
			CurrentLocation++;
			continue;
		}
		else
		{
			// Found closing flag.  Don't increment CurrentLocation.  The closing flag 
			// at CurrentLocation may be re-used as the opening flag of the next frame.
			bEndOfFrame = TRUE;
			break;
		}
	}

	ASSERT( CallObject->RxOffset <= CallObject->CurrentMaxMtuSize );

	if ( !bEndOfFrame )
	{
		if ( CallObject->RxOffset < CallObject->CurrentMaxMtuSize )
		{
			// Still more room, wait for more data to complete frame.
			return;
		}
		else
		{
			// Ran out of room.  Frame too large, drop it!
			DigiDump( DIGIERRORS, ("*** Error: ReadPPPFrame buffer overflow! ***\n" ) );
			CallObject->FramesDropped++;
			CallObject->RxOffset = 0;

		   // Indicate framing error to NdisWan
		   AdapterContextWanIndicateFragment( CallObject, WAN_ERROR_ALIGNMENT );
			goto BeginNewFrame;
		}
	}
#endif
//------------------------------------------------------------ MLZ 12/11/97 }

   // Found closing flag, process frame.

	if( CallObject->SpecialFlags & CALL_OBJ_DROP_FRAME )
	{
	   // Need to drop this frame.
	   CallObject->SpecialFlags &= ~CALL_OBJ_DROP_FRAME;
	   CallObject->FramesDropped++;
	   CallObject->RxOffset = 0;

	   // Indicate framing error to NdisWan
	   AdapterContextWanIndicateFragment( CallObject, WAN_ERROR_ALIGNMENT );
		goto BeginNewFrame;
	}

	ASSERT( CallObject->RxOffset <= CallObject->CurrentMaxMtuSize );
	DigiDump( DIGIDIAG3, 
	          ("   RxOffset before transparency 0x%x\n",CallObject->RxOffset) );

	// Apply transparency and FCS checks on the frame.

   if( CallObject->RxOffset <= 2 )
   {
      // Too small for a frame, assume an FCS error.
      AdapterContextWanIndicateFragment( CallObject, WAN_ERROR_CRC );
      CallObject->RxOffset = 0;
      CallObject->FramesDropped++;
      goto BeginNewFrame;
   }

   ValidFrame = RecvAsyncTransparency( CallObject );

	ASSERT( CallObject->RxOffset <= CallObject->CurrentMaxMtuSize );
	DigiDump( DIGIDIAG3, 
	          ("   RxOffset after  transparency 0x%x\n",CallObject->RxOffset) );

   if( !ValidFrame )
   {
      // Detected a Framing error.
      DigiDump( DIGIWANERR, ("Dropping a frame due to bad transparency: "
                            "Length = 0x%x(%d)\n",
                            CallObject->RxOffset,
                            CallObject->RxOffset) );

      DigiDumpData( DIGIWANERRDATA,
                    CallObject->RxBuffer,
                    CallObject->RxOffset );

      AdapterContextWanIndicateFragment( CallObject, WAN_ERROR_FRAMING );

      CallObject->RxOffset = 0;
      CallObject->FramesDropped++;

      goto BeginNewFrame;
   }

   //
   // Verify the FCS.  If okay, put it on the queue for receive
   // indication.  If not correct, put a FCS indication on the queue.
   //
   
	CalculatedFCS = pppfcs16( CallObject->RxBuffer,
	                          CallObject->RxOffset );

	if ( PPPGOODFCS16 != CalculatedFCS )
	{
      // Found an FCS error.  Indicate it and go on.

      DigiDump( DIGIWANERR, ("Dropping frame due to bad FCS: Length = 0x%x(%d), "
                             "recv'd FCS = 0x%x-0x%x, calculated FCS = 0x%x-0x%x\n",
                             CallObject->RxOffset,
                             CallObject->RxOffset,
                             CallObject->RxBuffer[CallObject->RxOffset-2],
                             CallObject->RxBuffer[CallObject->RxOffset-1],
                             (CalculatedFCS & 0x00FF),
                             ((CalculatedFCS >> 8) & 0x00FF)) );

		DigiDump( DIGIWANERRDATA, ( "Bad Frame (including FCS):\n" ) );
      DigiDumpData( DIGIWANERRDATA,
                    CallObject->RxBuffer,
                    CallObject->RxOffset );

      AdapterContextWanIndicateFragment( CallObject, WAN_ERROR_CRC );
      CallObject->RxOffset = 0;
      CallObject->FramesDropped++;

      goto BeginNewFrame;
   }

   // Whew!  The frame checks out ok, indicate it up to NdisWan.

   AdapterContextWanIndicateReceive( CallObject, 
                                     CallObject->RxBuffer, 
                                     CallObject->RxOffset );

	// AdapterContext now owns the buffer.  Must alloc a new one, so null 
	// current RxBuffer out, and one will be alloc'd at BeginNewFrame time.

	CallObject->RxBuffer = NULL;
	CallObject->RxOffset = 0;

   goto BeginNewFrame;
}  // end ReadPPPFrame
