#ident "$Id: frscfg.c,v 1.13 1997/10/21 20:48:06 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)    Added FRSCONF_InitLink()
						 Dropped FRSCONF_Reset()
  03-23-93 2.00F    H&J(CA)    Fixed flag setting for FRS_LINK_INJECT
	   2.00H    H&J        See H&J's bug reports
  06-16-94 2.20     H&J(AK)    Added FRSCONF_UnInitLink()          

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

  Revision 1.12  1997/09/23 16:38:12  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.11  1996/10/24 20:02:04  gary
  1) Fixed bug in FRI_GetLinkParameters that would cause garbage to be
  returned.
  2) Removed support for old method of compression (per-packet basis).
  The API has not changed (yet).  The STAC parameters are simply ignored.
  3) FRF.9 activated.

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

  Revision 1.9  1996/10/18 21:59:17  gary
  Minor Frame Relay directory clean-up.

  Revision 1.8  1996/09/11 18:10:32  gary
  Minor bug in FRSCONF_InsertPLL().  If the user tried to modify an already
  configured DLCI/LINK pair, the changes would be silently discarded.

  Revision 1.7  1996/06/21 16:36:32  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.6  1996/06/19 16:33:21  gary
  General cleanup and modifications so that the source files may be
  compiled using makefile v2.x.  References to the compiler flag
  "JUPITER" were replaced with "PCI".  #include <xxx> lines were
  replaced with #includ "xxx".

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

  Revision 1.4  1995/04/10 16:26:01  garyp
  removed strm.h

 * Revision 1.20  1994/07/27  20:27:41  stavrosm
 * Release 2.2
 *
 * Revision 1.19  1994/07/25  18:21:59  stavrosm
 * In FRSLocateStat(), the code does not check if LMI is
 * enabled before getting LMI statistics. This check is needed because
 * otherwise the LMI CCB pointer is NULL and a crash occurs.
 *
 * Revision 1.18  1994/07/13  15:53:19  stavrosm
 * In FRSCONF_InsertLink added initialization of link state to
 * default value.
 * Also in FRSCONF_InsertPLL added initialization of PLL state to
 * default value.
 *
 * Revision 1.17  1994/07/12  14:45:22  stavrosm
 * Case statement for FRS_LINK_STATE was in the wrong place
 * and had to be moved.
 *
 * Revision 1.16  1994/07/08  14:40:26  stavrosm
 * Added functionality for configuration of PLL state, link state,
 * forward node, forward link, and forward DLCI.
 *
 * Revision 1.15  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.14  1994/06/23  20:29:05  bfox
 * added checks, DLCI 16-991 and LMI DLCI 0 or 1023
 *
 * Revision 1.1  1994/06/23  17:45:56  bfox
 * Initial revision
 *
 * Revision 1.13  1994/06/16  18:50:22  akullber
 * Added new routine FRSCONF_UnInitLink(), which is the complement of
 * FRSCONF_InitLink().  This new routine frees the LCB and if an LMI
 * CCB exists, it is freed.  It is called from FRSCONF_DeleteLink().
 *
 * Revision 1.12  1994/06/16  18:34:03  stavrosm
 * Upon link initialization zero out LCB and LMI CCB
 *
 * Revision 1.11  1994/03/16  21:55:25  deepak
 * Many changes to reduce wastage of data space in the config database.
 *
 * Revision 1.10  1994/02/25  20:17:38  guest
 * Add support for Q.933 Annex A.  Bug Report SA-101.
 *
 * Revision 1.9  1993/11/10  16:44:12  stavrosm
 * Release 2.1
 *
 * Revision 1.8  93/11/10  16:26:20  stavrosm
 * Cleanup to make lint happy
 * 
 * Revision 1.7  93/11/02  11:13:32  stavrosm
 * 
..............................................................................

  DESCRIPTION:

  This module contains the H&J Frame Relay Stack Configuration Module.

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

  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 in frsopt.h

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

  FUNCTIONS INCLUDED in this module:

    FRSCONF_DeleteLink .............Delete a link from the link table.
    FRSCONF_DeletePLL ..............Delete a PLL from a link.
    FRSCONF_Disable ................Disable Frame Relay operations.
    FRSCONF_FindFlag ...............Determines the current setting of a param.
    FRSCONF_GetConfig ..............Get configuration parameter value.
    FRSCONF_GetResetStat ...........Get and Reset configuration parameter value.
    FRSCONF_GetStatistics ..........Get statistic value.
    FRSCONF_Init ...................System table creation and initialisation.
    FRSCONF_InitLink ...............LCB creation and link initialisation.
    FRSCONF_UnInitLink .............LCB cleanup and deletion.
    FRSCONF_InsertLink .............Insert a new link in the link table.
    FRSCONF_InsertPLL ..............Insert a PLL on a link.
    FRSCONF_SetConfig ..............Set configuration parameter value.
    FRSCONF_SetFlags ...............Returns FRS_FLAG_ON (OFF) for a parameter.
    FRSCONF_SetLinkConfig ..........Set a Link configuration parameter.
    FRSCONF_SetPLLConfig ...........Set a PLL configuration parameter.
    FRSCONF_SetSysConfig ...........Set a System configuration parameter.
    FRSCONF_ValidParam .............Validate a system/link/pll parameter.
    FRSLocateParam .................Locate the address of a config parameter.
    FRSLocatePLLCfg ................Locate the address of a PLL config record.
    FRSLocateStat ..................Locate the address of a statistic variable.
    hj_strcmpi .....................private version of strcmpi
    hj_ultoa .......................private version of ultoa

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


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


#define FRSCFG_C

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

/*
 * Includes
 */

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

#define STRCMP_BUFFER 50   /* Max size of the buffers used in hj_strcmpi */

/*
 * Local functions
 */
#if USE_PROTOTYPES
    u_short FRSLocateStat(u_long, u_long, u_long, u_long, u_long ** );
    u_short FRSLocateParam(u_long, u_long, u_long, void **);
    int hj_strcmpi( char *, char * );
    char *hj_ultoa( u_long, char *, int );
    extern char *strcpy(char *, const char *);
#else
    u_short FRSLocateStat();
    u_short FRSLocateParam();
    int hj_strcmpi();
    char *hj_ultoa(); 
    extern char *strcpy();
#endif

#if FRS_DCE_SUPPORT
    extern CMICCB  gCmiCCB;
#endif

extern fr_t fr_wtab[] ;

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

  PROCEDURE NAME:    hj_strcmpi

  DESCRIPTION:
    Performs a lower case comparision of strings. 

  PARAMETERS:

  char * string1  (string to compare)
  char * string2  (string to compare)

  RETURNS:
    < 0  - string1 less than string2
    = 0  - string1 and string2 are identical
    > 0  - string1 greater than string2

  NOTES:
    stricmp and strcmpi do not belong to a compatibility standard (ANSI, UNIX).
    Therefore, this routine is composed of function calls that belong to the
    ANSI standard. However, the function call should not be used outside of
    this file. The length of the string buffers used by the routine are 
    purposely set to a small value.
*****************************************************************************/
int
hj_strcmpi(string1, string2)
char *string1, *string2;
{
char lc_string1[STRCMP_BUFFER];
char lc_string2[STRCMP_BUFFER];
char *cp;

    /** We begin by clearing some buffer space and copying the strings
	** to temporary storage.
	**/
    memset(lc_string1, 0, STRCMP_BUFFER);    /* Null this buffer and.... */
    strcpy(lc_string1, string1);             /* copy the string... */
    memset(lc_string2, 0, STRCMP_BUFFER);    /* Null this buffer and.... */
    strcpy(lc_string2, string2);             /* copy the string... */

    /** Convert both strings to lowercase...
	**/
    for( cp = (char *) &lc_string1[0] ; *cp != (char) NULL; cp++ )
	   *cp = (char) tolower(*cp);
    for( cp = (char *) &lc_string2[0] ; *cp != (char) NULL; cp++ )
	   *cp = (char) tolower(*cp);

    /** Return the results from ANSI standard strcmp function call
	**/
    return(strcmp(lc_string1, lc_string2));
}

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

  PROCEDURE NAME:    hj_ultoa

  DESCRIPTION:
    converts 'value' to a null-terminated character string and stores the result
    ( up to 33 bytes ) in 'string'. No overflow checking is performed. The 
    'radix' argument specifies the base of 'value'; 
    it must be in the range 2 - 36.

  PARAMETERS:

    value - number to be converted.
    string - string result.
    radix - base of 'value'

  RETURNS:
	a pointer to 'string'. There is no error return. 

  NOTES:
    This private function is provided because there are no standards defined for
    this function (ANSI or UNIX).
*****************************************************************************/

char *
hj_ultoa( value, string, radix )
u_long value;
char *string;
int radix;
{
char buf[33];
char *p;


    buf[32] = '\0';

    if(!radix)
	return((char *)NULL) ;

    p = &buf[32];
    do {
	  p--;
	  *p = (char)(value % (u_long)radix);
	  *p += ((*p < (char)0x0a) ? (char)'0' : (char)'7');
	  }
    while ( value /= radix );

    return( strcpy(string,p) );
}

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

  PROCEDURE NAME:    FRSCONF_Init

  DESCRIPTION:

  This function is responsible for creating the LCB and CCB tables.

  PARAMETERS:        None

  RETURNS:

  FRS_SUCCESS or FRS_ENABLED or FRS_NO_BUFFERS.

  NOTES:

  This function expects that somebody has appropriately initialised

    (a) gGlobalCfg.frsTotalLinks, and 
    (b) gGlobalCfg.frsTotalPLLs 

  such that table space allocated to handle all frmrly links and PLLs on these
  links is sufficient during runtime operations. Ideally, we would want to 
  esize these tables depending upon actual real-time usage, though the cost of
  wasted table space is minimal considering that the tables store pointers to 
  dynamically allocated ctrl blks rather than the ctrl blks themselves.

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

u_short 
FRSCONF_Init()
{
	int i ;
    FRS_PLL_CFG *pPLLScan;

    /* Checkpoint Charlie...
	*/
    if(gFrsEnabled == TRUE)
	   return(FRS_ENABLED);

    /* Create and initialise the LCBtable...
	*/

#ifdef	STATIC_MEMORY

	frsLCBtable = frsLCBtable_ptrs ;
	frsCCBtable = frsCCBtable_ptrs ;
   	memset((void *)frsCCBtable, '\0', gGlobalCfg.frsTotalPLLs * sizeof(CCB *));
   	memset((void *)frsLCBtable, '\0', gGlobalCfg.frsTotalLinks * sizeof(LCB *));
#else
    if((frsLCBtable =
	 (LCB **)m_MMGR_SPL_ALLOC(gGlobalCfg.frsTotalLinks * sizeof(LCB *))) == 
	 (LCB **)NULL)
	   return(FRS_NO_BUFFERS);

    memset((void *)frsLCBtable, '\0', gGlobalCfg.frsTotalLinks * sizeof(LCB *));

    /* Create and initialise the CCBtable.. If allocation fails, free LCB table.
	*/
    if((frsCCBtable = 
	   (CCB **)m_MMGR_SPL_ALLOC( gGlobalCfg.frsTotalPLLs * sizeof(CCB *))) ==
	   (CCB **)NULL)
    {
	   m_MMGR_SPL_FREE((char *)frsLCBtable);
	   return(FRS_NO_BUFFERS);
    }

    memset((void *)frsCCBtable, '\0', gGlobalCfg.frsTotalPLLs * sizeof(CCB *));

#endif

#if FRS_DCE_SUPPORT
    gCmiCCB.cmi_polling_interval = (u_char) gGlobalCfg.frsCMI_mT1;
#endif

   pPLLScan = (FRS_PLL_CFG *)&gGlobalCfg._frmrly_ccbcfg[0];
   for(i=0; (i < FRS_MAX_CHANNELS);i++, pPLLScan++)
	    pPLLScan->frs_pll_link = FRS_DELETED ;

    gFrsEnabled = TRUE;
    return(FRS_SUCCESS);
}

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

  PROCEDURE NAME:    FRSCONF_InitLink

  DESCRIPTION:

  This function is responsible for initialising the link with
  appropriate configured parameters. An LCB will be created and initialised.
  Further, an lmi ccb will be also created if the link is configured to have LMI

  PARAMETERS: 
  FRS_CFG *  --- pointer to the configuration info
  proc_id    ---
  LCB **     ---

  RETURNS:

  FRS_SUCCESS or one of the API return codes from frs.h.


  NOTES:

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

