view src/g23m-aci/aci/gaci.c @ 135:ac4c0a9bc022

FFS dev.c: change from Magnetite for potential future embedded modem products with K5A32xxCTM flash chips
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 20 Dec 2018 04:24:06 +0000
parents d393cd9bb723
children
line wrap: on
line source

/* 
+----------------------------------------------------------------------------- 
|  Project :  
|  Modul   :  J:\g23m-aci\aci\gaci.c
+----------------------------------------------------------------------------- 
|  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 :  
+----------------------------------------------------------------------------- 
*/ 
#if defined (GPRS) && defined (DTI)

#ifndef GACI_C
#define GACI_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 "dti_conn_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 "cmh.h"
#ifdef SIM_TOOLKIT
#include "psa_cc.h"
#include "psa_sat.h"
#include "cmh_sat.h"
#endif /* SIM_TOOLKIT */
#include "cmh_sm.h"
#include "cmh_gppp.h"
#include "cmh_gmm.h"
#include "gaci_srcc.h"

#include "aci_mem.h"

/*==== CONSTANTS ==================================================*/
static T_ACI_CMD_SRC _ATZ_srcId;

#ifdef FF_SAT_E
static USHORT SAT_error_cause = SAT_GPRS_INV_CAUSE;
#endif /* FF_SAT_E */

/*==== EXPORT =====================================================*/
GLOBAL T_ACI_CMD_SRC cmhSM_getSrcIdOfRunningCGACTDeactivation(U8 cid);

/*==== VARIABLES ==================================================*/
T_PDP_CONTEXT_INTERNAL *p_pdp_context_list;


/*==== PROTOTYPES =================================================*/
GLOBAL T_PDP_CONTEXT_INTERNAL *pdp_context_create_node( U8 cid );
GLOBAL int                     pdp_context_remove_node( U8 cid );
GLOBAL T_PDP_CONTEXT_INTERNAL *pdp_context_find_node_from_cid( U8 cid );
GLOBAL T_PDP_CONTEXT_INTERNAL *pdp_context_find_node_from_dti_id( U8 dti_id );
GLOBAL T_PDP_CONTEXT_INTERNAL *pdp_context_find_last_node   ( void );
#ifdef REL99
GLOBAL T_TFT_INTERNAL         *pdp_context_add_tft_pf       ( U8 cid, U8 tft_pf_id );
GLOBAL BOOL                    pdp_context_del_tft_pf       ( U8 cid, U8 tft_pf_id );
GLOBAL T_TFT_INTERNAL         *pdp_context_find_tft_pf      ( U8 cid, U8 tft_pf_id );
GLOBAL U8                      pdp_context_get_no_of_tft_pfs( U8 cid );
GLOBAL void                    pdp_context_clear_tft_active_list( U8 cid );
//GLOBAL T_TFT_MODIFICATION      pdp_context_compare_tft      ( U8 cid );
//GLOBAL T_TFT_MODIFICATION      pdp_context_get_modification_action( U8 cid );
#endif /* REL99 */
GLOBAL U8                      pdp_context_check_if_nodes_exists( U8 *cid_array );
GLOBAL U8                      pdp_context_validate_pdp_type    ( U8 *cid_array, T_PDP_TYPE pdp_type );
/*=================================================================*/



/*
+----------------------------------------------------------------------+
| PROJECT :                       MODULE  : gaci                       |
|                                 ROUTINE :                            |
+----------------------------------------------------------------------+

  PURPOSE :
*/

GLOBAL void gaci_init ( void )
{
  /* Init of intern variable */
  _ATZ_srcId = CMD_SRC_NONE;

  /* GPRS Init */
  gpppEntStat.curCmd = AT_CMD_NONE;
  gpppEntStat.entOwn = CMD_SRC_NONE;

  cmhGMM_Init();
  cmhSM_Init();

  srcc_init();

  gaci_reset();
}

GLOBAL void gaci_reset( void )
{
  cmhSM_Reset();
}

GLOBAL void gaci_ATZ_reset( void )
{
  cmhSM_ResetNonWorkingContexts();
}

GLOBAL void gaci_finit ( void )
{
  /* here will be a functionality */
}

EXTERN T_ACI_RETURN sGsmAT_Z ( T_ACI_CMD_SRC srcId );

