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