/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,       */
/*	1999-2002, All Rights Reserved.					*/
/*                                                                      */
/*                  PROPRIETARY AND CONFIDENTIAL                        */
/*                                                                      */
/* MODULE NAME : goose.c                                                */
/* PRODUCT(S)  : ASN1DE                                                 */
/*                                                                      */
/* MODULE DESCRIPTION :                                                 */
/*                                                                      */
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :                            */
/*                                                                      */
/* MODIFICATION LOG :                                                   */
/*  Date     Who   Rev                  Comments                        */
/* --------  ---  ------   -------------------------------------------  */
/* 12/12/03  JRB     09    u_a_unit_data_ind: memset GOOSE_INFO struct	*/
/*			   to all 0 at start of decode.			*/
/* 03/31/03  JRB     08    asn1r_get_bitstr: add max_bits arg.		*/
/* 12/11/02  JRB     07    Del mvl_acse.h include.			*/
/* 03/05/02  JRB     06    Del unused proto. Add braces when initializing*/
/*			   ASN1R_TAG_CTRL_1 structs.			*/
/* 01/10/02  JRB     05    No longer need bind_id.			*/
/*			   Chg args to _goose_decode_mms.		*/
/*                         Add gse_uca_decode				*/
/* 07/25/00  RKR     04    Reconciled changes from BSD			*/
/* 12/10/99  JRB     03    Fix mmsl_send_goose & _goose_tx_audt_apdu	*/
/*			   to properly check return codes.		*/
/* 09/10/99  JRB     02    Use asn1r_get_u32 for decoding of SqNum,     */
/*                         StNum, HoldTim, & BackTim (encode is OK).    */
/* 06/25/99  MDE     01    Created                                      */
/************************************************************************/

#include "glbtypes.h"
#include "sysincs.h"
#include "asn1r.h"
#include "clnp_usr.h"
#include "clnp_sne.h"
#include "clnp.h"	/* for clnpl_decode	*/
#include "goose.h"

/* For loopback testing use this define */
/* #define GOOSE_TEST  */

/************************************************************************/
/************************************************************************/
/* Static functions */

static ST_RET _goose_tx_audt_apdu (GOOSE_INFO *gi, ST_UCHAR *goose_mms_pdu,
                                      ST_INT goose_mms_pdu_len);

static ST_RET _goose_encode_mms (ST_UCHAR *asn1_buf, ST_INT asn1_buf_size,
                   ST_UCHAR **goose_asn1_out, ST_INT *goose_asn1_len_out,
                   GOOSE_INFO *gi);

static ST_RET _goose_decode_mms (AUDT_APDU *audt_apdu, GOOSE_INFO *gi);

