#ident "$Id: frsencap.c,v 1.12 1997/10/21 20:48:08 gary Exp $"
/*****************************************************************************
 * Copyright (c) 1988-95 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. 1995
 * 
 * All Rights Reserved
 * 
 * US   Government   Users   Restricted   Rights   -   Use,   duplication   or
 * disclosure  restricted by GSA ADP Schedule Contract with Digi International
 * Inc.
 * 
 *****************************************************************************/

/*****************************************************************************
 *                                                                           *
 * (c) COPYRIGHT 1991, 1992 HARRIS & JEFFRIES, INC.                          *
 *                          888 WASHINGTON STREET                            *
 *                          SUITE 130                                        *
 *                          DEDHAM, MA  02026                                *
 * ALL RIGHTS RESERVED                                                       *
 *                                                                           *
 * This software is the property of Harris & Jeffries, Inc. and is furnished *
 * under license by Harris & Jeffries, Inc. and this software may be used    *
 * only in accordance with the terms of said license.  This copyright notice *
 * may not be removed, modified or obliterated without the prior written     *
 * permission of Harris & Jeffries, Inc.                                     *
 *                                                                           *
 * This software may not be copied, transmitted, provided to or otherwise    *
 * made available to any other person, company, corporation or other entiry  *
 * except as specified in the terms of said license.                         *
 *                                                                           *
 * No right, title, ownership or other interest in the software is hereby    *
 * granted or transferred.                                                   *
 *                                                                           *
 * The information contained herein is subject to change without notice and  *
 * should not be construed as a commitment by Harris & Jeffries, Inc.        *
 *                                                                           *
 *****************************************************************************

  MODULE NAME: frsencap.c


  ORIGINAL AUTHOR:  Greg A. Bellucci

  REVISION HISTORY:

  Date     Version  Name          Description
  -------- -------  ------------  --------------------------------------------
  07-14-92 1.00     H&J (GB)      Original
  10-16-92 2.00D    H&J (DJ)      Fix problem with fragmentation
  08-20-93 2.00F    H&J (GB)      Fix problem associated with buffer loss.
  09-10-93 2.00H    H&J (GB)      Substantial revisions to work in accordance
                                  with RFC1490.
           Other    H&J           See H&J's bug reports

  $Log: frsencap.c,v $
  Revision 1.12  1997/10/21 20:48:08  gary
  Modifications made to accomodate the SOH (Streams-on-host) folks.

  Revision 1.11  1996/10/24 20:02:05  gary
  1) Fixed bug in FRI_GetLinkParameters that would cause garbage to be
  returned.
  2) Removed support for old method of compression (per-packet basis).
  The API has not changed (yet).  The STAC parameters are simply ignored.
  3) FRF.9 activated.

  Revision 1.10  1996/10/22 21:18:53  gary
  A bit of rearranging was required in order to move the FRF9_Stats_t
  structure over to fr_cont.h. (needed by host drivers)

  Revision 1.9  1996/06/21 16:36:35  gary
  Next stage of FRF.9 implementation complete.  The appropriate functions
  have been inserted into the transmit and receive path, but are not
  active yet.  Development is being put on hold until a more pressing
  issue (flow control) is dealt with.

  Revision 1.8  1996/05/01 15:46:19  gary
  Fixed bug which caused the FEP to do an extra (unnecessary) bcopy for
  encapsulated frames that were not being fragmented.

  Revision 1.7  1996/04/26 19:57:39  gary
  First rev of changes: Frame Relay on PRI card.
  Bug fix: sequence number on segmented frames was wrong.

  Revision 1.6  1995/12/06 16:52:46  gary
  Fixed bug which would cause an invalid handle value to be returned
  by ENCAP_OpenPLL().

  Revision 1.5  1995/04/10 16:31:52  garyp
  remove strm.h

 * Revision 1.15  1994/07/27  20:27:41  stavrosm
 * Release 2.2
 *
 * Revision 1.14  1994/07/27  17:54:25  stavrosm
 * When the encapsulation header does not fit in the current
 * USRBUF a new USRBUF is allocated to hold that header.
 *
 * Revision 1.13  1994/06/28  14:04:38  stavrosm
 * Added IPX and ARP encapsulation.
 *
 * Revision 1.12  1994/06/22  19:27:15  stavrosm
 * Zero out protocol entry before populating it.
 *
 * Revision 1.11  1993/11/10  16:44:22  stavrosm
 * Release 2.1
 *
 * Revision 1.10  93/11/10  16:26:40  stavrosm
 * Cleanup to make lint happy
 * 
 * Revision 1.9  93/11/04  16:02:25  root
 * 
..............................................................................

  DESCRIPTION:

  This module contains the frame relay encapsulation routines for multiprotocol
interconnect over Frame Relay Networks.

..............................................................................

  REF DOCUMENTS:

  (a) U.S Sprint Frame Relay Service Interface Specification Doc #: 5136.02
  (b) Frame Relay Interface Specification, Stratacom Inc., Doc #: 001-207460
  (c) Frame Relay Stack Functional Specification Rel 1.2 Harris & Jeffries Inc.
  (d) Frame Relay Stack External Specification Rel 1.2 Harris & Jeffries Inc.
  (e) MIB for Frame Relay DTEs - RFC Draft from Internet Engr Task Force (IETF).
  (f) RFC 1490 Multiprotocol Interconnect over Frame Relay Networks - 
      Bradley, Brown, & Malis, July 1993. obsoletes: RFC 1294 

..............................................................................

  SPECIAL COMPILE/LINK INSTRUCTIONS:

        see notes/instructions in frs.h
..............................................................................

ENCAP API FUNCTIONS INCLUDED in this module:

ENCAP_OpenPLL      --- Open a PLL for use with encapsulation (unblocked).
ENCAP_OpenPLLBlkIO --- Open a PLL for use with encapsulation (blocked).
ENCAP_ClosePLL     --- Close a PLL used for encapsulation.
ENCAP_SendData     --- Send a user frame using encapsulation (unblocked).
ENCAP_SendDataWait --- Send a user frame using encapsulation (blocked).

ENCAP INTERNAL FUNCTIONS INCLUDED in this module:

encap_makeframe ........Fragments (if necessary) and creates encapsulated frame
                        relay frames.
encap_unmakeframe.......De-encapsulates frame relay frames and reassembles
                        fragmented user frames.
encap_parse_header......Determines if a frame is complete or fragmented.
encap_fragment_handler..Processes user frames 2 - n if fragmented.
encap_frame_complete....Determines the type of encapsulated frame transmitted.
encap_assemble_frame....Reassembles original upper level protocol frames.
encap_api_pid2pid.......Gets the NLPID and PID values for a given protocol id.
encap_pid2pid_api.......Gets the API PID for a given PID.
encap_apipid2frame......Gets the frame type given an API PID value.
encap_set_framebounds...Determines where encapsulation frame headers are to
                        be inserted given a linked list of user bufs.
encap_frame_error.......Logs a frame error and updates statistics.
encap_free_frame........Returns all usrbufs 'linked' in a frame to the heap.
encap_restore_frame.....Reassembles original upper level protocol frames if
                        the frame relay frames are not transmitted via LAPD.
encap_get_frmtype.......Returns the type of user frame encapsulated in a
                        frame relay frame. 

............................................................................ */

 /****************************************************************************
 * 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 of Digi International  *
 * Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly*
 * prohibited.                                                              *
 * Copyright 1994, Digi International Inc.                                  *
 * All rights reserved.                                                     *
 *       $Id: frsencap.c,v 1.12 1997/10/21 20:48:08 gary Exp $I
 ****************************************************************************/


#define FRSENCAP_C

/** Include files
 **/
/*
#include <stdlib.h>
#include <limits.h>
*/


#define STATIC static
#define BUFP_T 	bufp_t
#define DBDIAG(MP, BYTE)    (MP)->b_datap->db_diag=BYTE;

#include "types.h"
#include "errno.h"
#include "stream.h"
#include "strlog.h"
#include "system.h"
#include "timer.h"
#include "x25trace.h"
#include "fr_cont.h"
#include "frs.h"
#include "frssys.h"
#include "frsencap.h"

extern mblk_t *dupmsg();

struct tag_pid_table
{
    u_char  apipid;    /* API value - these MUST be unique */
    u_short pid;       /* PID value used in frame header */
    u_char  frametype; /* Frame type for the PID value used. */
} pid_table[] = 
{

/** PORTING NOTE:
 ** PID table entries PID_RSVD_01 to PID_RSVD_09 are reserved for target
 ** systems that wish to process their own protocol encapsulations. The
 ** define statements for PID_RSVD_01 - PID_RSVD_09 can be found in frs.h.
 ** If you wish to make the definition of the reserved pid more meaningful
 ** include the header file 'frs.h' before your header files; then use the
 ** following define statements in your header:
 **
 **     #define YOUR_MEANINGFUL_DEFINITION  PID_RSVD_*
 ** 
 ** Then use YOUR_MEANINGFUL_DEFINITION throughout your code. This will prevent
 ** you from modifying the PID table and/or the reserved definitions when you 
 ** receive an update of the frame relay source code.
 **/

/**      API VALUE         PID VALUE           FRAME TYPE                    **/

/** These frame types use an IETF SNAP header.
 **/
    { PID_ETHERNET_FCS,   _PID_ETHERNET_FCS,  IETF_SNAP_HEADER },
    { PID_ETHERNET,       _PID_ETHERNET,      IETF_SNAP_HEADER },
    { PID_TOKEN_BUS_FCS,  _PID_TOKEN_BUS_FCS, IETF_SNAP_HEADER },
    { PID_TOKEN_BUS,      _PID_TOKEN_BUS,     IETF_SNAP_HEADER },
    { PID_TOKEN_RING_FCS, _PID_TOKEN_RING_FCS,IETF_SNAP_HEADER },
    { PID_TOKEN_RING,     _PID_TOKEN_RING,    IETF_SNAP_HEADER },

/** currently not supported **/
    { PID_FDDI_FCS,       _PID_FDDI_FCS,        IETF_SNAP_HEADER },
    { PID_FDDI,           _PID_FDDI,        	IETF_SNAP_HEADER },
    { PID_MAN_FCS,        _PID_MAN_FCS,         IETF_SNAP_HEADER },
    { PID_MAN,            _PID_MAN,        		IETF_SNAP_HEADER },
    { PID_BPDU,           _PID_BPDU,            IETF_SNAP_HEADER },

/** These frame types have their own NLPID assigned to them
 **/
    { PID_IP,             _PID_IP,            ISO_NLPID_FRAME  },
    { PID_ISO_CLNP,       _PID_ISO_CLNP,      ISO_NLPID_FRAME  },
    { PID_ISO_ESIS,       _PID_ISO_ESIS,      ISO_NLPID_FRAME  },
    { PID_ISO_ISIS,       _PID_ISO_ISIS,      ISO_NLPID_FRAME  },
    { PID_OLD_ETHERNET,   _PID_OLD_ETHERNET,  ISO_NLPID_FRAME  },

/** These frame types use the Q933 frame header for specifying
 ** a layer 3 protocol. They do not have an NLPID assignment nor
 ** do they have a SNAP header PID value.
 **/
    { PID_SNA_SUBAREA,    _PID_SNA_SUBAREA,   Q933_USER_SPEC   },
    { PID_SNA_PERIPHERAL, _PID_SNA_PERIPHERAL,Q933_USER_SPEC   },
    { PID_SNA_APPN,       _PID_SNA_APPN,      Q933_USER_SPEC   },
    { PID_NETBIOS,        _PID_NETBIOS ,      Q933_USER_SPEC   },

/** These PID values have a SNAP header with 00-00-00 OUI
 **/
    { PID_ARP,            _PID_ARP,           IETF_SNAP_HEADER }, 
    { PID_IPX,            _PID_IPX,           IETF_SNAP_HEADER },    

    /** These PID values are reserved for target systems that wish to
 ** encapsulate proprietary protocols. Each uses the IETF snap encapsulation.
 **/
    { PID_CISCO_IP,       _PID_CISCO_IP,      CISCO_ENCAP_FRAME }, /* Reserved */
    { PID_RSVD_02,        _PID_RSVD_02,       IETF_SNAP_HEADER }, /* Reserved */
    { PID_RSVD_03,        _PID_RSVD_03,       IETF_SNAP_HEADER }, /* Reserved */
    { PID_RSVD_04,        _PID_RSVD_04,       IETF_SNAP_HEADER }, /* Reserved */
    { PID_RSVD_05,        _PID_RSVD_05,       IETF_SNAP_HEADER }, /* Reserved */
    { PID_RSVD_06,        _PID_RSVD_06,       IETF_SNAP_HEADER }, /* Reserved */
    { PID_RSVD_07,        _PID_RSVD_07,       IETF_SNAP_HEADER }, /* Reserved */
    { PID_RSVD_08,        _PID_RSVD_08,       IETF_SNAP_HEADER }, /* Reserved */
    { PID_RSVD_09,        _PID_RSVD_09,       IETF_SNAP_HEADER }, /* Reserved */
    { PID_RSVD_10,        _PID_RSVD_10,       IETF_SNAP_HEADER }, /* Reserved */

    { END_PID_TABLE,      END_PID_TABLE,      END_PID_TABLE }
    } ;


/**/
/****************************************************************************

  (API) PROCEDURE NAME:   ENCAP_OpenPLL 

  DESCRIPTION:
    This function opens a PLL intended for use with encapsulated frames. The
    function is similar to FRS_OpenPLL, however, this routine creates addition
    CCB structure information to support multiprotocol transmissions.

  PARAMETERS:
    LCB *            - pointer to a link control block structure.
    u_short          - The DLCI assigned to this PLL.
    BFN_PTR          - User callback function for handling completed frames.
    u_short *        - pointer to a pll handle (populated by this function).
    u_char           - Protocol to be encapsulated in the frame.
    queue_t 	     - Read Side Streams Queue that owns this instance.
    u_short          - Non-zero if caller wants STAC compression
    u_short          - Performance mode -- must be 0, 1, or 2
    u_short          - Performance parameter -- must be between 0 and 255

  RETURNS:
    FRS_SUCCESS       - PLL opened sucessfully, the handle is returned in phpll.
    FRS_DISABLED      - FRS is currently disabled.
    FRS_NO_SPACE      - Unable to allocate memory.
    FRS_INVALID       - PLL open failed, invalid or NULL LCB.
    ENCAP_INVALID     - Not configured for encapsulation
    ENCAP_UNK_PROT    - Protocol id supplied is unknown.

  NOTES:
    1.  One and only one subscriber of each protocol is allowed. However,
	a single PLL may be used to exchange any number of encapsulated
	protocols through multiple open calls.

    2.  The callback function may be modified by opening the PLL with the
	id of a previously 'registered' protocol with a new callback function
	pointer.

*****************************************************************************/

