#ident "$Id: frslmi.c,v 1.6 1997/10/21 20:48:10 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:

		 @@@@@@  @@@@@    @@@@   @       @    @     @             @@@@
		 @       @    @  @       @       @@  @@     @            @    @
		 @@@@@   @    @   @@@@   @       @ @@ @     @            @
		 @       @@@@@        @  @       @    @     @     @@@    @
		 @       @   @   @    @  @       @    @     @     @@@    @    @
		 @       @    @   @@@@   @@@@@@  @    @     @     @@@     @@@@

	ORIGINAL AUTHOR:  Deepak Shahane
	SUBJECT MATTER
	 SPECIALIST:   Deepak Shahane


	REVISION HISTORY:

	Date     Version  Name          Description
	-------- -------  ------------  --------------------------------------------
	01-16-92 1.10A    H&J (DS)      Original
	03-22-92 1.20A    H&J (DS)      Added T1.617 ANNEX-D Support
								Stricter LMI protocol syntax verification
								IETF Draft MIB compliance
	06-30-92 2.00A    H&J (DS)      Added support for DCE interface
	11-20-92 2.00E    H&J (DJ)      Add missing stats update to FRSLmiRcv().
	11-20-92 2.00E    H&J (DJ)      Change len argument in FRSLmiRcvStat()
								from u_short to short and change for-loop
								test to "len > 0", to catch invalid
								status format. 
	11-20-92 2.00E    H&J (DJ)      Remove unreferenced local variable pCCB
								from FRSLmiRcvStatEnq().
	11-20-92 2.00E    H&J (DJ)      Moved declaration of local variable stat
								to beginning of FRSLmiSendStat() for
								portability.
	11-20-92 2.00E    H&J (DJ)      Fix increment of lmi_stat_enq_rate_plus in
								FRSLmiPollXcessChk().
	02-24-93 2.00F    H&J (DS)      Fixes to conform to US-Sprint Certification
		 Other    H&J           See H&J's bug reports

	$Log: frslmi.c,v $
	Revision 1.6  1997/10/21 20:48:10  gary
	Modifications made to accomodate the SOH (Streams-on-host) folks.
	
	Revision 1.5  1996/10/22 21:18:56  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.4  1996/04/26 19:57:42  gary
	First rev of changes: Frame Relay on PRI card.
	Bug fix: sequence number on segmented frames was wrong.

	Revision 1.3  1995/04/10 16:32:04  garyp
	remove strm.h

 * Revision 1.13  1994/07/27  20:27:41  stavrosm
 * Release 2.2
 *
 * Revision 1.12  1994/06/16  17:46:05  akullber
 * Fixed call to FRSGetFrameDLCI() in FRSLmiMarkFrames() to pass a ptr.
 * to the beginning of the frame relay header.
 *
 * Revision 1.11  1994/03/16  21:47:54  deepak
 * Changed usage of "pCCB->plcb->my_cfg" reflecting change in LCB struct.
 *
 * Revision 1.10  1994/02/25  20:14:24  guest
 * Refer to bug reports SA-101, SA-102.
 *
 * Revision 1.9  1993/11/10  16:44:27  stavrosm
 * Release 2.1
 *
 * Revision 1.8  93/11/10  16:25:22  stavrosm
 * Celanup to make lint happy
 * 
 * Revision 1.7  93/11/04  16:10:25  root
 * 
..............................................................................

	DESCRIPTION:

	This module contains code that implements various dialects of Local Mgmt
	Interface (LMI) protocols (including T1.617 Annex-D) within the H&J Frame 
	Relay Stack.

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

	LMI 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) ANSI T1S1/90-213R1 (T1.617) - DSS1 - Signalling Specification for Frame
	 Relay Bearer Service, ANSI, July 1990.
	(d) Frame Relay Stack Functional Specification Rel 1.2 Harris & Jeffries Inc.
	(e) MIB for Frame Relay DTEs - RFC Draft from Internet Engr Task Force (IETF).

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

	SPECIAL COMPILE/LINK INSTRUCTIONS:

		 see notes/instructions in frs.h

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

	FUNCTIONS INCLUDED:

	FRSLmiRcv            --- Main receive processor for LMI,Annex-D frames
	FRSLmiRcvStat        --- Status Message handler (DTE)
	FRSLmiRcvUpdate      --- Update message handler (DTE)
	FRSLmiSendStatEnq    --- Xmit Status Enquiry Poll (DTE)
	FRSLmiRcvStatEnq     --- Rcvd Status Enquiry Poll (DTE or DCE)
	FRSLmiSendStat       --- Xmit Status Enquiry Poll (DTE or DCE)
	FRSLmiPerfThresh     --- Perform Thresholding over a given error statistic
	FRSLmiHeartbeatPoll  --- LMI Heartbeat Poll (DTE)
	FRSLmiPollVer        --- LMI poll verification timeout (DCE)
	FRSLmiPollXcessChk   --- LMI excess polling chk timeout (DCE)
	FRSLmiCleanupCCBs    --- Utility to mark down all unavailable PVCs
	FRSLmiLinkDown       --- Utility to declare a frame relay link down
	FRSLmiLinkUp         --- Utility to declare a frame relay link up
	FRSLmiMarkCCB        --- Utility to mark a frame relay PVC up/down
	FRSLmiThrottleCCB    --- Utility to flow-ctrl a frame relay channel
	FRSGetNextIe         --- Finds the address of the next information element    
	
	Formal Prototypes for all the above functions exist in <frs.h>.
	Strict prototyping is provided.

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


 /****************************************************************************
 * 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: frslmi.c,v 1.6 1997/10/21 20:48:10 gary Exp $I
 ****************************************************************************/


/*
 * Includes
 */

#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 "ll_proto.h"
#include "ll_control.h"
#include "timer.h"
#include "x25trace.h"
#include "fr_cont.h"
#include "fr.h"
#include "frs.h"
#include "lapb.h"

/*
 * Local functions
 */
#if USE_PROTOTYPES
		IE_UN *FRSGetNextIe( USRBUF *, IE_UN **, IE_UN *);
#else
		IE_UN *FRSGetNextIe();
#endif

/**/
/****************************************************************************
*
*                  RCV PROCESSOR FOR LMI MESSAGES
*
****************************************************************************/

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

	PROCEDURE NAME:   FRSLmiRcv

	DESCRIPTION:

	This procedure processes an LMI frame received from the network. Currently,
	four flavors of LMI are supported:

		(a) a Plain-Jane type used by US Sprint et al.
		(b) a Stratacom special used by Stratacom IPX/SPX switches.
		(c) an ANNEX-D type compliant with T1.617- AnnexD.
		(d) an ANNEX-A type compliant with Q.933- AnnexA.

	PARAMETERS:
		LMICCB         * --- Ptr to the Chan Ctrl Blk (CCB) used for LMI traffic
		USRBUF *         --- Ptr to the rcvd LMI frame.
		u_short          --- length of the lmi frame including header and data.

	RETURNS:

		FRS_SUCCESS      --- No error in frame syntax.
		Else API code    --- Invalid syntax.

	NOTES:

		This function makes the following assumptions:

		(1) The entire LMI frame is contained in the buffer pointed to by pRcv.
		(2) The caller is responsible for freeing the buffer.

	If this function returns !FRS_SUCCESS, then the called function must update
	appropriate statistics.
*****************************************************************************/

