view src/g23m-aci/aci/psa_ccs.c @ 303:f76436d19a7a default tip

!GPRS config: fix long-standing AT+COPS chance hanging bug There has been a long-standing bug in FreeCalypso going back years: sometimes in the AT command bring-up sequence of an ACI-only MS, the AT+COPS command would produce only a power scan followed by cessation of protocol stack activity (only L1 ADC traces), instead of the expected network search sequence. This behaviour was seen in different FC firmware versions going back to Citrine, and seemed to follow some law of chance, not reliably repeatable. This bug has been tracked down and found to be specific to !GPRS configuration, stemming from our TCS2/TCS3 hybrid and reconstruction of !GPRS support that was bitrotten in TCS3.2/LoCosto version. ACI module psa_mms.c, needed only for !GPRS, was missing in the TCS3 version and had to be pulled from TCS2 - but as it turns out, there is a new field in the MMR_REG_REQ primitive that needs to be set correctly, and that psa_mms.c module is the place where this initialization needed to be added.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 08 Jun 2023 08:23:37 +0000
parents fa8dc04885d8
children
line wrap: on
line source

/* 
+----------------------------------------------------------------------------- 
|  Project :  GSM-PS (6147)
|  Modul   :  PSA_CCS
+----------------------------------------------------------------------------- 
|  Copyright 2002 Texas Instruments Berlin, AG 
|                 All rights reserved. 
| 
|                 This file is confidential and a trade secret of Texas 
|                 Instruments Berlin, AG 
|                 The receipt of or possession of this file does not convey 
|                 any rights to reproduce or disclose its contents or to 
|                 manufacture, use, or sell anything it may describe, in 
|                 whole, or in part, without the specific written consent of 
|                 Texas Instruments Berlin, AG. 
+----------------------------------------------------------------------------- 
|  Purpose :  This module defines the signalling functions of the 
|             protocol stack adapter for call control.
+----------------------------------------------------------------------------- 
*/ 

#ifndef PSA_CCS_C
#define PSA_CCS_C
#endif

#include "aci_all.h"

/*==== INCLUDES ===================================================*/
#include "ccdapi.h"

#include "aci_cmh.h"
#include "ati_cmd.h"
#include "aci_cmd.h"
#include "aci.h"
#include "psa.h"
#include "psa_cc.h"
#include "psa_ss.h"
#include "aoc.h"
#include "l4_tim.h"

#if !defined (MFW)
#include "aci_io.h"
#endif

#include "aci_fd.h"
#include "cmh.h"

#include "cmh_cc.h"
#include "psa_cc.h"

#include "wap_aci.h"
#include "aci_mem.h"
/*==== CONSTANTS ==================================================*/

/*==== TYPES ======================================================*/

/*==== IMPORT =====================================================*/

/*==== VARIABLES ==================================================*/

/*==== FUNCTIONS ==================================================*/


/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)    MODULE  : PSA_CCS                      |
|                            ROUTINE : psaCC_NewCall                |
+-------------------------------------------------------------------+

  PURPOSE : setup a new call

*/

