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>
42 static void gsm322_cs_timeout(void *arg);
43 static int gsm322_cs_select(struct osmocom_ms *ms, int any);
44 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg);
50 /* Cell selection process
52 * The process depends on states and events (finites state machine).
54 * During states of cell selection or cell re-selection, the search for a cell
55 * is performed in two steps:
57 * 1. Measurement of received level of all relevant frequencies (rx-lev)
59 * 2. Receive system information messages of all relevant frequencies
61 * During this process, the results are stored in a list of all frequencies.
62 * This list is checked whenever a cell is selected. It depends on the results
63 * if the cell is 'suitable' and 'allowable' to 'camp' on.
65 * This list is also used to generate a list of available networks.
68 /* PLMN selection process
70 * The PLMN (Public Land Mobile Network = Operator's Network) has two different
77 * The process depends on states and events (finites state machine).
81 /* File format of BA list:
86 * where frequency 0 is bit 0 of first byte
88 * If not end-of-file, the next BA list is stored.
95 * The "PLMN Selector list" stores prefered networks to select during PLMN
96 * search process. This list is also stored in the SIM.
100 * The "forbidden PLMNs" list stores all networks that rejected us. The stored
101 * network will not be used when searching PLMN automatically. This list is
102 * also stored din the SIM.
104 * * plmn->forbidden_la
106 * The "forbidden LAs for roaming" list stores all location areas where roaming
111 * This list stores measurements and cell informations during cell selection
112 * process. It can be used to speed up repeated cell selection.
116 * This list stores a map of frequencies used for a PLMN. If this lists exists
117 * for a PLMN, it helps to speedup cell scan process.
119 * * plmn->sorted_plmn
121 * This list is generated whenever a PLMN search is started and a list of PLMNs
122 * is required. It consists of home PLMN, PLMN Selector list, and PLMNs found
123 * during scan process.
130 static const struct value_string gsm322_event_names[] = {
131 { GSM322_EVENT_SWITCH_ON, "EVENT_SWITCH_ON" },
132 { GSM322_EVENT_SWITCH_OFF, "EVENT_SWITCH_OFF" },
133 { GSM322_EVENT_SIM_INSERT, "EVENT_SIM_INSERT" },
134 { GSM322_EVENT_SIM_REMOVE, "EVENT_SIM_REMOVE" },
135 { GSM322_EVENT_REG_FAILED, "EVENT_REG_FAILED" },
136 { GSM322_EVENT_ROAMING_NA, "EVENT_ROAMING_NA" },
137 { GSM322_EVENT_INVALID_SIM, "EVENT_INVALID_SIM" },
138 { GSM322_EVENT_REG_SUCCESS, "EVENT_REG_SUCCESS" },
139 { GSM322_EVENT_NEW_PLMN, "EVENT_NEW_PLMN" },
140 { GSM322_EVENT_ON_PLMN, "EVENT_ON_PLMN" },
141 { GSM322_EVENT_HPLMN_SEARCH, "EVENT_HPLMN_SEARCH" },
142 { GSM322_EVENT_HPLMN_FOUND, "EVENT_HPLMN_FOUND" },
143 { GSM322_EVENT_HPLMN_NOT_FOUND, "EVENT_HPLMN_NOT_FOUND" },
144 { GSM322_EVENT_USER_RESEL, "EVENT_USER_RESEL" },
145 { GSM322_EVENT_PLMN_AVAIL, "EVENT_PLMN_AVAIL" },
146 { GSM322_EVENT_CHOSE_PLMN, "EVENT_CHOSE_PLMN" },
147 { GSM322_EVENT_SEL_MANUAL, "EVENT_SEL_MANUAL" },
148 { GSM322_EVENT_SEL_AUTO, "EVENT_SEL_AUTO" },
149 { GSM322_EVENT_CELL_FOUND, "EVENT_CELL_FOUND" },
150 { GSM322_EVENT_NO_CELL_FOUND, "EVENT_NO_CELL_FOUND" },
151 { GSM322_EVENT_LEAVE_IDLE, "EVENT_LEAVE_IDLE" },
152 { GSM322_EVENT_RET_IDLE, "EVENT_RET_IDLE" },
153 { GSM322_EVENT_CELL_RESEL, "EVENT_CELL_RESEL" },
154 { GSM322_EVENT_SYSINFO, "EVENT_SYSINFO" },
158 const char *get_event_name(int value)
160 return get_value_string(gsm322_event_names, value);
164 /* allocate a 03.22 event message */
165 struct msgb *gsm322_msgb_alloc(int msg_type)
168 struct gsm322_msg *gm;
170 msg = msgb_alloc_headroom(sizeof(*gm), 0, "GSM 03.22 event");
174 gm = (struct gsm322_msg *)msgb_put(msg, sizeof(*gm));
175 gm->msg_type = msg_type;
180 /* queue PLMN selection message */
181 int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
183 struct gsm322_plmn *plmn = &ms->plmn;
185 msgb_enqueue(&plmn->event_queue, msg);
190 /* queue cell selection message */
191 int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
193 struct gsm322_cellsel *cs = &ms->cellsel;
195 msgb_enqueue(&cs->event_queue, msg);
204 /* del forbidden PLMN */
205 int gsm322_del_forbidden_plmn(struct osmocom_ms *ms, uint16_t mcc,
208 struct gsm_subscriber *subscr = &ms->subscr;
209 struct gsm_sub_plmn_na *na;
211 llist_for_each_entry(na, &subscr->plmn_na, entry) {
212 if (na->mcc == mcc && na->mnc == mnc) {
213 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
214 "PLMNs (mcc=%03d, mnc=%02d)\n", mcc, mnc);
215 llist_del(&na->entry);
218 update plmn not allowed list on sim
227 /* add forbidden PLMN */
228 int gsm322_add_forbidden_plmn(struct osmocom_ms *ms, uint16_t mcc,
229 uint16_t mnc, uint8_t cause)
231 struct gsm_subscriber *subscr = &ms->subscr;
232 struct gsm_sub_plmn_na *na;
234 /* don't add Home PLMN */
235 if (subscr->sim_valid && mcc == subscr->mcc && mnc == subscr->mnc)
238 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs "
239 "(mcc=%03d, mnc=%02d)\n", mcc, mnc);
240 na = talloc_zero(l23_ctx, struct gsm_sub_plmn_na);
246 llist_add_tail(&na->entry, &subscr->plmn_na);
249 update plmn not allowed list on sim
255 /* search forbidden PLMN */
256 int gsm322_is_forbidden_plmn(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc)
258 struct gsm_subscriber *subscr = &ms->subscr;
259 struct gsm_sub_plmn_na *na;
261 llist_for_each_entry(na, &subscr->plmn_na, entry) {
262 if (na->mcc == mcc && na->mnc == mnc)
269 /* del forbidden LA */
270 int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
271 uint16_t mnc, uint16_t lac)
273 struct gsm322_plmn *plmn = &ms->plmn;
274 struct gsm322_la_list *la;
276 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
277 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
278 LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
279 "LAs (mcc=%03d, mnc=%02d, lac=%04x)\n",
281 llist_del(&la->entry);
290 /* add forbidden LA */
291 int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
292 uint16_t mnc, uint16_t lac, uint8_t cause)
294 struct gsm322_plmn *plmn = &ms->plmn;
295 struct gsm322_la_list *la;
297 LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
298 "(mcc=%03d, mnc=%02d, lac=%04x)\n", mcc, mnc, lac);
299 la = talloc_zero(l23_ctx, struct gsm322_la_list);
306 llist_add_tail(&la->entry, &plmn->forbidden_la);
311 /* search forbidden LA */
312 int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
315 struct gsm322_plmn *plmn = &ms->plmn;
316 struct gsm322_la_list *la;
318 llist_for_each_entry(la, &plmn->forbidden_la, entry) {
319 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
326 /* search for PLMN in all BA lists */
327 static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
328 uint16_t mcc, uint16_t mnc)
330 struct gsm322_ba_list *ba, *ba_found = NULL;
332 /* search for BA list */
333 llist_for_each_entry(ba, &cs->ba_list, entry) {
348 /*plmn search timer event */
349 static void plmn_timer_timeout(void *arg)
351 struct gsm322_plmn *plmn = arg;
354 LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
356 /* indicate PLMN selection T timeout */
357 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
360 gsm322_plmn_sendmsg(plmn->ms, nmsg);
363 /* start plmn search timer */
364 static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
366 LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
368 plmn->timer.cb = plmn_timer_timeout;
369 plmn->timer.data = plmn;
370 bsc_schedule_timer(&plmn->timer, secs, 0);
373 /* stop plmn search timer */
374 static void stop_plmn_timer(struct gsm322_plmn *plmn)
376 if (bsc_timer_pending(&plmn->timer)) {
377 LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
378 bsc_del_timer(&plmn->timer);
382 /* start cell selection timer */
383 static void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
385 LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec);
386 cs->timer.cb = gsm322_cs_timeout;
388 bsc_schedule_timer(&cs->timer, sec, micro);
391 /* stop cell selection timer */
392 static void stop_cs_timer(struct gsm322_cellsel *cs)
394 if (bsc_timer_pending(&cs->timer)) {
395 LOGP(DCS, LOGL_INFO, "stopping pending CS timer.\n");
396 bsc_del_timer(&cs->timer);
404 static const char *plmn_a_state_names[] = {
414 static const char *plmn_m_state_names[] = {
423 static const char *cs_state_names[] = {
425 "C1_NORMAL_CELL_SEL",
426 "C2_STORED_CELL_SEL",
427 "C3_CAMPED_NORMALLY",
428 "C4_NORMAL_CELL_RESEL",
431 "C7_CAMPED_ANY_CELL",
433 "C9_CHOOSE_ANY_CELL",
438 /* new automatic PLMN search state */
439 static void new_a_state(struct gsm322_plmn *plmn, int state)
441 if (plmn->mode != PLMN_MODE_AUTO) {
442 LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
446 stop_plmn_timer(plmn);
448 if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
451 LOGP(DPLMN, LOGL_INFO, "new state %s -> %s\n",
452 plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
457 /* new manual PLMN search state */
458 static void new_m_state(struct gsm322_plmn *plmn, int state)
460 if (plmn->mode != PLMN_MODE_MANUAL) {
461 LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
465 if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
468 LOGP(DPLMN, LOGL_INFO, "new state %s -> %s\n",
469 plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
474 /* new Cell selection state */
475 static void new_c_state(struct gsm322_cellsel *cs, int state)
477 if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
480 LOGP(DCS, LOGL_INFO, "new state %s -> %s\n",
481 cs_state_names[cs->state], cs_state_names[state]);
483 /* stop cell selection timer, if running */
486 /* stop scanning of power measurement */
501 /* 4.4.3 create sorted list of PLMN
503 * the source of entries are
506 * - entries found in the SIM's PLMN Selector list
507 * - scanned PLMNs above -85 dB (random order)
508 * - scanned PLMNs below or equal -85 (by received level)
512 * The list only includes networks found at last scan.
514 * The list always contains HPLMN if available, even if not used by PLMN
515 * search process at some conditions.
517 * The list contains all PLMNs even if not allowed, so entries have to be
518 * removed when selecting from the list. (In case we use manual cell selection,
519 * we need to provide non-allowed networks also.)
521 static int gsm322_sort_list(struct osmocom_ms *ms)
523 struct gsm322_plmn *plmn = &ms->plmn;
524 struct gsm322_cellsel *cs = &ms->cellsel;
525 struct gsm_subscriber *subscr = &ms->subscr;
526 struct gsm_sub_plmn_list *sim_entry;
527 struct gsm_sub_plmn_na *na_entry;
528 struct llist_head temp_list;
529 struct gsm322_plmn_list *temp, *found;
530 struct llist_head *lh, *lh2;
531 int i, entries, move;
532 int8_t search_db = 0;
535 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
540 /* Create a temporary list of all networks */
541 INIT_LLIST_HEAD(&temp_list);
542 for (i = 0; i <= 1023; i++) {
543 if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA))
546 /* search if network has multiple cells */
548 llist_for_each_entry(temp, &temp_list, entry) {
549 if (temp->mcc == cs->list[i].mcc
550 && temp->mnc == cs->list[i].mnc)
554 /* update or create */
556 if (cs->list[i].rxlev_db > found->rxlev_db)
557 found->rxlev_db = cs->list[i].rxlev_db;
559 temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
562 temp->mcc = cs->list[i].mcc;
563 temp->mnc = cs->list[i].mnc;
564 temp->rxlev_db = cs->list[i].rxlev_db;
565 llist_add_tail(&temp->entry, &temp_list);
569 /* move Home PLMN, if in list */
570 if (subscr->sim_valid) {
572 llist_for_each_entry(temp, &temp_list, entry) {
573 if (temp->mcc == subscr->mcc
574 && temp->mnc == subscr->mnc) {
580 llist_del(&found->entry);
581 llist_add_tail(&found->entry, &plmn->sorted_plmn);
585 /* move entries if in SIM's PLMN Selector list */
586 llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
588 llist_for_each_entry(temp, &temp_list, entry) {
589 if (temp->mcc == sim_entry->mcc
590 && temp->mnc == sim_entry->mnc) {
596 llist_del(&found->entry);
597 llist_add_tail(&found->entry, &plmn->sorted_plmn);
601 /* move PLMN above -85 dBm in random order */
603 llist_for_each_entry(temp, &temp_list, entry) {
604 if (temp->rxlev_db > -85)
608 move = random() % entries;
610 llist_for_each_entry(temp, &temp_list, entry) {
611 if (temp->rxlev_db > -85) {
613 llist_del(&temp->entry);
614 llist_add_tail(&temp->entry,
624 /* move ohter PLMN in decreasing order */
627 llist_for_each_entry(temp, &temp_list, entry) {
629 || temp->rxlev_db > search_db) {
630 search_db = temp->rxlev_db;
636 llist_del(&found->entry);
637 llist_add_tail(&found->entry, &plmn->sorted_plmn);
640 /* mark forbidden PLMNs, if in list of forbidden networks */
642 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
643 llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
644 if (temp->mcc == na_entry->mcc
645 && temp->mnc == na_entry->mnc) {
646 temp->cause = na_entry->cause;
650 LOGP(DPLMN, LOGL_INFO, "Crating Sorted PLMN list. "
651 "(%02d: mcc=%03d mnc=%02d allowed=%s rx-lev=%d)\n",
652 i, temp->mcc, temp->mnc, (temp->cause) ? "no ":"yes",
661 * handler for automatic search
664 /* go On PLMN state */
665 static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
667 struct gsm322_plmn *plmn = &ms->plmn;
668 struct gsm_subscriber *subscr = &ms->subscr;
670 /* set last registered PLMN */
671 subscr->plmn_valid = 1;
672 subscr->plmn_mcc = plmn->mcc;
673 subscr->plmn_mnc = plmn->mnc;
678 new_a_state(plmn, GSM322_A2_ON_PLMN);
680 /* start timer, if on VPLMN of home country OR special case */
681 if ((plmn->mcc == subscr->mcc && plmn->mcc != subscr->mnc)
682 || (subscr->always_search_hplmn && (plmn->mcc != subscr->mnc
683 || plmn->mcc != subscr->mnc))) {
684 if (subscr->sim_valid && subscr->t6m_hplmn)
685 start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
687 start_plmn_timer(plmn, 30 * 360);
689 stop_plmn_timer(plmn);
694 /* indicate selected PLMN */
695 static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
698 indicate selected plmn to user
701 return gsm322_a_go_on_plmn(ms, msg);
704 /* no (more) PLMN in list */
705 static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
707 struct gsm322_plmn *plmn = &ms->plmn;
708 struct gsm322_cellsel *cs = &ms->cellsel;
709 struct gsm_subscriber *subscr = &ms->subscr;
713 /* any PLMN available */
714 found = gsm322_cs_select(ms, 0);
716 /* if no PLMN in list */
718 if (subscr->plmn_valid) {
719 LOGP(DPLMN, LOGL_INFO, "Select RPLMN.\n");
720 plmn->mcc = subscr->plmn_mcc;
721 plmn->mnc = subscr->plmn_mnc;
723 LOGP(DPLMN, LOGL_INFO, "Select HPLMN.\n");
724 plmn->mcc = subscr->mcc;
725 plmn->mnc = subscr->mnc;
728 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
730 /* we must forward this, otherwhise "Any cell selection"
731 * will not start automatically.
733 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
736 gsm322_cs_sendmsg(ms, nmsg);
741 /* select first PLMN in list */
742 plmn->mcc = cs->list[found].mcc;
743 plmn->mnc = cs->list[found].mnc;
745 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%03d mnc=%02d %s, %s)\n",
746 plmn->mcc, plmn->mnc,
747 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
749 /* indicate New PLMN */
750 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
753 gsm322_cs_sendmsg(ms, nmsg);
756 return gsm322_a_indicate_selected(ms, msg);
759 /* select first PLMN in list */
760 static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
762 struct gsm322_plmn *plmn = &ms->plmn;
764 struct gsm322_plmn_list *plmn_entry;
765 struct gsm322_plmn_list *plmn_first = NULL;
769 gsm322_sort_list(ms);
771 /* select first entry */
773 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
774 /* if RPLMN is HPLMN, we skip that */
775 if (plmn->state == GSM322_A1_TRYING_RPLMN
776 && plmn_entry->mcc == plmn->mcc
777 && plmn_entry->mnc == plmn->mnc) {
781 /* select first allowed network */
782 if (!plmn_entry->cause) {
783 plmn_first = plmn_entry;
790 /* if no PLMN in list */
792 LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
793 gsm322_a_no_more_plmn(ms, msg);
798 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%03d "
799 "mnc=%02d %s, %s)\n", plmn->plmn_curr, plmn_first->mcc,
800 plmn_first->mnc, gsm_get_mcc(plmn_first->mcc),
801 gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
803 /* set current network */
804 plmn->mcc = plmn_first->mcc;
805 plmn->mnc = plmn_first->mnc;
807 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
809 /* indicate New PLMN */
810 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
813 gsm322_cs_sendmsg(ms, nmsg);
818 /* select next PLMN in list */
819 static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
821 struct gsm322_plmn *plmn = &ms->plmn;
823 struct gsm322_plmn_list *plmn_entry;
824 struct gsm322_plmn_list *plmn_next = NULL;
827 /* select next entry from list */
829 ii = plmn->plmn_curr + 1;
830 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
831 /* skip previously selected networks */
836 /* select next allowed network */
837 if (!plmn_entry->cause) {
838 plmn_next = plmn_entry;
845 /* if no more PLMN in list */
847 LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
848 gsm322_a_no_more_plmn(ms, msg);
853 LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%03d "
854 "mnc=%02d %s, %s)\n", plmn->plmn_curr, plmn_next->mcc,
856 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
858 /* set next network */
859 plmn->mcc = plmn_next->mcc;
860 plmn->mnc = plmn_next->mnc;
862 new_a_state(plmn, GSM322_A3_TRYING_PLMN);
864 /* indicate New PLMN */
865 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
868 gsm322_cs_sendmsg(ms, nmsg);
873 /* User re-selection event */
874 static int gsm322_a_user_reselection(struct osmocom_ms *ms, struct msgb *msg)
876 struct gsm322_plmn *plmn = &ms->plmn;
877 struct gsm322_plmn_list *plmn_entry;
878 struct gsm322_plmn_list *plmn_found = NULL;
880 /* search current PLMN in list */
881 llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
882 if (plmn_entry->mcc == plmn->mcc
883 && plmn_entry->mnc == plmn->mnc)
884 plmn_found = plmn_entry;
888 /* abort if list is empty */
890 LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
894 LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
895 "and restarting PLMN search process.\n");
897 /* move entry to end of list */
898 llist_del(&plmn_found->entry);
899 llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
901 /* select first PLMN in list */
902 return gsm322_a_sel_first_plmn(ms, msg);
905 /* PLMN becomes available */
906 static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
908 struct gsm322_plmn *plmn = &ms->plmn;
909 struct gsm_subscriber *subscr = &ms->subscr;
910 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
912 if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
913 && subscr->plmn_mnc == gm->mnc) {
917 LOGP(DPLMN, LOGL_INFO, "HPLMN became available.\n");
918 return gsm322_a_go_on_plmn(ms, msg);
920 /* select first PLMN in list */
921 LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
922 "search process.\n");
923 return gsm322_a_sel_first_plmn(ms, msg);
927 /* loss of radio coverage */
928 static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
930 struct gsm322_plmn *plmn = &ms->plmn;
931 struct gsm322_cellsel *cs = &ms->cellsel;
934 /* any PLMN available */
935 found = gsm322_cs_select(ms, 0);
937 /* if PLMN in list */
939 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%03d mnc=%02d "
940 "%s, %s)\n", cs->list[found].mcc, cs->list[found].mnc,
941 gsm_get_mcc(cs->list[found].mcc),
942 gsm_get_mnc(cs->list[found].mcc, cs->list[found].mnc));
943 return gsm322_a_sel_first_plmn(ms, msg);
946 LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
948 plmn->mcc = plmn->mnc = 0;
950 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
955 /* MS is switched on OR SIM is inserted OR removed */
956 static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
958 struct gsm_subscriber *subscr = &ms->subscr;
959 struct gsm322_plmn *plmn = &ms->plmn;
962 if (!subscr->sim_valid) {
963 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
964 new_a_state(plmn, GSM322_A6_NO_SIM);
969 /* if there is a registered PLMN */
970 if (subscr->plmn_valid) {
971 /* select the registered PLMN */
972 plmn->mcc = subscr->plmn_mcc;
973 plmn->mnc = subscr->plmn_mnc;
975 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%03d mnc=%02d "
976 "%s, %s)\n", plmn->mcc, plmn->mnc,
977 gsm_get_mcc(plmn->mcc),
978 gsm_get_mnc(plmn->mcc, plmn->mnc));
980 new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
982 /* indicate New PLMN */
983 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
986 gsm322_cs_sendmsg(ms, nmsg);
991 /* select first PLMN in list */
992 return gsm322_a_sel_first_plmn(ms, msg);
995 /* MS is switched off */
996 static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
998 struct gsm322_plmn *plmn = &ms->plmn;
1000 new_a_state(plmn, GSM322_A0_NULL);
1005 /* SIM is removed */
1006 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1010 /* indicate SIM remove to cell selection process */
1011 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1014 gsm322_cs_sendmsg(ms, nmsg);
1016 return gsm322_a_switch_on(ms, msg);
1019 /* location update response: "Roaming not allowed" */
1020 static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
1022 /* store in list of forbidden LAs is done in gsm48* */
1024 return gsm322_a_sel_first_plmn(ms, msg);
1027 /* On VPLMN of home country and timeout occurs */
1028 static int gsm322_a_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
1030 struct gsm322_plmn *plmn = &ms->plmn;
1031 struct gsm322_cellsel *cs = &ms->cellsel;
1034 /* try again later, if not idle */
1035 if (cs->state != GSM322_C3_CAMPED_NORMALLY) {
1036 LOGP(DPLMN, LOGL_INFO, "Not camping normal, wait some more.\n");
1037 start_plmn_timer(plmn, 60);
1042 new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
1044 /* initiate search at cell selection */
1045 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
1048 gsm322_cs_sendmsg(ms, nmsg);
1053 /* manual mode selected */
1054 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
1056 struct gsm322_plmn *plmn = &ms->plmn;
1058 /* restart state machine */
1059 gsm322_a_switch_off(ms, msg);
1060 plmn->mode = PLMN_MODE_MANUAL;
1061 gsm322_m_switch_on(ms, msg);
1067 * handler for manual search
1070 /* go Not on PLMN state */
1071 static int gsm322_m_go_not_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1073 struct gsm322_plmn *plmn = &ms->plmn;
1075 new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
1080 /* display PLMNs and to Not on PLMN */
1081 static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
1084 gsm322_sort_list(ms);
1087 display PLMNs to user
1090 /* go Not on PLMN state */
1091 return gsm322_m_go_not_on_plmn(ms, msg);
1094 /* MS is switched on OR SIM is inserted OR removed */
1095 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1097 struct gsm_subscriber *subscr = &ms->subscr;
1098 struct gsm322_plmn *plmn = &ms->plmn;
1100 if (!subscr->sim_valid) {
1101 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1102 new_m_state(plmn, GSM322_M5_NO_SIM);
1107 /* if there is a registered PLMN */
1108 if (subscr->plmn_valid) {
1109 /* select the registered PLMN */
1110 plmn->mcc = subscr->plmn_mcc;
1111 plmn->mnc = subscr->plmn_mnc;
1113 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%03d mnc=%02d "
1114 "%s, %s)\n", plmn->mcc, plmn->mnc,
1115 gsm_get_mcc(plmn->mcc),
1116 gsm_get_mnc(plmn->mcc, plmn->mnc));
1118 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1124 return gsm322_m_display_plmns(ms, msg);
1127 /* MS is switched off */
1128 static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1130 struct gsm322_plmn *plmn = &ms->plmn;
1132 stop_plmn_timer(plmn);
1134 new_m_state(plmn, GSM322_M0_NULL);
1139 /* SIM is removed */
1140 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1142 struct gsm322_plmn *plmn = &ms->plmn;
1145 stop_plmn_timer(plmn);
1147 /* indicate SIM remove to cell selection process */
1148 nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1151 gsm322_cs_sendmsg(ms, nmsg);
1153 return gsm322_m_switch_on(ms, msg);
1156 /* go to On PLMN state */
1157 static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1159 struct gsm322_plmn *plmn = &ms->plmn;
1160 struct gsm_subscriber *subscr = &ms->subscr;
1162 /* if selected PLMN is in list of forbidden PLMNs */
1163 gsm322_del_forbidden_plmn(ms, plmn->mcc, plmn->mnc);
1165 /* set last registered PLMN */
1166 subscr->plmn_valid = 1;
1167 subscr->plmn_mcc = plmn->mcc;
1168 subscr->plmn_mnc = plmn->mnc;
1173 new_m_state(plmn, GSM322_M2_ON_PLMN);
1178 /* indicate selected PLMN */
1179 static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
1182 indicate selected plmn to user
1185 return gsm322_m_go_on_plmn(ms, msg);
1188 /* previously selected PLMN becomes available again */
1189 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1191 struct gsm322_plmn *plmn = &ms->plmn;
1193 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1198 /* the user has selected given PLMN */
1199 static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
1201 struct gsm322_plmn *plmn = &ms->plmn;
1202 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1204 /* use user selection */
1205 plmn->mcc = gm->mcc;
1206 plmn->mnc = gm->mnc;
1208 LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%03d mnc=%02d "
1209 "%s, %s)\n", plmn->mcc, plmn->mnc,
1210 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1212 new_m_state(plmn, GSM322_M4_TRYING_PLMN);
1217 /* auto mode selected */
1218 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
1220 struct gsm322_plmn *plmn = &ms->plmn;
1222 /* restart state machine */
1223 gsm322_m_switch_off(ms, msg);
1224 plmn->mode = PLMN_MODE_AUTO;
1225 gsm322_a_switch_on(ms, msg);
1230 /* if no cell is found in other states than in *_TRYING_* states */
1231 static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
1235 /* Tell cell selection process to handle "no cell found". */
1236 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1239 gsm322_cs_sendmsg(ms, nmsg);
1245 * cell scanning process
1248 /* select a suitable and allowable cell */
1249 static int gsm322_cs_select(struct osmocom_ms *ms, int any)
1251 struct gsm322_cellsel *cs = &ms->cellsel;
1252 struct gsm_subscriber *subscr = &ms->subscr;
1253 int i, found = -1, power = 0;
1254 uint8_t flags, mask;
1257 /* set out access class depending on the cell selection type */
1259 acc_class = subscr->acc_class | 0x0400; /* add emergency */
1260 LOGP(DCS, LOGL_INFO, "Using access class with Emergency "
1263 acc_class = subscr->acc_class & 0xfbff; /* remove emergency */
1264 LOGP(DCS, LOGL_INFO, "Using access class without Emergency "
1268 /* flags to match */
1269 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1270 | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
1271 if (cs->state == GSM322_C2_STORED_CELL_SEL)
1272 mask |= GSM322_CS_FLAG_BA;
1273 flags = mask; /* all masked flags are requied */
1275 /* loop through all scanned frequencies and select cell */
1276 for (i = 0; i <= 1023; i++) {
1277 cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
1279 /* channel has no informations for us */
1280 if ((cs->list[i].flags & mask) != flags) {
1284 /* check C1 criteria not fullfilled */
1285 // TODO: C1 is also dependant on power class and max power
1286 if (cs->list[i].rxlev_db < cs->list[i].min_db) {
1287 LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
1288 "not met. (rxlev=%d < min=%d)\n", i,
1289 cs->list[i].rxlev_db, cs->list[i].min_db);
1293 /* if cell is barred and we don't override */
1294 if (!subscr->acc_barr
1295 && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
1296 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
1301 /* if cell is in list of forbidden LAs */
1302 if (!subscr->acc_barr
1303 && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
1304 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1305 "list of forbidden LAs. (mcc=%03d mnc=%02d "
1306 "lai=%04x)\n", i, cs->list[i].mcc,
1307 cs->list[i].mnc, cs->list[i].lac);
1311 /* if we have no access to the cell and we don't override */
1312 if (!subscr->acc_barr
1313 && !(acc_class & (cs->list[i].class_barr ^ 0xffff))) {
1314 LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
1315 "barred for out access. (access=%04x "
1316 "barred=%04x)\n", i, acc_class,
1317 cs->list[i].class_barr);
1321 /* store temporary available and allowable flag */
1322 cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
1324 /* if we search a specific PLMN, but it does not match */
1325 if (!any && (cs->mcc != cs->list[i].mcc
1326 || cs->mnc != cs->list[i].mnc)) {
1327 LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
1328 "does not match target PLMN. (mcc=%03d "
1329 "mnc=%02d)\n", i, cs->list[i].mcc,
1334 LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%d "
1335 "mcc=%03d mnc=%02d lac=%04x %s, %s)\n", i,
1336 cs->list[i].rxlev_db, cs->list[i].mcc, cs->list[i].mnc,
1337 cs->list[i].lac, gsm_get_mcc(cs->list[i].mcc),
1338 gsm_get_mnc(cs->list[i].mcc, cs->list[i].mnc));
1340 /* find highest power cell */
1341 if (found < 0 || cs->list[i].rxlev_db > power) {
1342 power = cs->list[i].rxlev_db;
1347 gsm322_dump_sorted_plmn(ms);
1350 LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
1355 /* tune to first/next unscanned frequency and search for PLMN */
1356 static int gsm322_cs_scan(struct osmocom_ms *ms)
1358 struct gsm322_cellsel *cs = &ms->cellsel;
1359 struct gsm_subscriber *subscr = &ms->subscr;
1360 struct gsm48_sysinfo *s = &ms->sysinfo;
1361 struct gsm48_rrlayer *rr = &ms->rrlayer;
1363 uint8_t mask, flags;
1364 uint32_t weight = 0, test = cs->scan_state;
1366 if (rr->state != GSM48_RR_ST_IDLE) {
1367 LOGP(DCS, LOGL_FATAL, "This must only happen in IDLE mode, "
1372 /* special prositive case for HPLMN search */
1373 if (cs->state == GSM322_HPLMN_SEARCH && s->mcc == subscr->mcc
1374 && s->mnc == subscr->mnc) {
1377 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_FOUND);
1378 LOGP(DCS, LOGL_INFO, "HPLMN cell available.\n");
1381 gsm322_plmn_sendmsg(ms, nmsg);
1386 /* search for strongest unscanned cell */
1387 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1388 | GSM322_CS_FLAG_SIGNAL;
1389 if (cs->state == GSM322_C2_STORED_CELL_SEL)
1390 mask |= GSM322_CS_FLAG_BA;
1391 flags = mask; /* all masked flags are requied */
1392 for (i = 0; i <= 1023; i++) {
1393 /* skip if band has enough frequencies scanned (3.2.1) */
1394 for (j = 0; gsm_sup_smax[j].max; j++) {
1395 if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
1396 if (gsm_sup_smax[j].start >= i
1397 && gsm_sup_smax[j].end <= i)
1400 if (gsm_sup_smax[j].end <= i
1401 || gsm_sup_smax[j].start >= i)
1405 if (gsm_sup_smax[j].max) {
1406 if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
1409 /* search for unscanned frequency */
1410 if ((cs->list[i].flags & mask) == flags) {
1411 /* weight depends on the power level
1412 * if it is the same, it depends on arfcn
1414 test = cs->list[i].rxlev_db + 128;
1415 test = (test << 16) | i;
1416 if (test >= cs->scan_state)
1422 cs->scan_state = weight;
1425 gsm322_dump_cs_list(ms);
1427 /* special negative case for HPLMN search */
1428 if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
1431 nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_NOT_FOUND);
1432 LOGP(DCS, LOGL_INFO, "No HPLMN cell available.\n");
1435 gsm322_plmn_sendmsg(ms, nmsg);
1437 /* re-tune back to current VPLMN */
1438 l1ctl_tx_ccch_req(ms, cs->arfcn);
1439 cs->ccch_active = 0;
1444 /* if all frequencies have been searched */
1449 LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
1451 /* just see, if we search for any cell */
1452 if (cs->state == GSM322_C6_ANY_CELL_SEL
1453 || cs->state == GSM322_C8_ANY_CELL_RESEL
1454 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1457 found = gsm322_cs_select(ms, any);
1461 LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1464 l1ctl_tx_ccch_req(ms, cs->arfcn);
1465 cs->ccch_active = 0;
1467 /* Clear system information. */
1468 gsm48_sysinfo_init(ms);
1470 /* tell PLMN process about available PLMNs */
1471 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1474 gsm322_plmn_sendmsg(ms, nmsg);
1476 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1478 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1479 LOGP(DCS, LOGL_INFO, "No cell available.\n");
1483 gsm322_cs_sendmsg(ms, nmsg);
1488 /* NOTE: We might already have system information from previous
1489 * scan. But we need recent informations, so we scan again!
1492 /* Tune to frequency for a while, to receive broadcasts. */
1493 cs->arfcn = weight & 1023;
1494 LOGP(DCS, LOGL_INFO, "Scanning frequency %d.\n", cs->arfcn);
1495 l1ctl_tx_ccch_req(ms, cs->arfcn);
1496 cs->ccch_active = 0;
1498 /* Clear system information. */
1499 gsm48_sysinfo_init(ms);
1501 /* increase scan counter for each maximum scan range */
1502 if (gsm_sup_smax[j].max)
1503 gsm_sup_smax[j].temp++;
1507 /* check if cell is now suitable and allowable */
1508 static int gsm322_cs_store(struct osmocom_ms *ms)
1510 struct gsm322_cellsel *cs = &ms->cellsel;
1511 struct gsm48_sysinfo *s = &ms->sysinfo;
1512 int i = cs->scan_state & 1023;
1514 if (cs->state != GSM322_C2_STORED_CELL_SEL
1515 && cs->state != GSM322_C1_NORMAL_CELL_SEL
1516 && cs->state != GSM322_C6_ANY_CELL_SEL
1517 && cs->state != GSM322_C4_NORMAL_CELL_RESEL
1518 && cs->state != GSM322_C8_ANY_CELL_RESEL
1519 && cs->state != GSM322_C5_CHOOSE_CELL
1520 && cs->state != GSM322_C9_CHOOSE_ANY_CELL) {
1521 LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
1522 "(re-)selection, please fix!\n");
1527 cs->list[i].flags |= GSM322_CS_FLAG_SYSINFO;
1529 cs->list[i].flags |= GSM322_CS_FLAG_BARRED;
1531 cs->list[i].flags &= ~GSM322_CS_FLAG_BARRED;
1532 cs->list[i].min_db = s->rxlev_acc_min_db;
1533 cs->list[i].class_barr = s->class_barr;
1534 cs->list[i].max_pwr = s->ms_txpwr_max_ccch;
1536 /* store selected network */
1538 cs->list[i].mcc = s->mcc;
1539 cs->list[i].mnc = s->mnc;
1540 cs->list[i].lac = s->lac;
1542 if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
1543 cs->list[i].flags |= GSM322_CS_FLAG_FORBIDD;
1545 cs->list[i].flags &= ~GSM322_CS_FLAG_FORBIDD;
1548 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev=%d "
1549 "mcc=%03d mnc=%02d lac=%04x)\n", i, cs->list[i].rxlev_db,
1550 cs->list[i].mcc, cs->list[i].mnc, cs->list[i].lac);
1552 /* tune to next cell */
1553 return gsm322_cs_scan(ms);
1556 /* process system information when returing to idle mode */
1557 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
1559 struct gsm322_cellsel *cs = &ms->cellsel;
1560 struct gsm48_sysinfo *s = &ms->sysinfo;
1561 struct gsm322_ba_list *ba = NULL;
1565 /* collect system information received during dedicated mode */
1567 && (!s->nb_ext_ind_si5
1568 || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
1569 || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
1570 && s->nb_ext_ind_si5bis))) {
1571 /* find or create ba list */
1572 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1574 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1579 llist_add_tail(&ba->entry, &cs->ba_list);
1581 /* update (add) ba list */
1582 memcpy(freq, ba->freq, sizeof(freq));
1583 for (i = 0; i <= 1023; i++) {
1584 if ((s->freq[i].mask & FREQ_TYPE_REP))
1585 freq[i >> 3] |= (1 << (i & 7));
1587 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
1588 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%d mnc=%d "
1589 "%s, %s).\n", ba->mcc, ba->mnc,
1590 gsm_get_mcc(ba->mcc),
1591 gsm_get_mnc(ba->mcc, ba->mnc));
1592 memcpy(ba->freq, freq, sizeof(freq));
1599 /* process system information during camping on a cell */
1600 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
1602 struct gsm322_cellsel *cs = &ms->cellsel;
1603 struct gsm48_sysinfo *s = &ms->sysinfo;
1604 struct gsm_subscriber *subscr = &ms->subscr;
1605 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1608 if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
1609 /* check if cell becomes barred */
1610 if (!subscr->acc_barr && s->cell_barr) {
1611 LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
1613 /* mark cell as unscanned */
1614 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1615 /* trigger reselection event */
1616 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
1619 gsm322_cs_sendmsg(ms, nmsg);
1623 /* check if cell access becomes barred */
1624 if (!((subscr->acc_class & 0xfbff)
1625 & (cs->list[cs->arfcn].class_barr ^ 0xffff))) {
1626 LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
1634 /* process system information during channel scanning */
1635 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
1637 struct gsm322_cellsel *cs = &ms->cellsel;
1638 struct gsm48_rrlayer *rr = &ms->rrlayer;
1639 struct gsm48_sysinfo *s = &ms->sysinfo;
1640 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1641 struct gsm322_ba_list *ba;
1645 if (rr->state != GSM48_RR_ST_IDLE) {
1646 LOGP(DCS, LOGL_FATAL, "This must only happen in IDLE mode, "
1651 /* no sysinfo if we are not done with power scan */
1652 if (cs->powerscan) {
1653 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
1657 /* Store BA if we have full system info about cells and neigbor cells.
1658 * Depending on the extended bit in the channel description,
1659 * we require more or less system informations about neighbor cells
1663 && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
1664 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
1665 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
1666 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
1669 && (!s->nb_ext_ind_si2
1670 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
1671 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
1672 && s->nb_ext_ind_si2bis))) {
1673 /* find or create ba list */
1674 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1676 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1681 llist_add_tail(&ba->entry, &cs->ba_list);
1683 /* update ba list */
1684 memset(freq, 0, sizeof(freq));
1685 freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
1686 for (i = 0; i <= 1023; i++) {
1687 if ((s->freq[i].mask &
1688 (FREQ_TYPE_SERV | FREQ_TYPE_NCELL)))
1689 freq[i >> 3] |= (1 << (i & 7));
1691 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
1692 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%d mnc=%d "
1693 "%s, %s).\n", ba->mcc, ba->mnc,
1694 gsm_get_mcc(ba->mcc),
1695 gsm_get_mnc(ba->mcc, ba->mnc));
1696 memcpy(ba->freq, freq, sizeof(freq));
1701 printf("%d %d %d\n", s->si1, s->si2, s->si3);
1702 /* all relevant system informations received */
1703 if (s->si1 && s->si2 && s->si3
1704 && (!s->nb_ext_ind_si2
1705 || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
1706 || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
1707 && s->nb_ext_ind_si2bis))) {
1711 gsm48_sysinfo_dump(ms);
1713 /* store sysinfo and continue scan */
1714 return gsm322_cs_store(ms);
1717 /* wait for more sysinfo or timeout */
1721 static void gsm322_cs_timeout(void *arg)
1723 struct gsm322_cellsel *cs = arg;
1724 struct osmocom_ms *ms = cs->ms;
1725 int i = cs->scan_state & 1023;
1727 LOGP(DCS, LOGL_INFO, "Cell selection timer has fired.\n");
1728 LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev=%d)\n",
1729 i, cs->list[i].rxlev_db);
1731 /* remove system information */
1732 cs->list[i].flags &= ~GSM322_CS_FLAG_SYSINFO;
1734 /* tune to next cell */
1741 * power scan process
1744 /* search for block of unscanned freqeuncies and start scanning */
1745 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
1747 struct gsm322_cellsel *cs = &ms->cellsel;
1748 struct gsm48_rrlayer *rr = &ms->rrlayer;
1750 uint8_t mask, flags;
1752 if (rr->state != GSM48_RR_ST_IDLE) {
1753 LOGP(DCS, LOGL_FATAL, "This must only happen in IDLE mode, "
1760 /* search for first frequency to scan */
1761 mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
1762 flags = GSM322_CS_FLAG_SUPPORT;
1763 if (cs->state == GSM322_C2_STORED_CELL_SEL) {
1764 LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA list.\n");
1765 mask |= GSM322_CS_FLAG_BA;
1766 flags |= GSM322_CS_FLAG_BA;
1768 LOGP(DCS, LOGL_FATAL, "Scanning power for all frequencies.\n");
1769 for (i = 0; i <= 1023; i++) {
1770 if ((cs->list[i].flags & mask) == flags) {
1776 /* if there is no more frequency, we can tune to that cell */
1780 /* stop power level scanning */
1783 /* check if not signal is found */
1784 for (i = 0; i <= 1023; i++) {
1785 if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
1791 LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
1792 /* on normal cell selection, start over */
1793 if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
1794 for (i = 0; i <= 1023; i++) {
1795 /* clear flag that this was scanned */
1796 cs->list[i].flags &=
1797 ~(GSM322_CS_FLAG_POWER
1798 | GSM322_CS_FLAG_SIGNAL
1799 | GSM322_CS_FLAG_SYSINFO);
1803 /* on other cell selection, indicate "no cell found" */
1804 /* NOTE: PLMN search process handles it.
1805 * If not handled there, CS process gets indicated.
1806 * If we would continue to process CS, then we might get
1807 * our list of scanned cells disturbed.
1809 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1812 gsm322_plmn_sendmsg(ms, nmsg);
1816 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
1817 cs->scan_state = 0xffffffff; /* higher than high */
1818 /* clear counter of scanned frequencies of each range */
1819 for (i = 0; gsm_sup_smax[i].max; i++)
1820 gsm_sup_smax[i].temp = 0;
1821 return gsm322_cs_scan(ms);
1824 /* search last frequency to scan (en block) */
1826 for (i = s + 1; i <= 1023; i++) {
1827 if ((cs->list[i].flags & mask) == flags)
1833 LOGP(DCS, LOGL_INFO, "Scanning frequecies. (%d..%d)\n", s, e);
1835 /* start scan on radio interface */
1837 return l1ctl_tx_pm_req_range(ms, s, e);
1840 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
1841 void *handler_data, void *signal_data)
1843 struct osmocom_ms *ms;
1844 struct gsm322_cellsel *cs;
1845 struct osmobb_meas_res *mr;
1849 if (subsys != SS_L1CTL)
1853 case S_L1CTL_PM_RES:
1859 i = mr->band_arfcn & 1023;
1860 rxlev_db = mr->rx_lev - 110;
1861 cs->list[i].rxlev_db = rxlev_db;
1862 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
1863 if (rxlev_db >= ms->support.min_rxlev_db) {
1864 cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
1865 LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
1866 "rxlev %d)\n", i, cs->list[i].rxlev_db);
1869 case S_L1CTL_PM_DONE:
1870 LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
1875 gsm322_cs_powerscan(ms);
1877 case S_L1CTL_CCCH_RESP:
1880 if (!cs->ccch_active) {
1881 LOGP(DCS, LOGL_INFO, "Channel activated.\n");
1882 cs->ccch_active = 1;
1883 /* set timer for reading BCCH */
1884 if (cs->state == GSM322_C2_STORED_CELL_SEL
1885 || cs->state == GSM322_C1_NORMAL_CELL_SEL
1886 || cs->state == GSM322_C6_ANY_CELL_SEL
1887 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
1888 || cs->state == GSM322_C8_ANY_CELL_RESEL
1889 || cs->state == GSM322_C5_CHOOSE_CELL
1890 || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1891 start_cs_timer(cs, 4, 0);
1892 // TODO: timer depends on BCCH config
1900 * handler for cell selection process
1903 /* start HPLMN search */
1904 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
1906 struct gsm322_cellsel *cs = &ms->cellsel;
1909 new_c_state(cs, GSM322_HPLMN_SEARCH);
1911 /* mark all frequencies except our own BA to be scanned */
1912 for (i = 0; i <= 1023; i++) {
1913 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
1914 && !(cs->list[i].flags & GSM322_CS_FLAG_BA))
1915 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
1916 | GSM322_CS_FLAG_SIGNAL
1917 | GSM322_CS_FLAG_SYSINFO);
1920 /* start power scan */
1921 return gsm322_cs_powerscan(ms);
1924 /* start stored cell selection */
1925 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
1927 struct gsm322_cellsel *cs = &ms->cellsel;
1930 new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
1932 /* flag all frequencies that are in current band allocation */
1933 for (i = 0; i <= 1023; i++) {
1934 if ((ba->freq[i >> 3] & (1 << (i & 7))))
1935 cs->list[i].flags |= GSM322_CS_FLAG_BA;
1937 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
1940 /* start power scan */
1941 return gsm322_cs_powerscan(ms);
1944 /* start noraml cell selection */
1945 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
1947 struct gsm322_cellsel *cs = &ms->cellsel;
1950 /* except for stored cell selection state, we weed to rescan ?? */
1951 if (cs->state != GSM322_C2_STORED_CELL_SEL) {
1952 for (i = 0; i <= 1023; i++)
1953 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
1954 | GSM322_CS_FLAG_SIGNAL
1955 | GSM322_CS_FLAG_SYSINFO);
1958 new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
1960 /* start power scan */
1961 return gsm322_cs_powerscan(ms);
1964 /* start any cell selection */
1965 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
1967 struct gsm322_cellsel *cs = &ms->cellsel;
1970 /* in case we already tried any cell selection, power scan again */
1971 if (cs->state == GSM322_C6_ANY_CELL_SEL) {
1972 for (i = 0; i <= 1023; i++)
1973 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
1974 | GSM322_CS_FLAG_SIGNAL
1975 | GSM322_CS_FLAG_SYSINFO);
1977 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
1980 cs->mcc = cs->mnc = 0;
1982 /* start power scan */
1983 return gsm322_cs_powerscan(ms);
1986 /* start noraml cell re-selection */
1987 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
1989 struct gsm322_cellsel *cs = &ms->cellsel;
1991 new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
1993 /* NOTE: We keep our scan info we have so far.
1994 * This may cause a skip in power scan. */
1996 /* start power scan */
1997 return gsm322_cs_powerscan(ms);
2000 /* start any cell re-selection */
2001 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2003 struct gsm322_cellsel *cs = &ms->cellsel;
2005 new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2007 /* NOTE: We keep our scan info we have so far.
2008 * This may cause a skip in power scan. */
2010 /* start power scan */
2011 return gsm322_cs_powerscan(ms);
2014 /* create temporary ba range with given frequency ranges */
2015 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2016 uint32_t *range, uint8_t ranges)
2018 static struct gsm322_ba_list ba;
2019 uint16_t lower, higher;
2021 memset(&ba, 0, sizeof(ba));
2024 lower = *range & 1023;
2025 higher = (*range >> 16) & 1023;
2027 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2030 ba.freq[lower >> 3] |= 1 << (lower & 7);
2031 if (lower == higher)
2033 lower = (lower + 1) & 1023;
2040 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2041 static int gsm322_cs_choose(struct osmocom_ms *ms)
2043 struct gsm322_cellsel *cs = &ms->cellsel;
2044 struct gsm322_ba_list *ba = NULL;
2048 if (message->ranges)
2049 ba = gsm322_cs_ba_range(ms, message->range, message->ranges);
2051 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2053 /* get and update BA of last received sysinfo 5* */
2054 ba = gsm322_cs_sysinfo_sacch(ms);
2062 LOGP(DCS, LOGL_INFO, "No BA list.\n");
2064 /* tell CS to start over */
2065 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2068 gsm322_cs_sendmsg(ms, nmsg);
2071 /* flag all frequencies that are in current band allocation */
2072 for (i = 0; i <= 1023; i++) {
2073 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2074 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2075 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2077 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2079 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2080 | GSM322_CS_FLAG_SIGNAL
2081 | GSM322_CS_FLAG_SYSINFO);
2086 /* use hacked frequency */
2087 cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_POWER;
2088 cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_SIGNAL;
2089 cs->list[ms->test_arfcn].rxlev_db = -40;
2092 /* start power scan */
2093 return gsm322_cs_powerscan(ms);
2096 /* start 'Choose cell' after returning to idle mode */
2097 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2099 struct gsm322_cellsel *cs = &ms->cellsel;
2101 new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2103 return gsm322_cs_choose(ms);
2106 /* start 'Choose any cell' after returning to idle mode */
2107 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2109 struct gsm322_cellsel *cs = &ms->cellsel;
2111 new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2113 return gsm322_cs_choose(ms);
2116 /* a new PLMN is selected by PLMN search process */
2117 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2119 struct gsm322_cellsel *cs = &ms->cellsel;
2120 struct gsm322_plmn *plmn = &ms->plmn;
2121 struct gsm322_ba_list *ba;
2123 cs->mcc = plmn->mcc;
2124 cs->mnc = plmn->mnc;
2126 /* search for BA list */
2127 ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2130 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2131 return gsm322_c_stored_cell_sel(ms, ba);
2133 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2134 return gsm322_c_normal_cell_sel(ms, msg);
2138 /* a suitable cell was found, so we camp normally */
2139 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2141 struct gsm322_cellsel *cs = &ms->cellsel;
2144 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NEW_LAI);
2147 gsm48_mmevent_msg(ms, nmsg);
2149 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2154 /* a not suitable cell was found, so we camp on any cell */
2155 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2157 struct gsm322_cellsel *cs = &ms->cellsel;
2159 new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2164 /* go connected mode */
2165 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2167 struct gsm322_cellsel *cs = &ms->cellsel;
2169 /* stop camping process */
2171 /* be sure to go to current camping frequency on return */
2172 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2173 l1ctl_tx_ccch_req(ms, cs->arfcn);
2174 cs->ccch_active = 0;
2179 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2181 struct gsm322_cellsel *cs = &ms->cellsel;
2183 /* stop camping process */
2185 /* be sure to go to current camping frequency on return */
2186 LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2187 l1ctl_tx_ccch_req(ms, cs->arfcn);
2188 cs->ccch_active = 0;
2194 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2196 struct gsm_subscriber *subscr = &ms->subscr;
2198 /* if no SIM is is MS */
2199 if (!subscr->sim_valid) {
2200 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2201 return gsm322_c_any_cell_sel(ms, msg);
2202 LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2205 /* stay in NULL state until PLMN is selected */
2214 /* state machine for automatic PLMN selection events */
2215 static struct plmnastatelist {
2218 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2219 } plmnastatelist[] = {
2220 {SBIT(GSM322_A0_NULL),
2221 GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2223 GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
2224 {SBIT(GSM322_A6_NO_SIM),
2225 GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
2227 GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
2229 GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
2230 {SBIT(GSM322_A1_TRYING_RPLMN),
2231 GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
2232 {SBIT(GSM322_A1_TRYING_RPLMN),
2233 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
2234 {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
2235 GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
2236 {SBIT(GSM322_A2_ON_PLMN),
2237 GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
2238 {SBIT(GSM322_A2_ON_PLMN),
2239 GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search},
2240 {SBIT(GSM322_A2_ON_PLMN),
2241 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
2242 {SBIT(GSM322_A2_ON_PLMN),
2243 GSM322_EVENT_USER_RESEL, gsm322_a_user_reselection},
2244 {SBIT(GSM322_A3_TRYING_PLMN),
2245 GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
2246 {SBIT(GSM322_A3_TRYING_PLMN),
2247 GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
2248 {SBIT(GSM322_A5_HPLMN_SEARCH),
2249 GSM322_EVENT_HPLMN_FOUND, gsm322_a_sel_first_plmn},
2250 {SBIT(GSM322_A5_HPLMN_SEARCH),
2251 GSM322_EVENT_HPLMN_NOT_FOUND, gsm322_a_go_on_plmn},
2252 {SBIT(GSM322_A4_WAIT_FOR_PLMN),
2253 GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
2255 GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
2257 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
2260 #define PLMNASLLEN \
2261 (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
2263 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
2265 struct gsm322_plmn *plmn = &ms->plmn;
2266 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2267 int msg_type = gm->msg_type;
2271 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
2272 "selection in state %s\n", ms->name, get_event_name(msg_type),
2273 plmn_a_state_names[plmn->state]);
2274 /* find function for current state and message */
2275 for (i = 0; i < PLMNASLLEN; i++)
2276 if ((msg_type == plmnastatelist[i].type)
2277 && ((1 << plmn->state) & plmnastatelist[i].states))
2279 if (i == PLMNASLLEN) {
2280 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
2284 rc = plmnastatelist[i].rout(ms, msg);
2289 /* state machine for manual PLMN selection events */
2290 static struct plmnmstatelist {
2293 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2294 } plmnmstatelist[] = {
2295 {SBIT(GSM322_M0_NULL),
2296 GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
2298 GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
2299 {SBIT(GSM322_M5_NO_SIM),
2300 GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
2302 GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
2303 {SBIT(GSM322_M1_TRYING_RPLMN),
2304 GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
2305 {SBIT(GSM322_M1_TRYING_RPLMN),
2306 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
2307 {SBIT(GSM322_M1_TRYING_RPLMN),
2308 GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
2309 {SBIT(GSM322_M2_ON_PLMN),
2310 GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
2311 {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
2312 SBIT(GSM322_M4_TRYING_PLMN),
2313 GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
2314 {SBIT(GSM322_M2_ON_PLMN),
2315 GSM322_EVENT_USER_RESEL, gsm322_m_display_plmns},
2316 {SBIT(GSM322_M3_NOT_ON_PLMN),
2317 GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
2318 {SBIT(GSM322_M3_NOT_ON_PLMN),
2319 GSM322_EVENT_CHOSE_PLMN, gsm322_m_choose_plmn},
2320 {SBIT(GSM322_M4_TRYING_PLMN),
2321 GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
2322 {SBIT(GSM322_M4_TRYING_PLMN),
2323 GSM322_EVENT_REG_FAILED, gsm322_m_go_not_on_plmn},
2324 {SBIT(GSM322_M4_TRYING_PLMN),
2325 GSM322_EVENT_NO_CELL_FOUND, gsm322_m_go_not_on_plmn},
2327 GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
2329 GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
2332 #define PLMNMSLLEN \
2333 (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
2335 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
2337 struct gsm322_plmn *plmn = &ms->plmn;
2338 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2339 int msg_type = gm->msg_type;
2343 LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
2344 "in state %s\n", ms->name, get_event_name(msg_type),
2345 plmn_m_state_names[plmn->state]);
2346 /* find function for current state and message */
2347 for (i = 0; i < PLMNMSLLEN; i++)
2348 if ((msg_type == plmnmstatelist[i].type)
2349 && ((1 << plmn->state) & plmnmstatelist[i].states))
2351 if (i == PLMNMSLLEN) {
2352 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
2356 rc = plmnmstatelist[i].rout(ms, msg);
2361 /* dequeue GSM 03.22 PLMN events */
2362 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
2364 struct gsm322_plmn *plmn = &ms->plmn;
2368 while ((msg = msgb_dequeue(&plmn->event_queue))) {
2369 /* send event to PLMN select process */
2370 if (plmn->mode == PLMN_MODE_AUTO)
2371 gsm322_a_event(ms, msg);
2373 gsm322_m_event(ms, msg);
2375 work = 1; /* work done */
2381 /* state machine for channel selection events */
2382 static struct cellselstatelist {
2385 int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2386 } cellselstatelist[] = {
2388 GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
2390 GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
2392 GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
2393 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
2394 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
2395 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
2396 {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
2397 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
2398 GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
2399 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
2400 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
2401 SBIT(GSM322_C0_NULL),
2402 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
2403 {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
2404 SBIT(GSM322_C4_NORMAL_CELL_RESEL),
2405 GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
2406 {SBIT(GSM322_C3_CAMPED_NORMALLY),
2407 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
2408 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2409 GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
2410 {SBIT(GSM322_C3_CAMPED_NORMALLY),
2411 GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
2412 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2413 GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
2414 {SBIT(GSM322_C3_CAMPED_NORMALLY),
2415 GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
2416 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2417 GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
2418 {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2419 GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
2420 {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
2421 SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
2422 SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
2423 SBIT(GSM322_C6_ANY_CELL_SEL),
2424 GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
2425 {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
2426 GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
2427 {SBIT(GSM322_C3_CAMPED_NORMALLY),
2428 GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search}
2431 #define CELLSELSLLEN \
2432 (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
2434 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
2436 struct gsm322_cellsel *cs = &ms->cellsel;
2437 struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2438 int msg_type = gm->msg_type;
2442 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
2443 "%s\n", ms->name, get_event_name(msg_type),
2444 cs_state_names[cs->state]);
2445 /* find function for current state and message */
2446 for (i = 0; i < CELLSELSLLEN; i++)
2447 if ((msg_type == cellselstatelist[i].type)
2448 && ((1 << cs->state) & cellselstatelist[i].states))
2450 if (i == CELLSELSLLEN) {
2451 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
2455 rc = cellselstatelist[i].rout(ms, msg);
2460 /* dequeue GSM 03.22 cell selection events */
2461 int gsm322_cs_dequeue(struct osmocom_ms *ms)
2463 struct gsm322_cellsel *cs = &ms->cellsel;
2467 while ((msg = msgb_dequeue(&cs->event_queue))) {
2468 /* send event to cell selection process */
2469 gsm322_c_event(ms, msg);
2471 work = 1; /* work done */
2481 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
2483 struct gsm322_plmn *plmn = &ms->plmn;
2484 struct gsm322_plmn_list *temp;
2486 printf("MCC |MNC |allowed|rx-lev\n");
2487 printf("-------+-------+-------+-------\n");
2488 llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
2489 printf("%03d |%02d |%s |%d\n", temp->mcc, temp->mnc,
2490 (temp->cause) ? "no ":"yes", temp->rxlev_db);
2496 int gsm322_dump_cs_list(struct osmocom_ms *ms)
2498 struct gsm322_cellsel *cs = &ms->cellsel;
2501 printf("rx-lev |MCC |MNC |forb.LA|barred,0123456789abcdef|"
2503 "-------+-------+-------+-------+-----------------------+"
2504 "-------+-------\n");
2505 for (i = 0; i <= 1023; i++) {
2506 if (!(cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2508 printf("%4d |", cs->list[i].rxlev_db);
2509 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
2510 printf("%03d |%02d |", cs->list[i].mcc,
2512 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
2516 if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
2520 for (j = 0; j < 16; j++) {
2521 if ((cs->list[i].class_barr & (1 << j)))
2526 printf("|%4d |%4d\n", cs->list[i].min_db,
2527 cs->list[i].max_pwr);
2529 printf("n/a |n/a | | "
2536 int gsm322_dump_sim_plmn(struct osmocom_ms *ms)
2538 struct gsm_subscriber *subscr = &ms->subscr;
2539 struct gsm_sub_plmn_list *temp;
2541 printf("MCC |MNC\n");
2542 printf("-------+-------\n");
2543 llist_for_each_entry(temp, &subscr->plmn_list, entry)
2544 printf("%03d |%02d\n", temp->mcc, temp->mnc);
2549 int gsm322_dump_forbidden_plmn(struct osmocom_ms *ms)
2551 struct gsm_subscriber *subscr = &ms->subscr;
2552 struct gsm_sub_plmn_na *temp;
2554 printf("MCC |MNC |cause\n");
2555 printf("-------+-------+-------\n");
2556 llist_for_each_entry(temp, &subscr->plmn_na, entry)
2557 printf("%03d |%02d |#%d\n", temp->mcc, temp->mnc,
2563 int gsm322_dump_forbidden_la(struct osmocom_ms *ms)
2565 struct gsm322_plmn *plmn = &ms->plmn;
2566 struct gsm322_la_list *temp;
2568 printf("MCC |MNC |LAC |cause\n");
2569 printf("-------+-------+-------+-------\n");
2570 llist_for_each_entry(temp, &plmn->forbidden_la, entry)
2571 printf("%03d |%02d |0x%04x |#%d\n", temp->mcc, temp->mnc,
2572 temp->lac, temp->cause);
2581 int gsm322_init(struct osmocom_ms *ms)
2583 struct gsm322_plmn *plmn = &ms->plmn;
2584 struct gsm322_cellsel *cs = &ms->cellsel;
2586 char suffix[] = ".ba";
2587 char filename[sizeof(ms->name) + strlen(suffix) + 1];
2589 struct gsm322_ba_list *ba;
2592 LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
2593 LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
2595 memset(plmn, 0, sizeof(*plmn));
2596 memset(cs, 0, sizeof(*cs));
2600 /* set initial state */
2603 plmn->mode = PLMN_MODE_AUTO;
2606 INIT_LLIST_HEAD(&plmn->event_queue);
2607 INIT_LLIST_HEAD(&cs->event_queue);
2608 INIT_LLIST_HEAD(&plmn->sorted_plmn);
2609 INIT_LLIST_HEAD(&plmn->forbidden_la);
2610 INIT_LLIST_HEAD(&cs->ba_list);
2612 /* set supported frequencies in cell selection list */
2613 for (i = 0; i <= 1023; i++)
2614 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
2615 cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
2618 strcpy(filename, ms->name);
2619 strcat(filename, suffix);
2620 fp = osmocom_fopen(filename, "r");
2624 while(!osmocom_feof(fp)) {
2625 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2628 rc = osmocom_fread(buf, 4, 1, fp);
2633 ba->mcc = (buf[0] << 8) | buf[1];
2634 ba->mnc = (buf[2] << 8) | buf[3];
2635 rc = osmocom_fread(ba->freq, sizeof(ba->freq), 1, fp);
2640 llist_add_tail(&ba->entry, &cs->ba_list);
2641 LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%d "
2642 "mnc=%d %s, %s)\n", ba->mcc, ba->mnc,
2643 gsm_get_mcc(ba->mcc),
2644 gsm_get_mnc(ba->mcc, ba->mnc));
2648 LOGP(DCS, LOGL_NOTICE, "No stored BA list\n");
2650 register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
2655 int gsm322_exit(struct osmocom_ms *ms)
2657 struct gsm322_plmn *plmn = &ms->plmn;
2658 struct gsm322_cellsel *cs = &ms->cellsel;
2659 struct llist_head *lh, *lh2;
2662 char suffix[] = ".ba";
2663 char filename[sizeof(ms->name) + strlen(suffix) + 1];
2664 struct gsm322_ba_list *ba;
2667 LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
2668 LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
2670 unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
2672 /* stop cell selection process (if any) */
2673 new_c_state(cs, GSM322_C0_NULL);
2677 stop_plmn_timer(plmn);
2680 strcpy(filename, ms->name);
2681 strcat(filename, suffix);
2682 fp = osmocom_fopen(filename, "w");
2686 llist_for_each_entry(ba, &cs->ba_list, entry) {
2687 buf[0] = ba->mcc >> 8;
2688 buf[1] = ba->mcc & 0xff;
2689 buf[2] = ba->mnc >> 8;
2690 buf[3] = ba->mnc & 0xff;
2691 rc = osmocom_fwrite(buf, 4, 1, fp);
2692 rc = osmocom_fwrite(ba->freq, sizeof(ba->freq), 1, fp);
2693 LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%d "
2694 "mnc=%d %s, %s)\n", ba->mcc, ba->mnc,
2695 gsm_get_mcc(ba->mcc),
2696 gsm_get_mnc(ba->mcc, ba->mnc));
2700 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
2703 while ((msg = msgb_dequeue(&plmn->event_queue)))
2705 while ((msg = msgb_dequeue(&cs->event_queue)))
2707 llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
2711 llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
2715 llist_for_each_safe(lh, lh2, &cs->ba_list) {