GLOBAL T_ACI_RETURN sGprsAT_Z ( T_ACI_CMD_SRC srcId )
{
  SHORT cid_array[1] = { PDP_CONTEXT_CID_INVALID };

 /*
  *-------------------------------------------------------------------
  *   rejects waiting network requests for PDP context activation
  *-------------------------------------------------------------------
  */
  if ( ( at.rngPrms.isRng EQ TRUE ) AND ( at.rngPrms.mode EQ CRING_MOD_Gprs) ) /* GPRS call */
  {
   /*
    *   brz patch: In the case of context reactivation over SMREG_PDP_ACTIVATE_IND with an used ti
    *              the GPRS ATZ command doesn't do anything!
    *
    *   Why?       Because the Windows Dial-Up Networking client send every time an ATZ after termination
    *              of the connection and with this a context reactivation was impossible. 
    */
    if ( gprs_call_table[current_gprs_ct_index].reactivation EQ GCTT_NORMAL )
    {
      sAT_PlusCGANS(srcId, CGANS_RESPONSE_REJECT, NULL, PDP_CONTEXT_CID_OMITTED );
    }
    else
    { /* Reactivation: stop GPRS ATZ */
      return sGsmAT_Z ( srcId );
    }
  }
  if ( AT_EXCT EQ sAT_PlusCGACT ( srcId, CGACT_STATE_DEACTIVATED, cid_array ))
  {
    _ATZ_srcId = srcId;    /* hold source Id */
    return( AT_EXCT );
  }
  
  srcId_cb = srcId;
  gaci_ATZ_reset();
  return sGsmAT_Z ( srcId );
}

LOCAL void endOfGprsAT_Z ( void )
{
  srcId_cb = _ATZ_srcId;
  gaci_ATZ_reset();
  if ( AT_CMPL EQ sGsmAT_Z ( _ATZ_srcId ) )
  {
    R_AT( RAT_OK, _ATZ_srcId ) ( AT_CMD_Z );

    /* log result */
    cmh_logRslt ( _ATZ_srcId,
                  RAT_OK, AT_CMD_Z, -1, BS_SPEED_NotPresent, CME_ERR_NotPresent );
  }

  _ATZ_srcId = CMD_SRC_NONE;
}

GLOBAL BOOL gaci_isATZcmd ( void )
{
  if ( _ATZ_srcId NEQ CMD_SRC_NONE )
  {
    endOfGprsAT_Z();
    return TRUE;
  }
  
  return FALSE;
}

/*
+----------------------------------------------------------------------+
| PROJECT : UMTS                  MODULE  : gaci                       |
|                                 ROUTINE : gaci_get_cid_over_dti_id   |
+----------------------------------------------------------------------+

  PURPOSE :

*/
GLOBAL SHORT gaci_get_cid_over_dti_id( UBYTE dti_id )
{
  T_PDP_CONTEXT_INTERNAL *p_pdp_context_node = NULL;
 
  p_pdp_context_node = p_pdp_context_list;
  while( p_pdp_context_node )
  {
  	/* Check for the link_id_new also because it will be filled for TCPIP call */
    if ( dti_id EQ EXTRACT_DTI_ID(p_pdp_context_node->internal_data.link_id) OR
         dti_id EQ EXTRACT_DTI_ID(p_pdp_context_node->internal_data.link_id_uart) OR
         dti_id EQ EXTRACT_DTI_ID(p_pdp_context_node->internal_data.link_id_new))

      return p_pdp_context_node->cid;

    p_pdp_context_node = p_pdp_context_node->p_next;
  }

  return PDP_CONTEXT_CID_INVALID;
}


GLOBAL SHORT gaci_get_cid_over_link_id ( T_DTI_CONN_LINK_ID  link_id )
{
  return gaci_get_cid_over_dti_id((UBYTE)EXTRACT_DTI_ID(link_id));
}



/*
+----------------------------------------------------------------------+
| PROJECT : -                     MODULE  : gaci                       |
|                                 ROUTINE : gaci_get_link_id_over_peer |
+----------------------------------------------------------------------+

  PURPOSE : */

/*
 *  Assumption: there is only one connection between UPM and the peer
 */
GLOBAL T_DTI_CONN_LINK_ID gaci_get_link_id_over_peer ( T_DTI_ENTITY_ID entity_id )
{
  T_PDP_CONTEXT_INTERNAL *p_pdp_context_node = NULL;

  p_pdp_context_node = p_pdp_context_list;

  while( p_pdp_context_node )
  {
    if( p_pdp_context_node->internal_data.entity_id EQ entity_id AND
        p_pdp_context_node->internal_data.link_id NEQ DTI_LINK_ID_NOTPRESENT ) 
    {
      return p_pdp_context_node->internal_data.link_id;
    }
  
    p_pdp_context_node = p_pdp_context_node->p_next;
  }
  return DTI_LINK_ID_NOTPRESENT;
}



