/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*	(c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*	1997-2005, All Rights Reserved					*/
/*									*/
/*			PROPRIETARY AND CONFIDENTIAL			*/
/*									*/
/* MODULE NAME : cospmain.c						*/
/* PRODUCT(S)  : MOSI Stack (over TP4)					*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*		This file implements functions common to CALLING and	*/
/* 		CALLED side of the COSP protocol.			*/
/*									*/
/*  For information see the:						*/
/*	ISO 8326 "Information processing systems - Open Systems		*/
/*	  Interconnection - Basic connection oriented session service	*/
/*	  definition.							*/
/*	ISO 8327 "Information processing systems - Open Systems		*/
/*	  Interconnection - Basic connection oriented session protocol	*/
/*	  specification.						*/
/*	ISO 8327/ADD.2 (Draft for Version2).				*/
/*									*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/*			cosp_bind					*/
/*			cosp_unbind					*/
/*									*/
/*			cosp_con_req					*/
/*			cosp_con_rsp_pos				*/
/*			cosp_con_rsp_neg				*/
/*									*/
/*			cosp_rel_req					*/
/*			cosp_rel_rsp_pos				*/
/*									*/
/*			cosp_u_abort_req				*/
/*									*/
/*			cosp_data_req					*/
/*									*/
/*			tp4_bind_cnf					*/
/*			tp4_unbind_cnf					*/
/*									*/
/*			tp4_connect_ind					*/
/*			tp4_connect_cnf					*/
/*			tp4_disconnect_ind				*/
/*			tp4_data_ind 					*/
/*			tp4_expdata_ind 				*/
/*									*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 07/30/08  JRB    21     Chg log msg from ERR to ENC.			*/
/* 04/28/08  GLB    20     Removed CALLED_ONLY and CALLING_ONLY         */
/* 04/09/08  JRB    19     Fix log message for unsupported SPDU.	*/
/* 05/05/05  EJV    18     cosp_con_req: copy new port param.		*/
/* 09/17/04  JRB    17     cosp_con_req: save tp4_conn_id returned from	*/
/*			   modified tp4_connect (need for abort fix).	*/
/*			   cosp_u_abort_req: if waiting for T-CONNECT.cnf,*/
/*			   call tp4_disconnect (correct ISO 8327 action).*/
/* 01/20/04  EJV    16     acse_free_con: add a_free_part_security_info.*/
/* 11/14/03  JRB    15     Save encrypt_ctrl ONLY for TP0 (not for TP4).*/
/* 08/25/03  EJV    14     Added param to tp4_connect().		*/
/*			   tp4_connect_ind, cnf: save encrypt_ctrl	*/
/* 06/20/02  JRB    13     Chg addr args to tp4_connect_ind &		*/
/*			   copy them to ACSE_CONN.			*/
/* 06/05/02  JRB    12     Allow Called SSEL=NULL (0 length) on conn req*/
/* 09/10/01  JRB    11     Remove last change but fix cosp_cn_ptr free.	*/
/* 08/01/01  JRB    10     Del rem_addr from cosp_enc_cn_ac calls.	*/
/*			   Del use of cosp_cn_ptr, cosp_cn_len.		*/
/* 05/09/01  JRB    09     On S-CONNECT.ind, save SSELs in ACSE_CONN.	*/
/* 03/14/01  JRB    08     Use new SMEM allocation functions.		*/
/*			   Add global variable "session_cfg".		*/
/* 09/13/99  MDE    07     Added SD_CONST modifiers			*/
/* 08/13/98  JRB    06     Lint cleanup.				*/
/* 06/17/98  JRB    05     Allow second call to cosp_bind if SSEL same.	*/
/* 12/30/97  JRB    04     cosp_bind return same as tp4_bind if it fails*/
/* 07/31/97  JRB    03     cosp_bind & cosp_con_req: set "tp_type" in	*/
/*			   tp4_addr.					*/ 
/*			   Don't set NSAP on tp4_bind call (ignored).	*/
/* 05/27/97  JRB   7.00    MMSEASE 7.0 release.				*/
/* 03/20/97  EJV    02     Enhanced logging and collision handling.	*/
/* 02/07/97  EJV    01     Created					*/
/************************************************************************/
#include "glbtypes.h"
#include "sysincs.h"

#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;	/* Define for SLOG mechanism	*/
#endif

#include "mem_chk.h"
#include "tp4api.h"
#include "tp4.h"	/* need MIN_TP0_CONN_ID	definition	*/
#include "acse2.h"

#include "cosp_log.h"
#include "cosp.h"
#include "cosp_usr.h"


	/*======================================================*/
	/*							*/
	/* 		L O C A L  V A R I A B L E S		*/
	/*			A N D				*/
	/*	F U N C T I O N S   P R O T O T Y P E S		*/
	/*							*/
	/*======================================================*/

/* define actions to perform after return from one of decode functions */
#define COSP_S_P_ABORT_IND		1
#define COSP_SEND_ABORT_SPDU		2
#define COSP_T_DISCONNECT		4


ST_RET cosp_send_abort (ACSE_CONN *con, ST_INT err_code);
 
static ST_RET cosp_process_connect (ACSE_CONN *con, ST_UINT spdu_len, char *spdu_buf);
static ST_RET cosp_process_finish  (ACSE_CONN *con, ST_UINT spdu_len, char *spdu_buf);
static ST_RET cosp_process_accept  (ACSE_CONN *con, ST_UINT spdu_len, char *spdu_buf);
static ST_RET cosp_process_refuse  (ACSE_CONN *con, ST_UINT spdu_len, char *spdu_buf);
static ST_RET cosp_process_discon  (ACSE_CONN *con, ST_UINT spdu_len, char *spdu_buf);
static ST_RET cosp_process_abort   (ACSE_CONN *con, ST_UINT spdu_len, char *spdu_buf);
static ST_RET cosp_process_data    (ACSE_CONN *con, ST_UINT spdu_len, char *spdu_buf);


/************************************************************************/
/* Global and static variables.						*/
/************************************************************************/
/* flag below prevents calling acse_free_con twice (if we are in func	*/
/* tp4_connect and tp4_disconnect_ind is called)			*/

static ST_BOOLEAN cosp_in_tp4_func;

static ST_BOOLEAN cosp_bind_called;	/* only 1 binding allowed 	*/

ST_UCHAR   cosp_only_ssel [1+MAX_SSEL_LEN];
SESSION_CFG session_cfg;		/* Session layer configuration	*/

	/*======================================================*/
	/*							*/
	/*		B I N D I N G   F U N C T I O N S	*/
	/*							*/
	/*======================================================*/


/************************************************************************/
/*			cosp_bind					*/
/*----------------------------------------------------------------------*/
/* Function called by SS-user to bind to local Session Address.		*/
/* The SS-user should implement the u_cosp_bind_cnf function.		*/
/*									*/
/* Parameters:								*/
/*  ST_LONG	cosp_user_bind_id	COSP User's id for this binding.*/
/*  PRES_ADDR  *loc_addr		Local Address to bind to	*/
/*  ST_INT	sharable		Ignored, (for compatibility with*/
/*  ST_INT	max_conns		Ignored, previous versions)	*/
/*									*/
/* Return:								*/
/*	SD_SUCCESS (0)		if bind successful			*/
/*	error code 		otherwise				*/
/************************************************************************/
ST_RET cosp_bind (ST_LONG cosp_user_bind_id, PRES_ADDR *loc_addr,
		   ST_INT sharable, ST_INT max_conns)
{
ST_INT		ret;
TP4_ADDR	tp4_addr;
ST_LONG		user_bind_id;	/* TP4 user bind id */

  COSP_LOG_ENC1 ("S-BIND.req:    cosp_user_bind_id =0x%08X", cosp_user_bind_id);

  ret = SD_SUCCESS;





  /* Check SSEL.  If OK, save it.		*/	
  if (!cosp_bind_called)
    {
    /* If local SSEL len is legal, save SSEL.	*/
    if (loc_addr->ssel_len <= MAX_SSEL_LEN)
      {
      if ((cosp_only_ssel [0] = (ST_UCHAR) loc_addr->ssel_len) > 0)
        memcpy (&cosp_only_ssel [1], loc_addr->ssel, loc_addr->ssel_len);
      }
    else
      {
      ret = COSP_ERR_INV_SSEL;			/* invalid loc SSEL	*/
      COSP_LOG_ERR1 ("COSP-ERROR: S-BIND.req: Invalid length=%d of SSEL.",
        loc_addr->ssel_len);
      }
    }
  else
    {    /* Second Bind. Only allow it if the SSEL is the same.		*/
    if (cosp_only_ssel [0] != (ST_UCHAR) loc_addr->ssel_len  ||
         (loc_addr->ssel_len != 0  &&
          memcmp (&cosp_only_ssel [1], loc_addr->ssel, loc_addr->ssel_len)))
      {  
      COSP_LOG_ERR0 ("COSP-ERROR: Cannot BIND to different SSEL");
      ret = COSP_ERR_INV_SSEL;
      }  
    }


  /* Bind to Transport.		*/
  if (ret == SD_SUCCESS)
    {
    /* Copy tsel from pres_addr to tp4_addr struct. Don't copy nsap	*/
    /* because tp4_bind ignores it anyway.				*/
    if (loc_addr->tsel_len <= sizeof (tp4_addr.tp4_sel))
      {
      tp4_addr.tp4_sel_len = loc_addr->tsel_len;
      memcpy (tp4_addr.tp4_sel, loc_addr->tsel, loc_addr->tsel_len);
      tp4_addr.tp_type = loc_addr->tp_type;	/* TP4 or TCP?		*/

      user_bind_id = cosp_user_bind_id;	/* pass bind id	down */



      if ((ret = tp4_bind (user_bind_id, &tp4_addr, sharable, max_conns))
          != SD_SUCCESS)
        {
        COSP_LOG_ERR0 ("COSP-ERROR: S-BIND.req: T-BIND.req failed.");
        }
      }
    else
      {
      ret = COSP_ERR_INV_TP4_ADDR;		/* TP4 bind failed	*/
      COSP_LOG_ERR2 ("COSP-ERROR: S-BIND.req: Invalid TSEL length=%d or NSAP length=%d.",
        loc_addr->tsel_len, loc_addr->nsap_len);
      }
    }	/* end Transport binding section	*/



  /* If successful, set called flag. Otherwise, treat next bind like first.*/
  if (ret == SD_SUCCESS)
    cosp_bind_called = SD_TRUE;

  return (ret);
}


/************************************************************************/
/*			tp4_bind_cnf					*/
/*----------------------------------------------------------------------*/
/* This function is called by the TP-provider to indicate the result of	*/
/* the bind operation (see cosp_bind func).				*/
/* 									*/
/* Parameters:								*/
/*  ST_LONG	user_bind_id	User's id (COSP id) for this binding.	*/
/*  ST_LONG	tp4_bind_id	TP4 assigned ID for this binding.	*/
/*  ST_INT	result  	Indicates if the bind was successful:	*/
/*					=  0	Success			*/
/*					<> 0	Error code		*/
/*									*/
/* Return:								*/
/*	ST_VOID								*/
/************************************************************************/
ST_VOID tp4_bind_cnf (ST_LONG user_bind_id, ST_LONG tp4_bind_id, ST_RET result)
{
ST_LONG	cosp_user_bind_id;
ST_LONG	cosp_bind_id;

  cosp_user_bind_id = user_bind_id;	/* we passing same bind id up */
  cosp_bind_id = tp4_bind_id;		/* COSP bind id same as TP4	*/

  COSP_LOG_DEC3 ("S-BIND.cnf:    cosp_user_bind_id =0x%08X  cosp_bind_id =%9ld  result =%d",
    cosp_user_bind_id, cosp_bind_id, result);

  u_cosp_bind_cnf (cosp_user_bind_id, cosp_bind_id, result);
}


/************************************************************************/
/*			cosp_unbind					*/
/*----------------------------------------------------------------------*/
/* Function called by SS-user to unbind from local Session Address.	*/
/* The SS-user should implement the u_cosp_unbind_cnf function.		*/
/* 									*/
/* Parameters:								*/
/*  ST_LONG	cosp_bind_id	COSP's id to unbind.			*/
/*									*/
/* Return:								*/
/*	SD_SUCCESS (0)		if unbind successful			*/
/*	error code		otherwise				*/
/************************************************************************/
ST_RET cosp_unbind (ST_LONG cosp_bind_id)
{
ST_LONG	tp4_bind_id;

  COSP_LOG_ENC1 ("S-UNBIND.req:                                 cosp_bind_id =%9ld", cosp_bind_id);

  tp4_bind_id = cosp_bind_id;

  /* unbind the transport */
  if (tp4_unbind (tp4_bind_id) != SD_SUCCESS)
    {
    COSP_LOG_ERR0 ("COSP-ERROR: S-UNBIND.req: T-UNBIND.req failed.");
    return (COSP_ERR_TP4_RET);
    }
  return (SD_SUCCESS);
}


/************************************************************************/
/*			tp4_unbind_cnf					*/
/*----------------------------------------------------------------------*/
/* This function is called by the TP4-provider to indicate that the	*/
/* unbind operation completed.						*/
/* 									*/
/* Parameters:								*/
/*  ST_LONG	user_bind_id	User's id for this binding.		*/
/*									*/
/* Return:								*/
/*	ST_VOID								*/
/************************************************************************/
ST_VOID tp4_unbind_cnf (ST_LONG user_bind_id)
{
ST_LONG	cosp_user_bind_id;

  cosp_user_bind_id = user_bind_id;

  COSP_LOG_DEC1 ("S-UNBIND.cnf:  cosp_user_bind_id =0x%08X", cosp_user_bind_id);

  u_cosp_unbind_cnf (cosp_user_bind_id);
}


/************************************************************************/
/*                      acse_free_con					*/
/*----------------------------------------------------------------------*/
/* This function will free the pointer to connection info.		*/
/* In most cases the COSP engine is responsible for releasing the con	*/
/* pointer.								*/
/*	  								*/
/* Parameters:								*/
/*  ACSE_CONN *con		Pointer to connection info struct	*/
/*	  								*/
/* Return:								*/
/*  ST_VOID		none						*/
/************************************************************************/
ST_VOID acse_free_con (ACSE_CONN *con)
{


  if (con)
    {
    /* free partner Authentication Info */

    a_free_part_security_info ((ST_LONG) con);

    if (con->cosp_cn_ptr)
      M_FREE (MSMEM_COSP_CN, con->cosp_cn_ptr);	/* free saved encoded CN SPDU	*/

    M_FREE (MSMEM_ACSE_CONN, con);
    }

}

	/*======================================================*/
	/*							*/
	/*   C O N N E C T  (CALLED SIDE)  F U N C T I O N S	*/
	/*							*/
	/*======================================================*/


/************************************************************************/
/*			tp4_connect_ind					*/
/*----------------------------------------------------------------------*/
/* This function is called by the TP4-provider to indicate that a	*/
/* remote node wishes to establish a connection.			*/
/* 									*/
/* Parameters:								*/
/*  ST_LONG	  user_bind_id	User's id for this binding.		*/
/*  ST_LONG	  tp4_conn_id	COSP connection id for this connect.	*/
/*  ST_UCHAR     *loc_tsel      Local (called) TSEL.			*/
/*  ST_UCHAR     *rem_tsel      Remote (calling) TSEL.			*/
/*  ST_UCHAR     *rem_nsap      Remote (calling) NSAP (TP4) or		*/
/*  ST_ULONG      rem_ip	Remote (calling) IP addr(TP0)		*/
/*  ST_INT	  conndata_len	Length of opt connect data <= 32 bytes	*/
/*  char	 *conndata	Pointer to opt connect data.		*/
/*	  								*/
/* Return:								*/
/*	ST_VOID								*/
/************************************************************************/
ST_VOID tp4_connect_ind (ST_LONG user_bind_id, ST_LONG tp4_conn_id,
                      ST_UCHAR *loc_tsel, ST_UCHAR *rem_tsel,
                      ST_UCHAR *rem_nsap, ST_ULONG rem_ip,
                      ST_INT conndata_len, char *conndata)
{
ACSE_CONN       *con;

  tp4_reldata (conndata);		/* !!! we ignoring conndata	*/

  con = (ACSE_CONN *) M_CALLOC (MSMEM_ACSE_CONN, 1, sizeof (ACSE_CONN));

  /* accept incomming transport connection */
  if (tp4_accept (tp4_conn_id, (ST_LONG) con, 0, NULL) == SD_SUCCESS)
    {
    con->cosp_state   = COSP_CSTATE_IDLE_TCON;
    con->cosp_bind_id = user_bind_id;	/* save the id to pass to COPP */
    con->cosp_vtca    = SD_TRUE;           /* transport conn acceptor	*/
    con->tp4_conn_id  = tp4_conn_id;
    /* Save TSELs, NSAP in ACSE_CONN struct.				*/
    memcpy (con->loc_tsel, loc_tsel, loc_tsel [0]+1);
    memcpy (con->rem_tsel, rem_tsel, rem_tsel [0]+1);
    if (tp4_conn_id >= MIN_TP0_CONN_ID)	/* This is TP0 conn. Save IP addr*/
      {
      con->remNetAddr.ip = rem_ip;
#if defined (TP0_ENABLED)
      /* If connection is TP0, save encrypt_ctrl.	*/
      if (tp4_conn_id >= MIN_TP0_CONN_ID)
        {	/* this is TP0 connection	*/
        TP0_CONN *tp0_conn = &tp0_conn_arr [tp4_conn_id-MIN_TP0_CONN_ID];
        /* save the encryption info in ACSE_CONN */
        con->encrypt_ctrl = tp0_conn->sock_info->encrypt_ctrl;
        }
#endif
      }
    else				/* This is TP4 conn. Save NSAP	*/
      memcpy (con->remNetAddr.nsap, rem_nsap, rem_nsap [0]+1);
    }
  else
    {
    COSP_LOG_ERR0 ("COSP-ERROR: T-CONNECT.ind: T-ACCEPT.req connection failed.");

    M_FREE (MSMEM_STARTUP, con);

    /* reject incomming transport connection, accept failed */
    tp4_disconnect (tp4_conn_id, 0, NULL);
    }
}


/************************************************************************/
/*			cosp_con_rsp_pos				*/
/*----------------------------------------------------------------------*/
/* Function called by SS-user to Accept an incomming connection.	*/
/* 									*/
/* Parameters:								*/
/*  ACSE_CONN    *con		Pointer to con info struct		*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
ST_RET cosp_con_rsp_pos (ACSE_CONN *con)
{
ST_INT		ret;
ST_UINT	spdu_len;
char           *spdu_ptr;

  COSP_LOG_ENC2 ("S-CONNECT.rsp+:  acse_conn_id =0x%08X  user_conn_id =%4ld",
    con, con->user_conn_id);
  COSP_LOG_ENCC1 ("User data length: %d", con->ppdu_len);
  COSP_LOG_ENCH (con->ppdu_len, con->ppdu_ptr);

  ret = SD_SUCCESS;

  if (con->cosp_state == COSP_CSTATE_WAIT_CON_RSP)
    {
    /* encode ACCEPT SPDU */
    ret = cosp_enc_cn_ac (con, NULL, &spdu_ptr, &spdu_len, COSP_SI_ACCEPT);
    if (ret == SD_SUCCESS)
      {
      if (tp4_data (con->tp4_conn_id, SD_TRUE, spdu_len, spdu_ptr) == SD_SUCCESS)
        {
        con->cosp_state = COSP_CSTATE_DATA_XFER;
        con->cosp_vcoll = SD_FALSE;
        con->cosp_vdnr  = SD_FALSE;
        }
      else
        {
        ret = COSP_ERR_TP4_RET;
        COSP_LOG_ERR1 ("COSP-ERROR: S-CONNECT.rsp+ acse_conn_id=0x%08X: T-DATA.req failed.",
          con);
        }
      }
    else
      {
      COSP_LOG_ERR1 ("COSP-ERROR: S-CONNECT.rsp+ acse_conn_id=0x%08X: Encoding ACCEPT SPDU failed.",
        con);
      }
    }
  else
    {
    ret = COSP_ERR_INV_CON_STATE;
    COSP_LOG_ERR2 ("COSP-ERROR: S-CONNECT.rsp+ acse_conn_id=0x%08X: Invalid state=%d.",
      con, con->cosp_state);
    }

  return (ret);
}


