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