#ifdef FF_SAT_E
GLOBAL void gaci_SAT_err(USHORT cause)
{
  SAT_error_cause = cause;
}
#endif /* FF_SAT_E */

GLOBAL void gaci_RAT_caller ( SHORT rat_id, SHORT cid, UBYTE cmdBuf, UBYTE cme_err )
{
  T_ACI_CMD_SRC  rat_owner = (T_ACI_CMD_SRC)get_owner_over_cid( (U8)cid );

  TRACE_FUNCTION("gaci_RAT_caller()");

#ifdef FF_SAT_E
  if ( !cmhSAT_OpChnGPRSPend( cid, OPCH_NONE ))
#endif /* FF_SAT_E */
  {
    switch ( rat_id )
    {
      case RAT_OK:
        R_AT( RAT_OK, rat_owner ) ( cmdBuf );
        break;
      case RAT_CME:
        ACI_ERR_DESC( ACI_ERR_CLASS_Cme, cme_err );     /* align aciErrDesc to cme_err */
        R_AT( RAT_CME, rat_owner ) ( cmdBuf, cme_err );
        /* log result */
        cmh_logRslt ( (T_ACI_CMD_SRC) rat_owner, RAT_CME, (T_ACI_AT_CMD) cmdBuf, 
                                     -1, BS_SPEED_NotPresent, (T_ACI_CME_ERR)cme_err );
        break;
      case RAT_NO_CARRIER:
        if (!(cmhSM_getSrcIdOfRunningCGACTDeactivation((U8)cid) EQ rat_owner))
        {
          R_AT( RAT_NO_CARRIER, rat_owner ) ( cmdBuf, 0 );
        }
        /* log result */
        cmh_logRslt ( (T_ACI_CMD_SRC) rat_owner, RAT_NO_CARRIER, (T_ACI_AT_CMD)cmdBuf, 
                                            (SHORT) 0, BS_SPEED_NotPresent,CME_ERR_NotPresent );
        break;
    }
  }
#ifdef FF_SAT_E
  else
  {
    /*
     *    SIM callback for SAT-class CE 
     */
    switch ( rat_id )
    {
      case RAT_OK:
        /* connection deactivated */
        cmhSAT_OpChnGPRSStat(SAT_GPRS_ACT, SAT_GPRS_INV_CAUSE); /* no cause given by primitive */
        break;
      case RAT_CME:
        if ( cmdBuf EQ AT_CMD_CGDATA )
        { /* 
           * Attach before ATD (UPM <-> IP <-> UDP <-> SIM) fails 
           */
          cmhSAT_OpChnGPRSStat(SAT_GPRS_ATT_FAILED, (UBYTE)SAT_error_cause);  
        }
        else
        { /* activate connection UPM <-> SIM fails */
          cmhSAT_OpChnGPRSStat(SAT_GPRS_ACT_FAILED, (UBYTE)SAT_error_cause);  
        }
        break;
      case RAT_NO_CARRIER:
          /* activate connection UPM <-> IP <-> UDP <-> SIM fails */
          cmhSAT_OpChnGPRSStat(SAT_GPRS_ACT_FAILED, (UBYTE)SAT_error_cause);
        break;
    }
  }
#endif /* FF_SAT_E */
}


