#ident "$Id: frstargt.c,v 1.18 1997/10/21 20:48:11 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
  06-08-92 1.20A    H&J (DS)      A few new callbacks have been added.
                                  Dropped sys_load_frame_relay_cfg().
                                  Moved FRSInitFrs() from frsutil.c.
  10-16-92 2.00A   H&J (DJ)       Add FRS_RCV_NOTIFY option.
  09-28-93 2.00H   H&J (GB)       Added sys_ins_oui, sys_oui_proc
           Other   H&J            See H&J bug reports

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

  Revision 1.17  1997/09/30 16:05:57  gary
  API now operates in either endian mode.

  Revision 1.16  1997/09/23 16:38:13  gary
  The Frame Relay and X.25 API's consist of 32-bit fields only.  This is
  to simplify structure alignment considerations, and make endian issues
  less cumbersome.

  In addition, the Frame Relay API now uses SNID's instead of links, thereby
  making it more compatible with the X.25 and ISDN API's.

  These changes span a large number of files.

  Revision 1.15  1997/08/05 15:01:35  gary
  Modifications for the Clydesdale (POWERPC) platform.

  Revision 1.14  1997/01/16 22:08:01  gary
  Removed FRAME_STAGE counter from code.

  Revision 1.13  1996/11/06 16:11:12  gary
  FRF.9 was not renegotiating when the circuit made a transition from
  inactive to active due to LMI activity.

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

  Revision 1.11  1996/10/22 21:18:59  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.10  1996/06/21 16:36:38  gary
  Next stage of FRF.9 implementation complete.  The appropriate functions
  have been inserted into the transmit and receive path, but are not
  active yet.  Development is being put on hold until a more pressing
  issue (flow control) is dealt with.

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

  Revision 1.8  1995/12/06 17:03:33  gary
  All physical device locations are now defined in device.h.
  Also introduced "anti-clock-wraparound" code.

  Revision 1.7  1995/11/07 22:27:16  gary
  The FR sys_get_sys_uptime() function is now defined in terms of HZ.

  Revision 1.6  1995/07/26 10:23:29  garyp
  receive data structure is named differently now

 * Revision 1.10  1994/07/27  20:27:41  stavrosm
 * Release 2.2
 *
 * Revision 1.9  1994/03/16  21:51:38  deepak
 * Changed FRSCONF_InitLink() call, and, usage of cfg structs reflecting
 * changes in the config database.
 *
 * Revision 1.8  1993/12/06  12:29:15  stavrosm
 * changes for support of more than one card per PC
 * changes will be included in rel 2.1
 *
 * Revision 1.7  93/11/05  10:36:52  stavrosm
 * Release 2.1
 * 
..............................................................................

  DESCRIPTION:

  This source module contains all standard integration functions required by
  the H&J FRS portable subsystem.  For each target system, the code provided
  in these functions will serve to integrate the FRS subsystem with the
  target system environment. In addition to the functions defined here, there
  are several integration macros defined in frssys.h which define in-line
  code fragments which also integrate the FRS with the target system
  environment. Macros are used wherever execution speed was considered to be
    of paramount importance. The integrator may opt to replace these calls
  with the actual system calls wherever these functions are called out. 

  Each function with its calling syntax and functionality requirements is
  described.
.............................................................................

    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:

os_task_create ....... Primitive to launch a target system process.
os_queue_wait ........ Primitive to block a process on a queue.
os_queue_sleep ....... Primitive to block a process on a queue.
os_queue_wakeup ...... Primitive to Unblock a process waiting on a queue.
os_queue_unsleep ..... Primitive to Unblock the process waiting on a queue.
sys_pll_rcvd_frame ... Primitive to inform the user process when a
                        complete frame has arrived on a PLL.
sys_throttle_ccb ..... Primitive to inform the user process to throttle
                        back or throttle forward outbound traffic on a PLL.
sys_link_down ........ Primitives to declare a link down.
sys_link_up .......... Primitives to declare a link up.
sys_add_pll .......... Primitives to add a pll to the link (call from lmi).
sys_hdt_ccb .......... Primitives to halt data traffic on a CCB.
sys_sdt_ccb .......... Primitives to start (resume) data traffic on a CCB.
sys_pvc_stat ......... Primitives to decide whether a given CCB is truly
                        up or not.
sys_update_config..... Called from frscfg.c when the configuration has changed
sys_get_sys_uptime ... Primitives to retrieve elapsed time since system startup.
sys_alloc_pkt ........ Primitive to allocate a frame relay packet w/payload area
sys_free_pkt ......... Primitive to free a frame relay packet and payload buffer
sys_trap_event ....... Primitive to process trap events.
sys_send_cmi ......... Primitive to request DXE to send a CMI packet to
                        a FRS on a remote node.
InitFrs .............. Performs initialization of run-time frame relay
                        environment.
sys_ins_oui........... Insert a proprietary OUI in the SNAP HEADER of an 
                        outbound frame.
sys_oui_proc.......... Verify/validate a proprietary OUI contained in an 
                        inbound frame header.

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


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



#define FRSTARGT_C

/** Include files....
 **/


#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 "param.h"
#include "strlog.h"
#include "system.h"
#include "timer.h"
#if EPC
#include "wan_proto.h"
#endif
#include "x25trace.h"
#include "fr_cont.h"
#include "fr.h"
#include "frs.h"
#include "frssys.h"
#include "cnt_arry.h"
#include "lzs.h"

extern int fr_ctrl_count;
extern int fr_ctrl_max;
extern int fr_ctrl_event();
extern mblk_t *allocb();

extern queue_t *fr_ctrl_q ;

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

    PROCEDURE NAME:    os_task_create

    DESCRIPTION:

    Primitive to launch a target system process...

    PARAMETERS:

    u_int --- TASK ID
    u_int --- TASK INSTANCE
    u_int --- TASK PRIORITY
    TASK_FUNC_PTR --- TASK ENTRY POINT
    u_char * --- TASK STACK TOP
    u_int --- TASK STACK SIZE

    RETURNS:
        To be determined during portation.

    NOTES:

    Notion of a process: A process is merely an execution thread with a context.
    The context includes a stack, a PID, and a one-way entry point. The process
    must be able to block itself until deblocking conditions are met. Typically,
    a process may block itself until further data is available in a Queue. As
    soon as data is deposited into the queue, the blocked process may be
    scheduled to execute.
*****************************************************************************/

void
os_task_create(task_id,
               task_inst,
               task_prio,
               task_entry,
               task_sttop,
               task_stsize)
u_int task_id;
u_int task_inst;
u_int task_prio;
TASK_FUNC_PTR task_entry;
u_char *task_sttop;
u_int task_stsize;
{

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert target system function call to create a task here.      **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    return;
}

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

    PROCEDURE NAME:    os_queue_wait

    DESCRIPTION:

    Primitive to block a process on a queue...

    PARAMETERS:

    QTCB * --- The Q to wait on
    EVENT ** --- Address of a pointer to an event buffer

    RETURNS:
        To be determined during portation

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