static ST_VOID _goose_unconf_req (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_info_rpt (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_list_of_var (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_seq_of_varspec (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_named (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_vmd_spec (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_named_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_seq_of_varspec_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_list_of_var_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_list_acc_rslt (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_struct (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_app_id (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_sent_time (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_seqnum (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_sub_seq (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_hold_time (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_back_time (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_phsid (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_dnabits (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_usrbits (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_struct_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_list_acc_rslt_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_info_rpt_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_unconf_req_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_done_ok (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_done_error (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_error (ASN1_DEC_CTXT *ac, ST_RET err);

/************************************************************************/
/************************************************************************/
/*                      mmsl_send_goose                                 */
/************************************************************************/

ST_RET mmsl_send_goose (GOOSE_INFO *gi)
  {
ST_UCHAR asn1_buf[GOOSE_MAX_PDU_SIZE];
ST_UCHAR *goose_mms_pdu;
ST_INT    goose_mms_pdu_len;
ST_RET rc;
#ifdef GOOSE_TEST
GOOSE_INFO goose_rx_info;
#endif

/* Encode the MMS PDU, given the user GOOSE information */
  rc = _goose_encode_mms (asn1_buf, GOOSE_MAX_PDU_SIZE,
                      &goose_mms_pdu, &goose_mms_pdu_len, gi);

/* Finish the a-unit data encoding and transmit the PDU */
  if (rc == SD_SUCCESS)
  rc = _goose_tx_audt_apdu (gi, goose_mms_pdu, goose_mms_pdu_len);

#ifdef GOOSE_TEST
/* For test, pretend we received it too! */
  memset(&goose_rx_info, 0, sizeof (GOOSE_INFO));
  memcpy(&goose_rx_info.audtApdu, &gi->audtApdu, sizeof (AUDT_APDU));

  rc = _goose_decode_mms (&gi->audtApdu, &goose_rx_info);
  if (rc != 0)
    printf ("\n Whooops!");
  rc = memcmp (gi, &goose_rx_info, sizeof (GOOSE_INFO));
  if (rc != 0)
    printf ("\n Whoa!");
  else
    u_mmsl_goose_received (&goose_rx_info);
#endif

  return (rc);
  }

/************************************************************************/
/*                      _goose_encode_mms                               */
/************************************************************************/

static ST_RET _goose_encode_mms (ST_UCHAR *asn1_buf, ST_INT asn1_buf_size,
                   ST_UCHAR **goose_mms_pdu_out, ST_INT *goose_mms_pdu_len_out,
                   GOOSE_INFO *gi)
  {
ASN1_ENC_CTXT ac;
ST_UCHAR *pdu_start;     /* 000725 BSD - Changed from ST_CHAR* to ST_UCHAR* */
ST_INT pdu_len;

  asn1r_strt_asn1_bld (&ac, asn1_buf, asn1_buf_size);
  asn1r_strt_constr (&ac);         /* unconfirmed requests               */
  asn1r_strt_constr (&ac);         /* information report body            */
  asn1r_strt_constr (&ac);         /* access result                      */
  asn1r_strt_constr (&ac);         /* GOOSE data is a structure          */

  asn1r_wr_bitstr (&ac, gi->UserSt, gi->num_usr_bits);
  asn1r_fin_prim (&ac, 4, CTX);
  asn1r_wr_bitstr (&ac, gi->DNA, gi->num_dna_bits);
  asn1r_fin_prim (&ac, 4, CTX);
  asn1r_wr_u16 (&ac, gi->PhsID);
  asn1r_fin_prim (&ac, 6, CTX);
  asn1r_wr_u32 (&ac, gi->BackTim);
  asn1r_fin_prim (&ac, 6, CTX);
  asn1r_wr_u32 (&ac, gi->HoldTim);
  asn1r_fin_prim (&ac, 6, CTX);
  asn1r_wr_u32 (&ac, gi->StNum);
  asn1r_fin_prim (&ac, 6, CTX);
  asn1r_wr_u32 (&ac, gi->SqNum);
  asn1r_fin_prim (&ac, 6, CTX);
  asn1r_wr_btod (&ac, &gi->t);
  asn1r_fin_prim (&ac, 12, CTX);
  asn1r_wr_vstr (&ac, gi->SendingIED);  
  asn1r_fin_prim (&ac, 10, CTX);
  asn1r_fin_constr (&ac, 2, CTX, DEF);  /* end of GOOSE data struct     */
  asn1r_fin_constr (&ac, 0, CTX, DEF);     /* access result being finished */

  asn1r_strt_constr (&ac);
  asn1r_strt_constr (&ac);                 /* variable access specification        */
  asn1r_strt_constr (&ac);                 /* object name start                   */
  asn1r_wr_vstr (&ac, "GOOSE");
  asn1r_fin_prim (&ac, 0, CTX);              /* VMD Specific Name                    */
  asn1r_fin_constr (&ac, 0, CTX, DEF);
  asn1r_fin_constr (&ac, SEQ_CODE, UNI, DEF);/* end variable access specification */
  asn1r_fin_constr (&ac, 0, CTX, DEF);       /* end of information report body       */
  asn1r_fin_constr (&ac, 0, CTX, DEF);
  asn1r_fin_constr (&ac, 3, CTX, DEF);       /* end of unconfirmed service           */

  if (ac.asn1r_encode_overrun)          /* Check for encode overrun     */
    return (SD_FAILURE);

  pdu_start = ac.asn1r_field_ptr + 1;
  pdu_len = (asn1_buf + asn1_buf_size) - pdu_start;

  *goose_mms_pdu_out = pdu_start;
  *goose_mms_pdu_len_out = pdu_len;
  return (SD_SUCCESS);
  }

/************************************************************************/
/*                      _goose_tx_audt_apdu                             */
/************************************************************************/

static ST_RET _goose_tx_audt_apdu (GOOSE_INFO *gi, ST_UCHAR *goose_mms_pdu,
                                      ST_INT goose_mms_pdu_len)
  {
ST_RET ret;
  gi->audtApdu.user_info.ptr = goose_mms_pdu;
  gi->audtApdu.user_info.len = goose_mms_pdu_len;
  ret = a_unit_data_req (&gi->audtApdu);
  return (ret);
  }


/************************************************************************/
/************************************************************************/
/*                      u_a_unit_data_ind                               */
/* This "user" function processes the A-UNIT-DATA.ind. It is customized */
/* to handle only GOOSE packets. It decodes the GOOSE packet, and then  */
/* passes the decoded info to the user by calling "u_mmsl_goose_received.*/
/* If the packet is not a GOOSE, or it can't be decoded, it is ignored. */
/************************************************************************/
ST_VOID u_a_unit_data_ind (AUDT_APDU *audt_apdu)
  {
ST_RET rc;
GOOSE_INFO goose_info;  /* for u_a_unit_data_ind                        */

  /* Start with clean structure.	*/
  memset (&goose_info, 0, sizeof (goose_info));

  /* Decode GOOSE PDU.		*/
  rc = _goose_decode_mms (audt_apdu, &goose_info);

/* Tell the user about the received GOOSE message */
  if (rc == SD_SUCCESS)
    u_mmsl_goose_received (&goose_info);
  else
    {
 /*   SLOGALWAYS0 ("A-UNIT-DATA.ind is not a GOOSE. Ignoring it."); */
    }
  }

/************************************************************************/
/*                      _goose_decode_mms                               */
/************************************************************************/


static ST_RET _goose_decode_mms (AUDT_APDU *audt_apdu, GOOSE_INFO *gi)
  {
ASN1_DEC_CTXT asn1_ctxt;
ASN1_DEC_CTXT *ac;
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX | CONSTR, 3), _goose_unconf_req}}};

  /* Copy AUDT struct to goose_info so user can access all addressing info.*/
  memcpy (&gi->audtApdu, audt_apdu, sizeof (AUDT_APDU));

  ac = &asn1_ctxt;
  memset (ac, 0, sizeof (ASN1_DEC_CTXT));
  ac->usr_info[0] = gi;
  ac->asn1r_err_fun = _goose_error;
  ac->asn1r_decode_done_fun = _goose_done_error;
  ac->asn1r_decode_method = ASN1_TABLE_METHOD;
  ac->asn1r_tag_table = &asn1r_tags;
  asn1r_decode_asn1 (ac, audt_apdu->user_info.ptr, audt_apdu->user_info.len);
  if (ac->asn1r_pdu_dec_err != NO_DECODE_ERR)
    return (SD_FAILURE);

  return (SD_SUCCESS);
  }

/************************************************************************/
/*                      _goose_unconf_req                               */
/************************************************************************/

static ST_VOID _goose_unconf_req (ASN1_DEC_CTXT *ac)
  {
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL(CTX | CONSTR, 0), _goose_info_rpt}}};

  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_info_rpt                                 */
/************************************************************************/

static ST_VOID _goose_info_rpt (ASN1_DEC_CTXT *ac)
  {
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX | CONSTR, 0), _goose_list_of_var}}};

  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_list_of_var                              */
/************************************************************************/

static ST_VOID _goose_list_of_var (ASN1_DEC_CTXT *ac)
  {
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (UNI | CONSTR, SEQ_CODE), _goose_seq_of_varspec}}};

  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_seq_of_varspec                           */
/************************************************************************/

static ST_VOID _goose_seq_of_varspec (ASN1_DEC_CTXT *ac)
  {
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX | CONSTR, 0), _goose_named}}};

  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_named                                    */
/************************************************************************/

static ST_VOID _goose_named (ASN1_DEC_CTXT *ac)
  {
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX, 0), _goose_vmd_spec}}};

  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_vmd_spec                                 */
/************************************************************************/

static ST_VOID _goose_vmd_spec (ASN1_DEC_CTXT *ac)
  {
ST_CHAR *data_ptr;

  data_ptr = (ST_CHAR *) ac->asn1r_field_ptr-1;
  asn1r_get_vstr (ac, data_ptr);
  if (strcmp (data_ptr, "GOOSE") != 0)
    asn1r_set_dec_err (ac, GOOSE_NAME_MISMATCH);
  ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_named_done;
  }

/************************************************************************/
/*                      _goose_named_done                               */
/************************************************************************/

static ST_VOID _goose_named_done (ASN1_DEC_CTXT *ac)
  {
  ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_seq_of_varspec_done;
  }

/************************************************************************/
/*                      _goose_seq_of_varspec_done                      */
/************************************************************************/

static ST_VOID _goose_seq_of_varspec_done (ASN1_DEC_CTXT *ac)
  {
  ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_list_of_var_done;
  }

/************************************************************************/
/*                      _goose_list_of_var_done                         */
/************************************************************************/

static ST_VOID _goose_list_of_var_done (ASN1_DEC_CTXT *ac)
  {
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX | CONSTR, 0), _goose_list_acc_rslt}}};

  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_list_acc_rslt                            */
