/*
+-----------------------------------------------------------------------------
|  Project :  DCM and TCPIP
|  Modul   :  ACI
+-----------------------------------------------------------------------------
|  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.
+-----------------------------------------------------------------------------
|  Description :  This file contains Useful DCM (Data connection manager)functions.
+-----------------------------------------------------------------------------
*/

/************************************  INCLUDES ********************************************/
#include "aci_all.h"

#include "aci.h"
#include "aci_cmh.h"
#include "Gaci_cmh.h"
#include "dcm.h"
#include "dcm_utils.h"
#include "dcm_state.h"
#include "dcm_env.h"
#include "dcm_f.h"
#include "psa_dcm.h"
#include "socket_api.h"

#include "wap_aci.h"

/**************************** LOCAL VARIABLE DEFINITION  ************************************/

/**************************** EXTERN VARIABLE DEFINITION  ***********************************/
EXTERN T_DCM_ENV_CTRL_BLK *dcm_env_ctrl_blk_p;


/**************************** LOCAL FUCNTION DEFINITION  ************************************/
LOCAL T_DCM_RET dcm_send_cgdcont_get_cmd(U8 row);
LOCAL T_DCM_RET dcm_send_cgdcont_cmd(U8 row);
LOCAL T_DCM_RET dcm_send_cgatt_cmd(U8 row);
LOCAL T_DCM_RET dcm_send_cgdeact_cmd(U8 row);
LOCAL T_DCM_RET dcm_send_cgpco_auth_cmd(U8 row);
LOCAL T_DCM_RET dcm_send_cgpco_get_cmd(U8 row);
LOCAL T_DCM_RET dcm_send_cgerep_cmd(U8 row);
LOCAL T_DCM_RET dcm_send_cgpaddr_cmd(U8 current_row);
LOCAL T_DCM_RET dcm_send_percentppp_cmd(U8 row);
LOCAL T_DCM_RET dcm_send_sat_dn_cmd(U8 row);
LOCAL T_DCM_RET dcm_send_percentppp_get_cmd(U8 row);
LOCAL T_DCM_RET dcm_send_percentcal_get_cmd(UBYTE row);
LOCAL T_DCM_RET dcm_send_sat_abort_cmd(U8 row);

LOCAL ULONG dcm_ipaddr_htonl(ULONG horder_ipaddr);

LOCAL void set_state_from_ctrl_blk();

/************************** EXTERN FUCNTION DEFINITION  *********************************/
EXTERN T_DCM_RET dcm_handle_message (T_DCM_HDR *msg_p);
EXTERN void psaTCPIP_Shutdown_Req(void);



/***************************************************************************************
*   Function       :  dcm_process_unwaited_events_state_intermediate_conn
*   Parameter      :  T_DCM_HDR *
*                     -Pointer on the header of the message.
*   Return         :  T_DCM_RET
*                     DCM_OK or DCM errors
*   Description    :  Function used in all intermediate states where events is unwaited,
*                     but must be processed.
***************************************************************************************/
T_DCM_RET dcm_process_unwaited_events_state_intermediate_conn(T_DCM_HDR * msg_p)
{
  T_DCM_OPEN_CONN_REQ_MSG * dcm_open_conn_req_msg_p;
  T_DCM_CLOSE_CONN_REQ_MSG * dcm_close_conn_req_msg_p;
  U8 current_row = dcm_env_ctrl_blk_p->current_row;

  TRACE_FUNCTION("DCM: dcm_process_unwaited_events_state_intermediate_conn()");

  switch(msg_p->msg_id)
  {
    case DCM_ERROR_IND_MSG:
      return dcm_process_event_error_reception(msg_p);
      /*lint -e527 suppress Warning -- Unreachable */
      break;
      /*lint +e527 */
    case DCM_OPEN_CONN_REQ_MSG:
      dcm_open_conn_req_msg_p = (T_DCM_OPEN_CONN_REQ_MSG*)msg_p;
      return psaDCM_open_conn_cnf(DCM_BUSY,
                                 dcm_open_conn_req_msg_p->conn_req.api_instance);
      /*lint -e527 suppress Warning -- Unreachable */
      break;
      /*lint +e527 */
    case DCM_CLOSE_CONN_REQ_MSG:
      dcm_close_conn_req_msg_p = (T_DCM_CLOSE_CONN_REQ_MSG*)msg_p;
      // DCM CLOSE REQ have to be accepted in ACTVATING STATE
      if(dcm_env_ctrl_blk_p->state[0] == DCM_ACTIVATING_CONN)
      {
        dcm_process_close_conn_event(dcm_close_conn_req_msg_p);
      }
      else
      {
        psaDCM_close_conn_cnf(DCM_BUSY,
                              dcm_close_conn_req_msg_p->close_req.api_instance);
      }
      return DCM_OK;
      /*lint -e527 suppress Warning -- Unreachable */
      break;
      /*lint +e527 */
    default:
      /* Ignore event - Stay in the same state. */
      return DCM_UNKNOWN_EVENT;
  }
}


/*
 * Function used to close a connection (with PS and later with api_instance)
 *
 * The closing is always on demand of the IPU_id, or may be internally launched.
 * The closing of the connection begin by the closing of the port with the PS.
 * - Close the connection with the PS.
 *
 * @param   T_DCM_CLOSE_CONN_REQ_MSG*
 * @return  DCM_OK or DCM errors
 */
