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);
48 #define SKIP_MAX_PER_BAND
57 /* Cell selection process
59 * The process depends on states and events (finites state machine).
61 * During states of cell selection or cell re-selection, the search for a cell
62 * is performed in two steps:
64 * 1. Measurement of received level of all relevant frequencies (rx-lev)
66 * 2. Receive system information messages of all relevant frequencies
68 * During this process, the results are stored in a list of all frequencies.
69 * This list is checked whenever a cell is selected. It depends on the results
70 * if the cell is 'suitable' and 'allowable' to 'camp' on.
72 * This list is also used to generate a list of available networks.
76 * - cs->list[0..1023].xxx for each cell, where
77 * - flags and rxlev are used to store outcome of cell scanning process
78 * - sysinfo pointing to sysinfo memory, allocated temporarily
79 * - cs->selected and cs->sel_* states of the current / last selected cell.
82 * There is a special state: GSM322_PLMN_SEARCH
83 * It is used to search for all cells, to find the HPLMN. This is triggered
84 * by a timer. Also it is used before selecting PLMN from list.
88 /* PLMN selection process
90 * The PLMN (Public Land Mobile Network = Operator's Network) has two different
97 * The process depends on states and events (finites state machine).
101 /* File format of BA list:
106 * where frequency 0 is bit 0 of first byte
108 * If not end-of-file, the next BA list is stored.
113 * * subscr->plmn_list
115 * The "PLMN Selector list" stores prefered networks to select during PLMN
116 * search process. This list is also stored in the SIM.
120 * The "forbidden PLMNs" list stores all networks that rejected us. The stored
121 * network will not be used when searching PLMN automatically. This list is
122 * also stored din the SIM.
124 * * plmn->forbidden_la
126 * The "forbidden LAs for roaming" list stores all location areas where roaming
131 * This list stores measurements and cell informations during cell selection
132 * process. It can be used to speed up repeated cell selection.
136 * This list stores a map of frequencies used for a PLMN. If this lists exists
137 * for a PLMN, it helps to speedup cell scan process.
139 * * plmn->sorted_plmn
141 * This list is generated whenever a PLMN search is started and a list of PLMNs
142 * is required. It consists of home PLMN, PLMN Selector list, and PLMNs found
143 * during scan process.
150 static const struct value_string gsm322_event_names[] = {
151 { GSM322_EVENT_SWITCH_ON, "EVENT_SWITCH_ON" },
152 { GSM322_EVENT_SWITCH_OFF, "EVENT_SWITCH_OFF" },
153 { GSM322_EVENT_SIM_INSERT, "EVENT_SIM_INSERT" },
154 { GSM322_EVENT_SIM_REMOVE, "EVENT_SIM_REMOVE" },
155 { GSM322_EVENT_REG_FAILED, "EVENT_REG_FAILED" },
156 { GSM322_EVENT_ROAMING_NA, "EVENT_ROAMING_NA" },
157 { GSM322_EVENT_INVALID_SIM, "EVENT_INVALID_SIM" },
158 { GSM322_EVENT_REG_SUCCESS, "EVENT_REG_SUCCESS" },
159 { GSM322_EVENT_NEW_PLMN, "EVENT_NEW_PLMN" },
160 { GSM322_EVENT_ON_PLMN, "EVENT_ON_PLMN" },
161 { GSM322_EVENT_PLMN_SEARCH_START,"EVENT_PLMN_SEARCH_START" },
162 { GSM322_EVENT_PLMN_SEARCH_END, "EVENT_PLMN_SEARCH_END" },
163 { GSM322_EVENT_USER_RESEL, "EVENT_USER_RESEL" },
164 { GSM322_EVENT_PLMN_AVAIL, "EVENT_PLMN_AVAIL" },
165 { GSM322_EVENT_CHOOSE_PLMN, "EVENT_CHOOSE_PLMN" },
166 { GSM322_EVENT_SEL_MANUAL, "EVENT_SEL_MANUAL" },
167 { GSM322_EVENT_SEL_AUTO, "EVENT_SEL_AUTO" },
168 { GSM322_EVENT_CELL_FOUND, "EVENT_CELL_FOUND" },
169 { GSM322_EVENT_NO_CELL_FOUND, "EVENT_NO_CELL_FOUND" },
170 { GSM322_EVENT_LEAVE_IDLE, "EVENT_LEAVE_IDLE" },
171 { GSM322_EVENT_RET_IDLE, "EVENT_RET_IDLE" },
172 { GSM322_EVENT_CELL_RESEL, "EVENT_CELL_RESEL" },
173 { GSM322_EVENT_SYSINFO, "EVENT_SYSINFO" },
174 { GSM322_EVENT_HPLMN_SEARCH, "EVENT_HPLMN_SEARCH" },
178 const char *get_event_name(int value)
180 return get_value_string(gsm322_event_names, value);
184 /* allocate a 03.22 event message */
185 struct msgb *gsm322_msgb_alloc(int msg_type)
188 struct gsm322_msg *gm;
190 msg = msgb_alloc_headroom(sizeof(*gm), 0, "GSM 03.22 event");
194 gm = (struct gsm322_msg *)msgb_put(msg, sizeof(*gm));
195 gm->msg_type = msg_type;
200 /* queue PLMN selection message */
201 int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
203 struct gsm322_plmn *plmn = &ms->plmn;
205 msgb_enqueue(&plmn->event_queue, msg);
210 /* queue cell selection message */
211 int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
213 struct gsm322_cellsel *cs = &ms->cellsel;
215 msgb_enqueue(&cs->event_queue, msg);
224 char *gsm_print_rxlev(uint8_t rxlev)
226 static char string[5];
231 sprintf(string, "-%d", 110 - rxlev);
235 static int gsm322_sync_to_cell(struct gsm322_cellsel *cs)
237 struct osmocom_ms *ms = cs->ms;
238 struct gsm48_sysinfo *s = cs->si;
239 struct rx_meas_stat *meas = &ms->meas;
241 cs->ccch_state = GSM322_CCCH_ST_INIT;
243 if (s->ccch_conf == 1) {
244 LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%d rxlev=%s "
245 "(Sysinfo, ccch mode COMB)\n", cs->arfcn,
246 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
247 cs->ccch_mode = CCCH_MODE_COMBINED;
249 LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%d rxlev=%s "
250 "(Sysinfo, ccch mode NON-COMB)\n", cs->arfcn,
251 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
252 cs->ccch_mode = CCCH_MODE_NON_COMBINED;
255 LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%d rxlev=%s (No sysinfo "
256 "yet, ccch mode NONE)\n", cs->arfcn,
257 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
258 cs->ccch_mode = CCCH_MODE_NONE;
261 meas->frames = meas->snr = meas->berr = meas->rxlev = 0;
263 l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
264 return l1ctl_tx_fbsb_req(ms, cs->arfcn,
265 L1CTL_FBSB_F_FB01SB, 100, 0,
269 static void gsm322_unselect_cell(struct gsm322_cellsel *cs)
273 cs->si->si5 = 0; /* unset SI5* */
275 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
276 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
279 /* print to DCS logging */
280 static void print_dcs(void *priv, const char *fmt, ...)
286 vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
287 buffer[sizeof(buffer) - 1] = '\0';
291 // LOGP(DCS, LOGL_INFO, "%s", buffer);
292 printf("%s", buffer);
295 /* del forbidden LA */
296 int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
297 uint16_t mnc, uint16_t lac)
299 struct gsm322_plmn *plmn = &ms->plmn;
300 struct gsm322_la_list *la;
302 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
303 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
304 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
305 "LAs (mcc=%s, mnc=%s, lac=%04x)\n",
306 gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac);
307 llist_del(&la->entry);
316 /* add forbidden LA */
317 int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
318 uint16_t mnc, uint16_t lac, uint8_t cause)
320 struct gsm322_plmn *plmn = &ms->plmn;
321 struct gsm322_la_list *la;
323 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
324 "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc),
325 gsm_print_mnc(mnc), lac);
326 la = talloc_zero(l23_ctx, struct gsm322_la_list);
333 llist_add_tail(&la->entry, &plmn->forbidden_la);
338 /* search forbidden LA */
339 int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
342 struct gsm322_plmn *plmn = &ms->plmn;
343 struct gsm322_la_list *la;
345 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
346 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
353 /* search for PLMN in all BA lists */
354 static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
355 uint16_t mcc, uint16_t mnc)
357 struct gsm322_ba_list *ba, *ba_found = NULL;
359 /* search for BA list */
360 llist_for_each_entry(ba, &cs->ba_list, entry) {
371 /* search available PLMN */
372 int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
376 for (i = 0; i <= 1023; i++) {
377 if (cs->list[i].sysinfo
378 && cs->list[i].sysinfo->mcc == mcc
379 && cs->list[i].sysinfo->mnc == mnc)
386 /* search available HPLMN */
387 int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi)
391 for (i = 0; i <= 1023; i++) {
392 if (cs->list[i].sysinfo
393 && gsm_match_mnc(cs->list[i].sysinfo->mcc,
394 cs->list[i].sysinfo->mnc, imsi))
401 /* del forbidden LA */
406 /*plmn search timer event */
407 static void plmn_timer_timeout(void *arg)
409 struct gsm322_plmn *plmn = arg;
412 LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
414 /* indicate PLMN selection T timeout */
415 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
418 gsm322_plmn_sendmsg(plmn->ms, nmsg);
421 /* start plmn search timer */
422 static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
424 LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
426 plmn->timer.cb = plmn_timer_timeout;
427 plmn->timer.data = plmn;
428 bsc_schedule_timer(&plmn->timer, secs, 0);
431 /* stop plmn search timer */
432 static void stop_plmn_timer(struct gsm322_plmn *plmn)
434 if (bsc_timer_pending(&plmn->timer)) {
435 LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
436 bsc_del_timer(&plmn->timer);
440 /* start cell selection timer */
441 void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
443 LOGP(DCS, LOGL_DEBUG, "Starting CS timer with %d seconds.\n", sec);
444 cs->timer.cb = gsm322_cs_timeout;
446 bsc_schedule_timer(&cs->timer, sec, micro);
449 /* stop cell selection timer */
450 static void stop_cs_timer(struct gsm322_cellsel *cs)
452 if (bsc_timer_pending(&cs->timer)) {
453 LOGP(DCS, LOGL_DEBUG, "stopping pending CS timer.\n");
454 bsc_del_timer(&cs->timer);
462 const char *plmn_a_state_names[] = {
467 "A4 wait for PLMN to appear",
472 const char *plmn_m_state_names[] = {
481 const char *cs_state_names[] = {
483 "C1 normal cell selection",
484 "C2 stored cell selection",
485 "C3 camped normally",
486 "C4 normal cell re-selection",
488 "C6 any cell selection",
489 "C7 camped on any cell",
490 "C8 any cell re-selection",
491 "C9 choose any cell",
497 /* new automatic PLMN search state */
498 static void new_a_state(struct gsm322_plmn *plmn, int state)
500 if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) {
501 LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
505 stop_plmn_timer(plmn);
507 if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
510 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
511 plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
516 /* new manual PLMN search state */
517 static void new_m_state(struct gsm322_plmn *plmn, int state)
519 if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
520 LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
524 if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
527 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
528 plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
533 /* new Cell selection state */
534 static void new_c_state(struct gsm322_cellsel *cs, int state)
536 if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
539 LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n",
540 cs_state_names[cs->state], cs_state_names[state]);
542 /* stop cell selection timer, if running */
545 /* stop scanning of power measurement */
547 LOGP(DCS, LOGL_INFO, "changing state while power scanning\n");
548 l1ctl_tx_reset_req(cs->ms, L1CTL_RES_T_FULL);
559 /* 4.4.3 create sorted list of PLMN
561 * the source of entries are
564 * - entries found in the SIM's PLMN Selector list
565 * - scanned PLMNs above -85 dB (random order)
566 * - scanned PLMNs below or equal -85 (by received level)
570 * The list only includes networks found at last scan.
572 * The list always contains HPLMN if available, even if not used by PLMN
573 * search process at some conditions.
575 * The list contains all PLMNs even if not allowed, so entries have to be
576 * removed when selecting from the list. (In case we use manual cell selection,
577 * we need to provide non-allowed networks also.)
579 static int gsm322_sort_list(struct osmocom_ms *ms)
581 struct gsm322_plmn *plmn = &ms->plmn;
582 struct gsm322_cellsel *cs = &ms->cellsel;
583 struct gsm_subscriber *subscr = &ms->subscr;
584 struct gsm_sub_plmn_list *sim_entry;
585 struct gsm_sub_plmn_na *na_entry;
586 struct llist_head temp_list;
587 struct gsm322_plmn_list *temp, *found;
588 struct llist_head *lh, *lh2;
589 int i, entries, move;
593 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
598 /* Create a temporary list of all networks */
599 INIT_LLIST_HEAD(&temp_list);
600 for (i = 0; i <= 1023; i++) {
601 if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)
602 || !cs->list[i].sysinfo)
605 /* search if network has multiple cells */
607 llist_for_each_entry(temp, &temp_list, entry) {
608 if (temp->mcc == cs->list[i].sysinfo->mcc
609 && temp->mnc == cs->list[i].sysinfo->mnc) {
614 /* update or create */
616 if (cs->list[i].rxlev > found->rxlev)
617 found->rxlev = cs->list[i].rxlev;
619 temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
622 temp->mcc = cs->list[i].sysinfo->mcc;
623 temp->mnc = cs->list[i].sysinfo->mnc;
624 temp->rxlev = cs->list[i].rxlev;
625 llist_add_tail(&temp->entry, &temp_list);
629 /* move Home PLMN, if in list, else add it */
630 if (subscr->sim_valid) {
632 llist_for_each_entry(temp, &temp_list, entry) {
633 if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) {
640 llist_del(&found->entry);
641 llist_add_tail(&found->entry, &plmn->sorted_plmn);
645 /* move entries if in SIM's PLMN Selector list */
646 llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
648 llist_for_each_entry(temp, &temp_list, entry) {
649 if (temp->mcc == sim_entry->mcc
650 && temp->mnc == sim_entry->mnc) {
656 llist_del(&found->entry);
657 llist_add_tail(&found->entry, &plmn->sorted_plmn);
661 /* move PLMN above -85 dBm in random order */
663 llist_for_each_entry(temp, &temp_list, entry) {
664 if (rxlev2dbm(temp->rxlev) > -85)
668 move = random() % entries;
670 llist_for_each_entry(temp, &temp_list, entry) {
671 if (rxlev2dbm(temp->rxlev) > -85) {
673 llist_del(&temp->entry);
674 llist_add_tail(&temp->entry,
684 /* move ohter PLMN in decreasing order */
687 llist_for_each_entry(temp, &temp_list, entry) {
689 || temp->rxlev > search) {
690 search = temp->rxlev;
696 llist_del(&found->entry);
697 llist_add_tail(&found->entry, &plmn->sorted_plmn);
700 /* mark forbidden PLMNs, if in list of forbidden networks */
702 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
703 llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
704 if (temp->mcc == na_entry->mcc
705 && temp->mnc == na_entry->mnc) {
706 temp->cause = na_entry->cause;
710 LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. "
711 "(%02d: mcc %s mnc %s allowed %s rx-lev %s)\n",
712 i, gsm_print_mcc(temp->mcc),
713 gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes",
714 gsm_print_rxlev(temp->rxlev));
718 gsm322_dump_sorted_plmn(ms);
724 * handler for automatic search
727 /* go On PLMN state */
728 static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
730 struct gsm322_plmn *plmn = &ms->plmn;
731 struct gsm_subscriber *subscr = &ms->subscr;
733 new_a_state(plmn, GSM322_A2_ON_PLMN);
735 /* start timer, if on VPLMN of home country OR special case */
736 if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi)
737 && (subscr->always_search_hplmn
738 || gsm_match_mcc(plmn->mcc, subscr->imsi))
739 && subscr->sim_valid && subscr->t6m_hplmn)
740 start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
742 stop_plmn_timer(plmn);
747 /* indicate selected PLMN */
748 static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
750 struct gsm322_plmn *plmn = &ms->plmn;
752 vty_notify(ms, NULL);
753 vty_notify(ms, "Selected Network: %s, %s\n",
754 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
756 return gsm322_a_go_on_plmn(ms, msg);
759 /* no (more) PLMN in list */
760 static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
762 struct gsm322_plmn *plmn = &ms->plmn;
763 struct gsm322_cellsel *cs = &ms->cellsel;
767 /* any allowable PLMN available? */
768 plmn->mcc = plmn->mnc = 0;
769 found = gsm322_cs_select(ms, 0, 1);
771 /* if no PLMN in list */
773 LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable.\n");
775 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
778 /* we must forward this, otherwhise "Any cell selection"
779 * will not start automatically.
781 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
784 gsm322_cs_sendmsg(ms, nmsg);
786 LOGP(DPLMN, LOGL_INFO, "Trigger full PLMN search.\n");
788 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
791 gsm322_cs_sendmsg(ms, nmsg);
796 /* select first PLMN in list */
797 plmn->mcc = cs->list[found].sysinfo->mcc;
798 plmn->mnc = cs->list[found].sysinfo->mnc;
800 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n",
801 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
802 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
804 /* indicate New PLMN */
805 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
808 gsm322_cs_sendmsg(ms, nmsg);
811 return gsm322_a_indicate_selected(ms, msg);
814 /* select first PLMN in list */
815 static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
817 struct gsm322_plmn *plmn = &ms->plmn;
818 struct gsm_subscriber *subscr = &ms->subscr;
820 struct gsm322_plmn_list *plmn_entry;
821 struct gsm322_plmn_list *plmn_first = NULL;
825 gsm322_sort_list(ms);
827 /* select first entry */
829 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
830 /* if last selected PLMN was HPLMN, we skip that */
831 if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc,
833 && plmn_entry->mcc == plmn->mcc
834 && plmn_entry->mnc == plmn->mnc) {
835 LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was "
836 "previously selected.\n");
840 /* select first allowed network */
841 if (!plmn_entry->cause) {
842 plmn_first = plmn_entry;
845 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
846 "because it is not allowed (cause %d).\n", i,
847 gsm_print_mcc(plmn_entry->mcc),
848 gsm_print_mnc(plmn_entry->mnc),
854 /* if no PLMN in list */
856 LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
857 gsm322_a_no_more_plmn(ms, msg);
862 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
863 "mnc=%s %s, %s)\n", plmn->plmn_curr,
864 gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc),
865 gsm_get_mcc(plmn_first->mcc),
866 gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
868 /* set current network */
869 plmn->mcc = plmn_first->mcc;
870 plmn->mnc = plmn_first->mnc;
872 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
874 /* indicate New PLMN */
875 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
878 gsm322_cs_sendmsg(ms, nmsg);
883 /* select next PLMN in list */
884 static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
886 struct gsm322_plmn *plmn = &ms->plmn;
888 struct gsm322_plmn_list *plmn_entry;
889 struct gsm322_plmn_list *plmn_next = NULL;
892 /* select next entry from list */
894 ii = plmn->plmn_curr + 1;
895 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
896 /* skip previously selected networks */
901 /* select next allowed network */
902 if (!plmn_entry->cause) {
903 plmn_next = plmn_entry;
906 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
907 "because it is not allowed (cause %d).\n", i,
908 gsm_print_mcc(plmn_entry->mcc),
909 gsm_print_mnc(plmn_entry->mnc),
915 /* if no more PLMN in list */
917 LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
918 gsm322_a_no_more_plmn(ms, msg);
922 /* set next network */
923 plmn->mcc = plmn_next->mcc;
924 plmn->mnc = plmn_next->mnc;
926 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
927 "mnc=%s %s, %s)\n", plmn->plmn_curr,
928 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
929 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
931 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
933 /* indicate New PLMN */
934 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
937 gsm322_cs_sendmsg(ms, nmsg);
942 /* User re-selection event */
943 static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg)
945 struct gsm322_plmn *plmn = &ms->plmn;
946 struct gsm_subscriber *subscr = &ms->subscr;
947 struct gsm48_rrlayer *rr = &ms->rrlayer;
948 struct gsm322_plmn_list *plmn_entry;
949 struct gsm322_plmn_list *plmn_found = NULL;
951 if (!subscr->sim_valid) {
955 /* try again later, if not idle */
956 if (rr->state != GSM48_RR_ST_IDLE) {
957 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
962 /* search current PLMN in list */
963 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
964 if (plmn_entry->mcc == plmn->mcc
965 && plmn_entry->mnc == plmn->mnc)
966 plmn_found = plmn_entry;
970 /* abort if list is empty */
972 LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
976 LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
977 "and restarting PLMN search process.\n");
979 /* move entry to end of list */
980 llist_del(&plmn_found->entry);
981 llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
983 /* select first PLMN in list */
984 return gsm322_a_sel_first_plmn(ms, msg);
987 /* PLMN becomes available */
988 static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
990 struct gsm322_plmn *plmn = &ms->plmn;
991 struct gsm_subscriber *subscr = &ms->subscr;
992 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
994 if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
995 && subscr->plmn_mnc == gm->mnc) {
999 LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n");
1000 return gsm322_a_go_on_plmn(ms, msg);
1002 /* select first PLMN in list */
1003 LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
1004 "search process.\n");
1005 return gsm322_a_sel_first_plmn(ms, msg);
1009 /* loss of radio coverage */
1010 static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
1012 struct gsm322_plmn *plmn = &ms->plmn;
1013 struct gsm322_cellsel *cs = &ms->cellsel;
1017 /* any PLMN available */
1018 found = gsm322_cs_select(ms, 0, 1);
1020 /* if PLMN in list */
1022 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s "
1023 "%s, %s)\n", gsm_print_mcc(
1024 cs->list[found].sysinfo->mcc),
1025 gsm_print_mnc(cs->list[found].sysinfo->mnc),
1026 gsm_get_mcc(cs->list[found].sysinfo->mcc),
1027 gsm_get_mnc(cs->list[found].sysinfo->mcc,
1028 cs->list[found].sysinfo->mnc));
1029 return gsm322_a_sel_first_plmn(ms, msg);
1032 LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
1034 plmn->mcc = plmn->mnc = 0;
1036 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
1038 /* Tell cell selection process to handle "no cell found". */
1039 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1042 gsm322_cs_sendmsg(ms, nmsg);
1047 /* MS is switched on OR SIM is inserted OR removed */
1048 static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1050 struct gsm_subscriber *subscr = &ms->subscr;
1051 struct gsm322_plmn *plmn = &ms->plmn;
1054 if (!subscr->sim_valid) {
1055 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1056 LOGP(DPLMN, LOGL_INFO, "SIM is removed\n");
1057 new_a_state(plmn, GSM322_A6_NO_SIM);
1062 /* if there is a registered PLMN */
1063 if (subscr->plmn_valid) {
1064 /* select the registered PLMN */
1065 plmn->mcc = subscr->plmn_mcc;
1066 plmn->mnc = subscr->plmn_mnc;
1068 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1069 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1070 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1071 gsm_get_mnc(plmn->mcc, plmn->mnc));
1072 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1073 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1074 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1075 gsm_get_mnc(plmn->mcc, plmn->mnc));
1077 new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
1079 /* indicate New PLMN */
1080 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1083 gsm322_cs_sendmsg(ms, nmsg);
1088 /* initiate search at cell selection */
1089 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1090 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1092 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1095 gsm322_cs_sendmsg(ms, nmsg);
1100 /* MS is switched off */
1101 static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1103 struct gsm322_plmn *plmn = &ms->plmn;
1105 new_a_state(plmn, GSM322_A0_NULL);
1110 static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1112 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1116 /* SIM is removed */
1117 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1121 /* indicate SIM remove to cell selection process */
1122 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1125 gsm322_cs_sendmsg(ms, nmsg);
1127 return gsm322_a_switch_on(ms, msg);
1130 /* location update response: "Roaming not allowed" */
1131 static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
1133 /* store in list of forbidden LAs is done in gsm48* */
1135 return gsm322_a_sel_first_plmn(ms, msg);
1138 /* On VPLMN of home country and timeout occurs */
1139 static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg)
1141 struct gsm48_rrlayer *rr = &ms->rrlayer;
1142 struct gsm322_plmn *plmn = &ms->plmn;
1143 struct gsm322_cellsel *cs = &ms->cellsel;
1146 /* try again later, if not idle and not camping */
1147 if (rr->state != GSM48_RR_ST_IDLE
1148 || cs->state != GSM322_C3_CAMPED_NORMALLY) {
1149 LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n");
1150 start_plmn_timer(plmn, 60);
1155 new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
1157 /* initiate search at cell selection */
1158 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1161 gsm322_cs_sendmsg(ms, nmsg);
1166 /* manual mode selected */
1167 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
1171 /* restart state machine */
1172 gsm322_a_switch_off(ms, msg);
1173 ms->settings.plmn_mode = PLMN_MODE_MANUAL;
1174 gsm322_m_switch_on(ms, msg);
1176 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1179 gsm48_mmevent_msg(ms, nmsg);
1185 * handler for manual search
1188 /* display PLMNs and to Not on PLMN */
1189 static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
1191 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1192 int msg_type = gm->msg_type;
1193 struct gsm322_plmn *plmn = &ms->plmn;
1194 struct gsm_sub_plmn_list *temp;
1197 gsm322_sort_list(ms);
1199 vty_notify(ms, NULL);
1201 case GSM322_EVENT_REG_FAILED:
1202 vty_notify(ms, "Failed to register to network %s, %s "
1204 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1205 gsm_get_mcc(plmn->mcc),
1206 gsm_get_mnc(plmn->mcc, plmn->mnc));
1208 case GSM322_EVENT_NO_CELL_FOUND:
1209 vty_notify(ms, "No cell found for network %s, %s "
1211 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1212 gsm_get_mcc(plmn->mcc),
1213 gsm_get_mnc(plmn->mcc, plmn->mnc));
1215 case GSM322_EVENT_ROAMING_NA:
1216 vty_notify(ms, "Roaming not allowed 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));
1224 if (llist_empty(&plmn->sorted_plmn))
1225 vty_notify(ms, "Search network!\n");
1227 vty_notify(ms, "Search or select from network:\n");
1228 llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
1229 vty_notify(ms, " Network %s, %s (%s, %s)\n",
1230 gsm_print_mcc(temp->mcc),
1231 gsm_print_mnc(temp->mnc),
1232 gsm_get_mcc(temp->mcc),
1233 gsm_get_mnc(temp->mcc, temp->mnc));
1236 /* go Not on PLMN state */
1237 new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
1242 /* user starts reselection */
1243 static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg)
1245 struct gsm_subscriber *subscr = &ms->subscr;
1246 struct gsm48_rrlayer *rr = &ms->rrlayer;
1249 if (!subscr->sim_valid) {
1253 /* try again later, if not idle */
1254 if (rr->state != GSM48_RR_ST_IDLE) {
1255 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
1260 /* initiate search at cell selection */
1261 vty_notify(ms, NULL);
1262 vty_notify(ms, "Searching Network, please wait...\n");
1263 LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n");
1265 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1268 gsm322_cs_sendmsg(ms, nmsg);
1273 /* MS is switched on OR SIM is inserted OR removed */
1274 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1276 struct gsm_subscriber *subscr = &ms->subscr;
1277 struct gsm322_plmn *plmn = &ms->plmn;
1280 if (!subscr->sim_valid) {
1281 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1282 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1283 new_m_state(plmn, GSM322_M5_NO_SIM);
1288 /* if there is a registered PLMN */
1289 if (subscr->plmn_valid) {
1292 /* select the registered PLMN */
1293 plmn->mcc = subscr->plmn_mcc;
1294 plmn->mnc = subscr->plmn_mnc;
1296 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1297 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1298 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1299 gsm_get_mnc(plmn->mcc, plmn->mnc));
1300 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1301 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1302 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1303 gsm_get_mnc(plmn->mcc, plmn->mnc));
1305 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1307 /* indicate New PLMN */
1308 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1311 gsm322_cs_sendmsg(ms, nmsg);
1316 /* initiate search at cell selection */
1317 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1318 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1319 vty_notify(ms, NULL);
1320 vty_notify(ms, "Searching Network, please wait...\n");
1322 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1325 gsm322_cs_sendmsg(ms, nmsg);
1330 /* MS is switched off */
1331 static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1333 struct gsm322_plmn *plmn = &ms->plmn;
1335 stop_plmn_timer(plmn);
1337 new_m_state(plmn, GSM322_M0_NULL);
1342 static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1344 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1348 /* SIM is removed */
1349 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1351 struct gsm322_plmn *plmn = &ms->plmn;
1354 stop_plmn_timer(plmn);
1356 /* indicate SIM remove to cell selection process */
1357 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1360 gsm322_cs_sendmsg(ms, nmsg);
1362 return gsm322_m_switch_on(ms, msg);
1365 /* go to On PLMN state */
1366 static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1368 struct gsm322_plmn *plmn = &ms->plmn;
1369 struct gsm_subscriber *subscr = &ms->subscr;
1371 /* set last registered PLMN */
1372 subscr->plmn_valid = 1;
1373 subscr->plmn_mcc = plmn->mcc;
1374 subscr->plmn_mnc = plmn->mnc;
1379 new_m_state(plmn, GSM322_M2_ON_PLMN);
1384 /* indicate selected PLMN */
1385 static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
1387 struct gsm322_plmn *plmn = &ms->plmn;
1389 vty_notify(ms, NULL);
1390 vty_notify(ms, "Selected Network: %s, %s\n",
1391 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1393 return gsm322_m_go_on_plmn(ms, msg);
1396 /* previously selected PLMN becomes available again */
1397 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1399 struct gsm322_plmn *plmn = &ms->plmn;
1400 struct gsm322_cellsel *cs = &ms->cellsel;
1402 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1404 if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
1407 LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
1408 "selected, so start selection.\n");
1410 /* indicate New PLMN */
1411 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1414 gsm322_cs_sendmsg(ms, nmsg);
1420 /* the user has selected given PLMN */
1421 static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
1423 struct gsm322_plmn *plmn = &ms->plmn;
1424 struct gsm_subscriber *subscr = &ms->subscr;
1425 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1428 /* use user selection */
1429 plmn->mcc = gm->mcc;
1430 plmn->mnc = gm->mnc;
1432 vty_notify(ms, NULL);
1433 vty_notify(ms, "Selected Network: %s, %s\n",
1434 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1435 LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s "
1436 "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1437 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1439 /* if selected PLMN is in list of forbidden PLMNs */
1440 gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc);
1442 new_m_state(plmn, GSM322_M4_TRYING_PLMN);
1444 /* indicate New PLMN */
1445 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1448 gsm322_cs_sendmsg(ms, nmsg);
1453 /* auto mode selected */
1454 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
1458 /* restart state machine */
1459 gsm322_m_switch_off(ms, msg);
1460 ms->settings.plmn_mode = PLMN_MODE_AUTO;
1461 gsm322_a_switch_on(ms, msg);
1463 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1466 gsm48_mmevent_msg(ms, nmsg);
1471 /* if no cell is found in other states than in *_TRYING_* states */
1472 static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
1476 /* Tell cell selection process to handle "no cell found". */
1477 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1480 gsm322_cs_sendmsg(ms, nmsg);
1486 * cell scanning process
1489 /* select a suitable and allowable cell */
1490 static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed)
1492 struct gsm322_cellsel *cs = &ms->cellsel;
1493 struct gsm_subscriber *subscr = &ms->subscr;
1494 struct gsm48_sysinfo *s;
1495 int i, found = -1, power = 0;
1496 uint8_t flags, mask;
1499 /* set out access class depending on the cell selection type */
1501 acc_class = subscr->acc_class | 0x0400; /* add emergency */
1502 LOGP(DCS, LOGL_DEBUG, "Select using access class with "
1503 "Emergency class.\n");
1505 acc_class = subscr->acc_class;
1506 LOGP(DCS, LOGL_DEBUG, "Select using access class \n");
1509 /* flags to match */
1510 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1511 | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
1512 if (cs->state == GSM322_C2_STORED_CELL_SEL
1513 || cs->state == GSM322_C5_CHOOSE_CELL)
1514 mask |= GSM322_CS_FLAG_BA;
1515 flags = mask; /* all masked flags are requied */
1517 /* loop through all scanned frequencies and select cell */
1518 for (i = 0; i <= 1023; i++) {
1519 cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
1520 s = cs->list[i].sysinfo;
1522 /* channel has no informations for us */
1523 if (!s || (cs->list[i].flags & mask) != flags) {
1527 /* check C1 criteria not fullfilled */
1528 // TODO: C1 is also dependant on power class and max power
1529 if (rxlev2dbm(cs->list[i].rxlev) < s->rxlev_acc_min_db) {
1530 LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
1531 "not met. (rxlev %s < min %d)\n", i,
1532 gsm_print_rxlev(cs->list[i].rxlev),
1533 s->rxlev_acc_min_db);
1537 /* if cell is barred and we don't override */
1538 if (!subscr->acc_barr
1539 && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
1540 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
1545 /* if cell is in list of forbidden LAs */
1546 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
1547 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1548 "list of forbidden LAs. (mcc=%s mnc=%s "
1549 "lai=%04x)\n", i, gsm_print_mcc(s->mcc),
1550 gsm_print_mnc(s->mnc), s->lac);
1554 /* if cell is in list of forbidden PLMNs */
1555 if (plmn_allowed && gsm_subscr_is_forbidden_plmn(subscr,
1557 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1558 "list of forbidden PLMNs. (mcc=%s mnc=%s)\n", i,
1559 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc));
1563 /* if we have no access to the cell and we don't override */
1564 if (!subscr->acc_barr
1565 && !(acc_class & (s->class_barr ^ 0xffff))) {
1566 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
1567 "barred for out access. (access=%04x "
1568 "barred=%04x)\n", i, acc_class, s->class_barr);
1572 /* store temporary available and allowable flag */
1573 cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
1575 /* if we search a specific PLMN, but it does not match */
1576 if (!any && cs->mcc && (cs->mcc != s->mcc
1577 || cs->mnc != s->mnc)) {
1578 LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
1579 "does not match target PLMN. (mcc=%s "
1580 "mnc=%s)\n", i, gsm_print_mcc(s->mcc),
1581 gsm_print_mnc(s->mnc));
1585 LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%s "
1586 "mcc=%s mnc=%s lac=%04x %s, %s)\n", i,
1587 gsm_print_rxlev(cs->list[i].rxlev),
1588 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac,
1589 gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc));
1591 /* find highest power cell */
1592 if (found < 0 || cs->list[i].rxlev > power) {
1593 power = cs->list[i].rxlev;
1599 LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
1604 /* tune to first/next unscanned frequency and search for PLMN */
1605 static int gsm322_cs_scan(struct osmocom_ms *ms)
1607 struct gsm322_cellsel *cs = &ms->cellsel;
1609 #ifndef SKIP_MAX_PER_BAND
1612 uint8_t mask, flags;
1613 uint32_t weight = 0, test = cs->scan_state;
1615 /* search for strongest unscanned cell */
1616 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1617 | GSM322_CS_FLAG_SIGNAL;
1618 if (cs->state == GSM322_C2_STORED_CELL_SEL
1619 || cs->state == GSM322_C5_CHOOSE_CELL)
1620 mask |= GSM322_CS_FLAG_BA;
1621 flags = mask; /* all masked flags are requied */
1622 for (i = 0; i <= 1023; i++) {
1623 #ifndef SKIP_MAX_PER_BAND
1624 /* skip if band has enough frequencies scanned (3.2.1) */
1625 for (j = 0; gsm_sup_smax[j].max; j++) {
1626 if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
1627 if (gsm_sup_smax[j].start >= i
1628 && gsm_sup_smax[j].end <= i)
1631 if (gsm_sup_smax[j].end <= i
1632 || gsm_sup_smax[j].start >= i)
1636 if (gsm_sup_smax[j].max) {
1637 if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
1641 /* search for unscanned frequency */
1642 if ((cs->list[i].flags & mask) == flags) {
1643 /* weight depends on the power level
1644 * if it is the same, it depends on arfcn
1646 test = cs->list[i].rxlev + 1;
1647 test = (test << 16) | i;
1648 if (test >= cs->scan_state)
1654 cs->scan_state = weight;
1657 gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs,
1660 /* special case for PLMN search */
1661 if (cs->state == GSM322_PLMN_SEARCH && !weight) {
1664 /* create AA flag */
1665 cs->mcc = cs->mnc = 0;
1666 gsm322_cs_select(ms, 0, 0);
1668 new_c_state(cs, GSM322_C0_NULL);
1670 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END);
1671 LOGP(DCS, LOGL_INFO, "PLMN search finished.\n");
1674 gsm322_plmn_sendmsg(ms, nmsg);
1679 /* special case for HPLMN search */
1680 if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
1683 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1684 LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n");
1687 gsm322_plmn_sendmsg(ms, nmsg);
1689 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
1691 cs->arfcn = cs->sel_arfcn;
1692 LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev "
1693 "%s).\n", cs->arfcn,
1694 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
1696 gsm322_sync_to_cell(cs);
1701 /* if all frequencies have been searched */
1707 LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
1709 /* just see, if we search for any cell */
1710 if (cs->state == GSM322_C6_ANY_CELL_SEL
1711 || cs->state == GSM322_C8_ANY_CELL_RESEL
1712 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1715 found = gsm322_cs_select(ms, any, 0);
1719 struct gsm322_plmn *plmn = &ms->plmn;
1721 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1724 cs->si = cs->list[cs->arfcn].sysinfo;
1726 gsm322_sync_to_cell(cs);
1728 /* selected PLMN (manual) or any PLMN (auto) */
1729 switch (ms->settings.plmn_mode) {
1730 case PLMN_MODE_AUTO:
1731 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1732 /* PLMN becomes available */
1733 nmsg = gsm322_msgb_alloc(
1734 GSM322_EVENT_PLMN_AVAIL);
1737 gsm322_plmn_sendmsg(ms, nmsg);
1740 case PLMN_MODE_MANUAL:
1741 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1742 && gsm322_is_plmn_avail(cs, plmn->mcc,
1744 /* PLMN becomes available */
1745 nmsg = gsm322_msgb_alloc(
1746 GSM322_EVENT_PLMN_AVAIL);
1749 gsm322_plmn_sendmsg(ms, nmsg);
1754 /* set selected cell */
1756 cs->sel_arfcn = cs->arfcn;
1757 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1758 cs->sel_mcc = cs->si->mcc;
1759 cs->sel_mnc = cs->si->mnc;
1760 cs->sel_lac = cs->si->lac;
1761 cs->sel_id = cs->si->cell_id;
1763 /* tell CS process about available cell */
1764 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1765 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1768 /* unset selected cell */
1769 gsm322_unselect_cell(cs);
1771 /* tell CS process about no cell available */
1772 LOGP(DCS, LOGL_INFO, "No cell available.\n");
1773 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1777 gsm322_c_event(ms, nmsg);
1783 /* NOTE: We might already have system information from previous
1784 * scan. But we need recent informations, so we scan again!
1787 /* Tune to frequency for a while, to receive broadcasts. */
1788 cs->arfcn = weight & 1023;
1789 LOGP(DCS, LOGL_DEBUG, "Scanning frequency %d (rxlev %s).\n", cs->arfcn,
1790 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
1792 gsm322_sync_to_cell(cs);
1794 /* Allocate/clean system information. */
1795 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1796 if (cs->list[cs->arfcn].sysinfo)
1797 memset(cs->list[cs->arfcn].sysinfo, 0,
1798 sizeof(struct gsm48_sysinfo));
1800 cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
1801 struct gsm48_sysinfo);
1802 if (!cs->list[cs->arfcn].sysinfo)
1804 cs->si = cs->list[cs->arfcn].sysinfo;
1806 /* increase scan counter for each maximum scan range */
1807 #ifndef SKIP_MAX_PER_BAND
1808 if (gsm_sup_smax[j].max) {
1809 LOGP(DCS, LOGL_DEBUG, "%d frequencies left in band %d..%d\n",
1810 gsm_sup_smax[j].max - gsm_sup_smax[j].temp,
1811 gsm_sup_smax[j].start, gsm_sup_smax[j].end);
1812 gsm_sup_smax[j].temp++;
1819 /* check if cell is now suitable and allowable */
1820 static int gsm322_cs_store(struct osmocom_ms *ms)
1822 struct gsm322_cellsel *cs = &ms->cellsel;
1823 struct gsm48_sysinfo *s = cs->si;
1824 struct gsm322_plmn *plmn = &ms->plmn;
1828 if (cs->state != GSM322_C2_STORED_CELL_SEL
1829 && cs->state != GSM322_C1_NORMAL_CELL_SEL
1830 && cs->state != GSM322_C6_ANY_CELL_SEL
1831 && cs->state != GSM322_C4_NORMAL_CELL_RESEL
1832 && cs->state != GSM322_C8_ANY_CELL_RESEL
1833 && cs->state != GSM322_C5_CHOOSE_CELL
1834 && cs->state != GSM322_C9_CHOOSE_ANY_CELL
1835 && cs->state != GSM322_PLMN_SEARCH
1836 && cs->state != GSM322_HPLMN_SEARCH) {
1837 LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
1838 "(re-)selection, please fix!\n");
1843 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO;
1845 && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp &&
1846 cs->list[cs->arfcn].sysinfo->sp_cbq))
1847 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED;
1849 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED;
1852 cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db;
1853 cs->list[cs->arfcn].class_barr = s->class_barr;
1854 cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch;
1857 /* store selected network */
1860 cs->list[cs->arfcn].mcc = s->mcc;
1861 cs->list[cs->arfcn].mnc = s->mnc;
1862 cs->list[cs->arfcn].lac = s->lac;
1865 if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
1866 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD;
1868 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD;
1871 LOGP(DCS, LOGL_DEBUG, "Scan frequency %d: Cell found. (rxlev %s "
1872 "mcc %s mnc %s lac %04x)\n", cs->arfcn,
1873 gsm_print_rxlev(cs->list[cs->arfcn].rxlev),
1874 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac);
1876 /* special case for PLMN search */
1877 if (cs->state == GSM322_PLMN_SEARCH)
1878 /* tune to next cell */
1879 return gsm322_cs_scan(ms);
1881 /* special case for HPLMN search */
1882 if (cs->state == GSM322_HPLMN_SEARCH) {
1883 struct gsm_subscriber *subscr = &ms->subscr;
1886 if (!gsm322_is_hplmn_avail(cs, subscr->imsi))
1887 /* tune to next cell */
1888 return gsm322_cs_scan(ms);
1890 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1891 LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n");
1894 gsm322_plmn_sendmsg(ms, nmsg);
1899 /* just see, if we search for any cell */
1900 if (cs->state == GSM322_C6_ANY_CELL_SEL
1901 || cs->state == GSM322_C8_ANY_CELL_RESEL
1902 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1905 found = gsm322_cs_select(ms, any, 0);
1909 LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n");
1910 /* tune to next cell */
1911 return gsm322_cs_scan(ms);
1914 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1917 cs->si = cs->list[cs->arfcn].sysinfo;
1919 gsm322_sync_to_cell(cs);
1921 /* selected PLMN (manual) or any PLMN (auto) */
1922 switch (ms->settings.plmn_mode) {
1923 case PLMN_MODE_AUTO:
1924 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1925 /* PLMN becomes available */
1926 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1929 gsm322_plmn_sendmsg(ms, nmsg);
1932 case PLMN_MODE_MANUAL:
1933 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1934 && gsm322_is_plmn_avail(cs, plmn->mcc,
1936 /* PLMN becomes available */
1937 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1940 gsm322_plmn_sendmsg(ms, nmsg);
1945 /* set selected cell */
1947 cs->sel_arfcn = cs->arfcn;
1948 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1949 cs->sel_mcc = cs->si->mcc;
1950 cs->sel_mnc = cs->si->mnc;
1951 cs->sel_lac = cs->si->lac;
1952 cs->sel_id = cs->si->cell_id;
1954 /* tell CS process about available cell */
1955 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1956 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1959 gsm322_c_event(ms, nmsg);
1965 /* process system information when returing to idle mode */
1966 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
1968 struct gsm322_cellsel *cs = &ms->cellsel;
1969 struct gsm48_sysinfo *s = cs->si;
1970 struct gsm322_ba_list *ba = NULL;
1974 /* collect system information received during dedicated mode */
1976 && (!s->nb_ext_ind_si5
1977 || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
1978 || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
1979 && s->nb_ext_ind_si5bis))) {
1980 /* find or create ba list */
1981 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1983 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1988 llist_add_tail(&ba->entry, &cs->ba_list);
1990 /* update (add) ba list */
1991 memset(freq, 0, sizeof(freq));
1992 for (i = 0; i <= 1023; i++) {
1993 if ((s->freq[i].mask & (FREQ_TYPE_SERV
1994 | FREQ_TYPE_NCELL | FREQ_TYPE_REP)))
1995 freq[i >> 3] |= (1 << (i & 7));
1997 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
1998 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
1999 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2000 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2001 gsm_get_mnc(ba->mcc, ba->mnc));
2002 memcpy(ba->freq, freq, sizeof(freq));
2009 /* store BA whenever a system informations changes */
2010 static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
2011 struct gsm48_sysinfo *s)
2013 struct gsm322_ba_list *ba;
2017 /* find or create ba list */
2018 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
2020 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2025 llist_add_tail(&ba->entry, &cs->ba_list);
2027 /* update ba list */
2028 memset(freq, 0, sizeof(freq));
2029 freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
2030 for (i = 0; i <= 1023; i++) {
2031 if ((s->freq[i].mask &
2032 (FREQ_TYPE_SERV | FREQ_TYPE_NCELL | FREQ_TYPE_REP)))
2033 freq[i >> 3] |= (1 << (i & 7));
2035 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
2036 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
2037 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2038 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2039 gsm_get_mnc(ba->mcc, ba->mnc));
2040 memcpy(ba->freq, freq, sizeof(freq));
2046 /* process system information during camping on a cell */
2047 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2049 // struct gsm48_rrlayer *rr = &ms->rrlayer;
2050 struct gsm322_cellsel *cs = &ms->cellsel;
2051 struct gsm48_sysinfo *s = cs->si;
2052 struct gsm_subscriber *subscr = &ms->subscr;
2053 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2057 if (rr->state != GSM48_RR_ST_IDLE) {
2058 LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n");
2063 /* Store BA if we have full system info about cells and neigbor cells.
2064 * Depending on the extended bit in the channel description,
2065 * we require more or less system informations about neighbor cells
2069 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2070 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2071 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2072 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2075 && (!s->nb_ext_ind_si2
2076 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2077 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2078 && s->nb_ext_ind_si2bis)))
2079 gsm322_store_ba_list(cs, s);
2081 /* update sel_si, if all relevant system informations received */
2082 if (s->si1 && s->si2 && s->si3
2083 && (!s->nb_ext_ind_si2
2084 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2085 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2086 && s->nb_ext_ind_si2bis))) {
2088 LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is "
2090 memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
2091 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2095 /* check for barred cell */
2096 if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
2097 /* check if cell becomes barred */
2098 if (!subscr->acc_barr && s->cell_barr
2099 && !(cs->list[cs->arfcn].sysinfo
2100 && cs->list[cs->arfcn].sysinfo->sp
2101 && cs->list[cs->arfcn].sysinfo->sp_cbq)) {
2102 LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
2104 /* mark cell as unscanned */
2105 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2106 if (cs->list[cs->arfcn].sysinfo) {
2107 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2109 talloc_free(cs->list[cs->arfcn].sysinfo);
2110 cs->list[cs->arfcn].sysinfo = NULL;
2111 gsm322_unselect_cell(cs);
2113 /* trigger reselection without queueing,
2114 * because other sysinfo message may be queued
2117 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2120 gsm322_c_event(ms, nmsg);
2125 /* check if cell access becomes barred */
2126 if (!((subscr->acc_class & 0xfbff)
2127 & (s->class_barr ^ 0xffff))) {
2128 LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
2133 /* check if MCC, MNC, LAC, cell ID changes */
2134 if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc
2135 || cs->sel_lac != s->lac) {
2136 LOGP(DCS, LOGL_NOTICE, "Cell changes location area. "
2137 "This is not good!\n");
2140 if (cs->sel_id != s->cell_id) {
2141 LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. "
2142 "This is not good!\n");
2149 /* process system information during channel scanning */
2150 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2152 struct gsm322_cellsel *cs = &ms->cellsel;
2153 struct gsm48_sysinfo *s = cs->si;
2154 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2156 /* no sysinfo if we are not done with power scan */
2157 if (cs->powerscan) {
2158 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
2162 /* Store BA if we have full system info about cells and neigbor cells.
2163 * Depending on the extended bit in the channel description,
2164 * we require more or less system informations about neighbor cells
2168 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2169 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2170 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2171 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2174 && (!s->nb_ext_ind_si2
2175 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2176 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2177 && s->nb_ext_ind_si2bis)))
2178 gsm322_store_ba_list(cs, s);
2180 /* all relevant system informations received */
2181 if (s->si1 && s->si2 && s->si3
2182 && (!s->nb_ext_ind_si2
2183 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2184 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2185 && s->nb_ext_ind_si2bis))) {
2186 LOGP(DCS, LOGL_DEBUG, "Received relevant sysinfo.\n");
2190 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2192 /* store sysinfo and continue scan */
2193 return gsm322_cs_store(ms);
2196 /* wait for more sysinfo or timeout */
2200 static void gsm322_cs_timeout(void *arg)
2202 struct gsm322_cellsel *cs = arg;
2203 struct osmocom_ms *ms = cs->ms;
2205 /* if we have no lock, we retry */
2206 if (cs->ccch_state != GSM322_CCCH_ST_SYNC)
2207 LOGP(DCS, LOGL_INFO, "Cell selection failed, sync timeout.\n");
2209 LOGP(DCS, LOGL_INFO, "Cell selection failed, read timeout.\n");
2211 /* remove system information */
2212 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2213 if (cs->list[cs->arfcn].sysinfo) {
2214 LOGP(DCS, LOGL_DEBUG, "free sysinfo arfcn=%d\n", cs->arfcn);
2215 talloc_free(cs->list[cs->arfcn].sysinfo);
2216 cs->list[cs->arfcn].sysinfo = NULL;
2217 gsm322_unselect_cell(cs);
2220 /* tune to next cell */
2227 * power scan process
2230 /* search for block of unscanned frequencies and start scanning */
2231 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
2233 struct gsm322_cellsel *cs = &ms->cellsel;
2234 struct gsm_settings *set = &ms->settings;
2236 uint8_t mask, flags;
2240 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
2241 flags = GSM322_CS_FLAG_SUPPORT;
2243 /* in case of sticking to a cell, we only select it */
2245 LOGP(DCS, LOGL_DEBUG, "Scanning power for sticked cell.\n");
2246 i = set->stick_arfcn;
2247 if ((cs->list[i].flags & mask) == flags)
2250 /* search for first frequency to scan */
2251 if (cs->state == GSM322_C2_STORED_CELL_SEL
2252 || cs->state == GSM322_C5_CHOOSE_CELL) {
2253 LOGP(DCS, LOGL_DEBUG, "Scanning power for stored BA "
2255 mask |= GSM322_CS_FLAG_BA;
2256 flags |= GSM322_CS_FLAG_BA;
2258 LOGP(DCS, LOGL_DEBUG, "Scanning power for all "
2260 for (i = 0; i <= 1023; i++) {
2261 if ((cs->list[i].flags & mask) == flags) {
2268 /* if there is no more frequency, we can tune to that cell */
2272 /* stop power level scanning */
2275 /* check if not signal is found */
2276 for (i = 0; i <= 1023; i++) {
2277 if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2283 LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
2284 /* on normal cell selection, start over */
2285 if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
2286 for (i = 0; i <= 1023; i++) {
2287 /* clear flag that this was scanned */
2288 cs->list[i].flags &=
2289 ~(GSM322_CS_FLAG_POWER
2290 | GSM322_CS_FLAG_SIGNAL
2291 | GSM322_CS_FLAG_SYSINFO);
2292 if (cs->list[i].sysinfo) {
2293 LOGP(DCS, LOGL_INFO, "free "
2294 "sysinfo arfcn=%d\n",
2297 cs->list[i].sysinfo);
2298 cs->list[i].sysinfo = NULL;
2301 /* no cell selected */
2302 gsm322_unselect_cell(cs);
2305 /* on other cell selection, indicate "no cell found" */
2306 /* NOTE: PLMN search process handles it.
2307 * If not handled there, CS process gets indicated.
2308 * If we would continue to process CS, then we might get
2309 * our list of scanned cells disturbed.
2311 if (cs->state == GSM322_PLMN_SEARCH)
2312 nmsg = gsm322_msgb_alloc(
2313 GSM322_EVENT_PLMN_SEARCH_END);
2315 nmsg = gsm322_msgb_alloc(
2316 GSM322_EVENT_NO_CELL_FOUND);
2319 gsm322_plmn_sendmsg(ms, nmsg);
2321 /* if HPLMN search, select last frequency */
2322 if (cs->state == GSM322_HPLMN_SEARCH) {
2323 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2325 cs->arfcn = cs->sel_arfcn;
2326 LOGP(DCS, LOGL_INFO, "Tuning back to frequency "
2327 "%d (rxlev %s).\n", cs->arfcn,
2329 cs->list[cs->arfcn].rxlev));
2331 gsm322_sync_to_cell(cs);
2334 new_c_state(cs, GSM322_C0_NULL);
2338 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
2339 cs->scan_state = 0xffffffff; /* higher than high */
2340 /* clear counter of scanned frequencies of each range */
2341 for (i = 0; gsm_sup_smax[i].max; i++)
2342 gsm_sup_smax[i].temp = 0;
2343 return gsm322_cs_scan(ms);
2346 /* search last frequency to scan (en block) */
2349 for (i = s + 1; i <= 1023; i++) {
2350 if ((cs->list[i].flags & mask) == flags)
2357 LOGP(DCS, LOGL_DEBUG, "Scanning frequencies. (%d..%d)\n", s, e);
2359 /* start scan on radio interface */
2360 if (!cs->powerscan) {
2361 l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
2364 //#warning TESTING!!!!
2366 return l1ctl_tx_pm_req_range(ms, s, e);
2369 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
2370 void *handler_data, void *signal_data)
2372 struct osmocom_ms *ms;
2373 struct gsm322_cellsel *cs;
2374 struct osmobb_meas_res *mr;
2375 struct osmobb_fbsb_res *fr;
2379 if (subsys != SS_L1CTL)
2383 case S_L1CTL_PM_RES:
2389 i = mr->band_arfcn & 1023;
2391 if ((cs->list[i].flags & GSM322_CS_FLAG_POWER)) {
2392 LOGP(DCS, LOGL_ERROR, "Getting PM for frequency %d "
2393 "twice. Overwriting the first! Please fix "
2396 cs->list[i].rxlev = rxlev;
2397 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2398 cs->list[i].flags &= ~GSM322_CS_FLAG_SIGNAL;
2399 /* if minimum level is reached or if we stick to a cell */
2400 if (rxlev2dbm(rxlev) >= ms->settings.min_rxlev_db
2401 || ms->settings.stick) {
2402 cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2403 LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2404 "rxlev %s (%d))\n", i,
2405 gsm_print_rxlev(rxlev), rxlev);
2408 case S_L1CTL_PM_DONE:
2409 LOGP(DCS, LOGL_DEBUG, "Done with power scanning range.\n");
2414 gsm322_cs_powerscan(ms);
2416 case S_L1CTL_FBSB_RESP:
2420 if (cs->ccch_state == GSM322_CCCH_ST_INIT) {
2421 LOGP(DCS, LOGL_INFO, "Channel synched. (ARFCN=%d, "
2422 "snr=%u, BSIC=%u)\n", cs->arfcn, fr->snr,
2424 cs->ccch_state = GSM322_CCCH_ST_SYNC;
2426 cs->si->bsic = fr->bsic;
2430 /* in dedicated mode */
2431 if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
2432 return gsm48_rr_tx_rand_acc(ms, NULL);
2435 /* set timer for reading BCCH */
2436 if (cs->state == GSM322_C2_STORED_CELL_SEL
2437 || cs->state == GSM322_C1_NORMAL_CELL_SEL
2438 || cs->state == GSM322_C6_ANY_CELL_SEL
2439 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2440 || cs->state == GSM322_C8_ANY_CELL_RESEL
2441 || cs->state == GSM322_C5_CHOOSE_CELL
2442 || cs->state == GSM322_C9_CHOOSE_ANY_CELL
2443 || cs->state == GSM322_PLMN_SEARCH
2444 || cs->state == GSM322_HPLMN_SEARCH)
2445 start_cs_timer(cs, ms->support.scan_to, 0);
2446 // TODO: timer depends on BCCH config
2448 /* set downlink signalling failure criterion */
2449 ms->meas.ds_fail = ms->meas.dsc = ms->settings.dsc_max;
2450 LOGP(DRR, LOGL_INFO, "using DSC of %d\n", ms->meas.dsc);
2453 case S_L1CTL_FBSB_ERR:
2458 gsm322_sync_to_cell(cs);
2460 LOGP(DCS, LOGL_INFO, "Channel sync error, try again\n");
2464 LOGP(DCS, LOGL_INFO, "Channel sync error.\n");
2472 gsm322_cs_timeout(cs);
2474 case S_L1CTL_LOSS_IND:
2481 if (ms->mmlayer.power_off_idle) {
2491 static void gsm322_cs_loss(void *arg)
2493 struct gsm322_cellsel *cs = arg;
2494 struct osmocom_ms *ms = cs->ms;
2495 struct gsm48_rrlayer *rr = &ms->rrlayer;
2497 LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n");
2498 if (cs->state == GSM322_C3_CAMPED_NORMALLY
2499 || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
2500 if (rr->state == GSM48_RR_ST_IDLE) {
2503 LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n");
2505 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2508 gsm322_c_event(ms, nmsg);
2511 LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n");
2513 /* be shure that nothing else is done after here
2514 * because the function call above may cause
2515 * to return from idle state and trigger cell re-sel.
2524 * handler for cell selection process
2527 /* start PLMN search */
2528 static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg)
2530 struct gsm322_cellsel *cs = &ms->cellsel;
2533 new_c_state(cs, GSM322_PLMN_SEARCH);
2535 /* mark all frequencies except our own BA to be scanned */
2536 for (i = 0; i <= 1023; i++) {
2537 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2538 | GSM322_CS_FLAG_SIGNAL
2539 | GSM322_CS_FLAG_SYSINFO);
2540 if (cs->list[i].sysinfo) {
2541 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2542 talloc_free(cs->list[i].sysinfo);
2543 cs->list[i].sysinfo = NULL;
2544 gsm322_unselect_cell(cs);
2548 /* unset selected cell */
2549 gsm322_unselect_cell(cs);
2551 /* start power scan */
2552 return gsm322_cs_powerscan(ms);
2555 /* start HPLMN search */
2556 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2558 struct gsm322_cellsel *cs = &ms->cellsel;
2561 new_c_state(cs, GSM322_HPLMN_SEARCH);
2563 /* mark all frequencies except our own BA to be scanned */
2564 for (i = 0; i <= 1023; i++) {
2565 if (i != cs->sel_arfcn
2566 && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2567 && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2568 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2569 | GSM322_CS_FLAG_SIGNAL
2570 | GSM322_CS_FLAG_SYSINFO);
2571 if (cs->list[i].sysinfo) {
2572 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2574 talloc_free(cs->list[i].sysinfo);
2575 cs->list[i].sysinfo = NULL;
2580 /* no cell selected */
2581 gsm322_unselect_cell(cs);
2583 /* start power scan */
2584 return gsm322_cs_powerscan(ms);
2587 /* start stored cell selection */
2588 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2590 struct gsm322_cellsel *cs = &ms->cellsel;
2593 new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2595 /* flag all frequencies that are in current band allocation */
2596 for (i = 0; i <= 1023; i++) {
2597 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2598 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2600 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2603 /* unset selected cell */
2604 gsm322_unselect_cell(cs);
2606 /* start power scan */
2607 return gsm322_cs_powerscan(ms);
2610 /* start noraml cell selection */
2611 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2613 struct gsm322_cellsel *cs = &ms->cellsel;
2616 /* except for stored cell selection state, we weed to rescan ?? */
2617 if (cs->state != GSM322_C2_STORED_CELL_SEL) {
2618 for (i = 0; i <= 1023; i++) {
2619 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2620 | GSM322_CS_FLAG_SIGNAL
2621 | GSM322_CS_FLAG_SYSINFO);
2622 if (cs->list[i].sysinfo) {
2623 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2625 talloc_free(cs->list[i].sysinfo);
2626 cs->list[i].sysinfo = NULL;
2631 new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2633 /* unset selected cell */
2634 gsm322_unselect_cell(cs);
2636 /* start power scan */
2637 return gsm322_cs_powerscan(ms);
2640 /* start any cell selection */
2641 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2643 struct gsm322_cellsel *cs = &ms->cellsel;
2645 /* in case we already tried any cell (re-)selection, power scan again */
2646 if (cs->state == GSM322_C0_NULL
2647 || cs->state == GSM322_C6_ANY_CELL_SEL
2648 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2651 for (i = 0; i <= 1023; i++) {
2652 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2653 | GSM322_CS_FLAG_SIGNAL
2654 | GSM322_CS_FLAG_SYSINFO);
2655 if (cs->list[i].sysinfo) {
2656 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2658 talloc_free(cs->list[i].sysinfo);
2659 cs->list[i].sysinfo = NULL;
2663 /* after re-selection, indicate no cell found */
2664 if (cs->state == GSM322_C6_ANY_CELL_SEL
2665 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2668 /* tell that we have no cell found */
2669 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2672 gsm48_mmevent_msg(ms, nmsg);
2675 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2678 cs->mcc = cs->mnc = 0;
2680 /* unset selected cell */
2681 gsm322_unselect_cell(cs);
2683 /* start power scan */
2684 return gsm322_cs_powerscan(ms);
2687 /* start noraml cell re-selection */
2688 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2690 struct gsm322_cellsel *cs = &ms->cellsel;
2692 new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2694 /* NOTE: We keep our scan info we have so far.
2695 * This may cause a skip in power scan. */
2697 /* start power scan */
2698 return gsm322_cs_powerscan(ms);
2701 /* start any cell re-selection */
2702 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2704 struct gsm322_cellsel *cs = &ms->cellsel;
2706 new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2708 /* NOTE: We keep our scan info we have so far.
2709 * This may cause a skip in power scan. */
2711 /* start power scan */
2712 return gsm322_cs_powerscan(ms);
2715 /* a suitable cell was found, so we camp normally */
2716 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2718 struct gsm322_cellsel *cs = &ms->cellsel;
2721 LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s "
2722 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2723 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2724 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2726 /* tell that we have selected a (new) cell */
2727 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2730 gsm48_mmevent_msg(ms, nmsg);
2732 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2737 /* a not suitable cell was found, so we camp on any cell */
2738 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2740 struct gsm322_cellsel *cs = &ms->cellsel;
2743 LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s "
2744 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2745 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2746 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2749 /* tell that we have selected a (new) cell */
2750 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2753 gsm48_mmevent_msg(ms, nmsg);
2755 new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2760 /* create temporary ba range with given frequency ranges */
2761 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2762 uint32_t *range, uint8_t ranges)
2764 static struct gsm322_ba_list ba;
2765 uint16_t lower, higher;
2767 memset(&ba, 0, sizeof(ba));
2770 lower = *range & 1023;
2771 higher = (*range >> 16) & 1023;
2773 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2776 ba.freq[lower >> 3] |= 1 << (lower & 7);
2777 if (lower == higher)
2779 lower = (lower + 1) & 1023;
2786 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2787 static int gsm322_cs_choose(struct osmocom_ms *ms)
2789 struct gsm322_cellsel *cs = &ms->cellsel;
2790 struct gsm48_rrlayer *rr = &ms->rrlayer;
2791 struct gsm322_ba_list *ba = NULL;
2794 /* NOTE: The call to this function is synchron to RR layer, so
2795 * we may access the BA range there.
2798 ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges);
2800 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2801 /* get and update BA of last received sysinfo 5* */
2802 ba = gsm322_cs_sysinfo_sacch(ms);
2804 LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored "
2806 ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2814 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2816 /* tell CS to start over */
2817 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2820 gsm322_c_event(ms, nmsg);
2826 /* flag all frequencies that are in current band allocation */
2827 for (i = 0; i <= 1023; i++) {
2828 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2829 if ((ba->freq[i >> 3] & (1 << (i & 7)))) {
2830 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2832 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2835 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2836 | GSM322_CS_FLAG_SIGNAL
2837 | GSM322_CS_FLAG_SYSINFO);
2838 if (cs->list[i].sysinfo) {
2839 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2840 talloc_free(cs->list[i].sysinfo);
2841 cs->list[i].sysinfo = NULL;
2845 /* unset selected cell */
2846 gsm322_unselect_cell(cs);
2848 /* start power scan */
2849 return gsm322_cs_powerscan(ms);
2852 /* start 'Choose cell' after returning to idle mode */
2853 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2855 struct gsm322_cellsel *cs = &ms->cellsel;
2856 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2858 /* After location updating, we choose the last cell */
2859 if (gm->same_cell) {
2862 if (!cs->selected) {
2863 printf("No cell selected when ret.idle, please fix!\n");
2866 cs->arfcn = cs->sel_arfcn;
2868 /* be sure to go to current camping frequency on return */
2869 LOGP(DCS, LOGL_INFO, "Selecting frequency %d. after LOC.UPD.\n",
2872 gsm322_sync_to_cell(cs);
2873 cs->si = cs->list[cs->arfcn].sysinfo;
2875 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2877 /* tell that we have selected the cell, so RR returns IDLE */
2878 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2881 gsm48_mmevent_msg(ms, nmsg);
2886 new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2888 return gsm322_cs_choose(ms);
2891 /* start 'Choose any cell' after returning to idle mode */
2892 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2894 struct gsm322_cellsel *cs = &ms->cellsel;
2896 new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2898 return gsm322_cs_choose(ms);
2901 /* a new PLMN is selected by PLMN search process */
2902 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2904 struct gsm322_cellsel *cs = &ms->cellsel;
2905 struct gsm322_plmn *plmn = &ms->plmn;
2906 struct gsm322_ba_list *ba;
2908 cs->mcc = plmn->mcc;
2909 cs->mnc = plmn->mnc;
2911 LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s "
2912 "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc),
2913 gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc),
2914 gsm_get_mnc(cs->mcc, cs->mnc));
2916 /* search for BA list */
2917 ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2920 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2921 return gsm322_c_stored_cell_sel(ms, ba);
2923 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2924 return gsm322_c_normal_cell_sel(ms, msg);
2928 /* go connected mode */
2929 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2931 struct gsm322_cellsel *cs = &ms->cellsel;
2933 /* check for error */
2936 cs->arfcn = cs->sel_arfcn;
2938 /* be sure to go to current camping frequency on return */
2939 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2941 gsm322_sync_to_cell(cs);
2942 cs->si = cs->list[cs->arfcn].sysinfo;
2947 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2949 struct gsm322_cellsel *cs = &ms->cellsel;
2951 /* check for error */
2954 cs->arfcn = cs->sel_arfcn;
2956 /* be sure to go to current camping frequency on return */
2957 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2959 gsm322_sync_to_cell(cs);
2960 cs->si = cs->list[cs->arfcn].sysinfo;
2966 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2968 struct gsm_subscriber *subscr = &ms->subscr;
2970 /* if no SIM is is MS */
2971 if (!subscr->sim_valid) {
2972 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2973 return gsm322_c_any_cell_sel(ms, msg);
2975 LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2977 /* stay in NULL state until PLMN is selected */
2986 /* state machine for automatic PLMN selection events */
2987 static struct plmnastatelist {
2990 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2991 } plmnastatelist[] = {
2992 {SBIT(GSM322_A0_NULL),
2993 GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2995 /* special case for full search */
2996 {SBIT(GSM322_A0_NULL),
2997 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn},
3000 GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
3002 {SBIT(GSM322_A0_NULL) | SBIT(GSM322_A6_NO_SIM),
3003 GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
3006 GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
3009 GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
3012 GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
3014 {SBIT(GSM322_A1_TRYING_RPLMN),
3015 GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
3017 {SBIT(GSM322_A1_TRYING_RPLMN),
3018 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn},
3020 {SBIT(GSM322_A1_TRYING_RPLMN),
3021 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
3023 {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
3024 GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
3026 {SBIT(GSM322_A2_ON_PLMN),
3027 GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
3029 {SBIT(GSM322_A2_ON_PLMN),
3030 GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start},
3032 {SBIT(GSM322_A2_ON_PLMN),
3033 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
3035 {SBIT(GSM322_A2_ON_PLMN),
3036 GSM322_EVENT_USER_RESEL, gsm322_a_user_resel},
3038 {SBIT(GSM322_A3_TRYING_PLMN),
3039 GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
3041 {SBIT(GSM322_A3_TRYING_PLMN),
3042 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn},
3044 {SBIT(GSM322_A3_TRYING_PLMN),
3045 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
3047 {SBIT(GSM322_A5_HPLMN_SEARCH),
3048 GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn},
3050 {SBIT(GSM322_A5_HPLMN_SEARCH),
3051 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn},
3053 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
3054 GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
3056 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
3057 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_plmn_avail},
3060 GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
3063 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3066 #define PLMNASLLEN \
3067 (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
3069 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
3071 struct gsm322_plmn *plmn = &ms->plmn;
3072 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3073 int msg_type = gm->msg_type;
3077 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
3078 "selection in state '%s'\n", ms->name, get_event_name(msg_type),
3079 plmn_a_state_names[plmn->state]);
3080 /* find function for current state and message */
3081 for (i = 0; i < PLMNASLLEN; i++)
3082 if ((msg_type == plmnastatelist[i].type)
3083 && ((1 << plmn->state) & plmnastatelist[i].states))
3085 if (i == PLMNASLLEN) {
3086 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3090 rc = plmnastatelist[i].rout(ms, msg);
3095 /* state machine for manual PLMN selection events */
3096 static struct plmnmstatelist {
3099 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3100 } plmnmstatelist[] = {
3101 {SBIT(GSM322_M0_NULL),
3102 GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
3104 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) |
3105 SBIT(GSM322_M2_ON_PLMN),
3106 GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns},
3109 GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
3111 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M5_NO_SIM),
3112 GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
3115 GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
3118 GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
3120 {SBIT(GSM322_M1_TRYING_RPLMN),
3121 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3123 {SBIT(GSM322_M1_TRYING_RPLMN),
3124 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3126 {SBIT(GSM322_M1_TRYING_RPLMN),
3127 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3129 {SBIT(GSM322_M1_TRYING_RPLMN),
3130 GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
3132 {SBIT(GSM322_M2_ON_PLMN),
3133 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3135 {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
3136 SBIT(GSM322_M4_TRYING_PLMN),
3137 GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
3139 {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN),
3140 GSM322_EVENT_USER_RESEL, gsm322_m_user_resel},
3142 {SBIT(GSM322_M3_NOT_ON_PLMN),
3143 GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
3145 {SBIT(GSM322_M3_NOT_ON_PLMN),
3146 GSM322_EVENT_CHOOSE_PLMN, gsm322_m_choose_plmn},
3148 {SBIT(GSM322_M4_TRYING_PLMN),
3149 GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
3151 {SBIT(GSM322_M4_TRYING_PLMN),
3152 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3154 {SBIT(GSM322_M4_TRYING_PLMN),
3155 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3157 {SBIT(GSM322_M4_TRYING_PLMN),
3158 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3161 GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
3164 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3167 #define PLMNMSLLEN \
3168 (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
3170 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
3172 struct gsm322_plmn *plmn = &ms->plmn;
3173 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3174 int msg_type = gm->msg_type;
3178 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
3179 "in state '%s'\n", ms->name, get_event_name(msg_type),
3180 plmn_m_state_names[plmn->state]);
3181 /* find function for current state and message */
3182 for (i = 0; i < PLMNMSLLEN; i++)
3183 if ((msg_type == plmnmstatelist[i].type)
3184 && ((1 << plmn->state) & plmnmstatelist[i].states))
3186 if (i == PLMNMSLLEN) {
3187 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3191 rc = plmnmstatelist[i].rout(ms, msg);
3196 /* dequeue GSM 03.22 PLMN events */
3197 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
3199 struct gsm322_plmn *plmn = &ms->plmn;
3203 while ((msg = msgb_dequeue(&plmn->event_queue))) {
3204 /* send event to PLMN select process */
3205 if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
3206 gsm322_a_event(ms, msg);
3208 gsm322_m_event(ms, msg);
3210 work = 1; /* work done */
3216 /* state machine for channel selection events */
3217 static struct cellselstatelist {
3220 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3221 } cellselstatelist[] = {
3223 GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
3226 GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
3229 GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
3232 GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search},
3234 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3235 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
3236 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
3238 {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3239 SBIT(GSM322_C8_ANY_CELL_RESEL),
3240 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
3242 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3243 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3244 SBIT(GSM322_C0_NULL),
3245 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
3247 {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3248 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
3249 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
3251 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3252 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
3254 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3255 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
3257 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3258 GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
3260 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3261 GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
3263 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3264 GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
3266 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3267 GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
3269 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3270 GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
3272 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3273 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3274 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3275 SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH),
3276 GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
3278 {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
3279 GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
3281 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3282 GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search},
3285 #define CELLSELSLLEN \
3286 (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
3288 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
3290 struct gsm322_cellsel *cs = &ms->cellsel;
3291 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3292 int msg_type = gm->msg_type;
3296 if (msg_type != GSM322_EVENT_SYSINFO)
3297 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection "
3298 "in state '%s'\n", ms->name, get_event_name(msg_type),
3299 cs_state_names[cs->state]);
3300 /* find function for current state and message */
3301 for (i = 0; i < CELLSELSLLEN; i++)
3302 if ((msg_type == cellselstatelist[i].type)
3303 && ((1 << cs->state) & cellselstatelist[i].states))
3305 if (i == CELLSELSLLEN) {
3306 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
3310 rc = cellselstatelist[i].rout(ms, msg);
3315 /* dequeue GSM 03.22 cell selection events */
3316 int gsm322_cs_dequeue(struct osmocom_ms *ms)
3318 struct gsm322_cellsel *cs = &ms->cellsel;
3322 while ((msg = msgb_dequeue(&cs->event_queue))) {
3323 /* send event to cell selection process */
3324 gsm322_c_event(ms, msg);
3326 work = 1; /* work done */
3336 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
3338 struct gsm322_plmn *plmn = &ms->plmn;
3339 struct gsm322_plmn_list *temp;
3341 printf("MCC |MNC |allowed|rx-lev\n");
3342 printf("-------+-------+-------+-------\n");
3343 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
3344 printf("%s |%s%s |%s |%s\n", gsm_print_mcc(temp->mcc),
3345 gsm_print_mnc(temp->mnc),
3346 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3347 (temp->cause) ? "no ":"yes",
3348 gsm_print_rxlev(temp->rxlev));
3354 int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
3355 void (*print)(void *, const char *, ...), void *priv)
3358 struct gsm48_sysinfo *s;
3360 print(priv, "arfcn |MCC |MNC |LAC |cell ID|forb.LA|prio |"
3361 "min-db |max-pwr|rx-lev\n");
3362 print(priv, "-------+-------+-------+-------+-------+-------+-------+"
3363 "-------+-------+-------\n");
3364 for (i = 0; i <= 1023; i++) {
3365 s = cs->list[i].sysinfo;
3366 if (!s || !(cs->list[i].flags & flags))
3368 print(priv, "%4d |", i);
3369 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
3370 print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc),
3371 gsm_print_mnc(s->mnc),
3372 ((s->mnc & 0x00f) == 0x00f) ? " ":"");
3373 print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id);
3374 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
3375 print(priv, "yes |");
3377 print(priv, "no |");
3378 if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
3379 print(priv, "barred |");
3381 if (cs->list[i].sysinfo->cell_barr)
3382 print(priv, "low |");
3384 print(priv, "normal |");
3386 print(priv, "%4d |%4d |%s\n", s->rxlev_acc_min_db,
3387 s->ms_txpwr_max_cch,
3388 gsm_print_rxlev(cs->list[i].rxlev));
3390 print(priv, "n/a |n/a |n/a |n/a |n/a |"
3398 int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
3399 void (*print)(void *, const char *, ...), void *priv)
3401 struct gsm322_plmn *plmn = &ms->plmn;
3402 struct gsm322_la_list *temp;
3404 print(priv, "MCC |MNC |LAC |cause\n");
3405 print(priv, "-------+-------+-------+-------\n");
3406 llist_for_each_entry(temp, &plmn->forbidden_la, entry)
3407 print(priv, "%s |%s%s |0x%04x |#%d\n",
3408 gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
3409 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3410 temp->lac, temp->cause);
3415 int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
3416 void (*print)(void *, const char *, ...), void *priv)
3418 struct gsm322_ba_list *ba;
3421 llist_for_each_entry(ba, &cs->ba_list, entry) {
3422 if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc))
3424 print(priv, "Band Allocation of network: MCC %s MNC %s "
3425 "(%s, %s)\n", gsm_print_mcc(ba->mcc),
3426 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3427 gsm_get_mnc(ba->mcc, ba->mnc));
3428 for (i = 0; i <= 1023; i++) {
3429 if ((ba->freq[i >> 3] & (1 << (i & 7))))
3430 print(priv, " %d", i);
3442 int gsm322_init(struct osmocom_ms *ms)
3444 struct gsm322_plmn *plmn = &ms->plmn;
3445 struct gsm322_cellsel *cs = &ms->cellsel;
3449 struct gsm322_ba_list *ba;
3452 LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
3453 LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
3455 memset(plmn, 0, sizeof(*plmn));
3456 memset(cs, 0, sizeof(*cs));
3460 /* set initial state */
3463 ms->settings.plmn_mode = PLMN_MODE_AUTO;
3466 INIT_LLIST_HEAD(&plmn->event_queue);
3467 INIT_LLIST_HEAD(&cs->event_queue);
3468 INIT_LLIST_HEAD(&plmn->sorted_plmn);
3469 INIT_LLIST_HEAD(&plmn->forbidden_la);
3470 INIT_LLIST_HEAD(&cs->ba_list);
3472 /* set supported frequencies in cell selection list */
3473 for (i = 0; i <= 1023; i++)
3474 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
3475 cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
3478 sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3479 fp = fopen(filename, "r");
3484 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
3487 rc = fread(buf, 4, 1, fp);
3492 ba->mcc = (buf[0] << 8) | buf[1];
3493 ba->mnc = (buf[2] << 8) | buf[3];
3494 rc = fread(ba->freq, sizeof(ba->freq), 1, fp);
3499 llist_add_tail(&ba->entry, &cs->ba_list);
3500 LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s "
3501 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3502 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3503 gsm_get_mnc(ba->mcc, ba->mnc));
3507 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
3509 register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3514 int gsm322_exit(struct osmocom_ms *ms)
3516 struct gsm322_plmn *plmn = &ms->plmn;
3517 struct gsm322_cellsel *cs = &ms->cellsel;
3518 struct llist_head *lh, *lh2;
3522 struct gsm322_ba_list *ba;
3526 LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
3527 LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
3529 unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3531 /* stop cell selection process (if any) */
3532 new_c_state(cs, GSM322_C0_NULL);
3536 stop_plmn_timer(plmn);
3539 for (i = 0; i <= 1023; i++) {
3540 if (cs->list[i].sysinfo) {
3541 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
3542 talloc_free(cs->list[i].sysinfo);
3543 cs->list[i].sysinfo = NULL;
3545 cs->list[i].flags = 0;
3549 sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3550 fp = fopen(filename, "w");
3554 llist_for_each_entry(ba, &cs->ba_list, entry) {
3555 buf[0] = ba->mcc >> 8;
3556 buf[1] = ba->mcc & 0xff;
3557 buf[2] = ba->mnc >> 8;
3558 buf[3] = ba->mnc & 0xff;
3559 rc = fwrite(buf, 4, 1, fp);
3560 rc = fwrite(ba->freq, sizeof(ba->freq), 1, fp);
3561 LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s "
3562 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3563 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3564 gsm_get_mnc(ba->mcc, ba->mnc));
3568 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
3571 while ((msg = msgb_dequeue(&plmn->event_queue)))
3573 while ((msg = msgb_dequeue(&cs->event_queue)))
3575 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
3579 llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
3583 llist_for_each_safe(lh, lh2, &cs->ba_list) {