2 * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include <osmocore/msgb.h>
29 #include <osmocore/talloc.h>
30 #include <osmocore/utils.h>
31 #include <osmocore/gsm48.h>
32 #include <osmocore/signal.h>
34 #include <osmocom/bb/common/logging.h>
35 #include <osmocom/bb/common/l1ctl.h>
36 #include <osmocom/bb/common/l23_app.h>
37 #include <osmocom/bb/common/osmocom_data.h>
38 #include <osmocom/bb/common/networks.h>
39 #include <osmocom/bb/mobile/vty.h>
43 static void gsm322_cs_timeout(void *arg);
44 static void gsm322_cs_loss(void *arg);
45 static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed);
46 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg);
48 //#define CS_HEAVY_DEBUG
57 /* Cell selection process
59 * The process depends on states and events (finites state machine).
61 * During states of cell selection or cell re-selection, the search for a cell
62 * is performed in two steps:
64 * 1. Measurement of received level of all relevant frequencies (rx-lev)
66 * 2. Receive system information messages of all relevant frequencies
68 * During this process, the results are stored in a list of all frequencies.
69 * This list is checked whenever a cell is selected. It depends on the results
70 * if the cell is 'suitable' and 'allowable' to 'camp' on.
72 * This list is also used to generate a list of available networks.
76 * - cs->list[0..1023].xxx for each cell, where
77 * - flags and rxlev are used to store outcome of cell scanning process
78 * - sysinfo pointing to sysinfo memory, allocated temporarily
79 * - cs->selected and cs->sel_* states of the current / last selected cell.
82 * There is a special state: GSM322_PLMN_SEARCH
83 * It is used to search for all cells, to find the HPLMN. This is triggered
84 * by a timer. Also it is used before selecting PLMN from list.
88 /* PLMN selection process
90 * The PLMN (Public Land Mobile Network = Operator's Network) has two different
97 * The process depends on states and events (finites state machine).
101 /* File format of BA list:
106 * where frequency 0 is bit 0 of first byte
108 * If not end-of-file, the next BA list is stored.
113 * * subscr->plmn_list
115 * The "PLMN Selector list" stores prefered networks to select during PLMN
116 * search process. This list is also stored in the SIM.
120 * The "forbidden PLMNs" list stores all networks that rejected us. The stored
121 * network will not be used when searching PLMN automatically. This list is
122 * also stored din the SIM.
124 * * plmn->forbidden_la
126 * The "forbidden LAs for roaming" list stores all location areas where roaming
131 * This list stores measurements and cell informations during cell selection
132 * process. It can be used to speed up repeated cell selection.
136 * This list stores a map of frequencies used for a PLMN. If this lists exists
137 * for a PLMN, it helps to speedup cell scan process.
139 * * plmn->sorted_plmn
141 * This list is generated whenever a PLMN search is started and a list of PLMNs
142 * is required. It consists of home PLMN, PLMN Selector list, and PLMNs found
143 * during scan process.
150 static const struct value_string gsm322_event_names[] = {
151 { GSM322_EVENT_SWITCH_ON, "EVENT_SWITCH_ON" },
152 { GSM322_EVENT_SWITCH_OFF, "EVENT_SWITCH_OFF" },
153 { GSM322_EVENT_SIM_INSERT, "EVENT_SIM_INSERT" },
154 { GSM322_EVENT_SIM_REMOVE, "EVENT_SIM_REMOVE" },
155 { GSM322_EVENT_REG_FAILED, "EVENT_REG_FAILED" },
156 { GSM322_EVENT_ROAMING_NA, "EVENT_ROAMING_NA" },
157 { GSM322_EVENT_INVALID_SIM, "EVENT_INVALID_SIM" },
158 { GSM322_EVENT_REG_SUCCESS, "EVENT_REG_SUCCESS" },
159 { GSM322_EVENT_NEW_PLMN, "EVENT_NEW_PLMN" },
160 { GSM322_EVENT_ON_PLMN, "EVENT_ON_PLMN" },
161 { GSM322_EVENT_PLMN_SEARCH_START,"EVENT_PLMN_SEARCH_START" },
162 { GSM322_EVENT_PLMN_SEARCH_END, "EVENT_PLMN_SEARCH_END" },
163 { GSM322_EVENT_USER_RESEL, "EVENT_USER_RESEL" },
164 { GSM322_EVENT_PLMN_AVAIL, "EVENT_PLMN_AVAIL" },
165 { GSM322_EVENT_CHOOSE_PLMN, "EVENT_CHOOSE_PLMN" },
166 { GSM322_EVENT_SEL_MANUAL, "EVENT_SEL_MANUAL" },
167 { GSM322_EVENT_SEL_AUTO, "EVENT_SEL_AUTO" },
168 { GSM322_EVENT_CELL_FOUND, "EVENT_CELL_FOUND" },
169 { GSM322_EVENT_NO_CELL_FOUND, "EVENT_NO_CELL_FOUND" },
170 { GSM322_EVENT_LEAVE_IDLE, "EVENT_LEAVE_IDLE" },
171 { GSM322_EVENT_RET_IDLE, "EVENT_RET_IDLE" },
172 { GSM322_EVENT_CELL_RESEL, "EVENT_CELL_RESEL" },
173 { GSM322_EVENT_SYSINFO, "EVENT_SYSINFO" },
174 { GSM322_EVENT_HPLMN_SEARCH, "EVENT_HPLMN_SEARCH" },
178 const char *get_event_name(int value)
180 return get_value_string(gsm322_event_names, value);
184 /* allocate a 03.22 event message */
185 struct msgb *gsm322_msgb_alloc(int msg_type)
188 struct gsm322_msg *gm;
190 msg = msgb_alloc_headroom(sizeof(*gm), 0, "GSM 03.22 event");
194 gm = (struct gsm322_msg *)msgb_put(msg, sizeof(*gm));
195 gm->msg_type = msg_type;
200 /* queue PLMN selection message */
201 int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
203 struct gsm322_plmn *plmn = &ms->plmn;
205 msgb_enqueue(&plmn->event_queue, msg);
210 /* queue cell selection message */
211 int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
213 struct gsm322_cellsel *cs = &ms->cellsel;
215 msgb_enqueue(&cs->event_queue, msg);
224 char *gsm_print_rxlev(uint8_t rxlev)
226 static char string[5];
231 sprintf(string, "-%d", 110 - rxlev);
235 static int gsm322_sync_to_cell(struct gsm322_cellsel *cs)
237 struct osmocom_ms *ms = cs->ms;
238 struct gsm48_sysinfo *s = cs->si;
240 cs->ccch_state = GSM322_CCCH_ST_INIT;
242 if (s->ccch_conf == 1) {
243 LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%d rxlev=%s "
244 "(Sysinfo, ccch mode COMB)\n", cs->arfcn,
245 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
246 cs->ccch_mode = CCCH_MODE_COMBINED;
248 LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%d rxlev=%s "
249 "(Sysinfo, ccch mode NON-COMB)\n", cs->arfcn,
250 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
251 cs->ccch_mode = CCCH_MODE_NON_COMBINED;
254 LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%d rxlev=%s (No sysinfo "
255 "yet, ccch mode NONE)\n", cs->arfcn,
256 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
257 cs->ccch_mode = CCCH_MODE_NONE;
260 l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
261 return l1ctl_tx_fbsb_req(ms, cs->arfcn,
262 L1CTL_FBSB_F_FB01SB, 100, 0,
266 static void gsm322_unselect_cell(struct gsm322_cellsel *cs)
270 cs->si->si5 = 0; /* unset SI5* */
272 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
273 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
276 /* print to DCS logging */
277 static void print_dcs(void *priv, const char *fmt, ...)
283 vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
284 buffer[sizeof(buffer) - 1] = '\0';
288 // LOGP(DCS, LOGL_INFO, "%s", buffer);
289 printf("%s", buffer);
292 /* del forbidden LA */
293 int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
294 uint16_t mnc, uint16_t lac)
296 struct gsm322_plmn *plmn = &ms->plmn;
297 struct gsm322_la_list *la;
299 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
300 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
301 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
302 "LAs (mcc=%s, mnc=%s, lac=%04x)\n",
303 gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac);
304 llist_del(&la->entry);
313 /* add forbidden LA */
314 int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
315 uint16_t mnc, uint16_t lac, uint8_t cause)
317 struct gsm322_plmn *plmn = &ms->plmn;
318 struct gsm322_la_list *la;
320 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
321 "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc),
322 gsm_print_mnc(mnc), lac);
323 la = talloc_zero(l23_ctx, struct gsm322_la_list);
330 llist_add_tail(&la->entry, &plmn->forbidden_la);
335 /* search forbidden LA */
336 int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
339 struct gsm322_plmn *plmn = &ms->plmn;
340 struct gsm322_la_list *la;
342 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
343 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
350 /* search for PLMN in all BA lists */
351 static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
352 uint16_t mcc, uint16_t mnc)
354 struct gsm322_ba_list *ba, *ba_found = NULL;
356 /* search for BA list */
357 llist_for_each_entry(ba, &cs->ba_list, entry) {
368 /* search available PLMN */
369 int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
373 for (i = 0; i <= 1023; i++) {
374 if (cs->list[i].sysinfo
375 && cs->list[i].sysinfo->mcc == mcc
376 && cs->list[i].sysinfo->mnc == mnc)
383 /* search available HPLMN */
384 int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi)
388 for (i = 0; i <= 1023; i++) {
389 if (cs->list[i].sysinfo
390 && gsm_match_mnc(cs->list[i].sysinfo->mcc,
391 cs->list[i].sysinfo->mnc, imsi))
398 /* del forbidden LA */
403 /*plmn search timer event */
404 static void plmn_timer_timeout(void *arg)
406 struct gsm322_plmn *plmn = arg;
409 LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
411 /* indicate PLMN selection T timeout */
412 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
415 gsm322_plmn_sendmsg(plmn->ms, nmsg);
418 /* start plmn search timer */
419 static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
421 LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
423 plmn->timer.cb = plmn_timer_timeout;
424 plmn->timer.data = plmn;
425 bsc_schedule_timer(&plmn->timer, secs, 0);
428 /* stop plmn search timer */
429 static void stop_plmn_timer(struct gsm322_plmn *plmn)
431 if (bsc_timer_pending(&plmn->timer)) {
432 LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
433 bsc_del_timer(&plmn->timer);
437 /* start cell selection timer */
438 void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
440 #ifdef CS_HEAVY_DEBUG
441 LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec);
443 cs->timer.cb = gsm322_cs_timeout;
445 bsc_schedule_timer(&cs->timer, sec, micro);
448 /* start loss timer */
449 void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro)
452 cs->timer.cb = gsm322_cs_loss;
454 if (bsc_timer_pending(&cs->timer)) {
455 struct timeval current_time;
456 unsigned long long currentTime;
458 gettimeofday(¤t_time, NULL);
459 currentTime = current_time.tv_sec * 1000000LL
460 + current_time.tv_usec;
461 currentTime += sec * 1000000LL + micro;
462 cs->timer.timeout.tv_sec = currentTime / 1000000LL;
463 cs->timer.timeout.tv_usec = currentTime % 1000000LL;
468 #ifdef CS_HEAVY_DEBUG
469 LOGP(DCS, LOGL_INFO, "Starting loss CS timer with %d seconds.\n", sec);
471 bsc_schedule_timer(&cs->timer, sec, micro);
474 /* stop cell selection timer */
475 static void stop_cs_timer(struct gsm322_cellsel *cs)
477 if (bsc_timer_pending(&cs->timer)) {
478 #ifdef CS_HEAVY_DEBUG
479 LOGP(DCS, LOGL_INFO, "stopping pending CS timer.\n");
481 bsc_del_timer(&cs->timer);
489 const char *plmn_a_state_names[] = {
494 "A4 wait for PLMN to appear",
499 const char *plmn_m_state_names[] = {
508 const char *cs_state_names[] = {
510 "C1 normal cell selection",
511 "C2 stored cell selection",
512 "C3 camped normally",
513 "C4 normal cell re-selection",
515 "C6 any cell selection",
516 "C7 camped on any cell",
517 "C8 any cell re-selection",
518 "C9 choose any cell",
524 /* new automatic PLMN search state */
525 static void new_a_state(struct gsm322_plmn *plmn, int state)
527 if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) {
528 LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
532 stop_plmn_timer(plmn);
534 if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
537 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
538 plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
543 /* new manual PLMN search state */
544 static void new_m_state(struct gsm322_plmn *plmn, int state)
546 if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
547 LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
551 if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
554 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
555 plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
560 /* new Cell selection state */
561 static void new_c_state(struct gsm322_cellsel *cs, int state)
563 if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
566 LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n",
567 cs_state_names[cs->state], cs_state_names[state]);
569 /* stop cell selection timer, if running */
572 /* stop scanning of power measurement */
574 LOGP(DCS, LOGL_INFO, "changing state while power scanning\n");
575 l1ctl_tx_reset_req(cs->ms, L1CTL_RES_T_FULL);
586 /* 4.4.3 create sorted list of PLMN
588 * the source of entries are
591 * - entries found in the SIM's PLMN Selector list
592 * - scanned PLMNs above -85 dB (random order)
593 * - scanned PLMNs below or equal -85 (by received level)
597 * The list only includes networks found at last scan.
599 * The list always contains HPLMN if available, even if not used by PLMN
600 * search process at some conditions.
602 * The list contains all PLMNs even if not allowed, so entries have to be
603 * removed when selecting from the list. (In case we use manual cell selection,
604 * we need to provide non-allowed networks also.)
606 static int gsm322_sort_list(struct osmocom_ms *ms)
608 struct gsm322_plmn *plmn = &ms->plmn;
609 struct gsm322_cellsel *cs = &ms->cellsel;
610 struct gsm_subscriber *subscr = &ms->subscr;
611 struct gsm_sub_plmn_list *sim_entry;
612 struct gsm_sub_plmn_na *na_entry;
613 struct llist_head temp_list;
614 struct gsm322_plmn_list *temp, *found;
615 struct llist_head *lh, *lh2;
616 int i, entries, move;
620 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
625 /* Create a temporary list of all networks */
626 INIT_LLIST_HEAD(&temp_list);
627 for (i = 0; i <= 1023; i++) {
628 if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)
629 || !cs->list[i].sysinfo)
632 /* search if network has multiple cells */
634 llist_for_each_entry(temp, &temp_list, entry) {
635 if (temp->mcc == cs->list[i].sysinfo->mcc
636 && temp->mnc == cs->list[i].sysinfo->mnc) {
641 /* update or create */
643 if (cs->list[i].rxlev > found->rxlev)
644 found->rxlev = cs->list[i].rxlev;
646 temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
649 temp->mcc = cs->list[i].sysinfo->mcc;
650 temp->mnc = cs->list[i].sysinfo->mnc;
651 temp->rxlev = cs->list[i].rxlev;
652 llist_add_tail(&temp->entry, &temp_list);
656 /* move Home PLMN, if in list, else add it */
657 if (subscr->sim_valid) {
659 llist_for_each_entry(temp, &temp_list, entry) {
660 if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) {
667 llist_del(&found->entry);
668 llist_add_tail(&found->entry, &plmn->sorted_plmn);
672 /* move entries if in SIM's PLMN Selector list */
673 llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
675 llist_for_each_entry(temp, &temp_list, entry) {
676 if (temp->mcc == sim_entry->mcc
677 && temp->mnc == sim_entry->mnc) {
683 llist_del(&found->entry);
684 llist_add_tail(&found->entry, &plmn->sorted_plmn);
688 /* move PLMN above -85 dBm in random order */
690 llist_for_each_entry(temp, &temp_list, entry) {
691 if (rxlev2dbm(temp->rxlev) > -85)
695 move = random() % entries;
697 llist_for_each_entry(temp, &temp_list, entry) {
698 if (rxlev2dbm(temp->rxlev) > -85) {
700 llist_del(&temp->entry);
701 llist_add_tail(&temp->entry,
711 /* move ohter PLMN in decreasing order */
714 llist_for_each_entry(temp, &temp_list, entry) {
716 || temp->rxlev > search) {
717 search = temp->rxlev;
723 llist_del(&found->entry);
724 llist_add_tail(&found->entry, &plmn->sorted_plmn);
727 /* mark forbidden PLMNs, if in list of forbidden networks */
729 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
730 llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
731 if (temp->mcc == na_entry->mcc
732 && temp->mnc == na_entry->mnc) {
733 temp->cause = na_entry->cause;
737 LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. "
738 "(%02d: mcc %s mnc %s allowed %s rx-lev %s)\n",
739 i, gsm_print_mcc(temp->mcc),
740 gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes",
741 gsm_print_rxlev(temp->rxlev));
745 gsm322_dump_sorted_plmn(ms);
751 * handler for automatic search
754 /* go On PLMN state */
755 static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
757 struct gsm322_plmn *plmn = &ms->plmn;
758 struct gsm_subscriber *subscr = &ms->subscr;
760 new_a_state(plmn, GSM322_A2_ON_PLMN);
762 /* start timer, if on VPLMN of home country OR special case */
763 if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi)
764 && (subscr->always_search_hplmn
765 || gsm_match_mcc(plmn->mcc, subscr->imsi))
766 && subscr->sim_valid && subscr->t6m_hplmn)
767 start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
769 stop_plmn_timer(plmn);
774 /* indicate selected PLMN */
775 static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
777 struct gsm322_plmn *plmn = &ms->plmn;
779 vty_notify(ms, NULL);
780 vty_notify(ms, "Selected Network: %s, %s\n",
781 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
783 return gsm322_a_go_on_plmn(ms, msg);
786 /* no (more) PLMN in list */
787 static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
789 struct gsm322_plmn *plmn = &ms->plmn;
790 struct gsm322_cellsel *cs = &ms->cellsel;
794 /* any allowable PLMN available? */
795 plmn->mcc = plmn->mnc = 0;
796 found = gsm322_cs_select(ms, 0, 1);
798 /* if no PLMN in list */
800 LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable.\n");
802 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
805 /* we must forward this, otherwhise "Any cell selection"
806 * will not start automatically.
808 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
811 gsm322_cs_sendmsg(ms, nmsg);
813 LOGP(DPLMN, LOGL_INFO, "Trigger full PLMN search.\n");
815 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
818 gsm322_cs_sendmsg(ms, nmsg);
823 /* select first PLMN in list */
824 plmn->mcc = cs->list[found].sysinfo->mcc;
825 plmn->mnc = cs->list[found].sysinfo->mnc;
827 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n",
828 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
829 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
831 /* indicate New PLMN */
832 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
835 gsm322_cs_sendmsg(ms, nmsg);
838 return gsm322_a_indicate_selected(ms, msg);
841 /* select first PLMN in list */
842 static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
844 struct gsm322_plmn *plmn = &ms->plmn;
845 struct gsm_subscriber *subscr = &ms->subscr;
847 struct gsm322_plmn_list *plmn_entry;
848 struct gsm322_plmn_list *plmn_first = NULL;
852 gsm322_sort_list(ms);
854 /* select first entry */
856 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
857 /* if last selected PLMN was HPLMN, we skip that */
858 if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc,
860 && plmn_entry->mcc == plmn->mcc
861 && plmn_entry->mnc == plmn->mnc) {
862 LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was "
863 "previously selected.\n");
867 /* select first allowed network */
868 if (!plmn_entry->cause) {
869 plmn_first = plmn_entry;
872 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
873 "because it is not allowed (cause %d).\n", i,
874 gsm_print_mcc(plmn_entry->mcc),
875 gsm_print_mnc(plmn_entry->mnc),
881 /* if no PLMN in list */
883 LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
884 gsm322_a_no_more_plmn(ms, msg);
889 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
890 "mnc=%s %s, %s)\n", plmn->plmn_curr,
891 gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc),
892 gsm_get_mcc(plmn_first->mcc),
893 gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
895 /* set current network */
896 plmn->mcc = plmn_first->mcc;
897 plmn->mnc = plmn_first->mnc;
899 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
901 /* indicate New PLMN */
902 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
905 gsm322_cs_sendmsg(ms, nmsg);
910 /* select next PLMN in list */
911 static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
913 struct gsm322_plmn *plmn = &ms->plmn;
915 struct gsm322_plmn_list *plmn_entry;
916 struct gsm322_plmn_list *plmn_next = NULL;
919 /* select next entry from list */
921 ii = plmn->plmn_curr + 1;
922 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
923 /* skip previously selected networks */
928 /* select next allowed network */
929 if (!plmn_entry->cause) {
930 plmn_next = plmn_entry;
933 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
934 "because it is not allowed (cause %d).\n", i,
935 gsm_print_mcc(plmn_entry->mcc),
936 gsm_print_mnc(plmn_entry->mnc),
942 /* if no more PLMN in list */
944 LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
945 gsm322_a_no_more_plmn(ms, msg);
949 /* set next network */
950 plmn->mcc = plmn_next->mcc;
951 plmn->mnc = plmn_next->mnc;
953 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
954 "mnc=%s %s, %s)\n", plmn->plmn_curr,
955 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
956 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
958 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
960 /* indicate New PLMN */
961 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
964 gsm322_cs_sendmsg(ms, nmsg);
969 /* User re-selection event */
970 static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg)
972 struct gsm322_plmn *plmn = &ms->plmn;
973 struct gsm_subscriber *subscr = &ms->subscr;
974 struct gsm48_rrlayer *rr = &ms->rrlayer;
975 struct gsm322_plmn_list *plmn_entry;
976 struct gsm322_plmn_list *plmn_found = NULL;
978 if (!subscr->sim_valid) {
982 /* try again later, if not idle */
983 if (rr->state != GSM48_RR_ST_IDLE) {
984 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
989 /* search current PLMN in list */
990 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
991 if (plmn_entry->mcc == plmn->mcc
992 && plmn_entry->mnc == plmn->mnc)
993 plmn_found = plmn_entry;
997 /* abort if list is empty */
999 LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
1003 LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
1004 "and restarting PLMN search process.\n");
1006 /* move entry to end of list */
1007 llist_del(&plmn_found->entry);
1008 llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
1010 /* select first PLMN in list */
1011 return gsm322_a_sel_first_plmn(ms, msg);
1014 /* PLMN becomes available */
1015 static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1017 struct gsm322_plmn *plmn = &ms->plmn;
1018 struct gsm_subscriber *subscr = &ms->subscr;
1019 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1021 if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
1022 && subscr->plmn_mnc == gm->mnc) {
1024 plmn->mcc = gm->mcc;
1025 plmn->mnc = gm->mnc;
1026 LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n");
1027 return gsm322_a_go_on_plmn(ms, msg);
1029 /* select first PLMN in list */
1030 LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
1031 "search process.\n");
1032 return gsm322_a_sel_first_plmn(ms, msg);
1036 /* loss of radio coverage */
1037 static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
1039 struct gsm322_plmn *plmn = &ms->plmn;
1040 struct gsm322_cellsel *cs = &ms->cellsel;
1044 /* any PLMN available */
1045 found = gsm322_cs_select(ms, 0, 1);
1047 /* if PLMN in list */
1049 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s "
1050 "%s, %s)\n", gsm_print_mcc(
1051 cs->list[found].sysinfo->mcc),
1052 gsm_print_mnc(cs->list[found].sysinfo->mnc),
1053 gsm_get_mcc(cs->list[found].sysinfo->mcc),
1054 gsm_get_mnc(cs->list[found].sysinfo->mcc,
1055 cs->list[found].sysinfo->mnc));
1056 return gsm322_a_sel_first_plmn(ms, msg);
1059 LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
1061 plmn->mcc = plmn->mnc = 0;
1063 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
1065 /* Tell cell selection process to handle "no cell found". */
1066 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1069 gsm322_cs_sendmsg(ms, nmsg);
1074 /* MS is switched on OR SIM is inserted OR removed */
1075 static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1077 struct gsm_subscriber *subscr = &ms->subscr;
1078 struct gsm322_plmn *plmn = &ms->plmn;
1081 if (!subscr->sim_valid) {
1082 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1083 LOGP(DPLMN, LOGL_INFO, "SIM is removed\n");
1084 new_a_state(plmn, GSM322_A6_NO_SIM);
1089 /* if there is a registered PLMN */
1090 if (subscr->plmn_valid) {
1091 /* select the registered PLMN */
1092 plmn->mcc = subscr->plmn_mcc;
1093 plmn->mnc = subscr->plmn_mnc;
1095 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1096 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1097 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1098 gsm_get_mnc(plmn->mcc, plmn->mnc));
1099 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1100 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1101 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1102 gsm_get_mnc(plmn->mcc, plmn->mnc));
1104 new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
1106 /* indicate New PLMN */
1107 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1110 gsm322_cs_sendmsg(ms, nmsg);
1115 /* initiate search at cell selection */
1116 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1117 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1119 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1122 gsm322_cs_sendmsg(ms, nmsg);
1127 /* MS is switched off */
1128 static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1130 struct gsm322_plmn *plmn = &ms->plmn;
1132 new_a_state(plmn, GSM322_A0_NULL);
1137 static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1139 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1143 /* SIM is removed */
1144 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1148 /* indicate SIM remove to cell selection process */
1149 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1152 gsm322_cs_sendmsg(ms, nmsg);
1154 return gsm322_a_switch_on(ms, msg);
1157 /* location update response: "Roaming not allowed" */
1158 static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
1160 /* store in list of forbidden LAs is done in gsm48* */
1162 return gsm322_a_sel_first_plmn(ms, msg);
1165 /* On VPLMN of home country and timeout occurs */
1166 static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg)
1168 struct gsm48_rrlayer *rr = &ms->rrlayer;
1169 struct gsm322_plmn *plmn = &ms->plmn;
1170 struct gsm322_cellsel *cs = &ms->cellsel;
1173 /* try again later, if not idle and not camping */
1174 if (rr->state != GSM48_RR_ST_IDLE
1175 || cs->state != GSM322_C3_CAMPED_NORMALLY) {
1176 LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n");
1177 start_plmn_timer(plmn, 60);
1182 new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
1184 /* initiate search at cell selection */
1185 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1188 gsm322_cs_sendmsg(ms, nmsg);
1193 /* manual mode selected */
1194 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
1198 /* restart state machine */
1199 gsm322_a_switch_off(ms, msg);
1200 ms->settings.plmn_mode = PLMN_MODE_MANUAL;
1201 gsm322_m_switch_on(ms, msg);
1203 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1206 gsm48_mmevent_msg(ms, nmsg);
1212 * handler for manual search
1215 /* display PLMNs and to Not on PLMN */
1216 static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
1218 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1219 int msg_type = gm->msg_type;
1220 struct gsm322_plmn *plmn = &ms->plmn;
1221 struct gsm_sub_plmn_list *temp;
1224 gsm322_sort_list(ms);
1226 vty_notify(ms, NULL);
1228 case GSM322_EVENT_REG_FAILED:
1229 vty_notify(ms, "Failed to register to network %s, %s "
1231 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1232 gsm_get_mcc(plmn->mcc),
1233 gsm_get_mnc(plmn->mcc, plmn->mnc));
1235 case GSM322_EVENT_NO_CELL_FOUND:
1236 vty_notify(ms, "No cell found for network %s, %s "
1238 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1239 gsm_get_mcc(plmn->mcc),
1240 gsm_get_mnc(plmn->mcc, plmn->mnc));
1242 case GSM322_EVENT_ROAMING_NA:
1243 vty_notify(ms, "Roaming not allowed to network %s, %s "
1245 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1246 gsm_get_mcc(plmn->mcc),
1247 gsm_get_mnc(plmn->mcc, plmn->mnc));
1251 if (llist_empty(&plmn->sorted_plmn))
1252 vty_notify(ms, "Search network!\n");
1254 vty_notify(ms, "Search or select from network:\n");
1255 llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
1256 vty_notify(ms, " Network %s, %s (%s, %s)\n",
1257 gsm_print_mcc(temp->mcc),
1258 gsm_print_mnc(temp->mnc),
1259 gsm_get_mcc(temp->mcc),
1260 gsm_get_mnc(temp->mcc, temp->mnc));
1263 /* go Not on PLMN state */
1264 new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
1269 /* user starts reselection */
1270 static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg)
1272 struct gsm_subscriber *subscr = &ms->subscr;
1273 struct gsm48_rrlayer *rr = &ms->rrlayer;
1276 if (!subscr->sim_valid) {
1280 /* try again later, if not idle */
1281 if (rr->state != GSM48_RR_ST_IDLE) {
1282 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
1287 /* initiate search at cell selection */
1288 vty_notify(ms, NULL);
1289 vty_notify(ms, "Searching Network, please wait...\n");
1290 LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n");
1292 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1295 gsm322_cs_sendmsg(ms, nmsg);
1300 /* MS is switched on OR SIM is inserted OR removed */
1301 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1303 struct gsm_subscriber *subscr = &ms->subscr;
1304 struct gsm322_plmn *plmn = &ms->plmn;
1307 if (!subscr->sim_valid) {
1308 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1309 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1310 new_m_state(plmn, GSM322_M5_NO_SIM);
1315 /* if there is a registered PLMN */
1316 if (subscr->plmn_valid) {
1319 /* select the registered PLMN */
1320 plmn->mcc = subscr->plmn_mcc;
1321 plmn->mnc = subscr->plmn_mnc;
1323 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1324 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1325 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1326 gsm_get_mnc(plmn->mcc, plmn->mnc));
1327 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1328 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1329 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1330 gsm_get_mnc(plmn->mcc, plmn->mnc));
1332 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1334 /* indicate New PLMN */
1335 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1338 gsm322_cs_sendmsg(ms, nmsg);
1343 /* initiate search at cell selection */
1344 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1345 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1346 vty_notify(ms, NULL);
1347 vty_notify(ms, "Searching Network, please wait...\n");
1349 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1352 gsm322_cs_sendmsg(ms, nmsg);
1357 /* MS is switched off */
1358 static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1360 struct gsm322_plmn *plmn = &ms->plmn;
1362 stop_plmn_timer(plmn);
1364 new_m_state(plmn, GSM322_M0_NULL);
1369 static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1371 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1375 /* SIM is removed */
1376 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1378 struct gsm322_plmn *plmn = &ms->plmn;
1381 stop_plmn_timer(plmn);
1383 /* indicate SIM remove to cell selection process */
1384 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1387 gsm322_cs_sendmsg(ms, nmsg);
1389 return gsm322_m_switch_on(ms, msg);
1392 /* go to On PLMN state */
1393 static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1395 struct gsm322_plmn *plmn = &ms->plmn;
1396 struct gsm_subscriber *subscr = &ms->subscr;
1398 /* set last registered PLMN */
1399 subscr->plmn_valid = 1;
1400 subscr->plmn_mcc = plmn->mcc;
1401 subscr->plmn_mnc = plmn->mnc;
1406 new_m_state(plmn, GSM322_M2_ON_PLMN);
1411 /* indicate selected PLMN */
1412 static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
1414 struct gsm322_plmn *plmn = &ms->plmn;
1416 vty_notify(ms, NULL);
1417 vty_notify(ms, "Selected Network: %s, %s\n",
1418 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1420 return gsm322_m_go_on_plmn(ms, msg);
1423 /* previously selected PLMN becomes available again */
1424 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1426 struct gsm322_plmn *plmn = &ms->plmn;
1427 struct gsm322_cellsel *cs = &ms->cellsel;
1429 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1431 if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
1434 LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
1435 "selected, so start selection.\n");
1437 /* indicate New PLMN */
1438 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1441 gsm322_cs_sendmsg(ms, nmsg);
1447 /* the user has selected given PLMN */
1448 static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
1450 struct gsm322_plmn *plmn = &ms->plmn;
1451 struct gsm_subscriber *subscr = &ms->subscr;
1452 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1455 /* use user selection */
1456 plmn->mcc = gm->mcc;
1457 plmn->mnc = gm->mnc;
1459 vty_notify(ms, NULL);
1460 vty_notify(ms, "Selected Network: %s, %s\n",
1461 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1462 LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s "
1463 "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1464 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1466 /* if selected PLMN is in list of forbidden PLMNs */
1467 gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc);
1469 new_m_state(plmn, GSM322_M4_TRYING_PLMN);
1471 /* indicate New PLMN */
1472 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1475 gsm322_cs_sendmsg(ms, nmsg);
1480 /* auto mode selected */
1481 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
1485 /* restart state machine */
1486 gsm322_m_switch_off(ms, msg);
1487 ms->settings.plmn_mode = PLMN_MODE_AUTO;
1488 gsm322_a_switch_on(ms, msg);
1490 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1493 gsm48_mmevent_msg(ms, nmsg);
1498 /* if no cell is found in other states than in *_TRYING_* states */
1499 static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
1503 /* Tell cell selection process to handle "no cell found". */
1504 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1507 gsm322_cs_sendmsg(ms, nmsg);
1513 * cell scanning process
1516 /* select a suitable and allowable cell */
1517 static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed)
1519 struct gsm322_cellsel *cs = &ms->cellsel;
1520 struct gsm_subscriber *subscr = &ms->subscr;
1521 struct gsm48_sysinfo *s;
1522 int i, found = -1, power = 0;
1523 uint8_t flags, mask;
1526 /* set out access class depending on the cell selection type */
1528 acc_class = subscr->acc_class | 0x0400; /* add emergency */
1529 #ifdef CS_HEAVY_DEBUG
1530 LOGP(DCS, LOGL_INFO, "Select using access class with Emergency "
1534 acc_class = subscr->acc_class;
1535 #ifdef CS_HEAVY_DEBUG
1536 LOGP(DCS, LOGL_INFO, "Select using access class \n");
1540 /* flags to match */
1541 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1542 | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
1543 if (cs->state == GSM322_C2_STORED_CELL_SEL
1544 || cs->state == GSM322_C5_CHOOSE_CELL)
1545 mask |= GSM322_CS_FLAG_BA;
1546 flags = mask; /* all masked flags are requied */
1548 /* loop through all scanned frequencies and select cell */
1549 for (i = 0; i <= 1023; i++) {
1550 cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
1551 s = cs->list[i].sysinfo;
1553 /* channel has no informations for us */
1554 if (!s || (cs->list[i].flags & mask) != flags) {
1558 /* check C1 criteria not fullfilled */
1559 // TODO: C1 is also dependant on power class and max power
1560 if (rxlev2dbm(cs->list[i].rxlev) < s->rxlev_acc_min_db) {
1561 LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
1562 "not met. (rxlev %s < min %d)\n", i,
1563 gsm_print_rxlev(cs->list[i].rxlev),
1564 s->rxlev_acc_min_db);
1568 /* if cell is barred and we don't override */
1569 if (!subscr->acc_barr
1570 && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
1571 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
1576 /* if cell is in list of forbidden LAs */
1577 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
1578 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1579 "list of forbidden LAs. (mcc=%s mnc=%s "
1580 "lai=%04x)\n", i, gsm_print_mcc(s->mcc),
1581 gsm_print_mnc(s->mnc), s->lac);
1585 /* if cell is in list of forbidden PLMNs */
1586 if (plmn_allowed && gsm_subscr_is_forbidden_plmn(subscr,
1588 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1589 "list of forbidden PLMNs. (mcc=%s mnc=%s)\n", i,
1590 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc));
1594 /* if we have no access to the cell and we don't override */
1595 if (!subscr->acc_barr
1596 && !(acc_class & (s->class_barr ^ 0xffff))) {
1597 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
1598 "barred for out access. (access=%04x "
1599 "barred=%04x)\n", i, acc_class, s->class_barr);
1603 /* store temporary available and allowable flag */
1604 cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
1606 /* if we search a specific PLMN, but it does not match */
1607 if (!any && cs->mcc && (cs->mcc != s->mcc
1608 || cs->mnc != s->mnc)) {
1609 LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
1610 "does not match target PLMN. (mcc=%s "
1611 "mnc=%s)\n", i, gsm_print_mcc(s->mcc),
1612 gsm_print_mnc(s->mnc));
1616 LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%s "
1617 "mcc=%s mnc=%s lac=%04x %s, %s)\n", i,
1618 gsm_print_rxlev(cs->list[i].rxlev),
1619 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac,
1620 gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc));
1622 /* find highest power cell */
1623 if (found < 0 || cs->list[i].rxlev > power) {
1624 power = cs->list[i].rxlev;
1630 LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
1635 /* tune to first/next unscanned frequency and search for PLMN */
1636 static int gsm322_cs_scan(struct osmocom_ms *ms)
1638 struct gsm322_cellsel *cs = &ms->cellsel;
1640 uint8_t mask, flags;
1641 uint32_t weight = 0, test = cs->scan_state;
1643 /* search for strongest unscanned cell */
1644 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1645 | GSM322_CS_FLAG_SIGNAL;
1646 if (cs->state == GSM322_C2_STORED_CELL_SEL
1647 || cs->state == GSM322_C5_CHOOSE_CELL)
1648 mask |= GSM322_CS_FLAG_BA;
1649 flags = mask; /* all masked flags are requied */
1650 for (i = 0; i <= 1023; i++) {
1651 /* skip if band has enough frequencies scanned (3.2.1) */
1652 for (j = 0; gsm_sup_smax[j].max; j++) {
1653 if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
1654 if (gsm_sup_smax[j].start >= i
1655 && gsm_sup_smax[j].end <= i)
1658 if (gsm_sup_smax[j].end <= i
1659 || gsm_sup_smax[j].start >= i)
1663 if (gsm_sup_smax[j].max) {
1664 if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
1667 /* search for unscanned frequency */
1668 if ((cs->list[i].flags & mask) == flags) {
1669 /* weight depends on the power level
1670 * if it is the same, it depends on arfcn
1672 test = cs->list[i].rxlev + 1;
1673 test = (test << 16) | i;
1674 if (test >= cs->scan_state)
1680 cs->scan_state = weight;
1683 gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs,
1686 /* special case for PLMN search */
1687 if (cs->state == GSM322_PLMN_SEARCH && !weight) {
1690 /* create AA flag */
1691 cs->mcc = cs->mnc = 0;
1692 gsm322_cs_select(ms, 0, 0);
1694 new_c_state(cs, GSM322_C0_NULL);
1696 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END);
1697 LOGP(DCS, LOGL_INFO, "PLMN search finished.\n");
1700 gsm322_plmn_sendmsg(ms, nmsg);
1705 /* special case for HPLMN search */
1706 if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
1709 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1710 LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n");
1713 gsm322_plmn_sendmsg(ms, nmsg);
1715 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
1717 cs->arfcn = cs->sel_arfcn;
1718 LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev "
1719 "%s).\n", cs->arfcn,
1720 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
1722 gsm322_sync_to_cell(cs);
1727 /* if all frequencies have been searched */
1733 LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
1735 /* just see, if we search for any cell */
1736 if (cs->state == GSM322_C6_ANY_CELL_SEL
1737 || cs->state == GSM322_C8_ANY_CELL_RESEL
1738 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1741 found = gsm322_cs_select(ms, any, 0);
1745 struct gsm322_plmn *plmn = &ms->plmn;
1747 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1750 cs->si = cs->list[cs->arfcn].sysinfo;
1752 gsm322_sync_to_cell(cs);
1754 /* selected PLMN (manual) or any PLMN (auto) */
1755 switch (ms->settings.plmn_mode) {
1756 case PLMN_MODE_AUTO:
1757 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1758 /* PLMN becomes available */
1759 nmsg = gsm322_msgb_alloc(
1760 GSM322_EVENT_PLMN_AVAIL);
1763 gsm322_plmn_sendmsg(ms, nmsg);
1766 case PLMN_MODE_MANUAL:
1767 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1768 && gsm322_is_plmn_avail(cs, plmn->mcc,
1770 /* PLMN becomes available */
1771 nmsg = gsm322_msgb_alloc(
1772 GSM322_EVENT_PLMN_AVAIL);
1775 gsm322_plmn_sendmsg(ms, nmsg);
1780 /* set selected cell */
1782 cs->sel_arfcn = cs->arfcn;
1783 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1784 cs->sel_mcc = cs->si->mcc;
1785 cs->sel_mnc = cs->si->mnc;
1786 cs->sel_lac = cs->si->lac;
1787 cs->sel_id = cs->si->cell_id;
1789 /* tell CS process about available cell */
1790 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1791 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1794 /* unset selected cell */
1795 gsm322_unselect_cell(cs);
1797 /* tell CS process about no cell available */
1798 LOGP(DCS, LOGL_INFO, "No cell available.\n");
1799 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1803 gsm322_c_event(ms, nmsg);
1809 /* NOTE: We might already have system information from previous
1810 * scan. But we need recent informations, so we scan again!
1813 /* Tune to frequency for a while, to receive broadcasts. */
1814 cs->arfcn = weight & 1023;
1815 #ifdef CS_HEAVY_DEBUG
1816 LOGP(DCS, LOGL_INFO, "Scanning frequency %d (rxlev %s).\n", cs->arfcn,
1817 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
1820 gsm322_sync_to_cell(cs);
1822 /* Allocate/clean system information. */
1823 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1824 if (cs->list[cs->arfcn].sysinfo)
1825 memset(cs->list[cs->arfcn].sysinfo, 0,
1826 sizeof(struct gsm48_sysinfo));
1828 cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
1829 struct gsm48_sysinfo);
1830 if (!cs->list[cs->arfcn].sysinfo)
1832 cs->si = cs->list[cs->arfcn].sysinfo;
1834 /* increase scan counter for each maximum scan range */
1835 if (gsm_sup_smax[j].max) {
1836 #ifdef CS_HEAVY_DEBUG
1837 LOGP(DCS, LOGL_INFO, "%d frequencies left in band %d..%d\n",
1838 gsm_sup_smax[j].max - gsm_sup_smax[j].temp,
1839 gsm_sup_smax[j].start, gsm_sup_smax[j].end);
1841 gsm_sup_smax[j].temp++;
1847 /* check if cell is now suitable and allowable */
1848 static int gsm322_cs_store(struct osmocom_ms *ms)
1850 struct gsm322_cellsel *cs = &ms->cellsel;
1851 struct gsm48_sysinfo *s = cs->si;
1852 struct gsm322_plmn *plmn = &ms->plmn;
1856 if (cs->state != GSM322_C2_STORED_CELL_SEL
1857 && cs->state != GSM322_C1_NORMAL_CELL_SEL
1858 && cs->state != GSM322_C6_ANY_CELL_SEL
1859 && cs->state != GSM322_C4_NORMAL_CELL_RESEL
1860 && cs->state != GSM322_C8_ANY_CELL_RESEL
1861 && cs->state != GSM322_C5_CHOOSE_CELL
1862 && cs->state != GSM322_C9_CHOOSE_ANY_CELL
1863 && cs->state != GSM322_PLMN_SEARCH
1864 && cs->state != GSM322_HPLMN_SEARCH) {
1865 LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
1866 "(re-)selection, please fix!\n");
1871 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO;
1873 && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp &&
1874 cs->list[cs->arfcn].sysinfo->sp_cbq))
1875 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED;
1877 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED;
1880 cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db;
1881 cs->list[cs->arfcn].class_barr = s->class_barr;
1882 cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch;
1885 /* store selected network */
1888 cs->list[cs->arfcn].mcc = s->mcc;
1889 cs->list[cs->arfcn].mnc = s->mnc;
1890 cs->list[cs->arfcn].lac = s->lac;
1893 if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
1894 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD;
1896 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD;
1899 #ifdef CS_HEAVY_DEBUG
1900 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev %s "
1901 "mcc %s mnc %s lac %04x)\n", cs->arfcn,
1902 gsm_print_rxlev(cs->list[cs->arfcn].rxlev),
1903 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac);
1906 /* special case for PLMN search */
1907 if (cs->state == GSM322_PLMN_SEARCH)
1908 /* tune to next cell */
1909 return gsm322_cs_scan(ms);
1911 /* special case for HPLMN search */
1912 if (cs->state == GSM322_HPLMN_SEARCH) {
1913 struct gsm_subscriber *subscr = &ms->subscr;
1916 if (!gsm322_is_hplmn_avail(cs, subscr->imsi))
1917 /* tune to next cell */
1918 return gsm322_cs_scan(ms);
1920 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1921 LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n");
1924 gsm322_plmn_sendmsg(ms, nmsg);
1929 /* just see, if we search for any cell */
1930 if (cs->state == GSM322_C6_ANY_CELL_SEL
1931 || cs->state == GSM322_C8_ANY_CELL_RESEL
1932 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1935 found = gsm322_cs_select(ms, any, 0);
1939 LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n");
1940 /* tune to next cell */
1941 return gsm322_cs_scan(ms);
1944 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1947 cs->si = cs->list[cs->arfcn].sysinfo;
1949 gsm322_sync_to_cell(cs);
1951 /* selected PLMN (manual) or any PLMN (auto) */
1952 switch (ms->settings.plmn_mode) {
1953 case PLMN_MODE_AUTO:
1954 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1955 /* PLMN becomes available */
1956 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1959 gsm322_plmn_sendmsg(ms, nmsg);
1962 case PLMN_MODE_MANUAL:
1963 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1964 && gsm322_is_plmn_avail(cs, plmn->mcc,
1966 /* PLMN becomes available */
1967 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1970 gsm322_plmn_sendmsg(ms, nmsg);
1975 /* set selected cell */
1977 cs->sel_arfcn = cs->arfcn;
1978 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1979 cs->sel_mcc = cs->si->mcc;
1980 cs->sel_mnc = cs->si->mnc;
1981 cs->sel_lac = cs->si->lac;
1982 cs->sel_id = cs->si->cell_id;
1984 /* tell CS process about available cell */
1985 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1986 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1989 gsm322_c_event(ms, nmsg);
1995 /* process system information when returing to idle mode */
1996 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
1998 struct gsm322_cellsel *cs = &ms->cellsel;
1999 struct gsm48_sysinfo *s = cs->si;
2000 struct gsm322_ba_list *ba = NULL;
2004 /* collect system information received during dedicated mode */
2006 && (!s->nb_ext_ind_si5
2007 || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
2008 || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
2009 && s->nb_ext_ind_si5bis))) {
2010 /* find or create ba list */
2011 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
2013 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2018 llist_add_tail(&ba->entry, &cs->ba_list);
2020 /* update (add) ba list */
2021 memset(freq, 0, sizeof(freq));
2022 for (i = 0; i <= 1023; i++) {
2023 if ((s->freq[i].mask & (FREQ_TYPE_SERV
2024 | FREQ_TYPE_NCELL | FREQ_TYPE_REP)))
2025 freq[i >> 3] |= (1 << (i & 7));
2027 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
2028 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
2029 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2030 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2031 gsm_get_mnc(ba->mcc, ba->mnc));
2032 memcpy(ba->freq, freq, sizeof(freq));
2039 /* store BA whenever a system informations changes */
2040 static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
2041 struct gsm48_sysinfo *s)
2043 struct gsm322_ba_list *ba;
2047 /* find or create ba list */
2048 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
2050 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2055 llist_add_tail(&ba->entry, &cs->ba_list);
2057 /* update ba list */
2058 memset(freq, 0, sizeof(freq));
2059 freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
2060 for (i = 0; i <= 1023; i++) {
2061 if ((s->freq[i].mask &
2062 (FREQ_TYPE_SERV | FREQ_TYPE_NCELL | FREQ_TYPE_REP)))
2063 freq[i >> 3] |= (1 << (i & 7));
2065 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
2066 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
2067 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2068 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2069 gsm_get_mnc(ba->mcc, ba->mnc));
2070 memcpy(ba->freq, freq, sizeof(freq));
2076 /* process system information during camping on a cell */
2077 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2079 // struct gsm48_rrlayer *rr = &ms->rrlayer;
2080 struct gsm322_cellsel *cs = &ms->cellsel;
2081 struct gsm48_sysinfo *s = cs->si;
2082 struct gsm_subscriber *subscr = &ms->subscr;
2083 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2087 if (rr->state != GSM48_RR_ST_IDLE) {
2088 LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n");
2093 /* Store BA if we have full system info about cells and neigbor cells.
2094 * Depending on the extended bit in the channel description,
2095 * we require more or less system informations about neighbor cells
2099 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2100 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2101 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2102 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2105 && (!s->nb_ext_ind_si2
2106 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2107 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2108 && s->nb_ext_ind_si2bis)))
2109 gsm322_store_ba_list(cs, s);
2111 /* update sel_si, if all relevant system informations received */
2112 if (s->si1 && s->si2 && s->si3
2113 && (!s->nb_ext_ind_si2
2114 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2115 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2116 && s->nb_ext_ind_si2bis))) {
2118 LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is "
2120 memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
2121 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2125 /* check for barred cell */
2126 if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
2127 /* check if cell becomes barred */
2128 if (!subscr->acc_barr && s->cell_barr
2129 && !(cs->list[cs->arfcn].sysinfo
2130 && cs->list[cs->arfcn].sysinfo->sp
2131 && cs->list[cs->arfcn].sysinfo->sp_cbq)) {
2132 LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
2134 /* mark cell as unscanned */
2135 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2136 if (cs->list[cs->arfcn].sysinfo) {
2137 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2139 talloc_free(cs->list[cs->arfcn].sysinfo);
2140 cs->list[cs->arfcn].sysinfo = NULL;
2141 gsm322_unselect_cell(cs);
2143 /* trigger reselection without queueing,
2144 * because other sysinfo message may be queued
2147 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2150 gsm322_c_event(ms, nmsg);
2155 /* check if cell access becomes barred */
2156 if (!((subscr->acc_class & 0xfbff)
2157 & (s->class_barr ^ 0xffff))) {
2158 LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
2163 /* check if MCC, MNC, LAC, cell ID changes */
2164 if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc
2165 || cs->sel_lac != s->lac) {
2166 LOGP(DCS, LOGL_NOTICE, "Cell changes location area. "
2167 "This is not good!\n");
2170 if (cs->sel_id != s->cell_id) {
2171 LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. "
2172 "This is not good!\n");
2179 /* process system information during channel scanning */
2180 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2182 struct gsm322_cellsel *cs = &ms->cellsel;
2183 struct gsm48_sysinfo *s = cs->si;
2184 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2186 /* no sysinfo if we are not done with power scan */
2187 if (cs->powerscan) {
2188 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
2192 /* Store BA if we have full system info about cells and neigbor cells.
2193 * Depending on the extended bit in the channel description,
2194 * we require more or less system informations about neighbor cells
2198 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2199 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2200 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2201 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2204 && (!s->nb_ext_ind_si2
2205 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2206 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2207 && s->nb_ext_ind_si2bis)))
2208 gsm322_store_ba_list(cs, s);
2210 /* all relevant system informations received */
2211 if (s->si1 && s->si2 && s->si3
2212 && (!s->nb_ext_ind_si2
2213 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2214 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2215 && s->nb_ext_ind_si2bis))) {
2216 #ifdef CS_HEAVY_DEBUG
2217 LOGP(DCS, LOGL_INFO, "Received relevant sysinfo.\n");
2222 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2224 /* store sysinfo and continue scan */
2225 return gsm322_cs_store(ms);
2228 /* wait for more sysinfo or timeout */
2232 static void gsm322_cs_timeout(void *arg)
2234 struct gsm322_cellsel *cs = arg;
2235 struct osmocom_ms *ms = cs->ms;
2237 /* if we have no lock, we retry */
2238 if (cs->ccch_state != GSM322_CCCH_ST_SYNC)
2239 LOGP(DCS, LOGL_INFO, "Cell selection failed, sync timeout.\n");
2241 LOGP(DCS, LOGL_INFO, "Cell selection failed, read timeout.\n");
2243 /* remove system information */
2244 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2245 if (cs->list[cs->arfcn].sysinfo) {
2246 #ifdef CS_HEAVY_DEBUG
2247 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", cs->arfcn);
2249 talloc_free(cs->list[cs->arfcn].sysinfo);
2250 cs->list[cs->arfcn].sysinfo = NULL;
2251 gsm322_unselect_cell(cs);
2254 /* tune to next cell */
2261 * power scan process
2264 /* search for block of unscanned frequencies and start scanning */
2265 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
2267 struct gsm322_cellsel *cs = &ms->cellsel;
2268 struct gsm_settings *set = &ms->settings;
2270 uint8_t mask, flags;
2274 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
2275 flags = GSM322_CS_FLAG_SUPPORT;
2277 /* in case of sticking to a cell, we only select it */
2279 #ifdef CS_HEAVY_DEBUG
2280 LOGP(DCS, LOGL_FATAL, "Scanning power for sticked cell.\n");
2282 i = set->stick_arfcn;
2283 if ((cs->list[i].flags & mask) == flags)
2286 /* search for first frequency to scan */
2287 if (cs->state == GSM322_C2_STORED_CELL_SEL
2288 || cs->state == GSM322_C5_CHOOSE_CELL) {
2289 #ifdef CS_HEAVY_DEBUG
2290 LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA "
2293 mask |= GSM322_CS_FLAG_BA;
2294 flags |= GSM322_CS_FLAG_BA;
2296 #ifdef CS_HEAVY_DEBUG
2298 LOGP(DCS, LOGL_FATAL, "Scanning power for all "
2301 for (i = 0; i <= 1023; i++) {
2302 if ((cs->list[i].flags & mask) == flags) {
2309 /* if there is no more frequency, we can tune to that cell */
2313 /* stop power level scanning */
2316 /* check if not signal is found */
2317 for (i = 0; i <= 1023; i++) {
2318 if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2324 LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
2325 /* on normal cell selection, start over */
2326 if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
2327 for (i = 0; i <= 1023; i++) {
2328 /* clear flag that this was scanned */
2329 cs->list[i].flags &=
2330 ~(GSM322_CS_FLAG_POWER
2331 | GSM322_CS_FLAG_SIGNAL
2332 | GSM322_CS_FLAG_SYSINFO);
2333 if (cs->list[i].sysinfo) {
2334 LOGP(DCS, LOGL_INFO, "free "
2335 "sysinfo arfcn=%d\n",
2338 cs->list[i].sysinfo);
2339 cs->list[i].sysinfo = NULL;
2342 /* no cell selected */
2343 gsm322_unselect_cell(cs);
2346 /* on other cell selection, indicate "no cell found" */
2347 /* NOTE: PLMN search process handles it.
2348 * If not handled there, CS process gets indicated.
2349 * If we would continue to process CS, then we might get
2350 * our list of scanned cells disturbed.
2352 if (cs->state == GSM322_PLMN_SEARCH)
2353 nmsg = gsm322_msgb_alloc(
2354 GSM322_EVENT_PLMN_SEARCH_END);
2356 nmsg = gsm322_msgb_alloc(
2357 GSM322_EVENT_NO_CELL_FOUND);
2360 gsm322_plmn_sendmsg(ms, nmsg);
2362 /* if HPLMN search, select last frequency */
2363 if (cs->state == GSM322_HPLMN_SEARCH) {
2364 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2366 cs->arfcn = cs->sel_arfcn;
2367 LOGP(DCS, LOGL_INFO, "Tuning back to frequency "
2368 "%d (rxlev %s).\n", cs->arfcn,
2370 cs->list[cs->arfcn].rxlev));
2372 gsm322_sync_to_cell(cs);
2375 new_c_state(cs, GSM322_C0_NULL);
2379 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
2380 cs->scan_state = 0xffffffff; /* higher than high */
2381 /* clear counter of scanned frequencies of each range */
2382 for (i = 0; gsm_sup_smax[i].max; i++)
2383 gsm_sup_smax[i].temp = 0;
2384 return gsm322_cs_scan(ms);
2387 /* search last frequency to scan (en block) */
2390 for (i = s + 1; i <= 1023; i++) {
2391 if ((cs->list[i].flags & mask) == flags)
2398 #ifdef CS_HEAVY_DEBUG
2399 LOGP(DCS, LOGL_INFO, "Scanning frequencies. (%d..%d)\n", s, e);
2402 /* start scan on radio interface */
2403 if (!cs->powerscan) {
2404 l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
2407 //#warning TESTING!!!!
2409 return l1ctl_tx_pm_req_range(ms, s, e);
2412 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
2413 void *handler_data, void *signal_data)
2415 struct osmocom_ms *ms;
2416 struct gsm322_cellsel *cs;
2417 struct osmobb_meas_res *mr;
2418 struct osmobb_fbsb_res *fr;
2422 if (subsys != SS_L1CTL)
2426 case S_L1CTL_PM_RES:
2432 i = mr->band_arfcn & 1023;
2434 cs->list[i].rxlev = rxlev;
2435 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2436 /* if minimum level is reached or if we stick to a cell */
2437 if (rxlev2dbm(rxlev) >= ms->support.min_rxlev_db
2438 || ms->settings.stick) {
2439 cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2440 LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2441 "rxlev %s (%d))\n", i,
2442 gsm_print_rxlev(rxlev), rxlev);
2445 case S_L1CTL_PM_DONE:
2446 #ifdef CS_HEAVY_DEBUG
2447 LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
2453 gsm322_cs_powerscan(ms);
2455 case S_L1CTL_FBSB_RESP:
2459 if (cs->ccch_state == GSM322_CCCH_ST_INIT) {
2460 LOGP(DCS, LOGL_INFO, "Channel synched. (ARFCN=%d, "
2461 "snr=%u, BSIC=%u)\n", cs->arfcn, fr->snr,
2463 cs->ccch_state = GSM322_CCCH_ST_SYNC;
2465 cs->si->bsic = fr->bsic;
2469 /* in dedicated mode */
2470 if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
2471 return gsm48_rr_tx_rand_acc(ms, NULL);
2474 /* set timer for reading BCCH */
2475 if (cs->state == GSM322_C2_STORED_CELL_SEL
2476 || cs->state == GSM322_C1_NORMAL_CELL_SEL
2477 || cs->state == GSM322_C6_ANY_CELL_SEL
2478 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2479 || cs->state == GSM322_C8_ANY_CELL_RESEL
2480 || cs->state == GSM322_C5_CHOOSE_CELL
2481 || cs->state == GSM322_C9_CHOOSE_ANY_CELL
2482 || cs->state == GSM322_PLMN_SEARCH
2483 || cs->state == GSM322_HPLMN_SEARCH)
2484 start_cs_timer(cs, ms->support.scan_to, 0);
2485 // TODO: timer depends on BCCH config
2488 case S_L1CTL_FBSB_ERR:
2493 gsm322_sync_to_cell(cs);
2495 LOGP(DCS, LOGL_INFO, "Channel sync error, try again\n");
2499 LOGP(DCS, LOGL_INFO, "Channel sync error.\n");
2507 gsm322_cs_timeout(cs);
2511 if (ms->mmlayer.power_off_idle) {
2521 static void gsm322_cs_loss(void *arg)
2523 struct gsm322_cellsel *cs = arg;
2524 struct osmocom_ms *ms = cs->ms;
2525 struct gsm48_rrlayer *rr = &ms->rrlayer;
2527 LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n");
2528 if (cs->state == GSM322_C3_CAMPED_NORMALLY
2529 || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
2530 if (rr->state == GSM48_RR_ST_IDLE) {
2533 LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n");
2535 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2538 gsm322_c_event(ms, nmsg);
2541 LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n");
2543 /* be shure that nothing else is done after here
2544 * because the function call above may cause
2545 * to return from idle state and trigger cell re-sel.
2554 * handler for cell selection process
2557 /* start PLMN search */
2558 static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg)
2560 struct gsm322_cellsel *cs = &ms->cellsel;
2563 new_c_state(cs, GSM322_PLMN_SEARCH);
2565 /* mark all frequencies except our own BA to be scanned */
2566 for (i = 0; i <= 1023; i++) {
2567 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2568 | GSM322_CS_FLAG_SIGNAL
2569 | GSM322_CS_FLAG_SYSINFO);
2570 if (cs->list[i].sysinfo) {
2571 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2572 talloc_free(cs->list[i].sysinfo);
2573 cs->list[i].sysinfo = NULL;
2574 gsm322_unselect_cell(cs);
2578 /* unset selected cell */
2579 gsm322_unselect_cell(cs);
2581 /* start power scan */
2582 return gsm322_cs_powerscan(ms);
2585 /* start HPLMN search */
2586 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2588 struct gsm322_cellsel *cs = &ms->cellsel;
2591 new_c_state(cs, GSM322_HPLMN_SEARCH);
2593 /* mark all frequencies except our own BA to be scanned */
2594 for (i = 0; i <= 1023; i++) {
2595 if (i != cs->sel_arfcn
2596 && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2597 && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2598 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2599 | GSM322_CS_FLAG_SIGNAL
2600 | GSM322_CS_FLAG_SYSINFO);
2601 if (cs->list[i].sysinfo) {
2602 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2604 talloc_free(cs->list[i].sysinfo);
2605 cs->list[i].sysinfo = NULL;
2610 /* no cell selected */
2611 gsm322_unselect_cell(cs);
2613 /* start power scan */
2614 return gsm322_cs_powerscan(ms);
2617 /* start stored cell selection */
2618 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2620 struct gsm322_cellsel *cs = &ms->cellsel;
2623 new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2625 /* flag all frequencies that are in current band allocation */
2626 for (i = 0; i <= 1023; i++) {
2627 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2628 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2630 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2633 /* unset selected cell */
2634 gsm322_unselect_cell(cs);
2636 /* start power scan */
2637 return gsm322_cs_powerscan(ms);
2640 /* start noraml cell selection */
2641 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2643 struct gsm322_cellsel *cs = &ms->cellsel;
2646 /* except for stored cell selection state, we weed to rescan ?? */
2647 if (cs->state != GSM322_C2_STORED_CELL_SEL) {
2648 for (i = 0; i <= 1023; i++) {
2649 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2650 | GSM322_CS_FLAG_SIGNAL
2651 | GSM322_CS_FLAG_SYSINFO);
2652 if (cs->list[i].sysinfo) {
2653 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2655 talloc_free(cs->list[i].sysinfo);
2656 cs->list[i].sysinfo = NULL;
2661 new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2663 /* unset selected cell */
2664 gsm322_unselect_cell(cs);
2666 /* start power scan */
2667 return gsm322_cs_powerscan(ms);
2670 /* start any cell selection */
2671 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2673 struct gsm322_cellsel *cs = &ms->cellsel;
2675 /* in case we already tried any cell (re-)selection, power scan again */
2676 if (cs->state == GSM322_C0_NULL
2677 || cs->state == GSM322_C6_ANY_CELL_SEL
2678 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2681 for (i = 0; i <= 1023; i++) {
2682 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2683 | GSM322_CS_FLAG_SIGNAL
2684 | GSM322_CS_FLAG_SYSINFO);
2685 if (cs->list[i].sysinfo) {
2686 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2688 talloc_free(cs->list[i].sysinfo);
2689 cs->list[i].sysinfo = NULL;
2693 /* after re-selection, indicate no cell found */
2694 if (cs->state == GSM322_C6_ANY_CELL_SEL
2695 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2698 /* tell that we have no cell found */
2699 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2702 gsm48_mmevent_msg(ms, nmsg);
2705 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2708 cs->mcc = cs->mnc = 0;
2710 /* unset selected cell */
2711 gsm322_unselect_cell(cs);
2713 /* start power scan */
2714 return gsm322_cs_powerscan(ms);
2717 /* start noraml cell re-selection */
2718 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2720 struct gsm322_cellsel *cs = &ms->cellsel;
2722 new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2724 /* NOTE: We keep our scan info we have so far.
2725 * This may cause a skip in power scan. */
2727 /* start power scan */
2728 return gsm322_cs_powerscan(ms);
2731 /* start any cell re-selection */
2732 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2734 struct gsm322_cellsel *cs = &ms->cellsel;
2736 new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2738 /* NOTE: We keep our scan info we have so far.
2739 * This may cause a skip in power scan. */
2741 /* start power scan */
2742 return gsm322_cs_powerscan(ms);
2745 /* a suitable cell was found, so we camp normally */
2746 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2748 struct gsm322_cellsel *cs = &ms->cellsel;
2751 LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s "
2752 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2753 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2754 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2756 /* tell that we have selected a (new) cell */
2757 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2760 gsm48_mmevent_msg(ms, nmsg);
2762 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2767 /* a not suitable cell was found, so we camp on any cell */
2768 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2770 struct gsm322_cellsel *cs = &ms->cellsel;
2773 LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s "
2774 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2775 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2776 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2779 /* tell that we have selected a (new) cell */
2780 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2783 gsm48_mmevent_msg(ms, nmsg);
2785 new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2790 /* create temporary ba range with given frequency ranges */
2791 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2792 uint32_t *range, uint8_t ranges)
2794 static struct gsm322_ba_list ba;
2795 uint16_t lower, higher;
2797 memset(&ba, 0, sizeof(ba));
2800 lower = *range & 1023;
2801 higher = (*range >> 16) & 1023;
2803 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2806 ba.freq[lower >> 3] |= 1 << (lower & 7);
2807 if (lower == higher)
2809 lower = (lower + 1) & 1023;
2816 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2817 static int gsm322_cs_choose(struct osmocom_ms *ms)
2819 struct gsm322_cellsel *cs = &ms->cellsel;
2820 struct gsm48_rrlayer *rr = &ms->rrlayer;
2821 struct gsm322_ba_list *ba = NULL;
2824 /* NOTE: The call to this function is synchron to RR layer, so
2825 * we may access the BA range there.
2828 ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges);
2830 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2831 /* get and update BA of last received sysinfo 5* */
2832 ba = gsm322_cs_sysinfo_sacch(ms);
2834 LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored "
2836 ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2844 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2846 /* tell CS to start over */
2847 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2850 gsm322_c_event(ms, nmsg);
2856 /* flag all frequencies that are in current band allocation */
2857 for (i = 0; i <= 1023; i++) {
2858 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2859 if ((ba->freq[i >> 3] & (1 << (i & 7)))) {
2860 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2862 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2865 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2866 | GSM322_CS_FLAG_SIGNAL
2867 | GSM322_CS_FLAG_SYSINFO);
2868 if (cs->list[i].sysinfo) {
2869 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2870 talloc_free(cs->list[i].sysinfo);
2871 cs->list[i].sysinfo = NULL;
2875 /* unset selected cell */
2876 gsm322_unselect_cell(cs);
2878 /* start power scan */
2879 return gsm322_cs_powerscan(ms);
2882 /* start 'Choose cell' after returning to idle mode */
2883 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2885 struct gsm322_cellsel *cs = &ms->cellsel;
2886 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2888 /* After location updating, we choose the last cell */
2889 if (gm->same_cell) {
2892 if (!cs->selected) {
2893 printf("No cell selected when ret.idle, please fix!\n");
2896 cs->arfcn = cs->sel_arfcn;
2898 /* be sure to go to current camping frequency on return */
2899 LOGP(DCS, LOGL_INFO, "Selecting frequency %d. after LOC.UPD.\n",
2902 gsm322_sync_to_cell(cs);
2903 cs->si = cs->list[cs->arfcn].sysinfo;
2905 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2907 /* tell that we have selected the cell, so RR returns IDLE */
2908 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2911 gsm48_mmevent_msg(ms, nmsg);
2916 new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2918 return gsm322_cs_choose(ms);
2921 /* start 'Choose any cell' after returning to idle mode */
2922 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2924 struct gsm322_cellsel *cs = &ms->cellsel;
2926 new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2928 return gsm322_cs_choose(ms);
2931 /* a new PLMN is selected by PLMN search process */
2932 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2934 struct gsm322_cellsel *cs = &ms->cellsel;
2935 struct gsm322_plmn *plmn = &ms->plmn;
2936 struct gsm322_ba_list *ba;
2938 cs->mcc = plmn->mcc;
2939 cs->mnc = plmn->mnc;
2941 LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s "
2942 "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc),
2943 gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc),
2944 gsm_get_mnc(cs->mcc, cs->mnc));
2946 /* search for BA list */
2947 ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2950 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2951 return gsm322_c_stored_cell_sel(ms, ba);
2953 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2954 return gsm322_c_normal_cell_sel(ms, msg);
2958 /* go connected mode */
2959 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2961 struct gsm322_cellsel *cs = &ms->cellsel;
2963 /* check for error */
2966 cs->arfcn = cs->sel_arfcn;
2968 /* be sure to go to current camping frequency on return */
2969 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2971 gsm322_sync_to_cell(cs);
2972 cs->si = cs->list[cs->arfcn].sysinfo;
2977 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2979 struct gsm322_cellsel *cs = &ms->cellsel;
2981 /* check for error */
2984 cs->arfcn = cs->sel_arfcn;
2986 /* be sure to go to current camping frequency on return */
2987 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2989 gsm322_sync_to_cell(cs);
2990 cs->si = cs->list[cs->arfcn].sysinfo;
2996 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2998 struct gsm_subscriber *subscr = &ms->subscr;
3000 /* if no SIM is is MS */
3001 if (!subscr->sim_valid) {
3002 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
3003 return gsm322_c_any_cell_sel(ms, msg);
3005 LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
3007 /* stay in NULL state until PLMN is selected */
3016 /* state machine for automatic PLMN selection events */
3017 static struct plmnastatelist {
3020 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3021 } plmnastatelist[] = {
3022 {SBIT(GSM322_A0_NULL),
3023 GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
3025 /* special case for full search */
3026 {SBIT(GSM322_A0_NULL),
3027 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn},
3030 GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
3032 {SBIT(GSM322_A0_NULL) | SBIT(GSM322_A6_NO_SIM),
3033 GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
3036 GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
3039 GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
3042 GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
3044 {SBIT(GSM322_A1_TRYING_RPLMN),
3045 GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
3047 {SBIT(GSM322_A1_TRYING_RPLMN),
3048 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn},
3050 {SBIT(GSM322_A1_TRYING_RPLMN),
3051 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
3053 {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
3054 GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
3056 {SBIT(GSM322_A2_ON_PLMN),
3057 GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
3059 {SBIT(GSM322_A2_ON_PLMN),
3060 GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start},
3062 {SBIT(GSM322_A2_ON_PLMN),
3063 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
3065 {SBIT(GSM322_A2_ON_PLMN),
3066 GSM322_EVENT_USER_RESEL, gsm322_a_user_resel},
3068 {SBIT(GSM322_A3_TRYING_PLMN),
3069 GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
3071 {SBIT(GSM322_A3_TRYING_PLMN),
3072 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn},
3074 {SBIT(GSM322_A3_TRYING_PLMN),
3075 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
3077 {SBIT(GSM322_A5_HPLMN_SEARCH),
3078 GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn},
3080 {SBIT(GSM322_A5_HPLMN_SEARCH),
3081 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn},
3083 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
3084 GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
3086 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
3087 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_plmn_avail},
3090 GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
3093 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3096 #define PLMNASLLEN \
3097 (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
3099 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
3101 struct gsm322_plmn *plmn = &ms->plmn;
3102 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3103 int msg_type = gm->msg_type;
3107 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
3108 "selection in state '%s'\n", ms->name, get_event_name(msg_type),
3109 plmn_a_state_names[plmn->state]);
3110 /* find function for current state and message */
3111 for (i = 0; i < PLMNASLLEN; i++)
3112 if ((msg_type == plmnastatelist[i].type)
3113 && ((1 << plmn->state) & plmnastatelist[i].states))
3115 if (i == PLMNASLLEN) {
3116 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3120 rc = plmnastatelist[i].rout(ms, msg);
3125 /* state machine for manual PLMN selection events */
3126 static struct plmnmstatelist {
3129 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3130 } plmnmstatelist[] = {
3131 {SBIT(GSM322_M0_NULL),
3132 GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
3134 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) |
3135 SBIT(GSM322_M2_ON_PLMN),
3136 GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns},
3139 GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
3141 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M5_NO_SIM),
3142 GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
3145 GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
3148 GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
3150 {SBIT(GSM322_M1_TRYING_RPLMN),
3151 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3153 {SBIT(GSM322_M1_TRYING_RPLMN),
3154 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3156 {SBIT(GSM322_M1_TRYING_RPLMN),
3157 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3159 {SBIT(GSM322_M1_TRYING_RPLMN),
3160 GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
3162 {SBIT(GSM322_M2_ON_PLMN),
3163 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3165 {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
3166 SBIT(GSM322_M4_TRYING_PLMN),
3167 GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
3169 {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN),
3170 GSM322_EVENT_USER_RESEL, gsm322_m_user_resel},
3172 {SBIT(GSM322_M3_NOT_ON_PLMN),
3173 GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
3175 {SBIT(GSM322_M3_NOT_ON_PLMN),
3176 GSM322_EVENT_CHOOSE_PLMN, gsm322_m_choose_plmn},
3178 {SBIT(GSM322_M4_TRYING_PLMN),
3179 GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
3181 {SBIT(GSM322_M4_TRYING_PLMN),
3182 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3184 {SBIT(GSM322_M4_TRYING_PLMN),
3185 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3187 {SBIT(GSM322_M4_TRYING_PLMN),
3188 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3191 GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
3194 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3197 #define PLMNMSLLEN \
3198 (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
3200 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
3202 struct gsm322_plmn *plmn = &ms->plmn;
3203 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3204 int msg_type = gm->msg_type;
3208 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
3209 "in state '%s'\n", ms->name, get_event_name(msg_type),
3210 plmn_m_state_names[plmn->state]);
3211 /* find function for current state and message */
3212 for (i = 0; i < PLMNMSLLEN; i++)
3213 if ((msg_type == plmnmstatelist[i].type)
3214 && ((1 << plmn->state) & plmnmstatelist[i].states))
3216 if (i == PLMNMSLLEN) {
3217 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3221 rc = plmnmstatelist[i].rout(ms, msg);
3226 /* dequeue GSM 03.22 PLMN events */
3227 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
3229 struct gsm322_plmn *plmn = &ms->plmn;
3233 while ((msg = msgb_dequeue(&plmn->event_queue))) {
3234 /* send event to PLMN select process */
3235 if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
3236 gsm322_a_event(ms, msg);
3238 gsm322_m_event(ms, msg);
3240 work = 1; /* work done */
3246 /* state machine for channel selection events */
3247 static struct cellselstatelist {
3250 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3251 } cellselstatelist[] = {
3253 GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
3256 GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
3259 GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
3262 GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search},
3264 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3265 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
3266 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
3268 {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3269 SBIT(GSM322_C8_ANY_CELL_RESEL),
3270 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
3272 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3273 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3274 SBIT(GSM322_C0_NULL),
3275 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
3277 {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3278 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
3279 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
3281 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3282 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
3284 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3285 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
3287 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3288 GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
3290 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3291 GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
3293 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3294 GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
3296 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3297 GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
3299 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3300 GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
3302 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3303 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3304 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3305 SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH),
3306 GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
3308 {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
3309 GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
3311 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3312 GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search},
3315 #define CELLSELSLLEN \
3316 (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
3318 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
3320 struct gsm322_cellsel *cs = &ms->cellsel;
3321 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3322 int msg_type = gm->msg_type;
3326 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
3327 "'%s'\n", ms->name, get_event_name(msg_type),
3328 cs_state_names[cs->state]);
3329 /* find function for current state and message */
3330 for (i = 0; i < CELLSELSLLEN; i++)
3331 if ((msg_type == cellselstatelist[i].type)
3332 && ((1 << cs->state) & cellselstatelist[i].states))
3334 if (i == CELLSELSLLEN) {
3335 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
3339 rc = cellselstatelist[i].rout(ms, msg);
3344 /* dequeue GSM 03.22 cell selection events */
3345 int gsm322_cs_dequeue(struct osmocom_ms *ms)
3347 struct gsm322_cellsel *cs = &ms->cellsel;
3351 while ((msg = msgb_dequeue(&cs->event_queue))) {
3352 /* send event to cell selection process */
3353 gsm322_c_event(ms, msg);
3355 work = 1; /* work done */
3365 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
3367 struct gsm322_plmn *plmn = &ms->plmn;
3368 struct gsm322_plmn_list *temp;
3370 printf("MCC |MNC |allowed|rx-lev\n");
3371 printf("-------+-------+-------+-------\n");
3372 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
3373 printf("%s |%s%s |%s |%s\n", gsm_print_mcc(temp->mcc),
3374 gsm_print_mnc(temp->mnc),
3375 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3376 (temp->cause) ? "no ":"yes",
3377 gsm_print_rxlev(temp->rxlev));
3383 int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
3384 void (*print)(void *, const char *, ...), void *priv)
3387 struct gsm48_sysinfo *s;
3389 print(priv, "arfcn |MCC |MNC |LAC |cell ID|forb.LA|prio |"
3390 "min-db |max-pwr|rx-lev\n");
3391 print(priv, "-------+-------+-------+-------+-------+-------+-------+"
3392 "-------+-------+-------\n");
3393 for (i = 0; i <= 1023; i++) {
3394 s = cs->list[i].sysinfo;
3395 if (!s || !(cs->list[i].flags & flags))
3397 print(priv, "%4d |", i);
3398 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
3399 print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc),
3400 gsm_print_mnc(s->mnc),
3401 ((s->mnc & 0x00f) == 0x00f) ? " ":"");
3402 print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id);
3403 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
3404 print(priv, "yes |");
3406 print(priv, "no |");
3407 if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
3408 print(priv, "barred |");
3410 if (cs->list[i].sysinfo->cell_barr)
3411 print(priv, "low |");
3413 print(priv, "normal |");
3415 print(priv, "%4d |%4d |%s\n", s->rxlev_acc_min_db,
3416 s->ms_txpwr_max_cch,
3417 gsm_print_rxlev(cs->list[i].rxlev));
3419 print(priv, "n/a |n/a |n/a |n/a |n/a |"
3427 int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
3428 void (*print)(void *, const char *, ...), void *priv)
3430 struct gsm322_plmn *plmn = &ms->plmn;
3431 struct gsm322_la_list *temp;
3433 print(priv, "MCC |MNC |LAC |cause\n");
3434 print(priv, "-------+-------+-------+-------\n");
3435 llist_for_each_entry(temp, &plmn->forbidden_la, entry)
3436 print(priv, "%s |%s%s |0x%04x |#%d\n",
3437 gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
3438 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3439 temp->lac, temp->cause);
3444 int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
3445 void (*print)(void *, const char *, ...), void *priv)
3447 struct gsm322_ba_list *ba;
3450 llist_for_each_entry(ba, &cs->ba_list, entry) {
3451 if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc))
3453 print(priv, "Band Allocation of network: MCC %s MNC %s "
3454 "(%s, %s)\n", gsm_print_mcc(ba->mcc),
3455 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3456 gsm_get_mnc(ba->mcc, ba->mnc));
3457 for (i = 0; i <= 1023; i++) {
3458 if ((ba->freq[i >> 3] & (1 << (i & 7))))
3459 print(priv, " %d", i);
3471 int gsm322_init(struct osmocom_ms *ms)
3473 struct gsm322_plmn *plmn = &ms->plmn;
3474 struct gsm322_cellsel *cs = &ms->cellsel;
3478 struct gsm322_ba_list *ba;
3481 LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
3482 LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
3484 memset(plmn, 0, sizeof(*plmn));
3485 memset(cs, 0, sizeof(*cs));
3489 /* set initial state */
3492 ms->settings.plmn_mode = PLMN_MODE_AUTO;
3495 INIT_LLIST_HEAD(&plmn->event_queue);
3496 INIT_LLIST_HEAD(&cs->event_queue);
3497 INIT_LLIST_HEAD(&plmn->sorted_plmn);
3498 INIT_LLIST_HEAD(&plmn->forbidden_la);
3499 INIT_LLIST_HEAD(&cs->ba_list);
3501 /* set supported frequencies in cell selection list */
3502 for (i = 0; i <= 1023; i++)
3503 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
3504 cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
3507 sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3508 fp = fopen(filename, "r");
3513 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
3516 rc = fread(buf, 4, 1, fp);
3521 ba->mcc = (buf[0] << 8) | buf[1];
3522 ba->mnc = (buf[2] << 8) | buf[3];
3523 rc = fread(ba->freq, sizeof(ba->freq), 1, fp);
3528 llist_add_tail(&ba->entry, &cs->ba_list);
3529 LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s "
3530 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3531 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3532 gsm_get_mnc(ba->mcc, ba->mnc));
3536 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
3538 register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3543 int gsm322_exit(struct osmocom_ms *ms)
3545 struct gsm322_plmn *plmn = &ms->plmn;
3546 struct gsm322_cellsel *cs = &ms->cellsel;
3547 struct llist_head *lh, *lh2;
3551 struct gsm322_ba_list *ba;
3555 LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
3556 LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
3558 unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3560 /* stop cell selection process (if any) */
3561 new_c_state(cs, GSM322_C0_NULL);
3565 stop_plmn_timer(plmn);
3568 for (i = 0; i <= 1023; i++) {
3569 if (cs->list[i].sysinfo) {
3570 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
3571 talloc_free(cs->list[i].sysinfo);
3572 cs->list[i].sysinfo = NULL;
3574 cs->list[i].flags = 0;
3578 sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3579 fp = fopen(filename, "w");
3583 llist_for_each_entry(ba, &cs->ba_list, entry) {
3584 buf[0] = ba->mcc >> 8;
3585 buf[1] = ba->mcc & 0xff;
3586 buf[2] = ba->mnc >> 8;
3587 buf[3] = ba->mnc & 0xff;
3588 rc = fwrite(buf, 4, 1, fp);
3589 rc = fwrite(ba->freq, sizeof(ba->freq), 1, fp);
3590 LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s "
3591 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3592 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3593 gsm_get_mnc(ba->mcc, ba->mnc));
3597 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
3600 while ((msg = msgb_dequeue(&plmn->event_queue)))
3602 while ((msg = msgb_dequeue(&cs->event_queue)))
3604 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
3608 llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
3612 llist_for_each_safe(lh, lh2, &cs->ba_list) {