/*
* @version: $Revision: 1.2 $
* @date:    $Date: 2018/12/19 08:59:51 $
* @author:  $Author: lizhongming $
* @state:   $State: Exp $
*
* @latest:  $Id: mvl_acse.c,v 1.2 2018/12/19 08:59:51 lizhongming Exp $
*
*/
/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*      	1986 - 2004, All Rights Reserved		        */
/*									*/
/* MODULE NAME : mvl_acse.c    						*/
/* PRODUCT(S)  : MMSEASE-LITE						*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*	NONE								*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 07/13/05  MDE    104    Fixed crash caused using mvl_init_ind_hold	*/
/* 06/07/05  MDE    103    Fixed deadlock caused by mvl_init_ind_hold	*/
/* 05/10/05  JRB    102    Call new mvl_init_aa_obj_ctrl to init struct	*/
/*			   to control AA-Specific object creation	*/
/*			   (no longer done by Foundry).			*/
/* 05/04/05  MDE    101    Added mvl_init_ind_hold			*/
/* 04/28/05  MDE    100    Added user_conn_id check on abort ind	*/
/* 04/06/05  MDE     99    Added test probes				*/
/* 03/09/05  MDE     98    Set stats startTime				*/
/* 08/11/04  JRB     97    Del global funct ptr u_mvl_connect_ind_fun.	*/
/*			   Fix mvl_abort_req(_ex) returns (ST_RET).	*/
/* 07/14/04  MDE     96    Set mvl_net_info->acse_conn_id on init req	*/
/* 07/08/04  JRB     95    Del unused global var mvl_local_cl_ar_name.	*/
/* 06/07/04  EJV     94	   Removed call to a_free_security_info		*/
/* 04/09/04  MDE     93    Removed call to a_free_security_info		*/
/* 01/21/04  EJV     92    u_a_associate_ind: added a_free_security_info*/
/*			     added reinfo.auth_info.auth_pres = SD_FALSE*/
/* 01/20/04  MDE     91    Added logging				*/
/* 11/07/03  JRB     90    Add mmsop_en_version1.			*/
/* 08/10/03  EJV     89    ACSE_AUTH_ENABLED code wo/defines		*/
/*			   Reworked mvl_net_info_to_chan.		*/
/*			   Removed defs from around find_rem_dib_entry()*/
/*			   For MAP30_ACSE use a_get_chan().		*/
/* 07/02/03  JRB     88    _mvl_net_service set activityFlag if more	*/
/*			   events on list (so user keeps calling).	*/
/* 06/16/03  EJV     87    Removed m_match.h, s_match.h includes.	*/
/*			   Renamed the match funcs & chg arg.		*/
/* 06/10/03  MDE     86    Added address matching			*/
/* 04/14/03  JRB     85    Eliminate compiler warnings.			*/
/* 12/18/02  ASK     84    Only check authentication on POSITIVE connect*/
/*			   confirm					*/
/* 10/30/02  JRB     83    mvl_init_audt_addr: check remote tp_type.	*/
/*			   _mvl_send_msg: do lock around a_data_req	*/
/*			   call to make it thread-safe.			*/
/*			   Chg all connID, bindID to ST_LONG to avoid	*/
/*			   compile errors on 64-bit systems.		*/
/* 10/11/02  ASK     82    Added mvla_initiate_req_ex, 			*/
/*			   u_mvl_connect_ind_ex, and 			*/
/*			   u_mvl_connect_cnf_ex functions for ACSE 	*/
/*			   authentication.				*/
/* 07/19/02  ASK     81    Fix net_info variable in _mvl_send_msg	*/
/* 07/17/02  MDE     80    Added mvl_conn_filtered_logging to send	*/
/* 07/10/02  JRB     79    Add maxpend_ind, numpend_ind.		*/
/* 07/09/02  MDE     78    Fixed bad free for bad decode of init resp	*/
/* 05/24/02  JRB     77    Put back MVL_COMM_EVENT linked list.		*/
/*			   Occasionally copp_event gets 2 events, one	*/
/*			   from TP0 and one from TP4.			*/
/* 05/17/02  JRB     76    mvla_init..: set *req_out=NULL if freed	*/
/*			   so user CAN'T free it again.			*/
/* 05/13/02  MDE     75    Don't scan unbound mvl_net_info		*/
/* 05/13/02  MDE     74    Now clean up requests for release conf	*/
/* 05/03/02  MDE     73    Added mvl_add_bind_ctrl, associated changes	*/
/* 04/29/02  MDE     72    Added MLOG for initiate			*/
/* 04/29/02  JRB     71    DON'T re-use MVL_NET_INFO until req_pend_list*/
/*			   is empty, assert if pend_ind is not empty.	*/
/* 03/07/02  MDE     70    mvla_set_init_ar now takes NULL		*/
/* 03/04/02  JRB     69    Start with clean "temp_bind_ctrl" so garbage	*/
/*			   doesn't get copied. 				*/
/*			   Return error if num_call*>0 but not supported*/
/* 02/25/02  MDE     68    More changes to MVL_CFG_INFO			*/
/* 02/20/02  MDE     67    Changes for new MVL_CFG_INFO, multiple bind	*/
/* 02/14/02  RKR     66    call a_associate_reqm if Marben stack	*/
/* 02/08/02  JRB     65	   Pass MVL_CFG_INFO struct to mvl_start_acse.	*/
/*			   Del mvl_local_ar_name,mvl_set_num_connections*/
/* 01/30/02  JRB     64    Add args to copp_initialize (same as MAP30..)*/
/*			   Del call to a_set_msgsize.			*/
/*			   Make sure mmsl_max_msg_size, mvl_num_calling,*/
/*			   mvl_num_called are set.			*/
/*			   Use MMS_INIT_EN instead of MVL_NUM_CALL*.	*/
/*			   Del use of COACSE define.			*/
/*			   Del mvl_max_comm_event (not used).		*/
/*			   Merge mvl_init, _mvl_init_conn_ctrl into	*/
/*			   mvl_start_acse.				*/
/*			   Replace mvl_init_cl_netinfo funct with	*/
/*			   mvl_init_audt_addr.				*/
/*			   Del u_a_unit_data_ind & all use of stack_sel.*/
/* 01/09/02  JRB     63    Del all use of clpp_bind & bind id.		*/
/*			   Del unused u_cla_except callback function.	*/
/* 12/04/01  MDE     62    Change to work with MAP30_ACSE		*/
/* 11/30/01  GLB     61    changed MVL_LOG_ERR1 to MVL_LOG_ERR0         */
/* 11/28/01  MDE     60    mvl_set_num_connections,mvl_set_max_msg_size */
/* 11/21/01  MDE     59    mvl_loc_ar_name now auto set			*/
/* 11/21/01  MDE     58    Safer num channel operation			*/
/* 11/05/01  JRB     57    mvl_wait_req_done now returns ST_RET.	*/
/* 09/21/01  JRB     56    Alloc global bufs only once at startup.	*/
/*			   Fix M_FREE context.				*/
/* 07/31/01  MDE     55    Added mvl_conn_filtered_logging		*/
/* 05/21/01  MDE     54    Free NET_INFO				*/
/* 03/13/01  JRB     53    Check for (MMS_INIT_EN & REQ_EN).		*/
/* 02/26/01  MDE     52    Moved m_smem_ctxt to mem_chks.c      	*/
/* 11/28/00  EJV     51    Changes to interface with Marben stack SUIC	*/
/*                         (see MAP30_ACSE define):			*/
/*                         Use SUIC find_loc_..., find_rem_dib_entry.	*/
/*			   Added   param  in copp_initialize().		*/
/*			   Added   param  in mvla_initiate_req().	*/
/*			   Changed params in copp_bind().		*/
/* 10/25/00  JRB     50    Del MVL_COMM_EVENT linked list. Only one used*/
/*			   Del call to _mvl_free_req_pend.		*/
/* 09/22/00  MDE     49    Changed index init				*/
/* 08/21/00  RKR     48    Changed prototype of mvl_init_cl_netinfo 	*/
/* 08/23/00  MDE     47    Call '_mvl_set_net_req_done' on aborts too	*/
/* 05/15/00  MDE     46    Added MMS statistics				*/
/* 04/27/00  JRB     45    Fixed calloc (0) problem			*/
/* 04/14/00  JRB     44    Lint cleanup.				*/
/* 04/12/00  MDE     43    Added AA resize, MVL_STATISTICS		*/
/* 03/15/00  JRB     42    Del #ifdef MVL_REQ_BUF_ENABLED: not used.	*/
/* 03/09/00  MDE     41    Added _mvl_init_conn_ctrl			*/
/* 01/21/00  MDE     40    Now use MEM_SMEM for dynamic memory		*/
/* 12/20/99  GLB     39    Remove extra initialization and add asserts  */
/* 11/23/99  MDE     38    Added mvla_check_req_que (from mvl_serv.c)	*/
/* 10/20/99  NAV     37    Add maxpend_req support			*/
/* 11/03/99  RKR     36    Set aborted pend req results to DISCONN	*/
/* 10/29/99  GLB     35    Added check for lack of connection           */
/* 09/07/99  MDE     34    Added _mvlu_rpt_disconnect_rcvd_fun 		*/
/* 07/30/99  JRB     33    Added MVL_GOOSE_SUPPORT define use		*/
/*			   Chg ma_clbind_ctrl from static to global.	*/
/* 07/14/99  JRB     32    On assoc ind, save acse_conn_id first.	*/
/* 04/13/99  MDE     31    Added '_mvla_initiate_locDe'			*/
/* 04/07/99  MDE     30    Fixed mvla_initiate_req return code		*/
/* 04/01/99  MDE     29    Eliminated connection handling memory leaks	*/
/* 04/01/99  MDE     28    Changes to decode buffer allocation scheme   */
/* 03/18/99  MDE     27    Made # connection limits changable runtime 	*/
/* 03/08/99  MDE     26    Added ICCP support				*/
/* 12/08/98  MDE     25    Cleanup on request control handling		*/
/* 12/08/98  MDE     24    Allow user to initiate w/o supplying a 	*/
/*			   'resp_info' buffer.				*/
/* 11/16/98  MDE     23    Conclude/Release cleanup, misc cleanup	*/
/* 10/22/98  JRB     22    mvl_start_acse init mvl_debug_sel. Other lint*/
/* 09/21/98  MDE     21    Minor lint cleanup				*/
/* 08/25/98  MDE     20    Fixed bind/unbind bug for CLACSE		*/
/* 08/14/98  MDE     19    Removed !ALLOW_COMPLEX_NET_EVENTS code	*/
/* 08/14/98  MDE     18    Updated 'u_a_data_ind' parameters as reqd	*/
/* 07/20/98  JRB     17    If MVL_NUM_CALLED not defined, reject all	*/
/*			   associate indications.			*/
/* 07/16/98  MDE     16    Addded 'mvl_dib_entry_to_app_ref'		*/
/* 07/06/98  JRB     15    mvl_init_cl_netinfo use new DIB_ENTRY and	*/
/*			   init rem_mac_valid so it sends to ALL-ES.	*/
/* 06/18/98  JRB     14    Bind to TP4 and TCP if tp_type=TP_TYPE_TPX.	*/
/* 02/10/98  MDE     13    Added 'index' to connection control.		*/
/* 12/29/97  JRB     12    Call c?pp_terminate if bind fails.		*/
/* 12/22/97  MDE     11    Changed '_mvl_net_service' to report activity	*/
/* 09/18/97  MDE     10    Now call 'a_set_msgsize'			*/
/* 02/07/97  MDE     09    Changed to use new MMS-EASE opcode defines	*/
/* 12/13/96  MDE     08    Now support CLACSE, other enhancements	*/
/* 11/18/96  MDE     07    Minor name cleanup, some CLACSE stuff	*/
/* 11/15/96  MDE     06    Global variable work				*/
/* 11/11/96  MDE     05    Added async functions, other cleanup		*/
/* 10/31/96  MDE     04    Added ALLOW_COMPLEX_NET_EVENTS code		*/
/* 08/19/96  MDE     03    Now save all remote node's initiate info	*/
/* 07/22/96  MDE     02    Changes towards A-Unit-Data support		*/
/* 01/21/94  MDE     01    Initial                       		*/
/************************************************************************/
#include <assert.h>
#include "rdb_client.h"
#include "glbtypes.h"
#include "sysincs.h"
#include "glbsem.h"

