#ident "$Id: frstx.c,v 1.9 1997/10/21 20:48:12 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:  David Jeffries
	SUBJECT MATTER
		 SPECIALIST:   Deepak Shahane

	REVISION HISTORY:

	Date     Version  Name          Description
	-------- -------  ------------  --------------------------------------------
	01-16-92 1.10A    H&J (DJ)      Original
		 Other    H&J           See H&J's bug reports

	$Log: frstx.c,v $
	Revision 1.9  1997/10/21 20:48:12  gary
	Modifications made to accomodate the SOH (Streams-on-host) folks.
	
	Revision 1.8  1997/01/16 22:08:02  gary
	Removed FRAME_STAGE counter from code.

	Revision 1.7  1996/10/22 21:19:01  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.6  1996/07/09 15:30:25  gary
	Modified flow control algorithm slightly.
	For Phoenix cards, the Frame Relay layer will convey the max frame size
	to the IWAN layer so that the proper Rx buffers are allocated.

	Revision 1.5  1996/06/21 21:37:33  gary
	Backflowing now implemented on the Frame Relay stack!
	This minimizes the amount of loss encountered under
	Forced and Buffered injection.

	Revision 1.4  1996/04/26 19:57:46  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:10  garyp
	remove strm.h

 * Revision 1.22  1994/07/27  20:27:41  stavrosm
 * Release 2.2
 *
 * Revision 1.21  1994/07/12  14:47:12  stavrosm
 * In FRSInject() variable pCosb was defined but not used.
 *
 * Revision 1.20  1994/07/06  20:14:20  saula
 * Bug Report #SA-940706:
 * 1. Efficiency improvements using timers for Traffic Control
 *    (CIR & Excess Burst enforcement)
 * 2. Improvement in congestion control for fallback:
 *    Excess bits get zapped temporarily during BECN / congestion.
 *
 * Revision 1.19  1994/06/16  15:09:34  akullber
 * A call to m_END_CRITICAL was added to the function FRSTxDone() before
 * returning because there is no "first_frag".
 *
 * Revision 1.18  1994/02/25  20:16:52  guest
 * See bug report SA-104.
 *
 * Revision 1.17  1993/12/06  10:50:28  stavrosm
 * Code was added so that FRSTxDone works when encapsulation
 * is turned off. This change is included in 2.1 release.
 *
 * Revision 1.16  93/11/10  16:44:33  stavrosm
 * Release 2.1
 * 
 * Revision 1.15  93/11/10  16:28:52  stavrosm
 * In a task based environment (USE_TASKING is 1) FRSTransmit
 * never calls frsTxState()
 * To fix added pLCB->State = frsTxState( pLCB, pEvent );
 * 
 * Revision 1.14  93/11/09  16:03:34  gregb
 * cleanup
 * 
	$Revision
 * Revision 1.13  93/11/05  10:18:56  stavrosm
 * 
..............................................................................

	DESCRIPTION:

	This module is the H&J Frame Relay Stack Transmission Queue processor.

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

	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) Multiprotocol Interconnect over Frame Relay Networks - T. Bradley, C.Brown
		 Wellfleet Communications, Inc. A. Malis BBN Communications Oct 14, 1991.

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

	SPECIAL COMPILE/LINK INSTRUCTIONS:

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

	FUNCTIONS INCLUDED in this module:

FRSFR_GetWriteQueueDepth  --- Get number of frames and bytes in a PLL 
						Write_Queue.
FRSFR_SendData       --- Prepend the frame relay header and enqueue the buffer 
				into the CCB's Write_queue.
FRSFR_SendDataWait   --- FRSFR_SendData with blocking of user process.
FRSLAPD_SendData     --- FRSFR_SendData without prepending frame relay header.
FRSLAPD_SendDataWait --- FRSFR_SendDataWait without prepending header.
FRSTxFrameEvt        --- Generates an FRS_FRAME event and wakes up the transmit 
				process.
frsTxState           --- Tx state event processor.
frsTx_LkDwn_LkUp     --- Tx state event processing routine
frsTx_LkDwn_TxDone   --- Tx state event processing routine
frsTx_LkUp_LkDwn     --- Tx state event processing routine
frsTx_LkUp_Frame     --- Tx state event processing routine
frsTx_LkUp_TxDone    --- Tx state event processing routine
FRSInject            --- Transfers data from Write Q to the Injection Q after 
				verification of the Committed Burst Size.
FRSInjectForce       --- Injects data into the network unconditionally.
FRSInjectNormal      --- Injects data into the network if the rate is within the
				subscribed limit.
FRSInjectHold        --- Injects data into the network when rate has crossed the
				basic and the excess burst size thresholds.
