807c6624c901f39871d30c684d626ebf25e451fe
[osmocom-bb.git] / src / host / layer23 / src / mobile / gsm322.c
1 /*
2  * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
3  *
4  * All Rights Reserved
5  *
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.
10  *
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.
15  *
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.
19  *
20  */
21
22 #include <stdint.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27
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>
33
34 #include <osmocom/bb/common/logging.h>
35 #include <osmocom/bb/common/l1ctl.h>
36 #include <osmocom/bb/common/l23_app.h>
37 #include <osmocom/bb/common/osmocom_data.h>
38 #include <osmocom/bb/common/networks.h>
39 #include <osmocom/bb/mobile/vty.h>
40
41 extern void *l23_ctx;
42
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, int plmn_allowed);
46 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg);
47
48 #define SKIP_MAX_PER_BAND
49
50 #warning HACKING!!!
51 int hack;
52
53 /*
54  * notes
55  */
56
57 /* Cell selection process
58  *
59  * The process depends on states and events (finites state machine).
60  *
61  * During states of cell selection or cell re-selection, the search for a cell
62  * is performed in two steps:
63  *
64  *  1. Measurement of received level of all relevant frequencies (rx-lev)
65  *
66  *  2. Receive system information messages of all relevant frequencies
67  *
68  * During this process, the results are stored in a list of all frequencies.
69  * This list is checked whenever a cell is selected. It depends on the results
70  * if the cell is 'suitable' and 'allowable' to 'camp' on.
71  *
72  * This list is also used to generate a list of available networks.
73  *
74  * The states are:
75  *
76  * - cs->list[0..1023].xxx for each cell, where
77  *  - flags and rxlev are used to store outcome of cell scanning process
78  *  - sysinfo pointing to sysinfo memory, allocated temporarily
79  * - cs->selected and cs->sel_* states of the current / last selected cell.
80  *
81  *
82  * There is a special state: GSM322_PLMN_SEARCH
83  * It is used to search for all cells, to find the HPLMN. This is triggered
84  * by a timer. Also it is used before selecting PLMN from list.
85  *
86  */
87
88 /* PLMN selection process
89  *
90  * The PLMN (Public Land Mobile Network = Operator's Network) has two different
91  * search processes:
92  *
93  *  1. Automatic search
94  *
95  *  2. Manual search
96  *
97  * The process depends on states and events (finites state machine).
98  *
99  */
100
101 /* File format of BA list:
102  *
103  * uint16_t     mcc
104  * uint16_t     mcc
105  * uint8_t      freq[128];
106  *      where frequency 0 is bit 0 of first byte
107  * 
108  * If not end-of-file, the next BA list is stored.
109  */
110
111 /* List of lists:
112  *
113  * * subscr->plmn_list
114  *
115  * The "PLMN Selector list" stores prefered networks to select during PLMN
116  * search process. This list is also stored in the SIM.
117  *
118  * * subscr->plmn_na
119  *
120  * The "forbidden PLMNs" list stores all networks that rejected us. The stored
121  * network will not be used when searching PLMN automatically. This list is
122  * also stored din the SIM.
123  *
124  * * plmn->forbidden_la
125  *
126  * The "forbidden LAs for roaming" list stores all location areas where roaming
127  * was not allowed.
128  *
129  * * cs->list[1024]
130  *
131  * This list stores measurements and cell informations during cell selection
132  * process. It can be used to speed up repeated cell selection.
133  *
134  * * cs->ba_list
135  *
136  * This list stores a map of frequencies used for a PLMN. If this lists exists
137  * for a PLMN, it helps to speedup cell scan process.
138  *
139  * * plmn->sorted_plmn
140  *
141  * This list is generated whenever a PLMN search is started and a list of PLMNs
142  * is required. It consists of home PLMN, PLMN Selector list, and PLMNs found
143  * during scan process.
144  */
145
146 /*
147  * event messages
148  */
149
150 static const struct value_string gsm322_event_names[] = {
151         { GSM322_EVENT_SWITCH_ON,       "EVENT_SWITCH_ON" },
152         { GSM322_EVENT_SWITCH_OFF,      "EVENT_SWITCH_OFF" },
153         { GSM322_EVENT_SIM_INSERT,      "EVENT_SIM_INSERT" },
154         { GSM322_EVENT_SIM_REMOVE,      "EVENT_SIM_REMOVE" },
155         { GSM322_EVENT_REG_FAILED,      "EVENT_REG_FAILED" },
156         { GSM322_EVENT_ROAMING_NA,      "EVENT_ROAMING_NA" },
157         { GSM322_EVENT_INVALID_SIM,     "EVENT_INVALID_SIM" },
158         { GSM322_EVENT_REG_SUCCESS,     "EVENT_REG_SUCCESS" },
159         { GSM322_EVENT_NEW_PLMN,        "EVENT_NEW_PLMN" },
160         { GSM322_EVENT_ON_PLMN,         "EVENT_ON_PLMN" },
161         { GSM322_EVENT_PLMN_SEARCH_START,"EVENT_PLMN_SEARCH_START" },
162         { GSM322_EVENT_PLMN_SEARCH_END, "EVENT_PLMN_SEARCH_END" },
163         { GSM322_EVENT_USER_RESEL,      "EVENT_USER_RESEL" },
164         { GSM322_EVENT_PLMN_AVAIL,      "EVENT_PLMN_AVAIL" },
165         { GSM322_EVENT_CHOOSE_PLMN,     "EVENT_CHOOSE_PLMN" },
166         { GSM322_EVENT_SEL_MANUAL,      "EVENT_SEL_MANUAL" },
167         { GSM322_EVENT_SEL_AUTO,        "EVENT_SEL_AUTO" },
168         { GSM322_EVENT_CELL_FOUND,      "EVENT_CELL_FOUND" },
169         { GSM322_EVENT_NO_CELL_FOUND,   "EVENT_NO_CELL_FOUND" },
170         { GSM322_EVENT_LEAVE_IDLE,      "EVENT_LEAVE_IDLE" },
171         { GSM322_EVENT_RET_IDLE,        "EVENT_RET_IDLE" },
172         { GSM322_EVENT_CELL_RESEL,      "EVENT_CELL_RESEL" },
173         { GSM322_EVENT_SYSINFO,         "EVENT_SYSINFO" },
174         { GSM322_EVENT_HPLMN_SEARCH,    "EVENT_HPLMN_SEARCH" },
175         { 0,                            NULL }
176 };
177
178 const char *get_event_name(int value)
179 {
180         return get_value_string(gsm322_event_names, value);
181 }
182
183
184 /* allocate a 03.22 event message */
185 struct msgb *gsm322_msgb_alloc(int msg_type)
186 {
187         struct msgb *msg;
188         struct gsm322_msg *gm;
189
190         msg = msgb_alloc_headroom(sizeof(*gm), 0, "GSM 03.22 event");
191         if (!msg)
192                 return NULL;
193
194         gm = (struct gsm322_msg *)msgb_put(msg, sizeof(*gm));
195         gm->msg_type = msg_type;
196
197         return msg;
198 }
199
200 /* queue PLMN selection message */
201 int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
202 {
203         struct gsm322_plmn *plmn = &ms->plmn;
204
205         msgb_enqueue(&plmn->event_queue, msg);
206
207         return 0;
208 }
209
210 /* queue cell selection message */
211 int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
212 {
213         struct gsm322_cellsel *cs = &ms->cellsel;
214
215         msgb_enqueue(&cs->event_queue, msg);
216
217         return 0;
218 }
219
220 /*
221  * support
222  */
223
224 char *gsm_print_rxlev(uint8_t rxlev)
225 {
226         static char string[5];
227         if (rxlev == 0)
228                 return "<=-110";
229         if (rxlev >= 63)
230                 return ">=-48";
231         sprintf(string, "-%d", 110 - rxlev);
232         return string;
233 }
234
235 static int gsm322_sync_to_cell(struct gsm322_cellsel *cs)
236 {
237         struct osmocom_ms *ms = cs->ms;
238         struct gsm48_sysinfo *s = cs->si;
239         struct rx_meas_stat *meas = &ms->meas;
240
241         cs->ccch_state = GSM322_CCCH_ST_INIT;
242         if (s && s->si3) {
243                 if (s->ccch_conf == 1) {
244                         LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%d rxlev=%s "
245                                 "(Sysinfo, ccch mode COMB)\n", cs->arfcn,
246                                 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
247                         cs->ccch_mode = CCCH_MODE_COMBINED;
248                 } else {
249                         LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%d rxlev=%s "
250                                 "(Sysinfo, ccch mode NON-COMB)\n", cs->arfcn,
251                                 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
252                         cs->ccch_mode = CCCH_MODE_NON_COMBINED;
253                 }
254         } else {
255                 LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%d rxlev=%s (No sysinfo "
256                         "yet, ccch mode NONE)\n", cs->arfcn,
257                         gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
258                 cs->ccch_mode = CCCH_MODE_NONE;
259         }
260
261         meas->frames = meas->snr = meas->berr = meas->rxlev = 0;
262
263         l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
264         return l1ctl_tx_fbsb_req(ms, cs->arfcn,
265                                  L1CTL_FBSB_F_FB01SB, 100, 0,
266                                  cs->ccch_mode);
267 }
268
269 static void gsm322_unselect_cell(struct gsm322_cellsel *cs)
270 {
271         cs->selected = 0;
272         if (cs->si)
273                 cs->si->si5 = 0; /* unset SI5* */
274         cs->si = NULL;
275         memset(&cs->sel_si, 0, sizeof(cs->sel_si));
276         cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
277 }
278
279 /* print to DCS logging */
280 static void print_dcs(void *priv, const char *fmt, ...)
281 {
282         char buffer[1000];
283         va_list args;
284
285         va_start(args, fmt);
286         vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
287         buffer[sizeof(buffer) - 1] = '\0';
288         va_end(args);
289
290         if (buffer[0])
291 //              LOGP(DCS, LOGL_INFO, "%s", buffer);
292                 printf("%s", buffer);
293 }
294
295 /* del forbidden LA */
296 int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
297         uint16_t mnc, uint16_t lac)
298 {
299         struct gsm322_plmn *plmn = &ms->plmn;
300         struct gsm322_la_list *la;
301
302         llist_for_each_entry(la, &plmn->forbidden_la, entry) {
303                 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
304                         LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
305                                 "LAs (mcc=%s, mnc=%s, lac=%04x)\n",
306                                 gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac);
307                         llist_del(&la->entry);
308                         talloc_free(la);
309                         return 0;
310                 }
311         }
312
313         return -EINVAL;
314 }
315
316 /* add forbidden LA */
317 int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
318         uint16_t mnc, uint16_t lac, uint8_t cause)
319 {
320         struct gsm322_plmn *plmn = &ms->plmn;
321         struct gsm322_la_list *la;
322
323         LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
324                 "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc),
325                 gsm_print_mnc(mnc), lac);
326         la = talloc_zero(l23_ctx, struct gsm322_la_list);
327         if (!la)
328                 return -ENOMEM;
329         la->mcc = mcc;
330         la->mnc = mnc;
331         la->lac = lac;
332         la->cause = cause;
333         llist_add_tail(&la->entry, &plmn->forbidden_la);
334
335         return 0;
336 }
337
338 /* search forbidden LA */
339 int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
340         uint16_t lac)
341 {
342         struct gsm322_plmn *plmn = &ms->plmn;
343         struct gsm322_la_list *la;
344
345         llist_for_each_entry(la, &plmn->forbidden_la, entry) {
346                 if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
347                         return 1;
348         }
349
350         return 0;
351 }
352
353 /* search for PLMN in all BA lists */
354 static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
355         uint16_t mcc, uint16_t mnc)
356 {
357         struct gsm322_ba_list *ba, *ba_found = NULL;
358
359         /* search for BA list */
360         llist_for_each_entry(ba, &cs->ba_list, entry) {
361                 if (ba->mcc == mcc
362                  && ba->mnc == mnc) {
363                         ba_found = ba;
364                         break;
365                 }
366         }
367
368         return ba_found;
369 }
370
371 /* search available PLMN */
372 int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
373 {
374         int i;
375
376         for (i = 0; i <= 1023; i++) {
377                 if (cs->list[i].sysinfo
378                  && cs->list[i].sysinfo->mcc == mcc
379                  && cs->list[i].sysinfo->mnc == mnc)
380                         return 1;
381         }
382
383         return 0;
384 }
385
386 /* search available HPLMN */
387 int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi)
388 {
389         int i;
390
391         for (i = 0; i <= 1023; i++) {
392                 if (cs->list[i].sysinfo
393                  && gsm_match_mnc(cs->list[i].sysinfo->mcc,
394                         cs->list[i].sysinfo->mnc, imsi))
395                         return 1;
396         }
397
398         return 0;
399 }
400
401 /* del forbidden LA */
402 /*
403  * timer
404  */
405
406 /*plmn search timer event */
407 static void plmn_timer_timeout(void *arg)
408 {
409         struct gsm322_plmn *plmn = arg;
410         struct msgb *nmsg;
411
412         LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
413
414         /* indicate PLMN selection T timeout */
415         nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
416         if (!nmsg)
417                 return;
418         gsm322_plmn_sendmsg(plmn->ms, nmsg);
419 }
420
421 /* start plmn search timer */
422 static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
423 {
424         LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
425                 secs / 60);
426         plmn->timer.cb = plmn_timer_timeout;
427         plmn->timer.data = plmn;
428         bsc_schedule_timer(&plmn->timer, secs, 0);
429 }
430
431 /* stop plmn search timer */
432 static void stop_plmn_timer(struct gsm322_plmn *plmn)
433 {
434         if (bsc_timer_pending(&plmn->timer)) {
435                 LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
436                 bsc_del_timer(&plmn->timer);
437         }
438 }
439
440 /* start cell selection timer */
441 void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
442 {
443         LOGP(DCS, LOGL_DEBUG, "Starting CS timer with %d seconds.\n", sec);
444         cs->timer.cb = gsm322_cs_timeout;
445         cs->timer.data = cs;
446         bsc_schedule_timer(&cs->timer, sec, micro);
447 }
448
449 /* stop cell selection timer */
450 static void stop_cs_timer(struct gsm322_cellsel *cs)
451 {
452         if (bsc_timer_pending(&cs->timer)) {
453                 LOGP(DCS, LOGL_DEBUG, "stopping pending CS timer.\n");
454                 bsc_del_timer(&cs->timer);
455         }
456 }
457
458 /*
459  * state change
460  */
461
462 const char *plmn_a_state_names[] = {
463         "A0 null",
464         "A1 trying RPLMN",
465         "A2 on PLMN",
466         "A3 trying PLMN",
467         "A4 wait for PLMN to appear",
468         "A5 HPLMN search",
469         "A6 no SIM inserted"
470 };
471
472 const char *plmn_m_state_names[] = {
473         "M0 null",
474         "M1 trying RPLMN",
475         "M2 on PLMN",
476         "M3 not on PLMN",
477         "M4 trying PLMN",
478         "M5 no SIM inserted"
479 };
480
481 const char *cs_state_names[] = {
482         "C0 null",
483         "C1 normal cell selection",
484         "C2 stored cell selection",
485         "C3 camped normally",
486         "C4 normal cell re-selection",
487         "C5 choose cell",
488         "C6 any cell selection",
489         "C7 camped on any cell",
490         "C8 any cell re-selection",
491         "C9 choose any cell",
492         "PLMN search",
493         "HPLMN search"
494 };
495
496
497 /* new automatic PLMN search state */
498 static void new_a_state(struct gsm322_plmn *plmn, int state)
499 {
500         if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) {
501                 LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
502                 return;
503         }
504
505         stop_plmn_timer(plmn);
506
507         if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
508                 return;
509
510         LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
511                 plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
512
513         plmn->state = state;
514 }
515
516 /* new manual PLMN search state */
517 static void new_m_state(struct gsm322_plmn *plmn, int state)
518 {
519         if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
520                 LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
521                 return;
522         }
523
524         if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
525                 return;
526
527         LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
528                 plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
529
530         plmn->state = state;
531 }
532
533 /* new Cell selection state */
534 static void new_c_state(struct gsm322_cellsel *cs, int state)
535 {
536         if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
537                 return;
538
539         LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n",
540                 cs_state_names[cs->state], cs_state_names[state]);
541
542         /* stop cell selection timer, if running */
543         stop_cs_timer(cs);
544
545         /* stop scanning of power measurement */
546         if (cs->powerscan) {
547                 LOGP(DCS, LOGL_INFO, "changing state while power scanning\n");
548                 l1ctl_tx_reset_req(cs->ms, L1CTL_RES_T_FULL);
549                 cs->powerscan = 0;
550         }
551
552         cs->state = state;
553 }
554
555 /*
556  * list of PLMNs
557  */
558
559 /* 4.4.3 create sorted list of PLMN
560  *
561  * the source of entries are
562  * 
563  * - HPLMN
564  * - entries found in the SIM's PLMN Selector list
565  * - scanned PLMNs above -85 dB (random order)
566  * - scanned PLMNs below or equal -85 (by received level)
567  *
568  * NOTE:
569  *
570  * The list only includes networks found at last scan.
571  *
572  * The list always contains HPLMN if available, even if not used by PLMN
573  * search process at some conditions.
574  *
575  * The list contains all PLMNs even if not allowed, so entries have to be
576  * removed when selecting from the list. (In case we use manual cell selection,
577  * we need to provide non-allowed networks also.)
578  */
579 static int gsm322_sort_list(struct osmocom_ms *ms)
580 {
581         struct gsm322_plmn *plmn = &ms->plmn;
582         struct gsm322_cellsel *cs = &ms->cellsel;
583         struct gsm_subscriber *subscr = &ms->subscr;
584         struct gsm_sub_plmn_list *sim_entry;
585         struct gsm_sub_plmn_na *na_entry;
586         struct llist_head temp_list;
587         struct gsm322_plmn_list *temp, *found;
588         struct llist_head *lh, *lh2;
589         int i, entries, move;
590         int8_t search = 0;
591
592         /* flush list */
593         llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
594                 llist_del(lh);
595                 talloc_free(lh);
596         }
597
598         /* Create a temporary list of all networks */
599         INIT_LLIST_HEAD(&temp_list);
600         for (i = 0; i <= 1023; i++) {
601                 if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)
602                  || !cs->list[i].sysinfo)
603                         continue;
604
605                 /* search if network has multiple cells */
606                 found = NULL;
607                 llist_for_each_entry(temp, &temp_list, entry) {
608                         if (temp->mcc == cs->list[i].sysinfo->mcc
609                          && temp->mnc == cs->list[i].sysinfo->mnc) {
610                                 found = temp;
611                                 break;
612                         }
613                 }
614                 /* update or create */
615                 if (found) {
616                         if (cs->list[i].rxlev > found->rxlev)
617                                 found->rxlev = cs->list[i].rxlev;
618                 } else {
619                         temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
620                         if (!temp)
621                                 return -ENOMEM;
622                         temp->mcc = cs->list[i].sysinfo->mcc;
623                         temp->mnc = cs->list[i].sysinfo->mnc;
624                         temp->rxlev = cs->list[i].rxlev;
625                         llist_add_tail(&temp->entry, &temp_list);
626                 }
627         }
628
629         /* move Home PLMN, if in list, else add it */
630         if (subscr->sim_valid) {
631                 found = NULL;
632                 llist_for_each_entry(temp, &temp_list, entry) {
633                         if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) {
634                                 found = temp;
635                                 break;
636                         }
637                 }
638                 if (found) {
639                         /* move */
640                         llist_del(&found->entry);
641                         llist_add_tail(&found->entry, &plmn->sorted_plmn);
642                 }
643         }
644
645         /* move entries if in SIM's PLMN Selector list */
646         llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
647                 found = NULL;
648                 llist_for_each_entry(temp, &temp_list, entry) {
649                         if (temp->mcc == sim_entry->mcc
650                          && temp->mnc == sim_entry->mnc) {
651                                 found = temp;
652                                 break;
653                         }
654                 }
655                 if (found) {
656                         llist_del(&found->entry);
657                         llist_add_tail(&found->entry, &plmn->sorted_plmn);
658                 }
659         }
660
661         /* move PLMN above -85 dBm in random order */
662         entries = 0;
663         llist_for_each_entry(temp, &temp_list, entry) {
664                 if (rxlev2dbm(temp->rxlev) > -85)
665                         entries++;
666         }
667         while(entries) {
668                 move = random() % entries;
669                 i = 0;
670                 llist_for_each_entry(temp, &temp_list, entry) {
671                         if (rxlev2dbm(temp->rxlev) > -85) {
672                                 if (i == move) {
673                                         llist_del(&temp->entry);
674                                         llist_add_tail(&temp->entry,
675                                                 &plmn->sorted_plmn);
676                                         break;
677                                 }
678                                 i++;
679                         }
680                 }
681                 entries--;
682         }
683
684         /* move ohter PLMN in decreasing order */
685         while(1) {
686                 found = NULL;
687                 llist_for_each_entry(temp, &temp_list, entry) {
688                         if (!found
689                          || temp->rxlev > search) {
690                                 search = temp->rxlev;
691                                 found = temp;
692                         }
693                 }
694                 if (!found)
695                         break;
696                 llist_del(&found->entry);
697                 llist_add_tail(&found->entry, &plmn->sorted_plmn);
698         }
699
700         /* mark forbidden PLMNs, if in list of forbidden networks */
701         i = 0;
702         llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
703                 llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
704                         if (temp->mcc == na_entry->mcc
705                          && temp->mnc == na_entry->mnc) {
706                                 temp->cause = na_entry->cause;
707                                 break;
708                         }
709                 }
710                 LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. "
711                         "(%02d: mcc %s mnc %s allowed %s rx-lev %s)\n",
712                         i, gsm_print_mcc(temp->mcc),
713                         gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes",
714                         gsm_print_rxlev(temp->rxlev));
715                 i++;
716         }
717
718         gsm322_dump_sorted_plmn(ms);
719
720         return 0;
721 }
722
723 /*
724  * handler for automatic search
725  */
726
727 /* go On PLMN state */
728 static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
729 {
730         struct gsm322_plmn *plmn = &ms->plmn;
731         struct gsm_subscriber *subscr = &ms->subscr;
732
733         new_a_state(plmn, GSM322_A2_ON_PLMN);
734
735         /* start timer, if on VPLMN of home country OR special case */
736         if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi)
737          && (subscr->always_search_hplmn
738           || gsm_match_mcc(plmn->mcc, subscr->imsi))
739          && subscr->sim_valid && subscr->t6m_hplmn)
740                 start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
741         else
742                 stop_plmn_timer(plmn);
743
744         return 0;
745 }
746
747 /* indicate selected PLMN */
748 static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
749 {
750         struct gsm322_plmn *plmn = &ms->plmn;
751
752         vty_notify(ms, NULL);
753         vty_notify(ms, "Selected Network: %s, %s\n",
754                 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
755
756         return gsm322_a_go_on_plmn(ms, msg);
757 }
758
759 /* no (more) PLMN in list */
760 static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
761 {
762         struct gsm322_plmn *plmn = &ms->plmn;
763         struct gsm322_cellsel *cs = &ms->cellsel;
764         struct msgb *nmsg;
765         int found;
766
767         /* any allowable PLMN available? */
768         plmn->mcc = plmn->mnc = 0;
769         found = gsm322_cs_select(ms, 0, 1);
770
771         /* if no PLMN in list */
772         if (found < 0) {
773                 LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable.\n");
774
775                 new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
776
777 #if 0
778                 /* we must forward this, otherwhise "Any cell selection" 
779                  * will not start automatically.
780                  */
781                 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
782                 if (!nmsg)
783                         return -ENOMEM;
784                 gsm322_cs_sendmsg(ms, nmsg);
785 #endif
786                 LOGP(DPLMN, LOGL_INFO, "Trigger full PLMN search.\n");
787
788                 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
789                 if (!nmsg)
790                         return -ENOMEM;
791                 gsm322_cs_sendmsg(ms, nmsg);
792
793                 return 0;
794         }
795
796         /* select first PLMN in list */
797         plmn->mcc = cs->list[found].sysinfo->mcc;
798         plmn->mnc = cs->list[found].sysinfo->mnc;
799
800         LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s  %s, %s)\n",
801                 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
802                 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
803
804         /* indicate New PLMN */
805         nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
806         if (!nmsg)
807                 return -ENOMEM;
808         gsm322_cs_sendmsg(ms, nmsg);
809
810         /* go On PLMN */
811         return gsm322_a_indicate_selected(ms, msg);
812 }
813
814 /* select first PLMN in list */
815 static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
816 {
817         struct gsm322_plmn *plmn = &ms->plmn;
818         struct gsm_subscriber *subscr = &ms->subscr;
819         struct msgb *nmsg;
820         struct gsm322_plmn_list *plmn_entry;
821         struct gsm322_plmn_list *plmn_first = NULL;
822         int i;
823
824         /* generate list */
825         gsm322_sort_list(ms);
826
827         /* select first entry */
828         i = 0;
829         llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
830                 /* if last selected PLMN was HPLMN, we skip that */
831                 if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc,
832                                         subscr->imsi)
833                  && plmn_entry->mcc == plmn->mcc
834                  && plmn_entry->mnc == plmn->mnc) {
835                         LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was "
836                                 "previously selected.\n");
837                         i++;
838                         continue;
839                 }
840                 /* select first allowed network */
841                 if (!plmn_entry->cause) {
842                         plmn_first = plmn_entry;
843                         break;
844                 }
845                 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
846                         "because it is not allowed (cause %d).\n", i,
847                         gsm_print_mcc(plmn_entry->mcc),
848                         gsm_print_mnc(plmn_entry->mnc),
849                         plmn_entry->cause);
850                 i++;
851         }
852         plmn->plmn_curr = i;
853
854         /* if no PLMN in list */
855         if (!plmn_first) {
856                 LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
857                 gsm322_a_no_more_plmn(ms, msg);
858
859                 return 0;
860         }
861
862         LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
863                 "mnc=%s  %s, %s)\n", plmn->plmn_curr,
864                 gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc),
865                 gsm_get_mcc(plmn_first->mcc),
866                 gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
867
868         /* set current network */
869         plmn->mcc = plmn_first->mcc;
870         plmn->mnc = plmn_first->mnc;
871
872         new_a_state(plmn, GSM322_A3_TRYING_PLMN);
873
874         /* indicate New PLMN */
875         nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
876         if (!nmsg)
877                 return -ENOMEM;
878         gsm322_cs_sendmsg(ms, nmsg);
879
880         return 0;
881 }
882
883 /* select next PLMN in list */
884 static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
885 {
886         struct gsm322_plmn *plmn = &ms->plmn;
887         struct msgb *nmsg;
888         struct gsm322_plmn_list *plmn_entry;
889         struct gsm322_plmn_list *plmn_next = NULL;
890         int i, ii;
891
892         /* select next entry from list */
893         i = 0;
894         ii = plmn->plmn_curr + 1;
895         llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
896                 /* skip previously selected networks */
897                 if (i < ii) {
898                         i++;
899                         continue;
900                 }
901                 /* select next allowed network */
902                 if (!plmn_entry->cause) {
903                         plmn_next = plmn_entry;
904                         break;
905                 }
906                 LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
907                         "because it is not allowed (cause %d).\n", i,
908                         gsm_print_mcc(plmn_entry->mcc),
909                         gsm_print_mnc(plmn_entry->mnc),
910                         plmn_entry->cause);
911                 i++;
912         }
913         plmn->plmn_curr = i;
914
915         /* if no more PLMN in list */
916         if (!plmn_next) {
917                 LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
918                 gsm322_a_no_more_plmn(ms, msg);
919                 return 0;
920         }
921
922         /* set next network */
923         plmn->mcc = plmn_next->mcc;
924         plmn->mnc = plmn_next->mnc;
925
926         LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
927                 "mnc=%s  %s, %s)\n", plmn->plmn_curr,
928                 gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
929                 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
930
931         new_a_state(plmn, GSM322_A3_TRYING_PLMN);
932
933         /* indicate New PLMN */
934         nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
935         if (!nmsg)
936                 return -ENOMEM;
937         gsm322_cs_sendmsg(ms, nmsg);
938
939         return 0;
940 }
941
942 /* User re-selection event */
943 static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg)
944 {
945         struct gsm322_plmn *plmn = &ms->plmn;
946         struct gsm_subscriber *subscr = &ms->subscr;
947         struct gsm48_rrlayer *rr = &ms->rrlayer;
948         struct gsm322_plmn_list *plmn_entry;
949         struct gsm322_plmn_list *plmn_found = NULL;
950
951         if (!subscr->sim_valid) {
952                 return 0;
953         }
954
955         /* try again later, if not idle */
956         if (rr->state != GSM48_RR_ST_IDLE) {
957                 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
958
959                 return 0;
960         }
961
962         /* search current PLMN in list */
963         llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
964                 if (plmn_entry->mcc == plmn->mcc
965                  && plmn_entry->mnc == plmn->mnc)
966                         plmn_found = plmn_entry;
967                         break;
968         }
969
970         /* abort if list is empty */
971         if (!plmn_found) {
972                 LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
973                 return 0;
974         }
975
976         LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
977                 "and restarting PLMN search process.\n");
978
979         /* move entry to end of list */
980         llist_del(&plmn_found->entry);
981         llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
982
983         /* select first PLMN in list */
984         return gsm322_a_sel_first_plmn(ms, msg);
985 }
986
987 /* PLMN becomes available */
988 static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
989 {
990         struct gsm322_plmn *plmn = &ms->plmn;
991         struct gsm_subscriber *subscr = &ms->subscr;
992         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
993
994         if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
995          && subscr->plmn_mnc == gm->mnc) {
996                 /* go On PLMN */
997                 plmn->mcc = gm->mcc;
998                 plmn->mnc = gm->mnc;
999                 LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n");
1000                 return gsm322_a_go_on_plmn(ms, msg);
1001         } else {
1002                 /* select first PLMN in list */
1003                 LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
1004                         "search process.\n");
1005                 return gsm322_a_sel_first_plmn(ms, msg);
1006         }
1007 }
1008                 
1009 /* loss of radio coverage */
1010 static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
1011 {
1012         struct gsm322_plmn *plmn = &ms->plmn;
1013         struct gsm322_cellsel *cs = &ms->cellsel;
1014         int found;
1015         struct msgb *nmsg;
1016
1017         /* any PLMN available */
1018         found = gsm322_cs_select(ms, 0, 1);
1019
1020         /* if PLMN in list */
1021         if (found >= 0) {
1022                 LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s  "
1023                         "%s, %s)\n", gsm_print_mcc(
1024                         cs->list[found].sysinfo->mcc),
1025                         gsm_print_mnc(cs->list[found].sysinfo->mnc),
1026                         gsm_get_mcc(cs->list[found].sysinfo->mcc),
1027                         gsm_get_mnc(cs->list[found].sysinfo->mcc,
1028                                 cs->list[found].sysinfo->mnc));
1029                 return gsm322_a_sel_first_plmn(ms, msg);
1030         }
1031
1032         LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
1033
1034         plmn->mcc = plmn->mnc = 0;
1035
1036         new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
1037
1038         /* Tell cell selection process to handle "no cell found". */
1039         nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1040         if (!nmsg)
1041                 return -ENOMEM;
1042         gsm322_cs_sendmsg(ms, nmsg);
1043
1044         return 0;
1045 }
1046
1047 /* MS is switched on OR SIM is inserted OR removed */
1048 static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1049 {
1050         struct gsm_subscriber *subscr = &ms->subscr;
1051         struct gsm322_plmn *plmn = &ms->plmn;
1052         struct msgb *nmsg;
1053
1054         if (!subscr->sim_valid) {
1055                 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1056                 LOGP(DPLMN, LOGL_INFO, "SIM is removed\n");
1057                 new_a_state(plmn, GSM322_A6_NO_SIM);
1058
1059                 return 0;
1060         }
1061
1062         /* if there is a registered PLMN */
1063         if (subscr->plmn_valid) {
1064                 /* select the registered PLMN */
1065                 plmn->mcc = subscr->plmn_mcc;
1066                 plmn->mnc = subscr->plmn_mnc;
1067
1068                 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1069                         "(mcc=%s mnc=%s  %s, %s)\n", gsm_print_mcc(plmn->mcc),
1070                         gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1071                         gsm_get_mnc(plmn->mcc, plmn->mnc));
1072                 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s  "
1073                         "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1074                         gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1075                         gsm_get_mnc(plmn->mcc, plmn->mnc));
1076
1077                 new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
1078
1079                 /* indicate New PLMN */
1080                 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1081                 if (!nmsg)
1082                         return -ENOMEM;
1083                 gsm322_cs_sendmsg(ms, nmsg);
1084
1085                 return 0;
1086         }
1087
1088         /* initiate search at cell selection */
1089         LOGP(DSUM, LOGL_INFO, "Search for network\n");
1090         LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1091
1092         nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1093         if (!nmsg)
1094                 return -ENOMEM;
1095         gsm322_cs_sendmsg(ms, nmsg);
1096
1097         return 0;
1098 }
1099
1100 /* MS is switched off */
1101 static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1102 {
1103         struct gsm322_plmn *plmn = &ms->plmn;
1104
1105         new_a_state(plmn, GSM322_A0_NULL);
1106
1107         return 0;
1108 }
1109
1110 static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1111 {
1112         LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1113         return 0;
1114 }
1115
1116 /* SIM is removed */
1117 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1118 {
1119         struct msgb *nmsg;
1120
1121         /* indicate SIM remove to cell selection process */
1122         nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1123         if (!nmsg)
1124                 return -ENOMEM;
1125         gsm322_cs_sendmsg(ms, nmsg);
1126
1127         return gsm322_a_switch_on(ms, msg);
1128 }
1129
1130 /* location update response: "Roaming not allowed" */
1131 static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
1132 {
1133         /* store in list of forbidden LAs is done in gsm48* */
1134
1135         return gsm322_a_sel_first_plmn(ms, msg);
1136 }
1137
1138 /* On VPLMN of home country and timeout occurs */
1139 static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg)
1140 {
1141         struct gsm48_rrlayer *rr = &ms->rrlayer;
1142         struct gsm322_plmn *plmn = &ms->plmn;
1143         struct gsm322_cellsel *cs = &ms->cellsel;
1144         struct msgb *nmsg;
1145
1146         /* try again later, if not idle and not camping */
1147         if (rr->state != GSM48_RR_ST_IDLE
1148          || cs->state != GSM322_C3_CAMPED_NORMALLY) {
1149                 LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n");
1150                 start_plmn_timer(plmn, 60);
1151
1152                 return 0;
1153         }
1154
1155         new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
1156
1157         /* initiate search at cell selection */
1158         nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1159         if (!nmsg)
1160                 return -ENOMEM;
1161         gsm322_cs_sendmsg(ms, nmsg);
1162
1163         return 0;
1164 }
1165
1166 /* manual mode selected */
1167 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
1168 {
1169         struct msgb *nmsg;
1170
1171         /* restart state machine */
1172         gsm322_a_switch_off(ms, msg);
1173         ms->settings.plmn_mode = PLMN_MODE_MANUAL;
1174         gsm322_m_switch_on(ms, msg);
1175
1176         nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1177         if (!nmsg)
1178                 return -ENOMEM;
1179         gsm48_mmevent_msg(ms, nmsg);
1180
1181         return 0;
1182 }
1183
1184 /*
1185  * handler for manual search
1186  */
1187
1188 /* display PLMNs and to Not on PLMN */
1189 static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
1190 {
1191         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1192         int msg_type = gm->msg_type;
1193         struct gsm322_plmn *plmn = &ms->plmn;
1194         struct gsm_sub_plmn_list *temp;
1195
1196         /* generate list */
1197         gsm322_sort_list(ms);
1198
1199         vty_notify(ms, NULL);
1200         switch (msg_type) {
1201         case GSM322_EVENT_REG_FAILED:
1202                 vty_notify(ms, "Failed to register to network %s, %s "
1203                         "(%s, %s)\n",
1204                         gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1205                         gsm_get_mcc(plmn->mcc),
1206                         gsm_get_mnc(plmn->mcc, plmn->mnc));
1207                 break;
1208         case GSM322_EVENT_NO_CELL_FOUND:
1209                 vty_notify(ms, "No cell found for network %s, %s "
1210                         "(%s, %s)\n",
1211                         gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1212                         gsm_get_mcc(plmn->mcc),
1213                         gsm_get_mnc(plmn->mcc, plmn->mnc));
1214                 break;
1215         case GSM322_EVENT_ROAMING_NA:
1216                 vty_notify(ms, "Roaming not allowed to network %s, %s "
1217                         "(%s, %s)\n",
1218                         gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1219                         gsm_get_mcc(plmn->mcc),
1220                         gsm_get_mnc(plmn->mcc, plmn->mnc));
1221                 break;
1222         }
1223
1224         if (llist_empty(&plmn->sorted_plmn))
1225                 vty_notify(ms, "Search network!\n");
1226         else {
1227                 vty_notify(ms, "Search or select from network:\n");
1228                 llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
1229                         vty_notify(ms, " Network %s, %s (%s, %s)\n",
1230                                 gsm_print_mcc(temp->mcc),
1231                                 gsm_print_mnc(temp->mnc),
1232                                 gsm_get_mcc(temp->mcc),
1233                                 gsm_get_mnc(temp->mcc, temp->mnc));
1234         }
1235         
1236         /* go Not on PLMN state */
1237         new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
1238
1239         return 0;
1240 }
1241
1242 /* user starts reselection */
1243 static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg)
1244 {
1245         struct gsm_subscriber *subscr = &ms->subscr;
1246         struct gsm48_rrlayer *rr = &ms->rrlayer;
1247         struct msgb *nmsg;
1248
1249         if (!subscr->sim_valid) {
1250                 return 0;
1251         }
1252
1253         /* try again later, if not idle */
1254         if (rr->state != GSM48_RR_ST_IDLE) {
1255                 LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
1256
1257                 return 0;
1258         }
1259
1260         /* initiate search at cell selection */
1261         vty_notify(ms, NULL);
1262         vty_notify(ms, "Searching Network, please wait...\n");
1263         LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n");
1264
1265         nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1266         if (!nmsg)
1267                 return -ENOMEM;
1268         gsm322_cs_sendmsg(ms, nmsg);
1269
1270         return 0;
1271 }
1272
1273 /* MS is switched on OR SIM is inserted OR removed */
1274 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
1275 {
1276         struct gsm_subscriber *subscr = &ms->subscr;
1277         struct gsm322_plmn *plmn = &ms->plmn;
1278         struct msgb *nmsg;
1279
1280         if (!subscr->sim_valid) {
1281                 LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
1282                 LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
1283                 new_m_state(plmn, GSM322_M5_NO_SIM);
1284
1285                 return 0;
1286         }
1287
1288         /* if there is a registered PLMN */
1289         if (subscr->plmn_valid) {
1290                 struct msgb *nmsg;
1291
1292                 /* select the registered PLMN */
1293                 plmn->mcc = subscr->plmn_mcc;
1294                 plmn->mnc = subscr->plmn_mnc;
1295
1296                 LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
1297                         "(mcc=%s mnc=%s  %s, %s)\n", gsm_print_mcc(plmn->mcc),
1298                         gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1299                         gsm_get_mnc(plmn->mcc, plmn->mnc));
1300                 LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s  "
1301                         "%s, %s)\n", gsm_print_mcc(plmn->mcc),
1302                         gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
1303                         gsm_get_mnc(plmn->mcc, plmn->mnc));
1304
1305                 new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1306
1307                 /* indicate New PLMN */
1308                 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1309                 if (!nmsg)
1310                         return -ENOMEM;
1311                 gsm322_cs_sendmsg(ms, nmsg);
1312
1313                 return 0;
1314         }
1315
1316         /* initiate search at cell selection */
1317         LOGP(DSUM, LOGL_INFO, "Search for network\n");
1318         LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
1319         vty_notify(ms, NULL);
1320         vty_notify(ms, "Searching Network, please wait...\n");
1321
1322         nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
1323         if (!nmsg)
1324                 return -ENOMEM;
1325         gsm322_cs_sendmsg(ms, nmsg);
1326
1327         return 0;
1328 }
1329
1330 /* MS is switched off */
1331 static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
1332 {
1333         struct gsm322_plmn *plmn = &ms->plmn;
1334
1335         stop_plmn_timer(plmn);
1336
1337         new_m_state(plmn, GSM322_M0_NULL);
1338
1339         return 0;
1340 }
1341
1342 static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
1343 {
1344         LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
1345         return 0;
1346 }
1347
1348 /* SIM is removed */
1349 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
1350 {
1351         struct gsm322_plmn *plmn = &ms->plmn;
1352         struct msgb *nmsg;
1353
1354         stop_plmn_timer(plmn);
1355
1356         /* indicate SIM remove to cell selection process */
1357         nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
1358         if (!nmsg)
1359                 return -ENOMEM;
1360         gsm322_cs_sendmsg(ms, nmsg);
1361
1362         return gsm322_m_switch_on(ms, msg);
1363 }
1364
1365 /* go to On PLMN state */
1366 static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
1367 {
1368         struct gsm322_plmn *plmn = &ms->plmn;
1369         struct gsm_subscriber *subscr = &ms->subscr;
1370
1371         /* set last registered PLMN */
1372         subscr->plmn_valid = 1;
1373         subscr->plmn_mcc = plmn->mcc;
1374         subscr->plmn_mnc = plmn->mnc;
1375 #ifdef TODO
1376         store on sim
1377 #endif
1378
1379         new_m_state(plmn, GSM322_M2_ON_PLMN);
1380
1381         return 0;
1382 }
1383
1384 /* indicate selected PLMN */
1385 static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
1386 {
1387         struct gsm322_plmn *plmn = &ms->plmn;
1388
1389         vty_notify(ms, NULL);
1390         vty_notify(ms, "Selected Network: %s, %s\n",
1391                 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1392
1393         return gsm322_m_go_on_plmn(ms, msg);
1394 }
1395
1396 /* previously selected PLMN becomes available again */
1397 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
1398 {
1399         struct gsm322_plmn *plmn = &ms->plmn;
1400         struct gsm322_cellsel *cs = &ms->cellsel;
1401
1402         new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
1403
1404         if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
1405                 struct msgb *nmsg;
1406
1407                 LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
1408                         "selected, so start selection.\n");
1409
1410                 /* indicate New PLMN */
1411                 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1412                 if (!nmsg)
1413                         return -ENOMEM;
1414                 gsm322_cs_sendmsg(ms, nmsg);
1415         }
1416         
1417         return 0;
1418 }
1419
1420 /* the user has selected given PLMN */
1421 static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
1422 {
1423         struct gsm322_plmn *plmn = &ms->plmn;
1424         struct gsm_subscriber *subscr = &ms->subscr;
1425         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1426         struct msgb *nmsg;
1427
1428         /* use user selection */
1429         plmn->mcc = gm->mcc;
1430         plmn->mnc = gm->mnc;
1431
1432         vty_notify(ms, NULL);
1433         vty_notify(ms, "Selected Network: %s, %s\n",
1434                 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1435         LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s  "
1436                 "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
1437                 gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
1438
1439         /* if selected PLMN is in list of forbidden PLMNs */
1440         gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc);
1441
1442         new_m_state(plmn, GSM322_M4_TRYING_PLMN);
1443
1444         /* indicate New PLMN */
1445         nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
1446         if (!nmsg)
1447                 return -ENOMEM;
1448         gsm322_cs_sendmsg(ms, nmsg);
1449
1450         return 0;
1451 }
1452
1453 /* auto mode selected */
1454 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
1455 {
1456         struct msgb *nmsg;
1457
1458         /* restart state machine */
1459         gsm322_m_switch_off(ms, msg);
1460         ms->settings.plmn_mode = PLMN_MODE_AUTO;
1461         gsm322_a_switch_on(ms, msg);
1462
1463         nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
1464         if (!nmsg)
1465                 return -ENOMEM;
1466         gsm48_mmevent_msg(ms, nmsg);
1467
1468         return 0;
1469 }
1470
1471 /* if no cell is found in other states than in *_TRYING_* states */
1472 static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
1473 {
1474         struct msgb *nmsg;
1475
1476         /* Tell cell selection process to handle "no cell found". */
1477         nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1478         if (!nmsg)
1479                 return -ENOMEM;
1480         gsm322_cs_sendmsg(ms, nmsg);
1481
1482         return 0;
1483 }
1484
1485 /*
1486  * cell scanning process
1487  */
1488
1489 /* select a suitable and allowable cell */
1490 static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed)
1491 {
1492         struct gsm322_cellsel *cs = &ms->cellsel;
1493         struct gsm_subscriber *subscr = &ms->subscr;
1494         struct gsm48_sysinfo *s;
1495         int i, found = -1, power = 0;
1496         uint8_t flags, mask;
1497         uint16_t acc_class;
1498
1499         /* set out access class depending on the cell selection type */
1500         if (any) {
1501                 acc_class = subscr->acc_class | 0x0400; /* add emergency */
1502                 LOGP(DCS, LOGL_DEBUG, "Select using access class with "
1503                         "Emergency class.\n");
1504         } else {
1505                 acc_class = subscr->acc_class;
1506                 LOGP(DCS, LOGL_DEBUG, "Select using access class \n");
1507         }
1508
1509         /* flags to match */
1510         mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1511                 | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
1512         if (cs->state == GSM322_C2_STORED_CELL_SEL
1513          || cs->state == GSM322_C5_CHOOSE_CELL)
1514                 mask |= GSM322_CS_FLAG_BA;
1515         flags = mask; /* all masked flags are requied */
1516
1517         /* loop through all scanned frequencies and select cell */
1518         for (i = 0; i <= 1023; i++) {
1519                 cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
1520                 s = cs->list[i].sysinfo;
1521
1522                 /* channel has no informations for us */
1523                 if (!s || (cs->list[i].flags & mask) != flags) {
1524                         continue;
1525                 }
1526
1527                 /* check C1 criteria not fullfilled */
1528                 // TODO: C1 is also dependant on power class and max power
1529                 if (rxlev2dbm(cs->list[i].rxlev) < s->rxlev_acc_min_db) {
1530                         LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
1531                                 "not met. (rxlev %s < min %d)\n", i,
1532                                 gsm_print_rxlev(cs->list[i].rxlev),
1533                                 s->rxlev_acc_min_db);
1534                         continue;
1535                 }
1536
1537                 /* if cell is barred and we don't override */
1538                 if (!subscr->acc_barr
1539                  && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
1540                         LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
1541                                 "barred.\n", i);
1542                         continue;
1543                 }
1544
1545                 /* if cell is in list of forbidden LAs */
1546                 if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
1547                         LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1548                                 "list of forbidden LAs. (mcc=%s mnc=%s "
1549                                 "lai=%04x)\n", i, gsm_print_mcc(s->mcc),
1550                                 gsm_print_mnc(s->mnc), s->lac);
1551                         continue;
1552                 }
1553
1554                 /* if cell is in list of forbidden PLMNs */
1555                 if (plmn_allowed && gsm_subscr_is_forbidden_plmn(subscr,
1556                                                 s->mcc, s->mnc)) {
1557                         LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
1558                                 "list of forbidden PLMNs. (mcc=%s mnc=%s)\n", i,
1559                                 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc));
1560                         continue;
1561                 }
1562
1563                 /* if we have no access to the cell and we don't override */
1564                 if (!subscr->acc_barr 
1565                  && !(acc_class & (s->class_barr ^ 0xffff))) {
1566                         LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
1567                                 "barred for out access. (access=%04x "
1568                                 "barred=%04x)\n", i, acc_class, s->class_barr);
1569                         continue;
1570                 }
1571
1572                 /* store temporary available and allowable flag */
1573                 cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
1574
1575                 /* if we search a specific PLMN, but it does not match */
1576                 if (!any && cs->mcc && (cs->mcc != s->mcc
1577                                 || cs->mnc != s->mnc)) {
1578                         LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
1579                                 "does not match target PLMN. (mcc=%s "
1580                                 "mnc=%s)\n", i, gsm_print_mcc(s->mcc),
1581                                 gsm_print_mnc(s->mnc));
1582                         continue;
1583                 }
1584
1585                 LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%s "
1586                         "mcc=%s mnc=%s lac=%04x  %s, %s)\n", i,
1587                         gsm_print_rxlev(cs->list[i].rxlev),
1588                         gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac,
1589                         gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc));
1590
1591                 /* find highest power cell */
1592                 if (found < 0 || cs->list[i].rxlev > power) {
1593                         power = cs->list[i].rxlev;
1594                         found = i;
1595                 }
1596         }
1597
1598         if (found >= 0)
1599                 LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
1600
1601         return found;
1602 }
1603
1604 /* tune to first/next unscanned frequency and search for PLMN */
1605 static int gsm322_cs_scan(struct osmocom_ms *ms)
1606 {
1607         struct gsm322_cellsel *cs = &ms->cellsel;
1608         int i;
1609 #ifndef SKIP_MAX_PER_BAND
1610         int j;
1611 #endif
1612         uint8_t mask, flags;
1613         uint32_t weight = 0, test = cs->scan_state;
1614
1615         /* search for strongest unscanned cell */
1616         mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
1617                 | GSM322_CS_FLAG_SIGNAL;
1618         if (cs->state == GSM322_C2_STORED_CELL_SEL
1619          || cs->state == GSM322_C5_CHOOSE_CELL)
1620                 mask |= GSM322_CS_FLAG_BA;
1621         flags = mask; /* all masked flags are requied */
1622         for (i = 0; i <= 1023; i++) {
1623 #ifndef SKIP_MAX_PER_BAND
1624                 /* skip if band has enough frequencies scanned (3.2.1) */
1625                 for (j = 0; gsm_sup_smax[j].max; j++) {
1626                         if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
1627                                 if (gsm_sup_smax[j].start >= i
1628                                  && gsm_sup_smax[j].end <= i)
1629                                         break;
1630                         } else {
1631                                 if (gsm_sup_smax[j].end <= i
1632                                  || gsm_sup_smax[j].start >= i)
1633                                         break;
1634                         }
1635                 }
1636                 if (gsm_sup_smax[j].max) {
1637                         if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
1638                                 continue;
1639                 }
1640 #endif
1641                 /* search for unscanned frequency */
1642                 if ((cs->list[i].flags & mask) == flags) {
1643                         /* weight depends on the power level
1644                          * if it is the same, it depends on arfcn
1645                          */
1646                         test = cs->list[i].rxlev + 1;
1647                         test = (test << 16) | i;
1648                         if (test >= cs->scan_state)
1649                                 continue;
1650                         if (test > weight)
1651                                 weight = test;
1652                 }
1653         }
1654         cs->scan_state = weight;
1655
1656         if (!weight)
1657                 gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs,
1658                         NULL);
1659
1660         /* special case for PLMN search */
1661         if (cs->state == GSM322_PLMN_SEARCH && !weight) {
1662                 struct msgb *nmsg;
1663
1664                 /* create AA flag */
1665                 cs->mcc = cs->mnc = 0;
1666                 gsm322_cs_select(ms, 0, 0);
1667
1668                 new_c_state(cs, GSM322_C0_NULL);
1669
1670                 nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END);
1671                 LOGP(DCS, LOGL_INFO, "PLMN search finished.\n");
1672                 if (!nmsg)
1673                         return -ENOMEM;
1674                 gsm322_plmn_sendmsg(ms, nmsg);
1675
1676                 return 0;
1677         }
1678
1679         /* special case for HPLMN search */
1680         if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
1681                 struct msgb *nmsg;
1682
1683                 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1684                 LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n");
1685                 if (!nmsg)
1686                         return -ENOMEM;
1687                 gsm322_plmn_sendmsg(ms, nmsg);
1688
1689                 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
1690
1691                 cs->arfcn = cs->sel_arfcn;
1692                 LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev "
1693                         "%s).\n", cs->arfcn,
1694                         gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
1695                 hack = 1;
1696                 gsm322_sync_to_cell(cs);
1697
1698                 return 0;
1699         }
1700
1701         /* if all frequencies have been searched */
1702         if (!weight) {
1703                 struct msgb *nmsg;
1704 #if 0
1705                 int found, any = 0;
1706
1707                 LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
1708
1709                 /* just see, if we search for any cell */
1710                 if (cs->state == GSM322_C6_ANY_CELL_SEL
1711                  || cs->state == GSM322_C8_ANY_CELL_RESEL
1712                  || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1713                         any = 1;
1714
1715                 found = gsm322_cs_select(ms, any, 0);
1716
1717                 /* if found */
1718                 if (found >= 0) {
1719                         struct gsm322_plmn *plmn = &ms->plmn;
1720
1721                         LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1722                         /* tune */
1723                         cs->arfcn = found;
1724                         cs->si = cs->list[cs->arfcn].sysinfo;
1725                         hack = 1;
1726                         gsm322_sync_to_cell(cs);
1727
1728                         /* selected PLMN (manual) or any PLMN (auto) */
1729                         switch (ms->settings.plmn_mode) {
1730                         case PLMN_MODE_AUTO:
1731                                 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1732                                         /* PLMN becomes available */
1733                                         nmsg = gsm322_msgb_alloc(
1734                                                 GSM322_EVENT_PLMN_AVAIL);
1735                                         if (!nmsg)
1736                                                 return -ENOMEM;
1737                                         gsm322_plmn_sendmsg(ms, nmsg);
1738                                 }
1739                                 break;
1740                         case PLMN_MODE_MANUAL:
1741                                 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1742                                   && gsm322_is_plmn_avail(cs, plmn->mcc,
1743                                         plmn->mnc)) {
1744                                         /* PLMN becomes available */
1745                                         nmsg = gsm322_msgb_alloc(
1746                                                 GSM322_EVENT_PLMN_AVAIL);
1747                                         if (!nmsg)
1748                                                 return -ENOMEM;
1749                                         gsm322_plmn_sendmsg(ms, nmsg);
1750                                 }
1751                                 break;
1752                         }
1753
1754                         /* set selected cell */
1755                         cs->selected = 1;
1756                         cs->sel_arfcn = cs->arfcn;
1757                         memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1758                         cs->sel_mcc = cs->si->mcc;
1759                         cs->sel_mnc = cs->si->mnc;
1760                         cs->sel_lac = cs->si->lac;
1761                         cs->sel_id = cs->si->cell_id;
1762
1763                         /* tell CS process about available cell */
1764                         LOGP(DCS, LOGL_INFO, "Cell available.\n");
1765                         nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1766                 } else {
1767 #endif
1768                         /* unset selected cell */
1769                         gsm322_unselect_cell(cs);
1770
1771                         /* tell CS process about no cell available */
1772                         LOGP(DCS, LOGL_INFO, "No cell available.\n");
1773                         nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1774 //              }
1775                 if (!nmsg)
1776                         return -ENOMEM;
1777                 gsm322_c_event(ms, nmsg);
1778                 msgb_free(nmsg);
1779
1780                 return 0;
1781         }
1782
1783         /* NOTE: We might already have system information from previous
1784          * scan. But we need recent informations, so we scan again!
1785          */
1786
1787         /* Tune to frequency for a while, to receive broadcasts. */
1788         cs->arfcn = weight & 1023;
1789         LOGP(DCS, LOGL_DEBUG, "Scanning frequency %d (rxlev %s).\n", cs->arfcn,
1790                 gsm_print_rxlev(cs->list[cs->arfcn].rxlev));
1791         hack = 1;
1792         gsm322_sync_to_cell(cs);
1793
1794         /* Allocate/clean system information. */
1795         cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1796         if (cs->list[cs->arfcn].sysinfo)
1797                 memset(cs->list[cs->arfcn].sysinfo, 0,
1798                         sizeof(struct gsm48_sysinfo));
1799         else
1800                 cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
1801                                                 struct gsm48_sysinfo);
1802         if (!cs->list[cs->arfcn].sysinfo)
1803                 exit(-ENOMEM);
1804         cs->si = cs->list[cs->arfcn].sysinfo;
1805
1806         /* increase scan counter for each maximum scan range */
1807 #ifndef SKIP_MAX_PER_BAND
1808         if (gsm_sup_smax[j].max) {
1809                 LOGP(DCS, LOGL_DEBUG, "%d frequencies left in band %d..%d\n",
1810                         gsm_sup_smax[j].max - gsm_sup_smax[j].temp,
1811                         gsm_sup_smax[j].start, gsm_sup_smax[j].end);
1812                 gsm_sup_smax[j].temp++;
1813         }
1814 #endif
1815
1816         return 0;
1817 }
1818
1819 /* check if cell is now suitable and allowable */
1820 static int gsm322_cs_store(struct osmocom_ms *ms)
1821 {
1822         struct gsm322_cellsel *cs = &ms->cellsel;
1823         struct gsm48_sysinfo *s = cs->si;
1824         struct gsm322_plmn *plmn = &ms->plmn;
1825         struct msgb *nmsg;
1826         int found, any = 0;
1827
1828         if (cs->state != GSM322_C2_STORED_CELL_SEL
1829          && cs->state != GSM322_C1_NORMAL_CELL_SEL
1830          && cs->state != GSM322_C6_ANY_CELL_SEL
1831          && cs->state != GSM322_C4_NORMAL_CELL_RESEL
1832          && cs->state != GSM322_C8_ANY_CELL_RESEL
1833          && cs->state != GSM322_C5_CHOOSE_CELL
1834          && cs->state != GSM322_C9_CHOOSE_ANY_CELL
1835          && cs->state != GSM322_PLMN_SEARCH
1836          && cs->state != GSM322_HPLMN_SEARCH) {
1837                 LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
1838                         "(re-)selection, please fix!\n");
1839                 return -EINVAL;
1840         }
1841
1842         /* store sysinfo */
1843         cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO;
1844         if (s->cell_barr
1845          && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp &&
1846                         cs->list[cs->arfcn].sysinfo->sp_cbq))
1847                 cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED;
1848         else
1849                 cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED;
1850
1851 #if 0
1852         cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db;
1853         cs->list[cs->arfcn].class_barr = s->class_barr;
1854         cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch;
1855 #endif
1856
1857         /* store selected network */
1858         if (s->mcc) {
1859 #if 0
1860                 cs->list[cs->arfcn].mcc = s->mcc;
1861                 cs->list[cs->arfcn].mnc = s->mnc;
1862                 cs->list[cs->arfcn].lac = s->lac;
1863 #endif
1864
1865                 if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
1866                         cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD;
1867                 else
1868                         cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD;
1869         }
1870
1871         LOGP(DCS, LOGL_DEBUG, "Scan frequency %d: Cell found. (rxlev %s "
1872                 "mcc %s mnc %s lac %04x)\n", cs->arfcn,
1873                 gsm_print_rxlev(cs->list[cs->arfcn].rxlev),
1874                 gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac);
1875
1876         /* special case for PLMN search */
1877         if (cs->state == GSM322_PLMN_SEARCH)
1878                 /* tune to next cell */
1879                 return gsm322_cs_scan(ms);
1880
1881         /* special case for HPLMN search */
1882         if (cs->state == GSM322_HPLMN_SEARCH) {
1883                 struct gsm_subscriber *subscr = &ms->subscr;
1884                 struct msgb *nmsg;
1885
1886                 if (!gsm322_is_hplmn_avail(cs, subscr->imsi))
1887                         /* tune to next cell */
1888                         return gsm322_cs_scan(ms);
1889
1890                 nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1891                 LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n");
1892                 if (!nmsg)
1893                         return -ENOMEM;
1894                 gsm322_plmn_sendmsg(ms, nmsg);
1895
1896                 return 0;
1897         }
1898
1899         /* just see, if we search for any cell */
1900         if (cs->state == GSM322_C6_ANY_CELL_SEL
1901          || cs->state == GSM322_C8_ANY_CELL_RESEL
1902          || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
1903                 any = 1;
1904
1905         found = gsm322_cs_select(ms, any, 0);
1906
1907         /* if not found */
1908         if (found < 0) {
1909                 LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n");
1910                 /* tune to next cell */
1911                 return gsm322_cs_scan(ms);
1912         }
1913
1914         LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
1915         /* tune */
1916         cs->arfcn = found;
1917         cs->si = cs->list[cs->arfcn].sysinfo;
1918         hack = 1;
1919         gsm322_sync_to_cell(cs);
1920
1921         /* selected PLMN (manual) or any PLMN (auto) */
1922         switch (ms->settings.plmn_mode) {
1923         case PLMN_MODE_AUTO:
1924                 if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
1925                         /* PLMN becomes available */
1926                         nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1927                         if (!nmsg)
1928                                 return -ENOMEM;
1929                         gsm322_plmn_sendmsg(ms, nmsg);
1930                 }
1931                 break;
1932         case PLMN_MODE_MANUAL:
1933                 if (plmn->state == GSM322_M3_NOT_ON_PLMN
1934                   && gsm322_is_plmn_avail(cs, plmn->mcc,
1935                         plmn->mnc)) {
1936                         /* PLMN becomes available */
1937                         nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
1938                         if (!nmsg)
1939                                 return -ENOMEM;
1940                         gsm322_plmn_sendmsg(ms, nmsg);
1941                 }
1942                 break;
1943         }
1944
1945         /* set selected cell */
1946         cs->selected = 1;
1947         cs->sel_arfcn = cs->arfcn;
1948         memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
1949         cs->sel_mcc = cs->si->mcc;
1950         cs->sel_mnc = cs->si->mnc;
1951         cs->sel_lac = cs->si->lac;
1952         cs->sel_id = cs->si->cell_id;
1953
1954         /* tell CS process about available cell */
1955         LOGP(DCS, LOGL_INFO, "Cell available.\n");
1956         nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
1957         if (!nmsg)
1958                 return -ENOMEM;
1959         gsm322_c_event(ms, nmsg);
1960         msgb_free(nmsg);
1961
1962         return 0;
1963 }
1964
1965 /* process system information when returing to idle mode */
1966 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
1967 {
1968         struct gsm322_cellsel *cs = &ms->cellsel;
1969         struct gsm48_sysinfo *s = cs->si;
1970         struct gsm322_ba_list *ba = NULL;
1971         int i;
1972         uint8_t freq[128];
1973
1974         /* collect system information received during dedicated mode */
1975         if (s->si5
1976          && (!s->nb_ext_ind_si5 
1977           || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
1978           || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
1979                 && s->nb_ext_ind_si5bis))) {
1980                 /* find or create ba list */
1981                 ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
1982                 if (!ba) {
1983                         ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
1984                         if (!ba)
1985                                 return NULL;
1986                         ba->mcc = s->mcc;
1987                         ba->mnc = s->mnc;
1988                         llist_add_tail(&ba->entry, &cs->ba_list);
1989                 }
1990                 /* update (add) ba list */
1991                 memset(freq, 0, sizeof(freq));
1992                 for (i = 0; i <= 1023; i++) {
1993                         if ((s->freq[i].mask & (FREQ_TYPE_SERV
1994                                 | FREQ_TYPE_NCELL | FREQ_TYPE_REP)))
1995                                 freq[i >> 3] |= (1 << (i & 7));
1996                 }
1997                 if (!!memcmp(freq, ba->freq, sizeof(freq))) {
1998                         LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s  "
1999                                 "%s, %s).\n", gsm_print_mcc(ba->mcc),
2000                                 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2001                                 gsm_get_mnc(ba->mcc, ba->mnc));
2002                         memcpy(ba->freq, freq, sizeof(freq));
2003                 }
2004         }
2005
2006         return ba;
2007 }
2008
2009 /* store BA whenever a system informations changes */
2010 static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
2011         struct gsm48_sysinfo *s)
2012 {
2013         struct gsm322_ba_list *ba;
2014         int i;
2015         uint8_t freq[128];
2016
2017         /* find or create ba list */
2018         ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
2019         if (!ba) {
2020                 ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2021                 if (!ba)
2022                         return -ENOMEM;
2023                 ba->mcc = s->mcc;
2024                 ba->mnc = s->mnc;
2025                 llist_add_tail(&ba->entry, &cs->ba_list);
2026         }
2027         /* update ba list */
2028         memset(freq, 0, sizeof(freq));
2029         freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
2030         for (i = 0; i <= 1023; i++) {
2031                 if ((s->freq[i].mask &
2032                     (FREQ_TYPE_SERV | FREQ_TYPE_NCELL | FREQ_TYPE_REP)))
2033                         freq[i >> 3] |= (1 << (i & 7));
2034         }
2035         if (!!memcmp(freq, ba->freq, sizeof(freq))) {
2036                 LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s  "
2037                         "%s, %s).\n", gsm_print_mcc(ba->mcc),
2038                         gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
2039                         gsm_get_mnc(ba->mcc, ba->mnc));
2040                 memcpy(ba->freq, freq, sizeof(freq));
2041         }
2042
2043         return 0;
2044 }
2045
2046 /* process system information during camping on a cell */
2047 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2048 {
2049 //      struct gsm48_rrlayer *rr = &ms->rrlayer;
2050         struct gsm322_cellsel *cs = &ms->cellsel;
2051         struct gsm48_sysinfo *s = cs->si;
2052         struct gsm_subscriber *subscr = &ms->subscr;
2053         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2054         struct msgb *nmsg;
2055
2056 #if 0
2057         if (rr->state != GSM48_RR_ST_IDLE) {
2058                 LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n");
2059                 return -EBUSY;
2060         }
2061 #endif
2062
2063         /* Store BA if we have full system info about cells and neigbor cells.
2064          * Depending on the extended bit in the channel description,
2065          * we require more or less system informations about neighbor cells
2066          */
2067         if (s->mcc
2068          && s->mnc
2069          && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2070           || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2071           || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2072           || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2073          && s->si1
2074          && s->si2
2075          && (!s->nb_ext_ind_si2 
2076           || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2077           || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2078                 && s->nb_ext_ind_si2bis)))
2079                 gsm322_store_ba_list(cs, s);
2080
2081         /* update sel_si, if all relevant system informations received */
2082         if (s->si1 && s->si2 && s->si3
2083          && (!s->nb_ext_ind_si2
2084           || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2085           || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2086                 && s->nb_ext_ind_si2bis))) {
2087                 if (cs->selected) {
2088                         LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is "
2089                                 "updated.\n");
2090                         memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
2091                         //gsm48_sysinfo_dump(s, print_dcs, NULL);
2092                 }
2093         }
2094
2095         /* check for barred cell */
2096         if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
2097                 /* check if cell becomes barred */
2098                 if (!subscr->acc_barr && s->cell_barr
2099                  && !(cs->list[cs->arfcn].sysinfo
2100                    && cs->list[cs->arfcn].sysinfo->sp
2101                    && cs->list[cs->arfcn].sysinfo->sp_cbq)) {
2102                         LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
2103                         trigger_resel:
2104                         /* mark cell as unscanned */
2105                         cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
2106                         if (cs->list[cs->arfcn].sysinfo) {
2107                                 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2108                                         cs->arfcn);
2109                                 talloc_free(cs->list[cs->arfcn].sysinfo);
2110                                 cs->list[cs->arfcn].sysinfo = NULL;
2111                                 gsm322_unselect_cell(cs);
2112                         }
2113                         /* trigger reselection without queueing,
2114                          * because other sysinfo message may be queued
2115                          * before
2116                          */
2117                         nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2118                         if (!nmsg)
2119                                 return -ENOMEM;
2120                         gsm322_c_event(ms, nmsg);
2121                         msgb_free(nmsg);
2122
2123                         return 0;
2124                 }
2125                 /* check if cell access becomes barred */
2126                 if (!((subscr->acc_class & 0xfbff)
2127                         & (s->class_barr ^ 0xffff))) {
2128                         LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
2129                         goto trigger_resel;
2130                 }
2131         }
2132
2133         /* check if MCC, MNC, LAC, cell ID changes */
2134         if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc
2135          || cs->sel_lac != s->lac) {
2136                 LOGP(DCS, LOGL_NOTICE, "Cell changes location area. "
2137                         "This is not good!\n");
2138                 goto trigger_resel;
2139         }
2140         if (cs->sel_id != s->cell_id) {
2141                 LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. "
2142                         "This is not good!\n");
2143                 goto trigger_resel;
2144         }
2145
2146         return 0;
2147 }
2148
2149 /* process system information during channel scanning */
2150 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
2151 {
2152         struct gsm322_cellsel *cs = &ms->cellsel;
2153         struct gsm48_sysinfo *s = cs->si;
2154         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2155
2156         /* no sysinfo if we are not done with power scan */
2157         if (cs->powerscan) {
2158                 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
2159                 return -EINVAL;
2160         }
2161
2162         /* Store BA if we have full system info about cells and neigbor cells.
2163          * Depending on the extended bit in the channel description,
2164          * we require more or less system informations about neighbor cells
2165          */
2166         if (s->mcc
2167          && s->mnc
2168          && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
2169           || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
2170           || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
2171           || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
2172          && s->si1
2173          && s->si2
2174          && (!s->nb_ext_ind_si2 
2175           || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2176           || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2177                 && s->nb_ext_ind_si2bis)))
2178                 gsm322_store_ba_list(cs, s);
2179
2180         /* all relevant system informations received */
2181         if (s->si1 && s->si2 && s->si3
2182          && (!s->nb_ext_ind_si2
2183           || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
2184           || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
2185                 && s->nb_ext_ind_si2bis))) {
2186                 LOGP(DCS, LOGL_DEBUG, "Received relevant sysinfo.\n");
2187                 /* stop timer */
2188                 stop_cs_timer(cs);
2189
2190                 //gsm48_sysinfo_dump(s, print_dcs, NULL);
2191
2192                 /* store sysinfo and continue scan */
2193                 return gsm322_cs_store(ms);
2194         }
2195
2196         /* wait for more sysinfo or timeout */
2197         return 0;
2198 }
2199
2200 static void gsm322_cs_timeout(void *arg)
2201 {
2202         struct gsm322_cellsel *cs = arg;
2203         struct osmocom_ms *ms = cs->ms;
2204
2205         /* if we have no lock, we retry */
2206         if (cs->ccch_state != GSM322_CCCH_ST_SYNC)
2207                 LOGP(DCS, LOGL_INFO, "Cell selection failed, sync timeout.\n");
2208         else
2209                 LOGP(DCS, LOGL_INFO, "Cell selection failed, read timeout.\n");
2210
2211         /* remove system information */
2212         cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO; 
2213         if (cs->list[cs->arfcn].sysinfo) {
2214                 LOGP(DCS, LOGL_DEBUG, "free sysinfo arfcn=%d\n", cs->arfcn);
2215                 talloc_free(cs->list[cs->arfcn].sysinfo);
2216                 cs->list[cs->arfcn].sysinfo = NULL;
2217                 gsm322_unselect_cell(cs);
2218         }
2219
2220         /* tune to next cell */
2221         gsm322_cs_scan(ms);
2222
2223         return;
2224 }
2225
2226 /*
2227  * power scan process
2228  */
2229
2230 /* search for block of unscanned frequencies and start scanning */
2231 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
2232 {
2233         struct gsm322_cellsel *cs = &ms->cellsel;
2234         struct gsm_settings *set = &ms->settings;
2235         int i, s = -1, e;
2236         uint8_t mask, flags;
2237
2238         again:
2239
2240         mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
2241         flags = GSM322_CS_FLAG_SUPPORT;
2242
2243         /* in case of sticking to a cell, we only select it */
2244         if (set->stick) {
2245                 LOGP(DCS, LOGL_DEBUG, "Scanning power for sticked cell.\n");
2246                 i = set->stick_arfcn;
2247                 if ((cs->list[i].flags & mask) == flags)
2248                         s = e = i;
2249         } else {
2250                 /* search for first frequency to scan */
2251                 if (cs->state == GSM322_C2_STORED_CELL_SEL
2252                  || cs->state == GSM322_C5_CHOOSE_CELL) {
2253                         LOGP(DCS, LOGL_DEBUG, "Scanning power for stored BA "
2254                                 "list.\n");
2255                         mask |= GSM322_CS_FLAG_BA;
2256                         flags |= GSM322_CS_FLAG_BA;
2257                 } else
2258                         LOGP(DCS, LOGL_DEBUG, "Scanning power for all "
2259                                 "frequencies.\n");
2260                 for (i = 0; i <= 1023; i++) {
2261                         if ((cs->list[i].flags & mask) == flags) {
2262                                 s = e = i;
2263                                 break;
2264                         }
2265                 }
2266         }
2267
2268         /* if there is no more frequency, we can tune to that cell */
2269         if (s < 0) {
2270                 int found = 0;
2271
2272                 /* stop power level scanning */
2273                 cs->powerscan = 0;
2274
2275                 /* check if not signal is found */
2276                 for (i = 0; i <= 1023; i++) {
2277                         if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2278                                 found++;
2279                 }
2280                 if (!found) {
2281                         struct msgb *nmsg;
2282
2283                         LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
2284                         /* on normal cell selection, start over */
2285                         if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
2286                                 for (i = 0; i <= 1023; i++) {
2287                                         /* clear flag that this was scanned */
2288                                         cs->list[i].flags &=
2289                                                 ~(GSM322_CS_FLAG_POWER
2290                                                 | GSM322_CS_FLAG_SIGNAL
2291                                                 | GSM322_CS_FLAG_SYSINFO);
2292                                         if (cs->list[i].sysinfo) {
2293                                                 LOGP(DCS, LOGL_INFO, "free "
2294                                                         "sysinfo arfcn=%d\n",
2295                                                         i);
2296                                                 talloc_free(
2297                                                         cs->list[i].sysinfo);
2298                                                 cs->list[i].sysinfo = NULL;
2299                                         }
2300                                 }
2301                                 /* no cell selected */
2302                                 gsm322_unselect_cell(cs);
2303                                 goto again;
2304                         }
2305                         /* on other cell selection, indicate "no cell found" */
2306                         /* NOTE: PLMN search process handles it.
2307                          * If not handled there, CS process gets indicated.
2308                          * If we would continue to process CS, then we might get
2309                          * our list of scanned cells disturbed.
2310                          */
2311                         if (cs->state == GSM322_PLMN_SEARCH)
2312                                 nmsg = gsm322_msgb_alloc(
2313                                         GSM322_EVENT_PLMN_SEARCH_END);
2314                         else
2315                                 nmsg = gsm322_msgb_alloc(
2316                                         GSM322_EVENT_NO_CELL_FOUND);
2317                         if (!nmsg)
2318                                 return -ENOMEM;
2319                         gsm322_plmn_sendmsg(ms, nmsg);
2320
2321                         /* if HPLMN search, select last frequency */
2322                         if (cs->state == GSM322_HPLMN_SEARCH) {
2323                                 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2324
2325                                 cs->arfcn = cs->sel_arfcn;
2326                                 LOGP(DCS, LOGL_INFO, "Tuning back to frequency "
2327                                         "%d (rxlev %s).\n", cs->arfcn,
2328                                         gsm_print_rxlev(
2329                                                 cs->list[cs->arfcn].rxlev));
2330                                 hack = 1;
2331                                 gsm322_sync_to_cell(cs);
2332
2333                         } else
2334                                 new_c_state(cs, GSM322_C0_NULL);
2335
2336                         return 0;
2337                 }
2338                 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
2339                 cs->scan_state = 0xffffffff; /* higher than high */
2340                 /* clear counter of scanned frequencies of each range */
2341                 for (i = 0; gsm_sup_smax[i].max; i++)
2342                         gsm_sup_smax[i].temp = 0;
2343                 return gsm322_cs_scan(ms);
2344         }
2345
2346         /* search last frequency to scan (en block) */
2347         e = i;
2348         if (!set->stick) {
2349                 for (i = s + 1; i <= 1023; i++) {
2350                         if ((cs->list[i].flags & mask) == flags)
2351                                 e = i;
2352                         else
2353                                 break;
2354                 }
2355         }
2356
2357         LOGP(DCS, LOGL_DEBUG, "Scanning frequencies. (%d..%d)\n", s, e);
2358
2359         /* start scan on radio interface */
2360         if (!cs->powerscan) {
2361                 l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
2362                 cs->powerscan = 1;
2363         }
2364 //#warning TESTING!!!!
2365 //usleep(300000);
2366         return l1ctl_tx_pm_req_range(ms, s, e);
2367 }
2368
2369 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
2370                      void *handler_data, void *signal_data)
2371 {
2372         struct osmocom_ms *ms;
2373         struct gsm322_cellsel *cs;
2374         struct osmobb_meas_res *mr;
2375         struct osmobb_fbsb_res *fr;
2376         int i;
2377         int8_t rxlev;
2378
2379         if (subsys != SS_L1CTL)
2380                 return 0;
2381
2382         switch (signal) {
2383         case S_L1CTL_PM_RES:
2384                 mr = signal_data;
2385                 ms = mr->ms;
2386                 cs = &ms->cellsel;
2387                 if (!cs->powerscan)
2388                         return -EINVAL;
2389                 i = mr->band_arfcn & 1023;
2390                 rxlev = mr->rx_lev;
2391                 if ((cs->list[i].flags & GSM322_CS_FLAG_POWER)) {
2392                         LOGP(DCS, LOGL_ERROR, "Getting PM for frequency %d "
2393                                 "twice. Overwriting the first! Please fix "
2394                                 "prim_pm.c\n", i);
2395                 }
2396                 cs->list[i].rxlev = rxlev;
2397                 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2398                 cs->list[i].flags &= ~GSM322_CS_FLAG_SIGNAL;
2399                 /* if minimum level is reached or if we stick to a cell */
2400                 if (rxlev2dbm(rxlev) >= ms->settings.min_rxlev_db
2401                  || ms->settings.stick) {
2402                         cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2403                         LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2404                                 "rxlev %s (%d))\n", i,
2405                                 gsm_print_rxlev(rxlev), rxlev);
2406                 }
2407                 break;
2408         case S_L1CTL_PM_DONE:
2409                 LOGP(DCS, LOGL_DEBUG, "Done with power scanning range.\n");
2410                 ms = signal_data;
2411                 cs = &ms->cellsel;
2412                 if (!cs->powerscan)
2413                         return -EINVAL;
2414                 gsm322_cs_powerscan(ms);
2415                 break;
2416         case S_L1CTL_FBSB_RESP:
2417                 fr = signal_data;
2418                 ms = fr->ms;
2419                 cs = &ms->cellsel;
2420                 if (cs->ccch_state == GSM322_CCCH_ST_INIT) {
2421                         LOGP(DCS, LOGL_INFO, "Channel synched. (ARFCN=%d, "
2422                                 "snr=%u, BSIC=%u)\n", cs->arfcn, fr->snr,
2423                                 fr->bsic);
2424                         cs->ccch_state = GSM322_CCCH_ST_SYNC;
2425                         if (cs->si)
2426                                 cs->si->bsic = fr->bsic;
2427 #if 0
2428                         stop_cs_timer(cs);
2429
2430                         /* in dedicated mode */
2431                         if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
2432                                 return gsm48_rr_tx_rand_acc(ms, NULL);
2433 #endif
2434
2435                         /* set timer for reading BCCH */
2436                         if (cs->state == GSM322_C2_STORED_CELL_SEL
2437                          || cs->state == GSM322_C1_NORMAL_CELL_SEL
2438                          || cs->state == GSM322_C6_ANY_CELL_SEL
2439                          || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2440                          || cs->state == GSM322_C8_ANY_CELL_RESEL
2441                          || cs->state == GSM322_C5_CHOOSE_CELL
2442                          || cs->state == GSM322_C9_CHOOSE_ANY_CELL
2443                          || cs->state == GSM322_PLMN_SEARCH
2444                          || cs->state == GSM322_HPLMN_SEARCH)
2445                                 start_cs_timer(cs, ms->support.scan_to, 0);
2446                                         // TODO: timer depends on BCCH config
2447
2448                         /* set downlink signalling failure criterion */
2449                         ms->meas.ds_fail = ms->meas.dsc = ms->settings.dsc_max;
2450                         LOGP(DRR, LOGL_INFO, "using DSC of %d\n", ms->meas.dsc);
2451                 }
2452                 break;
2453         case S_L1CTL_FBSB_ERR:
2454 #if 0
2455                 if (hack) {
2456                         ms = signal_data;
2457                         cs = &ms->cellsel;
2458                         gsm322_sync_to_cell(cs);
2459                         hack--;
2460                         LOGP(DCS, LOGL_INFO, "Channel sync error, try again\n");
2461                         break;
2462                 }
2463 #endif
2464                 LOGP(DCS, LOGL_INFO, "Channel sync error.\n");
2465                 ms = signal_data;
2466                 cs = &ms->cellsel;
2467
2468                 stop_cs_timer(cs);
2469                 if (cs->selected)
2470                         gsm322_cs_loss(cs);
2471                 else
2472                         gsm322_cs_timeout(cs);
2473                 break;
2474         case S_L1CTL_LOSS_IND:
2475                 ms = signal_data;
2476                 cs = &ms->cellsel;
2477                 gsm322_cs_loss(cs);
2478                 break;
2479         case S_L1CTL_RESET:
2480                 ms = signal_data;
2481                 if (ms->mmlayer.power_off_idle) {
2482                         l23_app_exit(ms);
2483                         exit(0);
2484                 }
2485                 break;
2486         }
2487
2488         return 0;
2489 }
2490
2491 static void gsm322_cs_loss(void *arg)
2492 {
2493         struct gsm322_cellsel *cs = arg;
2494         struct osmocom_ms *ms = cs->ms;
2495         struct gsm48_rrlayer *rr = &ms->rrlayer;
2496
2497         LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n");
2498         if (cs->state == GSM322_C3_CAMPED_NORMALLY
2499          || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
2500                 if (rr->state == GSM48_RR_ST_IDLE) {
2501                         struct msgb *nmsg;
2502
2503                         LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n");
2504
2505                         nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
2506                         if (!nmsg)
2507                                 return;
2508                         gsm322_c_event(ms, nmsg);
2509                         msgb_free(nmsg);
2510                 } else {
2511                         LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n");
2512                         gsm48_rr_los(ms);
2513                         /* be shure that nothing else is done after here
2514                          * because the function call above may cause
2515                          * to return from idle state and trigger cell re-sel.
2516                          */
2517                 }
2518         }
2519
2520         return;
2521 }
2522
2523 /*
2524  * handler for cell selection process
2525  */
2526
2527 /* start PLMN search */
2528 static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg)
2529 {
2530         struct gsm322_cellsel *cs = &ms->cellsel;
2531         int i;
2532
2533         new_c_state(cs, GSM322_PLMN_SEARCH);
2534
2535         /* mark all frequencies except our own BA to be scanned */
2536         for (i = 0; i <= 1023; i++) {
2537                 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2538                                         | GSM322_CS_FLAG_SIGNAL
2539                                         | GSM322_CS_FLAG_SYSINFO);
2540                 if (cs->list[i].sysinfo) {
2541                         LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2542                         talloc_free(cs->list[i].sysinfo);
2543                         cs->list[i].sysinfo = NULL;
2544                         gsm322_unselect_cell(cs);
2545                 }
2546         }
2547
2548         /* unset selected cell */
2549         gsm322_unselect_cell(cs);
2550
2551         /* start power scan */
2552         return gsm322_cs_powerscan(ms);
2553 }
2554
2555 /* start HPLMN search */
2556 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2557 {
2558         struct gsm322_cellsel *cs = &ms->cellsel;
2559         int i;
2560
2561         new_c_state(cs, GSM322_HPLMN_SEARCH);
2562
2563         /* mark all frequencies except our own BA to be scanned */
2564         for (i = 0; i <= 1023; i++) {
2565                 if (i != cs->sel_arfcn
2566                  && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2567                  && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2568                         cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2569                                                 | GSM322_CS_FLAG_SIGNAL
2570                                                 | GSM322_CS_FLAG_SYSINFO);
2571                         if (cs->list[i].sysinfo) {
2572                                 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2573                                         i);
2574                                 talloc_free(cs->list[i].sysinfo);
2575                                 cs->list[i].sysinfo = NULL;
2576                         }
2577                 }
2578         }
2579
2580         /* no cell selected */
2581         gsm322_unselect_cell(cs);
2582
2583         /* start power scan */
2584         return gsm322_cs_powerscan(ms);
2585 }
2586
2587 /* start stored cell selection */
2588 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2589 {
2590         struct gsm322_cellsel *cs = &ms->cellsel;
2591         int i;
2592
2593         new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2594
2595         /* flag all frequencies that are in current band allocation */
2596         for (i = 0; i <= 1023; i++) {
2597                 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2598                         cs->list[i].flags |= GSM322_CS_FLAG_BA;
2599                 else
2600                         cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2601         }
2602
2603         /* unset selected cell */
2604         gsm322_unselect_cell(cs);
2605
2606         /* start power scan */
2607         return gsm322_cs_powerscan(ms);
2608 }
2609
2610 /* start noraml cell selection */
2611 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2612 {
2613         struct gsm322_cellsel *cs = &ms->cellsel;
2614         int i;
2615
2616         /* except for stored cell selection state, we weed to rescan ?? */
2617         if (cs->state != GSM322_C2_STORED_CELL_SEL) {
2618                 for (i = 0; i <= 1023; i++) {
2619                         cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2620                                                 | GSM322_CS_FLAG_SIGNAL
2621                                                 | GSM322_CS_FLAG_SYSINFO);
2622                         if (cs->list[i].sysinfo) {
2623                                 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2624                                         i);
2625                                 talloc_free(cs->list[i].sysinfo);
2626                                 cs->list[i].sysinfo = NULL;
2627                         }
2628                 }
2629         }
2630
2631         new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2632
2633         /* unset selected cell */
2634         gsm322_unselect_cell(cs);
2635
2636         /* start power scan */
2637         return gsm322_cs_powerscan(ms);
2638 }
2639
2640 /* start any cell selection */
2641 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2642 {
2643         struct gsm322_cellsel *cs = &ms->cellsel;
2644
2645         /* in case we already tried any cell (re-)selection, power scan again */
2646         if (cs->state == GSM322_C0_NULL
2647          || cs->state == GSM322_C6_ANY_CELL_SEL
2648          || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2649                 int i;
2650
2651                 for (i = 0; i <= 1023; i++) {
2652                         cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2653                                                 | GSM322_CS_FLAG_SIGNAL
2654                                                 | GSM322_CS_FLAG_SYSINFO);
2655                         if (cs->list[i].sysinfo) {
2656                                 LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
2657                                         i);
2658                                 talloc_free(cs->list[i].sysinfo);
2659                                 cs->list[i].sysinfo = NULL;
2660                         }
2661                 }
2662         }
2663         /* after re-selection, indicate no cell found */
2664         if (cs->state == GSM322_C6_ANY_CELL_SEL
2665          || cs->state == GSM322_C8_ANY_CELL_RESEL) {
2666                 struct msgb *nmsg;
2667
2668                 /* tell that we have no cell found */
2669                 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2670                 if (!nmsg)
2671                         return -ENOMEM;
2672                 gsm48_mmevent_msg(ms, nmsg);
2673
2674         } else { 
2675                 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2676         }
2677
2678         cs->mcc = cs->mnc = 0;
2679
2680         /* unset selected cell */
2681         gsm322_unselect_cell(cs);
2682
2683         /* start power scan */
2684         return gsm322_cs_powerscan(ms);
2685 }
2686
2687 /* start noraml cell re-selection */
2688 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2689 {
2690         struct gsm322_cellsel *cs = &ms->cellsel;
2691
2692         new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2693
2694         /* NOTE: We keep our scan info we have so far.
2695          * This may cause a skip in power scan. */
2696
2697         /* start power scan */
2698         return gsm322_cs_powerscan(ms);
2699 }
2700
2701 /* start any cell re-selection */
2702 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2703 {
2704         struct gsm322_cellsel *cs = &ms->cellsel;
2705
2706         new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2707
2708         /* NOTE: We keep our scan info we have so far.
2709          * This may cause a skip in power scan. */
2710
2711         /* start power scan */
2712         return gsm322_cs_powerscan(ms);
2713 }
2714
2715 /* a suitable cell was found, so we camp normally */
2716 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2717 {
2718         struct gsm322_cellsel *cs = &ms->cellsel;
2719         struct msgb *nmsg;
2720
2721         LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s "
2722                 "mnc=%s  %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2723                 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2724                 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2725
2726         /* tell that we have selected a (new) cell */
2727         nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2728         if (!nmsg)
2729                 return -ENOMEM;
2730         gsm48_mmevent_msg(ms, nmsg);
2731
2732         new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2733
2734         return 0;
2735 }
2736
2737 /* a not suitable cell was found, so we camp on any cell */
2738 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2739 {
2740         struct gsm322_cellsel *cs = &ms->cellsel;
2741         struct msgb *nmsg;
2742
2743         LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s "
2744                 "mnc=%s  %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
2745                 gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
2746                 gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
2747
2748
2749         /* tell that we have selected a (new) cell */
2750         nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2751         if (!nmsg)
2752                 return -ENOMEM;
2753         gsm48_mmevent_msg(ms, nmsg);
2754
2755         new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2756
2757         return 0;
2758 }
2759
2760 /* create temporary ba range with given frequency ranges */
2761 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2762         uint32_t *range, uint8_t ranges)
2763 {
2764         static struct gsm322_ba_list ba;
2765         uint16_t lower, higher;
2766
2767         memset(&ba, 0, sizeof(ba));
2768
2769         while(ranges--) {
2770                 lower = *range & 1023;
2771                 higher = (*range >> 16) & 1023;
2772                 range++;
2773                 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2774                 /* GSM 05.08 6.3 */
2775                 while (1) {
2776                         ba.freq[lower >> 3] |= 1 << (lower & 7);
2777                         if (lower == higher)
2778                                 break;
2779                         lower = (lower + 1) & 1023;
2780                 }
2781         }
2782
2783         return &ba;
2784 }
2785
2786 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2787 static int gsm322_cs_choose(struct osmocom_ms *ms)
2788 {
2789         struct gsm322_cellsel *cs = &ms->cellsel;
2790         struct gsm48_rrlayer *rr = &ms->rrlayer;
2791         struct gsm322_ba_list *ba = NULL;
2792         int i;
2793
2794         /* NOTE: The call to this function is synchron to RR layer, so
2795          * we may access the BA range there.
2796          */
2797         if (rr->ba_ranges)
2798                 ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges);
2799         else {
2800                 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2801                 /* get and update BA of last received sysinfo 5* */
2802                 ba = gsm322_cs_sysinfo_sacch(ms);
2803                 if (!ba) {
2804                         LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored "
2805                                 "BA list.\n");
2806                         ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2807                                 cs->sel_si.mnc);
2808                 }
2809         }
2810
2811         if (!ba) {
2812                 struct msgb *nmsg;
2813
2814                 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2815
2816                 /* tell CS to start over */
2817                 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2818                 if (!nmsg)
2819                         return -ENOMEM;
2820                 gsm322_c_event(ms, nmsg);
2821                 msgb_free(nmsg);
2822
2823                 return 0;
2824         }
2825
2826         /* flag all frequencies that are in current band allocation */
2827         for (i = 0; i <= 1023; i++) {
2828                 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2829                         if ((ba->freq[i >> 3] & (1 << (i & 7)))) {
2830                                 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2831                         } else {
2832                                 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2833                         }
2834                 }
2835                 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2836                                         | GSM322_CS_FLAG_SIGNAL
2837                                         | GSM322_CS_FLAG_SYSINFO);
2838                 if (cs->list[i].sysinfo) {
2839                         LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
2840                         talloc_free(cs->list[i].sysinfo);
2841                         cs->list[i].sysinfo = NULL;
2842                 }
2843         }
2844
2845         /* unset selected cell */
2846         gsm322_unselect_cell(cs);
2847
2848         /* start power scan */
2849         return gsm322_cs_powerscan(ms);
2850 }
2851
2852 /* start 'Choose cell' after returning to idle mode */
2853 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2854 {
2855         struct gsm322_cellsel *cs = &ms->cellsel;
2856         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2857
2858         /* After location updating, we choose the last cell */
2859         if (gm->same_cell) {
2860                 struct msgb *nmsg;
2861
2862                 if (!cs->selected) {
2863                         printf("No cell selected when ret.idle, please fix!\n");
2864                         exit(0L);
2865                 }
2866                 cs->arfcn = cs->sel_arfcn;
2867
2868                 /* be sure to go to current camping frequency on return */
2869                 LOGP(DCS, LOGL_INFO, "Selecting frequency %d. after LOC.UPD.\n",
2870                         cs->arfcn);
2871                 hack = 1;
2872                 gsm322_sync_to_cell(cs);
2873                 cs->si = cs->list[cs->arfcn].sysinfo;
2874
2875                 new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2876
2877                 /* tell that we have selected the cell, so RR returns IDLE */
2878                 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2879                 if (!nmsg)
2880                         return -ENOMEM;
2881                 gsm48_mmevent_msg(ms, nmsg);
2882
2883                 return 0;
2884         }
2885
2886         new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2887
2888         return gsm322_cs_choose(ms);
2889 }
2890
2891 /* start 'Choose any cell' after returning to idle mode */
2892 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2893 {
2894         struct gsm322_cellsel *cs = &ms->cellsel;
2895
2896         new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2897
2898         return gsm322_cs_choose(ms);
2899 }
2900
2901 /* a new PLMN is selected by PLMN search process */
2902 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2903 {
2904         struct gsm322_cellsel *cs = &ms->cellsel;
2905         struct gsm322_plmn *plmn = &ms->plmn;
2906         struct gsm322_ba_list *ba;
2907
2908         cs->mcc = plmn->mcc;
2909         cs->mnc = plmn->mnc;
2910
2911         LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s "
2912                 "mnc=%s  %s, %s)\n", gsm_print_mcc(cs->mcc),
2913                 gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc),
2914                 gsm_get_mnc(cs->mcc, cs->mnc));
2915
2916         /* search for BA list */
2917         ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2918
2919         if (ba) {
2920                 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2921                 return gsm322_c_stored_cell_sel(ms, ba);
2922         } else {
2923                 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2924                 return gsm322_c_normal_cell_sel(ms, msg);
2925         }
2926 }
2927
2928 /* go connected mode */
2929 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2930 {
2931         struct gsm322_cellsel *cs = &ms->cellsel;
2932
2933         /* check for error */
2934         if (!cs->selected)
2935                 return -EINVAL;
2936         cs->arfcn = cs->sel_arfcn;
2937
2938         /* be sure to go to current camping frequency on return */
2939         LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2940         hack = 1;
2941         gsm322_sync_to_cell(cs);
2942         cs->si = cs->list[cs->arfcn].sysinfo;
2943
2944         return 0;
2945 }
2946
2947 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2948 {
2949         struct gsm322_cellsel *cs = &ms->cellsel;
2950
2951         /* check for error */
2952         if (!cs->selected)
2953                 return -EINVAL;
2954         cs->arfcn = cs->sel_arfcn;
2955
2956         /* be sure to go to current camping frequency on return */
2957         LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2958         hack = 1;
2959         gsm322_sync_to_cell(cs);
2960         cs->si = cs->list[cs->arfcn].sysinfo;
2961
2962         return 0;
2963 }
2964
2965 /* switch on */
2966 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2967 {
2968         struct gsm_subscriber *subscr = &ms->subscr;
2969
2970         /* if no SIM is is MS */
2971         if (!subscr->sim_valid) {
2972                 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2973                 return gsm322_c_any_cell_sel(ms, msg);
2974         }
2975         LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2976
2977         /* stay in NULL state until PLMN is selected */
2978
2979         return 0;
2980 }
2981
2982 /*
2983  * state machines
2984  */
2985
2986 /* state machine for automatic PLMN selection events */
2987 static struct plmnastatelist {
2988         uint32_t        states;
2989         int             type;
2990         int             (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2991 } plmnastatelist[] = {
2992         {SBIT(GSM322_A0_NULL),
2993          GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2994
2995         /* special case for full search */
2996         {SBIT(GSM322_A0_NULL),
2997          GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn},
2998
2999         {ALL_STATES,
3000          GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
3001
3002         {SBIT(GSM322_A0_NULL) | SBIT(GSM322_A6_NO_SIM),
3003          GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
3004
3005         {ALL_STATES,
3006          GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
3007
3008         {ALL_STATES,
3009          GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
3010
3011         {ALL_STATES,
3012          GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
3013
3014         {SBIT(GSM322_A1_TRYING_RPLMN),
3015          GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
3016
3017         {SBIT(GSM322_A1_TRYING_RPLMN),
3018          GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn},
3019
3020         {SBIT(GSM322_A1_TRYING_RPLMN),
3021          GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
3022
3023         {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
3024          GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
3025
3026         {SBIT(GSM322_A2_ON_PLMN),
3027          GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
3028
3029         {SBIT(GSM322_A2_ON_PLMN),
3030          GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start},
3031
3032         {SBIT(GSM322_A2_ON_PLMN),
3033          GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
3034
3035         {SBIT(GSM322_A2_ON_PLMN),
3036          GSM322_EVENT_USER_RESEL, gsm322_a_user_resel},
3037
3038         {SBIT(GSM322_A3_TRYING_PLMN),
3039          GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
3040
3041         {SBIT(GSM322_A3_TRYING_PLMN),
3042          GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn},
3043
3044         {SBIT(GSM322_A3_TRYING_PLMN),
3045          GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
3046
3047         {SBIT(GSM322_A5_HPLMN_SEARCH),
3048          GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn},
3049
3050         {SBIT(GSM322_A5_HPLMN_SEARCH),
3051          GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn},
3052
3053         {SBIT(GSM322_A4_WAIT_FOR_PLMN),
3054          GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
3055
3056         {SBIT(GSM322_A4_WAIT_FOR_PLMN),
3057          GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_plmn_avail},
3058
3059         {ALL_STATES,
3060          GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
3061
3062         {ALL_STATES,
3063          GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3064 };
3065
3066 #define PLMNASLLEN \
3067         (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
3068
3069 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
3070 {
3071         struct gsm322_plmn *plmn = &ms->plmn;
3072         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3073         int msg_type = gm->msg_type;
3074         int rc;
3075         int i;
3076
3077         LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
3078                 "selection in state '%s'\n", ms->name, get_event_name(msg_type),
3079                 plmn_a_state_names[plmn->state]);
3080         /* find function for current state and message */
3081         for (i = 0; i < PLMNASLLEN; i++)
3082                 if ((msg_type == plmnastatelist[i].type)
3083                  && ((1 << plmn->state) & plmnastatelist[i].states))
3084                         break;
3085         if (i == PLMNASLLEN) {
3086                 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3087                 return 0;
3088         }
3089
3090         rc = plmnastatelist[i].rout(ms, msg);
3091
3092         return rc;
3093 }
3094
3095 /* state machine for manual PLMN selection events */
3096 static struct plmnmstatelist {
3097         uint32_t        states;
3098         int             type;
3099         int             (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3100 } plmnmstatelist[] = {
3101         {SBIT(GSM322_M0_NULL),
3102          GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
3103
3104         {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) |
3105          SBIT(GSM322_M2_ON_PLMN),
3106          GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns},
3107
3108         {ALL_STATES,
3109          GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
3110
3111         {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M5_NO_SIM),
3112          GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
3113
3114         {ALL_STATES,
3115          GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
3116
3117         {ALL_STATES,
3118          GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
3119
3120         {SBIT(GSM322_M1_TRYING_RPLMN),
3121          GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3122
3123         {SBIT(GSM322_M1_TRYING_RPLMN),
3124          GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3125
3126         {SBIT(GSM322_M1_TRYING_RPLMN),
3127          GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3128
3129         {SBIT(GSM322_M1_TRYING_RPLMN),
3130          GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
3131
3132         {SBIT(GSM322_M2_ON_PLMN),
3133          GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3134
3135         {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
3136          SBIT(GSM322_M4_TRYING_PLMN),
3137          GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
3138
3139         {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN),
3140          GSM322_EVENT_USER_RESEL, gsm322_m_user_resel},
3141
3142         {SBIT(GSM322_M3_NOT_ON_PLMN),
3143          GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
3144
3145         {SBIT(GSM322_M3_NOT_ON_PLMN),
3146          GSM322_EVENT_CHOOSE_PLMN, gsm322_m_choose_plmn},
3147
3148         {SBIT(GSM322_M4_TRYING_PLMN),
3149          GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
3150
3151         {SBIT(GSM322_M4_TRYING_PLMN),
3152          GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
3153
3154         {SBIT(GSM322_M4_TRYING_PLMN),
3155          GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
3156
3157         {SBIT(GSM322_M4_TRYING_PLMN),
3158          GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
3159
3160         {ALL_STATES,
3161          GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
3162
3163         {ALL_STATES,
3164          GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
3165 };
3166
3167 #define PLMNMSLLEN \
3168         (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
3169
3170 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
3171 {
3172         struct gsm322_plmn *plmn = &ms->plmn;
3173         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3174         int msg_type = gm->msg_type;
3175         int rc;
3176         int i;
3177
3178         LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
3179                 "in state '%s'\n", ms->name, get_event_name(msg_type),
3180                 plmn_m_state_names[plmn->state]);
3181         /* find function for current state and message */
3182         for (i = 0; i < PLMNMSLLEN; i++)
3183                 if ((msg_type == plmnmstatelist[i].type)
3184                  && ((1 << plmn->state) & plmnmstatelist[i].states))
3185                         break;
3186         if (i == PLMNMSLLEN) {
3187                 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
3188                 return 0;
3189         }
3190
3191         rc = plmnmstatelist[i].rout(ms, msg);
3192
3193         return rc;
3194 }
3195
3196 /* dequeue GSM 03.22 PLMN events */
3197 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
3198 {
3199         struct gsm322_plmn *plmn = &ms->plmn;
3200         struct msgb *msg;
3201         int work = 0;
3202         
3203         while ((msg = msgb_dequeue(&plmn->event_queue))) {
3204                 /* send event to PLMN select process */
3205                 if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
3206                         gsm322_a_event(ms, msg);
3207                 else
3208                         gsm322_m_event(ms, msg);
3209                 msgb_free(msg);
3210                 work = 1; /* work done */
3211         }
3212         
3213         return work;
3214 }
3215
3216 /* state machine for channel selection events */
3217 static struct cellselstatelist {
3218         uint32_t        states;
3219         int             type;
3220         int             (*rout) (struct osmocom_ms *ms, struct msgb *msg);
3221 } cellselstatelist[] = {
3222         {ALL_STATES,
3223          GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
3224
3225         {ALL_STATES,
3226          GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
3227
3228         {ALL_STATES,
3229          GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
3230
3231         {ALL_STATES,
3232          GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search},
3233
3234         {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3235          SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
3236          GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
3237
3238         {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3239          SBIT(GSM322_C8_ANY_CELL_RESEL),
3240          GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
3241
3242         {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
3243          SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3244          SBIT(GSM322_C0_NULL),
3245          GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
3246
3247         {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3248          SBIT(GSM322_C4_NORMAL_CELL_RESEL),
3249          GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
3250
3251         {SBIT(GSM322_C3_CAMPED_NORMALLY),
3252          GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
3253
3254         {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3255          GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
3256
3257         {SBIT(GSM322_C3_CAMPED_NORMALLY),
3258          GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
3259
3260         {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3261          GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
3262
3263         {SBIT(GSM322_C3_CAMPED_NORMALLY),
3264          GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
3265
3266         {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3267          GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
3268
3269         {SBIT(GSM322_C7_CAMPED_ANY_CELL),
3270          GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
3271
3272         {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
3273          SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
3274          SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
3275          SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH),
3276          GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
3277
3278         {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
3279          GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
3280
3281         {SBIT(GSM322_C3_CAMPED_NORMALLY),
3282          GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search},
3283 };
3284
3285 #define CELLSELSLLEN \
3286         (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
3287
3288 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
3289 {
3290         struct gsm322_cellsel *cs = &ms->cellsel;
3291         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
3292         int msg_type = gm->msg_type;
3293         int rc;
3294         int i;
3295
3296         if (msg_type != GSM322_EVENT_SYSINFO)
3297                 LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection "
3298                         "in state '%s'\n", ms->name, get_event_name(msg_type),
3299                         cs_state_names[cs->state]);
3300         /* find function for current state and message */
3301         for (i = 0; i < CELLSELSLLEN; i++)
3302                 if ((msg_type == cellselstatelist[i].type)
3303                  && ((1 << cs->state) & cellselstatelist[i].states))
3304                         break;
3305         if (i == CELLSELSLLEN) {
3306                 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
3307                 return 0;
3308         }
3309
3310         rc = cellselstatelist[i].rout(ms, msg);
3311
3312         return rc;
3313 }
3314
3315 /* dequeue GSM 03.22 cell selection events */
3316 int gsm322_cs_dequeue(struct osmocom_ms *ms)
3317 {
3318         struct gsm322_cellsel *cs = &ms->cellsel;
3319         struct msgb *msg;
3320         int work = 0;
3321         
3322         while ((msg = msgb_dequeue(&cs->event_queue))) {
3323                 /* send event to cell selection process */
3324                 gsm322_c_event(ms, msg);
3325                 msgb_free(msg);
3326                 work = 1; /* work done */
3327         }
3328         
3329         return work;
3330 }
3331
3332 /*
3333  * dump lists
3334  */
3335
3336 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
3337 {
3338         struct gsm322_plmn *plmn = &ms->plmn;
3339         struct gsm322_plmn_list *temp;
3340
3341         printf("MCC    |MNC    |allowed|rx-lev\n");
3342         printf("-------+-------+-------+-------\n");
3343         llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
3344                 printf("%s    |%s%s    |%s    |%s\n", gsm_print_mcc(temp->mcc),
3345                         gsm_print_mnc(temp->mnc),
3346                         ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3347                         (temp->cause) ? "no ":"yes",
3348                         gsm_print_rxlev(temp->rxlev));
3349         }
3350
3351         return 0;
3352 }
3353
3354 int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
3355                         void (*print)(void *, const char *, ...), void *priv)
3356 {
3357         int i;
3358         struct gsm48_sysinfo *s;
3359
3360         print(priv, "arfcn  |MCC    |MNC    |LAC    |cell ID|forb.LA|prio   |"
3361                 "min-db |max-pwr|rx-lev\n");
3362         print(priv, "-------+-------+-------+-------+-------+-------+-------+"
3363                 "-------+-------+-------\n");
3364         for (i = 0; i <= 1023; i++) {
3365                 s = cs->list[i].sysinfo;
3366                 if (!s || !(cs->list[i].flags & flags))
3367                         continue;
3368                 print(priv, "%4d   |", i);
3369                 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
3370                         print(priv, "%s    |%s%s    |", gsm_print_mcc(s->mcc),
3371                                 gsm_print_mnc(s->mnc),
3372                                 ((s->mnc & 0x00f) == 0x00f) ? " ":"");
3373                         print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id);
3374                         if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
3375                                 print(priv, "yes    |");
3376                         else
3377                                 print(priv, "no     |");
3378                         if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
3379                                 print(priv, "barred |");
3380                         else {
3381                                 if (cs->list[i].sysinfo->cell_barr)
3382                                         print(priv, "low    |");
3383                                 else
3384                                         print(priv, "normal |");
3385                         }
3386                         print(priv, "%4d   |%4d   |%s\n", s->rxlev_acc_min_db,
3387                                 s->ms_txpwr_max_cch,
3388                                 gsm_print_rxlev(cs->list[i].rxlev));
3389                 } else
3390                         print(priv, "n/a    |n/a    |n/a    |n/a    |n/a    |"
3391                                 "n/a    |n/a    |n/a\n");
3392         }
3393         print(priv, "\n");
3394
3395         return 0;
3396 }
3397
3398 int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
3399                         void (*print)(void *, const char *, ...), void *priv)
3400 {
3401         struct gsm322_plmn *plmn = &ms->plmn;
3402         struct gsm322_la_list *temp;
3403
3404         print(priv, "MCC    |MNC    |LAC    |cause\n");
3405         print(priv, "-------+-------+-------+-------\n");
3406         llist_for_each_entry(temp, &plmn->forbidden_la, entry)
3407                 print(priv, "%s    |%s%s    |0x%04x |#%d\n",
3408                         gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
3409                         ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
3410                         temp->lac, temp->cause);
3411
3412         return 0;
3413 }
3414
3415 int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
3416                         void (*print)(void *, const char *, ...), void *priv)
3417 {
3418         struct gsm322_ba_list *ba;
3419         int i;
3420
3421         llist_for_each_entry(ba, &cs->ba_list, entry) {
3422                 if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc))
3423                         continue;
3424                 print(priv, "Band Allocation of network: MCC %s MNC %s "
3425                         "(%s, %s)\n", gsm_print_mcc(ba->mcc),
3426                         gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3427                         gsm_get_mnc(ba->mcc, ba->mnc));
3428                 for (i = 0; i <= 1023; i++) {
3429                         if ((ba->freq[i >> 3] & (1 << (i & 7))))
3430                                 print(priv, " %d", i);
3431                 }
3432                 print(priv, "\n");
3433         }
3434
3435         return 0;
3436 }
3437
3438 /*
3439  * initialization
3440  */
3441
3442 int gsm322_init(struct osmocom_ms *ms)
3443 {
3444         struct gsm322_plmn *plmn = &ms->plmn;
3445         struct gsm322_cellsel *cs = &ms->cellsel;
3446         FILE *fp;
3447         char filename[128];
3448         int i;
3449         struct gsm322_ba_list *ba;
3450         uint8_t buf[4];
3451
3452         LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
3453         LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
3454
3455         memset(plmn, 0, sizeof(*plmn));
3456         memset(cs, 0, sizeof(*cs));
3457         plmn->ms = ms;
3458         cs->ms = ms;
3459
3460         /* set initial state */
3461         plmn->state = 0;
3462         cs->state = 0;
3463         ms->settings.plmn_mode = PLMN_MODE_AUTO;
3464
3465         /* init lists */
3466         INIT_LLIST_HEAD(&plmn->event_queue);
3467         INIT_LLIST_HEAD(&cs->event_queue);
3468         INIT_LLIST_HEAD(&plmn->sorted_plmn);
3469         INIT_LLIST_HEAD(&plmn->forbidden_la);
3470         INIT_LLIST_HEAD(&cs->ba_list);
3471
3472         /* set supported frequencies in cell selection list */
3473         for (i = 0; i <= 1023; i++)
3474                 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
3475                         cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
3476
3477         /* read BA list */
3478         sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3479         fp = fopen(filename, "r");
3480         if (fp) {
3481                 int rc;
3482
3483                 while(!feof(fp)) {
3484                         ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
3485                         if (!ba)
3486                                 return -ENOMEM;
3487                         rc = fread(buf, 4, 1, fp);
3488                         if (!rc) {
3489                                 talloc_free(ba);
3490                                 break;
3491                         }
3492                         ba->mcc = (buf[0] << 8) | buf[1];
3493                         ba->mnc = (buf[2] << 8) | buf[3];
3494                         rc = fread(ba->freq, sizeof(ba->freq), 1, fp);
3495                         if (!rc) {
3496                                 talloc_free(ba);
3497                                 break;
3498                         }
3499                         llist_add_tail(&ba->entry, &cs->ba_list);
3500                         LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s "
3501                                 "mnc=%s  %s, %s)\n", gsm_print_mcc(ba->mcc),
3502                                 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3503                                 gsm_get_mnc(ba->mcc, ba->mnc));
3504                 }
3505                 fclose(fp);
3506         } else
3507                 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
3508
3509         register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3510
3511         return 0;
3512 }
3513
3514 int gsm322_exit(struct osmocom_ms *ms)
3515 {
3516         struct gsm322_plmn *plmn = &ms->plmn;
3517         struct gsm322_cellsel *cs = &ms->cellsel;
3518         struct llist_head *lh, *lh2;
3519         struct msgb *msg;
3520         FILE *fp;
3521         char filename[128];
3522         struct gsm322_ba_list *ba;
3523         uint8_t buf[4];
3524         int i;
3525
3526         LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
3527         LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
3528
3529         unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
3530
3531         /* stop cell selection process (if any) */
3532         new_c_state(cs, GSM322_C0_NULL);
3533
3534         /* stop timers */
3535         stop_cs_timer(cs);
3536         stop_plmn_timer(plmn);
3537
3538         /* flush sysinfo */
3539         for (i = 0; i <= 1023; i++) {
3540                 if (cs->list[i].sysinfo) {
3541                         LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
3542                         talloc_free(cs->list[i].sysinfo);
3543                         cs->list[i].sysinfo = NULL;
3544                 }
3545                 cs->list[i].flags = 0;
3546         }
3547
3548         /* store BA list */
3549         sprintf(filename, "/etc/osmocom/%s.ba", ms->name);
3550         fp = fopen(filename, "w");
3551         if (fp) {
3552                 int rc;
3553
3554                 llist_for_each_entry(ba, &cs->ba_list, entry) {
3555                         buf[0] = ba->mcc >> 8;
3556                         buf[1] = ba->mcc & 0xff;
3557                         buf[2] = ba->mnc >> 8;
3558                         buf[3] = ba->mnc & 0xff;
3559                         rc = fwrite(buf, 4, 1, fp);
3560                         rc = fwrite(ba->freq, sizeof(ba->freq), 1, fp);
3561                         LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s "
3562                                 "mnc=%s  %s, %s)\n", gsm_print_mcc(ba->mcc),
3563                                 gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
3564                                 gsm_get_mnc(ba->mcc, ba->mnc));
3565                 }
3566                 fclose(fp);
3567         } else
3568                 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
3569
3570         /* free lists */
3571         while ((msg = msgb_dequeue(&plmn->event_queue)))
3572                 msgb_free(msg);
3573         while ((msg = msgb_dequeue(&cs->event_queue)))
3574                 msgb_free(msg);
3575         llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
3576                 llist_del(lh);
3577                 talloc_free(lh);
3578         }
3579         llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
3580                 llist_del(lh);
3581                 talloc_free(lh);
3582         }
3583         llist_for_each_safe(lh, lh2, &cs->ba_list) {
3584                 llist_del(lh);
3585                 talloc_free(lh);
3586         }
3587         return 0;
3588 }
3589
3590