/* * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The OpenAirInterface Software Alliance licenses this file to You under * the OAI Public License, Version 1.1 (the "License"); you may not use this file * except in compliance with the License. * You may obtain a copy of the License at * * http://www.openairinterface.org/?page_id=698 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *------------------------------------------------------------------------------- * For more information about the OpenAirInterface (OAI) Software Alliance: * contact@openairinterface.org */ /***************************************************************************** Source IdleMode.c Version 0.1 Date 2012/10/18 Product NAS stack Subsystem EPS Mobility Management Author Frederic Maurel Description Defines EMM procedures executed by the Non-Access Stratum when the UE is in idle mode. When a UE is switched on, a Public Land Mobile Network is selected and the UE searches for a suitable cell of this PLMN to camp on. The UE will, if necessary, register its presence in the registration area of the chosen cell and as outcome of a successful Location Registration the selected PLMN becomes the registered PLMN. If the UE loses coverage of the registered PLMN, either a new PLMN is selected automatically (automatic mode), or an indi- cation of which PLMNs are available is given to the user, so that a manual selection can be made (manual mode). If the UE is unable to find a suitable cell to camp on, or the USIM is not inserted, or if the location registration failed under certain conditions, it attempts to camp on a cell irrespective of the PLMN identity, and enters a "limited service" state in which it can only attempt to make emergency calls. *****************************************************************************/ #include "emm_proc.h" #include "nas_log.h" #include "utils.h" #include "emm_sap.h" #include "IdleMode.h" #include "emmData.h" #include <assert.h> #include <stdio.h> // sprintf #include <string.h> // strlen, strncpy /****************************************************************************/ /**************** 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 *******************/ /****************************************************************************/ static int _IdleMode_plmn_str(char *plmn_str, const plmn_t *plmn); static int _IldlMode_get_opnn_id(emm_data_t *emm_data, const plmn_t *plmn); static int _IdleMode_get_suitable_cell(nas_user_t *user, int index); /* Callback executed whenever a network indication is received */ static IdleMode_callback_t _emm_indication_notify; /****************************************************************************/ /****************** E X P O R T E D F U N C T I O N S ******************/ /****************************************************************************/ /**************************************************************************** ** ** ** Name: IdleMode_initialize() ** ** ** ** Description: Initializes EMM internal data used when the UE operates ** ** in idle mode ** ** ** ** Inputs: cb: The function to executed whenever a net- ** ** work indication is received ** ** Others: None ** ** ** ** Outputs: None ** ** Return: None ** ** Others: _emm_plmn_list, _emm_indication_notify ** ** ** ***************************************************************************/ void IdleMode_initialize(nas_user_t *user, IdleMode_callback_t cb) { emm_plmn_list_t *emm_plmn_list = calloc_or_fail(1, sizeof(emm_plmn_list_t)); user->emm_plmn_list = emm_plmn_list; /* Initialize the list of available PLMNs */ emm_plmn_list->n_plmns = 0; emm_plmn_list->index = 0; emm_plmn_list->hplmn = -1; emm_plmn_list->fplmn = -1; emm_plmn_list->splmn = -1; emm_plmn_list->rplmn = -1; /* Initialize the network notification handler */ _emm_indication_notify = *cb; /* Initialize EMM Service Access Point */ emm_sap_initialize(user); } /* *--------------------------------------------------------------------------- * Functions used to get information from the local list of available PLMNs *--------------------------------------------------------------------------- */ /**************************************************************************** ** ** ** Name: IdleMode_get_nb_plmns() ** ** ** ** Description: Get the number of available PLMNs in the ordered list. ** ** ** ** Inputs: None ** ** Others: _emm_plmn_list ** ** ** ** Outputs: None ** ** Return: The number of PLMNs in the ordered list of ** ** available PLMNs ** ** Others: None ** ** ** ***************************************************************************/ int IdleMode_get_nb_plmns(emm_plmn_list_t *emm_plmn_list) { return emm_plmn_list->n_plmns; } /**************************************************************************** ** ** ** Name: IdleMode_get_hplmn_index() ** ** ** ** Description: Get the index of the Home PLMN or the highest priority ** ** Equivalent HPLMN in the ordered list of available PLMNs. ** ** ** ** Inputs: None ** ** Others: _emm_plmn_list ** ** ** ** Outputs: None ** ** Return: The index of the HPLMN or the first EHPLMN ** ** in the list ** ** Others: None ** ** ** ***************************************************************************/ int IdleMode_get_hplmn_index(emm_plmn_list_t *emm_plmn_list) { return emm_plmn_list->hplmn; } /**************************************************************************** ** ** ** Name: IdleMode_get_rplmn_index() ** ** ** ** Description: Get the index of the registered PLMN in the ordered list ** ** of available PLMNs. ** ** ** ** Inputs: None ** ** Others: _emm_plmn_list ** ** ** ** Outputs: None ** ** Return: The index of the registered PLMN in the ** ** list ** ** Others: None ** ** ** ***************************************************************************/ int IdleMode_get_rplmn_index(emm_plmn_list_t *emm_plmn_list) { return emm_plmn_list->rplmn; } /**************************************************************************** ** ** ** Name: IdleMode_get_splmn_index() ** ** ** ** Description: Get the index of the selected PLMN in the ordered list of ** ** available PLMNs. ** ** ** ** Inputs: None ** ** ** ** Outputs: None ** ** Return: The index of the selected PLMN in the list ** ** Others: None ** ** ** ***************************************************************************/ int IdleMode_get_splmn_index(emm_plmn_list_t *emm_plmn_list) { return emm_plmn_list->splmn; } /**************************************************************************** ** ** ** Name: IdleMode_update_plmn_list() ** ** ** ** Description: Updates the string representation of the list of opera- ** ** tors present in the network ** ** ** ** Inputs: i: Index of the first operator to update ** ** ** ** Outputs: None ** ** Return: The size of the list in bytes ** ** ** ***************************************************************************/ int IdleMode_update_plmn_list(emm_plmn_list_t *emm_plmn_list, emm_data_t *emm_data, int i) { int offset = 0; int n = 1; while ( (i < emm_plmn_list->n_plmns) && (offset < EMM_DATA_BUFFER_SIZE) ) { struct plmn_param_t *plmn = &(emm_plmn_list->param[i++]); if (n++ > 1) { offset += snprintf(emm_data->plist.buffer + offset, EMM_DATA_BUFFER_SIZE - offset, ","); } offset += snprintf(emm_data->plist.buffer + offset, EMM_DATA_BUFFER_SIZE - offset, "(%d,%s,%s,%s", plmn->stat, plmn->fullname, plmn->shortname, plmn->num); if (plmn->rat != NET_ACCESS_UNAVAILABLE) { offset += snprintf(emm_data->plist.buffer + offset, EMM_DATA_BUFFER_SIZE - offset, ",%d", plmn->rat); } offset += snprintf(emm_data->plist.buffer + offset, EMM_DATA_BUFFER_SIZE - offset, ")"); } return (offset); } /**************************************************************************** ** ** ** Name: IdleMode_get_plmn_fullname() ** ** ** ** Description: Get the full name of the PLMN at the given index in the ** ** ordered list of available PLMNs. ** ** ** ** Inputs: plmn: The PLMN of which the name is queried ** ** index: The index of the PLMN in the ordered list ** ** of available PLMNs ** ** Others: _emm_plmn_list ** ** ** ** Outputs: size: The length of the PLMN's name ** ** Return: A pointer to the full name of the PLMN ** ** Others: None ** ** ** ***************************************************************************/ const char *IdleMode_get_plmn_fullname(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index, size_t *len) { if (index < emm_plmn_list->n_plmns) { assert( PLMNS_ARE_EQUAL(*plmn, *emm_plmn_list->plmn[index]) ); *len = strlen(emm_plmn_list->param[index].fullname); return emm_plmn_list->param[index].fullname; } return NULL; } /**************************************************************************** ** ** ** Name: IdleMode_get_plmn_shortname() ** ** ** ** Description: Get the short name of the PLMN at the given index in the ** ** ordered list of available PLMNs. ** ** ** ** Inputs: plmn: The PLMN of which the name is queried ** ** index: The index of the PLMN in the ordered list ** ** of available PLMNs ** ** Others: _emm_plmn_list ** ** ** ** Outputs: size: The length of the PLMN's name ** ** Return: A pointer to the short name of the PLMN ** ** Others: None ** ** ** ***************************************************************************/ const char *IdleMode_get_plmn_shortname(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index, size_t *len) { if (index < emm_plmn_list->n_plmns) { assert( PLMNS_ARE_EQUAL(*plmn, *emm_plmn_list->plmn[index]) ); *len = strlen(emm_plmn_list->param[index].shortname); return emm_plmn_list->param[index].shortname; } return NULL; } /**************************************************************************** ** ** ** Name: IdleMode_get_plmn_id() ** ** ** ** Description: Get the numeric identifier of the PLMN at the given index ** ** in the ordered list of available PLMNs. ** ** ** ** Inputs: plmn: The PLMN of which the name is queried ** ** index: The index of the PLMN in the ordered list ** ** of available PLMNs ** ** Others: _emm_plmn_list ** ** ** ** Outputs: size: The length of the PLMN's name ** ** Return: A pointer to the numeric identifier of the ** ** PLMN ** ** Others: None ** ** ** ***************************************************************************/ const char *IdleMode_get_plmn_id(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index, size_t *len) { if (index < emm_plmn_list->n_plmns) { assert( PLMNS_ARE_EQUAL(*plmn, *emm_plmn_list->plmn[index]) ); *len = strlen(emm_plmn_list->param[index].num); return emm_plmn_list->param[index].num; } return NULL; } /**************************************************************************** ** ** ** Name: IdleMode_get_plmn_fullname_index() ** ** ** ** Description: Search the list of available PLMNs for the index of the ** ** PLMN identifier given by the specified fullname ** ** ** ** Inputs: plmn: The full name of the PLMN ** ** Others: _emm_plmn_list ** ** ** ** Outputs: None ** ** Return: The index of the PLMN, if found in the ** ** list of available PLMNs; -1 otherwise. ** ** Others: None ** ** ** ***************************************************************************/ int IdleMode_get_plmn_fullname_index(emm_plmn_list_t *emm_plmn_list, const char *plmn) { int index; /* Get the index of the PLMN identifier with specified full name */ for (index = 0; index < emm_plmn_list->n_plmns; index++) { if ( strncmp(plmn, emm_plmn_list->param[index].fullname, NET_FORMAT_LONG_SIZE) != 0 ) { continue; } return (index); } return (-1); } /**************************************************************************** ** ** ** Name: IdleMode_get_plmn_shortname_index() ** ** ** ** Description: Search the list of available PLMNs for the index of the ** ** PLMN identifier given by the specified shortname ** ** ** ** Inputs: plmn: The short name of the PLMN ** ** Others: _emm_plmn_list ** ** ** ** Outputs: None ** ** Return: The index of the PLMN, if found in the ** ** list of available PLMNs; -1 otherwise. ** ** Others: None ** ** ** ***************************************************************************/ int IdleMode_get_plmn_shortname_index(emm_plmn_list_t *emm_plmn_list, const char *plmn) { int index; /* Get the index of the PLMN identifier with specified short name */ for (index = 0; index < emm_plmn_list->n_plmns; index++) { if ( !strncmp(plmn, emm_plmn_list->param[index].shortname, NET_FORMAT_SHORT_SIZE) ) { continue; } return (index); } return (-1); } /**************************************************************************** ** ** ** Name: IdleMode_get_plmn_id_index() ** ** ** ** Description: Search the list of available PLMNs for the index of the ** ** PLMN identifier given by the specified numeric identifier ** ** ** ** Inputs: plmn: The numeric identifier of the PLMN ** ** Others: _emm_plmn_list ** ** ** ** Outputs: None ** ** Return: The index of the PLMN, if found in the ** ** list of available PLMNs; -1 otherwise. ** ** Others: None ** ** ** ***************************************************************************/ int IdleMode_get_plmn_id_index(emm_plmn_list_t *emm_plmn_list, const char *plmn) { int index; /* Get the index of the PLMN identifier with specified numeric identifier */ for (index = 0; index < emm_plmn_list->n_plmns; index++) { if ( !strncmp(plmn, emm_plmn_list->param[index].num, NET_FORMAT_LONG_SIZE) ) { continue; } return (index); } return (-1); } /* *--------------------------------------------------------------------------- * Idle mode EMM procedures *--------------------------------------------------------------------------- */ /**************************************************************************** ** ** ** Name: emm_proc_initialize() ** ** ** ** Description: Initialize the ordered list of available PLMNs candidate ** ** to PLMN selection procedure. ** ** ** ** Inputs: None ** ** Others: user->emm_data-> ** ** ** ** Outputs: None ** ** Return: RETURNok, RETURNerror ** ** Others: _emm_plmn_list ** ** ** ***************************************************************************/ int emm_proc_initialize(nas_user_t *user) { LOG_FUNC_IN; emm_sap_t emm_sap; int rc; int i; emm_plmn_list_t *emm_plmn_list = user->emm_plmn_list; if (!user->emm_data->usim_is_valid) { /* The USIM application is not present or not valid */ LOG_TRACE(WARNING, "EMM-IDLE - USIM is not valid"); emm_sap.primitive = EMMREG_NO_IMSI; } else { /* The highest priority is given to either the "equivalent PLMNs" * if available, or the last registered PLMN */ if (user->emm_data->nvdata.eplmn.n_plmns > 0) { for (i=0; i < user->emm_data->nvdata.eplmn.n_plmns; i++) { emm_plmn_list->plmn[emm_plmn_list->n_plmns++] = &user->emm_data->nvdata.eplmn.plmn[i]; } } else if ( PLMN_IS_VALID(user->emm_data->nvdata.rplmn) ) { emm_plmn_list->plmn[emm_plmn_list->n_plmns++] = &user->emm_data->nvdata.rplmn; } /* Update the index of the HPLMN or EHPLM of highest priority. * When switched on, the UE will try to automatically register * to each previous PLMN within the ordered list of available * PLMNs regardless of the network selection mode of operation */ emm_plmn_list->hplmn = emm_plmn_list->n_plmns - 1; // LGemm_plmn_list->hplmn = emm_plmn_list->n_plmns; /* Add the highest priority PLMN in the list of "equivalent HPLMNs" if present and not empty, or the HPLMN derived from the IMSI */ if (user->emm_data->ehplmn.n_plmns > 0) { emm_plmn_list->plmn[emm_plmn_list->n_plmns++] = &user->emm_data->ehplmn.plmn[0]; } else { emm_plmn_list->plmn[emm_plmn_list->n_plmns++] = &user->emm_data->hplmn; } /* Each PLMN/access technology combination in the "User * Controlled PLMN Selector with Access Technology" */ for (i=0; i < user->emm_data->plmn.n_plmns; i++) { emm_plmn_list->plmn[emm_plmn_list->n_plmns++] = &user->emm_data->plmn.plmn[i]; } /* Each PLMN/access technology combination in the "Operator * Controlled PLMN Selector with Access Technology" */ for (i=0; i < user->emm_data->oplmn.n_plmns; i++) { emm_plmn_list->plmn[emm_plmn_list->n_plmns++] = &user->emm_data->oplmn.plmn[i]; } /* Other PLMN/access technology combinations with received * high quality signal in random order */ /* Other PLMN/access technology combinations in order of * decreasing signal quality */ /* TODO: Schedule periodic network selection attemps (hpplmn timer) */ /* Initialize the PLMNs' parameters */ for (i=0; i < emm_plmn_list->n_plmns; i++) { struct plmn_param_t *plmn = &(emm_plmn_list->param[i]); int id = _IldlMode_get_opnn_id(user->emm_data, emm_plmn_list->plmn[i]); if (id < 0) { plmn->fullname[0] = '\0'; plmn->shortname[0] = '\0'; } else { strncpy(plmn->fullname, user->emm_data->opnn[id].fullname, NET_FORMAT_LONG_SIZE); strncpy(plmn->shortname, user->emm_data->opnn[id].shortname, NET_FORMAT_SHORT_SIZE); } (void)_IdleMode_plmn_str(plmn->num, emm_plmn_list->plmn[i]); plmn->stat = NET_OPER_UNKNOWN; plmn->tac = 0; plmn->ci = 0; plmn->rat = NET_ACCESS_UNAVAILABLE; } LOG_TRACE(INFO, "EMM-IDLE - %d PLMNs available for network selection", emm_plmn_list->n_plmns); /* Notify EMM that PLMN selection procedure has to be executed */ emm_sap.primitive = EMMREG_REGISTER_REQ; emm_sap.u.emm_reg.u.regist.index = 0; } rc = emm_sap_send(user, &emm_sap); LOG_FUNC_RETURN(rc); /* TODO: Update the list of PLMNs upon receiving AS system information */ } /**************************************************************************** ** ** ** Name: emm_proc_plmn_selection() ** ** ** ** Description: Performs the network selection procedure when the UE is ** ** swicthed on. ** ** ** ** The MS shall select the registered PLMN or equivalent ** ** PLMN (if it is available) using all access technologies ** ** that the MS is capable. ** ** If there is no registered PLMN, or if registration is ** ** not possible due to the PLMN being unavailable or regis- ** ** tration failure, the MS performs the automatic or the ma- ** ** nual procedure depending on its PLMN selection operating ** ** mode. ** ** ** ** Inputs: None ** ** Others: _emm_plmn_list, user->emm_data-> ** ** ** ** Outputs: None ** ** Return: None ** ** Others: emm_plmn_list->index ** ** ** ***************************************************************************/ int emm_proc_plmn_selection(nas_user_t *user, int index) { LOG_FUNC_IN; emm_data_t *emm_data = user->emm_data; user_api_id_t *user_api_id = user->user_api_id; emm_plmn_list_t *emm_plmn_list = user->emm_plmn_list; int rc = RETURNok; if (emm_data->plmn_mode != EMM_DATA_PLMN_AUTO) { /* * Manual or manual/automatic mode of operation * -------------------------------------------- */ if (index >= emm_plmn_list->hplmn) { /* * Selection of the last registered or equivalent PLMNs failed */ if (emm_data->plmn_index < 0) { /* * The user did not select any PLMN yet; display the ordered * list of available PLMNs to the user */ index = -1; rc = emm_proc_network_notify(emm_plmn_list, user_api_id, emm_data, emm_plmn_list->hplmn); if (rc != RETURNok) { LOG_TRACE(WARNING, "EMM-IDLE - Failed to notify " "network list update"); } } else { /* * Try to register to the PLMN manually selected by the user */ index = emm_data->plmn_index; } } } if ( !(index < 0) ) { /* * Search for a suitable cell of the currently selected PLMN: * It can be the last registered or one of the equivalent PLMNs * if available, or the PLMN selected by the user in manual mode, * or any other PLMN in the ordered list of available PLMNs in * automatic mode. */ emm_plmn_list->index = index; rc = _IdleMode_get_suitable_cell(user, index); } LOG_FUNC_RETURN (rc); } /**************************************************************************** ** ** ** Name: emm_proc_plmn_end() ** ** ** ** Description: Completes the network selection procedure or triggers a ** ** new one until a suitable or acceptable cell of the chosen ** ** PLMN is found. ** ** ** ** If a suitable cell of the selected PLMN has been found, ** ** that cell is chosen to camp on and provide available ser- ** ** vices, and tunes to its control channel. ** ** The MS will then register its presence in the registra- ** ** tion area of the chosen cell, if it is capable of servi- ** ** ces which require registration. As outcome of a success- ** ** ful Location Registration the selected PLMN becomes the ** ** registered PLMN. ** ** ** ** When no suitable cell can be found, the MS is unable to ** ** obtain normal service from a PLMN. Then the MS attempts ** ** to camp on an acceptable cell, irrespective of its PLMN ** ** identity, so that only emergency calls can be made. ** ** ** ** Inputs: found: true if a suitable cell of the chosen ** ** PLMN has been found; false otherwise. ** ** tac: The code of the location/tracking area the ** ** chosen PLMN belongs to ** ** ci: The identifier of the cell ** ** rat: The radio access technology supported by ** ** the cell ** ** ** ** Outputs: None ** ** Return: None ** ** ** ***************************************************************************/ int emm_proc_plmn_selection_end(nas_user_t *user, int found, tac_t tac, ci_t ci, AcT_t rat) { LOG_FUNC_IN; emm_sap_t emm_sap; int rc = RETURNerror; emm_data_t *emm_data = user->emm_data; emm_plmn_list_t *emm_plmn_list = user->emm_plmn_list; user_api_id_t *user_api_id = user->user_api_id; int index = emm_plmn_list->index; bool select_next_plmn = false; LOG_TRACE(INFO, "EMM-IDLE - %s cell found for PLMN %d in %s mode", (found)? "One" : "No", index, (emm_data->plmn_mode == EMM_DATA_PLMN_AUTO)? "Automatic" : (emm_data->plmn_mode == EMM_DATA_PLMN_MANUAL)? "Manual" : "Automatic/manual"); if (found) { bool is_forbidden = false; /* Select the PLMN of which a suitable cell has been found */ emm_data->splmn = *emm_plmn_list->plmn[index]; /* Update the selected PLMN's parameters */ emm_plmn_list->param[index].tac = tac; emm_plmn_list->param[index].ci = ci; emm_plmn_list->param[index].rat = rat; /* Update the location data and notify EMM that data have changed */ rc = emm_proc_location_notify(user_api_id, emm_data, tac, ci , rat); if (rc != RETURNok) { LOG_TRACE(WARNING, "EMM-IDLE - Failed to notify location update"); } if (emm_data->plmn_mode == EMM_DATA_PLMN_AUTO) { /* * Automatic mode of operation * --------------------------- */ int i; /* Check if the selected PLMN is in the forbidden list */ for (i = 0; i < emm_data->fplmn.n_plmns; i++) { if (PLMNS_ARE_EQUAL(emm_data->splmn, emm_data->fplmn.plmn[i])) { is_forbidden = true; break; } } if (!is_forbidden) { for (i = 0; i < emm_data->fplmn_gprs.n_plmns; i++) { if (PLMNS_ARE_EQUAL(emm_data->splmn, emm_data->fplmn_gprs.plmn[i])) { is_forbidden = true; break; } } } /* Check if the selected PLMN belongs to a forbidden * tracking area */ tai_t tai; tai.plmn = emm_data->splmn; tai.tac = tac; if (!is_forbidden) { for (i = 0; i < emm_data->ftai.n_tais; i++) { if (TAIS_ARE_EQUAL(tai, emm_data->ftai.tai[i])) { is_forbidden = true; break; } } } if (!is_forbidden) { for (i = 0; i < emm_data->ftai_roaming.n_tais; i++) { if (TAIS_ARE_EQUAL(tai, emm_data->ftai_roaming.tai[i])) { is_forbidden = true; break; } } } } if (is_forbidden) { /* The selected cell is known not to be able to provide normal * service */ LOG_TRACE(INFO, "EMM-IDLE - UE may camp on this acceptable cell for limited services"); /* Save the index of the first forbidden PLMN */ if (emm_plmn_list->fplmn < 0) { emm_plmn_list->fplmn = index; } emm_plmn_list->param[index].stat = NET_OPER_FORBIDDEN; } else { /* A suitable cell has been found and the PLMN or tracking area * is not in the forbidden list */ LOG_TRACE(INFO, "EMM-IDLE - UE may camp on this suitable cell for normal services"); emm_plmn_list->fplmn = -1; emm_plmn_list->param[index].stat = NET_OPER_CURRENT; emm_sap.primitive = EMMREG_REGISTER_CNF; } /* Duplicate the new selected PLMN at the end of the ordered list */ emm_plmn_list->plmn[emm_plmn_list->n_plmns] = &emm_data->splmn; } else if (emm_data->plmn_mode == EMM_DATA_PLMN_AUTO) { /* * Automatic mode of operation * --------------------------- * No suitable cell of the chosen PLMN has been found; * Try to select the next PLMN in the ordered list of available PLMNs */ index += 1; select_next_plmn = true; /* Bypass the previously selected PLMN */ if (index == emm_plmn_list->splmn) { index += 1; } } else if (emm_data->plmn_index < 0) { /* * Manual or manual/automatic mode of operation * -------------------------------------------- * Attempt to automatically find a suitable cell of the last * registered or equivalent PLMNs is ongoing */ index += 1; select_next_plmn = true; } else if (emm_data->plmn_mode == EMM_DATA_PLMN_MANUAL) { /* * Manual mode of operation * ------------------------ * No suitable cell of the PLMN selected by the user has been found */ emm_sap.primitive = EMMREG_NO_CELL; } else { /* * Manual/automatic mode of operation * -------------------------------------------- * Attempt to find a suitable cell of the PLMN selected by the user * failed; Try to automatically select another PLMN */ emm_data->plmn_mode = EMM_DATA_PLMN_AUTO; index = emm_plmn_list->hplmn; select_next_plmn = true; } /* * Force an attempt to register to the next PLMN */ if (select_next_plmn) { int last_plmn_index = emm_plmn_list->n_plmns; if (emm_plmn_list->splmn != -1) { /* The last attempt was to register the previously selected PLMN */ last_plmn_index += 1; } if (index < last_plmn_index) { /* Try to select the next PLMN in the list of available PLMNs */ emm_plmn_list->index = index; rc = emm_proc_plmn_selection(user, index); } else { /* No suitable cell of any PLMN within the ordered list * of available PLMNs has been found */ select_next_plmn = false; emm_sap.primitive = EMMREG_NO_CELL; } } /* * Or terminate the PLMN selection procedure */ if (!select_next_plmn) { if (emm_plmn_list->fplmn >= 0) { /* There were one or more PLMNs which were available and allowable, * but an LR failure made registration on those PLMNs unsuccessful * or an entry in any of the forbidden area lists prevented a * registration attempt; select the first such PLMN and enters a * limited service state. */ index = emm_plmn_list->fplmn; emm_plmn_list->fplmn = -1; emm_sap.primitive = EMMREG_REGISTER_REJ; } /* Update the availability indicator of the previously selected PLMN */ if (emm_plmn_list->splmn != -1) { emm_plmn_list->param[emm_plmn_list->splmn].stat = NET_OPER_UNKNOWN; } /* Update the index of the new selected PLMN */ if (emm_sap.primitive != EMMREG_NO_CELL) { emm_plmn_list->splmn = index; } else { emm_plmn_list->splmn = -1; } /* * Notify EMM that PLMN selection procedure has completed */ rc = emm_sap_send(user, &emm_sap); if (emm_plmn_list->splmn != -1) { if (emm_plmn_list->splmn == emm_plmn_list->rplmn) { /* The selected PLMN is the registered PLMN */ LOG_TRACE(INFO, "EMM-IDLE - The selected PLMN is the registered PLMN"); emm_data->is_rplmn = true; } else if (emm_plmn_list->splmn < emm_plmn_list->hplmn) { /* The selected PLMN is in the list of equivalent PLMNs */ LOG_TRACE(INFO, "EMM-IDLE - The selected PLMN is in the list of equivalent PLMNs"); emm_data->is_eplmn = true; } /* * Notify EMM that an attach procedure has to be initiated * to register the presence of the UE to the selected PLMN */ emm_sap.primitive = EMMREG_ATTACH_INIT; rc = emm_sap_send(user, &emm_sap); } } LOG_FUNC_RETURN (rc); } /* *--------------------------------------------------------------------------- * Network indication handlers *--------------------------------------------------------------------------- */ /**************************************************************************** ** ** ** Name: emm_proc_registration_notify() ** ** ** ** Description: Updates the current network registration status and noti- ** ** fy the EMM main controller that network registration sta- ** ** tus has changed. ** ** ** ** Inputs: status: The new network registraton status ** ** Others: None ** ** ** ** Outputs: None ** ** Return: RETURNok, RETURNerror ** ** Others: user->emm_data-> ** ** ** ***************************************************************************/ int emm_proc_registration_notify(user_api_id_t *user_api_id, emm_data_t *emm_data, Stat_t status) { LOG_FUNC_IN; int rc = RETURNok; /* Update the network registration status */ if (emm_data->stat != status) { emm_data->stat = status; /* Notify EMM that data has changed */ rc = (*_emm_indication_notify)(user_api_id, emm_data, 1); } LOG_FUNC_RETURN (rc); } /**************************************************************************** ** ** ** Name: emm_proc_location_notify() ** ** ** ** Description: Updates the current location information and notify the ** ** EMM main controller that location area has changed. ** ** ** ** Inputs: tac: The code of the new location/tracking area ** ** ci: The identifier of the new serving cell ** ** rat: The Radio Access Technology supported by ** ** the new serving cell ** ** Others: None ** ** ** ** Outputs: None ** ** Return: RETURNok, RETURNerror ** ** Others: user->emm_data-> ** ** ** ***************************************************************************/ int emm_proc_location_notify(user_api_id_t *user_api_id, emm_data_t *emm_data, tac_t tac, ci_t ci, AcT_t rat) { LOG_FUNC_IN; int rc = RETURNok; /* Update the location information */ if ( (emm_data->tac != tac) || (emm_data->ci != ci) || (emm_data->rat != rat) ) { emm_data->tac = tac; emm_data->ci = ci; emm_data->rat = rat; /* Notify EMM that data has changed */ rc = (*_emm_indication_notify)(user_api_id, emm_data, 0); } LOG_FUNC_RETURN (rc); } /**************************************************************************** ** ** ** Name: emm_proc_network_notify() ** ** ** ** Description: Updates the string representation of the list of opera- ** ** tors present in the network and notify the EMM main con- ** ** troller that the list has to be displayed to the user ** ** application. ** ** ** ** Inputs: index: Index of the first operator to update ** ** Others: None ** ** ** ** Outputs: None ** ** Return: RETURNok, RETURNerror ** ** Others: user->emm_data-> ** ** ** ***************************************************************************/ int emm_proc_network_notify(emm_plmn_list_t *emm_plmn_list, user_api_id_t *user_api_id, emm_data_t *emm_data, int index) { LOG_FUNC_IN; /* Update the list of operators present in the network */ int size = IdleMode_update_plmn_list(emm_plmn_list, emm_data, index); /* Notify EMM that data has changed */ int rc = (*_emm_indication_notify)(user_api_id, emm_data, size); LOG_FUNC_RETURN (rc); } /****************************************************************************/ /********************* L O C A L F U N C T I O N S *********************/ /****************************************************************************/ /**************************************************************************** ** ** ** Name: _IdleMain_plmn_str() ** ** ** ** Description: Converts a PLMN identifier to a string representation in ** ** the numeric format ** ** ** ** Inputs: plmn: The PLMN identifier to convert ** ** Others: None ** ** ** ** Outputs: plmn_str: The PLMN identifier in numeric format ** ** Return: The size in bytes of the string represen- ** ** tation of the PLMN identifier ** ** Others: None ** ** ** ***************************************************************************/ static int _IdleMode_plmn_str(char *plmn_str, const plmn_t *plmn) { char *p = plmn_str; if (plmn == NULL) { return 0; } if (plmn->MCCdigit1 != 0x0F) { sprintf(p++, "%u", plmn->MCCdigit1); } if (plmn->MCCdigit2 != 0x0F) { sprintf(p++, "%u", plmn->MCCdigit2); } if (plmn->MCCdigit3 != 0x0F) { sprintf(p++, "%u", plmn->MCCdigit3); } if (plmn->MNCdigit1 != 0x0F) { sprintf(p++, "%u", plmn->MNCdigit1); } if (plmn->MNCdigit2 != 0x0F) { sprintf(p++, "%u", plmn->MNCdigit2); } if (plmn->MNCdigit3 != 0x0F) { sprintf(p++, "%u", plmn->MNCdigit3); } return (p - plmn_str); } /**************************************************************************** ** ** ** Name: _IldlMode_get_opnn_id() ** ** ** ** Description: Get the index of the specified PLMN in the list of opera- ** ** tor network name records ** ** ** ** Inputs: plmn: The PLMN identifier ** ** ** ** Outputs: None ** ** Return: The index of the PLMN if found in the list ** ** of operator network name records; ** ** -1 otherwise; ** ** Others: None ** ** ** ***************************************************************************/ static int _IldlMode_get_opnn_id(emm_data_t *emm_data, const plmn_t *plmn) { int i; for (i = 0; i < emm_data->n_opnns; i++) { if (plmn->MCCdigit1 != emm_data->opnn[i].plmn->MCCdigit1) { continue; } if (plmn->MCCdigit2 != emm_data->opnn[i].plmn->MCCdigit2) { continue; } if (plmn->MCCdigit3 != emm_data->opnn[i].plmn->MCCdigit3) { continue; } if (plmn->MNCdigit1 != emm_data->opnn[i].plmn->MNCdigit1) { continue; } if (plmn->MNCdigit2 != emm_data->opnn[i].plmn->MNCdigit2) { continue; } if (plmn->MNCdigit3 != emm_data->opnn[i].plmn->MNCdigit3) { continue; } /* Found */ return (i); } /* Not found */ return (-1); } /**************************************************************************** ** ** ** Name: _IdleMode_get_suitable_cell() ** ** ** ** Description: Query the Access Stratum to search for a suitable cell ** ** that belongs to the selected PLMN. ** ** ** ** Inputs: index: Index of the selected PLMN in the ordered ** ** list of available PLMNs ** ** ** ** Outputs: None ** ** Return: RETURNok, RETURNerror ** ** Others: None ** ** ** ***************************************************************************/ static int _IdleMode_get_suitable_cell(nas_user_t *user, int index) { emm_sap_t emm_sap; emm_data_t *emm_data = user->emm_data; emm_plmn_list_t *emm_plmn_list = user->emm_plmn_list; const plmn_t *plmn = emm_plmn_list->plmn[index]; LOG_TRACE(INFO, "EMM-IDLE - Trying to search a suitable cell " "of PLMN %d in %s mode", index, (emm_data->plmn_mode == EMM_DATA_PLMN_AUTO)? "Automatic" : (emm_data->plmn_mode == EMM_DATA_PLMN_MANUAL)? "Manual" : "Automatic/manual"); /* * Notify EMM-AS SAP that cell information related to the given * PLMN are requested from the Access-Stratum */ emm_sap.primitive = EMMAS_CELL_INFO_REQ; emm_sap.u.emm_as.u.cell_info.plmnIDs.n_plmns = 1; emm_sap.u.emm_as.u.cell_info.plmnIDs.plmn[0] = *plmn; if (emm_data->plmn_rat != NET_ACCESS_UNAVAILABLE) { emm_sap.u.emm_as.u.cell_info.rat = (1 << emm_data->plmn_rat); } else { emm_sap.u.emm_as.u.cell_info.rat = NET_ACCESS_UNAVAILABLE; } return emm_sap_send(user, &emm_sap); }