/* 
+----------------------------------------------------------------------------- 
|  Project :  
|  Modul   :  
+----------------------------------------------------------------------------- 
|  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 implements the set fuinctions related to the
|             protocol stack adapter for GPRS session management ( SM ).
+----------------------------------------------------------------------------- 
*/ 

#if defined (GPRS) && defined (DTI)

#ifndef CMH_SMS_C
#define CMH_SMS_C
#endif

#include "aci_all.h"

/*==== INCLUDES ===================================================*/
#include "dti.h"      /* functionality of the dti library */
#include "aci_cmh.h"
#include "ati_cmd.h"
#include "aci_cmd.h"
#include "aci_io.h"

#include "dti_conn_mng.h"
#include "dti_cntrl_mng.h"

#include "gaci.h"
#include "gaci_cmh.h"
#include "psa.h"
#include "psa_sm.h"
#include "psa_gppp.h"
#include "psa_gmm.h"
#include "psa_tcpip.h"

#include "cmh.h"
#include "cmh_sm.h"
#include "cmh_gppp.h"
#include "cmh_gmm.h"
#include "gaci_srcc.h"
#include "aci_mem.h"
#include "phb.h"
#include "wap_aci.h"

/*==== CONSTANTS ==================================================*/

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

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

/*==== FUNCTIONS ==================================================*/
LOCAL void string_to_dns(CHAR* dns, ULONG *dns_long);
/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (8441)         MODULE  : CMH_SMS                  |
| STATE   : finnished             ROUTINE : sAT_PlusCGQREQ           |
+--------------------------------------------------------------------+

PURPOSE : This is the functional counterpart to the +CGQREQ= AT
          command which sets the requested QOS.
*/
GLOBAL T_ACI_RETURN sAT_PlusCGQREQ
            ( T_ACI_CMD_SRC srcId, SHORT cid ,T_QOS *qos)
{
  T_CONTEXT_STATE c_state;      /* state of context */


  TRACE_FUNCTION ("sAT_PlusCGQREQ()");

/*
 *-------------------------------------------------------------------
 * check entity status
 *-------------------------------------------------------------------
 */
  if( smEntStat.curCmd NEQ AT_CMD_NONE )

    return( AT_BUSY );

/*
 *-------------------------------------------------------------------
 * check parameter
 *-------------------------------------------------------------------
 */
  if ( (cid < GPRS_CID_OMITTED) || (cid >= GPRS_CID_INVALID) )
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return AT_FAIL;
  }

  if ( qos )
  {
    if ( /* qos->preced < GPRS_QOS_OMITTED || */ qos->preced > 3 )
    {
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
    }

    if ( /* qos->delay < GPRS_QOS_OMITTED || */ qos->delay > 4 )
    {
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
    }
    if ( /* qos->relclass < GPRS_QOS_OMITTED || */ qos->relclass > 5 )
    {
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
    }
    if ( /* qos->peak < GPRS_QOS_OMITTED || */ qos->peak > 9 )
    {
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
    }
    if ( (/* qos->mean < GPRS_QOS_OMITTED || */ qos->mean > 18) && qos->mean NEQ 31 )
    {
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
    }
  }

/*
 *-------------------------------------------------------------------
 * process parameter
 *-------------------------------------------------------------------
 */
  if (cid EQ GPRS_CID_OMITTED )
 /*
  *   cid omitted: A special form of the set command that is not defined in the Spec.
  *   This set the default value of QoS.
  */
  {
    cmhSM_change_def_QOS(qos);

  }
  else
  {
    c_state = get_state_over_cid( cid );

    if ( !qos ||  (qos->preced    EQ GPRS_QOS_OMITTED &&
                   qos->delay     EQ GPRS_QOS_OMITTED &&
                   qos->relclass  EQ GPRS_QOS_OMITTED &&
                   qos->peak      EQ GPRS_QOS_OMITTED &&
                   qos->mean      EQ GPRS_QOS_OMITTED     ) )
    { /* QoS omitted ->  undefine the requested QOS */
      if ( c_state NEQ CS_DEFINED )
      {
        ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
        return( AT_FAIL );
      }

      cmhSM_Set_default_QOS(cid);

    }
    else
    { /* define the requested QOS */
      if ( c_state NEQ CS_DEFINED )
      {
        ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
        return( AT_FAIL );
      }

      memcpy( &pdp_context[cid - 1].con.qos, qos, sizeof(T_QOS));
    }
  }

  return AT_CMPL;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (8441)         MODULE  : CMH_SMS                  |
| STATE   : finnished             ROUTINE : sAT_PlusCGQMIN           |
+--------------------------------------------------------------------+

PURPOSE : This is the functional counterpart to the +CGQMIN= AT
          command which sets the minimum acceptable QOS.
*/
GLOBAL T_ACI_RETURN sAT_PlusCGQMIN
            ( T_ACI_CMD_SRC srcId, SHORT cid ,T_QOS *qos)
{
  T_CONTEXT_STATE c_state;      /* state of context */

  TRACE_FUNCTION ("sAT_PlusCGQMIN()");

/*
 *-------------------------------------------------------------------
 * check entity status
 *-------------------------------------------------------------------
 */
  if( smEntStat.curCmd NEQ AT_CMD_NONE )

    return( AT_BUSY );

/*
 *-------------------------------------------------------------------
 * check parameter
 *-------------------------------------------------------------------
 */
  if ( (cid < GPRS_CID_OMITTED) || (cid >= GPRS_CID_INVALID) )
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return AT_FAIL;
  }

  if ( qos )
  {
    if ( /* qos->preced < GPRS_QOS_OMITTED || */ qos->preced > 3 )
    {
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
    }

    if ( /* qos->delay < GPRS_QOS_OMITTED || */ qos->delay > 4 )
    {
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
    }
    if ( /* qos->relclass < GPRS_QOS_OMITTED || */ qos->relclass > 5 )
    {
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
    }
    if ( /* qos->peak < GPRS_QOS_OMITTED || */ qos->peak > 9 )
    {
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
    }
    if ( (/* qos->mean < GPRS_QOS_OMITTED || */ qos->mean > 18) && qos->mean NEQ 31 )
    {
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
    }
  }