/*
+----------------------------------------------------------------------------+
| PROJECT : UMTS                  MODULE  : gaci                             |
|                                 ROUTINE : pdp_context_create_node          |
+----------------------------------------------------------------------------+

  PURPOSE :

*/
GLOBAL T_PDP_CONTEXT_INTERNAL *pdp_context_create_node( U8 cid )
{
  T_PDP_CONTEXT_INTERNAL *p_new_pdp_context_node  = NULL;
  T_PDP_CONTEXT_INTERNAL *p_last_pdp_context_node = NULL;

  TRACE_FUNCTION("pdp_context_create_node()");
  
  if( !pdp_context_find_node_from_cid( cid ) )
  {
    /* No PDP context exist with same <cid> */
    p_last_pdp_context_node = pdp_context_find_last_node();

    if( p_last_pdp_context_node )
    {
      ACI_MALLOC( p_new_pdp_context_node, sizeof(T_PDP_CONTEXT_INTERNAL) );

      if( p_new_pdp_context_node )  
      {
        memset(p_new_pdp_context_node, 0x00, sizeof(T_PDP_CONTEXT_INTERNAL) );
        
        p_new_pdp_context_node->cid         = cid;
        p_new_pdp_context_node->p_next      = NULL;
        p_new_pdp_context_node->tft_changed = FALSE;

        /* As alborg code doesn't work on SN_SWITCH_REQ, they don't set the 
           values of link_id to 0xFF. But this is required in Berlin code.
         */

        p_new_pdp_context_node->internal_data.link_id      = DTI_LINK_ID_NOTPRESENT;
        /*p_new_pdp_context_node->internal_data.link_id_new  = DTI_LINK_ID_NOTPRESENT;*/
        p_new_pdp_context_node->internal_data.link_id_uart = DTI_LINK_ID_NOTPRESENT;

        if( p_last_pdp_context_node EQ (T_PDP_CONTEXT_INTERNAL*) &p_pdp_context_list )
          p_pdp_context_list = p_new_pdp_context_node;
        else
          p_last_pdp_context_node->p_next = p_new_pdp_context_node;

      }
    }  
  }
  
  return p_new_pdp_context_node;
  
}


/*
+----------------------------------------------------------------------------+
| PROJECT : UMTS                  MODULE  : gaci                             |
|                                 ROUTINE : pdp_context_remove_node          |
+----------------------------------------------------------------------------+

  PURPOSE :

*/
GLOBAL int pdp_context_remove_node( U8 cid )
{
  SHORT result = 0;
  T_PDP_CONTEXT_INTERNAL *p_pdp_context_node     = NULL;
  T_PDP_CONTEXT_INTERNAL *p_old_pdp_context_node = NULL;

  if( p_pdp_context_list )
  {
    if( p_pdp_context_list->cid NEQ cid )
    {
      p_pdp_context_node = p_pdp_context_list;
  
      /* find PDP context node for given <cid> */

      while( p_pdp_context_node->p_next AND p_pdp_context_node->p_next->cid NEQ cid )
      {
         p_pdp_context_node = p_pdp_context_node->p_next;
      }

      if( p_pdp_context_node->p_next->cid EQ cid )
      {
        p_old_pdp_context_node = p_pdp_context_node->p_next;
        p_pdp_context_node->p_next = p_pdp_context_node->p_next->p_next;
        ACI_MFREE( p_old_pdp_context_node );
        result = cid;
      }
    }
    else
    {
      p_old_pdp_context_node = p_pdp_context_list;
      
      if( p_pdp_context_list->p_next )
      {

        p_pdp_context_list = p_pdp_context_list->p_next;
      }
      else
      {
        p_pdp_context_list = NULL;
      }
      
      ACI_MFREE( p_old_pdp_context_node );
    }
    result = cid;
  }
  
  return result;
  
}


/*
+----------------------------------------------------------------------------+
| PROJECT : UMTS                  MODULE  : gaci                             |
|                                 ROUTINE : pdp_context_find_node_from_cid   |
+----------------------------------------------------------------------------+

  PURPOSE :

*/
GLOBAL T_PDP_CONTEXT_INTERNAL *pdp_context_find_node_from_cid( U8 cid )
{

  T_PDP_CONTEXT_INTERNAL *p_found_pdp_context_node = NULL;
  T_PDP_CONTEXT_INTERNAL *p_temp_pdp_context_node  = NULL;

  TRACE_EVENT("pdp_context_find_node_from_cid()");
  if( p_pdp_context_list )
  {
    if( p_pdp_context_list->cid EQ cid )
    {
      p_found_pdp_context_node = p_pdp_context_list;
    }
    else
    {
      p_temp_pdp_context_node = p_pdp_context_list->p_next;
      
      while( p_temp_pdp_context_node AND !p_found_pdp_context_node )
      {
        if( p_temp_pdp_context_node->cid EQ cid )
          p_found_pdp_context_node = p_temp_pdp_context_node;
        else
          p_temp_pdp_context_node = p_temp_pdp_context_node->p_next;
      }
    }
  }
  
  return p_found_pdp_context_node;

}