#include "mmsdefs.h"
#include "mms_pvar.h"
#include "mms_vvar.h"
#include "mms_err.h"
#include "mms_pcon.h"
#include "asn1defs.h"
#include "slog.h"
#include "tp4api.h"
#include "mvl_acse.h"
#include "mvl_defs.h"
#include "mvl_log.h"
#include "mloguser.h"
#include "mmsop_en.h"
#include "../cfg_parse/custom_printf.h"//lnk20250225
#if defined(MAP30_ACSE)
#include "suicacse.h"

/* To limit the number of Marben stack files in the \mmslite\inc we	*/
/* prototype few referenced SUIC functions here.			*/
DIB_ENTRY *find_loc_dib_entry (ST_CHAR *name);
DIB_ENTRY *find_dib_entry (ST_CHAR *name);
#elif defined(LEAN_T)
#include "acse2.h"
#endif

#if defined(_S_TEST_PROBES)
#include "stestprb.h"
#endif

/************************************************************************/
/* For debug version, use a static pointer to avoid duplication of 	*/
/* __FILE__ strings.							*/
/************************************************************************/

#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif

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

MVL_MMS_STATISTICS mvl_mms_statistics;

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

#define NEGIOTIATE_INITIATE_PARAM
#define MVL_MMSOP_RELEASE 512	/* Add a MMS opcode define ... */

/************************************************************************/
/* Global variables - Connection Oriented Only				*/
/* The user must fill in a table of DIB entries for local and remote	*/
/* nodes of interest. This can be done by the user via the 'init_dirser'*/
/* function, can be hard coded, etc..					*/

#if !defined(MAP30_ACSE)    /* For MAP30_ACSE use SUIC function instead */
ST_INT num_loc_dib_entries;
DIB_ENTRY *loc_dib_table;
ST_INT num_rem_dib_entries;
DIB_ENTRY *rem_dib_table;
#endif

DIB_ENTRY *_mvla_initiate_locDe;
NET_BIND_CTRL *_mvla_initiate_bc;

/* Our connection controls - we support multiple called and/or calling	*/
/* simultanious connections.						*/

MVL_NET_INFO *mvl_calling_conn_ctrl;
MVL_NET_INFO *mvl_called_conn_ctrl;

/* Other user configurable parameters					*/
#ifndef MMS_INIT_EN
#error MMS_INIT_EN not defined. Must include mmsop_en.h
#endif
#if (MMS_INIT_EN & RESP_EN) 
INIT_INFO *mvl_init_resp_info;
#endif

ST_VOID (*u_mvl_disc_ind_fun) (MVL_NET_INFO *cc, ST_INT discType);
ST_VOID (*_mvlu_rpt_disconnect_rcvd_fun)(MVL_NET_INFO *netInfo);

/************************************************************************/
/* This variable is set SD_TRUE when the ACSE interface is viable		*/
ST_INT mvl_acse_ok;

/************************************************************************/
/* These arrays are created in mmsop_en.c */
extern SD_CONST ST_UCHAR  m_param[2];
extern SD_CONST ST_UCHAR  m_service_resp[11];

/************************************************************************/
/************************************************************************/
/* Bind Control								*/


NET_BIND_CTRL ma_clbind_ctrl;	/* to save clpp_bind info.	*/

/* These variables are used in making some asynchronous ACSE functions 	*/
/* work in a blocking mode						*/
static ST_INT ma_cmd_done;
static ST_INT ma_cmd_rslt;

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

MVL_CFG_INFO *mvl_cfg_info;

/************************************************************************/
/* MMS COMM EVENT CONTROL MANAGEMENT */

static MVL_COMM_EVENT *mvl_comm_event_up_head;	/* ptr to linked list	*/

/************************************************************************/
/* MVL_NET_INFO handling for incoming A-Unit Data				*/

#if defined(CLACSE)
ST_INT16 mvl_max_net_info = 10;
static ST_INT16 mvl_net_info_count;
static MVL_NET_INFO *mvl_net_info_avail_head;

static MVL_NET_INFO *mvl_get_net_info (ST_VOID);
static ST_VOID mvl_free_net_info (MVL_NET_INFO *netInfo);
#endif

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

typedef struct
  {
  DBL_LNK l;
  ST_LONG u_bind_id;
  ST_LONG acse_conn_id; 
  AARQ_APDU *info;
  } MVL_ASSOC_IND;
MVL_ASSOC_IND *mvlAssocIndList;

ST_BOOLEAN mvl_init_ind_hold;

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

/* Internal functions */
static ST_VOID ma_wait_req_done (ST_RET *retOut);
#if (MMS_INIT_EN & RESP_EN)
static MVL_NET_INFO *ma_get_called_conn_ctrl (NET_BIND_CTRL *);
#endif
#if (MMS_INIT_EN & REQ_EN)
static MVL_NET_INFO *ma_get_calling_conn_ctrl (NET_BIND_CTRL *);
#endif
static ST_VOID copp_unbind_sync (ST_LONG copp_bind_id);
static ST_RET ma_bind (NET_BIND_CTRL *bc, ST_BOOLEAN activate);

static MVL_COMM_EVENT *mvl_get_com_event (ST_VOID);

/************************************************************************/
/************************************************************************/
/*                       _mvl_net_service				*/
/************************************************************************/

ST_BOOLEAN _mvl_net_service (MVL_COMM_EVENT **eventOut)
  {
ST_BOOLEAN activityFlag;
MVL_ASSOC_IND *mvlAssocInd;

  *eventOut = NULL;
  if (mvl_acse_ok != SD_TRUE)
    return (SD_FALSE);

  S_LOCK_COMMON_RESOURCES ();

/* If we have a delayed initiate indication (ICCP Lite redundancy),	*/
/* handle it now 							*/
  if (mvlAssocIndList && !mvl_init_ind_hold)
    {
    mvlAssocInd = list_get_first (&mvlAssocIndList);
    u_a_associate_ind (mvlAssocInd->u_bind_id, 
    		       mvlAssocInd->acse_conn_id, 
    		       mvlAssocInd->info);
    chk_free (mvlAssocInd);
    S_UNLOCK_COMMON_RESOURCES ();
    return (SD_TRUE);
    }

/* May cause the 'acse' user function to be called if something is	*/
/* going on that we need to know about. If so, the functions will	*/
/* get the event and put them onto the 'mvl_comm_event_up_head' list	*/

  activityFlag = copp_event ();
  /* Occasionally copp_event gets 2 events, one from TP0 and one from TP4,*/
  /* so they are put on this linked list. "Usually" only one on list.	*/
  if (mvl_comm_event_up_head)
    *eventOut = (MVL_COMM_EVENT *) list_get_first((ST_VOID **) &mvl_comm_event_up_head);

  if (mvl_comm_event_up_head != NULL)
    {
    /* more events on linked list	*/
    activityFlag=SD_TRUE;	/* force it so user knows to call again	*/
    }

  S_UNLOCK_COMMON_RESOURCES ();
  return (activityFlag);
  }

/************************************************************************/
/*			mvl_get_com_event				*/
/************************************************************************/
/* This function is used to get a MVL_COMM_EVENT control buffer to be	*/
/* passed up to the caller of '_mvl_net_service'. 			*/

static MVL_COMM_EVENT *mvl_get_com_event (ST_VOID)
  {
MVL_COMM_EVENT *commEvent;

  S_LOCK_COMMON_RESOURCES ();
  commEvent = (MVL_COMM_EVENT *) M_CALLOC (MSMEM_COM_EVENT, 1, sizeof (MVL_COMM_EVENT));  
  if (commEvent == NULL)
    {
    MVL_LOG_ERR0 ("Error: No MVL_COMM_EVENT available");
    S_UNLOCK_COMMON_RESOURCES ();
    return (NULL);
    }

  list_add_last((ST_VOID **) &mvl_comm_event_up_head, (ST_VOID *) commEvent);
  S_UNLOCK_COMMON_RESOURCES ();
  return (commEvent);
  }

/************************************************************************/
/*			_mvl_free_comm_event				*/
/************************************************************************/

ST_VOID _mvl_free_comm_event (MVL_COMM_EVENT *event)
  {
  S_LOCK_COMMON_RESOURCES ();
  if (event->event_type == MMS_MSG_RCVD)
    {
    M_FREE (MSMEM_RXPDU, event->u.mms.pdu);

    if (event->u.mms.dec_rslt.data_pres)
      {
      M_FREE (MSMEM_DEC_OS_INFO, event->u.mms.dec_rslt.data_ptr);
      }  
    }

  M_FREE (MSMEM_COM_EVENT, event);
  S_UNLOCK_COMMON_RESOURCES ();
  }

/************************************************************************/
/*		 	mvl_set_max_msg_size 				*/
/************************************************************************/

ST_VOID mvl_set_max_msg_size (ST_INT max_msg_size)
  {
  mvl_cfg_info->max_msg_size = max_msg_size;
  mmsl_enc_buf_size = max_msg_size + ASN1_MAX_ELEMENT_OVERHEAD;
  if (mmsl_enc_buf != NULL)
    M_FREE (MSMEM_STARTUP, mmsl_enc_buf);

  mmsl_enc_buf = M_MALLOC (MSMEM_STARTUP, mmsl_enc_buf_size);
  }

/************************************************************************/
/*			mvl_init_aa_obj_ctrl				*/
/* Init struct to control AA-Specific object creation (var and nvl).	*/
/* Init one MVL_AA_OBJ_CTRL struct for one connection.			*/
/* NOTE: if OBSOLETE_AA_OBJ_INIT, this initialization is done by Foundry.*/
/************************************************************************/
#if !defined(OBSOLETE_AA_OBJ_INIT)
static MVL_AA_OBJ_CTRL *mvl_init_aa_obj_ctrl ()
  {
MVL_AA_OBJ_CTRL *aa_obj;
  aa_obj = (MVL_AA_OBJ_CTRL *) M_CALLOC (MSMEM_STARTUP, 1, sizeof(MVL_AA_OBJ_CTRL));

  /* Set up for dynamic creation of AA-Specific Variables.	*/
  aa_obj->max_num_var_assoc = mvl_max_dyn.aa_vars;
  aa_obj->num_var_assoc = 0;
  if (aa_obj->max_num_var_assoc)
    aa_obj->var_assoc_tbl = (MVL_VAR_ASSOC **) M_CALLOC (MSMEM_STARTUP, aa_obj->max_num_var_assoc, sizeof (MVL_VAR_ASSOC *));

  /* Set up for dynamic creation of AA-Specific NamedVariableLists (NVL).	*/
  aa_obj->max_num_nvlist = mvl_max_dyn.aa_nvls;
  aa_obj->num_nvlist = 0;
  if (aa_obj->max_num_nvlist)
    aa_obj->nvlist_tbl = (MVL_NVLIST_CTRL **) M_CALLOC (MSMEM_STARTUP, aa_obj->max_num_nvlist, sizeof (MVL_NVLIST_CTRL *));
  return (aa_obj);
  }
#endif	/* #if !defined(OBSOLETE_AA_OBJ_INIT)	*/

/************************************************************************/
/*                       mvl_start_acse					*/
/************************************************************************/