/*
*-------------------------------------------------------------------
* process parameter
*-------------------------------------------------------------------
*/
  if (cid EQ GPRS_CID_OMITTED )
 /*
  *   cid omitted: A special form of the set command that is not defined in the Spec.
  *   This set the default value of QoS.
  */
  {
    cmhSM_change_def_QOS_min(qos);
  }
  else
  {
    c_state = get_state_over_cid( cid );

    if ( !qos ||  (qos->preced    EQ GPRS_QOS_OMITTED &&
                   qos->delay     EQ GPRS_QOS_OMITTED &&
                   qos->relclass  EQ GPRS_QOS_OMITTED &&
                   qos->peak      EQ GPRS_QOS_OMITTED &&
                   qos->mean      EQ GPRS_QOS_OMITTED     ) )
    { /* QoS omitted ->  undefine the requested QOS */
      if ( c_state NEQ CS_DEFINED )
      {
        ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
        return( AT_FAIL );
      }

      cmhSM_Set_default_QOS_min(cid);
    }
    else
    { /* define the requested QOS */
      if ( c_state NEQ CS_DEFINED )
      {
        ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
        return( AT_FAIL );
      }

      memcpy( &pdp_context[cid - 1].con.min_qos, qos, sizeof(T_QOS));
    }

  }

  return AT_CMPL;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (8441)         MODULE  : CMH_SMS                  |
| STATE   : finnished             ROUTINE : sAT_PlusCGDCONT          |
+--------------------------------------------------------------------+

PURPOSE : This is the functional counterpart to the +CGDCONT= AT
          command which sets the current setting for each context.

          GACI Context Definition GSM - 7.60 10.2.1

          Special case +CGDCONT=<n> undefines context n
          is handled as separate function call

          otherwise:

+CGDCONT=[<cid> [,<PDP_TYPE> [,<APN> [,<PDP_addr> [,<h_comp> [,<d_comp>]]]]]]

 Issue of what happens if user changes data of an active context.

 Take simple approach, do not renegotiate current context.
 Undefinition is more complex, reject attempt if context is active?

 Current pdp address is left alone and only reset when context is
 explicitly undefined.
 See GSM 7.60 10.2.7.

*/
GLOBAL T_ACI_RETURN sAT_PlusCGDCONT ( T_ACI_CMD_SRC srcId, SHORT cid, T_GPRS_CONT_REC *inputCtxt)
{
  T_CONTEXT_STATE c_state;      /* state of context */
  SHORT state = 0;

  TRACE_FUNCTION ("sAT_PlusCGDCONT()");

/*
 *-------------------------------------------------------------------
 * check parameter
 *-------------------------------------------------------------------
 */
  /* Daniel : to convert the lower-case "ip"  
               to the upper-case "IP" as a pdp type */
  if (!strcmp(inputCtxt->pdp_type, "ip"))
    strcpy(inputCtxt->pdp_type, "IP");
  if ( (cid < GPRS_CID_OMITTED) || (cid >= GPRS_CID_INVALID) )
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return AT_FAIL;
  }

  if ( inputCtxt->d_comp < CGDCONT_D_COMP_OMITTED || inputCtxt->d_comp >= CGDCONT_D_COMP_INVALID )
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return( AT_FAIL );
  }

  /* right now data compression is not supported, remove this block if enabled in the future */
  if ( inputCtxt->d_comp EQ CGDCONT_D_COMP_ON )
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return( AT_FAIL );
  }

  if ( inputCtxt->h_comp < CGDCONT_H_COMP_OMITTED || inputCtxt->h_comp >= CGDCONT_H_COMP_INVALID )
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return( AT_FAIL );
  }

/*
 *-------------------------------------------------------------------
 * A special form of set command
 *-------------------------------------------------------------------
 */
  if (  !(cid NEQ GPRS_CID_OMITTED             &&
          !*(inputCtxt->pdp_type) && !*(inputCtxt->apn) && !*(inputCtxt->pdp_addr)  &&
          inputCtxt->d_comp EQ CGDCONT_D_COMP_OMITTED   && inputCtxt->h_comp EQ CGDCONT_H_COMP_OMITTED) )
  {
  /*
   *-------------------------------------------------------------------
   * check parameter
   *-------------------------------------------------------------------
   */
    if ( !*(inputCtxt->pdp_type) && *(inputCtxt->pdp_addr) )
      {
        ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
        return AT_FAIL;
      }

    if ( *(inputCtxt->pdp_addr) && *(inputCtxt->pdp_type) )
      if ( !cmhSM_pdp_addr_well_formed( cmhSM_transform_pdp_type( inputCtxt->pdp_type ), inputCtxt->pdp_addr ) )
      {
        ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
        return AT_FAIL;
      }

    if ( *(inputCtxt->apn) )
      if ( !cmhSM_apn_well_formed( inputCtxt->apn ) )
      {
        ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
        return AT_FAIL;
      }

  /*
   *-------------------------------------------------------------------
   * default parameter
   *-------------------------------------------------------------------
   */
    if ( inputCtxt->d_comp EQ CGDCONT_D_COMP_OMITTED )
      inputCtxt->d_comp = CGDCONT_D_COMP_OFF;

    if ( inputCtxt->h_comp EQ CGDCONT_H_COMP_OMITTED )
      inputCtxt->h_comp = CGDCONT_H_COMP_OFF;

    if ( cid EQ GPRS_CID_OMITTED )
    {
      do
      {
        c_state = get_state_over_cid( ++ cid );
      }
      while ( cid < GPRS_CID_INVALID && c_state NEQ CS_UNDEFINED );

      if( c_state NEQ CS_UNDEFINED )
      {
        ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
        return( AT_FAIL );
      }
    }

    if ( !*(inputCtxt->pdp_type) )
      strcpy(inputCtxt->pdp_type, "IP");

    state = 1;
  }