u_short 
FRSCONF_InitLink( pLinkCfg, Pdownstack, ppLCB)
FRS_LINK_CFG *pLinkCfg;
proc_id Pdownstack;
LCB **ppLCB;
{
    CCB *pCCB;
    LCB *pLCB;
    LMICCB *pLmiCCB;
    int n ;
    fr_t *lp ;

    *ppLCB = LCBNULL;

    /* Allocate an LCB and record it in the LCB table...
	*/

    for(n = NLCBS, lp = fr_wtab; n--; lp++)
	if (lp->snid == pLinkCfg->frs_link_snid) break ;

    if (n == -1) return (FRS_INIT_FAILURE) ;

#ifndef	STATIC_MEMORY
    if((pLCB = (LCB *)m_MMGR_SPL_ALLOC( sizeof(LCB))) == LCBNULL)
    {
	   /* Error return...
	    */
	   return(FRS_NO_BUFFERS);
    }
#else
    pLCB = lp->pLCB ;
#endif


    /* Save the task_id of the process below us...
	*/
    pLCB->child_id = Pdownstack;
    pLCB->lcb_opt = (u_short) pLinkCfg->frs_link_flags;
    pLCB->State = FRS_LKDWN;
    pLCB->num_plls = 0;

    if( m_FRS_IS_LINK_ENCAP(pLCB) )
    {
	   pLCB->fMakeFrame   = (MFN_PTR) encap_makeframe; 
	   pLCB->fUnMakeFrame = (FN_PTR) encap_unmakeframe;
    }
    else
    {
	   pLCB->fMakeFrame   = (MFN_PTR) FRSMakeFrame; 
	   pLCB->fUnMakeFrame = (FN_PTR) FRSUnMakeFrame;
    }

    pLCB->my_cfg = pLinkCfg;

    /* Initialise the LMI CCB, if necessary...
	* For DTE links: Explicit configuration is required.
	* For DCE links: Automatic configuration will occur.
	*/
    if((m_FRS_IS_ANY_LMI_CFG(pLCB)) || (m_FRS_IS_LINK_DCE(pLCB)))
    {

#ifdef	STATIC_MEMORY

	   if((pLmiCCB = 
		  (LMICCB *) m_MMGR_LMICCB_ALLOC()) ==
		  (LMICCB *) NULL)
	   {
		  /* Cleanup and return...
		   */
		  m_MMGR_LCB_DEALLOC(pLCB);
		  return(FRS_NO_BUFFERS);
	   }


#else
	   if((pLmiCCB = 
		  (LMICCB *) m_MMGR_SPL_ALLOC(sizeof(LMICCB))) ==
		  (LMICCB *) NULL)
	   {
		  /* Cleanup and return...
		   */
		  m_MMGR_SPL_FREE(pLCB);
		  return(FRS_NO_BUFFERS);
	   }


#endif	   
	   /* zero out the entire Lmi CCB */
	   m_MMGR_MEMSET(pLmiCCB, 0, sizeof(LMICCB));

	   pLCB->lmi_ccb = pLmiCCB;
	   pLCB->lmi_dlci = (u_short) pLinkCfg->frs_link_lmi_dlci;

	   /* Populate the common ccb...
	    */
	   pCCB = &pLmiCCB->common_ccb;
	   pCCB->plcb = pLCB;
	   pCCB->dlci = pLinkCfg->frs_link_lmi_dlci;
	   mSetDLCI(pCCB);
	   pCCB->my_cosb.State = COSB_NORMAL;
	   m_FRS_SET_CCB_LMI_CCB(pCCB);

	   /* Configuration Parameters...
	    */
	   pLmiCCB->lmi_alarm_event_thresh = (u_char)pLinkCfg->frs_link_mN2;
	   pLmiCCB->lmi_reset_event_count = (u_char)pLinkCfg->frs_link_mN3;

	   /* Set optional configuration parameters...
	    */
	   if(m_FRS_IS_LINK_DCE(pLCB))
	   {
	   /* DCE only...
	    * A timer is created at this time but not used until the DTE
	    * side starts polling us for LMI responses. This timer gets activated 
	    * at that time.
	    */
		  pLmiCCB->lmi_max_stat_enq_count = (u_char)pLinkCfg->frs_link_mN4;
		  pLmiCCB->lmi_max_poll_interval = (u_char)pLinkCfg->frs_link_mT2;
		  pLmiCCB->lmi_xcess_poll_interval = (u_char)pLinkCfg->frs_link_mT3;
		  m_LMI_SET_F_FULL_STAT(pLmiCCB);

	   /* Create poll check timer...don't start it yet.
	    */
		  pLCB->LmiTimerId =
		    FRSTMR_create( pLCB->hLCB, FRSLmiPollVer, LMITIMR );
	   }
	   else
	   {
	   /* DTE only...
	    */
		  pLmiCCB->lmi_polling_interval = (u_char)pLinkCfg->frs_link_mT1;
		  pLmiCCB->full_status_int_count = (u_char)pLinkCfg->frs_link_mN1;
		  m_Set(pLmiCCB->lmi_flag,(LMI_FORCE_FULL_STATUS));

	   /* Create the polling timer...
	    * Initially, LMI will poll at the rate of once per second to get
	    * an initial response from the switch. After first contact, poll
	    * rate will drop to the configured polling interval.
	    */
		  pLCB->LmiTimerId =
		    FRSTMR_create( pLCB->hLCB, FRSLmiHeartbeatPoll, LMITIMR );
	   }
    }

    /* Return OK value...
	*/
    *ppLCB = pLCB;
    return(FRS_SUCCESS);
}

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

  PROCEDURE NAME:    FRSCONF_UnInitLink

  DESCRIPTION:

  This function is responsible for uninitialising the link.
  The LCB will be freed.
  Further, the lmi ccb will be freed if the link is configured to have LMI.

  PARAMETERS: 
  link -- 	link to uninitialize

  RETURNS:

  FRS_SUCCESS or one of the API return codes from frs.h.


  NOTES:

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

u_short 
FRSCONF_UnInitLink(link)
u_long link;
{
    LCB *pLCB;


    pLCB = frsLCBtable[link-1];

    /* free the lmi ccb (if previously allocated) */

    if (pLCB->lmi_ccb)
    {
#ifdef	STATIC_MEMORY
	m_MMGR_LMICCB_DEALLOC(pLCB->lmi_ccb);
#else
	m_MMGR_SPL_FREE(pLCB->lmi_ccb);
#endif
    }

    /* free the LCB */
#ifdef	STATIC_MEMORY
    m_MMGR_LCB_DEALLOC(pLCB);
#else
    m_MMGR_SPL_FREE(pLCB);
#endif

    frsLCBtable[link-1] = 0;

    return(FRS_SUCCESS);
}

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

  PROCEDURE NAME:    FRSCONF_Disable

  DESCRIPTION:

  This procedure disables the frame relay stack from further operations.
  It frees up all memory resources utilised by itself and gracefully
  effects a shutdown of all comm operations...

  PARAMETERS:

  None

  RETURNS:

  FRS_SUCCESS

  NOTES:

*****************************************************************************/
u_short
FRSCONF_Disable()
{

    CCB *pCCB;
    LCB *pLCB;
    u_short i;

    /* Cleanup all CCBs...
	*/
    for(i=0; i < gGlobalCfg.frsTotalPLLs; i++)
    {
	   if((pCCB = frsCCBtable[i]) == CCBNULL)
		  continue;

	   FRSUnlinkCCB( pCCB);
    }

    /* Link-level Cleanup...
	*/
    for(i=0; i < gGlobalCfg.frsTotalLinks; i++)
    {
	   pLCB = frsLCBtable[i];

	   /* Inform target system of link down condition...
	    */
	   sys_link_down(frsLCBtable[i]);

	   /* Inform low-level driver to shutdown links...
	    */
	   FRSDRV_LinkControl(frsLCBtable[i], FRS_LINK_EDT);

	   /* If LMI was configured, free lmi ccb...
	    */
	   if(m_FRS_IS_ANY_LMI_CFG(pLCB))
#ifdef	STATIC_MEMORY
		  m_MMGR_LMICCB_DEALLOC(pLCB->lmi_ccb);
#else
		  m_MMGR_SPL_FREE((char *) pLCB->lmi_ccb);
#endif
	   /* Finally, free the lcb...
	    */
#ifdef	STATIC_MEMORY
	   m_MMGR_LCB_DEALLOC(pLCB);
#else
	   m_MMGR_SPL_FREE((char *) pLCB);
#endif
    }

    /* Free the LCB and CCB tables...
	*/
#ifdef	STATIC_MEMORY    

#else
    m_MMGR_SPL_FREE((char *) frsCCBtable);
    m_MMGR_SPL_FREE((char *) frsLCBtable);
#endif
    gFrsEnabled = FALSE;
    return(FRS_SUCCESS);
}

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

  PROCEDURE NAME:    FRSCONF_GetStatistics

  DESCRIPTION:

  This procedure returns a pointer to a statistic value for a global, link or
  PLL stat.

  PARAMETERS:

  u_short --- link number (always base+1)
  u_short --- dlci
  u_short --- stat id(from frs.h)
  char *  --- Pointer to an array where the statistic value will be stored.
		    char array must be at least 17 chars long.
  
  RETURNS:

  Return value is FRS_SUCCESS or a failure code
  If FRS_SUCCESS, pValue will contain a statistic value...

  NOTES:

*****************************************************************************/
u_short
FRSCONF_GetStatistics(snid, dlci, which_stat, protocol, pValue)
u_long snid;
u_long dlci;
u_long which_stat;
u_long protocol;
char  *pValue;
{
    u_long *pNativeValue;
    u_short rcode;
    u_long link;

    if(!m_FRS_CHECK) return FRS_DISABLED;

    if(!(link = snid_to_hLCB(snid))) return FRS_DISABLED ;

    if((rcode =
	   FRSLocateStat(link, dlci, which_stat, protocol, &pNativeValue)) == 
		FRS_SUCCESS)
    {
	   hj_ultoa(*pNativeValue, pValue, 10);
	   return(FRS_SUCCESS);
    }
    else
	   return(rcode);
}

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

  PROCEDURE NAME:    FRSCONF_GetResetStat

  DESCRIPTION:

  This procedure returns a statistic value and resets it.

  PARAMETERS:

  u_short --- link number
  u_short --- dlci
  u_short --- stat id(from frs.h)
  char *  --- Pointer to an array where the statistic value will be stored.
		    char array must be atleast 17 chars long.
  
  RETURNS:

  Return value is FRS_SUCCESS or a failure code
  If FRS_SUCCESS, pValue points to a statistic value...

  NOTES:

*****************************************************************************/
u_short
FRSCONF_GetResetStat(snid, dlci, which_stat, protocol, pValue)
u_long snid;
u_long dlci;
u_long which_stat;
u_long protocol;
char  *pValue;
{
    u_long *pNativeValue;
    u_short rcode;
    u_long link;

    if(!m_FRS_CHECK) return FRS_DISABLED;

    if(!(link = snid_to_hLCB(snid))) return FRS_DISABLED ;

    if((rcode =
	   FRSLocateStat(link, dlci, which_stat, protocol, &pNativeValue)) == 
		  FRS_SUCCESS)
    {
	   hj_ultoa(*pNativeValue, pValue, 10);
	   *pNativeValue = 0;
    }
    else
	   return(rcode);

    return(FRS_SUCCESS);
}

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

  PROCEDURE NAME:    FRSLocateStat

  DESCRIPTION:

  A function used to locate the address of a statistic variable.  For
  Link and PLL statistics, pass protocol id PID_NONE.  Otherwise,
  for Protocol statistics, pass the corresponding PID constant.

  PARAMETERS:

  u_long	--- link number (can be 0 - gGlobalCfg.frsTotalLinks+1)
  u_long	--- dlci
  u_short	--- stat id(from frs.h)
  u_long	--- protocol id(from frs.h)
  u_long ** --- Address of a pointer to a statistic value.
  
  RETURNS:

  Return value is FRS_SUCCESS or a failure code
  If FRS_SUCCESS, pValue points to a statistic value...

  NOTES:

*****************************************************************************/
u_short
FRSLocateStat( link, dlci, which_stat, protocol, pValue )
u_long link;
u_long dlci;
u_long which_stat;
u_long protocol;
u_long  **pValue;
{
    CCB *pCCB;
    LCB *pLCB;
    u_long ndx = which_stat & 0xFF;
    u_long *pVal;

    static u_long *global_stat_array[2] = 
	   {
	  (u_long *)&gGlobalStats.global_gen_stats,
	  (u_long *)&gGlobalStats.global_err_stats,
	   };

    /** Asking for system statistics ?
	**/
    if( m_FRS_SYSTEM_STATISTICS( link, dlci ) )
    {

#if FRS_DCE_SUPPORT

	   /* CMI stats ?
	    */
	   if((which_stat >> 8) == CMI_STATS)
	   {
		  switch( which_stat )
		  {
		  case FRS_CMI_TOTL_STAT_ENQS :
			 pVal = (u_long *) 
			   ((u_int *) &(gCmiCCB.cmi_total_status_sent));
			 break;

		  case FRS_CMI_TOTL_STAT_RCVD :
			 pVal = (u_long *)
			 ((u_int *) &(gCmiCCB.cmi_total_status_rcvd));
			 break;

		  case FRS_CMI_FRAME_ERRORS   :
			 pVal = (u_long *)
				((u_int *) &(gCmiCCB.cmi_proto_err));
			 break;

		  case FRS_CMI_INV_MSG_RCVD   : 
			 pVal = (u_long *)
				((u_int *) &(gCmiCCB.cmi_inv_msg_rcvd));
			 break;

		  case FRS_CMI_IE_UNKNOWN     :
			 pVal = (u_long *)
				((u_int *) &(gCmiCCB.cmi_no_ie_rep_type));
			 break;

		  case FRS_CMI_IE_MISSING     :
			 pVal = (u_long *)
				((u_int *) &(gCmiCCB.cmi_unsupp_ie_msg));
			 break;

		  case FRS_CMI_UNEXP_PVC_STAT :
			 pVal = (u_long *)
				((u_int *) &(gCmiCCB.cmi_unexpected_pvc_num));
			 break;

		  case FRS_CMI_UNEXP_LINK_STAT:
			 pVal = (u_long *)
				((u_int *) &(gCmiCCB.cmi_unexpected_link_num));
			 break;

		  default:
			 return(FRS_INVALID);

		  }/* end switch */
	   }/* end if CMI STATS */

    else

#endif

	   /* Return GLOBAL stats...
	    */
	   pVal = &global_stat_array[which_stat >> 8][ndx];

    *pValue = pVal;
    return(FRS_SUCCESS);

    }/* end if system statistics */

    /** Asking for specific link statistics....
	**/
    if(m_FRS_LINK_STATISTICS(link, dlci))
    {
	   /* Link stats ?
	    */
	   if((pLCB = frsLCBtable[link-1]) == LCBNULL)
		  return(FRS_INVALID);

	   /* Link error stats ?
	    */
	   if((which_stat >> 8) == ERROR_STATS )
		  pVal = &((u_long *)&pLCB->my_err_stats)[ndx];

	   /* LMI stats ?
	    */
	   else if((which_stat >> 8) == LMI_STATS)
	   {
                  /* check if LMI is enabled before getting
                     LMI statistics
                   */
                  if (!m_FRS_IS_ANY_LMI_CFG(pLCB))
                         return(FRS_INVALID);

		  switch( which_stat )
		  {
		  case FRS_LMI_NET_SEQ_NUM:
			 pVal = (u_long *)
				   ((u_char *)&(pLCB->lmi_ccb->net_seq_num));
			 break;

		  case FRS_LMI_LOCAL_SEQ_NUM:
			 pVal = (u_long *)(
				   (u_char *)&(pLCB->lmi_ccb->local_seq_num));
			 break;

		  case FRS_LMI_STAT_ENQ_CNT:
			 pVal = (u_long *)
				   ((u_char *)&(pLCB->lmi_ccb->stat_enq_count));
			 break;

		  case FRS_LMI_TOTL_STAT_ENQS:
			pVal = (u_long *)
				  ((u_int *)&(pLCB->lmi_ccb->lmi_total_stat_enqs));
			 break;

		  case FRS_LMI_FULL_STAT_ENQS:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_full_stat_enqs));
			 break;

		  case FRS_LMI_TOTAL_STATUS:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_total_status));
			 break;

		  case FRS_LMI_FULL_STATUS:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_full_status));
			 break;

		  case FRS_LMI_TOTL_UPDATES:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_total_updates));
			 break;

		  case FRS_LMI_INV_MSG_RCVD:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_inv_msg_rcvd));
			 break;

		  case FRS_LMI_NO_IE_REP_TYPE:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_no_ie_rep_type));
			 break;

		  case FRS_LMI_NO_IE_KA:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_no_ie_keep_alive));
			 break;

		  case FRS_LMI_LOST_SEQ_NUM:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_lost_seq_num_sync_event));
			 break;

		  case FRS_LMI_STAT_TIMEOUT:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_stat_resp_timeout));
			 break;

		  case FRS_LMI_UNSUPP_IE_MSG:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_unsupp_ie_msg));
			 break;

		  case FRS_LMI_UNEXP_PVC_NUM:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_unexpected_pvc_num));
			 break;

		  case FRS_LMI_UNEXP_PVC_STAT:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_unexpected_pvc_stat_msg));
			 break;

		  case FRS_LMI_STAT_RATE_PLUS:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_stat_enq_rate_plus));
			 break;

		  case FRS_LMI_ENQ_TIMEOUT:
			 pVal = (u_long *)
				   ((u_int *)&(pLCB->lmi_ccb->lmi_stat_enq_timeout));
			 break;

		  default:
			 return(FRS_INVALID);

		  }/* end switch */      
	   }/* end else-if LMI_STATS */

	   /* Link general stats ?
	    */
	   else if( which_stat == FRS_LINK_CREATE_TIME )
		  pVal = (u_long *)((frs_time *)&(pLCB->link_CreateTime));

	   else if( which_stat == FRS_LINK_ERR_TIME )
		  pVal = (u_long *)((frs_time *)&(pLCB->err_time));

	   else if( which_stat == FRS_LINK_ERR_TYPE )
		  pVal = (u_long *)((u_short *)&(pLCB->err_type));

	   else
		  pVal = &((u_long *)&pLCB->my_stats)[ndx];

    }/* end if */

    else
    {
	   /** Make sure the link number is NOT 0
	    **/
	   if( link == 0 )
		  return( FRS_INVALID );

	   /* PLL stats...
	    */
	   if((pLCB = frsLCBtable[link-1]) == LCBNULL)
		  return(FRS_INVALID);

	   if((pCCB = FRSLocateCCBbyDLCI( pLCB, dlci)) == CCBNULL)
		  return(FRS_INVALID);

	   /* If no protocol specified...
	    */
	   if((protocol == PID_NONE) )
	   {
	   /* ...and if which_stat has no prefix, then look for
	    * variable in general stats...
	    */
		  if( !(which_stat >> 8) )
			 pVal = &((u_long *)&pCCB->my_stats)[ndx];
		  /* ...or in the injection stats...
		   */
		  else if( (which_stat >> 8) == INJ_MGMNT_STATS )
			 pVal = &((u_long *)&pCCB->my_inj_stats)[ndx];

		  /*
		   * Circuit Stats ?
		   */
		  else if((which_stat >> 8) == CIRCUIT_STATS)
		  {
			 switch( which_stat )
			 {
			 case FRS_CCB_REV_COUNT:
				pVal = (u_long *)
					  ((u_int *)&(pCCB->my_rev_count));
				break;

			 case FRS_CCB_CREATE_TIME:
				pVal = (u_long *)
					  ((frs_time *)&(pCCB->CreateTime));
				break;

			 case FRS_CCB_STAT_CHNG:
				pVal = (u_long *)
					  ((frs_time *)&(pCCB->LastStatChgTime));
				break;

			 case FRS_INJ_NORM_CNT:
				pVal = (u_long *)
					  &(pCCB->my_inj_stats.InjNormalFrames);
				break;

			 case FRS_INJ_EXCESS_CNT:
				pVal = (u_long *)
					  &(pCCB->my_inj_stats.InjExcessFrames);
				break;

			 case FRS_INJ_HOLD_CNT:
				pVal = (u_long *)
					  &(pCCB->my_inj_stats.InjHoldFrames);
				break;

			 case FRS_FALLBACKS:
				pVal = (u_long *)
					  &(pCCB->circuit_stats.total_fallbacks);
				break;

			 case FRS_FALLFORWARDS:
				pVal = (u_long *)
					  &(pCCB->circuit_stats.total_fallforwards);
				break;

			 case FRS_TOTL_RBITS:
				pVal = (u_long *)
					  &(pCCB->circuit_stats.total_stat_Rbit);
				break;

			 case FRS_CCB_WRITEQ:
				pVal = (u_long *)
					  ((u_short *)&(pCCB->Write_Queue.qcnt));
				break;

			 case FRS_CCB_READQ:
				pVal = (u_long *)
					  ((u_short *)&(pCCB->Read_Queue.qcnt));
				break;

			 default:
				return(FRS_INVALID);
			 }
		  }
		  /* Protocol Errors ?
		   */
		  else if((which_stat >> 8) == PROTOCOL_ERR_STATS )
			 pVal = &((u_long *)&pCCB->encap->prot_errs)[ndx];
	   }
	   /* Otherwise, a protocol is specified and we need to look
	    * for the variable in either the fragmentation stats or
	    * the general stats for the specified protocol.
	    */
	   else
	   {
		  if((which_stat >> 8) == FRAGMENT_STATS)
		  {
			 if( which_stat == FRS_PROT_CREATE_TIME )
				pVal = (u_long *)((frs_time *)
					  &(pCCB->encap->mp[protocol]->prot_CreateTime));
			 else
				pVal = &((u_long *)
					  &pCCB->encap->mp[protocol]->frag_stats)[ndx];
		  }
		  else
			 pVal = &((u_long *)
				   &pCCB->encap->mp[protocol]->gen_stats)[ndx];
	   }/* end else */
    }/* end else */

    *pValue = pVal;
    return(FRS_SUCCESS);
}

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

  PROCEDURE NAME:    FRSCONF_GetConfig

  DESCRIPTION:

  This procedure will return the value of a cfg parameter for the global,
  link or PLL entity.

  PARAMETERS:

  u_long  --- link number
  u_long  --- dlci
  u_short --- param id(from frscfg.h)
  char *  --- Pointer to an array where the parameter value will be stored.
		    char array must be atleast 17 chars long.

  RETURNS:

  If return value is FRS_SUCCESS, param_value will contain the value.

  NOTES:

*****************************************************************************/
u_short
FRSCONF_GetConfig(snid, dlci, param_id, param_value)
u_long snid;
u_long dlci;
u_long param_id;
char *param_value;
{
    void *pParam;
    u_short rcode, link_is_dce;
    u_long  val;
    u_long link ;
    
    if(!m_FRS_CHECK) return FRS_DISABLED;

    if(!(link = snid_to_hLCB(snid))) return FRS_DISABLED ;

    if((rcode = FRSLocateParam((u_short)link, dlci, param_id, &pParam)) == FRS_SUCCESS)
	   {
	   /** System parameter....
	    **/
	   if( (m_FRS_SYSTEM_PARAMETERS(link, dlci)) == TRUE )
		  {
		  switch( param_id )
			 {
			 case FRS_SYS_MAX_LINKS:
			 case FRS_SYS_MAX_PLLS:

#if !(USE_CONTIGUOUS_DATA)

			 case FRS_SYS_BUFSIZE:
#endif

#if FRS_DCE_SUPPORT

			 case FRS_SYS_NODE_NUM:
			 case FRS_SYS_CMI_mT1:
#endif
				val = (u_long) (*((u_short *)pParam));
				hj_ultoa(val, param_value, 10);
				break;

			 case FRS_SYS_SW_REL:
				strcpy(param_value, (char *)pParam);
				break;

			 default:
				rcode = FRS_INVALID;
				break;
			 }

		  return(rcode);
		  }

	   else if( link == 0 )
		  return( FRS_INVALID );

	   /** Current link information is extracted from the off-line
	    ** database....
	    **/
	   if(dlci == FRS_LINK_INFORMATION)
	   {
		  /** The link must be configured.....
		   **/
		  if( m_dbFRS_IS_LINK_ENBL(link-1) != TRUE )
			 return( FRS_INVALID );

		  /** What kind of link is this ?
		   **/
		  link_is_dce = (u_short) (m_dbFRS_IS_LINK_DCE(link-1) ? TRUE: FALSE);

		  /** Which parameter ?
		   **/
		  switch( param_id)
			 {
			 case FRS_LINK_CONF_NAME:
				strcpy(param_value, (char *)pParam);
				return(rcode);

			 case FRS_LINK_ENABLE:
				val = (u_long) (m_dbFRS_IS_LINK_ENBL(link-1) ? TRUE: FALSE);
				break;

    /** Currently Not Supported **/
			 case FRS_LINK_MULTI:
				return( FRS_INVALID );   /* Remove this when supported */
    /***
				val = (u_long) (m_dbFRS_IS_MULTI_CFG(link-1) ? TRUE: FALSE);
				break;
	***/

			 case FRS_LINK_LMIXON:
				val = (u_long)(m_dbFRS_IS_LMI_XONXOFF(link-1) ? TRUE:FALSE);
				break;

			 case FRS_LINK_STATS:
				val = (u_long)(m_dbFRS_IS_STATS_ENBL(link-1) ? TRUE: FALSE);
				break;

			 case FRS_LINK_LMIBAND:
				val = (u_long)(m_dbFRS_IS_LMI_BW_CTRL(link-1) ? TRUE:FALSE);
				break;

			 case FRS_LINK_TYPE:
				val = (u_long) (m_dbFRS_IS_LINK_DCE(link-1) ? TRUE: FALSE);
				break;

			 case FRS_LINK_BECN:
				val = (u_long)(m_dbFRS_IS_BECN_XONXOFF(link-1) ?TRUE:FALSE);
				break;

			 case FRS_LINK_INJECT:
				val = (u_long) 
					((m_dbFRS_IS_INJ_MGR_FORCED(link-1) ? FRS_INJ_FORCED:
					(m_dbFRS_IS_INJ_MGR_BUFFERED(link-1)? FRS_INJ_BUFFERED:
												FRS_INJ_STANDARD)));
				break;

			 case FRS_LINK_ENCAP:
				val = (u_long)(m_dbFRS_IS_LINK_ENCAP(link-1) ? TRUE:FALSE);
				break;

			 case FRS_LINK_LMI:
				val = (u_long)(m_dbFRS_IS_ANY_LMI_CFG(link-1) ? TRUE:FALSE);
				if( val != (u_long) FALSE )
				    {
				    val = 
					(u_long)((m_dbFRS_IS_PJ_LMI_CFG(link-1) ?FRS_LMI_PLAIN:
						    (m_dbFRS_IS_S_LMI_CFG(link-1)  ?FRS_LMI_STRAT:
						    (m_dbFRS_IS_ANNEXD_CFG(link-1) ?FRS_LMI_ANXD:
												    FRS_LMI_ANXA))));
				    }
				break;

			 case FRS_LINK_mT1:
				if( link_is_dce )
				    return( FRS_INVALID );
				else
				    val = (u_long)(*((u_short *)pParam));
				break;

			 case FRS_LINK_mT2:
				if( !link_is_dce )
				    return( FRS_INVALID );
				else
				    val = (u_long)(*((u_short *)pParam));
				break;

			 case FRS_LINK_mT3:
				if( !link_is_dce )
				    return( FRS_INVALID );
				else
				    val = (u_long)(*((u_short *)pParam));
				break;

			 case FRS_LINK_mN1:
				if( link_is_dce )
				    return( FRS_INVALID );
				else
				    val = (u_long)(*((u_short *)pParam));
				break;

			 case FRS_LINK_mN4:
				if( !link_is_dce )
				    return( FRS_INVALID );
				else
				    val = (u_long)(*((u_short *)pParam));
				break;
 
			 case FRS_LINK_mP1:
		    val = (u_long)(*((u_short *)pParam));
		    break;

    /** Currently Not Supported **/
			 case FRS_LINK_mP10:
				return( FRS_INVALID );
				
			 case FRS_LINK_FLAGS:
			 case FRS_LINK_mN2:
			 case FRS_LINK_mN3:
			 case FRS_LINK_STATE:
			 default:
				val = (u_long)(*((u_short *)pParam));
				break;
			 }

		  }
	   else
		  {
		  /** Must be PLL information that's being requested here...
		   **/
		  switch( param_id)
			 {
			 case FRS_PLL_CONF_NAME:
				strcpy(param_value, (char *)pParam);
				return(rcode);

			 case FRS_PLL_BCMAX:
			 case FRS_PLL_BCMIN:
			 case FRS_PLL_BE:
			 case FRS_PLL_CIR:
				val = (u_long) (*((u_long *)pParam));
				break; 

			 case FRS_PLL_LINK:
			 case FRS_PLL_NUMBER:
			 case FRS_PLL_DLCI:
			 case FRS_PLL_CMP:
#if FRS_DCE_SUPPORT
			 case FRS_PLL_FWDNODE:
			 case FRS_PLL_FWDLINK:
			 case FRS_PLL_FWDDLCI:
#endif
			 case FRS_PLL_STATE:
			 default:
				val = (u_long) (*((u_short *)pParam));
				break;
			 }
		  }

	   /** Convert the numeric value to ascii
	    **/
	   hj_ultoa(val, param_value, 10);
	   }

    return(rcode);
}

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

  PROCEDURE NAME:    FRSCONF_SetConfig

  DESCRIPTION:

  This procedure will check for valid parameters, then call the 
  appropriate function to set the configuration for a link or a pll.

  PARAMETERS:

  u_short --- link number
  u_short --- dlci
  u_short --- param id(from frscfg.h)
  char * --- ASCII representation of the parameter's value

  RETURNS:

  FRS_SUCCESS or one of the API return codes from frs.h.

  NOTES:

*****************************************************************************/
u_short
FRSCONF_SetConfig(snid, dlci, param_id, param_value_str)
u_long snid;
u_long dlci;
u_long param_id;
char *param_value_str;
{
    u_short rcode;
    u_long link;

    if(!m_FRS_CHECK) return FRS_DISABLED;

    if(!(link = snid_to_hLCB(snid))) return FRS_DISABLED ;

    if((rcode =
	   FRSCONF_ValidParam( link, dlci, param_id, param_value_str)) !=
		  FRS_SUCCESS )
	   return( rcode );

#if (!(USE_CONTIGUOUS_DATA) || (FRS_DCE_SUPPORT))

    /** For updating system configurables....
	**/
    if( m_FRS_SYSTEM_PARAMETERS(link, dlci) )
	   rcode = FRSCONF_SetSysConfig(link, dlci, param_id, param_value_str);

    else

#endif

    /** For updating link configurables....
	**/
    if( m_FRS_LINK_PARAMETERS(link, dlci) )
	   rcode = FRSCONF_SetLinkConfig(link, dlci, param_id, param_value_str);
    else

    /** For updating PLL configurables....
	**/
	   rcode = FRSCONF_SetPLLConfig(link, dlci, param_id, param_value_str);

    return( rcode );
}

/**/
#if (!(USE_CONTIGUOUS_DATA) || (FRS_DCE_SUPPORT))
/*****************************************************************************

  PROCEDURE NAME:    FRSCONF_SetSysConfig

  DESCRIPTION:

  This procedure will set the value of a system parameter.

  PARAMETERS:

  u_short --- link number
  u_short --- dlci
  u_short --- param id(from frscfg.h)
  char * --- ASCII representation of the parameter's value

  RETURNS:

  FRS_SUCCESS or one of the API return codes from frs.h.

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

u_short
FRSCONF_SetSysConfig(link, dlci, param_id, param_value_str)
u_long link;
u_long dlci;
u_long param_id;
char *param_value_str;
{
    void *pParam;
    u_short rcode;
    CFG_UPDATE update_info;

    if((rcode = FRSLocateParam(link, dlci, param_id, &pParam)) == FRS_SUCCESS)
	   {
	   switch(param_id)
		  {

#if FRS_DCE_SUPPORT

		  case FRS_SYS_NODE_NUM:  /* read-write */
		  case FRS_SYS_CMI_mT1:   /* read-write */
			 *((u_short *) pParam) = (u_short) atoi(param_value_str);
			 break;
#endif

#if !(USE_CONTIGUOUS_DATA)

		  case FRS_SYS_BUFSIZE:  /* read-write (special) */
			 *((u_short *) pParam) = (u_short) atoi(param_value_str);
			 break;
#endif
		  default:
			 return( FRS_INVALID );
		  }/* end switch */
		  
	   /** Populate the update_info structure....
	    **/
	   update_info.action = FRS_CONFIG_SYS_PARAMETER_UPDATE;
	   update_info.link = (u_short) link;
	   update_info.dlci = (u_short) dlci;
	   update_info.config = &gGlobalCfg;
	   update_info.param_id = param_id;
	   update_info.param_value = pParam;
	   update_info.record_ptr = (void *) 0;

	   if( (sys_update_config( &update_info )) == FALSE )
		  {

		  }/* end if sys_update_sys call */

	   }/* end if locate param */

    return( rcode );
}
#endif /* !USE_CONTIGUOUS_DATA || FRS_DCE_SUPPORT */

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

  PROCEDURE NAME:    FRSCONF_SetLinkConfig

  DESCRIPTION:

  This procedure will set the value of a link configuration parameter.

  PARAMETERS:

  u_short --- link number
  u_short --- dlci
  u_short --- param id(from frscfg.h)
  char * --- ASCII representation of the parameter's value

  RETURNS:

  FRS_SUCCESS or one of the API return codes from frs.h.

  NOTES:

  No error checking is performed.

