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/logging.h>
35 #include <osmocom/l1ctl.h>
36 #include <osmocom/file.h>
37 #include <osmocom/osmocom_data.h>
38 #include <osmocom/networks.h>
42 static void gsm322_cs_timeout(void *arg);
43 static int gsm322_cs_select(struct osmocom_ms *ms, int any);
44 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg);
47 #warning HACK to stay on one channel
48 int l1ctl_tx_ccch_req_(struct osmocom_ms *ms, uint16_t arfcn)
50 static int already = 0;
54 return l1ctl_tx_ccch_req(ms, arfcn);
56 ms->cellsel.ccch_active = 1;
64 /* Cell selection process
66 * The process depends on states and events (finites state machine).
68 * During states of cell selection or cell re-selection, the search for a cell
69 * is performed in two steps:
71 * 1. Measurement of received level of all relevant frequencies (rx-lev)
73 * 2. Receive system information messages of all relevant frequencies
75 * During this process, the results are stored in a list of all frequencies.
76 * This list is checked whenever a cell is selected. It depends on the results
77 * if the cell is 'suitable' and 'allowable' to 'camp' on.
79 * This list is also used to generate a list of available networks.
82 /* PLMN selection process
84 * The PLMN (Public Land Mobile Network = Operator's Network) has two different
91 * The process depends on states and events (finites state machine).
95 /* File format of BA list:
100 * where frequency 0 is bit 0 of first byte
102 * If not end-of-file, the next BA list is stored.
107 * * subscr->plmn_list
109 * The "PLMN Selector list" stores prefered networks to select during PLMN
110 * search process. This list is also stored in the SIM.
114 * The "forbidden PLMNs" list stores all networks that rejected us. The stored
115 * network will not be used when searching PLMN automatically. This list is
116 * also stored din the SIM.
118 * * plmn->forbidden_la
120 * The "forbidden LAs for roaming" list stores all location areas where roaming
125 * This list stores measurements and cell informations during cell selection
126 * process. It can be used to speed up repeated cell selection.
130 * This list stores a map of frequencies used for a PLMN. If this lists exists
131 * for a PLMN, it helps to speedup cell scan process.
133 * * plmn->sorted_plmn
135 * This list is generated whenever a PLMN search is started and a list of PLMNs
136 * is required. It consists of home PLMN, PLMN Selector list, and PLMNs found
137 * during scan process.
144 static const struct value_string gsm322_event_names[] = {
145 { GSM322_EVENT_SWITCH_ON, "EVENT_SWITCH_ON" },
146 { GSM322_EVENT_SWITCH_OFF, "EVENT_SWITCH_OFF" },
147 { GSM322_EVENT_SIM_INSERT, "EVENT_SIM_INSERT" },
148 { GSM322_EVENT_SIM_REMOVE, "EVENT_SIM_REMOVE" },
149 { GSM322_EVENT_REG_FAILED, "EVENT_REG_FAILED" },
150 { GSM322_EVENT_ROAMING_NA, "EVENT_ROAMING_NA" },
151 { GSM322_EVENT_INVALID_SIM, "EVENT_INVALID_SIM" },
152 { GSM322_EVENT_REG_SUCCESS, "EVENT_REG_SUCCESS" },
153 { GSM322_EVENT_NEW_PLMN, "EVENT_NEW_PLMN" },
154 { GSM322_EVENT_ON_PLMN, "EVENT_ON_PLMN" },
155 { GSM322_EVENT_HPLMN_SEARCH, "EVENT_HPLMN_SEARCH" },
156 { GSM322_EVENT_HPLMN_FOUND, "EVENT_HPLMN_FOUND" },
157 { GSM322_EVENT_HPLMN_NOT_FOUND, "EVENT_HPLMN_NOT_FOUND" },
158 { GSM322_EVENT_USER_RESEL, "EVENT_USER_RESEL" },
159 { GSM322_EVENT_PLMN_AVAIL, "EVENT_PLMN_AVAIL" },
160 { GSM322_EVENT_CHOSE_PLMN, "EVENT_CHOSE_PLMN" },
161 { GSM322_EVENT_SEL_MANUAL, "EVENT_SEL_MANUAL" },
162 { GSM322_EVENT_SEL_AUTO, "EVENT_SEL_AUTO" },
163 { GSM322_EVENT_CELL_FOUND, "EVENT_CELL_FOUND" },
164 { GSM322_EVENT_NO_CELL_FOUND, "EVENT_NO_CELL_FOUND" },
165 { GSM322_EVENT_LEAVE_IDLE, "EVENT_LEAVE_IDLE" },
166 { GSM322_EVENT_RET_IDLE, "EVENT_RET_IDLE" },
167 { GSM322_EVENT_CELL_RESEL, "EVENT_CELL_RESEL" },
168 { GSM322_EVENT_SYSINFO, "EVENT_SYSINFO" },
172 const char *get_event_name(int value)
174 return get_value_string(gsm322_event_names, value);
178 /* allocate a 03.22 event message */
179 struct msgb *gsm322_msgb_alloc(int msg_type)
182 struct gsm322_msg *gm;
184 msg = msgb_alloc_headroom(sizeof(*gm), 0, "GSM 03.22 event");
188 gm = (struct gsm322_msg *)msgb_put(msg, sizeof(*gm));
189 gm->msg_type = msg_type;
194 /* queue PLMN selection message */
195 int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
197 struct gsm322_plmn *plmn = &ms->plmn;
199 msgb_enqueue(&plmn->event_queue, msg);
204 /* queue cell selection message */
205 int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
207 struct gsm322_cellsel *cs = &ms->cellsel;
209 msgb_enqueue(&cs->event_queue, msg);
218 /* del forbidden PLMN */
219 int gsm322_del_forbidden_plmn(struct osmocom_ms *ms, uint16_t mcc,
222 struct gsm_subscriber *subscr = &ms->subscr;
223 struct gsm_sub_plmn_na *na;
225 llist_for_each_entry(na, &subscr->plmn_na, entry) {
226 if (na->mcc == mcc && na->mnc == mnc) {
227 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
228 "PLMNs (mcc=%03d, mnc=%02d)\n", mcc, mnc);
229 llist_del(&na->entry);
232 update plmn not allowed list on sim
241 /* add forbidden PLMN */
242 int gsm322_add_forbidden_plmn(struct osmocom_ms *ms, uint16_t mcc,
243 uint16_t mnc, uint8_t cause)
245 struct gsm_subscriber *subscr = &ms->subscr;
246 struct gsm_sub_plmn_na *na;
248 /* don't add Home PLMN */
249 if (subscr->sim_valid && mcc == subscr->mcc && mnc == subscr->mnc)
252 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs "
253 "(mcc=%03d, mnc=%02d)\n", mcc, mnc);
254 na = talloc_zero(l23_ctx, struct gsm_sub_plmn_na);
260 llist_add_tail(&na->entry, &subscr->plmn_na);
263 update plmn not allowed list on sim
269 /* search forbidden PLMN */
270 int gsm322_is_forbidden_plmn(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc)
272 struct gsm_subscriber *subscr = &ms->subscr;
273 struct gsm_sub_plmn_na *na;
275 llist_for_each_entry(na, &subscr->plmn_na, entry) {
276 if (na->mcc == mcc && na->mnc == mnc)
283 /* del forbidden LA */
284 int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
285 uint16_t mnc, uint16_t lac)
287 struct gsm322_plmn *plmn = &ms->plmn;
288 struct gsm322_la_list *la;
290 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
291 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
292 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
293 "LAs (mcc=%03d, mnc=%02d, lac=%04x)\n",
295 llist_del(&la->entry);
304 /* add forbidden LA */
305 int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
306 uint16_t mnc, uint16_t lac, uint8_t cause)
308 struct gsm322_plmn *plmn = &ms->plmn;
309 struct gsm322_la_list *la;
311 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
312 "(mcc=%03d, mnc=%02d, lac=%04x)\n", mcc, mnc, lac);
313 la = talloc_zero(l23_ctx, struct gsm322_la_list);
320 llist_add_tail(&la->entry, &plmn->forbidden_la);
325 /* search forbidden LA */
326 int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
329 struct gsm322_plmn *plmn = &ms->plmn;
330 struct gsm322_la_list *la;
332 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
333 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
340 /* search for PLMN in all BA lists */
341 static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
342 uint16_t mcc, uint16_t mnc)
344 struct gsm322_ba_list *ba, *ba_found = NULL;
346 /* search for BA list */
347 llist_for_each_entry(ba, &cs->ba_list, entry) {
358 /* search available PLMN */
359 int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
363 for (i = 0; i <= 1023; i++) {
364 if (cs->list[i].sysinfo->mcc == mcc
365 && cs->list[i].sysinfo->mnc == mnc)
372 /* del forbidden LA */
377 /*plmn search timer event */
378 static void plmn_timer_timeout(void *arg)
380 struct gsm322_plmn *plmn = arg;
383 LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
385 /* indicate PLMN selection T timeout */
386 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
389 gsm322_plmn_sendmsg(plmn->ms, nmsg);
392 /* start plmn search timer */
393 static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
395 LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
397 plmn->timer.cb = plmn_timer_timeout;
398 plmn->timer.data = plmn;
399 bsc_schedule_timer(&plmn->timer, secs, 0);
402 /* stop plmn search timer */
403 static void stop_plmn_timer(struct gsm322_plmn *plmn)
405 if (bsc_timer_pending(&plmn->timer)) {
406 LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
407 bsc_del_timer(&plmn->timer);
411 /* start cell selection timer */
412 static void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
414 LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec);
415 cs->timer.cb = gsm322_cs_timeout;
417 bsc_schedule_timer(&cs->timer, sec, micro);
420 /* stop cell selection timer */
421 static void stop_cs_timer(struct gsm322_cellsel *cs)
423 if (bsc_timer_pending(&cs->timer)) {
424 LOGP(DCS, LOGL_INFO, "stopping pending CS timer.\n");
425 bsc_del_timer(&cs->timer);
433 static const char *plmn_a_state_names[] = {
443 static const char *plmn_m_state_names[] = {
452 static const char *cs_state_names[] = {
454 "C1_NORMAL_CELL_SEL",
455 "C2_STORED_CELL_SEL",
456 "C3_CAMPED_NORMALLY",
457 "C4_NORMAL_CELL_RESEL",
460 "C7_CAMPED_ANY_CELL",
462 "C9_CHOOSE_ANY_CELL",
467 /* new automatic PLMN search state */
468 static void new_a_state(struct gsm322_plmn *plmn, int state)
470 if (plmn->mode != PLMN_MODE_AUTO) {
471 LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
475 stop_plmn_timer(plmn);
477 if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
480 LOGP(DPLMN, LOGL_INFO, "new state %s -> %s\n",
481 plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
486 /* new manual PLMN search state */
487 static void new_m_state(struct gsm322_plmn *plmn, int state)
489 if (plmn->mode != PLMN_MODE_MANUAL) {
490 LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
494 if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
497 LOGP(DPLMN, LOGL_INFO, "new state %s -> %s\n",
498 plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
503 /* new Cell selection state */
504 static void new_c_state(struct gsm322_cellsel *cs, int state)
506 if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
509 LOGP(DCS, LOGL_INFO, "new state %s -> %s\n",
510 cs_state_names[cs->state], cs_state_names[state]);
512 /* stop cell selection timer, if running */
515 /* stop scanning of power measurement */
530 /* 4.4.3 create sorted list of PLMN
532 * the source of entries are
535 * - entries found in the SIM's PLMN Selector list
536 * - scanned PLMNs above -85 dB (random order)
537 * - scanned PLMNs below or equal -85 (by received level)
541 * The list only includes networks found at last scan.
543 * The list always contains HPLMN if available, even if not used by PLMN
544 * search process at some conditions.
546 * The list contains all PLMNs even if not allowed, so entries have to be
547 * removed when selecting from the list. (In case we use manual cell selection,
548 * we need to provide non-allowed networks also.)
550 static int gsm322_sort_list(struct osmocom_ms *ms)
552 struct gsm322_plmn *plmn = &ms->plmn;
553 struct gsm322_cellsel *cs = &ms->cellsel;
554 struct gsm_subscriber *subscr = &ms->subscr;
555 struct gsm_sub_plmn_list *sim_entry;
556 struct gsm_sub_plmn_na *na_entry;
557 struct llist_head temp_list;
558 struct gsm322_plmn_list *temp, *found;
559 struct llist_head *lh, *lh2;
560 int i, entries, move;
561 int8_t search_db = 0;
564 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
569 /* Create a temporary list of all networks */
570 INIT_LLIST_HEAD(&temp_list);
571 for (i = 0; i <= 1023; i++) {
572 if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA))
575 /* search if network has multiple cells */
577 llist_for_each_entry(temp, &temp_list, entry) {
578 if (temp->mcc == cs->list[i].sysinfo->mcc
579 && temp->mnc == cs->list[i].sysinfo->mnc)
583 /* update or create */
585 if (cs->list[i].rxlev_db > found->rxlev_db)
586 found->rxlev_db = cs->list[i].rxlev_db;
588 temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
591 temp->mcc = cs->list[i].sysinfo->mcc;
592 temp->mnc = cs->list[i].sysinfo->mnc;
593 temp->rxlev_db = cs->list[i].rxlev_db;
594 llist_add_tail(&temp->entry, &temp_list);
598 /* move Home PLMN, if in list */
599 if (subscr->sim_valid) {
601 llist_for_each_entry(temp, &temp_list, entry) {
602 if (temp->mcc == subscr->mcc
603 && temp->mnc == subscr->mnc) {
609 llist_del(&found->entry);
610 llist_add_tail(&found->entry, &plmn->sorted_plmn);
614 /* move entries if in SIM's PLMN Selector list */
615 llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
617 llist_for_each_entry(temp, &temp_list, entry) {
618 if (temp->mcc == sim_entry->mcc
619 && temp->mnc == sim_entry->mnc) {
625 llist_del(&found->entry);
626 llist_add_tail(&found->entry, &plmn->sorted_plmn);
630 /* move PLMN above -85 dBm in random order */
632 llist_for_each_entry(temp, &temp_list, entry) {
633 if (temp->rxlev_db > -85)
637 move = random() % entries;
639 llist_for_each_entry(temp, &temp_list, entry) {
640 if (temp->rxlev_db > -85) {
642 llist_del(&temp->entry);
643 llist_add_tail(&temp->entry,
653 /* move ohter PLMN in decreasing order */
656 llist_for_each_entry(temp, &temp_list, entry) {
658 || temp->rxlev_db > search_db) {
659 search_db = temp->rxlev_db;
665 llist_del(&found->entry);
666 llist_add_tail(&found->entry, &plmn->sorted_plmn);
669 /* mark forbidden PLMNs, if in list of forbidden networks */
671 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
672 llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
673 if (temp->mcc == na_entry->mcc
674 && temp->mnc == na_entry->mnc) {
675 temp->cause = na_entry->cause;
679 LOGP(DPLMN, LOGL_INFO, "Crating Sorted PLMN list. "
680 "(%02d: mcc=%03d mnc=%02d allowed=%s rx-lev=%d)\n",
681 i, temp->mcc, temp->mnc, (temp->cause) ? "no ":"yes",
690 * handler for automatic search
693 /* go On PLMN state */
694 static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
696 struct gsm322_plmn *plmn = &ms->plmn;
697 struct gsm_subscriber *subscr = &ms->subscr;
699 /* set last registered PLMN */
700 subscr->plmn_valid = 1;
701 subscr->plmn_mcc = plmn->mcc;
702 subscr->plmn_mnc = plmn->mnc;
707 new_a_state(plmn, GSM322_A2_ON_PLMN);
709 /* start timer, if on VPLMN of home country OR special case */
710 if ((plmn->mcc == subscr->mcc && plmn->mcc != subscr->mnc)
711 || (subscr->always_search_hplmn && (plmn->mcc != subscr->mnc
712 || plmn->mcc != subscr->mnc))) {
713 if (subscr->sim_valid && subscr->t6m_hplmn)
714 start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
716 start_plmn_timer(plmn, 30 * 360);
718 stop_plmn_timer(plmn);
723 /* indicate selected PLMN */
724 static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
727 indicate selected plmn to user
730 return gsm322_a_go_on_plmn(ms, msg);
733 /* no (more) PLMN in list */
734 static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
736 struct gsm322_plmn *plmn = &ms->plmn;
737 struct gsm322_cellsel *cs = &ms->cellsel;
738 struct gsm_subscriber *subscr = &ms->subscr;
742 /* any PLMN available */
743 found = gsm322_cs_select(ms, 0);
745 /* if no PLMN in list */
747 if (subscr->plmn_valid) {
748 LOGP(DPLMN, LOGL_INFO, "Select RPLMN.\n");
749 plmn->mcc = subscr->plmn_mcc;
750 plmn->mnc = subscr->plmn_mnc;
752 LOGP(DPLMN, LOGL_INFO, "Select HPLMN.\n");
753 plmn->mcc = subscr->mcc;
754 plmn->mnc = subscr->mnc;
757 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
759 /* we must forward this, otherwhise "Any cell selection"
760 * will not start automatically.
762 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
765 gsm322_cs_sendmsg(ms, nmsg);
770 /* select first PLMN in list */
771 plmn->mcc = cs->list[found].sysinfo->mcc;
772 plmn->mnc = cs->list[found].sysinfo->mnc;
774 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%03d mnc=%02d %s, %s)\n",
775 plmn->mcc, plmn->mnc,
776 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
778 /* indicate New PLMN */
779 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
782 gsm322_cs_sendmsg(ms, nmsg);
785 return gsm322_a_indicate_selected(ms, msg);
788 /* select first PLMN in list */
789 static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
791 struct gsm322_plmn *plmn = &ms->plmn;
793 struct gsm322_plmn_list *plmn_entry;
794 struct gsm322_plmn_list *plmn_first = NULL;
798 gsm322_sort_list(ms);
800 /* select first entry */
802 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
803 /* if RPLMN is HPLMN, we skip that */
804 if (plmn->state == GSM322_A1_TRYING_RPLMN
805 && plmn_entry->mcc == plmn->mcc
806 && plmn_entry->mnc == plmn->mnc) {
810 /* select first allowed network */
811 if (!plmn_entry->cause) {
812 plmn_first = plmn_entry;
819 /* if no PLMN in list */
821 LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
822 gsm322_a_no_more_plmn(ms, msg);
827 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%03d "
828 "mnc=%02d %s, %s)\n", plmn->plmn_curr, plmn_first->mcc,
829 plmn_first->mnc, gsm_get_mcc(plmn_first->mcc),
830 gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
832 /* set current network */
833 plmn->mcc = plmn_first->mcc;
834 plmn->mnc = plmn_first->mnc;
836 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
838 /* indicate New PLMN */
839 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
842 gsm322_cs_sendmsg(ms, nmsg);
847 /* select next PLMN in list */
848 static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
850 struct gsm322_plmn *plmn = &ms->plmn;
852 struct gsm322_plmn_list *plmn_entry;
853 struct gsm322_plmn_list *plmn_next = NULL;
856 /* select next entry from list */
858 ii = plmn->plmn_curr + 1;
859 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
860 /* skip previously selected networks */
865 /* select next allowed network */
866 if (!plmn_entry->cause) {
867 plmn_next = plmn_entry;
874 /* if no more PLMN in list */
876 LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
877 gsm322_a_no_more_plmn(ms, msg);
882 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%03d "
883 "mnc=%02d %s, %s)\n", plmn->plmn_curr, plmn_next->mcc,
885 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
887 /* set next network */
888 plmn->mcc = plmn_next->mcc;
889 plmn->mnc = plmn_next->mnc;
891 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
893 /* indicate New PLMN */
894 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
897 gsm322_cs_sendmsg(ms, nmsg);
902 /* User re-selection event */
903 static int gsm322_a_user_reselection(struct osmocom_ms *ms, struct msgb *msg)
905 struct gsm322_plmn *plmn = &ms->plmn;
906 struct gsm322_plmn_list *plmn_entry;
907 struct gsm322_plmn_list *plmn_found = NULL;
909 /* search current PLMN in list */
910 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
911 if (plmn_entry->mcc == plmn->mcc
912 && plmn_entry->mnc == plmn->mnc)
913 plmn_found = plmn_entry;
917 /* abort if list is empty */
919 LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
923 LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
924 "and restarting PLMN search process.\n");
926 /* move entry to end of list */
927 llist_del(&plmn_found->entry);
928 llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
930 /* select first PLMN in list */
931 return gsm322_a_sel_first_plmn(ms, msg);
934 /* PLMN becomes available */
935 static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
937 struct gsm322_plmn *plmn = &ms->plmn;
938 struct gsm_subscriber *subscr = &ms->subscr;
939 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
941 if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
942 && subscr->plmn_mnc == gm->mnc) {
946 LOGP(DPLMN, LOGL_INFO, "HPLMN became available.\n");
947 return gsm322_a_go_on_plmn(ms, msg);
949 /* select first PLMN in list */
950 LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
951 "search process.\n");
952 return gsm322_a_sel_first_plmn(ms, msg);
956 /* loss of radio coverage */
957 static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
959 struct gsm322_plmn *plmn = &ms->plmn;
960 struct gsm322_cellsel *cs = &ms->cellsel;
963 /* any PLMN available */
964 found = gsm322_cs_select(ms, 0);
966 /* if PLMN in list */
968 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%03d mnc=%02d "
969 "%s, %s)\n", cs->list[found].sysinfo->mcc,
970 cs->list[found].sysinfo->mnc,
971 gsm_get_mcc(cs->list[found].sysinfo->mcc),
972 gsm_get_mnc(cs->list[found].sysinfo->mcc,
973 cs->list[found].sysinfo->mnc));
974 return gsm322_a_sel_first_plmn(ms, msg);
977 LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
979 plmn->mcc = plmn->mnc = 0;
981 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
986 /* MS is switched on OR SIM is inserted OR removed */
987 static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
989 struct gsm_subscriber *subscr = &ms->subscr;
990 struct gsm322_plmn *plmn = &ms->plmn;
993 if (!subscr->sim_valid) {
994 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
995 new_a_state(plmn, GSM322_A6_NO_SIM);
1000 /* if there is a registered PLMN */
1001 if (subscr->plmn_valid) {
1002 /* select the registered PLMN */
1003 plmn->mcc = subscr->plmn_mcc;
1004 plmn->mnc = subscr->plmn_mnc;
1006 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%03d mnc=%02d "
1007 "%s, %s)\n", plmn->mcc, plmn->mnc,
1008 gsm_get_mcc(plmn->mcc),
1009 gsm_get_mnc(plmn->mcc, plmn->mnc));
1011 new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
1013 /* indicate New PLMN */
1014 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1017 gsm322_cs_sendmsg(ms, nmsg);
1022 /* select first PLMN in list */
1023 return gsm322_a_sel_first_plmn(ms, msg);
1026 /* MS is switched off */
1027 static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1029 struct gsm322_plmn *plmn = &ms->plmn;
1031 new_a_state(plmn, GSM322_A0_NULL);
1036 static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1038 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1042 /* SIM is removed */
1043 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1047 /* indicate SIM remove to cell selection process */
1048 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1051 gsm322_cs_sendmsg(ms, nmsg);
1053 return gsm322_a_switch_on(ms, msg);
1056 /* location update response: "Roaming not allowed" */
1057 static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
1059 /* store in list of forbidden LAs is done in gsm48* */
1061 return gsm322_a_sel_first_plmn(ms, msg);
1064 /* On VPLMN of home country and timeout occurs */
1065 static int gsm322_a_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
1067 struct gsm322_plmn *plmn = &ms->plmn;
1068 struct gsm322_cellsel *cs = &ms->cellsel;
1071 /* try again later, if not idle */
1072 if (cs->state != GSM322_C3_CAMPED_NORMALLY) {
1073 LOGP(DPLMN, LOGL_INFO, "Not camping normal, wait some more.\n");
1074 start_plmn_timer(plmn, 60);
1079 new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
1081 /* initiate search at cell selection */
1082 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
1085 gsm322_cs_sendmsg(ms, nmsg);
1090 /* manual mode selected */
1091 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
1093 struct gsm322_plmn *plmn = &ms->plmn;
1096 /* restart state machine */
1097 gsm322_a_switch_off(ms, msg);
1098 plmn->mode = PLMN_MODE_MANUAL;
1099 gsm322_m_switch_on(ms, msg);
1101 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1104 gsm48_mmevent_msg(ms, nmsg);
1110 * handler for manual search
1113 /* go Not on PLMN state */
1114 static int gsm322_m_go_not_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1116 struct gsm322_plmn *plmn = &ms->plmn;
1118 new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
1123 /* display PLMNs and to Not on PLMN */
1124 static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
1127 gsm322_sort_list(ms);
1130 display PLMNs to user
1133 /* go Not on PLMN state */
1134 return gsm322_m_go_not_on_plmn(ms, msg);
1137 /* MS is switched on OR SIM is inserted OR removed */
1138 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1140 struct gsm_subscriber *subscr = &ms->subscr;
1141 struct gsm322_plmn *plmn = &ms->plmn;
1143 if (!subscr->sim_valid) {
1144 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1145 new_m_state(plmn, GSM322_M5_NO_SIM);
1150 /* if there is a registered PLMN */
1151 if (subscr->plmn_valid) {
1154 /* select the registered PLMN */
1155 plmn->mcc = subscr->plmn_mcc;
1156 plmn->mnc = subscr->plmn_mnc;
1158 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%03d mnc=%02d "
1159 "%s, %s)\n", plmn->mcc, plmn->mnc,
1160 gsm_get_mcc(plmn->mcc),
1161 gsm_get_mnc(plmn->mcc, plmn->mnc));
1163 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1165 /* indicate New PLMN */
1166 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1169 gsm322_cs_sendmsg(ms, nmsg);
1175 return gsm322_m_display_plmns(ms, msg);
1178 /* MS is switched off */
1179 static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1181 struct gsm322_plmn *plmn = &ms->plmn;
1183 stop_plmn_timer(plmn);
1185 new_m_state(plmn, GSM322_M0_NULL);
1190 static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1192 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1196 /* SIM is removed */
1197 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1199 struct gsm322_plmn *plmn = &ms->plmn;
1202 stop_plmn_timer(plmn);
1204 /* indicate SIM remove to cell selection process */
1205 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1208 gsm322_cs_sendmsg(ms, nmsg);
1210 return gsm322_m_switch_on(ms, msg);
1213 /* go to On PLMN state */
1214 static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1216 struct gsm322_plmn *plmn = &ms->plmn;
1217 struct gsm_subscriber *subscr = &ms->subscr;
1219 /* if selected PLMN is in list of forbidden PLMNs */
1220 gsm322_del_forbidden_plmn(ms, plmn->mcc, plmn->mnc);
1222 /* set last registered PLMN */
1223 subscr->plmn_valid = 1;
1224 subscr->plmn_mcc = plmn->mcc;
1225 subscr->plmn_mnc = plmn->mnc;
1230 new_m_state(plmn, GSM322_M2_ON_PLMN);
1235 /* indicate selected PLMN */
1236 static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
1239 indicate selected plmn to user
1242 return gsm322_m_go_on_plmn(ms, msg);
1245 /* previously selected PLMN becomes available again */
1246 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1248 struct gsm322_plmn *plmn = &ms->plmn;
1249 struct gsm322_cellsel *cs = &ms->cellsel;
1251 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1253 if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
1256 LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
1257 "selected, so start selection.\n");
1259 /* indicate New PLMN */
1260 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1263 gsm322_cs_sendmsg(ms, nmsg);
1269 /* the user has selected given PLMN */
1270 static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
1272 struct gsm322_plmn *plmn = &ms->plmn;
1273 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1276 /* use user selection */
1277 plmn->mcc = gm->mcc;
1278 plmn->mnc = gm->mnc;
1280 LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%03d mnc=%02d "
1281 "%s, %s)\n", plmn->mcc, plmn->mnc,
1282 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1284 new_m_state(plmn, GSM322_M4_TRYING_PLMN);
1286 /* indicate New PLMN */
1287 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1290 gsm322_cs_sendmsg(ms, nmsg);
1295 /* auto mode selected */
1296 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
1298 struct gsm322_plmn *plmn = &ms->plmn;
1301 /* restart state machine */
1302 gsm322_m_switch_off(ms, msg);
1303 plmn->mode = PLMN_MODE_AUTO;
1304 gsm322_a_switch_on(ms, msg);
1306 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1309 gsm48_mmevent_msg(ms, nmsg);
1314 /* if no cell is found in other states than in *_TRYING_* states */
1315 static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
1319 /* Tell cell selection process to handle "no cell found". */
1320 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1323 gsm322_cs_sendmsg(ms, nmsg);
1329 * cell scanning process
1332 /* select a suitable and allowable cell */
1333 static int gsm322_cs_select(struct osmocom_ms *ms, int any)
1335 struct gsm322_cellsel *cs = &ms->cellsel;
1336 struct gsm_subscriber *subscr = &ms->subscr;
1337 struct gsm48_sysinfo *s;
1338 int i, found = -1, power = 0;
1339 uint8_t flags, mask;
1342 /* set out access class depending on the cell selection type */
1344 acc_class = subscr->acc_class | 0x0400; /* add emergency */
1345 LOGP(DCS, LOGL_INFO, "Using access class with Emergency "
1348 acc_class = subscr->acc_class & 0xfbff; /* remove emergency */
1349 LOGP(DCS, LOGL_INFO, "Using access class without Emergency "
1353 /* flags to match */
1354 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1355 | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
1356 if (cs->state == GSM322_C2_STORED_CELL_SEL)
1357 mask |= GSM322_CS_FLAG_BA;
1358 flags = mask; /* all masked flags are requied */
1360 /* loop through all scanned frequencies and select cell */
1361 for (i = 0; i <= 1023; i++) {
1362 cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
1363 s = cs->list[i].sysinfo;
1365 /* channel has no informations for us */
1366 if ((cs->list[i].flags & mask) != flags) {
1370 /* check C1 criteria not fullfilled */
1371 // TODO: C1 is also dependant on power class and max power
1372 if (cs->list[i].rxlev_db < s->rxlev_acc_min_db) {
1373 LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
1374 "not met. (rxlev=%d < min=%d)\n", i,
1375 cs->list[i].rxlev_db, s->rxlev_acc_min_db);
1379 /* if cell is barred and we don't override */
1380 if (!subscr->acc_barr
1381 && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
1382 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
1387 /* if cell is in list of forbidden LAs */
1388 if (!subscr->acc_barr
1389 && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
1390 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1391 "list of forbidden LAs. (mcc=%03d mnc=%02d "
1392 "lai=%04x)\n", i, s->mcc, s->mnc, s->lac);
1396 /* if we have no access to the cell and we don't override */
1397 if (!subscr->acc_barr
1398 && !(acc_class & (s->class_barr ^ 0xffff))) {
1399 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
1400 "barred for out access. (access=%04x "
1401 "barred=%04x)\n", i, acc_class, s->class_barr);
1405 /* store temporary available and allowable flag */
1406 cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
1408 /* if we search a specific PLMN, but it does not match */
1409 if (!any && (cs->mcc != s->mcc
1410 || cs->mnc != s->mnc)) {
1411 LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
1412 "does not match target PLMN. (mcc=%03d "
1413 "mnc=%02d)\n", i, s->mcc, s->mnc);
1417 LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%d "
1418 "mcc=%03d mnc=%02d lac=%04x %s, %s)\n", i,
1419 cs->list[i].rxlev_db, s->mcc, s->mnc,
1420 s->lac, gsm_get_mcc(s->mcc),
1421 gsm_get_mnc(s->mcc, s->mnc));
1423 /* find highest power cell */
1424 if (found < 0 || cs->list[i].rxlev_db > power) {
1425 power = cs->list[i].rxlev_db;
1430 gsm322_dump_sorted_plmn(ms);
1433 LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
1438 /* tune to first/next unscanned frequency and search for PLMN */
1439 static int gsm322_cs_scan(struct osmocom_ms *ms)
1441 struct gsm322_cellsel *cs = &ms->cellsel;
1443 uint8_t mask, flags;
1444 uint32_t weight = 0, test = cs->scan_state;
1446 /* search for strongest unscanned cell */
1447 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1448 | GSM322_CS_FLAG_SIGNAL;
1449 if (cs->state == GSM322_C2_STORED_CELL_SEL)
1450 mask |= GSM322_CS_FLAG_BA;
1451 flags = mask; /* all masked flags are requied */
1452 for (i = 0; i <= 1023; i++) {
1453 /* skip if band has enough frequencies scanned (3.2.1) */
1454 for (j = 0; gsm_sup_smax[j].max; j++) {
1455 if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
1456 if (gsm_sup_smax[j].start >= i
1457 && gsm_sup_smax[j].end <= i)
1460 if (gsm_sup_smax[j].end <= i
1461 || gsm_sup_smax[j].start >= i)
1465 if (gsm_sup_smax[j].max) {
1466 if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
1469 /* search for unscanned frequency */
1470 if ((cs->list[i].flags & mask) == flags) {
1471 /* weight depends on the power level
1472 * if it is the same, it depends on arfcn
1474 test = cs->list[i].rxlev_db + 128;
1475 test = (test << 16) | i;
1476 if (test >= cs->scan_state)
1482 cs->scan_state = weight;
1485 gsm322_dump_cs_list(ms);
1487 /* special negative case for HPLMN search */
1488 if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
1491 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_NOT_FOUND);
1492 LOGP(DCS, LOGL_INFO, "No HPLMN cell available.\n");
1495 gsm322_plmn_sendmsg(ms, nmsg);
1497 /* re-tune back to current VPLMN */
1498 cs->ccch_active = 0;
1499 l1ctl_tx_ccch_req_(ms, cs->arfcn);
1504 /* if all frequencies have been searched */
1509 LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
1511 /* just see, if we search for any cell */
1512 if (cs->state == GSM322_C6_ANY_CELL_SEL
1513 || cs->state == GSM322_C8_ANY_CELL_RESEL
1514 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1517 found = gsm322_cs_select(ms, any);
1521 struct gsm322_plmn *plmn = &ms->plmn;
1523 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1526 cs->si = cs->list[cs->arfcn].sysinfo;
1527 cs->ccch_active = 0;
1528 l1ctl_tx_ccch_req_(ms, cs->arfcn);
1530 /* selected PLMN (manual) or any PLMN (auto) */
1531 switch (plmn->mode) {
1532 case PLMN_MODE_AUTO:
1533 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1534 /* PLMN becomes available */
1535 nmsg = gsm322_msgb_alloc(
1536 GSM322_EVENT_PLMN_AVAIL);
1539 gsm322_plmn_sendmsg(ms, nmsg);
1542 case PLMN_MODE_MANUAL:
1543 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1544 && gsm322_is_plmn_avail(cs, plmn->mcc,
1546 /* PLMN becomes available */
1547 nmsg = gsm322_msgb_alloc(
1548 GSM322_EVENT_PLMN_AVAIL);
1551 gsm322_plmn_sendmsg(ms, nmsg);
1556 /* set selected cell */
1558 cs->sel_arfcn = cs->arfcn;
1559 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1560 cs->sel_mcc = cs->si->mcc;
1561 cs->sel_mnc = cs->si->mnc;
1562 cs->sel_lac = cs->si->lac;
1564 /* tell CS process about available cell */
1565 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1566 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1568 /* unset selected cell */
1570 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
1571 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
1573 /* tell CS process about no cell available */
1574 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1575 LOGP(DCS, LOGL_INFO, "No cell available.\n");
1579 gsm322_cs_sendmsg(ms, nmsg);
1584 /* NOTE: We might already have system information from previous
1585 * scan. But we need recent informations, so we scan again!
1588 /* Tune to frequency for a while, to receive broadcasts. */
1589 cs->arfcn = weight & 1023;
1590 LOGP(DCS, LOGL_INFO, "Scanning frequency %d.\n", cs->arfcn);
1591 cs->ccch_active = 0;
1592 l1ctl_tx_ccch_req_(ms, cs->arfcn);
1594 /* Allocate system information. */
1595 cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
1596 struct gsm48_sysinfo);
1597 if (!cs->list[cs->arfcn].sysinfo)
1599 cs->si = cs->list[cs->arfcn].sysinfo;
1601 /* increase scan counter for each maximum scan range */
1602 if (gsm_sup_smax[j].max)
1603 gsm_sup_smax[j].temp++;
1607 /* check if cell is now suitable and allowable */
1608 static int gsm322_cs_store(struct osmocom_ms *ms)
1610 struct gsm322_cellsel *cs = &ms->cellsel;
1611 struct gsm_subscriber *subscr = &ms->subscr;
1612 int i = cs->scan_state & 1023;
1613 struct gsm48_sysinfo *s = cs->list[i].sysinfo;
1615 if (cs->state != GSM322_C2_STORED_CELL_SEL
1616 && cs->state != GSM322_C1_NORMAL_CELL_SEL
1617 && cs->state != GSM322_C6_ANY_CELL_SEL
1618 && cs->state != GSM322_C4_NORMAL_CELL_RESEL
1619 && cs->state != GSM322_C8_ANY_CELL_RESEL
1620 && cs->state != GSM322_C5_CHOOSE_CELL
1621 && cs->state != GSM322_C9_CHOOSE_ANY_CELL) {
1622 LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
1623 "(re-)selection, please fix!\n");
1628 cs->list[i].flags |= GSM322_CS_FLAG_SYSINFO;
1630 cs->list[i].flags |= GSM322_CS_FLAG_BARRED;
1632 cs->list[i].flags &= ~GSM322_CS_FLAG_BARRED;
1635 cs->list[i].min_db = s->rxlev_acc_min_db;
1636 cs->list[i].class_barr = s->class_barr;
1637 cs->list[i].max_pwr = s->ms_txpwr_max_ccch;
1640 /* store selected network */
1643 cs->list[i].mcc = s->mcc;
1644 cs->list[i].mnc = s->mnc;
1645 cs->list[i].lac = s->lac;
1648 if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
1649 cs->list[i].flags |= GSM322_CS_FLAG_FORBIDD;
1651 cs->list[i].flags &= ~GSM322_CS_FLAG_FORBIDD;
1654 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev=%d "
1655 "mcc=%03d mnc=%02d lac=%04x)\n", i, cs->list[i].rxlev_db,
1656 s->mcc, s->mnc, s->lac);
1658 /* special prositive case for HPLMN search */
1659 if (cs->state == GSM322_HPLMN_SEARCH && s->mcc == subscr->mcc
1660 && s->mnc == subscr->mnc) {
1663 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_FOUND);
1664 LOGP(DCS, LOGL_INFO, "HPLMN cell available.\n");
1667 gsm322_plmn_sendmsg(ms, nmsg);
1672 /* tune to next cell */
1673 return gsm322_cs_scan(ms);
1676 /* process system information when returing to idle mode */
1677 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
1679 struct gsm322_cellsel *cs = &ms->cellsel;
1680 struct gsm48_sysinfo *s = cs->si;
1681 struct gsm322_ba_list *ba = NULL;
1685 /* collect system information received during dedicated mode */
1687 && (!s->nb_ext_ind_si5
1688 || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
1689 || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
1690 && s->nb_ext_ind_si5bis))) {
1691 /* find or create ba list */
1692 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1694 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1699 llist_add_tail(&ba->entry, &cs->ba_list);
1701 /* update (add) ba list */
1702 memcpy(freq, ba->freq, sizeof(freq));
1703 for (i = 0; i <= 1023; i++) {
1704 if ((s->freq[i].mask & FREQ_TYPE_REP))
1705 freq[i >> 3] |= (1 << (i & 7));
1707 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
1708 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%d mnc=%d "
1709 "%s, %s).\n", ba->mcc, ba->mnc,
1710 gsm_get_mcc(ba->mcc),
1711 gsm_get_mnc(ba->mcc, ba->mnc));
1712 memcpy(ba->freq, freq, sizeof(freq));
1719 /* store BA whenever a system informations changes */
1720 static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
1721 struct gsm48_sysinfo *s)
1723 struct gsm322_ba_list *ba;
1727 /* find or create ba list */
1728 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1730 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1735 llist_add_tail(&ba->entry, &cs->ba_list);
1737 /* update ba list */
1738 memset(freq, 0, sizeof(freq));
1739 freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
1740 for (i = 0; i <= 1023; i++) {
1741 if ((s->freq[i].mask &
1742 (FREQ_TYPE_SERV | FREQ_TYPE_NCELL)))
1743 freq[i >> 3] |= (1 << (i & 7));
1745 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
1746 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%d mnc=%d "
1747 "%s, %s).\n", ba->mcc, ba->mnc,
1748 gsm_get_mcc(ba->mcc),
1749 gsm_get_mnc(ba->mcc, ba->mnc));
1750 memcpy(ba->freq, freq, sizeof(freq));
1756 /* process system information during camping on a cell */
1757 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
1759 struct gsm322_cellsel *cs = &ms->cellsel;
1760 struct gsm48_sysinfo *s = cs->si;
1761 struct gsm_subscriber *subscr = &ms->subscr;
1762 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1765 /* Store BA if we have full system info about cells and neigbor cells.
1766 * Depending on the extended bit in the channel description,
1767 * we require more or less system informations about neighbor cells
1771 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
1772 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
1773 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
1774 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
1777 && (!s->nb_ext_ind_si2
1778 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
1779 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
1780 && s->nb_ext_ind_si2bis)))
1781 gsm322_store_ba_list(cs, s);
1783 /* update sel_si, if all relevant system informations received */
1784 if (s->si1 && s->si2 && s->si3
1785 && (!s->nb_ext_ind_si2
1786 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
1787 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
1788 && s->nb_ext_ind_si2bis))) {
1790 LOGP(DCS, LOGL_INFO, "Selected sysinfo is updated.\n");
1791 memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
1795 /* check for barred cell */
1796 if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
1797 /* check if cell becomes barred */
1798 if (!subscr->acc_barr && s->cell_barr) {
1799 LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
1801 /* mark cell as unscanned */
1802 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1803 /* trigger reselection event */
1804 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
1807 gsm322_cs_sendmsg(ms, nmsg);
1811 /* check if cell access becomes barred */
1812 if (!((subscr->acc_class & 0xfbff)
1813 & (s->class_barr ^ 0xffff))) {
1814 LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
1822 /* process system information during channel scanning */
1823 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
1825 struct gsm322_cellsel *cs = &ms->cellsel;
1826 struct gsm48_sysinfo *s = cs->si;
1827 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1829 /* no sysinfo if we are not done with power scan */
1830 if (cs->powerscan) {
1831 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
1835 /* Store BA if we have full system info about cells and neigbor cells.
1836 * Depending on the extended bit in the channel description,
1837 * we require more or less system informations about neighbor cells
1841 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
1842 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
1843 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
1844 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
1847 && (!s->nb_ext_ind_si2
1848 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
1849 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
1850 && s->nb_ext_ind_si2bis)))
1851 gsm322_store_ba_list(cs, s);
1853 /* all relevant system informations received */
1854 if (s->si1 && s->si2 && s->si3
1855 && (!s->nb_ext_ind_si2
1856 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
1857 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
1858 && s->nb_ext_ind_si2bis))) {
1862 gsm48_sysinfo_dump(ms, s);
1864 /* store sysinfo and continue scan */
1865 return gsm322_cs_store(ms);
1868 /* wait for more sysinfo or timeout */
1872 static void gsm322_cs_timeout(void *arg)
1874 struct gsm322_cellsel *cs = arg;
1875 struct osmocom_ms *ms = cs->ms;
1876 int i = cs->scan_state & 1023;
1878 LOGP(DCS, LOGL_INFO, "Cell selection timer has fired.\n");
1879 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev=%d)\n",
1880 i, cs->list[i].rxlev_db);
1882 /* remove system information */
1883 cs->list[i].flags &= ~GSM322_CS_FLAG_SYSINFO;
1884 if (cs->list[i].sysinfo) {
1885 talloc_free(cs->list[i].sysinfo);
1886 cs->list[i].sysinfo = NULL;
1889 /* tune to next cell */
1896 * power scan process
1899 /* search for block of unscanned freqeuncies and start scanning */
1900 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
1902 struct gsm322_cellsel *cs = &ms->cellsel;
1904 uint8_t mask, flags;
1908 /* search for first frequency to scan */
1909 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
1910 flags = GSM322_CS_FLAG_SUPPORT;
1911 if (cs->state == GSM322_C2_STORED_CELL_SEL) {
1912 LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA list.\n");
1913 mask |= GSM322_CS_FLAG_BA;
1914 flags |= GSM322_CS_FLAG_BA;
1916 LOGP(DCS, LOGL_FATAL, "Scanning power for all frequencies.\n");
1917 for (i = 0; i <= 1023; i++) {
1918 if ((cs->list[i].flags & mask) == flags) {
1925 cs->list[10].rxlev_db = -50;
1926 cs->list[10].flags |= GSM322_CS_FLAG_POWER;
1927 cs->list[10].flags |= GSM322_CS_FLAG_SIGNAL;
1929 /* if there is no more frequency, we can tune to that cell */
1933 /* stop power level scanning */
1936 /* check if not signal is found */
1937 for (i = 0; i <= 1023; i++) {
1938 if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
1944 LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
1945 /* on normal cell selection, start over */
1946 if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
1947 for (i = 0; i <= 1023; i++) {
1948 /* clear flag that this was scanned */
1949 cs->list[i].flags &=
1950 ~(GSM322_CS_FLAG_POWER
1951 | GSM322_CS_FLAG_SIGNAL
1952 | GSM322_CS_FLAG_SYSINFO);
1953 if (cs->list[i].sysinfo) {
1955 cs->list[i].sysinfo);
1956 cs->list[i].sysinfo = NULL;
1961 /* on other cell selection, indicate "no cell found" */
1962 /* NOTE: PLMN search process handles it.
1963 * If not handled there, CS process gets indicated.
1964 * If we would continue to process CS, then we might get
1965 * our list of scanned cells disturbed.
1967 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1970 gsm322_plmn_sendmsg(ms, nmsg);
1974 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
1975 cs->scan_state = 0xffffffff; /* higher than high */
1976 /* clear counter of scanned frequencies of each range */
1977 for (i = 0; gsm_sup_smax[i].max; i++)
1978 gsm_sup_smax[i].temp = 0;
1979 return gsm322_cs_scan(ms);
1982 /* search last frequency to scan (en block) */
1984 for (i = s + 1; i <= 1023; i++) {
1985 if ((cs->list[i].flags & mask) == flags)
1991 LOGP(DCS, LOGL_INFO, "Scanning frequecies. (%d..%d)\n", s, e);
1993 /* start scan on radio interface */
1995 return l1ctl_tx_pm_req_range(ms, s, e);
1998 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
1999 void *handler_data, void *signal_data)
2001 struct osmocom_ms *ms;
2002 struct gsm322_cellsel *cs;
2003 struct osmobb_meas_res *mr;
2007 if (subsys != SS_L1CTL)
2011 case S_L1CTL_PM_RES:
2017 i = mr->band_arfcn & 1023;
2018 rxlev_db = mr->rx_lev - 110;
2019 cs->list[i].rxlev_db = rxlev_db;
2020 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2021 if (rxlev_db >= ms->support.min_rxlev_db) {
2022 cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2023 LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2024 "rxlev %d)\n", i, cs->list[i].rxlev_db);
2027 case S_L1CTL_PM_DONE:
2028 LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
2033 gsm322_cs_powerscan(ms);
2035 case S_L1CTL_CCCH_RESP:
2038 if (!cs->ccch_active) {
2039 LOGP(DCS, LOGL_INFO, "Channel activated.\n");
2040 cs->ccch_active = 1;
2041 /* set timer for reading BCCH */
2042 if (cs->state == GSM322_C2_STORED_CELL_SEL
2043 || cs->state == GSM322_C1_NORMAL_CELL_SEL
2044 || cs->state == GSM322_C6_ANY_CELL_SEL
2045 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2046 || cs->state == GSM322_C8_ANY_CELL_RESEL
2047 || cs->state == GSM322_C5_CHOOSE_CELL
2048 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
2049 start_cs_timer(cs, 8, 0);
2050 // TODO: timer depends on BCCH config
2058 * handler for cell selection process
2061 /* start HPLMN search */
2062 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2064 struct gsm322_cellsel *cs = &ms->cellsel;
2067 new_c_state(cs, GSM322_HPLMN_SEARCH);
2069 /* mark all frequencies except our own BA to be scanned */
2070 for (i = 0; i <= 1023; i++) {
2071 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2072 && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2073 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2074 | GSM322_CS_FLAG_SIGNAL
2075 | GSM322_CS_FLAG_SYSINFO);
2076 if (cs->list[i].sysinfo) {
2077 talloc_free(cs->list[i].sysinfo);
2078 cs->list[i].sysinfo = NULL;
2083 /* unset selected cell */
2085 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2086 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2088 /* start power scan */
2089 return gsm322_cs_powerscan(ms);
2092 /* start stored cell selection */
2093 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2095 struct gsm322_cellsel *cs = &ms->cellsel;
2098 new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2100 /* flag all frequencies that are in current band allocation */
2101 for (i = 0; i <= 1023; i++) {
2102 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2103 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2105 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2108 /* unset selected cell */
2110 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2111 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2113 /* start power scan */
2114 return gsm322_cs_powerscan(ms);
2117 /* start noraml cell selection */
2118 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2120 struct gsm322_cellsel *cs = &ms->cellsel;
2123 /* except for stored cell selection state, we weed to rescan ?? */
2124 if (cs->state != GSM322_C2_STORED_CELL_SEL) {
2125 for (i = 0; i <= 1023; i++) {
2126 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2127 | GSM322_CS_FLAG_SIGNAL
2128 | GSM322_CS_FLAG_SYSINFO);
2129 if (cs->list[i].sysinfo) {
2130 talloc_free(cs->list[i].sysinfo);
2131 cs->list[i].sysinfo = NULL;
2136 new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2138 /* unset selected cell */
2140 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2141 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2143 /* start power scan */
2144 return gsm322_cs_powerscan(ms);
2147 /* start any cell selection */
2148 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2150 struct gsm322_cellsel *cs = &ms->cellsel;
2153 /* in case we already tried any cell selection, power scan again */
2154 if (cs->state == GSM322_C6_ANY_CELL_SEL) {
2157 /* tell that we have no cell found */
2158 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2161 gsm48_mmevent_msg(ms, nmsg);
2163 for (i = 0; i <= 1023; i++) {
2164 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2165 | GSM322_CS_FLAG_SIGNAL
2166 | GSM322_CS_FLAG_SYSINFO);
2167 if (cs->list[i].sysinfo) {
2168 talloc_free(cs->list[i].sysinfo);
2169 cs->list[i].sysinfo = NULL;
2173 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2176 cs->mcc = cs->mnc = 0;
2178 /* unset selected cell */
2180 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2181 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2183 /* start power scan */
2184 return gsm322_cs_powerscan(ms);
2187 /* start noraml cell re-selection */
2188 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2190 struct gsm322_cellsel *cs = &ms->cellsel;
2192 new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2194 /* NOTE: We keep our scan info we have so far.
2195 * This may cause a skip in power scan. */
2197 /* start power scan */
2198 return gsm322_cs_powerscan(ms);
2201 /* start any cell re-selection */
2202 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2204 struct gsm322_cellsel *cs = &ms->cellsel;
2206 new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2208 /* NOTE: We keep our scan info we have so far.
2209 * This may cause a skip in power scan. */
2211 /* start power scan */
2212 return gsm322_cs_powerscan(ms);
2215 /* create temporary ba range with given frequency ranges */
2216 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2217 uint32_t *range, uint8_t ranges)
2219 static struct gsm322_ba_list ba;
2220 uint16_t lower, higher;
2222 memset(&ba, 0, sizeof(ba));
2225 lower = *range & 1023;
2226 higher = (*range >> 16) & 1023;
2228 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2231 ba.freq[lower >> 3] |= 1 << (lower & 7);
2232 if (lower == higher)
2234 lower = (lower + 1) & 1023;
2241 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2242 static int gsm322_cs_choose(struct osmocom_ms *ms)
2244 struct gsm322_cellsel *cs = &ms->cellsel;
2245 struct gsm322_ba_list *ba = NULL;
2249 what we have todo here:
2250 if we return from dedicated mode and we have a ba range, we can use that for cell reselection
2251 if (message->ranges)
2252 ba = gsm322_cs_ba_range(ms, message->range, message->ranges);
2254 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2256 /* get and update BA of last received sysinfo 5* */
2257 ba = gsm322_cs_sysinfo_sacch(ms);
2259 ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2268 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2270 /* tell CS to start over */
2271 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2274 gsm322_cs_sendmsg(ms, nmsg);
2277 /* flag all frequencies that are in current band allocation */
2278 for (i = 0; i <= 1023; i++) {
2279 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2280 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2281 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2283 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2285 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2286 | GSM322_CS_FLAG_SIGNAL
2287 | GSM322_CS_FLAG_SYSINFO);
2288 if (cs->list[i].sysinfo) {
2289 talloc_free(cs->list[i].sysinfo);
2290 cs->list[i].sysinfo = NULL;
2294 /* unset selected cell */
2296 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2297 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2299 /* start power scan */
2300 return gsm322_cs_powerscan(ms);
2303 /* start 'Choose cell' after returning to idle mode */
2304 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2306 struct gsm322_cellsel *cs = &ms->cellsel;
2308 new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2310 return gsm322_cs_choose(ms);
2313 /* start 'Choose any cell' after returning to idle mode */
2314 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2316 struct gsm322_cellsel *cs = &ms->cellsel;
2318 new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2320 return gsm322_cs_choose(ms);
2323 /* a new PLMN is selected by PLMN search process */
2324 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2326 struct gsm322_cellsel *cs = &ms->cellsel;
2327 struct gsm322_plmn *plmn = &ms->plmn;
2328 struct gsm322_ba_list *ba;
2330 cs->mcc = plmn->mcc;
2331 cs->mnc = plmn->mnc;
2333 /* search for BA list */
2334 ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2337 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2338 return gsm322_c_stored_cell_sel(ms, ba);
2340 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2341 return gsm322_c_normal_cell_sel(ms, msg);
2345 /* a suitable cell was found, so we camp normally */
2346 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2348 struct gsm322_cellsel *cs = &ms->cellsel;
2351 /* tell that we have selected a (new) cell */
2352 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2355 gsm48_mmevent_msg(ms, nmsg);
2357 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2362 /* a not suitable cell was found, so we camp on any cell */
2363 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2365 struct gsm322_cellsel *cs = &ms->cellsel;
2368 /* tell that we have selected a (new) cell */
2369 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2372 gsm48_mmevent_msg(ms, nmsg);
2374 new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2379 /* go connected mode */
2380 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2382 struct gsm322_cellsel *cs = &ms->cellsel;
2384 /* check for error */
2387 cs->arfcn = cs->sel_arfcn;
2389 /* be sure to go to current camping frequency on return */
2390 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2391 cs->ccch_active = 0;
2392 l1ctl_tx_ccch_req_(ms, cs->arfcn);
2393 cs->si = cs->list[cs->arfcn].sysinfo;
2398 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2400 struct gsm322_cellsel *cs = &ms->cellsel;
2402 /* check for error */
2405 cs->arfcn = cs->sel_arfcn;
2407 /* be sure to go to current camping frequency on return */
2408 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2409 cs->ccch_active = 0;
2410 l1ctl_tx_ccch_req_(ms, cs->arfcn);
2411 cs->si = cs->list[cs->arfcn].sysinfo;
2417 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2419 struct gsm_subscriber *subscr = &ms->subscr;
2421 /* if no SIM is is MS */
2422 if (!subscr->sim_valid) {
2423 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2424 return gsm322_c_any_cell_sel(ms, msg);
2425 LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2428 /* stay in NULL state until PLMN is selected */
2437 /* state machine for automatic PLMN selection events */
2438 static struct plmnastatelist {
2441 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2442 } plmnastatelist[] = {
2443 {SBIT(GSM322_A0_NULL),
2444 GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2446 GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
2447 {SBIT(GSM322_A6_NO_SIM),
2448 GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
2450 GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
2452 GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
2454 GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
2455 {SBIT(GSM322_A1_TRYING_RPLMN),
2456 GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
2457 {SBIT(GSM322_A1_TRYING_RPLMN),
2458 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
2459 {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
2460 GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
2461 {SBIT(GSM322_A2_ON_PLMN),
2462 GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
2463 {SBIT(GSM322_A2_ON_PLMN),
2464 GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search},
2465 {SBIT(GSM322_A2_ON_PLMN),
2466 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
2467 {SBIT(GSM322_A2_ON_PLMN),
2468 GSM322_EVENT_USER_RESEL, gsm322_a_user_reselection},
2469 {SBIT(GSM322_A3_TRYING_PLMN),
2470 GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
2471 {SBIT(GSM322_A3_TRYING_PLMN),
2472 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
2473 {SBIT(GSM322_A5_HPLMN_SEARCH),
2474 GSM322_EVENT_HPLMN_FOUND, gsm322_a_sel_first_plmn},
2475 {SBIT(GSM322_A5_HPLMN_SEARCH),
2476 GSM322_EVENT_HPLMN_NOT_FOUND, gsm322_a_go_on_plmn},
2477 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
2478 GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
2480 GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
2482 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
2485 #define PLMNASLLEN \
2486 (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
2488 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
2490 struct gsm322_plmn *plmn = &ms->plmn;
2491 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2492 int msg_type = gm->msg_type;
2496 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
2497 "selection in state %s\n", ms->name, get_event_name(msg_type),
2498 plmn_a_state_names[plmn->state]);
2499 /* find function for current state and message */
2500 for (i = 0; i < PLMNASLLEN; i++)
2501 if ((msg_type == plmnastatelist[i].type)
2502 && ((1 << plmn->state) & plmnastatelist[i].states))
2504 if (i == PLMNASLLEN) {
2505 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
2509 rc = plmnastatelist[i].rout(ms, msg);
2514 /* state machine for manual PLMN selection events */
2515 static struct plmnmstatelist {
2518 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2519 } plmnmstatelist[] = {
2520 {SBIT(GSM322_M0_NULL),
2521 GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
2523 GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
2524 {SBIT(GSM322_M5_NO_SIM),
2525 GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
2527 GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
2529 GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
2530 {SBIT(GSM322_M1_TRYING_RPLMN),
2531 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
2532 {SBIT(GSM322_M1_TRYING_RPLMN),
2533 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
2534 {SBIT(GSM322_M1_TRYING_RPLMN),
2535 GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
2536 {SBIT(GSM322_M2_ON_PLMN),
2537 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
2538 {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
2539 SBIT(GSM322_M4_TRYING_PLMN),
2540 GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
2541 {SBIT(GSM322_M2_ON_PLMN),
2542 GSM322_EVENT_USER_RESEL, gsm322_m_display_plmns},
2543 {SBIT(GSM322_M3_NOT_ON_PLMN),
2544 GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
2545 {SBIT(GSM322_M3_NOT_ON_PLMN),
2546 GSM322_EVENT_CHOSE_PLMN, gsm322_m_choose_plmn},
2547 {SBIT(GSM322_M4_TRYING_PLMN),
2548 GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
2549 {SBIT(GSM322_M4_TRYING_PLMN),
2550 GSM322_EVENT_REG_FAILED, gsm322_m_go_not_on_plmn},
2551 {SBIT(GSM322_M4_TRYING_PLMN),
2552 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_go_not_on_plmn},
2554 GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
2556 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
2559 #define PLMNMSLLEN \
2560 (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
2562 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
2564 struct gsm322_plmn *plmn = &ms->plmn;
2565 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2566 int msg_type = gm->msg_type;
2570 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
2571 "in state %s\n", ms->name, get_event_name(msg_type),
2572 plmn_m_state_names[plmn->state]);
2573 /* find function for current state and message */
2574 for (i = 0; i < PLMNMSLLEN; i++)
2575 if ((msg_type == plmnmstatelist[i].type)
2576 && ((1 << plmn->state) & plmnmstatelist[i].states))
2578 if (i == PLMNMSLLEN) {
2579 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
2583 rc = plmnmstatelist[i].rout(ms, msg);
2588 /* dequeue GSM 03.22 PLMN events */
2589 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
2591 struct gsm322_plmn *plmn = &ms->plmn;
2595 while ((msg = msgb_dequeue(&plmn->event_queue))) {
2596 /* send event to PLMN select process */
2597 if (plmn->mode == PLMN_MODE_AUTO)
2598 gsm322_a_event(ms, msg);
2600 gsm322_m_event(ms, msg);
2602 work = 1; /* work done */
2608 /* state machine for channel selection events */
2609 static struct cellselstatelist {
2612 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2613 } cellselstatelist[] = {
2615 GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
2617 GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
2619 GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
2620 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
2621 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
2622 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
2623 {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
2624 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
2625 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
2626 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
2627 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
2628 SBIT(GSM322_C0_NULL),
2629 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
2630 {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
2631 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
2632 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
2633 {SBIT(GSM322_C3_CAMPED_NORMALLY),
2634 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
2635 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2636 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
2637 {SBIT(GSM322_C3_CAMPED_NORMALLY),
2638 GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
2639 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2640 GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
2641 {SBIT(GSM322_C3_CAMPED_NORMALLY),
2642 GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
2643 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2644 GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
2645 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2646 GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
2647 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
2648 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
2649 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
2650 SBIT(GSM322_C6_ANY_CELL_SEL),
2651 GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
2652 {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
2653 GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
2654 {SBIT(GSM322_C3_CAMPED_NORMALLY),
2655 GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search}
2658 #define CELLSELSLLEN \
2659 (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
2661 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
2663 struct gsm322_cellsel *cs = &ms->cellsel;
2664 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2665 int msg_type = gm->msg_type;
2669 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
2670 "%s\n", ms->name, get_event_name(msg_type),
2671 cs_state_names[cs->state]);
2672 /* find function for current state and message */
2673 for (i = 0; i < CELLSELSLLEN; i++)
2674 if ((msg_type == cellselstatelist[i].type)
2675 && ((1 << cs->state) & cellselstatelist[i].states))
2677 if (i == CELLSELSLLEN) {
2678 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
2682 rc = cellselstatelist[i].rout(ms, msg);
2687 /* dequeue GSM 03.22 cell selection events */
2688 int gsm322_cs_dequeue(struct osmocom_ms *ms)
2690 struct gsm322_cellsel *cs = &ms->cellsel;
2694 while ((msg = msgb_dequeue(&cs->event_queue))) {
2695 /* send event to cell selection process */
2696 gsm322_c_event(ms, msg);
2698 work = 1; /* work done */
2708 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
2710 struct gsm322_plmn *plmn = &ms->plmn;
2711 struct gsm322_plmn_list *temp;
2713 printf("MCC |MNC |allowed|rx-lev\n");
2714 printf("-------+-------+-------+-------\n");
2715 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
2716 printf("%03d |%02d |%s |%d\n", temp->mcc, temp->mnc,
2717 (temp->cause) ? "no ":"yes", temp->rxlev_db);
2723 int gsm322_dump_cs_list(struct osmocom_ms *ms)
2725 struct gsm322_cellsel *cs = &ms->cellsel;
2728 printf("rx-lev |MCC |MNC |forb.LA|barred,0123456789abcdef|"
2730 "-------+-------+-------+-------+-----------------------+"
2731 "-------+-------\n");
2732 for (i = 0; i <= 1023; i++) {
2733 if (!(cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2735 printf("%4d |", cs->list[i].rxlev_db);
2736 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
2737 struct gsm48_sysinfo *s = cs->list[i].sysinfo;
2738 printf("%03d |%02d |", s->mcc, s->mnc);
2739 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
2743 if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
2747 for (j = 0; j < 16; j++) {
2748 if ((s->class_barr & (1 << j)))
2753 printf("|%4d |%4d\n", s->rxlev_acc_min_db,
2754 s->ms_txpwr_max_ccch);
2756 printf("n/a |n/a | | "
2763 int gsm322_dump_sim_plmn(struct osmocom_ms *ms)
2765 struct gsm_subscriber *subscr = &ms->subscr;
2766 struct gsm_sub_plmn_list *temp;
2768 printf("MCC |MNC\n");
2769 printf("-------+-------\n");
2770 llist_for_each_entry(temp, &subscr->plmn_list, entry)
2771 printf("%03d |%02d\n", temp->mcc, temp->mnc);
2776 int gsm322_dump_forbidden_plmn(struct osmocom_ms *ms)
2778 struct gsm_subscriber *subscr = &ms->subscr;
2779 struct gsm_sub_plmn_na *temp;
2781 printf("MCC |MNC |cause\n");
2782 printf("-------+-------+-------\n");
2783 llist_for_each_entry(temp, &subscr->plmn_na, entry)
2784 printf("%03d |%02d |#%d\n", temp->mcc, temp->mnc,
2790 int gsm322_dump_forbidden_la(struct osmocom_ms *ms)
2792 struct gsm322_plmn *plmn = &ms->plmn;
2793 struct gsm322_la_list *temp;
2795 printf("MCC |MNC |LAC |cause\n");
2796 printf("-------+-------+-------+-------\n");
2797 llist_for_each_entry(temp, &plmn->forbidden_la, entry)
2798 printf("%03d |%02d |0x%04x |#%d\n", temp->mcc, temp->mnc,
2799 temp->lac, temp->cause);
2808 int gsm322_init(struct osmocom_ms *ms)
2810 struct gsm322_plmn *plmn = &ms->plmn;
2811 struct gsm322_cellsel *cs = &ms->cellsel;
2813 char suffix[] = ".ba";
2814 char filename[sizeof(ms->name) + strlen(suffix) + 1];
2816 struct gsm322_ba_list *ba;
2819 LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
2820 LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
2822 memset(plmn, 0, sizeof(*plmn));
2823 memset(cs, 0, sizeof(*cs));
2827 /* set initial state */
2830 plmn->mode = PLMN_MODE_AUTO;
2833 INIT_LLIST_HEAD(&plmn->event_queue);
2834 INIT_LLIST_HEAD(&cs->event_queue);
2835 INIT_LLIST_HEAD(&plmn->sorted_plmn);
2836 INIT_LLIST_HEAD(&plmn->forbidden_la);
2837 INIT_LLIST_HEAD(&cs->ba_list);
2839 /* set supported frequencies in cell selection list */
2840 for (i = 0; i <= 1023; i++)
2841 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
2842 cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
2845 strcpy(filename, ms->name);
2846 strcat(filename, suffix);
2847 fp = osmocom_fopen(filename, "r");
2851 while(!osmocom_feof(fp)) {
2852 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2855 rc = osmocom_fread(buf, 4, 1, fp);
2860 ba->mcc = (buf[0] << 8) | buf[1];
2861 ba->mnc = (buf[2] << 8) | buf[3];
2862 rc = osmocom_fread(ba->freq, sizeof(ba->freq), 1, fp);
2867 llist_add_tail(&ba->entry, &cs->ba_list);
2868 LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%d "
2869 "mnc=%d %s, %s)\n", ba->mcc, ba->mnc,
2870 gsm_get_mcc(ba->mcc),
2871 gsm_get_mnc(ba->mcc, ba->mnc));
2875 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
2877 register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
2882 int gsm322_exit(struct osmocom_ms *ms)
2884 struct gsm322_plmn *plmn = &ms->plmn;
2885 struct gsm322_cellsel *cs = &ms->cellsel;
2886 struct llist_head *lh, *lh2;
2889 char suffix[] = ".ba";
2890 char filename[sizeof(ms->name) + strlen(suffix) + 1];
2891 struct gsm322_ba_list *ba;
2895 LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
2896 LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
2898 unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
2900 /* stop cell selection process (if any) */
2901 new_c_state(cs, GSM322_C0_NULL);
2905 stop_plmn_timer(plmn);
2908 for (i = 0; i <= 1023; i++) {
2909 if (cs->list[i].sysinfo) {
2910 talloc_free(cs->list[i].sysinfo);
2911 cs->list[i].sysinfo = NULL;
2913 cs->list[i].flags = 0;
2917 strcpy(filename, ms->name);
2918 strcat(filename, suffix);
2919 fp = osmocom_fopen(filename, "w");
2923 llist_for_each_entry(ba, &cs->ba_list, entry) {
2924 buf[0] = ba->mcc >> 8;
2925 buf[1] = ba->mcc & 0xff;
2926 buf[2] = ba->mnc >> 8;
2927 buf[3] = ba->mnc & 0xff;
2928 rc = osmocom_fwrite(buf, 4, 1, fp);
2929 rc = osmocom_fwrite(ba->freq, sizeof(ba->freq), 1, fp);
2930 LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%d "
2931 "mnc=%d %s, %s)\n", ba->mcc, ba->mnc,
2932 gsm_get_mcc(ba->mcc),
2933 gsm_get_mnc(ba->mcc, ba->mnc));
2937 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
2940 while ((msg = msgb_dequeue(&plmn->event_queue)))
2942 while ((msg = msgb_dequeue(&cs->event_queue)))
2944 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
2948 llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
2952 llist_for_each_safe(lh, lh2, &cs->ba_list) {