/*
 *-------------------------------------------------------------------
 * process parameter
 *-------------------------------------------------------------------
 */
  c_state = get_state_over_cid( cid );

  switch ( state )
  {

    case 0: /* undefine a PDP context */
      if ( c_state NEQ CS_DEFINED )
      {
        ACI_ERR_DESC( ACI_ERR_CLASS_Cme, CME_ERR_OpNotAllow );
        return( AT_FAIL );
      }

      set_state_over_cid( cid, CS_UNDEFINED );

      break;
    case 1: /* define a PDP context */
      switch(c_state)
      {
          /* every time allowed */
        case CS_INVALID_STATE:
        case CS_UNDEFINED:
        case CS_DEFINED:
          c_state = CS_DEFINED;
          break;
          /* allowed during context deactivation, but
             WITHOUT state change                     */
        case CS_ABORT_ESTABLISH:
        case CS_DEACTIVATE_NORMAL:
        case CS_BREAKDOWN_LINK_NORMAL:
        case CS_BREAKDOWN_LINK_ERROR:
        case CS_CONTEXT_REACTIVATION_1:
        case CS_CONTEXT_REACTIVATION_2:
          break;
          /* Not allowed during context activation or
             for activated context                    */
        case CS_ATTACHING_AFTER_UNDEF:
        case CS_ATTACHING_AFTER_DEF:
        case CS_ESTABLISH_1:
        case CS_ESTABLISH_2:
        case CS_ESTABLISH_3:
        case CS_WAITS_FOR_ACTIVATING:
        case CS_ACTIVATING:
        case CS_ACTIVATED:
        case CS_DATA_LINK:
        default:
        ACI_ERR_DESC( ACI_ERR_CLASS_Cme, CME_ERR_OpNotAllow );
        return( AT_FAIL );
      }

      sAT_PlusCGDCONT_exec(srcId, cid, inputCtxt);
      set_state_over_cid( cid, c_state );

      break;
  }

  return AT_CMPL;
}

GLOBAL void sAT_PlusCGDCONT_exec ( T_ACI_CMD_SRC srcId, SHORT cid, T_GPRS_CONT_REC *inputCtxt)
{
  T_GPRS_CONT_REC *con = &pdp_context[cid - 1].con;

  TRACE_FUNCTION("sAT_PlusCGDCONT_exec");

  memcpy(&con->apn,       &inputCtxt->apn,      sizeof( T_APN ));
  memcpy(&con->pdp_type,  &inputCtxt->pdp_type, sizeof( T_PDP_TYPE ));
  memcpy(&con->pdp_addr,  &inputCtxt->pdp_addr, sizeof( T_PDP_ADDRESS ));
  con->d_comp = inputCtxt->d_comp;
  con->h_comp = inputCtxt->h_comp;
  
  if ( CS_DEFINED NEQ get_state_over_cid( cid ) )
  {
    cmhSM_Set_default_QOS     ( cid );
    cmhSM_Set_default_QOS_min ( cid );
  }

  set_state_over_cid( cid, CS_DEFINED );

}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (8441)         MODULE  : CMH_SMS                  |
| STATE   : finnished             ROUTINE : sAT_PlusCGACT            |
+--------------------------------------------------------------------+

PURPOSE : This is the functional counterpart to the +CGACT= AT
          command which causes the cids specified in the cids list to
          be activated or deactivated according to state.

          An empty list will cause all defined contexts to be
          activated or deactivated. If taken literally, this means that
          if more contexts are defined than supported, each will be
          activated, resulting in 'no resource' errors for the late ones
          as the GACI SEM will reject requests for more activations
          than it can cope with.

          Note that the context is/are activated, but no CONNECT is sent
          to the TE. This is the difference between ACT and DATA commands.

          SMREG activate req does not need l2p to be sent, but in this case
          the PDP config options are undefined (see below).

          How does a DATA call bind these 'orphan' connections to a TE given
          that a cid definition is a 'template' due to its ambiguity.

          Practically, the activate form of this command has little meaning in the
          case of PPP and loopback protocols (only ones supported at present).

          Simplest option at the moment is not to support the activate form until
          a protocol type is supported can make real use of it. The deactivate form
          is of use in switching off a Loopback connection.

          If activation before protocol establishment is supported, a NULL protocol service
          will have to be provided which supplies a default (empty?) PCO list to SMREG for
          activation and stores the network PCO response until a CGDATA is issued, must then
          convert the protocol into that requested by the CGDATA command.
          For future implementation


          Other issues for multiple context activation :

          - need to add a para onto GACI activate to tell it whether
          to do a CONNECT or an OK callback on activation.