u_short
FRSLmiRcv(pLmiCCB, pRcv)
LMICCB *pLmiCCB;
USRBUF *pRcv;
{
		FR_HDR_LMI *lmi_frm;
		LCB *pLCB = pLmiCCB->common_ccb.plcb;
		u_short nlen;

		/* Update statistics...if necessary
	*/
		if(m_FRS_IS_STATS_ENBL(pLCB))
		 FRSUpdateStats((CCB *) pLmiCCB, pRcv, FRS_DIRECTION_INBOUND);

		/* Data length...
	*/
		nlen = (u_short)m_MMGR_FRAME_LEN(pRcv);

		/* Develop lmi frame header pointer...
	*/
		lmi_frm = m_MMGR_DATA(pRcv, FR_HDR_LMI *);

		/* Verify if DE, FECN, BECN are set correctly...
	*/
		if((lmi_frm->lmi_header.dlciLSB &
		(FECN_MASK | BECN_MASK | DE_MASK )) != 0)
		 return FRS_LMI_INV_FRM_HDR;

		/* Verify if C/R is set correctly...
	*/
		if((lmi_frm->lmi_header.dlciMSB & CR_MASK) != 0)
		 return FRS_LMI_INV_FRM_HDR;

		/* Verify all fixed fields including MBZ fields...
	*/
		if(lmi_frm->frame_type != FRS_UNNUM_INFO_FRAME_TYPE)
		 return FRS_LMI_INV_FRM_TYPE;

		/* Verify appropriate protocol discriminator for DTEs...
	*/
		if(!(m_FRS_IS_LINK_DCE( pLCB)))
		{
		 if(m_FRS_IS_ANNEXD_CFG(pLCB))
		 {
			if(lmi_frm->prot_disc != FRS_LMI_ANNEXD_PROT_DISC)
			 return FRS_LMI_INV_FRM_PROT_DISC;
		 }
		 else if(m_FRS_IS_ANNEXA_CFG(pLCB))
		 {
			if(lmi_frm->prot_disc != FRS_LMI_ANNEXA_PROT_DISC)
			 return FRS_LMI_INV_FRM_PROT_DISC;
		 }
		 else
			if(lmi_frm->prot_disc != FRS_LMI_PROT_DISCRIMINATOR)
			return FRS_LMI_INV_FRM_PROT_DISC;
		}
		else
		{
		 /* For DCE, configure our LMI type...
			*   Based on the protocol discriminator, we can 
			*   decide only if it's {Annex D / Annex A} or not.
			*   Update link configuration information.
			*/
		 switch( lmi_frm->prot_disc)
		 {
		 case FRS_LMI_ANNEXD_PROT_DISC: /* This is also ANNEXA_PROT_DISC */
			m_Rst(pLCB->lcb_opt,
			 (FRS_OPT_PJ_LMI_CFG | FRS_OPT_S_LMI_CFG | FRS_OPT_ANNEXA_CFG));
			m_Set(pLCB->lcb_opt, FRS_OPT_ANNEXD_CFG);  /* Assume (for now) Annex D */
			break;

		 case FRS_LMI_PROT_DISCRIMINATOR:
			m_Rst(pLCB->lcb_opt,
			 (FRS_OPT_PJ_LMI_CFG | FRS_OPT_ANNEXD_CFG | FRS_OPT_ANNEXA_CFG));
			m_Set(pLCB->lcb_opt, FRS_OPT_S_LMI_CFG);

			/* Create & Start excess poll check timer...
			 */
			if (pLCB->LmiXcessChkTimerId == 0) 
			pLCB->LmiXcessChkTimerId = FRSTMR_create( pLCB->hLCB, 
									FRSLmiPollXcessChk,
									CHKTIMR );
			FRSTMR_start(pLCB->LmiXcessChkTimerId,
			 (u_short) (pLmiCCB->lmi_xcess_poll_interval* (u_short) 1000));
			break;

		 default:
			return FRS_LMI_INV_FRM_PROT_DISC;
		 }

		 pLCB->my_cfg->frs_link_flags = pLCB->lcb_opt;
		}

		if(lmi_frm->call_ref != 0)
		 return FRS_LMI_INV_FRM_CALL_REF;

		/* Switch to appropriate sub-processor...
	*/
		switch( lmi_frm->msg_type)
		{
		case FRS_LMI_STATUS_MSG_TYPE:
		 FRSLmiRcvStat( pLmiCCB, pRcv, (u_short) (nlen - sizeof(FR_HDR_LMI)));
		 break;

		case FRS_LMI_STATENQ_MSG_TYPE:
		 if(m_FRS_IS_LINK_DCE(pLCB))
			FRSLmiRcvStatEnq( pLmiCCB, pRcv, (u_short) (nlen - sizeof(FR_HDR_LMI)));
		 else
		 {
			pLmiCCB->lmi_inv_msg_rcvd++;
			FRSLogErr( pLmiCCB->common_ccb.plcb, pRcv, FRS_LNK_UNKRPT);
		 }
		 break;

		case FRS_LMI_UPDATE_MSG_TYPE:
		 /* This message type is valid only for Stratacom IPX switches...
			*/
		 if(m_FRS_IS_S_LMI_CFG( pLmiCCB->common_ccb.plcb))
		 {
			FRSLmiRcvUpdate( pLmiCCB, pRcv, (u_short) (nlen - sizeof(FR_HDR_LMI)));
			break;
		 }

		 /* Fall thru' */

		default:
		 /* Invalid message(report) type rcvd...
			* Update statistic.
			*/
		 pLmiCCB->lmi_inv_msg_rcvd++;
		 FRSLogErr( pLmiCCB->common_ccb.plcb, pRcv, FRS_LNK_UNKRPT);
		 return FRS_LMI_PROTOERR;
		}

		return FRS_SUCCESS;
}

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

	PROCEDURE NAME:   FRSLmiRcvStat

	DESCRIPTION:

	This procedure processes a STATUS message rcvd from the network.

	PARAMETERS:
		LMICCB         * --- Ptr to the Chan Ctrl Blk (CCB) used for LMI traffic
		USRBUF *         --- Buffer containing the rcvd LMI frame.
		u_short          --- data length of the lmi frame.

	RETURNS:          nothing

	NOTES:

