Files

1172 lines
51 KiB
C

/*
* 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 = {0};
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);
}