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