*/
GLOBAL T_ACI_RETURN sAT_PlusCGACT
            ( T_ACI_CMD_SRC srcId, T_CGACT_STATE state, SHORT *cids )
{
  T_CONTEXT_STATE ctx_state;
  SHORT i = 0, j = 0;
  int ret_val;
  TRACE_FUNCTION ("sAT_PlusCGACT()");

/*
 *-------------------------------------------------------------------
 * check command source
 *-------------------------------------------------------------------
 */
  if(!cmh_IsVldCmdSrc (srcId))
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return( AT_FAIL );
  }

/*
 *-------------------------------------------------------------------
 * check parameter
 *-------------------------------------------------------------------
 */
  if ( (state < CGACT_STATE_OMITTED) || (state >= CGACT_STATE_INVALID) )
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return AT_FAIL;
  }

  for (i = 0; cids[i] NEQ INVALID_CID; i++)
  {
    if ( (cids[i] < GPRS_CID_1 || cids[i] >= GPRS_CID_INVALID) || i >= MAX_CID_PLUS_EINS )
    {
      ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
      return( AT_FAIL );
    }
  }

/*
 *-------------------------------------------------------------------
 * default parameter
 *-------------------------------------------------------------------
 */
  if ( state EQ CGACT_STATE_OMITTED )
  { /* state is not optional */
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return AT_FAIL;
  }

/*
 *-------------------------------------------------------------------
 * enable +CGACT to deactivate a context during activation
 *-------------------------------------------------------------------
 */
  if( CGACT_STATE_DEACTIVATED EQ state )
  {
    ret_val = cmhSM_deactivateContexts(srcId, cids);
    switch(ret_val)
    {
      case AT_EXCT:
       smEntStat.curCmd = AT_CMD_CGACT;
       smEntStat.entOwn = srcId;
       smShrdPrm.owner  = (UBYTE) srcId;
       return AT_EXCT;
      default:
       return ret_val;
    }
  }

/*
 *-------------------------------------------------------------------
 * check entity status
 *-------------------------------------------------------------------
 */
  if( smEntStat.curCmd NEQ AT_CMD_NONE )
    return( AT_BUSY );

  if( gpppEntStat.curCmd EQ AT_CMD_CGDATA )
    return( AT_BUSY );


/*
 *-------------------------------------------------------------------
 * process parameter
 *-------------------------------------------------------------------
 */

  cid_pointer = 0;

  if ( *cids EQ INVALID_CID ) /* all defined or activated contexts (dependent by state) */
  {
    for (i = 0; i < MAX_CID; i++)
    {
        if ( CS_DEFINED EQ get_state_over_cid((SHORT) (i + 1)) )
        {
          work_cids[j] = i + 1;
          j++;
        }
      }
    work_cids[j] = INVALID_CID;

    if (!*work_cids)
      return AT_CMPL;
  }
  else   /* all declarated contexts */
  {
    /* copy cid list */
    for (i = 0; cids[i] NEQ INVALID_CID; i++)
    {
      work_cids[i] = cids[i];
    }
    work_cids[i] = INVALID_CID;

    for (j = 0; work_cids[j] NEQ INVALID_CID; j++)
    {
      ctx_state = get_state_over_cid( work_cids[j] );

        if ( CS_UNDEFINED EQ ctx_state )
        { /* context not defined */
          sAT_PlusCGDCONT_exec (srcId, work_cids[j], &defaultCtx);
          set_state_over_cid(work_cids[j], CS_UNDEFINED);
        }
      else if ( CS_DEFINED NEQ ctx_state )
          {
            cid_pointer = 0;
            work_cids[0] = INVALID_CID;
            return ( AT_FAIL );
          }
        }
      }

 /*
  *-------------------------------------------------------------------
  * check number of context
  *-------------------------------------------------------------------
  */
  TRACE_EVENT("activating context!");
      if ( TRUE NEQ srcc_reserve_sources( SRCC_NULL_SNDCP_LINK, j ) )
  {
    cid_pointer  = 0;
    *work_cids = 0;
    return AT_FAIL;
  }

      smEntStat.curCmd = AT_CMD_CGACT;
      smEntStat.entOwn = srcId;
      smShrdPrm.owner  = (UBYTE) srcId;

      set_conn_param_on_all_working_cids( (UBYTE)srcId, DTI_ENTITY_NULL );
  if (AT_FAIL EQ cmhSM_activate_context())
  {
     set_conn_param_on_all_working_cids( (UBYTE)srcId, DTI_ENTITY_INVALID );
     smEntStat.curCmd = AT_CMD_NONE;
     gpppEntStat.curCmd = AT_CMD_NONE;
     cid_pointer  = 0;
     *work_cids = 0;
    return AT_FAIL;
  }

  return AT_EXCT;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (8441)         MODULE  : CMH_SMS                  |
| STATE   : finnished             ROUTINE : sAT_PlusCGDATA           |
+--------------------------------------------------------------------+

PURPOSE : This is the functional counterpart to the +CGDATA= AT
          command which establish the communication.
*/
GLOBAL T_ACI_RETURN sAT_PlusCGDATA  ( T_ACI_CMD_SRC srcId, char *L2P, SHORT *cids )
{
  SHORT j = 0;
  T_ACI_RETURN    retCd     = AT_CMPL;             /* holds return code */
  T_DTI_ENTITY_ID connectToEntity = DTI_ENTITY_INVALID;

  TRACE_FUNCTION ("sAT_PlusCGDATA()");

/*
 *-------------------------------------------------------------------
 * check command source
 *-------------------------------------------------------------------
 */
  if(!cmh_IsVldCmdSrc (srcId))
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return( AT_FAIL );
  }