u_short
ENCAP_OpenPLL( plcb, dlci, fcallback, phpll, protocol_id , q)
LCB *plcb;
u_long dlci;
BFN_PTR fcallback;
u_long *phpll;
u_long protocol_id;
queue_t *q ;
{
    /** Local variables
     **/
    u_char new_ccb = FALSE;
    CCB *pccb;
    FRS_PROTOCOL_ENTRY *pe;
    u_short pid;

printf("ENCAP_OpenPLL: ");
    /** Get the protocol type we're opening this for...
     **/
    pid = encap_api_pid2pid((u_char)protocol_id);

    if (!m_FRS_CHECK) return FRS_DISABLED;

printf("FRS_CHECK ");
    if( plcb == LCBNULL )
	return( FRS_INVALID );

printf("LCB_NOT_NULL ");
    if( !(m_FRS_IS_LINK_ENCAP(plcb)) )
	return( ENCAP_INVALID );

printf("IS_LINK_ENCAP ");
    /** Is the protocol id valid ?
     **/
    if( pid == PID_UNKNOWN )
	return( ENCAP_UNK_PROT );

printf("PID_KNOWN ");
    /** If this PLL is already opened then the caller is assumed to be
     ** changing the callback function or registering additional protocol
     ** support for this PLL. Otherwise, we open the PLL and initialize the 
     ** ENCAP pointer in the CCB structure.
     **/
    if(( pccb = FRSLocateCCBbyDLCI( plcb, dlci )) == CCBNULL )
	{
printf("CCB_BY_DLCI_NULL ");
	/** CCB not found - create a new one....
	 **/
	if(( pccb = FRSCreateCCB( plcb, dlci, TRUE )) == CCBNULL )
	    return( FRS_NO_SPACE );
	else
	    {
printf("GET_NEW_CCB_HANDLE ");
	    new_ccb = 1; /* remember that this is new */
	    if((FRSFetchCCBHandle(pccb, phpll)) != FRS_SUCCESS)
		return( FRS_INVALID );
	    }
	    InitFRF9Session(pccb);
	}
	else
	{
		if ((FRSFetchCCBHandle(pccb, phpll)) != FRS_SUCCESS)
			return( FRS_INVALID );
	}/* end if */

    /** Create (if necessary) memory for the ENCAP pointer in the CCB.
     **/
printf("CHECK_ENCAP ");
    if( pccb->encap == (NPMULTIPROTOCOL) 0 )
	{

#ifdef	STATIC_MEMORY

	if(( pccb->encap =
	    m_MMGR_MULTIPROTOCOL_ALLOC()) == (NPMULTIPROTOCOL) 0 )
	    {
	    /** allocation failed - if we just created this PLL
	     ** then close it and return an error code.
	     **/
printf("MP_ALLOC_FAILED ");
	    if( new_ccb )
		FRSFR_ClosePLL( *phpll ); /* Use the normal closure call */
printf("FAILED_RECOVERY_DONE ");
	    return( FRS_NO_SPACE );       /* return error */
	    }

#else
	if(( pccb->encap =
	    m_MMGR_SPL_ALLOC(sizeof(MULTIPROTOCOL))) == (NPMULTIPROTOCOL) 0 )
	    {
	    /** allocation failed - if we just created this PLL
	     ** then close it and return an error code.
	     **/
	    if( new_ccb )
		FRSFR_ClosePLL( *phpll ); /* Use the normal closure call */
	    return( FRS_NO_SPACE );       /* return error */
	    }
#endif
	else
	    /** Initialize the encap protocol table
	     **/
	    m_MMGR_MEMSET( pccb->encap, 0, sizeof( MULTIPROTOCOL ) );

	}/* end if */

    /** Is this protocol already registered ?
     **/
printf("CHECK_REGISTRY ");
    if( pccb->encap->mp[protocol_id] != (FRS_PROTOCOL_ENTRY *)0 )
	{
printf("ALREADY_REGISTERED ");
	/** Set the callback function if a new one was provided....
	 **/
	return(FRS_INVALID) ;
/*
	if( fcallback != (BFN_PTR) 0 )
	    pccb->encap->mp[protocol_id]->fcallback = fcallback;
*/
	}
    else
	{
	/** Allocate space for the protocol entry and initialize 
	 ** the table entry for this protocol.
	 **/

#ifdef	STATIC_MEMORY

printf("NEW_REGISTRY ");
	if(( pe = (FRS_PROTOCOL_ENTRY *) 
	    m_MMGR_FRS_PROTOCOL_ENTRY_ALLOC()) != 
		(FRS_PROTOCOL_ENTRY *) 0)
	    {
            m_MMGR_MEMSET( (void *)pe, 0, sizeof(FRS_PROTOCOL_ENTRY) );
	    pccb->encap->inuse++;
	    pccb->encap->mp[protocol_id] = pe;
	    pccb->encap->mp[protocol_id]->fcallback = fcallback;
	    pccb->plcb->fMakeFrame   = (MFN_PTR) encap_makeframe;
	    pccb->plcb->fUnMakeFrame = (FN_PTR) encap_unmakeframe;
#ifndef STREAM_MESSAGES
	    pccb->TxDoneFunc = encap_frame_complete;
#else
	    pccb->TxDoneFunc = (BFN_PTR) 0;
#endif
	    pccb->encap->mp[protocol_id]->prot_CreateTime =sys_get_sys_uptime();
	    pccb->encap->mp[protocol_id]->rcvd_q = RD(q) ;
	    }

#else
	if(( pe = (FRS_PROTOCOL_ENTRY *) 
	    m_MMGR_SPL_ALLOC(sizeof(FRS_PROTOCOL_ENTRY))) != 
		(FRS_PROTOCOL_ENTRY *) 0)
	    {
            m_MMGR_MEMSET( (void *)pe, 0, sizeof(FRS_PROTOCOL_ENTRY) );
	    pccb->encap->inuse++;
	    pccb->encap->mp[protocol_id] = pe;
	    pccb->encap->mp[protocol_id]->fcallback = fcallback;
	    pccb->plcb->fMakeFrame   = (MFN_PTR) encap_makeframe;
	    pccb->plcb->fUnMakeFrame = (FN_PTR) encap_unmakeframe;
#ifndef STREAM_MESSAGES
	    pccb->TxDoneFunc = encap_frame_complete;
#else
	    pccb->TxDoneFunc = (BFN_PTR) 0;
#endif
	    pccb->encap->mp[protocol_id]->prot_CreateTime =sys_get_sys_uptime();
	    pccb->encap->mp[protocol_id]->rcvd_q = RD(q) ;
	    }

#endif
	else
	    /** No space left....
	     **/
	    {
printf("NEW_REGISTRY_FAILED ");
	    if( new_ccb )
#ifdef	STATIC_MEMORY
		m_MMGR_MULTIPROTOCOL_DEALLOC(pccb->encap);
#else
		m_MMGR_SPL_FREE(pccb->encap);
#endif

	    return( FRS_NO_SPACE );
	    }
	}/* end else */

    return( FRS_SUCCESS );
}

/**/
#if USE_BLOCKED_IO
/*****************************************************************************

  (API) PROCEDURE NAME:    ENCAP_OpenPLLBlkIO

  DESCRIPTION:

  This API function will open a PLL for i/o operations. After the PLL is
  appropriately initialised, the channel will start transmiting/receiving data.

  When opened with this function, the PLL will support ENCAP_SendDataWait()
  and FRSFR_RcvDataWait() calls.

  PARAMETERS:

  LCB *         - ptr link ctrl blk
  u_short       - pvc number
  LFN_PTR       - ptr to the TxDoneFunc callback function
  u_short *     - ptr to the PLL handle (return value)
  BLK_FN_PTR    - ptr to Rcv process blocking function
  BLK_FN_PTR    - ptr to Rcv process un-blocking function
  BLK_FN_PTR    - ptr to Send process blocking function
  BLK_FN_PTR    - ptr to Send process un-blocking function
  u_short       - user argument to pass to blockin/un-blocking functions.
  u_char        - protocol identifier

  
  RETURNS:
    See return values from ENCAP_OpenPLL function call.

  NOTES:

*****************************************************************************/

u_short
ENCAP_OpenPLLBlkIO( LCB *pLCB,
	      u_short    dlci,
	     BFN_PTR    fTxDone,
	     u_long   *phPLL,
	     BLK_FN_PTR fRcvBlk,
	     BLK_FN_PTR fRcvUnb,
	     BLK_FN_PTR fSndBlk,
	     BLK_FN_PTR fSndUnb,
	     u_int      uarg,
	     u_char     pid)
{

    /** Local variables
     **/
    u_short rcode = ENCAP_OpenPLL(pLCB, dlci, fTxDone, phPLL, pid);
    CCB *pCCB;

    if (rcode == FRS_SUCCESS)
	{
	pCCB = FRSLocateCCBbyDLCI(pLCB, dlci);
	pCCB->fReadBlock = fRcvBlk;
	pCCB->fReadUnblock = fRcvUnb;
	pCCB->fWriteBlock = fSndBlk;
	pCCB->fWriteUnblock = fSndUnb;
	pCCB->uarg = uarg;
	}

    return(rcode);
}
#endif

/**/
/****************************************************************************

  (API) PROCEDURE NAME:   ENCAP_ClosePLL 

  DESCRIPTION:
    This routine may be called to effectively 'deregister' protocol usage
    on a PLL. If the PLL has been opened for use with several protocols,
    this close functionality will prevent the accepted use of the protocol
    specified without affecting other protocols which may still be in use
    on the CCB. If the protocol to be 'closed' is the only one registered, 
    the CCB will be closed and the memory returned to the heap.

  PARAMETERS:
    u_short  - handle of the PLL.
    u_char   -  API value of the protocol to be 'closed' on  the PLL.

  RETURNS:
    FRS_DISABLED   - Frame Relay disabled.
    FRS_SUCCESS    - PLL closed or protocol closed successfully.
    FRS_CLOSED     - PLL or protocol already closed.
    FRS_INVALID    - PLL not previously opened or not opened for
		     encapsulated frames, or invalid pll handle.
    ENCAP_UNK_PROT - unknown/not registered protocol id.

  NOTES:

*****************************************************************************/

u_short
ENCAP_ClosePLL( hpll, protocol_id )
u_long hpll;
u_long protocol_id;
{

    /** Local Variables
     **/
    CCB *pccb;

    if (!m_FRS_CHECK) return FRS_DISABLED;
printf("ENCAP_ClosePLL: ");
    /** Validate the PLL handle and get the pointer....
     **/
    if( !(m_IS_PLL_VALID( hpll, pccb )) )
	return( FRS_INVALID );

printf("PLL_VALID ");
    /** Get the link pointer and make sure this link was initialized for
     ** use with encapsulation....
     **/
    if( !(m_FRS_IS_LINK_ENCAP( pccb->plcb )) )
	return( FRS_INVALID );

printf("IS_LINK_ENCAP ");
    /** Is this protocol id valid ?
     **/
    if((encap_api_pid2pid((u_char) protocol_id)) == PID_UNKNOWN )
	return( ENCAP_UNK_PROT );

printf("PID_KNOWN ");
    /** Validate the PLL handle and get the pointer....
     **/
    if( m_IS_PLL_VALID( hpll, pccb ) )
	{
	/** We only know that the CCB is open....
	 **/
printf("PLL_STILL_VALID ");
	if( m_IS_FRS_PLL_OPEN(pccb) )
	    {
printf("IS_ENCAP_OKAY? ");
	    if( pccb->encap == (NPMULTIPROTOCOL) 0 )
		return( FRS_INVALID ); /* not opened for encapsulation */
printf("ENCAP_IS_OKAY ");

	/** Is this protocol in use ?
	 **/
	    if( pccb->encap->mp[protocol_id] == (FRS_PROTOCOL_ENTRY *)0 )
		return( FRS_CLOSED );  /* no.... */
	    else
		{
		/** Decrement the 'in use' count and close operations
		 ** only for this protocol.... If the use count goes to zero
		 ** then close the CCB.
		 **/
		pccb->encap->inuse--;
#ifdef	STATIC_MEMORY
		m_MMGR_FRS_PROTOCOL_ENTRY_DEALLOC(pccb->encap->mp[protocol_id]);
#else
		m_MMGR_SPL_FREE(pccb->encap->mp[protocol_id]);
#endif
		pccb->encap->mp[protocol_id] = (FRS_PROTOCOL_ENTRY *)0;

		/** Are there any protocols registered ?
		 **/
		if( !(pccb->encap->inuse) )
		    {
		    /** Free the memory for the ENCAP pointer..... then
		     ** call the normal close routine to close the CCB.
		     **/

		    /*
		     * Free any fragments that were waiting for reassembly
		     * By convention (?), this is when psframe is not null
		     * AND fragproc is not null.
		     */
		    if (pccb->encap->psframe != BNULL && pccb->encap->fragproc != (FFN_PTR) 0)
			freemsg(pccb->encap->psframe);
#ifdef	STATIC_MEMORY

		    m_MMGR_MULTIPROTOCOL_DEALLOC(pccb->encap) ;
#else
		    m_MMGR_SPL_FREE(pccb->encap);
#endif
printf("FRSFR_ClosePLL_call ");
		    return( FRSFR_ClosePLL(hpll) );
		    }
		else
		    return( FRS_SUCCESS );
		}/* end else */
	    }/* end if */
	else
	    return( FRS_CLOSED );  /* already closed... */
	}/* end if */
    else
	return( FRS_INVALID ); /* bad ccb handle .... */
}

/**/
/*****************************************************************************

  (API) PROCEDURE NAME:    ENCAP_SendData

  DESCRIPTION:

  This function accepts a CCB handle (obtained via an encap open call),
  a pointer to a user buffer containing the payload data and the protocol id
  of the encapsulated protocol. This function will prepend the frame relay
  encapsulation header and return to the caller after enqueueing the buffer 
  into the CCB's Write_queue.

  PARAMETERS:

    u_short   -- PLL handle.
    USRBUF *  -- Pointer to a user buffer containing frame relay payload data.
    u_char    -- API protocol identifier

  RETURNS:
	FRS_SUCCESS or one of the API codes in frs.h.

  NOTES:

*****************************************************************************/

#ifndef	STREAM_MESSAGES

u_short
ENCAP_SendData( hpll, pframe, protocol_id )
u_short hpll;
USRBUF *pframe;
u_char protocol_id;
{

    /** Local variables
     **/
    CCB *pccb;
    u_short rcode = FRS_BAD_CCB_HANDLE;

    if (!m_FRS_CHECK) return FRS_DISABLED;

    if( m_IS_PLL_VALID( hpll, pccb ) )
	{
	/** Make the frame - this may mean fragmenting the user frame...
	 **/

	rcode = pccb->plcb->fMakeFrame( pccb, pframe, protocol_id );
	switch( rcode )
	    {

	    /** Frame is ready to send...
	     **/
	    case FRS_SUCCESS:

		if((rcode = FRSLAPD_SendData( hpll, pframe )) != FRS_SUCCESS)
		    {
		    /** LAPD couldn't send the frame.... we will have to restore
		     ** the user's frame before returning....
		     **/
		    encap_restore_frame( pframe, protocol_id );
		    }

		break;
		
	    /** All other return values from makeframe... these return codes
	     ** do not require us to restore the frame.
	     **/
	    default:
		break;

	    }/* end switch */
	}/* end if */

    return(rcode);
}
#else

/*
 * pframe and any revisions of it must be destroyed before returning
 */
u_short
ENCAP_SendData( hpll, pframe, protocol_id )
u_long hpll;
USRBUF *pframe;
u_long protocol_id;
{

    /** Local variables
     **/
    CCB *pccb;
    u_short rcode = FRS_BAD_CCB_HANDLE;
    USRBUF *new_pframe , *mp;

    if (!m_FRS_CHECK) {
	if (pframe) freemsg(pframe);
	return FRS_DISABLED;
	}

    if( m_IS_PLL_VALID( hpll, pccb ) )
	{
	/** Make the frame - this may mean fragmenting the user frame...
	 **/

	rcode = pccb->plcb->fMakeFrame( pccb, pframe, (u_char) protocol_id, &new_pframe );
	switch( rcode )
	    {

	    /** Frame is ready to send...
	     **/
	    case FRS_SUCCESS:

		if (pccb->FRF9.Phase == FRF9_PHASE_OPERATIONAL) 
		{
			USRBUF *frag_mp = new_pframe ;
			USRBUF *next_frag_mp ; 

			for(;;)
			{
				next_frag_mp    = frag_mp->b_next ;
				frag_mp->b_next = BNULL ;	

#if 0
				printf("FRF9 TX Operational") ;
#endif
				if (pccb->FRF9.TxQueue.Q.qtop) 
				{
					if (pccb->FRF9.TxQueue.qcnt > 50) 
					{
						pccb->FRF9.FRF9_Stats.OverflowErr++ ;
						freemsg(frag_mp) ;
						rcode = FRS_NO_SPACE ;
					} 
					else
					{
#if 0
						printf("FRF9 TX QTENQUEUE") ;
#endif
						FRSQTEnqueue(frag_mp, (QTCB *) &(pccb->FRF9.TxQueue)) ;
					}
				} 
				else 
				{

					mp = (mblk_t *) TxFRF9Frame(pccb, frag_mp) ;
#if 0
					printf("FRF9 TX FRF9Frame = %x", mp) ;
#endif
					if (mp == (mblk_t *) -1) 
					{
						FRSQTEnqueue(frag_mp, (QTCB *) &(pccb->FRF9.TxQueue)) ;
					}
					else
					if (mp)
					{
						rcode = FRSLAPD_SendData( hpll, mp );
#if 0
						printf("FRF9 TX senddata rcode = %x", rcode) ;
#endif
					}
				}

				if(!next_frag_mp)
						break ;

				frag_mp = next_frag_mp ;
			}
		} 
		else 
		{
			rcode = FRSLAPD_SendData( hpll, new_pframe );
		}
		break;
		
	    /** All other return values from makeframe... these return codes
	     ** do not require us to restore the frame.  In fact, we should
	     ** nail the original.
	     **/
	    default:
		break;

	    }/* end switch */
	}/* end if */
	else
	if (pframe) freemsg(pframe);

    return(rcode);
}