/************************************************************************/
/*			cosp_con_rsp_neg				*/
/*----------------------------------------------------------------------*/
/* Function called by SS-user to Reject an incomming connection.	*/
/* 									*/
/* Parameters:								*/
/*  ACSE_CONN    *con		Pointer to con info struct		*/
/*  ST_INT	reason		Reason for reject COSP_CON_RSP_U_...	*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
ST_RET cosp_con_rsp_neg (ACSE_CONN *con, ST_INT reason)
{
ST_INT		ret;
ST_UINT	spdu_len;
char           *spdu_ptr;
ST_UCHAR		reason_code;

  COSP_LOG_ENC3 ("S-CONNECT.rsp-:  acse_conn_id =0x%08X  user_conn_id =%4ld  reason =%d",
    con, con->user_conn_id, reason);
  COSP_LOG_ENCC1 ("User data length: %d", con->ppdu_len);
  COSP_LOG_ENCH (con->ppdu_len, con->ppdu_ptr);

  ret = SD_SUCCESS;

  if (con->cosp_state == COSP_CSTATE_WAIT_CON_RSP)
    {
    /* encode REFUSE SPDU */
    switch (reason)
      {				   
      case COSP_CON_RSP_U_CONGESTION:
        reason_code = COSP_RF_REASON_U_CONGESTION;
      break;

      case COSP_CON_RSP_U_IN_UDATA:
        reason_code = COSP_RF_REASON_U_REJECT;
        /* SS-user data should be present in this case */
      break;

      case COSP_CON_RSP_U_NOT_SPECIFIED:
      default:
        reason_code = COSP_RF_REASON_U_NOT_SPECIFIED;
      }

    /* check if user data present */
    if (reason == COSP_CON_RSP_U_IN_UDATA)
      {
      /* SS-user data should be present in this case */
      if (con->ppdu_len == 0)
        ret = COSP_ERR_INV_UDATA_LEN;
      }
    else
      {
      /* SS-user data should NOT be present in this case */
      if (con->ppdu_len != 0)
        ret = COSP_ERR_INV_UDATA_LEN;
      }

    if (ret == SD_SUCCESS)
      {
      ret = cosp_enc_rf (con, &spdu_ptr, &spdu_len, reason_code);
      if (ret == SD_SUCCESS)
        {
        if (tp4_data (con->tp4_conn_id, SD_TRUE, spdu_len, spdu_ptr) == SD_SUCCESS)
          {
          con->cosp_state = COSP_CSTATE_WAIT_TDISCON_IND;
          tp4_session_timer (con->tp4_conn_id, SD_TRUE);	/* start timer	*/
          }
        else
          {
          ret = COSP_ERR_TP4_RET;
          COSP_LOG_ERR1 ("COSP-ERROR: S-CONNECT.rsp- acse_conn_id=0x%08X: T-DATA.req failed.",
            con);
          }
        }
      else
        {
        COSP_LOG_ERR1 ("COSP-ERROR: S-CONNECT.rsp- acse_conn_id=0x%08X: Encoding REFUSE SPDU failed.",
          con);
        }
      }
    else
      {
      COSP_LOG_ERR3 ("COSP-ERROR: S-CONNECT.rsp- acse_conn_id=0x%08X: Invalid SS-UserData length=%u for Reason=%d.",
        con, con->ppdu_len, reason);
      }
    }
  else
    {
    ret = COSP_ERR_INV_CON_STATE;
    COSP_LOG_ERR2 ("COSP-ERROR: S-CONNECT.rsp- acse_conn_id=0x%08X: Invalid state=%d.",
      con, con->cosp_state);
    }

  return (ret);
}

	/*======================================================*/
	/*							*/
	/*   C O N N E C T  (CALLING SIDE)  F U N C T I O N S	*/
	/*							*/
	/*======================================================*/