/*
+----------------------------------------------------------------------------+
| PROJECT : UMTS                MODULE  : gaci                               |
|                               ROUTINE : pdp_context_find_node_from_dti_id  |
+----------------------------------------------------------------------------+

  PURPOSE :

*/
GLOBAL T_PDP_CONTEXT_INTERNAL *pdp_context_find_node_from_dti_id( U8 dti_id )
{
  T_PDP_CONTEXT_INTERNAL *p_found_pdp_context_node  = NULL;

  p_found_pdp_context_node = p_pdp_context_list;
    
  while (p_found_pdp_context_node)
  {
    if( ( EXTRACT_DTI_ID(p_found_pdp_context_node->internal_data.link_id) EQ dti_id ) OR
        ( EXTRACT_DTI_ID(p_found_pdp_context_node->internal_data.link_id_uart) EQ dti_id ) )
    {
      return (p_found_pdp_context_node);
    }
    p_found_pdp_context_node = p_found_pdp_context_node->p_next;
  }
  
  return NULL;
}


/*
+-------------------------------------------------------------------------------+
| PROJECT : UMTS                  MODULE  : gaci                                |
|                                 ROUTINE : pdp_context_find_node_from_smreg_ti |
+-------------------------------------------------------------------------------+

  PURPOSE :

*/
GLOBAL T_PDP_CONTEXT_INTERNAL *pdp_context_find_node_from_smreg_ti( U8 smreg_ti )
{
  T_PDP_CONTEXT_INTERNAL *p_found_pdp_context_node  = NULL;
  T_PDP_CONTEXT_INTERNAL *p_temp_pdp_context_node   = NULL;

  if( p_pdp_context_list )
  {
    if( p_pdp_context_list->internal_data.smreg_ti EQ smreg_ti )
    {
      p_found_pdp_context_node = p_pdp_context_list;
    }
    else
    {
      p_temp_pdp_context_node = p_pdp_context_list->p_next;
      
      while( p_temp_pdp_context_node AND !p_found_pdp_context_node )
      {
        if( p_temp_pdp_context_node->internal_data.smreg_ti EQ smreg_ti )
          p_found_pdp_context_node = p_temp_pdp_context_node;
        else
          p_temp_pdp_context_node = p_temp_pdp_context_node->p_next;
      }
    }
  }
  
  return p_found_pdp_context_node;
  
}


/*
+----------------------------------------------------------------------------+
| PROJECT : UMTS                  MODULE  : gaci                             |
|                                 ROUTINE : pdp_context_find_last_node       |
+----------------------------------------------------------------------------+

  PURPOSE :

*/
GLOBAL T_PDP_CONTEXT_INTERNAL *pdp_context_find_last_node( void )
{

  T_PDP_CONTEXT_INTERNAL *p_last_pdp_context_node = NULL;

  if( p_pdp_context_list )
  {
    if( !p_pdp_context_list->p_next )
    {
      p_last_pdp_context_node = p_pdp_context_list;
    }
    else
    {
      p_last_pdp_context_node = p_pdp_context_list;

      while( p_last_pdp_context_node->p_next )
      {
         p_last_pdp_context_node = p_last_pdp_context_node->p_next;
         
         if( !p_last_pdp_context_node )
           break;
      }
    }
  }
  else
  {
    p_last_pdp_context_node = (T_PDP_CONTEXT_INTERNAL*) &p_pdp_context_list;
  }
  
  return p_last_pdp_context_node;

}

/*
+----------------------------------------------------------------------------+
| PROJECT : UMTS                  MODULE  : gaci                             |
|                                 ROUTINE : pdp_context_find_matching_node   |
+----------------------------------------------------------------------------+

  PURPOSE :

*/
GLOBAL T_PDP_CONTEXT_INTERNAL *pdp_context_find_matching_node( T_SMREG_VAL_pdp_type smreg_pdp_type, T_NAS_ctrl_ip_address ctrl_ip_addr, T_NAS_ip_address *ip_addr )
{

  T_PDP_CONTEXT_INTERNAL *p_pdp_context_node = NULL;
  T_PDP_TYPE             pdp_type;
  BOOL                   match = FALSE;


  switch( smreg_pdp_type )
  {
    case SMREG_PDP_PPP:
      strcpy( pdp_type, "PPP");
      break;
    
    case SMREG_PDP_IPV4:
      strcpy( pdp_type, "IP");
      break;
        
    case SMREG_PDP_IPV6:
      strcpy( pdp_type, "IPV6");
      break;
      
    default:
      return NULL;
  }

  p_pdp_context_node = p_pdp_context_list;

  while( p_pdp_context_node AND match EQ FALSE)
  {
    if( !strcmp(p_pdp_context_node->attributes.pdp_type, pdp_type) )
    {
      if( p_pdp_context_node->attributes.pdp_addr.ctrl_ip_address EQ ctrl_ip_addr )
      {
        switch( ctrl_ip_addr )
        {
          case NAS_is_ipv4:
          {
            if( !memcmp( &ip_addr->ipv4_addr,
                         &p_pdp_context_node->attributes.pdp_addr.ip_address.ipv4_addr,
                         NAS_SIZE_IPv4_ADDR ) )
                        
            {
              match = TRUE;
            }
            break;
          }

          case NAS_is_ipv6:
          {
            if( !memcmp( &ip_addr->ipv6_addr,
                         &p_pdp_context_node->attributes.pdp_addr.ip_address.ipv6_addr,
                         NAS_SIZE_IPv6_ADDR ) )
                      
            {
              match = TRUE;
            }
            break;
          }
        }
                        
      }

    }
    
    if( match EQ TRUE )
    {
      break;
    }
    else
    {
      p_pdp_context_node = p_pdp_context_node->p_next;
    }
    
  }
  
  return p_pdp_context_node;

}