FRSInjectTimeout     --- Callback function invoked upon completion of the Tc 
				measurement period.
FRSTransmit          --- Top-level of an FRS transmit process.
FRSTxDone            --- Returns frame fragments to TxDoneFunc() in order 
..............................................................................
*/


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


#define FRSTX_C

/*
 * 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"
#include "cnt_arry.h"


/* The following Macro is used in various places to determine
 * if the current Tc period (for measuring CIR and Excess Burst)
 * has expired or not.  Arguments passed are the cosb pointer
 * and the current time. (in "frs_time" units.)  
 *
 * The Macro returns a boolean (True/False).  (True means "expired".)
 *
 * The computation is complicated by the fact that either 
 * "Now" or the "TcEnd" field (in the cosb structure) may have
 * wrapped around, making normal calculations difficult.
 */
#define mFRS_TcExpired(pCosb, Now) \
(((pCosb)->TcEnd >= (pCosb)->TcBegin) ?  /* This is normal */ \
 (  (Now >= (pCosb)->TcEnd) /* If time has passed... */ \
  ||(Now < (pCosb)->TcBegin)     /* ... or if 'Now' has wrapped */ \
  ) : \
 (  (Now >= (pCosb)->TcEnd)    /* unsigned arithmetical comparisons ... */ \
  &&(Now < (pCosb)->TcBegin) /* are such a pain sometimes. */ \
  ) \
 ) \

