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 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
265 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
268 /* print to DCS logging */
269 static void print_dcs(void *priv, const char *fmt, ...)
275 vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
276 buffer[sizeof(buffer) - 1] = '\0';
280 // LOGP(DCS, LOGL_INFO, "%s", buffer);
281 printf("%s", buffer);
284 /* del forbidden LA */
285 int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
286 uint16_t mnc, uint16_t lac)
288 struct gsm322_plmn *plmn = &ms->plmn;
289 struct gsm322_la_list *la;
291 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
292 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
293 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
294 "LAs (mcc=%s, mnc=%s, lac=%04x)\n",
295 gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac);
296 llist_del(&la->entry);
305 /* add forbidden LA */
306 int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
307 uint16_t mnc, uint16_t lac, uint8_t cause)
309 struct gsm322_plmn *plmn = &ms->plmn;
310 struct gsm322_la_list *la;
312 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
313 "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc),
314 gsm_print_mnc(mnc), lac);
315 la = talloc_zero(l23_ctx, struct gsm322_la_list);
322 llist_add_tail(&la->entry, &plmn->forbidden_la);
327 /* search forbidden LA */
328 int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
331 struct gsm322_plmn *plmn = &ms->plmn;
332 struct gsm322_la_list *la;
334 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
335 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
342 /* search for PLMN in all BA lists */
343 static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
344 uint16_t mcc, uint16_t mnc)
346 struct gsm322_ba_list *ba, *ba_found = NULL;
348 /* search for BA list */
349 llist_for_each_entry(ba, &cs->ba_list, entry) {
360 /* search available PLMN */
361 int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
365 for (i = 0; i <= 1023; i++) {
366 if (cs->list[i].sysinfo
367 && cs->list[i].sysinfo->mcc == mcc
368 && cs->list[i].sysinfo->mnc == mnc)
375 /* search available HPLMN */
376 int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi)
380 for (i = 0; i <= 1023; i++) {
381 if (cs->list[i].sysinfo
382 && gsm_match_mnc(cs->list[i].sysinfo->mcc,
383 cs->list[i].sysinfo->mnc, imsi))
390 /* del forbidden LA */
395 /*plmn search timer event */
396 static void plmn_timer_timeout(void *arg)
398 struct gsm322_plmn *plmn = arg;
401 LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
403 /* indicate PLMN selection T timeout */
404 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
407 gsm322_plmn_sendmsg(plmn->ms, nmsg);
410 /* start plmn search timer */
411 static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
413 LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
415 plmn->timer.cb = plmn_timer_timeout;
416 plmn->timer.data = plmn;
417 bsc_schedule_timer(&plmn->timer, secs, 0);
420 /* stop plmn search timer */
421 static void stop_plmn_timer(struct gsm322_plmn *plmn)
423 if (bsc_timer_pending(&plmn->timer)) {
424 LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
425 bsc_del_timer(&plmn->timer);
429 /* start cell selection timer */
430 void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
432 LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec);
433 cs->timer.cb = gsm322_cs_timeout;
435 bsc_schedule_timer(&cs->timer, sec, micro);
438 /* start loss timer */
439 void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro)
442 cs->timer.cb = gsm322_cs_loss;
444 if (bsc_timer_pending(&cs->timer)) {
445 struct timeval current_time;
446 unsigned long long currentTime;
448 gettimeofday(¤t_time, NULL);
449 currentTime = current_time.tv_sec * 1000000LL
450 + current_time.tv_usec;
451 currentTime += sec * 1000000LL + micro;
452 cs->timer.timeout.tv_sec = currentTime / 1000000LL;
453 cs->timer.timeout.tv_usec = currentTime % 1000000LL;
458 LOGP(DCS, LOGL_INFO, "Starting loss CS timer with %d seconds.\n", sec);
459 bsc_schedule_timer(&cs->timer, sec, micro);
462 /* stop cell selection timer */
463 static void stop_cs_timer(struct gsm322_cellsel *cs)
465 if (bsc_timer_pending(&cs->timer)) {
466 LOGP(DCS, LOGL_INFO, "stopping pending CS timer.\n");
467 bsc_del_timer(&cs->timer);
475 const char *plmn_a_state_names[] = {
480 "A4 wait for PLMN to appear",
485 const char *plmn_m_state_names[] = {
494 const char *cs_state_names[] = {
496 "C1 normal cell selection",
497 "C2 stored cell selection",
498 "C3 camped normally",
499 "C4 normal cell re-selection",
501 "C6 any cell selection",
502 "C7 camped on any cell",
503 "C8 any cell re-selection",
504 "C9 choose any cell",
510 /* new automatic PLMN search state */
511 static void new_a_state(struct gsm322_plmn *plmn, int state)
513 if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) {
514 LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
518 stop_plmn_timer(plmn);
520 if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
523 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
524 plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
529 /* new manual PLMN search state */
530 static void new_m_state(struct gsm322_plmn *plmn, int state)
532 if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
533 LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
537 if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
540 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
541 plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
546 /* new Cell selection state */
547 static void new_c_state(struct gsm322_cellsel *cs, int state)
549 if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
552 LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n",
553 cs_state_names[cs->state], cs_state_names[state]);
555 /* stop cell selection timer, if running */
558 /* stop scanning of power measurement */
560 LOGP(DCS, LOGL_INFO, "changing state while power scanning\n");
561 l1ctl_tx_reset_req(cs->ms, L1CTL_RES_T_FULL);
572 /* 4.4.3 create sorted list of PLMN
574 * the source of entries are
577 * - entries found in the SIM's PLMN Selector list
578 * - scanned PLMNs above -85 dB (random order)
579 * - scanned PLMNs below or equal -85 (by received level)
583 * The list only includes networks found at last scan.
585 * The list always contains HPLMN if available, even if not used by PLMN
586 * search process at some conditions.
588 * The list contains all PLMNs even if not allowed, so entries have to be
589 * removed when selecting from the list. (In case we use manual cell selection,
590 * we need to provide non-allowed networks also.)
592 static int gsm322_sort_list(struct osmocom_ms *ms)
594 struct gsm322_plmn *plmn = &ms->plmn;
595 struct gsm322_cellsel *cs = &ms->cellsel;
596 struct gsm_subscriber *subscr = &ms->subscr;
597 struct gsm_sub_plmn_list *sim_entry;
598 struct gsm_sub_plmn_na *na_entry;
599 struct llist_head temp_list;
600 struct gsm322_plmn_list *temp, *found;
601 struct llist_head *lh, *lh2;
602 int i, entries, move;
606 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
611 /* Create a temporary list of all networks */
612 INIT_LLIST_HEAD(&temp_list);
613 for (i = 0; i <= 1023; i++) {
614 if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)
615 || !cs->list[i].sysinfo)
618 /* search if network has multiple cells */
620 llist_for_each_entry(temp, &temp_list, entry) {
621 if (temp->mcc == cs->list[i].sysinfo->mcc
622 && temp->mnc == cs->list[i].sysinfo->mnc) {
627 /* update or create */
629 if (cs->list[i].rxlev > found->rxlev)
630 found->rxlev = cs->list[i].rxlev;
632 temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
635 temp->mcc = cs->list[i].sysinfo->mcc;
636 temp->mnc = cs->list[i].sysinfo->mnc;
637 temp->rxlev = cs->list[i].rxlev;
638 llist_add_tail(&temp->entry, &temp_list);
642 /* move Home PLMN, if in list, else add it */
643 if (subscr->sim_valid) {
645 llist_for_each_entry(temp, &temp_list, entry) {
646 if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) {
653 llist_del(&found->entry);
654 llist_add_tail(&found->entry, &plmn->sorted_plmn);
658 /* move entries if in SIM's PLMN Selector list */
659 llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
661 llist_for_each_entry(temp, &temp_list, entry) {
662 if (temp->mcc == sim_entry->mcc
663 && temp->mnc == sim_entry->mnc) {
669 llist_del(&found->entry);
670 llist_add_tail(&found->entry, &plmn->sorted_plmn);
674 /* move PLMN above -85 dBm in random order */
676 llist_for_each_entry(temp, &temp_list, entry) {
677 if (rxlev2dbm(temp->rxlev) > -85)
681 move = random() % entries;
683 llist_for_each_entry(temp, &temp_list, entry) {
684 if (rxlev2dbm(temp->rxlev) > -85) {
686 llist_del(&temp->entry);
687 llist_add_tail(&temp->entry,
697 /* move ohter PLMN in decreasing order */
700 llist_for_each_entry(temp, &temp_list, entry) {
702 || temp->rxlev > search) {
703 search = temp->rxlev;
709 llist_del(&found->entry);
710 llist_add_tail(&found->entry, &plmn->sorted_plmn);
713 /* mark forbidden PLMNs, if in list of forbidden networks */
715 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
716 llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
717 if (temp->mcc == na_entry->mcc
718 && temp->mnc == na_entry->mnc) {
719 temp->cause = na_entry->cause;
723 LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. "
724 "(%02d: mcc %s mnc %s allowed %s rx-lev %s)\n",
725 i, gsm_print_mcc(temp->mcc),
726 gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes",
727 gsm_print_rxlev(temp->rxlev));
731 gsm322_dump_sorted_plmn(ms);
737 * handler for automatic search
740 /* go On PLMN state */
741 static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
743 struct gsm322_plmn *plmn = &ms->plmn;
744 struct gsm_subscriber *subscr = &ms->subscr;
746 new_a_state(plmn, GSM322_A2_ON_PLMN);
748 /* start timer, if on VPLMN of home country OR special case */
749 if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi)
750 && (subscr->always_search_hplmn
751 || gsm_match_mcc(plmn->mcc, subscr->imsi))
752 && subscr->sim_valid && subscr->t6m_hplmn)
753 start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
755 stop_plmn_timer(plmn);
760 /* indicate selected PLMN */
761 static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
763 struct gsm322_plmn *plmn = &ms->plmn;
765 vty_notify(ms, NULL);
766 vty_notify(ms, "Selected Network: %s, %s\n",
767 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
769 return gsm322_a_go_on_plmn(ms, msg);
772 /* no (more) PLMN in list */
773 static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
775 struct gsm322_plmn *plmn = &ms->plmn;
776 struct gsm322_cellsel *cs = &ms->cellsel;
780 /* any allowable PLMN available? */
781 plmn->mcc = plmn->mnc = 0;
782 found = gsm322_cs_select(ms, 0, 1);
784 /* if no PLMN in list */
786 LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable.\n");
788 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
791 /* we must forward this, otherwhise "Any cell selection"
792 * will not start automatically.
794 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
797 gsm322_cs_sendmsg(ms, nmsg);
799 LOGP(DPLMN, LOGL_INFO, "Trigger full PLMN search.\n");
801 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
804 gsm322_cs_sendmsg(ms, nmsg);
809 /* select first PLMN in list */
810 plmn->mcc = cs->list[found].sysinfo->mcc;
811 plmn->mnc = cs->list[found].sysinfo->mnc;
813 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n",
814 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
815 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
817 /* indicate New PLMN */
818 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
821 gsm322_cs_sendmsg(ms, nmsg);
824 return gsm322_a_indicate_selected(ms, msg);
827 /* select first PLMN in list */
828 static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
830 struct gsm322_plmn *plmn = &ms->plmn;
831 struct gsm_subscriber *subscr = &ms->subscr;
833 struct gsm322_plmn_list *plmn_entry;
834 struct gsm322_plmn_list *plmn_first = NULL;
838 gsm322_sort_list(ms);
840 /* select first entry */
842 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
843 /* if last selected PLMN was HPLMN, we skip that */
844 if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc,
846 && plmn_entry->mcc == plmn->mcc
847 && plmn_entry->mnc == plmn->mnc) {
848 LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was "
849 "previously selected.\n");
853 /* select first allowed network */
854 if (!plmn_entry->cause) {
855 plmn_first = plmn_entry;
858 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
859 "because it is not allowed (cause %d).\n", i,
860 gsm_print_mcc(plmn_entry->mcc),
861 gsm_print_mnc(plmn_entry->mnc),
867 /* if no PLMN in list */
869 LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
870 gsm322_a_no_more_plmn(ms, msg);
875 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
876 "mnc=%s %s, %s)\n", plmn->plmn_curr,
877 gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc),
878 gsm_get_mcc(plmn_first->mcc),
879 gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
881 /* set current network */
882 plmn->mcc = plmn_first->mcc;
883 plmn->mnc = plmn_first->mnc;
885 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
887 /* indicate New PLMN */
888 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
891 gsm322_cs_sendmsg(ms, nmsg);
896 /* select next PLMN in list */
897 static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
899 struct gsm322_plmn *plmn = &ms->plmn;
901 struct gsm322_plmn_list *plmn_entry;
902 struct gsm322_plmn_list *plmn_next = NULL;
905 /* select next entry from list */
907 ii = plmn->plmn_curr + 1;
908 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
909 /* skip previously selected networks */
914 /* select next allowed network */
915 if (!plmn_entry->cause) {
916 plmn_next = plmn_entry;
919 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
920 "because it is not allowed (cause %d).\n", i,
921 gsm_print_mcc(plmn_entry->mcc),
922 gsm_print_mnc(plmn_entry->mnc),
928 /* if no more PLMN in list */
930 LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
931 gsm322_a_no_more_plmn(ms, msg);
935 /* set next network */
936 plmn->mcc = plmn_next->mcc;
937 plmn->mnc = plmn_next->mnc;
939 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
940 "mnc=%s %s, %s)\n", plmn->plmn_curr,
941 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
942 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
944 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
946 /* indicate New PLMN */
947 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
950 gsm322_cs_sendmsg(ms, nmsg);
955 /* User re-selection event */
956 static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg)
958 struct gsm322_plmn *plmn = &ms->plmn;
959 struct gsm_subscriber *subscr = &ms->subscr;
960 struct gsm48_rrlayer *rr = &ms->rrlayer;
961 struct gsm322_plmn_list *plmn_entry;
962 struct gsm322_plmn_list *plmn_found = NULL;
964 if (!subscr->sim_valid) {
968 /* try again later, if not idle */
969 if (rr->state != GSM48_RR_ST_IDLE) {
970 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
975 /* search current PLMN in list */
976 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
977 if (plmn_entry->mcc == plmn->mcc
978 && plmn_entry->mnc == plmn->mnc)
979 plmn_found = plmn_entry;
983 /* abort if list is empty */
985 LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
989 LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
990 "and restarting PLMN search process.\n");
992 /* move entry to end of list */
993 llist_del(&plmn_found->entry);
994 llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
996 /* select first PLMN in list */
997 return gsm322_a_sel_first_plmn(ms, msg);
1000 /* PLMN becomes available */
1001 static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1003 struct gsm322_plmn *plmn = &ms->plmn;
1004 struct gsm_subscriber *subscr = &ms->subscr;
1005 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1007 if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
1008 && subscr->plmn_mnc == gm->mnc) {
1010 plmn->mcc = gm->mcc;
1011 plmn->mnc = gm->mnc;
1012 LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n");
1013 return gsm322_a_go_on_plmn(ms, msg);
1015 /* select first PLMN in list */
1016 LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
1017 "search process.\n");
1018 return gsm322_a_sel_first_plmn(ms, msg);
1022 /* loss of radio coverage */
1023 static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
1025 struct gsm322_plmn *plmn = &ms->plmn;
1026 struct gsm322_cellsel *cs = &ms->cellsel;
1030 /* any PLMN available */
1031 found = gsm322_cs_select(ms, 0, 1);
1033 /* if PLMN in list */
1035 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s "
1036 "%s, %s)\n", gsm_print_mcc(
1037 cs->list[found].sysinfo->mcc),
1038 gsm_print_mnc(cs->list[found].sysinfo->mnc),
1039 gsm_get_mcc(cs->list[found].sysinfo->mcc),
1040 gsm_get_mnc(cs->list[found].sysinfo->mcc,
1041 cs->list[found].sysinfo->mnc));
1042 return gsm322_a_sel_first_plmn(ms, msg);
1045 LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
1047 plmn->mcc = plmn->mnc = 0;
1049 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
1051 /* Tell cell selection process to handle "no cell found". */
1052 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1055 gsm322_cs_sendmsg(ms, nmsg);
1060 /* MS is switched on OR SIM is inserted OR removed */
1061 static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1063 struct gsm_subscriber *subscr = &ms->subscr;
1064 struct gsm322_plmn *plmn = &ms->plmn;
1067 if (!subscr->sim_valid) {
1068 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1069 LOGP(DPLMN, LOGL_INFO, "SIM is removed\n");
1070 new_a_state(plmn, GSM322_A6_NO_SIM);
1075 /* if there is a registered PLMN */
1076 if (subscr->plmn_valid) {
1077 /* select the registered PLMN */
1078 plmn->mcc = subscr->plmn_mcc;
1079 plmn->mnc = subscr->plmn_mnc;
1081 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1082 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1083 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1084 gsm_get_mnc(plmn->mcc, plmn->mnc));
1085 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1086 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1087 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1088 gsm_get_mnc(plmn->mcc, plmn->mnc));
1090 new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
1092 /* indicate New PLMN */
1093 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1096 gsm322_cs_sendmsg(ms, nmsg);
1101 /* initiate search at cell selection */
1102 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1103 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1105 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1108 gsm322_cs_sendmsg(ms, nmsg);
1113 /* MS is switched off */
1114 static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1116 struct gsm322_plmn *plmn = &ms->plmn;
1118 new_a_state(plmn, GSM322_A0_NULL);
1123 static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1125 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1129 /* SIM is removed */
1130 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1134 /* indicate SIM remove to cell selection process */
1135 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1138 gsm322_cs_sendmsg(ms, nmsg);
1140 return gsm322_a_switch_on(ms, msg);
1143 /* location update response: "Roaming not allowed" */
1144 static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
1146 /* store in list of forbidden LAs is done in gsm48* */
1148 return gsm322_a_sel_first_plmn(ms, msg);
1151 /* On VPLMN of home country and timeout occurs */
1152 static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg)
1154 struct gsm48_rrlayer *rr = &ms->rrlayer;
1155 struct gsm322_plmn *plmn = &ms->plmn;
1156 struct gsm322_cellsel *cs = &ms->cellsel;
1159 /* try again later, if not idle and not camping */
1160 if (rr->state != GSM48_RR_ST_IDLE
1161 || cs->state != GSM322_C3_CAMPED_NORMALLY) {
1162 LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n");
1163 start_plmn_timer(plmn, 60);
1168 new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
1170 /* initiate search at cell selection */
1171 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1174 gsm322_cs_sendmsg(ms, nmsg);
1179 /* manual mode selected */
1180 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
1184 /* restart state machine */
1185 gsm322_a_switch_off(ms, msg);
1186 ms->settings.plmn_mode = PLMN_MODE_MANUAL;
1187 gsm322_m_switch_on(ms, msg);
1189 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1192 gsm48_mmevent_msg(ms, nmsg);
1198 * handler for manual search
1201 /* display PLMNs and to Not on PLMN */
1202 static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
1204 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1205 int msg_type = gm->msg_type;
1206 struct gsm322_plmn *plmn = &ms->plmn;
1207 struct gsm_sub_plmn_list *temp;
1210 gsm322_sort_list(ms);
1212 vty_notify(ms, NULL);
1214 case GSM322_EVENT_REG_FAILED:
1215 vty_notify(ms, "Failed to register to network %s, %s "
1217 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1218 gsm_get_mcc(plmn->mcc),
1219 gsm_get_mnc(plmn->mcc, plmn->mnc));
1221 case GSM322_EVENT_NO_CELL_FOUND:
1222 vty_notify(ms, "No cell found for network %s, %s "
1224 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1225 gsm_get_mcc(plmn->mcc),
1226 gsm_get_mnc(plmn->mcc, plmn->mnc));
1228 case GSM322_EVENT_ROAMING_NA:
1229 vty_notify(ms, "Roaming not allowed to network %s, %s "
1231 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1232 gsm_get_mcc(plmn->mcc),
1233 gsm_get_mnc(plmn->mcc, plmn->mnc));
1237 if (llist_empty(&plmn->sorted_plmn))
1238 vty_notify(ms, "Search network!\n");
1240 vty_notify(ms, "Search or select from network:\n");
1241 llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
1242 vty_notify(ms, " Network %s, %s (%s, %s)\n",
1243 gsm_print_mcc(temp->mcc),
1244 gsm_print_mnc(temp->mnc),
1245 gsm_get_mcc(temp->mcc),
1246 gsm_get_mnc(temp->mcc, temp->mnc));
1249 /* go Not on PLMN state */
1250 new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
1255 /* user starts reselection */
1256 static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg)
1258 struct gsm_subscriber *subscr = &ms->subscr;
1259 struct gsm48_rrlayer *rr = &ms->rrlayer;
1262 if (!subscr->sim_valid) {
1266 /* try again later, if not idle */
1267 if (rr->state != GSM48_RR_ST_IDLE) {
1268 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
1273 /* initiate search at cell selection */
1274 vty_notify(ms, NULL);
1275 vty_notify(ms, "Searching Network, please wait...\n");
1276 LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n");
1278 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1281 gsm322_cs_sendmsg(ms, nmsg);
1286 /* MS is switched on OR SIM is inserted OR removed */
1287 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1289 struct gsm_subscriber *subscr = &ms->subscr;
1290 struct gsm322_plmn *plmn = &ms->plmn;
1293 if (!subscr->sim_valid) {
1294 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1295 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1296 new_m_state(plmn, GSM322_M5_NO_SIM);
1301 /* if there is a registered PLMN */
1302 if (subscr->plmn_valid) {
1305 /* select the registered PLMN */
1306 plmn->mcc = subscr->plmn_mcc;
1307 plmn->mnc = subscr->plmn_mnc;
1309 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1310 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1311 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1312 gsm_get_mnc(plmn->mcc, plmn->mnc));
1313 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1314 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1315 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1316 gsm_get_mnc(plmn->mcc, plmn->mnc));
1318 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1320 /* indicate New PLMN */
1321 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1324 gsm322_cs_sendmsg(ms, nmsg);
1329 /* initiate search at cell selection */
1330 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1331 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1332 vty_notify(ms, NULL);
1333 vty_notify(ms, "Searching Network, please wait...\n");
1335 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1338 gsm322_cs_sendmsg(ms, nmsg);
1343 /* MS is switched off */
1344 static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1346 struct gsm322_plmn *plmn = &ms->plmn;
1348 stop_plmn_timer(plmn);
1350 new_m_state(plmn, GSM322_M0_NULL);
1355 static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1357 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1361 /* SIM is removed */
1362 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1364 struct gsm322_plmn *plmn = &ms->plmn;
1367 stop_plmn_timer(plmn);
1369 /* indicate SIM remove to cell selection process */
1370 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1373 gsm322_cs_sendmsg(ms, nmsg);
1375 return gsm322_m_switch_on(ms, msg);
1378 /* go to On PLMN state */
1379 static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1381 struct gsm322_plmn *plmn = &ms->plmn;
1382 struct gsm_subscriber *subscr = &ms->subscr;
1384 /* set last registered PLMN */
1385 subscr->plmn_valid = 1;
1386 subscr->plmn_mcc = plmn->mcc;
1387 subscr->plmn_mnc = plmn->mnc;
1392 new_m_state(plmn, GSM322_M2_ON_PLMN);
1397 /* indicate selected PLMN */
1398 static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
1400 struct gsm322_plmn *plmn = &ms->plmn;
1402 vty_notify(ms, NULL);
1403 vty_notify(ms, "Selected Network: %s, %s\n",
1404 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1406 return gsm322_m_go_on_plmn(ms, msg);
1409 /* previously selected PLMN becomes available again */
1410 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1412 struct gsm322_plmn *plmn = &ms->plmn;
1413 struct gsm322_cellsel *cs = &ms->cellsel;
1415 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1417 if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
1420 LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
1421 "selected, so start selection.\n");
1423 /* indicate New PLMN */
1424 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1427 gsm322_cs_sendmsg(ms, nmsg);
1433 /* the user has selected given PLMN */
1434 static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
1436 struct gsm322_plmn *plmn = &ms->plmn;
1437 struct gsm_subscriber *subscr = &ms->subscr;
1438 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1441 /* use user selection */
1442 plmn->mcc = gm->mcc;
1443 plmn->mnc = gm->mnc;
1445 vty_notify(ms, NULL);
1446 vty_notify(ms, "Selected Network: %s, %s\n",
1447 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1448 LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s "
1449 "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1450 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1452 /* if selected PLMN is in list of forbidden PLMNs */
1453 gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc);
1455 new_m_state(plmn, GSM322_M4_TRYING_PLMN);
1457 /* indicate New PLMN */
1458 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1461 gsm322_cs_sendmsg(ms, nmsg);
1466 /* auto mode selected */
1467 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
1471 /* restart state machine */
1472 gsm322_m_switch_off(ms, msg);
1473 ms->settings.plmn_mode = PLMN_MODE_AUTO;
1474 gsm322_a_switch_on(ms, msg);
1476 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1479 gsm48_mmevent_msg(ms, nmsg);
1484 /* if no cell is found in other states than in *_TRYING_* states */
1485 static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
1489 /* Tell cell selection process to handle "no cell found". */
1490 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1493 gsm322_cs_sendmsg(ms, nmsg);
1499 * cell scanning process
1502 /* select a suitable and allowable cell */
1503 static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed)
1505 struct gsm322_cellsel *cs = &ms->cellsel;
1506 struct gsm_subscriber *subscr = &ms->subscr;
1507 struct gsm48_sysinfo *s;
1508 int i, found = -1, power = 0;
1509 uint8_t flags, mask;
1512 /* set out access class depending on the cell selection type */
1514 acc_class = subscr->acc_class | 0x0400; /* add emergency */
1515 LOGP(DCS, LOGL_INFO, "Select using access class with Emergency "
1518 acc_class = subscr->acc_class;
1519 LOGP(DCS, LOGL_INFO, "Select using access class \n");
1522 /* flags to match */
1523 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1524 | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
1525 if (cs->state == GSM322_C2_STORED_CELL_SEL
1526 || cs->state == GSM322_C5_CHOOSE_CELL)
1527 mask |= GSM322_CS_FLAG_BA;
1528 flags = mask; /* all masked flags are requied */
1530 /* loop through all scanned frequencies and select cell */
1531 for (i = 0; i <= 1023; i++) {
1532 cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
1533 s = cs->list[i].sysinfo;
1535 /* channel has no informations for us */
1536 if (!s || (cs->list[i].flags & mask) != flags) {
1540 /* check C1 criteria not fullfilled */
1541 // TODO: C1 is also dependant on power class and max power
1542 if (rxlev2dbm(cs->list[i].rxlev) < s->rxlev_acc_min_db) {
1543 LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
1544 "not met. (rxlev %s < min %d)\n", i,
1545 gsm_print_rxlev(cs->list[i].rxlev),
1546 s->rxlev_acc_min_db);
1550 /* if cell is barred and we don't override */
1551 if (!subscr->acc_barr
1552 && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
1553 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
1558 /* if cell is in list of forbidden LAs */
1559 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
1560 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1561 "list of forbidden LAs. (mcc=%s mnc=%s "
1562 "lai=%04x)\n", i, gsm_print_mcc(s->mcc),
1563 gsm_print_mnc(s->mnc), s->lac);
1567 /* if cell is in list of forbidden PLMNs */
1568 if (plmn_allowed && gsm_subscr_is_forbidden_plmn(subscr,
1570 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1571 "list of forbidden PLMNs. (mcc=%s mnc=%s)\n", i,
1572 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc));
1576 /* if we have no access to the cell and we don't override */
1577 if (!subscr->acc_barr
1578 && !(acc_class & (s->class_barr ^ 0xffff))) {
1579 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
1580 "barred for out access. (access=%04x "
1581 "barred=%04x)\n", i, acc_class, s->class_barr);
1585 /* store temporary available and allowable flag */
1586 cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
1588 /* if we search a specific PLMN, but it does not match */
1589 if (!any && cs->mcc && (cs->mcc != s->mcc
1590 || cs->mnc != s->mnc)) {
1591 LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
1592 "does not match target PLMN. (mcc=%s "
1593 "mnc=%s)\n", i, gsm_print_mcc(s->mcc),
1594 gsm_print_mnc(s->mnc));
1598 LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%s "
1599 "mcc=%s mnc=%s lac=%04x %s, %s)\n", i,
1600 gsm_print_rxlev(cs->list[i].rxlev),
1601 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac,
1602 gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc));
1604 /* find highest power cell */
1605 if (found < 0 || cs->list[i].rxlev > power) {
1606 power = cs->list[i].rxlev;
1612 LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
1617 /* tune to first/next unscanned frequency and search for PLMN */
1618 static int gsm322_cs_scan(struct osmocom_ms *ms)
1620 struct gsm322_cellsel *cs = &ms->cellsel;
1622 uint8_t mask, flags;
1623 uint32_t weight = 0, test = cs->scan_state;
1625 /* search for strongest unscanned cell */
1626 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1627 | GSM322_CS_FLAG_SIGNAL;
1628 if (cs->state == GSM322_C2_STORED_CELL_SEL
1629 || cs->state == GSM322_C5_CHOOSE_CELL)
1630 mask |= GSM322_CS_FLAG_BA;
1631 flags = mask; /* all masked flags are requied */
1632 for (i = 0; i <= 1023; i++) {
1633 /* skip if band has enough frequencies scanned (3.2.1) */
1634 for (j = 0; gsm_sup_smax[j].max; j++) {
1635 if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
1636 if (gsm_sup_smax[j].start >= i
1637 && gsm_sup_smax[j].end <= i)
1640 if (gsm_sup_smax[j].end <= i
1641 || gsm_sup_smax[j].start >= i)
1645 if (gsm_sup_smax[j].max) {
1646 if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
1649 /* search for unscanned frequency */
1650 if ((cs->list[i].flags & mask) == flags) {
1651 /* weight depends on the power level
1652 * if it is the same, it depends on arfcn
1654 test = cs->list[i].rxlev + 1;
1655 test = (test << 16) | i;
1656 if (test >= cs->scan_state)
1662 cs->scan_state = weight;
1665 gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs,
1668 /* special case for PLMN search */
1669 if (cs->state == GSM322_PLMN_SEARCH && !weight) {
1672 /* create AA flag */
1673 cs->mcc = cs->mnc = 0;
1674 gsm322_cs_select(ms, 0, 0);
1676 new_c_state(cs, GSM322_C0_NULL);
1678 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END);
1679 LOGP(DCS, LOGL_INFO, "PLMN search finished.\n");
1682 gsm322_plmn_sendmsg(ms, nmsg);
1687 /* special case for HPLMN search */
1688 if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
1691 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1692 LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n");
1695 gsm322_plmn_sendmsg(ms, nmsg);
1697 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
1699 cs->arfcn = cs->sel_arfcn;
1700 LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev "
1701 "%s).\n", cs->arfcn,
1702 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
1704 gsm322_sync_to_cell(cs);
1705 // start_cs_timer(cs, ms->support.sync_to, 0);
1710 /* if all frequencies have been searched */
1716 LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
1718 /* just see, if we search for any cell */
1719 if (cs->state == GSM322_C6_ANY_CELL_SEL
1720 || cs->state == GSM322_C8_ANY_CELL_RESEL
1721 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1724 found = gsm322_cs_select(ms, any, 0);
1728 struct gsm322_plmn *plmn = &ms->plmn;
1730 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1733 cs->si = cs->list[cs->arfcn].sysinfo;
1735 gsm322_sync_to_cell(cs);
1737 /* selected PLMN (manual) or any PLMN (auto) */
1738 switch (ms->settings.plmn_mode) {
1739 case PLMN_MODE_AUTO:
1740 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1741 /* PLMN becomes available */
1742 nmsg = gsm322_msgb_alloc(
1743 GSM322_EVENT_PLMN_AVAIL);
1746 gsm322_plmn_sendmsg(ms, nmsg);
1749 case PLMN_MODE_MANUAL:
1750 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1751 && gsm322_is_plmn_avail(cs, plmn->mcc,
1753 /* PLMN becomes available */
1754 nmsg = gsm322_msgb_alloc(
1755 GSM322_EVENT_PLMN_AVAIL);
1758 gsm322_plmn_sendmsg(ms, nmsg);
1763 /* set selected cell */
1765 cs->sel_arfcn = cs->arfcn;
1766 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1767 cs->sel_mcc = cs->si->mcc;
1768 cs->sel_mnc = cs->si->mnc;
1769 cs->sel_lac = cs->si->lac;
1770 cs->sel_id = cs->si->cell_id;
1772 /* tell CS process about available cell */
1773 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1774 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1777 /* unset selected cell */
1778 gsm322_unselect_cell(cs);
1780 /* tell CS process about no cell available */
1781 LOGP(DCS, LOGL_INFO, "No cell available.\n");
1782 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1786 gsm322_c_event(ms, nmsg);
1792 /* NOTE: We might already have system information from previous
1793 * scan. But we need recent informations, so we scan again!
1796 /* Tune to frequency for a while, to receive broadcasts. */
1797 cs->arfcn = weight & 1023;
1798 LOGP(DCS, LOGL_INFO, "Scanning frequency %d (rxlev %s).\n", cs->arfcn,
1799 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
1801 gsm322_sync_to_cell(cs);
1802 // start_cs_timer(cs, ms->support.sync_to, 0);
1804 /* Allocate/clean system information. */
1805 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1806 if (cs->list[cs->arfcn].sysinfo)
1807 memset(cs->list[cs->arfcn].sysinfo, 0,
1808 sizeof(struct gsm48_sysinfo));
1810 cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
1811 struct gsm48_sysinfo);
1812 if (!cs->list[cs->arfcn].sysinfo)
1814 cs->si = cs->list[cs->arfcn].sysinfo;
1816 /* increase scan counter for each maximum scan range */
1817 if (gsm_sup_smax[j].max) {
1818 LOGP(DCS, LOGL_INFO, "%d frequencies left in band %d..%d\n",
1819 gsm_sup_smax[j].max - gsm_sup_smax[j].temp,
1820 gsm_sup_smax[j].start, gsm_sup_smax[j].end);
1821 gsm_sup_smax[j].temp++;
1827 /* check if cell is now suitable and allowable */
1828 static int gsm322_cs_store(struct osmocom_ms *ms)
1830 struct gsm322_cellsel *cs = &ms->cellsel;
1831 struct gsm48_sysinfo *s = cs->si;
1832 struct gsm322_plmn *plmn = &ms->plmn;
1836 if (cs->state != GSM322_C2_STORED_CELL_SEL
1837 && cs->state != GSM322_C1_NORMAL_CELL_SEL
1838 && cs->state != GSM322_C6_ANY_CELL_SEL
1839 && cs->state != GSM322_C4_NORMAL_CELL_RESEL
1840 && cs->state != GSM322_C8_ANY_CELL_RESEL
1841 && cs->state != GSM322_C5_CHOOSE_CELL
1842 && cs->state != GSM322_C9_CHOOSE_ANY_CELL
1843 && cs->state != GSM322_PLMN_SEARCH
1844 && cs->state != GSM322_HPLMN_SEARCH) {
1845 LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
1846 "(re-)selection, please fix!\n");
1851 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO;
1853 && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp &&
1854 cs->list[cs->arfcn].sysinfo->sp_cbq))
1855 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED;
1857 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED;
1860 cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db;
1861 cs->list[cs->arfcn].class_barr = s->class_barr;
1862 cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch;
1865 /* store selected network */
1868 cs->list[cs->arfcn].mcc = s->mcc;
1869 cs->list[cs->arfcn].mnc = s->mnc;
1870 cs->list[cs->arfcn].lac = s->lac;
1873 if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
1874 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD;
1876 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD;
1879 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev %s "
1880 "mcc %s mnc %s lac %04x)\n", cs->arfcn,
1881 gsm_print_rxlev(cs->list[cs->arfcn].rxlev),
1882 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac);
1884 /* special case for PLMN search */
1885 if (cs->state == GSM322_PLMN_SEARCH)
1886 /* tune to next cell */
1887 return gsm322_cs_scan(ms);
1889 /* special case for HPLMN search */
1890 if (cs->state == GSM322_HPLMN_SEARCH) {
1891 struct gsm_subscriber *subscr = &ms->subscr;
1894 if (!gsm322_is_hplmn_avail(cs, subscr->imsi))
1895 /* tune to next cell */
1896 return gsm322_cs_scan(ms);
1898 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1899 LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n");
1902 gsm322_plmn_sendmsg(ms, nmsg);
1907 /* just see, if we search for any cell */
1908 if (cs->state == GSM322_C6_ANY_CELL_SEL
1909 || cs->state == GSM322_C8_ANY_CELL_RESEL
1910 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1913 found = gsm322_cs_select(ms, any, 0);
1917 LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n");
1918 /* tune to next cell */
1919 return gsm322_cs_scan(ms);
1922 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1925 cs->si = cs->list[cs->arfcn].sysinfo;
1927 gsm322_sync_to_cell(cs);
1929 /* selected PLMN (manual) or any PLMN (auto) */
1930 switch (ms->settings.plmn_mode) {
1931 case PLMN_MODE_AUTO:
1932 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1933 /* PLMN becomes available */
1934 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1937 gsm322_plmn_sendmsg(ms, nmsg);
1940 case PLMN_MODE_MANUAL:
1941 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1942 && gsm322_is_plmn_avail(cs, plmn->mcc,
1944 /* PLMN becomes available */
1945 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1948 gsm322_plmn_sendmsg(ms, nmsg);
1953 /* set selected cell */
1955 cs->sel_arfcn = cs->arfcn;
1956 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1957 cs->sel_mcc = cs->si->mcc;
1958 cs->sel_mnc = cs->si->mnc;
1959 cs->sel_lac = cs->si->lac;
1960 cs->sel_id = cs->si->cell_id;
1962 /* tell CS process about available cell */
1963 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1964 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1967 gsm322_c_event(ms, nmsg);
1973 /* process system information when returing to idle mode */
1974 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
1976 struct gsm322_cellsel *cs = &ms->cellsel;
1977 struct gsm48_sysinfo *s = cs->si;
1978 struct gsm322_ba_list *ba = NULL;
1982 /* collect system information received during dedicated mode */
1984 && (!s->nb_ext_ind_si5
1985 || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
1986 || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
1987 && s->nb_ext_ind_si5bis))) {
1988 /* find or create ba list */
1989 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1991 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1996 llist_add_tail(&ba->entry, &cs->ba_list);
1998 /* update (add) ba list */
1999 memcpy(freq, ba->freq, sizeof(freq));
2000 for (i = 0; i <= 1023; i++) {
2001 if ((s->freq[i].mask & FREQ_TYPE_REP))
2002 freq[i >> 3] |= (1 << (i & 7));
2004 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
2005 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
2006 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2007 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2008 gsm_get_mnc(ba->mcc, ba->mnc));
2009 memcpy(ba->freq, freq, sizeof(freq));
2016 /* store BA whenever a system informations changes */
2017 static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
2018 struct gsm48_sysinfo *s)
2020 struct gsm322_ba_list *ba;
2024 /* find or create ba list */
2025 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
2027 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2032 llist_add_tail(&ba->entry, &cs->ba_list);
2034 /* update ba list */
2035 memset(freq, 0, sizeof(freq));
2036 freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
2037 for (i = 0; i <= 1023; i++) {
2038 if ((s->freq[i].mask &
2039 (FREQ_TYPE_SERV | FREQ_TYPE_NCELL)))
2040 freq[i >> 3] |= (1 << (i & 7));
2042 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
2043 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
2044 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2045 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2046 gsm_get_mnc(ba->mcc, ba->mnc));
2047 memcpy(ba->freq, freq, sizeof(freq));
2053 /* process system information during camping on a cell */
2054 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2056 // struct gsm48_rrlayer *rr = &ms->rrlayer;
2057 struct gsm322_cellsel *cs = &ms->cellsel;
2058 struct gsm48_sysinfo *s = cs->si;
2059 struct gsm_subscriber *subscr = &ms->subscr;
2060 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2064 if (rr->state != GSM48_RR_ST_IDLE) {
2065 LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n");
2070 /* Store BA if we have full system info about cells and neigbor cells.
2071 * Depending on the extended bit in the channel description,
2072 * we require more or less system informations about neighbor cells
2076 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2077 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2078 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2079 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2082 && (!s->nb_ext_ind_si2
2083 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2084 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2085 && s->nb_ext_ind_si2bis)))
2086 gsm322_store_ba_list(cs, s);
2088 /* update sel_si, if all relevant system informations received */
2089 if (s->si1 && s->si2 && s->si3
2090 && (!s->nb_ext_ind_si2
2091 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2092 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2093 && s->nb_ext_ind_si2bis))) {
2095 LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is "
2097 memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
2098 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2102 /* check for barred cell */
2103 if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
2104 /* check if cell becomes barred */
2105 if (!subscr->acc_barr && s->cell_barr
2106 && !(cs->list[cs->arfcn].sysinfo
2107 && cs->list[cs->arfcn].sysinfo->sp
2108 && cs->list[cs->arfcn].sysinfo->sp_cbq)) {
2109 LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
2111 /* mark cell as unscanned */
2112 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2113 if (cs->list[cs->arfcn].sysinfo) {
2114 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2116 talloc_free(cs->list[cs->arfcn].sysinfo);
2117 cs->list[cs->arfcn].sysinfo = NULL;
2118 gsm322_unselect_cell(cs);
2120 /* trigger reselection without queueing,
2121 * because other sysinfo message may be queued
2124 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2127 gsm322_c_event(ms, nmsg);
2132 /* check if cell access becomes barred */
2133 if (!((subscr->acc_class & 0xfbff)
2134 & (s->class_barr ^ 0xffff))) {
2135 LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
2140 /* check if MCC, MNC, LAC, cell ID changes */
2141 if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc
2142 || cs->sel_lac != s->lac) {
2143 LOGP(DCS, LOGL_NOTICE, "Cell changes location area. "
2144 "This is not good!\n");
2147 if (cs->sel_id != s->cell_id) {
2148 LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. "
2149 "This is not good!\n");
2156 /* process system information during channel scanning */
2157 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2159 struct gsm322_cellsel *cs = &ms->cellsel;
2160 struct gsm48_sysinfo *s = cs->si;
2161 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2163 /* no sysinfo if we are not done with power scan */
2164 if (cs->powerscan) {
2165 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
2169 /* Store BA if we have full system info about cells and neigbor cells.
2170 * Depending on the extended bit in the channel description,
2171 * we require more or less system informations about neighbor cells
2175 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2176 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2177 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2178 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2181 && (!s->nb_ext_ind_si2
2182 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2183 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2184 && s->nb_ext_ind_si2bis)))
2185 gsm322_store_ba_list(cs, s);
2187 /* all relevant system informations received */
2188 if (s->si1 && s->si2 && s->si3
2189 && (!s->nb_ext_ind_si2
2190 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2191 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2192 && s->nb_ext_ind_si2bis))) {
2193 LOGP(DCS, LOGL_INFO, "Received relevant sysinfo.\n");
2197 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2199 /* store sysinfo and continue scan */
2200 return gsm322_cs_store(ms);
2203 /* wait for more sysinfo or timeout */
2207 static void gsm322_cs_timeout(void *arg)
2209 struct gsm322_cellsel *cs = arg;
2210 struct osmocom_ms *ms = cs->ms;
2212 LOGP(DCS, LOGL_INFO, "Cell selection failed.\n");
2214 /* if we have no lock, we retry */
2215 if (cs->ccch_state != GSM322_CCCH_ST_SYNC)
2216 LOGP(DCS, LOGL_INFO, "Sync timeout.\n");
2218 LOGP(DCS, LOGL_INFO, "Read timeout.\n");
2220 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev %s)\n",
2221 cs->arfcn, gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
2223 /* remove system information */
2224 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2225 if (cs->list[cs->arfcn].sysinfo) {
2226 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", cs->arfcn);
2227 talloc_free(cs->list[cs->arfcn].sysinfo);
2228 cs->list[cs->arfcn].sysinfo = NULL;
2229 gsm322_unselect_cell(cs);
2232 /* tune to next cell */
2239 * power scan process
2242 /* search for block of unscanned frequencies and start scanning */
2243 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
2245 struct gsm322_cellsel *cs = &ms->cellsel;
2246 struct gsm_settings *set = &ms->settings;
2248 uint8_t mask, flags;
2252 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
2253 flags = GSM322_CS_FLAG_SUPPORT;
2255 /* in case of sticking to a cell, we only select it */
2257 LOGP(DCS, LOGL_FATAL, "Scanning power for sticked cell.\n");
2258 i = set->stick_arfcn;
2259 if ((cs->list[i].flags & mask) == flags)
2262 /* search for first frequency to scan */
2263 if (cs->state == GSM322_C2_STORED_CELL_SEL
2264 || cs->state == GSM322_C5_CHOOSE_CELL) {
2265 LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA "
2267 mask |= GSM322_CS_FLAG_BA;
2268 flags |= GSM322_CS_FLAG_BA;
2270 LOGP(DCS, LOGL_FATAL, "Scanning power for all "
2272 for (i = 0; i <= 1023; i++) {
2273 if ((cs->list[i].flags & mask) == flags) {
2280 /* if there is no more frequency, we can tune to that cell */
2284 /* stop power level scanning */
2287 /* check if not signal is found */
2288 for (i = 0; i <= 1023; i++) {
2289 if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2295 LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
2296 /* on normal cell selection, start over */
2297 if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
2298 for (i = 0; i <= 1023; i++) {
2299 /* clear flag that this was scanned */
2300 cs->list[i].flags &=
2301 ~(GSM322_CS_FLAG_POWER
2302 | GSM322_CS_FLAG_SIGNAL
2303 | GSM322_CS_FLAG_SYSINFO);
2304 if (cs->list[i].sysinfo) {
2305 LOGP(DCS, LOGL_INFO, "free "
2306 "sysinfo arfcn=%d\n",
2309 cs->list[i].sysinfo);
2310 cs->list[i].sysinfo = NULL;
2313 /* no cell selected */
2314 gsm322_unselect_cell(cs);
2317 /* on other cell selection, indicate "no cell found" */
2318 /* NOTE: PLMN search process handles it.
2319 * If not handled there, CS process gets indicated.
2320 * If we would continue to process CS, then we might get
2321 * our list of scanned cells disturbed.
2323 if (cs->state == GSM322_PLMN_SEARCH)
2324 nmsg = gsm322_msgb_alloc(
2325 GSM322_EVENT_PLMN_SEARCH_END);
2327 nmsg = gsm322_msgb_alloc(
2328 GSM322_EVENT_NO_CELL_FOUND);
2331 gsm322_plmn_sendmsg(ms, nmsg);
2333 /* if HPLMN search, select last frequency */
2334 if (cs->state == GSM322_HPLMN_SEARCH) {
2335 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2337 cs->arfcn = cs->sel_arfcn;
2338 LOGP(DCS, LOGL_INFO, "Tuning back to frequency "
2339 "%d (rxlev %s).\n", cs->arfcn,
2341 cs->list[cs->arfcn].rxlev));
2343 gsm322_sync_to_cell(cs);
2344 // start_cs_timer(cs, ms->support.sync_to, 0);
2347 new_c_state(cs, GSM322_C0_NULL);
2351 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
2352 cs->scan_state = 0xffffffff; /* higher than high */
2353 /* clear counter of scanned frequencies of each range */
2354 for (i = 0; gsm_sup_smax[i].max; i++)
2355 gsm_sup_smax[i].temp = 0;
2356 return gsm322_cs_scan(ms);
2359 /* search last frequency to scan (en block) */
2362 for (i = s + 1; i <= 1023; i++) {
2363 if ((cs->list[i].flags & mask) == flags)
2370 LOGP(DCS, LOGL_INFO, "Scanning frequencies. (%d..%d)\n", s, e);
2372 /* start scan on radio interface */
2373 if (!cs->powerscan) {
2374 l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
2377 //#warning TESTING!!!!
2379 return l1ctl_tx_pm_req_range(ms, s, e);
2382 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
2383 void *handler_data, void *signal_data)
2385 struct osmocom_ms *ms;
2386 struct gsm322_cellsel *cs;
2387 struct osmobb_meas_res *mr;
2391 if (subsys != SS_L1CTL)
2395 case S_L1CTL_PM_RES:
2401 i = mr->band_arfcn & 1023;
2403 cs->list[i].rxlev = rxlev;
2404 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2405 /* if minimum level is reached or if we stick to a cell */
2406 if (rxlev2dbm(rxlev) >= ms->support.min_rxlev_db
2407 || ms->settings.stick) {
2408 cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2409 LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2410 "rxlev %s (%d))\n", i,
2411 gsm_print_rxlev(rxlev), rxlev);
2414 case S_L1CTL_PM_DONE:
2415 LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
2420 gsm322_cs_powerscan(ms);
2422 case S_L1CTL_FBSB_RESP:
2425 if (cs->ccch_state == GSM322_CCCH_ST_INIT) {
2426 LOGP(DCS, LOGL_INFO, "Channel synched. (ARFCN=%d)\n",
2428 cs->ccch_state = GSM322_CCCH_ST_SYNC;
2432 /* in dedicated mode */
2433 if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
2434 return gsm48_rr_tx_rand_acc(ms, NULL);
2437 /* set timer for reading BCCH */
2438 if (cs->state == GSM322_C2_STORED_CELL_SEL
2439 || cs->state == GSM322_C1_NORMAL_CELL_SEL
2440 || cs->state == GSM322_C6_ANY_CELL_SEL
2441 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2442 || cs->state == GSM322_C8_ANY_CELL_RESEL
2443 || cs->state == GSM322_C5_CHOOSE_CELL
2444 || cs->state == GSM322_C9_CHOOSE_ANY_CELL
2445 || cs->state == GSM322_PLMN_SEARCH
2446 || cs->state == GSM322_HPLMN_SEARCH)
2447 start_cs_timer(cs, ms->support.scan_to, 0);
2448 // TODO: timer depends on BCCH config
2451 case S_L1CTL_FBSB_ERR:
2455 gsm322_sync_to_cell(cs);
2457 LOGP(DCS, LOGL_INFO, "Channel sync error, try again.\n");
2460 LOGP(DCS, LOGL_INFO, "Channel sync error.\n");
2468 gsm322_cs_timeout(cs);
2472 if (ms->mmlayer.power_off_idle) {
2482 static void gsm322_cs_loss(void *arg)
2484 struct gsm322_cellsel *cs = arg;
2485 struct osmocom_ms *ms = cs->ms;
2486 struct gsm48_rrlayer *rr = &ms->rrlayer;
2488 LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n");
2489 if (cs->state == GSM322_C3_CAMPED_NORMALLY
2490 || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
2491 if (rr->state == GSM48_RR_ST_IDLE) {
2494 LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n");
2496 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2499 gsm322_c_event(ms, nmsg);
2502 LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n");
2504 /* be shure that nothing else is done after here
2505 * because the function call above may cause
2506 * to return from idle state and trigger cell re-sel.
2515 * handler for cell selection process
2518 /* start PLMN search */
2519 static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg)
2521 struct gsm322_cellsel *cs = &ms->cellsel;
2524 new_c_state(cs, GSM322_PLMN_SEARCH);
2526 /* mark all frequencies except our own BA to be scanned */
2527 for (i = 0; i <= 1023; i++) {
2528 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2529 | GSM322_CS_FLAG_SIGNAL
2530 | GSM322_CS_FLAG_SYSINFO);
2531 if (cs->list[i].sysinfo) {
2532 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2533 talloc_free(cs->list[i].sysinfo);
2534 cs->list[i].sysinfo = NULL;
2535 gsm322_unselect_cell(cs);
2539 /* unset selected cell */
2540 gsm322_unselect_cell(cs);
2542 /* start power scan */
2543 return gsm322_cs_powerscan(ms);
2546 /* start HPLMN search */
2547 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2549 struct gsm322_cellsel *cs = &ms->cellsel;
2552 new_c_state(cs, GSM322_HPLMN_SEARCH);
2554 /* mark all frequencies except our own BA to be scanned */
2555 for (i = 0; i <= 1023; i++) {
2556 if (i != cs->sel_arfcn
2557 && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2558 && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2559 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2560 | GSM322_CS_FLAG_SIGNAL
2561 | GSM322_CS_FLAG_SYSINFO);
2562 if (cs->list[i].sysinfo) {
2563 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2565 talloc_free(cs->list[i].sysinfo);
2566 cs->list[i].sysinfo = NULL;
2571 /* no cell selected */
2572 gsm322_unselect_cell(cs);
2574 /* start power scan */
2575 return gsm322_cs_powerscan(ms);
2578 /* start stored cell selection */
2579 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2581 struct gsm322_cellsel *cs = &ms->cellsel;
2584 new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2586 /* flag all frequencies that are in current band allocation */
2587 for (i = 0; i <= 1023; i++) {
2588 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2589 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2591 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2594 /* unset selected cell */
2595 gsm322_unselect_cell(cs);
2597 /* start power scan */
2598 return gsm322_cs_powerscan(ms);
2601 /* start noraml cell selection */
2602 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2604 struct gsm322_cellsel *cs = &ms->cellsel;
2607 /* except for stored cell selection state, we weed to rescan ?? */
2608 if (cs->state != GSM322_C2_STORED_CELL_SEL) {
2609 for (i = 0; i <= 1023; i++) {
2610 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2611 | GSM322_CS_FLAG_SIGNAL
2612 | GSM322_CS_FLAG_SYSINFO);
2613 if (cs->list[i].sysinfo) {
2614 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2616 talloc_free(cs->list[i].sysinfo);
2617 cs->list[i].sysinfo = NULL;
2622 new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2624 /* unset selected cell */
2625 gsm322_unselect_cell(cs);
2627 /* start power scan */
2628 return gsm322_cs_powerscan(ms);
2631 /* start any cell selection */
2632 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2634 struct gsm322_cellsel *cs = &ms->cellsel;
2636 /* in case we already tried any cell (re-)selection, power scan again */
2637 if (cs->state == GSM322_C0_NULL
2638 || cs->state == GSM322_C6_ANY_CELL_SEL
2639 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2642 for (i = 0; i <= 1023; i++) {
2643 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2644 | GSM322_CS_FLAG_SIGNAL
2645 | GSM322_CS_FLAG_SYSINFO);
2646 if (cs->list[i].sysinfo) {
2647 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2649 talloc_free(cs->list[i].sysinfo);
2650 cs->list[i].sysinfo = NULL;
2654 /* after re-selection, indicate no cell found */
2655 if (cs->state == GSM322_C6_ANY_CELL_SEL
2656 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2659 /* tell that we have no cell found */
2660 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2663 gsm48_mmevent_msg(ms, nmsg);
2666 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2669 cs->mcc = cs->mnc = 0;
2671 /* unset selected cell */
2672 gsm322_unselect_cell(cs);
2674 /* start power scan */
2675 return gsm322_cs_powerscan(ms);
2678 /* start noraml cell re-selection */
2679 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2681 struct gsm322_cellsel *cs = &ms->cellsel;
2683 new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2685 /* NOTE: We keep our scan info we have so far.
2686 * This may cause a skip in power scan. */
2688 /* start power scan */
2689 return gsm322_cs_powerscan(ms);
2692 /* start any cell re-selection */
2693 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2695 struct gsm322_cellsel *cs = &ms->cellsel;
2697 new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2699 /* NOTE: We keep our scan info we have so far.
2700 * This may cause a skip in power scan. */
2702 /* start power scan */
2703 return gsm322_cs_powerscan(ms);
2706 /* a suitable cell was found, so we camp normally */
2707 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2709 struct gsm322_cellsel *cs = &ms->cellsel;
2712 LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s "
2713 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2714 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2715 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2717 /* tell that we have selected a (new) cell */
2718 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2721 gsm48_mmevent_msg(ms, nmsg);
2723 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2728 /* a not suitable cell was found, so we camp on any cell */
2729 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2731 struct gsm322_cellsel *cs = &ms->cellsel;
2734 LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s "
2735 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2736 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2737 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2740 /* tell that we have selected a (new) cell */
2741 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2744 gsm48_mmevent_msg(ms, nmsg);
2746 new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2751 /* create temporary ba range with given frequency ranges */
2752 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2753 uint32_t *range, uint8_t ranges)
2755 static struct gsm322_ba_list ba;
2756 uint16_t lower, higher;
2758 memset(&ba, 0, sizeof(ba));
2761 lower = *range & 1023;
2762 higher = (*range >> 16) & 1023;
2764 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2767 ba.freq[lower >> 3] |= 1 << (lower & 7);
2768 if (lower == higher)
2770 lower = (lower + 1) & 1023;
2777 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2778 static int gsm322_cs_choose(struct osmocom_ms *ms)
2780 struct gsm322_cellsel *cs = &ms->cellsel;
2781 struct gsm48_rrlayer *rr = &ms->rrlayer;
2782 struct gsm322_ba_list *ba = NULL;
2785 /* NOTE: The call to this function is synchron to RR layer, so
2786 * we may access the BA range there.
2789 ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges);
2791 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2792 /* get and update BA of last received sysinfo 5* */
2793 ba = gsm322_cs_sysinfo_sacch(ms);
2795 LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored "
2797 ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2805 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2807 /* tell CS to start over */
2808 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2811 gsm322_c_event(ms, nmsg);
2817 /* flag all frequencies that are in current band allocation */
2818 for (i = 0; i <= 1023; i++) {
2819 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2820 if ((ba->freq[i >> 3] & (1 << (i & 7)))) {
2821 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2823 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2826 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2827 | GSM322_CS_FLAG_SIGNAL
2828 | GSM322_CS_FLAG_SYSINFO);
2829 if (cs->list[i].sysinfo) {
2830 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2831 talloc_free(cs->list[i].sysinfo);
2832 cs->list[i].sysinfo = NULL;
2836 /* unset selected cell */
2837 gsm322_unselect_cell(cs);
2839 /* start power scan */
2840 return gsm322_cs_powerscan(ms);
2843 /* start 'Choose cell' after returning to idle mode */
2844 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2846 struct gsm322_cellsel *cs = &ms->cellsel;
2847 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2849 /* After location updating, we choose the last cell */
2850 if (gm->same_cell) {
2853 if (!cs->selected) {
2854 printf("No cell selected when ret.idle, please fix!\n");
2857 cs->arfcn = cs->sel_arfcn;
2859 /* be sure to go to current camping frequency on return */
2860 LOGP(DCS, LOGL_INFO, "Selecting frequency %d. after LOC.UPD.\n",
2863 gsm322_sync_to_cell(cs);
2864 cs->si = cs->list[cs->arfcn].sysinfo;
2866 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2868 /* tell that we have selected the cell, so RR returns IDLE */
2869 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2872 gsm48_mmevent_msg(ms, nmsg);
2877 new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2879 return gsm322_cs_choose(ms);
2882 /* start 'Choose any cell' after returning to idle mode */
2883 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2885 struct gsm322_cellsel *cs = &ms->cellsel;
2887 new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2889 return gsm322_cs_choose(ms);
2892 /* a new PLMN is selected by PLMN search process */
2893 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2895 struct gsm322_cellsel *cs = &ms->cellsel;
2896 struct gsm322_plmn *plmn = &ms->plmn;
2897 struct gsm322_ba_list *ba;
2899 cs->mcc = plmn->mcc;
2900 cs->mnc = plmn->mnc;
2902 LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s "
2903 "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc),
2904 gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc),
2905 gsm_get_mnc(cs->mcc, cs->mnc));
2907 /* search for BA list */
2908 ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2911 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2912 return gsm322_c_stored_cell_sel(ms, ba);
2914 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2915 return gsm322_c_normal_cell_sel(ms, msg);
2919 /* go connected mode */
2920 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2922 struct gsm322_cellsel *cs = &ms->cellsel;
2924 /* check for error */
2927 cs->arfcn = cs->sel_arfcn;
2929 /* be sure to go to current camping frequency on return */
2930 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2932 gsm322_sync_to_cell(cs);
2933 cs->si = cs->list[cs->arfcn].sysinfo;
2938 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2940 struct gsm322_cellsel *cs = &ms->cellsel;
2942 /* check for error */
2945 cs->arfcn = cs->sel_arfcn;
2947 /* be sure to go to current camping frequency on return */
2948 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2950 gsm322_sync_to_cell(cs);
2951 cs->si = cs->list[cs->arfcn].sysinfo;
2957 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2959 struct gsm_subscriber *subscr = &ms->subscr;
2961 /* if no SIM is is MS */
2962 if (!subscr->sim_valid) {
2963 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2964 return gsm322_c_any_cell_sel(ms, msg);
2965 LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2968 /* stay in NULL state until PLMN is selected */
2977 /* state machine for automatic PLMN selection events */
2978 static struct plmnastatelist {
2981 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2982 } plmnastatelist[] = {
2983 {SBIT(GSM322_A0_NULL),
2984 GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2986 /* special case for full search */
2987 {SBIT(GSM322_A0_NULL),
2988 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn},
2991 GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
2993 {SBIT(GSM322_A6_NO_SIM),
2994 GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
2997 GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
3000 GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
3003 GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
3005 {SBIT(GSM322_A1_TRYING_RPLMN),
3006 GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
3008 {SBIT(GSM322_A1_TRYING_RPLMN),
3009 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn},
3011 {SBIT(GSM322_A1_TRYING_RPLMN),
3012 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
3014 {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
3015 GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
3017 {SBIT(GSM322_A2_ON_PLMN),
3018 GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
3020 {SBIT(GSM322_A2_ON_PLMN),
3021 GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start},
3023 {SBIT(GSM322_A2_ON_PLMN),
3024 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
3026 {SBIT(GSM322_A2_ON_PLMN),
3027 GSM322_EVENT_USER_RESEL, gsm322_a_user_resel},
3029 {SBIT(GSM322_A3_TRYING_PLMN),
3030 GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
3032 {SBIT(GSM322_A3_TRYING_PLMN),
3033 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn},
3035 {SBIT(GSM322_A3_TRYING_PLMN),
3036 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
3038 {SBIT(GSM322_A5_HPLMN_SEARCH),
3039 GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn},
3041 {SBIT(GSM322_A5_HPLMN_SEARCH),
3042 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn},
3044 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
3045 GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
3047 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
3048 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_plmn_avail},
3051 GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
3054 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3057 #define PLMNASLLEN \
3058 (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
3060 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
3062 struct gsm322_plmn *plmn = &ms->plmn;
3063 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3064 int msg_type = gm->msg_type;
3068 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
3069 "selection in state '%s'\n", ms->name, get_event_name(msg_type),
3070 plmn_a_state_names[plmn->state]);
3071 /* find function for current state and message */
3072 for (i = 0; i < PLMNASLLEN; i++)
3073 if ((msg_type == plmnastatelist[i].type)
3074 && ((1 << plmn->state) & plmnastatelist[i].states))
3076 if (i == PLMNASLLEN) {
3077 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3081 rc = plmnastatelist[i].rout(ms, msg);
3086 /* state machine for manual PLMN selection events */
3087 static struct plmnmstatelist {
3090 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3091 } plmnmstatelist[] = {
3092 {SBIT(GSM322_M0_NULL),
3093 GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
3095 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) |
3096 SBIT(GSM322_M2_ON_PLMN),
3097 GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns},
3100 GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
3102 {SBIT(GSM322_M5_NO_SIM),
3103 GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
3106 GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
3109 GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
3111 {SBIT(GSM322_M1_TRYING_RPLMN),
3112 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3114 {SBIT(GSM322_M1_TRYING_RPLMN),
3115 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3117 {SBIT(GSM322_M1_TRYING_RPLMN),
3118 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3120 {SBIT(GSM322_M1_TRYING_RPLMN),
3121 GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
3123 {SBIT(GSM322_M2_ON_PLMN),
3124 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3126 {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
3127 SBIT(GSM322_M4_TRYING_PLMN),
3128 GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
3130 {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN),
3131 GSM322_EVENT_USER_RESEL, gsm322_m_user_resel},
3133 {SBIT(GSM322_M3_NOT_ON_PLMN),
3134 GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
3136 {SBIT(GSM322_M3_NOT_ON_PLMN),
3137 GSM322_EVENT_CHOOSE_PLMN, gsm322_m_choose_plmn},
3139 {SBIT(GSM322_M4_TRYING_PLMN),
3140 GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
3142 {SBIT(GSM322_M4_TRYING_PLMN),
3143 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3145 {SBIT(GSM322_M4_TRYING_PLMN),
3146 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3148 {SBIT(GSM322_M4_TRYING_PLMN),
3149 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3152 GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
3155 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3158 #define PLMNMSLLEN \
3159 (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
3161 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
3163 struct gsm322_plmn *plmn = &ms->plmn;
3164 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3165 int msg_type = gm->msg_type;
3169 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
3170 "in state '%s'\n", ms->name, get_event_name(msg_type),
3171 plmn_m_state_names[plmn->state]);
3172 /* find function for current state and message */
3173 for (i = 0; i < PLMNMSLLEN; i++)
3174 if ((msg_type == plmnmstatelist[i].type)
3175 && ((1 << plmn->state) & plmnmstatelist[i].states))
3177 if (i == PLMNMSLLEN) {
3178 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3182 rc = plmnmstatelist[i].rout(ms, msg);
3187 /* dequeue GSM 03.22 PLMN events */
3188 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
3190 struct gsm322_plmn *plmn = &ms->plmn;
3194 while ((msg = msgb_dequeue(&plmn->event_queue))) {
3195 /* send event to PLMN select process */
3196 if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
3197 gsm322_a_event(ms, msg);
3199 gsm322_m_event(ms, msg);
3201 work = 1; /* work done */
3207 /* state machine for channel selection events */
3208 static struct cellselstatelist {
3211 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3212 } cellselstatelist[] = {
3214 GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
3217 GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
3220 GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
3223 GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search},
3225 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3226 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
3227 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
3229 {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3230 SBIT(GSM322_C8_ANY_CELL_RESEL),
3231 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
3233 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3234 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3235 SBIT(GSM322_C0_NULL),
3236 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
3238 {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3239 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
3240 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
3242 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3243 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
3245 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3246 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
3248 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3249 GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
3251 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3252 GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
3254 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3255 GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
3257 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3258 GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
3260 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3261 GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
3263 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3264 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3265 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3266 SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH),
3267 GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
3269 {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
3270 GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
3272 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3273 GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search},
3276 #define CELLSELSLLEN \
3277 (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
3279 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
3281 struct gsm322_cellsel *cs = &ms->cellsel;
3282 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3283 int msg_type = gm->msg_type;
3287 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
3288 "'%s'\n", ms->name, get_event_name(msg_type),
3289 cs_state_names[cs->state]);
3290 /* find function for current state and message */
3291 for (i = 0; i < CELLSELSLLEN; i++)
3292 if ((msg_type == cellselstatelist[i].type)
3293 && ((1 << cs->state) & cellselstatelist[i].states))
3295 if (i == CELLSELSLLEN) {
3296 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
3300 rc = cellselstatelist[i].rout(ms, msg);
3305 /* dequeue GSM 03.22 cell selection events */
3306 int gsm322_cs_dequeue(struct osmocom_ms *ms)
3308 struct gsm322_cellsel *cs = &ms->cellsel;
3312 while ((msg = msgb_dequeue(&cs->event_queue))) {
3313 /* send event to cell selection process */
3314 gsm322_c_event(ms, msg);
3316 work = 1; /* work done */
3326 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
3328 struct gsm322_plmn *plmn = &ms->plmn;
3329 struct gsm322_plmn_list *temp;
3331 printf("MCC |MNC |allowed|rx-lev\n");
3332 printf("-------+-------+-------+-------\n");
3333 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
3334 printf("%s |%s%s |%s |%s\n", gsm_print_mcc(temp->mcc),
3335 gsm_print_mnc(temp->mnc),
3336 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3337 (temp->cause) ? "no ":"yes",
3338 gsm_print_rxlev(temp->rxlev));
3344 int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
3345 void (*print)(void *, const char *, ...), void *priv)
3348 struct gsm48_sysinfo *s;
3350 print(priv, "arfcn |MCC |MNC |LAC |cell ID|forb.LA|prio |"
3351 "min-db |max-pwr|rx-lev\n");
3352 print(priv, "-------+-------+-------+-------+-------+-------+-------+"
3353 "-------+-------+-------\n");
3354 for (i = 0; i <= 1023; i++) {
3355 s = cs->list[i].sysinfo;
3356 if (!s || !(cs->list[i].flags & flags))
3358 print(priv, "%4d |", i);
3359 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
3360 print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc),
3361 gsm_print_mnc(s->mnc),
3362 ((s->mnc & 0x00f) == 0x00f) ? " ":"");
3363 print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id);
3364 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
3365 print(priv, "yes |");
3367 print(priv, "no |");
3368 if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
3369 print(priv, "barred |");
3371 if (cs->list[i].sysinfo->cell_barr)
3372 print(priv, "low |");
3374 print(priv, "normal |");
3376 print(priv, "%4d |%4d |%s\n", s->rxlev_acc_min_db,
3377 s->ms_txpwr_max_cch,
3378 gsm_print_rxlev(cs->list[i].rxlev));
3380 print(priv, "n/a |n/a |n/a |n/a |n/a |"
3388 int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
3389 void (*print)(void *, const char *, ...), void *priv)
3391 struct gsm322_plmn *plmn = &ms->plmn;
3392 struct gsm322_la_list *temp;
3394 print(priv, "MCC |MNC |LAC |cause\n");
3395 print(priv, "-------+-------+-------+-------\n");
3396 llist_for_each_entry(temp, &plmn->forbidden_la, entry)
3397 print(priv, "%s |%s%s |0x%04x |#%d\n",
3398 gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
3399 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3400 temp->lac, temp->cause);
3405 int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
3406 void (*print)(void *, const char *, ...), void *priv)
3408 struct gsm322_ba_list *ba;
3411 llist_for_each_entry(ba, &cs->ba_list, entry) {
3412 if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc))
3414 print(priv, "Band Allocation of network: MCC %s MNC %s "
3415 "(%s, %s)\n", gsm_print_mcc(ba->mcc),
3416 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3417 gsm_get_mnc(ba->mcc, ba->mnc));
3418 for (i = 0; i <= 1023; i++) {
3419 if ((ba->freq[i >> 3] & (1 << (i & 7))))
3420 print(priv, " %d", i);
3432 int gsm322_init(struct osmocom_ms *ms)
3434 struct gsm322_plmn *plmn = &ms->plmn;
3435 struct gsm322_cellsel *cs = &ms->cellsel;
3439 struct gsm322_ba_list *ba;
3442 LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
3443 LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
3445 memset(plmn, 0, sizeof(*plmn));
3446 memset(cs, 0, sizeof(*cs));
3450 /* set initial state */
3453 ms->settings.plmn_mode = PLMN_MODE_AUTO;
3456 INIT_LLIST_HEAD(&plmn->event_queue);
3457 INIT_LLIST_HEAD(&cs->event_queue);
3458 INIT_LLIST_HEAD(&plmn->sorted_plmn);
3459 INIT_LLIST_HEAD(&plmn->forbidden_la);
3460 INIT_LLIST_HEAD(&cs->ba_list);
3462 /* set supported frequencies in cell selection list */
3463 for (i = 0; i <= 1023; i++)
3464 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
3465 cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
3468 sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3469 fp = fopen(filename, "r");
3474 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
3477 rc = fread(buf, 4, 1, fp);
3482 ba->mcc = (buf[0] << 8) | buf[1];
3483 ba->mnc = (buf[2] << 8) | buf[3];
3484 rc = fread(ba->freq, sizeof(ba->freq), 1, fp);
3489 llist_add_tail(&ba->entry, &cs->ba_list);
3490 LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s "
3491 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3492 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3493 gsm_get_mnc(ba->mcc, ba->mnc));
3497 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
3499 register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3504 int gsm322_exit(struct osmocom_ms *ms)
3506 struct gsm322_plmn *plmn = &ms->plmn;
3507 struct gsm322_cellsel *cs = &ms->cellsel;
3508 struct llist_head *lh, *lh2;
3512 struct gsm322_ba_list *ba;
3516 LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
3517 LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
3519 unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3521 /* stop cell selection process (if any) */
3522 new_c_state(cs, GSM322_C0_NULL);
3526 stop_plmn_timer(plmn);
3529 for (i = 0; i <= 1023; i++) {
3530 if (cs->list[i].sysinfo) {
3531 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
3532 talloc_free(cs->list[i].sysinfo);
3533 cs->list[i].sysinfo = NULL;
3535 cs->list[i].flags = 0;
3539 sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3540 fp = fopen(filename, "w");
3544 llist_for_each_entry(ba, &cs->ba_list, entry) {
3545 buf[0] = ba->mcc >> 8;
3546 buf[1] = ba->mcc & 0xff;
3547 buf[2] = ba->mnc >> 8;
3548 buf[3] = ba->mnc & 0xff;
3549 rc = fwrite(buf, 4, 1, fp);
3550 rc = fwrite(ba->freq, sizeof(ba->freq), 1, fp);
3551 LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s "
3552 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3553 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3554 gsm_get_mnc(ba->mcc, ba->mnc));
3558 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
3561 while ((msg = msgb_dequeue(&plmn->event_queue)))
3563 while ((msg = msgb_dequeue(&cs->event_queue)))
3565 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
3569 llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
3573 llist_for_each_safe(lh, lh2, &cs->ba_list) {