*****************************************************************************/
u_short
FRSCONF_SetLinkConfig(link, dlci, param_id, param_value_str)
u_long link;
u_long dlci;
u_long param_id;
char *param_value_str;
{
    LCB *pLCB;
    void *pParam;
    u_short flag_type = FRSCFG_FT_NONE;
    u_short rcode;
    u_short set_flag = FRS_FLAG_OFF;
    u_short  mask = 0;
    CFG_UPDATE update_info;
    u_short link_is_dce;

    if((rcode = FRSLocateParam(link, dlci, param_id,&pParam)) == FRS_SUCCESS)
    {
	   /** Populate some of the update_info structure....
	    **/
	   update_info.action = FRS_CONFIG_LNK_PARAMETER_UPDATE;
	   update_info.link = (u_short) link;
	   update_info.dlci = (u_short) dlci;
	   update_info.config = &gGlobalCfg;
	   update_info.record_ptr = (void *) m_dbFRS_GET_LINK_RECORD_PTR(link-1);

	   /** What kind of link is this ?
	    **/
	   link_is_dce = (u_short) (m_dbFRS_IS_LINK_DCE(link-1) ? TRUE: FALSE);

	   switch(param_id)
		  {
		  case FRS_LINK_CONF_NAME:
			 strcpy((char *)pParam, param_value_str);
			 break;

		  /* For these specific CONFIGURATION FLAGS, we: 
		   * a. Identify the storage type; 
		   * b. assign the right mask; and 
		   * c. determine whether option is to be turned on(1) or off(0)
		   */
		  case FRS_LINK_ENABLE:
			 flag_type = FRSCFG_FT_WORD;
			 set_flag = FRS_FLAG_OFF;
			 mask = FRS_OPT_LINK_ENBL;
			 if( FRSCONF_FindFlag( param_id, param_value_str ) )
				set_flag = FRS_FLAG_ON;
			 break;

		  case FRS_LINK_MULTI:
			 flag_type = FRSCFG_FT_WORD;
			 set_flag = FRS_FLAG_OFF;
			 mask = FRS_OPT_MULTI_CFG;
			 if( FRSCONF_FindFlag( param_id, param_value_str ) )
				set_flag = FRS_FLAG_ON;
			 break;

		  case FRS_LINK_LMIXON:
			 flag_type = FRSCFG_FT_WORD;
			 set_flag = FRS_FLAG_OFF;
			 mask = FRS_OPT_LMI_XONXOFF;
			 if( FRSCONF_FindFlag( param_id, param_value_str ) )
				set_flag = FRS_FLAG_ON;
			 break;

		  case FRS_LINK_STATS:
			 flag_type = FRSCFG_FT_WORD;
			 set_flag = FRS_FLAG_OFF;
			 mask = FRS_OPT_STATS_ENBL;
			 if( FRSCONF_FindFlag( param_id, param_value_str ) )
				set_flag = FRS_FLAG_ON;
			 break;

		  case FRS_LINK_LMIBAND:
			 flag_type = FRSCFG_FT_WORD;
			 set_flag = FRS_FLAG_OFF;
			 mask = FRS_OPT_LMI_BW_CTRL;
			 if( FRSCONF_FindFlag( param_id, param_value_str ) )
				set_flag = FRS_FLAG_ON;
			 break;

		  case FRS_LINK_TYPE:
			 flag_type = FRSCFG_FT_WORD;
			 set_flag = FRS_FLAG_OFF;
			 mask = FRS_OPT_LINK_DCE;
			 if( FRSCONF_FindFlag( param_id, param_value_str ) )
				set_flag = FRS_FLAG_ON;
			 break;

		  case FRS_LINK_BECN:
			 flag_type = FRSCFG_FT_WORD;
			 set_flag = FRS_FLAG_OFF;
			 mask = FRS_OPT_BECN_XONXOFF;
			 if( FRSCONF_FindFlag( param_id, param_value_str ) )
				set_flag = FRS_FLAG_ON;
			 break;

		  case FRS_LINK_INJECT:
			 /** Begin by setting all of the possible injection configuration
			  ** bits to off....
			  **/
			 flag_type = FRSCFG_FT_WORD;
			 set_flag = FRS_FLAG_OFF;

			 mask = FRS_OPT_INJ_MGR_FORCED |
				   FRS_OPT_INJ_MGR_BUFRD  |
				   FRS_OPT_INJ_MGR_STD;

			 FRSCONF_SetFlags( flag_type, set_flag, mask, pParam );

			 /** These are mutually exclusive....
			  **/
			 switch( FRSCONF_FindFlag( param_id, param_value_str) )
			 {
			 case FRS_INJ_STANDARD:
				mask = FRS_OPT_INJ_MGR_STD;
				set_flag = FRS_FLAG_ON;
				break;

			 case FRS_INJ_BUFFERED:            /* formerly enabled */
				mask = FRS_OPT_INJ_MGR_BUFRD;
				set_flag = FRS_FLAG_ON;
				break;

			 case FRS_INJ_FORCED:              /* formerly disabled */
				mask = FRS_OPT_INJ_MGR_FORCED;
				set_flag = FRS_FLAG_ON;
				break;
			 }
			 break;

		  case FRS_LINK_ENCAP:
			 flag_type = FRSCFG_FT_WORD;
			 set_flag = FRS_FLAG_OFF;
			 mask = FRS_OPT_ENCAP_ENABLE;
			 if( FRSCONF_FindFlag( param_id, param_value_str ) )
				set_flag = FRS_FLAG_ON;
			 break;

		  case FRS_LINK_LMI:
			 /** Begin by setting all of the possible LMI configuration
			  ** bits to off....
			  **/
			 flag_type = FRSCFG_FT_WORD;
			 set_flag = FRS_FLAG_OFF;
			 mask = FRS_OPT_PJ_LMI_CFG | 
				   FRS_OPT_S_LMI_CFG  | 
				   FRS_OPT_ANNEXD_CFG |
				   FRS_OPT_ANNEXA_CFG;

			 FRSCONF_SetFlags( flag_type, set_flag, mask, pParam );

			 /** These are mutually exclusive....
			  **/
			 switch( FRSCONF_FindFlag( param_id, param_value_str) )
			 {
			 case FRS_LMI_PLAIN:
				mask = FRS_OPT_PJ_LMI_CFG;
				set_flag = FRS_FLAG_ON;
				break;

			 case FRS_LMI_STRAT:
				mask = FRS_OPT_S_LMI_CFG;
				set_flag = FRS_FLAG_ON;
				break;

			 case FRS_LMI_ANXD:
				mask = FRS_OPT_ANNEXD_CFG;
				set_flag = FRS_FLAG_ON;
				break;

			 case FRS_LMI_ANXA:
				mask = FRS_OPT_ANNEXA_CFG;
				set_flag = FRS_FLAG_ON;
				break;
			 }
			 break;

		  case FRS_LINK_mT1: /* LMI Parameter */
			 /** DTE: Time for the network to respond to a user's 
			  ** STATUS ENQ
			  **/
			 if( link_is_dce )
				return( FRS_INVALID );
			 else
				*((u_short *)pParam) = (u_short)atoi(param_value_str);
			 break;

		  case FRS_LINK_mT2: /* LMI Parameter */
			 /** DCE: Frequency with which the network expects to
			  ** receive STATUS ENQ.
			  **/
			 if( !link_is_dce )
				return( FRS_INVALID );
			 else
				*((u_short *)pParam) = (u_short)atoi(param_value_str);
			 break;

		  case FRS_LINK_mT3: /* LMI Parameter */
			 /** DCE: Measurement interval for mN4
			  **/
			 if( !link_is_dce )
				return( FRS_INVALID );
			 else
				*((u_short *)pParam) = (u_short)atoi(param_value_str);
			 break;

		  case FRS_LINK_mN1: /* LMI Parameter */
			 /** DTE: Frequency that STATUS ENQ is sent by user
			  **/
			 if( link_is_dce )
				return( FRS_INVALID );
			 else
				*((u_short *)pParam) = (u_short)atoi(param_value_str);
			 break;

		  case FRS_LINK_mN4: /* LMI Parameter */
			 /** DCE: Maximum number of STATUS ENQs received by the network
			  **/
			 if( !link_is_dce )
				return( FRS_INVALID );
			 else
				*((u_short *)pParam) = (u_short)atoi(param_value_str);
			 break;

		  case FRS_LINK_mP1: /* LMI Parameter */
		         *((u_short *)pParam) = (u_short)atoi(param_value_str);
		         break;

		  case FRS_LINK_STATE:
			 flag_type = FRSCFG_FT_WORD;
			 set_flag = FRS_FLAG_OFF;
			 mask = FRS_OPT_LINK_STATE;
			 if( FRSCONF_FindFlag( param_id, param_value_str ) )
			     set_flag = FRS_FLAG_ON;
			 break;

/** Currently Not Supported **/
		  case FRS_LINK_mP10:
			 /** Maximum number of multicast groups
			  **/
			 return( FRS_INVALID );

		  case FRS_LINK_mN2: /* LMI Parameter */
			 /** DCE/DTE: Number of errors occurring on LMI link before
			  ** error is reported.
			  **/
				/** falls through **/

		  case FRS_LINK_mN3: /* LMI Parameter */
			 /** DCE/DTE: Measurement interval for mN2
			  **/
				/** falls through **/

		  case FRS_LINK_FLAGS:

				/** falls through **/

		  default:
			 *((u_short *)pParam) = (u_short)atoi(param_value_str);
			 break;
		  }/* end switch */

	   /** The following call updates the off-line database setting.
	    **/
	   if( flag_type != FRSCFG_FT_NONE )
		  FRSCONF_SetFlags( flag_type, set_flag, mask, pParam );

	   update_info.param_id = param_id;
	   update_info.param_value = (char *) &pParam;

	   /** The following target system callback function. We call the
	    ** routine with a pointer to our update_info structure as the
	    ** the only argument. If the target system returns TRUE, we do
	    ** NOT update the LCB table entry. If FALSE is returned we update
	    ** the LCB table entry for this link.
	    **/
	   if( (sys_update_config( &update_info )) == FALSE )
		  {
		  if(( pLCB = frsLCBtable[link-1] ) != LCBNULL )
			 {
			 pLCB->lcb_opt  = (u_short) pLCB->my_cfg->frs_link_flags;
			 pLCB->lmi_dlci = (u_short) pLCB->my_cfg->frs_link_lmi_dlci;

			 pLCB->lmi_ccb->full_status_int_count =
				(u_char)pLCB->my_cfg->frs_link_mN1;       

			 pLCB->lmi_ccb->lmi_alarm_event_thresh =
				(u_char)pLCB->my_cfg->frs_link_mN2;       

			 pLCB->lmi_ccb->lmi_reset_event_count =
				(u_char)pLCB->my_cfg->frs_link_mN3;       

			 pLCB->lmi_ccb->lmi_max_stat_enq_count =
				(u_char)pLCB->my_cfg->frs_link_mN4;       

			 pLCB->lmi_ccb->lmi_polling_interval =
				(u_char)pLCB->my_cfg->frs_link_mT1;       

			 pLCB->lmi_ccb->lmi_max_poll_interval =
				(u_char)pLCB->my_cfg->frs_link_mT2;       

			 pLCB->lmi_ccb->lmi_xcess_poll_interval =
				(u_char)pLCB->my_cfg->frs_link_mT3;
			 }
		  }/* end if sys_update_config call */
	   }/* end if locate param */

    return(rcode);
}

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

  PROCEDURE NAME:    FRSCONF_SetPLLConfig

  DESCRIPTION:

  This procedure will set the value of a PLL configuration parameter.

  PARAMETERS:

  u_short --- link number
  u_short --- dlci
  u_short --- param id(from frscfg.h)
  char * --- ASCII representation of the parameter's value

  RETURNS:

  FRS_SUCCESS or one of the API return codes from frs.h.

  NOTES:

  No error checking is performed.

*****************************************************************************/
u_short
FRSCONF_SetPLLConfig(link, dlci, param_id, param_value_str)
u_long link;
u_long dlci;
u_long param_id;
char *param_value_str;
{
    void *pParam;
    u_short rcode;
    CFG_UPDATE update_info;
    u_short mask = 0; 
    u_short set_flag = FRS_FLAG_OFF;
    u_short flag_type = FRSCFG_FT_NONE;
    FRS_PLL_CFG *pPLLRec;
    CCB *pCCB;
    LCB *pLCB;

    /** Find the parameter to be changed....
	**/
    if((rcode = FRSLocateParam(link,dlci,param_id,&pParam)) == FRS_SUCCESS)
	   {
	   switch(param_id)
		  {
		  case FRS_PLL_CONF_NAME:
			 strcpy((char *)pParam, param_value_str);
			 return(rcode);

		  case FRS_PLL_BCMAX:
		  case FRS_PLL_BCMIN:
		  case FRS_PLL_BE:
		  case FRS_PLL_CIR:
			 *((u_long *)pParam) = (u_long)atol(param_value_str);
			 break; 

		  case FRS_PLL_LINK:          /* read-only */
			 return( FRS_INVALID );

		  case FRS_PLL_STATE:
			 flag_type = FRSCFG_FT_WORD;
			 set_flag = FRS_FLAG_OFF;
			 mask = FRS_OPT_PLL_STATE;
			 if( FRSCONF_FindFlag( param_id, param_value_str ) )
			     set_flag = FRS_FLAG_ON;
			 break;

		  default:
			 *((u_short *)pParam) = (u_short)atoi(param_value_str);
			 break;
		  }

	   /** Set any flags
	    **/
	   if(flag_type != FRSCFG_FT_NONE)
		  FRSCONF_SetFlags( flag_type, set_flag, mask, pParam );
 
	   /** Get the PLL db record....
	    **/
	   FRSLocatePLLCfg(link, dlci, &pPLLRec);

	   /** Populate the update_info structure....
	    **/
	   update_info.action = FRS_CONFIG_PLL_PARAMETER_UPDATE;
	   update_info.link = (u_short) link;
	   update_info.dlci = (u_short) dlci;
	   update_info.config = &gGlobalCfg;
	   update_info.param_id = param_id;
	   update_info.param_value = pParam;
	   update_info.record_ptr = (void *) pPLLRec;

	   /** This target system callback is given a pointer to the update
	    ** info. If the routine returns FALSE then we will update the on-
	    ** line PLL information. If it returns TRUE, we don't do anything.
	    **/
	   if( (sys_update_config( &update_info )) == FALSE )
		  {
		  if( ((pLCB = frsLCBtable[link-1]) != LCBNULL) &&
			 ((pCCB = FRSLocateCCBbyDLCI(pLCB, dlci)) != CCBNULL) )
				{
				pCCB->my_cosb.bc_min = pPLLRec->frs_pll_BcMin / 8;
				pCCB->my_cosb.Tc = 
				   (u_short)((1000L*pPLLRec->frs_pll_BcMax) /
						    pPLLRec->frs_pll_CIR);
				pCCB->my_cosb.CMP = (u_short) pPLLRec->frs_pll_CMP;
				pCCB->my_cosb.bc_fwd = 
				    pCCB->my_cosb.bc_cur = pPLLRec->frs_pll_BcMax / 8;
				pCCB->my_cosb.be_fwd = pPLLRec->frs_pll_Be / 8;
#if FRS_DCE_SUPPORT
				pCCB->fwd_node = (u_char)pPLLRec->frs_pll_fwd_node;
				pCCB->fwd_link = (u_char)pPLLRec->frs_pll_fwd_link;
				pCCB->fwd_dlci = (u_char)pPLLRec->frs_pll_fwd_dlci;
#endif

				pCCB->my_cosb.be_bc_cur =
				  (pCCB->my_cosb.bc_fwd +
				   pCCB->my_cosb.be_fwd);  /* Sum of be & bc */
				}
			 }
		  }
	   return( rcode );
}

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

  PROCEDURE NAME:    FRSCONF_InsertLink

  DESCRIPTION:

  This procedure will insert a link...

  PARAMETERS:

  u_short --- link number
  CFG_LNK_CFG * -- a pointer to a link configuration record to be used for
			    specifying the link configuration. IF a NULL pointer is
			    passed the link is configured using the system defaults.

  RETURNS:

  FRS_SUCCESS or one of the API return codes from frs.h.

  NOTES:
*****************************************************************************/
u_short
FRSCONF_InsertLink(snid, pLinkConfig)
u_long snid;
FRS_LINK_CFG *pLinkConfig;
{
	FRS_LINK_CFG *pdbRec;
	LCB *pLCB;
	CFG_UPDATE update_info;
	u_short rcode;
	u_long link;

    /** First determine if this link already exists.... or the link #
	** is out-of-bounds....
	**/

    link = snid_to_hLCB(snid) ;

    if( link > FRS_MAX_LINKS || link == 0 || m_dbFRS_IS_LINK_ENBL(link-1) )
	   return( FRS_INVALID );
    else
	   pdbRec = m_dbFRS_GET_LINK_RECORD_PTR(link-1);

    pdbRec->frs_link_1st_pllcfg = FRS_CFG_DELIMITER;

    /** Populate the database link structure by either using the configuration
	** record provided (pLinkConfig) or by using the defaults....
	**/
    if( pLinkConfig != (FRS_LINK_CFG *) 0
	   && pLinkConfig->frs_link_flags != FRS_DELETED)
	   {
	   /** Use this record....
	    **/
	   strcpy( pdbRec->frs_conf_name, pLinkConfig->frs_conf_name );
	   pdbRec->frs_link_snid =  snid;
	   pdbRec->frs_link_flags = pLinkConfig->frs_link_flags| FRS_OPT_LINK_ENBL;
	   pdbRec->frs_link_lmi_dlci = pLinkConfig->frs_link_lmi_dlci;
	   pdbRec->frs_link_max_frsize = pLinkConfig->frs_link_max_frsize;
	   pdbRec->frs_link_mN1 =  pLinkConfig->frs_link_mN1;
	   pdbRec->frs_link_mN2 =  pLinkConfig->frs_link_mN2;
	   pdbRec->frs_link_mN3 =  pLinkConfig->frs_link_mN3;
	   pdbRec->frs_link_mN4 =  pLinkConfig->frs_link_mN4;
	   pdbRec->frs_link_mP1 =  pLinkConfig->frs_link_mP1;
	   pdbRec->frs_link_mP10 = pLinkConfig->frs_link_mP10;
	   pdbRec->frs_link_mT1 =  pLinkConfig->frs_link_mT1;
	   pdbRec->frs_link_mT2 =  pLinkConfig->frs_link_mT2;
	   pdbRec->frs_link_mT3 =  pLinkConfig->frs_link_mT3;
	   pdbRec->frs_link_state =  pLinkConfig->frs_link_state;
#if PRI
	   pdbRec->frs_link_tslotmap =  pLinkConfig->frs_link_tslotmap;
#endif
	   }
    else
	   /** Use the defaults....
	    **/
	   {
	   strcpy( pdbRec->frs_conf_name, FRS_LINK_DFLT_NAME );
	   pdbRec->frs_link_snid = snid;
	   pdbRec->frs_link_flags = FRS_LINK_DFLT_FLAGS | FRS_OPT_LINK_ENBL;
	   pdbRec->frs_link_lmi_dlci = FRS_LINK_DFLT_LMIDLCI;
	   pdbRec->frs_link_max_frsize = FRS_LINK_DFLT_FRSIZE;
	   pdbRec->frs_link_mN1 =  FRS_LINK_DFLT_mN1;
	   pdbRec->frs_link_mN2 =  FRS_LINK_DFLT_mN2;
	   pdbRec->frs_link_mN3 =  FRS_LINK_DFLT_mN3;
	   pdbRec->frs_link_mN4 =  FRS_LINK_DFLT_mN4;
	   pdbRec->frs_link_mP1 =  FRS_MAX_CHANNELS;
	   pdbRec->frs_link_mP10 = FRS_LINK_DFLT_mP10;
	   pdbRec->frs_link_mT1 =  FRS_LINK_DFLT_mT1;
	   pdbRec->frs_link_mT2 =  FRS_LINK_DFLT_mT2;
	   pdbRec->frs_link_mT3 =  FRS_LINK_DFLT_mT3;
	   pdbRec->frs_link_state =  FRS_LINK_DFLT_STATE;
#if PRI
	   pdbRec->frs_link_tslotmap =  FRS_LINK_DFLT_TSLOTMAP;
#endif
	   }

    /** Now we make a call to initialize the link....
	**/
    if((rcode = FRSCONF_InitLink( pdbRec, (proc_id) 0, &pLCB )) == FRS_SUCCESS)
	   {
	   /** Populate the update_info structure....
	    **/
	   update_info.action = FRS_CONFIG_LINK_INSERTED;
	   update_info.link = (u_short) link;
	   update_info.dlci = (u_short) FRS_LINK_INFORMATION;
	   update_info.config = &gGlobalCfg;
	   update_info.param_id = 0;
	   update_info.param_value = 0;
	   update_info.record_ptr = (void *) pdbRec;
	   rcode = sys_update_config( &update_info );
	   }

    return( rcode );
}

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

  PROCEDURE NAME:    FRSCONF_InsertPLL

  DESCRIPTION:

  This procedure will add a new pll entry to a link's pll array using the
  specified 'link' and 'dlci' number.

  PARAMETERS:

  u_short --- link number
  u_short --- dlci
  FRS_PLL_CFG * -- a pointer to a PLL configuration record to be used for
			    specifying the PLL configuration. IF a NULL pointer is
			    passed the PLL is configured using the system defaults.

  RETURNS:

  FRS_SUCCESS or one of the API return codes from frs.h.

  NOTES:
*****************************************************************************/
u_short
FRSCONF_InsertPLL(snid, dlci, pPLLConfig)
u_long snid;
u_long dlci;
FRS_PLL_CFG *pPLLConfig;
{
    FRS_LINK_CFG *pLinkCfg;
    FRS_PLL_CFG *pPLLRec, *pPLLScan;
    CFG_UPDATE update_info;
    u_short rcode, i;
	u_long  link;

    link = snid_to_hLCB(snid) ;

    /** First determine if this exists.... or the link # is out-of-bounds....
	**/
    if( link > FRS_MAX_LINKS || link == 0 || !(m_dbFRS_IS_LINK_ENBL(link-1)) )
	{
		printf("FRSCONF_InsertPLL FAIL-0 %x", link) ;
	   return( FRS_INVALID );
	}

    /** Check to see if DLCI is a valid one...
	**/
    if ( dlci < FRS_MIN_VALID_DLCI || dlci > FRS_MAX_VALID_DLCI )
	{
		printf("FRSCONF_InsertPLL FAIL-1 %x",dlci) ;
	   return ( FRS_INVALID );
	}

    pLinkCfg = m_dbFRS_GET_LINK_RECORD_PTR(link-1);

    /** Is there already a PLL with this dlci ?
	**/
    if(( rcode =  FRSLocatePLLCfg( link, dlci, &pPLLRec )) != FRS_SUCCESS )
    {
	   /** Find an available slot...
	    **/
	   pPLLScan = (FRS_PLL_CFG *)&gGlobalCfg._frmrly_ccbcfg[0];
	   pPLLRec = (FRS_PLL_CFG *) 0;
	   for(i=0; (i < FRS_MAX_CHANNELS);i++, pPLLScan++)
		  {
		    if (pPLLScan->frs_pll_link == FRS_DELETED)
		      {
			/*
			 * Yyyyeesssss...!
			 */
			pPLLRec = pPLLScan;
			break;
		      }

			  printf("pPLLScan->frs_pll_link = %x",pPLLScan->frs_pll_link) ;
		  }

	   printf("Open i = %d, %x", i,pPLLRec ) ;

	   /** Did we find one ?
	    **/
	   if( pPLLRec == (FRS_PLL_CFG *) 0 )
	   {
		  printf("FRSCONF_InsertPLL FAIL-2") ;
		  return( FRS_INVALID );
	   }
	   else
	     {
		  /** Set the link number and self-index...
		   **/
		  pPLLRec->frs_pll_link = link;
		  pPLLRec->frs_pll_ndx = i;


		  /** Update linked-list pointers...
		   **/
		  if (pLinkCfg->frs_link_1st_pllcfg == FRS_CFG_DELIMITER) /* Numero uno...? */
		    pPLLRec->frs_pll_next = FRS_CFG_DELIMITER;
		  else
		    {
		      /*
		       * Insert the PLL_CFG in the chain for this link.
		       */
		      pPLLRec->frs_pll_next = pLinkCfg->frs_link_1st_pllcfg;
		    }

		  pLinkCfg->frs_link_1st_pllcfg = i;
	     }
     }
	 else
		printf("FRSCONFIG_InsertPLL FAIL-3") ;

	   /** If we were given a PLL record then use the values in the record
	    ** to setup this PLL....
	    **/
	   if( pPLLConfig != ( FRS_PLL_CFG * ) 0 )
		  {
		  strcpy( pPLLRec->frs_conf_name, pPLLConfig->frs_conf_name );
		  pPLLRec->frs_pll_dlci = dlci;
		  pPLLRec->frs_pll_BcMax = pPLLConfig->frs_pll_BcMax;
		  pPLLRec->frs_pll_BcMin = pPLLConfig->frs_pll_BcMin;
		  pPLLRec->frs_pll_Be = pPLLConfig->frs_pll_Be;
		  pPLLRec->frs_pll_CIR = pPLLConfig->frs_pll_CIR;
		  pPLLRec->frs_pll_CMP = pPLLConfig->frs_pll_CMP;
		  pPLLRec->frs_pll_state = pPLLConfig->frs_pll_state;
		  pPLLRec->frs_pll_FRF9RetryPeriod = pPLLConfig->frs_pll_FRF9RetryPeriod;
		  pPLLRec->frs_pll_FRF9RetryCount = pPLLConfig->frs_pll_FRF9RetryCount;
		  pPLLRec->frs_pll_FRF9ModesWanted = pPLLConfig->frs_pll_FRF9ModesWanted;
		  pPLLRec->frs_pll_FRF9ContextMax = pPLLConfig->frs_pll_FRF9ContextMax;

#if FRS_DCE_SUPPORT
		  pPLLRec->frs_pll_fwd_node = pPLLConfig->frs_pll_fwd_node;
		  pPLLRec->frs_pll_fwd_link = pPLLConfig->frs_pll_fwd_link;
		  pPLLRec->frs_pll_fwd_dlci = pPLLConfig->frs_pll_fwd_dlci;
#endif
		  printf("Configured FRF9RetryPeriod = %x",pPLLConfig->frs_pll_FRF9RetryPeriod ) ;

		  }
	   else
		  {
		  /** Use the defaults
		   **/
		  strcpy( pPLLRec->frs_conf_name, FRS_PLL_DFLT_NAME );
		  pPLLRec->frs_pll_dlci  = dlci;
		  pPLLRec->frs_pll_BcMax = FRS_PLL_DFLT_BcMax;
		  pPLLRec->frs_pll_BcMin = FRS_PLL_DFLT_BcMin;
		  pPLLRec->frs_pll_Be    = FRS_PLL_DFLT_Be;
		  pPLLRec->frs_pll_CIR   = FRS_PLL_DFLT_CIR;
		  pPLLRec->frs_pll_CMP   = FRS_PLL_DFLT_CMP;
		  pPLLRec->frs_pll_CMP   = FRS_PLL_DFLT_STATE;
		  pPLLRec->frs_pll_FRF9RetryPeriod =
		  pPLLRec->frs_pll_FRF9RetryCount =
		  pPLLRec->frs_pll_FRF9ModesWanted =
		  pPLLRec->frs_pll_FRF9ContextMax = 0;

#if FRS_DCE_SUPPORT
		  pPLLRec->frs_pll_fwd_node = 0;
		  pPLLRec->frs_pll_fwd_link = 0;
		  pPLLRec->frs_pll_fwd_dlci = 0;
#endif
		  printf("Default FRF9RetryPeriod = 0" ) ;

		  }

	   /** Populate the update_info structure....
	    **/
	   update_info.action = FRS_CONFIG_PLL_INSERTED;
	   update_info.link = (u_short) link;
	   update_info.dlci = (u_short) dlci;
	   update_info.config = &gGlobalCfg;
	   update_info.param_id = 0;
	   update_info.param_value = 0;
	   update_info.record_ptr = (void *) pPLLRec;
	   rcode = sys_update_config( &update_info );

    return( rcode );
}

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

  PROCEDURE NAME:    FRSCONF_DeleteLink

  DESCRIPTION:

  This procedure will delete the specified 'link' from the system link
  array. It checks if any PLLs exist for this link which remain undeleted.
  Any undeleted PLLs will cause this function to return an error code.

  PARAMETERS:

  u_short --- link number

  RETURNS:

  FRS_SUCCESS or one of the API return codes from frs.h.

  NOTES:
*****************************************************************************/
u_short
FRSCONF_DeleteLink(snid)
u_long snid;
{
  FRS_LINK_CFG *pdbRec;
  CFG_UPDATE update_info;
  u_long old_flags;
  u_short rcode;
  u_long link;

  link = snid_to_hLCB(snid) ;

    /** First determine if this link already exists.... or the link #
	** is out-of-bounds....
	**/
    if( link > FRS_MAX_LINKS || link == 0 || !(m_dbFRS_IS_LINK_ENBL(link-1)) )
	   return( FRS_INVALID );
    else
	   pdbRec = m_dbFRS_GET_LINK_RECORD_PTR(link-1);

    /** Do not allow links to be deleted if any channels remain undeleted for
     ** this link...
     **/

    /** Attempt to find atleast one PLL which has not been deleted...
     **/
    if (pdbRec->frs_link_1st_pllcfg !=  FRS_CFG_DELIMITER)
	  return FRS_INVALID;

    /** Save flags, then mark the link as deleted
	**/
    old_flags = pdbRec->frs_link_flags;
    pdbRec->frs_link_flags = FRS_DELETED;

    /** Populate the update_info structure....
	**/
    update_info.action = FRS_CONFIG_LINK_REMOVED;
    update_info.link = (u_short) link;
    update_info.dlci = (u_short) FRS_LINK_INFORMATION;
    update_info.config = &gGlobalCfg;
    update_info.param_id = 0;
    update_info.param_value = 0;
    update_info.record_ptr = (void *) pdbRec;
    /** If the update to the user's config rec fails,
	** undo the delete.
	**/
    if ( rcode = sys_update_config( &update_info ) != FRS_SUCCESS )
	   pdbRec->frs_link_flags = old_flags;
    else
      FRSCONF_UnInitLink(link);
    return( rcode );
}

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

  PROCEDURE NAME:    FRSCONF_DeletePLL

  DESCRIPTION:

  This procedure will delete the specified 'dlci' on the specified 'link'.

  PARAMETERS:

  u_short --- link number
  u_short --- dlci

  RETURNS:

  FRS_SUCCESS or one of the API return codes from frs.h.

  NOTES:
*****************************************************************************/
u_short
FRSCONF_DeletePLL(snid, dlci)
u_long snid;
u_long dlci;
{
    FRS_LINK_CFG *pLinkCfg;
    FRS_PLL_CFG *pPLLRec, *pPLLPrev, *pPLLScan;
    CFG_UPDATE update_info;
    u_long i, j;
    u_long link;
    u_short rcode = FRS_INVALID;

    link = snid_to_hLCB(snid) ;

    /** First determine if this link already exists.... or the link #
	** is out-of-bounds....
	**/
    if( link > FRS_MAX_LINKS || link == 0 || !(m_dbFRS_IS_LINK_ENBL(link-1)) )
	   return( rcode );
    else
	   {
	     pLinkCfg = (FRS_LINK_CFG *) m_dbFRS_GET_LINK_RECORD_PTR(link-1);

	     pPLLPrev = pPLLRec = (FRS_PLL_CFG *)0;

	   /** Find a matching link, dlci pair....
	    **/
	     for( i = 0, j = pLinkCfg->frs_link_1st_pllcfg;
		 (i < pLinkCfg->frs_link_mP1) && (j != FRS_CFG_DELIMITER);
		 i++, j = pPLLScan->frs_pll_next )
	       {
		 pPLLScan = &gGlobalCfg._frmrly_ccbcfg[j];
		 
		 if(pPLLScan->frs_pll_dlci == dlci &&   /* matched dlci # */
		    pPLLScan->frs_pll_link == link)     /* matched link # */
		   {
		     pPLLRec = pPLLScan;              /* ... got it     */
		     break;
		   }
		 
		 pPLLPrev = pPLLScan;
	       }

	     /** Did we find locate the PLL to be marked...?
	      **/
	     if( pPLLRec != (FRS_PLL_CFG *)0 )
		  {
		  /** Mark the link as deleted
		   **/
		  m_dbFRS_MARK_PLL_DELETED( pPLLRec );

		  /** Update links within PLLCfg chain etc...
		   **/
		  if (pPLLPrev == (FRS_PLL_CFG *)0)
		    pLinkCfg->frs_link_1st_pllcfg = pPLLRec->frs_pll_next;
		  else
		    pPLLPrev->frs_pll_next = pPLLRec->frs_pll_next;

		  /** Populate the update_info structure....
		   **/
		  update_info.action = FRS_CONFIG_PLL_REMOVED;
		  update_info.link = (u_short) link;
		  update_info.dlci = (u_short) dlci;
		  update_info.config = &gGlobalCfg;
		  update_info.param_id = 0;
		  update_info.param_value = 0;
		  update_info.record_ptr = (void *) pPLLRec;
		  /** If the update to the user's config rec fails
		   ** undo the delete.
		   **/
		  if ( rcode = sys_update_config( &update_info ) != FRS_SUCCESS )
			 pPLLRec->frs_pll_link = link;
		  }
	   }

    return(rcode);
}

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

  PROCEDURE NAME:    FRSCONF_FindFlag

  DESCRIPTION:

  This procedure determines configuration option selections....

  PARAMETERS:

  char * --- ASCII representation of the parameter's value

  RETURNS:

  The selection identifier.

  NOTES:

  No error checking is performed.

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

u_short
FRSCONF_FindFlag( param_id, string )
u_long param_id;
char *string;
{
u_short rcode = (u_short) FRS_FLAG_OFF;

    if( param_id == FRS_LINK_LMI )
	   {
	   if( m_IS_SAME(string, "LMI") )
		  rcode = (u_short) FRS_LMI_PLAIN;

	   else
	   if( m_IS_SAME(string, "STRATACOM") || m_IS_SAME(string, "STRAT") )
		  rcode = (u_short) FRS_LMI_STRAT;

	   else
	   if( m_IS_SAME(string, "ANNEXD") || m_IS_SAME(string, "ANNEX D") )
		  rcode = (u_short) FRS_LMI_ANXD;

	   else
	   if( m_IS_SAME(string, "ANNEXA") || m_IS_SAME(string, "ANNEX A") )
		  rcode = (u_short) FRS_LMI_ANXA;
	   else
		  rcode = (u_short) FRS_LMI_NONE;

	   return( rcode );
	   }

    else if( param_id == FRS_LINK_TYPE )
	   {
	   if( m_IS_SAME(string, "DCE") )
		  rcode = (u_short) FRS_FLAG_ON;
	   else
	   if( m_IS_SAME(string, "DTE") )
		  rcode = (u_short) FRS_FLAG_OFF;

	   return( rcode );
	   }

    else if( param_id == FRS_LINK_INJECT )
	   {
	   if( m_IS_SAME(string, "STANDARD") )
		  rcode = (u_short) FRS_INJ_STANDARD;
	   else
	   if( m_IS_SAME(string, "FORCED") )
		  rcode = (u_short) FRS_INJ_FORCED;
	   else
	   if( m_IS_SAME(string, "BUFFERED") )
		  rcode = (u_short) FRS_INJ_BUFFERED;
	   else
		  rcode = (u_short) FRS_INJ_STANDARD;

	   return( rcode );
	   }
    else
    {
	   if(( m_IS_SAME(string, "YES")) || ( m_IS_SAME(string, "Y")) ||
		 ( m_IS_SAME(string, "ON"))  || ( m_IS_SAME(string, "1")) ||
	   ( m_IS_SAME(string, "ENABLE")))
		  rcode =  (u_short) FRS_FLAG_ON;
    }

    return( rcode );
}

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

  PROCEDURE NAME:    FRSCONF_ValidParam

  DESCRIPTION:

  This procedure will check the value of a configuration parameter.

  PARAMETERS:

    u_short link --- The link number.
    u_short dlci --- The dlci... link parameters (FRS_LINK_INFORMATION)
    u_short param_id --- param id(from frscfg.h)
    char *param_value_str --- pointer to ASCII rep of the parameter's value


  RETURNS:

  FRS_SUCCESS or one of the API return codes from frs.h.

  NOTES:

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

u_short
FRSCONF_ValidParam( link, dlci, param_id, param_value_str)
u_long link;
u_long dlci;
u_long param_id;
char *param_value_str;
{
    u_short i;
    u_short p_type;
    u_short str_length;
    FRSCFG_PARS *p_table[FRS_MAX_LINK_CFG];
    FRS_PLL_CFG *pPLLRec;

    extern FRSCFG_PARS link_vals[];
    extern FRSCFG_PARS pll_vals[];
    extern FRSCFG_VALS values[];

    /** Configurable system parameters....
	**/
    if( (m_FRS_SYSTEM_PARAMETERS(link, dlci)) == TRUE )
	   {
	   /** System parameters are not handled by validation data simply
	    ** because it is too difficult to conditionally compile the
	    ** table successfully. Besides there are so few parameters that
	    ** can be changed....
	    **/
	   switch( param_id )
		  {
		  default:
		  case FRS_SYS_SW_REL:    /* read-only */
		  case FRS_SYS_MAX_LINKS: /* read-only */
		  case FRS_SYS_MAX_PLLS:  /* read-only */
			 break;

#if FRS_DCE_SUPPORT

		  case FRS_SYS_NODE_NUM:  /* read-write */
			 i = (u_short) atoi( param_value_str );
			 if( i > 0xffff )
				break;
			 return( FRS_SUCCESS );

		  case FRS_SYS_CMI_mT1:  /* read-write */
			 i = (u_short) atoi( param_value_str );
			 if( i == 0 || i > 0xffff )
				break;
			 return( FRS_SUCCESS );
#endif

#if !(USE_CONTIGUOUS_DATA)

		  case FRS_SYS_BUFSIZE:  /* read-write (special) */
			 i = (u_short) atoi( param_value_str );
			 if( i < (u_short) FRS_MIN_FRAME_SIZE )
				break;
			 return( FRS_SUCCESS );
#endif
		  }/* end switch */

	   return( FRS_INVALID );
	   }

    /** Configurable PLL parameters...
	**/
    else if(( m_FRS_PLL_PARAMETERS( link, dlci )) == TRUE )
    {
	   for( i = 0; i < FRS_MAX_PLL_CFG; i++ )
		  p_table[i] = &pll_vals[i];
    }
    /** Configurable LINK parameters...
	**/
    else
    {
	   for( i = 0; i < FRS_MAX_LINK_CFG; i++ )
		  p_table[i] = &link_vals[i];
    }

    p_type = p_table[param_id]->par_type;
    str_length = (u_short)strlen( param_value_str );

    switch( p_type )
	   {
	   /** These are individual settings: a,b,c,d.....
	    **/
	   case FRS_VAL_YN:
	   case FRS_VAL_INJECT:
	   case FRS_VAL_LMITYPE:
	   case FRS_VAL_LINKTYPE:
	   case FRS_VAL_ONOFF:
	   case FRS_VAL_mTn:

		  for(i=0; values[p_type].vals[i]; i++ )
			 {
			 if( m_IS_SAME( param_value_str, values[p_type].vals[i]) )
				return(FRS_SUCCESS);
			 }
		  break;

	   /** These are ranges: x - y
	    **/
	   case FRS_VAL_mP10:
	   case FRS_VAL_mP1:
	   case FRS_VAL_DLCI:
	   case FRS_VAL_FRSIZE:
	   case FRS_VAL_nNn:
	   case FRS_VAL_nN1:
	   case FRS_VAL_WORD:
	   case FRS_VAL_LONG:
	   case FRS_VAL_BYTE:

		  if( atol( param_value_str ) >= atol( values[p_type].vals[0] ) && 
			 atol( param_value_str ) <= atol( values[p_type].vals[1]) )
			 {
			 /** For certain parameters we check further...
			  **/
			 switch( param_id )
				{
				/** Check for a PLL with the same dlci number on this link.
				 **/
				case FRS_PLL_DLCI:
				  i = (u_short) atoi( param_value_str );
				  if( (i < FRS_MIN_VALID_DLCI) || (i > FRS_MAX_VALID_DLCI) )
				     return( FRS_INVALID );

			       /** If the number was actually changed to something else
				**/
			       if( i != dlci )
			       {
				   if(( FRSLocatePLLCfg(link, i, &pPLLRec)) ==
				FRS_SUCCESS )
				      return( FRS_INVALID );
			       }
			       break;

			  case FRS_LINK_LMI_DLCI:
			      for(i=0; values[p_type].vals[i]; i++ )
			      {
				if( m_IS_SAME( param_value_str, values[p_type].vals[i]) )
				       return(FRS_SUCCESS);
			      }                        
			      return( FRS_INVALID ); /* not 0 or 1023 */
			      break;

			   }
			 return(FRS_SUCCESS);
			 }

		  break;

	   case FRS_VAL_HEX:

		  if( param_value_str[0] != '0' &&(param_value_str[1] != 'x' ||
			 param_value_str[1] != 'X') )
			 break;
		  else
			 return(FRS_SUCCESS);

	   case FRS_VAL_LINK_NAME:

		  if( str_length <= FRS_LINK_NAME_SIZE )
			 return(FRS_SUCCESS);
		  break;

	   case FRS_VAL_PLL_NAME:

		  if( str_length <= FRS_PLL_NAME_SIZE )
			 return(FRS_SUCCESS);
		  break;

	   case FRS_VAL_NONE:
		  return(FRS_SUCCESS);

	   default:
		  break;
	   }
    return( FRS_INVALID );
}

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

  PROCEDURE NAME:    FRSLocateParam

  DESCRIPTION:

  Static function used to locate the address of a configuration parameter

  PARAMETERS:

  u_short --- link number
  u_short --- dlci
  u_short --- param id(from frscfg.h)
  void ** --- Address of a pointer to a configuration parameter
  
  RETURNS:

  Return value is FRS_SUCCESS or a failure code
  If FRS_SUCCESS, param_ptr points to the config parameter.

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