void
os_queue_wait(Q, pEvent)
QTCB *Q;
EVENT **pEvent;
{

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert target system function call to suspend a task on an     **/
    /** event queue here.                                              **/

    /** When event(s) enqueued (via os_queue_wakeup(), task should     **/
    /** be rescheduled, and event dequeued.                            **/

    /** If event(s) in queue when this function is called, target      **/
    /** system may dequeue an event and return without suspending      **/
    /** OR suspend and immediately re-schedule for later execution.    **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    return;
}

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

    PROCEDURE NAME:    os_queue_sleep

    DESCRIPTION:

    Primitive to block a process on a queue...

    os_queue_sleep() is a variant of os_queue_wait() which simply puts the
    current task to sleep on a specified QTCB and DOES NOT dequeue an item
    when rescheduled.  This variant would be used in cases where the item
    will be dequeued by the caller after it is rescheduled.

    PARAMETERS:

    QTCB *    --- The Q to wait on

    RETURNS:
        Nothing.

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

void
os_queue_sleep(Q)
QTCB *Q;
{

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert target system function call to suspend a task on an     **/
    /** event queue here.                                              **/

    /** When event(s) enqueued (via os_queue_wakeup(), task should     **/
    /** be rescheduled, but no event dequeued (will be dequeued by     **/
    /** calling task).                                                 **/

    /** If event(s) in queue when this function is called, target      **/
    /** system may return without suspending                           **/
    /** OR suspend and immediately re-schedule for later execution.    **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    return;
}

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

    PROCEDURE NAME:    os_queue_wakeup

    DESCRIPTION:

    Primitive to Unblock a process waiting on a queue...

    PARAMETERS:

    QTCB * --- The Q a process is waiting on
    EVENT * --- Pointer to an event buffer

    RETURNS:
        To be determined during portation

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

void
os_queue_wakeup( Q, pEvent)
QTCB *Q;
EVENT *pEvent;
{
    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert target system function call to enqueue an event to      **/
    /** an event queue, (and if task is suspended, re-schedule it)     **/
    /** here.                                                          **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    return;
}

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

    PROCEDURE NAME:    os_queue_unsleep

    DESCRIPTION:

    Primitive to Unblock the process waiting on a queue...

  os_queue_unsleep() is a variant of os_queue_wakeup() which simply wakes
  a task sleeping on a specified QTCB and DOES NOT enqueue an item
  beforehand.  This variant would be used in cases where the item
  has been enqueued by the caller before calling this function.

    PARAMETERS:

    QTCB * --- The Q a process is waiting on

    RETURNS:
        Nothing.

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

void
os_queue_unsleep( Q)
QTCB *Q;
{

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert target system function call to re-schedule a task if    **/
    /** suspended on an event queue without enqueueing an event        **/
    /** here. (Caller has enqueued the event).                         **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    return;
}

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

  PROCEDURE NAME:   os_ditto_bfr 

  DESCRIPTION:

  Make a duplicate of a USRBUF

  PARAMETERS:

  USRBUF * -- pointer to buffer to be duplicated

  RETURNS:         Pointer to duplicate

  NOTES:
    This routine DOES NOT duplicate an entire chain of USRBUF structures,
    only the USRBUF pointed to by pBuf. Note that pointer values to 'link' and
    'next' are not copied.

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

USRBUF 
*os_ditto_bfr(pBuf)
USRBUF *pBuf;
{
    USRBUF *pDup = BNULL;
    u_char *pDp, *pBp;
    int nsize;

    if( pBuf != BNULL )
        {
        nsize = m_MMGR_DATA_LEN(pBuf);
        pDup = m_MMGR_ALLOC_BUFR(nsize);

        if( pDup != BNULL )
            {
#ifndef	STREAM_MESSAGES

            m_MMGR_DATA_LEN(pDup) = m_MMGR_DATA_LEN(pBuf);
            pDp = m_MMGR_DATA(pDup, u_char*);
            pBp = m_MMGR_DATA(pBuf, u_char*);
#else
	    bcopy(pBuf->b_rptr, pDup->b_wptr, nsize) ;
	    pDup->b_wptr += nsize ;
#endif
   /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert target system function call to copy binary data here    **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/
            }
        }

    return (pDup);
}

/**/

#if USE_RECEIVE_NOTIFY

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

  PROCEDURE NAME:    sys_pll_rcvd_frame

  DESCRIPTION:

  Primitive to inform the user process when a complete frame has
  arrived on a PLL.
    Called in the context of the FRSReceive state machine if the
    software was compiled with the USE_RECEIVE_NOTIFY option set.

    This routine may be used to signal another task, or perform
    other appropriate actions.

  PARAMETERS:

  struct ccb * --- Pointer to a ccb

  RETURNS:
     Nothing

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

void 
sys_pll_rcvd_frame( pCCB)
CCB * pCCB;
{
    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert target system processing to handle an inbound frame on  **/
    /** a PLL.  May receive immediately via FRSFR_RcvData() API call,  **/
    /** or schedule an asynchronous process to receive it.             **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

	USRBUF *pUsrbuf ;
	mblk_t *mp ;
	mblk_t *up ;
	FRP_FRSFR_RcvData_t *frp ;
	FR_UPWARD_MSG_t *fr_up ;	
	u_short rcode ;
	u_short index ;
	queue_t	*uprq ;

    for(;;) {
	rcode = FRSFR_RcvData(pCCB->hCCB, &pUsrbuf) ;

	/*
	 * Is there a valid buffer
	 */
	
	if(((rcode & FRS_RCODE_MASK) != FRS_COMPLETE_FRAME) ||
	   (pUsrbuf == (USRBUF *)NULL))
		return ;

cnt_arry[FRAME_SYS_PLL_VALID]++;
	/*
	 * Find the Protocol ID of the Payload
	 * Then find the queue to send it up on
  	 */

	index = rcode >> FRS_RC_PROT_SHIFT ;

	if(index == PID_NONE)
		uprq = pCCB->rcvd_q ; 
	else
	{
		if(pCCB->encap->mp[index] == (FRS_PROTOCOL_ENTRY *)0) 
		{
cnt_arry[FRAME_RDROP]++;
			m_MMGR_FREE_BUFR(pUsrbuf) ;
			return ;
		}

		uprq = pCCB->encap->mp[index]->rcvd_q ;
	}

	/* 
	 * Check if the there is a valid queue for this DLCI
	 */
	
	if(uprq == (queue_t *)0)
	{
		m_MMGR_FREE_BUFR(pUsrbuf) ;
cnt_arry[FRAME_RDROP]++;
		return ;	
	}

	/*
	 * Create a M_PROTO Message for the payload 
	 */

	if((mp = allocb(sizeof(FRP_FRSFR_RcvData_t), BPRI_MED)) == (USRBUF *)NULL)
	{
		m_MMGR_FREE_BUFR(pUsrbuf) ;
		/* 
		 * Keep a statistic here 
		 */
cnt_arry[FRAME_RDROP]++;
		return ;
	}

	mp->b_datap->db_type = M_PROTO;
	mp->b_cont = pUsrbuf ;
		
	/*  
	 * Fill in M_PROTO header 
	 */	
	frp = (FRP_FRSFR_RcvData_t *)mp->b_wptr ;

	frp->command = FRP_FRSFR_RcvData ;	
	frp->snid = pLCB_to_snid(pCCB->plcb) ; 
	frp->dlci = pCCB->dlci ;
 	frp->protocol_id = index ;
	
	mp->b_wptr += sizeof(FRP_FRSFR_RcvData_t);

#if ENDIAN_SWAP
	SWAP_THIS(frp, sizeof(FRP_FRSFR_RcvData_t)) ;
#endif
	/* Find out which queue this goes up
	 * The registration from the Open should fill in the
	 * type of circuit it is. If Encapsulated, then the 
	 * protocol ID determines which queue.
	 */  

	if((!fr_ctrl_q->q_first) && canput(uprq->q_next)) {
		putnext(uprq, mp);
cnt_arry[FRAME_RPUT]++;
	}
	else
	{
		/*
		 * If the message can't go straight up, the queue it on the
		 * upper read side service queue of the control stream.
		 */
	
		/* The idea is to create a message block that contains the
		 * information for what the real queue is that this message
		 * is suppose to go up. The real message is attached via
		 * b_cont. The read side service routine will try to move
		 * it upwards. If it can't in a "reasonable" metric, it
		 * will start to throw them away.
		 * 
		 * The concept of signatures is important here. It is possible
		 * that items are placed on the control stream queue, and the
		 * destination queue is closed. Then that queue is reopened,
		 * but those messages really are no longer destined for that
		 * stream. To prevent this each open on the queue has a 
		 * unique signature to distinquish it from previous opens.
		 */
 
		if((fr_ctrl_q == (queue_t *) 0) ||
		((up = allocb(sizeof(FR_UPWARD_MSG_t), BPRI_MED)) == (mblk_t *)0))
		{
			/* 
			 * Not able to queue this guy.
			 */
cnt_arry[FRAME_RDROP]++;
			 freemsg(mp) ;
			 return ;
		}

		fr_up = (FR_UPWARD_MSG_t *)up->b_wptr ;

		fr_up->signature = ((frdev_t *)uprq->q_ptr)->signature ;
		fr_up->queue = uprq ;

		fr_up->time_future = sys_get_sys_uptime() + MSG_TIME_TO_LIVE ;

		up->b_wptr += sizeof(FR_UPWARD_MSG_t) ;
	
		up->b_cont = mp ;
		putq(fr_ctrl_q, up) ;
		if (++fr_ctrl_count > fr_ctrl_max)
			fr_ctrl_max = fr_ctrl_count;
cnt_arry[FRAME_SYS_PLL_CALLED]++;
		fr_ctrl_event((caddr_t) 0) ;
	}
    }
}
#endif /* USE_RECEIVE_NOTIFY */

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

  PROCEDURE NAME:    sys_throttle_ccb

  DESCRIPTION:

  Primitive to inform the user process to throttle back or throttle fwd data
  traffic (outbound) on a PLL.

  PARAMETERS:

  struct ccb * --- Pointer to a ccb
  u_char       --- TRUE => Throttle back (Flow-ctrl enabled)
                   FALSE => Throttle fwd (Flow-ctrl disabled)

  RETURNS:
     Nothing

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