/************************************************************************/
/*			cosp_con_req					*/
/*----------------------------------------------------------------------*/
/* Function called by SS-user to establish Session Layer connection.	*/
/* SS-user should implement the u_cosp_con_cnf_pos & u_cosp_con_cnf_neg	*/
/* functions to receive confirm	for this request.			*/
/* If this function returns value other then SD_SUCCESS then the function	*/
/* u_cosp_con_cnf_xxx will not be called.				*/
/* 									*/
/* Parameters:								*/
/*  ST_LONG	 cosp_bind_id		COSP bind id			*/
/*  PRES_ADDR   *rem_addr		Remote Address			*/
/*  ACSE_CONN	*con			Pointer to connection info	*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
ST_RET cosp_con_req (ST_LONG cosp_bind_id, PRES_ADDR *rem_addr, ACSE_CONN *con)
{
ST_RET		ret;
TP4_ADDR        tp4_addr;
ST_UINT	spdu_len;
char           *spdu_ptr;
ST_LONG		tp4_user_bind_id;

  COSP_LOG_ENC3 ("S-CONNECT.req:   acse_conn_id =0x%08X  user_conn_id =%4ld  cosp_bind_id =%9ld",
    con, con->user_conn_id, cosp_bind_id);
  COSP_LOG_ENCC1 ("User data length: %d", con->ppdu_len);
  COSP_LOG_ENCH (con->ppdu_len, con->ppdu_ptr);

  if (con->cosp_state != COSP_CSTATE_IDLE)
    {
    ret = COSP_ERR_INV_CON_STATE;               /* invalid connect state*/
    COSP_LOG_ERR2 ("COSP-ERROR: S-CONNECT.req acse_conn_id=0x%08X: Invalid state=%d.",
      con, con->cosp_state);
    return (ret);
    }

  tp4_user_bind_id = cosp_bind_id;		/* we using only one id	*/

  /* Check "Called" SSEL. May be NULL (i.e. length=0) */
  if (rem_addr->ssel_len <= MAX_SSEL_LEN)
    {
    /* Save SSEL in ACSE_CONN struct.						*/
    con->rem_ssel[0] = rem_addr->ssel_len;	/* first byte is len	*/
    memcpy (&con->rem_ssel[1], rem_addr->ssel, rem_addr->ssel_len);

    ret = cosp_enc_cn_ac (con, rem_addr, &spdu_ptr, &spdu_len, COSP_SI_CONNECT);
    if (ret == SD_SUCCESS)
      {
      /* we always will initiate new transport connection for every new	*/
      /* session connection, so find empty spot for con info		*/

      /* Copy tsel and nsap from pres_addr to tp4_addr struct */
      tp4_addr.tp4_sel_len = rem_addr->tsel_len;
      memcpy (tp4_addr.tp4_sel, rem_addr->tsel, rem_addr->tsel_len);
      tp4_addr.tp_type = rem_addr->tp_type;
      if (rem_addr->tp_type == TP_TYPE_TP4)
        {
        tp4_addr.net_addr_len = rem_addr->nsap_len;
        memcpy (tp4_addr.netAddr.nsap, rem_addr->netAddr.nsap, rem_addr->nsap_len);
        }
      else
        {
        tp4_addr.netAddr.ip = rem_addr->netAddr.ip;	/* IP Addr	*/
        tp4_addr.port = rem_addr->port;
	}

      /* Note: !!! the tp4_disconnect_ind may be called before the	*/
      /* tp4_connect function returns (SD_FAILURE), we want to log	the err	*/
      /* only in one place (here)					*/
      cosp_in_tp4_func = SD_TRUE;			/* prevents logging err */

      /* request transport connection (no connect data)	*/
      if ((con->tp4_conn_id = tp4_connect (tp4_user_bind_id, (ST_LONG) con, &tp4_addr, 0, NULL,
                    &con->encrypt_ctrl)) >= 0)
        {
        /* save the encoded CN until T-CONNECT.cnf received */
        con->cosp_state  = COSP_CSTATE_WAIT_TCON_CNF;
        con->cosp_cn_len = spdu_len;
        con->cosp_cn_ptr = (ST_CHAR *) M_MALLOC (MSMEM_COSP_CN, spdu_len);
        memcpy (con->cosp_cn_ptr, spdu_ptr, spdu_len);
        con->cosp_vtca   = SD_FALSE;      /* transport conn initiator	*/
        }
      else
        {
        ret = COSP_ERR_TP4_RET;
        COSP_LOG_ENC1 ("COSP-ERROR: S-CONNECT.req acse_conn_id=0x%08X: T-CONNECT.req failed.",
          con);
        }

      cosp_in_tp4_func = SD_FALSE;	/* turn off the flag */
      }
    else
      {
      COSP_LOG_ERR1 ("COSP-ERROR: S-CONNECT.req acse_conn_id=0x%08X: Encoding CONNECT SPDU failed.",
        con);
      }
    }
  else
    {
    ret = COSP_ERR_INV_SSEL;			/* invalid SSEL (length) */
    COSP_LOG_ERR2 ("COSP-ERROR: S-CONNECT.req acse_conn_id=0x%08X: Invalid remote SSEL length=%d.",
      con, rem_addr->ssel_len);
    }

  return (ret);
}

 
/************************************************************************/
/*			tp4_connect_cnf					*/
/*----------------------------------------------------------------------*/
/* This function is called by the TP4-provider to indicate that	the	*/
/* transport connection has been established.				*/
/* NOTE: cosp_con_req saved the TP4 connection ID in con->tp4_conn_id.	*/
/*       It should match the tp4_conn_id passed to this function.	*/
/* 									*/
/* Parameters:								*/
/*  ST_LONG	  user_conn_id	User's connection id for this connect.	*/
/*  ST_LONG	  tp4_conn_id	TP4 connection id for this connect.	*/
/*  ST_INT	 conndata_len	Length of optional connect data.	*/
/*  char	*conndata	Pointer to optional connect data.	*/
/*	  								*/
/* Return:								*/
/*	ST_VOID								*/
/************************************************************************/
ST_VOID tp4_connect_cnf (ST_LONG user_conn_id, ST_LONG tp4_conn_id, 
  		      ST_INT conndata_len, char *conndata)
{
ACSE_CONN       *con;

  tp4_reldata (conndata);		/* !!! we ignoring conndata	*/

  con = (ACSE_CONN *) user_conn_id;

  if (con->cosp_state == COSP_CSTATE_WAIT_TCON_CNF)
    {
#if defined (TP0_ENABLED)
    /* If connection is TP0, save encrypt_ctrl.	*/
    if (tp4_conn_id >= MIN_TP0_CONN_ID)
      {	/* this is TP0 connection	*/
      TP0_CONN *tp0_conn = &tp0_conn_arr [tp4_conn_id-MIN_TP0_CONN_ID];
      /* save the encryption info in ACSE_CONN */
      con->encrypt_ctrl = tp0_conn->sock_info->encrypt_ctrl;
      }
#endif

    /* send the CN (already encoded) on the established transport connection */
    if (tp4_data (tp4_conn_id, SD_TRUE, con->cosp_cn_len, con->cosp_cn_ptr)
      == SD_SUCCESS)
      {
      con->cosp_state = COSP_CSTATE_WAIT_AC;		/* wait AC or RF */

      /* we are done with the encoding buffer for CN */
      if (con->cosp_cn_ptr)
        {
        M_FREE (MSMEM_COSP_CN, con->cosp_cn_ptr);	
        con->cosp_cn_len = 0;
        con->cosp_cn_ptr = NULL;
        }
      }
    else
      {
      COSP_LOG_ERR1 ("COSP-ERROR: T-CONNECT.cnf acse_conn_id=0x%08X: T-DATA.req failed",
        con);
      }
    }
  else
    {
    COSP_LOG_ERR2 ("COSP-ERROR: T-CONNECT.cnf acse_conn_id=0x%08X: Invalid state=%d.",
      con, con->cosp_state);
    }
}

	/*======================================================*/
	/*							*/
	/*  C O N N E C T I O N   R E L E A S E   (CALLED SIDE)	*/
	/*							*/
	/*======================================================*/

