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 /* print to DCS logging */
223 static void print_dcs(void *priv, const char *fmt, ...)
229 vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
230 buffer[sizeof(buffer) - 1] = '\0';
234 // LOGP(DCS, LOGL_INFO, "%s", buffer);
235 printf("%s", buffer);
238 /* del forbidden LA */
239 int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
240 uint16_t mnc, uint16_t lac)
242 struct gsm322_plmn *plmn = &ms->plmn;
243 struct gsm322_la_list *la;
245 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
246 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
247 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
248 "LAs (mcc=%03d, mnc=%02d, lac=%04x)\n",
250 llist_del(&la->entry);
259 /* add forbidden LA */
260 int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
261 uint16_t mnc, uint16_t lac, uint8_t cause)
263 struct gsm322_plmn *plmn = &ms->plmn;
264 struct gsm322_la_list *la;
266 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
267 "(mcc=%03d, mnc=%02d, lac=%04x)\n", mcc, mnc, lac);
268 la = talloc_zero(l23_ctx, struct gsm322_la_list);
275 llist_add_tail(&la->entry, &plmn->forbidden_la);
280 /* search forbidden LA */
281 int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
284 struct gsm322_plmn *plmn = &ms->plmn;
285 struct gsm322_la_list *la;
287 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
288 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
295 /* search for PLMN in all BA lists */
296 static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
297 uint16_t mcc, uint16_t mnc)
299 struct gsm322_ba_list *ba, *ba_found = NULL;
301 /* search for BA list */
302 llist_for_each_entry(ba, &cs->ba_list, entry) {
313 /* search available PLMN */
314 int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
318 for (i = 0; i <= 1023; i++) {
319 if (cs->list[i].sysinfo
320 && cs->list[i].sysinfo->mcc == mcc
321 && cs->list[i].sysinfo->mnc == mnc)
328 /* del forbidden LA */
333 /*plmn search timer event */
334 static void plmn_timer_timeout(void *arg)
336 struct gsm322_plmn *plmn = arg;
339 LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
341 /* indicate PLMN selection T timeout */
342 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
345 gsm322_plmn_sendmsg(plmn->ms, nmsg);
348 /* start plmn search timer */
349 static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
351 LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
353 plmn->timer.cb = plmn_timer_timeout;
354 plmn->timer.data = plmn;
355 bsc_schedule_timer(&plmn->timer, secs, 0);
358 /* stop plmn search timer */
359 static void stop_plmn_timer(struct gsm322_plmn *plmn)
361 if (bsc_timer_pending(&plmn->timer)) {
362 LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
363 bsc_del_timer(&plmn->timer);
367 /* start cell selection timer */
368 void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
370 LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec);
371 cs->timer.cb = gsm322_cs_timeout;
373 bsc_schedule_timer(&cs->timer, sec, micro);
376 /* start loss timer */
377 void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro)
380 cs->timer.cb = gsm322_cs_loss;
382 if (bsc_timer_pending(&cs->timer)) {
383 struct timeval current_time;
384 unsigned long long currentTime;
386 gettimeofday(¤t_time, NULL);
387 currentTime = current_time.tv_sec * 1000000LL
388 + current_time.tv_usec;
389 currentTime += sec * 1000000LL + micro;
390 cs->timer.timeout.tv_sec = currentTime / 1000000LL;
391 cs->timer.timeout.tv_usec = currentTime % 1000000LL;
396 LOGP(DCS, LOGL_INFO, "Starting loss CS timer with %d seconds.\n", sec);
397 bsc_schedule_timer(&cs->timer, sec, micro);
400 /* stop cell selection timer */
401 static void stop_cs_timer(struct gsm322_cellsel *cs)
403 if (bsc_timer_pending(&cs->timer)) {
404 LOGP(DCS, LOGL_INFO, "stopping pending CS timer.\n");
405 bsc_del_timer(&cs->timer);
413 static const char *plmn_a_state_names[] = {
423 static const char *plmn_m_state_names[] = {
432 static const char *cs_state_names[] = {
434 "C1_NORMAL_CELL_SEL",
435 "C2_STORED_CELL_SEL",
436 "C3_CAMPED_NORMALLY",
437 "C4_NORMAL_CELL_RESEL",
440 "C7_CAMPED_ANY_CELL",
442 "C9_CHOOSE_ANY_CELL",
448 /* new automatic PLMN search state */
449 static void new_a_state(struct gsm322_plmn *plmn, int state)
451 if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) {
452 LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
456 stop_plmn_timer(plmn);
458 if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
461 LOGP(DPLMN, LOGL_INFO, "new state %s -> %s\n",
462 plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
467 /* new manual PLMN search state */
468 static void new_m_state(struct gsm322_plmn *plmn, int state)
470 if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
471 LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
475 if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
478 LOGP(DPLMN, LOGL_INFO, "new state %s -> %s\n",
479 plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
484 /* new Cell selection state */
485 static void new_c_state(struct gsm322_cellsel *cs, int state)
487 if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
490 LOGP(DCS, LOGL_INFO, "new state %s -> %s\n",
491 cs_state_names[cs->state], cs_state_names[state]);
493 /* stop cell selection timer, if running */
496 /* stop scanning of power measurement */
511 /* 4.4.3 create sorted list of PLMN
513 * the source of entries are
516 * - entries found in the SIM's PLMN Selector list
517 * - scanned PLMNs above -85 dB (random order)
518 * - scanned PLMNs below or equal -85 (by received level)
522 * The list only includes networks found at last scan.
524 * The list always contains HPLMN if available, even if not used by PLMN
525 * search process at some conditions.
527 * The list contains all PLMNs even if not allowed, so entries have to be
528 * removed when selecting from the list. (In case we use manual cell selection,
529 * we need to provide non-allowed networks also.)
531 static int gsm322_sort_list(struct osmocom_ms *ms)
533 struct gsm322_plmn *plmn = &ms->plmn;
534 struct gsm322_cellsel *cs = &ms->cellsel;
535 struct gsm_subscriber *subscr = &ms->subscr;
536 struct gsm_sub_plmn_list *sim_entry;
537 struct gsm_sub_plmn_na *na_entry;
538 struct llist_head temp_list;
539 struct gsm322_plmn_list *temp, *found;
540 struct llist_head *lh, *lh2;
541 int i, entries, move;
542 int8_t search_db = 0;
545 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
550 /* Create a temporary list of all networks */
551 INIT_LLIST_HEAD(&temp_list);
552 for (i = 0; i <= 1023; i++) {
553 if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)
554 || !cs->list[i].sysinfo)
557 /* search if network has multiple cells */
559 llist_for_each_entry(temp, &temp_list, entry) {
560 if (temp->mcc == cs->list[i].sysinfo->mcc
561 && temp->mnc == cs->list[i].sysinfo->mnc) {
566 /* update or create */
568 if (cs->list[i].rxlev_db > found->rxlev_db)
569 found->rxlev_db = cs->list[i].rxlev_db;
571 temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
574 temp->mcc = cs->list[i].sysinfo->mcc;
575 temp->mnc = cs->list[i].sysinfo->mnc;
576 temp->rxlev_db = cs->list[i].rxlev_db;
577 llist_add_tail(&temp->entry, &temp_list);
581 /* move Home PLMN, if in list, else add it */
582 if (subscr->sim_valid) {
584 llist_for_each_entry(temp, &temp_list, entry) {
585 if (temp->mcc == subscr->mcc
586 && temp->mnc == subscr->mnc) {
593 llist_del(&found->entry);
594 llist_add_tail(&found->entry, &plmn->sorted_plmn);
599 temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
602 temp->mcc = subscr->mcc;
603 temp->mnc = subscr->mnc;
604 temp->rxlev_db = 0; /* unknown */
605 llist_add_tail(&temp->entry, &plmn->sorted_plmn);
610 /* move entries if in SIM's PLMN Selector list */
611 llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
613 llist_for_each_entry(temp, &temp_list, entry) {
614 if (temp->mcc == sim_entry->mcc
615 && temp->mnc == sim_entry->mnc) {
621 llist_del(&found->entry);
622 llist_add_tail(&found->entry, &plmn->sorted_plmn);
626 /* move PLMN above -85 dBm in random order */
628 llist_for_each_entry(temp, &temp_list, entry) {
629 if (temp->rxlev_db > -85)
633 move = random() % entries;
635 llist_for_each_entry(temp, &temp_list, entry) {
636 if (temp->rxlev_db > -85) {
638 llist_del(&temp->entry);
639 llist_add_tail(&temp->entry,
649 /* move ohter PLMN in decreasing order */
652 llist_for_each_entry(temp, &temp_list, entry) {
654 || temp->rxlev_db > search_db) {
655 search_db = temp->rxlev_db;
661 llist_del(&found->entry);
662 llist_add_tail(&found->entry, &plmn->sorted_plmn);
665 /* mark forbidden PLMNs, if in list of forbidden networks */
667 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
668 llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
669 if (temp->mcc == na_entry->mcc
670 && temp->mnc == na_entry->mnc) {
671 temp->cause = na_entry->cause;
675 LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. "
676 "(%02d: mcc=%03d mnc=%02d allowed=%s rx-lev=%d)\n",
677 i, temp->mcc, temp->mnc, (temp->cause) ? "no ":"yes",
682 gsm322_dump_sorted_plmn(ms);
688 * handler for automatic search
691 /* go On PLMN state */
692 static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
694 struct gsm322_plmn *plmn = &ms->plmn;
695 struct gsm_subscriber *subscr = &ms->subscr;
697 new_a_state(plmn, GSM322_A2_ON_PLMN);
699 /* start timer, if on VPLMN of home country OR special case */
700 if ((plmn->mcc == subscr->mcc && plmn->mcc != subscr->mnc)
701 || (subscr->always_search_hplmn && (plmn->mcc != subscr->mnc
702 || plmn->mcc != subscr->mnc))) {
703 if (subscr->sim_valid && subscr->t6m_hplmn)
704 start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
706 start_plmn_timer(plmn, 30 * 360);
708 stop_plmn_timer(plmn);
713 /* indicate selected PLMN */
714 static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
716 struct gsm322_plmn *plmn = &ms->plmn;
718 vty_notify(ms, NULL);
719 vty_notify(ms, "Selected Network: %s, %s\n",
720 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
722 return gsm322_a_go_on_plmn(ms, msg);
725 /* no (more) PLMN in list */
726 static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
728 struct gsm322_plmn *plmn = &ms->plmn;
729 struct gsm322_cellsel *cs = &ms->cellsel;
730 struct gsm_subscriber *subscr = &ms->subscr;
734 /* any allowable PLMN available? */
735 plmn->mcc = plmn->mnc = 0;
736 found = gsm322_cs_select(ms, 0);
738 /* if no PLMN in list */
740 if (subscr->plmn_valid) {
741 LOGP(DPLMN, LOGL_INFO, "Select RPLMN.\n");
742 plmn->mcc = subscr->plmn_mcc;
743 plmn->mnc = subscr->plmn_mnc;
745 LOGP(DPLMN, LOGL_INFO, "Select HPLMN.\n");
746 plmn->mcc = subscr->mcc;
747 plmn->mnc = subscr->mnc;
750 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
752 /* we must forward this, otherwhise "Any cell selection"
753 * will not start automatically.
755 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
758 gsm322_cs_sendmsg(ms, nmsg);
763 /* select first PLMN in list */
764 plmn->mcc = cs->list[found].sysinfo->mcc;
765 plmn->mnc = cs->list[found].sysinfo->mnc;
767 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%03d mnc=%02d %s, %s)\n",
768 plmn->mcc, plmn->mnc,
769 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
771 /* indicate New PLMN */
772 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
775 gsm322_cs_sendmsg(ms, nmsg);
778 return gsm322_a_indicate_selected(ms, msg);
781 /* select first PLMN in list */
782 static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
784 struct gsm322_plmn *plmn = &ms->plmn;
785 struct gsm_subscriber *subscr = &ms->subscr;
787 struct gsm322_plmn_list *plmn_entry;
788 struct gsm322_plmn_list *plmn_first = NULL;
792 gsm322_sort_list(ms);
794 /* select first entry */
796 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
797 /* if last selected PLMN was HPLMN, we skip that */
798 if (plmn_entry->mcc == subscr->mcc
799 && plmn_entry->mnc == subscr->mnc
800 && plmn_entry->mcc == plmn->mcc
801 && plmn_entry->mnc == plmn->mnc) {
802 LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was "
803 "previously selected.\n");
807 /* select first allowed network */
808 if (!plmn_entry->cause) {
809 plmn_first = plmn_entry;
812 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it "
813 "not allowed (cause %d).\n", plmn_entry->mcc,
814 plmn_entry->mnc, plmn_entry->cause);
819 /* if no PLMN in list */
821 LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
822 gsm322_a_no_more_plmn(ms, msg);
827 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%03d "
828 "mnc=%02d %s, %s)\n", plmn->plmn_curr, plmn_first->mcc,
829 plmn_first->mnc, gsm_get_mcc(plmn_first->mcc),
830 gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
832 /* set current network */
833 plmn->mcc = plmn_first->mcc;
834 plmn->mnc = plmn_first->mnc;
836 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
838 /* indicate New PLMN */
839 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
842 gsm322_cs_sendmsg(ms, nmsg);
847 /* select next PLMN in list */
848 static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
850 struct gsm322_plmn *plmn = &ms->plmn;
852 struct gsm322_plmn_list *plmn_entry;
853 struct gsm322_plmn_list *plmn_next = NULL;
856 /* select next entry from list */
858 ii = plmn->plmn_curr + 1;
859 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
860 /* skip previously selected networks */
865 /* select next allowed network */
866 if (!plmn_entry->cause) {
867 plmn_next = plmn_entry;
870 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it "
871 "not allowed (cause %d).\n", plmn_entry->mcc,
872 plmn_entry->mnc, plmn_entry->cause);
877 /* if no more PLMN in list */
879 LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
880 gsm322_a_no_more_plmn(ms, msg);
885 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%03d "
886 "mnc=%02d %s, %s)\n", plmn->plmn_curr, plmn_next->mcc,
888 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
890 /* set next network */
891 plmn->mcc = plmn_next->mcc;
892 plmn->mnc = plmn_next->mnc;
894 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
896 /* indicate New PLMN */
897 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
900 gsm322_cs_sendmsg(ms, nmsg);
905 /* User re-selection event */
906 static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg)
908 struct gsm322_plmn *plmn = &ms->plmn;
909 struct gsm_subscriber *subscr = &ms->subscr;
910 struct gsm48_rrlayer *rr = &ms->rrlayer;
911 struct gsm322_plmn_list *plmn_entry;
912 struct gsm322_plmn_list *plmn_found = NULL;
914 if (!subscr->sim_valid) {
918 /* try again later, if not idle */
919 if (rr->state != GSM48_RR_ST_IDLE) {
920 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
925 /* search current PLMN in list */
926 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
927 if (plmn_entry->mcc == plmn->mcc
928 && plmn_entry->mnc == plmn->mnc)
929 plmn_found = plmn_entry;
933 /* abort if list is empty */
935 LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
939 LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
940 "and restarting PLMN search process.\n");
942 /* move entry to end of list */
943 llist_del(&plmn_found->entry);
944 llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
946 /* select first PLMN in list */
947 return gsm322_a_sel_first_plmn(ms, msg);
950 /* PLMN becomes available */
951 static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
953 struct gsm322_plmn *plmn = &ms->plmn;
954 struct gsm_subscriber *subscr = &ms->subscr;
955 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
957 if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
958 && subscr->plmn_mnc == gm->mnc) {
962 LOGP(DPLMN, LOGL_INFO, "HPLMN became available.\n");
963 return gsm322_a_go_on_plmn(ms, msg);
965 /* select first PLMN in list */
966 LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
967 "search process.\n");
968 return gsm322_a_sel_first_plmn(ms, msg);
972 /* loss of radio coverage */
973 static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
975 struct gsm322_plmn *plmn = &ms->plmn;
976 struct gsm322_cellsel *cs = &ms->cellsel;
980 /* any PLMN available */
981 found = gsm322_cs_select(ms, 0);
983 /* if PLMN in list */
985 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%03d mnc=%02d "
986 "%s, %s)\n", cs->list[found].sysinfo->mcc,
987 cs->list[found].sysinfo->mnc,
988 gsm_get_mcc(cs->list[found].sysinfo->mcc),
989 gsm_get_mnc(cs->list[found].sysinfo->mcc,
990 cs->list[found].sysinfo->mnc));
991 return gsm322_a_sel_first_plmn(ms, msg);
994 LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
996 plmn->mcc = plmn->mnc = 0;
998 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
1000 /* Tell cell selection process to handle "no cell found". */
1001 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1004 gsm322_cs_sendmsg(ms, nmsg);
1009 /* MS is switched on OR SIM is inserted OR removed */
1010 static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1012 struct gsm_subscriber *subscr = &ms->subscr;
1013 struct gsm322_plmn *plmn = &ms->plmn;
1016 if (!subscr->sim_valid) {
1017 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1018 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1019 new_a_state(plmn, GSM322_A6_NO_SIM);
1024 /* if there is a registered PLMN */
1025 if (subscr->plmn_valid) {
1026 /* select the registered PLMN */
1027 plmn->mcc = subscr->plmn_mcc;
1028 plmn->mnc = subscr->plmn_mnc;
1030 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1031 "(mcc=%03d mnc=%02d %s, %s)\n", plmn->mcc, plmn->mnc,
1032 gsm_get_mcc(plmn->mcc),
1033 gsm_get_mnc(plmn->mcc, plmn->mnc));
1034 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%03d mnc=%02d "
1035 "%s, %s)\n", plmn->mcc, plmn->mnc,
1036 gsm_get_mcc(plmn->mcc),
1037 gsm_get_mnc(plmn->mcc, plmn->mnc));
1039 new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
1041 /* indicate New PLMN */
1042 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1045 gsm322_cs_sendmsg(ms, nmsg);
1050 /* initiate search at cell selection */
1051 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1052 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1054 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1057 gsm322_cs_sendmsg(ms, nmsg);
1062 /* MS is switched off */
1063 static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1065 struct gsm322_plmn *plmn = &ms->plmn;
1067 new_a_state(plmn, GSM322_A0_NULL);
1072 static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1074 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1078 /* SIM is removed */
1079 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1083 /* indicate SIM remove to cell selection process */
1084 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1087 gsm322_cs_sendmsg(ms, nmsg);
1089 return gsm322_a_switch_on(ms, msg);
1092 /* location update response: "Roaming not allowed" */
1093 static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
1095 /* store in list of forbidden LAs is done in gsm48* */
1097 return gsm322_a_sel_first_plmn(ms, msg);
1100 /* On VPLMN of home country and timeout occurs */
1101 static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg)
1103 struct gsm48_rrlayer *rr = &ms->rrlayer;
1104 struct gsm322_plmn *plmn = &ms->plmn;
1105 struct gsm322_cellsel *cs = &ms->cellsel;
1108 /* try again later, if not idle and not camping */
1109 if (rr->state != GSM48_RR_ST_IDLE
1110 || cs->state != GSM322_C3_CAMPED_NORMALLY) {
1111 LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n");
1112 start_plmn_timer(plmn, 60);
1117 new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
1119 /* initiate search at cell selection */
1120 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1123 gsm322_cs_sendmsg(ms, nmsg);
1128 /* manual mode selected */
1129 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
1133 /* restart state machine */
1134 gsm322_a_switch_off(ms, msg);
1135 ms->settings.plmn_mode = PLMN_MODE_MANUAL;
1136 gsm322_m_switch_on(ms, msg);
1138 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1141 gsm48_mmevent_msg(ms, nmsg);
1147 * handler for manual search
1150 /* go Not on PLMN state */
1151 static int gsm322_m_go_not_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1153 struct gsm322_plmn *plmn = &ms->plmn;
1155 new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
1160 /* display PLMNs and to Not on PLMN */
1161 static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
1163 struct gsm322_plmn *plmn = &ms->plmn;
1164 struct gsm_sub_plmn_list *temp;
1167 gsm322_sort_list(ms);
1169 vty_notify(ms, NULL);
1170 vty_notify(ms, "Select from Network:\n");
1172 llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
1173 vty_notify(ms, " Network %03d, %02d (%s, %s)\n", temp->mcc,
1174 temp->mnc, gsm_get_mcc(temp->mcc),
1175 gsm_get_mnc(temp->mcc, temp->mnc));
1177 /* go Not on PLMN state */
1178 return gsm322_m_go_not_on_plmn(ms, msg);
1181 /* user starts reselection */
1182 static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg)
1184 struct gsm_subscriber *subscr = &ms->subscr;
1185 struct gsm48_rrlayer *rr = &ms->rrlayer;
1188 if (!subscr->sim_valid) {
1192 /* try again later, if not idle */
1193 if (rr->state != GSM48_RR_ST_IDLE) {
1194 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
1199 /* initiate search at cell selection */
1200 vty_notify(ms, NULL);
1201 vty_notify(ms, "Searching Network, please wait...\n");
1202 LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n");
1204 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1207 gsm322_cs_sendmsg(ms, nmsg);
1212 /* MS is switched on OR SIM is inserted OR removed */
1213 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1215 struct gsm_subscriber *subscr = &ms->subscr;
1216 struct gsm322_plmn *plmn = &ms->plmn;
1219 if (!subscr->sim_valid) {
1220 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1221 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1222 new_m_state(plmn, GSM322_M5_NO_SIM);
1227 /* if there is a registered PLMN */
1228 if (subscr->plmn_valid) {
1231 /* select the registered PLMN */
1232 plmn->mcc = subscr->plmn_mcc;
1233 plmn->mnc = subscr->plmn_mnc;
1235 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1236 "(mcc=%03d mnc=%02d %s, %s)\n", plmn->mcc, plmn->mnc,
1237 gsm_get_mcc(plmn->mcc),
1238 gsm_get_mnc(plmn->mcc, plmn->mnc));
1239 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%03d mnc=%02d "
1240 "%s, %s)\n", plmn->mcc, plmn->mnc,
1241 gsm_get_mcc(plmn->mcc),
1242 gsm_get_mnc(plmn->mcc, plmn->mnc));
1244 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1246 /* indicate New PLMN */
1247 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1250 gsm322_cs_sendmsg(ms, nmsg);
1255 /* initiate search at cell selection */
1256 LOGP(DSUM, LOGL_INFO, "Search for network\n");
1257 LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1258 vty_notify(ms, NULL);
1259 vty_notify(ms, "Searching Network, please wait...\n");
1261 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1264 gsm322_cs_sendmsg(ms, nmsg);
1269 /* MS is switched off */
1270 static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1272 struct gsm322_plmn *plmn = &ms->plmn;
1274 stop_plmn_timer(plmn);
1276 new_m_state(plmn, GSM322_M0_NULL);
1281 static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1283 LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1287 /* SIM is removed */
1288 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1290 struct gsm322_plmn *plmn = &ms->plmn;
1293 stop_plmn_timer(plmn);
1295 /* indicate SIM remove to cell selection process */
1296 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1299 gsm322_cs_sendmsg(ms, nmsg);
1301 return gsm322_m_switch_on(ms, msg);
1304 /* go to On PLMN state */
1305 static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1307 struct gsm322_plmn *plmn = &ms->plmn;
1308 struct gsm_subscriber *subscr = &ms->subscr;
1310 /* if selected PLMN is in list of forbidden PLMNs */
1311 gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc);
1313 /* set last registered PLMN */
1314 subscr->plmn_valid = 1;
1315 subscr->plmn_mcc = plmn->mcc;
1316 subscr->plmn_mnc = plmn->mnc;
1321 new_m_state(plmn, GSM322_M2_ON_PLMN);
1326 /* indicate selected PLMN */
1327 static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
1329 struct gsm322_plmn *plmn = &ms->plmn;
1331 vty_notify(ms, NULL);
1332 vty_notify(ms, "Selected Network: %s, %s\n",
1333 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1335 return gsm322_m_go_on_plmn(ms, msg);
1338 /* previously selected PLMN becomes available again */
1339 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1341 struct gsm322_plmn *plmn = &ms->plmn;
1342 struct gsm322_cellsel *cs = &ms->cellsel;
1344 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1346 if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
1349 LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
1350 "selected, so start selection.\n");
1352 /* indicate New PLMN */
1353 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1356 gsm322_cs_sendmsg(ms, nmsg);
1362 /* the user has selected given PLMN */
1363 static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
1365 struct gsm322_plmn *plmn = &ms->plmn;
1366 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1369 /* use user selection */
1370 plmn->mcc = gm->mcc;
1371 plmn->mnc = gm->mnc;
1373 vty_notify(ms, NULL);
1374 vty_notify(ms, "Selected Network: %s, %s\n",
1375 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1376 LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%03d mnc=%02d "
1377 "%s, %s)\n", plmn->mcc, plmn->mnc,
1378 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1380 new_m_state(plmn, GSM322_M4_TRYING_PLMN);
1382 /* indicate New PLMN */
1383 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1386 gsm322_cs_sendmsg(ms, nmsg);
1391 /* auto mode selected */
1392 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
1396 /* restart state machine */
1397 gsm322_m_switch_off(ms, msg);
1398 ms->settings.plmn_mode = PLMN_MODE_AUTO;
1399 gsm322_a_switch_on(ms, msg);
1401 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1404 gsm48_mmevent_msg(ms, nmsg);
1409 /* if no cell is found in other states than in *_TRYING_* states */
1410 static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
1414 /* Tell cell selection process to handle "no cell found". */
1415 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1418 gsm322_cs_sendmsg(ms, nmsg);
1424 * cell scanning process
1427 /* select a suitable and allowable cell */
1428 static int gsm322_cs_select(struct osmocom_ms *ms, int any)
1430 struct gsm322_cellsel *cs = &ms->cellsel;
1431 struct gsm_subscriber *subscr = &ms->subscr;
1432 struct gsm48_sysinfo *s;
1433 int i, found = -1, power = 0;
1434 uint8_t flags, mask;
1437 /* set out access class depending on the cell selection type */
1439 acc_class = subscr->acc_class | 0x0400; /* add emergency */
1440 LOGP(DCS, LOGL_INFO, "Select using access class with Emergency "
1443 acc_class = subscr->acc_class;
1444 LOGP(DCS, LOGL_INFO, "Select using access class \n");
1447 /* flags to match */
1448 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1449 | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
1450 if (cs->state == GSM322_C2_STORED_CELL_SEL
1451 || cs->state == GSM322_C5_CHOOSE_CELL)
1452 mask |= GSM322_CS_FLAG_BA;
1453 flags = mask; /* all masked flags are requied */
1455 /* loop through all scanned frequencies and select cell */
1456 for (i = 0; i <= 1023; i++) {
1457 cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
1458 s = cs->list[i].sysinfo;
1460 /* channel has no informations for us */
1461 if (!s || (cs->list[i].flags & mask) != flags) {
1465 /* check C1 criteria not fullfilled */
1466 // TODO: C1 is also dependant on power class and max power
1467 if (cs->list[i].rxlev_db < s->rxlev_acc_min_db) {
1468 LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
1469 "not met. (rxlev=%d < min=%d)\n", i,
1470 cs->list[i].rxlev_db, s->rxlev_acc_min_db);
1474 /* if cell is barred and we don't override */
1475 if (!subscr->acc_barr
1476 && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
1477 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
1482 /* if cell is in list of forbidden LAs */
1483 if (!subscr->acc_barr
1484 && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
1485 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1486 "list of forbidden LAs. (mcc=%03d mnc=%02d "
1487 "lai=%04x)\n", i, s->mcc, s->mnc, s->lac);
1491 /* if we have no access to the cell and we don't override */
1492 if (!subscr->acc_barr
1493 && !(acc_class & (s->class_barr ^ 0xffff))) {
1494 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
1495 "barred for out access. (access=%04x "
1496 "barred=%04x)\n", i, acc_class, s->class_barr);
1500 /* store temporary available and allowable flag */
1501 cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
1503 /* if we search a specific PLMN, but it does not match */
1504 if (!any && cs->mcc && (cs->mcc != s->mcc
1505 || cs->mnc != s->mnc)) {
1506 LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
1507 "does not match target PLMN. (mcc=%03d "
1508 "mnc=%02d)\n", i, s->mcc, s->mnc);
1512 LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%d "
1513 "mcc=%03d mnc=%02d lac=%04x %s, %s)\n", i,
1514 cs->list[i].rxlev_db, s->mcc, s->mnc,
1515 s->lac, gsm_get_mcc(s->mcc),
1516 gsm_get_mnc(s->mcc, s->mnc));
1518 /* find highest power cell */
1519 if (found < 0 || cs->list[i].rxlev_db > power) {
1520 power = cs->list[i].rxlev_db;
1526 LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
1531 /* tune to first/next unscanned frequency and search for PLMN */
1532 static int gsm322_cs_scan(struct osmocom_ms *ms)
1534 struct gsm322_cellsel *cs = &ms->cellsel;
1536 uint8_t mask, flags;
1537 uint32_t weight = 0, test = cs->scan_state;
1539 /* search for strongest unscanned cell */
1540 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1541 | GSM322_CS_FLAG_SIGNAL;
1542 if (cs->state == GSM322_C2_STORED_CELL_SEL
1543 || cs->state == GSM322_C5_CHOOSE_CELL)
1544 mask |= GSM322_CS_FLAG_BA;
1545 flags = mask; /* all masked flags are requied */
1546 for (i = 0; i <= 1023; i++) {
1547 /* skip if band has enough frequencies scanned (3.2.1) */
1548 for (j = 0; gsm_sup_smax[j].max; j++) {
1549 if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
1550 if (gsm_sup_smax[j].start >= i
1551 && gsm_sup_smax[j].end <= i)
1554 if (gsm_sup_smax[j].end <= i
1555 || gsm_sup_smax[j].start >= i)
1559 if (gsm_sup_smax[j].max) {
1560 if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
1563 /* search for unscanned frequency */
1564 if ((cs->list[i].flags & mask) == flags) {
1565 /* weight depends on the power level
1566 * if it is the same, it depends on arfcn
1568 test = cs->list[i].rxlev_db + 128;
1569 test = (test << 16) | i;
1570 if (test >= cs->scan_state)
1576 cs->scan_state = weight;
1579 gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs,
1582 /* special case for PLMN search */
1583 if (cs->state == GSM322_PLMN_SEARCH && !weight) {
1586 /* create AA flag */
1587 cs->mcc = cs->mnc = 0;
1588 gsm322_cs_select(ms, 0);
1590 new_c_state(cs, GSM322_C0_NULL);
1592 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END);
1593 LOGP(DCS, LOGL_INFO, "PLMN search finished.\n");
1596 gsm322_plmn_sendmsg(ms, nmsg);
1601 /* special case for HPLMN search */
1602 if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
1605 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1606 LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n");
1609 gsm322_plmn_sendmsg(ms, nmsg);
1611 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
1613 cs->arfcn = cs->sel_arfcn;
1614 LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev "
1615 "%d).\n", cs->arfcn, cs->list[cs->arfcn].rxlev_db);
1616 cs->ccch_state = GSM322_CCCH_ST_INIT;
1618 l1ctl_tx_fbsb_req(ms, cs->arfcn, L1CTL_FBSB_F_FB01SB, 100, 0);
1619 // start_cs_timer(cs, ms->support.sync_to, 0);
1624 /* if all frequencies have been searched */
1630 LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
1632 /* just see, if we search for any cell */
1633 if (cs->state == GSM322_C6_ANY_CELL_SEL
1634 || cs->state == GSM322_C8_ANY_CELL_RESEL
1635 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1638 found = gsm322_cs_select(ms, any);
1642 struct gsm322_plmn *plmn = &ms->plmn;
1644 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1647 cs->si = cs->list[cs->arfcn].sysinfo;
1648 cs->ccch_state = GSM322_CCCH_ST_INIT;
1650 l1ctl_tx_fbsb_req(ms, cs->arfcn, L1CTL_FBSB_F_FB01SB, 100, 0);
1652 /* selected PLMN (manual) or any PLMN (auto) */
1653 switch (ms->settings.plmn_mode) {
1654 case PLMN_MODE_AUTO:
1655 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1656 /* PLMN becomes available */
1657 nmsg = gsm322_msgb_alloc(
1658 GSM322_EVENT_PLMN_AVAIL);
1661 gsm322_plmn_sendmsg(ms, nmsg);
1664 case PLMN_MODE_MANUAL:
1665 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1666 && gsm322_is_plmn_avail(cs, plmn->mcc,
1668 /* PLMN becomes available */
1669 nmsg = gsm322_msgb_alloc(
1670 GSM322_EVENT_PLMN_AVAIL);
1673 gsm322_plmn_sendmsg(ms, nmsg);
1678 /* set selected cell */
1680 cs->sel_arfcn = cs->arfcn;
1681 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1682 cs->sel_mcc = cs->si->mcc;
1683 cs->sel_mnc = cs->si->mnc;
1684 cs->sel_lac = cs->si->lac;
1685 cs->sel_id = cs->si->cell_id;
1687 /* tell CS process about available cell */
1688 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1689 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1692 /* unset selected cell */
1695 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
1696 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id
1699 /* tell CS process about no cell available */
1700 LOGP(DCS, LOGL_INFO, "No cell available.\n");
1701 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1705 gsm322_c_event(ms, nmsg);
1711 /* NOTE: We might already have system information from previous
1712 * scan. But we need recent informations, so we scan again!
1715 /* Tune to frequency for a while, to receive broadcasts. */
1716 cs->arfcn = weight & 1023;
1717 LOGP(DCS, LOGL_INFO, "Scanning frequency %d (rxlev %d).\n", cs->arfcn,
1718 cs->list[cs->arfcn].rxlev_db);
1719 cs->ccch_state = GSM322_CCCH_ST_INIT;
1721 l1ctl_tx_fbsb_req(ms, cs->arfcn, L1CTL_FBSB_F_FB01SB, 100, 0);
1722 // start_cs_timer(cs, ms->support.sync_to, 0);
1724 /* Allocate/clean system information. */
1725 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1726 if (cs->list[cs->arfcn].sysinfo)
1727 memset(cs->list[cs->arfcn].sysinfo, 0,
1728 sizeof(struct gsm48_sysinfo));
1730 cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
1731 struct gsm48_sysinfo);
1732 if (!cs->list[cs->arfcn].sysinfo)
1734 cs->si = cs->list[cs->arfcn].sysinfo;
1736 /* increase scan counter for each maximum scan range */
1737 if (gsm_sup_smax[j].max) {
1738 LOGP(DCS, LOGL_INFO, "%d frequencies left in band %d..%d\n",
1739 gsm_sup_smax[j].max - gsm_sup_smax[j].temp,
1740 gsm_sup_smax[j].start, gsm_sup_smax[j].end);
1741 gsm_sup_smax[j].temp++;
1747 /* check if cell is now suitable and allowable */
1748 static int gsm322_cs_store(struct osmocom_ms *ms)
1750 struct gsm322_cellsel *cs = &ms->cellsel;
1751 struct gsm48_sysinfo *s = cs->si;
1752 struct gsm322_plmn *plmn = &ms->plmn;
1756 if (cs->state != GSM322_C2_STORED_CELL_SEL
1757 && cs->state != GSM322_C1_NORMAL_CELL_SEL
1758 && cs->state != GSM322_C6_ANY_CELL_SEL
1759 && cs->state != GSM322_C4_NORMAL_CELL_RESEL
1760 && cs->state != GSM322_C8_ANY_CELL_RESEL
1761 && cs->state != GSM322_C5_CHOOSE_CELL
1762 && cs->state != GSM322_C9_CHOOSE_ANY_CELL
1763 && cs->state != GSM322_PLMN_SEARCH
1764 && cs->state != GSM322_HPLMN_SEARCH) {
1765 LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
1766 "(re-)selection, please fix!\n");
1771 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO;
1773 && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp &&
1774 cs->list[cs->arfcn].sysinfo->sp_cbq))
1775 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED;
1777 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED;
1780 cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db;
1781 cs->list[cs->arfcn].class_barr = s->class_barr;
1782 cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch;
1785 /* store selected network */
1788 cs->list[cs->arfcn].mcc = s->mcc;
1789 cs->list[cs->arfcn].mnc = s->mnc;
1790 cs->list[cs->arfcn].lac = s->lac;
1793 if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
1794 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD;
1796 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD;
1799 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev=%d "
1800 "mcc=%03d mnc=%02d lac=%04x)\n", cs->arfcn,
1801 cs->list[cs->arfcn].rxlev_db, s->mcc, s->mnc, s->lac);
1803 /* special case for PLMN search */
1804 if (cs->state == GSM322_PLMN_SEARCH)
1805 /* tune to next cell */
1806 return gsm322_cs_scan(ms);
1808 /* special case for HPLMN search */
1809 if (cs->state == GSM322_HPLMN_SEARCH) {
1810 struct gsm_subscriber *subscr = &ms->subscr;
1813 if (!gsm322_is_plmn_avail(cs, subscr->mcc, subscr->mnc))
1814 /* tune to next cell */
1815 return gsm322_cs_scan(ms);
1817 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1818 LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n");
1821 gsm322_plmn_sendmsg(ms, nmsg);
1826 /* just see, if we search for any cell */
1827 if (cs->state == GSM322_C6_ANY_CELL_SEL
1828 || cs->state == GSM322_C8_ANY_CELL_RESEL
1829 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1832 found = gsm322_cs_select(ms, any);
1836 LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n");
1837 /* tune to next cell */
1838 return gsm322_cs_scan(ms);
1841 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1844 cs->si = cs->list[cs->arfcn].sysinfo;
1845 cs->ccch_state = GSM322_CCCH_ST_INIT;
1847 l1ctl_tx_fbsb_req(ms, cs->arfcn, L1CTL_FBSB_F_FB01SB, 100, 0);
1849 /* selected PLMN (manual) or any PLMN (auto) */
1850 switch (ms->settings.plmn_mode) {
1851 case PLMN_MODE_AUTO:
1852 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1853 /* PLMN becomes available */
1854 nmsg = gsm322_msgb_alloc( GSM322_EVENT_PLMN_AVAIL);
1857 gsm322_plmn_sendmsg(ms, nmsg);
1860 case PLMN_MODE_MANUAL:
1861 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1862 && gsm322_is_plmn_avail(cs, plmn->mcc,
1864 /* PLMN becomes available */
1865 nmsg = gsm322_msgb_alloc( GSM322_EVENT_PLMN_AVAIL);
1868 gsm322_plmn_sendmsg(ms, nmsg);
1873 /* set selected cell */
1875 cs->sel_arfcn = cs->arfcn;
1876 memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1877 cs->sel_mcc = cs->si->mcc;
1878 cs->sel_mnc = cs->si->mnc;
1879 cs->sel_lac = cs->si->lac;
1880 cs->sel_id = cs->si->cell_id;
1882 /* tell CS process about available cell */
1883 LOGP(DCS, LOGL_INFO, "Cell available.\n");
1884 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1887 gsm322_c_event(ms, nmsg);
1893 /* process system information when returing to idle mode */
1894 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
1896 struct gsm322_cellsel *cs = &ms->cellsel;
1897 struct gsm48_sysinfo *s = cs->si;
1898 struct gsm322_ba_list *ba = NULL;
1902 /* collect system information received during dedicated mode */
1904 && (!s->nb_ext_ind_si5
1905 || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
1906 || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
1907 && s->nb_ext_ind_si5bis))) {
1908 /* find or create ba list */
1909 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1911 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1916 llist_add_tail(&ba->entry, &cs->ba_list);
1918 /* update (add) ba list */
1919 memcpy(freq, ba->freq, sizeof(freq));
1920 for (i = 0; i <= 1023; i++) {
1921 if ((s->freq[i].mask & FREQ_TYPE_REP))
1922 freq[i >> 3] |= (1 << (i & 7));
1924 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
1925 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%d mnc=%d "
1926 "%s, %s).\n", ba->mcc, ba->mnc,
1927 gsm_get_mcc(ba->mcc),
1928 gsm_get_mnc(ba->mcc, ba->mnc));
1929 memcpy(ba->freq, freq, sizeof(freq));
1936 /* store BA whenever a system informations changes */
1937 static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
1938 struct gsm48_sysinfo *s)
1940 struct gsm322_ba_list *ba;
1944 /* find or create ba list */
1945 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1947 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1952 llist_add_tail(&ba->entry, &cs->ba_list);
1954 /* update ba list */
1955 memset(freq, 0, sizeof(freq));
1956 freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
1957 for (i = 0; i <= 1023; i++) {
1958 if ((s->freq[i].mask &
1959 (FREQ_TYPE_SERV | FREQ_TYPE_NCELL)))
1960 freq[i >> 3] |= (1 << (i & 7));
1962 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
1963 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%d mnc=%d "
1964 "%s, %s).\n", ba->mcc, ba->mnc,
1965 gsm_get_mcc(ba->mcc),
1966 gsm_get_mnc(ba->mcc, ba->mnc));
1967 memcpy(ba->freq, freq, sizeof(freq));
1973 /* process system information during camping on a cell */
1974 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
1976 struct gsm48_rrlayer *rr = &ms->rrlayer;
1977 struct gsm322_cellsel *cs = &ms->cellsel;
1978 struct gsm48_sysinfo *s = cs->si;
1979 struct gsm_subscriber *subscr = &ms->subscr;
1980 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1983 if (rr->state != GSM48_RR_ST_IDLE) {
1984 LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n");
1988 /* Store BA if we have full system info about cells and neigbor cells.
1989 * Depending on the extended bit in the channel description,
1990 * we require more or less system informations about neighbor cells
1994 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
1995 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
1996 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
1997 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2000 && (!s->nb_ext_ind_si2
2001 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2002 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2003 && s->nb_ext_ind_si2bis)))
2004 gsm322_store_ba_list(cs, s);
2006 /* update sel_si, if all relevant system informations received */
2007 if (s->si1 && s->si2 && s->si3
2008 && (!s->nb_ext_ind_si2
2009 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2010 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2011 && s->nb_ext_ind_si2bis))) {
2013 LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is "
2015 memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
2016 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2020 /* check for barred cell */
2021 if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
2022 /* check if cell becomes barred */
2023 if (!subscr->acc_barr && s->cell_barr
2024 && !(cs->list[cs->arfcn].sysinfo
2025 && cs->list[cs->arfcn].sysinfo->sp
2026 && cs->list[cs->arfcn].sysinfo->sp_cbq)) {
2027 LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
2029 /* mark cell as unscanned */
2030 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2031 if (cs->list[cs->arfcn].sysinfo) {
2032 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2034 talloc_free(cs->list[cs->arfcn].sysinfo);
2035 cs->list[cs->arfcn].sysinfo = NULL;
2037 /* trigger reselection without queueing,
2038 * because other sysinfo message may be queued
2041 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2044 gsm322_c_event(ms, nmsg);
2049 /* check if cell access becomes barred */
2050 if (!((subscr->acc_class & 0xfbff)
2051 & (s->class_barr ^ 0xffff))) {
2052 LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
2057 /* check if MCC, MNC, LAC, cell ID changes */
2058 if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc
2059 || cs->sel_lac != s->lac) {
2060 LOGP(DCS, LOGL_NOTICE, "Cell changes location area. "
2061 "This is not good!\n");
2064 if (cs->sel_id != s->cell_id) {
2065 LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. "
2066 "This is not good!\n");
2073 /* process system information during channel scanning */
2074 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2076 struct gsm322_cellsel *cs = &ms->cellsel;
2077 struct gsm48_sysinfo *s = cs->si;
2078 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2080 /* no sysinfo if we are not done with power scan */
2081 if (cs->powerscan) {
2082 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
2086 /* Store BA if we have full system info about cells and neigbor cells.
2087 * Depending on the extended bit in the channel description,
2088 * we require more or less system informations about neighbor cells
2092 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2093 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2094 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2095 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2098 && (!s->nb_ext_ind_si2
2099 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2100 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2101 && s->nb_ext_ind_si2bis)))
2102 gsm322_store_ba_list(cs, s);
2104 /* all relevant system informations received */
2105 if (s->si1 && s->si2 && s->si3
2106 && (!s->nb_ext_ind_si2
2107 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2108 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2109 && s->nb_ext_ind_si2bis))) {
2110 LOGP(DCS, LOGL_INFO, "Received relevant sysinfo.\n");
2114 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2116 /* store sysinfo and continue scan */
2117 return gsm322_cs_store(ms);
2120 /* wait for more sysinfo or timeout */
2124 static void gsm322_cs_timeout(void *arg)
2126 struct gsm322_cellsel *cs = arg;
2127 struct osmocom_ms *ms = cs->ms;
2129 LOGP(DCS, LOGL_INFO, "Cell selection failed.\n");
2131 /* if we have no lock, we retry */
2132 if (cs->ccch_state != GSM322_CCCH_ST_SYNC)
2133 LOGP(DCS, LOGL_INFO, "Sync timeout.\n");
2135 LOGP(DCS, LOGL_INFO, "Read timeout.\n");
2137 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev=%d)\n",
2138 cs->arfcn, cs->list[cs->arfcn].rxlev_db);
2140 /* remove system information */
2141 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2142 if (cs->list[cs->arfcn].sysinfo) {
2143 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", cs->arfcn);
2144 talloc_free(cs->list[cs->arfcn].sysinfo);
2145 cs->list[cs->arfcn].sysinfo = NULL;
2148 /* tune to next cell */
2155 * power scan process
2158 /* search for block of unscanned frequencies and start scanning */
2159 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
2161 struct gsm322_cellsel *cs = &ms->cellsel;
2163 uint8_t mask, flags;
2167 /* search for first frequency to scan */
2168 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
2169 flags = GSM322_CS_FLAG_SUPPORT;
2170 if (cs->state == GSM322_C2_STORED_CELL_SEL
2171 || cs->state == GSM322_C5_CHOOSE_CELL) {
2172 LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA list.\n");
2173 mask |= GSM322_CS_FLAG_BA;
2174 flags |= GSM322_CS_FLAG_BA;
2176 LOGP(DCS, LOGL_FATAL, "Scanning power for all frequencies.\n");
2177 for (i = 0; i <= 1023; i++) {
2178 if ((cs->list[i].flags & mask) == flags) {
2184 /* if there is no more frequency, we can tune to that cell */
2188 /* stop power level scanning */
2191 /* check if not signal is found */
2192 for (i = 0; i <= 1023; i++) {
2193 if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2199 LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
2200 /* on normal cell selection, start over */
2201 if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
2202 for (i = 0; i <= 1023; i++) {
2203 /* clear flag that this was scanned */
2204 cs->list[i].flags &=
2205 ~(GSM322_CS_FLAG_POWER
2206 | GSM322_CS_FLAG_SIGNAL
2207 | GSM322_CS_FLAG_SYSINFO);
2208 if (cs->list[i].sysinfo) {
2209 LOGP(DCS, LOGL_INFO, "free "
2210 "sysinfo arfcn=%d\n",
2213 cs->list[i].sysinfo);
2214 cs->list[i].sysinfo = NULL;
2219 /* on other cell selection, indicate "no cell found" */
2220 /* NOTE: PLMN search process handles it.
2221 * If not handled there, CS process gets indicated.
2222 * If we would continue to process CS, then we might get
2223 * our list of scanned cells disturbed.
2225 if (cs->state == GSM322_PLMN_SEARCH)
2226 nmsg = gsm322_msgb_alloc(
2227 GSM322_EVENT_PLMN_SEARCH_END);
2229 nmsg = gsm322_msgb_alloc(
2230 GSM322_EVENT_NO_CELL_FOUND);
2233 gsm322_plmn_sendmsg(ms, nmsg);
2235 /* if HPLMN search, select last frequency */
2236 if (cs->state == GSM322_HPLMN_SEARCH) {
2237 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2239 cs->arfcn = cs->sel_arfcn;
2240 LOGP(DCS, LOGL_INFO, "Tuning back to frequency "
2241 "%d (rxlev %d).\n", cs->arfcn,
2242 cs->list[cs->arfcn].rxlev_db);
2243 cs->ccch_state = GSM322_CCCH_ST_INIT;
2245 l1ctl_tx_fbsb_req(ms, cs->arfcn, L1CTL_FBSB_F_FB01SB, 100, 0);
2246 // start_cs_timer(cs, ms->support.sync_to, 0);
2249 new_c_state(cs, GSM322_C0_NULL);
2253 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
2254 cs->scan_state = 0xffffffff; /* higher than high */
2255 /* clear counter of scanned frequencies of each range */
2256 for (i = 0; gsm_sup_smax[i].max; i++)
2257 gsm_sup_smax[i].temp = 0;
2258 return gsm322_cs_scan(ms);
2261 /* search last frequency to scan (en block) */
2263 for (i = s + 1; i <= 1023; i++) {
2264 if ((cs->list[i].flags & mask) == flags)
2270 LOGP(DCS, LOGL_INFO, "Scanning frequencies. (%d..%d)\n", s, e);
2272 /* start scan on radio interface */
2274 #warning TESTING!!!!
2276 return l1ctl_tx_pm_req_range(ms, s, e);
2279 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
2280 void *handler_data, void *signal_data)
2282 struct osmocom_ms *ms;
2283 struct gsm322_cellsel *cs;
2284 struct osmobb_meas_res *mr;
2288 if (subsys != SS_L1CTL)
2292 case S_L1CTL_PM_RES:
2298 i = mr->band_arfcn & 1023;
2299 rxlev_db = mr->rx_lev - 110;
2300 cs->list[i].rxlev_db = rxlev_db;
2301 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2302 if (rxlev_db >= ms->support.min_rxlev_db) {
2303 cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2304 LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2305 "rxlev %d)\n", i, cs->list[i].rxlev_db);
2308 case S_L1CTL_PM_DONE:
2309 LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
2314 gsm322_cs_powerscan(ms);
2316 case S_L1CTL_FBSB_RESP:
2319 if (cs->ccch_state == GSM322_CCCH_ST_INIT) {
2320 LOGP(DCS, LOGL_INFO, "Channel synched.\n");
2321 cs->ccch_state = GSM322_CCCH_ST_SYNC;
2325 /* in dedicated mode */
2326 if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
2327 return gsm48_rr_tx_rand_acc(ms, NULL);
2330 /* set timer for reading BCCH */
2331 if (cs->state == GSM322_C2_STORED_CELL_SEL
2332 || cs->state == GSM322_C1_NORMAL_CELL_SEL
2333 || cs->state == GSM322_C6_ANY_CELL_SEL
2334 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2335 || cs->state == GSM322_C8_ANY_CELL_RESEL
2336 || cs->state == GSM322_C5_CHOOSE_CELL
2337 || cs->state == GSM322_C9_CHOOSE_ANY_CELL
2338 || cs->state == GSM322_PLMN_SEARCH
2339 || cs->state == GSM322_HPLMN_SEARCH)
2340 start_cs_timer(cs, ms->support.scan_to, 0);
2341 // TODO: timer depends on BCCH config
2344 case S_L1CTL_FBSB_ERR:
2348 l1ctl_tx_fbsb_req(ms, cs->arfcn, L1CTL_FBSB_F_FB01SB, 100, 0);
2350 LOGP(DCS, LOGL_INFO, "Channel sync error, try again.\n");
2353 LOGP(DCS, LOGL_INFO, "Channel sync error.\n");
2361 gsm322_cs_timeout(cs);
2368 static void gsm322_cs_loss(void *arg)
2370 struct gsm322_cellsel *cs = arg;
2371 struct osmocom_ms *ms = cs->ms;
2372 struct gsm48_rrlayer *rr = &ms->rrlayer;
2374 LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n");
2375 if (cs->state == GSM322_C3_CAMPED_NORMALLY
2376 || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
2377 if (rr->state == GSM48_RR_ST_IDLE) {
2380 LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n");
2382 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2385 gsm322_c_event(ms, nmsg);
2388 LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n");
2390 /* be shure that nothing else is done after here
2391 * because the function call above may cause
2392 * to return from idle state and trigger cell re-sel.
2401 * handler for cell selection process
2404 /* start PLMN search */
2405 static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg)
2407 struct gsm322_cellsel *cs = &ms->cellsel;
2410 new_c_state(cs, GSM322_PLMN_SEARCH);
2412 /* mark all frequencies except our own BA to be scanned */
2413 for (i = 0; i <= 1023; i++) {
2414 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2415 | GSM322_CS_FLAG_SIGNAL
2416 | GSM322_CS_FLAG_SYSINFO);
2417 if (cs->list[i].sysinfo) {
2418 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2419 talloc_free(cs->list[i].sysinfo);
2420 cs->list[i].sysinfo = NULL;
2424 /* unset selected cell */
2427 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2428 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
2430 /* start power scan */
2431 return gsm322_cs_powerscan(ms);
2434 /* start HPLMN search */
2435 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2437 struct gsm322_cellsel *cs = &ms->cellsel;
2440 new_c_state(cs, GSM322_HPLMN_SEARCH);
2442 /* mark all frequencies except our own BA to be scanned */
2443 for (i = 0; i <= 1023; i++) {
2444 if (i != cs->sel_arfcn
2445 && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2446 && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2447 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2448 | GSM322_CS_FLAG_SIGNAL
2449 | GSM322_CS_FLAG_SYSINFO);
2450 if (cs->list[i].sysinfo) {
2451 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2453 talloc_free(cs->list[i].sysinfo);
2454 cs->list[i].sysinfo = NULL;
2459 /* start power scan */
2460 return gsm322_cs_powerscan(ms);
2463 /* start stored cell selection */
2464 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2466 struct gsm322_cellsel *cs = &ms->cellsel;
2469 new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2471 /* flag all frequencies that are in current band allocation */
2472 for (i = 0; i <= 1023; i++) {
2473 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2474 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2476 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2479 /* unset selected cell */
2482 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2483 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
2485 /* start power scan */
2486 return gsm322_cs_powerscan(ms);
2489 /* start noraml cell selection */
2490 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2492 struct gsm322_cellsel *cs = &ms->cellsel;
2495 /* except for stored cell selection state, we weed to rescan ?? */
2496 if (cs->state != GSM322_C2_STORED_CELL_SEL) {
2497 for (i = 0; i <= 1023; i++) {
2498 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2499 | GSM322_CS_FLAG_SIGNAL
2500 | GSM322_CS_FLAG_SYSINFO);
2501 if (cs->list[i].sysinfo) {
2502 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2504 talloc_free(cs->list[i].sysinfo);
2505 cs->list[i].sysinfo = NULL;
2510 new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2512 /* unset selected cell */
2515 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2516 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
2518 /* start power scan */
2519 return gsm322_cs_powerscan(ms);
2522 /* start any cell selection */
2523 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2525 struct gsm322_cellsel *cs = &ms->cellsel;
2527 /* in case we already tried any cell (re-)selection, power scan again */
2528 if (cs->state == GSM322_C0_NULL
2529 || cs->state == GSM322_C6_ANY_CELL_SEL
2530 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2533 for (i = 0; i <= 1023; i++) {
2534 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2535 | GSM322_CS_FLAG_SIGNAL
2536 | GSM322_CS_FLAG_SYSINFO);
2537 if (cs->list[i].sysinfo) {
2538 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2540 talloc_free(cs->list[i].sysinfo);
2541 cs->list[i].sysinfo = NULL;
2545 /* after re-selection, indicate no cell found */
2546 if (cs->state == GSM322_C6_ANY_CELL_SEL
2547 || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2550 /* tell that we have no cell found */
2551 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2554 gsm48_mmevent_msg(ms, nmsg);
2557 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2560 cs->mcc = cs->mnc = 0;
2562 /* unset selected cell */
2565 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2566 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
2568 /* start power scan */
2569 return gsm322_cs_powerscan(ms);
2572 /* start noraml cell re-selection */
2573 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2575 struct gsm322_cellsel *cs = &ms->cellsel;
2577 new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2579 /* NOTE: We keep our scan info we have so far.
2580 * This may cause a skip in power scan. */
2582 /* start power scan */
2583 return gsm322_cs_powerscan(ms);
2586 /* start any cell re-selection */
2587 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2589 struct gsm322_cellsel *cs = &ms->cellsel;
2591 new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2593 /* NOTE: We keep our scan info we have so far.
2594 * This may cause a skip in power scan. */
2596 /* start power scan */
2597 return gsm322_cs_powerscan(ms);
2600 /* create temporary ba range with given frequency ranges */
2601 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2602 uint32_t *range, uint8_t ranges)
2604 static struct gsm322_ba_list ba;
2605 uint16_t lower, higher;
2607 memset(&ba, 0, sizeof(ba));
2610 lower = *range & 1023;
2611 higher = (*range >> 16) & 1023;
2613 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2616 ba.freq[lower >> 3] |= 1 << (lower & 7);
2617 if (lower == higher)
2619 lower = (lower + 1) & 1023;
2626 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2627 static int gsm322_cs_choose(struct osmocom_ms *ms)
2629 struct gsm322_cellsel *cs = &ms->cellsel;
2630 struct gsm48_rrlayer *rr = &ms->rrlayer;
2631 struct gsm322_ba_list *ba = NULL;
2634 /* NOTE: The call to this function is synchron to RR layer, so
2635 * we may access the BA range there.
2638 ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges);
2640 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2641 /* get and update BA of last received sysinfo 5* */
2642 ba = gsm322_cs_sysinfo_sacch(ms);
2644 LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored "
2646 ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2654 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2656 /* tell CS to start over */
2657 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2660 gsm322_c_event(ms, nmsg);
2666 /* flag all frequencies that are in current band allocation */
2667 for (i = 0; i <= 1023; i++) {
2668 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2669 if ((ba->freq[i >> 3] & (1 << (i & 7)))) {
2670 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2672 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2675 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2676 | GSM322_CS_FLAG_SIGNAL
2677 | GSM322_CS_FLAG_SYSINFO);
2678 if (cs->list[i].sysinfo) {
2679 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2680 talloc_free(cs->list[i].sysinfo);
2681 cs->list[i].sysinfo = NULL;
2685 /* unset selected cell */
2688 memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2689 cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
2691 /* start power scan */
2692 return gsm322_cs_powerscan(ms);
2695 /* start 'Choose cell' after returning to idle mode */
2696 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2698 struct gsm322_cellsel *cs = &ms->cellsel;
2700 new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2702 return gsm322_cs_choose(ms);
2705 /* start 'Choose any cell' after returning to idle mode */
2706 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2708 struct gsm322_cellsel *cs = &ms->cellsel;
2710 new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2712 return gsm322_cs_choose(ms);
2715 /* a new PLMN is selected by PLMN search process */
2716 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2718 struct gsm322_cellsel *cs = &ms->cellsel;
2719 struct gsm322_plmn *plmn = &ms->plmn;
2720 struct gsm322_ba_list *ba;
2722 cs->mcc = plmn->mcc;
2723 cs->mnc = plmn->mnc;
2725 LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%03d "
2726 "mnc=%02d %s, %s)\n", cs->mcc, cs->mnc, gsm_get_mcc(cs->mcc),
2727 gsm_get_mnc(cs->mcc, cs->mnc));
2729 /* search for BA list */
2730 ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2733 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2734 return gsm322_c_stored_cell_sel(ms, ba);
2736 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2737 return gsm322_c_normal_cell_sel(ms, msg);
2741 /* a suitable cell was found, so we camp normally */
2742 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2744 struct gsm322_cellsel *cs = &ms->cellsel;
2747 LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%03d "
2748 "mnc=%02d %s, %s)\n", cs->sel_arfcn, cs->sel_mcc,
2749 cs->sel_mnc, gsm_get_mcc(cs->sel_mcc),
2750 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2752 /* tell that we have selected a (new) cell */
2753 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2756 gsm48_mmevent_msg(ms, nmsg);
2758 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2763 /* a not suitable cell was found, so we camp on any cell */
2764 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2766 struct gsm322_cellsel *cs = &ms->cellsel;
2769 LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%03d "
2770 "mnc=%02d %s, %s)\n", cs->sel_arfcn, cs->sel_mcc,
2771 cs->sel_mnc, gsm_get_mcc(cs->sel_mcc),
2772 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2775 /* tell that we have selected a (new) cell */
2776 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2779 gsm48_mmevent_msg(ms, nmsg);
2781 new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2786 /* go connected mode */
2787 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2789 struct gsm322_cellsel *cs = &ms->cellsel;
2791 /* check for error */
2794 cs->arfcn = cs->sel_arfcn;
2796 /* be sure to go to current camping frequency on return */
2797 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2798 cs->ccch_state = GSM322_CCCH_ST_INIT;
2800 l1ctl_tx_fbsb_req(ms, cs->arfcn, L1CTL_FBSB_F_FB01SB, 100, 0);
2801 cs->si = cs->list[cs->arfcn].sysinfo;
2802 #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.
2808 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2810 struct gsm322_cellsel *cs = &ms->cellsel;
2812 /* check for error */
2815 cs->arfcn = cs->sel_arfcn;
2817 /* be sure to go to current camping frequency on return */
2818 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2819 cs->ccch_state = GSM322_CCCH_ST_INIT;
2821 l1ctl_tx_fbsb_req(ms, cs->arfcn, L1CTL_FBSB_F_FB01SB, 100, 0);
2822 cs->si = cs->list[cs->arfcn].sysinfo;
2828 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2830 struct gsm_subscriber *subscr = &ms->subscr;
2832 /* if no SIM is is MS */
2833 if (!subscr->sim_valid) {
2834 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2835 return gsm322_c_any_cell_sel(ms, msg);
2836 LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2839 /* stay in NULL state until PLMN is selected */
2848 /* state machine for automatic PLMN selection events */
2849 static struct plmnastatelist {
2852 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2853 } plmnastatelist[] = {
2854 {SBIT(GSM322_A0_NULL),
2855 GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2857 {SBIT(GSM322_A0_NULL),
2858 GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn},
2861 GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
2863 {SBIT(GSM322_A6_NO_SIM),
2864 GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
2867 GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
2870 GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
2873 GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
2875 {SBIT(GSM322_A1_TRYING_RPLMN),
2876 GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
2878 {SBIT(GSM322_A1_TRYING_RPLMN),
2879 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn},
2881 {SBIT(GSM322_A1_TRYING_RPLMN),
2882 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
2884 {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
2885 GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
2887 {SBIT(GSM322_A2_ON_PLMN),
2888 GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
2890 {SBIT(GSM322_A2_ON_PLMN),
2891 GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start},
2893 {SBIT(GSM322_A2_ON_PLMN),
2894 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
2896 {SBIT(GSM322_A2_ON_PLMN),
2897 GSM322_EVENT_USER_RESEL, gsm322_a_user_resel},
2899 {SBIT(GSM322_A3_TRYING_PLMN),
2900 GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
2902 {SBIT(GSM322_A3_TRYING_PLMN),
2903 GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn},
2905 {SBIT(GSM322_A3_TRYING_PLMN),
2906 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
2908 {SBIT(GSM322_A5_HPLMN_SEARCH),
2909 GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn},
2911 {SBIT(GSM322_A5_HPLMN_SEARCH),
2912 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn},
2914 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
2915 GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
2918 GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
2921 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
2924 #define PLMNASLLEN \
2925 (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
2927 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
2929 struct gsm322_plmn *plmn = &ms->plmn;
2930 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2931 int msg_type = gm->msg_type;
2935 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
2936 "selection in state %s\n", ms->name, get_event_name(msg_type),
2937 plmn_a_state_names[plmn->state]);
2938 /* find function for current state and message */
2939 for (i = 0; i < PLMNASLLEN; i++)
2940 if ((msg_type == plmnastatelist[i].type)
2941 && ((1 << plmn->state) & plmnastatelist[i].states))
2943 if (i == PLMNASLLEN) {
2944 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
2948 rc = plmnastatelist[i].rout(ms, msg);
2953 /* state machine for manual PLMN selection events */
2954 static struct plmnmstatelist {
2957 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2958 } plmnmstatelist[] = {
2959 {SBIT(GSM322_M0_NULL),
2960 GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
2962 {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) |
2963 SBIT(GSM322_M2_ON_PLMN),
2964 GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns},
2967 GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
2969 {SBIT(GSM322_M5_NO_SIM),
2970 GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
2973 GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
2976 GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
2978 {SBIT(GSM322_M1_TRYING_RPLMN),
2979 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
2981 {SBIT(GSM322_M1_TRYING_RPLMN),
2982 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
2984 {SBIT(GSM322_M1_TRYING_RPLMN),
2985 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
2987 {SBIT(GSM322_M1_TRYING_RPLMN),
2988 GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
2990 {SBIT(GSM322_M2_ON_PLMN),
2991 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
2993 {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
2994 SBIT(GSM322_M4_TRYING_PLMN),
2995 GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
2997 {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN),
2998 GSM322_EVENT_USER_RESEL, gsm322_m_user_resel},
3000 {SBIT(GSM322_M3_NOT_ON_PLMN),
3001 GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
3003 {SBIT(GSM322_M3_NOT_ON_PLMN),
3004 GSM322_EVENT_CHOSE_PLMN, gsm322_m_choose_plmn},
3006 {SBIT(GSM322_M4_TRYING_PLMN),
3007 GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
3009 {SBIT(GSM322_M4_TRYING_PLMN),
3010 GSM322_EVENT_REG_FAILED, gsm322_m_go_not_on_plmn},
3012 {SBIT(GSM322_M4_TRYING_PLMN),
3013 GSM322_EVENT_ROAMING_NA, gsm322_m_go_not_on_plmn},
3015 {SBIT(GSM322_M4_TRYING_PLMN),
3016 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_go_not_on_plmn},
3019 GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
3022 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3025 #define PLMNMSLLEN \
3026 (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
3028 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
3030 struct gsm322_plmn *plmn = &ms->plmn;
3031 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3032 int msg_type = gm->msg_type;
3036 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
3037 "in state %s\n", ms->name, get_event_name(msg_type),
3038 plmn_m_state_names[plmn->state]);
3039 /* find function for current state and message */
3040 for (i = 0; i < PLMNMSLLEN; i++)
3041 if ((msg_type == plmnmstatelist[i].type)
3042 && ((1 << plmn->state) & plmnmstatelist[i].states))
3044 if (i == PLMNMSLLEN) {
3045 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3049 rc = plmnmstatelist[i].rout(ms, msg);
3054 /* dequeue GSM 03.22 PLMN events */
3055 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
3057 struct gsm322_plmn *plmn = &ms->plmn;
3061 while ((msg = msgb_dequeue(&plmn->event_queue))) {
3062 /* send event to PLMN select process */
3063 if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
3064 gsm322_a_event(ms, msg);
3066 gsm322_m_event(ms, msg);
3068 work = 1; /* work done */
3074 /* state machine for channel selection events */
3075 static struct cellselstatelist {
3078 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3079 } cellselstatelist[] = {
3081 GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
3084 GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
3087 GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
3090 GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search},
3092 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3093 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
3094 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
3096 {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3097 SBIT(GSM322_C8_ANY_CELL_RESEL),
3098 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
3100 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3101 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3102 SBIT(GSM322_C0_NULL),
3103 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
3105 {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3106 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
3107 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
3109 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3110 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
3112 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3113 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
3115 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3116 GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
3118 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3119 GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
3121 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3122 GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
3124 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3125 GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
3127 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3128 GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
3130 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3131 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3132 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3133 SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH),
3134 GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
3136 {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
3137 GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
3139 {SBIT(GSM322_C3_CAMPED_NORMALLY),
3140 GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search},
3143 #define CELLSELSLLEN \
3144 (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
3146 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
3148 struct gsm322_cellsel *cs = &ms->cellsel;
3149 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3150 int msg_type = gm->msg_type;
3154 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
3155 "%s\n", ms->name, get_event_name(msg_type),
3156 cs_state_names[cs->state]);
3157 /* find function for current state and message */
3158 for (i = 0; i < CELLSELSLLEN; i++)
3159 if ((msg_type == cellselstatelist[i].type)
3160 && ((1 << cs->state) & cellselstatelist[i].states))
3162 if (i == CELLSELSLLEN) {
3163 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
3167 rc = cellselstatelist[i].rout(ms, msg);
3172 /* dequeue GSM 03.22 cell selection events */
3173 int gsm322_cs_dequeue(struct osmocom_ms *ms)
3175 struct gsm322_cellsel *cs = &ms->cellsel;
3179 while ((msg = msgb_dequeue(&cs->event_queue))) {
3180 /* send event to cell selection process */
3181 gsm322_c_event(ms, msg);
3183 work = 1; /* work done */
3193 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
3195 struct gsm322_plmn *plmn = &ms->plmn;
3196 struct gsm322_plmn_list *temp;
3198 printf("MCC |MNC |allowed|rx-lev\n");
3199 printf("-------+-------+-------+-------\n");
3200 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
3201 printf("%03d |%02d |%s |%d\n", temp->mcc, temp->mnc,
3202 (temp->cause) ? "no ":"yes", temp->rxlev_db);
3208 int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
3209 void (*print)(void *, const char *, ...), void *priv)
3212 struct gsm48_sysinfo *s;
3214 print(priv, "arfcn |rx-lev |MCC |MNC |LAC |cell ID|forb.LA|"
3215 "prio |min-db |max-pwr\n");
3216 print(priv, "-------+-------+-------+-------+-------+-------+-------+"
3217 "-------+-------+-------\n");
3218 for (i = 0; i <= 1023; i++) {
3219 s = cs->list[i].sysinfo;
3220 if (!s || !(cs->list[i].flags & flags))
3222 print(priv, "%4d |%4d |", i, cs->list[i].rxlev_db);
3223 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
3224 print(priv, "%03d |%02d |", s->mcc, s->mnc);
3225 print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id);
3226 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
3227 print(priv, "yes |");
3229 print(priv, "no |");
3230 if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
3231 print(priv, "barred |");
3233 if (cs->list[i].sysinfo->cell_barr)
3234 print(priv, "low |");
3236 print(priv, "normal |");
3238 print(priv, "%4d |%4d\n", s->rxlev_acc_min_db,
3239 s->ms_txpwr_max_ccch);
3241 print(priv, "n/a |n/a |n/a |n/a |n/a |"
3249 int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
3250 void (*print)(void *, const char *, ...), void *priv)
3252 struct gsm322_plmn *plmn = &ms->plmn;
3253 struct gsm322_la_list *temp;
3255 print(priv, "MCC |MNC |LAC |cause\n");
3256 print(priv, "-------+-------+-------+-------\n");
3257 llist_for_each_entry(temp, &plmn->forbidden_la, entry)
3258 print(priv, "%03d |%02d |0x%04x |#%d\n",
3259 temp->mcc, temp->mnc, temp->lac, temp->cause);
3264 int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
3265 void (*print)(void *, const char *, ...), void *priv)
3267 struct gsm322_ba_list *ba;
3270 llist_for_each_entry(ba, &cs->ba_list, entry) {
3271 if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc))
3273 print(priv, "Band Allocation of network: MCC %03d MNC %02d "
3274 "(%s, %s)\n", ba->mcc, ba->mnc, gsm_get_mcc(ba->mcc),
3275 gsm_get_mnc(ba->mcc, ba->mnc));
3276 for (i = 0; i <= 1023; i++) {
3277 if ((ba->freq[i >> 3] & (1 << (i & 7))))
3278 print(priv, " %d", i);
3290 int gsm322_init(struct osmocom_ms *ms)
3292 struct gsm322_plmn *plmn = &ms->plmn;
3293 struct gsm322_cellsel *cs = &ms->cellsel;
3295 char suffix[] = ".ba";
3296 char filename[sizeof(ms->name) + strlen(suffix) + 1];
3298 struct gsm322_ba_list *ba;
3301 LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
3302 LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
3304 memset(plmn, 0, sizeof(*plmn));
3305 memset(cs, 0, sizeof(*cs));
3309 /* set initial state */
3312 ms->settings.plmn_mode = PLMN_MODE_AUTO;
3315 INIT_LLIST_HEAD(&plmn->event_queue);
3316 INIT_LLIST_HEAD(&cs->event_queue);
3317 INIT_LLIST_HEAD(&plmn->sorted_plmn);
3318 INIT_LLIST_HEAD(&plmn->forbidden_la);
3319 INIT_LLIST_HEAD(&cs->ba_list);
3321 /* set supported frequencies in cell selection list */
3322 for (i = 0; i <= 1023; i++)
3323 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
3324 cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
3327 strcpy(filename, ms->name);
3328 strcat(filename, suffix);
3329 fp = osmocom_fopen(filename, "r");
3333 while(!osmocom_feof(fp)) {
3334 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
3337 rc = osmocom_fread(buf, 4, 1, fp);
3342 ba->mcc = (buf[0] << 8) | buf[1];
3343 ba->mnc = (buf[2] << 8) | buf[3];
3344 rc = osmocom_fread(ba->freq, sizeof(ba->freq), 1, fp);
3349 llist_add_tail(&ba->entry, &cs->ba_list);
3350 LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%d "
3351 "mnc=%d %s, %s)\n", ba->mcc, ba->mnc,
3352 gsm_get_mcc(ba->mcc),
3353 gsm_get_mnc(ba->mcc, ba->mnc));
3357 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
3359 register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3364 int gsm322_exit(struct osmocom_ms *ms)
3366 struct gsm322_plmn *plmn = &ms->plmn;
3367 struct gsm322_cellsel *cs = &ms->cellsel;
3368 struct llist_head *lh, *lh2;
3371 char suffix[] = ".ba";
3372 char filename[sizeof(ms->name) + strlen(suffix) + 1];
3373 struct gsm322_ba_list *ba;
3377 LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
3378 LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
3380 unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3382 /* stop cell selection process (if any) */
3383 new_c_state(cs, GSM322_C0_NULL);
3387 stop_plmn_timer(plmn);
3390 for (i = 0; i <= 1023; i++) {
3391 if (cs->list[i].sysinfo) {
3392 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
3393 talloc_free(cs->list[i].sysinfo);
3394 cs->list[i].sysinfo = NULL;
3396 cs->list[i].flags = 0;
3400 strcpy(filename, ms->name);
3401 strcat(filename, suffix);
3402 fp = osmocom_fopen(filename, "w");
3406 llist_for_each_entry(ba, &cs->ba_list, entry) {
3407 buf[0] = ba->mcc >> 8;
3408 buf[1] = ba->mcc & 0xff;
3409 buf[2] = ba->mnc >> 8;
3410 buf[3] = ba->mnc & 0xff;
3411 rc = osmocom_fwrite(buf, 4, 1, fp);
3412 rc = osmocom_fwrite(ba->freq, sizeof(ba->freq), 1, fp);
3413 LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%d "
3414 "mnc=%d %s, %s)\n", ba->mcc, ba->mnc,
3415 gsm_get_mcc(ba->mcc),
3416 gsm_get_mnc(ba->mcc, ba->mnc));
3420 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
3423 while ((msg = msgb_dequeue(&plmn->event_queue)))
3425 while ((msg = msgb_dequeue(&cs->event_queue)))
3427 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
3431 llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
3435 llist_for_each_safe(lh, lh2, &cs->ba_list) {