GLOBAL SHORT psaCC_NewCall ( SHORT cId )
{
  T_CC_CALL_TBL *pCtbNtry = ccShrdPrm.ctb[cId];

  TRACE_FUNCTION ("psaCC_NewCall()");

  if (pCtbNtry EQ NULL)
    return -1;

  /* get a valid ti for a MOC, otherwise return */
  if (psaCC_ctb(cId)->calType NEQ CT_NI_MOC )
  {
    if( psaCC_getMOCTi( cId ) < 0 ) return ( -1 );
  }

#if defined (FF_WAP) || defined (FF_GPF_TCPIP) || defined (FF_SAT_E)
  /* if this call is a WAP call then set wapId to the current Call Id */
  if (Wap_Call EQ TRUE)
  {
    wapId = cId;
  }
#endif /* defined(WAP) OR defined(FF_GPF_TCPIP) */

/*
 *-------------------------------------------------------------------
 * create and send primitive
 *-------------------------------------------------------------------
 */
  {
    PALLOC (mncc_setup_req, MNCC_SETUP_REQ);

    /*
     * fill in primitive parameter: setup request
     */
    mncc_setup_req -> ti               = pCtbNtry -> ti;
    mncc_setup_req -> prio             = pCtbNtry -> prio;
    mncc_setup_req -> ri               = pCtbNtry -> rptInd;

    mncc_setup_req->called_party.ton          = pCtbNtry->cldPty.ton;
    mncc_setup_req->called_party.npi          = pCtbNtry->cldPty.npi;
    mncc_setup_req->called_party.c_called_num = pCtbNtry->cldPty.c_called_num;
    memcpy (mncc_setup_req->called_party.called_num, 
            pCtbNtry->cldPty.called_num,
            pCtbNtry->cldPty.c_called_num);

    mncc_setup_req -> called_party_sub = pCtbNtry -> cldPtySub;
    mncc_setup_req -> clir_sup         = pCtbNtry -> CLIRsup;
    memcpy( &(mncc_setup_req->bcpara),&(pCtbNtry->BC[0]), 
          sizeof( T_MNCC_bcpara) ); 
    memcpy( &(mncc_setup_req->bcpara2),&(pCtbNtry->BC[1]), 
          sizeof( T_MNCC_bcpara) ); 

    /*
     * fill in CUG info facility
     */
    if( !(pCtbNtry -> CUGidx EQ NOT_PRESENT_8BIT AND
          pCtbNtry -> CUGprf EQ FALSE AND
          pCtbNtry -> OAsup  EQ FALSE    ))
    {
      CCD_START;
      psaCC_asmCUGInfo( cId );

      {
        UBYTE ccdRet;

        MCAST( com, COMPONENT );

        memset( com, 0, sizeof( T_COMPONENT ));

        com -> v_inv_comp = TRUE;
        com -> inv_comp.v_inv_id  = TRUE;
        com -> inv_comp.inv_id    = pCtbNtry -> iId 
                                  = ccShrdPrm.iIdNxt++;
        com -> inv_comp.v_op_code = TRUE;
        com -> inv_comp.op_code   = pCtbNtry -> opCode
                                  = ssFIECodeBuf.buf[0];
        com -> inv_comp.v_params  = TRUE;
        com -> inv_comp.params.l_params = ssFIECodeBuf.l_buf-8;
        com -> inv_comp.params.o_params = 8;
        memcpy( com -> inv_comp.params.b_params, 
                ssFIECodeBuf.buf, ssFIECodeBuf.l_buf );

        mncc_setup_req -> fac_inf.l_fac = MNCC_FACILITY_LEN<<3;
        mncc_setup_req -> fac_inf.o_fac = 0;
        ccdRet = ccd_codeMsg (CCDENT_FAC,
                              UPLINK,
                              (T_MSGBUF *) &mncc_setup_req -> fac_inf,
                              (UBYTE    *) _decodedMsg,
                              COMPONENT);

        if( ccdRet NEQ ccdOK )
        {
          TRACE_EVENT_P1("CCD Coding Error: %d",ccdRet ); 
          memset( &mncc_setup_req -> fac_inf, 0, sizeof( T_MNCC_fac_inf));
        }
      }
      CCD_END;
    }
    else
    {
      memset( &mncc_setup_req -> fac_inf, 0, sizeof( T_MNCC_fac_inf));
    }

    if(pCtbNtry -> calType NEQ CT_MOC_RDL)
    {
      pCtbNtry -> calType = CT_MOC;
    }
    pCtbNtry -> curBC   = 0;

    PSENDX (CC, mncc_setup_req);

    /*
     * update call status
     */
    pCtbNtry -> calStat = CS_ACT_REQ;
    /* Inform that a call is initiated */
    psaCC_chngCalTypCnt( cId, +1 );   
  }
#ifdef FF_TTY
  /* TTY notification */
  cmhCC_notifyTTY (((pCtbNtry->BC[0].bearer_serv EQ MNCC_BEARER_SERV_SPEECH_CTM OR
                     pCtbNtry->BC[0].bearer_serv EQ MNCC_BEARER_SERV_AUX_SPEECH_CTM)?
                    CTTY_NEG_Request: CTTY_NEG_None),
                   CTTY_TRX_Unknown);
#endif /* FF_TTY */
  if(cmhCC_atdsendok ( cId ))
  {
    R_AT( RAT_OK, (T_ACI_CMD_SRC)pCtbNtry->curSrc ) ( pCtbNtry->curCmd );
  }

  /* start call time measurement */
  aoc_info (cId, AOC_START_TIME);

  /* Disable voice path in case this is first call */
  psaCC_setSpeechMode ();

  return 0;
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_AcceptCall        |
+-------------------------------------------------------------------+
  PURPOSE : In addition to sending the setup response for an MT call
            this function enables the vocoder if the call is a voice
            call
*/

GLOBAL void psaCC_AcceptCall ( SHORT cId )
{
  T_CC_CALL_TBL * pCtbNtry;     /* holds pointer to call table entry */

  TRACE_FUNCTION ("psaCC_AcceptCall()");

  /* update call status and attach the user if not done previously */
  pCtbNtry = ccShrdPrm.ctb[cId];
  pCtbNtry -> calStat = CS_CPL_REQ;

  
  psaCC_setSpeechMode();
  
  /* create and send primitive */
  {
    PALLOC (mncc_setup_res, MNCC_SETUP_RES);
    mncc_setup_res -> ti = pCtbNtry -> ti;
    PSENDX (CC, mncc_setup_res);
    psaCC_send_satevent( EVENT_CALL_CONN, cId, NEAR_END, FALSE );
  }

  /* start call time measurement */
  aoc_info (cId, AOC_START_TIME);
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_ClearCall         |
+-------------------------------------------------------------------+

  PURPOSE : clear a call
*/

GLOBAL void psaCC_ClearCall ( SHORT cId )
{
  T_CC_CALL_TBL *pCtbNtry = ccShrdPrm.ctb[cId];

  TRACE_FUNCTION ("psaCC_ClearCall()");

  if (!psaCC_ctbIsValid (cId))
    return; /* Ensure we are not dereferencing NULL */

  psaCC_StopDTMF ( cId );

  /* inform advice of charge module */
  aoc_info (cId, AOC_STOP_TIME);

  /*
   *-----------------------------------------------------------------
   * relase call if in disconnect request state
   *-----------------------------------------------------------------
   */
  if( pCtbNtry -> calStat EQ CS_DSC_REQ )
  {
    PALLOC (mncc_release_req, MNCC_RELEASE_REQ);

    /* fill in primitive parameter: release request */
    mncc_release_req -> ti    = pCtbNtry -> ti;
    mncc_release_req -> cause = pCtbNtry -> nrmCs;
      
    /* fill in CCBS request facility */
    if( pCtbNtry -> CCBSstat EQ CCBSS_REQ )
    {
      CCD_START;
      {
        psaCC_asmCCBSReq( cId );
        psaCC_asmComponent( cId );
      }
      CCD_END;
      
      mncc_release_req -> fac_inf.l_fac = ssFIECodeBuf.l_buf;
      mncc_release_req -> fac_inf.o_fac = 0;
      memcpy (mncc_release_req->fac_inf.fac, 
              ssFIECodeBuf.buf + (ssFIECodeBuf.o_buf >> 3),
              ssFIECodeBuf.l_buf >> 3);

      mncc_release_req -> ss_version = MNCC_SS_VERSION_3;
    }
    else
    {
      memset( &mncc_release_req -> fac_inf, 0, sizeof( T_MNCC_fac_inf));
      mncc_release_req -> ss_version = MNCC_SS_VER_NOT_PRES;
    }

    PSENDX (CC, mncc_release_req);

    psaCC_send_satevent( EVENT_CALL_DISC, cId, NEAR_END, TRUE );
  }

  /*
   *-----------------------------------------------------------------
   * relase call if in activate request state, network initiated
   *-----------------------------------------------------------------
   */
  else if( pCtbNtry -> calStat EQ CS_ACT_REQ AND
           pCtbNtry -> calType EQ CT_NI_MOC      )
  {
    PALLOC (mncc_reject_req, MNCC_REJECT_REQ);

    /* fill in primitive parameter: release request */
    mncc_reject_req -> ti    = pCtbNtry -> ti;
    mncc_reject_req -> cause = pCtbNtry -> nrmCs;

    PSENDX (CC, mncc_reject_req);

    /* free call table entry */
    psaCC_retMOCTi( pCtbNtry -> ti );

    psaCC_FreeCtbNtry (cId);
  }
      
  /*
   *-----------------------------------------------------------------
   * disconnect all other calls
   *-----------------------------------------------------------------
   */
  else
  {
    PALLOC (disc_req, MNCC_DISCONNECT_REQ); /* T_MNCC_DISCONNECT_REQ */
    disc_req -> ti    = pCtbNtry -> ti;
    disc_req -> cause = pCtbNtry -> nrmCs;
    if (pCtbNtry -> curCmd EQ AT_CMD_CTFR)
    {
      /* We expect here an already built facility in ssFIECodeBuf */
      disc_req -> fac_inf.l_fac = ssFIECodeBuf.l_buf;
      disc_req -> fac_inf.o_fac = 0;
      memcpy (disc_req->fac_inf.fac, 
              ssFIECodeBuf.buf, 
              ssFIECodeBuf.l_buf >> 3);
      disc_req -> ss_version = MNCC_SS_VERSION_2;
    }
    else
    {
      disc_req -> fac_inf.l_fac = 0;
      disc_req -> ss_version    = MNCC_SS_VER_NOT_PRES;
    }
    PSENDX (CC, disc_req);

    /* update call status */
    pCtbNtry -> calStat = CS_DSC_REQ;

//TISH, hot fix for GCF 27.22.7.3,  added in 2007-03-23
//start 38.patch for GCF test case:	27.22.7.3
	if (pCtbNtry -> nrmCs EQ 0x510)
	{
	pCtbNtry->numRawCauseBytes=2;
	ACI_MALLOC(pCtbNtry -> rawCauseBytes,2);
	pCtbNtry -> rawCauseBytes[0]=0xe0;
	pCtbNtry -> rawCauseBytes[1]=0x90;
	}
//end


    /* monitoring for SAT */
    psaCC_send_satevent( EVENT_CALL_DISC, cId , NEAR_END, TRUE );

//TISH, hot fix for GCF 27.22.7.3,  added in 2007-03-23
//start 38.patch for GCF test case:	27.22.7.3
	if (pCtbNtry -> nrmCs EQ 0x510)
	{
	pCtbNtry->numRawCauseBytes=0;
	ACI_MFREE(pCtbNtry -> rawCauseBytes);
	}	
//end

  }
  /* Switch off audio path after user action (e.g. hangup) */
/* clb this breaks ACI056 (among others..) necessary ??  pCtbNtry -> inBndTns = FALSE; */
  pCtbNtry -> inBndTns = FALSE;
  psaCC_setSpeechMode ();
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_HoldCall          |
+-------------------------------------------------------------------+

  PURPOSE : put a call into hold state

*/

GLOBAL void psaCC_HoldCall ( SHORT cId )
{
  T_CC_CALL_TBL *pCtbNtry = ccShrdPrm.ctb[cId];

  TRACE_FUNCTION ("psaCC_HoldCall()");

  if (pCtbNtry EQ NULL)
    return; /* Ensure not to dereference NULL */

/*
 *-------------------------------------------------------------------
 * create and send primitive
 *-------------------------------------------------------------------
 */
  psaCC_StopDTMF ( cId );

  {
    PALLOC (mncc_hold_req, MNCC_HOLD_REQ);

    /*
     * fill in primitive parameter: hold request
     */

    mncc_hold_req -> ti = pCtbNtry -> ti;
      
    PSENDX (CC, mncc_hold_req);

    /* update call status */
    pCtbNtry -> calStat = CS_HLD_REQ;

  }
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_RetrieveCall      |
+-------------------------------------------------------------------+

  PURPOSE : retrieve a held call

*/

GLOBAL void psaCC_RetrieveCall ( SHORT cId )
{
  T_CC_CALL_TBL *pCtbNtry = ccShrdPrm.ctb[cId];

  TRACE_FUNCTION ("psaCC_RetrieveCall()");

/*
 *-------------------------------------------------------------------
 * create and send primitive
 *-------------------------------------------------------------------
 */
  {
    PALLOC (mncc_retrieve_req, MNCC_RETRIEVE_REQ);

    /*
     * fill in primitive parameter: retrieve request
     */

    mncc_retrieve_req -> ti = pCtbNtry -> ti;
      
    PSENDX (CC, mncc_retrieve_req);

    /*
     * update call status   ???
     */
  }
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_ModifyCall        |
+-------------------------------------------------------------------+

  PURPOSE : modify an active call 

*/

GLOBAL SHORT psaCC_ModifyCall ( SHORT cId )
{
  T_CC_CALL_TBL *pCtbNtry = ccShrdPrm.ctb[cId];
  UBYTE nomBC;                  /* holds nominal bearer capabilities */

  TRACE_FUNCTION ("psaCC_ModifyCall()");

  if (pCtbNtry EQ NULL)
    return -1;

/*
 *-------------------------------------------------------------------
 * check if call is in a active state
 *-------------------------------------------------------------------
 */
 if(
     (pCtbNtry -> calStat NEQ CS_ACT)     AND
     (pCtbNtry -> calStat NEQ CS_ACT_REQ)
   )
   return( -1 );
/*
 *-------------------------------------------------------------------
 * create and send primitive
 *-------------------------------------------------------------------
 */
  psaCC_StopDTMF ( cId );
  {
    PALLOC (mncc_modify_req, MNCC_MODIFY_REQ);

    /*
     * fill in primitive parameter: modify request
     */
    nomBC = (pCtbNtry -> curBC)?0:1;

    switch( pCtbNtry -> BC[nomBC].bearer_serv )
    {
    case( MNCC_BEARER_SERV_AUX_SPEECH    ):
    case( MNCC_BEARER_SERV_SPEECH        ):
    case( MNCC_BEARER_SERV_AUX_SPEECH_CTM):
    case( MNCC_BEARER_SERV_SPEECH_CTM    ):
      mncc_modify_req -> serv = MNCC_SERV_SPEECH;
      break;

    case( MNCC_BEARER_SERV_FAX           ):
    case( MNCC_BEARER_SERV_ASYNC         ):
    case( MNCC_BEARER_SERV_SYNC          ):
    case( MNCC_BEARER_SERV_PAD_ACCESS    ):
    case( MNCC_BEARER_SERV_PACKET_ACCESS ):
      mncc_modify_req -> serv = MNCC_SERV_DATA;

      /* check for TTY service */
      cmhCC_TTY_Control ( cId, TTY_PAUSE );
      break;

    default:
      mncc_modify_req -> serv = MNCC_SERV_NOT_PRES;
    }
    mncc_modify_req ->   ti = pCtbNtry -> ti;

    PSENDX (CC, mncc_modify_req);

    /*
     * update call status
     */
    pCtbNtry -> calStat = CS_MDF_REQ;
  }
  return 0;
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_SendDTMF          |
+-------------------------------------------------------------------+

  PURPOSE : send DTMF tones

*/

GLOBAL SHORT psaCC_SendDTMF ( SHORT cId,
                              UBYTE digit,
                              UBYTE mode )
{

  TRACE_FUNCTION ("psaCC_SendDTMF()");

  if (ccShrdPrm.ctb[cId] EQ NULL)
    return -1; /* Ensure not to dereference NULL */

/*
 *-------------------------------------------------------------------
 * create and send primitive
 *-------------------------------------------------------------------
 */
  {
    PALLOC (mncc_start_dtmf_req, MNCC_START_DTMF_REQ);

    /*
     * fill in primitive parameter: DTMF tone request
     */

    mncc_start_dtmf_req -> ti       = psaCC_ctb(cId)->ti;
    mncc_start_dtmf_req -> key      = digit;
    mncc_start_dtmf_req -> dtmf_mod = mode;
      
    PSENDX (CC, mncc_start_dtmf_req);
  }

  return 0;
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_Config            |
+-------------------------------------------------------------------+
  PURPOSE : configure CC entity
*/

GLOBAL void psaCC_Config ( void )
{
  T_MNCC_bcpara *mtcBC;

  TRACE_FUNCTION ("psaCC_Config()");

  /* create and send primitive */
  {
    PALLOC (mncc_configure_req, MNCC_CONFIGURE_REQ);

    /* fill in primitive parameter: configure request */
    memset(&(mncc_configure_req -> called_party_sub), 0,
            sizeof(T_MNCC_called_party_sub));

    /* MTC bearer caps */
    mtcBC = &mncc_configure_req -> bcpara;
#ifdef FAX_AND_DATA
    mtcBC->rate = cmhCC_SelRate( ccShrdPrm.CBSTspeed );
#else
    mtcBC->rate = MNCC_UR_NOT_PRES;
#endif /* FAX_AND_DATA */

    if( ccShrdPrm.snsMode EQ CSNS_MOD_Fax      OR
        ccShrdPrm.snsMode EQ CSNS_MOD_VAFVoice OR
        ccShrdPrm.snsMode EQ CSNS_MOD_VAFFax )
    {
      mtcBC->bearer_serv = MNCC_BEARER_SERV_FAX;
      mtcBC->modem_type  = MNCC_MT_NONE;
      mtcBC->conn_elem   = MNCC_CONN_ELEM_TRANS;
    }
    else
    {
#ifdef FAX_AND_DATA
      mtcBC->bearer_serv = cmhCC_SelServ( ccShrdPrm.CBSTname );
      mtcBC->modem_type  = cmhCC_SelMT  ( ccShrdPrm.CBSTspeed );
      mtcBC->conn_elem   = cmhCC_SelCE  ( ccShrdPrm.CBSTce );
      mtcBC->transfer_cap  = cmhCC_SelTransferCap ( ccShrdPrm.CBSTspeed );
      mtcBC->rate_adaption = cmhCC_SelRateAdaption( ccShrdPrm.CBSTspeed );
#else
      mtcBC->bearer_serv = MNCC_BEARER_SERV_NOT_PRES;
      mtcBC->modem_type  = MNCC_MT_NONE;
      mtcBC->conn_elem   = MNCC_CONN_ELEM_NOT_PRES;
#endif /* FAX_AND_DATA */
    }


    /* not sure whether this is taken into account by CC */
    mtcBC->stop_bits   = DEF_BC1_SB; /*cmhCC_SelStopBit( srcId );*/
    mtcBC->data_bits   = DEF_BC1_DB; /*cmhCC_SelDataBit( srcId );*/
    mtcBC->parity      = DEF_BC1_PR; /*cmhCC_SelParity ( srcId );*/
    mtcBC->flow_control = DEF_BC1_FC;
    /*****************************************************/

/*    mncc_configure_req -> bcpara   = *mtcBC; superfluous */
    mncc_configure_req -> sns_mode = ccShrdPrm.snsMode;
#ifdef FF_TTY
    mncc_configure_req -> ctm_ena = ccShrdPrm.ctmReq;
#else
    mncc_configure_req -> ctm_ena = MNCC_CTM_DISABLED;
#endif /* FF_TTY */
    PSENDX (CC, mncc_configure_req);
  }
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_SendSS            |
+-------------------------------------------------------------------+

  PURPOSE : send SS facility

*/

LOCAL SHORT psaCC_SendSS ( SHORT cId )
{
  T_CC_CALL_TBL *pCtbNtry = ccShrdPrm.ctb[cId];
  UBYTE           ccdRet;       /* holds CCD return value */

  TRACE_FUNCTION ("psaCC_SendSS()");

  if (pCtbNtry EQ NULL)
    return -1; /* Ensure not to dereference NULL */

/*
 *-------------------------------------------------------------------
 * create and send primitive
 *-------------------------------------------------------------------
 */
  {
    PALLOC (mncc_facility_req, MNCC_FACILITY_REQ);

    /*
     * fill in primitive parameter: facility request
     */

    mncc_facility_req -> ti          = pCtbNtry -> ti;
    mncc_facility_req -> ss_version  = pCtbNtry -> SSver;

    CCD_START;
    {
      MCAST( com, COMPONENT );

      memset( com, 0, sizeof( T_COMPONENT ));
      switch( ccShrdPrm.cmpType )
      {
        case( CT_INV ):
          com -> v_inv_comp = TRUE;
          com -> inv_comp.v_inv_id  = TRUE;
          com -> inv_comp.inv_id    = pCtbNtry -> iId 
                                    = ccShrdPrm.iIdNxt++;
          com -> inv_comp.v_op_code = TRUE;
          com -> inv_comp.op_code   = pCtbNtry -> opCode
                                    = ssFIECodeBuf.buf[0];
          com -> inv_comp.v_params  = TRUE;
          com -> inv_comp.params.l_params = ssFIECodeBuf.l_buf-8;
          com -> inv_comp.params.o_params = 8;
          memcpy( com -> inv_comp.params.b_params, 
                  ssFIECodeBuf.buf, ssFIECodeBuf.l_buf );
          break;
        case( CT_RET_RSLT ):
          break;
        case( CT_RET_ERR ):
          break;
        case( CT_RET_REJ ):
          break;
      }
      mncc_facility_req -> fac_inf.l_fac = MNCC_FACILITY_LEN<<3;
      mncc_facility_req -> fac_inf.o_fac = 0;
      ccdRet = ccd_codeMsg (CCDENT_FAC,
                            UPLINK,
                            (T_MSGBUF *) &mncc_facility_req -> fac_inf,
                            (UBYTE    *) _decodedMsg,
                            COMPONENT);

      if( ccdRet NEQ ccdOK )
      {
        TRACE_EVENT_P1( "CCD Coding Error: %d",ccdRet ); 
        PFREE( mncc_facility_req );
        CCD_END;
        return( -1 );
      }
    }
    CCD_END;

    pCtbNtry -> srvType = ST_MOS;

    psaCC_DumpFIE ( &mncc_facility_req -> fac_inf );
    cmhCC_sendFie ( CSCN_FACILITY_DIRECTION_OUT,
                    cId,
                    &mncc_facility_req -> fac_inf );
    
    PSENDX (CC, mncc_facility_req);
  }

  return 0;
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_BuildMPTY         |
+-------------------------------------------------------------------+

  PURPOSE : build multiparty

*/

GLOBAL void psaCC_BuildMPTY ( SHORT cId )
{
  SHORT ctbIdx;     /* holds call table index */

  TRACE_FUNCTION ("psaCC_BuildMPTY()");

/*
 *-------------------------------------------------------------------
 * set multiparty status for held calls
 *-------------------------------------------------------------------
 */
  for( ctbIdx = 0; ctbIdx < MAX_CALL_NR; ctbIdx++ )
  {
    if (ccShrdPrm.ctb[ctbIdx] NEQ NULL AND
        psaCC_ctb(ctbIdx)->calStat EQ CS_HLD )
    {
      if( psaCC_ctb(ctbIdx)->mptyStat NEQ CS_ACT )
      
        psaCC_ctb(ctbIdx)->mptyStat = CS_ACT_REQ;
    }
  }

  /* set multiparty status for active call */
  if( psaCC_ctb(cId)->mptyStat NEQ CS_ACT )
    psaCC_ctb(cId)->mptyStat = CS_ACT_REQ;

/*
 *-------------------------------------------------------------------
 * send facility information element
 *-------------------------------------------------------------------
 */
  psaCC_asmBuildMPTY();

  if( psaCC_SendSS(cId) EQ 0 )
  {
    ccShrdPrm.cIdMPTY = cId;
    TIMERSTART( TMPTY_VALUE, ACI_TMPTY );
  }
    
  psaCC_MPTY (cId, MNCC_MPTY_BUILD_SENT);
}

/* Implements Measure # 86 */


/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_CountMPTY         |
+-------------------------------------------------------------------+

  PURPOSE : counts the number of parties in a MPTY

*/

GLOBAL int psaCC_CountMPTY ( void )
{
  int i = 0;
  SHORT cId;
  TRACE_FUNCTION ("psaCC_CountMPTY()");
  
  for( cId = 0; cId < MAX_CALL_NR; cId++ )
  {
    if (ccShrdPrm.ctb[cId] NEQ NULL AND
        psaCC_ctb(cId)->mptyStat EQ CS_ACT)
    {
      i++;
    }
  }
  return i;
}



/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_SplitMPTY         |
+-------------------------------------------------------------------+

  PURPOSE : split multiparty

*/

GLOBAL void psaCC_SplitMPTY ( SHORT cId )
{
  TRACE_FUNCTION ("psaCC_SplitMPTY()");

  /* set multiparty status for active call */
  psaCC_ctb(cId)->mptyStat = CS_DSC_REQ;

/*
 *-------------------------------------------------------------------
 * send facility information element
 *-------------------------------------------------------------------
 */
  psaCC_asmSplitMPTY();

  if( psaCC_SendSS(cId) EQ 0 )
  {
    ccShrdPrm.cIdMPTY = cId;
    TIMERSTART( TMPTY_VALUE, ACI_TMPTY );
  }

  psaCC_MPTY (cId, MNCC_MPTY_SPLIT_SENT);
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_ECT               |
+-------------------------------------------------------------------+

  PURPOSE : explicit call transfer

*/

GLOBAL SHORT psaCC_ECT ( SHORT cId )
{
  SHORT psa_ret;
  
  TRACE_FUNCTION ("psaCC_ECT()");

/*
 *-------------------------------------------------------------------
 * send facility information element
 *-------------------------------------------------------------------
 */
  psaCC_asmECT();

  psa_ret = psaCC_SendSS(cId);
    
  if( psa_ret EQ 0 )
  {
    TIMERSTART( TECT_VALUE, ACI_TECT );
  }
 
  return(psa_ret);
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_BCapCode          |
+-------------------------------------------------------------------+

  PURPOSE : code bearer capabilites according GSM 04.08

*/

GLOBAL SHORT psaCC_BCapCode ( UBYTE reqId, SHORT cId )
{
  TRACE_FUNCTION ("psaCC_BCapCode()");

/*
 *-------------------------------------------------------------------
 * create and send primitive
 *-------------------------------------------------------------------
 */
  {
    PALLOC (mncc_bearer_cap_req, MNCC_BEARER_CAP_REQ);

    /*
     * fill in primitive parameter: bearer capability coding request
     */
    
    mncc_bearer_cap_req -> req_id  = reqId;
    mncc_bearer_cap_req -> bc_mod  = MNCC_BC_MOD_CODE;
   /* 
    * Bearer Service parameter is used by CC to determine whether coding 
    * has to be done or not. Hence that resetting that alone is sufficient. 
    */
    mncc_bearer_cap_req -> bcpara.bearer_serv = MNCC_BEARER_SERV_NOT_PRES;
    mncc_bearer_cap_req -> bcpara2.bearer_serv = MNCC_BEARER_SERV_NOT_PRES;
    
    if ( ccShrdPrm.BC0_send_flag )
    {
      mncc_bearer_cap_req -> bcpara  = ccShrdPrm.ctb[cId]->BC[0];
    }
    if ( ccShrdPrm.BC1_send_flag )
    {
      mncc_bearer_cap_req -> bcpara2 = ccShrdPrm.ctb[cId]->BC[1];
    }

    PSENDX (CC, mncc_bearer_cap_req);
  }

  return 0;
}


/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_BCapDecode        |
+-------------------------------------------------------------------+

  PURPOSE : decode bearer cpabilities according GSM 04.08 into struct
            bcpara.

*/

GLOBAL SHORT psaCC_BCapDecode ( UBYTE reqId, UINT16 bcLen1, UBYTE *bc1, UINT16 bcLen2, UBYTE *bc2 )
{
  TRACE_FUNCTION ("psaCC_BCapDecode()");

/*
 *-------------------------------------------------------------------
 * create and send primitive
 *-------------------------------------------------------------------
 */
  if ((  bcLen1 > MNCC_MAX_BC_LEN ) OR ( bcLen2 > MNCC_MAX_BC_LEN )) return -1;

  {
    PALLOC (mncc_bearer_cap_req, MNCC_BEARER_CAP_REQ);

    /*
     * fill in primitive parameter: bearer capability decoding request
     */
    mncc_bearer_cap_req -> req_id         = reqId;
    mncc_bearer_cap_req -> bc_mod         = MNCC_BC_MOD_DECODE;
    mncc_bearer_cap_req -> bcconf.bc_len  = (UBYTE)bcLen1;
    mncc_bearer_cap_req -> bcconf2.bc_len = (UBYTE)bcLen2;

    if ( bcLen1 )
    {
      memcpy( mncc_bearer_cap_req -> bcconf.bc, bc1, bcLen1 );
    }

    if ( bcLen2 )
    {
      memcpy( mncc_bearer_cap_req -> bcconf2.bc, bc2, bcLen2 );
    }

    PSENDX (CC, mncc_bearer_cap_req);
  }

  return 0;
}

/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_MPTY              |
+-------------------------------------------------------------------+

  PURPOSE : Inform CC about an event related to a multiparty call
            which may change the auxiliary state hold or mpty in CC.

*/

GLOBAL SHORT psaCC_MPTY ( SHORT cId, UBYTE mpty_event )
{
  TRACE_FUNCTION ("psaCC_MPTY()");
  
  /* Send the synchronization request to CC */
  {
    PALLOC (mncc_sync_req, MNCC_SYNC_REQ); /* T_MNCC_SYNC_REQ */
    mncc_sync_req->synccs     = MNCC_SYNCCS_MPTY_EVENT;
    mncc_sync_req->ti         = psaCC_ctb(cId)->ti;
    mncc_sync_req->mpty_event = mpty_event;
    PSENDX (CC, mncc_sync_req);
  }
  return 0;
}

/* Implements Measure # 86 */
/*
+-------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)         MODULE  : PSA_CCS                 |
|                                 ROUTINE : psaCC_HoldMPTY          |
+-------------------------------------------------------------------+

  PURPOSE : Hold OR Retrieve multiparty

*/

GLOBAL void psaCC_Hold_RetrieveMPTY ( SHORT     cId, 
                                      T_CC_CLST call_stat,
                                      T_CC_CLST call_stat_new,
                                      UBYTE     cId_new,
                                      UBYTE     opc)
{
  SHORT ctbIdx;     /* holds call table index */

  TRACE_FUNCTION ("psaCC_Hold_RetrieveMPTY()");

  /*
   *-----------------------------------------------------------------
   * set call status for multiparty members
   *-----------------------------------------------------------------
   */
  for( ctbIdx = 0; ctbIdx < MAX_CALL_NR; ctbIdx++ )
  {
    if (ccShrdPrm.ctb[ctbIdx] NEQ NULL AND
        ccShrdPrm.ctb[ctbIdx]->calStat  EQ call_stat AND 
        ccShrdPrm.ctb[ctbIdx]->mptyStat EQ CS_ACT)
    {
      ccShrdPrm.ctb[ctbIdx]->calStat = call_stat_new;
    }
  }

  /* set multiparty status for active call */
  ccShrdPrm.ctb[cId]->calStat = call_stat_new;

  /*
   *-----------------------------------------------------------------
   * send facility information element
   *-----------------------------------------------------------------
   */

  /* 
   *-----------------------------------------------------------------
   * assemble the hold multiparty SS facility
   * information element, invoke id
   *-----------------------------------------------------------------
   */

  memset( &ssFIECodeBuf, 0, sizeof( ssFIECodeBuf ));

  ssFIECodeBuf.l_buf  = 8;
  ssFIECodeBuf.o_buf  = 0;
  ssFIECodeBuf.buf[0] = opc;

  ccShrdPrm.cmpType = CT_INV;

  if( psaCC_SendSS(cId) EQ 0 )
  {
    ccShrdPrm.cIdMPTY = cId;
    TIMERSTART( TMPTY_VALUE, ACI_TMPTY );
  }
  psaCC_MPTY (cId, cId_new);
}

/*==== EOF ========================================================*/