2 * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include <osmocore/msgb.h>
29 #include <osmocore/talloc.h>
30 #include <osmocore/utils.h>
31 #include <osmocore/gsm48.h>
32 #include <osmocore/signal.h>
34 #include <osmocom/bb/common/logging.h>
35 #include <osmocom/bb/common/l1ctl.h>
36 #include <osmocom/bb/common/l23_app.h>
37 #include <osmocom/bb/common/osmocom_data.h>
38 #include <osmocom/bb/common/networks.h>
39 #include <osmocom/bb/mobile/vty.h>
43 static void gsm322_cs_timeout(void *arg);
44 static void gsm322_cs_loss(void *arg);
45 static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed);
46 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg);
55 /* Cell selection process
57 * The process depends on states and events (finites state machine).
59 * During states of cell selection or cell re-selection, the search for a cell
60 * is performed in two steps:
62 * 1. Measurement of received level of all relevant frequencies (rx-lev)
64 * 2. Receive system information messages of all relevant frequencies
66 * During this process, the results are stored in a list of all frequencies.
67 * This list is checked whenever a cell is selected. It depends on the results
68 * if the cell is 'suitable' and 'allowable' to 'camp' on.
70 * This list is also used to generate a list of available networks.
74 * - cs->list[0..1023].xxx for each cell, where
75 * - flags and rxlev are used to store outcome of cell scanning process
76 * - sysinfo pointing to sysinfo memory, allocated temporarily
77 * - cs->selected and cs->sel_* states of the current / last selected cell.
80 * There is a special state: GSM322_PLMN_SEARCH
81 * It is used to search for all cells, to find the HPLMN. This is triggered
82 * by a timer. Also it is used before selecting PLMN from list.
86 /* PLMN selection process
88 * The PLMN (Public Land Mobile Network = Operator's Network) has two different
95 * The process depends on states and events (finites state machine).
99 /* File format of BA list:
104 * where frequency 0 is bit 0 of first byte
106 * If not end-of-file, the next BA list is stored.
111 * * subscr->plmn_list
113 * The "PLMN Selector list" stores prefered networks to select during PLMN
114 * search process. This list is also stored in the SIM.
118 * The "forbidden PLMNs" list stores all networks that rejected us. The stored
119 * network will not be used when searching PLMN automatically. This list is
120 * also stored din the SIM.
122 * * plmn->forbidden_la
124 * The "forbidden LAs for roaming" list stores all location areas where roaming
129 * This list stores measurements and cell informations during cell selection
130 * process. It can be used to speed up repeated cell selection.
134 * This list stores a map of frequencies used for a PLMN. If this lists exists
135 * for a PLMN, it helps to speedup cell scan process.
137 * * plmn->sorted_plmn
139 * This list is generated whenever a PLMN search is started and a list of PLMNs
140 * is required. It consists of home PLMN, PLMN Selector list, and PLMNs found
141 * during scan process.
148 static const struct value_string gsm322_event_names[] = {
149 { GSM322_EVENT_SWITCH_ON, "EVENT_SWITCH_ON" },
150 { GSM322_EVENT_SWITCH_OFF, "EVENT_SWITCH_OFF" },
151 { GSM322_EVENT_SIM_INSERT, "EVENT_SIM_INSERT" },
152 { GSM322_EVENT_SIM_REMOVE, "EVENT_SIM_REMOVE" },
153 { GSM322_EVENT_REG_FAILED, "EVENT_REG_FAILED" },
154 { GSM322_EVENT_ROAMING_NA, "EVENT_ROAMING_NA" },
155 { GSM322_EVENT_INVALID_SIM, "EVENT_INVALID_SIM" },
156 { GSM322_EVENT_REG_SUCCESS, "EVENT_REG_SUCCESS" },
157 { GSM322_EVENT_NEW_PLMN, "EVENT_NEW_PLMN" },
158 { GSM322_EVENT_ON_PLMN, "EVENT_ON_PLMN" },
159 { GSM322_EVENT_PLMN_SEARCH_START,"EVENT_PLMN_SEARCH_START" },
160 { GSM322_EVENT_PLMN_SEARCH_END, "EVENT_PLMN_SEARCH_END" },
161 { GSM322_EVENT_USER_RESEL, "EVENT_USER_RESEL" },
162 { GSM322_EVENT_PLMN_AVAIL, "EVENT_PLMN_AVAIL" },
163 { GSM322_EVENT_CHOOSE_PLMN, "EVENT_CHOOSE_PLMN" },
164 { GSM322_EVENT_SEL_MANUAL, "EVENT_SEL_MANUAL" },
165 { GSM322_EVENT_SEL_AUTO, "EVENT_SEL_AUTO" },
166 { GSM322_EVENT_CELL_FOUND, "EVENT_CELL_FOUND" },
167 { GSM322_EVENT_NO_CELL_FOUND, "EVENT_NO_CELL_FOUND" },
168 { GSM322_EVENT_LEAVE_IDLE, "EVENT_LEAVE_IDLE" },
169 { GSM322_EVENT_RET_IDLE, "EVENT_RET_IDLE" },
170 { GSM322_EVENT_CELL_RESEL, "EVENT_CELL_RESEL" },
171 { GSM322_EVENT_SYSINFO, "EVENT_SYSINFO" },
172 { GSM322_EVENT_HPLMN_SEARCH, "EVENT_HPLMN_SEARCH" },
176 const char *get_event_name(int value)
178 return get_value_string(gsm322_event_names, value);
182 /* allocate a 03.22 event message */
183 struct msgb *gsm322_msgb_alloc(int msg_type)
186 struct gsm322_msg *gm;
188 msg = msgb_alloc_headroom(sizeof(*gm), 0, "GSM 03.22 event");
192 gm = (struct gsm322_msg *)msgb_put(msg, sizeof(*gm));
193 gm->msg_type = msg_type;
198 /* queue PLMN selection message */
199 int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
201 struct gsm322_plmn *plmn = &ms->plmn;
203 msgb_enqueue(&plmn->event_queue, msg);
208 /* queue cell selection message */
209 int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
211 struct gsm322_cellsel *cs = &ms->cellsel;
213 msgb_enqueue(&cs->event_queue, msg);
222 char *gsm_print_rxlev(uint8_t rxlev)
224 static char string[5];
229 sprintf(string, "-%d", 110 - rxlev);
233 static int gsm322_sync_to_cell(struct gsm322_cellsel *cs)
235 struct osmocom_ms *ms = cs->ms;
236 struct gsm48_sysinfo *s = cs->si;
238 LOGP(DCS, LOGL_INFO, "Start syncing. (ARFCN=%d)\n", cs->arfcn);
239 cs->ccch_state = GSM322_CCCH_ST_INIT;
241 if (s->ccch_conf == 1) {
242 LOGP(DCS, LOGL_INFO, "Sysinfo, ccch mode COMB\n");
243 cs->ccch_mode = CCCH_MODE_COMBINED;
245 LOGP(DCS, LOGL_INFO, "Sysinfo, ccch mode NON-COMB\n");
246 cs->ccch_mode = CCCH_MODE_NON_COMBINED;
249 LOGP(DCS, LOGL_INFO, "No sysinfo, ccch mode NONE\n");
250 cs->ccch_mode = CCCH_MODE_NONE;
253 l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
254 return l1ctl_tx_fbsb_req(ms, cs->arfcn,
255 L1CTL_FBSB_F_FB01SB, 100, 0,
259 static void gsm322_unselect_cell(struct gsm322_cellsel *cs)
263 cs->si->si5 = 0; /* unset SI5* */
265 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
266 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
269 /* print to DCS logging */
270 static void print_dcs(void *priv, const char *fmt, ...)
276 vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
277 buffer[sizeof(buffer) - 1] = '\0';
281 // LOGP(DCS, LOGL_INFO, "%s", buffer);
282 printf("%s", buffer);
285 /* del forbidden LA */
286 int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
287 uint16_t mnc, uint16_t lac)
289 struct gsm322_plmn *plmn = &ms->plmn;
290 struct gsm322_la_list *la;
292 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
293 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
294 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
295 "LAs (mcc=%s, mnc=%s, lac=%04x)\n",
296 gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac);
297 llist_del(&la->entry);
306 /* add forbidden LA */
307 int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
308 uint16_t mnc, uint16_t lac, uint8_t cause)
310 struct gsm322_plmn *plmn = &ms->plmn;
311 struct gsm322_la_list *la;
313 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
314 "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc),
315 gsm_print_mnc(mnc), lac);
316 la = talloc_zero(l23_ctx, struct gsm322_la_list);
323 llist_add_tail(&la->entry, &plmn->forbidden_la);
328 /* search forbidden LA */
329 int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
332 struct gsm322_plmn *plmn = &ms->plmn;
333 struct gsm322_la_list *la;
335 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
336 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
343 /* search for PLMN in all BA lists */
344 static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
345 uint16_t mcc, uint16_t mnc)
347 struct gsm322_ba_list *ba, *ba_found = NULL;
349 /* search for BA list */
350 llist_for_each_entry(ba, &cs->ba_list, entry) {
361 /* search available PLMN */
362 int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
366 for (i = 0; i <= 1023; i++) {
367 if (cs->list[i].sysinfo
368 && cs->list[i].sysinfo->mcc == mcc
369 && cs->list[i].sysinfo->mnc == mnc)
376 /* search available HPLMN */
377 int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi)
381 for (i = 0; i <= 1023; i++) {
382 if (cs->list[i].sysinfo
383 && gsm_match_mnc(cs->list[i].sysinfo->mcc,
384 cs->list[i].sysinfo->mnc, imsi))
391 /* del forbidden LA */
396 /*plmn search timer event */
397 static void plmn_timer_timeout(void *arg)
399 struct gsm322_plmn *plmn = arg;
402 LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
404 /* indicate PLMN selection T timeout */
405 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
408 gsm322_plmn_sendmsg(plmn->ms, nmsg);
411 /* start plmn search timer */
412 static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
414 LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
416 plmn->timer.cb = plmn_timer_timeout;
417 plmn->timer.data = plmn;
418 bsc_schedule_timer(&plmn->timer, secs, 0);
421 /* stop plmn search timer */
422 static void stop_plmn_timer(struct gsm322_plmn *plmn)
424 if (bsc_timer_pending(&plmn->timer)) {
425 LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
426 bsc_del_timer(&plmn->timer);
430 /* start cell selection timer */
431 void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
433 LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec);
434 cs->timer.cb = gsm322_cs_timeout;
436 bsc_schedule_timer(&cs->timer, sec, micro);
439 /* start loss timer */
440 void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro)
443 cs->timer.cb = gsm322_cs_loss;
445 if (bsc_timer_pending(&cs->timer)) {
446 struct timeval current_time;
447 unsigned long long currentTime;
449 gettimeofday(¤t_time, NULL);
450 currentTime = current_time.tv_sec * 1000000LL
451 + current_time.tv_usec;
452 currentTime += sec * 1000000LL + micro;
453 cs->timer.timeout.tv_sec = currentTime / 1000000LL;
454 cs->timer.timeout.tv_usec = currentTime % 1000000LL;
459 LOGP(DCS, LOGL_INFO, "Starting loss CS timer with %d seconds.\n", sec);
460 bsc_schedule_timer(&cs->timer, sec, micro);
463 /* stop cell selection timer */
464 static void stop_cs_timer(struct gsm322_cellsel *cs)
466 if (bsc_timer_pending(&cs->timer)) {
467 LOGP(DCS, LOGL_INFO, "stopping pending CS timer.\n");
468 bsc_del_timer(&cs->timer);
476 const char *plmn_a_state_names[] = {
481 "A4 wait for PLMN to appear",
486 const char *plmn_m_state_names[] = {
495 const char *cs_state_names[] = {
497 "C1 normal cell selection",
498 "C2 stored cell selection",
499 "C3 camped normally",
500 "C4 normal cell re-selection",
502 "C6 any cell selection",
503 "C7 camped on any cell",
504 "C8 any cell re-selection",
505 "C9 choose any cell",
511 /* new automatic PLMN search state */
512 static void new_a_state(struct gsm322_plmn *plmn, int state)
514 if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) {
515 LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
519 stop_plmn_timer(plmn);
521 if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
524 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
525 plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
530 /* new manual PLMN search state */
531 static void new_m_state(struct gsm322_plmn *plmn, int state)
533 if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
534 LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
538 if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
541 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
542 plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
547 /* new Cell selection state */
548 static void new_c_state(struct gsm322_cellsel *cs, int state)
550 if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
553 LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n",
554 cs_state_names[cs->state], cs_state_names[state]);
556 /* stop cell selection timer, if running */
559 /* stop scanning of power measurement */
561 LOGP(DCS, LOGL_INFO, "changing state while power scanning\n");
562 l1ctl_tx_reset_req(cs->ms, L1CTL_RES_T_FULL);
573 /* 4.4.3 create sorted list of PLMN
575 * the source of entries are
578 * - entries found in the SIM's PLMN Selector list
579 * - scanned PLMNs above -85 dB (random order)
580 * - scanned PLMNs below or equal -85 (by received level)
584 * The list only includes networks found at last scan.
586 * The list always contains HPLMN if available, even if not used by PLMN
587 * search process at some conditions.
589 * The list contains all PLMNs even if not allowed, so entries have to be
590 * removed when selecting from the list. (In case we use manual cell selection,
591 * we need to provide non-allowed networks also.)
593 static int gsm322_sort_list(struct osmocom_ms *ms)
595 struct gsm322_plmn *plmn = &ms->plmn;
596 struct gsm322_cellsel *cs = &ms->cellsel;
597 struct gsm_subscriber *subscr = &ms->subscr;
598 struct gsm_sub_plmn_list *sim_entry;
599 struct gsm_sub_plmn_na *na_entry;
600 struct llist_head temp_list;
601 struct gsm322_plmn_list *temp, *found;
602 struct llist_head *lh, *lh2;
603 int i, entries, move;
607 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
612 /* Create a temporary list of all networks */
613 INIT_LLIST_HEAD(&temp_list);
614 for (i = 0; i <= 1023; i++) {
615 if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)
616 || !cs->list[i].sysinfo)
619 /* search if network has multiple cells */
621 llist_for_each_entry(temp, &temp_list, entry) {
622 if (temp->mcc == cs->list[i].sysinfo->mcc
623 && temp->mnc == cs->list[i].sysinfo->mnc) {
628 /* update or create */
630 if (cs->list[i].rxlev > found->rxlev)
631 found->rxlev = cs->list[i].rxlev;
633 temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
636 temp->mcc = cs->list[i].sysinfo->mcc;
637 temp->mnc = cs->list[i].sysinfo->mnc;
638 temp->rxlev = cs->list[i].rxlev;
639 llist_add_tail(&temp->entry, &temp_list);
643 /* move Home PLMN, if in list, else add it */
644 if (subscr->sim_valid) {
646 llist_for_each_entry(temp, &temp_list, entry) {
647 if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) {
654 llist_del(&found->entry);
655 llist_add_tail(&found->entry, &plmn->sorted_plmn);
659 /* move entries if in SIM's PLMN Selector list */
660 llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
662 llist_for_each_entry(temp, &temp_list, entry) {
663 if (temp->mcc == sim_entry->mcc
664 && temp->mnc == sim_entry->mnc) {
670 llist_del(&found->entry);
671 llist_add_tail(&found->entry, &plmn->sorted_plmn);
675 /* move PLMN above -85 dBm in random order */
677 llist_for_each_entry(temp, &temp_list, entry) {
678 if (rxlev2dbm(temp->rxlev) > -85)
682 move = random() % entries;
684 llist_for_each_entry(temp, &temp_list, entry) {
685 if (rxlev2dbm(temp->rxlev) > -85) {
687 llist_del(&temp->entry);
688 llist_add_tail(&temp->entry,
698 /* move ohter PLMN in decreasing order */
701 llist_for_each_entry(temp, &temp_list, entry) {
703 || temp->rxlev > search) {
704 search = temp->rxlev;
710 llist_del(&found->entry);
711 llist_add_tail(&found->entry, &plmn->sorted_plmn);
714 /* mark forbidden PLMNs, if in list of forbidden networks */
716 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
717 llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
718 if (temp->mcc == na_entry->mcc
719 && temp->mnc == na_entry->mnc) {
720 temp->cause = na_entry->cause;
724 LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. "
725 "(%02d: mcc %s mnc %s allowed %s rx-lev %s)\n",
726 i, gsm_print_mcc(temp->mcc),
727 gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes",
728 gsm_print_rxlev(temp->rxlev));
732 gsm322_dump_sorted_plmn(ms);
738 * handler for automatic search
741 /* go On PLMN state */
742 static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
744 struct gsm322_plmn *plmn = &ms->plmn;
745 struct gsm_subscriber *subscr = &ms->subscr;
747 new_a_state(plmn, GSM322_A2_ON_PLMN);
749 /* start timer, if on VPLMN of home country OR special case */
750 if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi)
751 && (subscr->always_search_hplmn
752 || gsm_match_mcc(plmn->mcc, subscr->imsi))
753 && subscr->sim_valid && subscr->t6m_hplmn)
754 start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
756 stop_plmn_timer(plmn);
761 /* indicate selected PLMN */
762 static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
764 struct gsm322_plmn *plmn = &ms->plmn;
766 vty_notify(ms, NULL);
767 vty_notify(ms, "Selected Network: %s, %s\n",
768 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
770 return gsm322_a_go_on_plmn(ms, msg);
773 /* no (more) PLMN in list */
774 static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
776 struct gsm322_plmn *plmn = &ms->plmn;
777 struct gsm322_cellsel *cs = &ms->cellsel;
781 /* any allowable PLMN available? */
782 plmn->mcc = plmn->mnc = 0;
783 found = gsm322_cs_select(ms, 0, 1);
785 /* if no PLMN in list */
787 LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable.\n");
789 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
792 /* we must forward this, otherwhise "Any cell selection"
793 * will not start automatically.
795 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
798 gsm322_cs_sendmsg(ms, nmsg);
800 LOGP(DPLMN, LOGL_INFO, "Trigger full PLMN search.\n");
802 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
805 gsm322_cs_sendmsg(ms, nmsg);
810 /* select first PLMN in list */
811 plmn->mcc = cs->list[found].sysinfo->mcc;
812 plmn->mnc = cs->list[found].sysinfo->mnc;
814 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n",
815 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
816 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
818 /* indicate New PLMN */
819 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
822 gsm322_cs_sendmsg(ms, nmsg);
825 return gsm322_a_indicate_selected(ms, msg);
828 /* select first PLMN in list */
829 static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
831 struct gsm322_plmn *plmn = &ms->plmn;
832 struct gsm_subscriber *subscr = &ms->subscr;
834 struct gsm322_plmn_list *plmn_entry;
835 struct gsm322_plmn_list *plmn_first = NULL;
839 gsm322_sort_list(ms);
841 /* select first entry */
843 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
844 /* if last selected PLMN was HPLMN, we skip that */
845 if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc,
847 && plmn_entry->mcc == plmn->mcc
848 && plmn_entry->mnc == plmn->mnc) {
849 LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was "
850 "previously selected.\n");
854 /* select first allowed network */
855 if (!plmn_entry->cause) {
856 plmn_first = plmn_entry;
859 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
860 "because it is not allowed (cause %d).\n", i,
861 gsm_print_mcc(plmn_entry->mcc),
862 gsm_print_mnc(plmn_entry->mnc),
868 /* if no PLMN in list */
870 LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
871 gsm322_a_no_more_plmn(ms, msg);
876 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
877 "mnc=%s %s, %s)\n", plmn->plmn_curr,
878 gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc),
879 gsm_get_mcc(plmn_first->mcc),
880 gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
882 /* set current network */
883 plmn->mcc = plmn_first->mcc;
884 plmn->mnc = plmn_first->mnc;
886 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
888 /* indicate New PLMN */
889 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
892 gsm322_cs_sendmsg(ms, nmsg);
897 /* select next PLMN in list */
898 static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
900 struct gsm322_plmn *plmn = &ms->plmn;
902 struct gsm322_plmn_list *plmn_entry;
903 struct gsm322_plmn_list *plmn_next = NULL;
906 /* select next entry from list */
908 ii = plmn->plmn_curr + 1;
909 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
910 /* skip previously selected networks */
915 /* select next allowed network */
916 if (!plmn_entry->cause) {
917 plmn_next = plmn_entry;
920 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
921 "because it is not allowed (cause %d).\n", i,
922 gsm_print_mcc(plmn_entry->mcc),
923 gsm_print_mnc(plmn_entry->mnc),
929 /* if no more PLMN in list */
931 LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
932 gsm322_a_no_more_plmn(ms, msg);
936 /* set next network */
937 plmn->mcc = plmn_next->mcc;
938 plmn->mnc = plmn_next->mnc;
940 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
941 "mnc=%s %s, %s)\n", plmn->plmn_curr,
942 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
943 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
945 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
947 /* indicate New PLMN */
948 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
951 gsm322_cs_sendmsg(ms, nmsg);
956 /* User re-selection event */
957 static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg)
959 struct gsm322_plmn *plmn = &ms->plmn;
960 struct gsm_subscriber *subscr = &ms->subscr;
961 struct gsm48_rrlayer *rr = &ms->rrlayer;
962 struct gsm322_plmn_list *plmn_entry;
963 struct gsm322_plmn_list *plmn_found = NULL;
965 if (!subscr->sim_valid) {
969 /* try again later, if not idle */
970 if (rr->state != GSM48_RR_ST_IDLE) {
971 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
976 /* search current PLMN in list */
977 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
978 if (plmn_entry->mcc == plmn->mcc
979 && plmn_entry->mnc == plmn->mnc)
980 plmn_found = plmn_entry;
984 /* abort if list is empty */
986 LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
990 LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
991 "and restarting PLMN search process.\n");
993 /* move entry to end of list */
994 llist_del(&plmn_found->entry);
995 llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
997 /* select first PLMN in list */
998 return gsm322_a_sel_first_plmn(ms, msg);
1001 /* PLMN becomes available */
1002 static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1004 struct gsm322_plmn *plmn = &ms->plmn;
1005 struct gsm_subscriber *subscr = &ms->subscr;
1006 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1008 if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
1009 && subscr->plmn_mnc == gm->mnc) {
1011 plmn->mcc = gm->mcc;
1012 plmn->mnc = gm->mnc;
1013 LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n");
1014 return gsm322_a_go_on_plmn(ms, msg);
1016 /* select first PLMN in list */
1017 LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
1018 "search process.\n");
1019 return gsm322_a_sel_first_plmn(ms, msg);
1023 /* loss of radio coverage */
1024 static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
1026 struct gsm322_plmn *plmn = &ms->plmn;
1027 struct gsm322_cellsel *cs = &ms->cellsel;
1031 /* any PLMN available */
1032 found = gsm322_cs_select(ms, 0, 1);
1034 /* if PLMN in list */
1036 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s "
1037 "%s, %s)\n", gsm_print_mcc(
1038 cs->list[found].sysinfo->mcc),
1039 gsm_print_mnc(cs->list[found].sysinfo->mnc),
1040 gsm_get_mcc(cs->list[found].sysinfo->mcc),
1041 gsm_get_mnc(cs->list[found].sysinfo->mcc,
1042 cs->list[found].sysinfo->mnc));
1043 return gsm322_a_sel_first_plmn(ms, msg);
1046 LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
1048 plmn->mcc = plmn->mnc = 0;
1050 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
1052 /* Tell cell selection process to handle "no cell found". */
1053 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1056 gsm322_cs_sendmsg(ms, nmsg);
1061 /* MS is switched on OR SIM is inserted OR removed */
1062 static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1064 struct gsm_subscriber *subscr = &ms->subscr;
1065 struct gsm322_plmn *plmn = &ms->plmn;
1068 if (!subscr->sim_valid) {
1069 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1070 LOGP(DPLMN, LOGL_INFO, "SIM is removed\n");
1071 new_a_state(plmn, GSM322_A6_NO_SIM);
1076 /* if there is a registered PLMN */
1077 if (subscr->plmn_valid) {
1078 /* select the registered PLMN */
1079 plmn->mcc = subscr->plmn_mcc;
1080 plmn->mnc = subscr->plmn_mnc;
1082 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1083 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1084 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1085 gsm_get_mnc(plmn->mcc, plmn->mnc));
1086 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1087 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1088 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1089 gsm_get_mnc(plmn->mcc, plmn->mnc));
1091 new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
1093 /* indicate New PLMN */
1094 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1097 gsm322_cs_sendmsg(ms, nmsg);
1102 /* initiate search at cell selection */
1103 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1104 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1106 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1109 gsm322_cs_sendmsg(ms, nmsg);
1114 /* MS is switched off */
1115 static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1117 struct gsm322_plmn *plmn = &ms->plmn;
1119 new_a_state(plmn, GSM322_A0_NULL);
1124 static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1126 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1130 /* SIM is removed */
1131 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1135 /* indicate SIM remove to cell selection process */
1136 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1139 gsm322_cs_sendmsg(ms, nmsg);
1141 return gsm322_a_switch_on(ms, msg);
1144 /* location update response: "Roaming not allowed" */
1145 static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
1147 /* store in list of forbidden LAs is done in gsm48* */
1149 return gsm322_a_sel_first_plmn(ms, msg);
1152 /* On VPLMN of home country and timeout occurs */
1153 static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg)
1155 struct gsm48_rrlayer *rr = &ms->rrlayer;
1156 struct gsm322_plmn *plmn = &ms->plmn;
1157 struct gsm322_cellsel *cs = &ms->cellsel;
1160 /* try again later, if not idle and not camping */
1161 if (rr->state != GSM48_RR_ST_IDLE
1162 || cs->state != GSM322_C3_CAMPED_NORMALLY) {
1163 LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n");
1164 start_plmn_timer(plmn, 60);
1169 new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
1171 /* initiate search at cell selection */
1172 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1175 gsm322_cs_sendmsg(ms, nmsg);
1180 /* manual mode selected */
1181 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
1185 /* restart state machine */
1186 gsm322_a_switch_off(ms, msg);
1187 ms->settings.plmn_mode = PLMN_MODE_MANUAL;
1188 gsm322_m_switch_on(ms, msg);
1190 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1193 gsm48_mmevent_msg(ms, nmsg);
1199 * handler for manual search
1202 /* display PLMNs and to Not on PLMN */
1203 static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
1205 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1206 int msg_type = gm->msg_type;
1207 struct gsm322_plmn *plmn = &ms->plmn;
1208 struct gsm_sub_plmn_list *temp;
1211 gsm322_sort_list(ms);
1213 vty_notify(ms, NULL);
1215 case GSM322_EVENT_REG_FAILED:
1216 vty_notify(ms, "Failed to register to network %s, %s "
1218 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1219 gsm_get_mcc(plmn->mcc),
1220 gsm_get_mnc(plmn->mcc, plmn->mnc));
1222 case GSM322_EVENT_NO_CELL_FOUND:
1223 vty_notify(ms, "No cell found for network %s, %s "
1225 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1226 gsm_get_mcc(plmn->mcc),
1227 gsm_get_mnc(plmn->mcc, plmn->mnc));
1229 case GSM322_EVENT_ROAMING_NA:
1230 vty_notify(ms, "Roaming not allowed to network %s, %s "
1232 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1233 gsm_get_mcc(plmn->mcc),
1234 gsm_get_mnc(plmn->mcc, plmn->mnc));
1238 if (llist_empty(&plmn->sorted_plmn))
1239 vty_notify(ms, "Search network!\n");
1241 vty_notify(ms, "Search or select from network:\n");
1242 llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
1243 vty_notify(ms, " Network %s, %s (%s, %s)\n",
1244 gsm_print_mcc(temp->mcc),
1245 gsm_print_mnc(temp->mnc),
1246 gsm_get_mcc(temp->mcc),
1247 gsm_get_mnc(temp->mcc, temp->mnc));
1250 /* go Not on PLMN state */
1251 new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
1256 /* user starts reselection */
1257 static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg)
1259 struct gsm_subscriber *subscr = &ms->subscr;
1260 struct gsm48_rrlayer *rr = &ms->rrlayer;
1263 if (!subscr->sim_valid) {
1267 /* try again later, if not idle */
1268 if (rr->state != GSM48_RR_ST_IDLE) {
1269 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
1274 /* initiate search at cell selection */
1275 vty_notify(ms, NULL);
1276 vty_notify(ms, "Searching Network, please wait...\n");
1277 LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n");
1279 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1282 gsm322_cs_sendmsg(ms, nmsg);
1287 /* MS is switched on OR SIM is inserted OR removed */
1288 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1290 struct gsm_subscriber *subscr = &ms->subscr;
1291 struct gsm322_plmn *plmn = &ms->plmn;
1294 if (!subscr->sim_valid) {
1295 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1296 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1297 new_m_state(plmn, GSM322_M5_NO_SIM);
1302 /* if there is a registered PLMN */
1303 if (subscr->plmn_valid) {
1306 /* select the registered PLMN */
1307 plmn->mcc = subscr->plmn_mcc;
1308 plmn->mnc = subscr->plmn_mnc;
1310 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1311 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1312 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1313 gsm_get_mnc(plmn->mcc, plmn->mnc));
1314 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1315 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1316 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1317 gsm_get_mnc(plmn->mcc, plmn->mnc));
1319 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1321 /* indicate New PLMN */
1322 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1325 gsm322_cs_sendmsg(ms, nmsg);
1330 /* initiate search at cell selection */
1331 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1332 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1333 vty_notify(ms, NULL);
1334 vty_notify(ms, "Searching Network, please wait...\n");
1336 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1339 gsm322_cs_sendmsg(ms, nmsg);
1344 /* MS is switched off */
1345 static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1347 struct gsm322_plmn *plmn = &ms->plmn;
1349 stop_plmn_timer(plmn);
1351 new_m_state(plmn, GSM322_M0_NULL);
1356 static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1358 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1362 /* SIM is removed */
1363 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1365 struct gsm322_plmn *plmn = &ms->plmn;
1368 stop_plmn_timer(plmn);
1370 /* indicate SIM remove to cell selection process */
1371 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1374 gsm322_cs_sendmsg(ms, nmsg);
1376 return gsm322_m_switch_on(ms, msg);
1379 /* go to On PLMN state */
1380 static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1382 struct gsm322_plmn *plmn = &ms->plmn;
1383 struct gsm_subscriber *subscr = &ms->subscr;
1385 /* set last registered PLMN */
1386 subscr->plmn_valid = 1;
1387 subscr->plmn_mcc = plmn->mcc;
1388 subscr->plmn_mnc = plmn->mnc;
1393 new_m_state(plmn, GSM322_M2_ON_PLMN);
1398 /* indicate selected PLMN */
1399 static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
1401 struct gsm322_plmn *plmn = &ms->plmn;
1403 vty_notify(ms, NULL);
1404 vty_notify(ms, "Selected Network: %s, %s\n",
1405 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1407 return gsm322_m_go_on_plmn(ms, msg);
1410 /* previously selected PLMN becomes available again */
1411 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1413 struct gsm322_plmn *plmn = &ms->plmn;
1414 struct gsm322_cellsel *cs = &ms->cellsel;
1416 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1418 if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
1421 LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
1422 "selected, so start selection.\n");
1424 /* indicate New PLMN */
1425 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1428 gsm322_cs_sendmsg(ms, nmsg);
1434 /* the user has selected given PLMN */
1435 static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
1437 struct gsm322_plmn *plmn = &ms->plmn;
1438 struct gsm_subscriber *subscr = &ms->subscr;
1439 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1442 /* use user selection */
1443 plmn->mcc = gm->mcc;
1444 plmn->mnc = gm->mnc;
1446 vty_notify(ms, NULL);
1447 vty_notify(ms, "Selected Network: %s, %s\n",
1448 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1449 LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s "
1450 "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1451 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1453 /* if selected PLMN is in list of forbidden PLMNs */
1454 gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc);
1456 new_m_state(plmn, GSM322_M4_TRYING_PLMN);
1458 /* indicate New PLMN */
1459 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1462 gsm322_cs_sendmsg(ms, nmsg);
1467 /* auto mode selected */
1468 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
1472 /* restart state machine */
1473 gsm322_m_switch_off(ms, msg);
1474 ms->settings.plmn_mode = PLMN_MODE_AUTO;
1475 gsm322_a_switch_on(ms, msg);
1477 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1480 gsm48_mmevent_msg(ms, nmsg);
1485 /* if no cell is found in other states than in *_TRYING_* states */
1486 static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
1490 /* Tell cell selection process to handle "no cell found". */
1491 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1494 gsm322_cs_sendmsg(ms, nmsg);
1500 * cell scanning process
1503 /* select a suitable and allowable cell */
1504 static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed)
1506 struct gsm322_cellsel *cs = &ms->cellsel;
1507 struct gsm_subscriber *subscr = &ms->subscr;
1508 struct gsm48_sysinfo *s;
1509 int i, found = -1, power = 0;
1510 uint8_t flags, mask;
1513 /* set out access class depending on the cell selection type */
1515 acc_class = subscr->acc_class | 0x0400; /* add emergency */
1516 LOGP(DCS, LOGL_INFO, "Select using access class with Emergency "
1519 acc_class = subscr->acc_class;
1520 LOGP(DCS, LOGL_INFO, "Select using access class \n");
1523 /* flags to match */
1524 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1525 | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
1526 if (cs->state == GSM322_C2_STORED_CELL_SEL
1527 || cs->state == GSM322_C5_CHOOSE_CELL)
1528 mask |= GSM322_CS_FLAG_BA;
1529 flags = mask; /* all masked flags are requied */
1531 /* loop through all scanned frequencies and select cell */
1532 for (i = 0; i <= 1023; i++) {
1533 cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
1534 s = cs->list[i].sysinfo;
1536 /* channel has no informations for us */
1537 if (!s || (cs->list[i].flags & mask) != flags) {
1541 /* check C1 criteria not fullfilled */
1542 // TODO: C1 is also dependant on power class and max power
1543 if (rxlev2dbm(cs->list[i].rxlev) < s->rxlev_acc_min_db) {
1544 LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
1545 "not met. (rxlev %s < min %d)\n", i,
1546 gsm_print_rxlev(cs->list[i].rxlev),
1547 s->rxlev_acc_min_db);
1551 /* if cell is barred and we don't override */
1552 if (!subscr->acc_barr
1553 && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
1554 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
1559 /* if cell is in list of forbidden LAs */
1560 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
1561 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1562 "list of forbidden LAs. (mcc=%s mnc=%s "
1563 "lai=%04x)\n", i, gsm_print_mcc(s->mcc),
1564 gsm_print_mnc(s->mnc), s->lac);
1568 /* if cell is in list of forbidden PLMNs */
1569 if (plmn_allowed && gsm_subscr_is_forbidden_plmn(subscr,
1571 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1572 "list of forbidden PLMNs. (mcc=%s mnc=%s)\n", i,
1573 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc));
1577 /* if we have no access to the cell and we don't override */
1578 if (!subscr->acc_barr
1579 && !(acc_class & (s->class_barr ^ 0xffff))) {
1580 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
1581 "barred for out access. (access=%04x "
1582 "barred=%04x)\n", i, acc_class, s->class_barr);
1586 /* store temporary available and allowable flag */
1587 cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
1589 /* if we search a specific PLMN, but it does not match */
1590 if (!any && cs->mcc && (cs->mcc != s->mcc
1591 || cs->mnc != s->mnc)) {
1592 LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
1593 "does not match target PLMN. (mcc=%s "
1594 "mnc=%s)\n", i, gsm_print_mcc(s->mcc),
1595 gsm_print_mnc(s->mnc));
1599 LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%s "
1600 "mcc=%s mnc=%s lac=%04x %s, %s)\n", i,
1601 gsm_print_rxlev(cs->list[i].rxlev),
1602 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac,
1603 gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc));
1605 /* find highest power cell */
1606 if (found < 0 || cs->list[i].rxlev > power) {
1607 power = cs->list[i].rxlev;
1613 LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
1618 /* tune to first/next unscanned frequency and search for PLMN */
1619 static int gsm322_cs_scan(struct osmocom_ms *ms)
1621 struct gsm322_cellsel *cs = &ms->cellsel;
1623 uint8_t mask, flags;
1624 uint32_t weight = 0, test = cs->scan_state;
1626 /* search for strongest unscanned cell */
1627 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1628 | GSM322_CS_FLAG_SIGNAL;
1629 if (cs->state == GSM322_C2_STORED_CELL_SEL
1630 || cs->state == GSM322_C5_CHOOSE_CELL)
1631 mask |= GSM322_CS_FLAG_BA;
1632 flags = mask; /* all masked flags are requied */
1633 for (i = 0; i <= 1023; i++) {
1634 /* skip if band has enough frequencies scanned (3.2.1) */
1635 for (j = 0; gsm_sup_smax[j].max; j++) {
1636 if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
1637 if (gsm_sup_smax[j].start >= i
1638 && gsm_sup_smax[j].end <= i)
1641 if (gsm_sup_smax[j].end <= i
1642 || gsm_sup_smax[j].start >= i)
1646 if (gsm_sup_smax[j].max) {
1647 if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
1650 /* search for unscanned frequency */
1651 if ((cs->list[i].flags & mask) == flags) {
1652 /* weight depends on the power level
1653 * if it is the same, it depends on arfcn
1655 test = cs->list[i].rxlev + 1;
1656 test = (test << 16) | i;
1657 if (test >= cs->scan_state)
1663 cs->scan_state = weight;
1666 gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs,
1669 /* special case for PLMN search */
1670 if (cs->state == GSM322_PLMN_SEARCH && !weight) {
1673 /* create AA flag */
1674 cs->mcc = cs->mnc = 0;
1675 gsm322_cs_select(ms, 0, 0);
1677 new_c_state(cs, GSM322_C0_NULL);
1679 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END);
1680 LOGP(DCS, LOGL_INFO, "PLMN search finished.\n");
1683 gsm322_plmn_sendmsg(ms, nmsg);
1688 /* special case for HPLMN search */
1689 if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
1692 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1693 LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n");
1696 gsm322_plmn_sendmsg(ms, nmsg);
1698 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
1700 cs->arfcn = cs->sel_arfcn;
1701 LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev "
1702 "%s).\n", cs->arfcn,
1703 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
1705 gsm322_sync_to_cell(cs);
1706 // start_cs_timer(cs, ms->support.sync_to, 0);
1711 /* if all frequencies have been searched */
1717 LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
1719 /* just see, if we search for any cell */
1720 if (cs->state == GSM322_C6_ANY_CELL_SEL
1721 || cs->state == GSM322_C8_ANY_CELL_RESEL
1722 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1725 found = gsm322_cs_select(ms, any, 0);
1729 struct gsm322_plmn *plmn = &ms->plmn;
1731 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1734 cs->si = cs->list[cs->arfcn].sysinfo;
1736 gsm322_sync_to_cell(cs);
1738 /* selected PLMN (manual) or any PLMN (auto) */
1739 switch (ms->settings.plmn_mode) {
1740 case PLMN_MODE_AUTO:
1741 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1742 /* PLMN becomes available */
1743 nmsg = gsm322_msgb_alloc(
1744 GSM322_EVENT_PLMN_AVAIL);
1747 gsm322_plmn_sendmsg(ms, nmsg);
1750 case PLMN_MODE_MANUAL:
1751 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1752 && gsm322_is_plmn_avail(cs, plmn->mcc,
1754 /* PLMN becomes available */
1755 nmsg = gsm322_msgb_alloc(
1756 GSM322_EVENT_PLMN_AVAIL);
1759 gsm322_plmn_sendmsg(ms, nmsg);
1764 /* set selected cell */
1766 cs->sel_arfcn = cs->arfcn;
1767 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1768 cs->sel_mcc = cs->si->mcc;
1769 cs->sel_mnc = cs->si->mnc;
1770 cs->sel_lac = cs->si->lac;
1771 cs->sel_id = cs->si->cell_id;
1773 /* tell CS process about available cell */
1774 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1775 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1778 /* unset selected cell */
1779 gsm322_unselect_cell(cs);
1781 /* tell CS process about no cell available */
1782 LOGP(DCS, LOGL_INFO, "No cell available.\n");
1783 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1787 gsm322_c_event(ms, nmsg);
1793 /* NOTE: We might already have system information from previous
1794 * scan. But we need recent informations, so we scan again!
1797 /* Tune to frequency for a while, to receive broadcasts. */
1798 cs->arfcn = weight & 1023;
1799 LOGP(DCS, LOGL_INFO, "Scanning frequency %d (rxlev %s).\n", cs->arfcn,
1800 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
1802 gsm322_sync_to_cell(cs);
1803 // start_cs_timer(cs, ms->support.sync_to, 0);
1805 /* Allocate/clean system information. */
1806 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1807 if (cs->list[cs->arfcn].sysinfo)
1808 memset(cs->list[cs->arfcn].sysinfo, 0,
1809 sizeof(struct gsm48_sysinfo));
1811 cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
1812 struct gsm48_sysinfo);
1813 if (!cs->list[cs->arfcn].sysinfo)
1815 cs->si = cs->list[cs->arfcn].sysinfo;
1817 /* increase scan counter for each maximum scan range */
1818 if (gsm_sup_smax[j].max) {
1819 LOGP(DCS, LOGL_INFO, "%d frequencies left in band %d..%d\n",
1820 gsm_sup_smax[j].max - gsm_sup_smax[j].temp,
1821 gsm_sup_smax[j].start, gsm_sup_smax[j].end);
1822 gsm_sup_smax[j].temp++;
1828 /* check if cell is now suitable and allowable */
1829 static int gsm322_cs_store(struct osmocom_ms *ms)
1831 struct gsm322_cellsel *cs = &ms->cellsel;
1832 struct gsm48_sysinfo *s = cs->si;
1833 struct gsm322_plmn *plmn = &ms->plmn;
1837 if (cs->state != GSM322_C2_STORED_CELL_SEL
1838 && cs->state != GSM322_C1_NORMAL_CELL_SEL
1839 && cs->state != GSM322_C6_ANY_CELL_SEL
1840 && cs->state != GSM322_C4_NORMAL_CELL_RESEL
1841 && cs->state != GSM322_C8_ANY_CELL_RESEL
1842 && cs->state != GSM322_C5_CHOOSE_CELL
1843 && cs->state != GSM322_C9_CHOOSE_ANY_CELL
1844 && cs->state != GSM322_PLMN_SEARCH
1845 && cs->state != GSM322_HPLMN_SEARCH) {
1846 LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
1847 "(re-)selection, please fix!\n");
1852 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO;
1854 && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp &&
1855 cs->list[cs->arfcn].sysinfo->sp_cbq))
1856 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED;
1858 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED;
1861 cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db;
1862 cs->list[cs->arfcn].class_barr = s->class_barr;
1863 cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch;
1866 /* store selected network */
1869 cs->list[cs->arfcn].mcc = s->mcc;
1870 cs->list[cs->arfcn].mnc = s->mnc;
1871 cs->list[cs->arfcn].lac = s->lac;
1874 if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
1875 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD;
1877 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD;
1880 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev %s "
1881 "mcc %s mnc %s lac %04x)\n", cs->arfcn,
1882 gsm_print_rxlev(cs->list[cs->arfcn].rxlev),
1883 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac);
1885 /* special case for PLMN search */
1886 if (cs->state == GSM322_PLMN_SEARCH)
1887 /* tune to next cell */
1888 return gsm322_cs_scan(ms);
1890 /* special case for HPLMN search */
1891 if (cs->state == GSM322_HPLMN_SEARCH) {
1892 struct gsm_subscriber *subscr = &ms->subscr;
1895 if (!gsm322_is_hplmn_avail(cs, subscr->imsi))
1896 /* tune to next cell */
1897 return gsm322_cs_scan(ms);
1899 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1900 LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n");
1903 gsm322_plmn_sendmsg(ms, nmsg);
1908 /* just see, if we search for any cell */
1909 if (cs->state == GSM322_C6_ANY_CELL_SEL
1910 || cs->state == GSM322_C8_ANY_CELL_RESEL
1911 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1914 found = gsm322_cs_select(ms, any, 0);
1918 LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n");
1919 /* tune to next cell */
1920 return gsm322_cs_scan(ms);
1923 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1926 cs->si = cs->list[cs->arfcn].sysinfo;
1928 gsm322_sync_to_cell(cs);
1930 /* selected PLMN (manual) or any PLMN (auto) */
1931 switch (ms->settings.plmn_mode) {
1932 case PLMN_MODE_AUTO:
1933 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1934 /* PLMN becomes available */
1935 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1938 gsm322_plmn_sendmsg(ms, nmsg);
1941 case PLMN_MODE_MANUAL:
1942 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1943 && gsm322_is_plmn_avail(cs, plmn->mcc,
1945 /* PLMN becomes available */
1946 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1949 gsm322_plmn_sendmsg(ms, nmsg);
1954 /* set selected cell */
1956 cs->sel_arfcn = cs->arfcn;
1957 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1958 cs->sel_mcc = cs->si->mcc;
1959 cs->sel_mnc = cs->si->mnc;
1960 cs->sel_lac = cs->si->lac;
1961 cs->sel_id = cs->si->cell_id;
1963 /* tell CS process about available cell */
1964 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1965 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1968 gsm322_c_event(ms, nmsg);
1974 /* process system information when returing to idle mode */
1975 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
1977 struct gsm322_cellsel *cs = &ms->cellsel;
1978 struct gsm48_sysinfo *s = cs->si;
1979 struct gsm322_ba_list *ba = NULL;
1983 /* collect system information received during dedicated mode */
1985 && (!s->nb_ext_ind_si5
1986 || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
1987 || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
1988 && s->nb_ext_ind_si5bis))) {
1989 /* find or create ba list */
1990 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1992 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1997 llist_add_tail(&ba->entry, &cs->ba_list);
1999 /* update (add) ba list */
2000 memset(freq, 0, sizeof(freq));
2001 for (i = 0; i <= 1023; i++) {
2002 if ((s->freq[i].mask & (FREQ_TYPE_SERV
2003 | FREQ_TYPE_NCELL | FREQ_TYPE_REP)))
2004 freq[i >> 3] |= (1 << (i & 7));
2006 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
2007 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
2008 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2009 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2010 gsm_get_mnc(ba->mcc, ba->mnc));
2011 memcpy(ba->freq, freq, sizeof(freq));
2018 /* store BA whenever a system informations changes */
2019 static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
2020 struct gsm48_sysinfo *s)
2022 struct gsm322_ba_list *ba;
2026 /* find or create ba list */
2027 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
2029 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2034 llist_add_tail(&ba->entry, &cs->ba_list);
2036 /* update ba list */
2037 memset(freq, 0, sizeof(freq));
2038 freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
2039 for (i = 0; i <= 1023; i++) {
2040 if ((s->freq[i].mask &
2041 (FREQ_TYPE_SERV | FREQ_TYPE_NCELL | FREQ_TYPE_REP)))
2042 freq[i >> 3] |= (1 << (i & 7));
2044 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
2045 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
2046 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2047 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2048 gsm_get_mnc(ba->mcc, ba->mnc));
2049 memcpy(ba->freq, freq, sizeof(freq));
2055 /* process system information during camping on a cell */
2056 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2058 // struct gsm48_rrlayer *rr = &ms->rrlayer;
2059 struct gsm322_cellsel *cs = &ms->cellsel;
2060 struct gsm48_sysinfo *s = cs->si;
2061 struct gsm_subscriber *subscr = &ms->subscr;
2062 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2066 if (rr->state != GSM48_RR_ST_IDLE) {
2067 LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n");
2072 /* Store BA if we have full system info about cells and neigbor cells.
2073 * Depending on the extended bit in the channel description,
2074 * we require more or less system informations about neighbor cells
2078 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2079 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2080 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2081 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2084 && (!s->nb_ext_ind_si2
2085 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2086 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2087 && s->nb_ext_ind_si2bis)))
2088 gsm322_store_ba_list(cs, s);
2090 /* update sel_si, if all relevant system informations received */
2091 if (s->si1 && s->si2 && s->si3
2092 && (!s->nb_ext_ind_si2
2093 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2094 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2095 && s->nb_ext_ind_si2bis))) {
2097 LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is "
2099 memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
2100 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2104 /* check for barred cell */
2105 if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
2106 /* check if cell becomes barred */
2107 if (!subscr->acc_barr && s->cell_barr
2108 && !(cs->list[cs->arfcn].sysinfo
2109 && cs->list[cs->arfcn].sysinfo->sp
2110 && cs->list[cs->arfcn].sysinfo->sp_cbq)) {
2111 LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
2113 /* mark cell as unscanned */
2114 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2115 if (cs->list[cs->arfcn].sysinfo) {
2116 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2118 talloc_free(cs->list[cs->arfcn].sysinfo);
2119 cs->list[cs->arfcn].sysinfo = NULL;
2120 gsm322_unselect_cell(cs);
2122 /* trigger reselection without queueing,
2123 * because other sysinfo message may be queued
2126 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2129 gsm322_c_event(ms, nmsg);
2134 /* check if cell access becomes barred */
2135 if (!((subscr->acc_class & 0xfbff)
2136 & (s->class_barr ^ 0xffff))) {
2137 LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
2142 /* check if MCC, MNC, LAC, cell ID changes */
2143 if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc
2144 || cs->sel_lac != s->lac) {
2145 LOGP(DCS, LOGL_NOTICE, "Cell changes location area. "
2146 "This is not good!\n");
2149 if (cs->sel_id != s->cell_id) {
2150 LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. "
2151 "This is not good!\n");
2158 /* process system information during channel scanning */
2159 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2161 struct gsm322_cellsel *cs = &ms->cellsel;
2162 struct gsm48_sysinfo *s = cs->si;
2163 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2165 /* no sysinfo if we are not done with power scan */
2166 if (cs->powerscan) {
2167 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
2171 /* Store BA if we have full system info about cells and neigbor cells.
2172 * Depending on the extended bit in the channel description,
2173 * we require more or less system informations about neighbor cells
2177 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2178 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2179 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2180 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2183 && (!s->nb_ext_ind_si2
2184 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2185 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2186 && s->nb_ext_ind_si2bis)))
2187 gsm322_store_ba_list(cs, s);
2189 /* all relevant system informations received */
2190 if (s->si1 && s->si2 && s->si3
2191 && (!s->nb_ext_ind_si2
2192 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2193 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2194 && s->nb_ext_ind_si2bis))) {
2195 LOGP(DCS, LOGL_INFO, "Received relevant sysinfo.\n");
2199 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2201 /* store sysinfo and continue scan */
2202 return gsm322_cs_store(ms);
2205 /* wait for more sysinfo or timeout */
2209 static void gsm322_cs_timeout(void *arg)
2211 struct gsm322_cellsel *cs = arg;
2212 struct osmocom_ms *ms = cs->ms;
2214 LOGP(DCS, LOGL_INFO, "Cell selection failed.\n");
2216 /* if we have no lock, we retry */
2217 if (cs->ccch_state != GSM322_CCCH_ST_SYNC)
2218 LOGP(DCS, LOGL_INFO, "Sync timeout.\n");
2220 LOGP(DCS, LOGL_INFO, "Read timeout.\n");
2222 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev %s)\n",
2223 cs->arfcn, gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
2225 /* remove system information */
2226 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2227 if (cs->list[cs->arfcn].sysinfo) {
2228 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", cs->arfcn);
2229 talloc_free(cs->list[cs->arfcn].sysinfo);
2230 cs->list[cs->arfcn].sysinfo = NULL;
2231 gsm322_unselect_cell(cs);
2234 /* tune to next cell */
2241 * power scan process
2244 /* search for block of unscanned frequencies and start scanning */
2245 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
2247 struct gsm322_cellsel *cs = &ms->cellsel;
2248 struct gsm_settings *set = &ms->settings;
2250 uint8_t mask, flags;
2254 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
2255 flags = GSM322_CS_FLAG_SUPPORT;
2257 /* in case of sticking to a cell, we only select it */
2259 LOGP(DCS, LOGL_FATAL, "Scanning power for sticked cell.\n");
2260 i = set->stick_arfcn;
2261 if ((cs->list[i].flags & mask) == flags)
2264 /* search for first frequency to scan */
2265 if (cs->state == GSM322_C2_STORED_CELL_SEL
2266 || cs->state == GSM322_C5_CHOOSE_CELL) {
2267 LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA "
2269 mask |= GSM322_CS_FLAG_BA;
2270 flags |= GSM322_CS_FLAG_BA;
2272 LOGP(DCS, LOGL_FATAL, "Scanning power for all "
2274 for (i = 0; i <= 1023; i++) {
2275 if ((cs->list[i].flags & mask) == flags) {
2282 /* if there is no more frequency, we can tune to that cell */
2286 /* stop power level scanning */
2289 /* check if not signal is found */
2290 for (i = 0; i <= 1023; i++) {
2291 if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2297 LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
2298 /* on normal cell selection, start over */
2299 if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
2300 for (i = 0; i <= 1023; i++) {
2301 /* clear flag that this was scanned */
2302 cs->list[i].flags &=
2303 ~(GSM322_CS_FLAG_POWER
2304 | GSM322_CS_FLAG_SIGNAL
2305 | GSM322_CS_FLAG_SYSINFO);
2306 if (cs->list[i].sysinfo) {
2307 LOGP(DCS, LOGL_INFO, "free "
2308 "sysinfo arfcn=%d\n",
2311 cs->list[i].sysinfo);
2312 cs->list[i].sysinfo = NULL;
2315 /* no cell selected */
2316 gsm322_unselect_cell(cs);
2319 /* on other cell selection, indicate "no cell found" */
2320 /* NOTE: PLMN search process handles it.
2321 * If not handled there, CS process gets indicated.
2322 * If we would continue to process CS, then we might get
2323 * our list of scanned cells disturbed.
2325 if (cs->state == GSM322_PLMN_SEARCH)
2326 nmsg = gsm322_msgb_alloc(
2327 GSM322_EVENT_PLMN_SEARCH_END);
2329 nmsg = gsm322_msgb_alloc(
2330 GSM322_EVENT_NO_CELL_FOUND);
2333 gsm322_plmn_sendmsg(ms, nmsg);
2335 /* if HPLMN search, select last frequency */
2336 if (cs->state == GSM322_HPLMN_SEARCH) {
2337 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2339 cs->arfcn = cs->sel_arfcn;
2340 LOGP(DCS, LOGL_INFO, "Tuning back to frequency "
2341 "%d (rxlev %s).\n", cs->arfcn,
2343 cs->list[cs->arfcn].rxlev));
2345 gsm322_sync_to_cell(cs);
2346 // start_cs_timer(cs, ms->support.sync_to, 0);
2349 new_c_state(cs, GSM322_C0_NULL);
2353 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
2354 cs->scan_state = 0xffffffff; /* higher than high */
2355 /* clear counter of scanned frequencies of each range */
2356 for (i = 0; gsm_sup_smax[i].max; i++)
2357 gsm_sup_smax[i].temp = 0;
2358 return gsm322_cs_scan(ms);
2361 /* search last frequency to scan (en block) */
2364 for (i = s + 1; i <= 1023; i++) {
2365 if ((cs->list[i].flags & mask) == flags)
2372 LOGP(DCS, LOGL_INFO, "Scanning frequencies. (%d..%d)\n", s, e);
2374 /* start scan on radio interface */
2375 if (!cs->powerscan) {
2376 l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
2379 //#warning TESTING!!!!
2381 return l1ctl_tx_pm_req_range(ms, s, e);
2384 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
2385 void *handler_data, void *signal_data)
2387 struct osmocom_ms *ms;
2388 struct gsm322_cellsel *cs;
2389 struct osmobb_meas_res *mr;
2390 struct osmobb_fbsb_res *fr;
2394 if (subsys != SS_L1CTL)
2398 case S_L1CTL_PM_RES:
2404 i = mr->band_arfcn & 1023;
2406 cs->list[i].rxlev = rxlev;
2407 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2408 /* if minimum level is reached or if we stick to a cell */
2409 if (rxlev2dbm(rxlev) >= ms->support.min_rxlev_db
2410 || ms->settings.stick) {
2411 cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2412 LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2413 "rxlev %s (%d))\n", i,
2414 gsm_print_rxlev(rxlev), rxlev);
2417 case S_L1CTL_PM_DONE:
2418 LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
2423 gsm322_cs_powerscan(ms);
2425 case S_L1CTL_FBSB_RESP:
2429 if (cs->ccch_state == GSM322_CCCH_ST_INIT) {
2430 LOGP(DCS, LOGL_INFO, "Channel synched. (ARFCN=%d, "
2431 "snr=%u, BSIC=%u)\n", cs->arfcn, fr->snr,
2433 cs->ccch_state = GSM322_CCCH_ST_SYNC;
2435 cs->si->bsic = fr->bsic;
2439 /* in dedicated mode */
2440 if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
2441 return gsm48_rr_tx_rand_acc(ms, NULL);
2444 /* set timer for reading BCCH */
2445 if (cs->state == GSM322_C2_STORED_CELL_SEL
2446 || cs->state == GSM322_C1_NORMAL_CELL_SEL
2447 || cs->state == GSM322_C6_ANY_CELL_SEL
2448 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2449 || cs->state == GSM322_C8_ANY_CELL_RESEL
2450 || cs->state == GSM322_C5_CHOOSE_CELL
2451 || cs->state == GSM322_C9_CHOOSE_ANY_CELL
2452 || cs->state == GSM322_PLMN_SEARCH
2453 || cs->state == GSM322_HPLMN_SEARCH)
2454 start_cs_timer(cs, ms->support.scan_to, 0);
2455 // TODO: timer depends on BCCH config
2458 case S_L1CTL_FBSB_ERR:
2462 gsm322_sync_to_cell(cs);
2464 LOGP(DCS, LOGL_INFO, "Channel sync error, try again.\n");
2467 LOGP(DCS, LOGL_INFO, "Channel sync error.\n");
2475 gsm322_cs_timeout(cs);
2479 if (ms->mmlayer.power_off_idle) {
2489 static void gsm322_cs_loss(void *arg)
2491 struct gsm322_cellsel *cs = arg;
2492 struct osmocom_ms *ms = cs->ms;
2493 struct gsm48_rrlayer *rr = &ms->rrlayer;
2495 LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n");
2496 if (cs->state == GSM322_C3_CAMPED_NORMALLY
2497 || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
2498 if (rr->state == GSM48_RR_ST_IDLE) {
2501 LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n");
2503 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2506 gsm322_c_event(ms, nmsg);
2509 LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n");
2511 /* be shure that nothing else is done after here
2512 * because the function call above may cause
2513 * to return from idle state and trigger cell re-sel.
2522 * handler for cell selection process
2525 /* start PLMN search */
2526 static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg)
2528 struct gsm322_cellsel *cs = &ms->cellsel;
2531 new_c_state(cs, GSM322_PLMN_SEARCH);
2533 /* mark all frequencies except our own BA to be scanned */
2534 for (i = 0; i <= 1023; i++) {
2535 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2536 | GSM322_CS_FLAG_SIGNAL
2537 | GSM322_CS_FLAG_SYSINFO);
2538 if (cs->list[i].sysinfo) {
2539 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2540 talloc_free(cs->list[i].sysinfo);
2541 cs->list[i].sysinfo = NULL;
2542 gsm322_unselect_cell(cs);
2546 /* unset selected cell */
2547 gsm322_unselect_cell(cs);
2549 /* start power scan */
2550 return gsm322_cs_powerscan(ms);
2553 /* start HPLMN search */
2554 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2556 struct gsm322_cellsel *cs = &ms->cellsel;
2559 new_c_state(cs, GSM322_HPLMN_SEARCH);
2561 /* mark all frequencies except our own BA to be scanned */
2562 for (i = 0; i <= 1023; i++) {
2563 if (i != cs->sel_arfcn
2564 && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2565 && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2566 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2567 | GSM322_CS_FLAG_SIGNAL
2568 | GSM322_CS_FLAG_SYSINFO);
2569 if (cs->list[i].sysinfo) {
2570 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2572 talloc_free(cs->list[i].sysinfo);
2573 cs->list[i].sysinfo = NULL;
2578 /* no cell selected */
2579 gsm322_unselect_cell(cs);
2581 /* start power scan */
2582 return gsm322_cs_powerscan(ms);
2585 /* start stored cell selection */
2586 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2588 struct gsm322_cellsel *cs = &ms->cellsel;
2591 new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2593 /* flag all frequencies that are in current band allocation */
2594 for (i = 0; i <= 1023; i++) {
2595 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2596 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2598 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2601 /* unset selected cell */
2602 gsm322_unselect_cell(cs);
2604 /* start power scan */
2605 return gsm322_cs_powerscan(ms);
2608 /* start noraml cell selection */
2609 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2611 struct gsm322_cellsel *cs = &ms->cellsel;
2614 /* except for stored cell selection state, we weed to rescan ?? */
2615 if (cs->state != GSM322_C2_STORED_CELL_SEL) {
2616 for (i = 0; i <= 1023; i++) {
2617 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2618 | GSM322_CS_FLAG_SIGNAL
2619 | GSM322_CS_FLAG_SYSINFO);
2620 if (cs->list[i].sysinfo) {
2621 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2623 talloc_free(cs->list[i].sysinfo);
2624 cs->list[i].sysinfo = NULL;
2629 new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2631 /* unset selected cell */
2632 gsm322_unselect_cell(cs);
2634 /* start power scan */
2635 return gsm322_cs_powerscan(ms);
2638 /* start any cell selection */
2639 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2641 struct gsm322_cellsel *cs = &ms->cellsel;
2643 /* in case we already tried any cell (re-)selection, power scan again */
2644 if (cs->state == GSM322_C0_NULL
2645 || cs->state == GSM322_C6_ANY_CELL_SEL
2646 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2649 for (i = 0; i <= 1023; i++) {
2650 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2651 | GSM322_CS_FLAG_SIGNAL
2652 | GSM322_CS_FLAG_SYSINFO);
2653 if (cs->list[i].sysinfo) {
2654 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2656 talloc_free(cs->list[i].sysinfo);
2657 cs->list[i].sysinfo = NULL;
2661 /* after re-selection, indicate no cell found */
2662 if (cs->state == GSM322_C6_ANY_CELL_SEL
2663 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2666 /* tell that we have no cell found */
2667 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2670 gsm48_mmevent_msg(ms, nmsg);
2673 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2676 cs->mcc = cs->mnc = 0;
2678 /* unset selected cell */
2679 gsm322_unselect_cell(cs);
2681 /* start power scan */
2682 return gsm322_cs_powerscan(ms);
2685 /* start noraml cell re-selection */
2686 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2688 struct gsm322_cellsel *cs = &ms->cellsel;
2690 new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2692 /* NOTE: We keep our scan info we have so far.
2693 * This may cause a skip in power scan. */
2695 /* start power scan */
2696 return gsm322_cs_powerscan(ms);
2699 /* start any cell re-selection */
2700 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2702 struct gsm322_cellsel *cs = &ms->cellsel;
2704 new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2706 /* NOTE: We keep our scan info we have so far.
2707 * This may cause a skip in power scan. */
2709 /* start power scan */
2710 return gsm322_cs_powerscan(ms);
2713 /* a suitable cell was found, so we camp normally */
2714 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2716 struct gsm322_cellsel *cs = &ms->cellsel;
2719 LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s "
2720 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2721 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2722 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2724 /* tell that we have selected a (new) cell */
2725 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2728 gsm48_mmevent_msg(ms, nmsg);
2730 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2735 /* a not suitable cell was found, so we camp on any cell */
2736 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2738 struct gsm322_cellsel *cs = &ms->cellsel;
2741 LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s "
2742 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2743 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2744 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2747 /* tell that we have selected a (new) cell */
2748 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2751 gsm48_mmevent_msg(ms, nmsg);
2753 new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2758 /* create temporary ba range with given frequency ranges */
2759 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2760 uint32_t *range, uint8_t ranges)
2762 static struct gsm322_ba_list ba;
2763 uint16_t lower, higher;
2765 memset(&ba, 0, sizeof(ba));
2768 lower = *range & 1023;
2769 higher = (*range >> 16) & 1023;
2771 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2774 ba.freq[lower >> 3] |= 1 << (lower & 7);
2775 if (lower == higher)
2777 lower = (lower + 1) & 1023;
2784 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2785 static int gsm322_cs_choose(struct osmocom_ms *ms)
2787 struct gsm322_cellsel *cs = &ms->cellsel;
2788 struct gsm48_rrlayer *rr = &ms->rrlayer;
2789 struct gsm322_ba_list *ba = NULL;
2792 /* NOTE: The call to this function is synchron to RR layer, so
2793 * we may access the BA range there.
2796 ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges);
2798 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2799 /* get and update BA of last received sysinfo 5* */
2800 ba = gsm322_cs_sysinfo_sacch(ms);
2802 LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored "
2804 ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2812 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2814 /* tell CS to start over */
2815 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2818 gsm322_c_event(ms, nmsg);
2824 /* flag all frequencies that are in current band allocation */
2825 for (i = 0; i <= 1023; i++) {
2826 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2827 if ((ba->freq[i >> 3] & (1 << (i & 7)))) {
2828 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2830 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2833 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2834 | GSM322_CS_FLAG_SIGNAL
2835 | GSM322_CS_FLAG_SYSINFO);
2836 if (cs->list[i].sysinfo) {
2837 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2838 talloc_free(cs->list[i].sysinfo);
2839 cs->list[i].sysinfo = NULL;
2843 /* unset selected cell */
2844 gsm322_unselect_cell(cs);
2846 /* start power scan */
2847 return gsm322_cs_powerscan(ms);
2850 /* start 'Choose cell' after returning to idle mode */
2851 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2853 struct gsm322_cellsel *cs = &ms->cellsel;
2854 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2856 /* After location updating, we choose the last cell */
2857 if (gm->same_cell) {
2860 if (!cs->selected) {
2861 printf("No cell selected when ret.idle, please fix!\n");
2864 cs->arfcn = cs->sel_arfcn;
2866 /* be sure to go to current camping frequency on return */
2867 LOGP(DCS, LOGL_INFO, "Selecting frequency %d. after LOC.UPD.\n",
2870 gsm322_sync_to_cell(cs);
2871 cs->si = cs->list[cs->arfcn].sysinfo;
2873 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2875 /* tell that we have selected the cell, so RR returns IDLE */
2876 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2879 gsm48_mmevent_msg(ms, nmsg);
2884 new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2886 return gsm322_cs_choose(ms);
2889 /* start 'Choose any cell' after returning to idle mode */
2890 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2892 struct gsm322_cellsel *cs = &ms->cellsel;
2894 new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2896 return gsm322_cs_choose(ms);
2899 /* a new PLMN is selected by PLMN search process */
2900 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2902 struct gsm322_cellsel *cs = &ms->cellsel;
2903 struct gsm322_plmn *plmn = &ms->plmn;
2904 struct gsm322_ba_list *ba;
2906 cs->mcc = plmn->mcc;
2907 cs->mnc = plmn->mnc;
2909 LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s "
2910 "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc),
2911 gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc),
2912 gsm_get_mnc(cs->mcc, cs->mnc));
2914 /* search for BA list */
2915 ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2918 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2919 return gsm322_c_stored_cell_sel(ms, ba);
2921 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2922 return gsm322_c_normal_cell_sel(ms, msg);
2926 /* go connected mode */
2927 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2929 struct gsm322_cellsel *cs = &ms->cellsel;
2931 /* check for error */
2934 cs->arfcn = cs->sel_arfcn;
2936 /* be sure to go to current camping frequency on return */
2937 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2939 gsm322_sync_to_cell(cs);
2940 cs->si = cs->list[cs->arfcn].sysinfo;
2945 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2947 struct gsm322_cellsel *cs = &ms->cellsel;
2949 /* check for error */
2952 cs->arfcn = cs->sel_arfcn;
2954 /* be sure to go to current camping frequency on return */
2955 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2957 gsm322_sync_to_cell(cs);
2958 cs->si = cs->list[cs->arfcn].sysinfo;
2964 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2966 struct gsm_subscriber *subscr = &ms->subscr;
2968 /* if no SIM is is MS */
2969 if (!subscr->sim_valid) {
2970 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2971 return gsm322_c_any_cell_sel(ms, msg);
2973 LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2975 /* stay in NULL state until PLMN is selected */
2984 /* state machine for automatic PLMN selection events */
2985 static struct plmnastatelist {
2988 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2989 } plmnastatelist[] = {
2990 {SBIT(GSM322_A0_NULL),
2991 GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2993 /* special case for full search */
2994 {SBIT(GSM322_A0_NULL),
2995 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn},
2998 GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
3000 {SBIT(GSM322_A0_NULL) | SBIT(GSM322_A6_NO_SIM),
3001 GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
3004 GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
3007 GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
3010 GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
3012 {SBIT(GSM322_A1_TRYING_RPLMN),
3013 GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
3015 {SBIT(GSM322_A1_TRYING_RPLMN),
3016 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn},
3018 {SBIT(GSM322_A1_TRYING_RPLMN),
3019 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
3021 {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
3022 GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
3024 {SBIT(GSM322_A2_ON_PLMN),
3025 GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
3027 {SBIT(GSM322_A2_ON_PLMN),
3028 GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start},
3030 {SBIT(GSM322_A2_ON_PLMN),
3031 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
3033 {SBIT(GSM322_A2_ON_PLMN),
3034 GSM322_EVENT_USER_RESEL, gsm322_a_user_resel},
3036 {SBIT(GSM322_A3_TRYING_PLMN),
3037 GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
3039 {SBIT(GSM322_A3_TRYING_PLMN),
3040 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn},
3042 {SBIT(GSM322_A3_TRYING_PLMN),
3043 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
3045 {SBIT(GSM322_A5_HPLMN_SEARCH),
3046 GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn},
3048 {SBIT(GSM322_A5_HPLMN_SEARCH),
3049 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn},
3051 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
3052 GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
3054 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
3055 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_plmn_avail},
3058 GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
3061 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3064 #define PLMNASLLEN \
3065 (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
3067 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
3069 struct gsm322_plmn *plmn = &ms->plmn;
3070 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3071 int msg_type = gm->msg_type;
3075 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
3076 "selection in state '%s'\n", ms->name, get_event_name(msg_type),
3077 plmn_a_state_names[plmn->state]);
3078 /* find function for current state and message */
3079 for (i = 0; i < PLMNASLLEN; i++)
3080 if ((msg_type == plmnastatelist[i].type)
3081 && ((1 << plmn->state) & plmnastatelist[i].states))
3083 if (i == PLMNASLLEN) {
3084 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3088 rc = plmnastatelist[i].rout(ms, msg);
3093 /* state machine for manual PLMN selection events */
3094 static struct plmnmstatelist {
3097 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3098 } plmnmstatelist[] = {
3099 {SBIT(GSM322_M0_NULL),
3100 GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
3102 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) |
3103 SBIT(GSM322_M2_ON_PLMN),
3104 GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns},
3107 GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
3109 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M5_NO_SIM),
3110 GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
3113 GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
3116 GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
3118 {SBIT(GSM322_M1_TRYING_RPLMN),
3119 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3121 {SBIT(GSM322_M1_TRYING_RPLMN),
3122 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3124 {SBIT(GSM322_M1_TRYING_RPLMN),
3125 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3127 {SBIT(GSM322_M1_TRYING_RPLMN),
3128 GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
3130 {SBIT(GSM322_M2_ON_PLMN),
3131 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3133 {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
3134 SBIT(GSM322_M4_TRYING_PLMN),
3135 GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
3137 {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN),
3138 GSM322_EVENT_USER_RESEL, gsm322_m_user_resel},
3140 {SBIT(GSM322_M3_NOT_ON_PLMN),
3141 GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
3143 {SBIT(GSM322_M3_NOT_ON_PLMN),
3144 GSM322_EVENT_CHOOSE_PLMN, gsm322_m_choose_plmn},
3146 {SBIT(GSM322_M4_TRYING_PLMN),
3147 GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
3149 {SBIT(GSM322_M4_TRYING_PLMN),
3150 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3152 {SBIT(GSM322_M4_TRYING_PLMN),
3153 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3155 {SBIT(GSM322_M4_TRYING_PLMN),
3156 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3159 GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
3162 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3165 #define PLMNMSLLEN \
3166 (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
3168 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
3170 struct gsm322_plmn *plmn = &ms->plmn;
3171 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3172 int msg_type = gm->msg_type;
3176 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
3177 "in state '%s'\n", ms->name, get_event_name(msg_type),
3178 plmn_m_state_names[plmn->state]);
3179 /* find function for current state and message */
3180 for (i = 0; i < PLMNMSLLEN; i++)
3181 if ((msg_type == plmnmstatelist[i].type)
3182 && ((1 << plmn->state) & plmnmstatelist[i].states))
3184 if (i == PLMNMSLLEN) {
3185 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3189 rc = plmnmstatelist[i].rout(ms, msg);
3194 /* dequeue GSM 03.22 PLMN events */
3195 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
3197 struct gsm322_plmn *plmn = &ms->plmn;
3201 while ((msg = msgb_dequeue(&plmn->event_queue))) {
3202 /* send event to PLMN select process */
3203 if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
3204 gsm322_a_event(ms, msg);
3206 gsm322_m_event(ms, msg);
3208 work = 1; /* work done */
3214 /* state machine for channel selection events */
3215 static struct cellselstatelist {
3218 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3219 } cellselstatelist[] = {
3221 GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
3224 GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
3227 GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
3230 GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search},
3232 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3233 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
3234 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
3236 {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3237 SBIT(GSM322_C8_ANY_CELL_RESEL),
3238 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
3240 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3241 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3242 SBIT(GSM322_C0_NULL),
3243 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
3245 {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3246 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
3247 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
3249 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3250 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
3252 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3253 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
3255 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3256 GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
3258 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3259 GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
3261 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3262 GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
3264 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3265 GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
3267 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3268 GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
3270 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3271 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3272 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3273 SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH),
3274 GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
3276 {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
3277 GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
3279 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3280 GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search},
3283 #define CELLSELSLLEN \
3284 (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
3286 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
3288 struct gsm322_cellsel *cs = &ms->cellsel;
3289 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3290 int msg_type = gm->msg_type;
3294 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
3295 "'%s'\n", ms->name, get_event_name(msg_type),
3296 cs_state_names[cs->state]);
3297 /* find function for current state and message */
3298 for (i = 0; i < CELLSELSLLEN; i++)
3299 if ((msg_type == cellselstatelist[i].type)
3300 && ((1 << cs->state) & cellselstatelist[i].states))
3302 if (i == CELLSELSLLEN) {
3303 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
3307 rc = cellselstatelist[i].rout(ms, msg);
3312 /* dequeue GSM 03.22 cell selection events */
3313 int gsm322_cs_dequeue(struct osmocom_ms *ms)
3315 struct gsm322_cellsel *cs = &ms->cellsel;
3319 while ((msg = msgb_dequeue(&cs->event_queue))) {
3320 /* send event to cell selection process */
3321 gsm322_c_event(ms, msg);
3323 work = 1; /* work done */
3333 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
3335 struct gsm322_plmn *plmn = &ms->plmn;
3336 struct gsm322_plmn_list *temp;
3338 printf("MCC |MNC |allowed|rx-lev\n");
3339 printf("-------+-------+-------+-------\n");
3340 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
3341 printf("%s |%s%s |%s |%s\n", gsm_print_mcc(temp->mcc),
3342 gsm_print_mnc(temp->mnc),
3343 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3344 (temp->cause) ? "no ":"yes",
3345 gsm_print_rxlev(temp->rxlev));
3351 int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
3352 void (*print)(void *, const char *, ...), void *priv)
3355 struct gsm48_sysinfo *s;
3357 print(priv, "arfcn |MCC |MNC |LAC |cell ID|forb.LA|prio |"
3358 "min-db |max-pwr|rx-lev\n");
3359 print(priv, "-------+-------+-------+-------+-------+-------+-------+"
3360 "-------+-------+-------\n");
3361 for (i = 0; i <= 1023; i++) {
3362 s = cs->list[i].sysinfo;
3363 if (!s || !(cs->list[i].flags & flags))
3365 print(priv, "%4d |", i);
3366 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
3367 print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc),
3368 gsm_print_mnc(s->mnc),
3369 ((s->mnc & 0x00f) == 0x00f) ? " ":"");
3370 print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id);
3371 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
3372 print(priv, "yes |");
3374 print(priv, "no |");
3375 if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
3376 print(priv, "barred |");
3378 if (cs->list[i].sysinfo->cell_barr)
3379 print(priv, "low |");
3381 print(priv, "normal |");
3383 print(priv, "%4d |%4d |%s\n", s->rxlev_acc_min_db,
3384 s->ms_txpwr_max_cch,
3385 gsm_print_rxlev(cs->list[i].rxlev));
3387 print(priv, "n/a |n/a |n/a |n/a |n/a |"
3395 int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
3396 void (*print)(void *, const char *, ...), void *priv)
3398 struct gsm322_plmn *plmn = &ms->plmn;
3399 struct gsm322_la_list *temp;
3401 print(priv, "MCC |MNC |LAC |cause\n");
3402 print(priv, "-------+-------+-------+-------\n");
3403 llist_for_each_entry(temp, &plmn->forbidden_la, entry)
3404 print(priv, "%s |%s%s |0x%04x |#%d\n",
3405 gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
3406 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3407 temp->lac, temp->cause);
3412 int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
3413 void (*print)(void *, const char *, ...), void *priv)
3415 struct gsm322_ba_list *ba;
3418 llist_for_each_entry(ba, &cs->ba_list, entry) {
3419 if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc))
3421 print(priv, "Band Allocation of network: MCC %s MNC %s "
3422 "(%s, %s)\n", gsm_print_mcc(ba->mcc),
3423 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3424 gsm_get_mnc(ba->mcc, ba->mnc));
3425 for (i = 0; i <= 1023; i++) {
3426 if ((ba->freq[i >> 3] & (1 << (i & 7))))
3427 print(priv, " %d", i);
3439 int gsm322_init(struct osmocom_ms *ms)
3441 struct gsm322_plmn *plmn = &ms->plmn;
3442 struct gsm322_cellsel *cs = &ms->cellsel;
3446 struct gsm322_ba_list *ba;
3449 LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
3450 LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
3452 memset(plmn, 0, sizeof(*plmn));
3453 memset(cs, 0, sizeof(*cs));
3457 /* set initial state */
3460 ms->settings.plmn_mode = PLMN_MODE_AUTO;
3463 INIT_LLIST_HEAD(&plmn->event_queue);
3464 INIT_LLIST_HEAD(&cs->event_queue);
3465 INIT_LLIST_HEAD(&plmn->sorted_plmn);
3466 INIT_LLIST_HEAD(&plmn->forbidden_la);
3467 INIT_LLIST_HEAD(&cs->ba_list);
3469 /* set supported frequencies in cell selection list */
3470 for (i = 0; i <= 1023; i++)
3471 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
3472 cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
3475 sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3476 fp = fopen(filename, "r");
3481 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
3484 rc = fread(buf, 4, 1, fp);
3489 ba->mcc = (buf[0] << 8) | buf[1];
3490 ba->mnc = (buf[2] << 8) | buf[3];
3491 rc = fread(ba->freq, sizeof(ba->freq), 1, fp);
3496 llist_add_tail(&ba->entry, &cs->ba_list);
3497 LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s "
3498 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3499 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3500 gsm_get_mnc(ba->mcc, ba->mnc));
3504 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
3506 register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3511 int gsm322_exit(struct osmocom_ms *ms)
3513 struct gsm322_plmn *plmn = &ms->plmn;
3514 struct gsm322_cellsel *cs = &ms->cellsel;
3515 struct llist_head *lh, *lh2;
3519 struct gsm322_ba_list *ba;
3523 LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
3524 LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
3526 unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3528 /* stop cell selection process (if any) */
3529 new_c_state(cs, GSM322_C0_NULL);
3533 stop_plmn_timer(plmn);
3536 for (i = 0; i <= 1023; i++) {
3537 if (cs->list[i].sysinfo) {
3538 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
3539 talloc_free(cs->list[i].sysinfo);
3540 cs->list[i].sysinfo = NULL;
3542 cs->list[i].flags = 0;
3546 sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3547 fp = fopen(filename, "w");
3551 llist_for_each_entry(ba, &cs->ba_list, entry) {
3552 buf[0] = ba->mcc >> 8;
3553 buf[1] = ba->mcc & 0xff;
3554 buf[2] = ba->mnc >> 8;
3555 buf[3] = ba->mnc & 0xff;
3556 rc = fwrite(buf, 4, 1, fp);
3557 rc = fwrite(ba->freq, sizeof(ba->freq), 1, fp);
3558 LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s "
3559 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3560 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3561 gsm_get_mnc(ba->mcc, ba->mnc));
3565 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
3568 while ((msg = msgb_dequeue(&plmn->event_queue)))
3570 while ((msg = msgb_dequeue(&cs->event_queue)))
3572 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
3576 llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
3580 llist_for_each_safe(lh, lh2, &cs->ba_list) {