*****************************************************************************/
void
FRSLmiRcvStat(pLmiCCB, pRcv, len)
LMICCB *pLmiCCB;
USRBUF *pRcv ;
short len;
{
		FR_HDR_LMI *lmi_frm =  m_MMGR_DATA(pRcv, FR_HDR_LMI *) ;
		IE_UN *pIe = (IE_UN *)((char *)lmi_frm + sizeof(FR_HDR_LMI));
		u_char SeqNumXchgFlag = FRS_LMI_SEQ_NUM_XCHG;
		u_char NoIeRepTypeFlag = TRUE;
		u_char NoIeKeepAliveFlag = TRUE;
		CCB *pCCB = CCBNULL;
		LCB *pLCB = pLmiCCB->common_ccb.plcb;
		u_short pvc_num, prev_pvc_num = 0;
		IE_UN contigIe, *pContigIe;    
		u_char dlciMSB, dlciLSB, pvc_status, net_seq_num;

		/* Update statistics...
	*/
		pLmiCCB->lmi_total_status++;

		/* Clear pending flag...
	*/
#ifdef	NONCONFORMANCE
		m_LMI_RESET_STAT_PEND( pLmiCCB);
#endif
		/* Bump the pIe pointer by a byte to account for LOCK_SHIFT in AnnexD...
	* Also, drop the count by a byte.
	*/
		if(m_FRS_IS_ANNEXD_CFG( pLCB))
		{
		 if(*((u_char *)pIe) != FRS_LMI_LOCK_SHIFT)
		 {
			pLmiCCB->full_status_int_count = 0;
			pLmiCCB->lmi_proto_err++;
			FRSLogErr( pLCB, pRcv, FRS_LNK_LMIPROTOERR);
			return;
		 }

		 pIe = (IE_UN *)((u_char *)pIe + 1);
		 len--;
		}

		/* Check for a NULL LMI frame...
	*/
		if(len == 0)
		{
		 pLmiCCB->full_status_int_count = 0;
		 return;
		}

		pLCB->DLCIEntryCount = 0 ;


		for (; len > 0;)
		{
	if (!(pContigIe = FRSGetNextIe( pRcv, &pIe, &contigIe)))
	{
			pLmiCCB->full_status_int_count = 0;
			pLmiCCB->lmi_proto_err++;
			FRSLogErr( pLCB, pRcv, FRS_LNK_LMIPROTOERR);
			return;
	}

	if (  (  (!m_FRS_IS_ANNEXA_CFG(pLCB))
			&&(pContigIe->ie_id != FRS_LMI_IE_REP_TYPE) 
			&&(pContigIe->ie_id != FRS_LMI_IE_KEEP_ALIVE)
			&&(pContigIe->ie_id != FRS_LMI_IE_PVC_STAT)
			)
			||(  (m_FRS_IS_ANNEXA_CFG(pLCB))
			&&(pContigIe->ie_id != FRS_LMI_ANNEXA_IE_REP_TYPE) 
			&&(pContigIe->ie_id != FRS_LMI_ANNEXA_IE_KEEP_ALIVE)
			&&(pContigIe->ie_id != FRS_LMI_ANNEXA_IE_PVC_STAT)
			)
		 )
	{
		/* Unsupported message type. Bail out. */
		pLmiCCB->lmi_unsupp_ie_msg++;
#ifdef NONCONFORMANCE
		pLmiCCB->full_status_int_count = 0;
#endif
		FRSLogErr( pLCB, pRcv, FRS_LNK_UNKNOWNIE);
#ifdef NONCONFORMANCE
		return;
#else
		 /* Decrement the length of the Information Element */

		len -= FRS_LMI_COMMON_HDR_LEN + pContigIe->ie_len ;
		continue ;
#endif
	}

	switch (pContigIe->ie_id)
		 {
		 case FRS_LMI_IE_REP_TYPE:
		 case FRS_LMI_ANNEXA_IE_REP_TYPE:

			if(pContigIe->ie_len != FRS_LMI_IE_REP_TYPE_LEN)
			{
			 pLmiCCB->full_status_int_count = 0;
			 pLmiCCB->lmi_proto_err++;
			 FRSLogErr( pLCB, pRcv, FRS_LNK_LMIPROTOERR);
			 return;
			}

			NoIeRepTypeFlag = FALSE;

			/* Set flag indicating a Sequence_Number_Xchange_Only or Full_Status
			 * report...
			 */
#ifdef	NONCONFORMANCE
			switch (SeqNumXchgFlag = pContigIe->ie_type.ie_rep_type.rep_type)
#else
			switch (pContigIe->ie_type.ie_rep_type.rep_type)
#endif
			{
			case FRS_LMI_FULL_STAT_RPT:
			 /* Update Statistics...
				*/
#ifndef	NONCONFORMANCE
			/* PS2_02I */
			 if(!m_LMI_IS_STAT_PEND(pLmiCCB))
			 {
				pLmiCCB->lmi_proto_err++;
			 	pLmiCCB->full_status_int_count = 0;
				FRSLogErr( pLCB, pRcv, FRS_LNK_LMIPROTOERR);
				return;
			 }
#endif

#ifndef	NONCONFORMANCE
			SeqNumXchgFlag = pContigIe->ie_type.ie_rep_type.rep_type;
#endif
			 pLmiCCB->common_ccb.my_rev_count++;
			 pLmiCCB->lmi_full_status++;
#ifndef	NONCONFORMANCE
			 m_LMI_RESET_STAT_PEND( pLmiCCB);
#endif
			 m_Rst(pLmiCCB->lmi_flag, (LMI_EXPECT_F_STATUS));
			 break;

			case FRS_LMI_SEQ_NUM_XCHG:
			 /* If we were expecting FULL STATUS, ignore this one...
				* Drop the packet completely.
				*/
#ifdef	NONCONFORMANCE			
 
			 if(m_TST(pLmiCCB->lmi_flag, (LMI_EXPECT_F_STATUS)))
			 {
				pLmiCCB->full_status_int_count = 0;
				pLmiCCB->lmi_proto_err++;
				FRSLogErr( pLCB, pRcv, FRS_LNK_LMIPROTOERR);
				return;
			 }
			 break;
#else
			/* PS2_24N Conformance Test */
			 if((m_TST(pLmiCCB->lmi_flag, (LMI_EXPECT_F_STATUS))) ||
				!m_LMI_IS_STAT_PEND(pLmiCCB))
			 {
				pLmiCCB->full_status_int_count = 0;
				pLmiCCB->lmi_proto_err++;
				FRSLogErr( pLCB, pRcv, FRS_LNK_LMIPROTOERR);
				return;
			 }

			 m_LMI_RESET_STAT_PEND( pLmiCCB);
			 break;

#endif
			case FRS_LMI_ASYNC_PVC_STAT:
			 /* Allowed for only AnnexD or AnnexA compliant LMI...
				*/
			 if(  (m_FRS_IS_ANNEXD_CFG( pLCB))
					||(m_FRS_IS_ANNEXA_CFG( pLCB))
					)
			 {
				pLmiCCB->lmi_total_updates++;
#ifndef	NONCONFORMANCE
				m_LMI_RESET_STAT_PEND( pLmiCCB);
#endif
				break;
			 }
			 /* Fall thru' */

			default:
			 /* Unknown Report type sent...
				*/
#ifdef	NONCONFORMANCE
			 pLmiCCB->full_status_int_count = 0;
#endif
			 pLmiCCB->lmi_proto_err++;
			 FRSLogErr(pLCB, pRcv, FRS_LNK_UNKRPT);
#ifdef	NONCONFORMANCE
			 return;
#else
			 break ;
#endif
			}
			break;

		 case FRS_LMI_IE_KEEP_ALIVE:
		 case FRS_LMI_ANNEXA_IE_KEEP_ALIVE:

#ifdef NONCONFORMANCE
			NoIeKeepAliveFlag = FALSE;
#endif
			/* Verify the following:
			 * (1) IE length
			 * (2) non-zero sequence number from switch
			 * (3) non-zero sequence number (our's) seen by the switch
			 * (4) IE REP TYPE has already been processed
			
			 * Also, verify:
			 * If this is *NOT* the first STATUS_MSG from the switch,
			 * ensure received network seq num is exactly one more than
			 * the previous value accounting for rollovers(!)...
			 */
#ifdef	NONCONFORMANCE

			NoIeKeepAliveFlag = FALSE;

			if((pContigIe->ie_len != FRS_LMI_IE_KEEP_ALIVE_LEN) ||
		((net_seq_num = pContigIe->ie_type.ie_keep_alive.current_seq) == 0) ||
		(pContigIe->ie_type.ie_keep_alive.last_rcvd_seq == 0) ||
			 (NoIeRepTypeFlag == TRUE))
			{
			 pLmiCCB->lmi_proto_err++;
			 pLmiCCB->full_status_int_count = 0;
			 FRSLogErr( pLCB, pRcv, FRS_LNK_LMIPROTOERR);
			 return;
			}

#else

			if(pContigIe->ie_len != FRS_LMI_IE_KEEP_ALIVE_LEN)
			{
			 pLmiCCB->lmi_proto_err++;
			 FRSLogErr( pLCB, pRcv, FRS_LNK_LMIPROTOERR);
			 break ; 
			}

 			NoIeKeepAliveFlag = FALSE;

		if(((net_seq_num = pContigIe->ie_type.ie_keep_alive.current_seq) == 0) ||
		(pContigIe->ie_type.ie_keep_alive.last_rcvd_seq == 0) ||
			 (NoIeRepTypeFlag == TRUE))
			{
			 pLmiCCB->lmi_proto_err++;
			 pLmiCCB->full_status_int_count = 0;
			 FRSLogErr( pLCB, pRcv, FRS_LNK_LMIPROTOERR);
			 return;
			}
#endif
			/* Validate Last Rcvd Seq with local_seq_num and update latter...
			 */
			if(pLmiCCB->local_seq_num != 
			pContigIe->ie_type.ie_keep_alive.last_rcvd_seq)

			 /* Ooh! We have things out of whack.
				* Inc counter and verify thresholding.
				*/
			{
			 pLmiCCB->lmi_lost_seq_num_sync_event++;

			 /* Perform thresholding... If threshold crossed, 
				* declare link down.
				*/
			 if((FRSLmiPerfThresh( pLmiCCB,
				FRS_EVT_NOSYNC_KEEP_ALIVE,
				FRS_EVT_ALARM)) == TRUE)
			 {
				pLmiCCB->local_seq_num = 0;
				FRSLmiLinkDown( pLmiCCB->common_ccb.plcb);
			 }

			 /* Request full statuses...
				*/
			 pLmiCCB->full_status_int_count = 0;
			 FRSLogErr( pLCB, pRcv, FRS_LNK_SEQNUMERR);
			 return;
			}
			else
			{
			 /* Perform thresholding...
				* Declare link up if this is the first status response.
				*/
			 if((pLmiCCB->net_seq_num == 0) ||
				(FRSLmiPerfThresh( pLmiCCB,
				FRS_EVT_NOSYNC_KEEP_ALIVE,
				FRS_EVT_CLEAR)) == TRUE)
			 {
				FRSLmiLinkUp( pLmiCCB->common_ccb.plcb);
			 }
			}

			break;

		 case FRS_LMI_IE_PVC_STAT:
		 case FRS_LMI_ANNEXA_IE_PVC_STAT:

			if(SeqNumXchgFlag == FRS_LMI_FULL_STAT_RPT)
			{
		dlciMSB = pContigIe->ie_type.ie_pvc_status.dlciMSB;
		dlciLSB = pContigIe->ie_type.ie_pvc_status.dlciLSB;
		pvc_status = pContigIe->ie_type.ie_pvc_status.pvc_status;

			 /* Figure out PVC number depending upon LMI type...
				*/
			 if(  (m_FRS_IS_ANNEXD_CFG( pLCB))
					||(m_FRS_IS_ANNEXA_CFG( pLCB))
					)
			 {
				/* For AnnexD/Annex A compliance...
				 */
				pvc_num = (dlciMSB << 4) | ((dlciLSB & 0x78) >> 3);

				/* Verify:
				(1) IE length
				(2) Precedence of Rep Type IE
				(3) Precedence of Keep Alive IE
				(4) EXT & SPARE bits in Octet 1 (MSB)
				(5) EXT & SPARE bits in Octet 2 (LSB)
				(6) EXT & SPARE bits in Octet 3 (STATUS)
				 */
				if((pContigIe->ie_len != FRS_LMI_IE_PVCS_LEN) ||
						(NoIeRepTypeFlag == TRUE) ||
						(NoIeKeepAliveFlag == TRUE) ||
						((dlciMSB & (FRS_ANNEXD_PVCS_EX_MASK | 
							 FRS_ANNEXD_PVCS_OC1_SPARE)) != 0) ||

						((dlciLSB & (FRS_ANNEXD_PVCS_EX_MASK |
							 FRS_ANNEXD_PVCS_OC2_SPARE)) != 
							 FRS_ANNEXD_PVCS_EX_MASK) ||

						((pvc_status & (FRS_ANNEXD_PVCS_EX_MASK |
							 FRS_ANNEXD_PVCS_OC3_SPARE)) != 
							 FRS_ANNEXD_PVCS_EX_MASK))
				{
						pLmiCCB->lmi_proto_err++;
#ifdef NONCONFORMANCE
						pLmiCCB->full_status_int_count = 0;
#endif
						FRSLogErr( pLCB, pRcv, FRS_LNK_LMIPROTOERR);
#ifdef NONCONFORMANCE
						return;
#else
						break ;
#endif
				}
			 }
			 else
			 {
				/* Stratacom or PJ LMI...
				 */
				pvc_num = (dlciMSB << 8) | dlciLSB;
			 }

			 /* Verify if pvc_num is legit.
				*/
#ifdef NONCONFORMANCE
			 if((pvc_num < FRS_MIN_VALID_DLCI) ||
					(pvc_num > FRS_MAX_VALID_DLCI) ||
					(pvc_num <= prev_pvc_num))
			 {
				pLmiCCB->lmi_unexpected_pvc_num++;
				pLmiCCB->full_status_int_count = 0;
				FRSLogErr( pLCB, pRcv, FRS_LNK_INVDLCI);
				return;            /* Ignore this status message */
			 }
#else
#if 0
			 if(pvc_num < FRS_MIN_VALID_DLCI)
			 {
				pLmiCCB->lmi_unexpected_pvc_num++;
				FRSLogErr( pLCB, pRcv, FRS_LNK_INVDLCI);

				len -= FRS_LMI_COMMON_HDR_LEN + pContigIe->ie_len;
				continue ;            /* Ignore this Information Element */
			 }
#endif
			 if((pvc_num < FRS_MIN_VALID_DLCI) ||
					(pvc_num > FRS_MAX_VALID_DLCI) ||
					(pvc_num < prev_pvc_num))
			 {
				pLmiCCB->lmi_unexpected_pvc_num++;
				pLmiCCB->full_status_int_count = 0;
				FRSLogErr( pLCB, pRcv, FRS_LNK_INVDLCI);
				return ;            /* Ignore this Information Element */
			 }

#endif

			 prev_pvc_num = pvc_num;

		pCCB = FRSLocateCCBbyDLCI( pLCB, pvc_num) ;

/*StartUpDebug*/
	printf("LMI sTAT pvc_num = %d, %x %x",pvc_num ,pCCB,pContigIe->ie_type.ie_pvc_status.pvc_status ) ;

		/* If CCB not found, create one, maybe. */

		if (pCCB == CCBNULL)
			sys_add_pll(pLCB, &pCCB, pvc_num);

		switch(pContigIe->ie_type.ie_pvc_status.pvc_status &
					 FRS_LMI_NBIT_MASK)
			 {
			 case FRS_LMI_STATUS_EXIST:
				/* PVC Exists */

			 case FRS_LMI_STATUS_ADDED:
			 /* N bit Set */
				break;
			 }

		if( !(pContigIe->ie_type.ie_pvc_status.pvc_status &
					FRS_LMI_DBIT_MASK) )
			 {
				/* D-bit is set to 0, indicating deleted PVC.  Mark down 
				 * the CCB associated with pvc_num and inform user
				 * process about it...
				 */
				if(pCCB != CCBNULL)
				{
						FRSLmiMarkCCB( pCCB, FALSE);
				}
			 }

			 /* Mark the CCB active_or_not & congested_or_not
				*/
			 if((pCCB != CCBNULL) && (pLCB->State == FRS_LKUP))
			 {
				FRSLmiMarkCCB(pCCB,(u_char)
						 ((pContigIe->ie_type.ie_pvc_status.pvc_status &
								 FRS_LMI_DBIT_MASK) != 0));

				/* For Stratacom IPX switches, verify network buffer
				 * congestion status...  */
				if(m_FRS_IS_S_LMI_CFG( pLCB))
				{
						FRSLmiThrottleCCB( pCCB,
					(u_char)(pContigIe->ie_type.ie_pvc_status.pvc_status
						 & FRS_LMI_STATUS_BUFR));

						/* Update bandwidth for PVC (for STRATACOM IPX switches)
					* if configured to do so...
					*/
						if(m_FRS_IS_LMI_BW_CTRL( pLCB))
						 pCCB->my_cosb.bc_cur =
				((pContigIe->ie_type.ie_pvc_status.pvc_bw[0] << 16) +
				(pContigIe->ie_type.ie_pvc_status.pvc_bw[1] << 8) +
				pContigIe->ie_type.ie_pvc_status.pvc_bw[2]) / 8;
				}

				pCCB->my_rev_count = pLmiCCB->common_ccb.my_rev_count;
			 }
			}
			else
			{
			 /* Received a status ie in a seq_num_xchg_only lmi message...
				*/
			 pLmiCCB->lmi_unexpected_pvc_stat_msg++;
			 pLmiCCB->full_status_int_count = 0;
			 FRSLogErr( pLCB, pRcv, FRS_LNK_LMIPROTOERR);
			 return;
			}

			break;
		 }

		if((pLCB->DLCIEntryCount + 1) < FRS_MAX_CHANNELS)
		{
			bcopy((char *)pContigIe,
				  (char *)&pLCB->DLCIEntrys[pLCB->DLCIEntryCount],
				  sizeof(IE_UN)) ;

			pLCB->DLCIEntryCount++ ;
		}
		 /* Decrement the length of the packet */
	len -= FRS_LMI_COMMON_HDR_LEN + pContigIe->ie_len;
		}

		/* Finally, verify if indeed we rcvd IE_REP_TYPE and IE_KEEP_ALIVE...
	*/
		if((NoIeRepTypeFlag == TRUE) || (NoIeKeepAliveFlag == TRUE))
		{
		 pLmiCCB->full_status_int_count = 0;
		 FRSLogErr( pLCB, pRcv, FRS_LNK_LMIPROTOERR);
		 if(NoIeKeepAliveFlag == TRUE)
			pLmiCCB->lmi_no_ie_keep_alive++;
		 else
			pLmiCCB->lmi_no_ie_rep_type++;
		 return;
		}

		/* If we have rcvd a FULL status report, run a 'garbage-collect' check on
	* all CCBs (except lmi's)...Any previously existing pvcs not reported in
	* this FULL report are deemed to no longer exist and associated CCBs must
	* be deleted.
	*/
		if(SeqNumXchgFlag == FRS_LMI_FULL_STAT_RPT)
		{
		 FRSLmiCleanupCCBs( pLmiCCB);
		}

		/* Whew! Everything checks out...Now update net sequence number for LIV ops.
	*/
		pLmiCCB->net_seq_num = net_seq_num;
		return;
}

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

	PROCEDURE NAME:   FRSLmiRcvUpdate

	DESCRIPTION:

	This procedure processes an UPDATE message rcvd from the network.

	PARAMETERS:
		LMICCB         * --- Ptr to the Chan Ctrl Blk (CCB) used for LMI traffic
		USRBUF *         --- Buffer containing the rcvd LMI frame.
		u_short          --- data length of the lmi frame.

	RETURNS:          nothing

	NOTES:
		This message exists to conform to Stratacom's LMI spec.
		Unlike at the end of processing a STATUS message, processing for an UPDATE
		message does not conclude with a garbage collect on CCBs.