#endif
/**/
#if USE_BLOCKED_IO
/*****************************************************************************

    (API) PROCEDURE NAME:    ENCAP_SendDataWait

    DESCRIPTION:

    This function is identical to ENCAP_SendData except that the user
    specified fWriteBlock() is called to block the user process. The user
    process will remain blocked until this frame (buffer) is picked off
    from the Write_queue. In fact, the user process is unblocked after the
    Write_queue for this CCB has been emptied.

    This function accepts a CCB handle (obtained via an encap open call), 
    a pointer to a user buffer containing the payload data and the protocol
    id of the required encapsulation.

    PARAMETERS:
	u_short   - PLL handle.
	USRBUF *  - Pointer to a user buffer containing frame relay payload
		    data.
	u_char    - API protocol identifier

    RETURNS:
	FRS_SUCCESS or one of the API codes in frs.h.

    NOTES:
*****************************************************************************/

u_short
ENCAP_SendDataWait( hpll, pframe, protocol_id )
u_short hpll;
USRBUF *pframe;
u_char protocol_id;
{

    /** Local variables
     **/
    CCB *pccb;
    u_short rcode = FRS_BAD_CCB_HANDLE;

    if (!m_FRS_CHECK) return FRS_DISABLED;

    if( m_IS_PLL_VALID( hpll, pccb ) )
	{
	/** Make the frame - this may mean fragmenting the user frame...
	 **/
	rcode = pccb->plcb->fMakeFrame( pccb, pframe, protocol_id );

	switch( rcode )
	    {

	    /** Frame is ready to send...
	     **/
	    case FRS_SUCCESS:

		if((rcode = FRSLAPD_SendDataWait(hpll, pframe)) != FRS_SUCCESS)
		    {
		    /** LAPD couldn't send the frame.... we will have to restore
		     ** the user's frame before returning....
		     **/
		    encap_restore_frame( pframe, protocol_id );
		    }

		break;
		
	    /** All other return values from makeframe... these return codes
	     ** do not require us to restore the frame.
	     **/
	    default:
		break;

	    }/* end switch */
	}/* end if */

    return(rcode);
}

#endif /* USE_BLOCKED_IO */

/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_makeframe

  DESCRIPTION:
    This function is an INTERNAL function responsible for accepting a user
    frame and if necessary breaking the user frame into smaller frame relay 
    frames that can be transmitted by the transmitter task. This routine
    prepends the necessary encapsulation header. The function is called by
    ENCAP_SendData or ENCAP_SendDataWait.

  PARAMETERS:
    CCB *    - pointer to the CCB (aka PLL).
    USRBUF * - pointer to the user frame to encapsulate
    u_char   - the type of (protocol) encapsulation required.

  RETURNS:
    FRS_SUCCESS          - Frame encapsulation completed.
    FRS_INVALID          - PLL does not exist.
    ENCAP_INVALID        - PLL not opened for encapsulation.
    ENCAP_UNK_PROT_TX    - Protocol id supplied is unknown.
    ENCAP_NOT_REGISTERED - Protocol is not used on this PLL.
    ENCAP_NULL_FRAME_TX  - FRAME pointer is NULL.
    ENCAP_INV_DATA_SIZE  - USRBUF data is < the minimum data size.

  NOTES:

    For the STREAM_MESSAGES version, calling functions will be assuming
    that this function will either:

    a) create the encapsulated frame(s), destroy any outdated copies
       (this includes the original frame), and return FRS_SUCCESS

    b) fail to create the required frame(s), destroy any frames
       (including the original), and return an error code

*****************************************************************************/
#ifdef	STREAM_MESSAGES

u_short
encap_makeframe( pccb, pfrm, protocol_id , new_pframe)
CCB *pccb;
USRBUF *pfrm ;
u_char protocol_id;
USRBUF **new_pframe ;

#else

u_short
encap_makeframe( pccb, pfrm, protocol_id )
CCB *pccb;
USRBUF *pfrm ;
u_char protocol_id;

#endif
{

    /** Local Variables
     **/
    u_short seqnum, frame_type;
    u_long hpll;
    u_short i, frame_count;
    u_long  fpayload, tpayload, offset;
    u_short flags, rc, maxfrm, frbit;
    u_char errors;
    u_short pid, fsm_input, fsm_output, done;
    u_short fsm_state, fsm_nextstate;
    USRBUF * first_frag;

#ifdef	STREAM_MESSAGES 
	u_short rcode ; 
	mblk_t *newfrm ;
#endif

    NPSNAP_HDR p_snap;
    NPFRAGMENT_HDR p_frag;
    NPISO_NLPID_FRAME_HDR p_nlpid_hdr;
    NPISO_Q933_FRAME_HDR p_q933_hdr;
    NPQ922_ADDR p_q922_addr;
	NPCISCO_ENCAP_HDR p_cisco_encap_hdr ;


    /** Determimine if we have a valid CCB
     **/
    if((FRSFetchCCBHandle(pccb, &hpll)) != FRS_SUCCESS) {
	freemsg(pfrm);
	return( FRS_INVALID );
	}

    /** Is this PLL configured and opened for encapsulation ?
     **/
    if(pccb->encap == (NPMULTIPROTOCOL) 0) {
	freemsg(pfrm);
	return( ENCAP_INVALID );
	}

    if(( pid = encap_api_pid2pid( protocol_id )) == PID_UNKNOWN)
	return( encap_frame_error( pccb, pfrm, ENCAP_UNK_PROT_TX, FREE ) );

    /** Is the protocol registered on this PLL ?
     **/
    if( !(mIS_PID_REGISTERED(pccb->encap->mp[protocol_id])) ) {
	freemsg(pfrm);
	return( ENCAP_NOT_REGISTERED );
	}

    /** Is the USRBUF pointer NULL ?
     **/
    if( mIS_NULL_FRAME(pfrm) )
	return(encap_frame_error( pccb, pfrm, ENCAP_NULL_FRAME_TX, NOFREE ));

    /** Start the state machine - the state transition table for this routine
     ** can be found in frsencap.h
     **/
    fsm_state = fsm_nextstate = _IDLE;
    fsm_input = _begin;
    done = 0;

    while( !done )
	{
	switch( fsm_state )
	    {

/* -------------------------------------------------------------------------- */
	    case _IDLE:
/* -------------------------------------------------------------------------- */
		switch( fsm_input )
		    {
		    case _done:
			done = 1;
			break;

		    case _frame_complete:
			fsm_output = _done;
			rc = FRS_SUCCESS;
			break;

		    case _begin:

			/** Determine the type of frame that needs to be built.
			 ** and call encap_set_framebounds to fragment the frame
			 **/
			frame_type = 
			    (u_short) encap_apipid2frame(protocol_id);
			maxfrm = (u_short) mGET_MAX_FRAME_LEN(pccb);
#ifdef	STREAM_MESSAGES
			rcode = encap_set_framebounds(pfrm, maxfrm,
						     &frame_count, frame_type, &newfrm) ;
			
			/*
			 * Switch to newly built frames 
			 */

#if 0	
			printf("rcode = %d ; newfrm->b_rptr = %x count = %x\n", rcode, newfrm->b_rptr, frame_count); 
#endif
			freemsg(pfrm) ;			
			if(newfrm != BNULL)
			{
				*new_pframe = pfrm = newfrm ;
			}

			switch(rcode)
#else
			switch( encap_set_framebounds(pfrm, maxfrm,
						     &frame_count, frame_type) )
#endif

			    {
                            case FRS_NO_BUFFERS:
                                fsm_output = _no_buffers;
                                fsm_nextstate = _FRAME_ERROR;
                                break;

			    case ENCAP_BUFSIZE_INVALID:
				fsm_output = _invalid_bufsize;
				fsm_nextstate = _FRAME_ERROR;
				break;

			    default:

				fsm_nextstate = _BUILD_FRAME;

				/** Frame type to build ?
				 **/
				switch( frame_type )
				    {
				    case IETF_SNAP_HEADER:
					fsm_output = _bridged_frame;
					break;

				    case ISO_NLPID_FRAME:
					fsm_output = _iso_nlpid_frame;
					break;

				    case Q933_USER_SPEC:
					fsm_output = _iso_q933_frame;
					break;

					case CISCO_ENCAP_FRAME:
					fsm_output = _cisco_encap_frame ;
					break ;

				    }
				break;
			    }
			break;

		    default:
			fsm_nextstate = _FSM_ERROR;
			break;
		    }
		break;

/**/
/* -------------------------------------------------------------------------- */
	    case _BUILD_FRAME:
/* -------------------------------------------------------------------------- */
#ifndef	STREAM_MESSAGES   
		pfrm->first_frag = first_frag = pfrm;
		pfrm->code = FRS_IN_PROGRESS;
		pfrm->next_frag = (USRBUF *) NULL;
#endif
		switch( fsm_input )
		    {
		    case _iso_nlpid_frame:

			/** Set output token
			 **/
			fsm_output = 
			    (frame_count > 1) ? _frame_fragment: _add_q922_addr;

			/** Save the payload size of this frame
			 **/
			fpayload = (uint)m_MMGR_DATA_LEN(pfrm);

			/** Move the frame pointer to where this header should
			 ** begin.
			 **/
			mMK_HDR_SPC(pfrm, ISO_NLPID_HDR_LEN);
			p_nlpid_hdr= (NPISO_NLPID_FRAME_HDR) mSTARTOFRAME(pfrm);
			p_nlpid_hdr->ctrl = FRAME_TYPE_03;

			/** Single byte NLPID value
			 **/
			p_nlpid_hdr->nlpid = mGetLSB(pid);
			break;

		    case _bridged_frame:

			/** Set output token
			 **/
			fsm_output = 
			    (frame_count > 1) ? _frame_fragment: _add_q922_addr;

			/** Save the payload size of this frame
			 **/
			fpayload = m_MMGR_DATA_LEN(pfrm->b_cont);

			/** Move the frame pointer to where this header should
			 ** begin.
			 **/
			mMK_HDR_SPC(pfrm, SNAP_HDR_LEN);
			p_snap = (NPSNAP_HDR) mSTARTOFRAME(pfrm);
			p_snap->ctrl = FRAME_TYPE_03;
			p_snap->pad  = NULL_OCTET;
			p_snap->nlpid= NLPID_SNAP;

			/** Here we allow the target system to insert a
			 ** target system defined OUI value in the header by
			 ** calling 'sys_ins_oui'. If the function returns
			 ** FALSE then we put the standard IETF 802.1 OUI in
			 ** the frame header. If the function returns a non-zero
			 ** value it is assumed that the target system added an 
			 ** OUI value to the header.
			 **/
			if(( sys_ins_oui(pccb, p_snap)) == FALSE )
			    {
			    switch(pid) 
				{
				case _PID_ARP:
				case _PID_IPX:
				    /* this is Ethertype OUI for routed frames 
				     */
				    p_snap->oui[0] = 0x00;
				    p_snap->oui[1] = 0x00;
				    p_snap->oui[2] = 0x00;
				    break;

				default:
				    p_snap->oui[0] = 0x00;
				    p_snap->oui[1] = 0x80; /* IETF 802.1 */
				    p_snap->oui[2] = 0xC2;
				    break;
				}
			    }

			/** Finish up with the protocol identifier.
			 **/
			p_snap->pid[0] = mGetMSB(pid);
			p_snap->pid[1] = mGetLSB(pid);
			break;
		
		    case _iso_q933_frame:

			/** Set output token
			 **/
			fsm_output = 
			    (frame_count > 1) ? _frame_fragment: _add_q922_addr;

			/** Save the payload size of this frame
			 **/
			fpayload = m_MMGR_DATA_LEN(pfrm->b_cont);

			/** Move the frame pointer to where this header should 
			 ** begin.
			 **/
			mMK_HDR_SPC(pfrm, ISO_Q933_HDR_LEN);
			p_q933_hdr = (NPISO_Q933_FRAME_HDR) mSTARTOFRAME(pfrm);
			p_q933_hdr->ctrl = FRAME_TYPE_03;
			p_q933_hdr->nlpid = NLPID_Q933;

			/** Which Q.933 frame ?
			 **/
			switch( frame_type )
			    {
			    case Q933_USER_SPEC: /* User specified layer 3 */
				p_q933_hdr->l2_pid[0] = 0x4C;
				p_q933_hdr->l2_pid[1] = 0x80;
				p_q933_hdr->l3_pid[0] = USER_SPEC;
				p_q933_hdr->l3_pid[1] = mGetLSB(pid);
				break;

			    default:
				/** Other types of Q.933 headers are not
				 ** currently supported 
				 **/
				fsm_output = _invalid_frame;
				fsm_nextstate = _FRAME_ERROR;
				break;
			    }

			break;


			case _cisco_encap_frame:

			/** Set output token
			 **/
			fsm_output = _add_q922_addr;

			/** Save the payload size of this frame
			 **/
			fpayload = m_MMGR_DATA_LEN(pfrm);

			/** Move the frame pointer to where this header should
			 ** begin.
			 **/
			mMK_HDR_SPC(pfrm, CISCO_ENCAP_HDR_LEN);
			p_cisco_encap_hdr= (NPCISCO_ENCAP_HDR) mSTARTOFRAME(pfrm);
			p_cisco_encap_hdr->ctrl = FRAME_TYPE_08;

			/** Single byte NLPID value
			 **/
			p_cisco_encap_hdr->nlpid = mGetLSB(pid);

			break ;

		    case _frame_fragment:

			/** Set the next output token and state
			 **/
			fsm_output = _frame_complete;
			fsm_nextstate = _IDLE;

			/** We're here because the user frame is fragmented. The
			 ** first header is already on the frame. Now we add a
			 ** SNAP header and fragmentation info for reassembly at
			 ** the other end.
			 **/
			seqnum = ++pccb->encap->next_tx_seq_num ;
			tpayload = 0;    /* payload size of the entire frame */
			errors = 0;      /* no errors */

			/** Do this for all frame relay frames
			 **/
			for( i = 0; (i < frame_count) && !errors; i++ )
			    {
			    /** Are we working on the first frame ?
			     **/
			    if( i == 0 )
				flags = 0; /* 1st frame this is zero */
			    else
				{
				/** Frames 2-n contain the frame offset and
				 ** whether or not this is the final frame.
				 **/
				tpayload += fpayload;
				offset = (u_short) 
				    ( tpayload/MODULO32) & FRAME_OFFSET;
				frbit = (u_short) 
				    ((i+1) != frame_count) ? 0: FINAL_FRAME_BIT;
				flags = (u_short) 
				    ( frbit | (0 & FRAGMENT_RSVD) | offset);

				/** Get the payload size of this frame fragment
				 ** before we put the header on.
				 **/

				if(pfrm->b_cont)
					fpayload = m_MMGR_DATA_LEN(pfrm->b_cont);
				else
					printf("pfrm->bcont == 0") ;

#if WRONG_CODE
				/** Increment the frame sequence number;
				 **/
				seqnum += 1;
#endif
#ifndef	STREAM_MESSAGES
				pfrm->first_frag = first_frag;
				pfrm->code = FRS_IN_PROGRESS;
#endif
				}/* end else */

			    /** Is the frame full ? - must be unless it's the
			     ** last frame.
			     **/
			    if((fpayload % MODULO32) && ((i+1) != frame_count))
				{
				printf("oops! fpayload(%d) i(%d) frame_count(%d)\n", fpayload, i, frame_count);
				errors = 1;
				fsm_output = _invalid_bufsize;
				fsm_nextstate = _FRAME_ERROR;
				continue; /* for loop */
				}

			    /** Add the fragmentation information to the frame.
			     **/
			    mMK_HDR_SPC(pfrm, FRAG_HDR_LEN);
			    p_frag = (NPFRAGMENT_HDR) mSTARTOFRAME(pfrm);
			    p_frag->seq[0] = mGetMSB(seqnum);
			    p_frag->seq[1] = mGetLSB(seqnum);
			    p_frag->info[0] = mGetMSB(flags);
			    p_frag->info[1] = mGetLSB(flags);

			    /** Add the final snap header.
			     **/
			    mMK_HDR_SPC(pfrm, SNAP_HDR_LEN);
			    p_snap = (NPSNAP_HDR) mSTARTOFRAME(pfrm);
			    p_snap->ctrl   = FRAME_TYPE_03;
			    p_snap->pad    = NULL_OCTET;
			    p_snap->nlpid  = NLPID_SNAP;
			    p_snap->oui[0] = 0x00;
			    p_snap->oui[1] = 0x80;  /* IETF 802.1 */
			    p_snap->oui[2] = 0xC2;
			    p_snap->pid[0] = 0x00;
			    p_snap->pid[1] = FRAGMENT_PID;

			    /** ... and the DLCI
			     **/
			    mMK_HDR_SPC(pfrm, Q922_ADDR_LEN);
			    p_q922_addr = (NPQ922_ADDR) mSTARTOFRAME(pfrm);
			    p_q922_addr->address = pccb->my_header;

#ifndef	STREAM_MESSAGES
			    /** next fragment
			     **/
			    if (i < (frame_count-1))
			       pfrm->next_frag = pfrm->next;
			    else
			       pfrm->next_frag = (USRBUF *) NULL;

			    /** Next frame...
			     **/
			    pfrm = pfrm->next;
#else

				if( i < (frame_count-1))
				pfrm = pfrm->b_next ;	
			    else
				pfrm = BNULL ;
			
#endif
			    }/* end for */

			break;

		    case _add_q922_addr:

			/** Add the DLCI...
			 **/
			mMK_HDR_SPC(pfrm, Q922_ADDR_LEN);
			p_q922_addr = (NPQ922_ADDR) mSTARTOFRAME(pfrm);
			p_q922_addr->address = pccb->my_header;

			fsm_output = _frame_complete;
			fsm_nextstate = _IDLE;
			break;

		    default:
			fsm_nextstate = _FSM_ERROR;
			break;
		    }
		break;

/**/
/* -------------------------------------------------------------------------- */
	    case _FRAME_ERROR:
/* -------------------------------------------------------------------------- */

		fsm_output = _done;
		fsm_nextstate = _IDLE;

		switch( fsm_input )
		    {
                   case _no_buffers:
                        rc = FRS_NO_BUFFERS;
                        break;

		    case _invalid_bufsize:
			rc = ENCAP_BUFSIZE_INVALID;
			break;

		    case _invalid_frame:
			rc = ENCAP_UNK_PROT_TX;
			break;

		    default:
			fsm_nextstate = _FSM_ERROR;
			break;
		    }
		break;

/* -------------------------------------------------------------------------- */
	    default:
	    case _FSM_ERROR:
/* -------------------------------------------------------------------------- */

		/** Something wrong with the fsm. Probably an input condition
		 ** that doesn't have a case statement....
		 **/
		fsm_output = _done;
		fsm_nextstate = _IDLE;
		rc = FRS_INVALID;
		break;
	    }/* end state switch */

	fsm_state = fsm_nextstate;
	fsm_input = fsm_output;

	}/* end while */

    if (rc != FRS_SUCCESS && newfrm != BNULL) freemsg(newfrm);
    return( rc );
}
#ifdef	STREAM_MESSAGES