/***************************************************************************************
*   Function       :  dcm_process_close_conn_event
*   Parameter      :  T_APPLI_USER
*                     -Pointer on the header of the message.
*   Return         :  T_DCM_RET
*                     DCM_OK or DCM errors
*   Description    :  Function used in all intermediate states where events is unwaited,
*                     but must be processed.
***************************************************************************************/
T_DCM_RET dcm_process_close_conn_event(T_DCM_CLOSE_CONN_REQ_MSG *close_conn_p )
{
  U8 i;
  U8 current_bearer_count;

  TRACE_FUNCTION("DCM: dcm_process_close_conn_event()");

  for (i=0; i < DCM_MAX_NUMBER_IPU; i++)
  {
    if( (dcm_env_ctrl_blk_p->ipu_list[i].api_instance ==
         close_conn_p->close_req.api_instance) &&
         (dcm_env_ctrl_blk_p->ipu_list[i].row_state) )
    {
      if(dcm_env_ctrl_blk_p->ipu_list[i].bearer_type == DCM_BEARER_GPRS)
      {
        current_bearer_count = dcm_env_ctrl_blk_p->gprs_current_total_row;
      }
      else
      {
        current_bearer_count = dcm_env_ctrl_blk_p->gsm_current_total_row;
      }
      /* raise the flag that indicates a disconnection */
      dcm_env_ctrl_blk_p->ipu_list[i].row_state = ROW_CLOSING;
      dcm_new_state(DCM_CLOSING_CONN , DCM_SUB_NO_ACTION);

      if(current_bearer_count == 1)
      {
        if(dcm_env_ctrl_blk_p->ipu_list[i].bearer_type == DCM_BEARER_GPRS)
        {
          dcm_send_cgdeact_cmd(i);
        }
        else
        {
          dcm_send_percentcal_get_cmd(i);
        }
      }
      else
      {
        psaDCM_close_conn_cnf(DCM_OK,close_conn_p->close_req.api_instance);
        /* free the row used */
        dcm_free_row(i);
        set_state_from_ctrl_blk();
      }
    }
  }
  return DCM_OK;
}