GLOBAL U8 pdp_context_get_free_cid( void )
{
#if (PDP_CONTEXT_CID_MAX > 15)
  #error "Size of used_cid_list must be changed to fit the value of PDP_CONTEXT_CID_MAX"
#else
  U16                     used_cid_list      = 0;
#endif

  T_PDP_CONTEXT_INTERNAL *p_pdp_context_node = NULL;
  U8                      free_cid           = 0;

  p_pdp_context_node = p_pdp_context_list;

  while( p_pdp_context_node )
  {
    used_cid_list |= ( 0x0001 << (p_pdp_context_node->cid - 1) );
    p_pdp_context_node = p_pdp_context_node->p_next;
  }

  while( ( 0x0001 & (used_cid_list >> free_cid)) AND free_cid <= PDP_CONTEXT_CID_MAX )
  {
    free_cid++;
  }
  
  free_cid++;
 
  if( free_cid > PDP_CONTEXT_CID_MAX )
    free_cid = PDP_CONTEXT_CID_INVALID;

  return free_cid;
}



/*
+----------------------------------------------------------------------------+
| PROJECT : UMTS                  MODULE  : gaci                             |
|                                 ROUTINE : pdp_context_add_pf               |
+----------------------------------------------------------------------------+

  PURPOSE :

*/
GLOBAL T_TFT_INTERNAL *pdp_context_add_tft_pf( U8 cid, U8 tft_pf_id )
{
  T_TFT_INTERNAL *p_tft_pf_node_tmp_1 = NULL; /* TFT_PF_NODE  n - 1    */
  T_TFT_INTERNAL *p_tft_pf_node_tmp_0 = NULL; /* TFT_PF_NODE  n        */

  T_TFT_INTERNAL *p_tft_pf_node_new   = NULL; /* TFT_PF_NODE  new      */  

  T_PDP_CONTEXT_INTERNAL *p_pdp_context_node = NULL;
 
  p_pdp_context_node = pdp_context_find_node_from_cid( cid );
    
  if( p_pdp_context_node )
  {
    p_tft_pf_node_new = pdp_context_find_tft_pf( cid, tft_pf_id );

    if( ! p_tft_pf_node_new )
    {
      ACI_MALLOC( p_tft_pf_node_new, sizeof(T_TFT_INTERNAL));
      if( p_tft_pf_node_new )
      {
        p_tft_pf_node_new->pf_attributes.tft_pf_id = tft_pf_id;
        p_tft_pf_node_new->p_next                  = NULL;


        if( p_pdp_context_node->p_tft_pf )
        {
          if( p_pdp_context_node->p_tft_pf->pf_attributes.tft_pf_id > tft_pf_id )
          {
            p_tft_pf_node_new->p_next    = p_pdp_context_node->p_tft_pf;
            p_pdp_context_node->p_tft_pf = p_tft_pf_node_new;
          }
          else
          {
            if( p_pdp_context_node->p_tft_pf->p_next )
            {
              p_tft_pf_node_tmp_1 = p_pdp_context_node->p_tft_pf;
              p_tft_pf_node_tmp_0 = p_pdp_context_node->p_tft_pf->p_next;

              while( p_tft_pf_node_tmp_0 )
              {
                if( p_tft_pf_node_tmp_0->pf_attributes.tft_pf_id > tft_pf_id )
                {
                  p_tft_pf_node_new->p_next   = p_tft_pf_node_tmp_0;
                  p_tft_pf_node_tmp_1->p_next = p_tft_pf_node_new;
                  break;
                }
                else
                {
                  p_tft_pf_node_tmp_1 = p_tft_pf_node_tmp_0;
                  p_tft_pf_node_tmp_0 = p_tft_pf_node_tmp_0->p_next;
                }
              }
              p_tft_pf_node_tmp_1->p_next = p_tft_pf_node_new;
            }
            else
            {
              p_pdp_context_node->p_tft_pf->p_next = p_tft_pf_node_new;
            }
          }
        }
        else
        {
          p_pdp_context_node->p_tft_pf = p_tft_pf_node_new;
        }
      }
    }
    else
    {
      TRACE_ERROR( "ERROR: TFT PF with same PF_ID exist" );
      p_tft_pf_node_new = NULL;
    }

  }
  else
  {
    TRACE_ERROR( "ERROR: PDP context not found, in function pdp_context_add_tft_pf");
  }

  return p_tft_pf_node_new;  
  
}


