/******************************************************************************* OpenAirInterface Copyright(c) 1999 - 2014 Eurecom OpenAirInterface is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenAirInterface is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenAirInterface.The full GNU General Public License is included in this distribution in the file called "COPYING". If not, see <http://www.gnu.org/licenses/>. Contact Information OpenAirInterface Admin: openair_admin@eurecom.fr OpenAirInterface Tech : openair_tech@eurecom.fr OpenAirInterface Dev : openair4g-devel@eurecom.fr Address : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France. *******************************************************************************/ /***************************************************************************** Source emm_main.c Version 0.1 Date 2012/10/10 Product NAS stack Subsystem EPS Mobility Management Author Frederic Maurel Description Defines the EPS Mobility Management procedure call manager, the main entry point for elementary EMM processing. *****************************************************************************/ #include "emm_main.h" #include "nas_log.h" #include "emmData.h" #ifdef NAS_UE #include "memory.h" #include "usim_api.h" #include "IdleMode.h" #include <string.h> // memset, memcpy, strlen #include <stdio.h> // sprintf #include <stdlib.h> // malloc, free #endif #if defined(EPC_BUILD) && defined(NAS_MME) # include "mme_config.h" #endif /****************************************************************************/ /**************** E X T E R N A L D E F I N I T I O N S ****************/ /****************************************************************************/ /****************************************************************************/ /******************* L O C A L D E F I N I T I O N S *******************/ /****************************************************************************/ #ifdef NAS_UE static int _emm_main_get_imei(imei_t *imei, const char *imei_str); static int _emm_main_imsi_cmp(imsi_t *imsi1, imsi_t *imsi2); static const char *_emm_main_get_plmn(const plmn_t *plmn, int index, int format, size_t *size); static int _emm_main_get_plmn_index(const char *plmn, int format); /* * USIM application data */ static usim_data_t _usim_data; /* * Callback executed whenever a change in the network has to be notified * to the user application */ static emm_indication_callback_t _emm_main_user_callback; static int _emm_main_callback(int); #endif /****************************************************************************/ /****************** E X P O R T E D F U N C T I O N S ******************/ /****************************************************************************/ #ifdef NAS_UE /**************************************************************************** ** ** ** Name: emm_main_initialize() ** ** ** ** Description: Initializes EMM internal data ** ** ** ** Inputs: cb: The user notification callback ** ** imei: The IMEI read from the UE's non-volatile ** ** memory ** ** Others: _usim_data ** ** ** ** Outputs: None ** ** Return: None ** ** Others: _emm_data ** ** ** ***************************************************************************/ void emm_main_initialize(emm_indication_callback_t cb, const char *imei) { LOG_FUNC_IN; /* USIM validity indicator */ _emm_data.usim_is_valid = FALSE; /* The IMEI read from the UE's non-volatile memory */ _emm_data.imei = (imei_t *)malloc(sizeof(imei_t)); _emm_data.imei->length = _emm_main_get_imei(_emm_data.imei, imei); /* The IMSI, valid only if USIM is present */ _emm_data.imsi = NULL; /* EPS location information */ _emm_data.guti = NULL; _emm_data.tai = NULL; _emm_data.ltai.n_tais = 0; /* EPS Connection Management status */ _emm_data.ecm_status = ECM_IDLE; /* Network selection mode of operation */ _emm_data.plmn_mode = EMM_DATA_PLMN_AUTO; /* Index of the PLMN manually selected by the user */ _emm_data.plmn_index = -1; /* Selected Radio Access Technology */ _emm_data.plmn_rat = NET_ACCESS_UNAVAILABLE; /* Selected PLMN */ memset(&_emm_data.splmn, 0xFF, sizeof(plmn_t)); _emm_data.is_rplmn = FALSE; _emm_data.is_eplmn = FALSE; /* Radio Access Technology of the serving cell */ _emm_data.rat = NET_ACCESS_UNAVAILABLE; /* Network registration status */ _emm_data.stat = NET_REG_STATE_OFF; _emm_data.is_attached = FALSE; _emm_data.is_emergency = FALSE; /* Location/Tracking area code */ _emm_data.tac = 0; // two byte in hexadecimal format /* Identifier of the serving cell */ _emm_data.ci = 0; // four byte in hexadecimal format /* List of operators present in the network */ memset(_emm_data.plist.buffer, 0, EMM_DATA_BUFFER_SIZE + 1); /* Home PLMN */ memset(&_emm_data.hplmn, 0xFF, sizeof(plmn_t)); /* List of Forbidden PLMNs */ _emm_data.fplmn.n_plmns = 0; /* List of Forbidden PLMNs for GPRS service */ _emm_data.fplmn_gprs.n_plmns = 0; /* List of Equivalent HPLMNs */ _emm_data.ehplmn.n_plmns = 0; /* List of user controlled PLMNs */ _emm_data.plmn.n_plmns = 0; /* List of operator controlled PLMNs */ _emm_data.oplmn.n_plmns = 0; /* List of operator network name records */ _emm_data.n_opnns = 0; /* List of Forbidden Tracking Areas */ _emm_data.ftai.n_tais = 0; /* List of Forbidden Tracking Areas for roaming */ _emm_data.ftai_roaming.n_tais = 0; /* * Get USIM application data */ if ( usim_api_read(&_usim_data) != RETURNok ) { /* The USIM application may not be present or not valid */ LOG_TRACE(WARNING, "EMM-MAIN - Failed to read USIM application data"); } else { int i; /* The USIM application is present and valid */ LOG_TRACE(INFO, "EMM-MAIN - USIM application data successfully read"); _emm_data.usim_is_valid = TRUE; /* Get the Home PLMN derived from the IMSI */ _emm_data.hplmn.MCCdigit1 = _usim_data.imsi.u.num.digit1; _emm_data.hplmn.MCCdigit2 = _usim_data.imsi.u.num.digit2; _emm_data.hplmn.MCCdigit3 = _usim_data.imsi.u.num.digit3; _emm_data.hplmn.MNCdigit1 = _usim_data.imsi.u.num.digit4; _emm_data.hplmn.MNCdigit2 = _usim_data.imsi.u.num.digit5; _emm_data.hplmn.MNCdigit3 = _usim_data.imsi.u.num.digit6; /* Get the list of forbidden PLMNs */ for (i=0; (i < EMM_DATA_FPLMN_MAX) && (i < USIM_FPLMN_MAX); i++) { if ( PLMN_IS_VALID(_usim_data.fplmn[i]) ) { _emm_data.fplmn.plmn[i] = _usim_data.fplmn[i]; _emm_data.fplmn.n_plmns += 1; } } /* Get the list of Equivalent HPLMNs */ for (i=0; (i < EMM_DATA_EHPLMN_MAX) && (i < USIM_EHPLMN_MAX); i++) { if ( PLMN_IS_VALID(_usim_data.ehplmn[i]) ) { _emm_data.ehplmn.plmn[i] = _usim_data.ehplmn[i]; _emm_data.ehplmn.n_plmns += 1; } } /* Get the list of User controlled PLMN Selector */ for (i=0; (i < EMM_DATA_PLMN_MAX) && (i < USIM_PLMN_MAX); i++) { if ( PLMN_IS_VALID(_usim_data.plmn[i].plmn) ) { _emm_data.plmn.plmn[i] = _usim_data.plmn[i].plmn; _emm_data.userAcT[i] = _usim_data.plmn[i].AcT; _emm_data.plmn.n_plmns += 1; } } /* Get the list of Operator controlled PLMN Selector */ for (i=0; (i < EMM_DATA_OPLMN_MAX) && (i < USIM_OPLMN_MAX); i++) { if ( PLMN_IS_VALID(_usim_data.oplmn[i].plmn) ) { _emm_data.oplmn.plmn[i] = _usim_data.oplmn[i].plmn; _emm_data.operAcT[i] = _usim_data.oplmn[i].AcT; _emm_data.oplmn.n_plmns += 1; } } /* Get the list of Operator network name records */ for (i=0; (i < EMM_DATA_OPNN_MAX) && (i < USIM_OPL_MAX); i++) { if ( PLMN_IS_VALID(_usim_data.opl[i].plmn) ) { int pnn_id = _usim_data.opl[i].record_id; _emm_data.opnn[i].plmn = &_usim_data.opl[i].plmn; _emm_data.opnn[i].fullname = (char *)_usim_data.pnn[pnn_id].fullname.value; _emm_data.opnn[i].shortname = (char *)_usim_data.pnn[pnn_id].shortname.value; _emm_data.n_opnns += 1; } } /* TODO: Get the Higher Priority PLMN search period parameter */ /* Get the EPS location information */ if (PLMN_IS_VALID(_usim_data.epsloci.guti.gummei.plmn)) { _emm_data.guti = &_usim_data.epsloci.guti; } if (TAI_IS_VALID(_usim_data.epsloci.tai)) { _emm_data.tai = &_usim_data.epsloci.tai; } _emm_data.status = _usim_data.epsloci.status; /* Get NAS configuration parameters */ _emm_data.NAS_SignallingPriority = _usim_data.nasconfig.NAS_SignallingPriority.value[0]; _emm_data.NMO_I_Behaviour = _usim_data.nasconfig.NMO_I_Behaviour.value[0]; _emm_data.AttachWithImsi = _usim_data.nasconfig.AttachWithImsi.value[0]; _emm_data.MinimumPeriodicSearchTimer = _usim_data.nasconfig.MinimumPeriodicSearchTimer.value[0]; _emm_data.ExtendedAccessBarring = _usim_data.nasconfig.ExtendedAccessBarring.value[0]; _emm_data.Timer_T3245_Behaviour = _usim_data.nasconfig.Timer_T3245_Behaviour.value[0]; /* * Get EPS NAS security context */ /* Create NAS security context */ _emm_data.security = (emm_security_context_t *)malloc(sizeof(emm_security_context_t)); if (_emm_data.security != NULL) { memset(_emm_data.security, 0, sizeof(emm_security_context_t)); /* Type of security context */ if (_usim_data.securityctx.KSIasme.value[0] != USIM_KSI_NOT_AVAILABLE) { _emm_data.security->type = EMM_KSI_NATIVE; } else { _emm_data.security->type = EMM_KSI_NOT_AVAILABLE; } /* EPS key set identifier */ _emm_data.security->eksi = _usim_data.securityctx.KSIasme.value[0]; /* ASME security key */ _emm_data.security->kasme.length = _usim_data.securityctx.Kasme.length; _emm_data.security->kasme.value = (uint8_t *)malloc(_emm_data.security->kasme.length); if (_emm_data.security->kasme.value) { memcpy(_emm_data.security->kasme.value, _usim_data.securityctx.Kasme.value, _emm_data.security->kasme.length); } /* Downlink count parameter */ if (_usim_data.securityctx.dlNAScount.length <= sizeof(UInt32_t)) { memcpy(&_emm_data.security->dl_count, _usim_data.securityctx.dlNAScount.value, _usim_data.securityctx.dlNAScount.length); } /* Uplink count parameter */ if (_usim_data.securityctx.ulNAScount.length <= sizeof(UInt32_t)) { memcpy(&_emm_data.security->ul_count, _usim_data.securityctx.ulNAScount.value, _usim_data.securityctx.ulNAScount.length); } /* Ciphering algorithm */ _emm_data.security->capability.eps_encryption = ((_usim_data.securityctx.algorithmID.value[0] >> 4) & 0xf); /* Identity protection algorithm */ _emm_data.security->capability.eps_integrity = (_usim_data.securityctx.algorithmID.value[0] & 0xf); /* NAS integrity and cyphering keys are not available */ } else { LOG_TRACE(WARNING, "EMM-PROC - Failed to create security context"); } /* * Get EMM data from the UE's non-volatile memory */ memset(&_emm_data.nvdata.rplmn, 0xFF, sizeof(plmn_t)); _emm_data.nvdata.eplmn.n_plmns = 0; /* Get EMM data pathname */ char *path = memory_get_path(EMM_NVRAM_DIRNAME, EMM_NVRAM_FILENAME); if (path == NULL) { LOG_TRACE(ERROR, "EMM-MAIN - Failed to get EMM data pathname"); } else { /* Get EMM data stored in the non-volatile memory device */ int rc = memory_read(path, &_emm_data.nvdata, sizeof(emm_nvdata_t)); if (rc != RETURNok) { LOG_TRACE(ERROR, "EMM-MAIN - Failed to read %s", path); } else { /* Check the IMSI */ LOG_TRACE(INFO, "EMM-MAIN - EMM data successfully read"); _emm_data.imsi = &_usim_data.imsi; int imsi_ok = _emm_main_imsi_cmp(&_emm_data.nvdata.imsi, &_usim_data.imsi); if (!imsi_ok) { LOG_TRACE(WARNING, "EMM-MAIN - IMSI checking failed nvram: " "%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x, " "usim: %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x", _emm_data.nvdata.imsi.u.value[0], _emm_data.nvdata.imsi.u.value[1], _emm_data.nvdata.imsi.u.value[2], _emm_data.nvdata.imsi.u.value[3], _emm_data.nvdata.imsi.u.value[4], _emm_data.nvdata.imsi.u.value[5], _emm_data.nvdata.imsi.u.value[6], _emm_data.nvdata.imsi.u.value[7], _usim_data.imsi.u.value[0], _usim_data.imsi.u.value[1], _usim_data.imsi.u.value[2], _usim_data.imsi.u.value[3], _usim_data.imsi.u.value[4], _usim_data.imsi.u.value[5], _usim_data.imsi.u.value[6], _usim_data.imsi.u.value[7]); memset(&_emm_data.nvdata.rplmn, 0xFF, sizeof(plmn_t)); _emm_data.nvdata.eplmn.n_plmns = 0; } } free(path); } } /* * Initialize EMM timers */ T3410.id = NAS_TIMER_INACTIVE_ID; T3410.sec = T3410_DEFAULT_VALUE; T3411.id = NAS_TIMER_INACTIVE_ID; T3411.sec = T3411_DEFAULT_VALUE; T3402.id = NAS_TIMER_INACTIVE_ID; T3402.sec = T3402_DEFAULT_VALUE; T3416.id = NAS_TIMER_INACTIVE_ID; T3416.sec = T3416_DEFAULT_VALUE; T3417.id = NAS_TIMER_INACTIVE_ID; T3417.sec = T3417_DEFAULT_VALUE; T3418.id = NAS_TIMER_INACTIVE_ID; T3418.sec = T3418_DEFAULT_VALUE; T3420.id = NAS_TIMER_INACTIVE_ID; T3420.sec = T3420_DEFAULT_VALUE; T3421.id = NAS_TIMER_INACTIVE_ID; T3421.sec = T3421_DEFAULT_VALUE; T3423.id = NAS_TIMER_INACTIVE_ID; T3423.sec = T3423_DEFAULT_VALUE; T3430.id = NAS_TIMER_INACTIVE_ID; T3430.sec = T3430_DEFAULT_VALUE; /* * Initialize the user notification callback */ _emm_main_user_callback = *cb; /* * Initialize EMM internal data used for UE in idle mode */ IdleMode_initialize(&_emm_main_callback); LOG_FUNC_OUT; } #endif #ifdef NAS_MME /**************************************************************************** ** ** ** Name: emm_main_initialize() ** ** ** ** Description: Initializes EMM internal data ** ** ** ** Inputs: None ** ** Others: None ** ** ** ** Outputs: None ** ** Return: None ** ** Others: _emm_data ** ** ** ***************************************************************************/ #if defined(EPC_BUILD) void emm_main_initialize(mme_config_t *mme_config_p) #else void emm_main_initialize(void) #endif { LOG_FUNC_IN; /* Retreive MME supported configuration data */ #if defined(EPC_BUILD) if (mme_api_get_emm_config(&_emm_data.conf, mme_config_p) != RETURNok) #else if (mme_api_get_emm_config(&_emm_data.conf) != RETURNok) #endif { LOG_TRACE(ERROR, "EMM-MAIN - Failed to get MME configuration data"); } #if defined(EPC_BUILD) RB_INIT(&_emm_data.ctx_map); #endif /* * Initialize EMM timers */ T3450.id = NAS_TIMER_INACTIVE_ID; T3450.sec = T3450_DEFAULT_VALUE; T3460.id = NAS_TIMER_INACTIVE_ID; T3460.sec = T3460_DEFAULT_VALUE; T3470.id = NAS_TIMER_INACTIVE_ID; T3470.sec = T3470_DEFAULT_VALUE; LOG_FUNC_OUT; } #endif /**************************************************************************** ** ** ** Name: emm_main_cleanup() ** ** ** ** Description: Performs the EPS Mobility Management clean up procedure ** ** ** ** Inputs: None ** ** Others: None ** ** ** ** Outputs: None ** ** Return: None ** ** Others: None ** ** ** ***************************************************************************/ void emm_main_cleanup(void) { LOG_FUNC_IN; #ifdef NAS_UE if (_emm_data.usim_is_valid) { /* * TODO: Update USIM application data */ #if 0 int i; /* Update the list of Forbidden PLMNs */ for (i=0; (i < _emm_data.fplmn.n_plmns) && (i < USIM_FPLMN_MAX); i++) { _usim_data.fplmn[i] = _emm_data.fplmn.plmn[i]; } /* Update the list of Equivalent HPLMNs */ for (i=0; (i < _emm_data.ehplmn.n_plmns) && (i < USIM_EHPLMN_MAX); i++) { _usim_data.ehplmn[i] = _emm_data.ehplmn.plmn[i]; } /* Update the GUTI */ if (_emm_data.guti) { _usim_data.epsloci.guti = *(_emm_data.guti); } /* Update the last visited registered TAI */ if (_emm_data.tai) { _usim_data.epsloci.tai = *(_emm_data.tai); } /* Update the EPS location information */ _usim_data.epsloci.status = _emm_data.status; if (_emm_data.security && (_emm_data.security->type == EMM_KSI_NATIVE)) { /* TODO: Update the EPS security context parameters from the full * native EPS security context */ } /* * Store USIM application data * - List of forbidden PLMNs */ if ( usim_api_write(&_usim_data) != RETURNok ) { /* The USIM application may not be present or not valid */ LOG_TRACE(WARNING, "EMM-MAIN - " "Failed to write USIM application data"); } #endif } /* * Store EMM data into the UE's non-volatile memory * - Registered PLMN * - List of equivalent PLMNs */ char *path = memory_get_path(EMM_NVRAM_DIRNAME, EMM_NVRAM_FILENAME); if (path == NULL) { LOG_TRACE(ERROR, "EMM-MAIN - Failed to get EMM data pathname"); } else { int rc = memory_write(path, &_emm_data.nvdata, sizeof(emm_nvdata_t)); if (rc != RETURNok) { LOG_TRACE(ERROR, "EMM-MAIN - Failed to write %s", path); } } /* Release dynamically allocated memory */ if (_emm_data.imei) { free(_emm_data.imei); _emm_data.imei = NULL; } if (_emm_data.security) { emm_security_context_t *security = _emm_data.security; if (security->kasme.value) { free(security->kasme.value); security->kasme.value = NULL; security->kasme.length = 0; } if (security->knas_enc.value) { free(security->knas_enc.value); security->knas_enc.value = NULL; security->knas_enc.length = 0; } if (security->knas_int.value) { free(security->knas_int.value); security->knas_int.value = NULL; security->knas_int.length = 0; } free(_emm_data.security); _emm_data.security = NULL; } #endif // NAS_UE LOG_FUNC_OUT; } #ifdef NAS_UE /**************************************************************************** ** ** ** Name: emm_main_get_imsi() ** ** ** ** Description: Get the International Mobile Subscriber Identity number ** ** ** ** Inputs: None ** ** Others: _emm_data ** ** ** ** Outputs: None ** ** Return: Pointer to the IMSI ** ** Others: None ** ** ** ***************************************************************************/ const imsi_t *emm_main_get_imsi(void) { LOG_FUNC_IN; LOG_FUNC_RETURN (&_emm_data.nvdata.imsi); } /**************************************************************************** ** ** ** Name: emm_main_get_msisdn() ** ** ** ** Description: Get the Mobile Subscriber Dialing Number from the USIM ** ** ** ** Inputs: None ** ** Others: _usim_data ** ** ** ** Outputs: None ** ** Return: Pointer to the subscriber dialing number ** ** Others: None ** ** ** ***************************************************************************/ const msisdn_t *emm_main_get_msisdn(void) { LOG_FUNC_IN; LOG_FUNC_RETURN (&_usim_data.msisdn.number); } /**************************************************************************** ** ** ** Name: emm_main_set_plmn_selection_mode() ** ** ** ** Description: Set the network selection mode of operation to the given ** ** mode and update the manually selected network selection ** ** data ** ** ** ** Inputs: mode: The specified network selection mode of ** ** operation ** ** format: The representation format of the PLMN ** ** identifier ** ** plmn: Identifier of the selected PLMN ** ** rat: The selected Radio Access Techonology ** ** Others: None ** ** ** ** Outputs: None ** ** Return: RETURNok, RETURNerror ** ** Others: _emm_data ** ** ** ***************************************************************************/ int emm_main_set_plmn_selection_mode(int mode, int format, const network_plmn_t *plmn, int rat) { LOG_FUNC_IN; int index; LOG_TRACE(INFO, "EMM-MAIN - PLMN selection: mode=%d, format=%d, plmn=%s, " "rat=%d", mode, format, (const char *)&plmn->id, rat); _emm_data.plmn_mode = mode; if (mode != EMM_DATA_PLMN_AUTO) { /* Get the index of the PLMN in the list of available PLMNs */ index = _emm_main_get_plmn_index((const char *)&plmn->id, format); if (index < 0) { LOG_TRACE(WARNING, "EMM-MAIN - PLMN %s not available", (const char *)&plmn->id); } else { /* Update the manually selected network selection data */ _emm_data.plmn_index = index; _emm_data.plmn_rat = rat; } } else { /* * Get the index of the last PLMN the UE already tried to automatically * register to when switched on; the equivalent PLMNs list shall not be * applied to the user reselection in Automatic Network Selection Mode. */ index = IdleMode_get_hplmn_index(); } LOG_FUNC_RETURN (index); } /**************************************************************************** ** ** ** Name: emm_main_get_plmn_selection_mode() ** ** ** ** Description: Get the current value of the network selection mode of ** ** operation ** ** ** ** Inputs: None ** ** Others: _emm_data ** ** ** ** Outputs: None ** ** Return: The value of the network selection mode ** ** Others: None ** ** ** ***************************************************************************/ int emm_main_get_plmn_selection_mode(void) { LOG_FUNC_IN; LOG_FUNC_RETURN (_emm_data.plmn_mode); } /**************************************************************************** ** ** ** Name: emm_main_get_plmn_list() ** ** ** ** Description: Get the list of available PLMNs ** ** ** ** Inputs: None ** ** Others: _emm_data ** ** ** ** Outputs: plist: Pointer to the list of available PLMNs ** ** Return: The size of the list in bytes ** ** Others: None ** ** ** ***************************************************************************/ int emm_main_get_plmn_list(const char **plist) { LOG_FUNC_IN; int size = IdleMode_update_plmn_list(0); *plist = _emm_data.plist.buffer; LOG_FUNC_RETURN (size); } /**************************************************************************** ** ** ** Name: emm_main_get_selected_plmn() ** ** ** ** Description: Get the identifier of the currently selected PLMN ** ** ** ** Inputs: format: The requested format of the string repre- ** ** sentation of the PLMN identifier ** ** Others: _emm_data ** ** ** ** Outputs: plmn: The selected PLMN identifier coded in the ** ** requested format ** ** Return: A pointer to the string representation of ** ** the selected PLMN ** ** Others: None ** ** ** ***************************************************************************/ const char *emm_main_get_selected_plmn(network_plmn_t *plmn, int format) { LOG_FUNC_IN; size_t size = 0; /* * Get the identifier of the selected PLMN in the list of available PLMNs */ int index = IdleMode_get_splmn_index(); if ( !(index < 0) ) { const char *name = _emm_main_get_plmn(&_emm_data.splmn, index, format, &size); if (size > 0) { LOG_FUNC_RETURN ((char *) memcpy(&plmn->id, name, size)); } } LOG_FUNC_RETURN (NULL); } /**************************************************************************** ** ** ** Name: emm_main_get_registered_plmn() ** ** ** ** Description: Get the identifier of the currently registered PLMN ** ** ** ** Inputs: format: The requested format of the string repre- ** ** sentation of the PLMN identifier ** ** Others: _emm_data ** ** ** ** Outputs: plmn: The registered PLMN identifier coded in ** ** the requested format ** ** Return: A pointer to the string representation of ** ** the registered PLMN ** ** Others: None ** ** ** ***************************************************************************/ const char *emm_main_get_registered_plmn(network_plmn_t *plmn, int format) { LOG_FUNC_IN; size_t size = 0; /* * Get the identifier of the registered PLMN in the list of available PLMNs */ int index = IdleMode_get_rplmn_index(); if ( !(index < 0) ) { const char *name = _emm_main_get_plmn(&_emm_data.nvdata.rplmn, index, format, &size); if (size > 0) { LOG_FUNC_RETURN ((char *) memcpy(&plmn->id, name, size)); } } LOG_FUNC_RETURN (NULL); } /**************************************************************************** ** ** ** Name: emm_main_get_plmn_status() ** ** ** ** Description: Get the value of the network registration status which ** ** shows whether the network has currently indicated the ** ** registration of the UE ** ** ** ** Inputs: None ** ** Others: _emm_data ** ** ** ** Outputs: None ** ** Return: The current network registration status ** ** Others: None ** ** ** ***************************************************************************/ Stat_t emm_main_get_plmn_status(void) { LOG_FUNC_IN; LOG_FUNC_RETURN (_emm_data.stat); } /**************************************************************************** ** ** ** Name: emm_main_get_plmn_tac() ** ** ** ** Description: Get the code of the Tracking area the registered PLMN ** ** belongs to ** ** ** ** Inputs: None ** ** Others: _emm_data ** ** ** ** Outputs: None ** ** Return: The Location/Tracking area code ** ** Others: None ** ** ** ***************************************************************************/ tac_t emm_main_get_plmn_tac(void) { LOG_FUNC_IN; LOG_FUNC_RETURN (_emm_data.tac); } /**************************************************************************** ** ** ** Name: emm_main_get_plmn_ci() ** ** ** ** Description: Get the identifier of the serving cell ** ** ** ** Inputs: None ** ** Others: _emm_data ** ** ** ** Outputs: None ** ** Return: The serving cell identifier ** ** Others: None ** ** ** ***************************************************************************/ ci_t emm_main_get_plmn_ci(void) { LOG_FUNC_IN; LOG_FUNC_RETURN (_emm_data.ci); } /**************************************************************************** ** ** ** Name: emm_main_get_plmn_rat() ** ** ** ** Description: Get the value of the Radio Access Technology of the ser- ** ** ving cell ** ** ** ** Inputs: None ** ** Others: _emm_data ** ** ** ** Outputs: None ** ** Return: The value of the Radio Access Technology ** ** of the serving cell ** ** Others: None ** ** ** ***************************************************************************/ AcT_t emm_main_get_plmn_rat(void) { LOG_FUNC_IN; LOG_FUNC_RETURN (_emm_data.rat); } /**************************************************************************** ** ** ** Name: emm_main_is_attached() ** ** ** ** Description: Indicates whether the UE is currently attached to the ** ** network for EPS services or emergency service only ** ** ** ** Inputs: None ** ** Others: _emm_data ** ** ** ** Outputs: None ** ** Return: TRUE if the UE is currently attached to ** ** the network; FALSE otherwise. ** ** Others: None ** ** ** ***************************************************************************/ int emm_main_is_attached(void) { LOG_FUNC_IN; LOG_FUNC_RETURN (_emm_data.is_attached); } /**************************************************************************** ** ** ** Name: emm_main_get_plmn_rat() ** ** ** ** Description: Indicates whether the UE is currently attached to the ** ** network for emergency bearer services ** ** ** ** Inputs: None ** ** Others: _emm_data ** ** ** ** Outputs: None ** ** Return: TRUE if the UE is currently attached or is ** ** attempting to attach to the network for ** ** emergency bearer services; FALSE otherwise ** ** Others: None ** ** ** ***************************************************************************/ int emm_main_is_emergency(void) { LOG_FUNC_IN; LOG_FUNC_RETURN (_emm_data.is_attached && _emm_data.is_emergency); } #endif // NAS_UE /****************************************************************************/ /********************* L O C A L F U N C T I O N S *********************/ /****************************************************************************/ #ifdef NAS_UE /**************************************************************************** ** ** ** Name: _emm_main_callback() ** ** ** ** Description: Forwards the network indication to the upper control la- ** ** yer (user API) to notify that network registration and/or ** ** location information has changed. ** ** ** ** Inputs: size: Size in byte of the list of operators ** ** present in the network. The list has to be ** ** displayed to the user application when ** ** size > 0. ** ** Others: _emm_data ** ** ** ** Outputs: None ** ** Return: RETURNok, RETURNerror ** ** Others: None ** ** ** ***************************************************************************/ static int _emm_main_callback(int size) { LOG_FUNC_IN; /* Forward the notification to the user API */ int rc = (*_emm_main_user_callback)(_emm_data.stat, _emm_data.tac, _emm_data.ci, _emm_data.rat, _emm_data.plist.buffer, size); LOG_FUNC_RETURN (rc); } /**************************************************************************** ** ** ** Name: _emm_main_get_imei() ** ** ** ** Description: Returns the International Mobile Equipment Identity con- ** ** tained in the given string representation ** ** ** ** Inputs: imei: The string representation of the IMEI ** ** Others: None ** ** ** ** Outputs: imei: The IMEI of the UE ** ** Return: The number of digits in the IMEI ** ** Others: None ** ** ** ***************************************************************************/ static int _emm_main_get_imei(imei_t *imei, const char *imei_str) { int len = strlen(imei_str); if (len % 2) { imei->u.num.parity = ODD_PARITY; } else { imei->u.num.parity = EVEN_PARITY; } imei->u.num.digit1 = imei_str[0] - '0'; imei->u.num.digit2 = imei_str[1] - '0'; imei->u.num.digit3 = imei_str[2] - '0'; imei->u.num.digit4 = imei_str[3] - '0'; imei->u.num.digit5 = imei_str[4] - '0'; imei->u.num.digit6 = imei_str[5] - '0'; imei->u.num.digit7 = imei_str[6] - '0'; imei->u.num.digit8 = imei_str[7] - '0'; imei->u.num.digit9 = imei_str[8] - '0'; imei->u.num.digit10 = imei_str[9] - '0'; imei->u.num.digit11 = imei_str[10] - '0'; imei->u.num.digit12 = imei_str[11] - '0'; imei->u.num.digit13 = imei_str[12] - '0'; imei->u.num.digit14 = imei_str[13] - '0'; imei->u.num.digit15 = imei_str[14] - '0'; return (len); } /**************************************************************************** ** ** ** Name: _emm_main_imsi_cmp() ** ** ** ** Description: Compares two International Mobile Subscriber Identifiers ** ** ** ** Inputs: imsi1: The first IMSI ** ** imsi2: The second IMSI to compare to ** ** Others: None ** ** ** ** Outputs: None ** ** Return: TRUE if the first IMSI is found to match ** ** the second; FALSE otherwise. ** ** Others: None ** ** ** ***************************************************************************/ static int _emm_main_imsi_cmp(imsi_t *imsi1, imsi_t *imsi2) { int i; if (imsi1->length != imsi2->length) { return FALSE; } for (i = 0; i < imsi1->length; i++) { if (imsi1->u.value[i] != imsi2->u.value[i]) { return FALSE; } } return TRUE; } /**************************************************************************** ** ** ** Name: _emm_main_get_plmn() ** ** ** ** Description: Get the identifier of the PLMN at the given index in the ** ** list of available PLMNs. ** ** ** ** Inputs: plmn: The PLMN to search for ** ** index: The index of the PLMN in the list of PLMNs ** ** format: The requested representation format of the ** ** PLMN identifier ** ** Others: None ** ** ** ** Outputs: size: The size in bytes of the PLMN identifier ** ** coded in the requested format ** ** Return: A pointer to the identifier of the PLMN ** ** Others: None ** ** ** ***************************************************************************/ static const char *_emm_main_get_plmn(const plmn_t *plmn, int index, int format, size_t *size) { if ( PLMN_IS_VALID(*plmn) ) { switch (format) { case NET_FORMAT_LONG: /* Get the long alpha-numeric representation of the PLMN */ return IdleMode_get_plmn_fullname(plmn, index, size); break; case NET_FORMAT_SHORT: /* Get the short alpha-numeric representation of the PLMN */ return IdleMode_get_plmn_shortname(plmn, index, size); break; case NET_FORMAT_NUM: /* Get the numeric representation of the PLMN */ return IdleMode_get_plmn_id(plmn, index, size); break; default: LOG_TRACE(WARNING, "EMM-MAIN - Format is not valid (%d)", format); *size = 0; break; } } return (NULL); } /**************************************************************************** ** ** ** Name: _emm_main_get_plmn_index() ** ** ** ** Description: Get the index of the given PLMN in the ordered list of ** ** available PLMNs ** ** ** ** Inputs: plmn: Identifier of the PLMN ** ** format: The representation format of the PLMN ** ** identifier ** ** Others: None ** ** ** ** Outputs: None ** ** Return: The index of the selected PLMN in the list ** ** of available PLMNs; -1 if the PLMN is not ** ** found ** ** Others: None ** ** ** ***************************************************************************/ static int _emm_main_get_plmn_index(const char *plmn, int format) { int index = -1; switch (format) { case NET_FORMAT_LONG: /* Get the index of the long alpha-numeric PLMN identifier */ index = IdleMode_get_plmn_fullname_index(plmn); break; case NET_FORMAT_SHORT: /* Get the index of the short alpha-numeric PLMN identifier */ index = IdleMode_get_plmn_shortname_index(plmn); break; case NET_FORMAT_NUM: /* Get the index of the numeric PLMN identifier */ index = IdleMode_get_plmn_id_index(plmn); break; default: LOG_TRACE(WARNING, "EMM-MAIN - Format is not valid (%d)", format); break; } return (index); } #endif // NAS_UE