/************************************************************************/
/*			cosp_rel_rsp_pos				*/
/*----------------------------------------------------------------------*/
/* Function called by SS-user to Accept a release of connection.	*/
/* 									*/
/* Parameters:								*/
/*  ACSE_CONN	*con		Pointer to connection info		*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
ST_RET cosp_rel_rsp_pos (ACSE_CONN *con)
{
ST_INT		ret;
ST_UINT	spdu_len;
char           *spdu_ptr;

  COSP_LOG_ENC2 ("S-RELEASE.rsp+:  acse_conn_id =0x%08X  user_conn_id =%4ld",
    con, con->user_conn_id);
  COSP_LOG_ENCC1 ("User data length: %d", con->ppdu_len);
  COSP_LOG_ENCH (con->ppdu_len, con->ppdu_ptr);

  if (con->cosp_state == COSP_CSTATE_WAIT_REL_RSP)
    {
    /* some additional predicates must be also true */
    if (!con->cosp_vcoll ||
        (con->cosp_vcoll && con->cosp_vdnr) ||	/* collision but DN received */
        (con->cosp_vcoll && !con->cosp_vtca))   /* collision and we are con initiator */
      {
      /* connection released confirmed by COPP, encode and send DISCONNECT SPDU */
      /* Transport connections are not reused.				*/
      ret = cosp_enc_fn_dn (con, &spdu_ptr, &spdu_len, COSP_SI_DISCON);
      if (ret == SD_SUCCESS)
        {
        if (tp4_data (con->tp4_conn_id, SD_TRUE, spdu_len, spdu_ptr) == SD_SUCCESS)
          {
          if (!con->cosp_vcoll ||
              (con->cosp_vcoll && con->cosp_vdnr)) /* collision but DN received */
            {
            con->cosp_state = COSP_CSTATE_WAIT_TDISCON_IND;
            tp4_session_timer (con->tp4_conn_id, SD_TRUE);	/* start timer	*/
            }
          else
            {
            /* collision and we are the con initiator */
            con->cosp_state = COSP_CSTATE_WAIT_DN;
            }
          }
        else
          {
          ret = COSP_ERR_TP4_RET;
          COSP_LOG_ERR1 ("COSP-ERROR: S-RELEASE.rsp+ acse_conn_id=0x%08X: T-DATA.req failed.",
            con);
          }
        }
      else
        {
        COSP_LOG_ERR1 ("COSP-ERROR: S-RELEASE.rsp+ acse_conn_id=0x%08X: Encoding DISCONNECT SPDU failed.",
          con);
        }
      }
    else
      {
      ret = COSP_ERR_INV_CON_STATE;
      COSP_LOG_ERR1 ("COSP-ERROR: S-RELEASE.rsp+ acse_conn_id=0x%08X: Connection Release Collision\n"
                     "            (DN not received or not a connection initiator).",
        con);
      }
    }
  else
    {
    ret = COSP_ERR_INV_CON_STATE;
    COSP_LOG_ERR2 ("COSP-ERROR: S-RELEASE.rsp+ acse_conn_id=0x%08X: Invalid state=%d.",
      con, con->cosp_state);
    }

  return (ret);
}


	/*======================================================*/
	/*							*/
	/*  C O N N E C T I O N   R E L E A S E   (CALLING SIDE)*/
	/*							*/
	/*======================================================*/


/************************************************************************/
/*			cosp_rel_req					*/
/*----------------------------------------------------------------------*/
/* Function called by SS-user to release a Session connection.		*/
/* SS-user should implement the u_cosp_rel_cnf_pos function to receive	*/
/* confirm for this request.  Negative confirm is not implemented.	*/
/* If this function returns value other then SD_SUCCESS then the function	*/
/* u_cosp_rel_cnf_pos will not be called.				*/
/* 									*/
/* Parameters:								*/
/*  ACSE_CONN	*con		Pointer to connection info		*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
ST_RET cosp_rel_req (ACSE_CONN *con)
{
ST_INT		ret;
ST_UINT	spdu_len;
char           *spdu_ptr;

  COSP_LOG_ENC2 ("S-RELEASE.req:   acse_conn_id =0x%08X  user_conn_id =%4ld",
    con, con->user_conn_id);
  COSP_LOG_ENCC1 ("User data length: %d", con->ppdu_len);
  COSP_LOG_ENCH (con->ppdu_len, con->ppdu_ptr);

  ret = SD_SUCCESS;

  if (con->cosp_state == COSP_CSTATE_WAIT_REL_RSP ||
      con->cosp_state == COSP_CSTATE_DATA_XFER)
    {
    /* connection is beeing released, encode and send FINISH SPDU */
    ret = cosp_enc_fn_dn (con, &spdu_ptr, &spdu_len, COSP_SI_FINISH);
    if (ret == SD_SUCCESS)
      {
      if (tp4_data (con->tp4_conn_id, SD_TRUE, spdu_len, spdu_ptr) == SD_SUCCESS)
        {
        if (con->cosp_state == COSP_CSTATE_WAIT_REL_RSP)
          {
          /* collision occurred, request to release con while waiting	*/
          /* for resp to S-RELEASE.ind					*/
          con->cosp_vcoll = SD_TRUE;
          /* remain in the same state	*/
          }
        else
          con->cosp_state = COSP_CSTATE_WAIT_DN;
        }
      else
        {
        ret = COSP_ERR_TP4_RET;
        COSP_LOG_ERR1 ("COSP-ERROR: S-RELEASE.req acse_conn_id=0x%08X: T-DATA.req failed.",
          con);
        }
      }
    else
      {
      COSP_LOG_ERR1 ("COSP-ERROR: S-RELEASE.req acse_conn_id=0x%08X: Encoding FINISH SPDU failed.",
        con);
      }
    }
  else
    {
    ret = COSP_ERR_INV_CON_STATE;
    COSP_LOG_ERR2 ("COSP-ERROR: S-RELEASE.req acse_conn_id=0x%08X: Invalid state=%d.",
      con, con->cosp_state);
    }

  return (ret);
}