ST_INT mvl_start_acse (MVL_CFG_INFO *cfgInfo)
  {
NET_BIND_CTRL *bind_ctrl_src;
NET_BIND_CTRL *bind_ctrl_dest;
NET_BIND_CTRL temp_bind_ctrl;
ST_INT mvl_cfg_info_size;
ST_INT ret;
ST_INT i;
ST_INT index = 0;
extern int mmsop_en_version1;

  mmsop_en_version1=0;	/* value of this var means nothing, but it	*/
			/* causes error if you link to older mmsop_en.c	*/

  MVL_LOG_ACSE0 ("ACSE: Starting");

  mvl_debug_sel |= MVLLOG_ERR;		/* Always enable error logging.	*/

/* Here we will make the MVL configuration information persistant 	*/
#if defined(MAP30_ACSE)
  if (cfgInfo->use_bind_ctrl != SD_TRUE)
#endif
    {	/* Create the required bind control structures, if not supplied */
    /* Use memset to start with clean structure	*/
    memset (&temp_bind_ctrl, 0, sizeof (NET_BIND_CTRL));	/* CRITICAL*/
    strcpy (temp_bind_ctrl.ar_name, cfgInfo->local_ar_name);
    temp_bind_ctrl.num_calling = cfgInfo->num_calling;
    temp_bind_ctrl.num_called = cfgInfo->num_called;
    cfgInfo->bind_ctrl = &temp_bind_ctrl;
    cfgInfo->max_bind_ctrl = 1;
    cfgInfo->num_bind_ctrl = 1;
    }

/* OK, now copy the MVL configuration info */
  mvl_cfg_info_size = sizeof (MVL_CFG_INFO) + 
  			  (cfgInfo->max_bind_ctrl * sizeof (NET_BIND_CTRL));
  mvl_cfg_info = chk_calloc (1, mvl_cfg_info_size);
  *mvl_cfg_info = *cfgInfo;

  if (mvl_cfg_info->max_bind_ctrl == 0)
    mvl_cfg_info->max_bind_ctrl = mvl_cfg_info->num_bind_ctrl;

  mvl_cfg_info->calling_used = 0;
  mvl_cfg_info->called_used = 0;

  mvl_cfg_info->bind_ctrl = (NET_BIND_CTRL *) (mvl_cfg_info + 1);
  bind_ctrl_dest = mvl_cfg_info->bind_ctrl;
  bind_ctrl_src = cfgInfo->bind_ctrl;
  for (i = 0; i < mvl_cfg_info->num_bind_ctrl; ++i)
    {
    *bind_ctrl_dest = *bind_ctrl_src;
    ++bind_ctrl_src;
    ++bind_ctrl_dest;
    }

/* Make sure max msg size has been set.					*/
  if (mvl_cfg_info->max_msg_size == 0)
    {
    MVL_LOG_ERR0 ("Max MMS message size must be set");
    return (SD_FAILURE);
    }
  mvl_set_max_msg_size (mvl_cfg_info->max_msg_size);	/* save to global & alloc bufs*/

/* Initialize function pointers for MMS allocation.			*/
  m_calloc_os_fun = _mvl_calloc_os;
  m_realloc_os_fun = _mvl_realloc_os;
  m_free_os_fun = _mvl_free_os;

#if !(MMS_INIT_EN & REQ_EN)
  if (mvl_cfg_info->num_calling != 0)
    {
    MVL_LOG_ERR0 ("num_calling > 0 is illegal because Initiate.Req is NOT enabled");
    return (SD_FAILURE);
    }
#endif
#if !(MMS_INIT_EN & RESP_EN)
  if (mvl_cfg_info->num_called != 0)
    {
    MVL_LOG_ERR0 ("num_called > 0 is illegal because Initiate.Resp is NOT enabled");
    return (SD_FAILURE);
    }
#endif

/* Make sure "num_calling" AND/OR "num_called" has been set.		*/
  if (mvl_cfg_info->num_calling == 0  &&  mvl_cfg_info->num_called == 0)
    {
    MVL_LOG_ERR0 ("num_calling AND num_called both 0, must set at least one." );
    return (SD_FAILURE);
    }

/* Alloc & fill in array of "calling" connection control structures.	*/
#if (MMS_INIT_EN & REQ_EN)
  if (mvl_cfg_info->num_calling != 0)
    mvl_calling_conn_ctrl = M_CALLOC (MSMEM_GEN, mvl_cfg_info->num_calling, sizeof (MVL_NET_INFO));
  for (i = 0; i < mvl_cfg_info->num_calling; ++i)
    {
    mvl_calling_conn_ctrl[i].index = index++;
#if !defined(OBSOLETE_AA_OBJ_INIT)
    mvl_calling_conn_ctrl[i].aa_objs = mvl_init_aa_obj_ctrl ();
#endif	/* #if !defined(OBSOLETE_AA_OBJ_INIT)	*/
    }
#endif

/* Alloc & fill in array of "called" connection control structures.	*/
#if (MMS_INIT_EN & RESP_EN)
  if (mvl_cfg_info->num_called != 0)
    mvl_called_conn_ctrl = M_CALLOC (MSMEM_GEN, mvl_cfg_info->num_called, sizeof (MVL_NET_INFO));
  for (i = 0; i < mvl_cfg_info->num_called; ++i)
    {
    mvl_called_conn_ctrl[i].index = index++;
#if !defined(OBSOLETE_AA_OBJ_INIT)
    mvl_called_conn_ctrl[i].aa_objs = mvl_init_aa_obj_ctrl ();
#endif	/* #if !defined(OBSOLETE_AA_OBJ_INIT)	*/
    }
#endif

/* Initialize Connection Oriented OSI Stack.	*/
  ret = copp_initialize (mvl_cfg_info->num_calling + mvl_cfg_info->num_called, mmsl_enc_buf_size);
  if (ret != SD_SUCCESS)
    {
    MVL_LOG_ERR1 ("Error: copp_initialize returned error '0x%x'", ret);
    return (ret);
    }

/* Initialize Connectionless OSI Stack.	*/
#if defined(CLACSE)
  ret = clpp_initialize ();
  if (ret != SD_SUCCESS)
    {
    MVL_LOG_ERR1 ("Error: clpp_initialize returned error '0x%x'", ret);
    return (ret);
    }
#endif

/* Bind local AR names */
  for (i = 0; i < mvl_cfg_info->num_bind_ctrl; ++i)
    {	
    ret = ma_bind (&mvl_cfg_info->bind_ctrl[i], SD_TRUE);
    if (ret != SD_SUCCESS)
      return (ret);
    }

/* Binding not necessary for CLACSE.	*/

/* Initialize all mms objects (generated by foundry).			*/
  if (ret == SD_SUCCESS)
    mvl_init_mms_objs ();

  if (ret == SD_SUCCESS)
    mvl_acse_ok = SD_TRUE;
  else
    {
    copp_terminate ();
#if defined(CLACSE)
    clpp_terminate ();
#endif
    }

  MVL_LOG_ACSE1 ("ACSE: Start complete, returning '0x%x'", ret);
  mvl_mms_statistics.startTime = sGetMsTime ();

  return (ret);
  }

/************************************************************************/
/*                       mvl_end_acse					*/
/************************************************************************/