/*******************************************************************************
 * Function used to open a connection (with PS)
 *
 * The opening is always on demand of the IPU_id.
 * - Open the connection with the PS.
 *
 * @return  DCM_OK or DCM errors
*******************************************************************************/
T_DCM_RET dcm_process_open_conn_event(T_DCM_OPEN_CONN_REQ_MSG *open_conn_p)
{
  U8 row_id, row_id_free;

  TRACE_FUNCTION("DCM: dcm_process_open_conn_event()");

  /* check if the max number of IPU is reached */
  if (( dcm_env_ctrl_blk_p->gsm_current_total_row +
        dcm_env_ctrl_blk_p->gprs_current_total_row ) >= DCM_MAX_NUMBER_IPU )
  {
    /* Too many IPU_id opened */
    /* send the negative confirmation to the IPU */
    return psaDCM_open_conn_cnf(DCM_NOT_READY,
                                open_conn_p->conn_req.api_instance);
  }

  /* if possible, get the next row */
  for (row_id = 0, row_id_free = DCM_MAX_NUMBER_IPU; row_id < DCM_MAX_NUMBER_IPU; row_id++)
  {
    if (dcm_env_ctrl_blk_p->ipu_list[row_id].row_state)
    {
      /* send a negative confirmation whether the IPU already exists */
      if (dcm_env_ctrl_blk_p->ipu_list[row_id].api_instance ==
          open_conn_p->conn_req.api_instance)
      {
        /* send the negative confirmation to the IPU */
        return psaDCM_open_conn_cnf(DCM_ALREADY_ACTIVATED,
                                    open_conn_p->conn_req.api_instance);
      }
    }
    else
    {
      /* get the first entry */
      if (row_id_free == DCM_MAX_NUMBER_IPU)
      {
        row_id_free = row_id;
      }
    }
  }

  if (row_id_free == DCM_MAX_NUMBER_IPU)
  {
    /* send the negative confirmation to the IPU */
    return psaDCM_open_conn_cnf(DCM_NOT_READY,
                                open_conn_p->conn_req.api_instance);
  }
  /* Check the bearer type */
  /* check best one bearer */
  if(open_conn_p->conn_req.bearer_select == DCM_BEARER_ANY)
  {
    T_CGATT_STATE  cgatt_state;
    qAT_PlusCGATT(CMD_SRC_LCL,&cgatt_state);

    if(cgatt_state == CGATT_STATE_ATTACHED)
      open_conn_p->conn_req.bearer_select = DCM_BEARER_GPRS;
    else
      open_conn_p->conn_req.bearer_select = DCM_BEARER_GSM;
  }

  /* If application doesn't give any connection parameters than use default */
  if(open_conn_p->conn_req.bearer_select == DCM_BEARER_GSM   OR
     open_conn_p->conn_req.bearer_select == DCM_BEARER_GPRS)
  {
    psaDCM_open_conn_cnf(DCM_INVALID_PARAMETER,open_conn_p->conn_req.api_instance);
    return DCM_INVALID_PARAMETER;
  }

  /* If application gives the necessary parameters for a connection use these */
  else if(open_conn_p->conn_req.bearer_select == DCM_BEARER_AS_SPECIFIED)
  {
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].bearer_handle =
      open_conn_p->conn_req.dcm_info_conn.bearer_handle;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].app_handle    =
      open_conn_p->conn_req.dcm_info_conn.app_handle;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].bearer_type   =
      open_conn_p->conn_req.dcm_info_conn.bearer_type;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].apn_valid     =
      open_conn_p->conn_req.dcm_info_conn.apn_valid;
    if(dcm_env_ctrl_blk_p->ipu_list[row_id_free].apn_valid)
    {
      strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row_id_free].apn,
             (char*)open_conn_p->conn_req.dcm_info_conn.apn);
    }
    else
    {
      strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row_id_free].apn,"");
    }
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].phone_number_valid =
      open_conn_p->conn_req.dcm_info_conn.phone_number_valid;
    if(dcm_env_ctrl_blk_p->ipu_list[row_id_free].phone_number_valid)
    {
      strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row_id_free].phone_number,
             (char*)open_conn_p->conn_req.dcm_info_conn.phone_number);
    }
    else
    {
      strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row_id_free].phone_number,"");
    }
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].user_id_valid =
      open_conn_p->conn_req.dcm_info_conn.user_id_valid;
    if(dcm_env_ctrl_blk_p->ipu_list[row_id_free].user_id_valid)
    {
      strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row_id_free].user_id,
             (char*)open_conn_p->conn_req.dcm_info_conn.user_id);
    }
    else
    {
      strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row_id_free].user_id,"");
    }
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].password_valid =
      open_conn_p->conn_req.dcm_info_conn.password_valid;
    if(dcm_env_ctrl_blk_p->ipu_list[row_id_free].password_valid)
    {
      strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row_id_free].password,
             (char*)open_conn_p->conn_req.dcm_info_conn.password);
    }
    else
    {
      strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row_id_free].password,"");
    }
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].cid             = open_conn_p->conn_req.dcm_info_conn.cid;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].ip_address      = open_conn_p->conn_req.dcm_info_conn.ip_address;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].dns1            = open_conn_p->conn_req.dcm_info_conn.dns1;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].dns2            = open_conn_p->conn_req.dcm_info_conn.dns2;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].gateway         = open_conn_p->conn_req.dcm_info_conn.gateway;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].auth_type       = open_conn_p->conn_req.dcm_info_conn.auth_type;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].data_compr      = open_conn_p->conn_req.dcm_info_conn.data_compr;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].header_compr    = open_conn_p->conn_req.dcm_info_conn.header_compr;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].precedence      = open_conn_p->conn_req.dcm_info_conn.precedence;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].delay           = open_conn_p->conn_req.dcm_info_conn.delay;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].reliability     = open_conn_p->conn_req.dcm_info_conn.reliability;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].peak_throughput = open_conn_p->conn_req.dcm_info_conn.peak_throughput;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].mean_throughput = open_conn_p->conn_req.dcm_info_conn.mean_throughput;
    dcm_env_ctrl_blk_p->ipu_list[row_id_free].shareable       = open_conn_p->conn_req.dcm_info_conn.shareable;
  }

  dcm_env_ctrl_blk_p->ipu_list[row_id_free].api_instance =
    open_conn_p->conn_req.api_instance;

  /* keep the curretn row */
  dcm_env_ctrl_blk_p->current_row = row_id_free;

  /* mark the row as used */
  dcm_env_ctrl_blk_p->ipu_list[row_id_free].row_state = ROW_ASSIGNED;

  /* sum the total of actual rows */
  if(dcm_env_ctrl_blk_p->ipu_list[row_id_free].bearer_type == DCM_BEARER_GPRS)
  {
    dcm_env_ctrl_blk_p->gprs_current_total_row++;
  }
  else
  {
    dcm_env_ctrl_blk_p->gsm_current_total_row++;
  }

  if(dcm_env_ctrl_blk_p->gprs_current_total_row > 1 OR
     dcm_env_ctrl_blk_p->gsm_current_total_row > 1)
  {
    psaDCM_open_conn_cnf(DCM_ALREADY_ACTIVATED,open_conn_p->conn_req.api_instance);
    return DCM_ALREADY_ACTIVATED;
  }

  set_gpf_tcpip_call();

  /* DCM state change */
  dcm_new_state(DCM_ACTIVATING_CONN,DCM_SUB_NO_ACTION);

  /* if GPRS: send first GPRS AT Command qAT_CGDCONT  */
  if(dcm_env_ctrl_blk_p->ipu_list[row_id_free].bearer_type == DCM_BEARER_GPRS)
  {
    dcm_send_cgdcont_get_cmd(dcm_env_ctrl_blk_p->current_row);
  }
  else /*send first GSM AT Command sAT_PercentPPP */
  {
    dcm_send_percentppp_cmd(dcm_env_ctrl_blk_p->current_row);
  }

  return DCM_OK;
}


/******************************************************************************/
T_DCM_RET dcm_process_get_current_conn_event(T_DCM_GET_CURRENT_CONN_REQ_MSG *current_conn_p)
{
  TRACE_FUNCTION("DCM: dcm_process_get_current_conn_event()");

  if(dcm_env_ctrl_blk_p->ipu_list[dcm_env_ctrl_blk_p->current_row].row_state ==
     ROW_ASSIGNED) {
    psaDCM_get_current_conn_cnf(DCM_OK,
                                current_conn_p->current_conn_req.api_instance,
                                dcm_env_ctrl_blk_p);
  }
  else {
    psaDCM_get_current_conn_cnf(DCM_NOT_READY,
                                current_conn_p->current_conn_req.api_instance,
                                dcm_env_ctrl_blk_p);
  }
  return DCM_OK;
}