/*
 *-------------------------------------------------------------------
 * check entity status
 *-------------------------------------------------------------------
 */
  if( smEntStat.curCmd NEQ AT_CMD_NONE )
    return( AT_BUSY );

  if( gpppEntStat.curCmd NEQ AT_CMD_NONE )
    return( AT_BUSY );

  if (pb_get_fdn_mode () EQ FDN_ENABLE)
  {
    if (pb_check_fdn (0, (const UBYTE *)"*99#") NEQ PHB_OK)
    {
      TRACE_EVENT("sAT_PlusCGDATA: Entry not found in FDN, GPRS not allowed.");
      ACI_ERR_DESC( ACI_ERR_CLASS_Cme, CME_ERR_OpNotAllow);
      return (AT_FAIL);
    }
    TRACE_EVENT("sAT_PlusCGDATA: Entry found in FDN, GPRS allowed.");
  }

/*
 *-------------------------------------------------------------------
 * check parameter
 *-------------------------------------------------------------------
 */
  /* 
   *  in maximum 1 cid allowed 
   */
  if ( GPRS_CID_OMITTED NEQ cids[0] &&
     GPRS_CID_OMITTED NEQ cids[1]   )
  {
    return AT_FAIL;
  }


  if ( L2P[0] EQ 0 )
    strcpy (L2P, "PPP"); /* default value */
  
  if ( !strcmp(L2P, "PPP"))
  {
    if ( TRUE NEQ srcc_reserve_sources( SRCC_PPPS_SNDCP_LINK, 1 ) )
      return ( AT_FAIL );
    
    connectToEntity = DTI_ENTITY_PPPS;
  }
#if defined(FF_PKTIO) OR defined(FF_TCP_IP) OR defined(FF_PSI)
  else if( !strcmp(L2P, "M-PKT") OR !strcmp(L2P, "M-IP")) 
  {
    if ( TRUE NEQ srcc_reserve_sources( SRCC_PKTIO_SNDCP_LINK, 1 ) )
      return ( AT_FAIL );
  }
#endif /* FF_PKTIO OR FF_TCP_IP OR FF_PSI */
  else 
  {
    return ( AT_FAIL );
  }

/*
 *-------------------------------------------------------------------
 * process parameter
 *-------------------------------------------------------------------
 */
  j = cmhSM_define_cid_list( srcId, cids );

  if ( !j )
    return( AT_FAIL );

/*
 *-------------------------------------------------------------------
 * process function
 *-------------------------------------------------------------------
 */
  smShrdPrm.direc = DIREC_MO;

  set_conn_param_on_working_cid( (UBYTE)srcId, connectToEntity );

  if( DTI_ENTITY_PPPS EQ connectToEntity )
  {
    retCd = cmhGMM_attach_if_necessary( srcId, AT_CMD_CGDATA );
    gpppEntStat.curCmd = AT_CMD_CGDATA;
    gpppEntStat.entOwn = srcId;
    gpppShrdPrm.owner  = (UBYTE) srcId;
  }
  else if( DTI_ENTITY_INVALID EQ connectToEntity )
  {
    smEntStat.curCmd = AT_CMD_CGDATA;
    smEntStat.entOwn = srcId;
    smShrdPrm.owner  = (UBYTE) srcId;  
  }

  if ( retCd EQ AT_CMPL )
    cmhSM_data_link_context();
  else
  {
    if ( retCd EQ AT_EXCT )
      switch ( get_state_working_cid() )
      {
        case CS_DEFINED:
          set_state_working_cid( CS_ATTACHING_AFTER_DEF );
          break;
        case CS_UNDEFINED:
          set_state_working_cid( CS_ATTACHING_AFTER_UNDEF );
          break;
      }
    else
    {
      smEntStat.curCmd  = AT_CMD_NONE;
      gpppEntStat.curCmd = AT_CMD_NONE;
      return AT_FAIL;
    }
  }

  return AT_EXCT;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (8441)         MODULE  : CMH_SMS                  |
| STATE   : finnished             ROUTINE : sAT_PlusCGPADDR          |
+--------------------------------------------------------------------+

PURPOSE : This is the functional counterpart to the +CGPADDR= AT
          command which give the PDP address back.