/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_set_framebounds

  DESCRIPTION:
    This is an INTERNAL call that determines where encapsulation frame headers
    are to be inserted given a message block that is from an upper layer 
    protocol frame. The protocol frame is split up into frame relay frames 
    message blocks that have the header space allocated. The upper layer
    message block can consist of one or several messages connected through
    b_cont. The new number of message blocks is determined by the length of
    the payload and the size of all the headers. The upper layer message 
    block is then copied into the new frame relay frames, and the read
    pointer is adjusted for acommedateing the header information in 
    subsequent calls. Although copying each user payload may be expensive,
    there is no way for the application to know what the max frame size 
    of the link is. The applications should not be bound to this type of
    a restriction. 

    Below is an example of how a single upper layer protocol frame could be 
    broken into two frame relay frames....

       |<------ Upper layer protocol frame -------------------->|
(1)     usrbuf.1
	 b_cont ----> usrbuf.2
	 next (0)    b_cont ----> usrbuf.3
		     next (0)    b_cont ----> usrbuf.4
				 next (0)    b_cont ----> usrbuf.5
					     next (0)    b_cont (0)
							 next (0)
  ........................FRAME CONVERSION..................................

       |<----- Frame Relay Frame ---->|
(1)     usrbuf.1
	 b_cont----> usrbuf.2
	  .         b_cont----> usrbuf.3
	  .         next (0)   b_cont (0)
	  .                    next (0)
  
PARAMETERS:
    USRBUF *    - points to the upper level protocol frame.
    u_short     - maximum frame relay frame size (octets)
    u_short *   - returned number of frame relay frames created.
    u_short     - type of frame that is being created.
    USRBUF **	- Points the pointer for the new frame.

  RETURNS:
    FRS_SUCCESS           -- Normal completion code.
    ENCAP_BUFSIZE_INVALID -- Configuration error - usrbuf and frame size
			     conflict....

  NOTES:

*****************************************************************************/

u_short
encap_set_framebounds( pfrm, maxfrm, frame_count, frame_type , new_frm)
USRBUF *pfrm;
u_short maxfrm;
u_short *frame_count;
u_short frame_type;
USRBUF **new_frm ;
{

    /** Local variables
     **/
	
   USRBUF *up = pfrm ;
   USRBUF *fp ;
   USRBUF *fp_next ;
   USRBUF *fp_first ;
   USRBUF *header_dp ;

   u_short payload_remaining ;
   u_short up_offset, fp_offset ;  
   u_short octets, header_size, frame_length, copy_length ;

    /** First determine the total length of the user's frame
     **/
    frame_length = (u_short) m_MMGR_FRAME_LEN(up);

    /** The frame type determines the initial header length to be added
     ** the number of octets in the user frame.
     **/
    switch( frame_type )
	{
	case ISO_NLPID_FRAME:
	    header_size = (ISO_NLPID_HDR_LEN + Q922_ADDR_LEN + FRF9_HDR_LEN);
	    break;

	default:
	case IETF_SNAP_HEADER:
	    header_size = (SNAP_HDR_LEN + Q922_ADDR_LEN + FRF9_HDR_LEN);
	    break;

	case Q933_USER_SPEC:
	    header_size = (ISO_Q933_HDR_LEN + Q922_ADDR_LEN + FRF9_HDR_LEN);
	    break;

	case CISCO_ENCAP_FRAME:
		header_size = (Q922_ADDR_LEN + CISCO_ENCAP_HDR_LEN) ;
		break ;
	
	}/* end switch */

    /** Will the user frame + the header size fit into a single frame relay
     ** frame ?
     **/
    if( (frame_length + header_size) > maxfrm ) {

	/** Since the frame will be too large we increase the header size
	 ** to accomodate fragmentation.... The length of the header here 
	 ** represents only the length of the first frame.
	 **/
	header_size += (FRAG_HDR_LEN + SNAP_HDR_LEN + FRF9_HDR_LEN );

	*frame_count = 1;              /* Set the frame counter to 1          */
					/* Get the first data block length */
	payload_remaining = (u_short)m_MMGR_DATA_LEN(up) ;

	up_offset = 0 ;

	fp_next = BNULL ;
	fp_first = BNULL ;
 
	for(;;)
	{
		if((header_dp = m_MMGR_ALLOC_BUFR(header_size)) == BNULL)
		{
			/* Free all allocated messages 
		  	 */
			// There needs to be more to this clean up.....	
			if(fp_first != BNULL)
			{
				freemsg(fp_first) ;			
			}	
			*new_frm = BNULL ;	
			return(ENCAP_BUFSIZE_INVALID) ;
		}


		if((fp = m_MMGR_ALLOC_BUFR(maxfrm)) == BNULL)
		{   
			/* Free all allocated messages 
		  	 */

			// There needs to be more of this clean up 
			if(fp_first != BNULL)
			{
				freemsg(header_dp) ;
				freemsg(fp_first) ;			
			}	
			*new_frm = BNULL ;	
			return(ENCAP_BUFSIZE_INVALID) ;
		}


		header_dp->b_cont = fp ;
		/*
		 * Save first message of this group
		 */

		if(fp_first == BNULL)
			*new_frm = fp_first = header_dp ;

		/*
		 * Chain the new buffer to the last buffer 
		 */

	   	if(fp_next != BNULL)
			fp_next->b_next = header_dp ;

		/*
		 * Make room for headers in encap_makeframe 
		 */	

		header_dp->b_rptr += header_size ;
		header_dp->b_wptr += header_size ;

		fp_offset = 0 ;

		for(;;)
		{
			/* 
			 * Determine amount to copy 
			 * Get as much out of the frame as possible
			 */
	
			if(payload_remaining > ((maxfrm - fp_offset) & 0xFFE0))
			{
			    /* Fill Frame completely
			     */	
				copy_length = (maxfrm - fp_offset) & 0xFFE0 ;
			}
			else
			{
			    /* Partial Fill of Frame
			     */
				copy_length = payload_remaining ;
			}

			bcopy(up->b_rptr+up_offset, fp->b_wptr, copy_length) ;

			fp_offset  	  += copy_length ;
			up_offset  	  += copy_length ;
			fp->b_wptr 	  += copy_length ;
			payload_remaining -= copy_length ;		

			/* 
			 * Time to fill up another Frame Relay buffer?
			 */
	
			if(payload_remaining != 0)
				break ;
		
			/* 
			 * Check next data block  
			 */
	
			if(up->b_cont == BNULL)
					/* All done */
				return(FRS_SUCCESS) ;

			up = up->b_cont ;
			payload_remaining = (u_short)m_MMGR_DATA_LEN(up) ;	
			up_offset = 0 ;
		}
	
		/** Adjust the header length because it changes for frames 2-n.
		 **/

//CDK Let's not		header_size = (FRAG_HDR_LEN + SNAP_HDR_LEN + Q922_ADDR_LEN);
		(*frame_count)++ ;
		fp_next = header_dp ;
	}
    } 
    else 
    {
		mblk_t *dp ;

		/**
		 ** Dup the payload
		**/
		if(!(dp = dupmsg(pfrm)))
		{   
			return(ENCAP_BUFSIZE_INVALID) ;
		}
	
		if(!(*new_frm = m_MMGR_ALLOC_BUFR(header_size)))
		{
			freemsg(dp) ;
			return(ENCAP_BUFSIZE_INVALID) ;
		}

		(*new_frm)->b_cont = dp ;
		(*new_frm)->b_next = 0 ;

		/* 
		 * Adjust the buffer for the header information 
		 */

		(*new_frm)->b_rptr += header_size ;
		(*new_frm)->b_wptr += header_size ;

		*frame_count = 1 ;
		return (FRS_SUCCESS) ;
    }
}



#else
/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_set_framebounds

  DESCRIPTION:
    This is an INTERNAL call that determines where encapsulation frame headers
    are to be inserted given a linked list of user bufs that consists of an
    upper layer protocol frame. The protocol frame is broken up into frame
    relay frames and the usrbuf pointers are adjusted accordingly. The user
    user frame is assumed to have enough space in each of the user buffs to
    accomodate the largest frame relay header.
 
    Below is an example of how a single upper layer protocol frame could be 
    broken into two frame relay frames....

       |<------ Upper layer protocol frame -------------------->|
(1)     usrbuf.1
	 link ----> usrbuf.2
	 next (0)    link ----> usrbuf.3
		     next (0)    link ----> usrbuf.4
				 next (0)    link ----> usrbuf.5
					     next (0)    link (0)
							 next (0)
  ........................FRAME CONVERSION..................................

       |<----- Frame Relay Frame ---->|
(1)     usrbuf.1
	 link----> usrbuf.2
	  .         link----> usrbuf.3
	  .         next (0)   link (0)
	  .                    next (0)
(2)      next----> usrbuf.4
		    link----> usrbuf.5
		    next (0)   link (0)
			       next (0)
  PARAMETERS:
    USRBUF *    - points to the upper level protocol frame.
    u_short     - maximum frame relay frame size (octets)
    u_short *   - returned number of frame relay frames created.
    u_short     - type of frame that is being created.

  RETURNS:
    FRS_SUCCESS           -- Normal completion code.
    ENCAP_BUFSIZE_INVALID -- Configuration error - usrbuf and frame size
			     conflict....

  NOTES:

*****************************************************************************/

u_short
encap_set_framebounds( pfrm, maxfrm, frame_count, frame_type )
USRBUF *pfrm;
u_short maxfrm;
u_short *frame_count;
u_short frame_type;
{

    /** Local variables
     **/
    USRBUF *pc = pfrm;
    USRBUF *pn = pc->link;
    USRBUF *pframe, *ptail;
    u_short octets, header_size, frame_length;

    /** First determine the total length of the user's frame
     **/
    frame_length = (u_short) m_MMGR_FRAME_LEN(pc);

    /** The frame type determines the initial header length to be added
     ** the number of octets in the user frame.
     **/
    switch( frame_type )
	{
	case ISO_NLPID_FRAME:
	    header_size = (ISO_NLPID_HDR_LEN + Q922_ADDR_LEN);
	    break;

	default:
	case IETF_SNAP_HEADER:
	    header_size = (SNAP_HDR_LEN + Q922_ADDR_LEN);
	    break;

	case Q933_USER_SPEC:
	    header_size = (ISO_Q933_HDR_LEN + Q922_ADDR_LEN);
	    break;
	}/* end switch */

    /** Will the user frame + the header size fit into a single frame relay
     ** frame ?
     **/
    if( (frame_length + header_size) > maxfrm )

	/** Since the frame will be too large we increase the header size
	 ** to accomodate fragmentation.... The length of the header here 
	 ** represents only the length of the first frame.
	 **/
	header_size += (FRAG_HDR_LEN + SNAP_HDR_LEN);

    /** If the length of the 1st USRBUF + the header_size is more than we
     ** can put into a single frame relay frame then the length of the user 
     ** buffer is incorrect.
     **/
    if( (header_size + m_MMGR_DATA_LEN(pc)) > maxfrm )
	return( ENCAP_BUFSIZE_INVALID );

    /** The header size (at this point) is the length required for the first
     ** (and possibly the only) frame.
     **/
    octets = header_size;          /* Begin with the header length        */
    octets += m_MMGR_DATA_LEN(pc); /* then add the length of this USRBUF  */
    pframe = pc;                   /* set the current frame pointer       */
    ptail  = pc;                   /* and the current tail                */
    pframe->next = BNULL;          /* initialize this...                  */
    *frame_count = 1;              /* Set the frame counter to 1          */
    pc = pn;                       /* set the next buff to look at..      */
    pn = pc->link;

    /** If the user frame is composed of a chain of USRBUFs then we process
     ** each one to determine if we have to break the user frame into fragments
     **/
    while( pc != BNULL )
	{
	/** Add the number of octets accumulated so far to the length of
	 ** this USRBUF. If we're going to overrun the maximum length of
	 ** a frame relay frame then we break the user frame here to
	 ** create a fragment.
	 **/
	if( (octets + m_MMGR_DATA_LEN(pc)) > maxfrm )
	    {
            /** Insert a new USRBUF here, if neccessary...
                NOTE: If there is no room to prepend a header in pc
                      we need to allocate and insert a new USRBUF here
                      to hold the header.
            **/
            if ( pc->start < header_size )
               {
               pn = pc;
               if ((pc = m_MMGR_ALLOC_BUFR(header_size)) == (USRBUF *)0)
                  {
                  /** Can't get needed USRBUF, abort and return error code...
                  **/

                  /** restore original USRBUF chain.
                      (i.e. undo "next" frame chain and reconnect
                            USRBUF "link"s)
                      We can't use encap_restore_frame() here because
                      no header's have been prepended yet.
                  **/

                  pframe = pfrm;    /** point to head of frame chain **/

                  while ( pframe->next != (USRBUF *)0)
                     {
                     pc = pframe;   /** point to first USRBUF in frame **/
                     pn = pc->link; /** and next USRBUF **/

                     /** find tail of current USRBUF chain **/

                     while ( pn )
                        {
                        pc = pn;
                        pn = pc->link;
                        }

							/** pc points to tail of current frame,   
                         set USRBUF link to head of next frame.
                     **/
         
                     pc->link = pframe->next;

                     /** Clear "next" pointer...
                     **/
                     pframe->next = (USRBUF *)0;

                     /** and set pframe to next frame.
                     **/
                     pframe = pc->link;
                     }

                  /** Tell caller we failed, and why.
                  **/
                  return(FRS_NO_BUFFERS);
                  }

               pc->link = pn;
               }

	    pframe->next = pc;         /* set the 'next' frame pointer */
	    ptail->link = BNULL;       /* and cut this 'link'...       */
	    pframe = pc;               /* this is a new frame...       */
	    (*frame_count)++;          /* bump the frame count         */

	    /** Adjust the header length because it changes for frames 2-n.
	     **/
	    header_size = (FRAG_HDR_LEN + SNAP_HDR_LEN + Q922_ADDR_LEN);
	    octets = header_size;      /* Set this to the header size  */
	    }

	octets += m_MMGR_DATA_LEN(pc); /* add this to the octet count  */
	ptail = pc;                    /* set tail pointer             */
	pc = pn;                       /* change the current usrbuf    */
	pn = pc->link;                 /* and the linked one....       */
	}/* end while */

    return( FRS_SUCCESS );
}
#endif
/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_unmakeframe

  DESCRIPTION:
    This function de-encapsulates a frame relay frame and reassembles fragmented
    user frames. This routine is called by the receiver task.

  PARAMETERS:
    CCB *      - pointer to the CCB (aka PLL).
    USRBUF **  - pointer to the frame to 'unmake'.

  RETURNS:
    FRS_INVALID             - ccb is invalid
    ENCAP_NULL_FRAME_RX     - the frame pointer is null.
    ENCAP_INVALID           - PLL is was opened for encapsulation.
    ENCAP_UNK_FRAME_RX      - unknown frame type (not 03).
    ENCAP_LOST_FRAME_RX     - a frame fragment was dropped by the sender.
    FRS_COMPLETE_FRAME      - SEE NOTE BELOW.
    FRS_INCOMPLETE_FRAME    - we have not received the complete frame yet.

  NOTES:
    This function returns the API protocol id in the upper byte and 
    FRS_COMPLETE_FRAME in the lower byte whenever a complete user
    frame has been received.