/*
+----------------------------------------------------------------------------+
| PROJECT : UMTS                  MODULE  : gaci                             |
|                                 ROUTINE : pdp_context_del_pf               |
+----------------------------------------------------------------------------+

  PURPOSE : Delete the specified packet filter from a TFT.

*/
GLOBAL BOOL pdp_context_del_tft_pf( U8 cid, U8 tft_pf_id )
{
  BOOL result = FALSE;
  T_PDP_CONTEXT_INTERNAL *p_pdp_context_node = NULL;
  T_TFT_INTERNAL         *p_tft_pf_node      = NULL;
  T_TFT_INTERNAL         *p_tft_pf_node_old  = NULL;

  p_pdp_context_node = pdp_context_find_node_from_cid( cid );
  
  if( p_pdp_context_node AND p_pdp_context_node->p_tft_pf )
  {
    if( p_pdp_context_node->p_tft_pf->pf_attributes.tft_pf_id NEQ tft_pf_id )
    {
      p_tft_pf_node = p_pdp_context_node->p_tft_pf;

      while( p_tft_pf_node->p_next AND p_tft_pf_node->p_next->pf_attributes.tft_pf_id NEQ tft_pf_id )
      {
        p_tft_pf_node = p_tft_pf_node->p_next;
      }

      if( p_tft_pf_node->p_next->pf_attributes.tft_pf_id EQ tft_pf_id )
      {
        p_tft_pf_node_old = p_tft_pf_node->p_next;
        p_tft_pf_node->p_next = p_tft_pf_node->p_next->p_next;
        ACI_MFREE( p_tft_pf_node_old );
        result = TRUE;
      }
      else
      {
        TRACE_EVENT( "ERROR: PF not found for given PDP cid, in function pdp_context_del_tft_pf" );
      }
    }
    else
    {
      p_tft_pf_node_old = p_pdp_context_node->p_tft_pf;

      if( p_pdp_context_node->p_tft_pf->p_next )
      {
        p_pdp_context_node->p_tft_pf = p_pdp_context_node->p_tft_pf->p_next;
      }
      else
      {
        p_pdp_context_node->p_tft_pf = NULL;
      }

      ACI_MFREE( p_tft_pf_node_old );
      result = TRUE;
    }
  }
  else
  {
    TRACE_EVENT( "ERROR: in function pdp_context_del_tft_pf" );
  }

  return result;
  
}


/*
+----------------------------------------------------------------------------+
| PROJECT : UMTS                  MODULE  : gaci                             |
|                                 ROUTINE : pdp_context_del_tft              |
+----------------------------------------------------------------------------+

  PURPOSE : Delete all packet filters for an entire TFT.

*/
GLOBAL BOOL pdp_context_del_tft( U8 cid )
{
  BOOL result = TRUE;
  T_PDP_CONTEXT_INTERNAL *p_pdp_context_node = NULL;

  p_pdp_context_node = pdp_context_find_node_from_cid( cid );
  if( p_pdp_context_node )
  {
    while( p_pdp_context_node->p_tft_pf )
    {
      if( !pdp_context_del_tft_pf( cid, p_pdp_context_node->p_tft_pf->pf_attributes.tft_pf_id ) )
      {
        result = FALSE;
        break;
      }
    }
  }
  else
  {
    result = FALSE;
  }

  return result;
}