*/
GLOBAL T_ACI_RETURN sAT_PlusCGPADDR ( T_ACI_CMD_SRC srcId, SHORT *cids, T_PDP_ADDRESS *pdp_adress )
{
  SHORT cid = 1, index = 0;

  TRACE_FUNCTION ("sAT_PlusCGPADDR()");

/*
 *-------------------------------------------------------------------
 * check entity status
 *-------------------------------------------------------------------
 */
  if( smEntStat.curCmd NEQ AT_CMD_NONE )
    return( AT_BUSY );

/*
 *-------------------------------------------------------------------
 * process parameter
 *-------------------------------------------------------------------
 */
  if ( *cids EQ GPRS_CID_OMITTED )
  {
    /*
     *  the PDP addresse for all defined contexts are returned
     */
    for (cid = 1; cid <= MAX_CID; cid++)
    {
      cids[index] = cmhSM_get_pdp_addr_for_CGPADDR( cid, pdp_adress[index] );
      if ( cids[index] NEQ INVALID_CID )
        index ++;
    }

    cids[index] = INVALID_CID;
  }
  else
  {
    /*
     *  the PDP addresse for all specified contexts are returned
     */
    while ( cids[index] NEQ GPRS_CID_OMITTED )
    {
      if ( cids[index] < GPRS_CID_1 || cids[index] >= GPRS_CID_INVALID )
        return AT_FAIL;

      cmhSM_get_pdp_addr_for_CGPADDR( cids[index], pdp_adress[index] );
      index ++;
    }
    cids[index] = INVALID_CID;
  }

  return AT_CMPL;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (8441)         MODULE  : CMH_SMS                  |
| STATE   : finnished             ROUTINE : sAT_PlusCGAUTO           |
+--------------------------------------------------------------------+

PURPOSE : This is the functional counterpart to the +CGAUTO= AT
          command which set the mode of automatic response to
          network request for PDP context activation.
*/
GLOBAL T_ACI_RETURN sAT_PlusCGAUTO ( T_ACI_CMD_SRC srcId, T_CGAUTO_N n )
{
  TRACE_FUNCTION ("sAT_PlusCGAUTO()");

/*
 *-------------------------------------------------------------------
 * check entity status
 *-------------------------------------------------------------------
 */
  if( smEntStat.curCmd NEQ AT_CMD_NONE )

    return( AT_BUSY );

/*
 *-------------------------------------------------------------------
 * check parameter
 *-------------------------------------------------------------------
 */
  if ( (n < CGAUTO_N_OMITTED) || (n >= CGAUTO_N_INVALID) )
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return AT_FAIL;
  }

/*
 *-------------------------------------------------------------------
 * default parameter
 *-------------------------------------------------------------------
 */
  if ( n EQ CGAUTO_N_OMITTED )
    n = CGAUTO_N_MCM_GPRS_CSC;

/*
 *-------------------------------------------------------------------
 * process parameter
 *-------------------------------------------------------------------
 */
  automatic_response_mode = (SHORT) n;

  /* the MT shall attempt to perform a GPRS attach if it is not already attached */
  if ( n EQ 1 )
  {
    return sAT_PlusCGATT ( srcId, CGATT_STATE_ATTACHED );
  }
  return AT_CMPL;
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (8441)         MODULE  : CMH_SMS                  |
| STATE   : finnished             ROUTINE : sAT_PlusCGANS            |
+--------------------------------------------------------------------+

PURPOSE : This is the functional counterpart to the +CGANS= AT
          command to respond manual to a network request for
          PDP context activation.
*/
GLOBAL T_ACI_RETURN sAT_PlusCGANS ( T_ACI_CMD_SRC srcId, SHORT response,
                                    char *l2p, SHORT cid )
{
  char L2P[MAX_L2P_LENGTH];
  T_GPRS_CONT_REC ctx = defaultCtx;
  SHORT           i = 0, cid_list[2] = { INVALID_CID };

  TRACE_FUNCTION ("sAT_PlusCGANS()");

/*
 *-------------------------------------------------------------------
 * check entity status
 *-------------------------------------------------------------------
 */
  if( smEntStat.curCmd NEQ AT_CMD_NONE )

    return( AT_BUSY );

/*
 *-------------------------------------------------------------------
 * check command source
 *-------------------------------------------------------------------
 */
  if(!cmh_IsVldCmdSrc (srcId))
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return( AT_FAIL );
  }

/*
 *-------------------------------------------------------------------
 * check parameter
 *-------------------------------------------------------------------
 */
  if ( (response <= CGANS_RESPONSE_OMITTED) || (response >= CGANS_RESPONSE_INVALID) )
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return( AT_FAIL );
  }

/*
 *-------------------------------------------------------------------
 * default parameter
 *-------------------------------------------------------------------
 */
  if ( response EQ CGANS_RESPONSE_OMITTED )
    response = CGANS_RESPONSE_REJECT;

/*
 *-------------------------------------------------------------------
 * check call table
 *-------------------------------------------------------------------
 */
  if ( gprs_ct_index EQ current_gprs_ct_index )
    return ( AT_FAIL );