*****************************************************************************/
void
FRSLmiRcvUpdate(pLmiCCB, pRcv, len)
LMICCB *pLmiCCB;
USRBUF *pRcv;
u_short len;
{
		FR_HDR_LMI *lmi_frm =  m_MMGR_DATA(pRcv, FR_HDR_LMI *) ;
		IE_UN *pIe = (IE_UN *)((char *)lmi_frm + sizeof(FR_HDR_LMI));
		u_short pvc_num;
		CCB *pCCB = CCBNULL;
		LCB *pLCB = pLmiCCB->common_ccb.plcb;
		IE_UN contigIe, *pContigIe;    

		pLmiCCB->lmi_total_updates++;

		pLCB->DLCIEntryCount = 0 ;

		for (; len;)
		{
	/* store current information element in contigIe and
		 update pIe 
	*/
	pContigIe = FRSGetNextIe ( pRcv, (IE_UN **)&pIe, &contigIe);

	switch (pContigIe->ie_id)
		 {
		 case FRS_LMI_IE_PVC_STAT:

			pvc_num = (pContigIe->ie_type.ie_pvc_status.dlciMSB << 8) |
		               pContigIe->ie_type.ie_pvc_status.dlciLSB;

			/* Fetch the CCB...
			 */
			pCCB = FRSLocateCCBbyDLCI(pLCB, pvc_num);

			/* If CCB not found, create one, maybe. */
			if (pCCB == CCBNULL)
				 sys_add_pll(pLCB, &pCCB, pvc_num);

			switch(pContigIe->ie_type.ie_pvc_status.pvc_status & FRS_LMI_NBIT_MASK)
			{
				case FRS_LMI_STATUS_EXIST:
				case FRS_LMI_STATUS_ADDED:

					if(pCCB == CCBNULL)
						pLmiCCB->lmi_unexpected_pvc_num++;
			 	break;
			}

			if( !(pContigIe->ie_type.ie_pvc_status.pvc_status & FRS_LMI_DBIT_MASK) )
			{
			 /* If the D-bit is 0, the PVC has been deleted. Drop the CCB
				* associated with pvc_num and inform user
				* process about it...
				*/
			 if(pCCB != CCBNULL)
			 {
				FRSLmiMarkCCB( pCCB, FALSE);
			 }

			}

			/* Mark the CCB active_or_not & congested_or_not
			 */
			if( (pCCB != CCBNULL) && (pLCB->State == FRS_LKUP) )
			{
			 FRSLmiMarkCCB( pCCB,
				(u_char)((pContigIe->ie_type.ie_pvc_status.pvc_status &
						 FRS_LMI_NBIT_MASK) != 0));

			 /* For Stratacom IPX switches, verify network buffer
				* congestion status...
				*/
			 if(m_FRS_IS_S_LMI_CFG( pLCB))
			 {
				FRSLmiThrottleCCB( pCCB,
			(u_char)(pContigIe->ie_type.ie_pvc_status.pvc_status
						& FRS_LMI_STATUS_BUFR));

				/* Update bandwidth for PVC (for STRATACOM IPX switches),
				 * if configured to do so...
				 */
				if(m_FRS_IS_LMI_BW_CTRL( pLCB))
						pCCB->my_cosb.bc_cur =
					((pContigIe->ie_type.ie_pvc_status.pvc_bw[0] << 16) +
					(pContigIe->ie_type.ie_pvc_status.pvc_bw[1] << 8) +
					pContigIe->ie_type.ie_pvc_status.pvc_bw[2]) / 8;
			 }
			}
			break;

		 default:
			/* Unsupported message type. Skip over.
			 */
			pLmiCCB->lmi_unsupp_ie_msg++;
			FRSLogErr( pLCB, pRcv, FRS_LNK_UNKNOWNIE);
			break;
		 }


		if((pLCB->DLCIEntryCount + 1) < FRS_MAX_CHANNELS)
		{
			bcopy((char *)pContigIe,
				  (char *)&pLCB->DLCIEntrys[pLCB->DLCIEntryCount],
				  sizeof(IE_UN)) ;

		    pLCB->DLCIEntryCount++ ;
		}
		 /* Drop the length of the packet...
			*/
	len -= (FRS_LMI_COMMON_HDR_LEN + pContigIe->ie_len);
		}

		return;
}

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

	PROCEDURE NAME:   FRSLmiSendStatEnq

	DESCRIPTION:

	This procedure creates an LMI STATUS ENQUIRY message and enqueues the
	frame for transmission to the network.

	PARAMETERS:

		LMICCB         * --- Ptr to the Chan Ctrl Blk (CCB) used for LMI traffic

	RETURNS:

		 FRS_SUCCESS      --- Successful completion
		 FRS_NO_BUFFERS   --- Out of memory buffers

	NOTES:

		This procedure assumes that a buffer of size FRS_LMI_STAT_ENQ_FRAME_LEN+1
		octets can be contiguously allocated using the allocate macro.
*****************************************************************************/

