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