前沿
之前的文章介绍PLMN是如何被搜索到的,在搜索到PLMN全部搜索之后,UE会选择其中的一个PLMN进行驻留。这个过程相对前面的内容来讲比较简单,本文来分析下srsLTE的代码是如何选择PLMN的。
RRC层的PLMN搜索过程
RRC层会进行全频段的PLMN扫描,收集周围的全部PLMN,并进行存储,并在全频段扫描之后,通知NAS层PLMN搜索过程结果,NAS层会进行PLMN 选择,选择完之后会发起RRC 信令连接的过程。
rrc::plmn_search_proc::plmn_search_proc(rrc* parent_) : rrc_ptr(parent_), log_h(srslte::logmap::get("RRC")) {}
proc_outcome_t rrc::plmn_search_proc::init()
{
Info("Starting PLMN search\n");
nof_plmns = 0;
cell_search_fut = rrc_ptr->cell_searcher.get_future();
if (not rrc_ptr->cell_searcher.launch(&cell_search_fut)) {
Error("Failed due to fail to init cell search...\n");
return proc_outcome_t::error;
}
return step();
}
/* NAS interface to search for available PLMNs.
* It goes through all known frequencies, synchronizes and receives SIB1 for each to extract PLMN.
* The function is blocking and waits until all frequencies have been
* searched and PLMNs are obtained.
*/
proc_outcome_t rrc::plmn_search_proc::step()
{
if (rrc_ptr->cell_searcher.run()) {
// wait for new TTI
return proc_outcome_t::yield;
}
if (cell_search_fut.is_error() or cell_search_fut.value()->found == phy_interface_rrc_lte::cell_search_ret_t::ERROR) {
// stop search
nof_plmns = -1;
Error("Failed due to failed cell search sub-procedure\n");
return proc_outcome_t::error;
}
//解到SIB1之后,保存PLMN和TAC
if (cell_search_fut.value()->found == phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND) {
if (rrc_ptr->serving_cell->has_sib1()) {
// Save PLMN and TAC to NAS
for (uint32_t i = 0; i < rrc_ptr->serving_cell->nof_plmns(); i++) {
if (nof_plmns < MAX_FOUND_PLMNS) {
found_plmns[nof_plmns].plmn_id = rrc_ptr->serving_cell->get_plmn(i);
found_plmns[nof_plmns].tac = rrc_ptr->serving_cell->get_tac();
nof_plmns++;
} else {
Error("No more space for plmns (%d)\n", nof_plmns);
}
}
} else {
Error("SIB1 not acquired\n");
}
}
if (cell_search_fut.value()->last_freq == phy_interface_rrc_lte::cell_search_ret_t::NO_MORE_FREQS) {
Info("completed PLMN search\n");
return proc_outcome_t::success;
}
if (not rrc_ptr->cell_searcher.launch(&cell_search_fut)) {
Error("Failed due to fail to init cell search...\n");
return proc_outcome_t::error;
}
// run again
return step();
}
void rrc::plmn_search_proc::then(const srslte::proc_state_t& result) const
{
// on cleanup, call plmn_search_completed
if (result.is_success()) {
Info("completed with success\n");
//RRC层的PLMN search过程结束之后将结果通知给NAS层
rrc_ptr->nas->plmn_search_completed(found_plmns, nof_plmns);
} else {
Error("PLMN Search completed with an error\n");
rrc_ptr->nas->plmn_search_completed(nullptr, -1);
}
}
NAS层的PLMN选择
NAS层的plmn_search_completed函数接口,会trigger NAS层的PLMN搜索过程的react函数。
void nas::plmn_search_completed(const rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS],
int nof_plmns)
{
//trigger plmn_search_proc的react函数 plmn_searcher.trigger(plmn_search_proc::plmn_search_complete_t(found_plmns, nof_plmns));
}
react函数的代码流程
关键步骤如下:
1、NAS层保存PLMN搜索结果;
2、执行PLMN选择;
3、发起建立RRC连接的过程。
proc_outcome_t nas::plmn_search_proc::react(const plmn_search_complete_t& t)
{
if (state != state_t::plmn_search) {
ProcWarning("PLMN Search Complete was received but PLMN Search is not running.\n");
return proc_outcome_t::yield; // ignore
}
// check whether the state hasn't changed
if (nas_ptr->state != EMM_STATE_DEREGISTERED or nas_ptr->plmn_is_selected) {
ProcError("ProcError while searching for PLMNs\n");
return proc_outcome_t::error;
}
if (t.nof_plmns < 0) {
ProcError("Error while searching for PLMNs\n");
return proc_outcome_t::error;
}
if (t.nof_plmns == 0) {
ProcWarning("Did not find any PLMN in the set of frequencies.\n");
return proc_outcome_t::error;
}
// Save PLMNs
nas_ptr->known_plmns.clear();
for (int i = 0; i < t.nof_plmns; i++) {
nas_ptr->known_plmns.push_back(t.found_plmns[i].plmn_id);
nas_ptr->nas_log->info(
"Found PLMN: Id=%s, TAC=%d\n", t.found_plmns[i].plmn_id.to_string().c_str(), t.found_plmns[i].tac);
nas_ptr->nas_log->console(
"Found PLMN: Id=%s, TAC=%d\n", t.found_plmns[i].plmn_id.to_string().c_str(), t.found_plmns[i].tac);
}
nas_ptr->select_plmn();
// Select PLMN in request establishment of RRC connection
if (not nas_ptr->plmn_is_selected) {
ProcError("PLMN is not selected because no suitable PLMN was found\n");
return proc_outcome_t::error;
}
nas_ptr->rrc->plmn_select(nas_ptr->current_plmn);
state = state_t::rrc_connect;
if (not nas_ptr->rrc_connector.launch(srslte::establishment_cause_t::mo_sig, nullptr)) {
ProcError("Unable to initiate RRC connection.\n");
return proc_outcome_t::error;
}
nas_ptr->callbacks.add_proc(nas_ptr->rrc_connector);
return proc_outcome_t::yield;
}
PLMN选择
PLMN选择会将搜索的结果和本地的PLMN(home_plmn)进行比较,如果找到,返回成功。如果没有,就选择第一个PLMN。
这里的home_plmn是在SIM卡存储的信息,开机时,NAS会从通过接口从SIM卡中获取信息,此外还有s-tmsi等信息。这里多介绍下SIM卡,SIM除了有存储的功能,还有处理加密等计算的功能。
void nas::select_plmn()
{
plmn_is_selected = false;
// First find if Home PLMN is available
for (const srslte::plmn_id_t& known_plmn : known_plmns) {
if (known_plmn == home_plmn) {
nas_log->info("Selecting Home PLMN Id=%s\n", known_plmn.to_string().c_str());
plmn_is_selected = true;
current_plmn = known_plmn;
return;
}
}
// If not, select the first available PLMN
if (not known_plmns.empty()) {
nas_log->info("Could not find Home PLMN Id=%s, trying to connect to PLMN Id=%s\n",
home_plmn.to_string().c_str(),
known_plmns[0].to_string().c_str());
nas_log->console("Could not find Home PLMN Id=%s, trying to connect to PLMN Id=%s\n",
home_plmn.to_string().c_str(),
known_plmns[0].to_string().c_str());
plmn_is_selected = true;
current_plmn = known_plmns[0];
}
// reset attach attempt counter (Sec. 5.2.2.3.4)
if (plmn_is_selected) {
attach_attempt_counter = 0;
}
}
总结
至此,现在才获取PLMN,但是UE的入网流程还有很远,虽然从代码上来分析,从NAS->RRC->MAC层,以这样的方式来看比较直观,但是,从空口报文的分析来看,MAC层的随机接入是在前面的,后续的过程,我们将以空口的报文顺序来分析代码流程。
如对本系列文章感兴趣,欢迎关注、收藏,有新的文档,平台会第一时间推送。