*****************************************************************************/

u_short
encap_unmakeframe( pccb, pfrm )
CCB *pccb;
USRBUF **pfrm;
{
    /** Local Variables
     **/
    u_long hpll;
    u_short rc, err;
    USRBUF *errframe;
    NPPARSE_HDR phdr;

    /** Is the USRBUF pointer NULL ?
     **/
    if( *pfrm == BNULL )
	return(encap_frame_error(pccb, *pfrm, ENCAP_NULL_FRAME_RX, NOFREE));

    /** Determimine if we have a valid CCB
     **/
    if((FRSFetchCCBHandle(pccb, &hpll)) != FRS_SUCCESS)
	return( encap_frame_error( pccb, *pfrm, FRS_INVALID, FREE ) );

    /** Has the PLL been opened for encapsulation ?
     **/
    if(pccb->encap == (NPMULTIPROTOCOL) 0 )
	return( encap_frame_error( pccb, *pfrm, ENCAP_INVALID, FREE ) );

    /** Strip off the Q.922 address and set a pointer to the start of the
     ** frame header.
     **/
    FRSUnMakeFrame( pccb, pfrm );
    phdr = (NPPARSE_HDR) mSTARTOFRAME(*pfrm);

    /** If the frame type is not valid then log the error....
     **/

	if(phdr->ctrl == 0x08) /* Cisco Encapsulation */
	{


	}
	else
    if( !(mIS_VALID_FRAMETYPE(phdr->ctrl)) )
	{

		printf("Unmakeframe: Invalid Frame Type") ;

	/** If we have already processed any frame fragments then connect
	 ** this invalid frame to the chain of processed frames so that they
	 ** can be returned to the heap.
	 **/
	if( pccb->encap->fragproc != (FFN_PTR) 0 )
	    {

		printf("Unmakeframe: Invalid Frame Type - 1") ;

	    errframe = pccb->encap->psframe;
#ifndef	STREAM_MESSAGES
	    pccb->encap->peframe->link = *pfrm;
	    pccb->encap->peframe->next = BNULL;
#else
	    pccb->encap->peframe->b_cont = *pfrm;
#endif
		errframe = pccb->encap->peframe->b_cont ;
	    pccb->encap->fragproc = (FFN_PTR) 0; /* reset this */
	    }
	else
	    errframe = *pfrm; /* Single frame - no previous fragments */

	/** Increment statistics and free the USRBUF(s)
	 **/

	printf("Unmakeframe: Invalid Frame Type - 2") ;
	return(encap_frame_error(pccb, errframe, ENCAP_UNK_FRAME_RX, FREE));
	}/* end if */

    /** If we are processing frame fragments then the fragproc function pointer
     ** will be set to 'encap_fragment_handler'. If this is the case then we
     ** give the frame to the fragment handler and wait to process the return
     ** code. Otherwise, we give the frame to 'encap_parse_header'.
     **/
    if( pccb->encap->fragproc != (FFN_PTR) 0 )
	{
	/** Call the fragment handler...
	 **/
	switch( rc = pccb->encap->fragproc(pccb, pfrm, phdr) )
	    {
	    case FRS_COMPLETE_FRAME:

		/** Return the protocol type in the upper byte of the
		 ** return code.
		 **/
		rc += (pccb->encap->index << FRS_RC_PROT_SHIFT);
		return( rc );

	    /** We haven't received everything yet...
	     **/
	    case FRS_INCOMPLETE_FRAME:
		return(rc);

	    default:

		/** Something has happened to the frame we were reconstructing
		 ** We received a new frame or something was wrong with the
		 ** frame we just received.
		 **/
		err = ( rc == _ENCAP_NEW_FRAME ) ? ENCAP_LOST_FRAME_RX: rc;

		/** If the frame that failed processing is not a new frame
		 ** then connect it to the previously processed frames so that
		 ** the entire chain can be returned to the heap.
		 **/
		if( rc != _ENCAP_NEW_FRAME )
		    {
#ifndef	STREAM_MESSAGES
		    pccb->encap->peframe->link = *pfrm;
		    pccb->encap->peframe->next = BNULL;
#else
			
		    pccb->encap->peframe->b_cont = *pfrm;
#endif
		    }

		/** Free up the entire chain of USRBUFs
		 **/
		errframe = pccb->encap->psframe;
		printf("Unmakeframe: err = %x", err ) ;
		encap_frame_error( pccb, errframe, err, FREE );
		pccb->encap->psframe = BNULL;
		pccb->encap->peframe = BNULL;
		pccb->encap->fragproc = (FFN_PTR) 0; /* reset this */

		/** If we've got a new frame in the middle of the one we were
		 ** working on then we begin processing this new one below...
		 **/
		if( rc == _ENCAP_NEW_FRAME )
		    break;
		else
		    return( err );

	    }/* end switch */
	}/* end if (fragment_handler) */

    /** We arrive here if we are not processing frame fragments...
     **/
    switch( rc = encap_parse_header( pccb, pfrm, phdr ) )
	{
	case FRS_COMPLETE_FRAME:

	    /** Return the protocol type in the upper byte of the return code.
	     **/
	    rc += (pccb->encap->index << FRS_RC_PROT_SHIFT);
	    break;

	/** We recieved a frame fragment.
	 **/
	case FRS_INCOMPLETE_FRAME:
	    break;

	default:

	    /** Anything else is a problem.... statistics are incremented
	     ** in the encap_frame_error routine.
	     **/
#if 0
		printf("UnMakeFrame: rc = %x",rc );  
#endif
	    return( encap_frame_error( pccb, *pfrm, rc, FREE ) );

	}/* end switch */

    return( rc );
}

/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_parse_header

  DESCRIPTION:
    This is an INTERNAL function that is called by encap_unmakeframe. This 
    routine parses a frame header to determine if the frame is a complete 
    frame or an incomplete frame (fragmented). If the frame relay frame is
    the 1st of a series of fragmented user frames then it will force 
    encap_unmakeframe to call encap_fragment_handler for all subsequently 
    received frames. encap_fragment_handler will force encap_unmakeframe to
    call encap_parse_header only after it has processed the last fragment of
    a frame or whenever encap_fragment_handler has determined that it has been
    given the start of a new frame.
   
  PARAMETERS:
    CCB *        - pointer to the CCB
    USRBUF *     - pointer to the frame
    NPPARSE_HDR  - pointer to the parse header.

  RETURNS:
    FRS_INCOMPLETE_FRAME   - we have not received all of the fragments.
    FRS_COMPLETE_FRAME     - the frame is complete
    ENCAP_UNK_PROT_RX      - frame contains an unknown protocol encapsulation
    ENCAP_UNK_ORG_RX       - frame header specifies an unknown organization id
    ENCAP_NOT_REGISTERED   - protocol encapsulation is not registered on ccb
    ENCAP_LOST_FRAME_RX    - received an out of sequence fragmented frame 

  NOTES:

*****************************************************************************/