/******************************************************************************/
T_DCM_RET dcm_process_unknown_event_in_idle(T_DCM_HDR* msg_p)
{
  T_DCM_CLOSE_CONN_REQ_MSG *close_conn_req_p;
  T_DCM_RET ret;

  TRACE_FUNCTION("DCM: dcm_process_unknown_event_in_idle()");

  if(msg_p == NULL)
    return DCM_INVALID_PARAMETER;

  switch(msg_p->msg_id)
  {
    case DCM_CLOSE_CONN_REQ_MSG :
      close_conn_req_p =(T_DCM_CLOSE_CONN_REQ_MSG *)msg_p;
      psaDCM_close_conn_cnf(DCM_UNKNOWN_EVENT,
                            close_conn_req_p->close_req.api_instance);
      set_state_from_ctrl_blk();
      ret = DCM_OK;
      break;
    default:
      ret = DCM_UNKNOWN_EVENT;
      break;
  }
  return ret;
}


/******************************************************************************/
T_DCM_RET dcm_free_row(U8 current_row)
{
  TRACE_FUNCTION("DCM: dcm_free_row()");

  /* Decrease the current number of IPU */
  dcm_env_ctrl_blk_p->ipu_list[current_row].row_state = ROW_FREE;
  if(dcm_env_ctrl_blk_p->ipu_list[current_row].bearer_type == DCM_BEARER_GPRS)
    dcm_env_ctrl_blk_p->gprs_current_total_row--;
  else
    dcm_env_ctrl_blk_p->gsm_current_total_row--;

  /* clear the row in the structure of IP Users */
  dcm_clear_ipu_info(current_row);
  return DCM_OK;
}


/*
 * Function used to store some IPU informations
 *
 * @param   row to access [0, 256],
 * @param   IPU id
 * @param   bearer type
 * @param   apn, mtu, pdp@, cid, user, password, dns1, dns2, gateway
 * @return    DCM_OK or DCM errors
 */
T_DCM_RET dcm_store_ipu_info(U8 row, T_BEARER_TYPE bearer_type, char *apn,
                             char *number, char *pdp_addr, U8 cid_used,
                             char *user, char *password, U32 dns1, U32 dns2,
                             U32 gateway)
{
  TRACE_FUNCTION("DCM: dcm_store_ipu_info()");

  if(bearer_type == DCM_BEARER_GPRS)
    strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row].apn, apn);
  else if (bearer_type == DCM_BEARER_GSM)
    strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row].phone_number,number);
  else
    return DCM_INVALID_PARAMETER;
  dcm_env_ctrl_blk_p->ipu_list[row].bearer_type = bearer_type;
  dcm_env_ctrl_blk_p->ipu_list[row].cid = cid_used;
  strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row].user_id, user);
  strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row].password, password);
  dcm_env_ctrl_blk_p->ipu_list[row].dns1 = dns1;
  dcm_env_ctrl_blk_p->ipu_list[row].dns2 = dns2;
  dcm_env_ctrl_blk_p->ipu_list[row].gateway = gateway;

  return DCM_OK;
}


/* resets parameters of a row */
T_DCM_RET dcm_clear_ipu_info(U8 row)
{
  char empty[] = "";
  TRACE_FUNCTION("DCM: dcm_clear_ipu_info()");

  dcm_env_ctrl_blk_p->ipu_list[row].bearer_type = DCM_BEARER_NO;
  strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row].apn, empty);
  strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row].phone_number,empty);
  dcm_env_ctrl_blk_p->ipu_list[row].cid = 0;
  strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row].user_id, empty);
  strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row].password, empty);
  dcm_env_ctrl_blk_p->ipu_list[row].dns1 = 0;
  dcm_env_ctrl_blk_p->ipu_list[row].dns2 = 0;
  dcm_env_ctrl_blk_p->ipu_list[row].gateway = 0;

  return DCM_OK;
}

/*
 * Function used to send the <AT+CGDCONT=?> command
 *
 * This command is used to get the next Context IDentifier available in the PS.
 * This cid will be used very often in the suite
 *
 * @param   row in the IPU structure related to the actual command
 * @return  DCM_OK or DCM errors
 */