/************************************************************************/
/*			tp4_disconnect_ind				*/
/*----------------------------------------------------------------------*/
/* This function is called by the TP4-provider to indicate that a	*/
/* transport connection is beeing released.				*/
/* 									*/
/* Parameters:								*/
/*  ST_LONG	  user_conn_id	User's connection id for this connect.	*/
/*  ST_INT	  reason	Reason for disconnect			*/
/*  ST_INT	  conndata_len	Length of opt connect data <= 32 bytes	*/
/*  char	 *conndata	Pointer to opt connect data.		*/
/*	  								*/
/* Return:								*/
/*	ST_VOID								*/
/************************************************************************/
ST_VOID tp4_disconnect_ind (ST_LONG user_conn_id, ST_INT reason,
                         ST_INT conndata_len, char *conndata)
{
ACSE_CONN       *con;

  tp4_reldata (conndata);		/* !!! we ignoring conndata	*/

  con = (ACSE_CONN *) user_conn_id;

  switch (con->cosp_state)
    {
    case COSP_CSTATE_IDLE:
      /* ignore this tp4_disconnect_ind if in tp4_connect (returns SD_FAILURE) */
      if (!cosp_in_tp4_func)
        {
        COSP_LOG_ERR2 ("COSP-ERROR: T-DISCONNECT.ind acse_conn_id=0x%08X: Invalid state=%d.",
          con, con->cosp_state);
        }
    break;

    case COSP_CSTATE_IDLE_TCON:
      /* disconnected before a CN has been received (COPP has no	*/
      /* knowledge about this connection at this point).		*/
      acse_free_con (con);
    break;

    case COSP_CSTATE_WAIT_TCON_CNF:
    case COSP_CSTATE_WAIT_AC:
    case COSP_CSTATE_WAIT_DN:
    case COSP_CSTATE_WAIT_CON_RSP:
    case COSP_CSTATE_WAIT_REL_RSP:
    case COSP_CSTATE_DATA_XFER:

      /* issue S-P-ABORT.ind to local SS-user (use code from cosp_usr.h)*/
      COSP_LOG_DEC4 ("S-P-ABORT.ind:   acse_conn_id =0x%08X  user_conn_id =%4ld  reason =%d (TP4 reason=0x%02x)",
        con, con->user_conn_id, (ST_INT) COSP_P_AB_IND_TP_DISCON, reason);

      u_cosp_p_abort_ind (con, COSP_P_AB_IND_TP_DISCON);
      acse_free_con (con);
    break;

    case COSP_CSTATE_WAIT_TDISCON_IND:
      tp4_session_timer (con->tp4_conn_id, SD_FALSE);	/* stop timer	*/
      acse_free_con (con);
    break;

    default:
      COSP_LOG_ERR2 ("COSP-ERROR: T-DISCONNECT.ind acse_conn_id=0x%08X: Invalid state=%d.",
        con, con->cosp_state);
    }
}


/************************************************************************/
/*                      tp4_session_timer_expired			*/
/*----------------------------------------------------------------------*/
/* This function is called by TP4 when the session_timer expire.	*/
/*	  								*/
/* Parameters:								*/
/*  ST_LONG	user_conn_id	COSP connection id for this connect.	*/
/*	  								*/
/* Return:								*/
/*	ST_VOID								*/
/************************************************************************/
ST_VOID tp4_session_timer_expired (ST_LONG user_conn_id)
{
ACSE_CONN       *con;

  con = (ACSE_CONN *) user_conn_id;

  if (con->cosp_state == COSP_CSTATE_WAIT_TDISCON_IND)
    {
    tp4_disconnect (con->tp4_conn_id, 0, NULL);
    acse_free_con (con);
    }
  else
    {
    COSP_LOG_ERR2 ("COSP-ERROR: S-TIMER.expired acse_conn_id=0x%08X: Invalid state=%d.",
      con, con->cosp_state);
    }
}


	/*======================================================*/
	/*							*/
	/*	A B O R T    F U N C T I O N S			*/
	/*							*/
	/*======================================================*/


/************************************************************************/
/*			cosp_u_abort_req				*/
/*----------------------------------------------------------------------*/
/* Function called by SS-user to Abort a connection (S-U-ABORT)		*/
/*	  								*/
/* Parameters:								*/
/*  ACSE_CONN	*con		Pointer to connection info		*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/* CRITICAL: this function may free the ACSE_CONN struct so the struct	*/
/*	MUST NOT be accessed after calling this function.		*/
/************************************************************************/
ST_RET cosp_u_abort_req (ACSE_CONN *con)
{
ST_INT	ret;

  COSP_LOG_ENC2 ("S-U-ABORT.req:   acse_conn_id =0x%08X  user_conn_id =%4ld",
    con, con->user_conn_id);
  COSP_LOG_ENCC1 ("User data length: %d", con->ppdu_len);
  COSP_LOG_ENCH (con->ppdu_len, con->ppdu_ptr);

  ret = SD_SUCCESS;

  switch (con->cosp_state)
    {
    case COSP_CSTATE_WAIT_TCON_CNF:
      tp4_disconnect (con->tp4_conn_id, 0, NULL);
      acse_free_con (con);		/* done with this connection	*/
    break;

    case COSP_CSTATE_WAIT_AC:
    case COSP_CSTATE_WAIT_DN:
    case COSP_CSTATE_WAIT_CON_RSP:
    case COSP_CSTATE_WAIT_REL_RSP:
    case COSP_CSTATE_DATA_XFER:
      /* send ABORT SPDU and wait for T-DISCONNECT.ind	*/
      if ((ret = cosp_send_abort (con, 0)) == SD_SUCCESS)
        {
        con->cosp_state = COSP_CSTATE_WAIT_TDISCON_IND;
        tp4_session_timer (con->tp4_conn_id, SD_TRUE);	/* start timer	*/
	}
    break;

    default:
      ret = COSP_ERR_INV_CON_STATE;
      COSP_LOG_ERR2 ("COSP-ERROR: S-U-ABORT.req acse_conn_id=0x%08X: Invalid state=%d.",
        con, con->cosp_state);
     }

  if (ret != SD_SUCCESS)
    {


    /* disconnect if we couldn't send abort */
    tp4_disconnect (con->tp4_conn_id, 0, NULL);
    acse_free_con (con);



    }

  /* we always will return success */
  return (SD_SUCCESS);
}


/************************************************************************/
/*			cosp_send_abort					*/
/*----------------------------------------------------------------------*/
/* Function to sends ABORT SPDU to remote SS-user.			*/
/*	  								*/
/* Parameters:								*/
/*  ACSE_CONN       	*con		Pointer to conn info struct	*/
/*  ST_INT		err_code	Error code (defined by COSP)	*/
/*					If  =0 then S-U-ABORT sent	*/
/*					If !=0 then S-P-ABORT sent	*/
/* Return:								*/
/*	SD_SUCCESS (0)		if abort successful			*/
/*	error code		otherwise				*/
/************************************************************************/
 ST_RET cosp_send_abort (ACSE_CONN *con, ST_INT err_code)
{
ST_RET   ret;
char      spdu_buf [COSP_P_AB_SPDU_LEN];
ST_UINT  spdu_len;
char	 *spdu_ptr;

  if (err_code != 0)
    {
    /* encode S-P-ABORT (error code will be encoded and we supply encoding buffer) */
    spdu_len = sizeof (spdu_buf);
    spdu_ptr = spdu_buf;
    if ((ret = cosp_enc_p_ab (spdu_ptr, err_code)) == SD_SUCCESS)
      {
      if (tp4_data (con->tp4_conn_id, SD_TRUE, spdu_len, spdu_ptr) != SD_SUCCESS)
        {
        ret = COSP_ERR_TP4_RET;
        COSP_LOG_ERR1 ("COSP-ERROR: S-P-ABORT acse_conn_id=0x%08X: T-DATA.req failed.",
          con);
        }
      }
    else
      {
      COSP_LOG_ERR1 ("COSP-ERROR: S-P-ABORT acse_conn_id=0x%08X: Encoding of P-ABORT SPDU failed.",
        con);
      }
    }
  else
    {
    /* encode S-U-ABORT (user data should be already encoded) */
    if ((ret = cosp_enc_u_ab (con, &spdu_ptr, &spdu_len)) == SD_SUCCESS)
      {
      if (tp4_data (con->tp4_conn_id, SD_TRUE, spdu_len, spdu_ptr) != SD_SUCCESS)
        {
        ret = COSP_ERR_TP4_RET;
        COSP_LOG_ERR1 ("COSP-ERROR: S-U-ABORT.req acse_conn_id=0x%08X: T-DATA.req failed.",
          con);
        }
      }
    else
      {
      COSP_LOG_ERR1 ("COSP-ERROR: S-U-ABORT.req acse_conn_id=0x%08X: Encoding of U-ABORT SPDU failed.",
        con);
      }
    }

  return (ret);
}


	/*======================================================*/
	/*							*/
	/*	D A T A    F U N C T I O N S			*/
	/*							*/
	/*======================================================*/

