2 * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include <osmocore/msgb.h>
29 #include <osmocore/talloc.h>
30 #include <osmocore/utils.h>
31 #include <osmocore/gsm48.h>
32 #include <osmocore/signal.h>
34 #include <osmocom/logging.h>
35 #include <osmocom/l1ctl.h>
36 #include <osmocom/file.h>
37 #include <osmocom/osmocom_data.h>
38 #include <osmocom/networks.h>
39 #include <osmocom/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);
46 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg);
55 /* Cell selection process
57 * The process depends on states and events (finites state machine).
59 * During states of cell selection or cell re-selection, the search for a cell
60 * is performed in two steps:
62 * 1. Measurement of received level of all relevant frequencies (rx-lev)
64 * 2. Receive system information messages of all relevant frequencies
66 * During this process, the results are stored in a list of all frequencies.
67 * This list is checked whenever a cell is selected. It depends on the results
68 * if the cell is 'suitable' and 'allowable' to 'camp' on.
70 * This list is also used to generate a list of available networks.
74 * - cs->list[0..1023].xxx for each cell, where
75 * - flags and rxlev_db are used to store outcome of cell scanning process
76 * - sysinfo pointing to sysinfo memory, allocated temporarily
77 * - cs->selected and cs->sel_* states of the current / last selected cell.
80 * There is a special state: GSM322_PLMN_SEARCH
81 * It is used to search for all cells, to find the HPLMN. This is triggered
82 * by a timer. Also it is used before selecting PLMN from list.
86 /* PLMN selection process
88 * The PLMN (Public Land Mobile Network = Operator's Network) has two different
95 * The process depends on states and events (finites state machine).
99 /* File format of BA list:
104 * where frequency 0 is bit 0 of first byte
106 * If not end-of-file, the next BA list is stored.
111 * * subscr->plmn_list
113 * The "PLMN Selector list" stores prefered networks to select during PLMN
114 * search process. This list is also stored in the SIM.
118 * The "forbidden PLMNs" list stores all networks that rejected us. The stored
119 * network will not be used when searching PLMN automatically. This list is
120 * also stored din the SIM.
122 * * plmn->forbidden_la
124 * The "forbidden LAs for roaming" list stores all location areas where roaming
129 * This list stores measurements and cell informations during cell selection
130 * process. It can be used to speed up repeated cell selection.
134 * This list stores a map of frequencies used for a PLMN. If this lists exists
135 * for a PLMN, it helps to speedup cell scan process.
137 * * plmn->sorted_plmn
139 * This list is generated whenever a PLMN search is started and a list of PLMNs
140 * is required. It consists of home PLMN, PLMN Selector list, and PLMNs found
141 * during scan process.
148 static const struct value_string gsm322_event_names[] = {
149 { GSM322_EVENT_SWITCH_ON, "EVENT_SWITCH_ON" },
150 { GSM322_EVENT_SWITCH_OFF, "EVENT_SWITCH_OFF" },
151 { GSM322_EVENT_SIM_INSERT, "EVENT_SIM_INSERT" },
152 { GSM322_EVENT_SIM_REMOVE, "EVENT_SIM_REMOVE" },
153 { GSM322_EVENT_REG_FAILED, "EVENT_REG_FAILED" },
154 { GSM322_EVENT_ROAMING_NA, "EVENT_ROAMING_NA" },
155 { GSM322_EVENT_INVALID_SIM, "EVENT_INVALID_SIM" },
156 { GSM322_EVENT_REG_SUCCESS, "EVENT_REG_SUCCESS" },
157 { GSM322_EVENT_NEW_PLMN, "EVENT_NEW_PLMN" },
158 { GSM322_EVENT_ON_PLMN, "EVENT_ON_PLMN" },
159 { GSM322_EVENT_PLMN_SEARCH_START,"EVENT_PLMN_SEARCH_START" },
160 { GSM322_EVENT_PLMN_SEARCH_END, "EVENT_PLMN_SEARCH_END" },
161 { GSM322_EVENT_USER_RESEL, "EVENT_USER_RESEL" },
162 { GSM322_EVENT_PLMN_AVAIL, "EVENT_PLMN_AVAIL" },
163 { GSM322_EVENT_CHOSE_PLMN, "EVENT_CHOSE_PLMN" },
164 { GSM322_EVENT_SEL_MANUAL, "EVENT_SEL_MANUAL" },
165 { GSM322_EVENT_SEL_AUTO, "EVENT_SEL_AUTO" },
166 { GSM322_EVENT_CELL_FOUND, "EVENT_CELL_FOUND" },
167 { GSM322_EVENT_NO_CELL_FOUND, "EVENT_NO_CELL_FOUND" },
168 { GSM322_EVENT_LEAVE_IDLE, "EVENT_LEAVE_IDLE" },
169 { GSM322_EVENT_RET_IDLE, "EVENT_RET_IDLE" },
170 { GSM322_EVENT_CELL_RESEL, "EVENT_CELL_RESEL" },
171 { GSM322_EVENT_SYSINFO, "EVENT_SYSINFO" },
172 { GSM322_EVENT_HPLMN_SEARCH, "EVENT_HPLMN_SEARCH" },
176 const char *get_event_name(int value)
178 return get_value_string(gsm322_event_names, value);
182 /* allocate a 03.22 event message */
183 struct msgb *gsm322_msgb_alloc(int msg_type)
186 struct gsm322_msg *gm;
188 msg = msgb_alloc_headroom(sizeof(*gm), 0, "GSM 03.22 event");
192 gm = (struct gsm322_msg *)msgb_put(msg, sizeof(*gm));
193 gm->msg_type = msg_type;
198 /* queue PLMN selection message */
199 int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
201 struct gsm322_plmn *plmn = &ms->plmn;
203 msgb_enqueue(&plmn->event_queue, msg);
208 /* queue cell selection message */
209 int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
211 struct gsm322_cellsel *cs = &ms->cellsel;
213 msgb_enqueue(&cs->event_queue, msg);
222 static int gsm322_sync_to_cell(struct gsm322_cellsel *cs)
224 struct osmocom_ms *ms = cs->ms;
225 struct gsm48_sysinfo *s = cs->si;
227 cs->ccch_state = GSM322_CCCH_ST_INIT;
229 if (s->ccch_conf == 1) {
230 LOGP(DCS, LOGL_INFO, "Sysinfo, ccch mode COMB\n");
231 cs->ccch_mode = CCCH_MODE_COMBINED;
233 LOGP(DCS, LOGL_INFO, "Sysinfo, ccch mode NON-COMB\n");
234 cs->ccch_mode = CCCH_MODE_NON_COMBINED;
237 LOGP(DCS, LOGL_INFO, "No sysinfo, ccch mode NONE\n");
238 cs->ccch_mode = CCCH_MODE_NONE;
240 // printf("s->ccch_conf %d\n", cs->si->ccch_conf);
242 l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
243 return l1ctl_tx_fbsb_req(ms, cs->arfcn,
244 L1CTL_FBSB_F_FB01SB, 100, 0,
248 static void gsm322_unselect_cell(struct gsm322_cellsel *cs)
252 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
253 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
256 /* print to DCS logging */
257 static void print_dcs(void *priv, const char *fmt, ...)
263 vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
264 buffer[sizeof(buffer) - 1] = '\0';
268 // LOGP(DCS, LOGL_INFO, "%s", buffer);
269 printf("%s", buffer);
272 /* del forbidden LA */
273 int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
274 uint16_t mnc, uint16_t lac)
276 struct gsm322_plmn *plmn = &ms->plmn;
277 struct gsm322_la_list *la;
279 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
280 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
281 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
282 "LAs (mcc=%s, mnc=%s, lac=%04x)\n",
283 gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac);
284 llist_del(&la->entry);
293 /* add forbidden LA */
294 int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
295 uint16_t mnc, uint16_t lac, uint8_t cause)
297 struct gsm322_plmn *plmn = &ms->plmn;
298 struct gsm322_la_list *la;
300 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
301 "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc),
302 gsm_print_mnc(mnc), lac);
303 la = talloc_zero(l23_ctx, struct gsm322_la_list);
310 llist_add_tail(&la->entry, &plmn->forbidden_la);
315 /* search forbidden LA */
316 int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
319 struct gsm322_plmn *plmn = &ms->plmn;
320 struct gsm322_la_list *la;
322 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
323 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
330 /* search for PLMN in all BA lists */
331 static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
332 uint16_t mcc, uint16_t mnc)
334 struct gsm322_ba_list *ba, *ba_found = NULL;
336 /* search for BA list */
337 llist_for_each_entry(ba, &cs->ba_list, entry) {
348 /* search available PLMN */
349 int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
353 for (i = 0; i <= 1023; i++) {
354 if (cs->list[i].sysinfo
355 && cs->list[i].sysinfo->mcc == mcc
356 && cs->list[i].sysinfo->mnc == mnc)
363 /* search available HPLMN */
364 int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi)
368 for (i = 0; i <= 1023; i++) {
369 if (cs->list[i].sysinfo
370 && gsm_match_mnc(cs->list[i].sysinfo->mcc,
371 cs->list[i].sysinfo->mnc, imsi))
378 /* del forbidden LA */
383 /*plmn search timer event */
384 static void plmn_timer_timeout(void *arg)
386 struct gsm322_plmn *plmn = arg;
389 LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
391 /* indicate PLMN selection T timeout */
392 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
395 gsm322_plmn_sendmsg(plmn->ms, nmsg);
398 /* start plmn search timer */
399 static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
401 LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
403 plmn->timer.cb = plmn_timer_timeout;
404 plmn->timer.data = plmn;
405 bsc_schedule_timer(&plmn->timer, secs, 0);
408 /* stop plmn search timer */
409 static void stop_plmn_timer(struct gsm322_plmn *plmn)
411 if (bsc_timer_pending(&plmn->timer)) {
412 LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
413 bsc_del_timer(&plmn->timer);
417 /* start cell selection timer */
418 void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
420 LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec);
421 cs->timer.cb = gsm322_cs_timeout;
423 bsc_schedule_timer(&cs->timer, sec, micro);
426 /* start loss timer */
427 void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro)
430 cs->timer.cb = gsm322_cs_loss;
432 if (bsc_timer_pending(&cs->timer)) {
433 struct timeval current_time;
434 unsigned long long currentTime;
436 gettimeofday(¤t_time, NULL);
437 currentTime = current_time.tv_sec * 1000000LL
438 + current_time.tv_usec;
439 currentTime += sec * 1000000LL + micro;
440 cs->timer.timeout.tv_sec = currentTime / 1000000LL;
441 cs->timer.timeout.tv_usec = currentTime % 1000000LL;
446 LOGP(DCS, LOGL_INFO, "Starting loss CS timer with %d seconds.\n", sec);
447 bsc_schedule_timer(&cs->timer, sec, micro);
450 /* stop cell selection timer */
451 static void stop_cs_timer(struct gsm322_cellsel *cs)
453 if (bsc_timer_pending(&cs->timer)) {
454 LOGP(DCS, LOGL_INFO, "stopping pending CS timer.\n");
455 bsc_del_timer(&cs->timer);
463 const char *plmn_a_state_names[] = {
468 "A4 wait for PLMN to appear",
473 const char *plmn_m_state_names[] = {
482 const char *cs_state_names[] = {
484 "C1 normal cell selection",
485 "C2 stored cell selection",
486 "C3 camped normally",
487 "C4 normal cell re-selection",
489 "C6 any cell selection",
490 "C7 camped on any cell",
491 "C8 any cell re-selection",
492 "C9 choose any cell",
498 /* new automatic PLMN search state */
499 static void new_a_state(struct gsm322_plmn *plmn, int state)
501 if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) {
502 LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
506 stop_plmn_timer(plmn);
508 if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
511 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
512 plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
517 /* new manual PLMN search state */
518 static void new_m_state(struct gsm322_plmn *plmn, int state)
520 if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
521 LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
525 if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
528 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
529 plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
534 /* new Cell selection state */
535 static void new_c_state(struct gsm322_cellsel *cs, int state)
537 if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
540 LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n",
541 cs_state_names[cs->state], cs_state_names[state]);
543 /* stop cell selection timer, if running */
546 /* stop scanning of power measurement */
561 /* 4.4.3 create sorted list of PLMN
563 * the source of entries are
566 * - entries found in the SIM's PLMN Selector list
567 * - scanned PLMNs above -85 dB (random order)
568 * - scanned PLMNs below or equal -85 (by received level)
572 * The list only includes networks found at last scan.
574 * The list always contains HPLMN if available, even if not used by PLMN
575 * search process at some conditions.
577 * The list contains all PLMNs even if not allowed, so entries have to be
578 * removed when selecting from the list. (In case we use manual cell selection,
579 * we need to provide non-allowed networks also.)
581 static int gsm322_sort_list(struct osmocom_ms *ms)
583 struct gsm322_plmn *plmn = &ms->plmn;
584 struct gsm322_cellsel *cs = &ms->cellsel;
585 struct gsm_subscriber *subscr = &ms->subscr;
586 struct gsm_sub_plmn_list *sim_entry;
587 struct gsm_sub_plmn_na *na_entry;
588 struct llist_head temp_list;
589 struct gsm322_plmn_list *temp, *found;
590 struct llist_head *lh, *lh2;
591 int i, entries, move;
592 int8_t search_db = 0;
595 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
600 /* Create a temporary list of all networks */
601 INIT_LLIST_HEAD(&temp_list);
602 for (i = 0; i <= 1023; i++) {
603 if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)
604 || !cs->list[i].sysinfo)
607 /* search if network has multiple cells */
609 llist_for_each_entry(temp, &temp_list, entry) {
610 if (temp->mcc == cs->list[i].sysinfo->mcc
611 && temp->mnc == cs->list[i].sysinfo->mnc) {
616 /* update or create */
618 if (cs->list[i].rxlev_db > found->rxlev_db)
619 found->rxlev_db = cs->list[i].rxlev_db;
621 temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
624 temp->mcc = cs->list[i].sysinfo->mcc;
625 temp->mnc = cs->list[i].sysinfo->mnc;
626 temp->rxlev_db = cs->list[i].rxlev_db;
627 llist_add_tail(&temp->entry, &temp_list);
631 /* move Home PLMN, if in list, else add it */
632 if (subscr->sim_valid) {
634 llist_for_each_entry(temp, &temp_list, entry) {
635 if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) {
642 llist_del(&found->entry);
643 llist_add_tail(&found->entry, &plmn->sorted_plmn);
647 /* move entries if in SIM's PLMN Selector list */
648 llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
650 llist_for_each_entry(temp, &temp_list, entry) {
651 if (temp->mcc == sim_entry->mcc
652 && temp->mnc == sim_entry->mnc) {
658 llist_del(&found->entry);
659 llist_add_tail(&found->entry, &plmn->sorted_plmn);
663 /* move PLMN above -85 dBm in random order */
665 llist_for_each_entry(temp, &temp_list, entry) {
666 if (temp->rxlev_db > -85)
670 move = random() % entries;
672 llist_for_each_entry(temp, &temp_list, entry) {
673 if (temp->rxlev_db > -85) {
675 llist_del(&temp->entry);
676 llist_add_tail(&temp->entry,
686 /* move ohter PLMN in decreasing order */
689 llist_for_each_entry(temp, &temp_list, entry) {
691 || temp->rxlev_db > search_db) {
692 search_db = temp->rxlev_db;
698 llist_del(&found->entry);
699 llist_add_tail(&found->entry, &plmn->sorted_plmn);
702 /* mark forbidden PLMNs, if in list of forbidden networks */
704 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
705 llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
706 if (temp->mcc == na_entry->mcc
707 && temp->mnc == na_entry->mnc) {
708 temp->cause = na_entry->cause;
712 LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. "
713 "(%02d: mcc=%s mnc=%s allowed=%s rx-lev=%d)\n",
714 i, gsm_print_mcc(temp->mcc),
715 gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes",
720 gsm322_dump_sorted_plmn(ms);
726 * handler for automatic search
729 /* go On PLMN state */
730 static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
732 struct gsm322_plmn *plmn = &ms->plmn;
733 struct gsm_subscriber *subscr = &ms->subscr;
735 new_a_state(plmn, GSM322_A2_ON_PLMN);
737 /* start timer, if on VPLMN of home country OR special case */
738 if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi)
739 && (subscr->always_search_hplmn
740 || gsm_match_mcc(plmn->mcc, subscr->imsi))) {
741 if (subscr->sim_valid && subscr->t6m_hplmn)
742 start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
744 start_plmn_timer(plmn, 30 * 360);
746 stop_plmn_timer(plmn);
751 /* indicate selected PLMN */
752 static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
754 struct gsm322_plmn *plmn = &ms->plmn;
756 vty_notify(ms, NULL);
757 vty_notify(ms, "Selected Network: %s, %s\n",
758 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
760 return gsm322_a_go_on_plmn(ms, msg);
763 /* no (more) PLMN in list */
764 static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
766 struct gsm322_plmn *plmn = &ms->plmn;
767 struct gsm322_cellsel *cs = &ms->cellsel;
771 /* any allowable PLMN available? */
772 plmn->mcc = plmn->mnc = 0;
773 found = gsm322_cs_select(ms, 0);
775 /* if no PLMN in list */
777 LOGP(DPLMN, LOGL_INFO, "Not any PLNs allowable.\n");
779 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
781 /* we must forward this, otherwhise "Any cell selection"
782 * will not start automatically.
784 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
787 gsm322_cs_sendmsg(ms, nmsg);
792 /* select first PLMN in list */
793 plmn->mcc = cs->list[found].sysinfo->mcc;
794 plmn->mnc = cs->list[found].sysinfo->mnc;
796 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n",
797 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
798 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
800 /* indicate New PLMN */
801 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
804 gsm322_cs_sendmsg(ms, nmsg);
807 return gsm322_a_indicate_selected(ms, msg);
810 /* select first PLMN in list */
811 static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
813 struct gsm322_plmn *plmn = &ms->plmn;
814 struct gsm_subscriber *subscr = &ms->subscr;
816 struct gsm322_plmn_list *plmn_entry;
817 struct gsm322_plmn_list *plmn_first = NULL;
821 gsm322_sort_list(ms);
823 /* select first entry */
825 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
826 /* if last selected PLMN was HPLMN, we skip that */
827 if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc,
829 && plmn_entry->mcc == plmn->mcc
830 && plmn_entry->mnc == plmn->mnc) {
831 LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was "
832 "previously selected.\n");
836 /* select first allowed network */
837 if (!plmn_entry->cause) {
838 plmn_first = plmn_entry;
841 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it "
842 "not allowed (cause %d).\n", plmn_entry->mcc,
843 plmn_entry->mnc, plmn_entry->cause);
848 /* if no PLMN in list */
850 LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
851 gsm322_a_no_more_plmn(ms, msg);
856 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
857 "mnc=%s %s, %s)\n", plmn->plmn_curr,
858 gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc),
859 gsm_get_mcc(plmn_first->mcc),
860 gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
862 /* set current network */
863 plmn->mcc = plmn_first->mcc;
864 plmn->mnc = plmn_first->mnc;
866 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
868 /* indicate New PLMN */
869 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
872 gsm322_cs_sendmsg(ms, nmsg);
877 /* select next PLMN in list */
878 static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
880 struct gsm322_plmn *plmn = &ms->plmn;
882 struct gsm322_plmn_list *plmn_entry;
883 struct gsm322_plmn_list *plmn_next = NULL;
886 /* select next entry from list */
888 ii = plmn->plmn_curr + 1;
889 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
890 /* skip previously selected networks */
895 /* select next allowed network */
896 if (!plmn_entry->cause) {
897 plmn_next = plmn_entry;
900 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it "
901 "not allowed (cause %d).\n", plmn_entry->mcc,
902 plmn_entry->mnc, plmn_entry->cause);
907 /* if no more PLMN in list */
909 LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
910 gsm322_a_no_more_plmn(ms, msg);
915 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
916 "mnc=%s %s, %s)\n", plmn->plmn_curr,
917 gsm_print_mcc(plmn_next->mcc), gsm_print_mnc(plmn_next->mnc),
918 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
920 /* set next network */
921 plmn->mcc = plmn_next->mcc;
922 plmn->mnc = plmn_next->mnc;
924 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
926 /* indicate New PLMN */
927 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
930 gsm322_cs_sendmsg(ms, nmsg);
935 /* User re-selection event */
936 static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg)
938 struct gsm322_plmn *plmn = &ms->plmn;
939 struct gsm_subscriber *subscr = &ms->subscr;
940 struct gsm48_rrlayer *rr = &ms->rrlayer;
941 struct gsm322_plmn_list *plmn_entry;
942 struct gsm322_plmn_list *plmn_found = NULL;
944 if (!subscr->sim_valid) {
948 /* try again later, if not idle */
949 if (rr->state != GSM48_RR_ST_IDLE) {
950 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
955 /* search current PLMN in list */
956 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
957 if (plmn_entry->mcc == plmn->mcc
958 && plmn_entry->mnc == plmn->mnc)
959 plmn_found = plmn_entry;
963 /* abort if list is empty */
965 LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
969 LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
970 "and restarting PLMN search process.\n");
972 /* move entry to end of list */
973 llist_del(&plmn_found->entry);
974 llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
976 /* select first PLMN in list */
977 return gsm322_a_sel_first_plmn(ms, msg);
980 /* PLMN becomes available */
981 static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
983 struct gsm322_plmn *plmn = &ms->plmn;
984 struct gsm_subscriber *subscr = &ms->subscr;
985 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
987 if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
988 && subscr->plmn_mnc == gm->mnc) {
992 LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n");
993 return gsm322_a_go_on_plmn(ms, msg);
995 /* select first PLMN in list */
996 LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
997 "search process.\n");
998 return gsm322_a_sel_first_plmn(ms, msg);
1002 /* loss of radio coverage */
1003 static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
1005 struct gsm322_plmn *plmn = &ms->plmn;
1006 struct gsm322_cellsel *cs = &ms->cellsel;
1010 /* any PLMN available */
1011 found = gsm322_cs_select(ms, 0);
1013 /* if PLMN in list */
1015 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s "
1016 "%s, %s)\n", gsm_print_mcc(
1017 cs->list[found].sysinfo->mcc),
1018 gsm_print_mnc(cs->list[found].sysinfo->mnc),
1019 gsm_get_mcc(cs->list[found].sysinfo->mcc),
1020 gsm_get_mnc(cs->list[found].sysinfo->mcc,
1021 cs->list[found].sysinfo->mnc));
1022 return gsm322_a_sel_first_plmn(ms, msg);
1025 LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
1027 plmn->mcc = plmn->mnc = 0;
1029 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
1031 /* Tell cell selection process to handle "no cell found". */
1032 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1035 gsm322_cs_sendmsg(ms, nmsg);
1040 /* MS is switched on OR SIM is inserted OR removed */
1041 static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1043 struct gsm_subscriber *subscr = &ms->subscr;
1044 struct gsm322_plmn *plmn = &ms->plmn;
1047 if (!subscr->sim_valid) {
1048 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1049 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1050 new_a_state(plmn, GSM322_A6_NO_SIM);
1055 /* if there is a registered PLMN */
1056 if (subscr->plmn_valid) {
1057 /* select the registered PLMN */
1058 plmn->mcc = subscr->plmn_mcc;
1059 plmn->mnc = subscr->plmn_mnc;
1061 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1062 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1063 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1064 gsm_get_mnc(plmn->mcc, plmn->mnc));
1065 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1066 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1067 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1068 gsm_get_mnc(plmn->mcc, plmn->mnc));
1070 new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
1072 /* indicate New PLMN */
1073 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1076 gsm322_cs_sendmsg(ms, nmsg);
1081 /* initiate search at cell selection */
1082 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1083 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1085 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1088 gsm322_cs_sendmsg(ms, nmsg);
1093 /* MS is switched off */
1094 static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1096 struct gsm322_plmn *plmn = &ms->plmn;
1098 new_a_state(plmn, GSM322_A0_NULL);
1103 static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1105 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1109 /* SIM is removed */
1110 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1114 /* indicate SIM remove to cell selection process */
1115 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1118 gsm322_cs_sendmsg(ms, nmsg);
1120 return gsm322_a_switch_on(ms, msg);
1123 /* location update response: "Roaming not allowed" */
1124 static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
1126 /* store in list of forbidden LAs is done in gsm48* */
1128 return gsm322_a_sel_first_plmn(ms, msg);
1131 /* On VPLMN of home country and timeout occurs */
1132 static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg)
1134 struct gsm48_rrlayer *rr = &ms->rrlayer;
1135 struct gsm322_plmn *plmn = &ms->plmn;
1136 struct gsm322_cellsel *cs = &ms->cellsel;
1139 /* try again later, if not idle and not camping */
1140 if (rr->state != GSM48_RR_ST_IDLE
1141 || cs->state != GSM322_C3_CAMPED_NORMALLY) {
1142 LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n");
1143 start_plmn_timer(plmn, 60);
1148 new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
1150 /* initiate search at cell selection */
1151 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1154 gsm322_cs_sendmsg(ms, nmsg);
1159 /* manual mode selected */
1160 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
1164 /* restart state machine */
1165 gsm322_a_switch_off(ms, msg);
1166 ms->settings.plmn_mode = PLMN_MODE_MANUAL;
1167 gsm322_m_switch_on(ms, msg);
1169 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1172 gsm48_mmevent_msg(ms, nmsg);
1178 * handler for manual search
1181 /* display PLMNs and to Not on PLMN */
1182 static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
1184 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1185 int msg_type = gm->msg_type;
1186 struct gsm322_plmn *plmn = &ms->plmn;
1187 struct gsm_sub_plmn_list *temp;
1190 gsm322_sort_list(ms);
1192 vty_notify(ms, NULL);
1194 case GSM322_EVENT_REG_FAILED:
1195 vty_notify(ms, "Failed to register to network %s, %s "
1197 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1198 gsm_get_mcc(plmn->mcc),
1199 gsm_get_mnc(plmn->mcc, plmn->mnc));
1201 case GSM322_EVENT_NO_CELL_FOUND:
1202 vty_notify(ms, "No cell found for network %s, %s "
1204 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1205 gsm_get_mcc(plmn->mcc),
1206 gsm_get_mnc(plmn->mcc, plmn->mnc));
1208 case GSM322_EVENT_ROAMING_NA:
1209 vty_notify(ms, "Roaming not allowed to network %s, %s "
1211 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1212 gsm_get_mcc(plmn->mcc),
1213 gsm_get_mnc(plmn->mcc, plmn->mnc));
1217 if (llist_empty(&plmn->sorted_plmn))
1218 vty_notify(ms, "Search network!\n");
1220 vty_notify(ms, "Search or select from network:\n");
1221 llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
1222 vty_notify(ms, " Network %s, %s (%s, %s)\n",
1223 gsm_print_mcc(temp->mcc),
1224 gsm_print_mnc(temp->mnc),
1225 gsm_get_mcc(temp->mcc),
1226 gsm_get_mnc(temp->mcc, temp->mnc));
1229 /* go Not on PLMN state */
1230 new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
1235 /* user starts reselection */
1236 static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg)
1238 struct gsm_subscriber *subscr = &ms->subscr;
1239 struct gsm48_rrlayer *rr = &ms->rrlayer;
1242 if (!subscr->sim_valid) {
1246 /* try again later, if not idle */
1247 if (rr->state != GSM48_RR_ST_IDLE) {
1248 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
1253 /* initiate search at cell selection */
1254 vty_notify(ms, NULL);
1255 vty_notify(ms, "Searching Network, please wait...\n");
1256 LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n");
1258 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1261 gsm322_cs_sendmsg(ms, nmsg);
1266 /* MS is switched on OR SIM is inserted OR removed */
1267 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1269 struct gsm_subscriber *subscr = &ms->subscr;
1270 struct gsm322_plmn *plmn = &ms->plmn;
1273 if (!subscr->sim_valid) {
1274 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1275 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1276 new_m_state(plmn, GSM322_M5_NO_SIM);
1281 /* if there is a registered PLMN */
1282 if (subscr->plmn_valid) {
1285 /* select the registered PLMN */
1286 plmn->mcc = subscr->plmn_mcc;
1287 plmn->mnc = subscr->plmn_mnc;
1289 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1290 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1291 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1292 gsm_get_mnc(plmn->mcc, plmn->mnc));
1293 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1294 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1295 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1296 gsm_get_mnc(plmn->mcc, plmn->mnc));
1298 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1300 /* indicate New PLMN */
1301 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1304 gsm322_cs_sendmsg(ms, nmsg);
1309 /* initiate search at cell selection */
1310 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1311 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1312 vty_notify(ms, NULL);
1313 vty_notify(ms, "Searching Network, please wait...\n");
1315 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1318 gsm322_cs_sendmsg(ms, nmsg);
1323 /* MS is switched off */
1324 static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1326 struct gsm322_plmn *plmn = &ms->plmn;
1328 stop_plmn_timer(plmn);
1330 new_m_state(plmn, GSM322_M0_NULL);
1335 static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1337 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1341 /* SIM is removed */
1342 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1344 struct gsm322_plmn *plmn = &ms->plmn;
1347 stop_plmn_timer(plmn);
1349 /* indicate SIM remove to cell selection process */
1350 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1353 gsm322_cs_sendmsg(ms, nmsg);
1355 return gsm322_m_switch_on(ms, msg);
1358 /* go to On PLMN state */
1359 static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1361 struct gsm322_plmn *plmn = &ms->plmn;
1362 struct gsm_subscriber *subscr = &ms->subscr;
1364 /* if selected PLMN is in list of forbidden PLMNs */
1365 gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc);
1367 /* set last registered PLMN */
1368 subscr->plmn_valid = 1;
1369 subscr->plmn_mcc = plmn->mcc;
1370 subscr->plmn_mnc = plmn->mnc;
1375 new_m_state(plmn, GSM322_M2_ON_PLMN);
1380 /* indicate selected PLMN */
1381 static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
1383 struct gsm322_plmn *plmn = &ms->plmn;
1385 vty_notify(ms, NULL);
1386 vty_notify(ms, "Selected Network: %s, %s\n",
1387 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1389 return gsm322_m_go_on_plmn(ms, msg);
1392 /* previously selected PLMN becomes available again */
1393 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1395 struct gsm322_plmn *plmn = &ms->plmn;
1396 struct gsm322_cellsel *cs = &ms->cellsel;
1398 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1400 if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
1403 LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
1404 "selected, so start selection.\n");
1406 /* indicate New PLMN */
1407 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1410 gsm322_cs_sendmsg(ms, nmsg);
1416 /* the user has selected given PLMN */
1417 static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
1419 struct gsm322_plmn *plmn = &ms->plmn;
1420 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1423 /* use user selection */
1424 plmn->mcc = gm->mcc;
1425 plmn->mnc = gm->mnc;
1427 vty_notify(ms, NULL);
1428 vty_notify(ms, "Selected Network: %s, %s\n",
1429 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1430 LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s "
1431 "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1432 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1434 new_m_state(plmn, GSM322_M4_TRYING_PLMN);
1436 /* indicate New PLMN */
1437 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1440 gsm322_cs_sendmsg(ms, nmsg);
1445 /* auto mode selected */
1446 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
1450 /* restart state machine */
1451 gsm322_m_switch_off(ms, msg);
1452 ms->settings.plmn_mode = PLMN_MODE_AUTO;
1453 gsm322_a_switch_on(ms, msg);
1455 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1458 gsm48_mmevent_msg(ms, nmsg);
1463 /* if no cell is found in other states than in *_TRYING_* states */
1464 static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
1468 /* Tell cell selection process to handle "no cell found". */
1469 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1472 gsm322_cs_sendmsg(ms, nmsg);
1478 * cell scanning process
1481 /* select a suitable and allowable cell */
1482 static int gsm322_cs_select(struct osmocom_ms *ms, int any)
1484 struct gsm322_cellsel *cs = &ms->cellsel;
1485 struct gsm_subscriber *subscr = &ms->subscr;
1486 struct gsm48_sysinfo *s;
1487 int i, found = -1, power = 0;
1488 uint8_t flags, mask;
1491 /* set out access class depending on the cell selection type */
1493 acc_class = subscr->acc_class | 0x0400; /* add emergency */
1494 LOGP(DCS, LOGL_INFO, "Select using access class with Emergency "
1497 acc_class = subscr->acc_class;
1498 LOGP(DCS, LOGL_INFO, "Select using access class \n");
1501 /* flags to match */
1502 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1503 | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
1504 if (cs->state == GSM322_C2_STORED_CELL_SEL
1505 || cs->state == GSM322_C5_CHOOSE_CELL)
1506 mask |= GSM322_CS_FLAG_BA;
1507 flags = mask; /* all masked flags are requied */
1509 /* loop through all scanned frequencies and select cell */
1510 for (i = 0; i <= 1023; i++) {
1511 cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
1512 s = cs->list[i].sysinfo;
1514 /* channel has no informations for us */
1515 if (!s || (cs->list[i].flags & mask) != flags) {
1519 /* check C1 criteria not fullfilled */
1520 // TODO: C1 is also dependant on power class and max power
1521 if (cs->list[i].rxlev_db < s->rxlev_acc_min_db) {
1522 LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
1523 "not met. (rxlev=%d < min=%d)\n", i,
1524 cs->list[i].rxlev_db, s->rxlev_acc_min_db);
1528 /* if cell is barred and we don't override */
1529 if (!subscr->acc_barr
1530 && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
1531 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
1536 /* if cell is in list of forbidden LAs */
1537 if (!subscr->acc_barr
1538 && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
1539 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1540 "list of forbidden LAs. (mcc=%s mnc=%s "
1541 "lai=%04x)\n", i, gsm_print_mcc(s->mcc),
1542 gsm_print_mnc(s->mnc), s->lac);
1546 /* if we have no access to the cell and we don't override */
1547 if (!subscr->acc_barr
1548 && !(acc_class & (s->class_barr ^ 0xffff))) {
1549 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
1550 "barred for out access. (access=%04x "
1551 "barred=%04x)\n", i, acc_class, s->class_barr);
1555 /* store temporary available and allowable flag */
1556 cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
1558 /* if we search a specific PLMN, but it does not match */
1559 if (!any && cs->mcc && (cs->mcc != s->mcc
1560 || cs->mnc != s->mnc)) {
1561 LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
1562 "does not match target PLMN. (mcc=%s "
1563 "mnc=%s)\n", i, gsm_print_mcc(s->mcc),
1564 gsm_print_mnc(s->mnc));
1568 LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%d "
1569 "mcc=%s mnc=%s lac=%04x %s, %s)\n", i,
1570 cs->list[i].rxlev_db, gsm_print_mcc(s->mcc),
1571 gsm_print_mnc(s->mnc), s->lac, gsm_get_mcc(s->mcc),
1572 gsm_get_mnc(s->mcc, s->mnc));
1574 /* find highest power cell */
1575 if (found < 0 || cs->list[i].rxlev_db > power) {
1576 power = cs->list[i].rxlev_db;
1582 LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
1587 /* tune to first/next unscanned frequency and search for PLMN */
1588 static int gsm322_cs_scan(struct osmocom_ms *ms)
1590 struct gsm322_cellsel *cs = &ms->cellsel;
1592 uint8_t mask, flags;
1593 uint32_t weight = 0, test = cs->scan_state;
1595 /* search for strongest unscanned cell */
1596 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1597 | GSM322_CS_FLAG_SIGNAL;
1598 if (cs->state == GSM322_C2_STORED_CELL_SEL
1599 || cs->state == GSM322_C5_CHOOSE_CELL)
1600 mask |= GSM322_CS_FLAG_BA;
1601 flags = mask; /* all masked flags are requied */
1602 for (i = 0; i <= 1023; i++) {
1603 /* skip if band has enough frequencies scanned (3.2.1) */
1604 for (j = 0; gsm_sup_smax[j].max; j++) {
1605 if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
1606 if (gsm_sup_smax[j].start >= i
1607 && gsm_sup_smax[j].end <= i)
1610 if (gsm_sup_smax[j].end <= i
1611 || gsm_sup_smax[j].start >= i)
1615 if (gsm_sup_smax[j].max) {
1616 if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
1619 /* search for unscanned frequency */
1620 if ((cs->list[i].flags & mask) == flags) {
1621 /* weight depends on the power level
1622 * if it is the same, it depends on arfcn
1624 test = cs->list[i].rxlev_db + 128;
1625 test = (test << 16) | i;
1626 if (test >= cs->scan_state)
1632 cs->scan_state = weight;
1635 gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs,
1638 /* special case for PLMN search */
1639 if (cs->state == GSM322_PLMN_SEARCH && !weight) {
1642 /* create AA flag */
1643 cs->mcc = cs->mnc = 0;
1644 gsm322_cs_select(ms, 0);
1646 new_c_state(cs, GSM322_C0_NULL);
1648 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END);
1649 LOGP(DCS, LOGL_INFO, "PLMN search finished.\n");
1652 gsm322_plmn_sendmsg(ms, nmsg);
1657 /* special case for HPLMN search */
1658 if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
1661 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1662 LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n");
1665 gsm322_plmn_sendmsg(ms, nmsg);
1667 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
1669 cs->arfcn = cs->sel_arfcn;
1670 LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev "
1671 "%d).\n", cs->arfcn, cs->list[cs->arfcn].rxlev_db);
1673 gsm322_sync_to_cell(cs);
1674 // start_cs_timer(cs, ms->support.sync_to, 0);
1679 /* if all frequencies have been searched */
1685 LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
1687 /* just see, if we search for any cell */
1688 if (cs->state == GSM322_C6_ANY_CELL_SEL
1689 || cs->state == GSM322_C8_ANY_CELL_RESEL
1690 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1693 found = gsm322_cs_select(ms, any);
1697 struct gsm322_plmn *plmn = &ms->plmn;
1699 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1702 cs->si = cs->list[cs->arfcn].sysinfo;
1704 gsm322_sync_to_cell(cs);
1706 /* selected PLMN (manual) or any PLMN (auto) */
1707 switch (ms->settings.plmn_mode) {
1708 case PLMN_MODE_AUTO:
1709 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1710 /* PLMN becomes available */
1711 nmsg = gsm322_msgb_alloc(
1712 GSM322_EVENT_PLMN_AVAIL);
1715 gsm322_plmn_sendmsg(ms, nmsg);
1718 case PLMN_MODE_MANUAL:
1719 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1720 && gsm322_is_plmn_avail(cs, plmn->mcc,
1722 /* PLMN becomes available */
1723 nmsg = gsm322_msgb_alloc(
1724 GSM322_EVENT_PLMN_AVAIL);
1727 gsm322_plmn_sendmsg(ms, nmsg);
1732 /* set selected cell */
1734 cs->sel_arfcn = cs->arfcn;
1735 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1736 cs->sel_mcc = cs->si->mcc;
1737 cs->sel_mnc = cs->si->mnc;
1738 cs->sel_lac = cs->si->lac;
1739 cs->sel_id = cs->si->cell_id;
1741 /* tell CS process about available cell */
1742 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1743 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1746 /* unset selected cell */
1747 gsm322_unselect_cell(cs);
1749 /* tell CS process about no cell available */
1750 LOGP(DCS, LOGL_INFO, "No cell available.\n");
1751 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1755 gsm322_c_event(ms, nmsg);
1761 /* NOTE: We might already have system information from previous
1762 * scan. But we need recent informations, so we scan again!
1765 /* Tune to frequency for a while, to receive broadcasts. */
1766 cs->arfcn = weight & 1023;
1767 LOGP(DCS, LOGL_INFO, "Scanning frequency %d (rxlev %d).\n", cs->arfcn,
1768 cs->list[cs->arfcn].rxlev_db);
1770 gsm322_sync_to_cell(cs);
1771 // start_cs_timer(cs, ms->support.sync_to, 0);
1773 /* Allocate/clean system information. */
1774 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1775 if (cs->list[cs->arfcn].sysinfo)
1776 memset(cs->list[cs->arfcn].sysinfo, 0,
1777 sizeof(struct gsm48_sysinfo));
1779 cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
1780 struct gsm48_sysinfo);
1781 if (!cs->list[cs->arfcn].sysinfo)
1783 cs->si = cs->list[cs->arfcn].sysinfo;
1785 /* increase scan counter for each maximum scan range */
1786 if (gsm_sup_smax[j].max) {
1787 LOGP(DCS, LOGL_INFO, "%d frequencies left in band %d..%d\n",
1788 gsm_sup_smax[j].max - gsm_sup_smax[j].temp,
1789 gsm_sup_smax[j].start, gsm_sup_smax[j].end);
1790 gsm_sup_smax[j].temp++;
1796 /* check if cell is now suitable and allowable */
1797 static int gsm322_cs_store(struct osmocom_ms *ms)
1799 struct gsm322_cellsel *cs = &ms->cellsel;
1800 struct gsm48_sysinfo *s = cs->si;
1801 struct gsm322_plmn *plmn = &ms->plmn;
1805 if (cs->state != GSM322_C2_STORED_CELL_SEL
1806 && cs->state != GSM322_C1_NORMAL_CELL_SEL
1807 && cs->state != GSM322_C6_ANY_CELL_SEL
1808 && cs->state != GSM322_C4_NORMAL_CELL_RESEL
1809 && cs->state != GSM322_C8_ANY_CELL_RESEL
1810 && cs->state != GSM322_C5_CHOOSE_CELL
1811 && cs->state != GSM322_C9_CHOOSE_ANY_CELL
1812 && cs->state != GSM322_PLMN_SEARCH
1813 && cs->state != GSM322_HPLMN_SEARCH) {
1814 LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
1815 "(re-)selection, please fix!\n");
1820 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO;
1822 && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp &&
1823 cs->list[cs->arfcn].sysinfo->sp_cbq))
1824 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED;
1826 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED;
1829 cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db;
1830 cs->list[cs->arfcn].class_barr = s->class_barr;
1831 cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch;
1834 /* store selected network */
1837 cs->list[cs->arfcn].mcc = s->mcc;
1838 cs->list[cs->arfcn].mnc = s->mnc;
1839 cs->list[cs->arfcn].lac = s->lac;
1842 if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
1843 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD;
1845 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD;
1848 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev=%d "
1849 "mcc=%s mnc=%s lac=%04x)\n", cs->arfcn,
1850 cs->list[cs->arfcn].rxlev_db, gsm_print_mcc(s->mcc),
1851 gsm_print_mnc(s->mnc), s->lac);
1853 /* special case for PLMN search */
1854 if (cs->state == GSM322_PLMN_SEARCH)
1855 /* tune to next cell */
1856 return gsm322_cs_scan(ms);
1858 /* special case for HPLMN search */
1859 if (cs->state == GSM322_HPLMN_SEARCH) {
1860 struct gsm_subscriber *subscr = &ms->subscr;
1863 if (!gsm322_is_hplmn_avail(cs, subscr->imsi))
1864 /* tune to next cell */
1865 return gsm322_cs_scan(ms);
1867 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1868 LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n");
1871 gsm322_plmn_sendmsg(ms, nmsg);
1876 /* just see, if we search for any cell */
1877 if (cs->state == GSM322_C6_ANY_CELL_SEL
1878 || cs->state == GSM322_C8_ANY_CELL_RESEL
1879 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1882 found = gsm322_cs_select(ms, any);
1886 LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n");
1887 /* tune to next cell */
1888 return gsm322_cs_scan(ms);
1891 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1894 cs->si = cs->list[cs->arfcn].sysinfo;
1896 gsm322_sync_to_cell(cs);
1898 /* selected PLMN (manual) or any PLMN (auto) */
1899 switch (ms->settings.plmn_mode) {
1900 case PLMN_MODE_AUTO:
1901 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1902 /* PLMN becomes available */
1903 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1906 gsm322_plmn_sendmsg(ms, nmsg);
1909 case PLMN_MODE_MANUAL:
1910 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1911 && gsm322_is_plmn_avail(cs, plmn->mcc,
1913 /* PLMN becomes available */
1914 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1917 gsm322_plmn_sendmsg(ms, nmsg);
1922 /* set selected cell */
1924 cs->sel_arfcn = cs->arfcn;
1925 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1926 cs->sel_mcc = cs->si->mcc;
1927 cs->sel_mnc = cs->si->mnc;
1928 cs->sel_lac = cs->si->lac;
1929 cs->sel_id = cs->si->cell_id;
1931 /* tell CS process about available cell */
1932 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1933 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1936 gsm322_c_event(ms, nmsg);
1942 /* process system information when returing to idle mode */
1943 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
1945 struct gsm322_cellsel *cs = &ms->cellsel;
1946 struct gsm48_sysinfo *s = cs->si;
1947 struct gsm322_ba_list *ba = NULL;
1951 /* collect system information received during dedicated mode */
1953 && (!s->nb_ext_ind_si5
1954 || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
1955 || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
1956 && s->nb_ext_ind_si5bis))) {
1957 /* find or create ba list */
1958 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1960 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1965 llist_add_tail(&ba->entry, &cs->ba_list);
1967 /* update (add) ba list */
1968 memcpy(freq, ba->freq, sizeof(freq));
1969 for (i = 0; i <= 1023; i++) {
1970 if ((s->freq[i].mask & FREQ_TYPE_REP))
1971 freq[i >> 3] |= (1 << (i & 7));
1973 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
1974 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
1975 "%s, %s).\n", gsm_print_mcc(ba->mcc),
1976 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
1977 gsm_get_mnc(ba->mcc, ba->mnc));
1978 memcpy(ba->freq, freq, sizeof(freq));
1985 /* store BA whenever a system informations changes */
1986 static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
1987 struct gsm48_sysinfo *s)
1989 struct gsm322_ba_list *ba;
1993 /* find or create ba list */
1994 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1996 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2001 llist_add_tail(&ba->entry, &cs->ba_list);
2003 /* update ba list */
2004 memset(freq, 0, sizeof(freq));
2005 freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
2006 for (i = 0; i <= 1023; i++) {
2007 if ((s->freq[i].mask &
2008 (FREQ_TYPE_SERV | FREQ_TYPE_NCELL)))
2009 freq[i >> 3] |= (1 << (i & 7));
2011 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
2012 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
2013 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2014 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2015 gsm_get_mnc(ba->mcc, ba->mnc));
2016 memcpy(ba->freq, freq, sizeof(freq));
2022 /* process system information during camping on a cell */
2023 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2025 // struct gsm48_rrlayer *rr = &ms->rrlayer;
2026 struct gsm322_cellsel *cs = &ms->cellsel;
2027 struct gsm48_sysinfo *s = cs->si;
2028 struct gsm_subscriber *subscr = &ms->subscr;
2029 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2033 if (rr->state != GSM48_RR_ST_IDLE) {
2034 LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n");
2039 /* Store BA if we have full system info about cells and neigbor cells.
2040 * Depending on the extended bit in the channel description,
2041 * we require more or less system informations about neighbor cells
2045 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2046 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2047 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2048 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2051 && (!s->nb_ext_ind_si2
2052 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2053 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2054 && s->nb_ext_ind_si2bis)))
2055 gsm322_store_ba_list(cs, s);
2057 /* update sel_si, if all relevant system informations received */
2058 if (s->si1 && s->si2 && s->si3
2059 && (!s->nb_ext_ind_si2
2060 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2061 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2062 && s->nb_ext_ind_si2bis))) {
2064 LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is "
2066 memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
2067 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2071 /* check for barred cell */
2072 if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
2073 /* check if cell becomes barred */
2074 if (!subscr->acc_barr && s->cell_barr
2075 && !(cs->list[cs->arfcn].sysinfo
2076 && cs->list[cs->arfcn].sysinfo->sp
2077 && cs->list[cs->arfcn].sysinfo->sp_cbq)) {
2078 LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
2080 /* mark cell as unscanned */
2081 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2082 if (cs->list[cs->arfcn].sysinfo) {
2083 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2085 talloc_free(cs->list[cs->arfcn].sysinfo);
2086 cs->list[cs->arfcn].sysinfo = NULL;
2087 gsm322_unselect_cell(cs);
2089 /* trigger reselection without queueing,
2090 * because other sysinfo message may be queued
2093 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2096 gsm322_c_event(ms, nmsg);
2101 /* check if cell access becomes barred */
2102 if (!((subscr->acc_class & 0xfbff)
2103 & (s->class_barr ^ 0xffff))) {
2104 LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
2109 /* check if MCC, MNC, LAC, cell ID changes */
2110 if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc
2111 || cs->sel_lac != s->lac) {
2112 LOGP(DCS, LOGL_NOTICE, "Cell changes location area. "
2113 "This is not good!\n");
2116 if (cs->sel_id != s->cell_id) {
2117 LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. "
2118 "This is not good!\n");
2125 /* process system information during channel scanning */
2126 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2128 struct gsm322_cellsel *cs = &ms->cellsel;
2129 struct gsm48_sysinfo *s = cs->si;
2130 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2132 /* no sysinfo if we are not done with power scan */
2133 if (cs->powerscan) {
2134 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
2138 /* Store BA if we have full system info about cells and neigbor cells.
2139 * Depending on the extended bit in the channel description,
2140 * we require more or less system informations about neighbor cells
2144 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2145 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2146 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2147 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2150 && (!s->nb_ext_ind_si2
2151 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2152 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2153 && s->nb_ext_ind_si2bis)))
2154 gsm322_store_ba_list(cs, s);
2156 /* all relevant system informations received */
2157 if (s->si1 && s->si2 && s->si3
2158 && (!s->nb_ext_ind_si2
2159 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2160 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2161 && s->nb_ext_ind_si2bis))) {
2162 LOGP(DCS, LOGL_INFO, "Received relevant sysinfo.\n");
2166 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2168 /* store sysinfo and continue scan */
2169 return gsm322_cs_store(ms);
2172 /* wait for more sysinfo or timeout */
2176 static void gsm322_cs_timeout(void *arg)
2178 struct gsm322_cellsel *cs = arg;
2179 struct osmocom_ms *ms = cs->ms;
2181 LOGP(DCS, LOGL_INFO, "Cell selection failed.\n");
2183 /* if we have no lock, we retry */
2184 if (cs->ccch_state != GSM322_CCCH_ST_SYNC)
2185 LOGP(DCS, LOGL_INFO, "Sync timeout.\n");
2187 LOGP(DCS, LOGL_INFO, "Read timeout.\n");
2189 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev=%d)\n",
2190 cs->arfcn, cs->list[cs->arfcn].rxlev_db);
2192 /* remove system information */
2193 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2194 if (cs->list[cs->arfcn].sysinfo) {
2195 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", cs->arfcn);
2196 talloc_free(cs->list[cs->arfcn].sysinfo);
2197 cs->list[cs->arfcn].sysinfo = NULL;
2198 gsm322_unselect_cell(cs);
2201 /* tune to next cell */
2208 * power scan process
2211 /* search for block of unscanned frequencies and start scanning */
2212 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
2214 struct gsm322_cellsel *cs = &ms->cellsel;
2215 struct gsm_settings *set = &ms->settings;
2217 uint8_t mask, flags;
2221 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
2222 flags = GSM322_CS_FLAG_SUPPORT;
2224 /* in case of sticking to a cell, we only select it */
2226 LOGP(DCS, LOGL_FATAL, "Scanning power for sticked cell.\n");
2227 i = set->stick_arfcn;
2228 if ((cs->list[i].flags & mask) == flags)
2231 /* search for first frequency to scan */
2232 if (cs->state == GSM322_C2_STORED_CELL_SEL
2233 || cs->state == GSM322_C5_CHOOSE_CELL) {
2234 LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA "
2236 mask |= GSM322_CS_FLAG_BA;
2237 flags |= GSM322_CS_FLAG_BA;
2239 LOGP(DCS, LOGL_FATAL, "Scanning power for all "
2241 for (i = 0; i <= 1023; i++) {
2242 if ((cs->list[i].flags & mask) == flags) {
2249 /* if there is no more frequency, we can tune to that cell */
2253 /* stop power level scanning */
2256 /* check if not signal is found */
2257 for (i = 0; i <= 1023; i++) {
2258 if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2264 LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
2265 /* on normal cell selection, start over */
2266 if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
2267 for (i = 0; i <= 1023; i++) {
2268 /* clear flag that this was scanned */
2269 cs->list[i].flags &=
2270 ~(GSM322_CS_FLAG_POWER
2271 | GSM322_CS_FLAG_SIGNAL
2272 | GSM322_CS_FLAG_SYSINFO);
2273 if (cs->list[i].sysinfo) {
2274 LOGP(DCS, LOGL_INFO, "free "
2275 "sysinfo arfcn=%d\n",
2278 cs->list[i].sysinfo);
2279 cs->list[i].sysinfo = NULL;
2282 /* no cell selected */
2283 gsm322_unselect_cell(cs);
2286 /* on other cell selection, indicate "no cell found" */
2287 /* NOTE: PLMN search process handles it.
2288 * If not handled there, CS process gets indicated.
2289 * If we would continue to process CS, then we might get
2290 * our list of scanned cells disturbed.
2292 if (cs->state == GSM322_PLMN_SEARCH)
2293 nmsg = gsm322_msgb_alloc(
2294 GSM322_EVENT_PLMN_SEARCH_END);
2296 nmsg = gsm322_msgb_alloc(
2297 GSM322_EVENT_NO_CELL_FOUND);
2300 gsm322_plmn_sendmsg(ms, nmsg);
2302 /* if HPLMN search, select last frequency */
2303 if (cs->state == GSM322_HPLMN_SEARCH) {
2304 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2306 cs->arfcn = cs->sel_arfcn;
2307 LOGP(DCS, LOGL_INFO, "Tuning back to frequency "
2308 "%d (rxlev %d).\n", cs->arfcn,
2309 cs->list[cs->arfcn].rxlev_db);
2311 gsm322_sync_to_cell(cs);
2312 // start_cs_timer(cs, ms->support.sync_to, 0);
2315 new_c_state(cs, GSM322_C0_NULL);
2319 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
2320 cs->scan_state = 0xffffffff; /* higher than high */
2321 /* clear counter of scanned frequencies of each range */
2322 for (i = 0; gsm_sup_smax[i].max; i++)
2323 gsm_sup_smax[i].temp = 0;
2324 return gsm322_cs_scan(ms);
2327 /* search last frequency to scan (en block) */
2330 for (i = s + 1; i <= 1023; i++) {
2331 if ((cs->list[i].flags & mask) == flags)
2338 LOGP(DCS, LOGL_INFO, "Scanning frequencies. (%d..%d)\n", s, e);
2340 /* start scan on radio interface */
2342 //#warning TESTING!!!!
2344 return l1ctl_tx_pm_req_range(ms, s, e);
2347 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
2348 void *handler_data, void *signal_data)
2350 struct osmocom_ms *ms;
2351 struct gsm322_cellsel *cs;
2352 struct osmobb_meas_res *mr;
2356 if (subsys != SS_L1CTL)
2360 case S_L1CTL_PM_RES:
2366 i = mr->band_arfcn & 1023;
2367 rxlev_db = mr->rx_lev - 110;
2368 cs->list[i].rxlev_db = rxlev_db;
2369 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2370 /* if minimum level is reached or if we stick to a cell */
2371 if (rxlev_db >= ms->support.min_rxlev_db
2372 || ms->settings.stick) {
2373 cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2374 LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2375 "rxlev %d)\n", i, cs->list[i].rxlev_db);
2378 case S_L1CTL_PM_DONE:
2379 LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
2384 gsm322_cs_powerscan(ms);
2386 case S_L1CTL_FBSB_RESP:
2389 if (cs->ccch_state == GSM322_CCCH_ST_INIT) {
2390 LOGP(DCS, LOGL_INFO, "Channel synched.\n");
2391 cs->ccch_state = GSM322_CCCH_ST_SYNC;
2395 /* in dedicated mode */
2396 if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
2397 return gsm48_rr_tx_rand_acc(ms, NULL);
2400 /* set timer for reading BCCH */
2401 if (cs->state == GSM322_C2_STORED_CELL_SEL
2402 || cs->state == GSM322_C1_NORMAL_CELL_SEL
2403 || cs->state == GSM322_C6_ANY_CELL_SEL
2404 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2405 || cs->state == GSM322_C8_ANY_CELL_RESEL
2406 || cs->state == GSM322_C5_CHOOSE_CELL
2407 || cs->state == GSM322_C9_CHOOSE_ANY_CELL
2408 || cs->state == GSM322_PLMN_SEARCH
2409 || cs->state == GSM322_HPLMN_SEARCH)
2410 start_cs_timer(cs, ms->support.scan_to, 0);
2411 // TODO: timer depends on BCCH config
2414 case S_L1CTL_FBSB_ERR:
2418 gsm322_sync_to_cell(cs);
2420 LOGP(DCS, LOGL_INFO, "Channel sync error, try again.\n");
2423 LOGP(DCS, LOGL_INFO, "Channel sync error.\n");
2431 gsm322_cs_timeout(cs);
2440 static void gsm322_cs_loss(void *arg)
2442 struct gsm322_cellsel *cs = arg;
2443 struct osmocom_ms *ms = cs->ms;
2444 struct gsm48_rrlayer *rr = &ms->rrlayer;
2446 LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n");
2447 if (cs->state == GSM322_C3_CAMPED_NORMALLY
2448 || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
2449 if (rr->state == GSM48_RR_ST_IDLE) {
2452 LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n");
2454 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2457 gsm322_c_event(ms, nmsg);
2460 LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n");
2462 /* be shure that nothing else is done after here
2463 * because the function call above may cause
2464 * to return from idle state and trigger cell re-sel.
2473 * handler for cell selection process
2476 /* start PLMN search */
2477 static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg)
2479 struct gsm322_cellsel *cs = &ms->cellsel;
2482 new_c_state(cs, GSM322_PLMN_SEARCH);
2484 /* mark all frequencies except our own BA to be scanned */
2485 for (i = 0; i <= 1023; i++) {
2486 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2487 | GSM322_CS_FLAG_SIGNAL
2488 | GSM322_CS_FLAG_SYSINFO);
2489 if (cs->list[i].sysinfo) {
2490 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2491 talloc_free(cs->list[i].sysinfo);
2492 cs->list[i].sysinfo = NULL;
2493 gsm322_unselect_cell(cs);
2497 /* unset selected cell */
2498 gsm322_unselect_cell(cs);
2500 /* start power scan */
2501 return gsm322_cs_powerscan(ms);
2504 /* start HPLMN search */
2505 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2507 struct gsm322_cellsel *cs = &ms->cellsel;
2510 new_c_state(cs, GSM322_HPLMN_SEARCH);
2512 /* mark all frequencies except our own BA to be scanned */
2513 for (i = 0; i <= 1023; i++) {
2514 if (i != cs->sel_arfcn
2515 && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2516 && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2517 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2518 | GSM322_CS_FLAG_SIGNAL
2519 | GSM322_CS_FLAG_SYSINFO);
2520 if (cs->list[i].sysinfo) {
2521 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2523 talloc_free(cs->list[i].sysinfo);
2524 cs->list[i].sysinfo = NULL;
2529 /* no cell selected */
2530 gsm322_unselect_cell(cs);
2532 /* start power scan */
2533 return gsm322_cs_powerscan(ms);
2536 /* start stored cell selection */
2537 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2539 struct gsm322_cellsel *cs = &ms->cellsel;
2542 new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2544 /* flag all frequencies that are in current band allocation */
2545 for (i = 0; i <= 1023; i++) {
2546 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2547 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2549 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2552 /* unset selected cell */
2553 gsm322_unselect_cell(cs);
2555 /* start power scan */
2556 return gsm322_cs_powerscan(ms);
2559 /* start noraml cell selection */
2560 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2562 struct gsm322_cellsel *cs = &ms->cellsel;
2565 /* except for stored cell selection state, we weed to rescan ?? */
2566 if (cs->state != GSM322_C2_STORED_CELL_SEL) {
2567 for (i = 0; i <= 1023; i++) {
2568 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2569 | GSM322_CS_FLAG_SIGNAL
2570 | GSM322_CS_FLAG_SYSINFO);
2571 if (cs->list[i].sysinfo) {
2572 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2574 talloc_free(cs->list[i].sysinfo);
2575 cs->list[i].sysinfo = NULL;
2580 new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2582 /* unset selected cell */
2583 gsm322_unselect_cell(cs);
2585 /* start power scan */
2586 return gsm322_cs_powerscan(ms);
2589 /* start any cell selection */
2590 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2592 struct gsm322_cellsel *cs = &ms->cellsel;
2594 /* in case we already tried any cell (re-)selection, power scan again */
2595 if (cs->state == GSM322_C0_NULL
2596 || cs->state == GSM322_C6_ANY_CELL_SEL
2597 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2600 for (i = 0; i <= 1023; i++) {
2601 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2602 | GSM322_CS_FLAG_SIGNAL
2603 | GSM322_CS_FLAG_SYSINFO);
2604 if (cs->list[i].sysinfo) {
2605 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2607 talloc_free(cs->list[i].sysinfo);
2608 cs->list[i].sysinfo = NULL;
2612 /* after re-selection, indicate no cell found */
2613 if (cs->state == GSM322_C6_ANY_CELL_SEL
2614 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2617 /* tell that we have no cell found */
2618 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2621 gsm48_mmevent_msg(ms, nmsg);
2624 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2627 cs->mcc = cs->mnc = 0;
2629 /* unset selected cell */
2630 gsm322_unselect_cell(cs);
2632 /* start power scan */
2633 return gsm322_cs_powerscan(ms);
2636 /* start noraml cell re-selection */
2637 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2639 struct gsm322_cellsel *cs = &ms->cellsel;
2641 new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2643 /* NOTE: We keep our scan info we have so far.
2644 * This may cause a skip in power scan. */
2646 /* start power scan */
2647 return gsm322_cs_powerscan(ms);
2650 /* start any cell re-selection */
2651 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2653 struct gsm322_cellsel *cs = &ms->cellsel;
2655 new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2657 /* NOTE: We keep our scan info we have so far.
2658 * This may cause a skip in power scan. */
2660 /* start power scan */
2661 return gsm322_cs_powerscan(ms);
2664 /* create temporary ba range with given frequency ranges */
2665 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2666 uint32_t *range, uint8_t ranges)
2668 static struct gsm322_ba_list ba;
2669 uint16_t lower, higher;
2671 memset(&ba, 0, sizeof(ba));
2674 lower = *range & 1023;
2675 higher = (*range >> 16) & 1023;
2677 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2680 ba.freq[lower >> 3] |= 1 << (lower & 7);
2681 if (lower == higher)
2683 lower = (lower + 1) & 1023;
2690 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2691 static int gsm322_cs_choose(struct osmocom_ms *ms)
2693 struct gsm322_cellsel *cs = &ms->cellsel;
2694 struct gsm48_rrlayer *rr = &ms->rrlayer;
2695 struct gsm322_ba_list *ba = NULL;
2698 /* NOTE: The call to this function is synchron to RR layer, so
2699 * we may access the BA range there.
2702 ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges);
2704 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2705 /* get and update BA of last received sysinfo 5* */
2706 ba = gsm322_cs_sysinfo_sacch(ms);
2708 LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored "
2710 ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2718 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2720 /* tell CS to start over */
2721 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2724 gsm322_c_event(ms, nmsg);
2730 /* flag all frequencies that are in current band allocation */
2731 for (i = 0; i <= 1023; i++) {
2732 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2733 if ((ba->freq[i >> 3] & (1 << (i & 7)))) {
2734 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2736 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2739 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2740 | GSM322_CS_FLAG_SIGNAL
2741 | GSM322_CS_FLAG_SYSINFO);
2742 if (cs->list[i].sysinfo) {
2743 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2744 talloc_free(cs->list[i].sysinfo);
2745 cs->list[i].sysinfo = NULL;
2749 /* unset selected cell */
2750 gsm322_unselect_cell(cs);
2752 /* start power scan */
2753 return gsm322_cs_powerscan(ms);
2756 /* start 'Choose cell' after returning to idle mode */
2757 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2759 struct gsm322_cellsel *cs = &ms->cellsel;
2761 new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2763 return gsm322_cs_choose(ms);
2766 /* start 'Choose any cell' after returning to idle mode */
2767 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2769 struct gsm322_cellsel *cs = &ms->cellsel;
2771 new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2773 return gsm322_cs_choose(ms);
2776 /* a new PLMN is selected by PLMN search process */
2777 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2779 struct gsm322_cellsel *cs = &ms->cellsel;
2780 struct gsm322_plmn *plmn = &ms->plmn;
2781 struct gsm322_ba_list *ba;
2783 cs->mcc = plmn->mcc;
2784 cs->mnc = plmn->mnc;
2786 LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s "
2787 "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc),
2788 gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc),
2789 gsm_get_mnc(cs->mcc, cs->mnc));
2791 /* search for BA list */
2792 ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2795 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2796 return gsm322_c_stored_cell_sel(ms, ba);
2798 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2799 return gsm322_c_normal_cell_sel(ms, msg);
2803 /* a suitable cell was found, so we camp normally */
2804 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2806 struct gsm322_cellsel *cs = &ms->cellsel;
2809 LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s "
2810 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2811 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2812 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2814 /* tell that we have selected a (new) cell */
2815 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2818 gsm48_mmevent_msg(ms, nmsg);
2820 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2825 /* a not suitable cell was found, so we camp on any cell */
2826 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2828 struct gsm322_cellsel *cs = &ms->cellsel;
2831 LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s "
2832 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2833 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2834 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2837 /* tell that we have selected a (new) cell */
2838 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2841 gsm48_mmevent_msg(ms, nmsg);
2843 new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2848 /* go connected mode */
2849 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2851 struct gsm322_cellsel *cs = &ms->cellsel;
2853 /* check for error */
2856 cs->arfcn = cs->sel_arfcn;
2858 /* be sure to go to current camping frequency on return */
2859 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2861 gsm322_sync_to_cell(cs);
2862 cs->si = cs->list[cs->arfcn].sysinfo;
2863 //#warning TESTING!!!!
2869 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2871 struct gsm322_cellsel *cs = &ms->cellsel;
2873 /* check for error */
2876 cs->arfcn = cs->sel_arfcn;
2878 /* be sure to go to current camping frequency on return */
2879 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2881 gsm322_sync_to_cell(cs);
2882 cs->si = cs->list[cs->arfcn].sysinfo;
2888 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2890 struct gsm_subscriber *subscr = &ms->subscr;
2892 /* if no SIM is is MS */
2893 if (!subscr->sim_valid) {
2894 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2895 return gsm322_c_any_cell_sel(ms, msg);
2896 LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2899 /* stay in NULL state until PLMN is selected */
2908 /* state machine for automatic PLMN selection events */
2909 static struct plmnastatelist {
2912 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2913 } plmnastatelist[] = {
2914 {SBIT(GSM322_A0_NULL),
2915 GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2917 {SBIT(GSM322_A0_NULL),
2918 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn},
2921 GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
2923 {SBIT(GSM322_A6_NO_SIM),
2924 GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
2927 GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
2930 GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
2933 GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
2935 {SBIT(GSM322_A1_TRYING_RPLMN),
2936 GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
2938 {SBIT(GSM322_A1_TRYING_RPLMN),
2939 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn},
2941 {SBIT(GSM322_A1_TRYING_RPLMN),
2942 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
2944 {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
2945 GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
2947 {SBIT(GSM322_A2_ON_PLMN),
2948 GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
2950 {SBIT(GSM322_A2_ON_PLMN),
2951 GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start},
2953 {SBIT(GSM322_A2_ON_PLMN),
2954 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
2956 {SBIT(GSM322_A2_ON_PLMN),
2957 GSM322_EVENT_USER_RESEL, gsm322_a_user_resel},
2959 {SBIT(GSM322_A3_TRYING_PLMN),
2960 GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
2962 {SBIT(GSM322_A3_TRYING_PLMN),
2963 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn},
2965 {SBIT(GSM322_A3_TRYING_PLMN),
2966 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
2968 {SBIT(GSM322_A5_HPLMN_SEARCH),
2969 GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn},
2971 {SBIT(GSM322_A5_HPLMN_SEARCH),
2972 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn},
2974 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
2975 GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
2978 GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
2981 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
2984 #define PLMNASLLEN \
2985 (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
2987 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
2989 struct gsm322_plmn *plmn = &ms->plmn;
2990 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2991 int msg_type = gm->msg_type;
2995 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
2996 "selection in state '%s'\n", ms->name, get_event_name(msg_type),
2997 plmn_a_state_names[plmn->state]);
2998 /* find function for current state and message */
2999 for (i = 0; i < PLMNASLLEN; i++)
3000 if ((msg_type == plmnastatelist[i].type)
3001 && ((1 << plmn->state) & plmnastatelist[i].states))
3003 if (i == PLMNASLLEN) {
3004 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3008 rc = plmnastatelist[i].rout(ms, msg);
3013 /* state machine for manual PLMN selection events */
3014 static struct plmnmstatelist {
3017 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3018 } plmnmstatelist[] = {
3019 {SBIT(GSM322_M0_NULL),
3020 GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
3022 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) |
3023 SBIT(GSM322_M2_ON_PLMN),
3024 GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns},
3027 GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
3029 {SBIT(GSM322_M5_NO_SIM),
3030 GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
3033 GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
3036 GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
3038 {SBIT(GSM322_M1_TRYING_RPLMN),
3039 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3041 {SBIT(GSM322_M1_TRYING_RPLMN),
3042 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3044 {SBIT(GSM322_M1_TRYING_RPLMN),
3045 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3047 {SBIT(GSM322_M1_TRYING_RPLMN),
3048 GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
3050 {SBIT(GSM322_M2_ON_PLMN),
3051 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3053 {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
3054 SBIT(GSM322_M4_TRYING_PLMN),
3055 GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
3057 {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN),
3058 GSM322_EVENT_USER_RESEL, gsm322_m_user_resel},
3060 {SBIT(GSM322_M3_NOT_ON_PLMN),
3061 GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
3063 {SBIT(GSM322_M3_NOT_ON_PLMN),
3064 GSM322_EVENT_CHOSE_PLMN, gsm322_m_choose_plmn},
3066 {SBIT(GSM322_M4_TRYING_PLMN),
3067 GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
3069 {SBIT(GSM322_M4_TRYING_PLMN),
3070 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3072 {SBIT(GSM322_M4_TRYING_PLMN),
3073 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3075 {SBIT(GSM322_M4_TRYING_PLMN),
3076 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3079 GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
3082 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3085 #define PLMNMSLLEN \
3086 (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
3088 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
3090 struct gsm322_plmn *plmn = &ms->plmn;
3091 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3092 int msg_type = gm->msg_type;
3096 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
3097 "in state '%s'\n", ms->name, get_event_name(msg_type),
3098 plmn_m_state_names[plmn->state]);
3099 /* find function for current state and message */
3100 for (i = 0; i < PLMNMSLLEN; i++)
3101 if ((msg_type == plmnmstatelist[i].type)
3102 && ((1 << plmn->state) & plmnmstatelist[i].states))
3104 if (i == PLMNMSLLEN) {
3105 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3109 rc = plmnmstatelist[i].rout(ms, msg);
3114 /* dequeue GSM 03.22 PLMN events */
3115 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
3117 struct gsm322_plmn *plmn = &ms->plmn;
3121 while ((msg = msgb_dequeue(&plmn->event_queue))) {
3122 /* send event to PLMN select process */
3123 if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
3124 gsm322_a_event(ms, msg);
3126 gsm322_m_event(ms, msg);
3128 work = 1; /* work done */
3134 /* state machine for channel selection events */
3135 static struct cellselstatelist {
3138 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3139 } cellselstatelist[] = {
3141 GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
3144 GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
3147 GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
3150 GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search},
3152 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3153 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
3154 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
3156 {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3157 SBIT(GSM322_C8_ANY_CELL_RESEL),
3158 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
3160 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3161 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3162 SBIT(GSM322_C0_NULL),
3163 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
3165 {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3166 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
3167 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
3169 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3170 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
3172 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3173 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
3175 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3176 GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
3178 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3179 GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
3181 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3182 GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
3184 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3185 GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
3187 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3188 GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
3190 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3191 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3192 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3193 SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH),
3194 GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
3196 {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
3197 GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
3199 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3200 GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search},
3203 #define CELLSELSLLEN \
3204 (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
3206 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
3208 struct gsm322_cellsel *cs = &ms->cellsel;
3209 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3210 int msg_type = gm->msg_type;
3214 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
3215 "'%s'\n", ms->name, get_event_name(msg_type),
3216 cs_state_names[cs->state]);
3217 /* find function for current state and message */
3218 for (i = 0; i < CELLSELSLLEN; i++)
3219 if ((msg_type == cellselstatelist[i].type)
3220 && ((1 << cs->state) & cellselstatelist[i].states))
3222 if (i == CELLSELSLLEN) {
3223 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
3227 rc = cellselstatelist[i].rout(ms, msg);
3232 /* dequeue GSM 03.22 cell selection events */
3233 int gsm322_cs_dequeue(struct osmocom_ms *ms)
3235 struct gsm322_cellsel *cs = &ms->cellsel;
3239 while ((msg = msgb_dequeue(&cs->event_queue))) {
3240 /* send event to cell selection process */
3241 gsm322_c_event(ms, msg);
3243 work = 1; /* work done */
3253 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
3255 struct gsm322_plmn *plmn = &ms->plmn;
3256 struct gsm322_plmn_list *temp;
3258 printf("MCC |MNC |allowed|rx-lev\n");
3259 printf("-------+-------+-------+-------\n");
3260 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
3261 printf("%s |%s%s |%s |%d\n", gsm_print_mcc(temp->mcc),
3262 gsm_print_mnc(temp->mnc),
3263 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3264 (temp->cause) ? "no ":"yes", temp->rxlev_db);
3270 int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
3271 void (*print)(void *, const char *, ...), void *priv)
3274 struct gsm48_sysinfo *s;
3276 print(priv, "arfcn |rx-lev |MCC |MNC |LAC |cell ID|forb.LA|"
3277 "prio |min-db |max-pwr\n");
3278 print(priv, "-------+-------+-------+-------+-------+-------+-------+"
3279 "-------+-------+-------\n");
3280 for (i = 0; i <= 1023; i++) {
3281 s = cs->list[i].sysinfo;
3282 if (!s || !(cs->list[i].flags & flags))
3284 print(priv, "%4d |%4d |", i, cs->list[i].rxlev_db);
3285 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
3286 print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc),
3287 gsm_print_mnc(s->mnc),
3288 ((s->mnc & 0x00f) == 0x00f) ? " ":"");
3289 print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id);
3290 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
3291 print(priv, "yes |");
3293 print(priv, "no |");
3294 if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
3295 print(priv, "barred |");
3297 if (cs->list[i].sysinfo->cell_barr)
3298 print(priv, "low |");
3300 print(priv, "normal |");
3302 print(priv, "%4d |%4d\n", s->rxlev_acc_min_db,
3303 s->ms_txpwr_max_cch);
3305 print(priv, "n/a |n/a |n/a |n/a |n/a |"
3313 int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
3314 void (*print)(void *, const char *, ...), void *priv)
3316 struct gsm322_plmn *plmn = &ms->plmn;
3317 struct gsm322_la_list *temp;
3319 print(priv, "MCC |MNC |LAC |cause\n");
3320 print(priv, "-------+-------+-------+-------\n");
3321 llist_for_each_entry(temp, &plmn->forbidden_la, entry)
3322 print(priv, "%s |%s%s |0x%04x |#%d\n",
3323 gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
3324 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3325 temp->lac, temp->cause);
3330 int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
3331 void (*print)(void *, const char *, ...), void *priv)
3333 struct gsm322_ba_list *ba;
3336 llist_for_each_entry(ba, &cs->ba_list, entry) {
3337 if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc))
3339 print(priv, "Band Allocation of network: MCC %03d MNC %02d "
3340 "(%s, %s)\n", ba->mcc, 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;
3361 char suffix[] = ".ba";
3362 char filename[sizeof(ms->name) + strlen(suffix) + 1];
3364 struct gsm322_ba_list *ba;
3367 LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
3368 LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
3370 memset(plmn, 0, sizeof(*plmn));
3371 memset(cs, 0, sizeof(*cs));
3375 /* set initial state */
3378 ms->settings.plmn_mode = PLMN_MODE_AUTO;
3381 INIT_LLIST_HEAD(&plmn->event_queue);
3382 INIT_LLIST_HEAD(&cs->event_queue);
3383 INIT_LLIST_HEAD(&plmn->sorted_plmn);
3384 INIT_LLIST_HEAD(&plmn->forbidden_la);
3385 INIT_LLIST_HEAD(&cs->ba_list);
3387 /* set supported frequencies in cell selection list */
3388 for (i = 0; i <= 1023; i++)
3389 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
3390 cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
3393 strcpy(filename, ms->name);
3394 strcat(filename, suffix);
3395 fp = osmocom_fopen(filename, "r");
3399 while(!osmocom_feof(fp)) {
3400 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
3403 rc = osmocom_fread(buf, 4, 1, fp);
3408 ba->mcc = (buf[0] << 8) | buf[1];
3409 ba->mnc = (buf[2] << 8) | buf[3];
3410 rc = osmocom_fread(ba->freq, sizeof(ba->freq), 1, fp);
3415 llist_add_tail(&ba->entry, &cs->ba_list);
3416 LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s "
3417 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3418 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3419 gsm_get_mnc(ba->mcc, ba->mnc));
3423 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
3425 register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3430 int gsm322_exit(struct osmocom_ms *ms)
3432 struct gsm322_plmn *plmn = &ms->plmn;
3433 struct gsm322_cellsel *cs = &ms->cellsel;
3434 struct llist_head *lh, *lh2;
3437 char suffix[] = ".ba";
3438 char filename[sizeof(ms->name) + strlen(suffix) + 1];
3439 struct gsm322_ba_list *ba;
3443 LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
3444 LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
3446 unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3448 /* stop cell selection process (if any) */
3449 new_c_state(cs, GSM322_C0_NULL);
3453 stop_plmn_timer(plmn);
3456 for (i = 0; i <= 1023; i++) {
3457 if (cs->list[i].sysinfo) {
3458 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
3459 talloc_free(cs->list[i].sysinfo);
3460 cs->list[i].sysinfo = NULL;
3462 cs->list[i].flags = 0;
3466 strcpy(filename, ms->name);
3467 strcat(filename, suffix);
3468 fp = osmocom_fopen(filename, "w");
3472 llist_for_each_entry(ba, &cs->ba_list, entry) {
3473 buf[0] = ba->mcc >> 8;
3474 buf[1] = ba->mcc & 0xff;
3475 buf[2] = ba->mnc >> 8;
3476 buf[3] = ba->mnc & 0xff;
3477 rc = osmocom_fwrite(buf, 4, 1, fp);
3478 rc = osmocom_fwrite(ba->freq, sizeof(ba->freq), 1, fp);
3479 LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s "
3480 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3481 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3482 gsm_get_mnc(ba->mcc, ba->mnc));
3486 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
3489 while ((msg = msgb_dequeue(&plmn->event_queue)))
3491 while ((msg = msgb_dequeue(&cs->event_queue)))
3493 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
3497 llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
3501 llist_for_each_safe(lh, lh2, &cs->ba_list) {