/************************************************************************/

static ST_VOID _goose_list_acc_rslt (ASN1_DEC_CTXT *ac)
  {
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX | CONSTR, 2), _goose_struct}}};

  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_struct                                   */
/************************************************************************/

static ST_VOID _goose_struct (ASN1_DEC_CTXT *ac)
  {
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX, 10), _goose_get_app_id}}};

  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_get_app_id                               */
/************************************************************************/

static ST_VOID _goose_get_app_id (ASN1_DEC_CTXT *ac)
  {
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX, 12), _goose_get_sent_time}}};

  _goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
                        
  asn1r_get_vstr (ac, _goose_dec_info->SendingIED);
  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_get_sent_time                            */
/************************************************************************/

static ST_VOID _goose_get_sent_time (ASN1_DEC_CTXT *ac)
  {
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX, 6), _goose_get_seqnum}}};

  _goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
  asn1r_get_btod (ac, &_goose_dec_info->t);
  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_get_seqnum                               */
/************************************************************************/

static ST_VOID _goose_get_seqnum (ASN1_DEC_CTXT *ac)
  {
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX, 6), _goose_get_sub_seq}}};

  _goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
  asn1r_get_u32 (ac, &_goose_dec_info->SqNum);
  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_get_sub_seq                              */
/************************************************************************/

