view src/g23m-gsm/cc/cc_act.c @ 685:3fb7384e820d

tpudrv12.h: FCDEV3B goes back to being itself A while back we had the idea of a FreeCalypso modem family whereby our current fcdev3b target would some day morph into fcmodem, with multiple FC modem family products, potentially either triband or quadband, being firmware-compatible with each other and with our original FCDEV3B. But in light of the discovery of Tango modules that earlier idea is now being withdrawn: instead the already existing Tango hw is being adopted into our FreeCalypso family. Tango cannot be firmware-compatible with triband OM/FCDEV3B targets because the original quadband RFFE on Tango modules is wired in TI's original Leonardo arrangement. Because this Leonardo/Tango way is now becoming the official FreeCalypso way of driving quadband RFFEs thanks to the adoption of Tango into our FC family, our earlier idea of extending FIC's triband RFFE control signals with TSPACT5 no longer makes much sense - we will probably never produce any new hardware with that once-proposed arrangement. Therefore, that triband-or-quadband FCFAM provision is being removed from the code base, and FCDEV3B goes back to being treated the same way as CONFIG_TARGET_GTAMODEM for RFFE control purposes.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 24 Sep 2020 21:03:08 +0000
parents 27a4235405c6
children
line wrap: on
line source

/* 
+----------------------------------------------------------------------------- 
|  Project :  GSM-PS (6147)
|  Modul   :  CC_ACT
+----------------------------------------------------------------------------- 
|  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 Modul defines the primitive processing functions
|             of the SDL process Call Control for the call active
|             phase of the component CC of the mobile station
+----------------------------------------------------------------------------- 
*/ 

#ifndef CC_ACT_C
#define CC_ACT_C

#define ENTITY_CC
/*==== INCLUDES ===================================================*/

#include <string.h>
#include "typedefs.h"
#include "pcm.h"
#include "vsi.h"
#include "custom.h"
#include "gsm.h"
#include "message.h"
#include "ccdapi.h"
#include "prim.h"
#include "cnf_cc.h"
#include "mon_cc.h"
#include "pei.h"
#include "tok.h"
#include "cc.h"
#include "cc_em.h"

/*==== EXPORT =====================================================*/

/*==== PROTOTYPES =================================================*/
LOCAL void cc_mncc_hold_retrieve_req (T_PRIM * prim);

/*==== PRIVAT =====================================================*/

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

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

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_dtmf_full               |
+--------------------------------------------------------------------+

  PURPOSE : Check whether DTMF buffer is full

*/