/*
 *-------------------------------------------------------------------
 * process parameter
 *-------------------------------------------------------------------
 */
  switch ( response )
  {
    case CGANS_RESPONSE_REJECT:
      psaSM_PDP_No_activate(gprs_call_table[current_gprs_ct_index].sm_ind.smreg_ti, SMREG_RC_ACT_REJ_UNSPEC);

      cmhSM_next_call_table_entry();

#ifdef FF_ATI
      io_setRngInd ( IO_RING_OFF, CRING_TYP_NotPresent, CRING_TYP_NotPresent ); /* V.24 Ring Indicator Line */
#endif

      for( i = 0; i < CMD_SRC_MAX; i++ )
      {
        R_AT( RAT_CRING_OFF, i )( 0 );
      }
      return AT_CMPL;
    case CGANS_RESPONSE_ACCEPT:
     /*
      *-------------------------------------------------------------------
      * check number of context
      *-------------------------------------------------------------------
      */
      if ( TRUE NEQ srcc_reserve_sources( SRCC_PPPS_SNDCP_LINK, 1 ) )
        return ( AT_FAIL );
     /*
      *-------------------------------------------------------------------
      * check the last two command arguments
      *-------------------------------------------------------------------
      */
      if ( !gprs_call_table[current_gprs_ct_index].L2P[0] )
      {
        if ( l2p NEQ NULL )
        {
          if ( !*l2p )
            strcpy(L2P, "PPP");
          else
          {
            strncpy(L2P, l2p, MAX_L2P_LENGTH - 1);
            L2P[MAX_L2P_LENGTH - 1] = 0;
          }
        }
        else
          strcpy(L2P, "PPP");

        if ( strcmp(L2P, "PPP") )
        {
          ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
          return( AT_FAIL );
        }

        cid_list[0] = cid;
      }
      else
      {
        cid_list[0] = gprs_call_table[current_gprs_ct_index].cid;
      }

      if ( 1 NEQ cmhSM_define_cid_list( srcId, cid_list ))
        return( AT_FAIL );

     /*
      *-------------------------------------------------------------------
      * set the actually context data
      *-------------------------------------------------------------------
      */
/*      ctx.qos.preced    =  gprs_call_table[current_gprs_ct_index].sm_ind.smreg_qos.preced;
      ctx.qos.delay     =  gprs_call_table[current_gprs_ct_index].sm_ind.smreg_qos.delay;
      ctx.qos.relclass  =  gprs_call_table[current_gprs_ct_index].sm_ind.smreg_qos.relclass;
      ctx.qos.peak      =  gprs_call_table[current_gprs_ct_index].sm_ind.smreg_qos.peak;
      ctx.qos.mean      =  gprs_call_table[current_gprs_ct_index].sm_ind.smreg_qos.mean;*/

      strncpy(ctx.apn,
              (const char *) gprs_call_table[current_gprs_ct_index].sm_ind.smreg_apn.buffer,
                             gprs_call_table[current_gprs_ct_index].sm_ind.smreg_apn.c_buffer);
      if ( 4 EQ gprs_call_table[current_gprs_ct_index].sm_ind.pdp_address.c_buff )
        sprintf(ctx.pdp_addr, "%03hd.%03hd.%03hd.%03hd",
                gprs_call_table[current_gprs_ct_index].sm_ind.pdp_address.buff[0],
                gprs_call_table[current_gprs_ct_index].sm_ind.pdp_address.buff[1],
                gprs_call_table[current_gprs_ct_index].sm_ind.pdp_address.buff[2],
                gprs_call_table[current_gprs_ct_index].sm_ind.pdp_address.buff[3]);
      else
        return( AT_FAIL );

      switch ( gprs_call_table[current_gprs_ct_index].sm_ind.pdp_type )
      {
        case X_121:
          return( AT_FAIL );
        case IP_V_4:
          strcpy(ctx.pdp_type, "IP");
          break;
        case IP_V_6:
          return( AT_FAIL );
        default:
          return( AT_FAIL );
      }

     /*
      *-------------------------------------------------------------------
      * set some parameter of the call table
      *-------------------------------------------------------------------
      */
      if ( !gprs_call_table[current_gprs_ct_index].L2P[0])
      {
        /*lint -e{645} */ /* L2P is initialized within the same if-construct some lines above */
        strcpy (gprs_call_table[current_gprs_ct_index].L2P, L2P);
        gprs_call_table[current_gprs_ct_index].cid = *cid_list;
      }

      sAT_PlusCGDCONT_exec (srcId, *cid_list, &ctx);
      
      pdp_context[*cid_list - 1].smreg_ti = gprs_call_table[current_gprs_ct_index].sm_ind.smreg_ti;

     /*
      *-------------------------------------------------------------------
      * process function
      *-------------------------------------------------------------------
      */
      gpppEntStat.curCmd = AT_CMD_CGDATA;
      gpppEntStat.entOwn = srcId;
      gpppShrdPrm.owner  = (UBYTE) srcId;

      smShrdPrm.direc    = DIREC_MT;

      set_conn_param_on_working_cid( (UBYTE)srcId, DTI_ENTITY_PPPS );

      cmhSM_data_link_context();
      return AT_EXCT;
  }

  return AT_FAIL;
}

GLOBAL T_ACI_RETURN sAT_PlusCGEREP  ( T_ACI_CMD_SRC srcId, T_CGEREP_MODE mode, T_CGEREP_BFR bfr )
{

  TRACE_FUNCTION ("sAT_PlusCGEREP()");

/*
 *-------------------------------------------------------------------
 * check entity status
 *-------------------------------------------------------------------
 */
  if( smEntStat.curCmd NEQ AT_CMD_NONE )
    return( AT_BUSY );

/*
 *-------------------------------------------------------------------
 * check first command argument
 *-------------------------------------------------------------------
 */
  if ( mode < CGEREP_MODE_OMITTED || mode >= CGEREP_MODE_INVALID )
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return( AT_FAIL );
  }

  if ( bfr < CGEREP_BFR_OMITTED || bfr >= CGEREP_BFR_INVALID )
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return( AT_FAIL );
  }

/*
 *-------------------------------------------------------------------
 * process parameter
 *-------------------------------------------------------------------
 */

  if ( mode NEQ CGEREP_MODE_OMITTED )
    ati_user_output_cfg[srcId].CGEREP_mode = mode;

  if ( bfr NEQ CGEREP_BFR_OMITTED )
    ati_user_output_cfg[srcId].CGEREP_bfr  = bfr;

  switch ( mode )
  {
    case CGEREP_MODE_BUFFER:
    case CGEREP_MODE_DICARD_RESERVED:
      srcId_cb = srcId;
      cmhSM_cgerep_buffer ( );
      break;
    case CGEREP_MODE_BUFFER_RESERVED:
      break;
    case CGEREP_MODE_INVALID:
    case CGEREP_MODE_OMITTED:
    default:
      break;
  }

  return AT_CMPL;
}