static ST_VOID _goose_get_sub_seq (ASN1_DEC_CTXT *ac)
  {
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX, 6), _goose_get_hold_time}}};

  _goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
  asn1r_get_u32 (ac, &_goose_dec_info->StNum);
  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_get_hold_time                            */
/************************************************************************/

static ST_VOID _goose_get_hold_time (ASN1_DEC_CTXT *ac)
  {
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX, 6), _goose_get_back_time}}};

  _goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
  asn1r_get_u32 (ac, &_goose_dec_info->HoldTim);
  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_get_back_time                            */
/************************************************************************/

static ST_VOID _goose_get_back_time (ASN1_DEC_CTXT *ac)
  {
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX, 6), _goose_get_phsid}}};

  _goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
  asn1r_get_u32 (ac, &_goose_dec_info->BackTim);
  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_get_phsid                                */
/************************************************************************/

static ST_VOID _goose_get_phsid (ASN1_DEC_CTXT *ac)
  {
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX, 4), _goose_get_dnabits}}};

  _goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
  asn1r_get_u16 (ac, &_goose_dec_info->PhsID);
  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_get_dnabits                              */
/************************************************************************/

static ST_VOID _goose_get_dnabits (ASN1_DEC_CTXT *ac)
  {
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
  {1, {{ASN1R_TAG_VAL (CTX, 4), _goose_get_usrbits}}};

  _goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
  asn1r_get_bitstr (ac, _goose_dec_info->DNA, GOOSE_MAX_NUM_DNA_BITS);
  _goose_dec_info->num_dna_bits = ac->asn1r_bitcount;
  ac->asn1r_tag_table = &asn1r_tags;
  }

