diff src/aci2/aci_ext/aci_ext_pers.c @ 3:93999a60b835

src/aci2, src/condat2: import of g23m/condat source pieces from TCS211
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 26 Sep 2016 00:29:36 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aci2/aci_ext/aci_ext_pers.c	Mon Sep 26 00:29:36 2016 +0000
@@ -0,0 +1,1211 @@
+/*
++-----------------------------------------------------------------------------
+|  Project :
+|  Modul   :  J:\g23m-aci\aci_ext\aci_ext_pers.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 :
++-----------------------------------------------------------------------------
+*/
+#include "aci_all.h"
+
+#include "aci_cmh.h"
+#include "ati_cmd.h"
+#include "aci_cmd.h"
+
+#ifdef FAX_AND_DATA
+#include "aci_fd.h"
+#endif    /* of #ifdef FAX_AND_DATA */
+
+#ifndef WIN32
+#define ACI_PERSONALISTION_USE_FFS
+#endif
+
+#include "aci.h"
+#include "psa.h"
+#include "psa_sim.h"
+#include "psa_sms.h"
+#include "psa_mmi.h"
+#include "cmh.h"
+#include "phb.h"
+#include "aoc.h"
+
+#include "psa_sim.h"            /* simShrdPrm */
+
+#include "aci_ext_pers.h"
+#include "aci_slock.h"
+
+#ifdef SIM_PERS
+#include "general.h" // inluded for UINT8 compilation error in sec_drv.h
+#include "sec_drv.h"
+#endif
+
+#include "pcm.h"                /* EF_SIMLCKEXT */
+
+#ifdef MMI_SIMLOCK_TEST
+  #define ACI_PERSONALISTION_USE_FFS
+#endif
+
+#ifdef ACI_PERSONALISTION_USE_FFS
+#include "ffs/ffs.h"
+#include "ffs_coat.h"
+#endif /* ACI_PERSONALISTION_USE_FFS */
+
+
+
+#define SLOCK_PCM_NRM 1         /* SIM lock data is stored as SIMLOCK to the PCM */
+#define SLOCK_PCM_EXT 2         /* SIM lock data is stored as SIMLOCKEXT to the PCM */
+
+#ifdef SIM_PERS
+#define TRUNCATION_KEY_LEN 8         /*  */
+#endif
+
+#ifdef _SIMULATION_
+EF_SIMLCKEXT simlock_pcm_memory;    /* emulates the PCM memory in WIN32 environment. */
+#endif
+
+#include "aci_ext_pers_cus.h" /* the customer specific personalisation data. */
+
+
+
+LOCAL void aci_ext_personalisation_MMI_save( void );
+
+static BOOL in_firstsimpersonalisation = FALSE;    /* this will be set when the phone is powered up after FIRST_SIMLOCK */
+static BOOL pre_firstsimpersonalisation = FALSE;   /* this will be set when a FIRST_SIM_LOCK is applied. All further locks
+                                                      will not be applied but only prepare the lock for the next power-cycle */
+LOCAL void decodeB2A (U8 *bcd, U8 bcd_len, U8 *ascii);          /* decode a BCD to ASCII */
+LOCAL void encodeA2B (char *ascii, UBYTE *bcd, UBYTE bcd_len);  /* encode ASCII to BCD, fill up with 0xf */
+#ifdef SIM_PERS
+EXTERN T_SEC_DRV_CONFIGURATION *cfg_data ;
+EXTERN  T_ACI_SIM_CONFIG aci_slock_sim_config;     /* SIM configuration, initialised by a T_SIM_MMI_INSERT_IND */
+#endif 
+
+
+
+
+/*
++------------------------------------------------------------------------------
+|  Function    : aci_ext_personalisation_init
++------------------------------------------------------------------------------
+|  Description : Initialisation of this extension. Might be used to initialise variables, set temporary data to default values and so on.
+  This method will be called once before any other public method of this extension.
+|
+|  Parameters  : void
+|
+|  Return      : void
+|
++------------------------------------------------------------------------------
+*/
+
+
+void aci_ext_personalisation_init( void )
+{
+#ifdef SIM_PERS
+  T_SIMLOCK_TYPE lock_type;
+  T_SEC_DRV_RETURN  status ;
+#endif
+
+  TRACE_FUNCTION("aci_ext_personalisation_init()");
+
+#ifndef MMI_SIMLOCK_TEST
+ 
+#ifdef SIM_PERS
+  
+  for (lock_type=SIMLOCK_NETWORK;lock_type<= SIMLOCK_FIRST_SIM ;lock_type++)  //prateek: changed loop till simlock_first_sim
+  {
+    switch (lock_type)
+    {
+      case SIMLOCK_NETWORK:       
+        status =sec_get_REC (lock_type, &personalisation_nw); 
+   if (status NEQ SEC_DRV_RET_Ok)
+     personalisation_nw = NULL;
+   else 
+   {
+          AciSLockShrd.status[lock_type]=(T_SIMLOCK_STATUS)personalisation_nw->Header.Status;
+          AciSLockShrd.dependency[lock_type]=personalisation_nw->Header.Dependency;
+        }
+      break;
+              
+      case SIMLOCK_NETWORK_SUBSET:   
+        status =sec_get_REC (lock_type, &personalisation_ns); 
+        if (status NEQ SEC_DRV_RET_Ok)
+          personalisation_ns= NULL;
+        else 
+        {
+          AciSLockShrd.status[lock_type]=(T_SIMLOCK_STATUS)personalisation_ns->Header.Status;
+          AciSLockShrd.dependency[lock_type]=personalisation_ns->Header.Dependency;
+        }
+      break;
+    
+      case SIMLOCK_SERVICE_PROVIDER:  
+   status =sec_get_REC (lock_type, &personalisation_sp); 
+        if (status NEQ SEC_DRV_RET_Ok)
+          personalisation_sp= NULL;
+        else 
+        {
+          AciSLockShrd.status[lock_type]=(T_SIMLOCK_STATUS)personalisation_sp->Header.Status;
+          AciSLockShrd.dependency[lock_type]=personalisation_sp->Header.Dependency;
+        }
+        break;
+    
+      case SIMLOCK_CORPORATE:      
+        status =sec_get_REC (lock_type, &personalisation_cp); 
+        if (status NEQ SEC_DRV_RET_Ok)
+          personalisation_cp = NULL;
+        else
+        {
+          AciSLockShrd.status[lock_type]=(T_SIMLOCK_STATUS)personalisation_cp->Header.Status;
+          AciSLockShrd.dependency[lock_type]=personalisation_cp->Header.Dependency;
+        }
+        break;
+    
+      case SIMLOCK_SIM:                  
+        status =sec_get_REC (lock_type, &personalisation_sim); 
+        if (status NEQ SEC_DRV_RET_Ok)
+          personalisation_sim = NULL;
+        else 
+        {
+          AciSLockShrd.status[lock_type]=(T_SIMLOCK_STATUS)personalisation_sim->Header.Status;
+          AciSLockShrd.dependency[lock_type]=personalisation_sim->Header.Dependency;
+        }
+        break;
+    
+        case SIMLOCK_FIRST_SIM:               
+          status =sec_get_REC (lock_type, &personalisation_first_sim); 
+          if (status NEQ SEC_DRV_RET_Ok)
+          {
+            TRACE_FUNCTION("personalisation_first_sim = NULL");
+            personalisation_first_sim = NULL;
+          }
+          else 
+          {
+            AciSLockShrd.status[lock_type]=(T_SIMLOCK_STATUS)personalisation_first_sim->Header.Status;
+            AciSLockShrd.dependency[lock_type]=personalisation_first_sim->Header.Dependency;
+          }
+          break;
+     }
+   }
+#endif //SIM_PERS
+
+#else /*for mmi testing, just read FFS*/  //MMI_SIMLOCK_TEST
+  
+#endif //MMI_SIMLOCK_TEST
+}
+
+#ifdef SIM_PERS
+
+/*
++------------------------------------------------------------------------------
+|  Function    : aci_ext_personalisation_free
++------------------------------------------------------------------------------
+|  Description : Free the MEPD structure Memory
+|
+|  Parameters  : void
+|
+|  Return      : void
+|
++------------------------------------------------------------------------------
+*/
+void aci_ext_personalisation_free( void )
+{
+   T_SIMLOCK_TYPE lock_type;
+
+   for (lock_type=SIMLOCK_NETWORK;lock_type<=SIMLOCK_FIRST_SIM;lock_type++)
+   {
+      switch (lock_type)
+      {
+         case SIMLOCK_NETWORK:   
+           if(personalisation_nw NEQ NULL)
+           {
+              if(personalisation_nw->pBody NEQ NULL)
+              {
+                MFREE(personalisation_nw->pBody);
+              }
+              MFREE(personalisation_nw);
+            }
+          break; 
+      
+          case SIMLOCK_NETWORK_SUBSET: 
+            if(personalisation_ns NEQ NULL)
+            {
+               if(personalisation_ns->pBody NEQ NULL)
+              {
+                MFREE(personalisation_ns->pBody);
+               }
+               MFREE(personalisation_ns);
+             }
+          break ; 
+
+          case SIMLOCK_SERVICE_PROVIDER:  
+            if(personalisation_sp NEQ NULL)
+            {
+               if(personalisation_sp->pBody NEQ NULL)
+              {
+                MFREE(personalisation_sp->pBody);
+              }
+              MFREE(personalisation_sp);
+            }
+            break ; 
+      
+            case SIMLOCK_CORPORATE: 
+              if(personalisation_cp NEQ NULL)
+             {
+                 if(personalisation_cp->pBody NEQ NULL)
+                {
+                  MFREE(personalisation_cp->pBody);
+                }
+                MFREE(personalisation_cp);
+             }
+             break ; 
+       
+             case SIMLOCK_SIM:   
+               if(personalisation_sim NEQ NULL)
+               {
+                 if(personalisation_sim->pBody NEQ NULL)
+                 {
+                    MFREE(personalisation_sim->pBody);
+                 }
+                 MFREE(personalisation_sim);
+               }
+               break ; 
+
+             case SIMLOCK_FIRST_SIM: 
+               if(personalisation_first_sim NEQ NULL)
+               {
+                  MFREE(personalisation_first_sim);
+               }
+               break; 
+      }
+   }
+}
+
+
+
+/*
+  ACI extension method for retrieving the status of a single personalisation type.
+  The personalisation status is stored in a separate customer managed memory area
+  and the customer has to implement this extension method to return the actual status
+  of the questioned personalisation type.
+  */
+T_SIMLOCK_STATUS aci_ext_personalisation_get_status( T_SIMLOCK_TYPE personalisation_type )
+{
+  TRACE_FUNCTION("aci_ext_personalisation_get_status()");
+  #ifdef SIM_PERS
+  switch (personalisation_type)
+  {
+    case SIMLOCK_FIRST_SIM: /* should not occour */
+    case SIMLOCK_SIM:         
+      if(personalisation_sim NEQ NULL)      
+        return personalisation_sim->Header.Status;
+      else 
+        return SIMLOCK_FAIL;
+                
+    case SIMLOCK_NETWORK:       
+      if(personalisation_nw NEQ NULL)      
+        return personalisation_nw->Header.Status;
+      else 
+        return SIMLOCK_FAIL;
+                    
+    case SIMLOCK_NETWORK_SUBSET:     
+      if(personalisation_ns NEQ NULL)    
+        return personalisation_ns->Header.Status;
+      else 
+        return SIMLOCK_FAIL;
+                    
+     case SIMLOCK_SERVICE_PROVIDER:  
+       if(personalisation_sp NEQ NULL)     
+         return personalisation_sp->Header.Status;
+       else 
+         return SIMLOCK_FAIL;
+     
+     case SIMLOCK_CORPORATE:      
+       if(personalisation_cp NEQ NULL)    
+         return personalisation_cp->Header.Status;
+       else 
+         return SIMLOCK_FAIL;
+
+     default:  return SIMLOCK_FAIL; /* should not occour */
+  }
+#endif
+}
+
+
+/*
++------------------------------------------------------------------------------
+|  Function    : aci_ext_personalisation_verify_password
++------------------------------------------------------------------------------
+|  Description : ACI extension method for verifying the passowrd.
+|f the password is false then the counter is increased. If maxcnt is reached then the key
+|will been blocked. If the password is correct then the counter is set to 0
+|
+|  Parameters  : personalisation_type - category type (Network,Network subset, Service Provider,Corporate,SIM actegory)
+|                     : passwd  - category password
+|
+|  Return      : T_SIMLOCK_STATUS   - Return SIMLOCK_DISABLED if compare key pass. 
+|                                                     - Return SIMLOCK_FAIL if compare key fails.
+|                                                     - Return SIMLOCK_BLOCKED if FC counter is greater than 
+|                                                        FC Max.
++------------------------------------------------------------------------------
+*/
+
+
+T_SIMLOCK_STATUS aci_ext_personalisation_verify_password( T_SIMLOCK_TYPE personalisation_type, char *passwd)
+{
+
+  T_SEC_DRV_RETURN ret;
+  UINT8 key_len = 0; /* For truncation check -Samir */
+
+
+  TRACE_FUNCTION("aci_ext_personalisation_verify_password()");
+
+
+  aci_slock_set_CFG();
+  if(cfg_data EQ NULL)
+  {
+   return SIMLOCK_FAIL;
+  }
+  
+  if(cfg_data->Flags & SEC_DRV_HDR_FLAG_Truncation)
+  {
+    key_len = TRUNCATION_KEY_LEN; 
+  }
+  ret = sec_cmp_KEY(personalisation_type, passwd, key_len) ;
+  MFREE(cfg_data);
+  
+  switch (ret)
+  {
+    case SEC_DRV_RET_Ok :
+      return SIMLOCK_DISABLED;
+    break;
+  
+    case SEC_DRV_RET_KeyWrong :
+    case SEC_DRV_RET_KeyMismatch:
+    /*Increment failure counter since input password is wrong*/
+      ret = sec_FC_Increment();
+      aci_slock_set_CFG();
+     if(cfg_data EQ NULL)
+     {
+       return SIMLOCK_FAIL;  
+     }
+      if(cfg_data->FC_Current >= cfg_data->FC_Max)
+      {
+        MFREE(cfg_data);
+        return SIMLOCK_BLOCKED;
+      }
+      else 
+      {
+         MFREE(cfg_data);
+         return SIMLOCK_FAIL;  
+      }
+    break;
+  
+    default:/*ERROR!- invalid input to sec_cmp_key OR some error in sec_cmp_key*/
+      return SIMLOCK_FAIL;
+    break;
+  }
+ }
+
+
+/*
++------------------------------------------------------------------------------
+|  Function    : aci_ext_personalisation_change_password
++------------------------------------------------------------------------------
+|  Description : ACI extension method for verifying the passowrd.
+|f the password is false then the counter is increased. If maxcnt is reached then the key
+|will been blocked. If the password is correct then the counter is set to 0
+|
+|  Parameters  : personalisation_type - category type (Network,Network subset, Service Provider,Corporate,SIM actegory)
+|                     : passwd  - category password
+|                     : new_passwd - New Password
+
+|  Return      : T_SIMLOCK_STATUS   - Return OPER_SUCCESS if changes password is successfull. 
+|                                                     - Return OPER_FAIL if changes password is fail.
+|                                                    
++------------------------------------------------------------------------------
+*/
+
+T_SIMLOCK_STATUS  aci_ext_personalisation_change_password( T_SIMLOCK_TYPE personalisation_type, char *passwd, char *new_passwd )
+{
+  T_SEC_DRV_RETURN   retstat;
+
+  retstat = sec_set_KEY (personalisation_type, passwd, new_passwd, 0);
+  switch(retstat)
+  {
+    case SEC_DRV_RET_Ok : 
+          return  OPER_SUCCESS;
+    case  SEC_DRV_RET_FCExeeded : 
+          AciSLockShrd.blocked = TRUE; 
+          return SIMLOCK_BLOCKED ; 
+   default : 
+         return OPER_FAIL;
+  }
+
+}
+ 
+
+/*
++------------------------------------------------------------------------------
+|  Function    : aci_ext_personalisation_set_status
++------------------------------------------------------------------------------
+|  Description : ACI extension method for lock/unlock the category record
+|
+|  Parameters   : personalisation_type - category type (Network,Network subset, Service Provider,Corporate,SIM actegory)
+|                     : lock   -   SIMLOCK_ENABLED to lock the category
+|                                -   SIMLOCK_DISABLED to unlock the category
+|                     : passwd  - category lock/unlock password
+|                     
+|
+|  Return      : T_SIMLOCK_STATUS   - Return SIMLOCK_ENABLED if lock is successfull. 
+|                                                     - Return SIMLOCK_DISABLED if unlock is successfull.
+|                                                     - Return SIMLOCK_FAIL if lock or unlock fails
+|                                                     - Return SIMLOCK_BLOCKED if FC exeeded for wrong password
+|                                                     - Return SIMLOCK_FAIL if lock/unlock fail
++------------------------------------------------------------------------------
+*/
+ 
+T_SIMLOCK_STATUS aci_ext_personalisation_set_status( T_SIMLOCK_TYPE personalisation_type,
+  T_SIMLOCK_STATUS lock, char* passwd)
+{
+
+//#ifdef SIM_PERS
+  int rec_num = personalisation_type;
+  int key_len = 0;
+  T_SEC_DRV_RETURN ret;
+
+  TRACE_FUNCTION("aci_ext_personalisation_set_status()");
+
+  if(lock EQ SIMLOCK_ENABLED)
+  {
+    ret=sec_rec_Lock(rec_num, passwd, key_len,0xffff/*depend mask*/);/*aci_ext_personalisation_verify_password(personalisation_type, passwd);*/
+    if (ret EQ SEC_DRV_RET_Ok){
+      return SIMLOCK_ENABLED;
+    }
+    else{
+      return SIMLOCK_FAIL;
+    }
+  }
+
+  if(lock EQ SIMLOCK_DISABLED)
+  {
+     if(cfg_data->Flags & SEC_DRV_HDR_FLAG_Truncation)
+     {
+        key_len = TRUNCATION_KEY_LEN; 
+     }
+     ret = sec_rec_Unlock(rec_num,TEMPORARY_UNLOCK,passwd,key_len,0xffff/*depend mask*/);
+     if (ret EQ SEC_DRV_RET_Ok){
+       return SIMLOCK_DISABLED;
+     }
+     else{
+       if(ret EQ SEC_DRV_RET_FCExeeded)
+      {
+         return SIMLOCK_BLOCKED;
+      }
+      return SIMLOCK_FAIL;
+     }
+  } 
+  return SIMLOCK_FAIL;
+  //#endif
+}
+
+
+
+
+//#ifdef SIM_PERS
+
+/*
++------------------------------------------------------------------------------
+|  Function    : aci_ext_slock_reset_fc
++------------------------------------------------------------------------------
+|  Description : For Failure Counter reset. Uses Security Drv. method to reset FC
+|
+|  Parameters   : fckey -  Password for resetting FC counter
+|                     
+|
+|  Return      : T_OPER_RET_STATUS   - Return OPER_SUCCESS if FC reset is successfull. 
+|                                                     - Return OPER_FAIL if FC reset is fail
+|                                                    
++------------------------------------------------------------------------------
+*/
+
+
+T_OPER_RET_STATUS  aci_ext_slock_reset_fc(char *fckey)
+{
+  T_SEC_DRV_RETURN retstat;
+
+  TRACE_FUNCTION("aci_ext_slock_reset_fc ()");
+
+  if ((retstat= sec_FC_Reset(fckey,0)) NEQ SEC_DRV_RET_Ok)
+  {
+    return OPER_FAIL;
+  }
+  else{
+    return OPER_SUCCESS;
+   }
+}
+
+
+
+/*
++------------------------------------------------------------------------------
+|  Function    : aci_ext_slock_sup_info
++------------------------------------------------------------------------------
+|  Description : For Supplementary Info( e.g. FC MAX, FC Current value).
+|
+|  Parameters   : sup_info -which has the element info type and Data value.  
+|                     
+|
+|  Return      : T_OPER_RET_STATUS   - Return OPER_SUCCESS if successfully read the Info type data
+|                                                         value from security driver. Info type data value is filled in this 
+|                                                         Function
+|                                                     - Return OPER_FAIL if unable to read Info type data value from 
+|                                                        security driverl
+|                                                    
++------------------------------------------------------------------------------
+*/
+
+
+T_OPER_RET_STATUS  aci_ext_slock_sup_info (T_SUP_INFO *sup_info)
+{
+  T_SEC_DRV_RETURN retstat;
+  T_SEC_DRV_CONFIGURATION *cfg_data;
+
+  TRACE_FUNCTION("aci_ext_slock_sup_info ()");
+
+  if ((retstat= sec_get_CFG(&cfg_data)) NEQ SEC_DRV_RET_Ok)
+  return OPER_FAIL;
+  else 
+  {
+     switch(sup_info->infoType )
+     {
+        case(FCMAX ): sup_info->datavalue = cfg_data->FC_Max;     break;
+        case(FCATTEMPTSLEFT): sup_info->datavalue = ((cfg_data->FC_Max) - (cfg_data->FC_Current));  break; 
+        case(FCRESETFAILMAX ): sup_info->datavalue = cfg_data->FC_Reset_Fail_Max ;   break;
+        case(FCRESETFAILATTEMPTSLEFT): sup_info->datavalue = ((cfg_data->FC_Reset_Fail_Max) - (cfg_data->FC_Reset_Fail_Current));  break;   
+        case(FCRESETSUCCESSMAX ): sup_info->datavalue = cfg_data->FC_Reset_Success_Max ;   break;
+        case(FCRESETSUCCESSATTEMPTSLEFT): sup_info->datavalue = ((cfg_data->FC_Reset_Success_Max) - (cfg_data->FC_Reset_Success_Current));  break; 
+        case (TIMERFLAG): sup_info->datavalue =(cfg_data->Flags & SEC_DRV_HDR_FLAG_Unlock_Timer)?(1):(0) ;break; 
+        case (ETSIFLAG) :  sup_info->datavalue =(cfg_data->Flags & SEC_DRV_HDR_FLAG_ETSI_Flag)?(1):(0) ;   break;
+        case (AIRTELINDFLAG) :  sup_info->datavalue =(cfg_data->Flags & SEC_DRV_HDR_FLAG_Airtel_Ind)?(1):(0) ; break;
+     }  /*end of switch*/
+
+     MFREE(cfg_data);/*  deallocate configuration data allocated from Security Driver;    */  
+     return OPER_SUCCESS;    
+  }
+}
+
+
+/*
++------------------------------------------------------------------------------
+|  Function    : aci_ext_auto_personalise
++------------------------------------------------------------------------------
+|  Description : It Auto personalise the sim. If Add new IMSI flag set and If Code is not present
+|                      then adds the code in the MEPD data  and lock the category ( Network, Sub Network,
+|                      sim )
+|  Parameters : void  
+|                     
+|
+|  Return        :    void
+|                                                    
++------------------------------------------------------------------------------
+*/
+
+T_AUTOLOCK_STATUS aci_ext_auto_personalise(T_SIMLOCK_TYPE   current_lock)
+{
+  UBYTE imsi_sim[MAX_IMSI_LEN+1];
+  T_SIMLOCK_TYPE type;
+  static UINT16 flag  = 0xffff;
+  UINT16 dependMask = 0xffff;
+ 
+  TRACE_FUNCTION("aci_ext_auto_personalise()");
+ 
+  psaSIM_decodeIMSI(simShrdPrm.imsi.field, simShrdPrm.imsi.c_field, (char *)imsi_sim);
+ 
+  if((AciSLockShrd.dependency[SIMLOCK_FIRST_SIM] & NW_AUTO_LOCK)AND (flag & NW_AUTO_LOCK))
+  {
+     type = SIMLOCK_NETWORK;
+     sim_code_present_in_me = FALSE;
+     aci_slock_check_NWlock(imsi_sim,1);
+     flag &= ~(NW_AUTO_LOCK);
+
+    if(sim_code_present_in_me EQ FALSE)
+    {
+      aci_ext_add_code(type);
+    }
+  }
+
+  if((AciSLockShrd.dependency[SIMLOCK_FIRST_SIM] & NS_AUTO_LOCK)AND (flag & NS_AUTO_LOCK))
+  {
+     type = SIMLOCK_NETWORK_SUBSET;
+     flag &= ~(NS_AUTO_LOCK);
+     sim_code_present_in_me = FALSE;
+     aci_slock_check_NSlock(imsi_sim,1);
+     if(sim_code_present_in_me EQ FALSE)
+     {
+       aci_ext_add_code(type);
+     }
+  }
+
+  if((AciSLockShrd.dependency[SIMLOCK_FIRST_SIM] & SIM_AUTO_LOCK ) AND (flag & SIM_AUTO_LOCK))
+  {
+    flag &= ~(SIM_AUTO_LOCK);
+    type = SIMLOCK_SIM;
+    sim_code_present_in_me = FALSE;
+    aci_slock_check_SMlock(imsi_sim,1);
+    if(sim_code_present_in_me EQ FALSE)
+    {
+      aci_ext_add_code(type);
+    }
+  }
+
+ if((AciSLockShrd.dependency[SIMLOCK_FIRST_SIM] & SP_AUTO_LOCK) AND (flag & SP_AUTO_LOCK))
+ {
+  if (psaSIM_ChkSIMSrvSup(SRV_GrpLvl1))
+  {
+     if(aci_slock_sim_config.sim_read_gid1 EQ FALSE)
+     {
+      aci_slock_sim_read_sim(SIM_GID1, NOT_PRESENT_8BIT, MAX_GID);
+      return AUTOLOCK_EXCT ;
+     }
+     else 
+      {       
+       type = SIMLOCK_SERVICE_PROVIDER;
+       sim_code_present_in_me = FALSE;
+       aci_slock_check_SPlock(imsi_sim,1);
+       TRACE_FUNCTION_P1("SP sim_code_present_in_me = %d", sim_code_present_in_me);
+       switch(sim_code_present_in_me)
+        {
+         case FALSE : 
+               aci_ext_add_code(type);
+               break;
+         case CHECK_FAIL :
+              dependMask &= ~(SP_AUTO_LOCK) ;
+              break;
+         case TRUE : 
+              break;
+         }
+         flag &= ~(SP_AUTO_LOCK);
+      
+     }
+  }
+  else 
+  {
+     flag &= ~(SP_AUTO_LOCK);
+     dependMask &= ~(SP_AUTO_LOCK) ;
+  }
+ } 
+
+
+if((AciSLockShrd.dependency[SIMLOCK_FIRST_SIM] & CP_AUTO_LOCK)AND (flag & CP_AUTO_LOCK)) 
+ {
+
+   if (psaSIM_ChkSIMSrvSup(SRV_GrpLvl1))
+     {
+        if(aci_slock_sim_config.sim_read_gid1 EQ FALSE)
+        {
+         aci_slock_sim_read_sim(SIM_GID1, NOT_PRESENT_8BIT, MAX_GID);
+         return AUTOLOCK_EXCT ;
+        }
+     }
+
+  if (psaSIM_ChkSIMSrvSup(SRV_GrpLvl2))
+     {
+        if(aci_slock_sim_config.sim_read_gid2 EQ FALSE)
+        {
+         aci_slock_sim_read_sim(SIM_GID2, NOT_PRESENT_8BIT, MAX_GID);
+         return AUTOLOCK_EXCT ;
+        }
+     }
+   
+ if(psaSIM_ChkSIMSrvSup(SRV_GrpLvl1) AND psaSIM_ChkSIMSrvSup(SRV_GrpLvl2))
+ {
+   type = SIMLOCK_CORPORATE;
+   sim_code_present_in_me = FALSE;
+   aci_slock_check_CPlock(imsi_sim,1);
+   switch(sim_code_present_in_me)
+    {
+      case FALSE : 
+           aci_ext_add_code(type);
+           break;
+      case CHECK_FAIL :
+           dependMask &= ~(CP_AUTO_LOCK) ;
+           break;
+       case TRUE : 
+            break;
+    }
+   flag &= ~(CP_AUTO_LOCK);
+ }
+  else 
+  {
+   flag &= ~(CP_AUTO_LOCK);
+   dependMask &= ~(CP_AUTO_LOCK) ;
+  }
+  
+}
+
+
+ sec_rec_Lock(SIMLOCK_FIRST_SIM,NULL,0,dependMask); 
+ return AUTOLOCK_CMPL ;
+  
+}
+
+
+
+
+/*
++------------------------------------------------------------------------------
+|  Function    : aci_ext_add_code
++------------------------------------------------------------------------------
+|  Description : It append the category code in MEPD. It foolows FIFO procedure while adding the code
+|  Parameters : type -- category type (Network,Network subset, Service Provider,Corporate,SIM actegory)  
+|  Return        :  void
+|                                                    
++------------------------------------------------------------------------------
+*/
+
+
+void aci_ext_add_code(T_SIMLOCK_TYPE type)
+{
+  UINT16 index =0;
+  UBYTE max_num_user_codes;
+  UBYTE num_user_codes;
+  UBYTE curr_user_code_index;
+  UBYTE  imsi_field[20];
+
+  TRACE_FUNCTION("aci_ext_add_code()");
+  switch (type)
+  {
+    case SIMLOCK_NETWORK :
+      max_num_user_codes =((UBYTE *) personalisation_nw->pBody)[index];
+      index += MAX_NUM_USERCODE_SIZE;
+      index += NUM_OPCODE_SIZE ;
+      index += OPCODE_LEN_SIZE + ((UBYTE *) personalisation_nw->pBody)[OPCODE_LEN_INDEX];
+
+      num_user_codes = ((UBYTE *)personalisation_nw->pBody)[index];
+      index +=NUM_USER_CODE_SIZE;
+      curr_user_code_index = ((UBYTE *)personalisation_nw->pBody)[index];
+      index += CURR_USER_CODE_INDEX_SIZE ;
+    
+      memcpy(imsi_field,simShrdPrm.imsi.field,simShrdPrm.imsi.c_field);
+      if(simShrdPrm.mnc_len EQ 3)
+        imsi_field[NW_CODE_LEN-1] |= 0xf0;  
+      else 
+        imsi_field[NW_CODE_LEN-1] |= 0xff;
+    
+      /*
+       * If no user code is present, curr_user_code_index will be ff. After that, curr_user_code_index starts from 0
+       */
+      if((curr_user_code_index EQ 0xff) || (curr_user_code_index EQ max_num_user_codes -1)) 
+        curr_user_code_index = 0;  
+      else 
+        curr_user_code_index++;
+
+      if(num_user_codes < max_num_user_codes)
+        num_user_codes++;
+    
+      memcpy(&((UBYTE *)personalisation_nw->pBody)[index + curr_user_code_index*NW_CODE_LEN/*multiplication added*/],imsi_field,NW_CODE_LEN);
+      ((UBYTE *)personalisation_nw->pBody)[index - CURR_USER_CODE_INDEX_SIZE] = curr_user_code_index;
+      ((UBYTE *)personalisation_nw->pBody)[index - CURR_USER_CODE_INDEX_SIZE - NUM_USER_CODE_SIZE] = num_user_codes;
+      sec_set_REC(type, personalisation_nw);
+    break;
+
+    
+    case SIMLOCK_NETWORK_SUBSET :
+      max_num_user_codes = ((UBYTE *)personalisation_ns->pBody)[index];
+      index += MAX_NUM_USERCODE_SIZE;
+      index += NUM_OPCODE_SIZE ;
+      index += OPCODE_LEN_SIZE + ((UBYTE *) personalisation_ns->pBody)[OPCODE_LEN_INDEX];
+  
+      num_user_codes = ((UBYTE *) personalisation_ns->pBody)[index];
+      index +=NUM_USER_CODE_SIZE;
+      curr_user_code_index = ((UBYTE *)personalisation_ns->pBody)[index];
+      index += CURR_USER_CODE_INDEX_SIZE ;
+  
+      memcpy(imsi_field,simShrdPrm.imsi.field,simShrdPrm.imsi.c_field);
+      if(simShrdPrm.mnc_len EQ 3)
+        imsi_field[NW_NS_CODE_LEN-1] |= 0xf0;  //prateek: unused nibbles to be '00' and not 'FF' to avoid problems during decodeIMSI
+      else 
+        imsi_field[NW_NS_CODE_LEN-1] |= 0xff;
+    
+      /*
+       * If no user code is present, curr_user_code_index will be ff. After that, curr_user_code_index starts from 0
+       */
+      if((curr_user_code_index EQ 0xff) || (curr_user_code_index EQ max_num_user_codes -1)) 
+        curr_user_code_index = 0;  
+      else 
+        curr_user_code_index++;
+  
+      if(num_user_codes < max_num_user_codes)
+        num_user_codes++;
+    
+      memcpy(&((UBYTE *)personalisation_ns->pBody)[index + curr_user_code_index*NW_NS_CODE_LEN/*multiplication added*/],imsi_field,NW_NS_CODE_LEN);
+      ((UBYTE *)personalisation_ns->pBody)[index - CURR_USER_CODE_INDEX_SIZE] = curr_user_code_index;
+      ((UBYTE *)personalisation_ns->pBody)[index - CURR_USER_CODE_INDEX_SIZE - NUM_USER_CODE_SIZE] = num_user_codes;
+      sec_set_REC(type, personalisation_ns);
+    break;
+    
+    case SIMLOCK_SERVICE_PROVIDER :
+      if(!psaSIM_ChkSIMSrvSup(SRV_GrpLvl1) OR (aci_slock_sim_config.sim_gidl1[0] EQ 0xff))
+      {
+        TRACE_FUNCTION("not adding sp as no service or maybe gid1 is ff");
+        break;
+      }
+        
+      max_num_user_codes =((UBYTE *) personalisation_sp->pBody)[index];
+      index += MAX_NUM_USERCODE_SIZE;
+      index += NUM_OPCODE_SIZE ;
+      index += OPCODE_LEN_SIZE +  ((UBYTE *)personalisation_sp->pBody)[OPCODE_LEN_INDEX];
+  
+      num_user_codes = ((UBYTE *)personalisation_sp->pBody)[index];
+      index +=NUM_USER_CODE_SIZE;
+      curr_user_code_index = ((UBYTE *)personalisation_sp->pBody)[index];
+      index += CURR_USER_CODE_INDEX_SIZE ;
+      
+      memcpy(imsi_field,simShrdPrm.imsi.field,simShrdPrm.imsi.c_field);
+      if(simShrdPrm.mnc_len EQ 3)
+        imsi_field[NW_CODE_LEN-1] |= 0xf0;  //prateek: unused nibbles to be '00' and not 'FF' to avoid problems during decodeIMSI
+      else 
+        imsi_field[NW_CODE_LEN-1] |= 0xff;
+    
+      if (psaSIM_ChkSIMSrvSup(SRV_GrpLvl1))
+      {
+        memcpy(imsi_field+NW_CODE_LEN,aci_slock_sim_config.sim_gidl1,GID1_LEN);
+        /*
+          * If no user code is present, curr_user_code_index will be ff. After that, curr_user_code_index starts from 0
+              */
+        if((curr_user_code_index EQ 0xff) || (curr_user_code_index EQ max_num_user_codes -1)) 
+          curr_user_code_index = 0;  
+        else 
+          curr_user_code_index++;
+  
+        if(num_user_codes < max_num_user_codes)
+          num_user_codes++;
+      
+        memcpy(&((UBYTE *)personalisation_sp->pBody)[index + curr_user_code_index*(NW_CODE_LEN+GID1_LEN)],imsi_field,NW_CODE_LEN+GID1_LEN);
+        ((UBYTE *)personalisation_sp->pBody)[index - CURR_USER_CODE_INDEX_SIZE] = curr_user_code_index;
+        ((UBYTE *)personalisation_sp->pBody)[index - CURR_USER_CODE_INDEX_SIZE - NUM_USER_CODE_SIZE] = num_user_codes;
+        sec_set_REC(type, personalisation_sp);
+       }
+     break;
+    
+    case SIMLOCK_CORPORATE :
+      if( !psaSIM_ChkSIMSrvSup(SRV_GrpLvl1) OR !psaSIM_ChkSIMSrvSup(SRV_GrpLvl2) OR (aci_slock_sim_config.sim_gidl1[0] EQ 0xff) OR (aci_slock_sim_config.sim_gidl2[0] EQ 0xff))
+      {
+        TRACE_FUNCTION("not adding cp as no service or maybe gid1/gid2 is ff");
+        break;
+      }
+      
+      max_num_user_codes = ((UBYTE *)personalisation_cp->pBody)[index];
+      index += MAX_NUM_USERCODE_SIZE;
+      index += NUM_OPCODE_SIZE ;
+      index += OPCODE_LEN_SIZE +  ((UBYTE *)personalisation_cp->pBody)[OPCODE_LEN_INDEX];
+    
+      num_user_codes = ((UBYTE *)personalisation_cp->pBody)[index];
+      index +=NUM_USER_CODE_SIZE;
+      curr_user_code_index =((UBYTE *) personalisation_cp->pBody)[index];
+      index += CURR_USER_CODE_INDEX_SIZE ;
+      
+      memcpy(imsi_field,simShrdPrm.imsi.field,simShrdPrm.imsi.c_field);
+      if(simShrdPrm.mnc_len EQ 3)
+        imsi_field[NW_CODE_LEN-1] |= 0xf0;  //prateek: unused nibbles to be '00' and not 'FF' to avoid problems during decodeIMSI
+      else
+        /* if(curr_user_code_index NEQ 0) */
+        imsi_field[NW_CODE_LEN-1] |= 0xff;
+    
+      if (psaSIM_ChkSIMSrvSup(SRV_GrpLvl1))
+      {
+        memcpy(imsi_field+NW_CODE_LEN,aci_slock_sim_config.sim_gidl1,GID1_LEN);
+        if (psaSIM_ChkSIMSrvSup(SRV_GrpLvl2))
+        {
+          memcpy(imsi_field+NW_CODE_LEN+GID1_LEN,aci_slock_sim_config.sim_gidl2,GID2_LEN);
+          /*
+          * If no user code is present, curr_user_code_index will be ff. After that, curr_user_code_index starts from 0
+          */
+          if((curr_user_code_index EQ 0xff) || (curr_user_code_index EQ max_num_user_codes -1)) 
+            curr_user_code_index = 0;  
+          else 
+            curr_user_code_index++;
+      
+          if(num_user_codes < max_num_user_codes)
+            num_user_codes++;
+      
+          memcpy(&((UBYTE *)personalisation_cp->pBody)[index + (curr_user_code_index*(NW_CODE_LEN+GID1_LEN+GID2_LEN))],imsi_field,NW_CODE_LEN+GID1_LEN+GID2_LEN);
+          ((UBYTE *)personalisation_cp->pBody)[index - CURR_USER_CODE_INDEX_SIZE] = curr_user_code_index;
+          ((UBYTE *)personalisation_cp->pBody)[index - CURR_USER_CODE_INDEX_SIZE - NUM_USER_CODE_SIZE] = num_user_codes;
+          sec_set_REC(type, personalisation_cp);
+        }
+      }
+    break;
+    
+    case SIMLOCK_SIM :
+      max_num_user_codes = ((UBYTE *)personalisation_sim->pBody)[index];
+      index += MAX_NUM_USERCODE_SIZE;
+      index += NUM_OPCODE_SIZE ;
+      index += OPCODE_LEN_SIZE +  ((UBYTE *)personalisation_sim->pBody)[OPCODE_LEN_INDEX];
+  
+      num_user_codes = ((UBYTE *)personalisation_sim->pBody)[index];
+      index +=NUM_USER_CODE_SIZE;
+      curr_user_code_index =((UBYTE *) personalisation_sim->pBody)[index];    
+      index += CURR_USER_CODE_INDEX_SIZE ;
+  
+      memcpy(imsi_field,simShrdPrm.imsi.field,simShrdPrm.imsi.c_field);
+
+      /*
+       * If no user code is present, curr_user_code_index will be ff. After that, curr_user_code_index starts from 0
+       */
+      if((curr_user_code_index EQ 0xff) || (curr_user_code_index EQ max_num_user_codes -1)) 
+        curr_user_code_index = 0;  
+      else 
+        curr_user_code_index++;
+  
+      if(num_user_codes < max_num_user_codes)
+        num_user_codes++;
+    
+      memcpy(&((UBYTE *)personalisation_sim->pBody)[index + (curr_user_code_index*NW_NS_MSIN_CODE_LEN)],imsi_field,NW_NS_MSIN_CODE_LEN);
+      ((UBYTE *)personalisation_sim->pBody)[index - CURR_USER_CODE_INDEX_SIZE] = curr_user_code_index;
+      ((UBYTE *)personalisation_sim->pBody)[index - CURR_USER_CODE_INDEX_SIZE - NUM_USER_CODE_SIZE] = num_user_codes;
+      sec_set_REC(type, personalisation_sim);
+    break;
+  }
+}
+
+#endif //SIM_PERS
+
+
+
+/*
+  PURPOSE : convert bcd to ASCII
+
+*/
+
+LOCAL void decodeB2A (U8 *bcd, U8 bcd_len, U8 *ascii)
+{
+  int i;
+
+  TRACE_FUNCTION("decodeB2A()");
+
+  for (i=0; i<bcd_len*2; i++)
+  {
+    ascii[i] = (i & 1) ? (bcd[i/2]>>4)+'0' : (bcd[i/2]&0x0f)+'0';
+    if (ascii[i]>'9')
+      break;  /* stop if invalid digit */
+  }
+  ascii[i] = 0;
+}
+
+LOCAL void encodeA2B (char *ascii, UBYTE *bcd, UBYTE bcd_len)
+{
+  UBYTE i;
+  UBYTE digit;
+
+  TRACE_FUNCTION("encodeA2B()");
+
+  for (i=0; i<bcd_len*2; i++)                 /* fill the whole field, pad remaining with 0xf */
+  {
+    if (i<strlen(ascii))
+    {
+      digit = ascii[i];
+      if (digit >= '0' OR digit <= '9')
+      {
+        digit-= '0';
+      }
+      else
+      {
+        TRACE_EVENT_P1("[WRN] invalid digit in PIN \"%d\", skipping!", digit);
+        digit = 0x0f;
+      }
+    }
+    else
+    {
+      digit = 0x0f;  /* unused nibbles are set to 'F'. */
+    }
+
+    if ((i & 1) EQ 0)
+    {
+      /* process PIN digit 1,3,5,... at i=0,2,4,...*/
+      bcd[i/2]  = digit;
+    }
+    else
+    {
+      /* process PIN digit 2,4,6,... at i=1,3,5,...*/
+      bcd[i/2] |= digit << 4;
+    }
+  }
+}
+
+/* That is a workaround to buld simulation test cases */
+#ifdef _SIMULATION_ 
+  #define effs_t int
+  #define EFFS_OK 0
+  #define FFS_fread(a,  b, c) 0
+  #define FFS_fwrite(a,  b, c) 0
+#endif /*_SIMULATION_*/ 
+
+#define FFS_MMILOCK_STATUS "/gsm/MELOCK/SecCode"
+
+/*
++------------------------------------------------------------------------------
+|  Function    : aci_ext_personalisation_CS_get_status
++------------------------------------------------------------------------------
+|  Description : Reads value of CS lock from file and returns it
+|  Parameters : void  
+|                     
+|
+|  Return        :    status of CS lock
+|  Remark:     :  It is proposed to implement through security driver
+|                                                    
++------------------------------------------------------------------------------
+*/
+T_SIMLOCK_STATUS aci_ext_personalisation_CS_get_status()
+{
+  effs_t ffs_rslt;
+  TRACE_FUNCTION("aci_ext_personalisation_CS_get_status()");
+
+  ffs_rslt = FFS_fread(FFS_MMILOCK_STATUS,  &MMI_personalisation_status, sizeof(MMI_personalisation_status));
+  if(ffs_rslt < EFFS_OK)   /* error */
+  {
+    TRACE_EVENT_P1("unable to read %s", FFS_MMILOCK_STATUS);                    
+    return SIMLOCK_FAIL;
+  }
+  return (T_SIMLOCK_STATUS) MMI_personalisation_status.State;
+}
+
+
+/*
++------------------------------------------------------------------------------
+|  Function    : aci_ext_personalisation_CS_set_status
++------------------------------------------------------------------------------
+|  Description : Reads value of CS lock from file compare passwords and if correct, sets CS lock status
+|  Parameters : lock - enable or disable
+|                      passwd - password to change lock
+|                     
+|
+|  Return        :  status of operation
+|  Remark:     :  It is proposed to implement through security driver
+|                                                    
++------------------------------------------------------------------------------
+*/
+T_SIMLOCK_STATUS aci_ext_personalisation_CS_set_status(T_SIMLOCK_STATUS lock , char * passwd)
+{
+  effs_t ffs_rslt;
+  U8 exp_pin[6+1];   /* +1 for '\0' */    
+  T_SIMLOCK_STATUS result;
+  TRACE_FUNCTION("aci_ext_personalisation_CS_set_status()");    
+
+  /* Read data from FFS */ 
+
+  ffs_rslt = FFS_fread(FFS_MMILOCK_STATUS,  &MMI_personalisation_status, sizeof(MMI_personalisation_status));
+
+  if(ffs_rslt < EFFS_OK)   /* error */
+  {
+    TRACE_EVENT_P1("unable to read %s", FFS_MMILOCK_STATUS);
+    return SIMLOCK_FAIL;
+  }
+ 
+  decodeB2A(MMI_personalisation_status.Cur_code, 3, exp_pin);    
+  
+  if (strncmp((char *)exp_pin, (char *)passwd, 3))  
+  {    
+     /* Block personalisation lock, if tried too often! */    
+     MMI_personalisation_status.cnt++;   
+     if (MMI_personalisation_status.cnt >= MMI_personalisation_status.maxcnt)    
+     {      
+        result = SIMLOCK_BLOCKED;
+     }    
+     else
+     {
+        result = SIMLOCK_LOCKED;
+     }
+   }  
+   else 
+   {    
+      /* correct pin passed */
+     MMI_personalisation_status.cnt = 0; 
+     MMI_personalisation_status.State = lock; 
+     result = lock;
+    }
+    ffs_rslt = FFS_fwrite(FFS_MMILOCK_STATUS,  &MMI_personalisation_status, sizeof(MMI_personalisation_status));
+
+    if (ffs_rslt < EFFS_OK)
+    {
+      TRACE_EVENT_P1("unable to write %s", FFS_MMILOCK_STATUS);                    
+      return SIMLOCK_FAIL;
+    }
+    return result;
+}
+
+/*
++------------------------------------------------------------------------------
+|  Function    : aci_ext_personalisation_CS_change_password
++------------------------------------------------------------------------------
+|  Description : Reads value of CS lock from file compare passwords and if correct, sets new password
+|  Parameters : passwd - old password
+|                      new_passwd - new password
+|                     
+|
+|  Return        :  status of operation
+|  Remark:     :  It is proposed to implement through security driver
+|                                                    
++------------------------------------------------------------------------------
+*/
+T_SIMLOCK_STATUS aci_ext_personalisation_CS_change_password( char *passwd, char *new_passwd )
+{
+  effs_t ffs_rslt;
+  U8 exp_pin[6+1];   /* +1 for '\0' */    
+  T_SIMLOCK_STATUS result;
+
+  TRACE_FUNCTION("aci_ext_personalisation_CS_change_password()");
+
+  ffs_rslt = FFS_fread(FFS_MMILOCK_STATUS,  &MMI_personalisation_status, sizeof(MMI_personalisation_status));
+  if(ffs_rslt < EFFS_OK)   /* error */
+  {
+    TRACE_EVENT_P1("unable to read %s", FFS_MMILOCK_STATUS);  
+    return SIMLOCK_FAIL;
+  }
+
+
+  decodeB2A(MMI_personalisation_status.Cur_code, 3, exp_pin);
+
+  if (strncmp((char *)exp_pin, (char *)passwd, 3))
+  {
+    /* Block personalisation lock, if tried too often! */
+    MMI_personalisation_status.cnt++;
+    if (MMI_personalisation_status.cnt >= MMI_personalisation_status.maxcnt)    
+     {      
+        result = SIMLOCK_BLOCKED;
+     }    
+     else
+     {
+        result = SIMLOCK_LOCKED;
+     }
+  }
+  else
+  {
+    /* correct pin passed */
+    MMI_personalisation_status.cnt=0;
+    if ((new_passwd NEQ NULL) AND (strlen(new_passwd) > 0))
+    {
+      encodeA2B(new_passwd, MMI_personalisation_status.Cur_code, 3);
+    }
+    result = (T_SIMLOCK_STATUS) MMI_personalisation_status.State;  
+  }
+   ffs_rslt = FFS_fwrite(FFS_MMILOCK_STATUS,  &MMI_personalisation_status, sizeof(MMI_personalisation_status));
+
+  if (ffs_rslt < EFFS_OK)
+  {
+    TRACE_EVENT_P1("unable to write %s", FFS_MMILOCK_STATUS);  
+    return SIMLOCK_FAIL;
+  }
+  return result;
+}
+
+