void
FRSTxDone(pUsrbuf, pLCB, rcode)
USRBUF *pUsrbuf;
LCB *pLCB;
u_short rcode;
{
  USRBUF *frag;
  USRBUF *nextfrag;
  CCB *pCCB;


  pCCB = FRSLocateCCB(pLCB, pUsrbuf);
  if ( pCCB != (CCB *)0 )
    {
      if ( pCCB->encap == NULL )
	{
	  /** This CCB is not encapsulated **/
	  if ( pCCB->TxDoneFunc != NULL )
	    pCCB->TxDoneFunc(pCCB,pUsrbuf,rcode);
	  else
	  {
/*StartUpDebug*/
/*	    printf("TxDone 1") ; */
	    m_MMGR_FREE_BUFR(pUsrbuf);
	  }
	}
      else
	{
	  m_START_CRITICAL;
#ifndef	STREAM_MESSAGES
	  pUsrbuf->code = rcode;
	  if (((frag = pUsrbuf->first_frag)) == BNULL)
#else
	  if (((frag = pUsrbuf->b_cont)) == BNULL)
#endif 
	    {
/*	      printf("TxDone 2") ; */
	      m_MMGR_FREE_BUFR(pUsrbuf);
	      m_END_CRITICAL;
	      return;
	    }
#ifndef	STREAM_MESSAGES	  
	  while (frag->code != FRS_IN_PROGRESS) 
	    {
	      frag = frag->next_frag;
	      
	      if (frag == BNULL) 
		{
		  /** Free the entire frame (all fragments)
		   **/
		  frag = pUsrbuf->first_frag;
#else

	  while (frag->b_cont != (mblk_t *)0) 
	    {
	      frag = frag->b_cont ;
	      
	      if (frag == BNULL) 
		{
		  /** Free the entire frame (all fragments)
		   **/
		  frag = pUsrbuf->b_cont ;

#endif
		  do 
		    {
#ifndef STREAM_MESSAGES
		      nextfrag = frag->next_frag;
#else
		      nextfrag = frag->b_cont;
#endif

		      pCCB = FRSLocateCCB(pLCB, frag);
		      
		      if ( pCCB == NULL || pCCB->TxDoneFunc == NULL )
/*StartUpDebug*/
			{
/*			printf("TxDone 3") ; */
			m_MMGR_FREE_BUFR(frag);
			}
		      else
			pCCB->TxDoneFunc(pCCB, frag, rcode);

		      frag = nextfrag;
		    } while (frag != BNULL);

		  break;
		} /* end if */
	    } /* end while */
	  
	  m_END_CRITICAL;
	}
    }
    return;
}

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

	PROCEDURE NAME:    FRSFR_SendData

	DESCRIPTION:

	This function accepts a CCB handle (obtained via an open call) and
	a pointer to a user buffer containing the payload data. This function
	will prepend the frame relay 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.

	RETURNS:

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

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

u_short 
FRSFR_SendData(hCCB,pUsrbuf)
u_long hCCB;
USRBUF *pUsrbuf;
{
  CCB *pCCB;
  u_short rcode = FRS_BAD_CCB_HANDLE;
		
  if(!m_FRS_CHECK) return FRS_DISABLED;

  /* Validate handle...
   */
  if ((hCCB < gGlobalCfg.frsTotalPLLs) &&
      ((pCCB = frsCCBtable[hCCB]) != CCBNULL))
    {
      /* Make a Frame...
       */
      rcode = FRSMakeFrame(pCCB, pUsrbuf, PID_NONE);
      if ( rcode == FRS_SUCCESS ) {
	rcode = FRSLAPD_SendData( hCCB, pUsrbuf);
	}
      else {
	m_MMGR_FREE_BUFR(pUsrbuf) ;
	}
    }
    else
    {
	m_MMGR_FREE_BUFR(pUsrbuf) ;
    }
  
  return(rcode);
}

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

		PROCEDURE NAME:    FRSFR_SendDataWait

		DESCRIPTION:

		This function is identical to FRSFR_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 open call) and
		a pointer to a user buffer containing the payload data. This function
		will prepend the frame relay header to the data.

		PARAMETERS:

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

		RETURNS:

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

		NOTES:

*****************************************************************************/
void FRSFR_SendDataWait(hCCB,pUsrbuf)
u_long hCCB;
USRBUF *pUsrbuf;
{
  CCB *pCCB;
  u_short rcode = FRS_INVALID;
  
  if(!m_FRS_CHECK) return FRS_DISABLED;
  
  if ((hCCB < gGlobalCfg.frsTotalPLLs) && 
      ((pCCB = frsCCBtable[hCCB]) != CCBNULL))
    {
      /* Make a Frame...
       */
      rcode = FRSMakeFrame(pCCB, pUsrbuf, PID_NONE);
      if ( rcode == FRS_SUCCESS )
	rcode = FRSLAPD_SendDataWait( hCCB, pUsrbuf);
    }


  return(rcode);
}

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

		PROCEDURE NAME:    FRSLAPD_SendDataWait

		DESCRIPTION:

		This function is similar to FRSFR_SendDataWait() except that it does not
		prepend the frame header as it passes data to the low-level driver.

		PARAMETERS:

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

		RETURNS:

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

		NOTES:

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

u_short
FRSLAPD_SendDataWait(hCCB,pUsrbuf)
u_long hCCB;
USRBUF *pUsrbuf;
{
  u_short rcode;
  CCB *pCCB;

  if(!m_FRS_CHECK) return FRS_DISABLED;

  if ((rcode = FRSLAPD_SendData(hCCB, pUsrbuf)) == FRS_SUCCESS)
    {
      /* If frame was transmitted, mark CCB and go blocking...
       * This process will remain blocked until the transmit
       * queue has been emptied.
       */
      pCCB = frsCCBtable[hCCB];
      m_FRS_MARK_CCB_XMT_BLOCKED(pCCB);
      pCCB->fWriteBlock(pCCB->uarg);
      
      /* We get here when the Transmit FSM has decided to wake
       * this process up...
       */
      m_FRS_MARK_CCB_XMT_UNBLOCKED( pCCB);
      
      rcode = FRS_SUCCESS;
    }

  return(rcode);
}
#endif /* USE_BLOCKED_IO */

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

	PROCEDURE NAME:    FRSLAPD_SendData

	DESCRIPTION:

	This function is similar to FRSFR_SendData() except that it does not prepend
	the frame header as it passes data to the low-level driver.

	PARAMETERS:

	u_short --- PLL handle
	USRBUF * --- Ptr to a user buffer

	RETURNS:

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

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

u_short
FRSLAPD_SendData(hCCB,pUsrbuf)
u_long hCCB;
USRBUF *pUsrbuf;
{
  u_short rcode = FRS_BAD_CCB_HANDLE;
  u_short buf_count = 0;
  CCB *pCCB;
  LCB *pLCB;

  if(!m_FRS_CHECK) return FRS_DISABLED;

  if (hCCB < gGlobalCfg.frsTotalPLLs && 
      ((pCCB = frsCCBtable[hCCB]) != CCBNULL))
    {
      pLCB = pCCB->plcb;
      if (pLCB->State == FRS_LKDWN)
	rcode = FRS_LINK_DOWN;
      else
	if (m_FRS_IS_CCB_UP( pCCB))
	  {
/* We are going to try our hand at backflowing */
#if 0
	    /* If injection mode is BUFFERED, check Q depth to avoid
	       buffer depletion. If Q cannot take entire frame, drop
	       the frame and return error.
	       */
	    if ( m_FRS_IS_INJ_MGR_BUFFERED(pCCB->plcb))
	      {
		buf_count = FRSCountUsrbufs(pUsrbuf);
		if( pCCB->Write_Queue.qcnt + buf_count >= MAX_Q_DEPTH )
		  {
		    pCCB->my_inj_stats.InjFramesDropped++;
		    pCCB->my_inj_stats.InjCharsDropped +=
		      (u_int) m_MMGR_FRAME_LEN(pUsrbuf);
		    if( pCCB->TxDoneFunc == NULL )
		      m_MMGR_FREE_BUFR(pUsrbuf);
		    else
		      pCCB->TxDoneFunc(pCCB, pUsrbuf, FRS_TX_FRAME_DROPPED);

		    return FRS_TX_FRAME_DROPPED;
		  }
	      }
#endif

	    /* Update statistics...if necessary
	     */
	    if (m_FRS_IS_STATS_ENBL(pLCB))
	      FRSUpdateStats(pCCB,pUsrbuf,FRS_DIRECTION_OUTBOUND);

	    FRSQTEnqueue(pUsrbuf,&(pCCB->Write_Queue));

	    /* Check queue depth and invoke flow control if needed */
	    if (pCCB->Write_Queue.qcnt >= MAX_Q_DEPTH) {
		m_FRS_MARK_CCB_XMT_BLOCKED(pCCB) ;
	    }

	    /* No need to generate events...  Invoke injection function...
	     */
	    return (FRSTxFrameEvt(pCCB));
	  }
	else
	  rcode = FRS_PLL_DOWN;
    }
  

  if(rcode != FRS_SUCCESS) {
	m_MMGR_FREE_BUFR(pUsrbuf) ;
	}

  return(rcode);
}

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

		PROCEDURE NAME:    FRSFR_GetWriteQueueDepth

		DESCRIPTION:

		This function returns the number of frames and bytes in a PLLs
		Write_Queue ( pending injection ) in user supplied variables.

		PARAMETERS:

		u_short --- PLL handle.
		u_short*--- address to return frame count
		u_long* --- address to return byte count

		RETURNS:

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

		NOTES:
****************************************************************************/
u_short
FRSFR_GetWriteQueueDepth(hCCB,pFrameCnt,pByteCnt)
u_long hCCB;
u_long *pFrameCnt;
u_long  *pByteCnt;
{
  CCB *pCCB;
  USRBUF *pFrame;
  
  if(!m_FRS_CHECK) return FRS_DISABLED;

  pCCB = frsCCBtable[hCCB];
  
  *pByteCnt = 0L;
  
  m_START_CRITICAL;
  
  if (*pFrameCnt = pCCB->Write_Queue.qcnt)
    {
      pFrame = pCCB->Write_Queue.Q.qtop;
      while ( pFrame != (USRBUF *)0 )
	{
	  *pByteCnt += m_MMGR_DATA_LEN(pFrame);
#ifndef STREAM_MESSAGES

	  pFrame = (USRBUF *)pFrame->next;
#else
	  pFrame = (USRBUF *)pFrame->b_next ;
#endif
	}
    }

  m_END_CRITICAL;

  return(FRS_SUCCESS);
}

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

		PROCEDURE NAME:    FRSTxFrameEvt

		DESCRIPTION:

		This function generates an FRS_FRAME event and wakes up the transmit
		process for the specified link.

		PARAMETERS:

		struct ccb * --- Pointer to a CCB

		RETURNS:         Nothing

		NOTES:

*****************************************************************************/
u_short
FRSTxFrameEvt( pCCB)
CCB *pCCB;
{

  EVENT *pEvent;

  if ((pEvent = FRSAllocEvent()) != (EVENT *) NULL)
    {
      pEvent->EventCode = FRS_FRAME;
      pEvent->Info = (void *)pCCB;
      
      /* Wakeup the link's transmit process...
       */

#if USE_TASKING

      os_queue_wakeup(&pCCB->plcb->TQTCB, pEvent);

#else

      FRSTransmit(pCCB->plcb, pEvent);

#endif
      return(FRS_SUCCESS);
    }
  else {
    return(FRS_NO_BUFFERS);
}
}
/**/
/*****************************************************************************

	PROCEDURE NAME:    FRSInject

	DESCRIPTION:

	This procedure is responsible for actually transfering outbound data from
	the Write Q to the Injection Q after the necessary verifications of the
	frame relay service Committed Burst Size (Bc) restrictions.
	The low-level driver effecting the actual Xmission is required to take data 
	off the Injection Q and Xmit it.

	PARAMETERS:

	CCB * --- Pointer to a CCB
	USRBUF * --- Pointer to buffer to be Xmitted to the network
	u_int --- byte count --> REMOVED

	RETURNS:         Nothing

	NOTES:

	This function is invoked from within FRSInjectNormal() or FRSInjectExcess()
	if appropriate conditions are met. Do not invoke this function directly, as
	it may lead to violation of network service subscription agreements.

*****************************************************************************/
int 
FRSInject( pCCB, pUsrbuf)
CCB *pCCB;
USRBUF *pUsrbuf;
{
  struct fr_hdr *pFrame;
  u_short rcode;
  LCB *pLCB = pCCB->plcb;
		
  if (pUsrbuf == FRSQT_Peek(pCCB->Write_Queue))
    {
      FRSQTDequeue(&pCCB->Write_Queue);

      /* Set the frame header...
       */
      pFrame = m_MMGR_DATA(pUsrbuf, struct fr_hdr *);
      *pFrame = pCCB->my_header;

      /* Send the buffer downstack...
       */
      if ((rcode = FRSDRV_SendData( pCCB, pUsrbuf)) != FRS_SUCCESS)
	{
	  (void) FRSTxDone(pUsrbuf, pLCB, rcode);
	} 
      return(FRS_SUCCESS);
    }
  else
    return(FRS_INJECT_FAILED);
}

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

	PROCEDURE NAME:    FRSInjectForce

	DESCRIPTION:

	This procedure is invoked indirectly by various Xmitting agents within the
	FRS with the intent of injecting data into the network. This function gets
	invoked when the Injection Management feature is disabled. It unconditionally
	injects the frame into the driver's queue.

	The low-level driver effecting the actual Xmission is required to take data
	off the Injection Q and Xmit it.

	PARAMETERS:

	CCB * --- Pointer to a CCB

	RETURNS:         Nothing

	NOTES:

*****************************************************************************/
void
FRSInjectForce(pCCB)
     CCB *pCCB;
{
  USRBUF *pUsrbuf;
  u_int  bytes_to_inject;
  register frs_time Now;
  register u_int    bytes_written_in_tc;
  
  /*
   *  Ensure link is up...
   *  There is no point in flooding the injection_Q if the link is
   *  down.
   */
  if (pCCB->plcb->State == FRS_LKDWN)
    {
      while ((pUsrbuf = FRSQTDequeue(&pCCB->Write_Queue)) != BNULL)
	{
	  (void) FRSTxDone(pUsrbuf, pCCB->plcb, FRS_LINK_DOWN);
	}
      return;
    }
  
  Now = sys_get_sys_uptime();
  bytes_written_in_tc = pCCB->my_cosb.bytes_written_in_tc;
  
  if (  (bytes_written_in_tc == 0)
      ||(mFRS_TcExpired(&pCCB->my_cosb, Now))
      )
    {
      bytes_written_in_tc = 0;  /* Start a new Tc period */
      pCCB->my_cosb.TcBegin = Now;
      pCCB->my_cosb.TcEnd = Now + pCCB->my_cosb.Tc;
    }

  /*
   * While any outbound frames in Write_Queue...
   */
  while ((pUsrbuf = FRSQT_Peek(pCCB->Write_Queue)) != BNULL)
    {
      /*
       * Inject unconditionally...
       */
      
      bytes_to_inject = (u_int) m_MMGR_DATA_LEN(pUsrbuf);
      if (FRSInject( pCCB, pUsrbuf) == FRS_SUCCESS)
	{
	  /*
	   * but keep track of Injection Management statistics...
	   */

	  bytes_written_in_tc += bytes_to_inject;

	  if (bytes_written_in_tc <= pCCB->my_cosb.bc_cur)
	    {
	      /* This frame is within CIR */
	      pCCB->my_inj_stats.InjNormalFrames++;
	      pCCB->my_inj_stats.InjNormalChars += bytes_to_inject;
	    }
	  else
	    {
	      pCCB->my_inj_stats.InjExcessFrames++;
	      pCCB->my_inj_stats.InjExcessChars += bytes_to_inject;
	    }
	}
    }

  pCCB->my_cosb.bytes_written_in_tc = bytes_written_in_tc;
  return;
}

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

	PROCEDURE NAME:    FRSInjectNormal

	DESCRIPTION:

	This procedure is invoked indirectly by various Xmitting agents within the
	FRS with the intent of injecting data into the network. This function gets
	invoked when the injection rate is maintained within the subscribed limit
	(Bc/Tc). If the limit is crossed, then a different injection function is
	invoked.

	The low-level driver effecting the actual Xmission is required to take data
	off the Injection Q and Xmit it.

	PARAMETERS:

	CCB * --- Pointer to a CCB

	RETURNS:         Nothing

	NOTES:

*****************************************************************************/
void
FRSInjectNormal(pCCB)
CCB *pCCB;
{

  USRBUF *pUsrbuf;
  u_int  bytes_to_inject;
  register frs_time Now;
  register u_int    bytes_written_in_tc;
  u_short StartingQCount ;

  /*
   *  Ensure link is up...
   *  There is no point in flooding the injection_Q if the link is
   *  down.
   */
  if (pCCB->plcb->State == FRS_LKDWN)
    {
      while ((pUsrbuf = FRSQTDequeue(&pCCB->Write_Queue)) != BNULL)
	{
	  (void) FRSTxDone(pUsrbuf, pCCB->plcb, FRS_LINK_DOWN);
	}
      return;
    }
  
  Now = sys_get_sys_uptime();

  if (  (pCCB->my_cosb.bytes_written_in_tc == 0)
      ||(mFRS_TcExpired(&pCCB->my_cosb, Now))
      )
    {
      pCCB->my_cosb.bytes_written_in_tc = 0;  /* Start a new Tc period */
      pCCB->my_cosb.TcBegin = Now;
      pCCB->my_cosb.TcEnd = Now + pCCB->my_cosb.Tc;
      mResetDE( pCCB);
    }

  /*
   * Mark the number of messages in the Write queue before
   * we attempt any injections
   */
  StartingQCount = pCCB->Write_Queue.qcnt ;
 
  /*
   * While any outbound frames in Write_Queue...
   */

  while ((pUsrbuf = FRSQT_Peek(pCCB->Write_Queue)) != BNULL)
    {
      /*
       * If injection rate not exceeded...
       */
      bytes_to_inject = (u_int) m_MMGR_FRAME_LEN(pUsrbuf);

      bytes_written_in_tc = (pCCB->my_cosb.bytes_written_in_tc + 
			     bytes_to_inject);

      if (bytes_written_in_tc <= pCCB->my_cosb.bc_cur)
	{
	  /* This frame is within CIR */

	  if (FRSInject( pCCB, pUsrbuf) == FRS_SUCCESS)
	    {
	      pCCB->my_cosb.bytes_written_in_tc = bytes_written_in_tc;
	      pCCB->my_inj_stats.InjNormalFrames++;
	      pCCB->my_inj_stats.InjNormalChars += bytes_to_inject;
	    }
	}
      else if (bytes_written_in_tc <= pCCB->my_cosb.be_bc_cur)
	{
	  /* The frame didn't fit in CIR... but it can fit in 
	   * as excess burst. 
	   */
	    
	  mSetDE( pCCB);
	    
	  if (FRSInject( pCCB, pUsrbuf) == FRS_SUCCESS)
	    {
	      pCCB->my_cosb.bytes_written_in_tc = bytes_written_in_tc;
	      pCCB->my_inj_stats.InjExcessFrames++;
	      pCCB->my_inj_stats.InjExcessChars += bytes_to_inject;
	    }
	}
      else if (m_FRS_IS_INJ_MGR_STANDARD(pCCB->plcb))
	{
	  /* Beyond Excess Rate and not queueing... */
	  FRSQTDequeue(&pCCB->Write_Queue);  /* guaranteed to be non-NULL */
	  pCCB->my_inj_stats.InjFramesDropped++;
	  pCCB->my_inj_stats.InjCharsDropped += m_MMGR_FRAME_LEN(pUsrbuf);
	      
	  (void) FRSTxDone(pUsrbuf, pCCB->plcb, FRS_TX_FRAME_DROPPED);
	}
      else
	{
	  /* Switch to hold mode...
	   */

	  pCCB->InjectionFunc = FRSInjectHold;
	  pCCB->my_inj_stats.InjHoldFrames++;
	  pCCB->my_inj_stats.InjHoldChars += bytes_to_inject;
	    
	  /* Start the timer.  The timer should be the
	   *  time until the Tc period expires.
	   */

	  FRSTMR_start(pCCB->InjectionTimerId, 
		       (u_short)(pCCB->my_cosb.TcEnd - Now));
	  break;  /* out of the 'while' */
	}
    }

  /*
   * Back enable check!!!
   *
   * If the queue count passed over the low-water mark, back-enable everyone!
   */
  if ((StartingQCount > Q_LOW_WATER_MARK) &&
  (pCCB->Write_Queue.qcnt <= Q_LOW_WATER_MARK)) {
  	int i;

	/* Unset flow control bits first */
	m_FRS_MARK_CCB_XMT_UNBLOCKED(pCCB) ;

  	if (pCCB->rcvd_q) qenable(WR(pCCB->rcvd_q)) ;

  	if (pCCB->encap) {
  		for(i = 0; i < MAX_PROTOCOL; i++) {
  			if (pCCB->encap->mp[i])
  				qenable(WR(pCCB->encap->mp[i]->rcvd_q)) ;
  		}
  	}
  }

  return;
}

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

	PROCEDURE NAME:    FRSInjectHold

	DESCRIPTION:

	This procedure is invoked indirectly by various Xmitting agents within the
	FRS with the intent of injecting data into the network. This function gets
	invoked when the injection rate has crossed the basic and the excess burst
	size thresholds agreed to at service subscription time. In this 'state',
	this function will effectively deny the injection request by doing nothing.
	At the end of the current Tc, the injection function pointer is reset to
	point to FRSInjectNormal to make a fresh beginning with a new Tc period.

	PARAMETERS:

	CCB * --- Pointer to a CCB

	RETURNS:         Nothing

	NOTES:

*****************************************************************************/
void
FRSInjectHold( pCCB)
CCB *pCCB;
{
  USRBUF *pUsrbuf;

  /* The situation is such that we can't inject this frame until
   * some time passes (and the timer has already been set)
   * so we just update the hold stats.
   */
  pUsrbuf = pCCB->Write_Queue.Q.qbot;
  pCCB->my_inj_stats.InjHoldFrames++;

  pCCB->my_inj_stats.InjHoldChars += (u_int)m_MMGR_DATA_LEN(pUsrbuf);

  if(pUsrbuf->b_cont)
  	pCCB->my_inj_stats.InjHoldChars += (u_int)m_MMGR_DATA_LEN(pUsrbuf->b_cont);

  return;
} 

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

	PROCEDURE NAME:    FRSInjectTimeout

	DESCRIPTION:

	This function is the callback function invoked upon completion of the Tc
	measurement period.

	PARAMETERS:

	CCB * --- Pointer to a CCB

	RETURNS:         Nothing

	NOTES:

*****************************************************************************/
void
FRSInjectTimeout(hCCB)
u_long hCCB;
{
  CCB *pCCB = frsCCBtable[hCCB];
  
  /* Stop the timer...
   */
  FRSTMR_stop(pCCB->InjectionTimerId);

  if(m_FRS_IS_INJ_MGR_FORCED(pCCB->plcb))
    pCCB->InjectionFunc = FRSInjectForce;
  else
    /** This injection function is used for
     ** BUFFERED and STANDARD schemes.
     **/
    pCCB->InjectionFunc = FRSInjectNormal;

  pCCB->InjectionFunc(pCCB);
  return;
}

/**/
/***********************************************************************
 *
 *  FRSTransmit process.
 *
 ***********************************************************************/

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

		PROCEDURE NAME:    FRSTransmit

		DESCRIPTION:

		This procedure, constituting the top-level of an FRS transmit process, waits
		for events to occur. It invokes the target-system function os_queue_wait
		to block until new events occur and expects the target system to
		reschedule this process until that time. As new events occur, the frs
		receiver is cycled and appropriate state routines are invoked.

		Events are generated by the API calls as and when data is enqueued by user
		processes for transmission.

		PARAMETERS:

		int --- task instance identifying the link being serviced

		RETURNS:  Nothing

		NOTES:

		Never return from this function.
		Caller is advised that should this function return:

		(a) Something truly wierd has happened, and,
		(b) To restart the frs receiver, call this function again after appropriate
		error diagnostics and attendant cleanup.

*****************************************************************************/
#if USE_TASKING

void
FRSTransmit(task_inst)
u_int task_inst;
{
  EVENT *pEvent;
  LCB *pLCB = frsLCBtable[task_inst];
  while ( TRUE )
    {
      os_queue_wait(&pLCB->TQTCB, &pEvent);
      pLCB->State = frsTxState( pLCB, pEvent );
      FRSFreeEvent(pEvent);
    } 
}

#else

void
FRSTransmit(pLCB,pEvent)
LCB *pLCB;
EVENT *pEvent;
{
  pLCB->State = frsTxState( pLCB, pEvent );
  FRSFreeEvent(pEvent);
  return;
}
#endif /* USE_TASKING */


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

		PROCEDURE NAME:    frsTxState

		DESCRIPTION:

		This internal function is called to dispatch a state/event processing.
		
		PARAMETERS:
	LCB * - pointer to the LCB that received the event
	EVENT * - point to an event structure.

		RETURNS:  the next state of the link (LCB)

		NOTES:

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

FRSstate
frsTxState( pLCB, pEvent )
LCB * pLCB;
EVENT *pEvent;
{
  FRSstate next_state;

  /** This should never be the case....
   **/
  if( pLCB == (LCB *) NULL )
    return(FRS_LKDWN);
  else
    next_state = pLCB->State;  /* set the next state to the current one */
  
  /** What state is the link in now ?
   **/
  switch( pLCB->State )
    {

      /** ================================================================ **/
      /** Link is up...                                                    **/
      /** ================================================================ **/
    case FRS_LKUP:
      
      /** Which event did we get ?
       **/
      switch( pEvent->EventCode )
	{
	default:
	  /** Link is up / link is up **/
	case FRS_LKUP:
	  /* no change in state */
	  break;
	  
	  /** Link is up / link went down **/
	case FRS_LKDWN:
	  next_state = frsTx_LkUp_LkDwn(pLCB);
	  break;
	  
	  /** Link is up / frame transmit start */
	case FRS_FRAME:
	  next_state = frsTx_LkUp_Frame(pEvent);
	  break;

	  /** Link is up / frame transmit complete
	   **/
	case FRS_TXDN:
	  next_state = frsTx_LkUp_TxDone(pLCB, pEvent);
	  break;
	}
      break;

      /** ================================================================ **/
      /** Link is down...                                                  **/
      /** ================================================================ **/
    case FRS_LKDWN:
      
      /** Which event did we get ?
       **/
      switch( pEvent->EventCode )
	{
	  /** Link is down / link is down **/
	default:
	case FRS_LKDWN:
	  /* no change in state */
	  break;
	  
	  /** Link is down / frame transmit start */
	case FRS_FRAME:
	  /* no change in state */
	  break;
	  
	  /** Link is down / link is up **/
	case FRS_LKUP:
	  next_state = frsTx_LkDwn_LkUp(pLCB);
	  break;

	  /** Link is down / frame transmit complete
	   **/
	case FRS_TXDN:
	  next_state = frsTx_LkDwn_TxDone(pLCB, pEvent);
	  break;
	}
      break;

    default:
      break;
    }

  return( next_state );
}

/**/
/**************************************************************************
 * This internal routine is called whenever the current state of the link
 * is FRS_LKDWN and the transmitter event is FRS_LKUP
 *
 **************************************************************************/

FRSstate
frsTx_LkDwn_LkUp(pLCB)
LCB *pLCB;
{
  CCB *pCCB;
  
  /* Inform target system about link up...
   */
  if (  !m_FRS_IS_ANY_LMI_CFG( pLCB) && !m_FRS_IS_LINK_DCE( pLCB) )
    {
      sys_link_up( pLCB);
      pLCB->link_uptime = sys_get_sys_uptime();
    }

  /* Link just came up. Transmit any residual data from all the Write
   * queues...
   */
  for (pCCB = pLCB->ccbList; pCCB; pCCB = pCCB->next_ccb)
    pCCB->InjectionFunc(pCCB);
  
  /** Send trap for Link down to up transition
   **/
  sys_trap_event(pLCB, CCBNULL, TRAP_TX_LINKUP);
  return ((FRSstate) FRS_LKUP);
}

/**/
/**************************************************************************
 * This internal routine is called whenever the current state of the link
 * is FRS_LKDWN and the transmitter event is FRS_TXDN
 *
 **************************************************************************/

FRSstate
frsTx_LkDwn_TxDone(pLCB, pEvent)
LCB *pLCB;
EVENT *pEvent;
{

  /* This event could occur if the link went down after a transmit event
   * was created by one of the FRSSend* API calls.
   */
  (void) FRSTxDone((USRBUF *) pEvent->Info, pLCB, FRS_LKDWN);
  return ((FRSstate)FRS_LKDWN);
}

/**/
/**************************************************************************
 * This internal routine is called whenever the current state of the link
 * is FRS_LKUP and the transmitter event is FRS_LKDWN
 *
 **************************************************************************/

FRSstate
frsTx_LkUp_LkDwn(pLCB)
LCB *pLCB;
{
  /* Inform target system about a link down situation...
   */
  sys_link_down( pLCB);
  sys_trap_event(pLCB, CCBNULL, TRAP_TX_LINKDOWN);
  return ((FRSstate) FRS_LKDWN);
}

/**/
/**************************************************************************
 * This internal routine is called whenever the current state of the link
 * is FRS_LKUP and the transmitter event is FRS_FRAME
 *
 **************************************************************************/

FRSstate
frsTx_LkUp_Frame(pEvent)
EVENT *pEvent;
{
  CCB *pCCB = (CCB *)pEvent->Info;
  
  if (pCCB != CCBNULL)
    pCCB->InjectionFunc(pCCB);
  
  return ((FRSstate) FRS_LKUP);
}

/**/
/**************************************************************************
 * This internal routine is called whenever the current state of the link
 * is FRS_LKUP and the transmitter event is FRS_TXDN
 *
 **************************************************************************/

FRSstate
frsTx_LkUp_TxDone(pLCB, pEvent)
LCB *pLCB;
EVENT *pEvent;
{
  (void) FRSTxDone((USRBUF *) pEvent->Info, pLCB, FRS_SUCCESS);
  return ((FRSstate) FRS_LKUP);
}