/************************************************************************/
/*			tp4_data_ind					*/
/*----------------------------------------------------------------------*/
/* Function to process data indication.					*/
/* Note that the tp4_data_ind is called when complete SPDU has been	*/
/* received, so the eot flag is always SD_TRUE.				*/
/* 									*/
/* Parameters:								*/
/*  ST_LONG	 user_conn_id	User's connection id for this connect.	*/
/*  ST_INT	 eot            Indicates if last part of SPDU received.*/
/*  ST_UINT	 data_len	Length of data.				*/
/*  char	*data		Pointer to data buffer.			*/
/*	  								*/
/* Return:								*/
/*	ST_VOID								*/
/************************************************************************/
//void called_conn_disconnect(ACSE_CONN  *con);//lnk编译删除，原前置程序未使用
ST_VOID tp4_data_ind (ST_LONG user_conn_id, ST_INT eot,
		   ST_UINT data_len, char *data)	
{
ST_INT		ret;
ST_UINT 	action;
ACSE_CONN       *con;

  ret    = SD_SUCCESS;
  action = 0;
  con    = (ACSE_CONN *) user_conn_id;

  /* Note: the protocol requires following steps to be taken for invalid*/
  /*	   incomming-event/state intersection (default: in switch) or	*/
  /*	   if the incomming SPDU can't be correctly process:		*/
  /*		- issue S-P-ABORT					*/
  /*		- send an ABORT SPDU					*/
  /*		- start timer						*/
  /*		- wait for T-DISCONNECT or ABORT ACCEPT SPDU		*/

  switch (data [0])	/* First byte is SPDU Session Identifier	*/
    {

    case COSP_SI_CONNECT:
      switch (con->cosp_state)
        {
        case COSP_CSTATE_IDLE_TCON:
          /* if we are the transport connection acceptor, process CN	*/
          if (con->cosp_vtca)
            ret = cosp_process_connect (con, data_len, data);
          else
            action = COSP_T_DISCONNECT;
        break;

        case COSP_CSTATE_WAIT_TDISCON_IND:
          tp4_session_timer (con->tp4_conn_id, SD_FALSE);	/* stop timer	*/
          action = COSP_T_DISCONNECT;
        break;

        default:				/* in invalid state	*/
          ret = COSP_ERR_INV_CON_STATE;
          COSP_LOG_ERR2 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Invalid state=%d (for CONNECT SPDU).",
            con, con->cosp_state);
        }
    break;

    case COSP_SI_FINISH:
      switch (con->cosp_state)
        {
        case COSP_CSTATE_IDLE_TCON:
          action = COSP_T_DISCONNECT;
        break;

        case COSP_CSTATE_WAIT_DN:	/* collision of conn releases	*/
          con->cosp_vcoll = SD_TRUE;
          /* go to next case */
        case COSP_CSTATE_DATA_XFER:
          ret = cosp_process_finish (con, data_len, data);
        break;

        case COSP_CSTATE_WAIT_TDISCON_IND:
          /* we ignore the FINISH SPDU and remain in the same state */
        break;

        default:				/* in invalid state	*/
          ret = COSP_ERR_INV_CON_STATE;
          COSP_LOG_ERR2 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Invalid state=%d (for FINISH SPDU).",
            con, con->cosp_state);
        }
    break;

    case COSP_SI_ACCEPT:
      switch (con->cosp_state)
        {
        case COSP_CSTATE_IDLE_TCON:
          action = COSP_T_DISCONNECT;
        break;

        case COSP_CSTATE_WAIT_AC:
          con->cosp_vcoll = SD_FALSE;
          con->cosp_vdnr  = SD_FALSE;
          ret = cosp_process_accept (con, data_len, data);
        break;

        case COSP_CSTATE_WAIT_TDISCON_IND:
          /* we ignore the ACCEPT SPDU and remain in the same state */
        break;

        default:				/* in invalid state	*/
          ret = COSP_ERR_INV_CON_STATE;
          COSP_LOG_ERR2 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Invalid state=%d (for ACCEPT SPDU).",
            con, con->cosp_state);
        }
    break;

    case COSP_SI_REFUSE:
      switch (con->cosp_state)
        {
        case COSP_CSTATE_IDLE_TCON:
          action = COSP_T_DISCONNECT;
        break;

        case COSP_CSTATE_WAIT_AC:
          if ((ret = cosp_process_refuse (con, data_len, data)) == SD_SUCCESS)
            action = COSP_T_DISCONNECT;
        break;

        case COSP_CSTATE_WAIT_TDISCON_IND:
          /* we ignore the REFUSE SPDU and remain in the same state */
        break;

        default:				/* in invalid state	*/
          ret = COSP_ERR_INV_CON_STATE;
          COSP_LOG_ERR2 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Invalid state=%d (for REFUSE SPDU).",
            con, con->cosp_state);
        }
    break;

    case COSP_SI_DISCON:
      switch (con->cosp_state)
        {
        case COSP_CSTATE_IDLE_TCON:
          action = COSP_T_DISCONNECT;
        break;

        case COSP_CSTATE_WAIT_DN:
          if ((ret = cosp_process_discon (con, data_len, data)) == SD_SUCCESS)
            action = COSP_T_DISCONNECT;
        break;

        case COSP_CSTATE_WAIT_REL_RSP:		/* collision */
          if (con->cosp_vcoll && con->cosp_vtca)
            {
            con->cosp_vdnr = SD_TRUE;
            ret = cosp_process_discon (con, data_len, data);
            /* remain in the same state */
            }
          else
            {
            ret = COSP_ERR_INV_CON_STATE;
            COSP_LOG_ERR1 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Collision flag not set or not a connection acceptor.", 
              con);
            }
        break;

        case COSP_CSTATE_WAIT_TDISCON_IND:
          /* we ignore the DN SPDU and remain in the same state */
        break;

        default:				/* in invalid state	*/
          ret = COSP_ERR_INV_CON_STATE;
          COSP_LOG_ERR2 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Invalid state=%d (for DISCONNECT SPDU).",
            con, con->cosp_state);
        }
    break;

    case COSP_SI_ABORT:		/* !always! disconnect transport	*/
    				/* otherwise we may loop sending ABORTs	*/
      action = COSP_T_DISCONNECT;

      switch (con->cosp_state)
        {
        case COSP_CSTATE_IDLE_TCON:
        break;

        case COSP_CSTATE_WAIT_AC:
        case COSP_CSTATE_WAIT_DN:
        case COSP_CSTATE_WAIT_CON_RSP:
        case COSP_CSTATE_WAIT_REL_RSP:
        case COSP_CSTATE_DATA_XFER:
          if ((ret = cosp_process_abort (con, data_len, data)) != SD_SUCCESS)
            {
            /* even if we failed to decode abort we still will generate S-P-ABORT */
            action |= COSP_S_P_ABORT_IND;
            }
        break;

        case COSP_CSTATE_WAIT_TDISCON_IND:
          tp4_session_timer (con->tp4_conn_id, SD_FALSE);	/* stop timer	*/
        break;

        default:				/* in invalid state	*/
          ret = COSP_ERR_INV_CON_STATE;
          COSP_LOG_ERR2 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Invalid state=%d (for ABORT SPDU).",
            con, con->cosp_state);
          action |= COSP_S_P_ABORT_IND;
        }
    break;

    case COSP_SI_AB_ACCEPT:
      /* this SPDU may be sent by remote COSP engine even if the	*/
      /* transport connection is not to be kept				*/

      action = COSP_T_DISCONNECT;  /* !always! disconnect transport 	*/

      switch (con->cosp_state)
        {
        case COSP_CSTATE_IDLE_TCON:
        break;

        case COSP_CSTATE_WAIT_TDISCON_IND:
          tp4_session_timer (con->tp4_conn_id, SD_FALSE);	/* stop timer	*/
        break;

        default:				/* in invalid state	*/
          ret = COSP_ERR_INV_CON_STATE;
          COSP_LOG_ERR2 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Invalid state=%d (for ABORT ACCEPT SPDU).",
            con, con->cosp_state);
	  /* ABORT ACCEPT can be received only in response to already	*/
	  /* sent ABORT, so we will not send another one here		*/
        }
    break;

/*  case COSP_SI_GIVE_TOKEN:	!!! GIVE-TOKEN is concatinated with DATA*/
    case COSP_SI_DATA:		/*  SPDU and both have the same code=1	*/
      switch (con->cosp_state)
        {
        case COSP_CSTATE_IDLE_TCON:
          action = COSP_T_DISCONNECT;
        break;

        case COSP_CSTATE_WAIT_DN:
          if (con->cosp_vcoll)
            {
            ret = COSP_ERR_INV_CON_STATE;
            COSP_LOG_ERR2 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Invalid state=%d (for DATA SPDU).",
              con, con->cosp_state);
            break;
            }
          /* process the received DATA SPDU and remain in the same state */
          /* go to next case						*/
        case COSP_CSTATE_DATA_XFER:
          ret = cosp_process_data (con, data_len, data);
        break;

        case COSP_CSTATE_WAIT_TDISCON_IND:
          /* we ignore the DATA SPDU and remain in the same state */
        break;

        default:				/* in invalid state	*/
          ret = COSP_ERR_INV_CON_STATE;
          COSP_LOG_ERR2 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Invalid state=%d (for DATA SPDU).",
            con, con->cosp_state);
        }
    break;

    default:
      ret = COSP_ERR_DEC_INV_SPDU;
      COSP_LOG_ERR2 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Unsupported SPDU SI code=%d received.",
        con, (ST_INT)(data [0]));
      action = COSP_S_P_ABORT_IND | COSP_SEND_ABORT_SPDU;
    } /* end of switch */

  tp4_reldata (data);		/* we are done with the TP4 data buffer	*/

  if (ret != SD_SUCCESS)
    {
    /* if error occurred and SPDU other then an ABORT or ABORT ACCEPT	*/
    /* received then issue S-P-ABORT and SEND an ABORT SPDU.		*/

    if ((data [0] != COSP_SI_ABORT) && (data [0] != COSP_SI_AB_ACCEPT))
      action = COSP_S_P_ABORT_IND | COSP_SEND_ABORT_SPDU;
    }

  /*--------------------------------------------------------------------*/
  /* abort/disconnect if processing of received SPDU failed or this is	*/
  /* the protocol specification						*/
  /*--------------------------------------------------------------------*/

  if (action & COSP_SEND_ABORT_SPDU)
    {
    /* encode and send ABORT SPDU to remote SS-user, ret is the error code */
    if (cosp_send_abort (con, ret) == SD_SUCCESS)
      {
      con->cosp_state = COSP_CSTATE_WAIT_TDISCON_IND;
      tp4_session_timer (con->tp4_conn_id, SD_TRUE);	/* start timer	*/
      }
    else
      {
      con->cosp_state = COSP_CSTATE_IDLE;
      action |= COSP_T_DISCONNECT;	/* disconnect if we can't abort */
      }
    }

  if (action & COSP_S_P_ABORT_IND)
    {
    /* issue S-P-ABORT.ind to local SS-user (use code from cosp_usr.h)	*/

    COSP_LOG_DEC3 ("S-P-ABORT.ind:   acse_conn_id =0x%08X  user_conn_id =%4ld  reason =%d",
      con, con->user_conn_id, (ST_INT) COSP_P_AB_IND_PROT_ERR);

    u_cosp_p_abort_ind (con, COSP_P_AB_IND_PROT_ERR);
    }

  if (action & COSP_T_DISCONNECT)
    {
    tp4_disconnect (con->tp4_conn_id, 0, NULL);
    /*renxiaobao20220831 °²È«¼ì²â*/
    //called_conn_disconnect(con);//lnk编译删除，原前置程序未使用

    acse_free_con (con);	/* we are disconnected, free con struct */
    }
}