u_short
FRSLocateParam(link, dlci, param_id, param_ptr)
u_long  link;
u_long dlci;
u_long param_id;
void **param_ptr;
{
    FRS_LINK_CFG *pLinkCfg;
    FRS_PLL_CFG *pConfigData;
    u_short rcode;

    /** Range check on the link number....
	**/
    if( link > gGlobalCfg.frsTotalLinks || link > FRS_MAX_LINKS )
	   return( FRS_INVALID );

    /** System parameters ?
	**/
    if( (m_FRS_SYSTEM_PARAMETERS(link, dlci)) == TRUE )
    {
	   switch(param_id)
	   {
	   case FRS_SYS_SW_REL:
		  *param_ptr = (void *) gGlobalCfg.frsSfwrRel;
		  break;

	   case FRS_SYS_MAX_LINKS:
		  *param_ptr = (void *) &(gGlobalCfg.frsTotalLinks);
		  break;

	   case FRS_SYS_MAX_PLLS:
		  *param_ptr = (void *) &(gGlobalCfg.frsTotalPLLs);
		  break;

#if !(USE_CONTIGUOUS_DATA)

	   case FRS_SYS_BUFSIZE:
		  *param_ptr = (void *) &gGlobalCfg.frsMaxBufSize;
		  break;
#endif

#if FRS_DCE_SUPPORT

	   case FRS_SYS_NODE_NUM:
		  *param_ptr = (void *) &gGlobalCfg.frsNodeNum;
		  break;

	   case FRS_SYS_CMI_mT1:
		  *param_ptr = (void *) &gGlobalCfg.frsCMI_mT1;
		  break;
#endif

	   default:
		  *param_ptr = (void *) 0;
		  return(FRS_INVALID);
	   }

	   return(FRS_SUCCESS);
    }

    /** Link Parameters ?
	**/
    if( (m_FRS_LINK_PARAMETERS(link, dlci)) == TRUE )
    {    
	   if( !m_dbFRS_IS_LINK_ENBL(link-1) )
		  return( FRS_INVALID );
	   else
		  pLinkCfg = m_dbFRS_GET_LINK_RECORD_PTR(link-1);

	   switch(param_id)    
	   {    
	   case FRS_LINK_CONF_NAME:
		  *param_ptr = (void *)pLinkCfg->frs_conf_name;    
		  break;    

	   case FRS_LINK_FLAGS:
	   case FRS_LINK_ENABLE:
	   case FRS_LINK_LMIXON:
	   case FRS_LINK_STATS:
	   case FRS_LINK_LMIBAND:
	   case FRS_LINK_TYPE:
	   case FRS_LINK_BECN:
	   case FRS_LINK_INJECT:
	   case FRS_LINK_ENCAP:
	   case FRS_LINK_LMI:
		  *param_ptr = (void *)&pLinkCfg->frs_link_flags;    
		  break;    

	   case FRS_LINK_LMI_DLCI:
		  *param_ptr = (void *)&pLinkCfg->frs_link_lmi_dlci;    
		  break;    

	   case FRS_LINK_MAX_FRSIZE:
		  *param_ptr = (void *)&pLinkCfg->frs_link_max_frsize;    
		  break;    

	   case FRS_LINK_mN1:
		  *param_ptr = (void *)&pLinkCfg->frs_link_mN1;
		  break;    

	   case FRS_LINK_mN2:
		  *param_ptr = (void *)&pLinkCfg->frs_link_mN2;
		  break;    

	   case FRS_LINK_mN3:
		  *param_ptr = (void *)&pLinkCfg->frs_link_mN3;
		  break;    

	   case FRS_LINK_mN4:
		  *param_ptr = (void *)&pLinkCfg->frs_link_mN4;
		  break;

	   case FRS_LINK_mP1:
		  *param_ptr = (void *)&pLinkCfg->frs_link_mP1;
		  break;    

	   case FRS_LINK_mP10:
		  *param_ptr = (void *)&pLinkCfg->frs_link_mP10;
		  break;    

	   case FRS_LINK_mT1:
		  *param_ptr = (void *)&pLinkCfg->frs_link_mT1;
		  break;    

	   case FRS_LINK_mT2:
		  *param_ptr = (void *)&pLinkCfg->frs_link_mT2;
		  break;    

	   case FRS_LINK_mT3:
		  *param_ptr = (void *)&pLinkCfg->frs_link_mT3;
		  break;    
		  
	   case FRS_LINK_STATE:
		  *param_ptr = (void *)&pLinkCfg->frs_link_state;     
		  break;
#if PRI
	   case FRS_LINK_TSLOTMAP:
		  *param_ptr = (void *)&pLinkCfg->frs_link_tslotmap;     
		  break;
#endif

/** Parameters not currently supported
 **/
	   case FRS_LINK_MULTI:
	   default:
		  *param_ptr = (void *) 0;
		  return(FRS_INVALID);

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

    else    
    {    
	   /** Must be PLL parameters....
	    */    
	   if((rcode = FRSLocatePLLCfg(link, dlci, &pConfigData)) != FRS_SUCCESS)
		  return( rcode );

	   switch( param_id)          
	   {    
	   case FRS_PLL_CONF_NAME:
		  *param_ptr = (void *)pConfigData->frs_conf_name;    
		  break;    

	   case FRS_PLL_LINK:
		  *param_ptr = (void *)&pConfigData->frs_pll_link;    
		  break;    

	   case FRS_PLL_DLCI:
		  *param_ptr = (void *)&pConfigData->frs_pll_dlci;    
		  break;    

	   case FRS_PLL_BCMAX:
		  *param_ptr = (void *)&pConfigData->frs_pll_BcMax;    
		  break;    

	   case FRS_PLL_BCMIN:
		  *param_ptr = (void *)&pConfigData->frs_pll_BcMin;    
		  break;    

	   case FRS_PLL_BE:
		  *param_ptr = (void *)&pConfigData->frs_pll_Be;    
		  break;    

	   case FRS_PLL_CIR:
		  *param_ptr = (void *)&pConfigData->frs_pll_CIR;    
		  break;    

	   case FRS_PLL_CMP:
		  *param_ptr = (void *)&pConfigData->frs_pll_CMP;    
		  break;    

#if FRS_DCE_SUPPORT
	   case FRS_PLL_FWDNODE:
		  *param_ptr = (void *)&pConfigData->frs_pll_fwd_node;    
		  break;    

	   case FRS_PLL_FWDLINK:
		  *param_ptr = (void *)&pConfigData->frs_pll_fwd_link;    
		  break;    

	   case FRS_PLL_FWDDLCI:
		  *param_ptr = (void *)&pConfigData->frs_pll_fwd_dlci;    
		  break;    
#endif

	   case FRS_PLL_STATE:
		  *param_ptr = (void *)&pConfigData->frs_pll_state;    
		  break;   

	   default:
		  *param_ptr = (void *) 0;
		  return(FRS_INVALID);

	   }/* end switch */
    }/* end else */

    return(FRS_SUCCESS);
}

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

  PROCEDURE NAME:    FRSCONF_SetFlags

  DESCRIPTION:

  This procedure will set the value of a bit configuration flag.

  PARAMETERS:

  u_short flag_type  ---   Flag indicating which data type we're setting.
  u_short mask ---  Bit mask for setting and unsetting configuration bits.
  void *pParam    ---   Pointer to the configuration record field.

  RETURNS:

  FRS_SUCCESS 

  NOTES:

  No error checking is performed.

*****************************************************************************/
u_short
FRSCONF_SetFlags( flag_type, set_flag, mask, pParam)
u_short flag_type;
u_short set_flag;
u_short mask;
void *pParam;
{
    switch( flag_type )
    {
    case FRSCFG_FT_BYTE:
	   if( set_flag == FRS_FLAG_ON )
		  m_Set(*((u_char *)pParam), mask);
	   else
		  m_Rst(*((u_char *)pParam), mask);
	   break;

    case FRSCFG_FT_WORD:
	   if( set_flag == FRS_FLAG_ON )
		  m_Set(*((u_short *)pParam), mask);
	   else
		  m_Rst(*((u_short *)pParam), mask);
	   break;

    case FRSCFG_FT_LONG:
	   if( set_flag == FRS_FLAG_ON )
		  m_Set(*((u_long *)pParam), mask);
	   else
		  m_Rst(*((u_long *)pParam), mask);
	   break;
    }

    return(FRS_SUCCESS);
}

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

  PROCEDURE NAME:    FRSLocatePLLCfg

  DESCRIPTION:

  Given a link and a dlci, this function returns a pointer to the appropriate
  config record.

  PARAMETERS:

  u_short --- link
  u_short --- dlci
  struct frs_pll_cfg ** -- Address of a pointer to the config record

  RETURNS:

  FRS_SUCCESS or a failure code from frs.h
  If FRS_SUCCESS, pPLLCfg will point to the config record.

  NOTES:

*****************************************************************************/
u_short
FRSLocatePLLCfg(link, dlci, pPLLCfg)
u_long link, dlci;
FRS_PLL_CFG **pPLLCfg;
{
    FRS_LINK_CFG *pLinkCfg;
    FRS_PLL_CFG *pPLL;
    u_long i, j;

    /** Is this link configured ?
	**/
 
    if( m_dbFRS_IS_LINK_ENBL(link-1) == TRUE )
	   {
	   pLinkCfg = (FRS_LINK_CFG *) m_dbFRS_GET_LINK_RECORD_PTR(link-1);


	   /** Do we have any PLLs for this link...?
	    **/
	   /* TBD */


	   /** Find a matching link, dlci pair....
	    **/
	   for( i = 0, j = pLinkCfg->frs_link_1st_pllcfg;
		(i < pLinkCfg->frs_link_mP1) && (j != FRS_CFG_DELIMITER);
		i++, j = pPLL->frs_pll_next )
		  {
		    pPLL = &gGlobalCfg._frmrly_ccbcfg[j];

		    if(pPLL->frs_pll_dlci == dlci &&  /* matched dlci # */
		       pPLL->frs_pll_link == link)    /* matched link # */
		      {
			*pPLLCfg = pPLL;              /* ... got it     */
			return(FRS_SUCCESS);
		      }
		      else
		      {
		      } 
		  }
	   }

    /* If we get here
	* the config record for this link, dlci pair does not exist...
	*/
    return(FRS_INVALID);
}

