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/osmocom_data.h>
37 #include <osmocom/networks.h>
38 #include <osmocom/vty.h>
42 static void gsm322_cs_timeout(void *arg);
43 static void gsm322_cs_loss(void *arg);
44 static int gsm322_cs_select(struct osmocom_ms *ms, int any);
45 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg);
54 /* Cell selection process
56 * The process depends on states and events (finites state machine).
58 * During states of cell selection or cell re-selection, the search for a cell
59 * is performed in two steps:
61 * 1. Measurement of received level of all relevant frequencies (rx-lev)
63 * 2. Receive system information messages of all relevant frequencies
65 * During this process, the results are stored in a list of all frequencies.
66 * This list is checked whenever a cell is selected. It depends on the results
67 * if the cell is 'suitable' and 'allowable' to 'camp' on.
69 * This list is also used to generate a list of available networks.
73 * - cs->list[0..1023].xxx for each cell, where
74 * - flags and rxlev_db are used to store outcome of cell scanning process
75 * - sysinfo pointing to sysinfo memory, allocated temporarily
76 * - cs->selected and cs->sel_* states of the current / last selected cell.
79 * There is a special state: GSM322_PLMN_SEARCH
80 * It is used to search for all cells, to find the HPLMN. This is triggered
81 * by a timer. Also it is used before selecting PLMN from list.
85 /* PLMN selection process
87 * The PLMN (Public Land Mobile Network = Operator's Network) has two different
94 * The process depends on states and events (finites state machine).
98 /* File format of BA list:
103 * where frequency 0 is bit 0 of first byte
105 * If not end-of-file, the next BA list is stored.
110 * * subscr->plmn_list
112 * The "PLMN Selector list" stores prefered networks to select during PLMN
113 * search process. This list is also stored in the SIM.
117 * The "forbidden PLMNs" list stores all networks that rejected us. The stored
118 * network will not be used when searching PLMN automatically. This list is
119 * also stored din the SIM.
121 * * plmn->forbidden_la
123 * The "forbidden LAs for roaming" list stores all location areas where roaming
128 * This list stores measurements and cell informations during cell selection
129 * process. It can be used to speed up repeated cell selection.
133 * This list stores a map of frequencies used for a PLMN. If this lists exists
134 * for a PLMN, it helps to speedup cell scan process.
136 * * plmn->sorted_plmn
138 * This list is generated whenever a PLMN search is started and a list of PLMNs
139 * is required. It consists of home PLMN, PLMN Selector list, and PLMNs found
140 * during scan process.
147 static const struct value_string gsm322_event_names[] = {
148 { GSM322_EVENT_SWITCH_ON, "EVENT_SWITCH_ON" },
149 { GSM322_EVENT_SWITCH_OFF, "EVENT_SWITCH_OFF" },
150 { GSM322_EVENT_SIM_INSERT, "EVENT_SIM_INSERT" },
151 { GSM322_EVENT_SIM_REMOVE, "EVENT_SIM_REMOVE" },
152 { GSM322_EVENT_REG_FAILED, "EVENT_REG_FAILED" },
153 { GSM322_EVENT_ROAMING_NA, "EVENT_ROAMING_NA" },
154 { GSM322_EVENT_INVALID_SIM, "EVENT_INVALID_SIM" },
155 { GSM322_EVENT_REG_SUCCESS, "EVENT_REG_SUCCESS" },
156 { GSM322_EVENT_NEW_PLMN, "EVENT_NEW_PLMN" },
157 { GSM322_EVENT_ON_PLMN, "EVENT_ON_PLMN" },
158 { GSM322_EVENT_PLMN_SEARCH_START,"EVENT_PLMN_SEARCH_START" },
159 { GSM322_EVENT_PLMN_SEARCH_END, "EVENT_PLMN_SEARCH_END" },
160 { GSM322_EVENT_USER_RESEL, "EVENT_USER_RESEL" },
161 { GSM322_EVENT_PLMN_AVAIL, "EVENT_PLMN_AVAIL" },
162 { GSM322_EVENT_CHOSE_PLMN, "EVENT_CHOSE_PLMN" },
163 { GSM322_EVENT_SEL_MANUAL, "EVENT_SEL_MANUAL" },
164 { GSM322_EVENT_SEL_AUTO, "EVENT_SEL_AUTO" },
165 { GSM322_EVENT_CELL_FOUND, "EVENT_CELL_FOUND" },
166 { GSM322_EVENT_NO_CELL_FOUND, "EVENT_NO_CELL_FOUND" },
167 { GSM322_EVENT_LEAVE_IDLE, "EVENT_LEAVE_IDLE" },
168 { GSM322_EVENT_RET_IDLE, "EVENT_RET_IDLE" },
169 { GSM322_EVENT_CELL_RESEL, "EVENT_CELL_RESEL" },
170 { GSM322_EVENT_SYSINFO, "EVENT_SYSINFO" },
171 { GSM322_EVENT_HPLMN_SEARCH, "EVENT_HPLMN_SEARCH" },
175 const char *get_event_name(int value)
177 return get_value_string(gsm322_event_names, value);
181 /* allocate a 03.22 event message */
182 struct msgb *gsm322_msgb_alloc(int msg_type)
185 struct gsm322_msg *gm;
187 msg = msgb_alloc_headroom(sizeof(*gm), 0, "GSM 03.22 event");
191 gm = (struct gsm322_msg *)msgb_put(msg, sizeof(*gm));
192 gm->msg_type = msg_type;
197 /* queue PLMN selection message */
198 int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
200 struct gsm322_plmn *plmn = &ms->plmn;
202 msgb_enqueue(&plmn->event_queue, msg);
207 /* queue cell selection message */
208 int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
210 struct gsm322_cellsel *cs = &ms->cellsel;
212 msgb_enqueue(&cs->event_queue, msg);
221 static int gsm322_sync_to_cell(struct gsm322_cellsel *cs)
223 struct osmocom_ms *ms = cs->ms;
224 struct gsm48_sysinfo *s = cs->si;
226 cs->ccch_state = GSM322_CCCH_ST_INIT;
228 if (s->ccch_conf == 1) {
229 LOGP(DCS, LOGL_INFO, "Sysinfo, ccch mode COMB\n");
230 cs->ccch_mode = CCCH_MODE_COMBINED;
232 LOGP(DCS, LOGL_INFO, "Sysinfo, ccch mode NON-COMB\n");
233 cs->ccch_mode = CCCH_MODE_NON_COMBINED;
236 LOGP(DCS, LOGL_INFO, "No sysinfo, ccch mode NONE\n");
237 cs->ccch_mode = CCCH_MODE_NONE;
239 // printf("s->ccch_conf %d\n", cs->si->ccch_conf);
241 l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
242 return l1ctl_tx_fbsb_req(ms, cs->arfcn,
243 L1CTL_FBSB_F_FB01SB, 100, 0,
247 static void gsm322_unselect_cell(struct gsm322_cellsel *cs)
251 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
252 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
255 /* print to DCS logging */
256 static void print_dcs(void *priv, const char *fmt, ...)
262 vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
263 buffer[sizeof(buffer) - 1] = '\0';
267 // LOGP(DCS, LOGL_INFO, "%s", buffer);
268 printf("%s", buffer);
271 /* del forbidden LA */
272 int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
273 uint16_t mnc, uint16_t lac)
275 struct gsm322_plmn *plmn = &ms->plmn;
276 struct gsm322_la_list *la;
278 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
279 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
280 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
281 "LAs (mcc=%s, mnc=%s, lac=%04x)\n",
282 gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac);
283 llist_del(&la->entry);
292 /* add forbidden LA */
293 int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
294 uint16_t mnc, uint16_t lac, uint8_t cause)
296 struct gsm322_plmn *plmn = &ms->plmn;
297 struct gsm322_la_list *la;
299 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
300 "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc),
301 gsm_print_mnc(mnc), lac);
302 la = talloc_zero(l23_ctx, struct gsm322_la_list);
309 llist_add_tail(&la->entry, &plmn->forbidden_la);
314 /* search forbidden LA */
315 int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
318 struct gsm322_plmn *plmn = &ms->plmn;
319 struct gsm322_la_list *la;
321 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
322 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
329 /* search for PLMN in all BA lists */
330 static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
331 uint16_t mcc, uint16_t mnc)
333 struct gsm322_ba_list *ba, *ba_found = NULL;
335 /* search for BA list */
336 llist_for_each_entry(ba, &cs->ba_list, entry) {
347 /* search available PLMN */
348 int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
352 for (i = 0; i <= 1023; i++) {
353 if (cs->list[i].sysinfo
354 && cs->list[i].sysinfo->mcc == mcc
355 && cs->list[i].sysinfo->mnc == mnc)
362 /* search available HPLMN */
363 int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi)
367 for (i = 0; i <= 1023; i++) {
368 if (cs->list[i].sysinfo
369 && gsm_match_mnc(cs->list[i].sysinfo->mcc,
370 cs->list[i].sysinfo->mnc, imsi))
377 /* del forbidden LA */
382 /*plmn search timer event */
383 static void plmn_timer_timeout(void *arg)
385 struct gsm322_plmn *plmn = arg;
388 LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
390 /* indicate PLMN selection T timeout */
391 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
394 gsm322_plmn_sendmsg(plmn->ms, nmsg);
397 /* start plmn search timer */
398 static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
400 LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
402 plmn->timer.cb = plmn_timer_timeout;
403 plmn->timer.data = plmn;
404 bsc_schedule_timer(&plmn->timer, secs, 0);
407 /* stop plmn search timer */
408 static void stop_plmn_timer(struct gsm322_plmn *plmn)
410 if (bsc_timer_pending(&plmn->timer)) {
411 LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
412 bsc_del_timer(&plmn->timer);
416 /* start cell selection timer */
417 void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
419 LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec);
420 cs->timer.cb = gsm322_cs_timeout;
422 bsc_schedule_timer(&cs->timer, sec, micro);
425 /* start loss timer */
426 void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro)
429 cs->timer.cb = gsm322_cs_loss;
431 if (bsc_timer_pending(&cs->timer)) {
432 struct timeval current_time;
433 unsigned long long currentTime;
435 gettimeofday(¤t_time, NULL);
436 currentTime = current_time.tv_sec * 1000000LL
437 + current_time.tv_usec;
438 currentTime += sec * 1000000LL + micro;
439 cs->timer.timeout.tv_sec = currentTime / 1000000LL;
440 cs->timer.timeout.tv_usec = currentTime % 1000000LL;
445 LOGP(DCS, LOGL_INFO, "Starting loss CS timer with %d seconds.\n", sec);
446 bsc_schedule_timer(&cs->timer, sec, micro);
449 /* stop cell selection timer */
450 static void stop_cs_timer(struct gsm322_cellsel *cs)
452 if (bsc_timer_pending(&cs->timer)) {
453 LOGP(DCS, LOGL_INFO, "stopping pending CS timer.\n");
454 bsc_del_timer(&cs->timer);
462 const char *plmn_a_state_names[] = {
467 "A4 wait for PLMN to appear",
472 const char *plmn_m_state_names[] = {
481 const char *cs_state_names[] = {
483 "C1 normal cell selection",
484 "C2 stored cell selection",
485 "C3 camped normally",
486 "C4 normal cell re-selection",
488 "C6 any cell selection",
489 "C7 camped on any cell",
490 "C8 any cell re-selection",
491 "C9 choose any cell",
497 /* new automatic PLMN search state */
498 static void new_a_state(struct gsm322_plmn *plmn, int state)
500 if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) {
501 LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
505 stop_plmn_timer(plmn);
507 if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
510 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
511 plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
516 /* new manual PLMN search state */
517 static void new_m_state(struct gsm322_plmn *plmn, int state)
519 if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
520 LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
524 if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
527 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
528 plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
533 /* new Cell selection state */
534 static void new_c_state(struct gsm322_cellsel *cs, int state)
536 if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
539 LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n",
540 cs_state_names[cs->state], cs_state_names[state]);
542 /* stop cell selection timer, if running */
545 /* stop scanning of power measurement */
560 /* 4.4.3 create sorted list of PLMN
562 * the source of entries are
565 * - entries found in the SIM's PLMN Selector list
566 * - scanned PLMNs above -85 dB (random order)
567 * - scanned PLMNs below or equal -85 (by received level)
571 * The list only includes networks found at last scan.
573 * The list always contains HPLMN if available, even if not used by PLMN
574 * search process at some conditions.
576 * The list contains all PLMNs even if not allowed, so entries have to be
577 * removed when selecting from the list. (In case we use manual cell selection,
578 * we need to provide non-allowed networks also.)
580 static int gsm322_sort_list(struct osmocom_ms *ms)
582 struct gsm322_plmn *plmn = &ms->plmn;
583 struct gsm322_cellsel *cs = &ms->cellsel;
584 struct gsm_subscriber *subscr = &ms->subscr;
585 struct gsm_sub_plmn_list *sim_entry;
586 struct gsm_sub_plmn_na *na_entry;
587 struct llist_head temp_list;
588 struct gsm322_plmn_list *temp, *found;
589 struct llist_head *lh, *lh2;
590 int i, entries, move;
591 int8_t search_db = 0;
594 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
599 /* Create a temporary list of all networks */
600 INIT_LLIST_HEAD(&temp_list);
601 for (i = 0; i <= 1023; i++) {
602 if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)
603 || !cs->list[i].sysinfo)
606 /* search if network has multiple cells */
608 llist_for_each_entry(temp, &temp_list, entry) {
609 if (temp->mcc == cs->list[i].sysinfo->mcc
610 && temp->mnc == cs->list[i].sysinfo->mnc) {
615 /* update or create */
617 if (cs->list[i].rxlev_db > found->rxlev_db)
618 found->rxlev_db = cs->list[i].rxlev_db;
620 temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
623 temp->mcc = cs->list[i].sysinfo->mcc;
624 temp->mnc = cs->list[i].sysinfo->mnc;
625 temp->rxlev_db = cs->list[i].rxlev_db;
626 llist_add_tail(&temp->entry, &temp_list);
630 /* move Home PLMN, if in list, else add it */
631 if (subscr->sim_valid) {
633 llist_for_each_entry(temp, &temp_list, entry) {
634 if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) {
641 llist_del(&found->entry);
642 llist_add_tail(&found->entry, &plmn->sorted_plmn);
646 /* move entries if in SIM's PLMN Selector list */
647 llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
649 llist_for_each_entry(temp, &temp_list, entry) {
650 if (temp->mcc == sim_entry->mcc
651 && temp->mnc == sim_entry->mnc) {
657 llist_del(&found->entry);
658 llist_add_tail(&found->entry, &plmn->sorted_plmn);
662 /* move PLMN above -85 dBm in random order */
664 llist_for_each_entry(temp, &temp_list, entry) {
665 if (temp->rxlev_db > -85)
669 move = random() % entries;
671 llist_for_each_entry(temp, &temp_list, entry) {
672 if (temp->rxlev_db > -85) {
674 llist_del(&temp->entry);
675 llist_add_tail(&temp->entry,
685 /* move ohter PLMN in decreasing order */
688 llist_for_each_entry(temp, &temp_list, entry) {
690 || temp->rxlev_db > search_db) {
691 search_db = temp->rxlev_db;
697 llist_del(&found->entry);
698 llist_add_tail(&found->entry, &plmn->sorted_plmn);
701 /* mark forbidden PLMNs, if in list of forbidden networks */
703 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
704 llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
705 if (temp->mcc == na_entry->mcc
706 && temp->mnc == na_entry->mnc) {
707 temp->cause = na_entry->cause;
711 LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. "
712 "(%02d: mcc=%s mnc=%s allowed=%s rx-lev=%d)\n",
713 i, gsm_print_mcc(temp->mcc),
714 gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes",
719 gsm322_dump_sorted_plmn(ms);
725 * handler for automatic search
728 /* go On PLMN state */
729 static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
731 struct gsm322_plmn *plmn = &ms->plmn;
732 struct gsm_subscriber *subscr = &ms->subscr;
734 new_a_state(plmn, GSM322_A2_ON_PLMN);
736 /* start timer, if on VPLMN of home country OR special case */
737 if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi)
738 && (subscr->always_search_hplmn
739 || gsm_match_mcc(plmn->mcc, subscr->imsi))) {
740 if (subscr->sim_valid && subscr->t6m_hplmn)
741 start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
743 start_plmn_timer(plmn, 30 * 360);
745 stop_plmn_timer(plmn);
750 /* indicate selected PLMN */
751 static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
753 struct gsm322_plmn *plmn = &ms->plmn;
755 vty_notify(ms, NULL);
756 vty_notify(ms, "Selected Network: %s, %s\n",
757 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
759 return gsm322_a_go_on_plmn(ms, msg);
762 /* no (more) PLMN in list */
763 static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
765 struct gsm322_plmn *plmn = &ms->plmn;
766 struct gsm322_cellsel *cs = &ms->cellsel;
770 /* any allowable PLMN available? */
771 plmn->mcc = plmn->mnc = 0;
772 found = gsm322_cs_select(ms, 0);
774 /* if no PLMN in list */
776 LOGP(DPLMN, LOGL_INFO, "Not any PLNs allowable.\n");
778 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
780 /* we must forward this, otherwhise "Any cell selection"
781 * will not start automatically.
783 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
786 gsm322_cs_sendmsg(ms, nmsg);
791 /* select first PLMN in list */
792 plmn->mcc = cs->list[found].sysinfo->mcc;
793 plmn->mnc = cs->list[found].sysinfo->mnc;
795 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n",
796 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
797 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
799 /* indicate New PLMN */
800 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
803 gsm322_cs_sendmsg(ms, nmsg);
806 return gsm322_a_indicate_selected(ms, msg);
809 /* select first PLMN in list */
810 static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
812 struct gsm322_plmn *plmn = &ms->plmn;
813 struct gsm_subscriber *subscr = &ms->subscr;
815 struct gsm322_plmn_list *plmn_entry;
816 struct gsm322_plmn_list *plmn_first = NULL;
820 gsm322_sort_list(ms);
822 /* select first entry */
824 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
825 /* if last selected PLMN was HPLMN, we skip that */
826 if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc,
828 && plmn_entry->mcc == plmn->mcc
829 && plmn_entry->mnc == plmn->mnc) {
830 LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was "
831 "previously selected.\n");
835 /* select first allowed network */
836 if (!plmn_entry->cause) {
837 plmn_first = plmn_entry;
840 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it "
841 "not allowed (cause %d).\n", plmn_entry->mcc,
842 plmn_entry->mnc, plmn_entry->cause);
847 /* if no PLMN in list */
849 LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
850 gsm322_a_no_more_plmn(ms, msg);
855 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
856 "mnc=%s %s, %s)\n", plmn->plmn_curr,
857 gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc),
858 gsm_get_mcc(plmn_first->mcc),
859 gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
861 /* set current network */
862 plmn->mcc = plmn_first->mcc;
863 plmn->mnc = plmn_first->mnc;
865 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
867 /* indicate New PLMN */
868 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
871 gsm322_cs_sendmsg(ms, nmsg);
876 /* select next PLMN in list */
877 static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
879 struct gsm322_plmn *plmn = &ms->plmn;
881 struct gsm322_plmn_list *plmn_entry;
882 struct gsm322_plmn_list *plmn_next = NULL;
885 /* select next entry from list */
887 ii = plmn->plmn_curr + 1;
888 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
889 /* skip previously selected networks */
894 /* select next allowed network */
895 if (!plmn_entry->cause) {
896 plmn_next = plmn_entry;
899 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it "
900 "not allowed (cause %d).\n", plmn_entry->mcc,
901 plmn_entry->mnc, plmn_entry->cause);
906 /* if no more PLMN in list */
908 LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
909 gsm322_a_no_more_plmn(ms, msg);
914 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
915 "mnc=%s %s, %s)\n", plmn->plmn_curr,
916 gsm_print_mcc(plmn_next->mcc), gsm_print_mnc(plmn_next->mnc),
917 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
919 /* set next network */
920 plmn->mcc = plmn_next->mcc;
921 plmn->mnc = plmn_next->mnc;
923 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
925 /* indicate New PLMN */
926 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
929 gsm322_cs_sendmsg(ms, nmsg);
934 /* User re-selection event */
935 static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg)
937 struct gsm322_plmn *plmn = &ms->plmn;
938 struct gsm_subscriber *subscr = &ms->subscr;
939 struct gsm48_rrlayer *rr = &ms->rrlayer;
940 struct gsm322_plmn_list *plmn_entry;
941 struct gsm322_plmn_list *plmn_found = NULL;
943 if (!subscr->sim_valid) {
947 /* try again later, if not idle */
948 if (rr->state != GSM48_RR_ST_IDLE) {
949 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
954 /* search current PLMN in list */
955 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
956 if (plmn_entry->mcc == plmn->mcc
957 && plmn_entry->mnc == plmn->mnc)
958 plmn_found = plmn_entry;
962 /* abort if list is empty */
964 LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
968 LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
969 "and restarting PLMN search process.\n");
971 /* move entry to end of list */
972 llist_del(&plmn_found->entry);
973 llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
975 /* select first PLMN in list */
976 return gsm322_a_sel_first_plmn(ms, msg);
979 /* PLMN becomes available */
980 static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
982 struct gsm322_plmn *plmn = &ms->plmn;
983 struct gsm_subscriber *subscr = &ms->subscr;
984 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
986 if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
987 && subscr->plmn_mnc == gm->mnc) {
991 LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n");
992 return gsm322_a_go_on_plmn(ms, msg);
994 /* select first PLMN in list */
995 LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
996 "search process.\n");
997 return gsm322_a_sel_first_plmn(ms, msg);
1001 /* loss of radio coverage */
1002 static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
1004 struct gsm322_plmn *plmn = &ms->plmn;
1005 struct gsm322_cellsel *cs = &ms->cellsel;
1009 /* any PLMN available */
1010 found = gsm322_cs_select(ms, 0);
1012 /* if PLMN in list */
1014 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s "
1015 "%s, %s)\n", gsm_print_mcc(
1016 cs->list[found].sysinfo->mcc),
1017 gsm_print_mnc(cs->list[found].sysinfo->mnc),
1018 gsm_get_mcc(cs->list[found].sysinfo->mcc),
1019 gsm_get_mnc(cs->list[found].sysinfo->mcc,
1020 cs->list[found].sysinfo->mnc));
1021 return gsm322_a_sel_first_plmn(ms, msg);
1024 LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
1026 plmn->mcc = plmn->mnc = 0;
1028 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
1030 /* Tell cell selection process to handle "no cell found". */
1031 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1034 gsm322_cs_sendmsg(ms, nmsg);
1039 /* MS is switched on OR SIM is inserted OR removed */
1040 static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1042 struct gsm_subscriber *subscr = &ms->subscr;
1043 struct gsm322_plmn *plmn = &ms->plmn;
1046 if (!subscr->sim_valid) {
1047 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1048 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1049 new_a_state(plmn, GSM322_A6_NO_SIM);
1054 /* if there is a registered PLMN */
1055 if (subscr->plmn_valid) {
1056 /* select the registered PLMN */
1057 plmn->mcc = subscr->plmn_mcc;
1058 plmn->mnc = subscr->plmn_mnc;
1060 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1061 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1062 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1063 gsm_get_mnc(plmn->mcc, plmn->mnc));
1064 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1065 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1066 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1067 gsm_get_mnc(plmn->mcc, plmn->mnc));
1069 new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
1071 /* indicate New PLMN */
1072 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1075 gsm322_cs_sendmsg(ms, nmsg);
1080 /* initiate search at cell selection */
1081 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1082 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1084 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1087 gsm322_cs_sendmsg(ms, nmsg);
1092 /* MS is switched off */
1093 static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1095 struct gsm322_plmn *plmn = &ms->plmn;
1097 new_a_state(plmn, GSM322_A0_NULL);
1102 static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1104 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1108 /* SIM is removed */
1109 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1113 /* indicate SIM remove to cell selection process */
1114 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1117 gsm322_cs_sendmsg(ms, nmsg);
1119 return gsm322_a_switch_on(ms, msg);
1122 /* location update response: "Roaming not allowed" */
1123 static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
1125 /* store in list of forbidden LAs is done in gsm48* */
1127 return gsm322_a_sel_first_plmn(ms, msg);
1130 /* On VPLMN of home country and timeout occurs */
1131 static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg)
1133 struct gsm48_rrlayer *rr = &ms->rrlayer;
1134 struct gsm322_plmn *plmn = &ms->plmn;
1135 struct gsm322_cellsel *cs = &ms->cellsel;
1138 /* try again later, if not idle and not camping */
1139 if (rr->state != GSM48_RR_ST_IDLE
1140 || cs->state != GSM322_C3_CAMPED_NORMALLY) {
1141 LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n");
1142 start_plmn_timer(plmn, 60);
1147 new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
1149 /* initiate search at cell selection */
1150 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1153 gsm322_cs_sendmsg(ms, nmsg);
1158 /* manual mode selected */
1159 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
1163 /* restart state machine */
1164 gsm322_a_switch_off(ms, msg);
1165 ms->settings.plmn_mode = PLMN_MODE_MANUAL;
1166 gsm322_m_switch_on(ms, msg);
1168 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1171 gsm48_mmevent_msg(ms, nmsg);
1177 * handler for manual search
1180 /* display PLMNs and to Not on PLMN */
1181 static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
1183 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1184 int msg_type = gm->msg_type;
1185 struct gsm322_plmn *plmn = &ms->plmn;
1186 struct gsm_sub_plmn_list *temp;
1189 gsm322_sort_list(ms);
1191 vty_notify(ms, NULL);
1193 case GSM322_EVENT_REG_FAILED:
1194 vty_notify(ms, "Failed to register to network %s, %s "
1196 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1197 gsm_get_mcc(plmn->mcc),
1198 gsm_get_mnc(plmn->mcc, plmn->mnc));
1200 case GSM322_EVENT_NO_CELL_FOUND:
1201 vty_notify(ms, "No cell found for network %s, %s "
1203 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1204 gsm_get_mcc(plmn->mcc),
1205 gsm_get_mnc(plmn->mcc, plmn->mnc));
1207 case GSM322_EVENT_ROAMING_NA:
1208 vty_notify(ms, "Roaming not allowed to network %s, %s "
1210 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1211 gsm_get_mcc(plmn->mcc),
1212 gsm_get_mnc(plmn->mcc, plmn->mnc));
1216 if (llist_empty(&plmn->sorted_plmn))
1217 vty_notify(ms, "Search network!\n");
1219 vty_notify(ms, "Search or select from network:\n");
1220 llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
1221 vty_notify(ms, " Network %s, %s (%s, %s)\n",
1222 gsm_print_mcc(temp->mcc),
1223 gsm_print_mnc(temp->mnc),
1224 gsm_get_mcc(temp->mcc),
1225 gsm_get_mnc(temp->mcc, temp->mnc));
1228 /* go Not on PLMN state */
1229 new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
1234 /* user starts reselection */
1235 static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg)
1237 struct gsm_subscriber *subscr = &ms->subscr;
1238 struct gsm48_rrlayer *rr = &ms->rrlayer;
1241 if (!subscr->sim_valid) {
1245 /* try again later, if not idle */
1246 if (rr->state != GSM48_RR_ST_IDLE) {
1247 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
1252 /* initiate search at cell selection */
1253 vty_notify(ms, NULL);
1254 vty_notify(ms, "Searching Network, please wait...\n");
1255 LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n");
1257 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1260 gsm322_cs_sendmsg(ms, nmsg);
1265 /* MS is switched on OR SIM is inserted OR removed */
1266 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1268 struct gsm_subscriber *subscr = &ms->subscr;
1269 struct gsm322_plmn *plmn = &ms->plmn;
1272 if (!subscr->sim_valid) {
1273 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1274 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1275 new_m_state(plmn, GSM322_M5_NO_SIM);
1280 /* if there is a registered PLMN */
1281 if (subscr->plmn_valid) {
1284 /* select the registered PLMN */
1285 plmn->mcc = subscr->plmn_mcc;
1286 plmn->mnc = subscr->plmn_mnc;
1288 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1289 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1290 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1291 gsm_get_mnc(plmn->mcc, plmn->mnc));
1292 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1293 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1294 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1295 gsm_get_mnc(plmn->mcc, plmn->mnc));
1297 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1299 /* indicate New PLMN */
1300 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1303 gsm322_cs_sendmsg(ms, nmsg);
1308 /* initiate search at cell selection */
1309 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1310 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1311 vty_notify(ms, NULL);
1312 vty_notify(ms, "Searching Network, please wait...\n");
1314 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1317 gsm322_cs_sendmsg(ms, nmsg);
1322 /* MS is switched off */
1323 static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1325 struct gsm322_plmn *plmn = &ms->plmn;
1327 stop_plmn_timer(plmn);
1329 new_m_state(plmn, GSM322_M0_NULL);
1334 static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1336 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1340 /* SIM is removed */
1341 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1343 struct gsm322_plmn *plmn = &ms->plmn;
1346 stop_plmn_timer(plmn);
1348 /* indicate SIM remove to cell selection process */
1349 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1352 gsm322_cs_sendmsg(ms, nmsg);
1354 return gsm322_m_switch_on(ms, msg);
1357 /* go to On PLMN state */
1358 static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1360 struct gsm322_plmn *plmn = &ms->plmn;
1361 struct gsm_subscriber *subscr = &ms->subscr;
1363 /* if selected PLMN is in list of forbidden PLMNs */
1364 gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc);
1366 /* set last registered PLMN */
1367 subscr->plmn_valid = 1;
1368 subscr->plmn_mcc = plmn->mcc;
1369 subscr->plmn_mnc = plmn->mnc;
1374 new_m_state(plmn, GSM322_M2_ON_PLMN);
1379 /* indicate selected PLMN */
1380 static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
1382 struct gsm322_plmn *plmn = &ms->plmn;
1384 vty_notify(ms, NULL);
1385 vty_notify(ms, "Selected Network: %s, %s\n",
1386 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1388 return gsm322_m_go_on_plmn(ms, msg);
1391 /* previously selected PLMN becomes available again */
1392 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1394 struct gsm322_plmn *plmn = &ms->plmn;
1395 struct gsm322_cellsel *cs = &ms->cellsel;
1397 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1399 if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
1402 LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
1403 "selected, so start selection.\n");
1405 /* indicate New PLMN */
1406 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1409 gsm322_cs_sendmsg(ms, nmsg);
1415 /* the user has selected given PLMN */
1416 static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
1418 struct gsm322_plmn *plmn = &ms->plmn;
1419 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1422 /* use user selection */
1423 plmn->mcc = gm->mcc;
1424 plmn->mnc = gm->mnc;
1426 vty_notify(ms, NULL);
1427 vty_notify(ms, "Selected Network: %s, %s\n",
1428 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1429 LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s "
1430 "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1431 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1433 new_m_state(plmn, GSM322_M4_TRYING_PLMN);
1435 /* indicate New PLMN */
1436 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1439 gsm322_cs_sendmsg(ms, nmsg);
1444 /* auto mode selected */
1445 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
1449 /* restart state machine */
1450 gsm322_m_switch_off(ms, msg);
1451 ms->settings.plmn_mode = PLMN_MODE_AUTO;
1452 gsm322_a_switch_on(ms, msg);
1454 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1457 gsm48_mmevent_msg(ms, nmsg);
1462 /* if no cell is found in other states than in *_TRYING_* states */
1463 static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
1467 /* Tell cell selection process to handle "no cell found". */
1468 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1471 gsm322_cs_sendmsg(ms, nmsg);
1477 * cell scanning process
1480 /* select a suitable and allowable cell */
1481 static int gsm322_cs_select(struct osmocom_ms *ms, int any)
1483 struct gsm322_cellsel *cs = &ms->cellsel;
1484 struct gsm_subscriber *subscr = &ms->subscr;
1485 struct gsm48_sysinfo *s;
1486 int i, found = -1, power = 0;
1487 uint8_t flags, mask;
1490 /* set out access class depending on the cell selection type */
1492 acc_class = subscr->acc_class | 0x0400; /* add emergency */
1493 LOGP(DCS, LOGL_INFO, "Select using access class with Emergency "
1496 acc_class = subscr->acc_class;
1497 LOGP(DCS, LOGL_INFO, "Select using access class \n");
1500 /* flags to match */
1501 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1502 | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
1503 if (cs->state == GSM322_C2_STORED_CELL_SEL
1504 || cs->state == GSM322_C5_CHOOSE_CELL)
1505 mask |= GSM322_CS_FLAG_BA;
1506 flags = mask; /* all masked flags are requied */
1508 /* loop through all scanned frequencies and select cell */
1509 for (i = 0; i <= 1023; i++) {
1510 cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
1511 s = cs->list[i].sysinfo;
1513 /* channel has no informations for us */
1514 if (!s || (cs->list[i].flags & mask) != flags) {
1518 /* check C1 criteria not fullfilled */
1519 // TODO: C1 is also dependant on power class and max power
1520 if (cs->list[i].rxlev_db < s->rxlev_acc_min_db) {
1521 LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
1522 "not met. (rxlev=%d < min=%d)\n", i,
1523 cs->list[i].rxlev_db, s->rxlev_acc_min_db);
1527 /* if cell is barred and we don't override */
1528 if (!subscr->acc_barr
1529 && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
1530 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
1535 /* if cell is in list of forbidden LAs */
1536 if (!subscr->acc_barr
1537 && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
1538 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1539 "list of forbidden LAs. (mcc=%s mnc=%s "
1540 "lai=%04x)\n", i, gsm_print_mcc(s->mcc),
1541 gsm_print_mnc(s->mnc), s->lac);
1545 /* if we have no access to the cell and we don't override */
1546 if (!subscr->acc_barr
1547 && !(acc_class & (s->class_barr ^ 0xffff))) {
1548 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
1549 "barred for out access. (access=%04x "
1550 "barred=%04x)\n", i, acc_class, s->class_barr);
1554 /* store temporary available and allowable flag */
1555 cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
1557 /* if we search a specific PLMN, but it does not match */
1558 if (!any && cs->mcc && (cs->mcc != s->mcc
1559 || cs->mnc != s->mnc)) {
1560 LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
1561 "does not match target PLMN. (mcc=%s "
1562 "mnc=%s)\n", i, gsm_print_mcc(s->mcc),
1563 gsm_print_mnc(s->mnc));
1567 LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%d "
1568 "mcc=%s mnc=%s lac=%04x %s, %s)\n", i,
1569 cs->list[i].rxlev_db, gsm_print_mcc(s->mcc),
1570 gsm_print_mnc(s->mnc), s->lac, gsm_get_mcc(s->mcc),
1571 gsm_get_mnc(s->mcc, s->mnc));
1573 /* find highest power cell */
1574 if (found < 0 || cs->list[i].rxlev_db > power) {
1575 power = cs->list[i].rxlev_db;
1581 LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
1586 /* tune to first/next unscanned frequency and search for PLMN */
1587 static int gsm322_cs_scan(struct osmocom_ms *ms)
1589 struct gsm322_cellsel *cs = &ms->cellsel;
1591 uint8_t mask, flags;
1592 uint32_t weight = 0, test = cs->scan_state;
1594 /* search for strongest unscanned cell */
1595 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1596 | GSM322_CS_FLAG_SIGNAL;
1597 if (cs->state == GSM322_C2_STORED_CELL_SEL
1598 || cs->state == GSM322_C5_CHOOSE_CELL)
1599 mask |= GSM322_CS_FLAG_BA;
1600 flags = mask; /* all masked flags are requied */
1601 for (i = 0; i <= 1023; i++) {
1602 /* skip if band has enough frequencies scanned (3.2.1) */
1603 for (j = 0; gsm_sup_smax[j].max; j++) {
1604 if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
1605 if (gsm_sup_smax[j].start >= i
1606 && gsm_sup_smax[j].end <= i)
1609 if (gsm_sup_smax[j].end <= i
1610 || gsm_sup_smax[j].start >= i)
1614 if (gsm_sup_smax[j].max) {
1615 if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
1618 /* search for unscanned frequency */
1619 if ((cs->list[i].flags & mask) == flags) {
1620 /* weight depends on the power level
1621 * if it is the same, it depends on arfcn
1623 test = cs->list[i].rxlev_db + 128;
1624 test = (test << 16) | i;
1625 if (test >= cs->scan_state)
1631 cs->scan_state = weight;
1634 gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs,
1637 /* special case for PLMN search */
1638 if (cs->state == GSM322_PLMN_SEARCH && !weight) {
1641 /* create AA flag */
1642 cs->mcc = cs->mnc = 0;
1643 gsm322_cs_select(ms, 0);
1645 new_c_state(cs, GSM322_C0_NULL);
1647 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END);
1648 LOGP(DCS, LOGL_INFO, "PLMN search finished.\n");
1651 gsm322_plmn_sendmsg(ms, nmsg);
1656 /* special case for HPLMN search */
1657 if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
1660 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1661 LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n");
1664 gsm322_plmn_sendmsg(ms, nmsg);
1666 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
1668 cs->arfcn = cs->sel_arfcn;
1669 LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev "
1670 "%d).\n", cs->arfcn, cs->list[cs->arfcn].rxlev_db);
1672 gsm322_sync_to_cell(cs);
1673 // start_cs_timer(cs, ms->support.sync_to, 0);
1678 /* if all frequencies have been searched */
1684 LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
1686 /* just see, if we search for any cell */
1687 if (cs->state == GSM322_C6_ANY_CELL_SEL
1688 || cs->state == GSM322_C8_ANY_CELL_RESEL
1689 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1692 found = gsm322_cs_select(ms, any);
1696 struct gsm322_plmn *plmn = &ms->plmn;
1698 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1701 cs->si = cs->list[cs->arfcn].sysinfo;
1703 gsm322_sync_to_cell(cs);
1705 /* selected PLMN (manual) or any PLMN (auto) */
1706 switch (ms->settings.plmn_mode) {
1707 case PLMN_MODE_AUTO:
1708 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1709 /* PLMN becomes available */
1710 nmsg = gsm322_msgb_alloc(
1711 GSM322_EVENT_PLMN_AVAIL);
1714 gsm322_plmn_sendmsg(ms, nmsg);
1717 case PLMN_MODE_MANUAL:
1718 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1719 && gsm322_is_plmn_avail(cs, plmn->mcc,
1721 /* PLMN becomes available */
1722 nmsg = gsm322_msgb_alloc(
1723 GSM322_EVENT_PLMN_AVAIL);
1726 gsm322_plmn_sendmsg(ms, nmsg);
1731 /* set selected cell */
1733 cs->sel_arfcn = cs->arfcn;
1734 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1735 cs->sel_mcc = cs->si->mcc;
1736 cs->sel_mnc = cs->si->mnc;
1737 cs->sel_lac = cs->si->lac;
1738 cs->sel_id = cs->si->cell_id;
1740 /* tell CS process about available cell */
1741 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1742 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1745 /* unset selected cell */
1746 gsm322_unselect_cell(cs);
1748 /* tell CS process about no cell available */
1749 LOGP(DCS, LOGL_INFO, "No cell available.\n");
1750 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1754 gsm322_c_event(ms, nmsg);
1760 /* NOTE: We might already have system information from previous
1761 * scan. But we need recent informations, so we scan again!
1764 /* Tune to frequency for a while, to receive broadcasts. */
1765 cs->arfcn = weight & 1023;
1766 LOGP(DCS, LOGL_INFO, "Scanning frequency %d (rxlev %d).\n", cs->arfcn,
1767 cs->list[cs->arfcn].rxlev_db);
1769 gsm322_sync_to_cell(cs);
1770 // start_cs_timer(cs, ms->support.sync_to, 0);
1772 /* Allocate/clean system information. */
1773 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1774 if (cs->list[cs->arfcn].sysinfo)
1775 memset(cs->list[cs->arfcn].sysinfo, 0,
1776 sizeof(struct gsm48_sysinfo));
1778 cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
1779 struct gsm48_sysinfo);
1780 if (!cs->list[cs->arfcn].sysinfo)
1782 cs->si = cs->list[cs->arfcn].sysinfo;
1784 /* increase scan counter for each maximum scan range */
1785 if (gsm_sup_smax[j].max) {
1786 LOGP(DCS, LOGL_INFO, "%d frequencies left in band %d..%d\n",
1787 gsm_sup_smax[j].max - gsm_sup_smax[j].temp,
1788 gsm_sup_smax[j].start, gsm_sup_smax[j].end);
1789 gsm_sup_smax[j].temp++;
1795 /* check if cell is now suitable and allowable */
1796 static int gsm322_cs_store(struct osmocom_ms *ms)
1798 struct gsm322_cellsel *cs = &ms->cellsel;
1799 struct gsm48_sysinfo *s = cs->si;
1800 struct gsm322_plmn *plmn = &ms->plmn;
1804 if (cs->state != GSM322_C2_STORED_CELL_SEL
1805 && cs->state != GSM322_C1_NORMAL_CELL_SEL
1806 && cs->state != GSM322_C6_ANY_CELL_SEL
1807 && cs->state != GSM322_C4_NORMAL_CELL_RESEL
1808 && cs->state != GSM322_C8_ANY_CELL_RESEL
1809 && cs->state != GSM322_C5_CHOOSE_CELL
1810 && cs->state != GSM322_C9_CHOOSE_ANY_CELL
1811 && cs->state != GSM322_PLMN_SEARCH
1812 && cs->state != GSM322_HPLMN_SEARCH) {
1813 LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
1814 "(re-)selection, please fix!\n");
1819 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO;
1821 && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp &&
1822 cs->list[cs->arfcn].sysinfo->sp_cbq))
1823 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED;
1825 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED;
1828 cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db;
1829 cs->list[cs->arfcn].class_barr = s->class_barr;
1830 cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch;
1833 /* store selected network */
1836 cs->list[cs->arfcn].mcc = s->mcc;
1837 cs->list[cs->arfcn].mnc = s->mnc;
1838 cs->list[cs->arfcn].lac = s->lac;
1841 if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
1842 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD;
1844 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD;
1847 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev=%d "
1848 "mcc=%s mnc=%s lac=%04x)\n", cs->arfcn,
1849 cs->list[cs->arfcn].rxlev_db, gsm_print_mcc(s->mcc),
1850 gsm_print_mnc(s->mnc), s->lac);
1852 /* special case for PLMN search */
1853 if (cs->state == GSM322_PLMN_SEARCH)
1854 /* tune to next cell */
1855 return gsm322_cs_scan(ms);
1857 /* special case for HPLMN search */
1858 if (cs->state == GSM322_HPLMN_SEARCH) {
1859 struct gsm_subscriber *subscr = &ms->subscr;
1862 if (!gsm322_is_hplmn_avail(cs, subscr->imsi))
1863 /* tune to next cell */
1864 return gsm322_cs_scan(ms);
1866 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1867 LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n");
1870 gsm322_plmn_sendmsg(ms, nmsg);
1875 /* just see, if we search for any cell */
1876 if (cs->state == GSM322_C6_ANY_CELL_SEL
1877 || cs->state == GSM322_C8_ANY_CELL_RESEL
1878 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1881 found = gsm322_cs_select(ms, any);
1885 LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n");
1886 /* tune to next cell */
1887 return gsm322_cs_scan(ms);
1890 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1893 cs->si = cs->list[cs->arfcn].sysinfo;
1895 gsm322_sync_to_cell(cs);
1897 /* selected PLMN (manual) or any PLMN (auto) */
1898 switch (ms->settings.plmn_mode) {
1899 case PLMN_MODE_AUTO:
1900 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1901 /* PLMN becomes available */
1902 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1905 gsm322_plmn_sendmsg(ms, nmsg);
1908 case PLMN_MODE_MANUAL:
1909 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1910 && gsm322_is_plmn_avail(cs, plmn->mcc,
1912 /* PLMN becomes available */
1913 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1916 gsm322_plmn_sendmsg(ms, nmsg);
1921 /* set selected cell */
1923 cs->sel_arfcn = cs->arfcn;
1924 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1925 cs->sel_mcc = cs->si->mcc;
1926 cs->sel_mnc = cs->si->mnc;
1927 cs->sel_lac = cs->si->lac;
1928 cs->sel_id = cs->si->cell_id;
1930 /* tell CS process about available cell */
1931 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1932 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1935 gsm322_c_event(ms, nmsg);
1941 /* process system information when returing to idle mode */
1942 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
1944 struct gsm322_cellsel *cs = &ms->cellsel;
1945 struct gsm48_sysinfo *s = cs->si;
1946 struct gsm322_ba_list *ba = NULL;
1950 /* collect system information received during dedicated mode */
1952 && (!s->nb_ext_ind_si5
1953 || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
1954 || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
1955 && s->nb_ext_ind_si5bis))) {
1956 /* find or create ba list */
1957 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1959 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1964 llist_add_tail(&ba->entry, &cs->ba_list);
1966 /* update (add) ba list */
1967 memcpy(freq, ba->freq, sizeof(freq));
1968 for (i = 0; i <= 1023; i++) {
1969 if ((s->freq[i].mask & FREQ_TYPE_REP))
1970 freq[i >> 3] |= (1 << (i & 7));
1972 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
1973 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
1974 "%s, %s).\n", gsm_print_mcc(ba->mcc),
1975 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
1976 gsm_get_mnc(ba->mcc, ba->mnc));
1977 memcpy(ba->freq, freq, sizeof(freq));
1984 /* store BA whenever a system informations changes */
1985 static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
1986 struct gsm48_sysinfo *s)
1988 struct gsm322_ba_list *ba;
1992 /* find or create ba list */
1993 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1995 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2000 llist_add_tail(&ba->entry, &cs->ba_list);
2002 /* update ba list */
2003 memset(freq, 0, sizeof(freq));
2004 freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
2005 for (i = 0; i <= 1023; i++) {
2006 if ((s->freq[i].mask &
2007 (FREQ_TYPE_SERV | FREQ_TYPE_NCELL)))
2008 freq[i >> 3] |= (1 << (i & 7));
2010 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
2011 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
2012 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2013 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2014 gsm_get_mnc(ba->mcc, ba->mnc));
2015 memcpy(ba->freq, freq, sizeof(freq));
2021 /* process system information during camping on a cell */
2022 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2024 // struct gsm48_rrlayer *rr = &ms->rrlayer;
2025 struct gsm322_cellsel *cs = &ms->cellsel;
2026 struct gsm48_sysinfo *s = cs->si;
2027 struct gsm_subscriber *subscr = &ms->subscr;
2028 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2032 if (rr->state != GSM48_RR_ST_IDLE) {
2033 LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n");
2038 /* Store BA if we have full system info about cells and neigbor cells.
2039 * Depending on the extended bit in the channel description,
2040 * we require more or less system informations about neighbor cells
2044 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2045 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2046 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2047 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2050 && (!s->nb_ext_ind_si2
2051 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2052 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2053 && s->nb_ext_ind_si2bis)))
2054 gsm322_store_ba_list(cs, s);
2056 /* update sel_si, if all relevant system informations received */
2057 if (s->si1 && s->si2 && s->si3
2058 && (!s->nb_ext_ind_si2
2059 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2060 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2061 && s->nb_ext_ind_si2bis))) {
2063 LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is "
2065 memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
2066 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2070 /* check for barred cell */
2071 if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
2072 /* check if cell becomes barred */
2073 if (!subscr->acc_barr && s->cell_barr
2074 && !(cs->list[cs->arfcn].sysinfo
2075 && cs->list[cs->arfcn].sysinfo->sp
2076 && cs->list[cs->arfcn].sysinfo->sp_cbq)) {
2077 LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
2079 /* mark cell as unscanned */
2080 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2081 if (cs->list[cs->arfcn].sysinfo) {
2082 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2084 talloc_free(cs->list[cs->arfcn].sysinfo);
2085 cs->list[cs->arfcn].sysinfo = NULL;
2086 gsm322_unselect_cell(cs);
2088 /* trigger reselection without queueing,
2089 * because other sysinfo message may be queued
2092 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2095 gsm322_c_event(ms, nmsg);
2100 /* check if cell access becomes barred */
2101 if (!((subscr->acc_class & 0xfbff)
2102 & (s->class_barr ^ 0xffff))) {
2103 LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
2108 /* check if MCC, MNC, LAC, cell ID changes */
2109 if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc
2110 || cs->sel_lac != s->lac) {
2111 LOGP(DCS, LOGL_NOTICE, "Cell changes location area. "
2112 "This is not good!\n");
2115 if (cs->sel_id != s->cell_id) {
2116 LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. "
2117 "This is not good!\n");
2124 /* process system information during channel scanning */
2125 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2127 struct gsm322_cellsel *cs = &ms->cellsel;
2128 struct gsm48_sysinfo *s = cs->si;
2129 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2131 /* no sysinfo if we are not done with power scan */
2132 if (cs->powerscan) {
2133 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
2137 /* Store BA if we have full system info about cells and neigbor cells.
2138 * Depending on the extended bit in the channel description,
2139 * we require more or less system informations about neighbor cells
2143 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2144 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2145 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2146 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2149 && (!s->nb_ext_ind_si2
2150 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2151 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2152 && s->nb_ext_ind_si2bis)))
2153 gsm322_store_ba_list(cs, s);
2155 /* all relevant system informations received */
2156 if (s->si1 && s->si2 && s->si3
2157 && (!s->nb_ext_ind_si2
2158 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2159 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2160 && s->nb_ext_ind_si2bis))) {
2161 LOGP(DCS, LOGL_INFO, "Received relevant sysinfo.\n");
2165 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2167 /* store sysinfo and continue scan */
2168 return gsm322_cs_store(ms);
2171 /* wait for more sysinfo or timeout */
2175 static void gsm322_cs_timeout(void *arg)
2177 struct gsm322_cellsel *cs = arg;
2178 struct osmocom_ms *ms = cs->ms;
2180 LOGP(DCS, LOGL_INFO, "Cell selection failed.\n");
2182 /* if we have no lock, we retry */
2183 if (cs->ccch_state != GSM322_CCCH_ST_SYNC)
2184 LOGP(DCS, LOGL_INFO, "Sync timeout.\n");
2186 LOGP(DCS, LOGL_INFO, "Read timeout.\n");
2188 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev=%d)\n",
2189 cs->arfcn, cs->list[cs->arfcn].rxlev_db);
2191 /* remove system information */
2192 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2193 if (cs->list[cs->arfcn].sysinfo) {
2194 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", cs->arfcn);
2195 talloc_free(cs->list[cs->arfcn].sysinfo);
2196 cs->list[cs->arfcn].sysinfo = NULL;
2197 gsm322_unselect_cell(cs);
2200 /* tune to next cell */
2207 * power scan process
2210 /* search for block of unscanned frequencies and start scanning */
2211 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
2213 struct gsm322_cellsel *cs = &ms->cellsel;
2214 struct gsm_settings *set = &ms->settings;
2216 uint8_t mask, flags;
2220 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
2221 flags = GSM322_CS_FLAG_SUPPORT;
2223 /* in case of sticking to a cell, we only select it */
2225 LOGP(DCS, LOGL_FATAL, "Scanning power for sticked cell.\n");
2226 i = set->stick_arfcn;
2227 if ((cs->list[i].flags & mask) == flags)
2230 /* search for first frequency to scan */
2231 if (cs->state == GSM322_C2_STORED_CELL_SEL
2232 || cs->state == GSM322_C5_CHOOSE_CELL) {
2233 LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA "
2235 mask |= GSM322_CS_FLAG_BA;
2236 flags |= GSM322_CS_FLAG_BA;
2238 LOGP(DCS, LOGL_FATAL, "Scanning power for all "
2240 for (i = 0; i <= 1023; i++) {
2241 if ((cs->list[i].flags & mask) == flags) {
2248 /* if there is no more frequency, we can tune to that cell */
2252 /* stop power level scanning */
2255 /* check if not signal is found */
2256 for (i = 0; i <= 1023; i++) {
2257 if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2263 LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
2264 /* on normal cell selection, start over */
2265 if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
2266 for (i = 0; i <= 1023; i++) {
2267 /* clear flag that this was scanned */
2268 cs->list[i].flags &=
2269 ~(GSM322_CS_FLAG_POWER
2270 | GSM322_CS_FLAG_SIGNAL
2271 | GSM322_CS_FLAG_SYSINFO);
2272 if (cs->list[i].sysinfo) {
2273 LOGP(DCS, LOGL_INFO, "free "
2274 "sysinfo arfcn=%d\n",
2277 cs->list[i].sysinfo);
2278 cs->list[i].sysinfo = NULL;
2281 /* no cell selected */
2282 gsm322_unselect_cell(cs);
2285 /* on other cell selection, indicate "no cell found" */
2286 /* NOTE: PLMN search process handles it.
2287 * If not handled there, CS process gets indicated.
2288 * If we would continue to process CS, then we might get
2289 * our list of scanned cells disturbed.
2291 if (cs->state == GSM322_PLMN_SEARCH)
2292 nmsg = gsm322_msgb_alloc(
2293 GSM322_EVENT_PLMN_SEARCH_END);
2295 nmsg = gsm322_msgb_alloc(
2296 GSM322_EVENT_NO_CELL_FOUND);
2299 gsm322_plmn_sendmsg(ms, nmsg);
2301 /* if HPLMN search, select last frequency */
2302 if (cs->state == GSM322_HPLMN_SEARCH) {
2303 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2305 cs->arfcn = cs->sel_arfcn;
2306 LOGP(DCS, LOGL_INFO, "Tuning back to frequency "
2307 "%d (rxlev %d).\n", cs->arfcn,
2308 cs->list[cs->arfcn].rxlev_db);
2310 gsm322_sync_to_cell(cs);
2311 // start_cs_timer(cs, ms->support.sync_to, 0);
2314 new_c_state(cs, GSM322_C0_NULL);
2318 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
2319 cs->scan_state = 0xffffffff; /* higher than high */
2320 /* clear counter of scanned frequencies of each range */
2321 for (i = 0; gsm_sup_smax[i].max; i++)
2322 gsm_sup_smax[i].temp = 0;
2323 return gsm322_cs_scan(ms);
2326 /* search last frequency to scan (en block) */
2329 for (i = s + 1; i <= 1023; i++) {
2330 if ((cs->list[i].flags & mask) == flags)
2337 LOGP(DCS, LOGL_INFO, "Scanning frequencies. (%d..%d)\n", s, e);
2339 /* start scan on radio interface */
2341 //#warning TESTING!!!!
2343 return l1ctl_tx_pm_req_range(ms, s, e);
2346 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
2347 void *handler_data, void *signal_data)
2349 struct osmocom_ms *ms;
2350 struct gsm322_cellsel *cs;
2351 struct osmobb_meas_res *mr;
2355 if (subsys != SS_L1CTL)
2359 case S_L1CTL_PM_RES:
2365 i = mr->band_arfcn & 1023;
2366 rxlev_db = mr->rx_lev - 110;
2367 cs->list[i].rxlev_db = rxlev_db;
2368 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2369 /* if minimum level is reached or if we stick to a cell */
2370 if (rxlev_db >= ms->support.min_rxlev_db
2371 || ms->settings.stick) {
2372 cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2373 LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2374 "rxlev %d)\n", i, cs->list[i].rxlev_db);
2377 case S_L1CTL_PM_DONE:
2378 LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
2383 gsm322_cs_powerscan(ms);
2385 case S_L1CTL_FBSB_RESP:
2388 if (cs->ccch_state == GSM322_CCCH_ST_INIT) {
2389 LOGP(DCS, LOGL_INFO, "Channel synched.\n");
2390 cs->ccch_state = GSM322_CCCH_ST_SYNC;
2394 /* in dedicated mode */
2395 if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
2396 return gsm48_rr_tx_rand_acc(ms, NULL);
2399 /* set timer for reading BCCH */
2400 if (cs->state == GSM322_C2_STORED_CELL_SEL
2401 || cs->state == GSM322_C1_NORMAL_CELL_SEL
2402 || cs->state == GSM322_C6_ANY_CELL_SEL
2403 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2404 || cs->state == GSM322_C8_ANY_CELL_RESEL
2405 || cs->state == GSM322_C5_CHOOSE_CELL
2406 || cs->state == GSM322_C9_CHOOSE_ANY_CELL
2407 || cs->state == GSM322_PLMN_SEARCH
2408 || cs->state == GSM322_HPLMN_SEARCH)
2409 start_cs_timer(cs, ms->support.scan_to, 0);
2410 // TODO: timer depends on BCCH config
2413 case S_L1CTL_FBSB_ERR:
2417 gsm322_sync_to_cell(cs);
2419 LOGP(DCS, LOGL_INFO, "Channel sync error, try again.\n");
2422 LOGP(DCS, LOGL_INFO, "Channel sync error.\n");
2430 gsm322_cs_timeout(cs);
2439 static void gsm322_cs_loss(void *arg)
2441 struct gsm322_cellsel *cs = arg;
2442 struct osmocom_ms *ms = cs->ms;
2443 struct gsm48_rrlayer *rr = &ms->rrlayer;
2445 LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n");
2446 if (cs->state == GSM322_C3_CAMPED_NORMALLY
2447 || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
2448 if (rr->state == GSM48_RR_ST_IDLE) {
2451 LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n");
2453 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2456 gsm322_c_event(ms, nmsg);
2459 LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n");
2461 /* be shure that nothing else is done after here
2462 * because the function call above may cause
2463 * to return from idle state and trigger cell re-sel.
2472 * handler for cell selection process
2475 /* start PLMN search */
2476 static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg)
2478 struct gsm322_cellsel *cs = &ms->cellsel;
2481 new_c_state(cs, GSM322_PLMN_SEARCH);
2483 /* mark all frequencies except our own BA to be scanned */
2484 for (i = 0; i <= 1023; i++) {
2485 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2486 | GSM322_CS_FLAG_SIGNAL
2487 | GSM322_CS_FLAG_SYSINFO);
2488 if (cs->list[i].sysinfo) {
2489 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2490 talloc_free(cs->list[i].sysinfo);
2491 cs->list[i].sysinfo = NULL;
2492 gsm322_unselect_cell(cs);
2496 /* unset selected cell */
2497 gsm322_unselect_cell(cs);
2499 /* start power scan */
2500 return gsm322_cs_powerscan(ms);
2503 /* start HPLMN search */
2504 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2506 struct gsm322_cellsel *cs = &ms->cellsel;
2509 new_c_state(cs, GSM322_HPLMN_SEARCH);
2511 /* mark all frequencies except our own BA to be scanned */
2512 for (i = 0; i <= 1023; i++) {
2513 if (i != cs->sel_arfcn
2514 && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2515 && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2516 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2517 | GSM322_CS_FLAG_SIGNAL
2518 | GSM322_CS_FLAG_SYSINFO);
2519 if (cs->list[i].sysinfo) {
2520 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2522 talloc_free(cs->list[i].sysinfo);
2523 cs->list[i].sysinfo = NULL;
2528 /* no cell selected */
2529 gsm322_unselect_cell(cs);
2531 /* start power scan */
2532 return gsm322_cs_powerscan(ms);
2535 /* start stored cell selection */
2536 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2538 struct gsm322_cellsel *cs = &ms->cellsel;
2541 new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2543 /* flag all frequencies that are in current band allocation */
2544 for (i = 0; i <= 1023; i++) {
2545 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2546 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2548 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2551 /* unset selected cell */
2552 gsm322_unselect_cell(cs);
2554 /* start power scan */
2555 return gsm322_cs_powerscan(ms);
2558 /* start noraml cell selection */
2559 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2561 struct gsm322_cellsel *cs = &ms->cellsel;
2564 /* except for stored cell selection state, we weed to rescan ?? */
2565 if (cs->state != GSM322_C2_STORED_CELL_SEL) {
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",
2573 talloc_free(cs->list[i].sysinfo);
2574 cs->list[i].sysinfo = NULL;
2579 new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2581 /* unset selected cell */
2582 gsm322_unselect_cell(cs);
2584 /* start power scan */
2585 return gsm322_cs_powerscan(ms);
2588 /* start any cell selection */
2589 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2591 struct gsm322_cellsel *cs = &ms->cellsel;
2593 /* in case we already tried any cell (re-)selection, power scan again */
2594 if (cs->state == GSM322_C0_NULL
2595 || cs->state == GSM322_C6_ANY_CELL_SEL
2596 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2599 for (i = 0; i <= 1023; i++) {
2600 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2601 | GSM322_CS_FLAG_SIGNAL
2602 | GSM322_CS_FLAG_SYSINFO);
2603 if (cs->list[i].sysinfo) {
2604 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2606 talloc_free(cs->list[i].sysinfo);
2607 cs->list[i].sysinfo = NULL;
2611 /* after re-selection, indicate no cell found */
2612 if (cs->state == GSM322_C6_ANY_CELL_SEL
2613 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2616 /* tell that we have no cell found */
2617 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2620 gsm48_mmevent_msg(ms, nmsg);
2623 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2626 cs->mcc = cs->mnc = 0;
2628 /* unset selected cell */
2629 gsm322_unselect_cell(cs);
2631 /* start power scan */
2632 return gsm322_cs_powerscan(ms);
2635 /* start noraml cell re-selection */
2636 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2638 struct gsm322_cellsel *cs = &ms->cellsel;
2640 new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2642 /* NOTE: We keep our scan info we have so far.
2643 * This may cause a skip in power scan. */
2645 /* start power scan */
2646 return gsm322_cs_powerscan(ms);
2649 /* start any cell re-selection */
2650 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2652 struct gsm322_cellsel *cs = &ms->cellsel;
2654 new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2656 /* NOTE: We keep our scan info we have so far.
2657 * This may cause a skip in power scan. */
2659 /* start power scan */
2660 return gsm322_cs_powerscan(ms);
2663 /* create temporary ba range with given frequency ranges */
2664 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2665 uint32_t *range, uint8_t ranges)
2667 static struct gsm322_ba_list ba;
2668 uint16_t lower, higher;
2670 memset(&ba, 0, sizeof(ba));
2673 lower = *range & 1023;
2674 higher = (*range >> 16) & 1023;
2676 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2679 ba.freq[lower >> 3] |= 1 << (lower & 7);
2680 if (lower == higher)
2682 lower = (lower + 1) & 1023;
2689 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2690 static int gsm322_cs_choose(struct osmocom_ms *ms)
2692 struct gsm322_cellsel *cs = &ms->cellsel;
2693 struct gsm48_rrlayer *rr = &ms->rrlayer;
2694 struct gsm322_ba_list *ba = NULL;
2697 /* NOTE: The call to this function is synchron to RR layer, so
2698 * we may access the BA range there.
2701 ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges);
2703 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2704 /* get and update BA of last received sysinfo 5* */
2705 ba = gsm322_cs_sysinfo_sacch(ms);
2707 LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored "
2709 ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2717 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2719 /* tell CS to start over */
2720 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2723 gsm322_c_event(ms, nmsg);
2729 /* flag all frequencies that are in current band allocation */
2730 for (i = 0; i <= 1023; i++) {
2731 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2732 if ((ba->freq[i >> 3] & (1 << (i & 7)))) {
2733 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2735 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2738 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2739 | GSM322_CS_FLAG_SIGNAL
2740 | GSM322_CS_FLAG_SYSINFO);
2741 if (cs->list[i].sysinfo) {
2742 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2743 talloc_free(cs->list[i].sysinfo);
2744 cs->list[i].sysinfo = NULL;
2748 /* unset selected cell */
2749 gsm322_unselect_cell(cs);
2751 /* start power scan */
2752 return gsm322_cs_powerscan(ms);
2755 /* start 'Choose cell' after returning to idle mode */
2756 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2758 struct gsm322_cellsel *cs = &ms->cellsel;
2760 new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2762 return gsm322_cs_choose(ms);
2765 /* start 'Choose any cell' after returning to idle mode */
2766 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2768 struct gsm322_cellsel *cs = &ms->cellsel;
2770 new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2772 return gsm322_cs_choose(ms);
2775 /* a new PLMN is selected by PLMN search process */
2776 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2778 struct gsm322_cellsel *cs = &ms->cellsel;
2779 struct gsm322_plmn *plmn = &ms->plmn;
2780 struct gsm322_ba_list *ba;
2782 cs->mcc = plmn->mcc;
2783 cs->mnc = plmn->mnc;
2785 LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s "
2786 "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc),
2787 gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc),
2788 gsm_get_mnc(cs->mcc, cs->mnc));
2790 /* search for BA list */
2791 ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2794 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2795 return gsm322_c_stored_cell_sel(ms, ba);
2797 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2798 return gsm322_c_normal_cell_sel(ms, msg);
2802 /* a suitable cell was found, so we camp normally */
2803 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2805 struct gsm322_cellsel *cs = &ms->cellsel;
2808 LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s "
2809 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2810 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2811 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2813 /* tell that we have selected a (new) cell */
2814 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2817 gsm48_mmevent_msg(ms, nmsg);
2819 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2824 /* a not suitable cell was found, so we camp on any cell */
2825 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2827 struct gsm322_cellsel *cs = &ms->cellsel;
2830 LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s "
2831 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2832 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2833 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2836 /* tell that we have selected a (new) cell */
2837 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2840 gsm48_mmevent_msg(ms, nmsg);
2842 new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2847 /* go connected mode */
2848 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2850 struct gsm322_cellsel *cs = &ms->cellsel;
2852 /* check for error */
2855 cs->arfcn = cs->sel_arfcn;
2857 /* be sure to go to current camping frequency on return */
2858 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2860 gsm322_sync_to_cell(cs);
2861 cs->si = cs->list[cs->arfcn].sysinfo;
2862 //#warning TESTING!!!!
2868 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2870 struct gsm322_cellsel *cs = &ms->cellsel;
2872 /* check for error */
2875 cs->arfcn = cs->sel_arfcn;
2877 /* be sure to go to current camping frequency on return */
2878 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2880 gsm322_sync_to_cell(cs);
2881 cs->si = cs->list[cs->arfcn].sysinfo;
2887 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2889 struct gsm_subscriber *subscr = &ms->subscr;
2891 /* if no SIM is is MS */
2892 if (!subscr->sim_valid) {
2893 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2894 return gsm322_c_any_cell_sel(ms, msg);
2895 LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2898 /* stay in NULL state until PLMN is selected */
2907 /* state machine for automatic PLMN selection events */
2908 static struct plmnastatelist {
2911 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2912 } plmnastatelist[] = {
2913 {SBIT(GSM322_A0_NULL),
2914 GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2916 {SBIT(GSM322_A0_NULL),
2917 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn},
2920 GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
2922 {SBIT(GSM322_A6_NO_SIM),
2923 GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
2926 GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
2929 GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
2932 GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
2934 {SBIT(GSM322_A1_TRYING_RPLMN),
2935 GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
2937 {SBIT(GSM322_A1_TRYING_RPLMN),
2938 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn},
2940 {SBIT(GSM322_A1_TRYING_RPLMN),
2941 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
2943 {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
2944 GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
2946 {SBIT(GSM322_A2_ON_PLMN),
2947 GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
2949 {SBIT(GSM322_A2_ON_PLMN),
2950 GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start},
2952 {SBIT(GSM322_A2_ON_PLMN),
2953 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
2955 {SBIT(GSM322_A2_ON_PLMN),
2956 GSM322_EVENT_USER_RESEL, gsm322_a_user_resel},
2958 {SBIT(GSM322_A3_TRYING_PLMN),
2959 GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
2961 {SBIT(GSM322_A3_TRYING_PLMN),
2962 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn},
2964 {SBIT(GSM322_A3_TRYING_PLMN),
2965 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
2967 {SBIT(GSM322_A5_HPLMN_SEARCH),
2968 GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn},
2970 {SBIT(GSM322_A5_HPLMN_SEARCH),
2971 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn},
2973 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
2974 GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
2977 GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
2980 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
2983 #define PLMNASLLEN \
2984 (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
2986 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
2988 struct gsm322_plmn *plmn = &ms->plmn;
2989 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2990 int msg_type = gm->msg_type;
2994 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
2995 "selection in state '%s'\n", ms->name, get_event_name(msg_type),
2996 plmn_a_state_names[plmn->state]);
2997 /* find function for current state and message */
2998 for (i = 0; i < PLMNASLLEN; i++)
2999 if ((msg_type == plmnastatelist[i].type)
3000 && ((1 << plmn->state) & plmnastatelist[i].states))
3002 if (i == PLMNASLLEN) {
3003 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3007 rc = plmnastatelist[i].rout(ms, msg);
3012 /* state machine for manual PLMN selection events */
3013 static struct plmnmstatelist {
3016 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3017 } plmnmstatelist[] = {
3018 {SBIT(GSM322_M0_NULL),
3019 GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
3021 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) |
3022 SBIT(GSM322_M2_ON_PLMN),
3023 GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns},
3026 GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
3028 {SBIT(GSM322_M5_NO_SIM),
3029 GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
3032 GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
3035 GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
3037 {SBIT(GSM322_M1_TRYING_RPLMN),
3038 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3040 {SBIT(GSM322_M1_TRYING_RPLMN),
3041 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3043 {SBIT(GSM322_M1_TRYING_RPLMN),
3044 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3046 {SBIT(GSM322_M1_TRYING_RPLMN),
3047 GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
3049 {SBIT(GSM322_M2_ON_PLMN),
3050 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3052 {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
3053 SBIT(GSM322_M4_TRYING_PLMN),
3054 GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
3056 {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN),
3057 GSM322_EVENT_USER_RESEL, gsm322_m_user_resel},
3059 {SBIT(GSM322_M3_NOT_ON_PLMN),
3060 GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
3062 {SBIT(GSM322_M3_NOT_ON_PLMN),
3063 GSM322_EVENT_CHOSE_PLMN, gsm322_m_choose_plmn},
3065 {SBIT(GSM322_M4_TRYING_PLMN),
3066 GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
3068 {SBIT(GSM322_M4_TRYING_PLMN),
3069 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3071 {SBIT(GSM322_M4_TRYING_PLMN),
3072 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3074 {SBIT(GSM322_M4_TRYING_PLMN),
3075 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3078 GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
3081 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3084 #define PLMNMSLLEN \
3085 (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
3087 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
3089 struct gsm322_plmn *plmn = &ms->plmn;
3090 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3091 int msg_type = gm->msg_type;
3095 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
3096 "in state '%s'\n", ms->name, get_event_name(msg_type),
3097 plmn_m_state_names[plmn->state]);
3098 /* find function for current state and message */
3099 for (i = 0; i < PLMNMSLLEN; i++)
3100 if ((msg_type == plmnmstatelist[i].type)
3101 && ((1 << plmn->state) & plmnmstatelist[i].states))
3103 if (i == PLMNMSLLEN) {
3104 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3108 rc = plmnmstatelist[i].rout(ms, msg);
3113 /* dequeue GSM 03.22 PLMN events */
3114 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
3116 struct gsm322_plmn *plmn = &ms->plmn;
3120 while ((msg = msgb_dequeue(&plmn->event_queue))) {
3121 /* send event to PLMN select process */
3122 if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
3123 gsm322_a_event(ms, msg);
3125 gsm322_m_event(ms, msg);
3127 work = 1; /* work done */
3133 /* state machine for channel selection events */
3134 static struct cellselstatelist {
3137 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3138 } cellselstatelist[] = {
3140 GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
3143 GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
3146 GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
3149 GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search},
3151 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3152 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
3153 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
3155 {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3156 SBIT(GSM322_C8_ANY_CELL_RESEL),
3157 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
3159 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3160 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3161 SBIT(GSM322_C0_NULL),
3162 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
3164 {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3165 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
3166 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
3168 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3169 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
3171 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3172 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
3174 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3175 GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
3177 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3178 GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
3180 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3181 GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
3183 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3184 GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
3186 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3187 GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
3189 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3190 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3191 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3192 SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH),
3193 GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
3195 {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
3196 GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
3198 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3199 GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search},
3202 #define CELLSELSLLEN \
3203 (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
3205 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
3207 struct gsm322_cellsel *cs = &ms->cellsel;
3208 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3209 int msg_type = gm->msg_type;
3213 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
3214 "'%s'\n", ms->name, get_event_name(msg_type),
3215 cs_state_names[cs->state]);
3216 /* find function for current state and message */
3217 for (i = 0; i < CELLSELSLLEN; i++)
3218 if ((msg_type == cellselstatelist[i].type)
3219 && ((1 << cs->state) & cellselstatelist[i].states))
3221 if (i == CELLSELSLLEN) {
3222 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
3226 rc = cellselstatelist[i].rout(ms, msg);
3231 /* dequeue GSM 03.22 cell selection events */
3232 int gsm322_cs_dequeue(struct osmocom_ms *ms)
3234 struct gsm322_cellsel *cs = &ms->cellsel;
3238 while ((msg = msgb_dequeue(&cs->event_queue))) {
3239 /* send event to cell selection process */
3240 gsm322_c_event(ms, msg);
3242 work = 1; /* work done */
3252 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
3254 struct gsm322_plmn *plmn = &ms->plmn;
3255 struct gsm322_plmn_list *temp;
3257 printf("MCC |MNC |allowed|rx-lev\n");
3258 printf("-------+-------+-------+-------\n");
3259 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
3260 printf("%s |%s%s |%s |%d\n", gsm_print_mcc(temp->mcc),
3261 gsm_print_mnc(temp->mnc),
3262 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3263 (temp->cause) ? "no ":"yes", temp->rxlev_db);
3269 int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
3270 void (*print)(void *, const char *, ...), void *priv)
3273 struct gsm48_sysinfo *s;
3275 print(priv, "arfcn |rx-lev |MCC |MNC |LAC |cell ID|forb.LA|"
3276 "prio |min-db |max-pwr\n");
3277 print(priv, "-------+-------+-------+-------+-------+-------+-------+"
3278 "-------+-------+-------\n");
3279 for (i = 0; i <= 1023; i++) {
3280 s = cs->list[i].sysinfo;
3281 if (!s || !(cs->list[i].flags & flags))
3283 print(priv, "%4d |%4d |", i, cs->list[i].rxlev_db);
3284 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
3285 print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc),
3286 gsm_print_mnc(s->mnc),
3287 ((s->mnc & 0x00f) == 0x00f) ? " ":"");
3288 print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id);
3289 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
3290 print(priv, "yes |");
3292 print(priv, "no |");
3293 if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
3294 print(priv, "barred |");
3296 if (cs->list[i].sysinfo->cell_barr)
3297 print(priv, "low |");
3299 print(priv, "normal |");
3301 print(priv, "%4d |%4d\n", s->rxlev_acc_min_db,
3302 s->ms_txpwr_max_cch);
3304 print(priv, "n/a |n/a |n/a |n/a |n/a |"
3312 int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
3313 void (*print)(void *, const char *, ...), void *priv)
3315 struct gsm322_plmn *plmn = &ms->plmn;
3316 struct gsm322_la_list *temp;
3318 print(priv, "MCC |MNC |LAC |cause\n");
3319 print(priv, "-------+-------+-------+-------\n");
3320 llist_for_each_entry(temp, &plmn->forbidden_la, entry)
3321 print(priv, "%s |%s%s |0x%04x |#%d\n",
3322 gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
3323 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3324 temp->lac, temp->cause);
3329 int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
3330 void (*print)(void *, const char *, ...), void *priv)
3332 struct gsm322_ba_list *ba;
3335 llist_for_each_entry(ba, &cs->ba_list, entry) {
3336 if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc))
3338 print(priv, "Band Allocation of network: MCC %s MNC %s "
3339 "(%s, %s)\n", gsm_print_mcc(ba->mcc),
3340 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3341 gsm_get_mnc(ba->mcc, ba->mnc));
3342 for (i = 0; i <= 1023; i++) {
3343 if ((ba->freq[i >> 3] & (1 << (i & 7))))
3344 print(priv, " %d", i);
3356 int gsm322_init(struct osmocom_ms *ms)
3358 struct gsm322_plmn *plmn = &ms->plmn;
3359 struct gsm322_cellsel *cs = &ms->cellsel;
3363 struct gsm322_ba_list *ba;
3366 LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
3367 LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
3369 memset(plmn, 0, sizeof(*plmn));
3370 memset(cs, 0, sizeof(*cs));
3374 /* set initial state */
3377 ms->settings.plmn_mode = PLMN_MODE_AUTO;
3380 INIT_LLIST_HEAD(&plmn->event_queue);
3381 INIT_LLIST_HEAD(&cs->event_queue);
3382 INIT_LLIST_HEAD(&plmn->sorted_plmn);
3383 INIT_LLIST_HEAD(&plmn->forbidden_la);
3384 INIT_LLIST_HEAD(&cs->ba_list);
3386 /* set supported frequencies in cell selection list */
3387 for (i = 0; i <= 1023; i++)
3388 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
3389 cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
3392 sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3393 fp = fopen(filename, "r");
3398 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
3401 rc = fread(buf, 4, 1, fp);
3406 ba->mcc = (buf[0] << 8) | buf[1];
3407 ba->mnc = (buf[2] << 8) | buf[3];
3408 rc = fread(ba->freq, sizeof(ba->freq), 1, fp);
3413 llist_add_tail(&ba->entry, &cs->ba_list);
3414 LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s "
3415 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3416 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3417 gsm_get_mnc(ba->mcc, ba->mnc));
3421 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
3423 register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3428 int gsm322_exit(struct osmocom_ms *ms)
3430 struct gsm322_plmn *plmn = &ms->plmn;
3431 struct gsm322_cellsel *cs = &ms->cellsel;
3432 struct llist_head *lh, *lh2;
3436 struct gsm322_ba_list *ba;
3440 LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
3441 LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
3443 unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3445 /* stop cell selection process (if any) */
3446 new_c_state(cs, GSM322_C0_NULL);
3450 stop_plmn_timer(plmn);
3453 for (i = 0; i <= 1023; i++) {
3454 if (cs->list[i].sysinfo) {
3455 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
3456 talloc_free(cs->list[i].sysinfo);
3457 cs->list[i].sysinfo = NULL;
3459 cs->list[i].flags = 0;
3463 sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3464 fp = fopen(filename, "w");
3468 llist_for_each_entry(ba, &cs->ba_list, entry) {
3469 buf[0] = ba->mcc >> 8;
3470 buf[1] = ba->mcc & 0xff;
3471 buf[2] = ba->mnc >> 8;
3472 buf[3] = ba->mnc & 0xff;
3473 rc = fwrite(buf, 4, 1, fp);
3474 rc = fwrite(ba->freq, sizeof(ba->freq), 1, fp);
3475 LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s "
3476 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3477 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3478 gsm_get_mnc(ba->mcc, ba->mnc));
3482 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
3485 while ((msg = msgb_dequeue(&plmn->event_queue)))
3487 while ((msg = msgb_dequeue(&cs->event_queue)))
3489 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
3493 llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
3497 llist_for_each_safe(lh, lh2, &cs->ba_list) {