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/telnet_interface.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 osmocom_ms *ms, struct gsm322_cellsel *cs)
224 return l1ctl_tx_fbsb_req(ms, cs->arfcn,
225 L1CTL_FBSB_F_FB01SB, 100, 0,
229 static void gsm322_unselect_cell(struct gsm322_cellsel *cs)
233 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
234 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
237 /* print to DCS logging */
238 static void print_dcs(void *priv, const char *fmt, ...)
244 vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
245 buffer[sizeof(buffer) - 1] = '\0';
249 // LOGP(DCS, LOGL_INFO, "%s", buffer);
250 printf("%s", buffer);
253 /* del forbidden LA */
254 int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
255 uint16_t mnc, uint16_t lac)
257 struct gsm322_plmn *plmn = &ms->plmn;
258 struct gsm322_la_list *la;
260 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
261 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
262 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
263 "LAs (mcc=%s, mnc=%s, lac=%04x)\n",
264 gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac);
265 llist_del(&la->entry);
274 /* add forbidden LA */
275 int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
276 uint16_t mnc, uint16_t lac, uint8_t cause)
278 struct gsm322_plmn *plmn = &ms->plmn;
279 struct gsm322_la_list *la;
281 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
282 "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc),
283 gsm_print_mnc(mnc), lac);
284 la = talloc_zero(l23_ctx, struct gsm322_la_list);
291 llist_add_tail(&la->entry, &plmn->forbidden_la);
296 /* search forbidden LA */
297 int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
300 struct gsm322_plmn *plmn = &ms->plmn;
301 struct gsm322_la_list *la;
303 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
304 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
311 /* search for PLMN in all BA lists */
312 static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
313 uint16_t mcc, uint16_t mnc)
315 struct gsm322_ba_list *ba, *ba_found = NULL;
317 /* search for BA list */
318 llist_for_each_entry(ba, &cs->ba_list, entry) {
329 /* search available PLMN */
330 int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
334 for (i = 0; i <= 1023; i++) {
335 if (cs->list[i].sysinfo
336 && cs->list[i].sysinfo->mcc == mcc
337 && cs->list[i].sysinfo->mnc == mnc)
344 /* search available HPLMN */
345 int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi)
349 for (i = 0; i <= 1023; i++) {
350 if (cs->list[i].sysinfo
351 && gsm_match_mnc(cs->list[i].sysinfo->mcc,
352 cs->list[i].sysinfo->mnc, imsi))
359 /* del forbidden LA */
364 /*plmn search timer event */
365 static void plmn_timer_timeout(void *arg)
367 struct gsm322_plmn *plmn = arg;
370 LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
372 /* indicate PLMN selection T timeout */
373 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
376 gsm322_plmn_sendmsg(plmn->ms, nmsg);
379 /* start plmn search timer */
380 static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
382 LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
384 plmn->timer.cb = plmn_timer_timeout;
385 plmn->timer.data = plmn;
386 bsc_schedule_timer(&plmn->timer, secs, 0);
389 /* stop plmn search timer */
390 static void stop_plmn_timer(struct gsm322_plmn *plmn)
392 if (bsc_timer_pending(&plmn->timer)) {
393 LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
394 bsc_del_timer(&plmn->timer);
398 /* start cell selection timer */
399 void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
401 LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec);
402 cs->timer.cb = gsm322_cs_timeout;
404 bsc_schedule_timer(&cs->timer, sec, micro);
407 /* start loss timer */
408 void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro)
411 cs->timer.cb = gsm322_cs_loss;
413 if (bsc_timer_pending(&cs->timer)) {
414 struct timeval current_time;
415 unsigned long long currentTime;
417 gettimeofday(¤t_time, NULL);
418 currentTime = current_time.tv_sec * 1000000LL
419 + current_time.tv_usec;
420 currentTime += sec * 1000000LL + micro;
421 cs->timer.timeout.tv_sec = currentTime / 1000000LL;
422 cs->timer.timeout.tv_usec = currentTime % 1000000LL;
427 LOGP(DCS, LOGL_INFO, "Starting loss CS timer with %d seconds.\n", sec);
428 bsc_schedule_timer(&cs->timer, sec, micro);
431 /* stop cell selection timer */
432 static void stop_cs_timer(struct gsm322_cellsel *cs)
434 if (bsc_timer_pending(&cs->timer)) {
435 LOGP(DCS, LOGL_INFO, "stopping pending CS timer.\n");
436 bsc_del_timer(&cs->timer);
444 const char *plmn_a_state_names[] = {
449 "A4 wait for PLMN to appear",
454 const char *plmn_m_state_names[] = {
463 const char *cs_state_names[] = {
465 "C1 normal cell selection",
466 "C2 stored cell selection",
467 "C3 camped normally",
468 "C4 normal cell re-selection",
470 "C6 any cell selection",
471 "C7 camped on any cell",
472 "C8 any cell re-selection",
473 "C9 choose any cell",
479 /* new automatic PLMN search state */
480 static void new_a_state(struct gsm322_plmn *plmn, int state)
482 if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) {
483 LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
487 stop_plmn_timer(plmn);
489 if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
492 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
493 plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
498 /* new manual PLMN search state */
499 static void new_m_state(struct gsm322_plmn *plmn, int state)
501 if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
502 LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
506 if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
509 LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
510 plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
515 /* new Cell selection state */
516 static void new_c_state(struct gsm322_cellsel *cs, int state)
518 if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
521 LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n",
522 cs_state_names[cs->state], cs_state_names[state]);
524 /* stop cell selection timer, if running */
527 /* stop scanning of power measurement */
542 /* 4.4.3 create sorted list of PLMN
544 * the source of entries are
547 * - entries found in the SIM's PLMN Selector list
548 * - scanned PLMNs above -85 dB (random order)
549 * - scanned PLMNs below or equal -85 (by received level)
553 * The list only includes networks found at last scan.
555 * The list always contains HPLMN if available, even if not used by PLMN
556 * search process at some conditions.
558 * The list contains all PLMNs even if not allowed, so entries have to be
559 * removed when selecting from the list. (In case we use manual cell selection,
560 * we need to provide non-allowed networks also.)
562 static int gsm322_sort_list(struct osmocom_ms *ms)
564 struct gsm322_plmn *plmn = &ms->plmn;
565 struct gsm322_cellsel *cs = &ms->cellsel;
566 struct gsm_subscriber *subscr = &ms->subscr;
567 struct gsm_sub_plmn_list *sim_entry;
568 struct gsm_sub_plmn_na *na_entry;
569 struct llist_head temp_list;
570 struct gsm322_plmn_list *temp, *found;
571 struct llist_head *lh, *lh2;
572 int i, entries, move;
573 int8_t search_db = 0;
576 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
581 /* Create a temporary list of all networks */
582 INIT_LLIST_HEAD(&temp_list);
583 for (i = 0; i <= 1023; i++) {
584 if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)
585 || !cs->list[i].sysinfo)
588 /* search if network has multiple cells */
590 llist_for_each_entry(temp, &temp_list, entry) {
591 if (temp->mcc == cs->list[i].sysinfo->mcc
592 && temp->mnc == cs->list[i].sysinfo->mnc) {
597 /* update or create */
599 if (cs->list[i].rxlev_db > found->rxlev_db)
600 found->rxlev_db = cs->list[i].rxlev_db;
602 temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
605 temp->mcc = cs->list[i].sysinfo->mcc;
606 temp->mnc = cs->list[i].sysinfo->mnc;
607 temp->rxlev_db = cs->list[i].rxlev_db;
608 llist_add_tail(&temp->entry, &temp_list);
612 /* move Home PLMN, if in list, else add it */
613 if (subscr->sim_valid) {
615 llist_for_each_entry(temp, &temp_list, entry) {
616 if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) {
623 llist_del(&found->entry);
624 llist_add_tail(&found->entry, &plmn->sorted_plmn);
628 /* move entries if in SIM's PLMN Selector list */
629 llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
631 llist_for_each_entry(temp, &temp_list, entry) {
632 if (temp->mcc == sim_entry->mcc
633 && temp->mnc == sim_entry->mnc) {
639 llist_del(&found->entry);
640 llist_add_tail(&found->entry, &plmn->sorted_plmn);
644 /* move PLMN above -85 dBm in random order */
646 llist_for_each_entry(temp, &temp_list, entry) {
647 if (temp->rxlev_db > -85)
651 move = random() % entries;
653 llist_for_each_entry(temp, &temp_list, entry) {
654 if (temp->rxlev_db > -85) {
656 llist_del(&temp->entry);
657 llist_add_tail(&temp->entry,
667 /* move ohter PLMN in decreasing order */
670 llist_for_each_entry(temp, &temp_list, entry) {
672 || temp->rxlev_db > search_db) {
673 search_db = temp->rxlev_db;
679 llist_del(&found->entry);
680 llist_add_tail(&found->entry, &plmn->sorted_plmn);
683 /* mark forbidden PLMNs, if in list of forbidden networks */
685 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
686 llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
687 if (temp->mcc == na_entry->mcc
688 && temp->mnc == na_entry->mnc) {
689 temp->cause = na_entry->cause;
693 LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. "
694 "(%02d: mcc=%s mnc=%s allowed=%s rx-lev=%d)\n",
695 i, gsm_print_mcc(temp->mcc),
696 gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes",
701 gsm322_dump_sorted_plmn(ms);
707 * handler for automatic search
710 /* go On PLMN state */
711 static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
713 struct gsm322_plmn *plmn = &ms->plmn;
714 struct gsm_subscriber *subscr = &ms->subscr;
716 new_a_state(plmn, GSM322_A2_ON_PLMN);
718 /* start timer, if on VPLMN of home country OR special case */
719 if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi)
720 && (subscr->always_search_hplmn
721 || gsm_match_mcc(plmn->mcc, subscr->imsi))) {
722 if (subscr->sim_valid && subscr->t6m_hplmn)
723 start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
725 start_plmn_timer(plmn, 30 * 360);
727 stop_plmn_timer(plmn);
732 /* indicate selected PLMN */
733 static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
735 struct gsm322_plmn *plmn = &ms->plmn;
737 vty_notify(ms, NULL);
738 vty_notify(ms, "Selected Network: %s, %s\n",
739 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
741 return gsm322_a_go_on_plmn(ms, msg);
744 /* no (more) PLMN in list */
745 static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
747 struct gsm322_plmn *plmn = &ms->plmn;
748 struct gsm322_cellsel *cs = &ms->cellsel;
752 /* any allowable PLMN available? */
753 plmn->mcc = plmn->mnc = 0;
754 found = gsm322_cs_select(ms, 0);
756 /* if no PLMN in list */
758 LOGP(DPLMN, LOGL_INFO, "Not any PLNs allowable.\n");
760 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
762 /* we must forward this, otherwhise "Any cell selection"
763 * will not start automatically.
765 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
768 gsm322_cs_sendmsg(ms, nmsg);
773 /* select first PLMN in list */
774 plmn->mcc = cs->list[found].sysinfo->mcc;
775 plmn->mnc = cs->list[found].sysinfo->mnc;
777 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n",
778 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
779 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
781 /* indicate New PLMN */
782 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
785 gsm322_cs_sendmsg(ms, nmsg);
788 return gsm322_a_indicate_selected(ms, msg);
791 /* select first PLMN in list */
792 static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
794 struct gsm322_plmn *plmn = &ms->plmn;
795 struct gsm_subscriber *subscr = &ms->subscr;
797 struct gsm322_plmn_list *plmn_entry;
798 struct gsm322_plmn_list *plmn_first = NULL;
802 gsm322_sort_list(ms);
804 /* select first entry */
806 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
807 /* if last selected PLMN was HPLMN, we skip that */
808 if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc,
810 && plmn_entry->mcc == plmn->mcc
811 && plmn_entry->mnc == plmn->mnc) {
812 LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was "
813 "previously selected.\n");
817 /* select first allowed network */
818 if (!plmn_entry->cause) {
819 plmn_first = plmn_entry;
822 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it "
823 "not allowed (cause %d).\n", plmn_entry->mcc,
824 plmn_entry->mnc, plmn_entry->cause);
829 /* if no PLMN in list */
831 LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
832 gsm322_a_no_more_plmn(ms, msg);
837 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
838 "mnc=%s %s, %s)\n", plmn->plmn_curr,
839 gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc),
840 gsm_get_mcc(plmn_first->mcc),
841 gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
843 /* set current network */
844 plmn->mcc = plmn_first->mcc;
845 plmn->mnc = plmn_first->mnc;
847 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
849 /* indicate New PLMN */
850 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
853 gsm322_cs_sendmsg(ms, nmsg);
858 /* select next PLMN in list */
859 static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
861 struct gsm322_plmn *plmn = &ms->plmn;
863 struct gsm322_plmn_list *plmn_entry;
864 struct gsm322_plmn_list *plmn_next = NULL;
867 /* select next entry from list */
869 ii = plmn->plmn_curr + 1;
870 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
871 /* skip previously selected networks */
876 /* select next allowed network */
877 if (!plmn_entry->cause) {
878 plmn_next = plmn_entry;
881 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it "
882 "not allowed (cause %d).\n", plmn_entry->mcc,
883 plmn_entry->mnc, plmn_entry->cause);
888 /* if no more PLMN in list */
890 LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
891 gsm322_a_no_more_plmn(ms, msg);
896 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
897 "mnc=%s %s, %s)\n", plmn->plmn_curr,
898 gsm_print_mcc(plmn_next->mcc), gsm_print_mnc(plmn_next->mnc),
899 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
901 /* set next network */
902 plmn->mcc = plmn_next->mcc;
903 plmn->mnc = plmn_next->mnc;
905 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
907 /* indicate New PLMN */
908 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
911 gsm322_cs_sendmsg(ms, nmsg);
916 /* User re-selection event */
917 static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg)
919 struct gsm322_plmn *plmn = &ms->plmn;
920 struct gsm_subscriber *subscr = &ms->subscr;
921 struct gsm48_rrlayer *rr = &ms->rrlayer;
922 struct gsm322_plmn_list *plmn_entry;
923 struct gsm322_plmn_list *plmn_found = NULL;
925 if (!subscr->sim_valid) {
929 /* try again later, if not idle */
930 if (rr->state != GSM48_RR_ST_IDLE) {
931 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
936 /* search current PLMN in list */
937 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
938 if (plmn_entry->mcc == plmn->mcc
939 && plmn_entry->mnc == plmn->mnc)
940 plmn_found = plmn_entry;
944 /* abort if list is empty */
946 LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
950 LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
951 "and restarting PLMN search process.\n");
953 /* move entry to end of list */
954 llist_del(&plmn_found->entry);
955 llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
957 /* select first PLMN in list */
958 return gsm322_a_sel_first_plmn(ms, msg);
961 /* PLMN becomes available */
962 static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
964 struct gsm322_plmn *plmn = &ms->plmn;
965 struct gsm_subscriber *subscr = &ms->subscr;
966 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
968 if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
969 && subscr->plmn_mnc == gm->mnc) {
973 LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n");
974 return gsm322_a_go_on_plmn(ms, msg);
976 /* select first PLMN in list */
977 LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
978 "search process.\n");
979 return gsm322_a_sel_first_plmn(ms, msg);
983 /* loss of radio coverage */
984 static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
986 struct gsm322_plmn *plmn = &ms->plmn;
987 struct gsm322_cellsel *cs = &ms->cellsel;
991 /* any PLMN available */
992 found = gsm322_cs_select(ms, 0);
994 /* if PLMN in list */
996 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s "
997 "%s, %s)\n", gsm_print_mcc(
998 cs->list[found].sysinfo->mcc),
999 gsm_print_mnc(cs->list[found].sysinfo->mnc),
1000 gsm_get_mcc(cs->list[found].sysinfo->mcc),
1001 gsm_get_mnc(cs->list[found].sysinfo->mcc,
1002 cs->list[found].sysinfo->mnc));
1003 return gsm322_a_sel_first_plmn(ms, msg);
1006 LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
1008 plmn->mcc = plmn->mnc = 0;
1010 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
1012 /* Tell cell selection process to handle "no cell found". */
1013 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1016 gsm322_cs_sendmsg(ms, nmsg);
1021 /* MS is switched on OR SIM is inserted OR removed */
1022 static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1024 struct gsm_subscriber *subscr = &ms->subscr;
1025 struct gsm322_plmn *plmn = &ms->plmn;
1028 if (!subscr->sim_valid) {
1029 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1030 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1031 new_a_state(plmn, GSM322_A6_NO_SIM);
1036 /* if there is a registered PLMN */
1037 if (subscr->plmn_valid) {
1038 /* select the registered PLMN */
1039 plmn->mcc = subscr->plmn_mcc;
1040 plmn->mnc = subscr->plmn_mnc;
1042 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1043 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1044 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1045 gsm_get_mnc(plmn->mcc, plmn->mnc));
1046 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1047 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1048 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1049 gsm_get_mnc(plmn->mcc, plmn->mnc));
1051 new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
1053 /* indicate New PLMN */
1054 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1057 gsm322_cs_sendmsg(ms, nmsg);
1062 /* initiate search at cell selection */
1063 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1064 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1066 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1069 gsm322_cs_sendmsg(ms, nmsg);
1074 /* MS is switched off */
1075 static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1077 struct gsm322_plmn *plmn = &ms->plmn;
1079 new_a_state(plmn, GSM322_A0_NULL);
1084 static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1086 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1090 /* SIM is removed */
1091 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1095 /* indicate SIM remove to cell selection process */
1096 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1099 gsm322_cs_sendmsg(ms, nmsg);
1101 return gsm322_a_switch_on(ms, msg);
1104 /* location update response: "Roaming not allowed" */
1105 static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
1107 /* store in list of forbidden LAs is done in gsm48* */
1109 return gsm322_a_sel_first_plmn(ms, msg);
1112 /* On VPLMN of home country and timeout occurs */
1113 static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg)
1115 struct gsm48_rrlayer *rr = &ms->rrlayer;
1116 struct gsm322_plmn *plmn = &ms->plmn;
1117 struct gsm322_cellsel *cs = &ms->cellsel;
1120 /* try again later, if not idle and not camping */
1121 if (rr->state != GSM48_RR_ST_IDLE
1122 || cs->state != GSM322_C3_CAMPED_NORMALLY) {
1123 LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n");
1124 start_plmn_timer(plmn, 60);
1129 new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
1131 /* initiate search at cell selection */
1132 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1135 gsm322_cs_sendmsg(ms, nmsg);
1140 /* manual mode selected */
1141 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
1145 /* restart state machine */
1146 gsm322_a_switch_off(ms, msg);
1147 ms->settings.plmn_mode = PLMN_MODE_MANUAL;
1148 gsm322_m_switch_on(ms, msg);
1150 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1153 gsm48_mmevent_msg(ms, nmsg);
1159 * handler for manual search
1162 /* display PLMNs and to Not on PLMN */
1163 static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
1165 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1166 int msg_type = gm->msg_type;
1167 struct gsm322_plmn *plmn = &ms->plmn;
1168 struct gsm_sub_plmn_list *temp;
1171 gsm322_sort_list(ms);
1173 vty_notify(ms, NULL);
1175 case GSM322_EVENT_REG_FAILED:
1176 vty_notify(ms, "Failed to register to network %s, %s "
1178 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1179 gsm_get_mcc(plmn->mcc),
1180 gsm_get_mnc(plmn->mcc, plmn->mnc));
1182 case GSM322_EVENT_NO_CELL_FOUND:
1183 vty_notify(ms, "No cell found for network %s, %s "
1185 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1186 gsm_get_mcc(plmn->mcc),
1187 gsm_get_mnc(plmn->mcc, plmn->mnc));
1189 case GSM322_EVENT_ROAMING_NA:
1190 vty_notify(ms, "Roaming not allowed to network %s, %s "
1192 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1193 gsm_get_mcc(plmn->mcc),
1194 gsm_get_mnc(plmn->mcc, plmn->mnc));
1198 if (llist_empty(&plmn->sorted_plmn))
1199 vty_notify(ms, "Search network!\n");
1201 vty_notify(ms, "Search or select from network:\n");
1202 llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
1203 vty_notify(ms, " Network %s, %s (%s, %s)\n",
1204 gsm_print_mcc(temp->mcc),
1205 gsm_print_mnc(temp->mnc),
1206 gsm_get_mcc(temp->mcc),
1207 gsm_get_mnc(temp->mcc, temp->mnc));
1210 /* go Not on PLMN state */
1211 new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
1216 /* user starts reselection */
1217 static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg)
1219 struct gsm_subscriber *subscr = &ms->subscr;
1220 struct gsm48_rrlayer *rr = &ms->rrlayer;
1223 if (!subscr->sim_valid) {
1227 /* try again later, if not idle */
1228 if (rr->state != GSM48_RR_ST_IDLE) {
1229 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
1234 /* initiate search at cell selection */
1235 vty_notify(ms, NULL);
1236 vty_notify(ms, "Searching Network, please wait...\n");
1237 LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n");
1239 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1242 gsm322_cs_sendmsg(ms, nmsg);
1247 /* MS is switched on OR SIM is inserted OR removed */
1248 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1250 struct gsm_subscriber *subscr = &ms->subscr;
1251 struct gsm322_plmn *plmn = &ms->plmn;
1254 if (!subscr->sim_valid) {
1255 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1256 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1257 new_m_state(plmn, GSM322_M5_NO_SIM);
1262 /* if there is a registered PLMN */
1263 if (subscr->plmn_valid) {
1266 /* select the registered PLMN */
1267 plmn->mcc = subscr->plmn_mcc;
1268 plmn->mnc = subscr->plmn_mnc;
1270 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1271 "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
1272 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1273 gsm_get_mnc(plmn->mcc, plmn->mnc));
1274 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
1275 "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1276 gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1277 gsm_get_mnc(plmn->mcc, plmn->mnc));
1279 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1281 /* indicate New PLMN */
1282 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1285 gsm322_cs_sendmsg(ms, nmsg);
1290 /* initiate search at cell selection */
1291 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1292 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1293 vty_notify(ms, NULL);
1294 vty_notify(ms, "Searching Network, please wait...\n");
1296 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1299 gsm322_cs_sendmsg(ms, nmsg);
1304 /* MS is switched off */
1305 static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1307 struct gsm322_plmn *plmn = &ms->plmn;
1309 stop_plmn_timer(plmn);
1311 new_m_state(plmn, GSM322_M0_NULL);
1316 static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1318 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1322 /* SIM is removed */
1323 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1325 struct gsm322_plmn *plmn = &ms->plmn;
1328 stop_plmn_timer(plmn);
1330 /* indicate SIM remove to cell selection process */
1331 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1334 gsm322_cs_sendmsg(ms, nmsg);
1336 return gsm322_m_switch_on(ms, msg);
1339 /* go to On PLMN state */
1340 static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1342 struct gsm322_plmn *plmn = &ms->plmn;
1343 struct gsm_subscriber *subscr = &ms->subscr;
1345 /* if selected PLMN is in list of forbidden PLMNs */
1346 gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc);
1348 /* set last registered PLMN */
1349 subscr->plmn_valid = 1;
1350 subscr->plmn_mcc = plmn->mcc;
1351 subscr->plmn_mnc = plmn->mnc;
1356 new_m_state(plmn, GSM322_M2_ON_PLMN);
1361 /* indicate selected PLMN */
1362 static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
1364 struct gsm322_plmn *plmn = &ms->plmn;
1366 vty_notify(ms, NULL);
1367 vty_notify(ms, "Selected Network: %s, %s\n",
1368 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1370 return gsm322_m_go_on_plmn(ms, msg);
1373 /* previously selected PLMN becomes available again */
1374 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1376 struct gsm322_plmn *plmn = &ms->plmn;
1377 struct gsm322_cellsel *cs = &ms->cellsel;
1379 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1381 if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
1384 LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
1385 "selected, so start selection.\n");
1387 /* indicate New PLMN */
1388 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1391 gsm322_cs_sendmsg(ms, nmsg);
1397 /* the user has selected given PLMN */
1398 static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
1400 struct gsm322_plmn *plmn = &ms->plmn;
1401 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1404 /* use user selection */
1405 plmn->mcc = gm->mcc;
1406 plmn->mnc = gm->mnc;
1408 vty_notify(ms, NULL);
1409 vty_notify(ms, "Selected Network: %s, %s\n",
1410 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1411 LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s "
1412 "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1413 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1415 new_m_state(plmn, GSM322_M4_TRYING_PLMN);
1417 /* indicate New PLMN */
1418 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1421 gsm322_cs_sendmsg(ms, nmsg);
1426 /* auto mode selected */
1427 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
1431 /* restart state machine */
1432 gsm322_m_switch_off(ms, msg);
1433 ms->settings.plmn_mode = PLMN_MODE_AUTO;
1434 gsm322_a_switch_on(ms, msg);
1436 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1439 gsm48_mmevent_msg(ms, nmsg);
1444 /* if no cell is found in other states than in *_TRYING_* states */
1445 static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
1449 /* Tell cell selection process to handle "no cell found". */
1450 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1453 gsm322_cs_sendmsg(ms, nmsg);
1459 * cell scanning process
1462 /* select a suitable and allowable cell */
1463 static int gsm322_cs_select(struct osmocom_ms *ms, int any)
1465 struct gsm322_cellsel *cs = &ms->cellsel;
1466 struct gsm_subscriber *subscr = &ms->subscr;
1467 struct gsm48_sysinfo *s;
1468 int i, found = -1, power = 0;
1469 uint8_t flags, mask;
1472 /* set out access class depending on the cell selection type */
1474 acc_class = subscr->acc_class | 0x0400; /* add emergency */
1475 LOGP(DCS, LOGL_INFO, "Select using access class with Emergency "
1478 acc_class = subscr->acc_class;
1479 LOGP(DCS, LOGL_INFO, "Select using access class \n");
1482 /* flags to match */
1483 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1484 | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
1485 if (cs->state == GSM322_C2_STORED_CELL_SEL
1486 || cs->state == GSM322_C5_CHOOSE_CELL)
1487 mask |= GSM322_CS_FLAG_BA;
1488 flags = mask; /* all masked flags are requied */
1490 /* loop through all scanned frequencies and select cell */
1491 for (i = 0; i <= 1023; i++) {
1492 cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
1493 s = cs->list[i].sysinfo;
1495 /* channel has no informations for us */
1496 if (!s || (cs->list[i].flags & mask) != flags) {
1500 /* check C1 criteria not fullfilled */
1501 // TODO: C1 is also dependant on power class and max power
1502 if (cs->list[i].rxlev_db < s->rxlev_acc_min_db) {
1503 LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
1504 "not met. (rxlev=%d < min=%d)\n", i,
1505 cs->list[i].rxlev_db, s->rxlev_acc_min_db);
1509 /* if cell is barred and we don't override */
1510 if (!subscr->acc_barr
1511 && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
1512 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
1517 /* if cell is in list of forbidden LAs */
1518 if (!subscr->acc_barr
1519 && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
1520 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1521 "list of forbidden LAs. (mcc=%s mnc=%s "
1522 "lai=%04x)\n", i, gsm_print_mcc(s->mcc),
1523 gsm_print_mnc(s->mnc), s->lac);
1527 /* if we have no access to the cell and we don't override */
1528 if (!subscr->acc_barr
1529 && !(acc_class & (s->class_barr ^ 0xffff))) {
1530 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
1531 "barred for out access. (access=%04x "
1532 "barred=%04x)\n", i, acc_class, s->class_barr);
1536 /* store temporary available and allowable flag */
1537 cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
1539 /* if we search a specific PLMN, but it does not match */
1540 if (!any && cs->mcc && (cs->mcc != s->mcc
1541 || cs->mnc != s->mnc)) {
1542 LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
1543 "does not match target PLMN. (mcc=%s "
1544 "mnc=%s)\n", i, gsm_print_mcc(s->mcc),
1545 gsm_print_mnc(s->mnc));
1549 LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%d "
1550 "mcc=%s mnc=%s lac=%04x %s, %s)\n", i,
1551 cs->list[i].rxlev_db, gsm_print_mcc(s->mcc),
1552 gsm_print_mnc(s->mnc), s->lac, gsm_get_mcc(s->mcc),
1553 gsm_get_mnc(s->mcc, s->mnc));
1555 /* find highest power cell */
1556 if (found < 0 || cs->list[i].rxlev_db > power) {
1557 power = cs->list[i].rxlev_db;
1563 LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
1568 /* tune to first/next unscanned frequency and search for PLMN */
1569 static int gsm322_cs_scan(struct osmocom_ms *ms)
1571 struct gsm322_cellsel *cs = &ms->cellsel;
1573 uint8_t mask, flags;
1574 uint32_t weight = 0, test = cs->scan_state;
1576 /* search for strongest unscanned cell */
1577 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1578 | GSM322_CS_FLAG_SIGNAL;
1579 if (cs->state == GSM322_C2_STORED_CELL_SEL
1580 || cs->state == GSM322_C5_CHOOSE_CELL)
1581 mask |= GSM322_CS_FLAG_BA;
1582 flags = mask; /* all masked flags are requied */
1583 for (i = 0; i <= 1023; i++) {
1584 /* skip if band has enough frequencies scanned (3.2.1) */
1585 for (j = 0; gsm_sup_smax[j].max; j++) {
1586 if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
1587 if (gsm_sup_smax[j].start >= i
1588 && gsm_sup_smax[j].end <= i)
1591 if (gsm_sup_smax[j].end <= i
1592 || gsm_sup_smax[j].start >= i)
1596 if (gsm_sup_smax[j].max) {
1597 if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
1600 /* search for unscanned frequency */
1601 if ((cs->list[i].flags & mask) == flags) {
1602 /* weight depends on the power level
1603 * if it is the same, it depends on arfcn
1605 test = cs->list[i].rxlev_db + 128;
1606 test = (test << 16) | i;
1607 if (test >= cs->scan_state)
1613 cs->scan_state = weight;
1616 gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs,
1619 /* special case for PLMN search */
1620 if (cs->state == GSM322_PLMN_SEARCH && !weight) {
1623 /* create AA flag */
1624 cs->mcc = cs->mnc = 0;
1625 gsm322_cs_select(ms, 0);
1627 new_c_state(cs, GSM322_C0_NULL);
1629 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END);
1630 LOGP(DCS, LOGL_INFO, "PLMN search finished.\n");
1633 gsm322_plmn_sendmsg(ms, nmsg);
1638 /* special case for HPLMN search */
1639 if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
1642 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1643 LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n");
1646 gsm322_plmn_sendmsg(ms, nmsg);
1648 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
1650 cs->arfcn = cs->sel_arfcn;
1651 LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev "
1652 "%d).\n", cs->arfcn, cs->list[cs->arfcn].rxlev_db);
1653 cs->ccch_state = GSM322_CCCH_ST_INIT;
1655 gsm322_sync_to_cell(ms, cs);
1656 // start_cs_timer(cs, ms->support.sync_to, 0);
1661 /* if all frequencies have been searched */
1667 LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
1669 /* just see, if we search for any cell */
1670 if (cs->state == GSM322_C6_ANY_CELL_SEL
1671 || cs->state == GSM322_C8_ANY_CELL_RESEL
1672 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1675 found = gsm322_cs_select(ms, any);
1679 struct gsm322_plmn *plmn = &ms->plmn;
1681 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1684 cs->si = cs->list[cs->arfcn].sysinfo;
1685 cs->ccch_state = GSM322_CCCH_ST_INIT;
1687 gsm322_sync_to_cell(ms, cs);
1689 /* selected PLMN (manual) or any PLMN (auto) */
1690 switch (ms->settings.plmn_mode) {
1691 case PLMN_MODE_AUTO:
1692 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1693 /* PLMN becomes available */
1694 nmsg = gsm322_msgb_alloc(
1695 GSM322_EVENT_PLMN_AVAIL);
1698 gsm322_plmn_sendmsg(ms, nmsg);
1701 case PLMN_MODE_MANUAL:
1702 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1703 && gsm322_is_plmn_avail(cs, plmn->mcc,
1705 /* PLMN becomes available */
1706 nmsg = gsm322_msgb_alloc(
1707 GSM322_EVENT_PLMN_AVAIL);
1710 gsm322_plmn_sendmsg(ms, nmsg);
1715 /* set selected cell */
1717 cs->sel_arfcn = cs->arfcn;
1718 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1719 cs->sel_mcc = cs->si->mcc;
1720 cs->sel_mnc = cs->si->mnc;
1721 cs->sel_lac = cs->si->lac;
1722 cs->sel_id = cs->si->cell_id;
1724 /* tell CS process about available cell */
1725 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1726 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1729 /* unset selected cell */
1730 gsm322_unselect_cell(cs);
1732 /* tell CS process about no cell available */
1733 LOGP(DCS, LOGL_INFO, "No cell available.\n");
1734 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1738 gsm322_c_event(ms, nmsg);
1744 /* NOTE: We might already have system information from previous
1745 * scan. But we need recent informations, so we scan again!
1748 /* Tune to frequency for a while, to receive broadcasts. */
1749 cs->arfcn = weight & 1023;
1750 LOGP(DCS, LOGL_INFO, "Scanning frequency %d (rxlev %d).\n", cs->arfcn,
1751 cs->list[cs->arfcn].rxlev_db);
1752 cs->ccch_state = GSM322_CCCH_ST_INIT;
1754 gsm322_sync_to_cell(ms, cs);
1755 // start_cs_timer(cs, ms->support.sync_to, 0);
1757 /* Allocate/clean system information. */
1758 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1759 if (cs->list[cs->arfcn].sysinfo)
1760 memset(cs->list[cs->arfcn].sysinfo, 0,
1761 sizeof(struct gsm48_sysinfo));
1763 cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
1764 struct gsm48_sysinfo);
1765 if (!cs->list[cs->arfcn].sysinfo)
1767 cs->si = cs->list[cs->arfcn].sysinfo;
1769 /* increase scan counter for each maximum scan range */
1770 if (gsm_sup_smax[j].max) {
1771 LOGP(DCS, LOGL_INFO, "%d frequencies left in band %d..%d\n",
1772 gsm_sup_smax[j].max - gsm_sup_smax[j].temp,
1773 gsm_sup_smax[j].start, gsm_sup_smax[j].end);
1774 gsm_sup_smax[j].temp++;
1780 /* check if cell is now suitable and allowable */
1781 static int gsm322_cs_store(struct osmocom_ms *ms)
1783 struct gsm322_cellsel *cs = &ms->cellsel;
1784 struct gsm48_sysinfo *s = cs->si;
1785 struct gsm322_plmn *plmn = &ms->plmn;
1789 if (cs->state != GSM322_C2_STORED_CELL_SEL
1790 && cs->state != GSM322_C1_NORMAL_CELL_SEL
1791 && cs->state != GSM322_C6_ANY_CELL_SEL
1792 && cs->state != GSM322_C4_NORMAL_CELL_RESEL
1793 && cs->state != GSM322_C8_ANY_CELL_RESEL
1794 && cs->state != GSM322_C5_CHOOSE_CELL
1795 && cs->state != GSM322_C9_CHOOSE_ANY_CELL
1796 && cs->state != GSM322_PLMN_SEARCH
1797 && cs->state != GSM322_HPLMN_SEARCH) {
1798 LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
1799 "(re-)selection, please fix!\n");
1804 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO;
1806 && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp &&
1807 cs->list[cs->arfcn].sysinfo->sp_cbq))
1808 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED;
1810 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED;
1813 cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db;
1814 cs->list[cs->arfcn].class_barr = s->class_barr;
1815 cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch;
1818 /* store selected network */
1821 cs->list[cs->arfcn].mcc = s->mcc;
1822 cs->list[cs->arfcn].mnc = s->mnc;
1823 cs->list[cs->arfcn].lac = s->lac;
1826 if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
1827 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD;
1829 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD;
1832 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev=%d "
1833 "mcc=%s mnc=%s lac=%04x)\n", cs->arfcn,
1834 cs->list[cs->arfcn].rxlev_db, gsm_print_mcc(s->mcc),
1835 gsm_print_mnc(s->mnc), s->lac);
1837 /* special case for PLMN search */
1838 if (cs->state == GSM322_PLMN_SEARCH)
1839 /* tune to next cell */
1840 return gsm322_cs_scan(ms);
1842 /* special case for HPLMN search */
1843 if (cs->state == GSM322_HPLMN_SEARCH) {
1844 struct gsm_subscriber *subscr = &ms->subscr;
1847 if (!gsm322_is_hplmn_avail(cs, subscr->imsi))
1848 /* tune to next cell */
1849 return gsm322_cs_scan(ms);
1851 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1852 LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n");
1855 gsm322_plmn_sendmsg(ms, nmsg);
1860 /* just see, if we search for any cell */
1861 if (cs->state == GSM322_C6_ANY_CELL_SEL
1862 || cs->state == GSM322_C8_ANY_CELL_RESEL
1863 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1866 found = gsm322_cs_select(ms, any);
1870 LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n");
1871 /* tune to next cell */
1872 return gsm322_cs_scan(ms);
1875 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1878 cs->si = cs->list[cs->arfcn].sysinfo;
1879 cs->ccch_state = GSM322_CCCH_ST_INIT;
1881 gsm322_sync_to_cell(ms, cs);
1883 /* selected PLMN (manual) or any PLMN (auto) */
1884 switch (ms->settings.plmn_mode) {
1885 case PLMN_MODE_AUTO:
1886 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1887 /* PLMN becomes available */
1888 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1891 gsm322_plmn_sendmsg(ms, nmsg);
1894 case PLMN_MODE_MANUAL:
1895 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1896 && gsm322_is_plmn_avail(cs, plmn->mcc,
1898 /* PLMN becomes available */
1899 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1902 gsm322_plmn_sendmsg(ms, nmsg);
1907 /* set selected cell */
1909 cs->sel_arfcn = cs->arfcn;
1910 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1911 cs->sel_mcc = cs->si->mcc;
1912 cs->sel_mnc = cs->si->mnc;
1913 cs->sel_lac = cs->si->lac;
1914 cs->sel_id = cs->si->cell_id;
1916 /* tell CS process about available cell */
1917 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1918 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1921 gsm322_c_event(ms, nmsg);
1927 /* process system information when returing to idle mode */
1928 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
1930 struct gsm322_cellsel *cs = &ms->cellsel;
1931 struct gsm48_sysinfo *s = cs->si;
1932 struct gsm322_ba_list *ba = NULL;
1936 /* collect system information received during dedicated mode */
1938 && (!s->nb_ext_ind_si5
1939 || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
1940 || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
1941 && s->nb_ext_ind_si5bis))) {
1942 /* find or create ba list */
1943 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1945 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1950 llist_add_tail(&ba->entry, &cs->ba_list);
1952 /* update (add) ba list */
1953 memcpy(freq, ba->freq, sizeof(freq));
1954 for (i = 0; i <= 1023; i++) {
1955 if ((s->freq[i].mask & FREQ_TYPE_REP))
1956 freq[i >> 3] |= (1 << (i & 7));
1958 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
1959 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
1960 "%s, %s).\n", gsm_print_mcc(ba->mcc),
1961 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
1962 gsm_get_mnc(ba->mcc, ba->mnc));
1963 memcpy(ba->freq, freq, sizeof(freq));
1970 /* store BA whenever a system informations changes */
1971 static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
1972 struct gsm48_sysinfo *s)
1974 struct gsm322_ba_list *ba;
1978 /* find or create ba list */
1979 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1981 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1986 llist_add_tail(&ba->entry, &cs->ba_list);
1988 /* update ba list */
1989 memset(freq, 0, sizeof(freq));
1990 freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
1991 for (i = 0; i <= 1023; i++) {
1992 if ((s->freq[i].mask &
1993 (FREQ_TYPE_SERV | FREQ_TYPE_NCELL)))
1994 freq[i >> 3] |= (1 << (i & 7));
1996 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
1997 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
1998 "%s, %s).\n", gsm_print_mcc(ba->mcc),
1999 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2000 gsm_get_mnc(ba->mcc, ba->mnc));
2001 memcpy(ba->freq, freq, sizeof(freq));
2007 /* process system information during camping on a cell */
2008 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2010 struct gsm48_rrlayer *rr = &ms->rrlayer;
2011 struct gsm322_cellsel *cs = &ms->cellsel;
2012 struct gsm48_sysinfo *s = cs->si;
2013 struct gsm_subscriber *subscr = &ms->subscr;
2014 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2017 if (rr->state != GSM48_RR_ST_IDLE) {
2018 LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n");
2022 /* Store BA if we have full system info about cells and neigbor cells.
2023 * Depending on the extended bit in the channel description,
2024 * we require more or less system informations about neighbor cells
2028 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2029 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2030 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2031 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2034 && (!s->nb_ext_ind_si2
2035 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2036 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2037 && s->nb_ext_ind_si2bis)))
2038 gsm322_store_ba_list(cs, s);
2040 /* update sel_si, if all relevant system informations received */
2041 if (s->si1 && s->si2 && s->si3
2042 && (!s->nb_ext_ind_si2
2043 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2044 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2045 && s->nb_ext_ind_si2bis))) {
2047 LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is "
2049 memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
2050 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2054 /* check for barred cell */
2055 if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
2056 /* check if cell becomes barred */
2057 if (!subscr->acc_barr && s->cell_barr
2058 && !(cs->list[cs->arfcn].sysinfo
2059 && cs->list[cs->arfcn].sysinfo->sp
2060 && cs->list[cs->arfcn].sysinfo->sp_cbq)) {
2061 LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
2063 /* mark cell as unscanned */
2064 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2065 if (cs->list[cs->arfcn].sysinfo) {
2066 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2068 talloc_free(cs->list[cs->arfcn].sysinfo);
2069 cs->list[cs->arfcn].sysinfo = NULL;
2070 gsm322_unselect_cell(cs);
2072 /* trigger reselection without queueing,
2073 * because other sysinfo message may be queued
2076 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2079 gsm322_c_event(ms, nmsg);
2084 /* check if cell access becomes barred */
2085 if (!((subscr->acc_class & 0xfbff)
2086 & (s->class_barr ^ 0xffff))) {
2087 LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
2092 /* check if MCC, MNC, LAC, cell ID changes */
2093 if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc
2094 || cs->sel_lac != s->lac) {
2095 LOGP(DCS, LOGL_NOTICE, "Cell changes location area. "
2096 "This is not good!\n");
2099 if (cs->sel_id != s->cell_id) {
2100 LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. "
2101 "This is not good!\n");
2108 /* process system information during channel scanning */
2109 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2111 struct gsm322_cellsel *cs = &ms->cellsel;
2112 struct gsm48_sysinfo *s = cs->si;
2113 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2115 /* no sysinfo if we are not done with power scan */
2116 if (cs->powerscan) {
2117 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
2121 /* Store BA if we have full system info about cells and neigbor cells.
2122 * Depending on the extended bit in the channel description,
2123 * we require more or less system informations about neighbor cells
2127 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2128 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2129 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2130 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2133 && (!s->nb_ext_ind_si2
2134 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2135 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2136 && s->nb_ext_ind_si2bis)))
2137 gsm322_store_ba_list(cs, s);
2139 /* all relevant system informations received */
2140 if (s->si1 && s->si2 && s->si3
2141 && (!s->nb_ext_ind_si2
2142 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2143 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2144 && s->nb_ext_ind_si2bis))) {
2145 LOGP(DCS, LOGL_INFO, "Received relevant sysinfo.\n");
2149 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2151 /* store sysinfo and continue scan */
2152 return gsm322_cs_store(ms);
2155 /* wait for more sysinfo or timeout */
2159 static void gsm322_cs_timeout(void *arg)
2161 struct gsm322_cellsel *cs = arg;
2162 struct osmocom_ms *ms = cs->ms;
2164 LOGP(DCS, LOGL_INFO, "Cell selection failed.\n");
2166 /* if we have no lock, we retry */
2167 if (cs->ccch_state != GSM322_CCCH_ST_SYNC)
2168 LOGP(DCS, LOGL_INFO, "Sync timeout.\n");
2170 LOGP(DCS, LOGL_INFO, "Read timeout.\n");
2172 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev=%d)\n",
2173 cs->arfcn, cs->list[cs->arfcn].rxlev_db);
2175 /* remove system information */
2176 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2177 if (cs->list[cs->arfcn].sysinfo) {
2178 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", cs->arfcn);
2179 talloc_free(cs->list[cs->arfcn].sysinfo);
2180 cs->list[cs->arfcn].sysinfo = NULL;
2181 gsm322_unselect_cell(cs);
2184 /* tune to next cell */
2191 * power scan process
2194 /* search for block of unscanned frequencies and start scanning */
2195 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
2197 struct gsm322_cellsel *cs = &ms->cellsel;
2199 uint8_t mask, flags;
2203 /* search for first frequency to scan */
2204 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
2205 flags = GSM322_CS_FLAG_SUPPORT;
2206 if (cs->state == GSM322_C2_STORED_CELL_SEL
2207 || cs->state == GSM322_C5_CHOOSE_CELL) {
2208 LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA list.\n");
2209 mask |= GSM322_CS_FLAG_BA;
2210 flags |= GSM322_CS_FLAG_BA;
2212 LOGP(DCS, LOGL_FATAL, "Scanning power for all frequencies.\n");
2213 for (i = 0; i <= 1023; i++) {
2214 if ((cs->list[i].flags & mask) == flags) {
2220 /* if there is no more frequency, we can tune to that cell */
2224 /* stop power level scanning */
2227 /* check if not signal is found */
2228 for (i = 0; i <= 1023; i++) {
2229 if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2235 LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
2236 /* on normal cell selection, start over */
2237 if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
2238 for (i = 0; i <= 1023; i++) {
2239 /* clear flag that this was scanned */
2240 cs->list[i].flags &=
2241 ~(GSM322_CS_FLAG_POWER
2242 | GSM322_CS_FLAG_SIGNAL
2243 | GSM322_CS_FLAG_SYSINFO);
2244 if (cs->list[i].sysinfo) {
2245 LOGP(DCS, LOGL_INFO, "free "
2246 "sysinfo arfcn=%d\n",
2249 cs->list[i].sysinfo);
2250 cs->list[i].sysinfo = NULL;
2253 /* no cell selected */
2254 gsm322_unselect_cell(cs);
2257 /* on other cell selection, indicate "no cell found" */
2258 /* NOTE: PLMN search process handles it.
2259 * If not handled there, CS process gets indicated.
2260 * If we would continue to process CS, then we might get
2261 * our list of scanned cells disturbed.
2263 if (cs->state == GSM322_PLMN_SEARCH)
2264 nmsg = gsm322_msgb_alloc(
2265 GSM322_EVENT_PLMN_SEARCH_END);
2267 nmsg = gsm322_msgb_alloc(
2268 GSM322_EVENT_NO_CELL_FOUND);
2271 gsm322_plmn_sendmsg(ms, nmsg);
2273 /* if HPLMN search, select last frequency */
2274 if (cs->state == GSM322_HPLMN_SEARCH) {
2275 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2277 cs->arfcn = cs->sel_arfcn;
2278 LOGP(DCS, LOGL_INFO, "Tuning back to frequency "
2279 "%d (rxlev %d).\n", cs->arfcn,
2280 cs->list[cs->arfcn].rxlev_db);
2281 cs->ccch_state = GSM322_CCCH_ST_INIT;
2283 gsm322_sync_to_cell(ms, cs);
2284 // start_cs_timer(cs, ms->support.sync_to, 0);
2287 new_c_state(cs, GSM322_C0_NULL);
2291 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
2292 cs->scan_state = 0xffffffff; /* higher than high */
2293 /* clear counter of scanned frequencies of each range */
2294 for (i = 0; gsm_sup_smax[i].max; i++)
2295 gsm_sup_smax[i].temp = 0;
2296 return gsm322_cs_scan(ms);
2299 /* search last frequency to scan (en block) */
2301 for (i = s + 1; i <= 1023; i++) {
2302 if ((cs->list[i].flags & mask) == flags)
2308 LOGP(DCS, LOGL_INFO, "Scanning frequencies. (%d..%d)\n", s, e);
2310 /* start scan on radio interface */
2312 #warning TESTING!!!!
2314 return l1ctl_tx_pm_req_range(ms, s, e);
2317 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
2318 void *handler_data, void *signal_data)
2320 struct osmocom_ms *ms;
2321 struct gsm322_cellsel *cs;
2322 struct osmobb_meas_res *mr;
2323 struct osmobb_rach_conf *rc;
2327 if (subsys != SS_L1CTL)
2331 case S_L1CTL_PM_RES:
2337 i = mr->band_arfcn & 1023;
2338 rxlev_db = mr->rx_lev - 110;
2339 cs->list[i].rxlev_db = rxlev_db;
2340 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2341 if (rxlev_db >= ms->support.min_rxlev_db) {
2342 cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2343 LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2344 "rxlev %d)\n", i, cs->list[i].rxlev_db);
2347 case S_L1CTL_PM_DONE:
2348 LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
2353 gsm322_cs_powerscan(ms);
2355 case S_L1CTL_FBSB_RESP:
2358 if (cs->ccch_state == GSM322_CCCH_ST_INIT) {
2359 LOGP(DCS, LOGL_INFO, "Channel synched.\n");
2360 cs->ccch_state = GSM322_CCCH_ST_SYNC;
2364 /* in dedicated mode */
2365 if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
2366 return gsm48_rr_tx_rand_acc(ms, NULL);
2369 /* set timer for reading BCCH */
2370 if (cs->state == GSM322_C2_STORED_CELL_SEL
2371 || cs->state == GSM322_C1_NORMAL_CELL_SEL
2372 || cs->state == GSM322_C6_ANY_CELL_SEL
2373 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2374 || cs->state == GSM322_C8_ANY_CELL_RESEL
2375 || cs->state == GSM322_C5_CHOOSE_CELL
2376 || cs->state == GSM322_C9_CHOOSE_ANY_CELL
2377 || cs->state == GSM322_PLMN_SEARCH
2378 || cs->state == GSM322_HPLMN_SEARCH)
2379 start_cs_timer(cs, ms->support.scan_to, 0);
2380 // TODO: timer depends on BCCH config
2383 case S_L1CTL_FBSB_ERR:
2387 gsm322_sync_to_cell(ms, cs);
2389 LOGP(DCS, LOGL_INFO, "Channel sync error, try again.\n");
2392 LOGP(DCS, LOGL_INFO, "Channel sync error.\n");
2400 gsm322_cs_timeout(cs);
2402 case S_L1CTL_RACH_CONF:
2405 gsm48_rr_rach_conf(ms, rc->fn);
2412 static void gsm322_cs_loss(void *arg)
2414 struct gsm322_cellsel *cs = arg;
2415 struct osmocom_ms *ms = cs->ms;
2416 struct gsm48_rrlayer *rr = &ms->rrlayer;
2418 LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n");
2419 if (cs->state == GSM322_C3_CAMPED_NORMALLY
2420 || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
2421 if (rr->state == GSM48_RR_ST_IDLE) {
2424 LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n");
2426 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2429 gsm322_c_event(ms, nmsg);
2432 LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n");
2434 /* be shure that nothing else is done after here
2435 * because the function call above may cause
2436 * to return from idle state and trigger cell re-sel.
2445 * handler for cell selection process
2448 /* start PLMN search */
2449 static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg)
2451 struct gsm322_cellsel *cs = &ms->cellsel;
2454 new_c_state(cs, GSM322_PLMN_SEARCH);
2456 /* mark all frequencies except our own BA to be scanned */
2457 for (i = 0; i <= 1023; i++) {
2458 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2459 | GSM322_CS_FLAG_SIGNAL
2460 | GSM322_CS_FLAG_SYSINFO);
2461 if (cs->list[i].sysinfo) {
2462 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2463 talloc_free(cs->list[i].sysinfo);
2464 cs->list[i].sysinfo = NULL;
2465 gsm322_unselect_cell(cs);
2469 /* unset selected cell */
2470 gsm322_unselect_cell(cs);
2472 /* start power scan */
2473 return gsm322_cs_powerscan(ms);
2476 /* start HPLMN search */
2477 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2479 struct gsm322_cellsel *cs = &ms->cellsel;
2482 new_c_state(cs, GSM322_HPLMN_SEARCH);
2484 /* mark all frequencies except our own BA to be scanned */
2485 for (i = 0; i <= 1023; i++) {
2486 if (i != cs->sel_arfcn
2487 && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2488 && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2489 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2490 | GSM322_CS_FLAG_SIGNAL
2491 | GSM322_CS_FLAG_SYSINFO);
2492 if (cs->list[i].sysinfo) {
2493 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2495 talloc_free(cs->list[i].sysinfo);
2496 cs->list[i].sysinfo = NULL;
2501 /* no cell selected */
2502 gsm322_unselect_cell(cs);
2504 /* start power scan */
2505 return gsm322_cs_powerscan(ms);
2508 /* start stored cell selection */
2509 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2511 struct gsm322_cellsel *cs = &ms->cellsel;
2514 new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2516 /* flag all frequencies that are in current band allocation */
2517 for (i = 0; i <= 1023; i++) {
2518 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2519 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2521 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2524 /* unset selected cell */
2525 gsm322_unselect_cell(cs);
2527 /* start power scan */
2528 return gsm322_cs_powerscan(ms);
2531 /* start noraml cell selection */
2532 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2534 struct gsm322_cellsel *cs = &ms->cellsel;
2537 /* except for stored cell selection state, we weed to rescan ?? */
2538 if (cs->state != GSM322_C2_STORED_CELL_SEL) {
2539 for (i = 0; i <= 1023; i++) {
2540 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2541 | GSM322_CS_FLAG_SIGNAL
2542 | GSM322_CS_FLAG_SYSINFO);
2543 if (cs->list[i].sysinfo) {
2544 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2546 talloc_free(cs->list[i].sysinfo);
2547 cs->list[i].sysinfo = NULL;
2552 new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2554 /* unset selected cell */
2555 gsm322_unselect_cell(cs);
2557 /* start power scan */
2558 return gsm322_cs_powerscan(ms);
2561 /* start any cell selection */
2562 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2564 struct gsm322_cellsel *cs = &ms->cellsel;
2566 /* in case we already tried any cell (re-)selection, power scan again */
2567 if (cs->state == GSM322_C0_NULL
2568 || cs->state == GSM322_C6_ANY_CELL_SEL
2569 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2572 for (i = 0; i <= 1023; i++) {
2573 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2574 | GSM322_CS_FLAG_SIGNAL
2575 | GSM322_CS_FLAG_SYSINFO);
2576 if (cs->list[i].sysinfo) {
2577 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2579 talloc_free(cs->list[i].sysinfo);
2580 cs->list[i].sysinfo = NULL;
2584 /* after re-selection, indicate no cell found */
2585 if (cs->state == GSM322_C6_ANY_CELL_SEL
2586 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2589 /* tell that we have no cell found */
2590 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2593 gsm48_mmevent_msg(ms, nmsg);
2596 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2599 cs->mcc = cs->mnc = 0;
2601 /* unset selected cell */
2602 gsm322_unselect_cell(cs);
2604 /* start power scan */
2605 return gsm322_cs_powerscan(ms);
2608 /* start noraml cell re-selection */
2609 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2611 struct gsm322_cellsel *cs = &ms->cellsel;
2613 new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2615 /* NOTE: We keep our scan info we have so far.
2616 * This may cause a skip in power scan. */
2618 /* start power scan */
2619 return gsm322_cs_powerscan(ms);
2622 /* start any cell re-selection */
2623 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2625 struct gsm322_cellsel *cs = &ms->cellsel;
2627 new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2629 /* NOTE: We keep our scan info we have so far.
2630 * This may cause a skip in power scan. */
2632 /* start power scan */
2633 return gsm322_cs_powerscan(ms);
2636 /* create temporary ba range with given frequency ranges */
2637 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2638 uint32_t *range, uint8_t ranges)
2640 static struct gsm322_ba_list ba;
2641 uint16_t lower, higher;
2643 memset(&ba, 0, sizeof(ba));
2646 lower = *range & 1023;
2647 higher = (*range >> 16) & 1023;
2649 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2652 ba.freq[lower >> 3] |= 1 << (lower & 7);
2653 if (lower == higher)
2655 lower = (lower + 1) & 1023;
2662 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2663 static int gsm322_cs_choose(struct osmocom_ms *ms)
2665 struct gsm322_cellsel *cs = &ms->cellsel;
2666 struct gsm48_rrlayer *rr = &ms->rrlayer;
2667 struct gsm322_ba_list *ba = NULL;
2670 /* NOTE: The call to this function is synchron to RR layer, so
2671 * we may access the BA range there.
2674 ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges);
2676 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2677 /* get and update BA of last received sysinfo 5* */
2678 ba = gsm322_cs_sysinfo_sacch(ms);
2680 LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored "
2682 ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2690 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2692 /* tell CS to start over */
2693 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2696 gsm322_c_event(ms, nmsg);
2702 /* flag all frequencies that are in current band allocation */
2703 for (i = 0; i <= 1023; i++) {
2704 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2705 if ((ba->freq[i >> 3] & (1 << (i & 7)))) {
2706 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2708 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2711 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2712 | GSM322_CS_FLAG_SIGNAL
2713 | GSM322_CS_FLAG_SYSINFO);
2714 if (cs->list[i].sysinfo) {
2715 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2716 talloc_free(cs->list[i].sysinfo);
2717 cs->list[i].sysinfo = NULL;
2721 /* unset selected cell */
2722 gsm322_unselect_cell(cs);
2724 /* start power scan */
2725 return gsm322_cs_powerscan(ms);
2728 /* start 'Choose cell' after returning to idle mode */
2729 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2731 struct gsm322_cellsel *cs = &ms->cellsel;
2733 new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2735 return gsm322_cs_choose(ms);
2738 /* start 'Choose any cell' after returning to idle mode */
2739 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2741 struct gsm322_cellsel *cs = &ms->cellsel;
2743 new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2745 return gsm322_cs_choose(ms);
2748 /* a new PLMN is selected by PLMN search process */
2749 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2751 struct gsm322_cellsel *cs = &ms->cellsel;
2752 struct gsm322_plmn *plmn = &ms->plmn;
2753 struct gsm322_ba_list *ba;
2755 cs->mcc = plmn->mcc;
2756 cs->mnc = plmn->mnc;
2758 LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s "
2759 "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc),
2760 gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc),
2761 gsm_get_mnc(cs->mcc, cs->mnc));
2763 /* search for BA list */
2764 ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2767 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2768 return gsm322_c_stored_cell_sel(ms, ba);
2770 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2771 return gsm322_c_normal_cell_sel(ms, msg);
2775 /* a suitable cell was found, so we camp normally */
2776 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2778 struct gsm322_cellsel *cs = &ms->cellsel;
2781 LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s "
2782 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2783 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2784 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2786 /* tell that we have selected a (new) cell */
2787 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2790 gsm48_mmevent_msg(ms, nmsg);
2792 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2797 /* a not suitable cell was found, so we camp on any cell */
2798 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2800 struct gsm322_cellsel *cs = &ms->cellsel;
2803 LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s "
2804 "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2805 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2806 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2809 /* tell that we have selected a (new) cell */
2810 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2813 gsm48_mmevent_msg(ms, nmsg);
2815 new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2820 /* go connected mode */
2821 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2823 struct gsm322_cellsel *cs = &ms->cellsel;
2825 /* check for error */
2828 cs->arfcn = cs->sel_arfcn;
2830 /* be sure to go to current camping frequency on return */
2831 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2832 cs->ccch_state = GSM322_CCCH_ST_INIT;
2834 gsm322_sync_to_cell(ms, cs);
2835 cs->si = cs->list[cs->arfcn].sysinfo;
2836 #warning TESTING: laforge must fix the sync error when sending fbsb request too close to each other. also we must get a response with arfcn or a confirm, so we know where the response belongs to.
2842 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2844 struct gsm322_cellsel *cs = &ms->cellsel;
2846 /* check for error */
2849 cs->arfcn = cs->sel_arfcn;
2851 /* be sure to go to current camping frequency on return */
2852 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2853 cs->ccch_state = GSM322_CCCH_ST_INIT;
2855 gsm322_sync_to_cell(ms, cs);
2856 cs->si = cs->list[cs->arfcn].sysinfo;
2862 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2864 struct gsm_subscriber *subscr = &ms->subscr;
2866 /* if no SIM is is MS */
2867 if (!subscr->sim_valid) {
2868 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2869 return gsm322_c_any_cell_sel(ms, msg);
2870 LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2873 /* stay in NULL state until PLMN is selected */
2882 /* state machine for automatic PLMN selection events */
2883 static struct plmnastatelist {
2886 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2887 } plmnastatelist[] = {
2888 {SBIT(GSM322_A0_NULL),
2889 GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2891 {SBIT(GSM322_A0_NULL),
2892 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn},
2895 GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
2897 {SBIT(GSM322_A6_NO_SIM),
2898 GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
2901 GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
2904 GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
2907 GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
2909 {SBIT(GSM322_A1_TRYING_RPLMN),
2910 GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
2912 {SBIT(GSM322_A1_TRYING_RPLMN),
2913 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn},
2915 {SBIT(GSM322_A1_TRYING_RPLMN),
2916 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
2918 {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
2919 GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
2921 {SBIT(GSM322_A2_ON_PLMN),
2922 GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
2924 {SBIT(GSM322_A2_ON_PLMN),
2925 GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start},
2927 {SBIT(GSM322_A2_ON_PLMN),
2928 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
2930 {SBIT(GSM322_A2_ON_PLMN),
2931 GSM322_EVENT_USER_RESEL, gsm322_a_user_resel},
2933 {SBIT(GSM322_A3_TRYING_PLMN),
2934 GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
2936 {SBIT(GSM322_A3_TRYING_PLMN),
2937 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn},
2939 {SBIT(GSM322_A3_TRYING_PLMN),
2940 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
2942 {SBIT(GSM322_A5_HPLMN_SEARCH),
2943 GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn},
2945 {SBIT(GSM322_A5_HPLMN_SEARCH),
2946 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn},
2948 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
2949 GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
2952 GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
2955 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
2958 #define PLMNASLLEN \
2959 (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
2961 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
2963 struct gsm322_plmn *plmn = &ms->plmn;
2964 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2965 int msg_type = gm->msg_type;
2969 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
2970 "selection in state '%s'\n", ms->name, get_event_name(msg_type),
2971 plmn_a_state_names[plmn->state]);
2972 /* find function for current state and message */
2973 for (i = 0; i < PLMNASLLEN; i++)
2974 if ((msg_type == plmnastatelist[i].type)
2975 && ((1 << plmn->state) & plmnastatelist[i].states))
2977 if (i == PLMNASLLEN) {
2978 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
2982 rc = plmnastatelist[i].rout(ms, msg);
2987 /* state machine for manual PLMN selection events */
2988 static struct plmnmstatelist {
2991 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2992 } plmnmstatelist[] = {
2993 {SBIT(GSM322_M0_NULL),
2994 GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
2996 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) |
2997 SBIT(GSM322_M2_ON_PLMN),
2998 GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns},
3001 GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
3003 {SBIT(GSM322_M5_NO_SIM),
3004 GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
3007 GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
3010 GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
3012 {SBIT(GSM322_M1_TRYING_RPLMN),
3013 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3015 {SBIT(GSM322_M1_TRYING_RPLMN),
3016 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3018 {SBIT(GSM322_M1_TRYING_RPLMN),
3019 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3021 {SBIT(GSM322_M1_TRYING_RPLMN),
3022 GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
3024 {SBIT(GSM322_M2_ON_PLMN),
3025 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3027 {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
3028 SBIT(GSM322_M4_TRYING_PLMN),
3029 GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
3031 {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN),
3032 GSM322_EVENT_USER_RESEL, gsm322_m_user_resel},
3034 {SBIT(GSM322_M3_NOT_ON_PLMN),
3035 GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
3037 {SBIT(GSM322_M3_NOT_ON_PLMN),
3038 GSM322_EVENT_CHOSE_PLMN, gsm322_m_choose_plmn},
3040 {SBIT(GSM322_M4_TRYING_PLMN),
3041 GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
3043 {SBIT(GSM322_M4_TRYING_PLMN),
3044 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3046 {SBIT(GSM322_M4_TRYING_PLMN),
3047 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3049 {SBIT(GSM322_M4_TRYING_PLMN),
3050 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3053 GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
3056 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3059 #define PLMNMSLLEN \
3060 (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
3062 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
3064 struct gsm322_plmn *plmn = &ms->plmn;
3065 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3066 int msg_type = gm->msg_type;
3070 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
3071 "in state '%s'\n", ms->name, get_event_name(msg_type),
3072 plmn_m_state_names[plmn->state]);
3073 /* find function for current state and message */
3074 for (i = 0; i < PLMNMSLLEN; i++)
3075 if ((msg_type == plmnmstatelist[i].type)
3076 && ((1 << plmn->state) & plmnmstatelist[i].states))
3078 if (i == PLMNMSLLEN) {
3079 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3083 rc = plmnmstatelist[i].rout(ms, msg);
3088 /* dequeue GSM 03.22 PLMN events */
3089 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
3091 struct gsm322_plmn *plmn = &ms->plmn;
3095 while ((msg = msgb_dequeue(&plmn->event_queue))) {
3096 /* send event to PLMN select process */
3097 if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
3098 gsm322_a_event(ms, msg);
3100 gsm322_m_event(ms, msg);
3102 work = 1; /* work done */
3108 /* state machine for channel selection events */
3109 static struct cellselstatelist {
3112 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3113 } cellselstatelist[] = {
3115 GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
3118 GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
3121 GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
3124 GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search},
3126 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3127 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
3128 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
3130 {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3131 SBIT(GSM322_C8_ANY_CELL_RESEL),
3132 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
3134 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3135 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3136 SBIT(GSM322_C0_NULL),
3137 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
3139 {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3140 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
3141 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
3143 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3144 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
3146 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3147 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
3149 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3150 GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
3152 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3153 GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
3155 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3156 GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
3158 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3159 GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
3161 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3162 GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
3164 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3165 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3166 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3167 SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH),
3168 GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
3170 {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
3171 GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
3173 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3174 GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search},
3177 #define CELLSELSLLEN \
3178 (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
3180 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
3182 struct gsm322_cellsel *cs = &ms->cellsel;
3183 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3184 int msg_type = gm->msg_type;
3188 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
3189 "'%s'\n", ms->name, get_event_name(msg_type),
3190 cs_state_names[cs->state]);
3191 /* find function for current state and message */
3192 for (i = 0; i < CELLSELSLLEN; i++)
3193 if ((msg_type == cellselstatelist[i].type)
3194 && ((1 << cs->state) & cellselstatelist[i].states))
3196 if (i == CELLSELSLLEN) {
3197 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
3201 rc = cellselstatelist[i].rout(ms, msg);
3206 /* dequeue GSM 03.22 cell selection events */
3207 int gsm322_cs_dequeue(struct osmocom_ms *ms)
3209 struct gsm322_cellsel *cs = &ms->cellsel;
3213 while ((msg = msgb_dequeue(&cs->event_queue))) {
3214 /* send event to cell selection process */
3215 gsm322_c_event(ms, msg);
3217 work = 1; /* work done */
3227 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
3229 struct gsm322_plmn *plmn = &ms->plmn;
3230 struct gsm322_plmn_list *temp;
3232 printf("MCC |MNC |allowed|rx-lev\n");
3233 printf("-------+-------+-------+-------\n");
3234 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
3235 printf("%s |%s%s |%s |%d\n", gsm_print_mcc(temp->mcc),
3236 gsm_print_mnc(temp->mnc),
3237 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3238 (temp->cause) ? "no ":"yes", temp->rxlev_db);
3244 int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
3245 void (*print)(void *, const char *, ...), void *priv)
3248 struct gsm48_sysinfo *s;
3250 print(priv, "arfcn |rx-lev |MCC |MNC |LAC |cell ID|forb.LA|"
3251 "prio |min-db |max-pwr\n");
3252 print(priv, "-------+-------+-------+-------+-------+-------+-------+"
3253 "-------+-------+-------\n");
3254 for (i = 0; i <= 1023; i++) {
3255 s = cs->list[i].sysinfo;
3256 if (!s || !(cs->list[i].flags & flags))
3258 print(priv, "%4d |%4d |", i, cs->list[i].rxlev_db);
3259 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
3260 print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc),
3261 gsm_print_mnc(s->mnc),
3262 ((s->mnc & 0x00f) == 0x00f) ? " ":"");
3263 print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id);
3264 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
3265 print(priv, "yes |");
3267 print(priv, "no |");
3268 if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
3269 print(priv, "barred |");
3271 if (cs->list[i].sysinfo->cell_barr)
3272 print(priv, "low |");
3274 print(priv, "normal |");
3276 print(priv, "%4d |%4d\n", s->rxlev_acc_min_db,
3277 s->ms_txpwr_max_ccch);
3279 print(priv, "n/a |n/a |n/a |n/a |n/a |"
3287 int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
3288 void (*print)(void *, const char *, ...), void *priv)
3290 struct gsm322_plmn *plmn = &ms->plmn;
3291 struct gsm322_la_list *temp;
3293 print(priv, "MCC |MNC |LAC |cause\n");
3294 print(priv, "-------+-------+-------+-------\n");
3295 llist_for_each_entry(temp, &plmn->forbidden_la, entry)
3296 print(priv, "%s |%s%s |0x%04x |#%d\n",
3297 gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
3298 ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3299 temp->lac, temp->cause);
3304 int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
3305 void (*print)(void *, const char *, ...), void *priv)
3307 struct gsm322_ba_list *ba;
3310 llist_for_each_entry(ba, &cs->ba_list, entry) {
3311 if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc))
3313 print(priv, "Band Allocation of network: MCC %03d MNC %02d "
3314 "(%s, %s)\n", ba->mcc, ba->mnc, gsm_get_mcc(ba->mcc),
3315 gsm_get_mnc(ba->mcc, ba->mnc));
3316 for (i = 0; i <= 1023; i++) {
3317 if ((ba->freq[i >> 3] & (1 << (i & 7))))
3318 print(priv, " %d", i);
3330 int gsm322_init(struct osmocom_ms *ms)
3332 struct gsm322_plmn *plmn = &ms->plmn;
3333 struct gsm322_cellsel *cs = &ms->cellsel;
3335 char suffix[] = ".ba";
3336 char filename[sizeof(ms->name) + strlen(suffix) + 1];
3338 struct gsm322_ba_list *ba;
3341 LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
3342 LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
3344 memset(plmn, 0, sizeof(*plmn));
3345 memset(cs, 0, sizeof(*cs));
3349 /* set initial state */
3352 ms->settings.plmn_mode = PLMN_MODE_AUTO;
3355 INIT_LLIST_HEAD(&plmn->event_queue);
3356 INIT_LLIST_HEAD(&cs->event_queue);
3357 INIT_LLIST_HEAD(&plmn->sorted_plmn);
3358 INIT_LLIST_HEAD(&plmn->forbidden_la);
3359 INIT_LLIST_HEAD(&cs->ba_list);
3361 /* set supported frequencies in cell selection list */
3362 for (i = 0; i <= 1023; i++)
3363 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
3364 cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
3367 strcpy(filename, ms->name);
3368 strcat(filename, suffix);
3369 fp = osmocom_fopen(filename, "r");
3373 while(!osmocom_feof(fp)) {
3374 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
3377 rc = osmocom_fread(buf, 4, 1, fp);
3382 ba->mcc = (buf[0] << 8) | buf[1];
3383 ba->mnc = (buf[2] << 8) | buf[3];
3384 rc = osmocom_fread(ba->freq, sizeof(ba->freq), 1, fp);
3389 llist_add_tail(&ba->entry, &cs->ba_list);
3390 LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s "
3391 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3392 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3393 gsm_get_mnc(ba->mcc, ba->mnc));
3397 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
3399 register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3404 int gsm322_exit(struct osmocom_ms *ms)
3406 struct gsm322_plmn *plmn = &ms->plmn;
3407 struct gsm322_cellsel *cs = &ms->cellsel;
3408 struct llist_head *lh, *lh2;
3411 char suffix[] = ".ba";
3412 char filename[sizeof(ms->name) + strlen(suffix) + 1];
3413 struct gsm322_ba_list *ba;
3417 LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
3418 LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
3420 unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3422 /* stop cell selection process (if any) */
3423 new_c_state(cs, GSM322_C0_NULL);
3427 stop_plmn_timer(plmn);
3430 for (i = 0; i <= 1023; i++) {
3431 if (cs->list[i].sysinfo) {
3432 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
3433 talloc_free(cs->list[i].sysinfo);
3434 cs->list[i].sysinfo = NULL;
3436 cs->list[i].flags = 0;
3440 strcpy(filename, ms->name);
3441 strcat(filename, suffix);
3442 fp = osmocom_fopen(filename, "w");
3446 llist_for_each_entry(ba, &cs->ba_list, entry) {
3447 buf[0] = ba->mcc >> 8;
3448 buf[1] = ba->mcc & 0xff;
3449 buf[2] = ba->mnc >> 8;
3450 buf[3] = ba->mnc & 0xff;
3451 rc = osmocom_fwrite(buf, 4, 1, fp);
3452 rc = osmocom_fwrite(ba->freq, sizeof(ba->freq), 1, fp);
3453 LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s "
3454 "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
3455 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3456 gsm_get_mnc(ba->mcc, ba->mnc));
3460 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
3463 while ((msg = msgb_dequeue(&plmn->event_queue)))
3465 while ((msg = msgb_dequeue(&cs->event_queue)))
3467 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
3471 llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
3475 llist_for_each_safe(lh, lh2, &cs->ba_list) {