u_short
encap_parse_header( pccb, initial_pfrm, initial_phdr )
CCB *pccb;
USRBUF **initial_pfrm;
NPPARSE_HDR initial_phdr;
{
    /** Local variables
     **/
    u_short fsm_input, fsm_output;
    u_short fsm_state, fsm_nextstate;
    u_short ffragment, pid, frame_type, done;
    u_short rc, non_std_oui, flags;
    u_short idx = 0;
    u_short seqnum = 0;
    USRBUF *pc, *pn;
    LCB *pLCB = pccb->plcb;
    NPISO_Q933_FRAME_HDR p_q933_hdr;

	USRBUF *pfrm;
	NPPARSE_HDR phdr;

	pfrm = *initial_pfrm ;
	phdr =  initial_phdr ;

    /** Initial fsm_state and fsm_input
     **/
    fsm_nextstate = fsm_state = _IDLE;
    fsm_input = _begin;
    fsm_output = 0;
    done = 0;
    ffragment = 0;
    non_std_oui = 0;

	if(!pfrm)
	{
		printf("encap_parse_header passed a null frame") ;
		return( ENCAP_LOST_FRAME_RX) ; /* we have to trash the frame... */
	}
    /** FSM for cracking the frame header. note that the state transition 
     ** table is outlined in frsencap.h
     **/
    while( !done )
	{
	/** Switch on current fsm state
	 **/
	switch( fsm_state )
	    {

/* -------------------------------------------------------------------------- */
	    case _IDLE:
/* -------------------------------------------------------------------------- */

		/** Switch on current fsm_input token...
		 **/
		switch( fsm_input )
		    {
		    case _done:
			done = 1;
			break;

		    case _frame_complete:
			/** If statistics are enabled then count the fragment
			 ** processed; the frames processed and the number
			 ** of characters.
			 **/
			if( m_FRS_IS_STATS_ENBL(pLCB) )
			    {
			    pccb->encap->mp[idx]->frag_stats.RcvFragments++;
			    pccb->encap->mp[idx]->gen_stats.RcvFrames++;
			    pccb->encap->mp[idx]->gen_stats.RcvChars +=
				m_MMGR_FRAME_LEN(pfrm);
			    }

			rc = FRS_COMPLETE_FRAME;
			fsm_output = _done;
			break;

		    case _frame_incomplete:
			/** If statistics are enabled then count this fragment.
			 **/
			if( m_FRS_IS_STATS_ENBL(pLCB) )
			    pccb->encap->mp[idx]->frag_stats.RcvFragments++;

			rc = FRS_INCOMPLETE_FRAME;
			fsm_output = _done;
			break;

		    case _begin:
			fsm_nextstate = _PARSE_FRAME;
			fsm_output = _new_frame;
			break;

		    default:
			printf("Encap_Parse_Header 01") ;
			fsm_nextstate = _FSM_ERROR;
			break;
		    }/* end fsm_input switch for _IDLE state */

		break;
/**/
/* -------------------------------------------------------------------------- */
	    case _PARSE_FRAME:
/* -------------------------------------------------------------------------- */

		/** Switch on current fsm_input token...
		 **/
		switch( fsm_input )
		    {
		    case _new_frame:

			/** Determine the frame type...
			 **/
			switch( frame_type = encap_get_frmtype(phdr) )
			    {
			    case ISO_NLPID_FRAME:
				fsm_output = _iso_nlpid_frame;
				break;

			    case ISO_FRF9_FRAME:
				fsm_output = _iso_frf9_frame;
				break;

			    case ISO_Q933_FRAME:
				fsm_output = _iso_q933_frame;
				break;

			    case IETF_SNAP_HEADER:
				fsm_output = _snap_header;
				break;

				case CISCO_ENCAP_FRAME:
				fsm_output = _cisco_encap_frame ;
				break ;

			    case UNKNOWN_NLPID:
#if 1
				printf("_FRAME_ERROR 01") ;

#endif
				fsm_output = _unknown_nlpid;
				fsm_nextstate = _FRAME_ERROR;
				break;
			    }
			break;

		    case _snap_header:

			/** Process the snap header...
			 **/
			if( !(mIS_VALID_FRAMETYPE(phdr->ctrl)) )
			    {

#if 1
				printf("_FRAME_ERROR 01-A") ;

#endif
			    fsm_output = _bad_frame_control;
			    fsm_nextstate = _FRAME_ERROR;
			    break;
			    }
       
			/** OUI Processing
			 **/
			if( (phdr->oui[0] == 0x00) &&  /* IETF 802.1 */
			    (phdr->oui[1] == 0x80) &&
			    (phdr->oui[2] == 0xC2) )
			    ;/* VOID */
			else 

			if( (phdr->oui[0] == 0x00) &&  /* Ethertype */
			    (phdr->oui[1] == 0x00) &&
			    (phdr->oui[2] == 0x00) )
			    ;  /* VOID */

			else
			    {
			     /** Here we allow the processing of other OUI values
			      ** by calling the function: 'sys_oui_proc'. 
			      ** If the function returns a non-zero value then
			      ** we considered the OUI in the frame header to be
			      ** valid - but non standard; this allows the 
			      ** remainder of the header to be processed. If the 
			      ** function returns FALSE then the OUI value is 
			      ** considered to be unknown.
			      **/

			      if( (sys_oui_proc(pccb, phdr)) == FALSE )
				 {

#if 1
				printf("_FRAME_ERROR 02") ;

#endif
				 fsm_output = _unknown_oui;
				 fsm_nextstate = _FRAME_ERROR;
				 break;
				 }
			      else 
				 non_std_oui = 1; /* Ok.. but non standard */
			    }/* end if IETF oui */

			/** Get the PID value from the protocol header.
			 **/
			pid = mMAKEUSHORT(phdr->pid[0], phdr->pid[1]);

			/** Fragmented frame ? 
			 ** The OUI must be IETF or 00-00-00 and the PID must be
			 ** FRAGMENT_PID.
			 **/
			if( mIS_FRAGMENTED_FRAME(pid) && !(non_std_oui) )
			    {
			    /** If we already have a fragmented frame
			     ** then we've seen two snap headers in this frame
			     ** with the PID set to FRAGMENT_PID....
			     **/
			    if( ffragment )
				{


#if 1
				printf("_FRAME_ERROR 03") ;

#endif
				fsm_output = _invalid_frame;
				fsm_nextstate = _FRAME_ERROR;
				break;
				}
			    else
				{
				fsm_output = _frame_fragment;
				fsm_nextstate = _PARSE_FRAGMENT;
				break;
				}
			    }/* end if fragment PID */

			/** Look up the API value for this PID.
			 **/
			pccb->encap->index = idx = 
			      (u_short)encap_pid2pid_api(pid, (u_char)frame_type);
			/** Make sure we support the PID and check for
			 ** registration on this ccb.
			 **/
			if( !(mIS_PID_VALID(idx)) )
			    {

#if 1
				printf("_FRAME_ERROR 04") ;

#endif
			    fsm_output = _unknown_pid;
			    fsm_nextstate = _FRAME_ERROR;
			    }
			else
			if( !(mIS_PID_REGISTERED(pccb->encap->mp[idx])) )
			    {


#if 1
				printf("_FRAME_ERROR 05") ;

#endif
			    fsm_output = _unregistered_pid;
			    fsm_nextstate = _FRAME_ERROR;
			    }
			else
			    {
			    /** Move the frame pointer to the start of the 
			     ** user payload.
			     **/
			    mREMOVE_FRAME_HDR(pfrm, SNAP_HDR_LEN);
			    fsm_output = _user_frame;
			    }
			break;

		    case _iso_nlpid_frame:

			pid = (u_short) phdr->nlpid_1; /* pid is NLPID */

			/** Look up the API value for this PID.
			 **/
			pccb->encap->index = idx = 
			  (u_short)encap_pid2pid_api(pid, (u_char) frame_type);
			/** Check for PID registration on this ccb.
			 **/
			if( !(mIS_PID_REGISTERED(pccb->encap->mp[idx])) )
			    {
			
#if 1
				printf("_FRAME_ERROR 06") ;

#endif
			    fsm_output = _unregistered_pid;
			    fsm_nextstate = _FRAME_ERROR;
			    }
			else
			    {
			    /** Move the frame pointer to the start of the 
			     ** user payload.
			     **/
			    mREMOVE_FRAME_HDR(pfrm, ISO_NLPID_HDR_LEN);
			    fsm_output = _user_frame;
			    }

			break;

		    case _iso_q933_frame:

			/** Is this a User specified layer 3 header ?
			 **/
			p_q933_hdr = (NPISO_Q933_FRAME_HDR) phdr;

			if( (p_q933_hdr->l2_pid[0] == 0x4C) &&
			    (p_q933_hdr->l2_pid[1] == 0x80) &&
			    (p_q933_hdr->l3_pid[0] == USER_SPEC) )
			    {
			    pid = (u_short) p_q933_hdr->l3_pid[1];
			    frame_type = Q933_USER_SPEC; /* change this... */
			    }
			else
			    {
			    /** Other Q933 headers are currently not supported.
			     **/


#if 1
				printf("_FRAME_ERROR 07") ;

#endif
			    fsm_output = _invalid_frame;
			    fsm_nextstate = _FRAME_ERROR;
			    break;
			    }

			/** Look up the API value for this PID.
			 **/
			pccb->encap->index = idx = 
			  (u_short)encap_pid2pid_api(pid, (u_char) frame_type);

			/** Make sure we support the PID and check for
			 ** registration on this ccb.
			 **/
			if( !(mIS_PID_VALID(idx)) )
			    {
			    fsm_output = _unknown_pid;
#if 1
				printf("_FRAME_ERROR 08") ;
#endif
			    fsm_nextstate = _FRAME_ERROR;
			    }
			else
			if( !(mIS_PID_REGISTERED(pccb->encap->mp[idx])) )
			    {
#if 1
				printf("_FRAME_ERROR 09") ;
#endif
			    fsm_output = _unregistered_pid;
			    fsm_nextstate = _FRAME_ERROR;
			    }
			else
			    {
			    /** Move the frame pointer to the start of the 
			     ** user payload.
			     **/
			    mREMOVE_FRAME_HDR(pfrm, ISO_Q933_HDR_LEN);
			    fsm_output = _user_frame;
			    }
			break;

		    case _user_frame:

			/** If this frame is fragmented...
			 **/
			if( ffragment )
			    {
			    /** Store the first and last frame pointers
			     **/
			    pc = pccb->encap->psframe = pfrm;
			    mGET_LAST_USRBUF(pc, pn);
			    pccb->encap->peframe = pc;

			    /** Store the next sequence number to expect.
			     **/
#if WRONG_CODE
			    pccb->encap->next_seq_number = seqnum+1;
#else
			    pccb->encap->next_rx_seq_num = seqnum ;
#endif

			    /** Set frame header processing for frames 2-n.
			     **/
			    pccb->encap->fragproc = encap_fragment_handler;
			    fsm_output = _frame_incomplete;
			    fsm_nextstate = _IDLE;
			    }
			else
			    {
			    fsm_output = _frame_complete;
			    fsm_nextstate = _IDLE;
			    }
			break;

		    case _iso_frf9_frame:
			/* FRF.9 DCP Frame */

			if (pc = (mblk_t *) ParseFRF9Frame(pccb, *initial_pfrm))
			{
					*initial_pfrm = pfrm = pc ;

					FRSUnMakeFrame( pccb, &pc ) ;
				    phdr = (NPPARSE_HDR) mSTARTOFRAME(*initial_pfrm) ;

					fsm_output    = _begin ;
					fsm_nextstate = _IDLE ;
			}
			else
			{
#if 0
				printf("_FRAME_ERROR 10") ;
#endif
				fsm_nextstate = _FSM_ERROR;
			}
			break ;

			case _cisco_encap_frame:
			
				pid = (u_short) phdr->nlpid_1; /* pid is NLPID */

				pid &= 0xFF ;

				/** Look up the API value for this PID.
				 **/
				pccb->encap->index = idx = 
				  (u_short)encap_pid2pid_api(pid, (u_char) frame_type);
				/** Check for PID registration on this ccb.
				 **/
				if( !(mIS_PID_REGISTERED(pccb->encap->mp[idx])) )
			    {
			
#if 1
				printf("_FRAME_ERROR 11") ;

#endif
				    fsm_output = _unregistered_pid;
				    fsm_nextstate = _FRAME_ERROR;
			    }
				else
			    {
				    /** Move the frame pointer to the start of the 
				     ** user payload.
				     **/
				    mREMOVE_FRAME_HDR(pfrm, CISCO_ENCAP_HDR_LEN);

			    	fsm_output = _frame_complete;
			    	fsm_nextstate = _IDLE;
			    }

			break ;


			default:
#if 1
			printf("Encap_Parse_Header 02") ;
#endif
			fsm_nextstate = _FSM_ERROR;
			break;
		    }/* end fsm_input switch for _PARSE_FRAME state */

		break;
/**/
/* -------------------------------------------------------------------------- */
	    case _PARSE_FRAGMENT:
/* -------------------------------------------------------------------------- */

		/** Switch on current fsm_input token...
		 **/
		switch( fsm_input )
		    {
		    case _frame_fragment:
			/** Confirm that this is the first frame in the
			 ** sequence...
			 **/
			flags = mMAKEUSHORT(phdr->fragment.info[0],
					    phdr->fragment.info[1]);
 
			if(( !(flags & FINAL_FRAME_BIT) ) && /* not last   */
			     !(flags & FRAME_OFFSET) ) /* with zero offset */
			    {
			    ffragment = 1; /* set this... */

			    /** Save the sequence number of this frame.
			     **/
			    seqnum = mMAKEUSHORT(phdr->fragment.seq[0],
						 phdr->fragment.seq[1]);

			    /** Move the frame pointer to the start of the 
			     ** next frame header.
			     **/
			    mREMOVE_FRAME_HDR(pfrm, SNAP_HDR_LEN+FRAG_HDR_LEN);
			    phdr = (NPPARSE_HDR) mSTARTOFRAME(pfrm);
			    fsm_output = _new_frame;
			    fsm_nextstate = _PARSE_FRAME;
			    }
			else
			    {
			    /** We've probably got the last fragment of a frame
			     ** but we didn't get the first one....
			     **/


#if 1
				printf("_FRAME_ERROR 11") ;

#endif
			    fsm_output = _lost_frame;
			    fsm_nextstate = _FRAME_ERROR;
			    }
			break;

		    default:
#if 1
			printf("Encap_Parse_Header 03") ;
#endif
			fsm_nextstate = _FSM_ERROR;
			break;

		    }/* end fsm_input switch for _PARSE_FRAGMENT state */

		break;
/**/
/* -------------------------------------------------------------------------- */
	    case _FRAME_ERROR:
/* -------------------------------------------------------------------------- */

		fsm_output = _done;
		fsm_nextstate = _IDLE;

		/** Switch on current fsm_input token...
		 **/
		switch( fsm_input )
		    {
		    case _unknown_pid:
			rc = ENCAP_UNK_PROT_RX;
			break;

		    case _unknown_oui:
			rc = ENCAP_UNK_ORG_RX;
			break;

		    case _bad_frame_control:
		    case _unknown_nlpid:
		    case _invalid_frame:
			rc = ENCAP_UNK_FRAME_RX;
			break;

		    case _unregistered_pid:
			rc = ENCAP_NOT_REGISTERED;
			break;

		    case _lost_frame:
			rc = ENCAP_LOST_FRAME_RX;
			break;


		    default:
#if 1
			printf("Encap_Parse_Header 04") ;
#endif
			fsm_nextstate = _FSM_ERROR;
			break;
		    }/* end fsm_input switch for _FRAME_ERROR state */
		break;

/**/
/* -------------------------------------------------------------------------- */
	    default:
	    case _FSM_ERROR:
/* -------------------------------------------------------------------------- */
		/** Something wrong with the fsm. Probably an input condition
		 ** that doesn't have a case statement....
		 **/
		fsm_output = _done;
		fsm_nextstate = _IDLE;
		rc = ENCAP_LOST_FRAME_RX; /* we have to trash the frame... */
		break;

	    }/* end switch state */

	/** Change the state and input
	 **/
	fsm_input = fsm_output;
	fsm_state = fsm_nextstate;

	}/* end while !done */

    return( rc );
}
/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_fragment_handler

  DESCRIPTION:
    This is an INTERNAL function is called by encap_parse_header whenever
    it has encountered a frame relay frame containing a fragmented user
    frame. The encap_parse_header function processes ONLY the 1st frame, this
    routine is called to process frames 2 - n. This routine will return
    FRS_COMPLETE_FRAME whenever it has processed the last frame fragment. If
    it has processed a fragment which was not the last frame then it returns
    FRS_INCOMPLETE_FRAME. If it has discovered that it has been given a new
    frame to process ( A new frame is a frame which is not part of the
    current fragmented user frame ) it will return _ENCAP_NEW_FRAME. This
    causes encap_parse_header to treat this frame as the start of a new
    frame.

  PARAMETERS:
    CCB *        - pointer to the CCB
    USRBUF **    - pointer to a frame pointer
    NPPARSE_HDR  - pointer to a parse header.

  RETURNS:
    FRS_INCOMPLETE_FRAME   - we have not received all of the fragments.
    FRS_COMPLETE_FRAME     - the frame is complete
    ENCAP_UNK_PROT_RX      - frame contains an unknown protocol encapsulation
    ENCAP_UNK_ORG_RX       - frame header specifies an unknown organization id
    ENCAP_LOST_FRAME_RX    - Invalid frame sequence number - frame is lost.
    _ENCAP_NEW_FRAME       - received a new user frame instead of a fragment.

  NOTES:

*****************************************************************************/

u_short
encap_fragment_handler( pccb, initial_pfrm, initial_phdr )
CCB *pccb;
USRBUF **initial_pfrm;
NPPARSE_HDR initial_phdr;
{
    /** Local variables
     **/
    u_short fsm_input, fsm_output;
    u_short fsm_state, fsm_nextstate;
    u_short pid, idx, done;
    u_short rc, seqnum, flags;
    USRBUF *pc, *pn;
    LCB *pLCB = pccb->plcb;

	USRBUF **pfrm ;
	NPPARSE_HDR phdr;

	phdr = initial_phdr ;
	pfrm = initial_pfrm ;

    /** Initial fsm_state and fsm_input
     **/
    fsm_nextstate = fsm_state = _IDLE;
    fsm_input = _begin;
    fsm_output = 0;
    done = 0;

    /** We are in this function because we have presumably processed the
     ** first frame of a fragmented user protocol frame. This function
     ** is similar to the parse header function. The transition table for
     ** this function is outlined in frsencap.h
     **/

    while( !done )
	{
	/** Switch on current fsm state
	 **/
	switch( fsm_state )
	    {

/* -------------------------------------------------------------------------- */
	    case _IDLE:
/* -------------------------------------------------------------------------- */

		/** Switch on current fsm_input token...
		 **/
		switch( fsm_input )
		    {
		    case _done:
			done = 1;
			break;

		    case _frame_complete:
			/** If statistics are enabled then count the frame,
			 ** characters received and reassembled frames.
			 **/
			if( m_FRS_IS_STATS_ENBL(pLCB) )
			    {
			    idx = pccb->encap->index;
			    pccb->encap->mp[idx]->gen_stats.RcvFrames++;
			    pccb->encap->mp[idx]->gen_stats.RcvChars +=
				m_MMGR_FRAME_LEN(*pfrm);
			    pccb->encap->mp[idx]->frag_stats.RcvReassembled++;
			    }

			rc = FRS_COMPLETE_FRAME;
			fsm_output = _done;
			break;

		    case _frame_incomplete:
			/** If statistics are enabled then count this fragment.
			 **/
			if( m_FRS_IS_STATS_ENBL(pLCB) )
			    {
			    idx = pccb->encap->index;
			    pccb->encap->mp[idx]->frag_stats.RcvFragments++;
			    }

			rc = FRS_INCOMPLETE_FRAME;
			fsm_output = _done;
			break;

		    case _begin:
			fsm_nextstate = _PARSE_FRAME;
			fsm_output = _new_frame;
			break;

		    default:
		
#if 1
			printf("Encap_Parse_Header 05") ;
#endif
			fsm_nextstate = _FSM_ERROR;
			break;
		    }/* end fsm_input switch for _IDLE state */

		break;
/**/
/* -------------------------------------------------------------------------- */
	    case _PARSE_FRAME:
/* -------------------------------------------------------------------------- */

		/** Switch on current fsm_input token...
		 **/
		switch( fsm_input )
		    {
		    /** This frame should only have a SNAP header indicating
		     ** that the frame is a fragment. If any other header is
		     ** discovered then the frame must be an entirely new
		     ** frame. The previous frame will be dropped and the new
		     ** one will be processed in its place.
		     **/
		    case _new_frame:

			/** Determine the frame type...
			 **/
			switch( encap_get_frmtype(phdr) )
			    {
			    case ISO_NLPID_FRAME:
			    case ISO_Q933_FRAME:
#if 1
				printf("_FRAME_ERROR 12") ;
#endif

				fsm_output = _drop_frame;
				fsm_nextstate = _FRAME_ERROR;
				break;

			    case ISO_FRF9_FRAME:
				fsm_output = _iso_frf9_frame;
				fsm_nextstate = _PARSE_FRAGMENT;
				break;

			    case IETF_SNAP_HEADER:
				fsm_output = _snap_header;
				break;

			    case UNKNOWN_NLPID:

#if 1
				printf("_FRAME_ERROR 13") ;

#endif
				fsm_output = _unknown_nlpid;
				fsm_nextstate = _FRAME_ERROR;
				break;
			    }
			break;

		    case _snap_header:

			/** Process the snap header; we're expecting it to
			 ** contain an IETF OUI with the PID set to FRAGMENT_PID
			 **/
			if( (phdr->oui[0] == 0x00) &&  /* IETF 802.1 */
			    (phdr->oui[1] == 0x80) &&
			    (phdr->oui[2] == 0xC2) )
			     ;/* VOID */
			else
			    {
			    /** The OUI is not 802.1 IETF - we must have a
			     ** new frame that begins with a SNAP header with
			     ** a non standard OUI value.
			     **/
#if 1
				printf("_FRAME_ERROR 14") ;
#endif
			    fsm_output = _drop_frame;
			    fsm_nextstate = _FRAME_ERROR;
			    break;
			    }

			/** Get the PID value from the protocol header.
			 **/
			pid = mMAKEUSHORT(phdr->pid[0], phdr->pid[1]);

			/** This should contain the FRAGMENT_PID...
			 **/
			if( !(mIS_FRAGMENTED_FRAME(pid)) )
			    {
			    /** The PID indicates something other than a
			     ** frame fragment...
			     **/

#if 1
				printf("_FRAME_ERROR 15") ;
#endif
			    fsm_output = _drop_frame;
			    fsm_nextstate = _FRAME_ERROR;
			    break;
			    }
		    
			fsm_output = _frame_fragment;
			fsm_nextstate = _PARSE_FRAGMENT;
			break;

		    default:
#if 1
			printf("Encap_Parse_Header 06") ;
#endif
			fsm_nextstate = _FSM_ERROR;
			break;
		    }/* end fsm_input switch for _PARSE_FRAME state */

		break;
/**/
/* -------------------------------------------------------------------------- */
	    case _PARSE_FRAGMENT:
/* -------------------------------------------------------------------------- */

		/** Switch on current fsm_input token...
		 **/
		switch( fsm_input )
		    {
		    case _frame_fragment:

			/** Make sure we don't have a new frame that is
			 ** fragmented...
			 **/
			flags = mMAKEUSHORT(phdr->fragment.info[0],
					    phdr->fragment.info[1]);
 
			if(( !(flags & FINAL_FRAME_BIT) ) && /* not last   */
			     !(flags & FRAME_OFFSET) ) /* with zero offset */
			    {
			    /** This is a new fragmented frame....
			     **/
				
#if 1
				printf("_FRAME_ERROR 16") ;
#endif
			    fsm_output = _drop_frame;
			    fsm_nextstate = _FRAME_ERROR;
			    break;
			    }

			/** Get the sequence number of this frame..
			 **/
			seqnum = mMAKEUSHORT(phdr->fragment.seq[0],
					     phdr->fragment.seq[1]);
			
			/** Is this the sequence number we were expecting to
			 ** receive ?
			 **/
			if( seqnum != pccb->encap->next_rx_seq_num )
			    {
			    /** We've missed a frame...
			     **/
				
#if 1
				printf("_FRAME_ERROR 17") ;
#endif
			    fsm_output = _lost_frame;
			    fsm_nextstate = _FRAME_ERROR;
			    break;
			    }

			/** Connect this frame with the last frame we received
			 **/
			pc = *pfrm;
			mGET_LAST_USRBUF(pc, pn);
#ifndef	STREAM_MESSAGES
			pccb->encap->peframe->link = *pfrm;
			pccb->encap->peframe->next = BNULL;
#else
			pccb->encap->peframe->b_cont = *pfrm;
#endif
			pccb->encap->peframe = pc;

			/** Set the frame pointer to the start of user payload.
			 **/
			mREMOVE_FRAME_HDR(*pfrm, SNAP_HDR_LEN+FRAG_HDR_LEN);

			/** Is this the last frame in the sequence ?
			 **/
			if( flags & FINAL_FRAME_BIT )
			    {
			    /** Set the fragproc pointer to NULL so that we
			     ** don't get called again; set the frame pointer
			     ** to the beginning of the user frame.
			     **/
			    pccb->encap->fragproc = (FFN_PTR) 0;
			    *pfrm = pccb->encap->psframe;
			    fsm_output = _frame_complete;
			    fsm_nextstate = _IDLE;
			    }
			else
			    {
#if WRONG_CODE
			    /** We're not done yet, increment the sequence 
			     ** number we're expecting to get on the next frame.
			     **/
			    pccb->encap->next_rx_seq_num++;
#endif
			    fsm_output = _frame_incomplete;
			    fsm_nextstate = _IDLE;
			    }
			break;

		    case _iso_frf9_frame:
			/* FRF.9 DCP Frame */

			if (pc = (mblk_t *) ParseFRF9Frame(pccb, *pfrm))
			{
					*pfrm = pc ;

					FRSUnMakeFrame( pccb, &pc ) ;
				    phdr = (NPPARSE_HDR) mSTARTOFRAME(*pfrm) ;

					fsm_output    = _begin ;
					fsm_nextstate = _IDLE ;
			}
			else
			{

#if 1
				printf("_FRAME_ERROR 18") ;
#endif
					fsm_nextstate = _FSM_ERROR;
			}
			break ;

		    default:

#if 1
			printf("Encap_Parse_Header 07") ;
#endif
			fsm_nextstate = _FSM_ERROR;
			break;

		    }/* end fsm_input switch for _PARSE_FRAGMENT state */

		break;
/**/
/* -------------------------------------------------------------------------- */
	    case _FRAME_ERROR:
/* -------------------------------------------------------------------------- */

		/** The next state and input is the same for all of these.
		 **/
		fsm_output = _done;
		fsm_nextstate = _IDLE;

		/** If statistics are enabled then count the frame we were
		 ** processing as a reassembly failure.
		 **/
		if (m_FRS_IS_STATS_ENBL(pLCB))
		    {
		    idx = pccb->encap->index;
		    pccb->encap->mp[idx]->frag_stats.RcvReassembleFail++;
		    }

		/** Reset the fragproc function pointer so that we are
		 ** not called again...
		 **/
		pccb->encap->fragproc = (FFN_PTR) 0;

		/** Switch on current fsm_input token...
		 **/
		switch( fsm_input )
		    {
		    case _drop_frame:
			rc = _ENCAP_NEW_FRAME;
			break;

		    case _bad_frame_control:
		    case _unknown_nlpid:
			rc = ENCAP_UNK_FRAME_RX;
			break;

		    case _lost_frame:
			rc = ENCAP_LOST_FRAME_RX;
			break;

		    default:
			fsm_nextstate = _FSM_ERROR;
			break;
		    }/* end fsm_input switch for _FRAME_ERROR state */
		break;
/**/
/* -------------------------------------------------------------------------- */
	    default:
	    case _FSM_ERROR:
/* -------------------------------------------------------------------------- */
		/** Something wrong with the fsm. Probably an input condition
		 ** that doesn't have a case statement....
		 **/
		fsm_output = _done;
		fsm_nextstate = _IDLE;
		rc = ENCAP_LOST_FRAME_RX; /* we have to trash the frame... */
		break;

	    }/* end switch state */

	/** Change the state and input
	 **/
	fsm_input = fsm_output;
	fsm_state = fsm_nextstate;

	}/* end while !done */

    return( rc );
}