void 
sys_throttle_ccb( pCCB, throttle_flag)
CCB * pCCB;
u_char throttle_flag;
{
    /* Increment the stats */
    if( throttle_flag == TRUE )
        pCCB->circuit_stats.total_fallbacks++;
    else
        pCCB->circuit_stats.total_fallforwards++;
    return;
}

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

  PROCEDURE NAME:    sys_link_down

  DESCRIPTION:

  Primitives to declare a link down...
    This function will cycle through all the CCBs for this link and mark
    the CCBs down. A change in status will trigger a call to sys_hdt_ccb()
    to inform each CCB about loss of link.

  PARAMETERS:

  struct lcb * --- Pointer to an lcb

    RETURNS:
        Nothing.

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

void
sys_link_down( pLCB)
LCB *pLCB;
{
    CCB *pCCB = pLCB->ccbList;
    u_short prev_stat;

    /* Inform all CCBs to hold-off on all Xmission...
    */
    while ( pCCB)
    {
        prev_stat = pCCB->ccb_status; /* Save old status */
        m_FRS_RESET_CCB_UP( pCCB);    /* Drop the status */

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

        pCCB = pCCB->next_ccb;
    }

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert any desired additional processing required by target    **/
    /** system in response to a "link down" event...                   **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    return;
}

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

  PROCEDURE NAME:    sys_add_pll

  DESCRIPTION:

  Primitive to request the user process to create a new PLL (called from LMI).

  PARAMETERS:

  LCB         * --- LCB pointer
  struct ccb ** --- Pointer to a ccb pointer
  u_short       --- dlci number to add

    RETURNS:
        Nothing.

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

void 
sys_add_pll( pLCB, ppCCB, dlci_no)
LCB         * pLCB;
CCB ** ppCCB;
u_short       dlci_no;
{
    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert target specific code here to open a new PLL on this link**/
    /** and perform any required initialization. For example:          **/
    /**                                                                **/
    /**        FRSFR_OpenPLL(pLCB, dlci_no, (BFN_PTR)0, &hpll);        **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    return;
}

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

  PROCEDURE NAME:    sys_link_up

  DESCRIPTION:

  Primitives to declare a link up...
    This function will cycle through all the CCBs for this link and mark
    the CCBs up. A change in status will trigger a call to sys_sdt_ccb()
    to inform each CCB about recovery of link.

  PARAMETERS:

  struct lcb * --- Pointer to an lcb

  RETURNS:
    Nothing.

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

void 
sys_link_up( pLCB)
LCB *pLCB;
{
    CCB *pCCB = pLCB->ccbList;
    u_short prev_stat;

    /* For an unmanaged link...
     */
    if ( !m_FRS_IS_ANY_LMI_CFG(pLCB) )
    {
        /* Inform all CCBs to begin Xmission...
         */
        while ( pCCB)
        {
            prev_stat = pCCB->ccb_status; /* Save old status */
            m_FRS_SET_CCB_UP( pCCB);      /* Assert the status */

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

            pCCB = pCCB->next_ccb;
        }
    }

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert any desired additional processing required by target    **/
    /** system in response to a "link up" event...                     **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    return;

}

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

  PROCEDURE NAME:    sys_hdt_ccb

  DESCRIPTION:

  Primitives to halt data traffic on a CCB...

  PARAMETERS:

  struct ccb * --- Pointer to a ccb

  RETURNS:
      Nothing.

  NOTES:

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

void 
sys_hdt_ccb( pCCB)
CCB *pCCB;
{
    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert any desired processing required by target system to     **/
    /** halt outbound data transfer on the specified PLL.              **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    StopFRF9OperationPhase(pCCB) ;

    return;
}

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

  PROCEDURE NAME:    sys_sdt_ccb

  DESCRIPTION:

  Primitives to start (resume) data traffic on a CCB...

  PARAMETERS:

  struct ccb * --- Pointer to a ccb

  RETURNS:
        Nothing.

  NOTES:

*****************************************************************************/
void 
sys_sdt_ccb( pCCB)
CCB *pCCB;
{
    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert any desired processing required by target system to     **/
    /** start outbound data transfer on the specified PLL.             **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    FRF9LaunchModeOne(pCCB->hCCB) ;

    return;
}

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

  PROCEDURE NAME:    sys_pvc_stat

  DESCRIPTION:

  Primitives to decide whether a given CCB is truly up or not.
  FOR DCEs only:

    This is decided by the verifying if this CCB's peer CCB is also up. For
    'local' endpoints, this function will always return (ACT | EXIST).

  PARAMETERS:

  struct ccb * --- Pointer to a ccb

  RETURNS:

    Combinations of FRS_LMI_STATUS_* defines.

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

u_char 
sys_pvc_stat( pCCB)
CCB *pCCB;
{
        /** -------------- TARGET SYSTEM CODE -------------------------**/

        /** Insert target system code to determine readiness of        **/
        /** specified channel to enter data transfer state.            **/

        /** The standard processing below should be invoked only if    **/
        /** target system is otherwise ready for data transfer...      **/

        /** To indicate NOT ready, call FRSLmiMarkCCB(pCCB,FALSE) and  **/
        /** skip to return statement at end of standard processing.    **/

        /** -------------- TARGET SYSTEM CODE -------------------------**/

#if FRS_DCE_SUPPORT

    /* For CCBs with local endpoints, always mark CCB up...
     */
    if ( m_FRS_IS_CCB_LOCAL_EP(pCCB) )
        FRSLmiMarkCCB( pCCB, TRUE);

    else if ( m_FRS_IS_CCB_LOCAL_SWITCH(pCCB) )
    {
        /* For Locally switched CCBs (i.e. switched inside FRS)...
        */

        /** If peer CCB's Link is Up, Mark them both Up...
        **/
        if ( pCCB->pFwd->plcb->State == FRS_LKUP )
        {
            FRSLmiMarkCCB( pCCB, TRUE);
            FRSLmiMarkCCB( pCCB->pFwd, TRUE);
        }
    }
    else /* _PLL_TERMINUS ( one end of PLL ) */
    {
        /* the CCB is examined and if in
         * the pending_up state (set by CMI), marked up. 
         */
        if ( m_FRS_IS_CCB_UP_PND(pCCB) )
            FRSLmiMarkCCB( pCCB, TRUE);
    }

#else /* DTE_ONLY... */

     /* For DTE Only, all CCB's are local endpoints by definition
     */
    FRSLmiMarkCCB( pCCB, (u_char)(pCCB->plcb->State == FRS_LKUP));

#endif

    return ((u_char)(m_FRS_IS_CCB_UP(pCCB)));
}

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

  PROCEDURE NAME:    sys_fwd_frame

  DESCRIPTION:

  Primitive to decide whether to forward a frame to its peer PLL in
    switching mode.

  PARAMETERS:

  USRBUF *        --- Pointer to USRBUF chain with frame to be forwarded
                         (frame still has dlci header)

  struct ccb * --- Pointer to receiving ccb
  struct ccb * --- Pointer to destination ccb

  RETURNS:    TRUE if frame is to be forwarded, FALSE if not.

  NOTES:      If FALSE is returned, it is this function's responsability
              to dispose of the frame (e.g. m_MMGR_FREE_BUFR() etc.)
*****************************************************************************/

u_short 
sys_fwd_frame(pBfr, pSrcCCB, pDestCCB)
USRBUF      *pBfr;
CCB *pSrcCCB;
CCB *pDestCCB;
{
    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert code here to make forwarding decision...                **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    return TRUE; /* This will cause the frame to be forwarded */
}

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

  PROCEDURE NAME:    sys_alloc_pkt

  DESCRIPTION:
    System primitive to allocate frame relay packets.

    The USRBUF structure (defined in frssys.h) contains variables that are used 
    to keep track of 'linked' and 'queued' USRBUF structures. It also includes
    the starting ('start') location within the user payload area of the user
    payload data, a 'count' indicating the number of characters in the payload
    data area, a void pointer ('extra') that may be used to keep track of target
    specific data associated with the USRBUF and a pointer ('payload') to the 
    payload data itself.

  PARAMETERS:
    size of the required payload data area.

  RETURNS:
    A pointer to a USRBUF structure or NULL if the allocation failed.

  NOTES:
    This routine should not be called directly. It should be called via
    the m_MMGR_ALLOC_BUFR macro defined in frssys.h. Other m_MMGR* macros
    can be used to manipulate other USRBUF variables. The macro m_MMGR_FREE_BUFR
    should be used to free the buffer.

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

USRBUF * 
sys_alloc_pkt(payload_size)
u_int payload_size;
{

#ifndef	STREAM_MESSAGES

USRBUF *c_pkt, *p_pkt;
u_int num_bufs;
u_int usr_avail;
u_short payload_length = PAYLOAD_BUF_SIZE;

    /** Determine the number of user available bytes contained within
     ** the payload area of each USRBUF.
     **/
    usr_avail = (payload_length - MIN_PAYLOAD_OFFSET);
     
    /** Determine the number of USRBUF structures that will be needed
     ** to give the caller a packet with a data area of 'payload_size'.
     **/
    num_bufs = ((payload_size + (usr_avail-1)) / usr_avail);
    c_pkt = p_pkt = (USRBUF *) 0;

    /** Start allocating packets....
     **/
    for(; num_bufs > 0; num_bufs--)
        {
        p_pkt = c_pkt;  /* remember the address of the current packet */

        /** Allocate the USRBUF structure...
         **/
        if(( c_pkt = m_MMGR_SPL_ALLOC(sizeof(USRBUF))) != (USRBUF *) 0)
            {
            c_pkt->next = (USRBUF *) 0; /* queuing pointer */
            c_pkt->link = p_pkt;        /* previous packet pointer */

#if !(USE_CONTIGUOUS_DATA) /* --------------------------------------- */

            /** Allocate memory for the payload area...
             **/
            if((c_pkt->payload = 
               (u_char *) m_MMGR_SPL_ALLOC(payload_length)) == (u_char *) 0)
                {
                /** No space left for payload, free any allocated packets
                 **/
                m_MMGR_SPL_FREE( c_pkt );
                if( p_pkt != (USRBUF *) 0 )
                    m_MMGR_FREE_BUFR( p_pkt );
                return( c_pkt = (USRBUF *) 0 );
                }

#endif /* -------------------------------------------------------------- */

            c_pkt->count = 0;                  /* payload data count */
            c_pkt->start = MIN_PAYLOAD_OFFSET; /* largest frame header */
            c_pkt->userbuf_extent = (void *) 0;
            c_pkt->first_frag = (void *)0;
            c_pkt->next_frag = (void *)0;
            c_pkt->code = FRS_IN_PROGRESS;
 
            /** ---------------- TARGET SYSTEM CODE -------------------------**/
            /**     additional code for allocation of 'extra' space, etc.    **/
            /**                                                              **/
            /** ---------------- TARGET SYSTEM CODE -------------------------**/

            }/* end if packet allocation */
        else
            {
            /** No space left for more packets, free any allocated packets
             **/
            if( p_pkt != (USRBUF *) 0 )
                m_MMGR_FREE_BUFR( p_pkt );
            c_pkt = (USRBUF *) 0;
            }

        }/* end for */

    return( c_pkt );
#else
	return( allocb((int)payload_size, BPRI_MED) ) ;
#endif

}

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

  PROCEDURE NAME:    sys_free_pkt

  DESCRIPTION:
    System primitive to free a frame relay packets created by sys_alloc_pkt.

  PARAMETERS:
    pointer to the packet.

  RETURNS:
    FRS_SUCCESS if ok, FRS_INVALID if the pointer is NULL.

  NOTES:
    This routine should not be called directly. It should be called via
    the m_MMGR_FREE_BUFR macro defined in frssys.h. Other m_MMGR* macros
    can be used to manipulate other USRBUF variables.
*****************************************************************************/

u_short
sys_free_pkt(pkt_ptr)
USRBUF *pkt_ptr;
{
USRBUF *n_pkt, *c_pkt;

    if( pkt_ptr == (USRBUF *) 0 )
        return( (u_short) FRS_INVALID );

#ifndef	STREAM_MESSAGES
    /** Free up all 'linked' packets
     **/
    for( c_pkt = pkt_ptr; c_pkt != (USRBUF *) 0; )
        {
        n_pkt = c_pkt->link; /* sets the next packet */

#if !(USE_CONTIGUOUS_DATA) /* ----------------------------------------- */

        /** The payload area was allocated separately....
         ** free it up....
         **/
        if( c_pkt->payload != (u_char *) 0 )
            m_MMGR_SPL_FREE( c_pkt->payload );

#endif /* ------------------------------------------------------------ */

        m_MMGR_SPL_FREE( c_pkt );
        c_pkt = n_pkt;
        }/* end for */

#else
	
	freemsg(pkt_ptr) ;

#endif
    return( (u_short) FRS_SUCCESS );
}


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

  PROCEDURE NAME:   FRSTMR_timeout 

  DESCRIPTION:

  Frame Relay Timeout handler

  PARAMETERS:
  
  RETURNS:

  NOTES:
      The timeout process will be called in the context of a timer
      interrupt.  Care should be taken to minimize processing within
      such functions.
*****************************************************************************/

int  
FRSTMR_timeout(Dummy)
caddr_t Dummy;
{
	
	/* 
	 * Check list of elements 
	 */

	frs_time time_now ;
	FRS_TIMER *tp ;

	/* 
	 * Reschedule timeout
	 */

	timeout(FRSTMR_timeout, 0 , HZ/25) ;

	/*
	 * Any elements ?
	 */
		
	if(frsTIMERdispatch_ptr == (FRS_TIMER *)NULL)
		return 1;

	time_now = sys_get_sys_uptime() ;
	tp = frsTIMERdispatch_ptr ;

#ifdef ACHECK
	if (!reality_check()) {
		printf("reality check 0 failed\n");
		while(1);
	}
#endif
	for(;;)
	{
		if(time_now - tp->time_future < TIMER_WRAP)
		{

			/* By convention, if *prev is NULL then,
			 * element was first on list
			 * Meaning, that dispatch_ptr was pointing
			 * at this element.
		 	 */

#ifdef ACHECK
			/*
			 * Safety catch:
			 * If item is on timeout queue, but is not active
			 * let's panic the system!
			 * (this is debug code... we should remove it
			 *  when we're done)
			 */
			if(!(tp->flags & FRS_TIMER_ACTIVE)) {
				printf("reality check 1 failed\n");
				while(1);
			}
#endif

			/* 
			 * Dequeue Element
			 */
		
			if(tp->prev != (FRS_TIMER *)NULL)
			{ 
				tp->prev->next = tp->next ;
			}
			else
			{
#ifdef ACHECK
				if (frsTIMERdispatch_ptr != tp) {
					printf("reality check 2 failed\n");
					while(1);
				}
#endif

				frsTIMERdispatch_ptr = tp->next ;
			}
	
			if(tp->next != (FRS_TIMER *)NULL)
			{
				tp->next->prev = tp->prev ;
			}

			/*
			 * Mark this bad boy as inactive
			 */
			tp->flags &= ~(FRS_TIMER_ACTIVE);

			/* 
			 * Call the element
			 */			
#ifdef ACHECK
			if (!reality_check()) {
				printf("reality check 3 failed\n");
				while(1);
			}
#endif

			(*tp->func)(tp->arg) ;

			  
			/*
			 * Check the next element in the queue 
			 */
			
			tp = frsTIMERdispatch_ptr ;

			if(tp == (FRS_TIMER *)NULL)
				return 1;

			continue ;
				 
		}
		else
			break;
	}

	return 1;
}

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

  PROCEDURE NAME:   FRSTMR_create 

  DESCRIPTION:

  Create (allocate) a timer...

  PARAMETERS:

  u_short    - handle passed to timeout process when timer expires.
  BLK_FN_PTR - pointer to timeout process 
  u_short    - type ( not used in this implementation )

  RETURNS:         u_short - timer id to be used in start/stop calls

  NOTES:
      The timeout process will be called in the context of a timer
      interrupt.  Care should be taken to minimize processing within
      such functions.
*****************************************************************************/

u_short 
FRSTMR_create( handle, fTimeoutFunc, type)
u_long handle;
void (*fTimeoutFunc)(u_long);
u_short type;
{
    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert target specific code here to create a timer...          **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/


/*
typedef	struct	frs_timer {
	int time_future ;
	u_short	flags ;
	u_short arg ;
	int	(*func)(u_short) ;
	struct	frs_timer *prev ;
	struct  frs_timer *next ;
}FRS_TIMER ;
*/
	u_short s = 0xFFFF ;

	if(frsTIMERfree_ptr == (FRS_TIMER *)NULL)
		return(s) ;

	s = frsTIMERfree_ptr - frsTIMERtable ;

	frsTIMERfree_ptr->func 	= fTimeoutFunc ;  
	frsTIMERfree_ptr->arg  	= handle ;
	frsTIMERfree_ptr->flags = FRS_TIMER_ALLOCATED ;
	frsTIMERfree_ptr->prev  = (FRS_TIMER *)NULL ;

	frsTIMERfree_ptr 	= frsTIMERfree_ptr->next ;   

    return (s);
}

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

  PROCEDURE NAME:   FRSTMR_destroy 

  DESCRIPTION:

  Destroy (de-allocate) a timer...

  PARAMETERS:

  u_short    - timer id.

  RETURNS:         Nothing

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

void  
FRSTMR_destroy( id)
u_long id;
{
    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert target specific code here to destroy a timer...         **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/


	if(id > NFRTIMERS || 
	  (frsTIMERtable[id].flags == FRS_TIMER_FREE))
		return ;

	if(frsTIMERtable[id].flags & (FRS_TIMER_ACTIVE))
	{
		/*
		 * Pull this guy off of active queue 
		 * Add to Free List
		 * Can you do this easily?
		 * *prev can be a problem. When the item is the first element
		 * on the queue it should be zero.
		 * *next could be zero as well. 
		 *     
		 */ 

		/* By convention, if *prev is NULL then,
		 * element was first on list
		 * Meaning, that dispatch_ptr was pointing
		 * at this element.
	 	 */
		
		if(frsTIMERtable[id].prev != (FRS_TIMER *)NULL)
		{ 
			frsTIMERtable[id].prev->next = frsTIMERtable[id].next ;
		}
		else
		{
			frsTIMERdispatch_ptr = frsTIMERtable[id].next ;
		}

		if(frsTIMERtable[id].next != (FRS_TIMER *)NULL)
		{
			frsTIMERtable[id].next->prev = frsTIMERtable[id].prev ;
		}
	}

	/* 
	 * Add to Free List 
	 */
	
	frsTIMERtable[id].next = frsTIMERfree_ptr ;

	frsTIMERtable[id].flags = FRS_TIMER_FREE ;

	frsTIMERtable[id].prev = (FRS_TIMER *)NULL ;	
	
	frsTIMERfree_ptr = &frsTIMERtable[id] ;

	return;
}

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

  PROCEDURE NAME:   FRSTMR_start

  DESCRIPTION:

  Start a previously created timer...

  PARAMETERS:

  u_short - timer id (value returned from FRSTMR_create())
  u_short - number of milliseconds to set timeout value

  RETURNS:         Nothing

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

void 
FRSTMR_start( id, msec)
u_short id;
u_short msec;
{

	frs_time time_insert ;
	FRS_TIMER	*tp ;

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert target specific code here to start a timer...           **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

	if(id > NFRTIMERS ||
	   (frsTIMERtable[id].flags == FRS_TIMER_FREE))
		return ;

	/*
	 * Case 1. Is the the element already active? If so, then the
	 * msec should be set from sys_get_sys_uptime() forward. The element,
	 * needs to be dequeued, then sorted and requeued in the right
	 * position on the timeout list.
	 */

	/* Case 2: The element needs to be placed on the list.
	 */

#ifdef ACHECK
	if (!reality_check()) {
		printf("reality check 4 failed\n");
		while(1);
	}
#endif
	if((frsTIMERtable[id].flags & (FRS_TIMER_ACTIVE | FRS_TIMER_ALLOCATED))
	   == (FRS_TIMER_ACTIVE | FRS_TIMER_ALLOCATED))
	{
		/*
		 * Pull this guy off of active queue 
		 * Add to Free List
		 * Can you do this easily?
		 * *prev can be a problem. When the item is the first element
		 * on the queue it should be zero.
		 * *next could be zero as well. 
		 *     
		 */ 

		/* By convention, if *prev is NULL then,
		 * element was first on list
		 * Meaning, that dispatch_ptr was pointing
		 * at this element.
	 	 */
		
		if(frsTIMERtable[id].prev != (FRS_TIMER *)NULL)
		{ 
			frsTIMERtable[id].prev->next = frsTIMERtable[id].next ;
		}
		else
		{
			frsTIMERdispatch_ptr = frsTIMERtable[id].next ;
		}

		if(frsTIMERtable[id].next != (FRS_TIMER *)NULL)
		{
			frsTIMERtable[id].next->prev = frsTIMERtable[id].prev ;
		}
#ifdef ACHECK
		if (!reality_check()) {
			printf("reality check 5 failed\n");
			while(1);
		}
#endif
	}

	/* 
	 * Walk down dispatch_ptr list and insert this in the correct
	 * place.
	 */

	time_insert = sys_get_sys_uptime() + msec ;

	/* Safety check to prevent infinite start/timeout loops */
	if (sys_get_sys_uptime() - time_insert < TIMER_WRAP)
		time_insert = sys_get_sys_uptime()+1;

	if(frsTIMERdispatch_ptr == (FRS_TIMER *)NULL)
	{
		/* 
	         * First Element in the queue
		 */

		frsTIMERdispatch_ptr = &frsTIMERtable[id] ;

		frsTIMERtable[id].next = (FRS_TIMER *)NULL ;
		frsTIMERtable[id].prev = (FRS_TIMER *)NULL ;

		frsTIMERtable[id].flags |= FRS_TIMER_ACTIVE ;

		frsTIMERtable[id].time_future = time_insert ; 

	}
	else
	{
		tp = frsTIMERdispatch_ptr ;     

		for(;;)     
		{
			if(tp->time_future - time_insert < TIMER_WRAP)
			{
				/* 
				 * This is the place to put it
				 */
				
				if(tp->prev != (FRS_TIMER *)NULL)
					tp->prev->next = &frsTIMERtable[id] ;
	
				frsTIMERtable[id].prev = tp->prev ;

				frsTIMERtable[id].next = tp ;

				tp->prev = &frsTIMERtable[id] ;

				frsTIMERtable[id].time_future = time_insert ;
				frsTIMERtable[id].flags |= FRS_TIMER_ACTIVE ;

				if (tp == frsTIMERdispatch_ptr)
					frsTIMERdispatch_ptr = &frsTIMERtable[id];
				
				break ;	
			}

			if(tp->next == (FRS_TIMER *)NULL)
			{
				/* 
				 * End of the list
				 * Insert at the end 
				 */

				tp->next = &frsTIMERtable[id] ;
			
				frsTIMERtable[id].prev = tp ;

				frsTIMERtable[id].next = (FRS_TIMER *)NULL ;

				frsTIMERtable[id].time_future = time_insert ;
				frsTIMERtable[id].flags |= FRS_TIMER_ACTIVE ;

				break ;
			} 
			
			tp = tp->next ;

			continue ;
		}
	}

#ifdef ACHECK
	if (!reality_check()) {
		printf("reality check 6 failed\n");
		while(1);
	}
#endif
    return;
}

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

  PROCEDURE NAME:   FRSTMR_stop 

  DESCRIPTION:

  Stop a previously started timer...

  PARAMETERS:

  u_short - timer id (value returned from FRSTMR_create())

  RETURNS:         Nothing

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

void 
FRSTMR_stop(id)
u_short id;
{
    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert target specific code here to stop a timer...            **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/


	if(id > NFRTIMERS || 
	  (frsTIMERtable[id].flags == FRS_TIMER_FREE) ||
	  ((frsTIMERtable[id].flags &  FRS_TIMER_ACTIVE) != FRS_TIMER_ACTIVE))
		return ;
#ifdef ACHECK
	if (!reality_check()) {
		printf("reality check 7 failed\n");
		while(1);
	}
#endif
	if(frsTIMERtable[id].flags & (FRS_TIMER_ACTIVE | FRS_TIMER_ALLOCATED))
	{
		/*
		 * Pull this guy off of active queue 
		 * Add to Free List
		 * Can you do this easily?
		 * *prev can be a problem. When the item is the first element
		 * on the queue it should be zero.
		 * *next could be zero as well. 
		 *     
		 */ 

		/* By convention, if *prev is NULL then,
		 * element was first on list
		 * Meaning, that dispatch_ptr was pointing
		 * at this element.
	 	 */
		
		if(frsTIMERtable[id].prev != (FRS_TIMER *)NULL)
		{ 
			frsTIMERtable[id].prev->next = frsTIMERtable[id].next ;
		}
		else
		{
			frsTIMERdispatch_ptr = frsTIMERtable[id].next ;
		}

		if(frsTIMERtable[id].next != (FRS_TIMER *)NULL)
		{
			frsTIMERtable[id].next->prev = frsTIMERtable[id].prev ;
		}

		/*
		 * Clear Active State 
		 */

		frsTIMERtable[id].flags &= ~FRS_TIMER_ACTIVE ;
#ifdef ACHECK
		if (!reality_check()) {
			printf("reality check 8 failed\n");
			while(1);
		}
#endif
	}

    return;
}

#ifdef ACHECK
int reality_check()
{
	FRS_TIMER *tp;
	int i;

	for(i = 0; i < NFRTIMERS; i++)
		/* nothing should EVER point to itself! */
		if (frsTIMERtable[i].prev == &frsTIMERtable[i] ||
		    frsTIMERtable[i].next == &frsTIMERtable[i]) return 0;

	if (frsTIMERdispatch_ptr == NULL) {
		/* nothing should be active */
		for(i = 0; i < NFRTIMERS; i++)
		if (frsTIMERtable[i].flags & FRS_TIMER_ACTIVE) return 0;

		return 1;
	}

	for(i = 0; i < NFRTIMERS; i++) {
		if ((&frsTIMERtable[i] == frsTIMERdispatch_ptr) &&
		(frsTIMERtable[i].prev != (FRS_TIMER *) NULL)) return 0;

		if ((frsTIMERtable[i].prev == (FRS_TIMER *) NULL) &&
		(frsTIMERtable[i].flags & FRS_TIMER_ACTIVE) &&
		(&frsTIMERtable[i] != frsTIMERdispatch_ptr)) return 0;
	}

	for(tp = frsTIMERdispatch_ptr, i = 0; i < (NFRTIMERS*2); i++) {
		if (!(tp->flags & FRS_TIMER_ACTIVE)) return 0;
		if (tp->next == (FRS_TIMER *) NULL) break;
		tp = tp->next;
	}

	if (i >= (NFRTIMERS*2)) return 0;

	for(i = 0; i < (NFRTIMERS*2); i++) {
		if (tp == frsTIMERdispatch_ptr) break;
		tp = tp->prev;
		if (tp == (FRS_TIMER *) NULL) return 0;
	}

	if (i >= (NFRTIMERS*2)) return 0;

	return 1;
}
#endif

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

  PROCEDURE NAME:    sys_get_sys_uptime

  DESCRIPTION:

  Primitives to retrieve elapsed time since system startup...

  PARAMETERS:

  Nothing

  RETURNS:         time since system startup

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

frs_time 
sys_get_sys_uptime()
{
    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Additional code required to return the elapsed up time...      **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /*
     * curtime increments every 20 ms.  The frame relay code expects
     * a resolution less than 50 ms, so that's fine.  But it expects
     * sys_get_sys_uptime() to express system time in _milliseconds_.
     */
    return((frs_time) (curtime() * (1000/HZ))); /* dummy return value = 0 */
}

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

  PROCEDURE NAME:    sys_trap_event

  DESCRIPTION:

  Primitives to process trap event.

  PARAMETERS:

  Pointers to the LCB, CCB and the trap code .
  Note that either the LCB or the CCB may be passed as a NULL pointer. 
  User is responsible for testing for CCBNULL or LCBNULL pointers.

  RETURNS:         

  NOTES:

*****************************************************************************/
void 
sys_trap_event( pLCB, pCCB, trap_code )
LCB *pLCB;
CCB *pCCB;
u_short trap_code;
{
    /** Switches on the trap... Insert target specific processing
     ** within the desired case statement below... The source of the
     ** trap is mask off to obtain the generic trap value.
     **/
    switch( trap_code & TRAP_ONLY_MASK )
        {
        case GENERIC_LINKUP:

            /** Received whenever the rx state machine or tx state machine
             ** transitions from link down to link up.
             **/

            /** --------------- TARGET SYSTEM CODE -------------------------**/
            /** --------------- TARGET SYSTEM CODE -------------------------**/

            break;

        case GENERIC_LINKDOWN:

            /** Received whenever the rx state machine or tx state machine
             ** transitions from link up to link down.
             **/

            /** --------------- TARGET SYSTEM CODE -------------------------**/
            /** --------------- TARGET SYSTEM CODE -------------------------**/

            break;

        default:

            /** --------------- TARGET SYSTEM CODE -------------------------**/
            /** --------------- TARGET SYSTEM CODE -------------------------**/

            break;
        }

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

  PROCEDURE NAME:    sys_update_config

  DESCRIPTION:
    This is a callback function called from frscfg.c whenever something
    has been inserted, deleted or modified in the frame relay configuration.
    The update_info structure contains information regarding what changed,
    the values, record information, etc (see notes in frscfg.h). 

  PARAMETERS:
    CFG_UPDATE * update_info ... contains information regarding what 
                                 happened...
  RETURNS:
    TRUE .... If the target system wishes to have the calling function
              update of the online record structures (in the instance
              of a modification).

    FALSE ... If the target system does not want the calling function to
              update the online record information (in the instance of
              a modification).

    FRS_SUCCESS or FRS_INVALID ... return one of these values in response
             to link or pll insertion/deletion. The rcode value is passed
             through the calling function to the calling function that 
             requested the insertion or deletion.
             
  NOTES:

    See application notes in frscfg.h on the update_info structure.

*****************************************************************************/
u_short 
sys_update_config( update_info )
CFG_UPDATE *update_info;
{
    /** ------------------ TARGET SYSTEM CODE -------------------------**/
u_short rcode;

    switch( update_info->action )
        {

#if( FRS_DCE_SUPPORT || !(USE_CONTIGUOUS_DATA) )

        case FRS_CONFIG_SYS_PARAMETER_UPDATE:
            rcode = TRUE;
            break;
#endif

        case FRS_CONFIG_LINK_REMOVED:
            rcode = FRS_SUCCESS;
            break;

        case FRS_CONFIG_LINK_INSERTED:
            rcode = FRS_SUCCESS;
            break;

        case FRS_CONFIG_PLL_INSERTED:
            rcode = FRS_SUCCESS;
            break;

        case FRS_CONFIG_PLL_REMOVED:
            rcode = FRS_SUCCESS;
            break;

        case FRS_CONFIG_LNK_PARAMETER_UPDATE:
            rcode = TRUE;
            break;

        case FRS_CONFIG_PLL_PARAMETER_UPDATE:
            rcode = TRUE;
            break;

        default:
            rcode = FRS_INVALID;
            break;
        }

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    return( rcode );
}

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

  PROCEDURE NAME:    sys_send_cmi

  DESCRIPTION:

  Primitive to request DXE to send a CMI packet to FRS on a remote node.

  PARAMETERS:

  u_short    - Destination node number.
  USRBUF *   - Pointer to USRBUF with CMI packet.

  RETURNS:   FRS_SUCCESS if packet sent, error code otherwise.
         

  NOTES:

*****************************************************************************/
#if FRS_DCE_SUPPORT
u_short 
sys_send_cmi( node_num, pBfr )
u_short node_num;
USRBUF *pBfr;
{
    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    /** Insert code to route the CMI packet through the interior protocol
         network to the specified node.
     **/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

    return FRS_SUCCESS;
}
#endif

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

  PROCEDURE NAME:    sys_mark_frames_drop

  DESCRIPTION:

  Function to mark frames belonging to a deactivated PVC in target system's 
  transmit queue(s) to be dropped before transmission.

  PARAMETERS:

  Pointer to the CCB.

  RETURNS:         Nothing.

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

void 
sys_mark_frames_drop( pccb )
CCB *pccb;
{
USRBUF *pFrame;
USRBUF *pFrame_temp ;

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

/*************************************************************************
 *************************************************************************
 *  In addition to the processing below to handle frames in the FRS LCB
 *  transmit queue...
 *
 *  Insert code to get head of low level driver's transmit queue 
 *  (external to FRS) and a test to see if it is empty.  If it's empty,
 *  return.
 *  Otherwise, go through queue and mark any frames belonging to the 
 *  deleted PVC.  
 *  NOTE:  A test for marked frames must be inserted into the low level
 *  driver's code at the point where frames are dequeued for transmission.
 *  This test should free any buffers that contain a marked frame and call
 *  FRSDRV_TxDone().
 *
 *************************************************************************
 *************************************************************************/

    /** ------------------ TARGET SYSTEM CODE -------------------------**/

 /** The following will mark frames from deleted CCB in the LCB transmit
     queue.  Marked frames will later be filtered out instead of being
     sent to low level driver...
  **/

      m_START_CRITICAL;

      if( (pFrame = FRSQT_Peek(pccb->plcb->TQTCB))
                                              == (USRBUF *)0 )
      {
         m_END_CRITICAL;
         return;
      }
      else
      {
         do
         {
#ifndef	STREAM_MESSAGES

            if( FRSGetFrameDLCI((&(pFrame->payload[pFrame->start]))) 
                                                       == pccb->dlci )
               mMarkFrameforDrop(pFrame);

            pFrame = pFrame->next;
#else

            if( FRSGetFrameDLCI(pFrame->b_rptr) == pccb->dlci )
		
            pFrame_temp = pFrame->b_cont ;
		m_MMGR_FREE_BUFR(pFrame) ;
	    pFrame = pFrame_temp ;
#endif


         }while( pFrame != (USRBUF *)0 );
      m_END_CRITICAL;
      }

    return;
}

#if 0
/**/
/*****************************************************************************
  NAME:  InitFrs

  DESCRIPTION:

    This procedure is called by the target oper sys to perform initialisation
    of run-time frame relay environment. Specifically, the LCB/CCB tables
    are allocated here.

  PARAMETERS: None

  RETURNS:    u_short    error   -- FRS_SUCCESS if initialisation went OK

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

u_short
InitFrs()
{
    u_short rcode;
    u_int i;
    LCB   *pLCB;

    /* Load and Initialise configuration. LCBs and CCBs get created 
     * via this call...
     */
    rcode = FRSCONF_Init();
    if (rcode != FRS_SUCCESS && rcode != FRS_ENABLED)
        return rcode;

    /* Create task sets for each configured FRS link...
     * Note that frs creates a receive task and a transmit task per link.
     */

    for (i=0; i < gGlobalCfg.frsTotalLinks; i++)
    {
        if (!(gGlobalCfg._frmrly_lkcfg[i].frs_link_flags &
              FRS_OPT_LINK_ENBL))
            continue;

        /* Create the LCB and optional LMICCB...
        */
        if ((rcode = FRSCONF_InitLink(&gGlobalCfg._frmrly_lkcfg[i], 0, &pLCB)) != FRS_SUCCESS)
            return rcode;

        /* Init the phys port...
         */
        FRSDRV_InitLink(&gGlobalCfg._frmrly_lkcfg[i]);

        /* Run diagnostics on the phys port. If diags fail do not launch
         * processes...If diags pass, start data traffic and launch processes.
         */
        if ((FRSDRV_LinkControl(frsLCBtable[i],
                                FRS_LINK_LOCAL_LOOP)) == FRS_SUCCESS)
        {
            if ((FRSDRV_LinkControl(frsLCBtable[i], 
                                    FRS_LINK_SDT)) == FRS_SUCCESS)
            {

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

                /** Transmit Task
                 **/
                os_task_create((u_int) TASKID_FRSTX,    /* Task Id */
                    i,                                  /* Task Instance */
                    (u_int) PRI_FRSTX,                  /* Task Priority */
                    FRSTransmit,                        /* Task EntryPoint */
                    g_Stack_FrsTx[i],                      /* Task StackTop */
                    (u_int) sizeof(g_Stack_FrsTx[i]));     /* Task StackSize */

                /** Receive Task
                 **/
                os_task_create((u_int) TASKID_FRSRX,
                    i,
                    (u_int) PRI_FRSRX,
                    FRSReceive,
                    g_Stack_FrsRx[i],
                    (u_int) sizeof(g_Stack_FrsRx[i]));
#else
            ; /* No receive or transmit tasks used */

#endif /* USE_TASKING ---------------------------------------------------- */

            }
        else
            {
             ; /* Target Specific error handling is required here */
            }
    }
    else
        {
         ; /* Target Specific error handling is required here */

        /** ------------------ TARGET SYSTEM CODE -------------------------**/
        /** ------------------ TARGET SYSTEM CODE -------------------------**/
        /** ------------------ TARGET SYSTEM CODE -------------------------**/
        /** ------------------ TARGET SYSTEM CODE -------------------------**/
        }

     }/* end of for loop */

    /** Set gGlobalCfg.frsNodeNum to local node number...
        NOTE: Replace the following with code to set actual node number

        gGlobalCfg.frsNodeNum = 1;
     **/

    return FRS_SUCCESS;
}
#endif

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

  PROCEDURE NAME:    sys_ins_oui

  DESCRIPTION:

  This is a target system callback function that is called from the 
  encap_makeframe function in frsencap.c. This routine is called while a
  SNAP HEADER is being created for a user protocol frame. The target system
  is given this opportunity to insert a proprietary OUI in the OUI array of
  the SNAP header. The encap_makeframe routine will NOT modify the oui members
  of the SNAP header if you have inserted an OUI and you have returned a 
  non-zero value to the caller. If you do not modify the oui or you do not
  intend to use this function then this routine MUST return FALSE to the caller.

  PARAMETERS:

  NPSNAP_HDR - pointer to a SNAP HEADER (see tag_snap_header struct in frs.h)
  CCB * - pointer to the ccb this frame is associated with.

  RETURNS:
    non-zero IF the oui was modified.
    FALSE if the oui was NOT modified.

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

u_short
sys_ins_oui(pccb, p_snap_header )
CCB *pccb;
NPSNAP_HDR p_snap_header;
{

    /** p_snap_header->oui[0] = 0x??;
        p_snap_header->oui[1] = 0x??;
        p_snap_header->oui[2] = 0x??; **/

        /** ------------------ TARGET SYSTEM CODE -------------------------**/
        /** ------------------ TARGET SYSTEM CODE -------------------------**/
        /** ------------------ TARGET SYSTEM CODE -------------------------**/

    return( (u_short) FALSE);
}

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

  PROCEDURE NAME:    sys_oui_proc

  DESCRIPTION:

  This is a target system callback function that is called from the 
  encap_parse_header function in frsencap.c. This routine is called while the
  encap_parse_header function is validating the OUI information in the header.
  The target system is given this opportunity to verify/validate the OUI 
  information in the frame's header if the OUI info is not recognized by the
  encap_parse_header routine. If you are using a proprietery OUI in the SNAP
  HEADER during frame transmission (see sys_ins_oui function above). The this
  routine allows you to recognize the OUI information in a received frame. If
  you are called by encap_parse_header AND you have processed the OUI info in
  the header then return a non-zero value. This instructs encap_parse_header
  to continue parsing the remaining members of the header structure. If you
  do not wish to use this routine OR you do not recognize the OUI information
  in the header then you MUST return the value FALSE.

  PARAMETERS:

  NPPARSE_HDR - pointer to a parse header (see tag_parse_header struct in frs.h)
  CCB * - pointer to the ccb this frame is associated with.

  RETURNS:
    non-zero IF the oui was processed.
    FALSE if the oui was NOT processed.

  NOTES:

     Since a pointer to the parsing header is passed to this function it
     gives the target system an opportunity to examine the entire contents
     of the header. However, to ensure that the frame reassembly routines work
     correctly DO NOT MODIFY any of the members of the header structure.
*****************************************************************************/

u_short
sys_oui_proc( pccb, p_parse_hdr )
CCB *pccb;
NPPARSE_HDR p_parse_hdr;
{

    /** if ( (p_parse_hdr->oui[0] == ?) &&
             (p_parse_hdr->oui[1] == ?) &&
             (p_parse_hdr->oui[2] == ?) )

            ------------------ TARGET SYSTEM CODE -------------------------
            ------------------ TARGET SYSTEM CODE -------------------------
            ------------------ TARGET SYSTEM CODE -------------------------

            return((u_short) TRUE);
        else
            **/

    return( (u_short) FALSE);
}
#ifdef	STATIC_MEMORY

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

  PROCEDURE NAME:    sys_lcb_alloc

  DESCRIPTION:
	This will allocate a LCB structure from the pool of statically
	allocated structures.

  PARAMETERS:
	i -- The index of the LCB array to allocate.

  RETURNS:
	NULL if no member available
	Pointer to a LCB Structure, if one is available	

  NOTES:

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

LCB *
sys_lcb_alloc(i)
u_int i ;
{
	if(i >= NLCBS || frsLCBtable_free[i] == TRUE)
		return((LCB *)NULL) ;
	
	frsLCBtable_free[i] = TRUE ;	

	m_MMGR_MEMSET(&frsLCBtab[i], 0, sizeof(LCB)) ;

	/* Handle is table array offset plus one */
	frsLCBtab[i].hLCB = i+1 ;
	frsLCBtable[i] = &frsLCBtab[i] ;

	return(&frsLCBtab[i]) ;
}

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

  PROCEDURE NAME:    sys_ccb_alloc

  DESCRIPTION:
	This will allocate a CCB structure from the pool of statically
	allocated structures.

  PARAMETERS:
	NONE

  RETURNS:
	NULL if no member available
	Pointer to a CCB Structure, if one is available	

  NOTES:

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

CCB *
sys_ccb_alloc()
{
	u_int i ;

	for(i = 0 ; i < NCCBS ; i++)
		if(frsCCBtable_free[i] == FALSE)
			break ;

	if(i == NCCBS)
		return((CCB *)NULL) ;
	
	frsCCBtable_free[i] = TRUE ;	

	return(&frsCCBtab[i]) ;
}



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

  PROCEDURE NAME:    sys_lmiccb_alloc

  DESCRIPTION:
	This will allocate a LMICCB structure from the pool of statically
	allocated structures.

  PARAMETERS:
	NONE

  RETURNS:
	NULL if no member available
	Pointer to a LMICCB Structure, if one is available	

  NOTES:

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

LMICCB *
sys_lmiccb_alloc()
{
	u_int i ;

	for(i = 0 ; i < NLMICCBS ; i++)
		if(frsLMICCBtable_free[i] == FALSE)
			break ;

	if(i == NLMICCBS)
		return((LMICCB *)NULL) ;
	
	frsLMICCBtable_free[i] = TRUE ;	

	return(&frsLMICCBtab[i]) ;
}



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

  PROCEDURE NAME:    sys_frs_protocol_entry_alloc

  DESCRIPTION:
	This will allocate a frs_protocol_entry structure from 
	the pool of statically allocated structures.

  PARAMETERS:
	NONE

  RETURNS:
	NULL if no member available
	Pointer to a FRS_PROTOCOL_ENTRY Structure, if one is available	

  NOTES:

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

FRS_PROTOCOL_ENTRY *
sys_frs_protocol_entry_alloc()
{
	u_int i ;

	for(i = 0 ; i < NFRS_PROTOCOL_ENTRYS ; i++)
		if(frsFRS_PROTOCOL_ENTRYtable_free[i] == FALSE)
			break ;

	if(i == NFRS_PROTOCOL_ENTRYS)
		return((FRS_PROTOCOL_ENTRY *)NULL) ;
	
	frsFRS_PROTOCOL_ENTRYtable_free[i] = TRUE ;	

	return(&frsFRS_PROTOCOL_ENTRYtab[i]) ;
}


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

  PROCEDURE NAME:    sys_multiprotocol_alloc

  DESCRIPTION:
	This will allocate a multiprotocol structure from 
	the pool of statically allocated structures.

  PARAMETERS:
	NONE

  RETURNS:
	NULL if no member available
	Pointer to a multiprotocol structure, if one is available	

  NOTES:

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

MULTIPROTOCOL *
sys_multiprotocol_alloc()
{
	u_int i ;

	for(i = 0 ; i < NMULTIPROTOCOLS; i++)
		if(frsMULTIPROTOCOLtable_free[i] == FALSE)
			break ;

	if(i == NMULTIPROTOCOLS)
		return((MULTIPROTOCOL *)NULL) ;
	
	frsMULTIPROTOCOLtable_free[i] = TRUE ;	

	return(&frsMULTIPROTOCOLtab[i]) ;
}

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

  PROCEDURE NAME:    sys_event_alloc

  DESCRIPTION:
	This will allocate a event structure from 
	the pool of statically allocated structures.

  PARAMETERS:
	NONE

  RETURNS:
	NULL if no member available
	Pointer to a event structure, if one is available	

  NOTES:

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

EVENT *
sys_event_alloc()
{
	u_int i ;

	for(i = 0 ; i < NEVENTS; i++)
		if(frsEVENTtable_free[i] == FALSE)
			break ;

	if(i == NEVENTS)
		return((EVENT *)NULL) ;
	
	frsEVENTtable_free[i] = TRUE ;	

	return(&frsEVENTtab[i]) ;
}

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

  PROCEDURE NAME:    sys_event_dealloc

  DESCRIPTION:
	This will mark an event structure available

  PARAMETERS:
	Pointer to the event structure to mark available 

  RETURNS:
	NONE 

  NOTES:

*****************************************************************************/
void
sys_event_dealloc(lp)
EVENT *lp ;
{
	u_int i ;

	i = lp - frsEVENTtab ;

	if(i < NEVENTS)
		frsEVENTtable_free[i] = FALSE ;
}

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

  PROCEDURE NAME:    sys_multiprotocol_dealloc

  DESCRIPTION:
	This will mark an multiprotocol structure available

  PARAMETERS:
	Pointer to the multiprotocol structure to mark available 

  RETURNS:
	NONE 

  NOTES:

*****************************************************************************/
void
sys_multiprotocol_dealloc(lp)
MULTIPROTOCOL *lp ;
{
	u_int i ;

	i = lp - frsMULTIPROTOCOLtab ;

	if(i < NMULTIPROTOCOLS)
		frsMULTIPROTOCOLtable_free[i] = FALSE ;
}

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

  PROCEDURE NAME:    sys_frs_protocol_entry_dealloc

  DESCRIPTION:
	This will mark an frs_protocol_entry structure available

  PARAMETERS:
	Pointer to the frs_protocol_entry structure to mark available 

  RETURNS:
	NONE 

  NOTES:

*****************************************************************************/
void
sys_frs_protocol_entry_dealloc(lp)
FRS_PROTOCOL_ENTRY *lp ;
{
	u_int i ;

	i = lp - frsFRS_PROTOCOL_ENTRYtab ;

	if(i < NFRS_PROTOCOL_ENTRYS)
		frsFRS_PROTOCOL_ENTRYtable_free[i] = FALSE ;
}


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

  PROCEDURE NAME:    sys_lmiccb_dealloc

  DESCRIPTION:
	This will mark an lmiccb structure available

  PARAMETERS:
	Pointer to the lmiccb structure to mark available 

  RETURNS:
	NONE 

  NOTES:

*****************************************************************************/
void
sys_lmiccb_dealloc(lp)
LMICCB *lp ;
{
	u_int i ;

	i = lp - frsLMICCBtab ;

	if(i < NLMICCBS)
		frsLMICCBtable_free[i] = FALSE ;
}



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

  PROCEDURE NAME:    sys_ccb_dealloc

  DESCRIPTION:
	This will mark an ccb structure available

  PARAMETERS:
	Pointer to the ccb structure to mark available 

  RETURNS:
	NONE 

  NOTES:

*****************************************************************************/
void
sys_ccb_dealloc(lp)
CCB *lp ;
{
	u_int i ;

	i = lp - frsCCBtab ;

	if(i < NCCBS)
		frsCCBtable_free[i] = FALSE ;
}



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

  PROCEDURE NAME:    sys_lcb_dealloc

  DESCRIPTION:
	This will mark an lcb structure available

  PARAMETERS:
	Pointer to the lcb structure to mark available 

  RETURNS:
	NONE 

  NOTES:

*****************************************************************************/
void
sys_lcb_dealloc(lp)
LCB *lp ;
{
	u_int i ;

	i = lp - frsLCBtab ;

	if(i < NLCBS)
		frsLCBtable_free[i] = FALSE ;
}
#endif

#ifdef ACHECK
int
messagecheck(mp, has_headers)
mblk_t *mp;
int has_headers;
{
	int i, msglen, bufsizecheck;
	u_char crccheck;
	u_char *dptr;
	mblk_t *tmp;

	msglen = 0;
	crccheck = 0;
	for(tmp = mp; tmp; tmp = tmp->b_cont)
		for(dptr = tmp->b_rptr; dptr != tmp->b_wptr; dptr++) {
			msglen++;
			crccheck += *dptr;
		}

	for(tmp = mp; tmp->b_cont; tmp = tmp->b_cont);

	crccheck -= *((unsigned char *) (tmp->b_wptr - 1));
	crccheck -= *((unsigned char *) (mp->b_rptr));

	bufsizecheck = (*((unsigned char *) (mp->b_rptr+1)) * 100) +
			*((unsigned char *) (mp->b_rptr+2));

	if (bufsizecheck != msglen ||
		crccheck != *((unsigned char *) (mp->b_rptr)) ||
		crccheck != *((unsigned char *) (tmp->b_wptr - 1))) {
		printf("CRC FAILURE (%x, %x, %x, %x, %x)\n",
		bufsizecheck, msglen, crccheck,
		*((unsigned char *) (mp->b_rptr)),
		*((unsigned char *) (tmp->b_wptr - 1)));
		for(tmp = mp; tmp; tmp = tmp->b_cont)
			printf("BLOCK: base(%x) lim(%x) rptr(%x) wptr(%x)\n",
			tmp->b_datap->db_base, tmp->b_datap->db_lim,
			tmp->b_rptr, tmp->b_wptr);
		while (1);
	}

	return 1;
}
#endif