LOCAL T_DCM_RET dcm_send_cgdcont_get_cmd(U8 row)
{
  T_GPRS_CONT_REC defCtxts[MAX_CID_PLUS_EINS];
  SHORT cid_array[MAX_CID_PLUS_EINS];
  UBYTE i;

  TRACE_FUNCTION("DCM: dcm_send_cgdcont_get_cmd()");

  if(qAT_PlusCGDCONT(CMD_SRC_LCL, &defCtxts, cid_array) != AT_CMPL)
  {
    psaDCM_open_conn_cnf( DCM_NOT_READY, dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
    set_state_from_ctrl_blk();
    return DCM_OK;
  }
  else
  {
    for( i= 0; i < MAX_CID_PLUS_EINS ; i++)
    {
      if(cid_array[i] == dcm_env_ctrl_blk_p->ipu_list[row].cid)
      {
        TRACE_EVENT_P2("DCM: dcm cid is the same %d=%d",
                       cid_array[i],
                       dcm_env_ctrl_blk_p->ipu_list[row].cid);
      }
    }
    return  dcm_send_cgdcont_cmd(row);
  }

}


/*
 * Function used to send the <AT+CGDCONT=cid, "IP", "apn", "", 0, 0> command
 *
 * This command is used to declare the PDP Context.
 *
 * @param   row in the IPU structure related to the actual command
 * @return  DCM_OK or DCM errors
 */
LOCAL T_DCM_RET dcm_send_cgdcont_cmd(U8 row)
{
  T_GPRS_CONT_REC input_txt;

  TRACE_FUNCTION("DCM: dcm_send_cgdcont_cmd()");
  TRACE_EVENT_P1("DCM: ipu_list[row].apn = %s",dcm_env_ctrl_blk_p->ipu_list[row].apn);

  strcpy(input_txt.apn,(char*)dcm_env_ctrl_blk_p->ipu_list[row].apn);
  strcpy(input_txt.pdp_type,"IP");
  memset(input_txt.pdp_addr,'\0',MAX_PDP_ADDR_LEN);
  input_txt.d_comp = CGDCONT_D_COMP_OMITTED;
  input_txt.h_comp = CGDCONT_H_COMP_OMITTED;
  memset(&input_txt.qos,0,sizeof(T_QOS));
  memset(&input_txt.min_qos,0,sizeof(T_QOS));

  if(sAT_PlusCGDCONT(CMD_SRC_LCL,dcm_env_ctrl_blk_p->ipu_list[row].cid, &input_txt) != AT_CMPL)
  {
    psaDCM_open_conn_cnf(DCM_NOT_READY, dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
    set_state_from_ctrl_blk();
    return DCM_OK;
  }
  else
  {
    dcm_send_cgatt_cmd(row);
    return DCM_OK;
  }
}


/*
 * Function used to send the <AT+CGATT=cid> command
 *
 * This command is used to force attachment to the network.
 *
 * @param   row in the IPU structure related to the actual command
 * @return  DCM_OK or DCM errors
 */
LOCAL T_DCM_RET dcm_send_cgatt_cmd(U8 row)
{
  TRACE_FUNCTION("DCM: dcm_send_cgatt_cmd()");

  /* prepare the AT command including some dynamic info like cid */
  switch(sAT_PlusCGATT(CMD_SRC_LCL, CGATT_STATE_ATTACHED))
  {
    case AT_FAIL:
    case AT_BUSY:
      psaDCM_open_conn_cnf(DCM_NOT_READY, dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
      dcm_free_row(row);
      set_state_from_ctrl_blk();
      break;

    case AT_CMPL:
      dcm_send_cgpco_auth_cmd(row);
      break;

    case AT_EXCT:
      dcm_env_ctrl_blk_p->dcm_call_back = dcm_handle_message;
      dcm_new_state(DCM_ACTIVATING_CONN, DCM_SUB_WAIT_CGATT_CNF);
      break;

    default :
      break;
  }
  return DCM_OK;
}


/*
 * Function used to send the <AT%CGPCO=0, cid, "PAP, username,
 * password, 0.0.0.0, 0.0.0.0"> command
 *
 * This command is used to configure the PS to TCPIP over SNDCP
 *
 * @param   row in the IPU structure related to the actual command
 * @return  DCM_OK or DCM errors
 */
LOCAL T_DCM_RET dcm_send_cgpco_auth_cmd(U8 row)
{
  CHAR dns[2];
  strcpy(dns, "");

  TRACE_FUNCTION("DCM: dcm_send_cgpco_auth_cmd()");
  TRACE_EVENT_P2("DCM: user=%s, password=%s",
                 dcm_env_ctrl_blk_p->ipu_list[row].user_id,
                 dcm_env_ctrl_blk_p->ipu_list[row].password);

  if(sAT_PercentCGPCO(CMD_SRC_LCL,dcm_env_ctrl_blk_p->ipu_list[row].cid,
                      ACI_PCO_AUTH_PROT_PAP,
                      (char*)dcm_env_ctrl_blk_p->ipu_list[row].user_id,
                      (char*)dcm_env_ctrl_blk_p->ipu_list[row].password,
                      dns,dns) != AT_CMPL)
  {
    psaDCM_open_conn_cnf(DCM_NOT_READY, dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
    set_state_from_ctrl_blk();
    return DCM_OK;
  }
  else
  {
    dcm_send_cgerep_cmd(row);
    return DCM_OK;
  }
}


/*
 * Function used to send the <AT+CGEREP=cid, 0> command
 *
 * This command is used to configure the PS to send EVENT to us
 *
 * @param   row in the IPU structure related to the actual command
 * @return  DCM_OK or DCM errors
 */
LOCAL T_DCM_RET dcm_send_cgerep_cmd(U8 row)
{
  TRACE_FUNCTION("DCM: dcm_send_cgerep_cmd()");

  /* prepare the AT command including some dynamic info like cid */
  if (sAT_PlusCGEREP(CMD_SRC_LCL,CGEREP_MODE_BUFFER,CGEREP_BFR_CLEAR) != AT_CMPL)
  {
    psaDCM_open_conn_cnf(DCM_NOT_READY, dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
    set_state_from_ctrl_blk();
    return DCM_OK;
  }
  else
  {
    strcpy((char*)dcm_env_ctrl_blk_p->ipu_list[row].phone_number,"*98*1#");
    dcm_send_sat_dn_cmd(row);
    return DCM_OK;
  }
}


/*
 * Function used to send the <AT+CGACT=cid, 0> command
 *
 * This command is used to deactivate the PDP decontext.
 *
 * @param   row in the IPU structure related to the actual command
 * @return  DCM_OK or DCM errors
 */
LOCAL T_DCM_RET dcm_send_cgdeact_cmd(U8 row)
{
  T_CGATT_STATE gprs_attach_state;
  SHORT cids[MAX_CID_PLUS_EINS] = {GPRS_CID_1,INVALID_CID};

  TRACE_FUNCTION("DCM: dcm_send_cgdeact_cmd()");

  /* Packet Counter */
  sAT_PercentSNCNT(CMD_SRC_LCL,SN_RESET_YES);

  qAT_PlusCGATT(CMD_SRC_LCL,&gprs_attach_state);
  TRACE_EVENT_P1("DCM: Attatch State %d",gprs_attach_state);

  if(gprs_attach_state != CGATT_STATE_ATTACHED)
  {
    psaDCM_close_conn_cnf(DCM_OK,dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
    set_state_from_ctrl_blk();
    return DCM_OK;
  }

  /* GPRS BEARER CLOSING */
  switch(sAT_PlusCGACT(CMD_SRC_LCL,CGACT_STATE_DEACTIVATED,cids))
  {
    case AT_FAIL:
    case AT_BUSY:
      psaDCM_close_conn_cnf(DCM_NOT_READY,
                            dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
      set_state_from_ctrl_blk();
      break;

    case AT_CMPL:
      psaDCM_close_conn_cnf(DCM_OK, dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
      dcm_free_row(row);
      set_state_from_ctrl_blk();
      break;

    case AT_EXCT:
      dcm_new_state(DCM_CLOSING_CONN, DCM_SUB_WAIT_CGDEACT_CNF);
      dcm_env_ctrl_blk_p->dcm_call_back = dcm_handle_message;
      break;

    default :
      break;
  }
  return DCM_OK;
}


LOCAL T_DCM_RET dcm_send_sat_h_cmd(U8 row)
{
  SHORT cid_array[1];
  cid_array[0] = INVALID_CID;

  TRACE_FUNCTION("DCM: dcm_send_sat_h_cmd()");

  switch(sAT_H(CMD_SRC_LCL))
  {
    case AT_FAIL:
    case AT_BUSY:
      psaDCM_close_conn_cnf(DCM_NOT_READY, dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
      set_state_from_ctrl_blk();
      break;

    case AT_CMPL:
      break;

    case AT_EXCT:
      dcm_new_state(DCM_CLOSING_CONN, DCM_SUB_WAIT_SATH_CNF);
      dcm_env_ctrl_blk_p->dcm_call_back = dcm_handle_message;
      break;

    default :
      break;
  }
  return DCM_OK;
}


/*
 * Function used to send the <AT+CGPADDR=cid> command
 *
 * This command is used to get back the pdp@ of the module
 *
 * @param   row in the IPU structure related to the actual command
 * @return  DCM_OK or DCM errors
 */
LOCAL T_DCM_RET dcm_send_cgpaddr_cmd(U8 row)
{
  T_PDP_ADDRESS pdp_address[MAX_CID];
  SHORT cid_array[MAX_CID];
  cid_array[0] = dcm_env_ctrl_blk_p->ipu_list[row].cid;
  cid_array[1] =INVALID_CID;

  TRACE_FUNCTION("DCM: dcm_send_cgpaddr_cmd()");

  memset(pdp_address , 0x00, sizeof(T_PDP_ADDRESS)*MAX_CID);

  /* prepare the AT command including some dynamic info like cid */
  if(sAT_PlusCGPADDR(CMD_SRC_LCL,cid_array,pdp_address) != AT_CMPL)
  {
    psaDCM_open_conn_cnf(DCM_NOT_READY,
                         dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
    set_state_from_ctrl_blk();
    return DCM_OK;
  }
  else
  {
    memcpy(dcm_env_ctrl_blk_p->ipu_list[row].pdp_addr,
           pdp_address[0],
           sizeof(T_PDP_ADDRESS));
    TRACE_EVENT_P1("DCM: PDP addr=%s",dcm_env_ctrl_blk_p->ipu_list[row].pdp_addr);
    dcm_send_cgpco_get_cmd(row);
    return DCM_OK;
  }
}


/*
 * Function used to send the <AT%CGPCO=1,1,,cid> command
 *
 * This command is used to get back the dns1, dns2 and gateway @
 *
 * @param   row in the IPU structure related to the actual command
 * @return  DCM_OK or DCM errors
 */
LOCAL T_DCM_RET dcm_send_cgpco_get_cmd(U8 row)
{
  TRACE_FUNCTION("DCM: dcm_send_cgpco_get_cmd()");

  psaDCM_open_conn_cnf(DCM_OK, dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
  dcm_env_ctrl_blk_p->dcm_call_back = dcm_handle_message;
  dcm_new_state(DCM_CONN_ACTIVATED, DCM_SUB_NO_ACTION);

  return DCM_OK;
}


T_DCM_RET dcm_send_percentppp_cmd(U8 row)
{
  TRACE_FUNCTION("DCM: dcm_send_percentppp_cmd()");
  if(sAT_PercentPPP(CMD_SRC_LCL,
                    A_PAP,
                    (char*)dcm_env_ctrl_blk_p->ipu_list[row].user_id,
                    (char*)dcm_env_ctrl_blk_p->ipu_list[row].password,
                    USE_NO_PPP_FOR_AAA) != AT_CMPL)
  {
    psaDCM_open_conn_cnf(DCM_NOT_READY,
                         dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
    set_state_from_ctrl_blk();  }
  else
  {
    // reset the WAP-data if not done at call termination before
    sAT_PercentWAP(0,0);
    sAT_PercentWAP(0,1);
    dcm_send_sat_dn_cmd(row);
  }

  return DCM_OK;
}


LOCAL T_DCM_RET dcm_send_sat_dn_cmd(U8 row)
{
  TRACE_FUNCTION("DCM: dcm_send_sat_dn_cmd()");

  switch(sAT_Dn(CMD_SRC_LCL,
                (char*)dcm_env_ctrl_blk_p->ipu_list[row].phone_number,
                D_CLIR_OVRD_Default,
                D_CUG_CTRL_NotPresent,
                D_TOC_Data))
  {
    case AT_FAIL:
    case AT_BUSY:
    case AT_CMPL:
      psaDCM_open_conn_cnf(DCM_NOT_READY,
                           dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
      dcm_free_row(row);
      set_state_from_ctrl_blk();
      break;

    case AT_EXCT:
      dcm_env_ctrl_blk_p->dcm_call_back = dcm_handle_message;
      if(dcm_env_ctrl_blk_p->ipu_list[row].bearer_type == DCM_BEARER_GPRS) {
        dcm_new_state(DCM_ACTIVATING_CONN, DCM_SUB_WAIT_CGACT_CNF);
      }
      else {
        dcm_new_state(DCM_ACTIVATING_CONN, DCM_SUB_WAIT_SATDN_CNF);
      }
      break;

    default:
      break;
  }

  return DCM_OK;
}


LOCAL T_DCM_RET dcm_send_percentppp_get_cmd(U8 row)
{
  ULONG dns1=0;
  ULONG dns2 =0;
  ULONG ipaddr = 0 ;

  TRACE_FUNCTION("DCM: dcm_send_percentppp_get_cmd()");

  if(qAT_PercentPPP(CMD_SRC_LCL, &ipaddr,&dns1,&dns2) != AT_CMPL)
  {
    psaDCM_open_conn_cnf(DCM_NOT_READY,
                         dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
    set_state_from_ctrl_blk();
  }
  else
  {
    psaDCM_open_conn_cnf(DCM_OK,
                         dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_env_ctrl_blk_p->dcm_call_back = dcm_handle_message;
    dcm_new_state(DCM_CONN_ACTIVATED,DCM_SUB_NO_ACTION);
  }

  return DCM_OK;
}


/*
 * Function used to process the events and errors received from PS
 *
 * @param   received message
 * @return  DCM_OK or DCM errors
 */
T_DCM_RET dcm_process_event_error_reception(T_DCM_HDR * msg_p)
{
  U8 row = dcm_env_ctrl_blk_p->current_row;;

  TRACE_FUNCTION("DCM: dcm_process_event_error_reception()");

  /* check if this port number is really used by DCM */
  if(dcm_env_ctrl_blk_p->ipu_list[row].row_state)
  {
    psaDCM_error_ind((T_DCM_STATUS_IND_MSG*)msg_p,
                      dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
    /* We cannot use this function call here (set_state_from_ctrl_blk();)
       since this will execute reset_gpf_tcpip_call() immediately, but we need
       to evaulate this flag later on */
    if((dcm_env_ctrl_blk_p->gsm_current_total_row +
       dcm_env_ctrl_blk_p->gprs_current_total_row ) > 0)
    {
      /* another active connection */
      dcm_new_state(DCM_CONN_ACTIVATED, DCM_SUB_NO_ACTION);
    }
    else
    {
      dcm_new_state(DCM_IDLE, DCM_SUB_NO_ACTION);
    }
  }
  return DCM_OK;
}


/*
 * Function used to process the reception of the answer to AT+CGATT=...
 *
 * @param   received message, and row in the IPU structure related to the actual command
 * @return  DCM_OK or DCM errors
 */
T_DCM_RET dcm_process_cgatt_ans(T_DCM_HDR * msg_p, U8 row)
{
  TRACE_FUNCTION("DCM: dcm_process_cgatt_ans()");

  if(msg_p->msg_id == DCM_NEXT_CMD_READY_MSG)
  {
    /* send next AT command */
    return dcm_send_cgpco_auth_cmd(row);
  }
  else
  {
    psaDCM_open_conn_cnf(DCM_NOT_READY,
                         dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
    set_state_from_ctrl_blk();
    return DCM_OK;
  }
}


/*
 * Function used to process the reception of the answer to AT+CGACT=...
 *
 * @param   received message, and row in the IPU structure related to the actual command
 * @return  DCM_OK or DCM errors
 */
T_DCM_RET dcm_process_cgact_ans(T_DCM_HDR * msg_p, U8 row)
{
  TRACE_FUNCTION("DCM: dcm_process_cgact_ans()");

  if (msg_p->msg_id == DCM_NEXT_CMD_READY_MSG)
  {
    return dcm_send_cgpaddr_cmd(row);
  }
  else
  {
    psaDCM_open_conn_cnf(DCM_NOT_READY,
                         dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
    /*set_state_from_ctrl_blk();*/
    /* We cannot use this function call here (set_state_from_ctrl_blk();)
       since this will execute reset_gpf_tcpip_call() immediately, but we need
       to evaulate this flag later on */

    if((dcm_env_ctrl_blk_p->gsm_current_total_row +
       dcm_env_ctrl_blk_p->gprs_current_total_row ) > 0)
    {
      /* another active connection */
      dcm_new_state(DCM_CONN_ACTIVATED, DCM_SUB_NO_ACTION);
    }
    else
    {
      dcm_new_state(DCM_IDLE, DCM_SUB_NO_ACTION);
    }
    return DCM_OK;
  }
}


T_DCM_RET dcm_process_cgdeact_ans(T_DCM_HDR * msg_p, U8 row)
{
  TRACE_FUNCTION("DCM: dcm_process_cgdeact_ans()");

  if(msg_p->msg_id == DCM_NEXT_CMD_READY_MSG)
  {
    psaDCM_close_conn_cnf(DCM_OK,
                          dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
  }
  else
  {
    psaDCM_close_conn_cnf(DCM_NOT_READY,
                          dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_env_ctrl_blk_p->ipu_list[row].row_state = ROW_ASSIGNED;
  }
  set_state_from_ctrl_blk();
  return DCM_OK;
}


T_DCM_RET dcm_process_sat_dn_ans(T_DCM_HDR * msg_p, U8 row)
{
  TRACE_FUNCTION("DCM: dcm_process_sat_dn_ans()");

  if(msg_p->msg_id == DCM_NEXT_CMD_READY_MSG)
  {
    return dcm_send_percentppp_get_cmd(row);
  }
  else
  {
    T_DCM_STATUS_IND_MSG * message = (T_DCM_STATUS_IND_MSG *)msg_p;
    /* We need to check if TCPIP has been already initialised successfully or not*/
    if ( wap_state EQ TCPIP_Activation)
    {
      /* Make shure we shutdown TCPIP properly */
      wap_state = TCPIP_Deactivation;
      psaTCPIP_Shutdown_Req();
    }
    psaDCM_open_conn_cnf(message->result, dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
    set_state_from_ctrl_blk();
    return DCM_OK;
  }
}


T_DCM_RET dcm_process_sat_h_ans(T_DCM_HDR * msg_p, U8 row)
{
  TRACE_FUNCTION("DCM: dcm_process_sat_h_ans()");

  if(msg_p->msg_id == DCM_NEXT_CMD_READY_MSG)
  {
    psaDCM_close_conn_cnf(DCM_OK,dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
    dcm_free_row(row);
  }
  else
  {
    psaDCM_close_conn_cnf(DCM_NOT_READY,
                          dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
  }
  set_state_from_ctrl_blk();
  return DCM_OK;
}


GLOBAL ULONG bytes2ipv4addr(UBYTE *host)
{
  UBYTE c1;
  ULONG addr = 0;
  char *tmp;
  if (!host || host[0]>'9' || host[0]<'0') return -1;

  tmp=(char *)host;
  c1 = atoi(tmp);
  addr = addr | c1 << 24;
  tmp = strstr(tmp, ".");
  if(!tmp) return -1;
  tmp++;
  c1 = atoi(tmp);
  addr = addr | c1 << 16;
  tmp = strstr(tmp, ".");
  if(!tmp) return -1;
  tmp++;
  c1 = atoi(tmp);
  addr = addr | c1 <<8 ;
  tmp = strstr(tmp, ".");
  if(!tmp) return -1;
  tmp++;
  c1 = atoi(tmp);
  addr = addr | c1 ;
  return dcm_ipaddr_htonl(addr);
}


LOCAL ULONG dcm_ipaddr_htonl(ULONG horder_ipaddr)
{
  return((U32)((((U32)(horder_ipaddr) & 0x000000ffU) << 24) |
          (((U32)(horder_ipaddr) & 0x0000ff00U) <<  8) |
          (((U32)(horder_ipaddr) & 0x00ff0000U) >>  8) |
          (((U32)(horder_ipaddr) & 0xff000000U) >> 24)));
}


LOCAL T_DCM_RET dcm_send_percentcal_get_cmd(UBYTE row)
{
  T_ACI_CAL_ENTR call_tab[MAX_CALL_NR];
  UBYTE i;
  UBYTE count = 0;

  TRACE_FUNCTION("DCM: dcm_send_percentcal_get_cmd()");

  if(qAT_PercentCAL(CMD_SRC_LCL, call_tab) EQ AT_CMPL)
  {
    for(i=0; i<MAX_CALL_NR; i++)
    {
      if(call_tab[i].index == -1)
      {
        count++;
        /* in other  words no active call*/
        if(count EQ (MAX_CALL_NR -1) )
        {
          TRACE_EVENT("DCM: No active call");
          psaDCM_close_conn_cnf(DCM_OK, dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
          dcm_free_row(row);
          set_state_from_ctrl_blk();
        }
        break;
      }
      switch(call_tab[i].status)
      {
        case CAL_STAT_Active:
          dcm_send_sat_h_cmd(row);
          break;
        case CAL_STAT_Dial:
        case CAL_STAT_Alerting:
          dcm_send_sat_abort_cmd(row);
          break;

        default:
          TRACE_EVENT("DCM: dcm_send_percentcal_get_cmd DEFAULT call status");
          break;
      }
    }
    return DCM_OK;
  }
  return DCM_OK;
}


LOCAL T_DCM_RET dcm_send_sat_abort_cmd(U8 row)
{
  TRACE_FUNCTION("DCM: dcm_send_sat_h_cmd()");

  switch(sAT_Abort(CMD_SRC_LCL,AT_CMD_D))
  {
    case AT_FAIL:
    case AT_BUSY:
      psaDCM_close_conn_cnf(DCM_NOT_READY,
                            dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
      set_state_from_ctrl_blk();
      break;

    case AT_CMPL:
      psaDCM_close_conn_cnf(DCM_OK,dcm_env_ctrl_blk_p->ipu_list[row].api_instance);
      dcm_free_row(row);
      set_state_from_ctrl_blk();
      break;

    case AT_EXCT:
      dcm_new_state(DCM_CLOSING_CONN, DCM_SUB_WAIT_SATH_CNF);
      dcm_env_ctrl_blk_p->dcm_call_back = dcm_handle_message;
      break;

    default :
      break;
  }
  return DCM_OK;
}


/* This functions checks if antother conneciton is active and changes the
   DCM state corresponding */
LOCAL void set_state_from_ctrl_blk()
{
  if((dcm_env_ctrl_blk_p->gsm_current_total_row +
      dcm_env_ctrl_blk_p->gprs_current_total_row ) > 0)
  {
    /* another active connection */
    dcm_new_state(DCM_CONN_ACTIVATED, DCM_SUB_NO_ACTION);
  }
  else
  {
    dcm_new_state(DCM_IDLE, DCM_SUB_NO_ACTION);
    reset_gpf_tcpip_call();
  }
}