LOCAL BOOL cc_dtmf_full (const T_DTMF *p_dtmf)
{
  TRACE_FUNCTION ("cc_dtmf_full()");
  
  return ((p_dtmf->read EQ p_dtmf->write + 1) OR
          (p_dtmf->read EQ 0 AND p_dtmf->write EQ DTMF_BUF_SIZE - 1));
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_dtmf_empty              |
+--------------------------------------------------------------------+

  PURPOSE : Check whether DTMF buffer is empty

*/

LOCAL BOOL cc_dtmf_empty (const T_DTMF *p_dtmf)
{
  TRACE_FUNCTION ("cc_dtmf_empty()");
 
  return (p_dtmf->read EQ p_dtmf->write);
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_dtmf_write              |
+--------------------------------------------------------------------+

  PURPOSE : Write next entry into DTMF buffer. 
            No checks are done (e.g. buffer full)

*/

LOCAL void cc_dtmf_write (T_DTMF *p_dtmf, UBYTE val)
{
  TRACE_FUNCTION ("cc_dtmf_write()");
  
  p_dtmf->buf[p_dtmf->write++] = val;
  if (p_dtmf->write EQ DTMF_BUF_SIZE) 
    p_dtmf->write = 0; 
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_dtmf_write              |
+--------------------------------------------------------------------+

  PURPOSE : Read next entry from DTMF buffer. 
            No checks are done (e.g. buffer empty)

*/

LOCAL UBYTE cc_dtmf_read (T_DTMF *p_dtmf)
{
  UBYTE val;
  
  TRACE_FUNCTION ("cc_dtmf_read()");

  val = p_dtmf->buf[p_dtmf->read++];
  if (p_dtmf->read EQ DTMF_BUF_SIZE)
    p_dtmf->read = 0;
  return val;
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_dtmf_allowed            |
+--------------------------------------------------------------------+

  PURPOSE : This functions returns TRUE if DTMF is allowed in the
            current state, otherwise FALSE.

*/

LOCAL BOOL cc_dtmf_allowed (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("cc_dtmf_allowed()");

  /* 
   * "The mobile station shall be capable of transmitting DTMF messages if
   * and only if the mobile station has the user connection for speech 
   * attached and an appropriate channel is available.
   * 
   * ".."
   * 
   * NOTE 1: This specification means that DTMF messages can generally be 
   *         sent in the active state of a call in speech transmission 
   *         mode or when a traffic channel is available during setup or 
   *         release and the progress indicator IE has been received."
   *
   * [TS 24.008 subclause 5.5.7]
   * 
   * As of GSM 04.08 the progress indicator IE can be received by 
   * CALL PROCEEDING, PROGRESS, CONNECT, SETUP, ALERTING and DISCONNECT.
   */ 
  switch (cc_data->state[cc_data->index_ti])
  {
    case M_CC_CS_3:   /* mobile originating call proceeding */
    case M_CC_CS_4:   /* call delivered */
    case M_CC_CS_8:   /* connect request */
    case M_CC_CS_11:  /* disconnect request */ 
    case M_CC_CS_12:  /* disconnect indication */
    case M_CC_CS_19:  /* release request */ 
    case CS_101: /* substate of CS_10, reestablish requested */     
    case M_CC_CS_26:  /* mobile originating modify */
    case CS_261: /* substate of CS_26, reestablish requested */
      if (cc_data->progress_desc[cc_data->index_ti] EQ NOT_PRESENT_8BIT)
        return FALSE; /* Progress indicator IE has not been received */
      /*FALLTHROUGH*/ /*lint -fallthrough*/
    case M_CC_CS_10: /* active */
      if (cc_data->channel_type EQ NAS_CH_SDCCH)
        return FALSE; /* No TCH assigned */
  
      if ((cc_data->channel_mode NEQ NAS_CHM_SPEECH) AND
          (cc_data->channel_mode NEQ NAS_CHM_SPEECH_V2) AND
          (cc_data->channel_mode NEQ NAS_CHM_SPEECH_V3))
        return FALSE; /* Channel mode is not speech */

      return TRUE;
    
    default:
      return FALSE;
  }
}

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

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

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_mncc_start_dtmf_req     |
+--------------------------------------------------------------------+

  PURPOSE : Sending of DTMF tones to the infrastructure side.

*/

GLOBAL void cc_mncc_start_dtmf_req (T_MNCC_START_DTMF_REQ * dtmf)
{
  GET_INSTANCE_DATA;
  T_DTMF         * p_dtmf;

  TRACE_FUNCTION ("cc_mncc_start_dtmf_req()");


  if ((cc_data->index_ti = srv_convert_ti (dtmf->ti))
       EQ NOT_PRESENT_8BIT)
  {
    PFREE (dtmf);
    return;
  }
  
  switch (cc_data->state[cc_data->index_ti])
  {
    default:
      if (!cc_dtmf_allowed ())
      {
        /* Tone dropped. Confirm DTMF request with negative cause */
        PALLOC (dtmf_cnf, MNCC_START_DTMF_CNF);
        dtmf_cnf->ti       = dtmf->ti;
        dtmf_cnf->key      = dtmf->key;
        dtmf_cnf->cause    = MNCC_CAUSE_DTMF_NOT_ALLOWED;
        dtmf_cnf->dtmf_mod = dtmf->dtmf_mod;
        PSENDX (MMI, dtmf_cnf);

        PFREE (dtmf); 
        return;
      }

      p_dtmf = &cc_data->dtmf [cc_data->index_ti];

      if ( dtmf->dtmf_mod EQ MNCC_DTMF_MOD_AUTO OR 
           dtmf->dtmf_mod EQ MNCC_DTMF_MOD_MAN_START )
      {
        if (p_dtmf->state EQ DTMF_IDLE)
        {
          /*
           * If sending of DTMF tones is not in
           * progress send the tone.
           */
          CCD_START;
          {
            MCAST (start_dtmf, U_START_DTMF);

            cc_build_start_dtmf (dtmf->key, start_dtmf);
            for_start_dtmf (start_dtmf);
          }
          CCD_END;
          
          /* On sending a START DTMF message the MS shall start T336 */
          TIMERSTART (T336, T336_VALUE);

          p_dtmf->state = DTMF_SEND_REQUEST;
          p_dtmf->mode  = (dtmf->dtmf_mod EQ MNCC_DTMF_MOD_AUTO)?DTMF_AUTO:DTMF_MAN;
          p_dtmf->key   = dtmf->key;
        }
        else
        {
          /*
           * Not DTMF_IDLE.
           */

          if (dtmf->dtmf_mod EQ MNCC_DTMF_MOD_AUTO)
          {
            /* 
             * If DTMF_MOD_AUTO, store the tone if the buffer is not full.
             */ 
            if (!cc_dtmf_full (p_dtmf))
              cc_dtmf_write (p_dtmf, (UBYTE)SET_DTMF_MOD(dtmf->key, DTMF_AUTO));
            // PATCH HM 13.04.00 >>>
            else
            {
              /* Tone dropped. Confirm DTMF request with negative cause */
              PALLOC (dtmf_cnf, MNCC_START_DTMF_CNF);
              dtmf_cnf->ti       = dtmf->ti;
              dtmf_cnf->key      = dtmf->key;
              dtmf_cnf->cause    = MNCC_CAUSE_DTMF_BUFFER_FULL;
              dtmf_cnf->dtmf_mod = dtmf->dtmf_mod;
              PSENDX(MMI, dtmf_cnf);
            }
            // PATCH HM 13.04.00 <<<
          }
          else
          {
            /* 
             * If DTMF_MOD_MAN_START, two conditions have to be fullfilled 
             * that the tone is stored and not dropped:
             * 
             * - The buffer is not full.
             * - There is enough room to store the stop which 
             *   will follow the tone.
             */
            if (!cc_dtmf_full (p_dtmf) AND
                 (p_dtmf->read NEQ (p_dtmf->write + 2) % DTMF_BUF_SIZE))
            {
              cc_dtmf_write (p_dtmf, (UBYTE)SET_DTMF_MOD(dtmf->key, DTMF_MAN));
            }
            // PATCH HM 13.04.00 >>>
            else
            {
              /* Tone dropped. Confirm DTMF request with negative cause */
              PALLOC (dtmf_cnf, MNCC_START_DTMF_CNF);
              dtmf_cnf->ti       = dtmf->ti;
              dtmf_cnf->key      = dtmf->key;
              dtmf_cnf->cause    = MNCC_CAUSE_DTMF_BUFFER_FULL;
              dtmf_cnf->dtmf_mod = dtmf->dtmf_mod;
              PSENDX (MMI, dtmf_cnf);
            }
            // PATCH HM 13.04.00 <<<
          }
        }
      }
      else
      {
        /* 
         * This is DTMF_MOD_MAN_STOP 
         */
        if ( p_dtmf->state EQ DTMF_SEND_ACKNOWLEDGE AND 
             cc_dtmf_empty (p_dtmf))
        {
          /*
           * If sending of DTMF tones is in
           * progress stop the tone.
           */
          for_stop_dtmf ();

          /* On sending a STOP DTMF message the MS shall start T337 */
          TIMERSTART (T337, T337_VALUE);

          p_dtmf->state = DTMF_STOP_REQUEST;
        }
        else
        {
          /* 
           * If DTMF_MOD_MAN_STOP, the stop mark is stored if 
           * the buffer is not full. If DTMF STOP ACK is received
           * and there are unexpected stops in the buffer due to 
           * a loss of the respective tones, the stops are dropped
           * until a tone is found.
           */
          if (!cc_dtmf_full(p_dtmf))
          {
            cc_dtmf_write (p_dtmf, NOT_PRESENT_8BIT);
          }
        }
      }
      PFREE (dtmf) 
      break;

    case M_CC_CS_26: /* mobile originating modify */
    case CS_101:
    case CS_261:
      srv_store_prim ((T_PRIM *)D2P(dtmf));
      break;
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_reset_dtmf              |
+--------------------------------------------------------------------+

  PURPOSE : Reset DTMF machine. If used before CC leaves any state in 
            which DTMF is possible MMI is informed about DTMF failure 
            if there was still a DTMF request outstanding.

*/

GLOBAL void cc_reset_dtmf (void)
{
  GET_INSTANCE_DATA;
  T_DTMF * p_dtmf;
  BOOL dtmf_aborted;

  TRACE_FUNCTION ("cc_reset_dtmf");

  p_dtmf = &cc_data->dtmf[cc_data->index_ti];

  if (cc_dtmf_allowed())
  {
    /* Stop all possibly running DTMF timers */
    TIMERSTOP (TIMER_DTMF);

    /*  
     * A DTMF acknowledge maybe outstanding for ACI either if the 
     * DTMF state is DTMF_SEND_REQ or 
     * a tone was found in the DTMF send buffer
     */
    dtmf_aborted = (p_dtmf->state EQ DTMF_SEND_REQUEST);
    while (!dtmf_aborted AND !cc_dtmf_empty(p_dtmf))
      dtmf_aborted = (cc_dtmf_read (p_dtmf) NEQ NOT_PRESENT_8BIT);

    if (dtmf_aborted)
    {
      /* Tone dropped. Confirm DTMF request with negative cause */
      PALLOC (dtmf_cnf, MNCC_START_DTMF_CNF);
      dtmf_cnf->ti       = cc_data->ti;
      dtmf_cnf->key      = NOT_PRESENT_8BIT;
      dtmf_cnf->cause    = MNCC_CAUSE_DTMF_ABORTED;
      dtmf_cnf->dtmf_mod = NOT_PRESENT_8BIT;
      PSENDX (MMI, dtmf_cnf);
    }

  }
  p_dtmf->state = DTMF_IDLE;
  p_dtmf->write = 0;
  p_dtmf->read  = 0;
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_start_dtmf_ack          |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming start dtmf acknowledge message.

*/

GLOBAL void cc_start_dtmf_ack (T_D_START_DTMF_ACK * start_dtmf_ack)
{
  GET_INSTANCE_DATA;
  T_DTMF               * p_dtmf;

  TRACE_FUNCTION ("cc_start_dtmf_ack()");

  if (cc_dtmf_allowed ())
  {
    if (cc_check_error_flag ())
    {
      p_dtmf = &cc_data->dtmf[cc_data->index_ti];
      
      /* Stop T336 */
      TIMERSTOP (TIMER_DTMF);
      
      if (p_dtmf->state EQ DTMF_SEND_REQUEST)
      {
        PALLOC (start, MNCC_START_DTMF_CNF);
        
        start->ti       = cc_data->ti;
        start->key      = start_dtmf_ack->key_facility.key;
        start->cause    = MNCC_CAUSE_DTMF_START_SUCCESS;
        start->dtmf_mod = (p_dtmf->mode EQ DTMF_AUTO) ?
                          MNCC_DTMF_MOD_AUTO : MNCC_DTMF_MOD_MAN_START;
        PSENDX (MMI, start);

        EM_CC_START_DTMF_ACKNOWLEDGE;

        if( p_dtmf->mode EQ DTMF_AUTO )
        {
          for_stop_dtmf ();
          
          /* On sending a STOP DTMF message the MS shall start T337 */
          TIMERSTART (T337, T337_VALUE);
          
          p_dtmf->state = DTMF_STOP_REQUEST;
        }
        else
        {
          /* This is DTMF_MAN */
          if (!cc_dtmf_empty (p_dtmf))
          {
            UBYTE tone;
            
            tone = cc_dtmf_read (p_dtmf);
            if (tone EQ NOT_PRESENT_8BIT)
            {
              /* Should be always true */
              p_dtmf->mode = GET_DTMF_MOD(tone);
              for_stop_dtmf ();
              
              /* On sending a STOP DTMF message the MS shall start T337 */
              TIMERSTART (T337, T337_VALUE);
              
              p_dtmf->state = DTMF_STOP_REQUEST;
            }
            while (!cc_dtmf_empty(p_dtmf) AND
              p_dtmf->buf[p_dtmf->read] EQ NOT_PRESENT_8BIT)
            {
              /* Consume all unwanted stop marks */
              (void)cc_dtmf_read(p_dtmf);
            }
          }              
          else
            p_dtmf->state = DTMF_SEND_ACKNOWLEDGE;
        }
/* Implements Measure#  3 and streamline encoding */
        CCD_END;
      }
      else
      {

/* Implements Measure#  3 and streamline encoding */
        CCD_END;
        cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      }
/* Implements Measure#  3 and streamline encoding */
    } /* if (cc_check_error_flag (cc_data)) */
  } /* if (cc_dtmf_allowed (cc_data)) */
  else
  {
    /* 
     * DTMF not allowed for some reason, e.g. wrong state,
     * call is not a speech call, no TCH assigned etc.
     */
    CCD_END;
/* Implements Measure#  3 and streamline encoding */
    cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_start_dtmf_rej          |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming start dtmf reject message.

*/

GLOBAL void cc_start_dtmf_rej (T_D_START_DTMF_REJ * start_dtmf_rej)
{
  GET_INSTANCE_DATA;
  T_DTMF               * p_dtmf;

  TRACE_FUNCTION ("cc_start_dtmf_rej()");

  if (cc_dtmf_allowed ())
  {
    if (cc_check_error_flag ())
    {
      /* Stop T336 */
      TIMERSTOP (TIMER_DTMF);
      p_dtmf = &cc_data->dtmf[cc_data->index_ti];
      if (p_dtmf->state EQ DTMF_SEND_REQUEST)
      {
        PALLOC (start, MNCC_START_DTMF_CNF);

        start->ti       = cc_data->ti;
        start->key      = NOT_PRESENT_8BIT;
        start->dtmf_mod = NOT_PRESENT_8BIT;
        /* 
         * cause IE is mandatory and the check for presence has been done already 
         * thus there is no need to check for validity of cause IE here anymore
         */
        start->cause = CAUSE_MAKE(DEFBY_STD, 
                                  ORIGSIDE_NET, 
                                  MNCC_CC_ORIGINATING_ENTITY, 
                                  start_dtmf_rej->cc_cause.cause);
        PSENDX (MMI, start);

        p_dtmf->state = DTMF_IDLE;
        p_dtmf->write = 0;
        p_dtmf->read  = 0;
/* Implements Measure#  3 and streamline encoding */
       CCD_END; 
      }
      else
      {
/* Implements Measure#  3 and streamline encoding */
        CCD_END;
        cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      }
/* Implements Measure#  3 and streamline encoding */
    }
  }
  else
  {
    /* 
     * DTMF not allowed for some reason, e.g. wrong state,
     * call is not a speech call, no TCH assigned etc.
     */
    CCD_END;
/* Implements Measure#  3 and streamline encoding */
    cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_stop_dtmf               |
+--------------------------------------------------------------------+

  PURPOSE : Stops DTMF, back to DTMF_IDLE state.
            Reasons for call: 
            1.) Reception of STOP DTMF ACK message
            2.) Expiry of T336
            3.) Expiry of T337

*/

GLOBAL void cc_stop_dtmf (void)
{
  GET_INSTANCE_DATA;
  T_DTMF               * p_dtmf;

  TRACE_FUNCTION ("cc_stop_dtmf()");

  p_dtmf = &cc_data->dtmf[cc_data->index_ti];

  while (!cc_dtmf_empty(p_dtmf))
  {
    UBYTE tone;
    
    tone = cc_dtmf_read (p_dtmf);
    if (tone NEQ NOT_PRESENT_8BIT)
    {
      /*
       * another DTMF tone has to be sent
       */
      p_dtmf->mode = GET_DTMF_MOD (tone);
      p_dtmf->key  = tone & 0x7f;
      CCD_START;
      {
        MCAST (start, U_START_DTMF);

        cc_build_start_dtmf (p_dtmf->key, start);
        for_start_dtmf (start);
      }
      CCD_END;
      
      /* On sending a START DTMF message the MS shall start T336 */
      TIMERSTART (T336, T336_VALUE);
      
      p_dtmf->state = DTMF_SEND_REQUEST;
      return;
    }
  }
  p_dtmf->state = DTMF_IDLE;
  srv_use_stored_prim ();
}    


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_stop_dtmf_ack           |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming stop dtmf ack message.

*/

GLOBAL void cc_stop_dtmf_ack (void)
{
  GET_INSTANCE_DATA;
  T_DTMF               * p_dtmf;

  TRACE_FUNCTION ("cc_stop_dtmf_ack()");

  if (cc_dtmf_allowed ())
  {
    if (cc_check_error_flag ()) 
    {
      CCD_END;
      p_dtmf = &cc_data->dtmf[cc_data->index_ti];
      if (p_dtmf->state EQ DTMF_STOP_REQUEST)
      {
        if (p_dtmf->mode EQ DTMF_MAN)
        {
          PALLOC (start, MNCC_START_DTMF_CNF);
          start->ti       = cc_data->ti;
          start->key      = NOT_PRESENT_8BIT;
          start->cause    = MNCC_CAUSE_DTMF_STOP_SUCCESS;
          start->dtmf_mod = MNCC_DTMF_MOD_MAN_STOP;
          PSENDX (MMI, start);
        }
        /* Stop T337 */
        TIMERSTOP (TIMER_DTMF);
        cc_stop_dtmf ();
      }
      else
      {
        /* 
         * The STOP DTMF ACK message was not expected as we did not sent
         * previously a START DTMF request.
         */
/* Implements Measure#  3 and streamline encoding */
        cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      }
    }
  }
  else
  {
    /* 
     * DTMF not allowed in the current state 
     */
    CCD_END;    
/* Implements Measure#  3 and streamline encoding */
    cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_mncc_facility_req       |
+--------------------------------------------------------------------+

  PURPOSE : Sending of Facility Information by the mobile side.

*/

GLOBAL void cc_mncc_facility_req (T_MNCC_FACILITY_REQ * facility)
{
  GET_INSTANCE_DATA;

  TRACE_FUNCTION ("cc_mncc_facility_req()");

  if ((cc_data->index_ti = srv_convert_ti (facility->ti))
       EQ NOT_PRESENT_8BIT)
  {
    PFREE (facility);
    return;
  }

  switch (cc_data->state[cc_data->index_ti])
  {
    case M_CC_CS_19:
      PFREE (facility);
      break;

    case M_CC_CS_01:
    case CS_101:
    case CS_261:
      srv_store_prim ((T_PRIM *)D2P(facility));
      break;

    default:
      CCD_START;
      {
        MCAST (facility_msg, U_FACILITY);
        cc_build_facility (facility, facility_msg);
        for_facility (facility_msg);

        EM_CC_FACILITY_TO_THE_NETWORK;

      }
      CCD_END;
      PFREE (facility);
      break;
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_mncc_modify_req         |
+--------------------------------------------------------------------+

  PURPOSE : Start of a mobile originated in-call modification.

*/

GLOBAL void cc_mncc_modify_req (T_MNCC_MODIFY_REQ * modify)
{
  GET_INSTANCE_DATA;

  TRACE_FUNCTION ("cc_mncc_modify_req()");

  if ((cc_data->index_ti = srv_convert_ti (modify->ti))
      EQ NOT_PRESENT_8BIT)
  {
    PFREE (modify);
    return;
  }

  switch (cc_data->state[cc_data->index_ti])
  {
    case M_CC_CS_10:
      if (((cc_data->hold_state[cc_data->index_ti] EQ M_CC_HLD_IDLE)) AND
           (cc_data->dtmf[cc_data->index_ti].state EQ DTMF_IDLE))
      {
        /*
         * If no other procedure is running
         */
        CCD_START;
        {
          MCAST (modify_msg, B_MODIFY);
          cc_build_modify (modify, modify_msg);
          for_modify (modify_msg);
        }
        CCD_END;
        PFREE (modify);
        cc_set_state (M_CC_CS_26);
        TIMERSTART (T323, T323_VALUE);

        EM_CC_MO_IN_CALL_MODIFICATION;

      }
      else
        srv_store_prim ((T_PRIM *)D2P(modify));
      break;

    default:
      PFREE (modify);
      break;
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_mncc_user_req           |
+--------------------------------------------------------------------+

  PURPOSE : Send user information or congestion control.

*/

GLOBAL void cc_mncc_user_req (T_MNCC_USER_REQ * user)
{
  GET_INSTANCE_DATA;

  TRACE_FUNCTION ("cc_mncc_user_req()");

  if ((cc_data->index_ti = srv_convert_ti (user->ti))
       EQ NOT_PRESENT_8BIT)
  {
    PFREE (user);
    return;
  }

  switch (cc_data->state[cc_data->index_ti])
  {
    case M_CC_CS_10:
      switch (user->congest_lev)
      {
        case M_CC_CL_RECEIVER_READY:
        case M_CC_CL_RECEIVER_NOT_READY:
          /*
           * Send Congestion Control
           */
          CCD_START;
          {
            MCAST (cong_ctrl, B_CONGEST_CTRL);
            
            cc_build_congestion_control (user, cong_ctrl);
            for_congestion_control (cong_ctrl);
          }
          CCD_END;
          PFREE (user);
          break;
        default:
          /*
           * Send User Information
           */
          CCD_START;
          {
            MCAST (user_msg, B_USER_INFO);

            cc_build_user_information (user, user_msg);
            for_user_information (user_msg);
          }
          CCD_END;
          PFREE (user);
          break;
      }
      break;

    case CS_101:
      srv_store_prim ((T_PRIM *)D2P(user));
      break;

    default:
      PFREE (user);
      break;
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_mncc_hold_req           |
+--------------------------------------------------------------------+

  PURPOSE : Send hold message.

*/

GLOBAL void cc_mncc_hold_req (T_MNCC_HOLD_REQ * hold)
{

  TRACE_FUNCTION ("cc_mncc_hold_req()");

/* Implements Measure# 18 */
  cc_mncc_hold_retrieve_req ((T_PRIM *)D2P(hold));
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_mncc_retrieve_req       |
+--------------------------------------------------------------------+

  PURPOSE : Send retrieve message.

*/
GLOBAL void cc_mncc_retrieve_req (T_MNCC_RETRIEVE_REQ * retrieve)
{

  TRACE_FUNCTION ("cc_mncc_retrieve_req()");

/* Implements Measure# 18 */
  cc_mncc_hold_retrieve_req ((T_PRIM *)D2P(retrieve));
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_call_is_active          |
+--------------------------------------------------------------------+

  PURPOSE : Return TRUE if the respective CC instance is in an active
            state, otherwise return FALSE.

*/

LOCAL BOOL cc_call_is_active (UBYTE index_ti)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("cc_call_is_active()");
  
  switch (cc_data->state[index_ti])
  {
    case M_CC_CS_10:
    case M_CC_CS_26:
    case CS_101:
    case CS_261:
      return TRUE;

    default:
      return FALSE;
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_mncc_sync_req           |
+--------------------------------------------------------------------+

  PURPOSE : Processing a MNCC_SYNC_REQ primitive. To be able to 
            understand  some details of this function, see first 
            GSM 03.84, GSM 04.84 and GSM 11.10 chapters 31.4.1 and 31.4.2.

*/

GLOBAL void cc_mncc_sync_req (T_MNCC_SYNC_REQ * mncc_sync_req)
{
  GET_INSTANCE_DATA;
  UBYTE i;

  TRACE_FUNCTION ("cc_mncc_sync_req()");

  if ((cc_data->index_ti = srv_convert_ti (mncc_sync_req->ti))
      EQ NOT_PRESENT_8BIT)
  {
    PFREE (mncc_sync_req);
    return;
  }

  switch (cc_data->state[cc_data->index_ti])
  {
    /* 
     * Note: MPTY and HOLD only has a meaning in the following states, 
     *       in other states these aux states have absolutely no meaning.
     */
    case M_CC_CS_10:
    case M_CC_CS_26:
    case CS_101:
    case CS_261:
      switch (mncc_sync_req->synccs)
      {
        case MNCC_SYNCCS_MPTY_EVENT:

          TRACE_EVENT_P2 ("  ti = %d, mpty_event = %d", 
                          mncc_sync_req->ti, 
                          mncc_sync_req->mpty_event);

          switch (mncc_sync_req->mpty_event)
          {
            case MNCC_MPTY_BUILD_SENT:
              for (i = 0; i < MAX_CC_CALLS; i++)
              {
                if (cc_call_is_active(i) AND
                    cc_data->mpty_state[i] EQ M_CC_MPTY_IDLE)
                {
                  cc_data->mpty_state[i] = M_CC_MPTY_REQ;
                }
              }
              break;

            case MNCC_MPTY_BUILD_FAIL:
              for (i = 0; i < MAX_CC_CALLS; i++)
              {
                if (cc_call_is_active(i) AND 
                    cc_data->mpty_state[i] EQ M_CC_MPTY_REQ)
                {
                  cc_data->mpty_state[i] =  M_CC_MPTY_IDLE;
                }
              }
              break;

            case MNCC_MPTY_BUILD_SUCCESS:
              for (i = 0; i < MAX_CC_CALLS; i++)
              {
                if (cc_call_is_active(i))
                {
                  cc_data->mpty_state[i] = M_CC_MPTY_CALL;
                  cc_data->hold_state[i] = M_CC_HLD_IDLE;
                }
              }
              break;

            /* -- */
            
            case MNCC_MPTY_SPLIT_SENT:
              if (cc_data->mpty_state[cc_data->index_ti] EQ M_CC_MPTY_CALL)
              {              
                cc_data->mpty_state[cc_data->index_ti] = M_CC_MPTY_SPLIT;
              }
              break;

            case MNCC_MPTY_SPLIT_FAIL:
              if (cc_data->mpty_state[cc_data->index_ti] EQ M_CC_MPTY_SPLIT)
              {              
                cc_data->mpty_state[cc_data->index_ti] = M_CC_MPTY_CALL;
              }
              break;

            case MNCC_MPTY_SPLIT_SUCCESS:
              cc_data->mpty_state[cc_data->index_ti] = M_CC_MPTY_IDLE;
              cc_data->hold_state[cc_data->index_ti] = M_CC_HLD_IDLE;
              for (i = 0; i < MAX_CC_CALLS; i++)
              {
                if (cc_call_is_active(i) AND 
                    cc_data->mpty_state[i] NEQ M_CC_MPTY_IDLE)
                {
                  cc_data->hold_state[i] = M_CC_HLD_CALL_HELD;
                }
              }
              break;

            /* -- */

            case MNCC_MPTY_HOLD_SENT:
              for (i = 0; i < MAX_CC_CALLS; i++)
              {
                if (cc_call_is_active(i) AND 
                    cc_data->mpty_state[i] NEQ M_CC_MPTY_IDLE)
                {
                  cc_data->hold_state[i] = M_CC_HLD_HOLD_REQ;
                }
              }
              break;

            case MNCC_MPTY_HOLD_FAIL:
              for (i = 0; i < MAX_CC_CALLS; i++)
              {
                if (cc_call_is_active(i) AND 
                    cc_data->mpty_state[i] NEQ M_CC_MPTY_IDLE)
                {
                  cc_data->hold_state[i] = M_CC_HLD_IDLE;
                }
              }
              break;

            case MNCC_MPTY_HOLD_SUCCESS:
              for (i = 0; i < MAX_CC_CALLS; i++)
              {
                if (cc_call_is_active(i) AND 
                    cc_data->mpty_state[i] NEQ M_CC_MPTY_IDLE)
                {
                  cc_data->hold_state[i] = M_CC_HLD_CALL_HELD;
                }
              }
              break;

            /* -- */

            case MNCC_MPTY_RETRIEVE_SENT:
              for (i = 0; i < MAX_CC_CALLS; i++)
              {
                if (cc_call_is_active(i) AND 
                    cc_data->mpty_state[i] NEQ M_CC_MPTY_IDLE)
                {
                  cc_data->hold_state[i] = M_CC_HLD_RETRIEVE_REQ;
                }
              }
              break;

            case MNCC_MPTY_RETRIEVE_FAIL:
              for (i = 0; i < MAX_CC_CALLS; i++)
              {
                if (cc_call_is_active(i) AND 
                    cc_data->mpty_state[i] NEQ M_CC_MPTY_IDLE)
                {
                  cc_data->hold_state[i] = M_CC_HLD_CALL_HELD;
                }
              }
              break;

            case MNCC_MPTY_RETRIEVE_SUCCESS:
              for (i = 0; i < MAX_CC_CALLS; i++)
              {
                if (cc_call_is_active(i) AND 
                    cc_data->mpty_state[i] NEQ M_CC_MPTY_IDLE)
                {
                  cc_data->hold_state[i] = M_CC_HLD_IDLE;
                }
              }
              break;

            /* -- */

            default: 
              TRACE_ERROR ("Illegal MPTY event");
              break;
          } /* switch (mncc_sync_req->mpty_event) */
          break; 
          
        default:  
          TRACE_ERROR ("Illegal SYNCCS");
      } /* switch (mncc_sync_req->synccs) */
      break;
      
    default:
      break; /* Ignore the event */
  } /* switch (cc_data->state[cc_data->index_ti]) */

#ifndef NTRACE
  for (i = 0; i < MAX_CC_CALLS; i++)
  {
    if (cc_call_is_active(i))
    {
      TRACE_EVENT_P4 ("  hold[%d] = %d, mpty[%d] = %d", 
                      cc_data->stored_ti_values[i],
                      cc_data->hold_state[i],
                      cc_data->stored_ti_values[i],
                      cc_data->mpty_state[i]);
    }
  }
#endif /* #ifndef NTRACE */

  PFREE (mncc_sync_req);
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_mncc_status_res         |
+--------------------------------------------------------------------+

  PURPOSE : Processing a MNCC_STATUS_RES primitive. There is an
            outstanding STATUS message which still has to be sent to 
            the network after having received a STATUS ENQUIRY in U10.

*/

GLOBAL void cc_mncc_status_res (T_MNCC_STATUS_RES * mncc_status_res)
{
  GET_INSTANCE_DATA;

  TRACE_FUNCTION ("cc_mncc_status_res()");

  if ((cc_data->index_ti = srv_convert_ti (mncc_status_res->ti))
      EQ NOT_PRESENT_8BIT)
  {
    PFREE (mncc_status_res);
    return;
  }

  switch (cc_data->state[cc_data->index_ti])
  {
    case M_CC_CS_01:  /* MM connection pending */
    case CS_101: /* No network connection, reestablishment in progress */
    case CS_261:
      break;

    default:
/* Implements Measure#  3 and streamline encoding */    
      cc_send_status (M_CC_CAUSE_STATUS_ENQUIRY);
      break;
  }

  PFREE (mncc_status_res);
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_facility                |
+--------------------------------------------------------------------+

  PURPOSE : Processing of an incoming facility message.

*/

GLOBAL void cc_facility (T_D_FACILITY * facility)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("cc_facility()");

  /* 
   * A facility message can be received in every state where CC has a 
   * MM connection, no need to check states here.
   */
  if (cc_check_error_flag ())
  {
    cc_build_facility_ind (MNCC_FAC_IN_FACILITY, TRUE, &facility->facility);
    CCD_END;
  }

  EM_CC_FACILITY_FROM_THE_NETWORK;

}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_hold_ack                |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming hold ack message.

*/

GLOBAL void cc_hold_ack (void)
{
  GET_INSTANCE_DATA;
  CCD_END;

  TRACE_FUNCTION ("cc_hold_ack()");

  switch (cc_data->state[cc_data->index_ti])
  {
      /*
       * CS_0 is handled by the formatter
       */
    case M_CC_CS_10:
      if (cc_check_error_flag ())
      {
        if (cc_data->hold_state[cc_data->index_ti] EQ M_CC_HLD_HOLD_REQ)
        {
          PALLOC (hold_cnf, MNCC_HOLD_CNF);

          cc_data->hold_state[cc_data->index_ti] = M_CC_HLD_CALL_HELD;
          hold_cnf->ti = cc_data->ti;
          hold_cnf->cause = MNCC_CAUSE_HOLD_SUCCESS;
          PSENDX (MMI, hold_cnf);

          EM_CC_CALL_HOLD_ACKNOWLEDGE;

        }
        else
        {
/* Implements Measure#  3 and streamline encoding */
          cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
        }
      }
      break;

    default:
/* Implements Measure#  3 and streamline encoding */
      cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      break;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_hold_rej                |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming hold rej message.

*/

GLOBAL void cc_hold_rej (T_D_HOLD_REJ * hold_rej)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("cc_hold_rej()");

  switch (cc_data->state[cc_data->index_ti])
  {
      /*
       * CS_0 is handled by the formatter
       */
    case M_CC_CS_10:
      if (cc_data->hold_state[cc_data->index_ti] EQ M_CC_HLD_HOLD_REQ)
      {
        PALLOC (hold_cnf, MNCC_HOLD_CNF);

        cc_data->hold_state[cc_data->index_ti] = M_CC_HLD_IDLE;
        hold_cnf->ti = cc_data->ti;

        /*
         * It is possible for the network to send a REJ without a Cause IE.
         * This causes the decoder to flag a Madatory IE error. According to
         * 04.08 Sec: 8.5.3.e, the REJ should still be managed as if a correctly
         * formatted REJ had been received. 
         */
        if (!hold_rej->cc_cause.v_cause)
        {
          hold_cnf->cause = MNCC_CAUSE_NO_NET_CAUSE;
        }
        else
        {
          hold_cnf->cause = CAUSE_MAKE(DEFBY_STD, 
                                       ORIGSIDE_NET, 
                                       MNCC_CC_ORIGINATING_ENTITY, 
                                       hold_rej->cc_cause.cause);
        }
        PSENDX (MMI, hold_cnf);
        srv_use_stored_prim ();

        EM_CC_CALL_HOLD_REJECT;

        CCD_END;
      }
      else
      {
        CCD_END;
/* Implements Measure#  3 and streamline encoding  */
        cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      }
      break;

    default:
      CCD_END;
/* Implements Measure#  3 and streamline encoding */
      cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      break;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_retrieve_ack            |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming retrieve ack message.

*/

GLOBAL void cc_retrieve_ack (void)
{
  GET_INSTANCE_DATA;

  TRACE_FUNCTION ("cc_retrieve_ack()");

  switch (cc_data->state[cc_data->index_ti])
  {
    /*
     * CS_0 is handled by the formatter
     */
    case M_CC_CS_10:
      if (cc_check_error_flag ())
      {
        CCD_END;
        if (cc_data->hold_state[cc_data->index_ti] EQ M_CC_HLD_RETRIEVE_REQ)
        {
          PALLOC (retrieve_cnf, MNCC_RETRIEVE_CNF);

          cc_data->hold_state[cc_data->index_ti] = M_CC_HLD_IDLE;
          retrieve_cnf->ti = cc_data->ti;
          retrieve_cnf->cause = MNCC_CAUSE_RETRIEVE_SUCCESS;
          PSENDX (MMI, retrieve_cnf);

           EM_CC_CALL_RETRIEVE_ACKNOWLEDGE;

        }
        else
        {
/* Implements Measure#  3 and streamline encoding */
          cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
        }
      }
      break;

    default:
      CCD_END;
/* Implements Measure#  3 and streamline encoding */
      cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      break;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_retrieve_rej            |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming retrieve rej message.

*/

GLOBAL void cc_retrieve_rej (T_D_RETRIEVE_REJ * retrieve_rej)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("cc_retrieve_rej()");

  switch (cc_data->state[cc_data->index_ti])
  {
    /*
     * CS_0 is handled by the formatter
     */
    case M_CC_CS_10:
      if (cc_data->hold_state[cc_data->index_ti] EQ M_CC_HLD_RETRIEVE_REQ)
      {
        PALLOC (retrieve_cnf, MNCC_RETRIEVE_CNF);

        cc_data->hold_state[cc_data->index_ti] = M_CC_HLD_CALL_HELD;
        retrieve_cnf->ti = cc_data->ti;

        /*
         * It is possible for the network to send a REJ without a Cause IE.
         * This causes the decoder to flag a Madatory IE error. According to
         * 04.08 Sec: 8.5.3.e, the REJ should still be managed as if a correctly
         * formatted REJ had been received. 
         */
        if (!retrieve_rej->cc_cause.v_cause)
        {
          retrieve_cnf->cause = MNCC_CAUSE_NO_NET_CAUSE;
        }
        else
        {
          retrieve_cnf->cause = CAUSE_MAKE(DEFBY_STD, 
                                           ORIGSIDE_NET, 
                                           MNCC_CC_ORIGINATING_ENTITY, 
                                           retrieve_rej->cc_cause.cause);
        }
        PSENDX (MMI, retrieve_cnf);
        srv_use_stored_prim ();

        EM_CC_CALL_RETRIEVE_REJECT;

        CCD_END;
      }
      else
      {
        CCD_END;    
/* Implements Measure#  3 and streamline encoding */
        cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      }
      break;

    default:
      CCD_END;
/* Implements Measure#  3 and streamline encoding */
      cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      break;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_congestion_control      |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming congestion control message.

*/

GLOBAL void cc_congestion_control (T_B_CONGEST_CTRL * cong)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("cc_congestion_control()");

  switch (cc_data->state[cc_data->index_ti])
  {
      /*
       * CS_0 is handled by the formatter
       */
    case M_CC_CS_10:
      if (cc_check_error_flag ())
      {
        PALLOC (user, MNCC_USER_IND);

        cc_build_user_ind_from_cong (user, cong);
        PSENDX (MMI, user);
        CCD_END;
      }
      break;

    default:
      CCD_END;
/* Implements Measure#  3 and streamline encoding*/
      cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      break;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_user_information        |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming user information message.

*/

GLOBAL void cc_user_information ( T_B_USER_INFO * user)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("cc_user_information()");

  switch (cc_data->state[cc_data->index_ti])
  {
    /*
     * CS_0 is handled by the formatter
     */
    case M_CC_CS_10:
      if (cc_check_error_flag ())
      {
        PALLOC (user_ind, MNCC_USER_IND);
        
        cc_build_user_ind_from_user (user_ind, user);
        CCD_END;
        PSENDX (MMI, user_ind);
 
        EM_CC_USER_TO_USER_DATA_RECEIVED;

      }
      break;

    default:
      CCD_END;
/* Implements Measure#  4 and streamline encoding*/
      cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      break;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_modify                  |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming modify message.

*/

GLOBAL void cc_modify (T_B_MODIFY * modify)
{
  GET_INSTANCE_DATA;

  TRACE_FUNCTION ("cc_modify()");

  switch (cc_data->state[cc_data->index_ti])
  {
      /*
       * CS_0 is handled by the formatter
       */
    case M_CC_CS_10:
      if (cc_check_error_flag ())
      {
        switch (cc_data->neg_ri)
        {
          case M_CC_REPEAT_CIRCULAR:
            if (cc_data->active_service EQ cc_data->neg_serv1)
            {
              if (cc_bcs_compatible (&modify->bearer_cap,
                                     &cc_data->neg_bc2, TRUE) EQ TRUE)
              {
                CCD_END;
                cc_mod_complete ();
              }
              else
              {
                CCD_END;
                cc_mod_reject ();
              }
            }
            else
            {
              if (cc_bcs_compatible (&modify->bearer_cap,
                                     &cc_data->neg_bc1, TRUE) EQ TRUE)
              {
                CCD_END;
                cc_mod_complete ();
              }
              else
              {
                CCD_END;
                cc_mod_reject ();
              }
            }
            break;
#ifndef REL99
          case M_CC_REPEAT_SEQUENTIAL:
            if (cc_data->active_service EQ cc_data->neg_serv1)
            {
              if (cc_bcs_compatible (&modify->bearer_cap,
                                     &cc_data->neg_bc2, TRUE) EQ TRUE)
              {
                CCD_END;
                cc_mod_complete ();
              }
              else
              {
                CCD_END;
                cc_mod_reject ();
              }
            }
            else
            {
              CCD_END;
              cc_mod_reject ();
            }
            break;
#endif /* #ifndef REL99 */
          default:
            CCD_END;
            cc_mod_reject ();
            break;
        }
      }
      break;

    default:
      CCD_END;
/* Implements Measure#  5 and streamline encoding*/
      cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      break;
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_mod_reject              |
+--------------------------------------------------------------------+

  PURPOSE : Negative end of a mobile terminated in-call modification.

*/

GLOBAL void cc_mod_reject (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("cc_mod_reject()");

  CCD_START;
  {
    MCAST (modify_rej, B_MODIFY_REJ);


    cc_build_modify_reject (modify_rej,
                            CAUSE_MAKE(DEFBY_STD, 
                                       ORIGSIDE_MS, 
                                       MNCC_CC_ORIGINATING_ENTITY,
                                       M_CC_CAUSE_BEARER_CAP_AUTHORIZ));
    for_modify_reject (modify_rej);
  }

  EM_CC_MT_IN_CALL_MODIFICATION_FAILED;

  CCD_END;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_mod_complete            |
+--------------------------------------------------------------------+

  PURPOSE : Positive end of a mobile terminated in-call modification.

*/

GLOBAL void cc_mod_complete (void)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("cc_mod_complete()");
  
  CCD_START;
  {
    MCAST (modify_com, B_MODIFY_COMP);
    PALLOC (mod_ind, MNCC_MODIFY_IND);


    if (cc_data->active_service EQ cc_data->neg_serv1)
      cc_data->active_service = cc_data->neg_serv2;
    else
      cc_data->active_service = cc_data->neg_serv1;

    mod_ind->ti = cc_data->ti;
    mod_ind->serv = cc_data->active_service;
    PSENDX (MMI, mod_ind);

    EM_CC_MT_IN_CALL_MODIFICATION_PASSED;

    cc_build_modify_complete (modify_com);
    for_modify_complete (modify_com);
  }
  CCD_END;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_modify_complete         |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming modify complete message.

*/

GLOBAL void cc_modify_complete ( T_B_MODIFY_COMP * mod_com)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("cc_modify_complete()");

  switch (cc_data->state[cc_data->index_ti])
  {
    /*
     * CS_0 is handled by the formatter
     */
    case M_CC_CS_26:
      if (cc_check_error_flag ())
      {
        if (cc_data->new_itc EQ mod_com->bearer_cap.trans_cap)
        {
          PALLOC (mod_cnf, MNCC_MODIFY_CNF);

          TIMERSTOP (TIMER_CC);
          if (cc_data->active_service EQ cc_data->serv1)
            cc_data->active_service = cc_data->serv2;
          else
            cc_data->active_service = cc_data->serv1;
          mod_cnf->ti = cc_data->ti;
          mod_cnf->cause = MNCC_CAUSE_MODIFY_SUCCESS;
          /* Setting raw_cause to empty as we are not sending any cause from network*/ 
          mod_cnf->c_raw_cause = 0;

          EM_CC_MO_IN_CALL_MODIFICATION_PASSED;

          PSENDX (MMI, mod_cnf);
          cc_set_state (M_CC_CS_10);
        }
        CCD_END;
      }
      break;

    default:
      CCD_END;
/* Implements Measure#  6 and streamline encoding*/
      cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      break;
  }  
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_modify_reject           |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming modify reject message.

*/

GLOBAL void cc_modify_reject (T_B_MODIFY_REJ * mod_rej)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("cc_modify_reject()");

  switch (cc_data->state[cc_data->index_ti])
  {
    /*
     * CS_0 is handled by the formatter
     */
    case M_CC_CS_26:
      if (cc_check_error_flag ())
      {
        if (cc_data->old_itc EQ mod_rej->bearer_cap.trans_cap)
        {
          PALLOC (mod_cnf, MNCC_MODIFY_CNF);

          TIMERSTOP (TIMER_CC);
          mod_cnf->ti = cc_data->ti;
          /* 
           * cause IE is mandatory and the check for presence has been done
           * already thus there is no need to check for validity of cause IE
           * here anymore
           */
          mod_cnf->cause = CAUSE_MAKE(DEFBY_STD, 
                                      ORIGSIDE_NET, 
                                      MNCC_CC_ORIGINATING_ENTITY, 
                                      mod_rej->cc_cause.cause);
          mod_cnf->c_raw_cause = cc_build_cause (&mod_rej->cc_cause,
                                                  mod_cnf->raw_cause);
          PSENDX (MMI, mod_cnf);
          cc_set_state (M_CC_CS_10);
        }

        EM_CC_MO_IN_CALL_MODIFICATION_FAILED;

        CCD_END;
      }
      break;

    default:
      CCD_END;
/* Implements Measure#  7 and streamline encoding*/
      cc_send_status (M_CC_CAUSE_MESSAGE_TYPE_INCOMPAT);
      break;
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_status                  |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming status message.

*/

GLOBAL void cc_status (T_B_STATUS * status)
{
  GET_INSTANCE_DATA;
  TRACE_FUNCTION ("cc_status()");

  /* The cause is in STATUS a mandatory IE (given intact message) */
  TRACE_EVENT_P1 ("STATUS cause: %02x", status->cc_cause.cause);

  switch (cc_data->state[cc_data->index_ti])
  {
    case M_CC_CS_0:
      CCD_END;
      if (status->call_state.state NEQ M_CC_CS_0)
      {
        CCD_START;
        {
          MCAST (rel_com, U_RELEASE_COMP);
 
          cc_build_release_complete (rel_com,
                                     CAUSE_MAKE(DEFBY_STD, 
                                                ORIGSIDE_MS, 
                                                MNCC_CC_ORIGINATING_ENTITY,
                                                M_CC_CAUSE_MESSAGE_INCOMPAT));
          for_release_complete (rel_com);
        }
        CCD_END;
      }
      for_rel_req ();
      break;

    case M_CC_CS_1:
    case M_CC_CS_3:
    case M_CC_CS_4:
    case M_CC_CS_6:
    case M_CC_CS_7:
    case M_CC_CS_8:
    case M_CC_CS_9:
    case M_CC_CS_10:
    case M_CC_CS_26:
      if (cc_check_error_flag ())
      {
        /* check for incompatible state */
        if (status->call_state.state NEQ cc_data->state[cc_data->index_ti])
        {
          PALLOC (rel_ind, MNCC_RELEASE_IND);

          cc_reset_dtmf ();  // China change HM 11.07.00
          
          TIMERSTOP (TIMER_CC);

          rel_ind->ti    = cc_data->ti;

          rel_ind->cause = CAUSE_MAKE(DEFBY_STD, 
                                      ORIGSIDE_MS, 
                                      MNCC_CC_ORIGINATING_ENTITY, 
                                      M_CC_CAUSE_MESSAGE_INCOMPAT);
          rel_ind->c_raw_cause = 0;
          PSENDX (MMI, rel_ind);
 
          CCD_END;
          CCD_START;
          {
            MCAST (rel_com, U_RELEASE_COMP);

            cc_build_release_complete (rel_com,
                                       CAUSE_MAKE(DEFBY_STD, 
                                                  ORIGSIDE_MS, 
                                                  MNCC_CC_ORIGINATING_ENTITY,
                                                  M_CC_CAUSE_MESSAGE_INCOMPAT));
            for_release_complete (rel_com);
          }
          CCD_END;
          for_rel_req ();
          cc_set_state (M_CC_CS_0);
        }
        else 
        /* 
         * compatible, perform checks/ actions of 04.08, ch. 5.5.3.2.2;  
         * Condat action (option!) is to initiate normal call clearing 
         * (with DISCONNECT)
         */
        {
          if ((status->cc_cause.cause >= M_CC_CAUSE_INCORRECT_MESSAGE)
              AND
              (status->cc_cause.cause <= M_CC_CAUSE_COND_INFO_ELEM))
          {
            /* 
             * Block entered for 
             * CAUSE_INCORRECT_MESSAGE, CAUSE_INVALID_MAND_INFO,
             * CAUSE_MESSAGE_TYPE_NOT_IMPLEM, CAUSE_MESSAGE_TYPE_INCOMPAT,
             * CAUSE_INFO_ELEM_NOT_IMPLEM, CAUSE_COND_INFO_ELEM. (0x5f..0x64)
             */
            if ((cc_data->state[cc_data->index_ti] EQ M_CC_CS_10) AND
                (cc_data->hold_state[cc_data->index_ti] EQ M_CC_HLD_HOLD_REQ))
            {
              /* 
               * Compatible state, last CC message was a hold request which is
               * not supported by the network. Handled like HOLD REJECT.
               */
              PALLOC (hold_cnf, MNCC_HOLD_CNF); /* T_MNCC_HOLD_CNF */
              cc_data->hold_state[cc_data->index_ti] = M_CC_HLD_IDLE;
              hold_cnf->ti = cc_data->ti;
              hold_cnf->cause = CAUSE_MAKE(DEFBY_STD, 
                                           ORIGSIDE_NET, 
                                           MNCC_CC_ORIGINATING_ENTITY, 
                                           status->cc_cause.cause);
              PSENDX (MMI, hold_cnf);
              CCD_END;
              srv_use_stored_prim ();
            }
            else if (status->cc_cause.cause NEQ M_CC_CAUSE_INFO_ELEM_NOT_IMPLEM)
            {
              /* 
               * Compatible state, special condition for HOLD REQUEST not met.
               * The network is also not complaining about an optional 
               * information element which can be simply ignored.
               * Disconnect the call.
               */              
              PALLOC (disc_ind, MNCC_DISCONNECT_IND);

              TIMERSTOP (TIMER_CC);

              disc_ind->ti    = cc_data->ti;
              disc_ind->cause = CAUSE_MAKE(DEFBY_STD, 
                                           ORIGSIDE_NET, 
                                           MNCC_CC_ORIGINATING_ENTITY, 
                                           status->cc_cause.cause);
              disc_ind->c_raw_cause = cc_build_cause (&status->cc_cause,
                                                       disc_ind->raw_cause);
              disc_ind->diagnostic = NOT_PRESENT_8BIT;
              disc_ind->progress_desc = MNCC_PROG_NOT_PRES;
              PSENDX (MMI, disc_ind);

              CCD_END;
              CCD_START;
              {
                MCAST (disconnect, U_DISCONNECT);
 
                cc_build_disconnect (disconnect, 
                                     CAUSE_MAKE(DEFBY_STD, 
                                                ORIGSIDE_MS, 
                                                MNCC_CC_ORIGINATING_ENTITY,
                                                M_CC_CAUSE_CALL_CLEAR),
                                     NULL, MNCC_SS_VER_NOT_PRES);
                for_disconnect (disconnect);
              }
              TIMERSTART (T305, T305_VALUE);
              cc_set_state (M_CC_CS_11);
            }
          }
          else 
          {
            /* 
             * STATUS message but neither incompatible state nor indicating 
             * incorrect message / IE; check if we received a STATUS with cause
             * "message not compatible with protocol state". GSM 04.08 does not
             * define what to do and also does not define that this case has to 
             * be handled at all;
             * Condat (HM) decided to "clear DTMF" if it was running to try to 
             * recover from the misaligned states scenario.
             */
            T_DTMF *p_dtmf;

            p_dtmf = &cc_data->dtmf[cc_data->index_ti];
            if ((p_dtmf->state EQ DTMF_SEND_REQUEST) AND
                (status->cc_cause.cause EQ M_CC_CAUSE_MESSAGE_INCOMPAT))
            {
              /* Send negative acknowledge for DTMF send request to MMI */
              PALLOC (mncc_start_dtmf_cnf, MNCC_START_DTMF_CNF);
              mncc_start_dtmf_cnf->ti       = cc_data->ti;
              mncc_start_dtmf_cnf->key      = NOT_PRESENT_8BIT;
              mncc_start_dtmf_cnf->dtmf_mod = NOT_PRESENT_8BIT;
              mncc_start_dtmf_cnf->cause = CAUSE_MAKE(DEFBY_STD, 
                                                      ORIGSIDE_NET, 
                                                      MNCC_CC_ORIGINATING_ENTITY, 
                                                      M_CC_CAUSE_MESSAGE_INCOMPAT);
              PSENDX (MMI, mncc_start_dtmf_cnf);
            }

            /* Bring screwed up network back to work */
            for_stop_dtmf ();

            /* Reset screwed up DTMF state machine */
            p_dtmf->state = DTMF_IDLE;
            p_dtmf->read  = 0;             
            p_dtmf->write = 0;
          }
          CCD_END;
        }
      }
      break;

    case M_CC_CS_11:
    case M_CC_CS_12:
      if (cc_check_error_flag ())
      {
        if (status->call_state.state NEQ cc_data->state[cc_data->index_ti])
        {
          /* 
           * STATUS message with incompatible state
           * On receipt of a STATUS message reporting an incompatible call
           * control state, the receiving entity shall clear the call by 
           * sending a RELEASE COMPLETE message with cause # 101 "message 
           * not compatible with protocol state". The reported call control
           * state is incompatible if the combination of call control states
           * at the sender and receiver side cannot occur, do not match or cannot
           * be aligned by actions of the receiver; the exact definition is 
           * implementation dependent. [04.08 clause 5.5.3.2.1] 
           */
          cc_reset_dtmf ();
          
          TIMERSTOP (TIMER_CC);
          
          /* Send MNCC_RELEASE_IND to MMI */
          {
            PALLOC (rel_ind, MNCC_RELEASE_IND); /* T_MNCC_RELEASE_IND */
            rel_ind->ti    = cc_data->ti;
            rel_ind->cause = CAUSE_MAKE(DEFBY_STD, 
                                        ORIGSIDE_MS, 
                                        MNCC_CC_ORIGINATING_ENTITY, 
                                        M_CC_CAUSE_MESSAGE_INCOMPAT);
            rel_ind->c_raw_cause = 0;
            PSENDX (MMI, rel_ind);
          }

          CCD_END;
          CCD_START;
          {
            MCAST (rel_com, U_RELEASE_COMP); /* T_U_RELEASE_COMP */
            cc_build_release_complete (rel_com, 
                                       CAUSE_MAKE(DEFBY_STD, 
                                                  ORIGSIDE_MS, 
                                                  MNCC_CC_ORIGINATING_ENTITY, 
                                                  M_CC_CAUSE_MESSAGE_INCOMPAT));
            for_release_complete (rel_com);
            for_rel_req ();
            cc_set_state (M_CC_CS_0);
          }
        }
        CCD_END;
      }
      break;

    case M_CC_CS_19:
      if (cc_check_error_flag ())
      {
        if (status->call_state.state NEQ cc_data->state[cc_data->index_ti])
        {
          TIMERSTOP (TIMER_CC);

          cc_reset_dtmf ();

          {
            PALLOC (rel_cnf, MNCC_RELEASE_CNF); /* T_MNCC_RELEASE_CNF */
            rel_cnf->ti    = cc_data->ti;
            rel_cnf->cause = CAUSE_MAKE(DEFBY_STD, 
                                        ORIGSIDE_MS, 
                                        MNCC_CC_ORIGINATING_ENTITY, 
                                        M_CC_CAUSE_MESSAGE_INCOMPAT);
            /* Setting raw_cause to empty as this is a local release
             * of MM connection,CC is not receiving any cause value
             * from Network
             */
            rel_cnf->c_raw_cause = 0;
            PSENDX (MMI, rel_cnf);
          }

          CCD_END;
          CCD_START;
          {
            MCAST (rel_com, U_RELEASE_COMP); /* T_U_RELEASE_COMP */
            cc_build_release_complete (rel_com, 
                                       CAUSE_MAKE(DEFBY_STD, 
                                                  ORIGSIDE_MS, 
                                                  MNCC_CC_ORIGINATING_ENTITY, 
                                                  M_CC_CAUSE_MESSAGE_INCOMPAT));
            for_release_complete (rel_com);
            for_rel_req ();
            cc_set_state (M_CC_CS_0);
          }
        }
        CCD_END;
      }
      break;

    default:
      CCD_END;
      break;
  }

  EM_CC_STATUS_RECEIVED;

}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_status_enquiry          |
+--------------------------------------------------------------------+

  PURPOSE : Processing an incoming status enquiry message.

*/

GLOBAL void cc_status_enquiry (void)
{
  GET_INSTANCE_DATA;
 
  TRACE_FUNCTION ("cc_status_enquiry()");

  /*
   * CS_0 is handled by the formatter
   */
  switch (cc_data->state[cc_data->index_ti])
  {
    case M_CC_CS_10:
      /*
       * Ensure synchronization of auxiliary states with ACI.
       */
      if (cc_check_error_flag ())
      {
        CCD_END;
        {
          PALLOC (mncc_status_ind, MNCC_STATUS_IND);
          mncc_status_ind->ti = cc_data->ti;
          PSENDX (MMI, mncc_status_ind);
        }

      EM_CC_STATUS_ENQUIRY_RECEIVED;

      }
      break;

    default:
      /*
       * No auxiliary states in non-active states. Immediate response is safe.
       */
      if (cc_check_error_flag ())
      {
        CCD_END;
        /* EM call moved above cc_send_status should not have any impact */
        EM_CC_STATUS_ENQUIRY_RECEIVED;

/* Implements Measure#  7 and streamline encoding*/
        cc_send_status (M_CC_CAUSE_STATUS_ENQUIRY);  
      }
      break;
  }
}


/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_unknown_message         |
+--------------------------------------------------------------------+

  PURPOSE : Processing of an incoming unknown message.

*/

GLOBAL void cc_unknown_message (void)
{

  CCD_END;
  CCD_START;
  {
    MCAST (status, B_STATUS);

    TRACE_FUNCTION ("cc_unknown_message()");

    cc_build_status (status, 
                     CAUSE_MAKE(DEFBY_STD,
                                ORIGSIDE_MS,
                                MNCC_CC_ORIGINATING_ENTITY,
                                M_CC_CAUSE_MESSAGE_TYPE_NOT_IMPLEM));
    for_status (status);
  }
  CCD_END;
}

/* Implements Measure#  18 */
/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (6147)       MODULE  : CC_ACT                     |
| STATE   : code                ROUTINE : cc_mncc_hold_retrieve_req  |
+--------------------------------------------------------------------+

  PURPOSE : Send hold REQ/retrieve REQ.

*/

LOCAL void cc_mncc_hold_retrieve_req (T_PRIM * prim)
{ 
  GET_INSTANCE_DATA;
  UBYTE      ti;

  TRACE_FUNCTION ("cc_mncc_hold_retrieve_req()");

  if(prim->custom.opc EQ MNCC_HOLD_REQ)
  {
    ti = ((T_MNCC_HOLD_REQ *)P2D(prim))->ti;    
  }
  else
  {
    ti = ((T_MNCC_RETRIEVE_REQ *)P2D(prim))->ti;
  }
  if ((cc_data->index_ti = srv_convert_ti (ti)) EQ NOT_PRESENT_8BIT)
  {
    PFREE (P2D(prim));
    return;
  }
  
  switch (cc_data->state[cc_data->index_ti])
  {
    case M_CC_CS_10:
      if(prim->custom.opc EQ MNCC_HOLD_REQ)
      {
        /* We should think about storing the hold request 
           until DTMF idle state reached. ...
           This causes some other changes, we have to add some 
           calls to serv_use_stored_prim() at some places after
           state transition of the DTMF state machine to DTMF idle.*/
        cc_data->hold_state[cc_data->index_ti] = M_CC_HLD_HOLD_REQ;
        for_hold ();
        EM_CC_CALL_HOLD;
      }
      else
      {
        cc_data->hold_state[cc_data->index_ti] = M_CC_HLD_RETRIEVE_REQ;
        for_retrieve ();
        EM_CC_CALL_RETRIEVE;
      }
      PFREE (P2D(prim));     

      break;

    case CS_101:
    case M_CC_CS_26:
    case CS_261:
      srv_store_prim (prim);
      break;

    default:
      PFREE (P2D(prim));
      break;
  }
}

#endif