u_short
FRSLmiSendStatEnq(pLmiCCB)
LMICCB *pLmiCCB;
{
		USRBUF *pUsrbuf;
		FR_HDR_LMI *lmi_frm;
		IE_UN  *lmi_data;
		CCB *pCCB = &pLmiCCB->common_ccb;
		LCB *pLCB = pCCB->plcb;
		u_char *pLkShft;

		/* Allocate a buffer...
	*/
		if((pUsrbuf = m_MMGR_ALLOC_BUFR(FRS_LMI_STAT_ENQ_FRAME_LEN+1)) == BNULL)
		 return FRS_NO_BUFFERS;

		lmi_frm = m_MMGR_DATA( pUsrbuf, FR_HDR_LMI *);

		/* Populate the frame header...
	*/
		lmi_frm->lmi_header = pCCB->my_header;
		lmi_frm->frame_type = FRS_UNNUM_INFO_FRAME_TYPE;
		lmi_frm->prot_disc =
		 (u_char)(m_FRS_IS_ANNEXD_CFG( pLCB) ? FRS_LMI_ANNEXD_PROT_DISC : 
				m_FRS_IS_ANNEXA_CFG( pLCB) ? FRS_LMI_ANNEXA_PROT_DISC :
									 FRS_LMI_PROT_DISCRIMINATOR);
		lmi_frm->call_ref = 0;
		lmi_frm->msg_type = FRS_LMI_STATENQ_MSG_TYPE;

		/* Insert locking shift if we are in AnnexD mode...
	*/
		if(m_FRS_IS_ANNEXD_CFG( pLCB))
		{
		 pLkShft = ((u_char *)lmi_frm + sizeof(FR_HDR_LMI));
		 *pLkShft++ = FRS_LMI_LOCK_SHIFT;
		 m_MMGR_INC_LEN(pUsrbuf, 1);
		 lmi_data = (IE_UN *)pLkShft;
		}
		else
		 lmi_data = (IE_UN *)((u_char *)lmi_frm + sizeof(FR_HDR_LMI));

		/* Populate mgmt layer data consisting of a REPORT TYPE element and a
	* KEEP ALIVE element...
	*/
		lmi_data->ie_id = (u_char)(m_FRS_IS_ANNEXA_CFG( pLCB) ? 
						 FRS_LMI_ANNEXA_IE_REP_TYPE:
						 FRS_LMI_IE_REP_TYPE);
		lmi_data->ie_len = FRS_LMI_IE_REP_TYPE_LEN;

		/* Check for FCS error.  Force a Full Status Enq if there's
	* an error.
	*/

		/** COMMENTED OUT ---------------------------------------------------------

		if( sys_get_fcs_errs( pLCB ) == FRS_INVALID )
		 pLmiCCB->full_status_int_count = 0;

	** --END OF COMMENT BLOCK ---------------------------------------------- */

		/* Request a FULL report if we have reached mN1 intervals OR
	* we have not heard from the switch lately...
	*/
		if((pLmiCCB->full_status_int_count-- == 0) || (m_TST(pLmiCCB->lmi_flag, 
						 (LMI_STATUS_PENDING | LMI_FORCE_FULL_STATUS))))
		{
		 lmi_data->ie_type.ie_rep_type.rep_type = FRS_LMI_FULL_STAT_RPT;

		 /* Reset full_status_int_count from configuration...
			*/
		 pLmiCCB->full_status_int_count =
			(u_char)pCCB->plcb->my_cfg->frs_link_mN1-(u_char)1;

		 m_Set(pLmiCCB->lmi_flag, (LMI_EXPECT_F_STATUS));
		 pLmiCCB->lmi_full_stat_enqs++;
		}
		else
		 lmi_data->ie_type.ie_rep_type.rep_type = FRS_LMI_SEQ_NUM_XCHG;

		/* Update local sequence number taking into account rollover...
	*/
		if(++pLmiCCB->local_seq_num == 0)
		 pLmiCCB->local_seq_num = 1;

		lmi_data = (IE_UN *)((u_char *)lmi_data +
		 FRS_LMI_COMMON_HDR_LEN + 1);

		lmi_data->ie_id = (u_char)(m_FRS_IS_ANNEXA_CFG( pLCB) ? 
						 FRS_LMI_ANNEXA_IE_KEEP_ALIVE:
						 FRS_LMI_IE_KEEP_ALIVE);
		lmi_data->ie_len = FRS_LMI_IE_KEEP_ALIVE_LEN;
		lmi_data->ie_type.ie_keep_alive.current_seq = pLmiCCB->local_seq_num;
		lmi_data->ie_type.ie_keep_alive.last_rcvd_seq = pLmiCCB->net_seq_num;

		/* Update data count and ship it...
	* Update link stats.
	*/
		m_MMGR_INC_LEN( pUsrbuf, FRS_LMI_STAT_ENQ_FRAME_LEN);
#ifndef STREAM_MESSAGES
		pUsrbuf->first_frag = pUsrbuf;
		pUsrbuf->code = FRS_IN_PROGRESS;
		pUsrbuf->next_frag = (USRBUF *) NULL;
#endif
		if (FRSDRV_SendData( pCCB, pUsrbuf) != FRS_SUCCESS)
	 m_MMGR_FREE_BUFR(pUsrbuf);

		if(m_FRS_IS_STATS_ENBL(pLCB))
		 FRSUpdateStats( pCCB, pUsrbuf, 0);

		pLmiCCB->lmi_total_stat_enqs++;
		return FRS_SUCCESS;
}

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

	PROCEDURE NAME:   FRSLmiRcvStatEnq

	DESCRIPTION:

	This procedure processes a STATUS_ENQ request rcvd from the DTE.

	PARAMETERS:

		LMICCB         * --- Ptr to the Chan Ctrl Blk (CCB) used for LMI traffic
		USRBUF *         --- Ptr to the rcvd LMI frame buffer.
		u_short          --- data length of the lmi frame.

	RETURNS:          nothing

	NOTES:

*****************************************************************************/
void
FRSLmiRcvStatEnq(pLmiCCB, pRcv, len)
LMICCB *pLmiCCB;
USRBUF *pRcv;
u_short len;
{
		FR_HDR_LMI *lmi_frm = m_MMGR_DATA(pRcv, FR_HDR_LMI *);
		IE_UN *pIe = (IE_UN *)((char *)lmi_frm + sizeof(FR_HDR_LMI));
		u_char SeqNumXchgFlag = FRS_LMI_SEQ_NUM_XCHG;
		u_char NoIeRepTypeFlag = TRUE;
		u_char NoIeKeepAliveFlag = TRUE;
		LCB *pLCB = pLmiCCB->common_ccb.plcb;

		/* Update statistics...
	*/
		pLmiCCB->lmi_total_stat_enqs++;
		pLmiCCB->stat_enq_count++;

		/* Bump the pIe pointer by a byte to account for LOCK_SHIFT in AnnexD...
	* Also, drop the count by a byte.
	*/
		if(m_FRS_IS_ANNEXD_CFG( pLCB))
		{
		 if(*((u_char *)pIe) != FRS_LMI_LOCK_SHIFT)
		 {
		/* Well, it might just be Q.933 Annex A
		 * (which doesn't have the LOCK SHIFT byte. */
		m_Rst(pLCB->lcb_opt,
			 (FRS_OPT_PJ_LMI_CFG | FRS_OPT_S_LMI_CFG | FRS_OPT_ANNEXD_CFG));
		m_Set(pLCB->lcb_opt, FRS_OPT_ANNEXA_CFG);  /* Assume (for now) Annex A */
		 }
		 else
		 {
		pIe = (IE_UN *)((u_char *)pIe + 1);
		len--;
		 }
		}

		/* Parse and process Stat Enq req...
	*/
		for (; len;)
		{
		 if (  (  (!m_FRS_IS_ANNEXA_CFG(pLCB))
			&&(pIe->ie_id != FRS_LMI_IE_REP_TYPE) 
			&&(pIe->ie_id != FRS_LMI_IE_KEEP_ALIVE)
			)
			||(  (m_FRS_IS_ANNEXA_CFG(pLCB))
			&&(pIe->ie_id != FRS_LMI_ANNEXA_IE_REP_TYPE) 
			&&(pIe->ie_id != FRS_LMI_ANNEXA_IE_KEEP_ALIVE)
			)
		 )
		 {
		/* Unsupported message type. Bail out. */
		pLmiCCB->lmi_unsupp_ie_msg++;
		pLmiCCB->full_status_int_count = 0;
		FRSLogErr( pLCB, pRcv, FRS_LNK_UNKNOWNIE);
		return;
		 }
		 switch (pIe->ie_id)
		 {
		 case FRS_LMI_IE_REP_TYPE:
		 case FRS_LMI_ANNEXA_IE_REP_TYPE:

			NoIeRepTypeFlag = FALSE;

			/* Set flag indicating a Sequence_Number_Xchange_Only or Full_Status
			 * report...
			 */
			SeqNumXchgFlag = pIe->ie_type.ie_rep_type.rep_type;
			if(SeqNumXchgFlag == FRS_LMI_FULL_STAT_RPT)
			{
			 /* Update Statistics...
				*/
			 pLmiCCB->lmi_full_stat_enqs++;
			}
			break;

		 case FRS_LMI_IE_KEEP_ALIVE:
		 case FRS_LMI_ANNEXA_IE_KEEP_ALIVE:

			NoIeKeepAliveFlag = FALSE;

			/* Current seq must be stored away for later use...
			 */
			pLmiCCB->net_seq_num = pIe->ie_type.ie_keep_alive.current_seq;

			/* Validate Last Rcvd Seq with local_seq_num and update latter...
			 */
			if(pLmiCCB->local_seq_num != 
			pIe->ie_type.ie_keep_alive.last_rcvd_seq)

			 /* Ooh! We have things out of whack.
				* Inc counter and verify thresholding.
				*/
			{
			 pLmiCCB->lmi_lost_seq_num_sync_event++;

			 /* Perform thresholding...
				*/
			 if((FRSLmiPerfThresh( pLmiCCB, FRS_EVT_NOSYNC_KEEP_ALIVE,
				FRS_EVT_ALARM)) == TRUE)
			 {
				pLmiCCB->local_seq_num = 0;
				FRSLmiLinkDown( pLmiCCB->common_ccb.plcb);
			 }

			}
			else
			{
			 /* Perform thresholding...
				*/
			 if(m_LMI_IS_F_FULL_STAT(pLmiCCB) || (FRSLmiPerfThresh( pLmiCCB,
				FRS_EVT_NOSYNC_KEEP_ALIVE, FRS_EVT_CLEAR)) == TRUE)
				FRSLmiLinkUp( pLmiCCB->common_ccb.plcb);
			}

			break;

		 }

		 /* Decrement the length of the packet and update ptr...
			*/
		 len -= (FRS_LMI_COMMON_HDR_LEN + pIe->ie_len);
		 pIe = (IE_UN *)((u_char *)pIe + FRS_LMI_COMMON_HDR_LEN + pIe->ie_len);
		}

		/* Kick the polling (watchdog) timer and perform thresholding...
	*/
		FRSTMR_start(pLmiCCB->common_ccb.plcb->LmiTimerId, 
		 (u_short) (pLmiCCB->lmi_max_poll_interval* (u_short) 1000));

		/* STATUS ENQ rcvd. Update threshold parameters...
	*/
		if (FRSLmiPerfThresh( pLmiCCB, FRS_EVT_STAT_MSG_TIMEOUT, FRS_EVT_CLEAR)
				== TRUE)
			FRSLmiLinkUp( pLmiCCB->common_ccb.plcb);

		/* Verify if indeed we rcvd IE_REP_TYPE and IE_KEEP_ALIVE...
	*/
		if(NoIeRepTypeFlag == TRUE)
		{
		 pLmiCCB->lmi_no_ie_rep_type++;
		 return;
		}

		if(NoIeKeepAliveFlag == TRUE)
		{
		 pLmiCCB->lmi_no_ie_keep_alive++;
		 return;
		}

		/* Finally, respond to the Status Enquiry...
	*/
		FRSLmiSendStat( pLmiCCB, SeqNumXchgFlag);
		return;
}

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

	PROCEDURE NAME:   FRSLmiSendStat

	DESCRIPTION:

	This procedure creates an LMI STATUS message and enqueues the
	frame for transmission to the DTE.

	PARAMETERS:

		LMICCB         * --- Ptr to the Chan Ctrl Blk (CCB) used for LMI traffic
		u_char           --- indicating a full status report or Seq Num Xchg only

	RETURNS:

		 FRS_SUCCESS      --- Successful completion
		 FRS_NO_BUFFERS   --- Out of memory buffers

	NOTES:

		 This functions assumes that the allocate macro is able to allocate
		 a contiguous piece of memory as large as:

		 FRS_LMI_STATUS_FRAME_LEN+1
		 +(sizeof(IE_PVC_STATUS)+FRS_LMI_COMMON_HDR_LEN) * Max_plls_per_link
		 
		 e.g.: For 16 plls on a link and SRATACOM LMI, memory required 
		 would be 13+1+8*16 = 142 octets.
		