#ifdef DTI
GLOBAL T_ACI_RETURN sAT_PlusCGSMS   ( T_ACI_CMD_SRC srcId, T_CGSMS_SERVICE service )
{
  T_ACI_RETURN    retCd = AT_CMPL;    /* holds return code */

  TRACE_FUNCTION ("sAT_PlusCGSMS()");

/*
 *-------------------------------------------------------------------
 * check entity status
 *-------------------------------------------------------------------
 */
  if( smEntStat.curCmd NEQ AT_CMD_NONE )

    return( AT_BUSY );

/*
 *-------------------------------------------------------------------
 * check first command argument
 *-------------------------------------------------------------------
 */
  if ( service < CGSMS_SERVICE_OMITTED || service >= CGSMS_SERVICE_INVALID )
  {
    ACI_ERR_DESC( ACI_ERR_CLASS_Ext, EXT_ERR_Parameter );
    return( AT_FAIL );
  }

/*
 *-------------------------------------------------------------------
 * process parameter
 *-------------------------------------------------------------------
 */
  if ( service EQ CGSMS_SERVICE_OMITTED )
    service = sm_cgsms_service;

  if ( service NEQ sm_cgsms_service )
  {
    smEntStat.curCmd = AT_CMD_CGSMS;
    smEntStat.entOwn = srcId;
    smShrdPrm.owner  = (UBYTE) srcId;

    cmhSM_set_sms_service ( service );

    retCd = AT_EXCT;
  }

  return retCd;
}
#endif
/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (8441)               MODULE  : CMH_SMS            |
| STATE   : finished                    ROUTINE : string_to_dns      |
+--------------------------------------------------------------------+

PURPOSE : 
*/
LOCAL void string_to_dns(CHAR* dns, ULONG *dns_long)
{
     UBYTE dns_len = 4;
     CHAR dns_adrtest[3];
     UBYTE dns_adr [4];
     UBYTE i = 0;

     memset(&dns_adrtest,0,dns_len-1);
     memset(&dns_adr,0,dns_len);

     if(strlen(dns) NEQ 0)
    {
        for(i=0;i<dns_len;i++)
        {
          strncpy(dns_adrtest,dns,dns_len-1);
          dns_adr[i] = (UBYTE)atoi(dns_adrtest);
          dns = dns+dns_len;
         }
         for(i=0;i<dns_len;i++)
         {
           *dns_long= *dns_long + dns_adr[i];
           if (i<(dns_len-1))
             *dns_long = *dns_long<<8;
        }
    }    
}

/*
+--------------------------------------------------------------------+
| PROJECT : GSM-PS (8441)         MODULE  : CMH_SMS                  |
| STATE   : finished              ROUTINE : sAT_PercentCGPCO         |
+--------------------------------------------------------------------+

PURPOSE : This is the functional counterpart to the ?CGPCO= AT
          command to set protocol configuration options for the
          PDP context activation.
*/

GLOBAL T_ACI_RETURN sAT_PercentCGPCO (T_ACI_CMD_SRC srcId, 
                                      SHORT cid, USHORT protocol,
                                      CHAR *user, CHAR *pwd, CHAR *dns1, CHAR *dns2)
{
  USHORT  pco_len = ACI_PCO_MAX_LEN;
  UBYTE   *pco_array;
  int     i, ret;
  ULONG dns_adr1 = 0x00000000;
  ULONG dns_adr2 = 0x00000000;


  TRACE_FUNCTION("sAT_PercentCGPCO");
  
  ACI_MALLOC (pco_array, ACI_PCO_MAX_LEN);

  string_to_dns(dns1,&dns_adr1);
  string_to_dns(dns2,&dns_adr2);
  
  ret = utl_create_pco (pco_array, &pco_len,
                        ACI_PCO_CONTENTMASK_AUTH | 
                        ACI_PCO_CONTENTMASK_DNS1 |
                        ACI_PCO_CONTENTMASK_DNS2,
                        ACI_PCO_CONFIG_PROT_PPP,
                        protocol, (UBYTE*)user, (UBYTE*)pwd, dns_adr1, dns_adr2);
  if (ret < 0)
  {
    TRACE_EVENT_P1 ("sAT_PercentCGPCO(): invalid protocol=%d", protocol);
    ACI_MFREE (pco_array);

    return (AT_FAIL);
  }
  if (cid EQ 0)
  {
    for (i = 0; i < MAX_CID; i++)
    {
      pdp_context[i].user_pco.len = (UBYTE)pco_len;
      memcpy (pdp_context[i].user_pco.pco, pco_array, pco_len);
    }
  }
  else
  {
    pdp_context[cid - 1].user_pco.len = (UBYTE)pco_len;
    memcpy (pdp_context[cid - 1].user_pco.pco, pco_array, pco_len);
  }

  ACI_MFREE (pco_array);

  return (AT_CMPL);
}

/*
+--------------------------------------------------------------------+
| PROJECT : GPRS                         MODULE  : CMH_SMS           |
| STATE   : finished                     ROUTINE : qAT_PercentCGPCO  |
+--------------------------------------------------------------------+

  PURPOSE : %CGPCO command
              * analyze network PCO a cid
*/

GLOBAL T_ACI_RETURN qAT_PercentCGPCO ( UBYTE srcId, ULONG * gateway, 
                                       ULONG * dns1,ULONG * dns2, USHORT cid)
{  

  TRACE_FUNCTION("qAT_PercentCGPCO");

  switch(pdp_context[cid - 1].state)
  {
     case CS_ACTIVATED:
     case CS_ESTABLISH_3:
     case CS_DATA_LINK:
          utl_analyze_pco((UBYTE*)pdp_context[cid - 1].network_pco.pco, (USHORT)pdp_context[cid - 1].network_pco.len, dns1, dns2, gateway);
          break;
     default:
          break;
   }
   return (AT_CMPL);
}


#endif  /* GPRS */
/*==== EOF ========================================================*/