/*
+----------------------------------------------------------------------------+
| PROJECT : UMTS                  MODULE  : gaci                             |
|                                 ROUTINE : pdp_context_find_tft_pf          |
+----------------------------------------------------------------------------+

  PURPOSE : Find the specified packet flter in a TFT and return the pointer.

*/
GLOBAL T_TFT_INTERNAL *pdp_context_find_tft_pf( U8 cid, U8 tft_pf_id )
{
  T_TFT_INTERNAL         *p_tft_pf_node      = NULL;
  T_PDP_CONTEXT_INTERNAL *p_pdp_context_node = NULL;

  p_pdp_context_node = pdp_context_find_node_from_cid( cid );

  if( p_pdp_context_node )
  {

    if( p_pdp_context_node->p_tft_pf )
    {
      p_tft_pf_node = p_pdp_context_node->p_tft_pf;
    }
    
    if( p_tft_pf_node )
    {
      while( p_tft_pf_node->pf_attributes.tft_pf_id NEQ tft_pf_id AND p_tft_pf_node->p_next )
      {
        p_tft_pf_node = p_tft_pf_node->p_next;
      }

      if( p_tft_pf_node->pf_attributes.tft_pf_id NEQ tft_pf_id )
      {
        p_tft_pf_node = NULL;
      }
      
    }
    
  }

  return p_tft_pf_node;
  
}


/*
+----------------------------------------------------------------------------+
| PROJECT : UMTS                  MODULE  : gaci                             |
|                                 ROUTINE : pdp_context_find_tft_pf          |
+----------------------------------------------------------------------------+

  PURPOSE : return the number of defined packet filters in a TFT.

*/
GLOBAL U8 pdp_context_get_no_of_tft_pfs( U8 cid )
{
  T_PDP_CONTEXT_INTERNAL *p_pdp_context_node = NULL;
  T_TFT_INTERNAL         *p_tft_pf_node      = NULL;
  U8 pf_count = 0;

  p_pdp_context_node = pdp_context_find_node_from_cid( cid );

  if( p_pdp_context_node )
  {
    p_tft_pf_node = p_pdp_context_node->p_tft_pf;
    while( p_tft_pf_node )
    {
      pf_count++;
      p_tft_pf_node = p_tft_pf_node->p_next;
    }
  }

  return pf_count;
  
}


/*
+-------------------------------------------------------------------------------+
| PROJECT :                       MODULE  : gaci                                |
|                                 ROUTINE : pdp_context_check_if_nodes_exists   |
+-------------------------------------------------------------------------------+

  PURPOSE : Check if the PDP contexts specified in the cid_array are defined.
            The function will return 0 if all specified PDP contexts are defind
            othervise the first undefined cid in the cid_array is returned.
*/
GLOBAL U8 pdp_context_check_if_nodes_exists( U8 *cid_array )
{
  T_PDP_CONTEXT_INTERNAL *p_pdp_context_node = NULL;
  U8 i = 0;
  U8 result = 0;

  while( (cid_array[i] NEQ PDP_CONTEXT_CID_OMITTED) AND (i < PDP_CONTEXT_CID_MAX) )
  {
    p_pdp_context_node = pdp_context_find_node_from_cid( cid_array[i] );
    if( p_pdp_context_node )
    {
      i++;
    }
    else
    {
      result = cid_array[i];
      break;
    }
  }

  return result;
}


/*
+-------------------------------------------------------------------------------+
| PROJECT :                       MODULE  : gaci                                |
|                                 ROUTINE : pdp_context_check_if_nodes_exists   |
+-------------------------------------------------------------------------------+

  PURPOSE : Check if the pdp_type matches the specified PDP contexts in the cid_array.
            The function will return 0 if all specified PDP contexts matches
            othervise the first unmatched PDP context in the cid_array is returned.
*/
GLOBAL U8 pdp_context_validate_pdp_type( U8 *cid_array, T_PDP_TYPE pdp_type )
{
  T_PDP_CONTEXT_INTERNAL *p_pdp_context_node = NULL;
  U8 i = 0;
  U8 result = 0;

  while( (cid_array[i] NEQ PDP_CONTEXT_CID_OMITTED) AND (i < PDP_CONTEXT_CID_MAX) )
  {
    p_pdp_context_node = pdp_context_find_node_from_cid( cid_array[i] );
    if( p_pdp_context_node )
    {
      if( strcmp(p_pdp_context_node->attributes.pdp_type, pdp_type) )
      {
        result = cid_array[i];
        break;
      }
      i++;
    }
    else
    {

      result = cid_array[i];
      break;
    }
  }

  return result;
}


#endif  /* GPRS */