/* These functions are not used */
#if 0
/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_frame_complete

  DESCRIPTION:
    This is an INTERNAL function that the transmit task calls after sending
    out a frame relay frame. The header is parsed to determine the type of
    frame relay encapsulation frame that was transmitted. If the frame is
    a component of a larger user frame then after processing the first fragment
    it replaces itself with encap_assemble_frame. Once the user's frame has been
    completely reconstructed, this routine (or encap_assemble_frame) will call
    the user provided 'TxDone' function. If the user didn't provide a 'TxDone'
    call then the frame is returned to the heap.

  PARAMETERS:
    CCB *     - pointer to the CCB.
    USRBUF *  - pointer to the transmitted frame relay frame.
    u_short   - a code that indicates the result of the transmission.

  RETURNS:
    nothing....

  NOTES:

*****************************************************************************/

void
encap_frame_complete( pccb, pfrm, code )
CCB *pccb;
USRBUF *pfrm;
u_short code;
{
    /** Local variables
     **/
    u_short fsm_input, fsm_output;
    u_short fsm_state, fsm_nextstate;
    u_short ffragment, pid, done;
    u_short non_std_oui, frame_type, flags;
    u_short idx = 0;
    NPPARSE_HDR phdr;
    BFN_PTR fp;
    USRBUF *pc, *pn;
    LCB *pLCB = pccb->plcb;
    NPISO_Q933_FRAME_HDR p_q933_hdr;

    /** Initial fsm_state and fsm_input
     **/
    fsm_nextstate = fsm_state = _IDLE;
    fsm_input = _begin;
    fsm_output = 0;
    done = 0;
    ffragment = 0;
    non_std_oui = 0;


    /** Strip off the Q922 address and set a pointer to the start
     ** of this frame.
     **/
    mREMOVE_FRAME_HDR(pfrm, Q922_ADDR_LEN);
    phdr = (NPPARSE_HDR) mSTARTOFRAME(pfrm);

    /** This frame header parser is similar to encap_parse_header. The state 
     ** transition table is outlined in frsencap.h
     **/
    while( !done )
	{
	/** Switch on current fsm state
	 **/
	switch( fsm_state )
	    {

/* -------------------------------------------------------------------------- */
	    case _IDLE:
/* -------------------------------------------------------------------------- */

		/** Switch on current fsm_input token...
		 **/
		switch( fsm_input )
		    {
		    case _done:
			done = 1;
			break;

		    case _frame_complete:

			/** Update statistics only if the frame was sent.
			 **/
			if( code == FRS_SUCCESS )
			    {
			    /** If statistics are enabled then count this frame.
			     **/
			    if( m_FRS_IS_STATS_ENBL(pLCB) )
				{
				pccb->encap->mp[idx]->gen_stats.SendFrames++;
				pccb->encap->mp[idx]->gen_stats.SendChars +=
				    m_MMGR_FRAME_LEN(pfrm);
				}
			    }

			/** Call the user callback function - if one doesn't
			 ** exist then free the frame ourselves.
			 **/
#ifndef	STREAM_MESSAGES
			fp = pccb->encap->mp[idx]->fcallback;
			if( fp == (BFN_PTR) 0 )
			    encap_free_frame( pfrm );
			else
			    fp( pccb, pfrm, code );
#else
			/* 
			 * If Streams are used then the message block
			 * was sent to the WAN driver which frees them 
			 */
#endif
			fsm_output = _done;
			break;

		    case _frame_incomplete:

			/** Update statistics only if the frame was sent.
			 **/
			if( code == FRS_SUCCESS )
			    {
			    /** If statistics are enabled then count this
			     ** frame fragment.
			     **/
			    if( m_FRS_IS_STATS_ENBL(pLCB) )
				{
				pccb->encap->mp[idx]->frag_stats.SendFragments++;
				pccb->encap->mp[idx]->frag_stats.SendFragmented++;
				}
			    }

			/** Set frame header processing for frames 2-n.
			 **/
			pccb->TxDoneFunc = encap_assemble_frame;
			fsm_output = _done;
			break;

		    case _begin:
			fsm_nextstate = _PARSE_FRAME;
			fsm_output = _new_frame;
			break;

		    default:
			fsm_nextstate = _FSM_ERROR;
			break;
		    }/* end fsm_input switch for _IDLE state */

		break;
/**/
/* -------------------------------------------------------------------------- */
	    case _PARSE_FRAME:
/* -------------------------------------------------------------------------- */

		/** Switch on current fsm_input token...
		 **/
		switch( fsm_input )
		    {
		    case _new_frame:

			/** Determine the frame type...
			 **/
			switch( frame_type = encap_get_frmtype(phdr) )
			    {
			    case ISO_NLPID_FRAME:
				fsm_output = _iso_nlpid_frame;
				break;

			    case ISO_Q933_FRAME:
				fsm_output = _iso_q933_frame;
				break;

			    case IETF_SNAP_HEADER:
				fsm_output = _snap_header;
				break;

			    default:
				fsm_nextstate = _FSM_ERROR;
			    }
			break;

		    case _snap_header:

			/** What is the OUI ?
			 **/
			if( (phdr->oui[0] == 0x00) &&  /* IETF 802.1 */
			    (phdr->oui[1] == 0x80) &&
			    (phdr->oui[2] == 0xC2) )
			     ;/* VOID */
			else
			    non_std_oui = 1;

			/** Get the PID value from the protocol header.
			 **/
			pid = mMAKEUSHORT(phdr->pid[0], phdr->pid[1]);

			/** Fragmented frame ?
			 ** The OUI must be IETF and the PID must be
			 ** FRAGMENT_PID.
			 **/
			if( mIS_FRAGMENTED_FRAME(pid) && !(non_std_oui) )
			    {
			    fsm_output = _frame_fragment;
			    fsm_nextstate = _PARSE_FRAGMENT;
			    break;
			    }/* end if fragment PID */

			/** Look up the API value for this PID.
			 **/
			pccb->encap->index = idx =
			    (u_short)encap_pid2pid_api(pid, (u_char) frame_type);

			/** Move the frame pointer to the start of the 
			 ** user payload.
			 **/
			mREMOVE_FRAME_HDR(pfrm, SNAP_HDR_LEN);
			fsm_output = _user_frame;
			break;

		    case _iso_nlpid_frame:

			pid = (u_short) phdr->nlpid_1; /* pid is NLPID */

			/** Look up the API value for this PID.
			 **/
			pccb->encap->index = idx =
			  (u_short)encap_pid2pid_api(pid, (u_char) frame_type);

			/** Move the frame pointer to the start of the 
			 ** user payload.
			 **/
			mREMOVE_FRAME_HDR(pfrm, ISO_NLPID_HDR_LEN);
			fsm_output = _user_frame;
			break;

		    case _iso_q933_frame:

			/** Is this a User specified layer 3 header ?
			 **/
			p_q933_hdr = (NPISO_Q933_FRAME_HDR) phdr;

			if( (p_q933_hdr->l2_pid[0] == 0x4C) &&
			    (p_q933_hdr->l2_pid[1] == 0x80) &&
			    (p_q933_hdr->l3_pid[0] == USER_SPEC) )
			    {
			    pid = (u_short) p_q933_hdr->l3_pid[1];
			    frame_type = Q933_USER_SPEC; /* change this... */
			    }
			else
			    {
			    /** Other Q933 headers are currently not supported.
			     **/
			    fsm_nextstate = _FSM_ERROR;
			    break;
			    }

			/** Look up the API value for this PID.
			 **/
			pccb->encap->index = idx =
			    (u_short)encap_pid2pid_api(pid, (u_char) frame_type);

			/** Move the frame pointer to the start of the 
			 ** user payload.
			 **/
			mREMOVE_FRAME_HDR(pfrm, ISO_Q933_HDR_LEN);
			fsm_output = _user_frame;
			break;

		    case _user_frame:

			/** If this frame is fragmented...
			 **/
			if( ffragment )
			    {
			    /** Store the first and last frame pointers
			     **/
			    pc = pccb->encap->ptxsframe = pfrm;
			    mGET_LAST_USRBUF(pc, pn);

			    pccb->encap->ptxeframe = pc;
#ifndef	STREAM_MESSAGES
			    pccb->encap->ptxeframe->next = BNULL;
#endif
			    pccb->encap->code = code;

			    fsm_output = _frame_incomplete;
			    fsm_nextstate = _IDLE;
			    }
			else
			    {
			    fsm_output = _frame_complete;
			    fsm_nextstate = _IDLE;
			    }
			break;

		    default:
			fsm_nextstate = _FSM_ERROR;
			break;
		    }/* end fsm_input switch for _PARSE_FRAME state */

		break;
/**/
/* -------------------------------------------------------------------------- */
	    case _PARSE_FRAGMENT:
/* -------------------------------------------------------------------------- */

		/** Switch on current fsm_input token...
		 **/
		switch( fsm_input )
		    {
		    case _frame_fragment:

			/** Get the fragment information from the header.
			 **/
			flags = mMAKEUSHORT(phdr->fragment.info[0],
					    phdr->fragment.info[1]);

			/** This should be the first frame in the series...
			 **/
			if( flags & FRAME_OFFSET )
			    {
			    /** This is a fragment BUT not the first one...
			     ** Forget we ever got this....
			     **/
			    encap_free_frame( pfrm );
			    fsm_output = _done;
			    fsm_nextstate = _IDLE;
			    break;
			    }

			ffragment = 1; /* set this because its fragmented */

			/** Move the frame pointer to the start of the next
			 ** header...
			 **/
			mREMOVE_FRAME_HDR(pfrm, SNAP_HDR_LEN+FRAG_HDR_LEN);
			phdr = (NPPARSE_HDR) mSTARTOFRAME(pfrm);

			fsm_output = _new_frame;
			fsm_nextstate = _PARSE_FRAME;
			break;

		    default:
			fsm_nextstate = _FSM_ERROR;
			break;

		    }/* end fsm_input switch for _PARSE_FRAGMENT state */
		break;
/**/
/* -------------------------------------------------------------------------- */
	    default:
	    case _FSM_ERROR:
/* -------------------------------------------------------------------------- */
		/** Something wrong with the fsm. Probably an input condition
		 ** that doesn't have a case statement....
		 **/
		fsm_output = _done;
		fsm_nextstate = _IDLE;
		break;

	    }/* end switch state */

	/** Change the state and input
	 **/
	fsm_input = fsm_output;
	fsm_state = fsm_nextstate;

	}/* end while !done */

    return;
}
/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_assemble_frame

  DESCRIPTION:
    This function dynamically replaces the normal TxDone function 
    (encap_frame_complete) after the TxDone function has processed the first 
    frame in a fragment. This routine is called to process fragments 2 to n, 
    after which it replaces the TxDone function with the normal call. This 
    routine is responsible for the reassembly of the original upper level 
    protocol frame during the time the fragments are individually transmitted.
    It effectively provides the inverse functionality of encap_set_framebounds.

  PARAMETERS:
    CCB *     - pointer to the PLL.
    USRBUF *  - pointer to the frame.
    u_short   - transmit task completion code.

  RETURNS:
    nothing...

  NOTES:

*****************************************************************************/

void
encap_assemble_frame( pccb, pfrm, code )
CCB *pccb;
USRBUF *pfrm;
u_short code;
{
    /** Local variables
     **/
    BFN_PTR fp;
    USRBUF *pc, *pn;
    NPPARSE_HDR phdr;
    u_short idx = pccb->encap->index;
    u_short flags;
    LCB  *pLCB = pccb->plcb;

    /** Strip off the Q922 Address and set a pointer to the start
     ** of the frame.
     **/
    mREMOVE_FRAME_HDR(pfrm, Q922_ADDR_LEN);
    phdr = (NPPARSE_HDR) mSTARTOFRAME(pfrm);

    /** Is this the first frame of a fragmented user frame ?
     **/
    flags = mMAKEUSHORT( phdr->fragment.info[0], phdr->fragment.info[1] );
    if( ! (flags & FRAME_OFFSET) )
	{
	/** This function should never process the first frame fragment. We
	 ** somehow missed the last fragment of the previous frame.... Time
	 ** to resync...
	 **
	 ** Begin by freeing any accumulated buffers for the previous frame.
	 **/
	if( pccb->encap->ptxsframe )
	    encap_free_frame( pccb->encap->ptxsframe );

	pccb->encap->ptxsframe = BNULL;   /* initialize this ... */
	pccb->encap->ptxeframe = BNULL;   /* initialize this ... */

	/** Adjust the buffer pointer so that it starts at the Q.922 address
	 ** because we're going to call encap_frame_complete to process this
	 ** frame.
	 **/
	mMK_HDR_SPC( pfrm, Q922_ADDR_LEN );
	encap_frame_complete( pccb, pfrm, code );

	/** Since this frame was the first fragment of a user frame; the 
	 ** remaining frames will be processed by this function so we leave
	 ** the pccb->TxDone function pointer alone....
	 **/
	return;
	}

    /** strip remaining headers...
    **/
    mREMOVE_FRAME_HDR(pfrm, (SNAP_HDR_LEN+FRAG_HDR_LEN));

    /** Get the last buffer in this USRBUF chain just transmitted.
     **/
    pc = pfrm;
    mGET_LAST_USRBUF(pc, pn);

    /** Link the last chain of USRBUFs transmitted with this one.
     **/

#ifndef	STREAM_MESSAGES 
    pccb->encap->ptxeframe->link = pfrm;
#else
    pccb->encap->ptxeframe->b_cont = pfrm;
#endif

    pccb->encap->ptxeframe = pc;

#ifndef	STREAM_MESSAGES
    pccb->encap->ptxeframe->next = BNULL;
#endif

    /** If the frame was transmitted successfully then count the
     ** fragment as sent.
     **/
    if( code == FRS_SUCCESS )
	{
	/** Are statistics enabled ?
	 **/
	if( m_FRS_IS_STATS_ENBL(pLCB) )
	    {
	    pccb->encap->mp[idx]->frag_stats.SendFragments++;
	    pccb->encap->mp[idx]->frag_stats.SendFragmented++;
	    }
	}

    /** Store the transmitter's completion code. Stored only if
     ** the last one was FRS_SUCCESSFUL
     **/
    if( pccb->encap->code == FRS_SUCCESS )
	pccb->encap->code = code;

    /** Is this the last frame in the sequence ?
     **/
    if( flags & FINAL_FRAME_BIT )
	{
	/** Update statistics if enabled
	 **/
	if( m_FRS_IS_STATS_ENBL(pLCB) )
	    {
	    pccb->encap->mp[idx]->gen_stats.SendFrames++;
	    pccb->encap->mp[idx]->gen_stats.SendChars +=
		m_MMGR_FRAME_LEN( pccb->encap->ptxsframe );
	    }

	/** Tell the target system that the frame was processed. If there is
	 ** no callback function then we put the frame buffers back on the
	 ** heap ourselves.
	 **/
#ifndef	STREAM_MESSAGES
	fp = pccb->encap->mp[idx]->fcallback;
	if( fp == (BFN_PTR) 0 )
	    encap_free_frame( pccb->encap->ptxsframe );
	else

	    /** Call the target system
	     **/
	    fp( pccb, pccb->encap->ptxsframe, pccb->encap->code );
#else
	/* 
	 * If Streams are used then the message block
	 * was sent to the WAN driver which frees them 
	 */
#endif
	/** Reset this for the next time.
	 **/
	pccb->encap->ptxsframe = BNULL;
	pccb->encap->ptxeframe = BNULL;
	pccb->encap->code = FRS_SUCCESS;
	pccb->TxDoneFunc = encap_frame_complete;
	}/* end if */

    return;
}

/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_restore_frame

  DESCRIPTION:
    This function is an INTERNAL function responsible for restoring a user
    frame that may have been fragmented by the encap_makeframe function. This
    routine is called by ENCAP_SendData or ENCAP_SendDataWait if the LAPD 
    routine could not transmit the user's frame.

  PARAMETERS:
    USRBUF * - pointer to the user's (possibly) fragmented frame.
    u_short  - the API protocol id associated with the frame.

  RETURNS:
    nothing...

  NOTES:
    This routine fixes the bug where buffers associated with a fragmented frame
    were not being released correctly if the frame was handed to LAPD but it
    could not be transmitted because the link or pll was down.
*****************************************************************************/

void
encap_restore_frame( pfrm, apipid )
USRBUF *pfrm;
u_short apipid;
{

    /** Local variables
     **/
    USRBUF *pc, *pn, *start_of_frame, *next_frame;
    u_short header_len = 0;
    u_short done = 0;
    u_short hdr_len_1 = Q922_ADDR_LEN; /* begin with the length of the DLCI */
    u_short hdr_len_2 = Q922_ADDR_LEN; /* for frames 2-n (if fragmented) */
    
    /** What is the frame type - we get it by doing a APIPID lookup to
     ** get the frame type associated with the APIPID.
     **/
    switch( encap_apipid2frame((u_char) apipid) )
	{
	case IETF_SNAP_HEADER:
	    hdr_len_1 += SNAP_HDR_LEN;      /* Add the length of this header */
	    break;

	case ISO_NLPID_FRAME:
	    hdr_len_1 += ISO_NLPID_HDR_LEN; /* Add the length of this header */
	    break;

	case Q933_USER_SPEC:
	    hdr_len_1 += ISO_Q933_HDR_LEN;  /* Add the length of this header */
	    break;
	
	default:
	    /** This can only happen if the apipid is not in
	     ** the pid table...
	     **/
	    return;
	}/* end switch */

    /** Was the user frame fragmented ? - to find out we look at the 'next'
     ** member of the USRBUF structure. If it points to something then the
     ** user frame was fragmented.
     **/
#ifndef	STREAM_MESSAGES
    if( pfrm->next != BNULL )
#else
    if( pfrm->b_cont != BNULL )
#endif
	{
	/** More than one frame exists. So we increment the header
	 ** sizes for the first frame and any subsequent frames.
	 **/
	hdr_len_1 += (SNAP_HDR_LEN + FRAG_HDR_LEN); /* 1st frame only */
	hdr_len_2 += (SNAP_HDR_LEN + FRAG_HDR_LEN); /* 2-n frames */
	}

    /** Now we fixup the USRBUF pointers so they point only to the payload
     ** data; if the user frame was fragmented then we put it back together
     ** again...
     **/
    pc = pfrm;              /* 1st frame */
    header_len = hdr_len_1; /* length of the 1st (and possibly only) header. */

    while( !done )
	{
	/** Fix frame pointer to point to user payload data.
	 **/
	mREMOVE_FRAME_HDR(pc, header_len);

	/** Is this frame connected to another one.
	 **/
#ifndef	STREAM_MESSAGES
	if( pc->next != BNULL )
#else 
	if( pc->b_cont != BNULL )
#endif
	    {
	    start_of_frame = pc;      /* save this */
#ifndef	STREAM_MESSAGES
	    next_frame = pc->next;    /* and this */
#else
	    next_frame = pc->b_cont;    /* and this */
#endif
	    /** Locate the last USRBUF in the current frame...
	     **/
	    mGET_LAST_USRBUF(pc, pn);

	    /**  'pc' now points to the last buffer in the current frame.
	     ** reconnect this buffer with the 1st buffer of the next frame.
	     **/
#ifndef	STREAM_MESSAGES
	    pc->link = next_frame;        /* Reconnect chain */
	    start_of_frame->next = BNULL; /* break this connection */
#else
	    pc->b_cont = next_frame;        /* Reconnect chain */
#endif
	    /** Set 'pc' to point to the (formerly) next complete frame
	     **/
	    pc = next_frame;
	    start_of_frame = BNULL;
	    next_frame = BNULL;
	    
	    /** Change the length of the header....
	     **/
	    header_len = hdr_len_2; /* for frames 2-n */
	    }/* end if */
	else
	    done = 1;
	}/* end while */

    return;
}
#endif	/* Unused functions */

/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_get_frmtype

  DESCRIPTION:
    This function is an INTERNAL function responsible for examining the start
    of a frame relay header to determine the type of network layer protocol
    encapsulation.

  PARAMETERS:
    NPPARSE_HDR - a pointer to a parse header.

  RETURNS:
    the frame type or...
    UNKNOWN_NLPID .

  NOTES:

*****************************************************************************/

u_short
encap_get_frmtype( hdr )
NPPARSE_HDR hdr;
{
    u_short frame_type = UNKNOWN_NLPID;

    /** Return the frame type based on nlpid values...
     ** Try the nlpid value in the 1st position of the parse frame header.
     **/

	if(hdr->ctrl == 0x08)
		return(CISCO_ENCAP_FRAME) ;		

    switch( hdr->nlpid_1 )
	{
	case NLPID_IP:
	case NLPID_ETHR_OLD:
	case NLPID_ISO_CLNP:
	case NLPID_ISO_ESIS:
	case NLPID_ISO_ISIS:
	    frame_type = ISO_NLPID_FRAME;
	    break;

	case DATA_COMPRESSION_NLPID:
	    frame_type = ISO_FRF9_FRAME;
	    break;

	case NLPID_Q933:
	    frame_type = ISO_Q933_FRAME;
	    break;

	case NLPID_ZERO: 
	    /** Try the alternative nlpid position - it should be NLPID_SNAP.
	     **/
	    switch( hdr->nlpid_2 )
		{
		case NLPID_SNAP:
		    frame_type = IETF_SNAP_HEADER;
		    break;

		default:
		    break;
		}
	    break;

	default:
	    break;
	}/* end switch */

    return(frame_type);
}
/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_apipid2frame

  DESCRIPTION:
    This is an INTERNAL call to get that returns the frame type of the frame
    associated with an API PID value.

  PARAMETERS:
    u_char - API PID value.

  RETURNS:
    returns the frame type associated with the API PID.

  NOTES:

*****************************************************************************/

u_char
encap_apipid2frame( apipid )
u_char apipid;
{
    /** Local variables
     **/
    u_short i = 0;
    u_char frametype = END_PID_TABLE;

    /** Search through the PID table looking for the protocol/frame type
     ** pair..
     **/
    while((pid_table[i].pid != END_PID_TABLE) && (frametype == END_PID_TABLE))
	{
	if( pid_table[i].apipid == apipid )
	    frametype = pid_table[i].frametype;
	i++;
	}

    return( frametype );
}

/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_api_pid2pid

  DESCRIPTION:
    This is an INTERNAL call to get the NLPID and PID values for a given
    API protocol id.

  PARAMETERS:
    u_char - an API PID value.

  RETURNS:
    returns the pid use in the encapsulation frame. 

  NOTES:

*****************************************************************************/
u_short
encap_api_pid2pid( apipid )
u_char  apipid;
{

    /** Local Variables
     **/
    u_short pid = PID_UNKNOWN;
    u_short i = 0;

    /** Search for the pid if we haven't reached the end of the table
     ** and the pid is still unknown....
     **/
    while(( pid_table[i].apipid != END_PID_TABLE ) && ( pid == PID_UNKNOWN ))
	{
	if( pid_table[i].apipid == (u_char) apipid )
	    pid = pid_table[i].pid;
	i++;
	}

    return( pid );
}

/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_pid2pid_api

  DESCRIPTION:
    This is an INTERNAL call to get the API PID for a given PID. This is
    the opposite call from encap_api_pid2pid.

  PARAMETERS:
    u_char - one of the pid values used in the protocol header.
    u_char - the frame type associated with the PID.
    
  RETURNS:
   returns the user API pid value from the PID table.

  NOTES:

*****************************************************************************/
u_char
encap_pid2pid_api( encap_protocol, frame_type )
u_short encap_protocol;
u_char frame_type;
{
    /** Local variables
     **/
    u_char apipid = PID_UNKNOWN;
    u_short i = 0;

    /** Search through the PID table looking for the protocol/frame type
     ** pair..
     **/
    while((pid_table[i].pid != END_PID_TABLE) && apipid == PID_UNKNOWN)
	{
	if( ( pid_table[i].pid       == encap_protocol &&
	      pid_table[i].frametype == frame_type) )
	    apipid = pid_table[i].apipid;
	i++;
	}

    return( apipid );
}


/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_frame_error

  DESCRIPTION:
    This is an INTERNAL call to log a frame error and if necessary,
    update statistics. If the ffreeframe is 1 then all usrbufs 'linked'
    in the frame are returned to the heap....

  PARAMETERS:
     pccb      - pointer to the channel control block.
    USRBUF *   - pointer to the frame. 
    u_short    - error code causing the problem
    u_short    - FREE if frame is to be given back to the heap.

  RETURNS:
    returns errcode

  NOTES:

*****************************************************************************/

u_short
encap_frame_error( pccb, pframe, errcode, ffreeframe )
CCB *pccb;
USRBUF *pframe;
u_short errcode;
u_short ffreeframe;
{

    switch( errcode )
	{
	/** These are captured diagnostics for MIB use....
	 **/
	case ENCAP_UNK_PROT_RX:
	    /* Frame with unknown protocol */
	    pccb->encap->prot_errs.RcvUnkProt++;
	    break;

	case ENCAP_UNK_PROT_TX:
	    /* Frame with unknown protocol */
	    pccb->encap->prot_errs.TxUnkProt++;
	    break;

	case ENCAP_UNK_FRAME_RX:
	    /* Received an unknown frame type */
	    pccb->encap->prot_errs.RcvUnkFrame++;
	    break;

	case ENCAP_UNK_ORG_RX:
	    /* Unknown organization in frame */
	    pccb->encap->prot_errs.RcvUnkOrg++;
	    break;

	case ENCAP_LOST_FRAME_RX:
	    /* A frame is missing */
	    pccb->encap->prot_errs.RcvLostFrame++;
	    break;

	/** These are more than likely programmer errors....
	 **/
	case ENCAP_NULL_FRAME_TX:
	    /* Tried to transmit a NULL frame */

	case ENCAP_NULL_FRAME_RX:
	    /* Received a NULL frame */

	case FRS_INVALID:
	    /* PLL handle is invalid */

	case ENCAP_INVALID:
	    /* PLL not opened for encapsulation */
	    break;

	/** These problems represent configuration errors
	 ** that can be corrected by changing the size of a frame
	 ** relay frame or the size of a USERBUF
	 **/
	case ENCAP_INV_DATA_SIZE:
	    /* Payload and header < 32 octets */

	case ENCAP_BUFSIZE_INVALID:
	    /* Misconfigured buffer size. */
	    break;

	default:
	    break;
	}

    /** Free the frame...
     **/
    if( ffreeframe == FREE )
	encap_free_frame( pframe );

    /** Returns what we received
     **/
    return( errcode );
}

/**/
/****************************************************************************

  (INTERNAL) PROCEDURE NAME:   encap_free_frame

  DESCRIPTION:
    This is an INTERNAL call to return all usrbufs 'linked' in a frame
    back to the heap.

  PARAMETERS:
    USRBUF -- pointer to the frame.

  RETURNS:
    nothing....

  NOTES:

*****************************************************************************/
void
encap_free_frame( pframe )
USRBUF *pframe;
{
    m_MMGR_FREE_BUFR( pframe ); /* free this usrbuf */
    return;
}