ST_INT mvl_end_acse (ST_VOID)
  {
NET_BIND_CTRL *bc;
ST_INT i;

  MVL_LOG_ACSE0 ("ACSE: Ending");

/* Unbind all */
  for (i = 0; i < mvl_cfg_info->num_bind_ctrl; ++i)
    {
    bc = &mvl_cfg_info->bind_ctrl[i];
#ifdef LEAN_T
    if (bc->copp_bind_id_tcp)
      copp_unbind_sync (bc->copp_bind_id_tcp);
    if (bc->copp_bind_id_tp4)
      copp_unbind_sync (bc->copp_bind_id_tp4);
#else	/* !LEAN_T	*/
    copp_unbind_sync (bc->copp_bind_id);
#endif	/* !LEAN_T	*/
    }

/* Shut down the ACSE interface, as well as it's subsystems		*/

  copp_terminate ();
#if defined(CLACSE)
  clpp_terminate ();
#endif

  MVL_LOG_ACSE0 ("ACSE: Ended");
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			_mvl_send_msg					*/
/************************************************************************/

ST_INT _mvl_send_msg (ST_UCHAR *pdu, ST_INT pdu_len, MVL_NET_INFO *net_info)
  {
ST_INT ret;

/* See if we are supposed to log ACSE data for this connection		*/
  if (mvl_conn_filtered_logging == SD_FALSE || net_info->log_enable)
    {
    MVL_LOG_ACSEDATA2 ("ACSE: Sending %d bytes on MVL_NET_INFO 0x%08lx", 
						pdu_len, net_info);
    MVL_LOG_ACSEDATAH (pdu_len, pdu);
    }

  ret = SD_FAILURE;

#if (MMS_INIT_EN != REQ_RESP_DIS)
    if (net_info->conn_active == SD_FALSE)
    { /* no connection */
      ret = MVL_ERR_CNF_DISCONNECTED;	/* more precise error code	*/
      MVL_LOG_NERR1 ("ACSE: no connection '0x%x'", ret);
    }
    else
    { /* valid connection */
      S_LOCK_COMMON_RESOURCES ();
      ret = a_data_req (net_info->acse_conn_id, pdu_len, pdu);
      S_UNLOCK_COMMON_RESOURCES ();
      if (ret != SD_SUCCESS)
        {
          MVL_LOG_NERR1 ("ACSE: a_data_req error '0x%x'", ret);
        }
    }
#endif
  return (ret);
  }

/************************************************************************/
/************************************************************************/
/*		DIRECTORY SERVICES FUNCTIONS				*/
/************************************************************************/
/************************************************************************/
/*                      find_loc_dib_entry                              */
/************************************************************************/
#if !defined(MAP30_ACSE)    /* For MAP30_ACSE use SUIC function instead */
DIB_ENTRY *find_loc_dib_entry (ST_CHAR *name)
  {
ST_INT i;
DIB_ENTRY *de;

  for (i = 0; i < num_loc_dib_entries; i++)
    {
    de = &loc_dib_table[i];
    if (!strcmp (de->name, name))
      return (de);
    }
  return (NULL);
  }
#endif  /* !defined(MAP30_ACSE) */

/************************************************************************/
/*                      find_rem_dib_entry                              */
/************************************************************************/

DIB_ENTRY *find_rem_dib_entry (ST_CHAR *name)
  {
#if !defined(MAP30_ACSE)
ST_INT i;
#endif
DIB_ENTRY *de;

#if defined(MAP30_ACSE)
  /* For Marben stack use SUIC function					*/
  de = find_dib_entry (name);	/* searches first loc then rem tables	*/
  return (de);
#else   /* !defined(MAP30_ACSE) */
  for (i = 0; i < num_rem_dib_entries; i++)
    {
    de = &rem_dib_table[i];
    if (!strcmp (de->name, name))
      return (de);
    }
  return (NULL);
#endif  /* !defined(MAP30_ACSE) */
  }

/************************************************************************/
/*			mvl_dib_entry_to_app_ref			*/
/************************************************************************/

ST_VOID mvl_dib_entry_to_app_ref (DIB_ENTRY *de, APP_REF *appRef)
  {
ST_INT i;

  appRef->form = APP_REF_FORM2;
  appRef->ap_title_pres = de->AP_title_pres;
  if (appRef->ap_title_pres == SD_TRUE)
    {
    appRef->ap_title.form_2.num_comps = de->AP_title.num_comps;
    for (i = 0; i < de->AP_title.num_comps; ++i)
      appRef->ap_title.form_2.comps[i] = de->AP_title.comps[i];
    }
  appRef->ap_invoke_pres = de->AP_inv_id_pres;
  appRef->ap_invoke = de->AP_invoke_id;
  appRef->ae_qual_pres = de->AE_qual_pres;
  appRef->ae_qual.form_2 = de->AE_qual;
  appRef->ae_invoke_pres = de->AE_inv_id_pres;
  appRef->ae_invoke = de->AE_invoke_id;
  }

/************************************************************************/
/************************************************************************/
#if (MMS_INIT_EN != REQ_RESP_DIS)	/* COACSE SPECIFIC FUNCTIONS	*/
/************************************************************************/

ST_RET mvla_set_init_ar (ST_CHAR *ar_name)
  {
NET_BIND_CTRL *bc;
ST_INT i;

/* See if user want's to use the default AR */
 if (ar_name == NULL) 
   {
   _mvla_initiate_bc = NULL;
   return (SD_SUCCESS);
   }

  for (i = 0; i < mvl_cfg_info->num_bind_ctrl; ++i)
    {
    bc = &mvl_cfg_info->bind_ctrl[i];
    if (!strcmp (ar_name, bc->ar_name))
      {
      _mvla_initiate_bc = bc;
      return (SD_SUCCESS);
      }
    }
  return (SD_FAILURE);
  }

#if (MMS_INIT_EN & REQ_EN)

/************************************************************************/
/*			mvl_initiate_req				*/
/************************************************************************/

ST_INT mvl_initiate_req (ST_CHAR *remAr,
			INIT_INFO *req_info, 
			INIT_INFO *resp_info,
			MVL_NET_INFO **net_info_out)
  {
MVL_REQ_PEND *req;
ST_INT rc;

  if (_mvl_comm_serve_active == SD_TRUE)
    {
    MVL_LOG_NERR0 ("Error: mvl_comm_serv active");
    return (SD_FAILURE);
    }

  rc = mvla_initiate_req (remAr, req_info, resp_info, net_info_out, &req);
  if (rc != SD_SUCCESS)
    return (rc);

  rc = mvl_wait_req_done (req);
  if (rc != SD_SUCCESS)
    {
    MVL_LOG_NERR0 ("ACSE: Initiate failed");
    }

  mvl_free_req_ctrl (req);        /* we are done with this req ctrl	*/
  return (rc);
  }

/************************************************************************/
/*			mvla_initiate_req				*/
/************************************************************************/

ST_INT mvla_initiate_req (ST_CHAR *remAr,
			INIT_INFO *req_info, 
			INIT_INFO *resp_info,
			MVL_NET_INFO **net_info_out,
			MVL_REQ_PEND **req_out)
  {
ST_INT rc;

  rc = mvla_initiate_req_ex (remAr, req_info, resp_info, net_info_out, 
			     req_out, NULL, NULL);

  return rc;
  }

/************************************************************************/
/*			mvla_initiate_req_ex				*/
/************************************************************************/


ST_INT mvla_initiate_req_ex (ST_CHAR *remAr,
			     INIT_INFO *req_info, 
			     INIT_INFO *resp_info,
			     MVL_NET_INFO **net_info_out,
			     MVL_REQ_PEND **req_out,
			     ACSE_AUTH_INFO *auth_info,
			     S_SEC_ENCRYPT_CTRL *encrypt_info)
  {
ST_INT rc;
MVL_NET_INFO *cc;
DIB_ENTRY *locDe;
DIB_ENTRY *remDe;
AARQ_APDU aarq_apdu;
ST_LONG acse_conn_id;
MVL_REQ_PEND *req;

  *req_out = NULL;			/* make sure it's initialized	*/

  MVL_LOG_ACSE1 ("ACSE: Initiate Request to '%s'", remAr);

  /* Get a calling connection control structure					*/

  cc = ma_get_calling_conn_ctrl (_mvla_initiate_bc);
  if (!cc)
    {
    MVL_LOG_ERR0 ("Error - Could not get calling MVL_NET_INFO");
    return (MVL_ERR_NO_CONN_CTRL);
    }
  MVL_LOG_CACSE1 ("Using MVL_NET_INFO %08lx", cc);

  req = _mvl_get_req_ctrl (cc, MMSOP_INITIATE);
  if (!req)
    {
    cc->in_use = SD_FALSE;
    return (MVL_ERR_NO_REQ_CTRL);
    }
  *req_out = req;
  req->invoke_id = -1;

  rc = mpl_init (req_info);
  if (rc == SD_SUCCESS)
    {
   /* We can log here if so desired */
   /* if ((mms_debug_sel & MMS_LOG_REQ) && ml_log_req_info_fun != NULL)
        (*ml_log_req_info_fun) (0, MMSOP_INITIATE, req_info);*/

  /* Save the output pointer in the MMS request control element		*/
    req->u.init.resp_info = resp_info;

  /* Save our initiate request information ... */
    memcpy (&cc->locl_init_info, req_info, sizeof (INIT_INFO));

  /* Retrieve the DIB_ENTRY's for local and remote. These are used to set	*/
  /* the AARQ_APDU information						*/
    if (_mvla_initiate_locDe != NULL)
      locDe = _mvla_initiate_locDe;
    else
      locDe = cc->bind_ctrl->de;

    remDe = find_rem_dib_entry (remAr);
    if (remDe)
      {  
    /* Now set up all AARQ_APDU information based on DIB_ENTRY information	*/
    
#if !defined(MAP30_ACSE)  /* most info not needed for Lite over Marben stack */
    /* AP Context is MMS */
      aarq_apdu.ASO_context_name_pres = SD_TRUE;
      aarq_apdu.ASO_context_name.num_comps = 5;
      aarq_apdu.ASO_context_name.comps[0] = 1;
      aarq_apdu.ASO_context_name.comps[1] = 0;
      aarq_apdu.ASO_context_name.comps[2] = 9506;
      aarq_apdu.ASO_context_name.comps[3] = 2;
      aarq_apdu.ASO_context_name.comps[4] = 3;

    /* Calling AE Title */
      aarq_apdu.calling_ae_title.AP_title_pres = locDe->AP_title_pres;
      memcpy (&aarq_apdu.calling_ae_title.AP_title, 
    	  &locDe->AP_title, 
    	  sizeof (MMS_OBJ_ID));
    
      aarq_apdu.calling_ae_title.AE_qual_pres   = locDe->AE_qual_pres;
      aarq_apdu.calling_ae_title.AE_qual        = locDe->AE_qual;
      aarq_apdu.calling_ae_title.AP_inv_id_pres = locDe->AP_inv_id_pres;
      aarq_apdu.calling_ae_title.AP_inv_id      = locDe->AP_invoke_id;
      aarq_apdu.calling_ae_title.AE_qual_pres   = locDe->AE_qual_pres;
      aarq_apdu.calling_ae_title.AE_qual        = locDe->AE_qual;
      aarq_apdu.calling_ae_title.AE_inv_id_pres = locDe->AE_inv_id_pres;
      aarq_apdu.calling_ae_title.AE_inv_id      = locDe->AE_invoke_id;
  
    /* Called AE Title */
      aarq_apdu.called_ae_title.AP_title_pres = remDe->AP_title_pres;
      memcpy (&aarq_apdu.called_ae_title.AP_title, 
    	  &remDe->AP_title, 
    	  sizeof (MMS_OBJ_ID));
    
      aarq_apdu.called_ae_title.AE_qual_pres   = remDe->AE_qual_pres;
      aarq_apdu.called_ae_title.AE_qual        = remDe->AE_qual;
      aarq_apdu.called_ae_title.AP_inv_id_pres = remDe->AP_inv_id_pres;
      aarq_apdu.called_ae_title.AP_inv_id      = remDe->AP_invoke_id;
      aarq_apdu.called_ae_title.AE_qual_pres   = remDe->AE_qual_pres;
      aarq_apdu.called_ae_title.AE_qual        = remDe->AE_qual;
      aarq_apdu.called_ae_title.AE_inv_id_pres = remDe->AE_inv_id_pres;
      aarq_apdu.called_ae_title.AE_inv_id      = remDe->AE_invoke_id;
#endif  /* !defined(MAP30_ACSE) */
  
#ifdef REDUCED_STACK
  /* For Reduced Stack, the presentation address consists only of the 	*/
  /* ADLC address.							*/
      aarq_apdu.called_paddr.adlcAddr  = remDe->adlcAddr;
#endif

#ifdef LEAN_T
    /* For Trim 7, the presentation address has all the parts		*/
      memcpy (&aarq_apdu.called_paddr, &remDe->pres_addr, sizeof (PRES_ADDR));
#endif

    /* ACSE Authentication */
      if(auth_info != NULL)
      	memcpy (&aarq_apdu.auth_info, auth_info, sizeof (ACSE_AUTH_INFO));
      else
        aarq_apdu.auth_info.auth_pres = SD_FALSE;
    /* copy encryption info */
      if (encrypt_info != NULL)
      	memcpy (&aarq_apdu.encrypt_ctrl, encrypt_info, sizeof (S_SEC_ENCRYPT_CTRL));
      else
        aarq_apdu.encrypt_ctrl.encryptMode = S_SEC_ENCRYPT_NONE;
    
    /* User Data */
      aarq_apdu.user_info.ptr = mmsl_msg_start;
      aarq_apdu.user_info.len = mmsl_msg_len;
  
      MVL_LOG_ACSEDATAH (mmsl_msg_len, mmsl_msg_start);
    
    /* Send the associate request						*/
#if defined(MAP30_ACSE)
      acse_conn_id = a_associate_reqm (cc->bind_ctrl->copp_bind_id, 
			(ST_LONG) cc, &aarq_apdu, 
			cc->bind_ctrl->ar_name, remAr);
#elif defined(LEAN_T)
      if (remDe->pres_addr.tp_type == TP_TYPE_TCP)
        acse_conn_id = a_associate_req (cc->bind_ctrl->copp_bind_id_tcp, (ST_LONG) cc, &aarq_apdu);
      else
        acse_conn_id = a_associate_req (cc->bind_ctrl->copp_bind_id_tp4, (ST_LONG) cc, &aarq_apdu);
#else
      acse_conn_id = a_associate_req (cc->bind_ctrl->copp_bind_id, (ST_LONG) cc, &aarq_apdu);
#endif
      if (!acse_conn_id)
        {
        MVL_LOG_NERR0 ("ACSE: a_associate_req failed");
        rc = MVL_ERR_ASSOC_REQ;
        }
      else
        ++mvl_mms_statistics.callingConn;
      }
    else
      {
      MVL_LOG_NERR1 ("ACSE: Remote name '%s' not found", remAr);
      rc = MVLE_REMOTE_NAME;
      }
    }
  else
    {
    MVL_LOG_NERR1 ("ACSE: mpl_init error '0x%x'", rc);
    }

  if (rc == SD_SUCCESS)
    {
    *net_info_out = cc;
    cc->acse_conn_id = acse_conn_id;
    }
  else
    {
    cc->in_use = SD_FALSE;
    mvl_free_req_ctrl (req);
    *req_out = NULL;			/* So user CANNOT free it too.	*/
    }

/* All is good, return the connection control too			*/
  return (rc);
  }
/************************************************************************/
#endif /* #if (MMS_INIT_EN & REQ_EN)					*/
/************************************************************************/

/************************************************************************/
/*			mvl_abort_req					*/
/*			mvl_abort_req_ex					*/
/************************************************************************/

ST_RET mvl_abort_req (MVL_NET_INFO *cc)
  {
  return (mvl_abort_req_ex (cc, SD_FALSE, 0));
  }


ST_RET mvl_abort_req_ex (MVL_NET_INFO *cc, 
			 ST_BOOLEAN diagnostic_pres, 
			 ST_ACSE_AUTH diagnostic)
  {
ABRT_APDU abrtinfo;
ST_RET ret;

  MVL_LOG_ACSE1 ("ACSE: sending abort for MVL_NET_INFO %08lx", cc);

  abrtinfo.source = 0;		/* user	*/
  abrtinfo.diagnostic_pres = diagnostic_pres;
  abrtinfo.diagnostic = diagnostic;
  abrtinfo.user_info.len = 0;

  ret = a_abort_req (cc->acse_conn_id, &abrtinfo);
  if (ret == SD_SUCCESS) 
    {
    ++mvl_mms_statistics.localAbort;
    cc->in_use = SD_FALSE;
    cc->conn_active = SD_FALSE;
    if (_mvlu_rpt_disconnect_rcvd_fun != NULL)
       (*_mvlu_rpt_disconnect_rcvd_fun)(cc);
    _mvl_set_net_req_done (NULL, cc, MVL_ERR_CNF_DISCONNECTED);
    }
  else
    {
    MVL_LOG_NERR1 ("ACSE: Abort error '0x%x'", ret);
	echo_warn1("ACSE: Abort error '0x%x'\n", ret);
    }
  return (ret);
  }

/************************************************************************/
/*                       mvl_release_req					*/
/*			 mvla_release_req				*/
/************************************************************************/

ST_INT mvl_release_req (MVL_NET_INFO *cc) 
  {
  MVL_LOG_ERR0 ("ACSE: Using obsolete function 'mvl_release_req'");
  return (SD_FAILURE);
  }

ST_INT mvla_release_req (MVL_NET_INFO *cc, MVL_REQ_PEND **req_out)
  {
  MVL_LOG_ERR0 ("ACSE: Using obsolete function 'mvla_release_req'");
  return (SD_FAILURE);
  }

/************************************************************************/
/*			_mvla_release					*/
/************************************************************************/
/* Called from conclude confirm						*/

ST_VOID _mvl_release (MVL_REQ_PEND *req)
  {
RLRQ_APDU rlinfo;
ST_INT ret;
MVL_NET_INFO *net_info;

  net_info = req->net_info;
  req->op = MVL_MMSOP_RELEASE;	/* Change the opcode in the req ctrl	*/

  MVL_LOG_ACSE1 ("ACSE: sending release for MVL_NET_INFO %08lx", net_info);

  rlinfo.reason_pres = SD_TRUE;
  rlinfo.reason = 1;
  rlinfo.user_info.len = 0;
  ret = a_release_req (net_info->acse_conn_id, &rlinfo);
  if (ret != SD_SUCCESS)
    {
    MVL_LOG_NERR1 ("ACSE: Release error '0x%x'", ret);
    _mvl_set_req_done (req, SD_FAILURE);
    }
  else
    ++mvl_mms_statistics.clientConcl;
  }

/************************************************************************/
/************************************************************************/
/*			mvl_add_bind_ctrl				*/
/************************************************************************/

ST_RET mvl_add_bind_ctrl (NET_BIND_CTRL *bc)
  {
NET_BIND_CTRL *mvlBc;
ST_BOOLEAN activate;
ST_INT i;
ST_RET rc;

/* First let's check to see if we have room for one more */
  if (mvl_cfg_info->num_bind_ctrl >= mvl_cfg_info->max_bind_ctrl)
    {
    MVL_LOG_NERR0 ("Error: no availble bind control");
    return (SD_FAILURE);
    }

/* Let's see if this local AR has already been activated	*/
  activate = SD_TRUE;
  for (i = 0; i < mvl_cfg_info->num_bind_ctrl; ++i)
    {
    if (!strcmp (mvl_cfg_info->bind_ctrl[i].ar_name, bc->ar_name))
      {
      activate = SD_FALSE;
      break;
      }
    }

/* OK, now we can copy the bind control information */
  
  mvlBc = &mvl_cfg_info->bind_ctrl[mvl_cfg_info->num_bind_ctrl];
  *mvlBc = *bc;
  ++mvl_cfg_info->num_bind_ctrl;
  
  rc = ma_bind (mvlBc, activate);
  return (rc);
  }

/************************************************************************/
/*			mvl_bind					*/
/************************************************************************/

static ST_RET ma_bind (NET_BIND_CTRL *bc, ST_BOOLEAN activate)
  {
#if !defined(MAP30_ACSE)
DIB_ENTRY *de;
PRES_ADDR loc_pres_addr;
#endif
ST_INT ret;
ST_INT numConn;
MVL_NET_INFO *ni;
ST_INT j;

  MVL_LOG_ACSE1 ("ACSE: binding local name '%s'", bc->ar_name);

#if !defined(MAP30_ACSE)
/* Locate the local DIB entry for this name, so we can find the addr	*/
  de = find_loc_dib_entry (bc->ar_name);
  if (!de)
    {
    MVL_LOG_ERR1 ("Error: Local AR name '%s' not found", bc->ar_name);
    return (MVLE_LOCAL_NAME);
    }
  bc->de = de;
#endif

  if (((bc->num_calling + mvl_cfg_info->calling_used) > mvl_cfg_info->num_calling) ||
      ((bc->num_called + mvl_cfg_info->called_used) > mvl_cfg_info->num_called))
    {							       
    MVL_LOG_ERR0 ("Error: not enough connections");
    return (MVLE_NUM_CONNECTIONS);
    }
  numConn = bc->num_calling + bc->num_called;

#ifdef REDUCED_STACK
/* For Reduced Stack, the presentation address consists only of the 	*/
/* ADLC address.							*/
  loc_pres_addr.adlcAddr = de->adlcAddr;
/* We make the binds synchronous */
  ma_cmd_done = SD_FALSE;
  ma_cmd_rslt = REQ_INCOMPLETE;
  ret = copp_bind ((ST_LONG) bc, &loc_pres_addr, SD_FALSE, numConn);
  ma_wait_req_done (&ret);
  if (ret != SD_SUCCESS)
    {
    MVL_LOG_ERR2 ("Error: copp_bind (TCP) of AR '%s' returned '0x%x'", 
  				bc->ar_name, ret);
    return (ret);
    }
#endif

#ifdef LEAN_T
/* For Trim 7, the presentation address has all the parts		*/
  memcpy (&loc_pres_addr, &de->pres_addr, sizeof (PRES_ADDR));
  if (de->pres_addr.tp_type == TP_TYPE_TPX)
    {				/* do 2 binds	*/
    /* Bind for TP4 (overwrite the "tp_type" in loc_pres_addr).	*/
    bc->tp_type = loc_pres_addr.tp_type = TP_TYPE_TP4;
    ma_cmd_done = SD_FALSE;
    ma_cmd_rslt = REQ_INCOMPLETE;
    ret = copp_bind ((ST_LONG) bc, &loc_pres_addr, SD_FALSE, numConn);
    ma_wait_req_done (&ret);
    if (ret != SD_SUCCESS)
      {
      MVL_LOG_ERR2 ("Error: copp_bind (TP4) of AR '%s' returned '0x%x'", 
    				bc->ar_name, ret);
      return (ret);
      }

    /* Bind for TCP (overwrite the "tp_type" in loc_pres_addr).	*/
    bc->tp_type = loc_pres_addr.tp_type = TP_TYPE_TCP;
    ret = copp_bind ((ST_LONG) bc, &loc_pres_addr, SD_FALSE, numConn);
    ma_wait_req_done (&ret);
    if (ret != SD_SUCCESS)
      {
      MVL_LOG_ERR2 ("Error: copp_bind (TCP) of AR '%s' returned '0x%x'", 
    				bc->ar_name, ret);
      return (ret);
      }
    }
  else	/* Not TPX */
    {
    bc->tp_type = loc_pres_addr.tp_type;
    ma_cmd_done = SD_FALSE;
    ma_cmd_rslt = REQ_INCOMPLETE;
    ret = copp_bind ((ST_LONG) bc, &loc_pres_addr, SD_FALSE, numConn);
    ma_wait_req_done (&ret);
    if (ret != SD_SUCCESS)
      {
      MVL_LOG_ERR2 ("Error: copp_bind of AR '%s' returned '0x%x'", 
    				bc->ar_name, ret);
      return (ret);
      }
    }
#endif

#if defined(MAP30_ACSE)
  ma_cmd_done = SD_FALSE;
  ma_cmd_rslt = REQ_INCOMPLETE;
  ret = _copp_bind ((ST_LONG) bc, bc->ar_name, bc->num_calling, bc->num_called, activate);
  ma_wait_req_done (&ret);
  if (ret != SD_SUCCESS)
    {
    MVL_LOG_ERR2 ("Error: copp_bind of AR '%s' failed '0x%x'", 
    				bc->ar_name, ret);
    return (ret);
    }
#endif  /* defined(MAP30_ACSE) */

  MVL_LOG_ACSE0 ("ACSE: bind OK");


/* OK, now attach the bind controls to the MVL_NET_INFO */      
  ni = mvl_called_conn_ctrl + mvl_cfg_info->called_used;
  for (j = 0; j < bc->num_called; ++j, ++ni)
    ni->bind_ctrl = bc;

  ni = mvl_calling_conn_ctrl + mvl_cfg_info->calling_used;
  for (j = 0; j < bc->num_calling; ++j, ++ni)
    ni->bind_ctrl = bc;

  mvl_cfg_info->calling_used += bc->num_calling;
  mvl_cfg_info->called_used += bc->num_called;
  return (ret);
  }

/************************************************************************/
/*			copp_unbind_sync				*/
/************************************************************************/

static ST_VOID copp_unbind_sync (ST_LONG copp_bind_id)
  {
ST_RET ret;

  ma_cmd_done = SD_FALSE;
  ma_cmd_rslt = REQ_INCOMPLETE;
  ret = copp_unbind (copp_bind_id);
  if (ret != SD_SUCCESS)
    {
    MVL_LOG_ERR1 ("Error: unbind result '0x%x'", ret);
    }
  else /* Now just wait for the 'confirm' function to be called		*/
    ma_wait_req_done (&ret);
  }

/************************************************************************/
/* 		     COACSE USER FUNCTIONS	       			*/
/************************************************************************/
/************************************************************************/
/*                       u_copp_bind_cnf				*/
/************************************************************************/

ST_VOID u_copp_bind_cnf (ST_LONG user_bind_id, ST_LONG copp_bind_id, ST_INT result)
  {
NET_BIND_CTRL *bc;

/* Save the acse interface's bind ID for future use */
  bc = (NET_BIND_CTRL *) user_bind_id;
#ifdef LEAN_T
  if (bc->tp_type == TP_TYPE_TP4)
    bc->copp_bind_id_tp4 = copp_bind_id;
  else
    bc->copp_bind_id_tcp = copp_bind_id;
#else
  bc->copp_bind_id = copp_bind_id;
#endif

/* Allow sync binds to complete */
  ma_cmd_done = SD_TRUE;
  ma_cmd_rslt = result;
  }

/************************************************************************/
/*                       u_copp_unbind_cnf				*/
/************************************************************************/

ST_VOID u_copp_unbind_cnf (ST_LONG user_bind_id)
  {
/* Allow the request function to complete */
  ma_cmd_done = SD_TRUE;
  ma_cmd_rslt = SD_SUCCESS;
  }

/************************************************************************/
/*                       u_a_associate_ind				*/
/************************************************************************/

ST_VOID u_a_associate_ind (ST_LONG u_bind_id, ST_LONG acse_conn_id, 
			   AARQ_APDU *info)
  {
#if (MMS_INIT_EN & RESP_EN)

MMSDEC_INFO rslt;
INIT_INFO *init_req; 
INIT_INFO init_resp; 
MVL_NET_INFO *cc;
#if !defined(ICCP_LITE)
ST_ACSE_AUTH auth_ret;
#endif
ST_INT ret;
AARE_APDU reinfo;
ST_INT accept;
ST_BOOLEAN free_dec_info;
MVL_ASSOC_IND *mvlAssocInd;
AARQ_APDU     *reqinfo; 
ST_UCHAR      *user_info_buf;

  if (mvl_init_ind_hold)
    {
  /* Can't rely on this data being persistent			*/
  /* Allocate buffer big enough for the control structure, 	*/
  /* the AARQ info, and the user data 				*/
    mvlAssocInd = chk_malloc (sizeof (MVL_ASSOC_IND) + 
    			      sizeof (AARQ_APDU) + 
    			      info->user_info.len);
    mvlAssocInd->u_bind_id    = u_bind_id; 
    mvlAssocInd->acse_conn_id = acse_conn_id;

  /* Copy the AARQ data */
    reqinfo = (AARQ_APDU *) (mvlAssocInd + 1); 
    *reqinfo = *info;	
    mvlAssocInd->info = reqinfo;

  /* Copy the user data, set the pointer to it */
    user_info_buf = (ST_UCHAR *) (reqinfo + 1);
    reqinfo->user_info.ptr = user_info_buf;
    memcpy (user_info_buf, info->user_info.ptr, info->user_info.len);

    list_add_last (&mvlAssocIndList, mvlAssocInd);
    return;
    }

#if defined(_S_TEST_PROBES)
  if (_sTestProbeEnabled(_MI_TEST_INITIATE_IND_NO_RESPOND))
    {
    echo_warn ("TEST: Initiate indication, do not respond");
    return;
    }
#endif

  ++mvl_mms_statistics.calledConn;

  MVL_LOG_ACSE0 ("ACSE: Associate Indication received");
  MVL_LOG_ACSEDATAH (info->user_info.len, info->user_info.ptr);

  free_dec_info = SD_FALSE;

/* Assume the worst ... */
  accept = SD_FALSE;
  reinfo.user_info.len = 0;
  reinfo.auth_info.auth_pres = SD_FALSE;

/* Get a 'called' connection control structure, if one is available 	*/
  cc = ma_get_called_conn_ctrl ((NET_BIND_CTRL *) u_bind_id);
  if (cc)
    {
    MVL_LOG_CACSE1 ("Using MVL_NET_INFO %08lx", cc);

    /* Copy the whole AARQ Info struct, for the user to see		*/
    memcpy (&cc->ass_ind_info, info, sizeof (AARQ_APDU));

    /* IMPORTANT: Save the ACSE connection ID before calling user funtions.*/
    cc->acse_conn_id = acse_conn_id;

    /* We need to parse the MMS Initiate PDU 				*/
    free_dec_info = SD_TRUE;
    _ms_mms_decode (info->user_info.ptr, info->user_info.len, &rslt);
    if (rslt.err_code == NO_DECODE_ERR && rslt.type == MMSINITREQ)
      {
      init_req = (INIT_INFO *) rslt.data_ptr;
      init_req->mms_p_context_pres = SD_TRUE;

   /* We can log here if so desired */
      if ((mms_debug_sel & MMS_LOG_IND) && ml_log_ind_info_fun != NULL)
          (*ml_log_ind_info_fun) (0, MMSOP_INITIATE, init_req);

/* OK, we handle telling the user differently depending on whether this	*/
/* is ICCP Lite, and whether ACSE Authentication is in use or not	*/
#ifdef ICCP_LITE
      ret = mi_connect_ind_fun (cc, init_req, &reinfo.auth_info);
#else
    /* Let the user have a look to see if they want to accept		*/
      auth_ret = u_mvl_connect_ind_ex(cc, init_req, 
      				      &cc->ass_ind_info.auth_info, /* partner authentication	*/
      				      &reinfo.auth_info);	   /* ret rsp authentication	*/
      /* User did not like something about the authentication 		*/
      if (auth_ret != ACSE_AUTH_SUCCESS)
        {
  	MVL_LOG_ACSE1 ("ACSE: ACSE Authentication failure. Reason: %d", auth_ret);
        mvl_abort_req_ex (cc, SD_TRUE, auth_ret);
  	return;
  	}
      ret = SD_SUCCESS;	/* User is OK with it */
#endif /* ICCP_LITE */

      if (ret == SD_SUCCESS)
        {
    /* Build an initiate response PDU, negiotiating the calling init	*/
    /* parameters vs. what we support					*/

#ifdef NEGIOTIATE_INITIATE_PARAM
      /* Start with the default initiate response info			*/
        memcpy (&init_resp, mvl_init_resp_info, sizeof (INIT_INFO));
        if (init_req->version < init_resp.version)/* thiers is lower	*/
          init_resp.version = init_req->version;	/* accept lower version */
        if (init_req->max_segsize_pres)
          {
          if (init_req->max_segsize < init_resp.max_segsize)
            init_resp.max_segsize = init_req->max_segsize;  
          }
        if (init_req->maxreq_calling < init_resp.maxreq_calling)
          init_resp.maxreq_calling = init_req->maxreq_calling;
        if (init_req->maxreq_called < init_resp.maxreq_called)
          init_resp.maxreq_called = init_req->maxreq_called;
        if (init_req->max_nest_pres)
          {
          if (init_req->max_nest < init_resp.max_nest)
            init_resp.max_nest = init_req->max_nest;
          }
      /* These 'supported' bitstring parameters are set up in mmsop_en.c	*/
        memcpy (init_resp.serv_supp, m_service_resp, 11);
        init_resp.param_supp[0] = m_param[0] & init_req->param_supp[0];
        init_resp.param_supp[1] = m_param[1] & init_req->param_supp[1];
#else
        /* Just send back what the user wants to send */ 
        memcpy (&init_resp, mvl_init_resp_info, sizeof (INIT_INFO));
#endif

        ret = mpl_init_resp (&init_resp);
        if (ret == SD_SUCCESS)
          {
         /* We can log here if so desired */
          if ((mms_debug_sel & MMS_LOG_RESP) && ml_log_resp_info_fun != NULL)
            (*ml_log_resp_info_fun) (0, MMSOP_INITIATE, &init_resp);

          accept = SD_TRUE;
      
          cc->conn_active = SD_TRUE;
        
          /* Save the remote node's initiate information			*/
          memcpy (&cc->rem_init_info, init_req, sizeof (INIT_INFO));
          memcpy (&cc->locl_init_info, &init_resp, sizeof (INIT_INFO));
          cc->max_pdu_size = (ST_INT) init_resp.max_segsize;
          /* NOTE: maxreq_calling = num of outstanding indications	*/
          /* allowed at the calling app (similar for maxreq_called).	*/
          cc->maxpend_req = init_resp.maxreq_calling;
          cc->maxpend_ind = init_resp.maxreq_called;
          cc->numpend_req = 0;
          cc->numpend_ind = 0;
          }
        }

      if (ret != SD_SUCCESS)
        {
        MVL_LOG_NERR0 ("ACSE: User does not want connection");
        ret = mpl_init_err (8, 0);
        cc->in_use = SD_FALSE;
        }

      if (ret == SD_SUCCESS)
	{
        reinfo.user_info.len = mmsl_msg_len;
        reinfo.user_info.ptr = mmsl_msg_start;
	}
      }
    else
      {
      MVL_LOG_NERR0 ("ACSE: Initiate indication PDU parse error");
      free_dec_info = SD_FALSE;
      cc->in_use = SD_FALSE;
      }
    }
  else
    {
    MVL_LOG_ERR0 ("Error - Could not get called MVL_NET_INFO");
    }
    
/* Initiate Response (or error) PDU is built, or len set to 0. 		*/
/* Set the remaining ACSE parameters and send the response		*/

/* Use the calling context as the responding, to be friendly		*/  
  reinfo.ASO_context_name_pres = info->ASO_context_name_pres; 
  memcpy (&reinfo.ASO_context_name, &info->ASO_context_name, 
  	  sizeof (MMS_OBJ_ID)); 
 
/* For now, just reflect the ACSE indication information 		*/
/* Note that it may (or may not) be more appropriate to use the 'local'	*/
/* DIB_ENTRY info for this purpose.					*/
  memcpy (&reinfo.responding_ae_title, &info->called_ae_title, 
            sizeof (AE_TITLE));


  if (accept)
    {
    MVL_LOG_ACSE0 ("Accepting the connection");
    reinfo.result = 0;	/* ACCEPT	*/
    reinfo.result_source_diag_pres = SD_FALSE;
    }
  else
    {
    MVL_LOG_ACSE0 ("Rejecting the connection");
    reinfo.result = 1;	/* REJECT	*/
    reinfo.result_source_diag_pres = SD_TRUE;
    reinfo.result_source = 1;
    reinfo.result_diag = 9;
    }

  /* Send ACSE response	*/
  MVL_LOG_ACSEDATAH (reinfo.user_info.len, reinfo.user_info.ptr);
  ret = a_associate_rsp (acse_conn_id, (ST_LONG) cc, &reinfo);
  if (ret != SD_SUCCESS)
    {
    MVL_LOG_NERR1 ("ACSE: a_associate_rsp error '0x%x", ret);
    if (cc != NULL)
      cc->in_use = SD_FALSE;
    ++mvl_mms_statistics.calledConnFail;
    }
  else
    ++mvl_mms_statistics.calledConnOK;


  if (free_dec_info == SD_TRUE)
    M_FREE (MSMEM_DEC_OS_INFO, _mms_dec_info);

#else	/* !(MMS_INIT_EN & RESP_EN)	*/

AARE_APDU reinfo;
ST_LONG user_conn_id;
ST_INT ret;

  ++mvl_mms_statistics.calledConn;

/* Incoming connections not supported, so Reject the association.	*/
  MVL_LOG_ACSE0 ("ACSE: Associate Indication received. Rejecting.");

/* Just use the ASO-context from the request in the response.		*/
  reinfo.ASO_context_name_pres = info->ASO_context_name_pres;
  memcpy (&reinfo.ASO_context_name, &info->ASO_context_name,
  	  sizeof (MMS_OBJ_ID));
 
/* Just use the called AE-Title as the responding AE-Title.		*/
  memcpy (&reinfo.responding_ae_title, &info->called_ae_title,
          sizeof (AE_TITLE));

  /* Reject	*/
  reinfo.result = 1;		/* Rejected-permanent	*/
  reinfo.result_source_diag_pres = SD_TRUE;
  reinfo.result_source = 1;	/* Source = user.	*/
  reinfo.result_diag = 1;	/* no-reason-given	*/
  /* No User Information	*/
  reinfo.user_info.len = 0;
  reinfo.user_info.ptr = NULL;
  reinfo.auth_info.auth_pres = SD_FALSE;

  user_conn_id = 0;	/* id doesn't matter because rejecting	*/
  if ((ret = a_associate_rsp (acse_conn_id, user_conn_id, &reinfo))!=SD_SUCCESS)
    {
    MVL_LOG_ERR1 ("Error sending a_associate_rsp. Error = %d", ret);
    }
  ++mvl_mms_statistics.calledConnFail;
#endif	/* !(MMS_INIT_EN & RESP_EN)	*/
  }


/************************************************************************/
/*			u_a_associate_cnf				*/
/************************************************************************/

ST_VOID u_a_associate_cnf (ST_LONG acse_conn_id, ST_LONG user_conn_id,
			AARE_APDU *info)
  {
#if (MMS_INIT_EN & REQ_EN)
MMSDEC_INFO rslt;
MVL_NET_INFO *cc;
INIT_INFO *init_resp; 
MVL_REQ_PEND *req;
ST_BOOLEAN free_dec_info;
ST_RET result;
ST_ACSE_AUTH auth_ret;

/* Need to save off the ACSE interface's connection ID for future use	*/
  cc = (MVL_NET_INFO *) user_conn_id;
  cc->acse_conn_id = acse_conn_id;

  MVL_LOG_ACSE1 ("ACSE: associate confirm for MVL_NET_INFO %08lx", cc); 
    
  req = _mvl_find_req_ctrl (cc, MMSOP_INITIATE, -1);
  if (!req)
    {
    MVL_LOG_ERR0 ("ACSE: Associate Confirm - no matching req ctrl");
    info->result = SD_FAILURE;		/* Cause disconnect		*/
    cc->in_use = SD_FALSE;
    return;
    }

  /* Only check ACSE authentication if this is a positive connect confirm */
  if (info->result == SD_SUCCESS) 
    {
    auth_ret = u_mvl_connect_cnf_ex(cc, info);
  
    /* User did not like something about the authentication 		*/
    if(auth_ret != ACSE_AUTH_SUCCESS)
      {
      MVL_LOG_ACSE1 ("ACSE: ACSE Authentication failure. Reason: %d", auth_ret);
      mvl_abort_req_ex (cc, SD_TRUE, auth_ret);
      return;
      }
    }

  free_dec_info = SD_FALSE;
  result = info->result;

  if (info->result == SD_SUCCESS) 
    {
    cc->conn_active = SD_TRUE;

    MVL_LOG_ACSEDATAH (info->user_info.len, info->user_info.ptr);

  /* OK, now we need to parse the MMS Initiate PDU 			*/
    free_dec_info = SD_TRUE;
    _ms_mms_decode (info->user_info.ptr, info->user_info.len, &rslt);
    if (rslt.err_code == NO_DECODE_ERR)
      {
      switch (rslt.type)
        {
        case MMSINITRESP :
          init_resp = (INIT_INFO *) rslt.data_ptr;
          init_resp->mms_p_context_pres = SD_TRUE;

         /* We can log here if so desired */
         /* if ((mms_debug_sel & MMS_LOG_CONF) && ml_log_conf_info_fun != NULL)
            (*ml_log_conf_info_fun) (0, MMSOP_INITIATE, init_resp);*/

          if (req->u.init.resp_info != NULL)
            memcpy (req->u.init.resp_info, init_resp, sizeof (INIT_INFO));

          memcpy (&cc->rem_init_info, init_resp, sizeof (INIT_INFO));
          MVL_LOG_CACSE0 ("ACSE: initiate confirm OK"); 
          cc->max_pdu_size = init_resp->max_segsize;
          /* NOTE: maxreq_calling = num of outstanding indications	*/
          /* allowed at the calling app (similar for maxreq_called).	*/
          cc->maxpend_req = init_resp->maxreq_called;
          cc->maxpend_ind = init_resp->maxreq_calling;
          cc->numpend_req = 0;
          cc->numpend_ind = 0;
          ++mvl_mms_statistics.callingConnOk;
        break;
  
        case MMSINITERR	:
          result = SD_FAILURE;
          cc->in_use = SD_FALSE;
          MVL_LOG_NERR0 ("ACSE: initiate confirm error"); 
          ++mvl_mms_statistics.callingConnFail;
        break;
  
        default :
          MVL_LOG_NERR1 ("ACSE: associate confirm PDU wrong type (%d)", 
								rslt.type);
          result = SD_FAILURE;	
          cc->in_use = SD_FALSE;
          info->result = SD_FAILURE;	/* Cause disconnect		*/
          ++mvl_mms_statistics.callingConnFail;
        break;
        }   
      }
    else  /* PDU decode error, abort the connection			*/
      {
      MVL_LOG_NERR0 ("ACSE: associate confirm PDU Decode Error");
      result = SD_FAILURE;
      info->result = SD_FAILURE;	/* Cause disconnect		*/
      cc->in_use = SD_FALSE;
      ++mvl_mms_statistics.callingConnFail;
      }
    }
  else
    {
    MVL_LOG_NERR0 ("ACSE: associate confirm result != SD_SUCCESS");
    cc->in_use = SD_FALSE;
    ++mvl_mms_statistics.callingConnFail;
    }

  _mvl_set_req_done (req, result);

  if (free_dec_info == SD_TRUE && rslt.data_pres)
    M_FREE (MSMEM_DEC_OS_INFO, rslt.data_ptr);
#endif
  }

/************************************************************************/
/*                       u_a_data_ind					*/
/************************************************************************/

ST_VOID u_a_data_ind (ST_LONG user_conn_id, ST_UINT data_len, ST_UCHAR *data)
  {
MVL_NET_INFO *ni;
MVL_COMM_EVENT *commEvent;

  ni = (MVL_NET_INFO *) user_conn_id;

/* See if we are supposed to log ACSE data for this connection		*/
  if (mvl_conn_filtered_logging == SD_FALSE || ni->log_enable)
    {
    MVL_LOG_ACSEDATA2 ("ACSE: Rx'd %d bytes on MVL_NET_INFO 0x%08lx", 
					data_len, ni);
    MVL_LOG_ACSEDATAH (data_len, data);
    }

  commEvent = mvl_get_com_event ();
  if (!commEvent)
    {
    MVL_LOG_ERR0 ("ACSE: Error - could not get MVL_COMM_EVENT");
    u_a_except (user_conn_id, SD_FAILURE, __FILE__, __LINE__);
    return;
    }

  commEvent->event_type = MMS_MSG_RCVD;
  commEvent->net_info = ni;
  commEvent->u.mms.pdu_len = data_len;
  commEvent->u.mms.pdu = (ST_UCHAR *) M_MALLOC (MSMEM_RXPDU,data_len);
  memcpy (commEvent->u.mms.pdu, data, data_len);
  }

/************************************************************************/
/*                       u_a_release_ind			       	*/
/************************************************************************/

ST_VOID u_a_release_ind (ST_LONG user_conn_id, RLRQ_APDU *info)
  {
RLRE_APDU reinfo;
MVL_NET_INFO *cc;

  cc = (MVL_NET_INFO *) user_conn_id;
  MVL_LOG_ACSE1 ("ACSE: Release Indication received on MVL_NET_INFO 0x%08lx", cc);
  ++mvl_mms_statistics.serverConcl;

  cc->conn_active = SD_FALSE;
  cc->in_use = SD_FALSE;
  if (_mvlu_rpt_disconnect_rcvd_fun != NULL)
     (*_mvlu_rpt_disconnect_rcvd_fun)(cc);

  reinfo.reason_pres = SD_TRUE;
  reinfo.reason = 1;

/* Send no data on the release response */
  reinfo.user_info.len = 0;
  reinfo.user_info.ptr = NULL;
  a_release_rsp (cc->acse_conn_id, &reinfo);

/* Set any outstanding requests to 'done' */
  _mvl_set_net_req_done (NULL, cc, SD_FAILURE);

#ifdef ICCP_LITE
  if (cc->mi_in_use)
    mi_release_ind_fun (cc);
  else
    {
    if (u_mvl_disc_ind_fun)
      (*u_mvl_disc_ind_fun) (cc, MVL_ACSE_RELEASE_IND);
    }
#else
  if (u_mvl_disc_ind_fun)
    (*u_mvl_disc_ind_fun) (cc, MVL_ACSE_RELEASE_IND);
#endif

  cc->maxpend_req = 0;
  cc->numpend_req = 0;
  }

/************************************************************************/
/*                       u_a_abort_ind					*/
/************************************************************************/

ST_VOID u_a_abort_ind (ST_LONG user_conn_id, ABRT_APDU *info)
  {
MVL_NET_INFO *cc;
MVL_ASSOC_IND *mvlAssocInd;
ST_BOOLEAN conn_active;

  cc = (MVL_NET_INFO *) user_conn_id;
  MVL_LOG_ACSE1 ("ACSE: Abort Indication received on MVL_NET_INFO 0x%08lx", cc);

/* This can happen if we have not responded to an initiate indication	*/
/* In this case, just flush the initiate indication hold list		*/
  if (cc == NULL)
    {
    while (mvlAssocIndList)
      {
      mvlAssocInd = list_get_first (&mvlAssocIndList);
      chk_free (mvlAssocInd);
      }
    return;
    }

  ++mvl_mms_statistics.remoteAbort;

/* Verify we can safely use this as a MVL_NET_INFO */
  if (user_conn_id == -1L)
    return;

  conn_active = cc->conn_active;
  cc->conn_active = SD_FALSE;
  cc->in_use = SD_FALSE;
  if (_mvlu_rpt_disconnect_rcvd_fun != NULL)
     (*_mvlu_rpt_disconnect_rcvd_fun)(cc);

/* Set any outstanding requests to 'done' */
  _mvl_set_net_req_done (NULL, cc, MVL_ERR_CNF_DISCONNECTED);
  if (conn_active == SD_TRUE)
    {
#ifdef ICCP_LITE
    if (cc->mi_in_use == SD_TRUE)
      mi_abort_ind_fun (cc);
    else
      {
      if (u_mvl_disc_ind_fun)
        (*u_mvl_disc_ind_fun) (cc, MVL_ACSE_ABORT_IND);
      }
#else
    if (u_mvl_disc_ind_fun)
      (*u_mvl_disc_ind_fun) (cc, MVL_ACSE_ABORT_IND);
#endif
    }

  cc->maxpend_req = 0;
  cc->numpend_req = 0;
  }

/************************************************************************/
/*                       u_a_release_cnf				*/
/************************************************************************/

ST_VOID u_a_release_cnf (ST_LONG user_conn_id, RLRE_APDU *info)
  {
MVL_NET_INFO *cc;
MVL_REQ_PEND *req;


  cc = (MVL_NET_INFO *) user_conn_id;

  MVL_LOG_ACSE1 ("ACSE: release confirm received for MVL_NET_INFO %08lx", cc);

/* Find the conclude request */
  req = _mvl_find_req_ctrl (cc, MVL_MMSOP_RELEASE, -1);
  if (!req)
    {
    MVL_LOG_ERR0 ("ACSE: Release Confirm - no matching req ctrl");
    return;
    }

/* Set any outstanding requests to 'done' */
  _mvl_set_net_req_done (req, cc, MVL_ERR_CNF_DISCONNECTED);

  cc->conn_active = SD_FALSE;
  cc->in_use = SD_FALSE;
  if (_mvlu_rpt_disconnect_rcvd_fun != NULL)
     (*_mvlu_rpt_disconnect_rcvd_fun)(cc);

  _mvl_set_req_done (req, SD_SUCCESS);
  cc->maxpend_req = 0;
  cc->numpend_req = 0;
  }

/************************************************************************/
/*                       u_a_except					*/
/************************************************************************/

ST_VOID u_a_except (ST_LONG user_conn_id, ST_INT code, 
		 ST_CHAR *filename, ST_INT line)
  {
  MVL_LOG_ERR2 ("ACSE: Error - Exception, code 0x%x, MVL_NET_INFO %08lx", 
			code, user_conn_id);
  MVL_LOG_CERR2 ("File %s, Line %d", filename, line);
  mvl_acse_ok = SD_FALSE;
  }

/************************************************************************/
/*		CONNECTION CONTROL RELATED STUFF			*/
/************************************************************************/
/*			ma_get_called_conn_ctrl				*/
/************************************************************************/

#if (MMS_INIT_EN & RESP_EN)

static MVL_NET_INFO *ma_get_called_conn_ctrl (NET_BIND_CTRL *bc)
  {
MVL_NET_INFO *ni;
ST_INT i;

  ni = mvl_called_conn_ctrl;
  for (i = 0; i < mvl_cfg_info->called_used; ++i, ++ni)
    {
    /* CRITICAL: don't reuse 'ni' until req_pend_list cleaned up from	*/
    /* last conn. May be waiting for user to call mvl_free_req_ctrl.	*/
    if (!ni->in_use && ni->req_pend_list==NULL)
      {
      if (bc == NULL || !strcmp (bc->ar_name, ni->bind_ctrl->ar_name))
        {
        ni->in_use = SD_TRUE;
        ni->conn_active = SD_FALSE;
        return (ni);
        }
      }
    }
  return (NULL);
  }

#endif

/************************************************************************/
/*			ma_get_calling_conn_ctrl			*/
/************************************************************************/
#if (MMS_INIT_EN & REQ_EN)

static MVL_NET_INFO *ma_get_calling_conn_ctrl (NET_BIND_CTRL *bc)
  {
MVL_NET_INFO *ni;
ST_INT i;

  ni = mvl_calling_conn_ctrl;
  for (i = 0; i < mvl_cfg_info->calling_used; ++i, ++ni)
    {
    /* CRITICAL: don't reuse 'ni' until req_pend_list cleaned up from	*/
    /* last conn. May be waiting for user to call mvl_free_req_ctrl.	*/
    if (!ni->in_use && ni->req_pend_list==NULL)
      {
      if (bc == NULL || !strcmp (bc->ar_name, ni->bind_ctrl->ar_name))
        {
        ni->in_use = SD_TRUE;
        ni->conn_active = SD_FALSE;
        return (ni);
        }
      }
    }
  return (NULL);
  }
#endif


/************************************************************************/
#endif	/* End of COACSE specific functions 				*/
/************************************************************************/

/************************************************************************/
/************************************************************************/
#if defined(CLACSE)	/* CONNECTIONLESS ACSE FUNCTIONS					*/
/************************************************************************/

/************************************************************************/
/*			mvl_get_com_event				*/
/************************************************************************/
/* This function is used to get a MVL_COMM_EVENT control buffer to be	*/
/* passed up to the caller of '_mvl_net_service'. 			*/

static MVL_NET_INFO *mvl_get_net_info (ST_VOID)
  {
MVL_NET_INFO *netInfo;

#if 1
  netInfo = (MVL_NET_INFO *) M_CALLOC (MSMEM_NETINFO, 1, sizeof (MVL_NET_INFO));  
  if (netInfo == NULL)
    {
    MVL_LOG_ERR0 ("Error: No MVL_NET_INFO available");
    return (NULL);
    }
#else
  if (mvl_net_info_avail_head)
    netInfo = (MVL_NET_INFO *) list_get_first((ST_VOID **) &mvl_net_info_avail_head);
  else
    {
    if (mvl_net_info_count >= mvl_max_net_info)
      {
      MVL_LOG_ERR0 ("Error: No MVL_NET_INFO available");
      return (NULL);
      }
    ++mvl_net_info_count;
    netInfo = (MVL_NET_INFO *) M_CALLOC (MSMEM_GEN, 1, sizeof (MVL_NET_INFO));  
    }
#endif

  return (netInfo);
  }

/************************************************************************/
/*			mvl_init_audt_addr				*/
/* User must pass pointer (AUDT_APDU *) to their struct.		*/
/* This function fills in the addressing info in the struct.		*/
/************************************************************************/

ST_RET mvl_init_audt_addr (AUDT_APDU *audt, ST_CHAR *locArName, ST_CHAR *remArName)
  {
DIB_ENTRY *locDe;
DIB_ENTRY *remDe;

/* Retrieve the DIB_ENTRY's for local and remote. These are used to set	*/
/* the AUDT_APDU information						*/

  locDe = find_loc_dib_entry (locArName);
  if (!locDe)
    {
    MVL_LOG_NERR1 ("ACSE: Local name '%s' not found", locArName);
    return (MVLE_LOCAL_NAME);
    }

  remDe = find_rem_dib_entry (remArName);
  if (!remDe)
    {
    MVL_LOG_NERR1 ("ACSE: Remote name '%s' not found", remArName);
    return (MVLE_REMOTE_NAME);
    }

  if (remDe->pres_addr.tp_type != TP_TYPE_TP4)	/* must be TP4 (need NSAP)	*/
    {
    MVL_LOG_NERR1 ("ACSE: Remote name '%s' is not TP4 Transport type", remArName);
    return (MVLE_REMOTE_NAME);
    }

  /* AP Context is MMS */
  audt->ASO_context_name_pres = SD_TRUE;
  audt->ASO_context_name.num_comps = 5;
  audt->ASO_context_name.comps[0] = 1;
  audt->ASO_context_name.comps[1] = 0;
  audt->ASO_context_name.comps[2] = 9506;
  audt->ASO_context_name.comps[3] = 2;
  audt->ASO_context_name.comps[4] = 3;

/* Calling Addressing information */
  audt->calling_ae_title.AP_title_pres = locDe->AP_title_pres;
  memcpy (&audt->calling_ae_title.AP_title, &locDe->AP_title, 
	  sizeof (MMS_OBJ_ID));

  audt->calling_ae_title.AE_qual_pres   = locDe->AE_qual_pres;
  audt->calling_ae_title.AE_qual        = locDe->AE_qual;
  audt->calling_ae_title.AP_inv_id_pres = locDe->AP_inv_id_pres;
  audt->calling_ae_title.AP_inv_id      = locDe->AP_invoke_id;
  audt->calling_ae_title.AE_qual_pres   = locDe->AE_qual_pres;
  audt->calling_ae_title.AE_qual        = locDe->AE_qual;
  audt->calling_ae_title.AE_inv_id_pres = locDe->AE_inv_id_pres;
  audt->calling_ae_title.AE_inv_id      = locDe->AE_invoke_id;

  memcpy (&audt->calling_paddr, &locDe->pres_addr, sizeof (PRES_ADDR));

/* Called Addressing information */
  audt->called_ae_title.AP_title_pres = remDe->AP_title_pres;
  memcpy (&audt->called_ae_title.AP_title, &remDe->AP_title, 
	  sizeof (MMS_OBJ_ID));
  audt->called_ae_title.AE_qual_pres   = remDe->AE_qual_pres;
  audt->called_ae_title.AE_qual        = remDe->AE_qual;
  audt->called_ae_title.AP_inv_id_pres = remDe->AP_inv_id_pres;
  audt->called_ae_title.AP_inv_id      = remDe->AP_invoke_id;
  audt->called_ae_title.AE_qual_pres   = remDe->AE_qual_pres;
  audt->called_ae_title.AE_qual        = remDe->AE_qual;
  audt->called_ae_title.AE_inv_id_pres = remDe->AE_inv_id_pres;
  audt->called_ae_title.AE_inv_id      = remDe->AE_invoke_id;

  memcpy (&audt->called_paddr, &remDe->pres_addr, sizeof (PRES_ADDR));

  audt->rem_mac_valid = SD_FALSE;	/* CRITICAL: must send to ALL-ES MAC*/

  return (SD_SUCCESS);
  }

/************************************************************************/
/*			mvl_free_net_info				*/
/************************************************************************/

static ST_VOID mvl_free_net_info (MVL_NET_INFO *netInfo)
  {
#if 1
  M_FREE (MSMEM_NETINFO, netInfo);
#else
  list_add_last((ST_VOID **) &mvl_net_info_avail_head, (ST_VOID *) netInfo);
#endif
  }

/************************************************************************/
#endif		/* End of CLACSE specific functions			*/
/************************************************************************/

/************************************************************************/
/************************************************************************/
/* UTILITY FUNCTIONS							*/
/************************************************************************/
/*                     ma_wait_req_done					*/
/************************************************************************/

static ST_VOID ma_wait_req_done (ST_RET *retOut)
  {
/* See if the call failed */
  if (*retOut != SD_SUCCESS)
    return;

/* Now just wait for the 'confirm' function to be called		*/
  while (ma_cmd_done != SD_TRUE)
    {
    copp_event ();
    if (u_mvl_check_timeout && (*u_mvl_check_timeout)())
      break;
    }

/* OK, the function is done or the user gave up */
  if (ma_cmd_done == SD_TRUE)
    *retOut = ma_cmd_rslt;
  else
    { /* User timeout */
    if (ma_cmd_rslt != SD_SUCCESS)
      *retOut = ma_cmd_rslt;
    else
      *retOut = SD_FAILURE;
    }
  }

/************************************************************************/
/************************************************************************/
/* AA OBJECT HANDLING FUNCTIONS						*/
/* These functions must be here because the sizeof MVL_NET_INFO		*/
/* varies depending on the stack selection.				*/
/************************************************************************/
/************************************************************************/
/*			mvl_aa_resize 					*/
/************************************************************************/

ST_VOID mvl_aa_resize (ST_INT max_num_var, ST_INT max_num_nvl, 
		       ST_INT max_num_jou)
  {
MVL_AA_OBJ_CTRL *aa_objs;
ST_INT i;

/* Do AA specific variables */
  for (i = 0; i < mvl_cfg_info->num_called; ++i)
    {
    aa_objs = (MVL_AA_OBJ_CTRL *) mvl_called_conn_ctrl[i].aa_objs;
    if (aa_objs == NULL)
      { 
      aa_objs = mvl_called_conn_ctrl[i].aa_objs = 
		(MVL_AA_OBJ_CTRL *) M_CALLOC (NULL, 1, sizeof(MVL_AA_OBJ_CTRL));
      }
    mvl_aa_resize_conn (aa_objs, max_num_var, max_num_nvl, max_num_jou);
    }

  for (i = 0; i < mvl_cfg_info->num_calling; ++i)
    {
    aa_objs = (MVL_AA_OBJ_CTRL *) mvl_calling_conn_ctrl[i].aa_objs;
    if (aa_objs == NULL)
      { 
      aa_objs = mvl_calling_conn_ctrl[i].aa_objs = 
		(MVL_AA_OBJ_CTRL *) M_CALLOC (NULL, 1, sizeof(MVL_AA_OBJ_CTRL));
      }
    mvl_aa_resize_conn (aa_objs, max_num_var, max_num_nvl, max_num_jou);
    }
  }

/************************************************************************/
/*			_mvl_clear_aa_objs 				*/
/************************************************************************/

ST_VOID _mvl_clear_aa_objs (MVL_AA_OBJ_CTRL *aa_objs)
  {
ST_INT i;

/* Delete NVL's. We need to delete the 'entries' pointer table for each	*/
/* NVL, then delete the table of NVL's.					*/
  if (aa_objs->max_num_nvlist)
    {
    for (i = 0; i < aa_objs->num_nvlist; ++i)
      mvl_nvl_destroy (aa_objs->nvlist_tbl[i]);
    }    

/* Delete the variable association table.				*/
  if (aa_objs->max_num_var_assoc)
    {
    for (i = 0; i < aa_objs->num_var_assoc; ++i)
      M_FREE (MSMEM_GEN, aa_objs->var_assoc_tbl[i]);
    }

/* Delete the Journal control table.					*/
  if (aa_objs->max_num_jou)
    {
    for (i = 0; i < aa_objs->num_jou; ++i)
      M_FREE (MSMEM_GEN, aa_objs->jou_tbl[i]);
    }

  aa_objs->num_var_assoc = 0;
  aa_objs->num_nvlist = 0;
  aa_objs->num_jou = 0;
  }

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

ST_VOID mvl_aa_resize_conn (MVL_AA_OBJ_CTRL *aa_objs, ST_INT max_num_var, ST_INT max_num_nvl, 
		       ST_INT max_num_jou)
  {
  if (max_num_var < aa_objs->num_var_assoc)
    {
    MVL_LOG_NERR0 ("Resizing VMD: new max VA is less than current"); 
    max_num_var = aa_objs->num_var_assoc;
    }
  if (max_num_nvl < aa_objs->num_nvlist)
    {
    MVL_LOG_NERR0 ("Resizing VMD: new max NVL is less than current"); 
    max_num_nvl = aa_objs->num_nvlist;
    }
  if (max_num_jou < aa_objs->num_jou)
    {
    MVL_LOG_NERR0 ("Resizing VMD: new max JOU is less than current"); 
    max_num_jou = aa_objs->num_jou;
    }
    
  _mvl_resize_ptr_tbl (&(aa_objs->max_num_var_assoc), 
  		         (ST_VOID ***)&(aa_objs->var_assoc_tbl), max_num_var);
  _mvl_resize_ptr_tbl (&(aa_objs->max_num_nvlist), 
    		         (ST_VOID ***)&(aa_objs->nvlist_tbl), max_num_nvl);
  _mvl_resize_ptr_tbl (&(aa_objs->max_num_jou), 
    		         (ST_VOID ***)&(aa_objs->jou_tbl), max_num_jou);
  }

/************************************************************************/
/************************************************************************/
/*			mvl_match_remote_ar				*/
/************************************************************************/

ST_RET mvl_match_remote_ar (DIB_MATCH_CTRL *matchCtrl, MVL_NET_INFO *cc, 
			    DIB_ENTRY **dib_entry_out)
  {
#if defined(MAP30_ACSE)
ST_INT chan;

  if (a_get_chan (cc->acse_conn_id, &chan) == SD_SUCCESS)
    return (dib_match_remote_ar (matchCtrl, chan, dib_entry_out));
  else
    return (SD_FAILURE);
#else
  return (dib_match_remote_ar (matchCtrl, &cc->ass_ind_info, dib_entry_out));
#endif
  }


/************************************************************************/
/*			mvl_match_local_ar				*/
/************************************************************************/

ST_RET mvl_match_local_ar  (DIB_MATCH_CTRL *matchCtrl, MVL_NET_INFO *cc, 
			    DIB_ENTRY **dib_entry_out)
  {
#if defined(MAP30_ACSE)
ST_INT chan;

  if (a_get_chan (cc->acse_conn_id, &chan) == SD_SUCCESS)
    return (dib_match_local_ar (matchCtrl, chan, dib_entry_out));
  else
    return (SD_FAILURE);
#else
  return (dib_match_local_ar (matchCtrl, &cc->ass_ind_info, dib_entry_out));
#endif
  }

/************************************************************************/
/*			mvl_cmp_local_addr				*/
/************************************************************************/

ST_INT mvl_cmp_local_addr  (DIB_MATCH_CTRL *matchCtrl, MVL_NET_INFO *cc, 
			    DIB_ENTRY *dib_entry)
  {
#if defined(MAP30_ACSE)
ST_INT chan;

  if (a_get_chan (cc->acse_conn_id, &chan) == SD_SUCCESS)
    return (dib_cmp_local_addr (matchCtrl, chan, dib_entry));
  else
    return (DIB_MATCH_NOT);
#else
  return (dib_cmp_local_addr (matchCtrl, &cc->ass_ind_info, dib_entry));
#endif
  }

/************************************************************************/
/*			mvl_cmp_remote_addr				*/
/************************************************************************/

ST_INT mvl_cmp_remote_addr (DIB_MATCH_CTRL *matchCtrl, MVL_NET_INFO *cc, 
			    DIB_ENTRY *dib_entry)
  {
#if defined(MAP30_ACSE)
ST_INT chan;

  if (a_get_chan (cc->acse_conn_id, &chan) == SD_SUCCESS)
    return (dib_cmp_remote_addr (matchCtrl, chan, dib_entry));
  else
    return (DIB_MATCH_NOT);
#else
  return (dib_cmp_remote_addr (matchCtrl, &cc->ass_ind_info, dib_entry));
#endif
  }


/************************************************************************/
/*			mvl_net_info_to_chan				*/
/************************************************************************/

ST_RET mvl_net_info_to_chan (MVL_NET_INFO *cc, ST_INT *chanOut)
  {
#if defined(MAP30_ACSE)
  return (a_get_chan (cc->acse_conn_id, chanOut));
#else
  return (SD_FAILURE);
#endif
  }