/************************************************************************/
/*			tp4_expdata_ind					*/
/*----------------------------------------------------------------------*/
/* Function to process a expedited S-DATA.ind.				*/
/* 									*/
/* Parameters:								*/
/*  ST_LONG	 user_conn_id	User's connection id for this connect.	*/
/*  ST_UINT	 data_len	Length of data.				*/
/*  char	*data		Pointer to data buffer.			*/
/*	  								*/
/* Return:								*/
/*	ST_VOID								*/
/************************************************************************/
ST_VOID tp4_expdata_ind (ST_LONG user_conn_id, ST_UINT data_len, char *data)	
{
  /* Treat like normal data.  Assume "eot" = 1 */
  tp4_data_ind (user_conn_id, 1, data_len, data);
}


/************************************************************************/
/*			cosp_data_req					*/
/*----------------------------------------------------------------------*/
/* Function called by SS-user to transfer normal data on a previously	*/
/* established connection.						*/
/* 									*/
/* Parameters:								*/
/*  ACSE_CONN	*con		Pointer to connection info		*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
ST_RET cosp_data_req (ACSE_CONN *con)
{
ST_INT		ret;
ST_UINT	spdu_len;
char           *spdu_ptr;

  COSP_LOG_ENC2 ("S-DATA.req:      acse_conn_id =0x%08X  user_conn_id =%4ld",
    con, con->user_conn_id);
  COSP_LOG_ENCC1 ("User data length: %d", con->ppdu_len);
  COSP_LOG_ENCH (con->ppdu_len, con->ppdu_ptr);

  ret = SD_SUCCESS;

  if ((con->cosp_state == COSP_CSTATE_DATA_XFER) ||
      (con->cosp_state == COSP_CSTATE_WAIT_REL_RSP && !con->cosp_vcoll))
    {
    if ((ret = cosp_enc_dt (con, &spdu_ptr, &spdu_len)) == SD_SUCCESS)
      {
      if (tp4_data (con->tp4_conn_id, SD_TRUE, spdu_len, spdu_ptr) != SD_SUCCESS)
        {
        ret = COSP_ERR_TP4_RET;
        COSP_LOG_ERR0 ("COSP-ERROR: S-DATA.req acse_conn_id=0x%08X: T-DATA.req failed.");
        }
      }
    else
      {
      COSP_LOG_ERR1 ("COSP-ERROR: S-DATA.req acse_conn_id=0x%08X: Encoding of DATA SPDU failed.",
        con);
      }
    }
  else
    {
    ret = COSP_ERR_INV_CON_STATE;
    COSP_LOG_ERR2 ("COSP-ERROR: S-DATA.req acse_conn_id=0x%08X: Invalid state=%d (or collision flag set).",
      con, con->cosp_state);
    }

  return (ret);
}


	/*======================================================*/
	/*							*/
	/*  L O C A L   S P D U   P R O C E S S I N G   F U N C	*/
	/*							*/
	/*======================================================*/


/************************************************************************/
/*			cosp_process_connect				*/
/*----------------------------------------------------------------------*/
/* This function will decode the CONNECT SPDU and call SS-user's funct	*/
/* u_cosp_con_ind to indicate to the SS-user incomming connection req.	*/
/*									*/
/* Parameters:								*/
/*  ACSE_CONN    *con		Pointer to con info struct		*/
/*  ST_UINT	 spdu_len	Length of SPDU				*/
/*  char	*spdu_buf	Pointer to SPDU buffer			*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
static ST_RET cosp_process_connect (ACSE_CONN *con, ST_UINT spdu_len,
				     char *spdu_buf)
{
ST_INT		ret;
COSP_CN_AC	dec_par;		/* decoded connection params	*/
ST_LONG		cosp_user_bind_id;

  /*----------------------------*/
  /* decode the CONNECT SPDU	*/
  /*----------------------------*/

  ret = cosp_dec_cn_ac (&dec_par, spdu_buf, spdu_len, COSP_SI_CONNECT);
  if (ret == SD_SUCCESS)
    {
    /* Save SSELs in ACSE_CONN struct.					*/
    memcpy (con->loc_ssel, dec_par.loc_ssel, dec_par.loc_ssel [0]+1);
    memcpy (con->rem_ssel, dec_par.rem_ssel, dec_par.rem_ssel [0]+1);

    /* generate S-CONNECT.ind to the SS-user */

    con->cosp_state = COSP_CSTATE_WAIT_CON_RSP;
    con->ppdu_len = dec_par.udata_len;
    con->ppdu_ptr = dec_par.udata_ptr;

    /* user_conn_id (created by ACSE-user) is invalid at this point,	*/
    /* we probably should set it to -1 (an invalid value).  Note that 0	*/
    /* is a valid id.							*/
    con->user_conn_id = -1L;

    cosp_user_bind_id = con->cosp_bind_id;  /* COSP id = COSP User's bind id */

    COSP_LOG_DEC1 ("S-CONNECT.ind:   acse_conn_id =0x%08X", con);
    COSP_LOG_DECC1 ("User data length: %d", con->ppdu_len);
    COSP_LOG_DECH (con->ppdu_len, con->ppdu_ptr);

    u_cosp_con_ind (cosp_user_bind_id, con);
    }
  else
    {
    COSP_LOG_ERR1 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Decoding of CONNECT SPDU failed.",
      con);
    }

  return (ret);
}


/************************************************************************/
/*			cosp_process_finish				*/
/*----------------------------------------------------------------------*/
/* This function will decode the FINISH SPDU and call SS-user's function*/
/* u_cosp_rel_ind to indicate to the SS-user that the remote wants to	*/
/* release connection.							*/
/*									*/
/* Parameters:								*/
/*  ACSE_CONN    *con		Pointer to con info struct		*/
/*  ST_UINT	 spdu_len	Length of SPDU				*/
/*  char	*spdu_buf	Pointer to SPDU buffer			*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
static ST_RET cosp_process_finish (ACSE_CONN *con, ST_UINT spdu_len,
     				    char *spdu_buf)
{
ST_INT		ret;
COSP_FN_DN	dec_par;		/* decoded con rel params	*/

  /*------------------------------------*/
  /* decode and check the FINISH SPDU	*/
  /*------------------------------------*/

  ret = cosp_dec_fn_dn (&dec_par, spdu_buf, spdu_len, COSP_SI_FINISH);
  if (ret == SD_SUCCESS)
    {
    /* generate S-RELEASE.ind */

    con->cosp_state = COSP_CSTATE_WAIT_REL_RSP;
    con->ppdu_len = dec_par.udata_len;
    con->ppdu_ptr = dec_par.udata_ptr;

    COSP_LOG_DEC2 ("S-RELEASE.ind:   acse_conn_id =0x%08X  user_conn_id =%4ld",
      con, con->user_conn_id);
    COSP_LOG_DECC1 ("User data length: %d", con->ppdu_len);
    COSP_LOG_DECH (con->ppdu_len, con->ppdu_ptr);

    u_cosp_rel_ind (con);
    }
  else
    {
    COSP_LOG_ERR1 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Decoding of FINISH SPDU failed.",
      con);
    }

  return (ret);
}


/************************************************************************/
/*			cosp_process_accept				*/
/*----------------------------------------------------------------------*/
/* This function will decode the ACCEPT SPDU and call SS-user's funct	*/
/* u_cosp_con_cnf_pos to indicate to the SS-user that the connection has*/
/* been established.							*/
/*									*/
/* Parameters:								*/
/*  ACSE_CONN    *con		Pointer to con info struct		*/
/*  ST_UINT	 spdu_len	Length of SPDU				*/
/*  char	*spdu_buf	Pointer to SPDU buffer			*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
static ST_RET cosp_process_accept (ACSE_CONN *con, ST_UINT spdu_len,
				    char *spdu_buf)
{
ST_INT		ret;
COSP_CN_AC	dec_par;		/* decoded accept params	*/

  /*----------------------------------*/
  /* decode and check the ACCEPT SPDU */
  /*----------------------------------*/

  ret = cosp_dec_cn_ac (&dec_par, spdu_buf, spdu_len, COSP_SI_ACCEPT);
  if (ret == SD_SUCCESS)
    {
    /* generate S-CONNECT.cnf+ */

    con->cosp_state = COSP_CSTATE_DATA_XFER;
    con->ppdu_len = dec_par.udata_len;
    con->ppdu_ptr = dec_par.udata_ptr;

    COSP_LOG_DEC2 ("S-CONNECT.cnf+:  acse_conn_id =0x%08X  user_conn_id =%4ld",
      con, con->user_conn_id);
    COSP_LOG_DECC1 ("User data length: %d", con->ppdu_len);
    COSP_LOG_DECH (con->ppdu_len, con->ppdu_ptr);

    u_cosp_con_cnf_pos (con);
    }
  else
    {
    COSP_LOG_ERR1 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Decoding of ACCEPT SPDU failed.",
      con);
    }

  return (ret);
}


