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 static int already = 0;
49 int l1ctl_tx_ccch_req_(struct osmocom_ms *ms, uint16_t arfcn)
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, GSM322_CS_FLAG_SYSINFO);
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, "Sysinfo of selected cell is "
1792 memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
1793 gsm48_sysinfo_dump(ms, s);
1797 /* check for barred cell */
1798 if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
1799 /* check if cell becomes barred */
1800 if (!subscr->acc_barr && s->cell_barr) {
1801 LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
1803 /* mark cell as unscanned */
1804 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1805 /* trigger reselection event */
1806 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
1809 gsm322_cs_sendmsg(ms, nmsg);
1813 /* check if cell access becomes barred */
1814 if (!((subscr->acc_class & 0xfbff)
1815 & (s->class_barr ^ 0xffff))) {
1816 LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
1824 /* process system information during channel scanning */
1825 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
1827 struct gsm322_cellsel *cs = &ms->cellsel;
1828 struct gsm48_sysinfo *s = cs->si;
1829 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1831 /* no sysinfo if we are not done with power scan */
1832 if (cs->powerscan) {
1833 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
1837 /* Store BA if we have full system info about cells and neigbor cells.
1838 * Depending on the extended bit in the channel description,
1839 * we require more or less system informations about neighbor cells
1843 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
1844 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
1845 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
1846 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
1849 && (!s->nb_ext_ind_si2
1850 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
1851 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
1852 && s->nb_ext_ind_si2bis)))
1853 gsm322_store_ba_list(cs, s);
1855 /* all relevant system informations received */
1856 if (s->si1 && s->si2 && s->si3
1857 && (!s->nb_ext_ind_si2
1858 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
1859 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
1860 && s->nb_ext_ind_si2bis))) {
1861 LOGP(DCS, LOGL_INFO, "Received relevant sysinfo.\n");
1865 gsm48_sysinfo_dump(ms, s);
1867 /* store sysinfo and continue scan */
1868 return gsm322_cs_store(ms);
1871 /* wait for more sysinfo or timeout */
1875 static void gsm322_cs_timeout(void *arg)
1877 struct gsm322_cellsel *cs = arg;
1878 struct osmocom_ms *ms = cs->ms;
1879 int i = cs->scan_state & 1023;
1881 LOGP(DCS, LOGL_INFO, "Cell selection timer has fired.\n");
1882 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev=%d)\n",
1883 i, cs->list[i].rxlev_db);
1885 /* remove system information */
1886 cs->list[i].flags &= ~GSM322_CS_FLAG_SYSINFO;
1887 if (cs->list[i].sysinfo) {
1888 talloc_free(cs->list[i].sysinfo);
1889 cs->list[i].sysinfo = NULL;
1892 /* tune to next cell */
1899 * power scan process
1902 /* search for block of unscanned freqeuncies and start scanning */
1903 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
1905 struct gsm322_cellsel *cs = &ms->cellsel;
1907 uint8_t mask, flags;
1911 /* search for first frequency to scan */
1912 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
1913 flags = GSM322_CS_FLAG_SUPPORT;
1914 if (cs->state == GSM322_C2_STORED_CELL_SEL) {
1915 LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA list.\n");
1916 mask |= GSM322_CS_FLAG_BA;
1917 flags |= GSM322_CS_FLAG_BA;
1919 LOGP(DCS, LOGL_FATAL, "Scanning power for all frequencies.\n");
1920 for (i = 0; i <= 1023; i++) {
1921 if ((cs->list[i].flags & mask) == flags) {
1928 if (already) s = -1;
1930 cs->list[ms->test_arfcn].rxlev_db = -50;
1931 cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_POWER;
1932 cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_SIGNAL;
1936 /* if there is no more frequency, we can tune to that cell */
1940 /* stop power level scanning */
1943 /* check if not signal is found */
1944 for (i = 0; i <= 1023; i++) {
1945 if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
1951 LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
1952 /* on normal cell selection, start over */
1953 if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
1954 for (i = 0; i <= 1023; i++) {
1955 /* clear flag that this was scanned */
1956 cs->list[i].flags &=
1957 ~(GSM322_CS_FLAG_POWER
1958 | GSM322_CS_FLAG_SIGNAL
1959 | GSM322_CS_FLAG_SYSINFO);
1960 if (cs->list[i].sysinfo) {
1962 cs->list[i].sysinfo);
1963 cs->list[i].sysinfo = NULL;
1968 /* on other cell selection, indicate "no cell found" */
1969 /* NOTE: PLMN search process handles it.
1970 * If not handled there, CS process gets indicated.
1971 * If we would continue to process CS, then we might get
1972 * our list of scanned cells disturbed.
1974 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1977 gsm322_plmn_sendmsg(ms, nmsg);
1981 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
1982 cs->scan_state = 0xffffffff; /* higher than high */
1983 /* clear counter of scanned frequencies of each range */
1984 for (i = 0; gsm_sup_smax[i].max; i++)
1985 gsm_sup_smax[i].temp = 0;
1986 return gsm322_cs_scan(ms);
1989 /* search last frequency to scan (en block) */
1991 for (i = s + 1; i <= 1023; i++) {
1992 if ((cs->list[i].flags & mask) == flags)
1998 LOGP(DCS, LOGL_INFO, "Scanning frequecies. (%d..%d)\n", s, e);
2000 /* start scan on radio interface */
2002 return l1ctl_tx_pm_req_range(ms, s, e);
2005 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
2006 void *handler_data, void *signal_data)
2008 struct osmocom_ms *ms;
2009 struct gsm322_cellsel *cs;
2010 struct osmobb_meas_res *mr;
2014 if (subsys != SS_L1CTL)
2018 case S_L1CTL_PM_RES:
2024 i = mr->band_arfcn & 1023;
2025 rxlev_db = mr->rx_lev - 110;
2026 cs->list[i].rxlev_db = rxlev_db;
2027 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2028 if (rxlev_db >= ms->support.min_rxlev_db) {
2029 cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2030 LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2031 "rxlev %d)\n", i, cs->list[i].rxlev_db);
2034 case S_L1CTL_PM_DONE:
2035 LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
2040 gsm322_cs_powerscan(ms);
2042 case S_L1CTL_CCCH_RESP:
2045 if (!cs->ccch_active) {
2046 LOGP(DCS, LOGL_INFO, "Channel activated.\n");
2047 cs->ccch_active = 1;
2048 /* set timer for reading BCCH */
2049 if (cs->state == GSM322_C2_STORED_CELL_SEL
2050 || cs->state == GSM322_C1_NORMAL_CELL_SEL
2051 || cs->state == GSM322_C6_ANY_CELL_SEL
2052 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2053 || cs->state == GSM322_C8_ANY_CELL_RESEL
2054 || cs->state == GSM322_C5_CHOOSE_CELL
2055 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
2056 start_cs_timer(cs, 8, 0);
2057 // TODO: timer depends on BCCH config
2065 * handler for cell selection process
2068 /* start HPLMN search */
2069 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2071 struct gsm322_cellsel *cs = &ms->cellsel;
2074 new_c_state(cs, GSM322_HPLMN_SEARCH);
2076 /* mark all frequencies except our own BA to be scanned */
2077 for (i = 0; i <= 1023; i++) {
2078 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2079 && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2080 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2081 | GSM322_CS_FLAG_SIGNAL
2082 | GSM322_CS_FLAG_SYSINFO);
2083 if (cs->list[i].sysinfo) {
2084 talloc_free(cs->list[i].sysinfo);
2085 cs->list[i].sysinfo = NULL;
2090 /* unset selected cell */
2092 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2093 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2095 /* start power scan */
2096 return gsm322_cs_powerscan(ms);
2099 /* start stored cell selection */
2100 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2102 struct gsm322_cellsel *cs = &ms->cellsel;
2105 new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2107 /* flag all frequencies that are in current band allocation */
2108 for (i = 0; i <= 1023; i++) {
2109 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2110 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2112 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2115 /* unset selected cell */
2117 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2118 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2120 /* start power scan */
2121 return gsm322_cs_powerscan(ms);
2124 /* start noraml cell selection */
2125 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2127 struct gsm322_cellsel *cs = &ms->cellsel;
2130 /* except for stored cell selection state, we weed to rescan ?? */
2131 if (cs->state != GSM322_C2_STORED_CELL_SEL) {
2132 for (i = 0; i <= 1023; i++) {
2133 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2134 | GSM322_CS_FLAG_SIGNAL
2135 | GSM322_CS_FLAG_SYSINFO);
2136 if (cs->list[i].sysinfo) {
2137 talloc_free(cs->list[i].sysinfo);
2138 cs->list[i].sysinfo = NULL;
2143 new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2145 /* unset selected cell */
2147 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2148 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2150 /* start power scan */
2151 return gsm322_cs_powerscan(ms);
2154 /* start any cell selection */
2155 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2157 struct gsm322_cellsel *cs = &ms->cellsel;
2160 /* in case we already tried any cell selection, power scan again */
2161 if (cs->state == GSM322_C6_ANY_CELL_SEL) {
2164 /* tell that we have no cell found */
2165 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2168 gsm48_mmevent_msg(ms, nmsg);
2170 for (i = 0; i <= 1023; i++) {
2171 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2172 | GSM322_CS_FLAG_SIGNAL
2173 | GSM322_CS_FLAG_SYSINFO);
2174 if (cs->list[i].sysinfo) {
2175 talloc_free(cs->list[i].sysinfo);
2176 cs->list[i].sysinfo = NULL;
2180 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2183 cs->mcc = cs->mnc = 0;
2185 /* unset selected cell */
2187 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2188 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2190 /* start power scan */
2191 return gsm322_cs_powerscan(ms);
2194 /* start noraml cell re-selection */
2195 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2197 struct gsm322_cellsel *cs = &ms->cellsel;
2199 new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2201 /* NOTE: We keep our scan info we have so far.
2202 * This may cause a skip in power scan. */
2204 /* start power scan */
2205 return gsm322_cs_powerscan(ms);
2208 /* start any cell re-selection */
2209 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2211 struct gsm322_cellsel *cs = &ms->cellsel;
2213 new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2215 /* NOTE: We keep our scan info we have so far.
2216 * This may cause a skip in power scan. */
2218 /* start power scan */
2219 return gsm322_cs_powerscan(ms);
2222 /* create temporary ba range with given frequency ranges */
2223 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2224 uint32_t *range, uint8_t ranges)
2226 static struct gsm322_ba_list ba;
2227 uint16_t lower, higher;
2229 memset(&ba, 0, sizeof(ba));
2232 lower = *range & 1023;
2233 higher = (*range >> 16) & 1023;
2235 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2238 ba.freq[lower >> 3] |= 1 << (lower & 7);
2239 if (lower == higher)
2241 lower = (lower + 1) & 1023;
2248 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2249 static int gsm322_cs_choose(struct osmocom_ms *ms)
2251 struct gsm322_cellsel *cs = &ms->cellsel;
2252 struct gsm322_ba_list *ba = NULL;
2256 what we have todo here:
2257 if we return from dedicated mode and we have a ba range, we can use that for cell reselection
2258 if (message->ranges)
2259 ba = gsm322_cs_ba_range(ms, message->range, message->ranges);
2261 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2263 /* get and update BA of last received sysinfo 5* */
2264 ba = gsm322_cs_sysinfo_sacch(ms);
2266 ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2275 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2277 /* tell CS to start over */
2278 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2281 gsm322_cs_sendmsg(ms, nmsg);
2284 /* flag all frequencies that are in current band allocation */
2285 for (i = 0; i <= 1023; i++) {
2286 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2287 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2288 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2290 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2292 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2293 | GSM322_CS_FLAG_SIGNAL
2294 | GSM322_CS_FLAG_SYSINFO);
2295 if (cs->list[i].sysinfo) {
2296 talloc_free(cs->list[i].sysinfo);
2297 cs->list[i].sysinfo = NULL;
2301 /* unset selected cell */
2303 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2304 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2306 /* start power scan */
2307 return gsm322_cs_powerscan(ms);
2310 /* start 'Choose cell' after returning to idle mode */
2311 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2313 struct gsm322_cellsel *cs = &ms->cellsel;
2315 new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2317 return gsm322_cs_choose(ms);
2320 /* start 'Choose any cell' after returning to idle mode */
2321 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2323 struct gsm322_cellsel *cs = &ms->cellsel;
2325 new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2327 return gsm322_cs_choose(ms);
2330 /* a new PLMN is selected by PLMN search process */
2331 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2333 struct gsm322_cellsel *cs = &ms->cellsel;
2334 struct gsm322_plmn *plmn = &ms->plmn;
2335 struct gsm322_ba_list *ba;
2337 cs->mcc = plmn->mcc;
2338 cs->mnc = plmn->mnc;
2340 /* search for BA list */
2341 ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2344 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2345 return gsm322_c_stored_cell_sel(ms, ba);
2347 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2348 return gsm322_c_normal_cell_sel(ms, msg);
2352 /* a suitable cell was found, so we camp normally */
2353 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2355 struct gsm322_cellsel *cs = &ms->cellsel;
2358 /* tell that we have selected a (new) cell */
2359 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2362 gsm48_mmevent_msg(ms, nmsg);
2364 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2369 /* a not suitable cell was found, so we camp on any cell */
2370 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2372 struct gsm322_cellsel *cs = &ms->cellsel;
2375 /* tell that we have selected a (new) cell */
2376 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2379 gsm48_mmevent_msg(ms, nmsg);
2381 new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2386 /* go connected mode */
2387 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2389 struct gsm322_cellsel *cs = &ms->cellsel;
2391 /* check for error */
2394 cs->arfcn = cs->sel_arfcn;
2396 /* be sure to go to current camping frequency on return */
2397 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2398 cs->ccch_active = 0;
2399 l1ctl_tx_ccch_req_(ms, cs->arfcn);
2400 cs->si = cs->list[cs->arfcn].sysinfo;
2405 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2407 struct gsm322_cellsel *cs = &ms->cellsel;
2409 /* check for error */
2412 cs->arfcn = cs->sel_arfcn;
2414 /* be sure to go to current camping frequency on return */
2415 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2416 cs->ccch_active = 0;
2417 l1ctl_tx_ccch_req_(ms, cs->arfcn);
2418 cs->si = cs->list[cs->arfcn].sysinfo;
2424 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2426 struct gsm_subscriber *subscr = &ms->subscr;
2428 /* if no SIM is is MS */
2429 if (!subscr->sim_valid) {
2430 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2431 return gsm322_c_any_cell_sel(ms, msg);
2432 LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2435 /* stay in NULL state until PLMN is selected */
2444 /* state machine for automatic PLMN selection events */
2445 static struct plmnastatelist {
2448 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2449 } plmnastatelist[] = {
2450 {SBIT(GSM322_A0_NULL),
2451 GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2453 GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
2454 {SBIT(GSM322_A6_NO_SIM),
2455 GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
2457 GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
2459 GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
2461 GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
2462 {SBIT(GSM322_A1_TRYING_RPLMN),
2463 GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
2464 {SBIT(GSM322_A1_TRYING_RPLMN),
2465 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
2466 {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
2467 GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
2468 {SBIT(GSM322_A2_ON_PLMN),
2469 GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
2470 {SBIT(GSM322_A2_ON_PLMN),
2471 GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search},
2472 {SBIT(GSM322_A2_ON_PLMN),
2473 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
2474 {SBIT(GSM322_A2_ON_PLMN),
2475 GSM322_EVENT_USER_RESEL, gsm322_a_user_reselection},
2476 {SBIT(GSM322_A3_TRYING_PLMN),
2477 GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
2478 {SBIT(GSM322_A3_TRYING_PLMN),
2479 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
2480 {SBIT(GSM322_A5_HPLMN_SEARCH),
2481 GSM322_EVENT_HPLMN_FOUND, gsm322_a_sel_first_plmn},
2482 {SBIT(GSM322_A5_HPLMN_SEARCH),
2483 GSM322_EVENT_HPLMN_NOT_FOUND, gsm322_a_go_on_plmn},
2484 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
2485 GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
2487 GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
2489 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
2492 #define PLMNASLLEN \
2493 (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
2495 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
2497 struct gsm322_plmn *plmn = &ms->plmn;
2498 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2499 int msg_type = gm->msg_type;
2503 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
2504 "selection in state %s\n", ms->name, get_event_name(msg_type),
2505 plmn_a_state_names[plmn->state]);
2506 /* find function for current state and message */
2507 for (i = 0; i < PLMNASLLEN; i++)
2508 if ((msg_type == plmnastatelist[i].type)
2509 && ((1 << plmn->state) & plmnastatelist[i].states))
2511 if (i == PLMNASLLEN) {
2512 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
2516 rc = plmnastatelist[i].rout(ms, msg);
2521 /* state machine for manual PLMN selection events */
2522 static struct plmnmstatelist {
2525 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2526 } plmnmstatelist[] = {
2527 {SBIT(GSM322_M0_NULL),
2528 GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
2530 GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
2531 {SBIT(GSM322_M5_NO_SIM),
2532 GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
2534 GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
2536 GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
2537 {SBIT(GSM322_M1_TRYING_RPLMN),
2538 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
2539 {SBIT(GSM322_M1_TRYING_RPLMN),
2540 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
2541 {SBIT(GSM322_M1_TRYING_RPLMN),
2542 GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
2543 {SBIT(GSM322_M2_ON_PLMN),
2544 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
2545 {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
2546 SBIT(GSM322_M4_TRYING_PLMN),
2547 GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
2548 {SBIT(GSM322_M2_ON_PLMN),
2549 GSM322_EVENT_USER_RESEL, gsm322_m_display_plmns},
2550 {SBIT(GSM322_M3_NOT_ON_PLMN),
2551 GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
2552 {SBIT(GSM322_M3_NOT_ON_PLMN),
2553 GSM322_EVENT_CHOSE_PLMN, gsm322_m_choose_plmn},
2554 {SBIT(GSM322_M4_TRYING_PLMN),
2555 GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
2556 {SBIT(GSM322_M4_TRYING_PLMN),
2557 GSM322_EVENT_REG_FAILED, gsm322_m_go_not_on_plmn},
2558 {SBIT(GSM322_M4_TRYING_PLMN),
2559 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_go_not_on_plmn},
2561 GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
2563 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
2566 #define PLMNMSLLEN \
2567 (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
2569 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
2571 struct gsm322_plmn *plmn = &ms->plmn;
2572 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2573 int msg_type = gm->msg_type;
2577 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
2578 "in state %s\n", ms->name, get_event_name(msg_type),
2579 plmn_m_state_names[plmn->state]);
2580 /* find function for current state and message */
2581 for (i = 0; i < PLMNMSLLEN; i++)
2582 if ((msg_type == plmnmstatelist[i].type)
2583 && ((1 << plmn->state) & plmnmstatelist[i].states))
2585 if (i == PLMNMSLLEN) {
2586 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
2590 rc = plmnmstatelist[i].rout(ms, msg);
2595 /* dequeue GSM 03.22 PLMN events */
2596 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
2598 struct gsm322_plmn *plmn = &ms->plmn;
2602 while ((msg = msgb_dequeue(&plmn->event_queue))) {
2603 /* send event to PLMN select process */
2604 if (plmn->mode == PLMN_MODE_AUTO)
2605 gsm322_a_event(ms, msg);
2607 gsm322_m_event(ms, msg);
2609 work = 1; /* work done */
2615 /* state machine for channel selection events */
2616 static struct cellselstatelist {
2619 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2620 } cellselstatelist[] = {
2622 GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
2624 GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
2626 GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
2627 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
2628 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
2629 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
2630 {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
2631 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
2632 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
2633 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
2634 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
2635 SBIT(GSM322_C0_NULL),
2636 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
2637 {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
2638 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
2639 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
2640 {SBIT(GSM322_C3_CAMPED_NORMALLY),
2641 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
2642 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2643 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
2644 {SBIT(GSM322_C3_CAMPED_NORMALLY),
2645 GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
2646 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2647 GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
2648 {SBIT(GSM322_C3_CAMPED_NORMALLY),
2649 GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
2650 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2651 GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
2652 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2653 GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
2654 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
2655 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
2656 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
2657 SBIT(GSM322_C6_ANY_CELL_SEL),
2658 GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
2659 {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
2660 GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
2661 {SBIT(GSM322_C3_CAMPED_NORMALLY),
2662 GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search}
2665 #define CELLSELSLLEN \
2666 (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
2668 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
2670 struct gsm322_cellsel *cs = &ms->cellsel;
2671 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2672 int msg_type = gm->msg_type;
2676 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
2677 "%s\n", ms->name, get_event_name(msg_type),
2678 cs_state_names[cs->state]);
2679 /* find function for current state and message */
2680 for (i = 0; i < CELLSELSLLEN; i++)
2681 if ((msg_type == cellselstatelist[i].type)
2682 && ((1 << cs->state) & cellselstatelist[i].states))
2684 if (i == CELLSELSLLEN) {
2685 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
2689 rc = cellselstatelist[i].rout(ms, msg);
2694 /* dequeue GSM 03.22 cell selection events */
2695 int gsm322_cs_dequeue(struct osmocom_ms *ms)
2697 struct gsm322_cellsel *cs = &ms->cellsel;
2701 while ((msg = msgb_dequeue(&cs->event_queue))) {
2702 /* send event to cell selection process */
2703 gsm322_c_event(ms, msg);
2705 work = 1; /* work done */
2715 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
2717 struct gsm322_plmn *plmn = &ms->plmn;
2718 struct gsm322_plmn_list *temp;
2720 printf("MCC |MNC |allowed|rx-lev\n");
2721 printf("-------+-------+-------+-------\n");
2722 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
2723 printf("%03d |%02d |%s |%d\n", temp->mcc, temp->mnc,
2724 (temp->cause) ? "no ":"yes", temp->rxlev_db);
2730 int gsm322_dump_cs_list(struct osmocom_ms *ms, uint8_t flags)
2732 struct gsm322_cellsel *cs = &ms->cellsel;
2735 printf("rx-lev |MCC |MNC |forb.LA|barred,0123456789abcdef|"
2737 "-------+-------+-------+-------+-----------------------+"
2738 "-------+-------\n");
2739 for (i = 0; i <= 1023; i++) {
2740 if (!(cs->list[i].flags & flags))
2742 printf("%4d |", cs->list[i].rxlev_db);
2743 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
2744 struct gsm48_sysinfo *s = cs->list[i].sysinfo;
2745 printf("%03d |%02d |", s->mcc, s->mnc);
2746 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
2750 if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
2754 for (j = 0; j < 16; j++) {
2755 if ((s->class_barr & (1 << j)))
2760 printf("|%4d |%4d\n", s->rxlev_acc_min_db,
2761 s->ms_txpwr_max_ccch);
2763 printf("n/a |n/a | | "
2770 int gsm322_dump_sim_plmn(struct osmocom_ms *ms)
2772 struct gsm_subscriber *subscr = &ms->subscr;
2773 struct gsm_sub_plmn_list *temp;
2775 printf("MCC |MNC\n");
2776 printf("-------+-------\n");
2777 llist_for_each_entry(temp, &subscr->plmn_list, entry)
2778 printf("%03d |%02d\n", temp->mcc, temp->mnc);
2783 int gsm322_dump_forbidden_plmn(struct osmocom_ms *ms)
2785 struct gsm_subscriber *subscr = &ms->subscr;
2786 struct gsm_sub_plmn_na *temp;
2788 printf("MCC |MNC |cause\n");
2789 printf("-------+-------+-------\n");
2790 llist_for_each_entry(temp, &subscr->plmn_na, entry)
2791 printf("%03d |%02d |#%d\n", temp->mcc, temp->mnc,
2797 int gsm322_dump_forbidden_la(struct osmocom_ms *ms)
2799 struct gsm322_plmn *plmn = &ms->plmn;
2800 struct gsm322_la_list *temp;
2802 printf("MCC |MNC |LAC |cause\n");
2803 printf("-------+-------+-------+-------\n");
2804 llist_for_each_entry(temp, &plmn->forbidden_la, entry)
2805 printf("%03d |%02d |0x%04x |#%d\n", temp->mcc, temp->mnc,
2806 temp->lac, temp->cause);
2815 int gsm322_init(struct osmocom_ms *ms)
2817 struct gsm322_plmn *plmn = &ms->plmn;
2818 struct gsm322_cellsel *cs = &ms->cellsel;
2820 char suffix[] = ".ba";
2821 char filename[sizeof(ms->name) + strlen(suffix) + 1];
2823 struct gsm322_ba_list *ba;
2826 LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
2827 LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
2829 memset(plmn, 0, sizeof(*plmn));
2830 memset(cs, 0, sizeof(*cs));
2834 /* set initial state */
2837 plmn->mode = PLMN_MODE_AUTO;
2840 INIT_LLIST_HEAD(&plmn->event_queue);
2841 INIT_LLIST_HEAD(&cs->event_queue);
2842 INIT_LLIST_HEAD(&plmn->sorted_plmn);
2843 INIT_LLIST_HEAD(&plmn->forbidden_la);
2844 INIT_LLIST_HEAD(&cs->ba_list);
2846 /* set supported frequencies in cell selection list */
2847 for (i = 0; i <= 1023; i++)
2848 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
2849 cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
2852 strcpy(filename, ms->name);
2853 strcat(filename, suffix);
2854 fp = osmocom_fopen(filename, "r");
2858 while(!osmocom_feof(fp)) {
2859 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2862 rc = osmocom_fread(buf, 4, 1, fp);
2867 ba->mcc = (buf[0] << 8) | buf[1];
2868 ba->mnc = (buf[2] << 8) | buf[3];
2869 rc = osmocom_fread(ba->freq, sizeof(ba->freq), 1, fp);
2874 llist_add_tail(&ba->entry, &cs->ba_list);
2875 LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%d "
2876 "mnc=%d %s, %s)\n", ba->mcc, ba->mnc,
2877 gsm_get_mcc(ba->mcc),
2878 gsm_get_mnc(ba->mcc, ba->mnc));
2882 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
2884 register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
2889 int gsm322_exit(struct osmocom_ms *ms)
2891 struct gsm322_plmn *plmn = &ms->plmn;
2892 struct gsm322_cellsel *cs = &ms->cellsel;
2893 struct llist_head *lh, *lh2;
2896 char suffix[] = ".ba";
2897 char filename[sizeof(ms->name) + strlen(suffix) + 1];
2898 struct gsm322_ba_list *ba;
2902 LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
2903 LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
2905 unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
2907 /* stop cell selection process (if any) */
2908 new_c_state(cs, GSM322_C0_NULL);
2912 stop_plmn_timer(plmn);
2915 for (i = 0; i <= 1023; i++) {
2916 if (cs->list[i].sysinfo) {
2917 talloc_free(cs->list[i].sysinfo);
2918 cs->list[i].sysinfo = NULL;
2920 cs->list[i].flags = 0;
2924 strcpy(filename, ms->name);
2925 strcat(filename, suffix);
2926 fp = osmocom_fopen(filename, "w");
2930 llist_for_each_entry(ba, &cs->ba_list, entry) {
2931 buf[0] = ba->mcc >> 8;
2932 buf[1] = ba->mcc & 0xff;
2933 buf[2] = ba->mnc >> 8;
2934 buf[3] = ba->mnc & 0xff;
2935 rc = osmocom_fwrite(buf, 4, 1, fp);
2936 rc = osmocom_fwrite(ba->freq, sizeof(ba->freq), 1, fp);
2937 LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%d "
2938 "mnc=%d %s, %s)\n", ba->mcc, ba->mnc,
2939 gsm_get_mcc(ba->mcc),
2940 gsm_get_mnc(ba->mcc, ba->mnc));
2944 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
2947 while ((msg = msgb_dequeue(&plmn->event_queue)))
2949 while ((msg = msgb_dequeue(&cs->event_queue)))
2951 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
2955 llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
2959 llist_for_each_safe(lh, lh2, &cs->ba_list) {