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;
252 // printf("s->ccch_conf %d\n", cs->si->ccch_conf);
254 l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
255 return l1ctl_tx_fbsb_req(ms, cs->arfcn,
256 L1CTL_FBSB_F_FB01SB, 100, 0,
260 static void gsm322_unselect_cell(struct gsm322_cellsel *cs)
264 cs->si->si5 = 0; /* unset SI5* */
266 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
267 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
270 /* print to DCS logging */
271 static void print_dcs(void *priv, const char *fmt, ...)
277 vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
278 buffer[sizeof(buffer) - 1] = '\0';
282 // LOGP(DCS, LOGL_INFO, "%s", buffer);
283 printf("%s", buffer);
286 /* del forbidden LA */
287 int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
288 uint16_t mnc, uint16_t lac)
290 struct gsm322_plmn *plmn = &ms->plmn;
291 struct gsm322_la_list *la;
293 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
294 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
295 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
296 "LAs (mcc=%s, mnc=%s, lac=%04x)\n",
297 gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac);
298 llist_del(&la->entry);
307 /* add forbidden LA */
308 int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
309 uint16_t mnc, uint16_t lac, uint8_t cause)
311 struct gsm322_plmn *plmn = &ms->plmn;
312 struct gsm322_la_list *la;
314 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
315 "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc),
316 gsm_print_mnc(mnc), lac);
317 la = talloc_zero(l23_ctx, struct gsm322_la_list);
324 llist_add_tail(&la->entry, &plmn->forbidden_la);
329 /* search forbidden LA */
330 int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
333 struct gsm322_plmn *plmn = &ms->plmn;
334 struct gsm322_la_list *la;
336 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
337 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
344 /* search for PLMN in all BA lists */
345 static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
346 uint16_t mcc, uint16_t mnc)
348 struct gsm322_ba_list *ba, *ba_found = NULL;
350 /* search for BA list */
351 llist_for_each_entry(ba, &cs->ba_list, entry) {
362 /* search available PLMN */
363 int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
367 for (i = 0; i <= 1023; i++) {
368 if (cs->list[i].sysinfo
369 && cs->list[i].sysinfo->mcc == mcc
370 && cs->list[i].sysinfo->mnc == mnc)
377 /* search available HPLMN */
378 int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi)
382 for (i = 0; i <= 1023; i++) {
383 if (cs->list[i].sysinfo
384 && gsm_match_mnc(cs->list[i].sysinfo->mcc,
385 cs->list[i].sysinfo->mnc, imsi))
392 /* del forbidden LA */
397 /*plmn search timer event */
398 static void plmn_timer_timeout(void *arg)
400 struct gsm322_plmn *plmn = arg;
403 LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
405 /* indicate PLMN selection T timeout */
406 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
409 gsm322_plmn_sendmsg(plmn->ms, nmsg);
412 /* start plmn search timer */
413 static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
415 LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
417 plmn->timer.cb = plmn_timer_timeout;
418 plmn->timer.data = plmn;
419 bsc_schedule_timer(&plmn->timer, secs, 0);
422 /* stop plmn search timer */
423 static void stop_plmn_timer(struct gsm322_plmn *plmn)
425 if (bsc_timer_pending(&plmn->timer)) {
426 LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
427 bsc_del_timer(&plmn->timer);
431 /* start cell selection timer */
432 void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
434 LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec);
435 cs->timer.cb = gsm322_cs_timeout;
437 bsc_schedule_timer(&cs->timer, sec, micro);
440 /* start loss timer */
441 void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro)
444 cs->timer.cb = gsm322_cs_loss;
446 if (bsc_timer_pending(&cs->timer)) {
447 struct timeval current_time;
448 unsigned long long currentTime;
450 gettimeofday(¤t_time, NULL);
451 currentTime = current_time.tv_sec * 1000000LL
452 + current_time.tv_usec;
453 currentTime += sec * 1000000LL + micro;
454 cs->timer.timeout.tv_sec = currentTime / 1000000LL;
455 cs->timer.timeout.tv_usec = currentTime % 1000000LL;
460 LOGP(DCS, LOGL_INFO, "Starting loss CS timer with %d seconds.\n", sec);
461 bsc_schedule_timer(&cs->timer, sec, micro);
464 /* stop cell selection timer */
465 static void stop_cs_timer(struct gsm322_cellsel *cs)
467 if (bsc_timer_pending(&cs->timer)) {
468 LOGP(DCS, LOGL_INFO, "stopping pending CS timer.\n");
469 bsc_del_timer(&cs->timer);
477 const char *plmn_a_state_names[] = {
482 "A4 wait for PLMN to appear",
487 const char *plmn_m_state_names[] = {
496 const char *cs_state_names[] = {
498 "C1 normal cell selection",
499 "C2 stored cell selection",
500 "C3 camped normally",
501 "C4 normal cell re-selection",
503 "C6 any cell selection",
504 "C7 camped on any cell",
505 "C8 any cell re-selection",
506 "C9 choose any cell",
512 /* new automatic PLMN search state */
513 static void new_a_state(struct gsm322_plmn *plmn, int state)
515 if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) {
516 LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
520 stop_plmn_timer(plmn);
522 if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
525 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
526 plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
531 /* new manual PLMN search state */
532 static void new_m_state(struct gsm322_plmn *plmn, int state)
534 if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
535 LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
539 if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
542 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
543 plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
548 /* new Cell selection state */
549 static void new_c_state(struct gsm322_cellsel *cs, int state)
551 if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
554 LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n",
555 cs_state_names[cs->state], cs_state_names[state]);
557 /* stop cell selection timer, if running */
560 /* stop scanning of power measurement */
562 LOGP(DCS, LOGL_INFO, "changing state while power scanning\n");
563 l1ctl_tx_reset_req(cs->ms, L1CTL_RES_T_FULL);
574 /* 4.4.3 create sorted list of PLMN
576 * the source of entries are
579 * - entries found in the SIM's PLMN Selector list
580 * - scanned PLMNs above -85 dB (random order)
581 * - scanned PLMNs below or equal -85 (by received level)
585 * The list only includes networks found at last scan.
587 * The list always contains HPLMN if available, even if not used by PLMN
588 * search process at some conditions.
590 * The list contains all PLMNs even if not allowed, so entries have to be
591 * removed when selecting from the list. (In case we use manual cell selection,
592 * we need to provide non-allowed networks also.)
594 static int gsm322_sort_list(struct osmocom_ms *ms)
596 struct gsm322_plmn *plmn = &ms->plmn;
597 struct gsm322_cellsel *cs = &ms->cellsel;
598 struct gsm_subscriber *subscr = &ms->subscr;
599 struct gsm_sub_plmn_list *sim_entry;
600 struct gsm_sub_plmn_na *na_entry;
601 struct llist_head temp_list;
602 struct gsm322_plmn_list *temp, *found;
603 struct llist_head *lh, *lh2;
604 int i, entries, move;
608 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
613 /* Create a temporary list of all networks */
614 INIT_LLIST_HEAD(&temp_list);
615 for (i = 0; i <= 1023; i++) {
616 if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)
617 || !cs->list[i].sysinfo)
620 /* search if network has multiple cells */
622 llist_for_each_entry(temp, &temp_list, entry) {
623 if (temp->mcc == cs->list[i].sysinfo->mcc
624 && temp->mnc == cs->list[i].sysinfo->mnc) {
629 /* update or create */
631 if (cs->list[i].rxlev > found->rxlev)
632 found->rxlev = cs->list[i].rxlev;
634 temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
637 temp->mcc = cs->list[i].sysinfo->mcc;
638 temp->mnc = cs->list[i].sysinfo->mnc;
639 temp->rxlev = cs->list[i].rxlev;
640 llist_add_tail(&temp->entry, &temp_list);
644 /* move Home PLMN, if in list, else add it */
645 if (subscr->sim_valid) {
647 llist_for_each_entry(temp, &temp_list, entry) {
648 if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) {
655 llist_del(&found->entry);
656 llist_add_tail(&found->entry, &plmn->sorted_plmn);
660 /* move entries if in SIM's PLMN Selector list */
661 llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
663 llist_for_each_entry(temp, &temp_list, entry) {
664 if (temp->mcc == sim_entry->mcc
665 && temp->mnc == sim_entry->mnc) {
671 llist_del(&found->entry);
672 llist_add_tail(&found->entry, &plmn->sorted_plmn);
676 /* move PLMN above -85 dBm in random order */
678 llist_for_each_entry(temp, &temp_list, entry) {
679 if (rxlev2dbm(temp->rxlev) > -85)
683 move = random() % entries;
685 llist_for_each_entry(temp, &temp_list, entry) {
686 if (rxlev2dbm(temp->rxlev) > -85) {
688 llist_del(&temp->entry);
689 llist_add_tail(&temp->entry,
699 /* move ohter PLMN in decreasing order */
702 llist_for_each_entry(temp, &temp_list, entry) {
704 || temp->rxlev > search) {
705 search = temp->rxlev;
711 llist_del(&found->entry);
712 llist_add_tail(&found->entry, &plmn->sorted_plmn);
715 /* mark forbidden PLMNs, if in list of forbidden networks */
717 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
718 llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
719 if (temp->mcc == na_entry->mcc
720 && temp->mnc == na_entry->mnc) {
721 temp->cause = na_entry->cause;
725 LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. "
726 "(%02d: mcc %s mnc %s allowed %s rx-lev %s)\n",
727 i, gsm_print_mcc(temp->mcc),
728 gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes",
729 gsm_print_rxlev(temp->rxlev));
733 gsm322_dump_sorted_plmn(ms);
739 * handler for automatic search
742 /* go On PLMN state */
743 static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
745 struct gsm322_plmn *plmn = &ms->plmn;
746 struct gsm_subscriber *subscr = &ms->subscr;
748 new_a_state(plmn, GSM322_A2_ON_PLMN);
750 /* start timer, if on VPLMN of home country OR special case */
751 if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi)
752 && (subscr->always_search_hplmn
753 || gsm_match_mcc(plmn->mcc, subscr->imsi))
754 && subscr->sim_valid && subscr->t6m_hplmn)
755 start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
757 stop_plmn_timer(plmn);
762 /* indicate selected PLMN */
763 static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
765 struct gsm322_plmn *plmn = &ms->plmn;
767 vty_notify(ms, NULL);
768 vty_notify(ms, "Selected Network: %s, %s\n",
769 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
771 return gsm322_a_go_on_plmn(ms, msg);
774 /* no (more) PLMN in list */
775 static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
777 struct gsm322_plmn *plmn = &ms->plmn;
778 struct gsm322_cellsel *cs = &ms->cellsel;
782 /* any allowable PLMN available? */
783 plmn->mcc = plmn->mnc = 0;
784 found = gsm322_cs_select(ms, 0, 1);
786 /* if no PLMN in list */
788 LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable.\n");
790 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
793 /* we must forward this, otherwhise "Any cell selection"
794 * will not start automatically.
796 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
799 gsm322_cs_sendmsg(ms, nmsg);
801 LOGP(DPLMN, LOGL_INFO, "Trigger full PLMN search.\n");
803 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
806 gsm322_cs_sendmsg(ms, nmsg);
811 /* select first PLMN in list */
812 plmn->mcc = cs->list[found].sysinfo->mcc;
813 plmn->mnc = cs->list[found].sysinfo->mnc;
815 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n",
816 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
817 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
819 /* indicate New PLMN */
820 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
823 gsm322_cs_sendmsg(ms, nmsg);
826 return gsm322_a_indicate_selected(ms, msg);
829 /* select first PLMN in list */
830 static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
832 struct gsm322_plmn *plmn = &ms->plmn;
833 struct gsm_subscriber *subscr = &ms->subscr;
835 struct gsm322_plmn_list *plmn_entry;
836 struct gsm322_plmn_list *plmn_first = NULL;
840 gsm322_sort_list(ms);
842 /* select first entry */
844 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
845 /* if last selected PLMN was HPLMN, we skip that */
846 if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc,
848 && plmn_entry->mcc == plmn->mcc
849 && plmn_entry->mnc == plmn->mnc) {
850 LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was "
851 "previously selected.\n");
855 /* select first allowed network */
856 if (!plmn_entry->cause) {
857 plmn_first = plmn_entry;
860 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
861 "because it is not allowed (cause %d).\n", i,
862 gsm_print_mcc(plmn_entry->mcc),
863 gsm_print_mnc(plmn_entry->mnc),
869 /* if no PLMN in list */
871 LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
872 gsm322_a_no_more_plmn(ms, msg);
877 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
878 "mnc=%s %s, %s)\n", plmn->plmn_curr,
879 gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc),
880 gsm_get_mcc(plmn_first->mcc),
881 gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
883 /* set current network */
884 plmn->mcc = plmn_first->mcc;
885 plmn->mnc = plmn_first->mnc;
887 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
889 /* indicate New PLMN */
890 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
893 gsm322_cs_sendmsg(ms, nmsg);
898 /* select next PLMN in list */
899 static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
901 struct gsm322_plmn *plmn = &ms->plmn;
903 struct gsm322_plmn_list *plmn_entry;
904 struct gsm322_plmn_list *plmn_next = NULL;
907 /* select next entry from list */
909 ii = plmn->plmn_curr + 1;
910 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
911 /* skip previously selected networks */
916 /* select next allowed network */
917 if (!plmn_entry->cause) {
918 plmn_next = plmn_entry;
921 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
922 "because it is not allowed (cause %d).\n", i,
923 gsm_print_mcc(plmn_entry->mcc),
924 gsm_print_mnc(plmn_entry->mnc),
930 /* if no more PLMN in list */
932 LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
933 gsm322_a_no_more_plmn(ms, msg);
937 /* set next network */
938 plmn->mcc = plmn_next->mcc;
939 plmn->mnc = plmn_next->mnc;
941 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
942 "mnc=%s %s, %s)\n", plmn->plmn_curr,
943 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
944 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
946 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
948 /* indicate New PLMN */
949 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
952 gsm322_cs_sendmsg(ms, nmsg);
957 /* User re-selection event */
958 static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg)
960 struct gsm322_plmn *plmn = &ms->plmn;
961 struct gsm_subscriber *subscr = &ms->subscr;
962 struct gsm48_rrlayer *rr = &ms->rrlayer;
963 struct gsm322_plmn_list *plmn_entry;
964 struct gsm322_plmn_list *plmn_found = NULL;
966 if (!subscr->sim_valid) {
970 /* try again later, if not idle */
971 if (rr->state != GSM48_RR_ST_IDLE) {
972 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
977 /* search current PLMN in list */
978 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
979 if (plmn_entry->mcc == plmn->mcc
980 && plmn_entry->mnc == plmn->mnc)
981 plmn_found = plmn_entry;
985 /* abort if list is empty */
987 LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
991 LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
992 "and restarting PLMN search process.\n");
994 /* move entry to end of list */
995 llist_del(&plmn_found->entry);
996 llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
998 /* select first PLMN in list */
999 return gsm322_a_sel_first_plmn(ms, msg);
1002 /* PLMN becomes available */
1003 static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1005 struct gsm322_plmn *plmn = &ms->plmn;
1006 struct gsm_subscriber *subscr = &ms->subscr;
1007 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1009 if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
1010 && subscr->plmn_mnc == gm->mnc) {
1012 plmn->mcc = gm->mcc;
1013 plmn->mnc = gm->mnc;
1014 LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n");
1015 return gsm322_a_go_on_plmn(ms, msg);
1017 /* select first PLMN in list */
1018 LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
1019 "search process.\n");
1020 return gsm322_a_sel_first_plmn(ms, msg);
1024 /* loss of radio coverage */
1025 static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
1027 struct gsm322_plmn *plmn = &ms->plmn;
1028 struct gsm322_cellsel *cs = &ms->cellsel;
1032 /* any PLMN available */
1033 found = gsm322_cs_select(ms, 0, 1);
1035 /* if PLMN in list */
1037 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s "
1038 "%s, %s)\n", gsm_print_mcc(
1039 cs->list[found].sysinfo->mcc),
1040 gsm_print_mnc(cs->list[found].sysinfo->mnc),
1041 gsm_get_mcc(cs->list[found].sysinfo->mcc),
1042 gsm_get_mnc(cs->list[found].sysinfo->mcc,
1043 cs->list[found].sysinfo->mnc));
1044 return gsm322_a_sel_first_plmn(ms, msg);
1047 LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
1049 plmn->mcc = plmn->mnc = 0;
1051 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
1053 /* Tell cell selection process to handle "no cell found". */
1054 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1057 gsm322_cs_sendmsg(ms, nmsg);
1062 /* MS is switched on OR SIM is inserted OR removed */
1063 static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1065 struct gsm_subscriber *subscr = &ms->subscr;
1066 struct gsm322_plmn *plmn = &ms->plmn;
1069 if (!subscr->sim_valid) {
1070 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1071 LOGP(DPLMN, LOGL_INFO, "SIM is removed\n");
1072 new_a_state(plmn, GSM322_A6_NO_SIM);
1077 /* if there is a registered PLMN */
1078 if (subscr->plmn_valid) {
1079 /* select the registered PLMN */
1080 plmn->mcc = subscr->plmn_mcc;
1081 plmn->mnc = subscr->plmn_mnc;
1083 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1084 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1085 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1086 gsm_get_mnc(plmn->mcc, plmn->mnc));
1087 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1088 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1089 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1090 gsm_get_mnc(plmn->mcc, plmn->mnc));
1092 new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
1094 /* indicate New PLMN */
1095 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1098 gsm322_cs_sendmsg(ms, nmsg);
1103 /* initiate search at cell selection */
1104 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1105 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1107 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1110 gsm322_cs_sendmsg(ms, nmsg);
1115 /* MS is switched off */
1116 static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1118 struct gsm322_plmn *plmn = &ms->plmn;
1120 new_a_state(plmn, GSM322_A0_NULL);
1125 static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1127 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1131 /* SIM is removed */
1132 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1136 /* indicate SIM remove to cell selection process */
1137 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1140 gsm322_cs_sendmsg(ms, nmsg);
1142 return gsm322_a_switch_on(ms, msg);
1145 /* location update response: "Roaming not allowed" */
1146 static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
1148 /* store in list of forbidden LAs is done in gsm48* */
1150 return gsm322_a_sel_first_plmn(ms, msg);
1153 /* On VPLMN of home country and timeout occurs */
1154 static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg)
1156 struct gsm48_rrlayer *rr = &ms->rrlayer;
1157 struct gsm322_plmn *plmn = &ms->plmn;
1158 struct gsm322_cellsel *cs = &ms->cellsel;
1161 /* try again later, if not idle and not camping */
1162 if (rr->state != GSM48_RR_ST_IDLE
1163 || cs->state != GSM322_C3_CAMPED_NORMALLY) {
1164 LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n");
1165 start_plmn_timer(plmn, 60);
1170 new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
1172 /* initiate search at cell selection */
1173 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1176 gsm322_cs_sendmsg(ms, nmsg);
1181 /* manual mode selected */
1182 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
1186 /* restart state machine */
1187 gsm322_a_switch_off(ms, msg);
1188 ms->settings.plmn_mode = PLMN_MODE_MANUAL;
1189 gsm322_m_switch_on(ms, msg);
1191 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1194 gsm48_mmevent_msg(ms, nmsg);
1200 * handler for manual search
1203 /* display PLMNs and to Not on PLMN */
1204 static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
1206 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1207 int msg_type = gm->msg_type;
1208 struct gsm322_plmn *plmn = &ms->plmn;
1209 struct gsm_sub_plmn_list *temp;
1212 gsm322_sort_list(ms);
1214 vty_notify(ms, NULL);
1216 case GSM322_EVENT_REG_FAILED:
1217 vty_notify(ms, "Failed to register to network %s, %s "
1219 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1220 gsm_get_mcc(plmn->mcc),
1221 gsm_get_mnc(plmn->mcc, plmn->mnc));
1223 case GSM322_EVENT_NO_CELL_FOUND:
1224 vty_notify(ms, "No cell found for network %s, %s "
1226 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1227 gsm_get_mcc(plmn->mcc),
1228 gsm_get_mnc(plmn->mcc, plmn->mnc));
1230 case GSM322_EVENT_ROAMING_NA:
1231 vty_notify(ms, "Roaming not allowed to network %s, %s "
1233 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1234 gsm_get_mcc(plmn->mcc),
1235 gsm_get_mnc(plmn->mcc, plmn->mnc));
1239 if (llist_empty(&plmn->sorted_plmn))
1240 vty_notify(ms, "Search network!\n");
1242 vty_notify(ms, "Search or select from network:\n");
1243 llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
1244 vty_notify(ms, " Network %s, %s (%s, %s)\n",
1245 gsm_print_mcc(temp->mcc),
1246 gsm_print_mnc(temp->mnc),
1247 gsm_get_mcc(temp->mcc),
1248 gsm_get_mnc(temp->mcc, temp->mnc));
1251 /* go Not on PLMN state */
1252 new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
1257 /* user starts reselection */
1258 static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg)
1260 struct gsm_subscriber *subscr = &ms->subscr;
1261 struct gsm48_rrlayer *rr = &ms->rrlayer;
1264 if (!subscr->sim_valid) {
1268 /* try again later, if not idle */
1269 if (rr->state != GSM48_RR_ST_IDLE) {
1270 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
1275 /* initiate search at cell selection */
1276 vty_notify(ms, NULL);
1277 vty_notify(ms, "Searching Network, please wait...\n");
1278 LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n");
1280 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1283 gsm322_cs_sendmsg(ms, nmsg);
1288 /* MS is switched on OR SIM is inserted OR removed */
1289 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1291 struct gsm_subscriber *subscr = &ms->subscr;
1292 struct gsm322_plmn *plmn = &ms->plmn;
1295 if (!subscr->sim_valid) {
1296 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1297 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1298 new_m_state(plmn, GSM322_M5_NO_SIM);
1303 /* if there is a registered PLMN */
1304 if (subscr->plmn_valid) {
1307 /* select the registered PLMN */
1308 plmn->mcc = subscr->plmn_mcc;
1309 plmn->mnc = subscr->plmn_mnc;
1311 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1312 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1313 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1314 gsm_get_mnc(plmn->mcc, plmn->mnc));
1315 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1316 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1317 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1318 gsm_get_mnc(plmn->mcc, plmn->mnc));
1320 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1322 /* indicate New PLMN */
1323 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1326 gsm322_cs_sendmsg(ms, nmsg);
1331 /* initiate search at cell selection */
1332 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1333 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1334 vty_notify(ms, NULL);
1335 vty_notify(ms, "Searching Network, please wait...\n");
1337 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1340 gsm322_cs_sendmsg(ms, nmsg);
1345 /* MS is switched off */
1346 static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1348 struct gsm322_plmn *plmn = &ms->plmn;
1350 stop_plmn_timer(plmn);
1352 new_m_state(plmn, GSM322_M0_NULL);
1357 static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1359 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1363 /* SIM is removed */
1364 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1366 struct gsm322_plmn *plmn = &ms->plmn;
1369 stop_plmn_timer(plmn);
1371 /* indicate SIM remove to cell selection process */
1372 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1375 gsm322_cs_sendmsg(ms, nmsg);
1377 return gsm322_m_switch_on(ms, msg);
1380 /* go to On PLMN state */
1381 static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1383 struct gsm322_plmn *plmn = &ms->plmn;
1384 struct gsm_subscriber *subscr = &ms->subscr;
1386 /* set last registered PLMN */
1387 subscr->plmn_valid = 1;
1388 subscr->plmn_mcc = plmn->mcc;
1389 subscr->plmn_mnc = plmn->mnc;
1394 new_m_state(plmn, GSM322_M2_ON_PLMN);
1399 /* indicate selected PLMN */
1400 static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
1402 struct gsm322_plmn *plmn = &ms->plmn;
1404 vty_notify(ms, NULL);
1405 vty_notify(ms, "Selected Network: %s, %s\n",
1406 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1408 return gsm322_m_go_on_plmn(ms, msg);
1411 /* previously selected PLMN becomes available again */
1412 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1414 struct gsm322_plmn *plmn = &ms->plmn;
1415 struct gsm322_cellsel *cs = &ms->cellsel;
1417 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1419 if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
1422 LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
1423 "selected, so start selection.\n");
1425 /* indicate New PLMN */
1426 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1429 gsm322_cs_sendmsg(ms, nmsg);
1435 /* the user has selected given PLMN */
1436 static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
1438 struct gsm322_plmn *plmn = &ms->plmn;
1439 struct gsm_subscriber *subscr = &ms->subscr;
1440 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1443 /* use user selection */
1444 plmn->mcc = gm->mcc;
1445 plmn->mnc = gm->mnc;
1447 vty_notify(ms, NULL);
1448 vty_notify(ms, "Selected Network: %s, %s\n",
1449 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1450 LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s "
1451 "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1452 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1454 /* if selected PLMN is in list of forbidden PLMNs */
1455 gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc);
1457 new_m_state(plmn, GSM322_M4_TRYING_PLMN);
1459 /* indicate New PLMN */
1460 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1463 gsm322_cs_sendmsg(ms, nmsg);
1468 /* auto mode selected */
1469 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
1473 /* restart state machine */
1474 gsm322_m_switch_off(ms, msg);
1475 ms->settings.plmn_mode = PLMN_MODE_AUTO;
1476 gsm322_a_switch_on(ms, msg);
1478 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1481 gsm48_mmevent_msg(ms, nmsg);
1486 /* if no cell is found in other states than in *_TRYING_* states */
1487 static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
1491 /* Tell cell selection process to handle "no cell found". */
1492 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1495 gsm322_cs_sendmsg(ms, nmsg);
1501 * cell scanning process
1504 /* select a suitable and allowable cell */
1505 static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed)
1507 struct gsm322_cellsel *cs = &ms->cellsel;
1508 struct gsm_subscriber *subscr = &ms->subscr;
1509 struct gsm48_sysinfo *s;
1510 int i, found = -1, power = 0;
1511 uint8_t flags, mask;
1514 /* set out access class depending on the cell selection type */
1516 acc_class = subscr->acc_class | 0x0400; /* add emergency */
1517 LOGP(DCS, LOGL_INFO, "Select using access class with Emergency "
1520 acc_class = subscr->acc_class;
1521 LOGP(DCS, LOGL_INFO, "Select using access class \n");
1524 /* flags to match */
1525 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1526 | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
1527 if (cs->state == GSM322_C2_STORED_CELL_SEL
1528 || cs->state == GSM322_C5_CHOOSE_CELL)
1529 mask |= GSM322_CS_FLAG_BA;
1530 flags = mask; /* all masked flags are requied */
1532 /* loop through all scanned frequencies and select cell */
1533 for (i = 0; i <= 1023; i++) {
1534 cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
1535 s = cs->list[i].sysinfo;
1537 /* channel has no informations for us */
1538 if (!s || (cs->list[i].flags & mask) != flags) {
1542 /* check C1 criteria not fullfilled */
1543 // TODO: C1 is also dependant on power class and max power
1544 if (rxlev2dbm(cs->list[i].rxlev) < s->rxlev_acc_min_db) {
1545 LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
1546 "not met. (rxlev %s < min %d)\n", i,
1547 gsm_print_rxlev(cs->list[i].rxlev),
1548 s->rxlev_acc_min_db);
1552 /* if cell is barred and we don't override */
1553 if (!subscr->acc_barr
1554 && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
1555 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
1560 /* if cell is in list of forbidden LAs */
1561 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
1562 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1563 "list of forbidden LAs. (mcc=%s mnc=%s "
1564 "lai=%04x)\n", i, gsm_print_mcc(s->mcc),
1565 gsm_print_mnc(s->mnc), s->lac);
1569 /* if cell is in list of forbidden PLMNs */
1570 if (plmn_allowed && gsm_subscr_is_forbidden_plmn(subscr,
1572 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1573 "list of forbidden PLMNs. (mcc=%s mnc=%s)\n", i,
1574 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc));
1578 /* if we have no access to the cell and we don't override */
1579 if (!subscr->acc_barr
1580 && !(acc_class & (s->class_barr ^ 0xffff))) {
1581 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
1582 "barred for out access. (access=%04x "
1583 "barred=%04x)\n", i, acc_class, s->class_barr);
1587 /* store temporary available and allowable flag */
1588 cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
1590 /* if we search a specific PLMN, but it does not match */
1591 if (!any && cs->mcc && (cs->mcc != s->mcc
1592 || cs->mnc != s->mnc)) {
1593 LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
1594 "does not match target PLMN. (mcc=%s "
1595 "mnc=%s)\n", i, gsm_print_mcc(s->mcc),
1596 gsm_print_mnc(s->mnc));
1600 LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%s "
1601 "mcc=%s mnc=%s lac=%04x %s, %s)\n", i,
1602 gsm_print_rxlev(cs->list[i].rxlev),
1603 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac,
1604 gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc));
1606 /* find highest power cell */
1607 if (found < 0 || cs->list[i].rxlev > power) {
1608 power = cs->list[i].rxlev;
1614 LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
1619 /* tune to first/next unscanned frequency and search for PLMN */
1620 static int gsm322_cs_scan(struct osmocom_ms *ms)
1622 struct gsm322_cellsel *cs = &ms->cellsel;
1624 uint8_t mask, flags;
1625 uint32_t weight = 0, test = cs->scan_state;
1627 /* search for strongest unscanned cell */
1628 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1629 | GSM322_CS_FLAG_SIGNAL;
1630 if (cs->state == GSM322_C2_STORED_CELL_SEL
1631 || cs->state == GSM322_C5_CHOOSE_CELL)
1632 mask |= GSM322_CS_FLAG_BA;
1633 flags = mask; /* all masked flags are requied */
1634 for (i = 0; i <= 1023; i++) {
1635 /* skip if band has enough frequencies scanned (3.2.1) */
1636 for (j = 0; gsm_sup_smax[j].max; j++) {
1637 if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
1638 if (gsm_sup_smax[j].start >= i
1639 && gsm_sup_smax[j].end <= i)
1642 if (gsm_sup_smax[j].end <= i
1643 || gsm_sup_smax[j].start >= i)
1647 if (gsm_sup_smax[j].max) {
1648 if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
1651 /* search for unscanned frequency */
1652 if ((cs->list[i].flags & mask) == flags) {
1653 /* weight depends on the power level
1654 * if it is the same, it depends on arfcn
1656 test = cs->list[i].rxlev + 1;
1657 test = (test << 16) | i;
1658 if (test >= cs->scan_state)
1664 cs->scan_state = weight;
1667 gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs,
1670 /* special case for PLMN search */
1671 if (cs->state == GSM322_PLMN_SEARCH && !weight) {
1674 /* create AA flag */
1675 cs->mcc = cs->mnc = 0;
1676 gsm322_cs_select(ms, 0, 0);
1678 new_c_state(cs, GSM322_C0_NULL);
1680 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END);
1681 LOGP(DCS, LOGL_INFO, "PLMN search finished.\n");
1684 gsm322_plmn_sendmsg(ms, nmsg);
1689 /* special case for HPLMN search */
1690 if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
1693 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1694 LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n");
1697 gsm322_plmn_sendmsg(ms, nmsg);
1699 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
1701 cs->arfcn = cs->sel_arfcn;
1702 LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev "
1703 "%s).\n", cs->arfcn,
1704 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
1706 gsm322_sync_to_cell(cs);
1707 // start_cs_timer(cs, ms->support.sync_to, 0);
1712 /* if all frequencies have been searched */
1718 LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
1720 /* just see, if we search for any cell */
1721 if (cs->state == GSM322_C6_ANY_CELL_SEL
1722 || cs->state == GSM322_C8_ANY_CELL_RESEL
1723 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1726 found = gsm322_cs_select(ms, any, 0);
1730 struct gsm322_plmn *plmn = &ms->plmn;
1732 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1735 cs->si = cs->list[cs->arfcn].sysinfo;
1737 gsm322_sync_to_cell(cs);
1739 /* selected PLMN (manual) or any PLMN (auto) */
1740 switch (ms->settings.plmn_mode) {
1741 case PLMN_MODE_AUTO:
1742 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1743 /* PLMN becomes available */
1744 nmsg = gsm322_msgb_alloc(
1745 GSM322_EVENT_PLMN_AVAIL);
1748 gsm322_plmn_sendmsg(ms, nmsg);
1751 case PLMN_MODE_MANUAL:
1752 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1753 && gsm322_is_plmn_avail(cs, plmn->mcc,
1755 /* PLMN becomes available */
1756 nmsg = gsm322_msgb_alloc(
1757 GSM322_EVENT_PLMN_AVAIL);
1760 gsm322_plmn_sendmsg(ms, nmsg);
1765 /* set selected cell */
1767 cs->sel_arfcn = cs->arfcn;
1768 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1769 cs->sel_mcc = cs->si->mcc;
1770 cs->sel_mnc = cs->si->mnc;
1771 cs->sel_lac = cs->si->lac;
1772 cs->sel_id = cs->si->cell_id;
1774 /* tell CS process about available cell */
1775 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1776 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1779 /* unset selected cell */
1780 gsm322_unselect_cell(cs);
1782 /* tell CS process about no cell available */
1783 LOGP(DCS, LOGL_INFO, "No cell available.\n");
1784 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1788 gsm322_c_event(ms, nmsg);
1794 /* NOTE: We might already have system information from previous
1795 * scan. But we need recent informations, so we scan again!
1798 /* Tune to frequency for a while, to receive broadcasts. */
1799 cs->arfcn = weight & 1023;
1800 LOGP(DCS, LOGL_INFO, "Scanning frequency %d (rxlev %s).\n", cs->arfcn,
1801 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
1803 gsm322_sync_to_cell(cs);
1804 // start_cs_timer(cs, ms->support.sync_to, 0);
1806 /* Allocate/clean system information. */
1807 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1808 if (cs->list[cs->arfcn].sysinfo)
1809 memset(cs->list[cs->arfcn].sysinfo, 0,
1810 sizeof(struct gsm48_sysinfo));
1812 cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
1813 struct gsm48_sysinfo);
1814 if (!cs->list[cs->arfcn].sysinfo)
1816 cs->si = cs->list[cs->arfcn].sysinfo;
1818 /* increase scan counter for each maximum scan range */
1819 if (gsm_sup_smax[j].max) {
1820 LOGP(DCS, LOGL_INFO, "%d frequencies left in band %d..%d\n",
1821 gsm_sup_smax[j].max - gsm_sup_smax[j].temp,
1822 gsm_sup_smax[j].start, gsm_sup_smax[j].end);
1823 gsm_sup_smax[j].temp++;
1829 /* check if cell is now suitable and allowable */
1830 static int gsm322_cs_store(struct osmocom_ms *ms)
1832 struct gsm322_cellsel *cs = &ms->cellsel;
1833 struct gsm48_sysinfo *s = cs->si;
1834 struct gsm322_plmn *plmn = &ms->plmn;
1838 if (cs->state != GSM322_C2_STORED_CELL_SEL
1839 && cs->state != GSM322_C1_NORMAL_CELL_SEL
1840 && cs->state != GSM322_C6_ANY_CELL_SEL
1841 && cs->state != GSM322_C4_NORMAL_CELL_RESEL
1842 && cs->state != GSM322_C8_ANY_CELL_RESEL
1843 && cs->state != GSM322_C5_CHOOSE_CELL
1844 && cs->state != GSM322_C9_CHOOSE_ANY_CELL
1845 && cs->state != GSM322_PLMN_SEARCH
1846 && cs->state != GSM322_HPLMN_SEARCH) {
1847 LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
1848 "(re-)selection, please fix!\n");
1853 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO;
1855 && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp &&
1856 cs->list[cs->arfcn].sysinfo->sp_cbq))
1857 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED;
1859 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED;
1862 cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db;
1863 cs->list[cs->arfcn].class_barr = s->class_barr;
1864 cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch;
1867 /* store selected network */
1870 cs->list[cs->arfcn].mcc = s->mcc;
1871 cs->list[cs->arfcn].mnc = s->mnc;
1872 cs->list[cs->arfcn].lac = s->lac;
1875 if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
1876 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD;
1878 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD;
1881 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev %s "
1882 "mcc %s mnc %s lac %04x)\n", cs->arfcn,
1883 gsm_print_rxlev(cs->list[cs->arfcn].rxlev),
1884 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac);
1886 /* special case for PLMN search */
1887 if (cs->state == GSM322_PLMN_SEARCH)
1888 /* tune to next cell */
1889 return gsm322_cs_scan(ms);
1891 /* special case for HPLMN search */
1892 if (cs->state == GSM322_HPLMN_SEARCH) {
1893 struct gsm_subscriber *subscr = &ms->subscr;
1896 if (!gsm322_is_hplmn_avail(cs, subscr->imsi))
1897 /* tune to next cell */
1898 return gsm322_cs_scan(ms);
1900 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1901 LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n");
1904 gsm322_plmn_sendmsg(ms, nmsg);
1909 /* just see, if we search for any cell */
1910 if (cs->state == GSM322_C6_ANY_CELL_SEL
1911 || cs->state == GSM322_C8_ANY_CELL_RESEL
1912 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1915 found = gsm322_cs_select(ms, any, 0);
1919 LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n");
1920 /* tune to next cell */
1921 return gsm322_cs_scan(ms);
1924 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1927 cs->si = cs->list[cs->arfcn].sysinfo;
1929 gsm322_sync_to_cell(cs);
1931 /* selected PLMN (manual) or any PLMN (auto) */
1932 switch (ms->settings.plmn_mode) {
1933 case PLMN_MODE_AUTO:
1934 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1935 /* PLMN becomes available */
1936 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1939 gsm322_plmn_sendmsg(ms, nmsg);
1942 case PLMN_MODE_MANUAL:
1943 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1944 && gsm322_is_plmn_avail(cs, plmn->mcc,
1946 /* PLMN becomes available */
1947 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1950 gsm322_plmn_sendmsg(ms, nmsg);
1955 /* set selected cell */
1957 cs->sel_arfcn = cs->arfcn;
1958 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1959 cs->sel_mcc = cs->si->mcc;
1960 cs->sel_mnc = cs->si->mnc;
1961 cs->sel_lac = cs->si->lac;
1962 cs->sel_id = cs->si->cell_id;
1964 /* tell CS process about available cell */
1965 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1966 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1969 gsm322_c_event(ms, nmsg);
1975 /* process system information when returing to idle mode */
1976 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
1978 struct gsm322_cellsel *cs = &ms->cellsel;
1979 struct gsm48_sysinfo *s = cs->si;
1980 struct gsm322_ba_list *ba = NULL;
1984 /* collect system information received during dedicated mode */
1986 && (!s->nb_ext_ind_si5
1987 || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
1988 || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
1989 && s->nb_ext_ind_si5bis))) {
1990 /* find or create ba list */
1991 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1993 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1998 llist_add_tail(&ba->entry, &cs->ba_list);
2000 /* update (add) ba list */
2001 memset(freq, 0, sizeof(freq));
2002 for (i = 0; i <= 1023; i++) {
2003 if ((s->freq[i].mask & (FREQ_TYPE_SERV
2004 | FREQ_TYPE_NCELL | FREQ_TYPE_REP)))
2005 freq[i >> 3] |= (1 << (i & 7));
2007 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
2008 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
2009 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2010 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2011 gsm_get_mnc(ba->mcc, ba->mnc));
2012 memcpy(ba->freq, freq, sizeof(freq));
2019 /* store BA whenever a system informations changes */
2020 static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
2021 struct gsm48_sysinfo *s)
2023 struct gsm322_ba_list *ba;
2027 /* find or create ba list */
2028 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
2030 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2035 llist_add_tail(&ba->entry, &cs->ba_list);
2037 /* update ba list */
2038 memset(freq, 0, sizeof(freq));
2039 freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
2040 for (i = 0; i <= 1023; i++) {
2041 if ((s->freq[i].mask &
2042 (FREQ_TYPE_SERV | FREQ_TYPE_NCELL | FREQ_TYPE_REP)))
2043 freq[i >> 3] |= (1 << (i & 7));
2045 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
2046 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
2047 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2048 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2049 gsm_get_mnc(ba->mcc, ba->mnc));
2050 memcpy(ba->freq, freq, sizeof(freq));
2056 /* process system information during camping on a cell */
2057 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2059 // struct gsm48_rrlayer *rr = &ms->rrlayer;
2060 struct gsm322_cellsel *cs = &ms->cellsel;
2061 struct gsm48_sysinfo *s = cs->si;
2062 struct gsm_subscriber *subscr = &ms->subscr;
2063 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2067 if (rr->state != GSM48_RR_ST_IDLE) {
2068 LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n");
2073 /* Store BA if we have full system info about cells and neigbor cells.
2074 * Depending on the extended bit in the channel description,
2075 * we require more or less system informations about neighbor cells
2079 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2080 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2081 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2082 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2085 && (!s->nb_ext_ind_si2
2086 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2087 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2088 && s->nb_ext_ind_si2bis)))
2089 gsm322_store_ba_list(cs, s);
2091 /* update sel_si, if all relevant system informations received */
2092 if (s->si1 && s->si2 && s->si3
2093 && (!s->nb_ext_ind_si2
2094 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2095 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2096 && s->nb_ext_ind_si2bis))) {
2098 LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is "
2100 memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
2101 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2105 /* check for barred cell */
2106 if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
2107 /* check if cell becomes barred */
2108 if (!subscr->acc_barr && s->cell_barr
2109 && !(cs->list[cs->arfcn].sysinfo
2110 && cs->list[cs->arfcn].sysinfo->sp
2111 && cs->list[cs->arfcn].sysinfo->sp_cbq)) {
2112 LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
2114 /* mark cell as unscanned */
2115 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2116 if (cs->list[cs->arfcn].sysinfo) {
2117 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2119 talloc_free(cs->list[cs->arfcn].sysinfo);
2120 cs->list[cs->arfcn].sysinfo = NULL;
2121 gsm322_unselect_cell(cs);
2123 /* trigger reselection without queueing,
2124 * because other sysinfo message may be queued
2127 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2130 gsm322_c_event(ms, nmsg);
2135 /* check if cell access becomes barred */
2136 if (!((subscr->acc_class & 0xfbff)
2137 & (s->class_barr ^ 0xffff))) {
2138 LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
2143 /* check if MCC, MNC, LAC, cell ID changes */
2144 if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc
2145 || cs->sel_lac != s->lac) {
2146 LOGP(DCS, LOGL_NOTICE, "Cell changes location area. "
2147 "This is not good!\n");
2150 if (cs->sel_id != s->cell_id) {
2151 LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. "
2152 "This is not good!\n");
2159 /* process system information during channel scanning */
2160 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2162 struct gsm322_cellsel *cs = &ms->cellsel;
2163 struct gsm48_sysinfo *s = cs->si;
2164 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2166 /* no sysinfo if we are not done with power scan */
2167 if (cs->powerscan) {
2168 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
2172 /* Store BA if we have full system info about cells and neigbor cells.
2173 * Depending on the extended bit in the channel description,
2174 * we require more or less system informations about neighbor cells
2178 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2179 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2180 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2181 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2184 && (!s->nb_ext_ind_si2
2185 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2186 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2187 && s->nb_ext_ind_si2bis)))
2188 gsm322_store_ba_list(cs, s);
2190 /* all relevant system informations received */
2191 if (s->si1 && s->si2 && s->si3
2192 && (!s->nb_ext_ind_si2
2193 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2194 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2195 && s->nb_ext_ind_si2bis))) {
2196 LOGP(DCS, LOGL_INFO, "Received relevant sysinfo.\n");
2200 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2202 /* store sysinfo and continue scan */
2203 return gsm322_cs_store(ms);
2206 /* wait for more sysinfo or timeout */
2210 static void gsm322_cs_timeout(void *arg)
2212 struct gsm322_cellsel *cs = arg;
2213 struct osmocom_ms *ms = cs->ms;
2215 LOGP(DCS, LOGL_INFO, "Cell selection failed.\n");
2217 /* if we have no lock, we retry */
2218 if (cs->ccch_state != GSM322_CCCH_ST_SYNC)
2219 LOGP(DCS, LOGL_INFO, "Sync timeout.\n");
2221 LOGP(DCS, LOGL_INFO, "Read timeout.\n");
2223 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev %s)\n",
2224 cs->arfcn, gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
2226 /* remove system information */
2227 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2228 if (cs->list[cs->arfcn].sysinfo) {
2229 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", cs->arfcn);
2230 talloc_free(cs->list[cs->arfcn].sysinfo);
2231 cs->list[cs->arfcn].sysinfo = NULL;
2232 gsm322_unselect_cell(cs);
2235 /* tune to next cell */
2242 * power scan process
2245 /* search for block of unscanned frequencies and start scanning */
2246 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
2248 struct gsm322_cellsel *cs = &ms->cellsel;
2249 struct gsm_settings *set = &ms->settings;
2251 uint8_t mask, flags;
2255 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
2256 flags = GSM322_CS_FLAG_SUPPORT;
2258 /* in case of sticking to a cell, we only select it */
2260 LOGP(DCS, LOGL_FATAL, "Scanning power for sticked cell.\n");
2261 i = set->stick_arfcn;
2262 if ((cs->list[i].flags & mask) == flags)
2265 /* search for first frequency to scan */
2266 if (cs->state == GSM322_C2_STORED_CELL_SEL
2267 || cs->state == GSM322_C5_CHOOSE_CELL) {
2268 LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA "
2270 mask |= GSM322_CS_FLAG_BA;
2271 flags |= GSM322_CS_FLAG_BA;
2273 LOGP(DCS, LOGL_FATAL, "Scanning power for all "
2275 for (i = 0; i <= 1023; i++) {
2276 if ((cs->list[i].flags & mask) == flags) {
2283 /* if there is no more frequency, we can tune to that cell */
2287 /* stop power level scanning */
2290 /* check if not signal is found */
2291 for (i = 0; i <= 1023; i++) {
2292 if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2298 LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
2299 /* on normal cell selection, start over */
2300 if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
2301 for (i = 0; i <= 1023; i++) {
2302 /* clear flag that this was scanned */
2303 cs->list[i].flags &=
2304 ~(GSM322_CS_FLAG_POWER
2305 | GSM322_CS_FLAG_SIGNAL
2306 | GSM322_CS_FLAG_SYSINFO);
2307 if (cs->list[i].sysinfo) {
2308 LOGP(DCS, LOGL_INFO, "free "
2309 "sysinfo arfcn=%d\n",
2312 cs->list[i].sysinfo);
2313 cs->list[i].sysinfo = NULL;
2316 /* no cell selected */
2317 gsm322_unselect_cell(cs);
2320 /* on other cell selection, indicate "no cell found" */
2321 /* NOTE: PLMN search process handles it.
2322 * If not handled there, CS process gets indicated.
2323 * If we would continue to process CS, then we might get
2324 * our list of scanned cells disturbed.
2326 if (cs->state == GSM322_PLMN_SEARCH)
2327 nmsg = gsm322_msgb_alloc(
2328 GSM322_EVENT_PLMN_SEARCH_END);
2330 nmsg = gsm322_msgb_alloc(
2331 GSM322_EVENT_NO_CELL_FOUND);
2334 gsm322_plmn_sendmsg(ms, nmsg);
2336 /* if HPLMN search, select last frequency */
2337 if (cs->state == GSM322_HPLMN_SEARCH) {
2338 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2340 cs->arfcn = cs->sel_arfcn;
2341 LOGP(DCS, LOGL_INFO, "Tuning back to frequency "
2342 "%d (rxlev %s).\n", cs->arfcn,
2344 cs->list[cs->arfcn].rxlev));
2346 gsm322_sync_to_cell(cs);
2347 // start_cs_timer(cs, ms->support.sync_to, 0);
2350 new_c_state(cs, GSM322_C0_NULL);
2354 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
2355 cs->scan_state = 0xffffffff; /* higher than high */
2356 /* clear counter of scanned frequencies of each range */
2357 for (i = 0; gsm_sup_smax[i].max; i++)
2358 gsm_sup_smax[i].temp = 0;
2359 return gsm322_cs_scan(ms);
2362 /* search last frequency to scan (en block) */
2365 for (i = s + 1; i <= 1023; i++) {
2366 if ((cs->list[i].flags & mask) == flags)
2373 LOGP(DCS, LOGL_INFO, "Scanning frequencies. (%d..%d)\n", s, e);
2375 /* start scan on radio interface */
2376 if (!cs->powerscan) {
2377 l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
2380 //#warning TESTING!!!!
2382 return l1ctl_tx_pm_req_range(ms, s, e);
2385 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
2386 void *handler_data, void *signal_data)
2388 struct osmocom_ms *ms;
2389 struct gsm322_cellsel *cs;
2390 struct osmobb_meas_res *mr;
2391 struct osmobb_fbsb_res *fr;
2395 if (subsys != SS_L1CTL)
2399 case S_L1CTL_PM_RES:
2405 i = mr->band_arfcn & 1023;
2407 cs->list[i].rxlev = rxlev;
2408 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2409 /* if minimum level is reached or if we stick to a cell */
2410 if (rxlev2dbm(rxlev) >= ms->support.min_rxlev_db
2411 || ms->settings.stick) {
2412 cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2413 LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2414 "rxlev %s (%d))\n", i,
2415 gsm_print_rxlev(rxlev), rxlev);
2418 case S_L1CTL_PM_DONE:
2419 LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
2424 gsm322_cs_powerscan(ms);
2426 case S_L1CTL_FBSB_RESP:
2430 if (cs->ccch_state == GSM322_CCCH_ST_INIT) {
2431 LOGP(DCS, LOGL_INFO, "Channel synched. (ARFCN=%d, "
2432 "snr=%u, BSIC=%u)\n", cs->arfcn, fr->snr,
2434 cs->ccch_state = GSM322_CCCH_ST_SYNC;
2436 cs->si->bsic = fr->bsic;
2440 /* in dedicated mode */
2441 if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
2442 return gsm48_rr_tx_rand_acc(ms, NULL);
2445 /* set timer for reading BCCH */
2446 if (cs->state == GSM322_C2_STORED_CELL_SEL
2447 || cs->state == GSM322_C1_NORMAL_CELL_SEL
2448 || cs->state == GSM322_C6_ANY_CELL_SEL
2449 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2450 || cs->state == GSM322_C8_ANY_CELL_RESEL
2451 || cs->state == GSM322_C5_CHOOSE_CELL
2452 || cs->state == GSM322_C9_CHOOSE_ANY_CELL
2453 || cs->state == GSM322_PLMN_SEARCH
2454 || cs->state == GSM322_HPLMN_SEARCH)
2455 start_cs_timer(cs, ms->support.scan_to, 0);
2456 // TODO: timer depends on BCCH config
2459 case S_L1CTL_FBSB_ERR:
2463 gsm322_sync_to_cell(cs);
2465 LOGP(DCS, LOGL_INFO, "Channel sync error, try again.\n");
2468 LOGP(DCS, LOGL_INFO, "Channel sync error.\n");
2476 gsm322_cs_timeout(cs);
2480 if (ms->mmlayer.power_off_idle) {
2490 static void gsm322_cs_loss(void *arg)
2492 struct gsm322_cellsel *cs = arg;
2493 struct osmocom_ms *ms = cs->ms;
2494 struct gsm48_rrlayer *rr = &ms->rrlayer;
2496 LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n");
2497 if (cs->state == GSM322_C3_CAMPED_NORMALLY
2498 || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
2499 if (rr->state == GSM48_RR_ST_IDLE) {
2502 LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n");
2504 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2507 gsm322_c_event(ms, nmsg);
2510 LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n");
2512 /* be shure that nothing else is done after here
2513 * because the function call above may cause
2514 * to return from idle state and trigger cell re-sel.
2523 * handler for cell selection process
2526 /* start PLMN search */
2527 static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg)
2529 struct gsm322_cellsel *cs = &ms->cellsel;
2532 new_c_state(cs, GSM322_PLMN_SEARCH);
2534 /* mark all frequencies except our own BA to be scanned */
2535 for (i = 0; i <= 1023; i++) {
2536 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2537 | GSM322_CS_FLAG_SIGNAL
2538 | GSM322_CS_FLAG_SYSINFO);
2539 if (cs->list[i].sysinfo) {
2540 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2541 talloc_free(cs->list[i].sysinfo);
2542 cs->list[i].sysinfo = NULL;
2543 gsm322_unselect_cell(cs);
2547 /* unset selected cell */
2548 gsm322_unselect_cell(cs);
2550 /* start power scan */
2551 return gsm322_cs_powerscan(ms);
2554 /* start HPLMN search */
2555 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2557 struct gsm322_cellsel *cs = &ms->cellsel;
2560 new_c_state(cs, GSM322_HPLMN_SEARCH);
2562 /* mark all frequencies except our own BA to be scanned */
2563 for (i = 0; i <= 1023; i++) {
2564 if (i != cs->sel_arfcn
2565 && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2566 && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2567 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2568 | GSM322_CS_FLAG_SIGNAL
2569 | GSM322_CS_FLAG_SYSINFO);
2570 if (cs->list[i].sysinfo) {
2571 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2573 talloc_free(cs->list[i].sysinfo);
2574 cs->list[i].sysinfo = NULL;
2579 /* no cell selected */
2580 gsm322_unselect_cell(cs);
2582 /* start power scan */
2583 return gsm322_cs_powerscan(ms);
2586 /* start stored cell selection */
2587 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2589 struct gsm322_cellsel *cs = &ms->cellsel;
2592 new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2594 /* flag all frequencies that are in current band allocation */
2595 for (i = 0; i <= 1023; i++) {
2596 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2597 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2599 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2602 /* unset selected cell */
2603 gsm322_unselect_cell(cs);
2605 /* start power scan */
2606 return gsm322_cs_powerscan(ms);
2609 /* start noraml cell selection */
2610 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2612 struct gsm322_cellsel *cs = &ms->cellsel;
2615 /* except for stored cell selection state, we weed to rescan ?? */
2616 if (cs->state != GSM322_C2_STORED_CELL_SEL) {
2617 for (i = 0; i <= 1023; i++) {
2618 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2619 | GSM322_CS_FLAG_SIGNAL
2620 | GSM322_CS_FLAG_SYSINFO);
2621 if (cs->list[i].sysinfo) {
2622 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2624 talloc_free(cs->list[i].sysinfo);
2625 cs->list[i].sysinfo = NULL;
2630 new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2632 /* unset selected cell */
2633 gsm322_unselect_cell(cs);
2635 /* start power scan */
2636 return gsm322_cs_powerscan(ms);
2639 /* start any cell selection */
2640 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2642 struct gsm322_cellsel *cs = &ms->cellsel;
2644 /* in case we already tried any cell (re-)selection, power scan again */
2645 if (cs->state == GSM322_C0_NULL
2646 || cs->state == GSM322_C6_ANY_CELL_SEL
2647 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2650 for (i = 0; i <= 1023; i++) {
2651 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2652 | GSM322_CS_FLAG_SIGNAL
2653 | GSM322_CS_FLAG_SYSINFO);
2654 if (cs->list[i].sysinfo) {
2655 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2657 talloc_free(cs->list[i].sysinfo);
2658 cs->list[i].sysinfo = NULL;
2662 /* after re-selection, indicate no cell found */
2663 if (cs->state == GSM322_C6_ANY_CELL_SEL
2664 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2667 /* tell that we have no cell found */
2668 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2671 gsm48_mmevent_msg(ms, nmsg);
2674 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2677 cs->mcc = cs->mnc = 0;
2679 /* unset selected cell */
2680 gsm322_unselect_cell(cs);
2682 /* start power scan */
2683 return gsm322_cs_powerscan(ms);
2686 /* start noraml cell re-selection */
2687 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2689 struct gsm322_cellsel *cs = &ms->cellsel;
2691 new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2693 /* NOTE: We keep our scan info we have so far.
2694 * This may cause a skip in power scan. */
2696 /* start power scan */
2697 return gsm322_cs_powerscan(ms);
2700 /* start any cell re-selection */
2701 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2703 struct gsm322_cellsel *cs = &ms->cellsel;
2705 new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2707 /* NOTE: We keep our scan info we have so far.
2708 * This may cause a skip in power scan. */
2710 /* start power scan */
2711 return gsm322_cs_powerscan(ms);
2714 /* a suitable cell was found, so we camp normally */
2715 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2717 struct gsm322_cellsel *cs = &ms->cellsel;
2720 LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s "
2721 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2722 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2723 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2725 /* tell that we have selected a (new) cell */
2726 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2729 gsm48_mmevent_msg(ms, nmsg);
2731 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2736 /* a not suitable cell was found, so we camp on any cell */
2737 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2739 struct gsm322_cellsel *cs = &ms->cellsel;
2742 LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s "
2743 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2744 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2745 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2748 /* tell that we have selected a (new) cell */
2749 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2752 gsm48_mmevent_msg(ms, nmsg);
2754 new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2759 /* create temporary ba range with given frequency ranges */
2760 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2761 uint32_t *range, uint8_t ranges)
2763 static struct gsm322_ba_list ba;
2764 uint16_t lower, higher;
2766 memset(&ba, 0, sizeof(ba));
2769 lower = *range & 1023;
2770 higher = (*range >> 16) & 1023;
2772 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2775 ba.freq[lower >> 3] |= 1 << (lower & 7);
2776 if (lower == higher)
2778 lower = (lower + 1) & 1023;
2785 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2786 static int gsm322_cs_choose(struct osmocom_ms *ms)
2788 struct gsm322_cellsel *cs = &ms->cellsel;
2789 struct gsm48_rrlayer *rr = &ms->rrlayer;
2790 struct gsm322_ba_list *ba = NULL;
2793 /* NOTE: The call to this function is synchron to RR layer, so
2794 * we may access the BA range there.
2797 ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges);
2799 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2800 /* get and update BA of last received sysinfo 5* */
2801 ba = gsm322_cs_sysinfo_sacch(ms);
2803 LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored "
2805 ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2813 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2815 /* tell CS to start over */
2816 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2819 gsm322_c_event(ms, nmsg);
2825 /* flag all frequencies that are in current band allocation */
2826 for (i = 0; i <= 1023; i++) {
2827 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2828 if ((ba->freq[i >> 3] & (1 << (i & 7)))) {
2829 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2831 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2834 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2835 | GSM322_CS_FLAG_SIGNAL
2836 | GSM322_CS_FLAG_SYSINFO);
2837 if (cs->list[i].sysinfo) {
2838 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2839 talloc_free(cs->list[i].sysinfo);
2840 cs->list[i].sysinfo = NULL;
2844 /* unset selected cell */
2845 gsm322_unselect_cell(cs);
2847 /* start power scan */
2848 return gsm322_cs_powerscan(ms);
2851 /* start 'Choose cell' after returning to idle mode */
2852 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2854 struct gsm322_cellsel *cs = &ms->cellsel;
2855 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2857 /* After location updating, we choose the last cell */
2858 if (gm->same_cell) {
2861 if (!cs->selected) {
2862 printf("No cell selected when ret.idle, please fix!\n");
2865 cs->arfcn = cs->sel_arfcn;
2867 /* be sure to go to current camping frequency on return */
2868 LOGP(DCS, LOGL_INFO, "Selecting frequency %d. after LOC.UPD.\n",
2871 gsm322_sync_to_cell(cs);
2872 cs->si = cs->list[cs->arfcn].sysinfo;
2874 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2876 /* tell that we have selected the cell, so RR returns IDLE */
2877 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2880 gsm48_mmevent_msg(ms, nmsg);
2885 new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2887 return gsm322_cs_choose(ms);
2890 /* start 'Choose any cell' after returning to idle mode */
2891 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2893 struct gsm322_cellsel *cs = &ms->cellsel;
2895 new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2897 return gsm322_cs_choose(ms);
2900 /* a new PLMN is selected by PLMN search process */
2901 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2903 struct gsm322_cellsel *cs = &ms->cellsel;
2904 struct gsm322_plmn *plmn = &ms->plmn;
2905 struct gsm322_ba_list *ba;
2907 cs->mcc = plmn->mcc;
2908 cs->mnc = plmn->mnc;
2910 LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s "
2911 "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc),
2912 gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc),
2913 gsm_get_mnc(cs->mcc, cs->mnc));
2915 /* search for BA list */
2916 ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2919 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2920 return gsm322_c_stored_cell_sel(ms, ba);
2922 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2923 return gsm322_c_normal_cell_sel(ms, msg);
2927 /* go connected mode */
2928 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2930 struct gsm322_cellsel *cs = &ms->cellsel;
2932 /* check for error */
2935 cs->arfcn = cs->sel_arfcn;
2937 /* be sure to go to current camping frequency on return */
2938 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2940 gsm322_sync_to_cell(cs);
2941 cs->si = cs->list[cs->arfcn].sysinfo;
2946 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2948 struct gsm322_cellsel *cs = &ms->cellsel;
2950 /* check for error */
2953 cs->arfcn = cs->sel_arfcn;
2955 /* be sure to go to current camping frequency on return */
2956 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2958 gsm322_sync_to_cell(cs);
2959 cs->si = cs->list[cs->arfcn].sysinfo;
2965 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2967 struct gsm_subscriber *subscr = &ms->subscr;
2969 /* if no SIM is is MS */
2970 if (!subscr->sim_valid) {
2971 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2972 return gsm322_c_any_cell_sel(ms, msg);
2974 LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2976 /* stay in NULL state until PLMN is selected */
2985 /* state machine for automatic PLMN selection events */
2986 static struct plmnastatelist {
2989 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2990 } plmnastatelist[] = {
2991 {SBIT(GSM322_A0_NULL),
2992 GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2994 /* special case for full search */
2995 {SBIT(GSM322_A0_NULL),
2996 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn},
2999 GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
3001 {SBIT(GSM322_A0_NULL) | SBIT(GSM322_A6_NO_SIM),
3002 GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
3005 GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
3008 GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
3011 GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
3013 {SBIT(GSM322_A1_TRYING_RPLMN),
3014 GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
3016 {SBIT(GSM322_A1_TRYING_RPLMN),
3017 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn},
3019 {SBIT(GSM322_A1_TRYING_RPLMN),
3020 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
3022 {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
3023 GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
3025 {SBIT(GSM322_A2_ON_PLMN),
3026 GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
3028 {SBIT(GSM322_A2_ON_PLMN),
3029 GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start},
3031 {SBIT(GSM322_A2_ON_PLMN),
3032 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
3034 {SBIT(GSM322_A2_ON_PLMN),
3035 GSM322_EVENT_USER_RESEL, gsm322_a_user_resel},
3037 {SBIT(GSM322_A3_TRYING_PLMN),
3038 GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
3040 {SBIT(GSM322_A3_TRYING_PLMN),
3041 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn},
3043 {SBIT(GSM322_A3_TRYING_PLMN),
3044 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
3046 {SBIT(GSM322_A5_HPLMN_SEARCH),
3047 GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn},
3049 {SBIT(GSM322_A5_HPLMN_SEARCH),
3050 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn},
3052 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
3053 GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
3055 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
3056 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_plmn_avail},
3059 GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
3062 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3065 #define PLMNASLLEN \
3066 (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
3068 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
3070 struct gsm322_plmn *plmn = &ms->plmn;
3071 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3072 int msg_type = gm->msg_type;
3076 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
3077 "selection in state '%s'\n", ms->name, get_event_name(msg_type),
3078 plmn_a_state_names[plmn->state]);
3079 /* find function for current state and message */
3080 for (i = 0; i < PLMNASLLEN; i++)
3081 if ((msg_type == plmnastatelist[i].type)
3082 && ((1 << plmn->state) & plmnastatelist[i].states))
3084 if (i == PLMNASLLEN) {
3085 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3089 rc = plmnastatelist[i].rout(ms, msg);
3094 /* state machine for manual PLMN selection events */
3095 static struct plmnmstatelist {
3098 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3099 } plmnmstatelist[] = {
3100 {SBIT(GSM322_M0_NULL),
3101 GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
3103 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) |
3104 SBIT(GSM322_M2_ON_PLMN),
3105 GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns},
3108 GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
3110 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M5_NO_SIM),
3111 GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
3114 GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
3117 GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
3119 {SBIT(GSM322_M1_TRYING_RPLMN),
3120 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3122 {SBIT(GSM322_M1_TRYING_RPLMN),
3123 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3125 {SBIT(GSM322_M1_TRYING_RPLMN),
3126 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3128 {SBIT(GSM322_M1_TRYING_RPLMN),
3129 GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
3131 {SBIT(GSM322_M2_ON_PLMN),
3132 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3134 {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
3135 SBIT(GSM322_M4_TRYING_PLMN),
3136 GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
3138 {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN),
3139 GSM322_EVENT_USER_RESEL, gsm322_m_user_resel},
3141 {SBIT(GSM322_M3_NOT_ON_PLMN),
3142 GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
3144 {SBIT(GSM322_M3_NOT_ON_PLMN),
3145 GSM322_EVENT_CHOOSE_PLMN, gsm322_m_choose_plmn},
3147 {SBIT(GSM322_M4_TRYING_PLMN),
3148 GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
3150 {SBIT(GSM322_M4_TRYING_PLMN),
3151 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3153 {SBIT(GSM322_M4_TRYING_PLMN),
3154 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3156 {SBIT(GSM322_M4_TRYING_PLMN),
3157 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3160 GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
3163 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3166 #define PLMNMSLLEN \
3167 (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
3169 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
3171 struct gsm322_plmn *plmn = &ms->plmn;
3172 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3173 int msg_type = gm->msg_type;
3177 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
3178 "in state '%s'\n", ms->name, get_event_name(msg_type),
3179 plmn_m_state_names[plmn->state]);
3180 /* find function for current state and message */
3181 for (i = 0; i < PLMNMSLLEN; i++)
3182 if ((msg_type == plmnmstatelist[i].type)
3183 && ((1 << plmn->state) & plmnmstatelist[i].states))
3185 if (i == PLMNMSLLEN) {
3186 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3190 rc = plmnmstatelist[i].rout(ms, msg);
3195 /* dequeue GSM 03.22 PLMN events */
3196 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
3198 struct gsm322_plmn *plmn = &ms->plmn;
3202 while ((msg = msgb_dequeue(&plmn->event_queue))) {
3203 /* send event to PLMN select process */
3204 if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
3205 gsm322_a_event(ms, msg);
3207 gsm322_m_event(ms, msg);
3209 work = 1; /* work done */
3215 /* state machine for channel selection events */
3216 static struct cellselstatelist {
3219 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3220 } cellselstatelist[] = {
3222 GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
3225 GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
3228 GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
3231 GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search},
3233 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3234 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
3235 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
3237 {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3238 SBIT(GSM322_C8_ANY_CELL_RESEL),
3239 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
3241 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3242 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3243 SBIT(GSM322_C0_NULL),
3244 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
3246 {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3247 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
3248 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
3250 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3251 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
3253 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3254 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
3256 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3257 GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
3259 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3260 GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
3262 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3263 GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
3265 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3266 GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
3268 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3269 GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
3271 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3272 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3273 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3274 SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH),
3275 GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
3277 {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
3278 GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
3280 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3281 GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search},
3284 #define CELLSELSLLEN \
3285 (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
3287 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
3289 struct gsm322_cellsel *cs = &ms->cellsel;
3290 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3291 int msg_type = gm->msg_type;
3295 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
3296 "'%s'\n", ms->name, get_event_name(msg_type),
3297 cs_state_names[cs->state]);
3298 /* find function for current state and message */
3299 for (i = 0; i < CELLSELSLLEN; i++)
3300 if ((msg_type == cellselstatelist[i].type)
3301 && ((1 << cs->state) & cellselstatelist[i].states))
3303 if (i == CELLSELSLLEN) {
3304 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
3308 rc = cellselstatelist[i].rout(ms, msg);
3313 /* dequeue GSM 03.22 cell selection events */
3314 int gsm322_cs_dequeue(struct osmocom_ms *ms)
3316 struct gsm322_cellsel *cs = &ms->cellsel;
3320 while ((msg = msgb_dequeue(&cs->event_queue))) {
3321 /* send event to cell selection process */
3322 gsm322_c_event(ms, msg);
3324 work = 1; /* work done */
3334 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
3336 struct gsm322_plmn *plmn = &ms->plmn;
3337 struct gsm322_plmn_list *temp;
3339 printf("MCC |MNC |allowed|rx-lev\n");
3340 printf("-------+-------+-------+-------\n");
3341 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
3342 printf("%s |%s%s |%s |%s\n", gsm_print_mcc(temp->mcc),
3343 gsm_print_mnc(temp->mnc),
3344 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3345 (temp->cause) ? "no ":"yes",
3346 gsm_print_rxlev(temp->rxlev));
3352 int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
3353 void (*print)(void *, const char *, ...), void *priv)
3356 struct gsm48_sysinfo *s;
3358 print(priv, "arfcn |MCC |MNC |LAC |cell ID|forb.LA|prio |"
3359 "min-db |max-pwr|rx-lev\n");
3360 print(priv, "-------+-------+-------+-------+-------+-------+-------+"
3361 "-------+-------+-------\n");
3362 for (i = 0; i <= 1023; i++) {
3363 s = cs->list[i].sysinfo;
3364 if (!s || !(cs->list[i].flags & flags))
3366 print(priv, "%4d |", i);
3367 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
3368 print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc),
3369 gsm_print_mnc(s->mnc),
3370 ((s->mnc & 0x00f) == 0x00f) ? " ":"");
3371 print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id);
3372 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
3373 print(priv, "yes |");
3375 print(priv, "no |");
3376 if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
3377 print(priv, "barred |");
3379 if (cs->list[i].sysinfo->cell_barr)
3380 print(priv, "low |");
3382 print(priv, "normal |");
3384 print(priv, "%4d |%4d |%s\n", s->rxlev_acc_min_db,
3385 s->ms_txpwr_max_cch,
3386 gsm_print_rxlev(cs->list[i].rxlev));
3388 print(priv, "n/a |n/a |n/a |n/a |n/a |"
3396 int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
3397 void (*print)(void *, const char *, ...), void *priv)
3399 struct gsm322_plmn *plmn = &ms->plmn;
3400 struct gsm322_la_list *temp;
3402 print(priv, "MCC |MNC |LAC |cause\n");
3403 print(priv, "-------+-------+-------+-------\n");
3404 llist_for_each_entry(temp, &plmn->forbidden_la, entry)
3405 print(priv, "%s |%s%s |0x%04x |#%d\n",
3406 gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
3407 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3408 temp->lac, temp->cause);
3413 int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
3414 void (*print)(void *, const char *, ...), void *priv)
3416 struct gsm322_ba_list *ba;
3419 llist_for_each_entry(ba, &cs->ba_list, entry) {
3420 if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc))
3422 print(priv, "Band Allocation of network: MCC %s MNC %s "
3423 "(%s, %s)\n", gsm_print_mcc(ba->mcc),
3424 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3425 gsm_get_mnc(ba->mcc, ba->mnc));
3426 for (i = 0; i <= 1023; i++) {
3427 if ((ba->freq[i >> 3] & (1 << (i & 7))))
3428 print(priv, " %d", i);
3440 int gsm322_init(struct osmocom_ms *ms)
3442 struct gsm322_plmn *plmn = &ms->plmn;
3443 struct gsm322_cellsel *cs = &ms->cellsel;
3447 struct gsm322_ba_list *ba;
3450 LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
3451 LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
3453 memset(plmn, 0, sizeof(*plmn));
3454 memset(cs, 0, sizeof(*cs));
3458 /* set initial state */
3461 ms->settings.plmn_mode = PLMN_MODE_AUTO;
3464 INIT_LLIST_HEAD(&plmn->event_queue);
3465 INIT_LLIST_HEAD(&cs->event_queue);
3466 INIT_LLIST_HEAD(&plmn->sorted_plmn);
3467 INIT_LLIST_HEAD(&plmn->forbidden_la);
3468 INIT_LLIST_HEAD(&cs->ba_list);
3470 /* set supported frequencies in cell selection list */
3471 for (i = 0; i <= 1023; i++)
3472 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
3473 cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
3476 sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3477 fp = fopen(filename, "r");
3482 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
3485 rc = fread(buf, 4, 1, fp);
3490 ba->mcc = (buf[0] << 8) | buf[1];
3491 ba->mnc = (buf[2] << 8) | buf[3];
3492 rc = fread(ba->freq, sizeof(ba->freq), 1, fp);
3497 llist_add_tail(&ba->entry, &cs->ba_list);
3498 LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s "
3499 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3500 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3501 gsm_get_mnc(ba->mcc, ba->mnc));
3505 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
3507 register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3512 int gsm322_exit(struct osmocom_ms *ms)
3514 struct gsm322_plmn *plmn = &ms->plmn;
3515 struct gsm322_cellsel *cs = &ms->cellsel;
3516 struct llist_head *lh, *lh2;
3520 struct gsm322_ba_list *ba;
3524 LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
3525 LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
3527 unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3529 /* stop cell selection process (if any) */
3530 new_c_state(cs, GSM322_C0_NULL);
3534 stop_plmn_timer(plmn);
3537 for (i = 0; i <= 1023; i++) {
3538 if (cs->list[i].sysinfo) {
3539 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
3540 talloc_free(cs->list[i].sysinfo);
3541 cs->list[i].sysinfo = NULL;
3543 cs->list[i].flags = 0;
3547 sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3548 fp = fopen(filename, "w");
3552 llist_for_each_entry(ba, &cs->ba_list, entry) {
3553 buf[0] = ba->mcc >> 8;
3554 buf[1] = ba->mcc & 0xff;
3555 buf[2] = ba->mnc >> 8;
3556 buf[3] = ba->mnc & 0xff;
3557 rc = fwrite(buf, 4, 1, fp);
3558 rc = fwrite(ba->freq, sizeof(ba->freq), 1, fp);
3559 LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s "
3560 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3561 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3562 gsm_get_mnc(ba->mcc, ba->mnc));
3566 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
3569 while ((msg = msgb_dequeue(&plmn->event_queue)))
3571 while ((msg = msgb_dequeue(&cs->event_queue)))
3573 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
3577 llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
3581 llist_for_each_safe(lh, lh2, &cs->ba_list) {