/************************************************************************/
/*			cosp_process_refuse				*/
/*----------------------------------------------------------------------*/
/* This function will decode the REFUSE SPDU and call SS-user's function*/
/* u_cosp_con_cnf_neg to indicate to the SS-user that the establishment	*/
/* of connection failed.						*/
/* Note that this implementation will not reuse the transport connection*/
/* for another session connect (transport will be disconnected).	*/
/*									*/
/* Parameters:								*/
/*  ACSE_CONN    *con		Pointer to con info struct		*/
/*  ST_UINT	 spdu_len	Length of SPDU				*/
/*  char	*spdu_buf	Pointer to SPDU buffer			*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
static ST_RET cosp_process_refuse (ACSE_CONN *con, ST_UINT spdu_len,
     				    char *spdu_buf)
{
ST_INT	ret;
COSP_RF	dec_par;		/* decoded refuse connection params	*/
ST_INT result;
ST_INT reason;

  /*----------------------------------*/
  /* decode and check the REFUSE SPDU */
  /*----------------------------------*/

  ret = cosp_dec_rf (&dec_par, spdu_buf, spdu_len);
  if (ret == SD_SUCCESS)
    {
    con->cosp_state = COSP_CSTATE_IDLE;

    /* generate S-CONNECT.cnf- */

    if (dec_par.reason <= 2)
      result = COSP_CON_CNF_U_REJECT; 		/* remote SS-user reject */
    else
      result = COSP_CON_CNF_P_REJECT;		/* remote SS-provider reject */

    /* convert the reason code from SPDU to reason codes in cosp_usr.h */
    switch ((ST_UINT) dec_par.reason)
      {
      case 0:		/* rejection by called SS-user??, reason not specified	*/
        reason = COSP_CON_CNF_U_NOT_SPECIFIED;
      break;

      case 1:		/* rejection by called SS-user, tmp congestion	*/
        reason = COSP_CON_CNF_U_CONGESTION;
      break;

      case 2:		/* rejection by called SS-user, user data may follow */
        reason = COSP_CON_CNF_U_IN_UDATA;
        con->ppdu_len = dec_par.udata_len;
        con->ppdu_ptr = dec_par.udata_ptr;
      break;

      case 128+1:	/* rejection by called SS-prov, SSEL unknown	*/
        reason = COSP_CON_CNF_P_INV_SSEL;
      break;

      case 128+2:	/* rejection by called SS-prov, SS-user not attached */
        reason = COSP_CON_CNF_P_NOT_ATTACHED;
      break;

      case 128+3:	/* rejection by called SS-prov, congestion	*/
        reason = COSP_CON_CNF_P_CONGESTION;
      break;

      case 128+4:	/* rejection by called SS-prov, prot version not supported */
      case 128+5:	/* rejection by called SS-prov, reason not specified */
      case 128+6:	/* rejection by called SS-prov, implementation restriction stated in PICS */
      default:
        reason = COSP_CON_CNF_P_NOT_SPECIFIED;
      } /* end of switch */

    COSP_LOG_DEC4 ("S-CONNECT.cnf-:  acse_conn_id =0x%08X  user_conn_id =%4ld  result =%d  reason =%d",
      con, con->user_conn_id, result, reason);
    COSP_LOG_DECC1 ("User data length: %d", con->ppdu_len);
    COSP_LOG_DECH (con->ppdu_len, con->ppdu_ptr);

    /* S-CONNECT.cnf- */
    u_cosp_con_cnf_neg (con, result, reason);
    }
  else
    {
    COSP_LOG_ERR1 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Decoding of REFUSE SPDU failed.",
      con);
    }

  return (ret);
}


/************************************************************************/
/*			cosp_process_discon				*/
/*----------------------------------------------------------------------*/
/* This function will decode the DN SPDU and call SS-user's function	*/
/* u_cosp_rel_cnf_pos to indicate to the SS-user the result of session	*/
/* release request.							*/
/*									*/
/* Parameters:								*/
/*  ACSE_CONN    *con		Pointer to con info struct		*/
/*  ST_UINT	 spdu_len	Length of SPDU				*/
/*  char	*spdu_buf	Pointer to SPDU buffer			*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
static ST_RET cosp_process_discon (ACSE_CONN *con, ST_UINT spdu_len,
     				    char *spdu_buf)
{
ST_INT		ret;
COSP_FN_DN	dec_par;		/* decoded con rel params	*/

  /*------------------------------------*/
  /* decode and check the DN SPDU	*/
  /*------------------------------------*/

  ret = cosp_dec_fn_dn (&dec_par, spdu_buf, spdu_len, COSP_SI_DISCON);
  if (ret == SD_SUCCESS)
    {
    /* do not change here the state, new state depends on previous	*/
    /* state and if collision of S-RELEASEs occurred.			*/

    /* generate S-RELEASE.cnf+ */

    con->ppdu_len = dec_par.udata_len;
    con->ppdu_ptr = dec_par.udata_ptr;

    COSP_LOG_DEC2 ("S-RELEASE.cnf+:  acse_conn_id =0x%08X  user_conn_id =%4ld",
      con, con->user_conn_id);
    COSP_LOG_DECC1 ("User data length: %d", con->ppdu_len);
    COSP_LOG_DECH (con->ppdu_len, con->ppdu_ptr);

    u_cosp_rel_cnf_pos (con);	/* currently only positive cnf possible	*/
    }
  else
    {
    COSP_LOG_ERR1 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Decoding of DISCONNECT SPDU failed.",
      con);
    }

  return (ret);
}


/************************************************************************/
/*			cosp_process_abort				*/
/*----------------------------------------------------------------------*/
/* This function will decode the ABORT SPDU and call SS-user's function	*/
/* u_cosp_abort_ind to indicate to the SS-user that the session		*/
/* connection has been aborted by remote.				*/
/* Note that this implementation will not reuse the transport connection*/
/* for another session connect (transport will be disconnected).	*/
/*									*/
/* Parameters:								*/
/*  ACSE_CONN    *con		Pointer to con info struct		*/
/*  ST_UINT	 spdu_len	Length of SPDU				*/
/*  char	*spdu_buf	Pointer to SPDU buffer			*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
static ST_RET cosp_process_abort (ACSE_CONN *con, ST_UINT spdu_len,
			           char *spdu_buf)
{
ST_INT	ret;
COSP_AB	dec_par;		/* decoded abort connection params	*/
ST_INT reason;
ST_INT err_code;

  /*------------------------------------*/
  /* decode and check the ABORT SPDU	*/
  /*------------------------------------*/

  ret = cosp_dec_ab (&dec_par, spdu_buf, spdu_len);
  if (ret == SD_SUCCESS)
    {
    con->cosp_state = COSP_CSTATE_IDLE;

    /* generate S-U-ABORT.ind or S-P-ABORT.ind */

    if (dec_par.reason & COSP_AB_REASON_USER_ABORT)
      {
      /* remote SS-user abort */
      con->ppdu_len = dec_par.udata_len;
      con->ppdu_ptr = dec_par.udata_ptr;

      COSP_LOG_DEC2 ("S-U-ABORT.ind:   acse_conn_id =0x%08X  user_conn_id =%4ld",
        con, con->user_conn_id);
      COSP_LOG_DECC1 ("User data length: %d", con->ppdu_len);
      COSP_LOG_DECH (con->ppdu_len, con->ppdu_ptr);

      u_cosp_u_abort_ind (con);
      }
    else
      {
      /* SS-provider abort */
      if (dec_par.reason & COSP_AB_REASON_PROT_ERROR)
        {
        reason = COSP_P_AB_IND_PROT_ERR;
        err_code = 0;

        /* Log the ABORT error code */
        if (dec_par.reflect_par_len == 2)
          err_code = ((ST_UINT16) dec_par.reflect_par [0] << 8)
                    + (ST_UINT16) dec_par.reflect_par [1];

        COSP_LOG_DEC4 ("S-P-ABORT.ind:   acse_conn_id =0x%08X  user_conn_id =%4ld  reason =%d  err_code =0x%04X",
          con, con->user_conn_id, reason, err_code);
        }
      else
        {
        reason = COSP_P_AB_IND_UNDEFINED;

        COSP_LOG_DEC3 ("S-P-ABORT.ind:   acse_conn_id =0x%08X  user_conn_id =%4ld  reason =%d",
          con, con->user_conn_id, reason);
        }

      u_cosp_p_abort_ind (con, reason);
      }
    }
  else
    {
    COSP_LOG_ERR1 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Decoding of ABORT SPDU failed.",
      con);
    }

  return (ret);
}


/************************************************************************/
/*			cosp_process_data				*/
/*----------------------------------------------------------------------*/
/* This function will decode a DATA SPDU and generate a S-DATA.ind.	*/
/*									*/
/* Parameters:								*/
/*  ACSE_CONN    *con		Pointer to con info struct		*/
/*  ST_UINT	 spdu_len	Length of SPDU				*/
/*  char	*spdu_buf	Pointer to SPDU buffer			*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
static ST_RET cosp_process_data (ACSE_CONN *con, ST_UINT spdu_len,
				  char *spdu_buf)
{
ST_INT	ret;
COSP_DT	dec_par;		/* decoded refuse connection params	*/

  /*------------------------------------*/
  /* decode and check the DATA SPDU	*/
  /*------------------------------------*/
  ret = cosp_dec_dt (&dec_par, spdu_buf, spdu_len);
  if (ret == SD_SUCCESS)
    {
    /* generate S-DATA.ind */
    con->ppdu_len = dec_par.udata_len;
    con->ppdu_ptr = dec_par.udata_ptr;

    COSP_LOG_DEC2 ("S-DATA.ind:      acse_conn_id =0x%08X  user_conn_id =%4ld",
      con, con->user_conn_id);
    COSP_LOG_DECC1 ("User data length: %d", con->ppdu_len);
    COSP_LOG_DECH (con->ppdu_len, con->ppdu_ptr);

    u_cosp_data_ind (con);
    }
  else
    {
    COSP_LOG_ERR1 ("COSP-ERROR: T-DATA.ind acse_conn_id=0x%08X: Decoding of DATA SPDU failed.",
      con);
    }

  return (ret);
}