/************************************************************************/
/*                      _goose_get_usrbits                              */
/************************************************************************/

static ST_VOID _goose_get_usrbits (ASN1_DEC_CTXT *ac)
  {
GOOSE_INFO *_goose_dec_info;

  _goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];

  asn1r_get_bitstr (ac, _goose_dec_info->UserSt, GOOSE_MAX_NUM_USR_BITS);
  _goose_dec_info->num_usr_bits = ac->asn1r_bitcount;
  ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_struct_done;
  }

/************************************************************************/
/*                      _goose_struct_done                              */
/************************************************************************/

static ST_VOID _goose_struct_done (ASN1_DEC_CTXT *ac)
  {
  ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_list_acc_rslt_done;
  }

/************************************************************************/
/*                      _goose_list_acc_rslt_done                       */
/************************************************************************/

static ST_VOID _goose_list_acc_rslt_done (ASN1_DEC_CTXT *ac)
  {
  ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_info_rpt_done;
  }

/************************************************************************/
/*                      _goose_info_rpt_done                            */
/************************************************************************/

static ST_VOID _goose_info_rpt_done (ASN1_DEC_CTXT *ac)
  {
  ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_unconf_req_done;
  }

/************************************************************************/
/*                      _goose_unconf_req_done                          */
/************************************************************************/

static ST_VOID _goose_unconf_req_done (ASN1_DEC_CTXT *ac)
  {
  ac->asn1r_decode_done_fun = _goose_done_ok;
  ac->asn1r_decode_done = SD_TRUE;
  }

/************************************************************************/
/*                      _goose_done_ok                                  */
/************************************************************************/

static ST_VOID _goose_done_ok (ASN1_DEC_CTXT *ac)
  {
  }

/************************************************************************/
/*                      _goose_done_error                                       */
/************************************************************************/

static ST_VOID _goose_done_error (ASN1_DEC_CTXT *ac)
  {
  asn1r_set_dec_err (ac, GOOSE_DONE_TOO_SOON);
  }

/************************************************************************/
/*                      _goose_error                                    */
/************************************************************************/

static ST_VOID _goose_error (ASN1_DEC_CTXT *ac, ST_RET err)
  {
  }

/************************************************************************/
/*                      gse_uca_decode					*/
/* This function decodes the subnet packet (sn_udt), and puts the	*/
/* results in the user structure (goose_info).				*/
/* First it decodes from SN_UNITDATA to N_UNITDATA.			*/
/* Then it decodes from N_UNITDATA to AUDT_APDU.			*/
/* Then it decodes from AUDT_APDU to GOOSE_INFO.			*/
/************************************************************************/
ST_RET gse_uca_decode (SN_UNITDATA *sn_udt,	/* input subnet packet	*/
		GOOSE_INFO *goose_info)		/* output GOOSE data	*/
  {
ST_RET rc;
N_UNITDATA *pNudt = NULL;	/* intermediate decode result	*/
AUDT_APDU audt;			/* intermediate decode result	*/

  /* Decode from SN_UNITDATA to N_UNITDATA.	*/
  /* clnpl_decode allocs a N_UNITDATA structure if this is a N-Unitdata	*/
  /* packet, and changes the value of pNudt to point to it.		*/
  if ((rc = clnpl_decode (sn_udt, &pNudt)) != SD_SUCCESS)
    return (rc);
  if (pNudt==NULL)
    return (SD_FAILURE);	/* does this ever happen?		*/

  /* Decode from N_UNITDATA to AUDT_APDU.	*/
  if ((rc = cltp_decode_nsdu_2 (pNudt, &audt)) != SD_SUCCESS)
    return (rc);
  /* CRITICAL: DON't free N_UNITDATA here, because AUDT_APDU has pointer into it.	*/

  /* Decode from AUDT_APDU to GOOSE_INFO.	*/
  rc = _goose_decode_mms (&audt, goose_info);

  /* Now it is safe to free N_UNITDATA. All data in GOOSE_INFO now.	*/
  clnp_free (pNudt);
  return (rc);
  }