*****************************************************************************/
u_short
FRSLmiSendStat(pLmiCCB, report_type)
LMICCB *pLmiCCB;
u_char report_type;
{
		USRBUF *pUsrbuf, *pCurbuf;
		FR_HDR_LMI *lmi_frm;
		IE_UN  *lmi_data;
		CCB *pScan;
		CCB *pCCB = &pLmiCCB->common_ccb;
		LCB *pLCB = pCCB->plcb;
		u_short nSize, remaining;
		u_char *pLkShft;
		u_char stat;

		/* Allocate appropriate buffer...
	*/
		if(report_type == FRS_LMI_FULL_STAT_RPT)
		{
		 nSize = FRS_LMI_STATUS_FRAME_LEN+1+pLCB->num_plls *
			 (sizeof(IE_UN)+FRS_LMI_COMMON_HDR_LEN);
		 if((pUsrbuf = m_MMGR_ALLOC_BUFR((u_short)nSize)) == BNULL)
			return FRS_NO_BUFFERS;
		}
		else
		 if((pUsrbuf = m_MMGR_ALLOC_BUFR(FRS_LMI_STATUS_FRAME_LEN+1)) == BNULL)
			return FRS_NO_BUFFERS;

		lmi_frm = m_MMGR_DATA( pUsrbuf, FR_HDR_LMI *);

		/* Populate the frame header...
	*/
		lmi_frm->lmi_header = pCCB->my_header;
		lmi_frm->frame_type = FRS_UNNUM_INFO_FRAME_TYPE;
		lmi_frm->prot_disc =
		 (u_char)(m_FRS_IS_ANNEXD_CFG( pLCB) ? FRS_LMI_ANNEXD_PROT_DISC : 
				m_FRS_IS_ANNEXA_CFG( pLCB) ? FRS_LMI_ANNEXA_PROT_DISC :
									 FRS_LMI_PROT_DISCRIMINATOR);
		lmi_frm->call_ref = 0;
		lmi_frm->msg_type = FRS_LMI_STATUS_MSG_TYPE;

		/* Insert locking shift if we are in AnnexD mode...
	*/
		if(m_FRS_IS_ANNEXD_CFG( pLCB))
		{
		 pLkShft = ((u_char *)lmi_frm + sizeof(FR_HDR_LMI));
		 *pLkShft++ = FRS_LMI_LOCK_SHIFT;
		 m_MMGR_INC_LEN(pUsrbuf, 1);
		 lmi_data = (IE_UN *)pLkShft;
		}
		else
		 lmi_data = (IE_UN *)((u_char *)lmi_frm + sizeof(FR_HDR_LMI));

		/* Populate mgmt layer data consisting of a REPORT TYPE element and a
	* KEEP ALIVE element...
	*/
		lmi_data->ie_id = (u_char)(m_FRS_IS_ANNEXA_CFG( pLCB) ? 
						 FRS_LMI_ANNEXA_IE_REP_TYPE:
						 FRS_LMI_IE_REP_TYPE);
		lmi_data->ie_len = FRS_LMI_IE_REP_TYPE_LEN;

		/* Send a FULL report if so requested by the DTE...
	*/
		if(report_type == FRS_LMI_FULL_STAT_RPT)
		{
		 lmi_data->ie_type.ie_rep_type.rep_type = FRS_LMI_FULL_STAT_RPT;
		 pLmiCCB->lmi_full_status++;
		}
		else
		 lmi_data->ie_type.ie_rep_type.rep_type = FRS_LMI_SEQ_NUM_XCHG;

		/* Update local sequence number taking into account rollover...
	*/
		if(++pLmiCCB->local_seq_num == 0)
		 pLmiCCB->local_seq_num = 1;

		lmi_data = (IE_UN *)((u_char *)lmi_data + 
		 FRS_LMI_COMMON_HDR_LEN + 
		 FRS_LMI_IE_REP_TYPE_LEN);

		lmi_data->ie_id = (u_char)(m_FRS_IS_ANNEXA_CFG( pLCB) ? 
						 FRS_LMI_ANNEXA_IE_KEEP_ALIVE:
						 FRS_LMI_IE_KEEP_ALIVE);
		lmi_data->ie_len = FRS_LMI_IE_KEEP_ALIVE_LEN;
		lmi_data->ie_type.ie_keep_alive.current_seq = pLmiCCB->local_seq_num;
		lmi_data->ie_type.ie_keep_alive.last_rcvd_seq = pLmiCCB->net_seq_num;

		/* Update data count...
	*/
		m_MMGR_INC_LEN( pUsrbuf, FRS_LMI_STATUS_FRAME_LEN);

		/* If FULL status was requested, create PVC_STATUS info elements...
	*/
		if(report_type == FRS_LMI_FULL_STAT_RPT)
		{
		 lmi_data = (IE_UN *)((u_char *)lmi_data + 
			FRS_LMI_COMMON_HDR_LEN + 
			FRS_LMI_IE_KEEP_ALIVE_LEN);

		 pScan = pLCB->ccbList;
		 pCurbuf = pUsrbuf;

		 while ( pScan)
		 {
			/* calculate size of ie data area
			*/
			if(  (m_FRS_IS_ANNEXD_CFG( pLCB))             
			||(m_FRS_IS_ANNEXA_CFG( pLCB))
			)
			 nSize = FRS_LMI_IE_PVCS_LEN; 
			else
			 nSize = nSize = sizeof(IE_PVC_STATUS);    
			 
			/* compute lmi_data which is a ptr to a contiguous
			area in a TX user buffer
			*/
			remaining = (u_short)(PAYLOAD_BUF_SIZE - m_MMGR_DATA_LEN(pCurbuf));
			if (remaining < nSize + FRS_LMI_COMMON_HDR_LEN)
			{
#ifndef	STREAM_MESSAGES
			 pCurbuf->link = m_MMGR_ALLOC_BUFR(nSize + FRS_LMI_COMMON_HDR_LEN);
			 pCurbuf = pCurbuf->link;
#else
			 pCurbuf = m_MMGR_ALLOC_BUFR(nSize + FRS_LMI_COMMON_HDR_LEN);

#endif
			 if (!pCurbuf)
				return FRS_NO_BUFFERS;     
			}
			lmi_data = (IE_UN *)(m_MMGR_DATA(pCurbuf, char *) +
						m_MMGR_DATA_LEN(pCurbuf));
			m_MMGR_INC_LEN(pCurbuf, nSize + FRS_LMI_COMMON_HDR_LEN);

			/* Figure out pvc status...
			 * While responding to status enquiries, we defer to the target
			 * system for telling us whether this PVC is truly up or not.
			 * This call will set the CCB up or down.
			 */
			sys_pvc_stat(pScan); 


			if(m_FRS_IS_CCB_UP(pScan))
			 stat = (u_char)(FRS_LMI_STATUS_EXIST | FRS_LMI_STATUS_ACT);
			else
			 stat = (u_char)(FRS_LMI_STATUS_EXIST);


			/* Populate the IE depending upon lmi type...
			 */
			lmi_data->ie_id = (u_char)(m_FRS_IS_ANNEXA_CFG( pLCB) ? 
																 FRS_LMI_ANNEXA_IE_PVC_STAT :
																 FRS_LMI_IE_PVC_STAT);

			if (  (m_FRS_IS_ANNEXD_CFG( pLCB))
					||(m_FRS_IS_ANNEXA_CFG( pLCB))
			 )
			{
			 /* AnnexD/AnnexA...
				* DLCI encoding is different from LMI encoding.
				*/
			lmi_data->ie_len = FRS_LMI_IE_PVCS_LEN;
			lmi_data->ie_type.ie_pvc_status.dlciMSB =
				(u_char)(pScan->dlci >> 4 & 0x3F);
			lmi_data->ie_type.ie_pvc_status.dlciLSB =
				(u_char)(((pScan->dlci & 0x0F) << 3) | 0x80);

			lmi_data->ie_type.ie_pvc_status.pvc_status =
						 (stat | (u_char)FRS_ANNEXD_PVCS_EX_MASK);
			}
			else
			{
			 /* Non-AnnexD/AnnexA...
				*/
			 lmi_data->ie_len = sizeof(IE_PVC_STATUS);
			 lmi_data->ie_type.ie_pvc_status.dlciMSB =
					 (u_char)(pScan->dlci >> 8 & 0x03);
			 lmi_data->ie_type.ie_pvc_status.dlciLSB =
					 (u_char)(pScan->dlci & 0xFF);

			 lmi_data->ie_type.ie_pvc_status.pvc_status = stat;
			 lmi_data->ie_type.ie_pvc_status.pvc_bw[0] = 0;
			 lmi_data->ie_type.ie_pvc_status.pvc_bw[1] = 0;
			 lmi_data->ie_type.ie_pvc_status.pvc_bw[2] = 0;
			}
			pScan = pScan->next_ccb;
		 }      
		}

		if ( FRSFrameSize(pUsrbuf) > 
	 pCCB->plcb->my_cfg->frs_link_max_frsize)
		{
	/* if frame is too big do not ship it and free the
			buffer 
	*/
	m_MMGR_FREE_BUFR(pUsrbuf);
	pLmiCCB->lmi_outbound_frame_too_big++;
		}
		else
		{
#ifndef	STREAM_MESSAGES
		 pUsrbuf->first_frag = pUsrbuf;
		 pUsrbuf->code = FRS_IN_PROGRESS;
		 pUsrbuf->next_frag = (USRBUF *) NULL;
#endif
	/* Ship it...  Update link stats.
	 */
	if (FRSDRV_SendData( pCCB, pUsrbuf) != FRS_SUCCESS)
			m_MMGR_FREE_BUFR(pUsrbuf);
		}

		if(m_FRS_IS_STATS_ENBL(pLCB))
		 FRSUpdateStats( pCCB, pUsrbuf, 0);

		pLmiCCB->lmi_total_status++;
		return FRS_SUCCESS;
}

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

	PROCEDURE NAME:   FRSLmiPerfThresh

	DESCRIPTION:


	PARAMETERS:
		LMICCB         * --- Ptr to the Chan Ctrl Blk (CCB) used for LMI traffic
		u_short            --- one of FRS_EVT_* identifying what error situation is
					being checked for thresholding
		u_short            --- FRS_EVT_ALARM or FRS_EVT_CLEAR indicating whether
					the type of event

	RETURNS:          u_short
		TRUE             --- Modal change indicating a threshold traversed.
		FALSE            --- No change indicating no thresholds traversed.

	NOTES:

*****************************************************************************/
u_short
FRSLmiPerfThresh( pLmiCCB, what, event)
LMICCB *pLmiCCB;
u_short  what;
u_short  event;
{

		/* Fetch the threshold parameters...
	*/
		THRESH_PARM *pThresh = &pLmiCCB->thresh_array[what];

		/* Do we have a positive (ALARM) event...?
	*/
		if(event == FRS_EVT_ALARM)
		{
		 /* Determine if we have crossed event threshold...
			*/
		 pThresh->consec_pos_events = 0;
		 switch (pThresh->x_state)
		 {
		 case FRS_LMI_THRESH_ST_NORMAL:
			pThresh->x_state = FRS_LMI_THRESH_ST_WATCH;
			pThresh->total_neg_events = 1;
			break;

		 case FRS_LMI_THRESH_ST_WATCH:
			if(++(pThresh->total_neg_events) >= pLmiCCB->lmi_alarm_event_thresh)
			{
			 pThresh->x_state = FRS_LMI_THRESH_ST_PASSED;
			 pThresh->total_neg_events = 0;
			 return TRUE; /* Yes, we have definitely taken a downturn */
			}
			break;

		 case FRS_LMI_THRESH_ST_PASSED:
			/* Reset the watch count because we need mN3 CONSECUTIVE ok events..
			 */
			break;

		 default:    /* ??? */
			break;
		 }
		}
		else
		{
		 switch (pThresh->x_state)
		 {
		 case FRS_LMI_THRESH_ST_NORMAL:
			break;

		 case FRS_LMI_THRESH_ST_WATCH:
			/* Go back to normal state if watch total count becomes mN3...
			 */
			if(++(pThresh->consec_pos_events) >= pLmiCCB->lmi_reset_event_count)
			 pThresh->x_state = FRS_LMI_THRESH_ST_NORMAL;
			break;

		 case FRS_LMI_THRESH_ST_PASSED:
			if(++(pThresh->consec_pos_events) >= pLmiCCB->lmi_reset_event_count)
			{
			 pThresh->x_state = FRS_LMI_THRESH_ST_NORMAL;
			 return TRUE;
			}
			break;

		 default: /* ??? */
			break;
		 }
		}

		return FALSE;
}

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

	PROCEDURE NAME:   FRSLmiHeartbeatPoll

	DESCRIPTION:


	PARAMETERS:       none

	RETURNS:          nothing

	NOTES:

*****************************************************************************/
void
FRSLmiHeartbeatPoll( hLCB)
u_long hLCB;
{
		LMICCB *pLmiCCB = frsLCBtable[hLCB-1]->lmi_ccb;

		/* Did we get a STATUS response message to our previous poll...?
	* In any event, perform threshold checks.
	*/
		if(m_LMI_IS_STAT_PEND(pLmiCCB))
		{
		 /* No STATUS msg in the last mT1 secs. Update threshold parameters and
			* look for modal changes...
			*/
		 pLmiCCB->lmi_stat_resp_timeout++;

		 if((FRSLmiPerfThresh( pLmiCCB,
			FRS_EVT_STAT_MSG_TIMEOUT,
			FRS_EVT_ALARM)) == TRUE)
			FRSLmiLinkDown( pLmiCCB->common_ccb.plcb);
		}
		else
		{
		 /* STATUS msg rcvd in the last mT1 secs. Update threshold parameters and
			* look for modal changes...
			*/
		 if((FRSLmiPerfThresh( pLmiCCB,
			FRS_EVT_STAT_MSG_TIMEOUT,
			FRS_EVT_CLEAR)) == TRUE)
			FRSLmiLinkUp(pLmiCCB->common_ccb.plcb);
		}

		/* Send a status enquiry to the network and set pending flag...
	*/
		FRSLmiSendStatEnq( pLmiCCB);
		m_LMI_SET_STAT_PEND(pLmiCCB);

		/* Kick the timer...
	* Have a shorter polling interval initially...
	*/
		FRSTMR_start(pLmiCCB->common_ccb.plcb->LmiTimerId,
		 (u_short) ((m_LMI_IS_FAST_TMR(pLmiCCB)) ? (u_short) 1000 : 
					(u_short) (pLmiCCB->lmi_polling_interval* (u_short) 1000)));

		return;
}

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

	PROCEDURE NAME:   FRSLmiPollVer

	DESCRIPTION:

		 This timeout will occur whenever the DTE fails to poll us (the DCE)
		 within the last nT2 (or T392) seconds.

	PARAMETERS:       LMICCB * --- Pointer to lmi ctrl blk

	RETURNS:          nothing

	NOTES:

		 Applicable for both LMI and Annex-D.

*****************************************************************************/
void
FRSLmiPollVer( hLCB)
u_long hLCB;
{
		LMICCB *pLmiCCB = frsLCBtable[hLCB-1]->lmi_ccb;

		/* We did not get a STATUS ENQUIRY request from the DTE in the last mT2 
	* secs.  Perform threshold checks and look for modal changes...
	*/
		pLmiCCB->lmi_stat_enq_timeout++;

		if((FRSLmiPerfThresh( pLmiCCB, FRS_EVT_STAT_MSG_TIMEOUT, FRS_EVT_ALARM))
		 == TRUE)
	 FRSLmiLinkDown( pLmiCCB->common_ccb.plcb);

		/* Kick the timer...
	*/
		FRSTMR_start(pLmiCCB->common_ccb.plcb->LmiTimerId, 
		 (u_short) (pLmiCCB->lmi_max_poll_interval* (u_short) 1000));

		return;
}

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

	PROCEDURE NAME:   FRSLmiPollXcessChk

	DESCRIPTION:
	
			This timer keeps track of an excessive polling rate from the DTE
			side.

	PARAMETERS:       LMICCB * --- Pointer to lmi ctrl blk

	RETURNS:          nothing

	NOTES:

		This timer is applicable only for non-AnnexD/AnnexA type lmi.

*****************************************************************************/
void
FRSLmiPollXcessChk(hLCB)
u_long hLCB;
{
		LCB *pLCB = frsLCBtable[hLCB-1];
		LMICCB *pLmiCCB = pLCB->lmi_ccb;

		/* Make sure DTE did not exceed stipulated poll rate...
	*/
		if(pLmiCCB->stat_enq_count > pLmiCCB->lmi_max_stat_enq_count)
		 pLmiCCB->lmi_stat_enq_rate_plus++;

		pLmiCCB->stat_enq_count = 0;

		/* Kick the timer...
	*/
		if(  (!(m_FRS_IS_ANNEXD_CFG( pLCB)))
		&&(!(m_FRS_IS_ANNEXA_CFG( pLCB)))
		)
		 FRSTMR_start(pLCB->LmiXcessChkTimerId,
					(u_short) (pLmiCCB->lmi_xcess_poll_interval* (u_short) 1000));

		return;
}

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

	PROCEDURE NAME:   FRSLmiCleanupCCBs

	DESCRIPTION:

		Routine scans all CCBs on a given link and deletes the CCBs as appropriate.

	PARAMETERS:
		LMICCB         * --- Ptr to the Chan Ctrl Blk (CCB) used for LMI traffic

	RETURNS:          nothing

	NOTES:

*****************************************************************************/
void
FRSLmiCleanupCCBs(pLmiCCB)
LMICCB *pLmiCCB;
{
		CCB *pCCB, *pCCBnext;

		/* LMI CCBs never make it into the ccblist of a link ctrl blk...
	* So, a scan of the ccb_list need not verify whether a ccb is an lmi
	* ccb or not.
	*/
		for (pCCB = pLmiCCB->common_ccb.plcb->ccbList; pCCB; pCCB = pCCBnext)
		{
		 pCCBnext = pCCB->next_ccb;
		 if(pCCB->my_rev_count != pLmiCCB->common_ccb.my_rev_count)
		 {
			FRSLmiMarkCCB( pCCB, FALSE);
		 }
		}
		return;
}

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

	PROCEDURE NAME:    FRSLmiLinkDown

	DESCRIPTION:

	Procedure to handle link down conditions reported via LMI.

	PARAMETERS:

	LCB * --- Ptr to an lcb

	RETURNS:    Nothing

	NOTES:

*****************************************************************************/
void
FRSLmiLinkDown( pLCB)
LCB *pLCB;
{
		/* Assert First Full Status Flag for next time...
	*/
		m_LMI_SET_F_FULL_STAT(pLCB->lmi_ccb);

		/* Declare the link down...
	*/
		pLCB->State = FRS_LKDWN;
		sys_link_down(pLCB);
		return;
}

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

	PROCEDURE NAME:    FRSLmiLinkUp

	DESCRIPTION:

	Procedure to handle link up conditions reported via LMI.

	PARAMETERS:

	LCB * --- Ptr to an lcb

	RETURNS:    Nothing

	NOTES:

*****************************************************************************/
void
FRSLmiLinkUp( pLCB)
LCB *pLCB;
{
		/* De-assert First Full Status Flag...
	*/
		m_LMI_RESET_F_FULL_STAT(pLCB->lmi_ccb);

		/* Declare the link up (operational)...
	*/
		pLCB->State = FRS_LKUP;
		sys_link_up(pLCB);
		return;
}

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

	PROCEDURE NAME:    FRSLmiMarkCCB

	DESCRIPTION:

	Marks a CCB active or inactive.

	PARAMETERS:

	CCB        * --- Pointer to a CCB
	u_char       --- Flag (TRUE => ccb up, FALSE => ccb down)

	RETURNS:         Nothing

	NOTES:

*****************************************************************************/
void
FRSLmiMarkCCB(pCCB, act_flag)
CCB *pCCB;
u_char act_flag;
{
		u_short prev_stat = pCCB->ccb_status;

		if(act_flag)
		{
		 m_FRS_SET_CCB_UP( pCCB);
		 m_Rst(pCCB->plcb->lmi_ccb->lmi_flag,
			 (LMI_FORCE_FULL_STATUS | LMI_FAST_TIMER));
		 pCCB->plcb->lmi_ccb->lmi_stat_enq_timeout = 0;
		}
		else
		{
		 m_FRS_RESET_CCB_UP( pCCB);
		 FRSLmiMarkFrames( pCCB );
		}

		/* If status changed, update timestamp...
	*/
		if(prev_stat != pCCB->ccb_status)
		{
		 pCCB->LastStatChgTime = sys_get_sys_uptime();
		if(act_flag)
			sys_sdt_ccb( pCCB) ;
		else
			sys_hdt_ccb( pCCB) ;

/*		 (act_flag) ? sys_sdt_ccb( pCCB) : sys_hdt_ccb( pCCB); */



		}
		return;
}

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

	PROCEDURE NAME:    FRSLmiThrottleCCB

	DESCRIPTION:

	Procedure to throttle data traffic on a PVC based upon availablity of
	network's buffers for this PLL.

	PARAMETERS:

	CCB        * --- Pointer to a CCB
	u_char       --- Flag

	RETURNS:         Nothing

	NOTES:

*****************************************************************************/
void
FRSLmiThrottleCCB( pCCB, bfr_flag)
CCB *pCCB;
u_char bfr_flag;
{

		/* Invokes the target system call to inform user process about
	* network congestion...if configured to do so.
	*/
		if(m_FRS_IS_LMI_XONXOFF(pCCB->plcb))
		 sys_throttle_ccb(pCCB, bfr_flag);

		return;
}

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

	PROCEDURE NAME:    FRSLmiMarkFrames

	DESCRIPTION:

	Marks frames with the dlci of a deactivated PVC so that they can be
	dropped before transmission.

	PARAMETERS:

	Pointer to the CCB of the deactivated PVC.
	
	RETURNS:         Nothing

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

void
FRSLmiMarkFrames( pCCB )
CCB *pCCB;
{
    USRBUF *pFrame;

    /* First, let's look for and mark frames in the Link hold queue.
     */   
    pFrame = (USRBUF *)(pCCB->plcb->TQTCB.Q.qtop);

    while( pFrame != (USRBUF *)0 )
    {
	if ( FRSGetFrameDLCI( m_MMGR_DATA(pFrame, char *) ) == pCCB->dlci )
#ifndef STREAM_MESSAGES
	  mMarkFrameforDrop( pFrame );
	pFrame = (USRBUF *)pFrame->next;
#else
	m_MMGR_FREE_BUFR(pFrame) ;
	pFrame = (USRBUF *)pFrame->b_next ;
#endif

    }

    /* Now let's go look in the host system's queues.
     */
    sys_mark_frames_drop( pCCB );
    return;
}

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

 PROCEDURE NAME: FRSGetNextIe

 DESCRIPTION:

		This function stores the current information element in a temporary
		buffer and returns the address of the next information element. 

 PARAMETERS:
		USRBUF *pframe    - pointer to the address of a user buffer
		UN_IE  **pIe      - pointer to the address of the current information 
						element
		UN_IE  *temp_ie   - pointer to the contiguous buffer where the current
						information element is stored

 RETURNS:
		A pointer to the contiguous buffer where the current information 
		element is stored, or NULL in case of failure.

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

IE_UN *
FRSGetNextIe( pframe, pIe, temp_ie)
USRBUF *pframe;
IE_UN  **pIe;
IE_UN  *temp_ie;
{
		int remainder, index;
		USRBUF *pcurbuf = pframe;
		IE_UN *pcurdata = m_MMGR_DATA (pframe, IE_UN *);
		IE_UN *pReturn;  /* value to return */

		/* find the user buffer and the address of the data area
		that the current information element is stored in.
		To do that check if "pIe" is within the data area of the 
		current buffer "pcurbuf". If not check within the data area 
		of "pcurbuf->link" and repeat the process until a buffer 
		that contains "pIe" is found.
		*/
		while (  ((char *)(*pIe) < (char *)pcurdata) 
		 ||((char *)(*pIe) > ((char *)pcurdata + m_MMGR_DATA_LEN(pcurbuf)))
		)
		 {
#ifndef STREAM_MESSAGES
		 if ((pcurbuf = pcurbuf->link) == NULL)
			return NULL;
#else
		 if ((pcurbuf = pcurbuf->b_cont) == NULL)
			return NULL;

#endif
		 pcurdata = m_MMGR_DATA (pcurbuf, IE_UN *);
		 } 

		/* "remainder" calculates 
		what is left in the data area of the current buffer. 
		*/  
		remainder = (int)(m_MMGR_DATA_LEN(pcurbuf) -
				((char *)(*pIe) - (char *)pcurdata));

		while (remainder == 0)
	 {
			/* Ugh! our pIE was RIGHT at the end of the buffer.  
			We must get ALL of the IE from the next buffer.
		*/
#ifndef	STREAM_MESSAGES

			if (!(pcurbuf = pcurbuf->link))
		 return NULL;
#else
			if (!(pcurbuf = pcurbuf->b_cont))
		 return NULL;
#endif
			*pIe = m_MMGR_DATA(pcurbuf, IE_UN *);
			remainder = m_MMGR_DATA_LEN(pcurbuf);
	 }

		pReturn = *pIe;  /* Assume IE is contiguous */     

		if (remainder < FRS_LMI_COMMON_HDR_LEN)
	 {
		 /* Ugh! our pIE doesn't even contain the length field.
		 We will move the IE to the contiguous area.
				*/
		 pReturn =  temp_ie;
		 temp_ie->ie_id = (*pIe)->ie_id;
		
#ifndef	STREAM_MESSAGES
		 if (!(pcurbuf = pcurbuf->link))
		 return NULL;
#else
		 if (!(pcurbuf = pcurbuf->b_cont))
		 return NULL;
#endif
		 *pIe = (IE_UN*)(m_MMGR_DATA(pcurbuf, char*) - 1);

		 for (index = 0; index < (int)((*pIe)->ie_len + sizeof((*pIe)->ie_len));
		index++)
		{
		*((char *)&(temp_ie->ie_len) + index) = 
			*((char *)&((*pIe)->ie_len) + index);
		}

	 }
		else if (remainder < (FRS_LMI_COMMON_HDR_LEN + (*pIe)->ie_len))
	 {
		 /* Ugh! our pIE contains the length field but not enough else
		 We will move the IE to the contiguous area.
				*/
		 pReturn =  temp_ie;

		 /* Copy what we can of the first buffer */

		 for (index = 0; index < (int)remainder; index++)
		*((char *)temp_ie + index) = *((char *)(*pIe) + index);

		/* now get the rest from the next buffer */
#ifndef STREAM_MESSAGES
		 if (!(pcurbuf = pcurbuf->link))
		 return NULL;
#else
		 if (!(pcurbuf = pcurbuf->b_cont))
		 return NULL;
#endif 
		 *pIe = (IE_UN*)(m_MMGR_DATA(pcurbuf, char *) - remainder);

		 for (index = 0; 
		index < (int)(temp_ie->ie_len + FRS_LMI_COMMON_HDR_LEN - remainder);
		index++)
		{
		*(((char *)temp_ie) + remainder + index) = 
			*(m_MMGR_DATA(pcurbuf, char *) + index);
		}
	 }

		/* Okay, pReturn points to a full, contiguous IE.
		Bump *pIe to point to the NEXT IE, for the next time.
	*/

		*pIe = (IE_UN *)((char *)*pIe + FRS_LMI_COMMON_HDR_LEN + pReturn->ie_len);

		return pReturn;      
}

