Many fixes in layer 3:
[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 int l1ctl_tx_ccch_req_(struct osmocom_ms *ms, uint16_t arfcn)
49 {
50         static int already = 0;
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);
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, "Selected sysinfo is updated.\n");
1791                         memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
1792                 }
1793         }
1794
1795         /* check for barred cell */
1796         if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
1797                 /* check if cell becomes barred */
1798                 if (!subscr->acc_barr && s->cell_barr) {
1799                         LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
1800                         trigger_resel:
1801                         /* mark cell as unscanned */
1802                         cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
1803                         /* trigger reselection event */
1804                         nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
1805                         if (!nmsg)
1806                                 return -ENOMEM;
1807                         gsm322_cs_sendmsg(ms, nmsg);
1808
1809                         return 0;
1810                 }
1811                 /* check if cell access becomes barred */
1812                 if (!((subscr->acc_class & 0xfbff)
1813                         & (s->class_barr ^ 0xffff))) {
1814                         LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
1815                         goto trigger_resel;
1816                 }
1817         }
1818
1819         return 0;
1820 }
1821
1822 /* process system information during channel scanning */
1823 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
1824 {
1825         struct gsm322_cellsel *cs = &ms->cellsel;
1826         struct gsm48_sysinfo *s = cs->si;
1827         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
1828
1829         /* no sysinfo if we are not done with power scan */
1830         if (cs->powerscan) {
1831                 LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
1832                 return -EINVAL;
1833         }
1834
1835         /* Store BA if we have full system info about cells and neigbor cells.
1836          * Depending on the extended bit in the channel description,
1837          * we require more or less system informations about neighbor cells
1838          */
1839         if (s->mcc
1840          && s->mnc
1841          && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
1842           || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
1843           || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
1844           || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
1845          && s->si1
1846          && s->si2
1847          && (!s->nb_ext_ind_si2 
1848           || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
1849           || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
1850                 && s->nb_ext_ind_si2bis)))
1851                 gsm322_store_ba_list(cs, s);
1852
1853         /* all relevant system informations received */
1854         if (s->si1 && s->si2 && s->si3
1855          && (!s->nb_ext_ind_si2
1856           || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
1857           || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
1858                 && s->nb_ext_ind_si2bis))) {
1859                 /* stop timer */
1860                 stop_cs_timer(cs);
1861
1862                 gsm48_sysinfo_dump(ms, s);
1863
1864                 /* store sysinfo and continue scan */
1865                 return gsm322_cs_store(ms);
1866         }
1867
1868         /* wait for more sysinfo or timeout */
1869         return 0;
1870 }
1871
1872 static void gsm322_cs_timeout(void *arg)
1873 {
1874         struct gsm322_cellsel *cs = arg;
1875         struct osmocom_ms *ms = cs->ms;
1876         int i = cs->scan_state & 1023;
1877
1878         LOGP(DCS, LOGL_INFO, "Cell selection timer has fired.\n");
1879         LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev=%d)\n",
1880                 i, cs->list[i].rxlev_db);
1881
1882         /* remove system information */
1883         cs->list[i].flags &= ~GSM322_CS_FLAG_SYSINFO; 
1884         if (cs->list[i].sysinfo) {
1885                 talloc_free(cs->list[i].sysinfo);
1886                 cs->list[i].sysinfo = NULL;
1887         }
1888
1889         /* tune to next cell */
1890         gsm322_cs_scan(ms);
1891
1892         return;
1893 }
1894
1895 /*
1896  * power scan process
1897  */
1898
1899 /* search for block of unscanned freqeuncies and start scanning */
1900 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
1901 {
1902         struct gsm322_cellsel *cs = &ms->cellsel;
1903         int i, s = -1, e;
1904         uint8_t mask, flags;
1905
1906         again:
1907
1908         /* search for first frequency to scan */
1909         mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
1910         flags = GSM322_CS_FLAG_SUPPORT;
1911         if (cs->state == GSM322_C2_STORED_CELL_SEL) {
1912                 LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA list.\n");
1913                 mask |= GSM322_CS_FLAG_BA;
1914                 flags |= GSM322_CS_FLAG_BA;
1915         } else
1916                 LOGP(DCS, LOGL_FATAL, "Scanning power for all frequencies.\n");
1917         for (i = 0; i <= 1023; i++) {
1918                 if ((cs->list[i].flags & mask) == flags) {
1919                         s = e = i;
1920                         break;
1921                 }
1922         }
1923
1924 #warning testing
1925 cs->list[10].rxlev_db = -50;
1926 cs->list[10].flags |= GSM322_CS_FLAG_POWER;
1927 cs->list[10].flags |= GSM322_CS_FLAG_SIGNAL;
1928 s = -1;
1929         /* if there is no more frequency, we can tune to that cell */
1930         if (s < 0) {
1931                 int found = 0;
1932
1933                 /* stop power level scanning */
1934                 cs->powerscan = 0;
1935
1936                 /* check if not signal is found */
1937                 for (i = 0; i <= 1023; i++) {
1938                         if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
1939                                 found++;
1940                 }
1941                 if (!found) {
1942                         struct msgb *nmsg;
1943
1944                         LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
1945                         /* on normal cell selection, start over */
1946                         if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
1947                                 for (i = 0; i <= 1023; i++) {
1948                                         /* clear flag that this was scanned */
1949                                         cs->list[i].flags &=
1950                                                 ~(GSM322_CS_FLAG_POWER
1951                                                 | GSM322_CS_FLAG_SIGNAL
1952                                                 | GSM322_CS_FLAG_SYSINFO);
1953                                         if (cs->list[i].sysinfo) {
1954                                                 talloc_free(
1955                                                         cs->list[i].sysinfo);
1956                                                 cs->list[i].sysinfo = NULL;
1957                                         }
1958                                 }
1959                                 goto again;
1960                         }
1961                         /* on other cell selection, indicate "no cell found" */
1962                         /* NOTE: PLMN search process handles it.
1963                          * If not handled there, CS process gets indicated.
1964                          * If we would continue to process CS, then we might get
1965                          * our list of scanned cells disturbed.
1966                          */
1967                         nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
1968                         if (!nmsg)
1969                                 return -ENOMEM;
1970                         gsm322_plmn_sendmsg(ms, nmsg);
1971
1972                         return 0;
1973                 }
1974                 LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
1975                 cs->scan_state = 0xffffffff; /* higher than high */
1976                 /* clear counter of scanned frequencies of each range */
1977                 for (i = 0; gsm_sup_smax[i].max; i++)
1978                         gsm_sup_smax[i].temp = 0;
1979                 return gsm322_cs_scan(ms);
1980         }
1981
1982         /* search last frequency to scan (en block) */
1983         e = i;
1984         for (i = s + 1; i <= 1023; i++) {
1985                 if ((cs->list[i].flags & mask) == flags)
1986                         e = i;
1987                 else
1988                         break;
1989         }
1990
1991         LOGP(DCS, LOGL_INFO, "Scanning frequecies. (%d..%d)\n", s, e);
1992
1993         /* start scan on radio interface */
1994         cs->powerscan = 1;
1995         return l1ctl_tx_pm_req_range(ms, s, e);
1996 }
1997
1998 static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
1999                      void *handler_data, void *signal_data)
2000 {
2001         struct osmocom_ms *ms;
2002         struct gsm322_cellsel *cs;
2003         struct osmobb_meas_res *mr;
2004         int i;
2005         int8_t rxlev_db;
2006
2007         if (subsys != SS_L1CTL)
2008                 return 0;
2009
2010         switch (signal) {
2011         case S_L1CTL_PM_RES:
2012                 mr = signal_data;
2013                 ms = mr->ms;
2014                 cs = &ms->cellsel;
2015                 if (!cs->powerscan)
2016                         return -EINVAL;
2017                 i = mr->band_arfcn & 1023;
2018                 rxlev_db = mr->rx_lev - 110;
2019                 cs->list[i].rxlev_db = rxlev_db;
2020                 cs->list[i].flags |= GSM322_CS_FLAG_POWER;
2021                 if (rxlev_db >= ms->support.min_rxlev_db) {
2022                         cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
2023                         LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
2024                                 "rxlev %d)\n", i, cs->list[i].rxlev_db);
2025                 }
2026                 break;
2027         case S_L1CTL_PM_DONE:
2028                 LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
2029                 ms = signal_data;
2030                 cs = &ms->cellsel;
2031                 if (!cs->powerscan)
2032                         return -EINVAL;
2033                 gsm322_cs_powerscan(ms);
2034                 break;
2035         case S_L1CTL_CCCH_RESP:
2036                 ms = signal_data;
2037                 cs = &ms->cellsel;
2038                 if (!cs->ccch_active) {
2039                         LOGP(DCS, LOGL_INFO, "Channel activated.\n");
2040                         cs->ccch_active = 1;
2041                         /* set timer for reading BCCH */
2042                         if (cs->state == GSM322_C2_STORED_CELL_SEL
2043                          || cs->state == GSM322_C1_NORMAL_CELL_SEL
2044                          || cs->state == GSM322_C6_ANY_CELL_SEL
2045                          || cs->state == GSM322_C4_NORMAL_CELL_RESEL
2046                          || cs->state == GSM322_C8_ANY_CELL_RESEL
2047                          || cs->state == GSM322_C5_CHOOSE_CELL
2048                          || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
2049                                 start_cs_timer(cs, 8, 0);
2050                                         // TODO: timer depends on BCCH config
2051                 }
2052                 break;
2053         }
2054         return 0;
2055 }
2056
2057 /*
2058  * handler for cell selection process
2059  */
2060
2061 /* start HPLMN search */
2062 static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
2063 {
2064         struct gsm322_cellsel *cs = &ms->cellsel;
2065         int i;
2066
2067         new_c_state(cs, GSM322_HPLMN_SEARCH);
2068
2069         /* mark all frequencies except our own BA to be scanned */
2070         for (i = 0; i <= 1023; i++) {
2071                 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
2072                  && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
2073                         cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2074                                                 | GSM322_CS_FLAG_SIGNAL
2075                                                 | GSM322_CS_FLAG_SYSINFO);
2076                         if (cs->list[i].sysinfo) {
2077                                 talloc_free(cs->list[i].sysinfo);
2078                                 cs->list[i].sysinfo = NULL;
2079                         }
2080                 }
2081         }
2082
2083         /* unset selected cell */
2084         cs->selected = 0;
2085         memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2086         cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2087
2088         /* start power scan */
2089         return gsm322_cs_powerscan(ms);
2090 }
2091
2092 /* start stored cell selection */
2093 static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
2094 {
2095         struct gsm322_cellsel *cs = &ms->cellsel;
2096         int i;
2097
2098         new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
2099
2100         /* flag all frequencies that are in current band allocation */
2101         for (i = 0; i <= 1023; i++) {
2102                 if ((ba->freq[i >> 3] & (1 << (i & 7))))
2103                         cs->list[i].flags |= GSM322_CS_FLAG_BA;
2104                 else
2105                         cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2106         }
2107
2108         /* unset selected cell */
2109         cs->selected = 0;
2110         memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2111         cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2112
2113         /* start power scan */
2114         return gsm322_cs_powerscan(ms);
2115 }
2116
2117 /* start noraml cell selection */
2118 static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2119 {
2120         struct gsm322_cellsel *cs = &ms->cellsel;
2121         int i;
2122
2123         /* except for stored cell selection state, we weed to rescan ?? */
2124         if (cs->state != GSM322_C2_STORED_CELL_SEL) {
2125                 for (i = 0; i <= 1023; i++) {
2126                         cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2127                                                 | GSM322_CS_FLAG_SIGNAL
2128                                                 | GSM322_CS_FLAG_SYSINFO);
2129                         if (cs->list[i].sysinfo) {
2130                                 talloc_free(cs->list[i].sysinfo);
2131                                 cs->list[i].sysinfo = NULL;
2132                         }
2133                 }
2134         }
2135
2136         new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
2137
2138         /* unset selected cell */
2139         cs->selected = 0;
2140         memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2141         cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2142
2143         /* start power scan */
2144         return gsm322_cs_powerscan(ms);
2145 }
2146
2147 /* start any cell selection */
2148 static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
2149 {
2150         struct gsm322_cellsel *cs = &ms->cellsel;
2151         int i;
2152
2153         /* in case we already tried any cell selection, power scan again */
2154         if (cs->state == GSM322_C6_ANY_CELL_SEL) {
2155                 struct msgb *nmsg;
2156
2157                 /* tell that we have no cell found */
2158                 nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
2159                 if (!nmsg)
2160                         return -ENOMEM;
2161                 gsm48_mmevent_msg(ms, nmsg);
2162
2163                 for (i = 0; i <= 1023; i++) {
2164                         cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2165                                                 | GSM322_CS_FLAG_SIGNAL
2166                                                 | GSM322_CS_FLAG_SYSINFO);
2167                         if (cs->list[i].sysinfo) {
2168                                 talloc_free(cs->list[i].sysinfo);
2169                                 cs->list[i].sysinfo = NULL;
2170                         }
2171                 }
2172         } else { 
2173                 new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
2174         }
2175
2176         cs->mcc = cs->mnc = 0;
2177
2178         /* unset selected cell */
2179         cs->selected = 0;
2180         memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2181         cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2182
2183         /* start power scan */
2184         return gsm322_cs_powerscan(ms);
2185 }
2186
2187 /* start noraml cell re-selection */
2188 static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2189 {
2190         struct gsm322_cellsel *cs = &ms->cellsel;
2191
2192         new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
2193
2194         /* NOTE: We keep our scan info we have so far.
2195          * This may cause a skip in power scan. */
2196
2197         /* start power scan */
2198         return gsm322_cs_powerscan(ms);
2199 }
2200
2201 /* start any cell re-selection */
2202 static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
2203 {
2204         struct gsm322_cellsel *cs = &ms->cellsel;
2205
2206         new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
2207
2208         /* NOTE: We keep our scan info we have so far.
2209          * This may cause a skip in power scan. */
2210
2211         /* start power scan */
2212         return gsm322_cs_powerscan(ms);
2213 }
2214
2215 /* create temporary ba range with given frequency ranges */
2216 struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
2217         uint32_t *range, uint8_t ranges)
2218 {
2219         static struct gsm322_ba_list ba;
2220         uint16_t lower, higher;
2221
2222         memset(&ba, 0, sizeof(ba));
2223
2224         while(ranges--) {
2225                 lower = *range & 1023;
2226                 higher = (*range >> 16) & 1023;
2227                 range++;
2228                 LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
2229                 /* GSM 05.08 6.3 */
2230                 while (1) {
2231                         ba.freq[lower >> 3] |= 1 << (lower & 7);
2232                         if (lower == higher)
2233                                 break;
2234                         lower = (lower + 1) & 1023;
2235                 }
2236         }
2237
2238         return &ba;
2239 }
2240
2241 /* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
2242 static int gsm322_cs_choose(struct osmocom_ms *ms)
2243 {
2244         struct gsm322_cellsel *cs = &ms->cellsel;
2245         struct gsm322_ba_list *ba = NULL;
2246         int i;
2247
2248 #ifdef TODO
2249 what we have todo here:
2250 if we return from dedicated mode and we have a ba range, we can use that for cell reselection
2251         if (message->ranges)
2252                 ba = gsm322_cs_ba_range(ms, message->range, message->ranges);
2253         else {
2254                 LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
2255 #endif
2256                 /* get and update BA of last received sysinfo 5* */
2257                 ba = gsm322_cs_sysinfo_sacch(ms);
2258                 if (!ba)
2259                         ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
2260                                 cs->sel_si.mnc);
2261 #ifdef TODO
2262         }
2263 #endif
2264
2265         if (!ba) {
2266                 struct msgb *nmsg;
2267
2268                 LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
2269
2270                 /* tell CS to start over */
2271                 nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
2272                 if (!nmsg)
2273                         return -ENOMEM;
2274                 gsm322_cs_sendmsg(ms, nmsg);
2275         }
2276
2277         /* flag all frequencies that are in current band allocation */
2278         for (i = 0; i <= 1023; i++) {
2279                 if (cs->state == GSM322_C5_CHOOSE_CELL) {
2280                         if ((ba->freq[i >> 3] & (1 << (i & 7))))
2281                                 cs->list[i].flags |= GSM322_CS_FLAG_BA;
2282                         else
2283                                 cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
2284                 }
2285                 cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
2286                                         | GSM322_CS_FLAG_SIGNAL
2287                                         | GSM322_CS_FLAG_SYSINFO);
2288                 if (cs->list[i].sysinfo) {
2289                         talloc_free(cs->list[i].sysinfo);
2290                         cs->list[i].sysinfo = NULL;
2291                 }
2292         }
2293
2294         /* unset selected cell */
2295         cs->selected = 0;
2296         memset(&cs->sel_si, 0, sizeof(cs->sel_si));
2297         cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
2298
2299         /* start power scan */
2300         return gsm322_cs_powerscan(ms);
2301 }
2302
2303 /* start 'Choose cell' after returning to idle mode */
2304 static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
2305 {
2306         struct gsm322_cellsel *cs = &ms->cellsel;
2307
2308         new_c_state(cs, GSM322_C5_CHOOSE_CELL);
2309
2310         return gsm322_cs_choose(ms);
2311 }
2312
2313 /* start 'Choose any cell' after returning to idle mode */
2314 static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2315 {
2316         struct gsm322_cellsel *cs = &ms->cellsel;
2317
2318         new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
2319
2320         return gsm322_cs_choose(ms);
2321 }
2322
2323 /* a new PLMN is selected by PLMN search process */
2324 static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
2325 {
2326         struct gsm322_cellsel *cs = &ms->cellsel;
2327         struct gsm322_plmn *plmn = &ms->plmn;
2328         struct gsm322_ba_list *ba;
2329
2330         cs->mcc = plmn->mcc;
2331         cs->mnc = plmn->mnc;
2332
2333         /* search for BA list */
2334         ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
2335
2336         if (ba) {
2337                 LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
2338                 return gsm322_c_stored_cell_sel(ms, ba);
2339         } else {
2340                 LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
2341                 return gsm322_c_normal_cell_sel(ms, msg);
2342         }
2343 }
2344
2345 /* a suitable cell was found, so we camp normally */
2346 static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
2347 {
2348         struct gsm322_cellsel *cs = &ms->cellsel;
2349         struct msgb *nmsg;
2350
2351         /* tell that we have selected a (new) cell */
2352         nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2353         if (!nmsg)
2354                 return -ENOMEM;
2355         gsm48_mmevent_msg(ms, nmsg);
2356
2357         new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
2358
2359         return 0;
2360 }
2361
2362 /* a not suitable cell was found, so we camp on any cell */
2363 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
2364 {
2365         struct gsm322_cellsel *cs = &ms->cellsel;
2366         struct msgb *nmsg;
2367
2368         /* tell that we have selected a (new) cell */
2369         nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
2370         if (!nmsg)
2371                 return -ENOMEM;
2372         gsm48_mmevent_msg(ms, nmsg);
2373
2374         new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
2375
2376         return 0;
2377 }
2378
2379 /* go connected mode */
2380 static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
2381 {
2382         struct gsm322_cellsel *cs = &ms->cellsel;
2383
2384         /* check for error */
2385         if (!cs->selected)
2386                 return -EINVAL;
2387         cs->arfcn = cs->sel_arfcn;
2388
2389         /* be sure to go to current camping frequency on return */
2390         LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2391         cs->ccch_active = 0;
2392         l1ctl_tx_ccch_req_(ms, cs->arfcn);
2393         cs->si = cs->list[cs->arfcn].sysinfo;
2394
2395         return 0;
2396 }
2397
2398 static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
2399 {
2400         struct gsm322_cellsel *cs = &ms->cellsel;
2401
2402         /* check for error */
2403         if (!cs->selected)
2404                 return -EINVAL;
2405         cs->arfcn = cs->sel_arfcn;
2406
2407         /* be sure to go to current camping frequency on return */
2408         LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
2409         cs->ccch_active = 0;
2410         l1ctl_tx_ccch_req_(ms, cs->arfcn);
2411         cs->si = cs->list[cs->arfcn].sysinfo;
2412
2413         return 0;
2414 }
2415
2416 /* switch on */
2417 static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
2418 {
2419         struct gsm_subscriber *subscr = &ms->subscr;
2420
2421         /* if no SIM is is MS */
2422         if (!subscr->sim_valid) {
2423                 LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
2424                 return gsm322_c_any_cell_sel(ms, msg);
2425         LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
2426         }
2427
2428         /* stay in NULL state until PLMN is selected */
2429
2430         return 0;
2431 }
2432
2433 /*
2434  * state machines
2435  */
2436
2437 /* state machine for automatic PLMN selection events */
2438 static struct plmnastatelist {
2439         uint32_t        states;
2440         int             type;
2441         int             (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2442 } plmnastatelist[] = {
2443         {SBIT(GSM322_A0_NULL),
2444          GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
2445         {ALL_STATES,
2446          GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
2447         {SBIT(GSM322_A6_NO_SIM),
2448          GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
2449         {ALL_STATES,
2450          GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
2451         {ALL_STATES,
2452          GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
2453         {ALL_STATES,
2454          GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
2455         {SBIT(GSM322_A1_TRYING_RPLMN),
2456          GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
2457         {SBIT(GSM322_A1_TRYING_RPLMN),
2458          GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
2459         {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
2460          GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
2461         {SBIT(GSM322_A2_ON_PLMN),
2462          GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
2463         {SBIT(GSM322_A2_ON_PLMN),
2464          GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search},
2465         {SBIT(GSM322_A2_ON_PLMN),
2466          GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
2467         {SBIT(GSM322_A2_ON_PLMN),
2468          GSM322_EVENT_USER_RESEL, gsm322_a_user_reselection},
2469         {SBIT(GSM322_A3_TRYING_PLMN),
2470          GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
2471         {SBIT(GSM322_A3_TRYING_PLMN),
2472          GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
2473         {SBIT(GSM322_A5_HPLMN_SEARCH),
2474          GSM322_EVENT_HPLMN_FOUND, gsm322_a_sel_first_plmn},
2475         {SBIT(GSM322_A5_HPLMN_SEARCH),
2476          GSM322_EVENT_HPLMN_NOT_FOUND, gsm322_a_go_on_plmn},
2477         {SBIT(GSM322_A4_WAIT_FOR_PLMN),
2478          GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
2479         {ALL_STATES,
2480          GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
2481         {ALL_STATES,
2482          GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
2483 };
2484
2485 #define PLMNASLLEN \
2486         (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
2487
2488 static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
2489 {
2490         struct gsm322_plmn *plmn = &ms->plmn;
2491         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2492         int msg_type = gm->msg_type;
2493         int rc;
2494         int i;
2495
2496         LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
2497                 "selection in state %s\n", ms->name, get_event_name(msg_type),
2498                 plmn_a_state_names[plmn->state]);
2499         /* find function for current state and message */
2500         for (i = 0; i < PLMNASLLEN; i++)
2501                 if ((msg_type == plmnastatelist[i].type)
2502                  && ((1 << plmn->state) & plmnastatelist[i].states))
2503                         break;
2504         if (i == PLMNASLLEN) {
2505                 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
2506                 return 0;
2507         }
2508
2509         rc = plmnastatelist[i].rout(ms, msg);
2510
2511         return rc;
2512 }
2513
2514 /* state machine for manual PLMN selection events */
2515 static struct plmnmstatelist {
2516         uint32_t        states;
2517         int             type;
2518         int             (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2519 } plmnmstatelist[] = {
2520         {SBIT(GSM322_M0_NULL),
2521          GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
2522         {ALL_STATES,
2523          GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
2524         {SBIT(GSM322_M5_NO_SIM),
2525          GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
2526         {ALL_STATES,
2527          GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
2528         {ALL_STATES,
2529          GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
2530         {SBIT(GSM322_M1_TRYING_RPLMN),
2531          GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
2532         {SBIT(GSM322_M1_TRYING_RPLMN),
2533          GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
2534         {SBIT(GSM322_M1_TRYING_RPLMN),
2535          GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
2536         {SBIT(GSM322_M2_ON_PLMN),
2537          GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
2538         {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
2539          SBIT(GSM322_M4_TRYING_PLMN),
2540          GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
2541         {SBIT(GSM322_M2_ON_PLMN),
2542          GSM322_EVENT_USER_RESEL, gsm322_m_display_plmns},
2543         {SBIT(GSM322_M3_NOT_ON_PLMN),
2544          GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
2545         {SBIT(GSM322_M3_NOT_ON_PLMN),
2546          GSM322_EVENT_CHOSE_PLMN, gsm322_m_choose_plmn},
2547         {SBIT(GSM322_M4_TRYING_PLMN),
2548          GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
2549         {SBIT(GSM322_M4_TRYING_PLMN),
2550          GSM322_EVENT_REG_FAILED, gsm322_m_go_not_on_plmn},
2551         {SBIT(GSM322_M4_TRYING_PLMN),
2552          GSM322_EVENT_NO_CELL_FOUND, gsm322_m_go_not_on_plmn},
2553         {ALL_STATES,
2554          GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
2555         {ALL_STATES,
2556          GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
2557 };
2558
2559 #define PLMNMSLLEN \
2560         (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
2561
2562 static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
2563 {
2564         struct gsm322_plmn *plmn = &ms->plmn;
2565         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2566         int msg_type = gm->msg_type;
2567         int rc;
2568         int i;
2569
2570         LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
2571                 "in state %s\n", ms->name, get_event_name(msg_type),
2572                 plmn_m_state_names[plmn->state]);
2573         /* find function for current state and message */
2574         for (i = 0; i < PLMNMSLLEN; i++)
2575                 if ((msg_type == plmnmstatelist[i].type)
2576                  && ((1 << plmn->state) & plmnmstatelist[i].states))
2577                         break;
2578         if (i == PLMNMSLLEN) {
2579                 LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
2580                 return 0;
2581         }
2582
2583         rc = plmnmstatelist[i].rout(ms, msg);
2584
2585         return rc;
2586 }
2587
2588 /* dequeue GSM 03.22 PLMN events */
2589 int gsm322_plmn_dequeue(struct osmocom_ms *ms)
2590 {
2591         struct gsm322_plmn *plmn = &ms->plmn;
2592         struct msgb *msg;
2593         int work = 0;
2594         
2595         while ((msg = msgb_dequeue(&plmn->event_queue))) {
2596                 /* send event to PLMN select process */
2597                 if (plmn->mode == PLMN_MODE_AUTO)
2598                         gsm322_a_event(ms, msg);
2599                 else
2600                         gsm322_m_event(ms, msg);
2601                 msgb_free(msg);
2602                 work = 1; /* work done */
2603         }
2604         
2605         return work;
2606 }
2607
2608 /* state machine for channel selection events */
2609 static struct cellselstatelist {
2610         uint32_t        states;
2611         int             type;
2612         int             (*rout) (struct osmocom_ms *ms, struct msgb *msg);
2613 } cellselstatelist[] = {
2614         {ALL_STATES,
2615          GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
2616         {ALL_STATES,
2617          GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
2618         {ALL_STATES,
2619          GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
2620         {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
2621          SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
2622          GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
2623         {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
2624          SBIT(GSM322_C4_NORMAL_CELL_RESEL),
2625          GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
2626         {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
2627          SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
2628          SBIT(GSM322_C0_NULL),
2629          GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
2630         {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
2631          SBIT(GSM322_C4_NORMAL_CELL_RESEL),
2632          GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
2633         {SBIT(GSM322_C3_CAMPED_NORMALLY),
2634          GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
2635         {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2636          GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
2637         {SBIT(GSM322_C3_CAMPED_NORMALLY),
2638          GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
2639         {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2640          GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
2641         {SBIT(GSM322_C3_CAMPED_NORMALLY),
2642          GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
2643         {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2644          GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
2645         {SBIT(GSM322_C7_CAMPED_ANY_CELL),
2646          GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
2647         {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
2648          SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
2649          SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
2650          SBIT(GSM322_C6_ANY_CELL_SEL),
2651          GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
2652         {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
2653          GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
2654         {SBIT(GSM322_C3_CAMPED_NORMALLY),
2655          GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search}
2656 };
2657
2658 #define CELLSELSLLEN \
2659         (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
2660
2661 int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
2662 {
2663         struct gsm322_cellsel *cs = &ms->cellsel;
2664         struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
2665         int msg_type = gm->msg_type;
2666         int rc;
2667         int i;
2668
2669         LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
2670                 "%s\n", ms->name, get_event_name(msg_type),
2671                 cs_state_names[cs->state]);
2672         /* find function for current state and message */
2673         for (i = 0; i < CELLSELSLLEN; i++)
2674                 if ((msg_type == cellselstatelist[i].type)
2675                  && ((1 << cs->state) & cellselstatelist[i].states))
2676                         break;
2677         if (i == CELLSELSLLEN) {
2678                 LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
2679                 return 0;
2680         }
2681
2682         rc = cellselstatelist[i].rout(ms, msg);
2683
2684         return rc;
2685 }
2686
2687 /* dequeue GSM 03.22 cell selection events */
2688 int gsm322_cs_dequeue(struct osmocom_ms *ms)
2689 {
2690         struct gsm322_cellsel *cs = &ms->cellsel;
2691         struct msgb *msg;
2692         int work = 0;
2693         
2694         while ((msg = msgb_dequeue(&cs->event_queue))) {
2695                 /* send event to cell selection process */
2696                 gsm322_c_event(ms, msg);
2697                 msgb_free(msg);
2698                 work = 1; /* work done */
2699         }
2700         
2701         return work;
2702 }
2703
2704 /*
2705  * dump lists
2706  */
2707
2708 int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
2709 {
2710         struct gsm322_plmn *plmn = &ms->plmn;
2711         struct gsm322_plmn_list *temp;
2712
2713         printf("MCC    |MNC    |allowed|rx-lev\n");
2714         printf("-------+-------+-------+-------\n");
2715         llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
2716                 printf("%03d    |%02d     |%s    |%d\n", temp->mcc, temp->mnc,
2717                         (temp->cause) ? "no ":"yes", temp->rxlev_db);
2718         }
2719
2720         return 0;
2721 }
2722
2723 int gsm322_dump_cs_list(struct osmocom_ms *ms)
2724 {
2725         struct gsm322_cellsel *cs = &ms->cellsel;
2726         int i, j;
2727
2728         printf("rx-lev |MCC    |MNC    |forb.LA|barred,0123456789abcdef|"
2729                 "min-db |max-pwr\n"
2730                 "-------+-------+-------+-------+-----------------------+"
2731                 "-------+-------\n");
2732         for (i = 0; i <= 1023; i++) {
2733                 if (!(cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
2734                         continue;
2735                 printf("%4d   |", cs->list[i].rxlev_db);
2736                 if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
2737                         struct gsm48_sysinfo *s = cs->list[i].sysinfo;
2738                         printf("%03d    |%02d     |", s->mcc, s->mnc);
2739                         if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
2740                                 printf("yes    |");
2741                         else
2742                                 printf("no     |");
2743                         if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
2744                                 printf("yes    ");
2745                         else
2746                                 printf("no     ");
2747                         for (j = 0; j < 16; j++) {
2748                                 if ((s->class_barr & (1 << j)))
2749                                         printf("*");
2750                                 else
2751                                         printf(" ");
2752                         }
2753                         printf("|%4d   |%4d\n", s->rxlev_acc_min_db,
2754                                 s->ms_txpwr_max_ccch);
2755                 } else
2756                         printf("n/a    |n/a    |       |                       "
2757                                 "|n/a    |n/a\n");
2758         }
2759
2760         return 0;
2761 }
2762
2763 int gsm322_dump_sim_plmn(struct osmocom_ms *ms)
2764 {
2765         struct gsm_subscriber *subscr = &ms->subscr;
2766         struct gsm_sub_plmn_list *temp;
2767
2768         printf("MCC    |MNC\n");
2769         printf("-------+-------\n");
2770         llist_for_each_entry(temp, &subscr->plmn_list, entry)
2771                 printf("%03d    |%02d\n", temp->mcc, temp->mnc);
2772
2773         return 0;
2774 }
2775
2776 int gsm322_dump_forbidden_plmn(struct osmocom_ms *ms)
2777 {
2778         struct gsm_subscriber *subscr = &ms->subscr;
2779         struct gsm_sub_plmn_na *temp;
2780
2781         printf("MCC    |MNC    |cause\n");
2782         printf("-------+-------+-------\n");
2783         llist_for_each_entry(temp, &subscr->plmn_na, entry)
2784                 printf("%03d    |%02d     |#%d\n", temp->mcc, temp->mnc,
2785                         temp->cause);
2786
2787         return 0;
2788 }
2789
2790 int gsm322_dump_forbidden_la(struct osmocom_ms *ms)
2791 {
2792         struct gsm322_plmn *plmn = &ms->plmn;
2793         struct gsm322_la_list *temp;
2794
2795         printf("MCC    |MNC    |LAC    |cause\n");
2796         printf("-------+-------+-------+-------\n");
2797         llist_for_each_entry(temp, &plmn->forbidden_la, entry)
2798                 printf("%03d    |%02d     |0x%04x |#%d\n", temp->mcc, temp->mnc,
2799                         temp->lac, temp->cause);
2800
2801         return 0;
2802 }
2803
2804 /*
2805  * initialization
2806  */
2807
2808 int gsm322_init(struct osmocom_ms *ms)
2809 {
2810         struct gsm322_plmn *plmn = &ms->plmn;
2811         struct gsm322_cellsel *cs = &ms->cellsel;
2812         OSMOCOM_FILE *fp;
2813         char suffix[] = ".ba";
2814         char filename[sizeof(ms->name) + strlen(suffix) + 1];
2815         int i;
2816         struct gsm322_ba_list *ba;
2817         uint8_t buf[4];
2818
2819         LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
2820         LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
2821
2822         memset(plmn, 0, sizeof(*plmn));
2823         memset(cs, 0, sizeof(*cs));
2824         plmn->ms = ms;
2825         cs->ms = ms;
2826
2827         /* set initial state */
2828         plmn->state = 0;
2829         cs->state = 0;
2830         plmn->mode = PLMN_MODE_AUTO;
2831
2832         /* init lists */
2833         INIT_LLIST_HEAD(&plmn->event_queue);
2834         INIT_LLIST_HEAD(&cs->event_queue);
2835         INIT_LLIST_HEAD(&plmn->sorted_plmn);
2836         INIT_LLIST_HEAD(&plmn->forbidden_la);
2837         INIT_LLIST_HEAD(&cs->ba_list);
2838
2839         /* set supported frequencies in cell selection list */
2840         for (i = 0; i <= 1023; i++)
2841                 if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
2842                         cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
2843
2844         /* read BA list */
2845         strcpy(filename, ms->name);
2846         strcat(filename, suffix);
2847         fp = osmocom_fopen(filename, "r");
2848         if (fp) {
2849                 int rc;
2850
2851                 while(!osmocom_feof(fp)) {
2852                         ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
2853                         if (!ba)
2854                                 return -ENOMEM;
2855                         rc = osmocom_fread(buf, 4, 1, fp);
2856                         if (!rc) {
2857                                 talloc_free(ba);
2858                                 break;
2859                         }
2860                         ba->mcc = (buf[0] << 8) | buf[1];
2861                         ba->mnc = (buf[2] << 8) | buf[3];
2862                         rc = osmocom_fread(ba->freq, sizeof(ba->freq), 1, fp);
2863                         if (!rc) {
2864                                 talloc_free(ba);
2865                                 break;
2866                         }
2867                         llist_add_tail(&ba->entry, &cs->ba_list);
2868                         LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%d "
2869                                 "mnc=%d  %s, %s)\n", ba->mcc, ba->mnc,
2870                                 gsm_get_mcc(ba->mcc),
2871                                 gsm_get_mnc(ba->mcc, ba->mnc));
2872                 }
2873                 osmocom_fclose(fp);
2874         } else
2875                 LOGP(DCS, LOGL_INFO, "No stored BA list\n");
2876
2877         register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
2878
2879         return 0;
2880 }
2881
2882 int gsm322_exit(struct osmocom_ms *ms)
2883 {
2884         struct gsm322_plmn *plmn = &ms->plmn;
2885         struct gsm322_cellsel *cs = &ms->cellsel;
2886         struct llist_head *lh, *lh2;
2887         struct msgb *msg;
2888         OSMOCOM_FILE *fp;
2889         char suffix[] = ".ba";
2890         char filename[sizeof(ms->name) + strlen(suffix) + 1];
2891         struct gsm322_ba_list *ba;
2892         uint8_t buf[4];
2893         int i;
2894
2895         LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
2896         LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
2897
2898         unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
2899
2900         /* stop cell selection process (if any) */
2901         new_c_state(cs, GSM322_C0_NULL);
2902
2903         /* stop timers */
2904         stop_cs_timer(cs);
2905         stop_plmn_timer(plmn);
2906
2907         /* flush sysinfo */
2908         for (i = 0; i <= 1023; i++) {
2909                 if (cs->list[i].sysinfo) {
2910                         talloc_free(cs->list[i].sysinfo);
2911                         cs->list[i].sysinfo = NULL;
2912                 }
2913                 cs->list[i].flags = 0;
2914         }
2915
2916         /* store BA list */
2917         strcpy(filename, ms->name);
2918         strcat(filename, suffix);
2919         fp = osmocom_fopen(filename, "w");
2920         if (fp) {
2921                 int rc;
2922
2923                 llist_for_each_entry(ba, &cs->ba_list, entry) {
2924                         buf[0] = ba->mcc >> 8;
2925                         buf[1] = ba->mcc & 0xff;
2926                         buf[2] = ba->mnc >> 8;
2927                         buf[3] = ba->mnc & 0xff;
2928                         rc = osmocom_fwrite(buf, 4, 1, fp);
2929                         rc = osmocom_fwrite(ba->freq, sizeof(ba->freq), 1, fp);
2930                         LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%d "
2931                                 "mnc=%d  %s, %s)\n", ba->mcc, ba->mnc,
2932                                 gsm_get_mcc(ba->mcc),
2933                                 gsm_get_mnc(ba->mcc, ba->mnc));
2934                 }
2935                 osmocom_fclose(fp);
2936         } else
2937                 LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
2938
2939         /* free lists */
2940         while ((msg = msgb_dequeue(&plmn->event_queue)))
2941                 msgb_free(msg);
2942         while ((msg = msgb_dequeue(&cs->event_queue)))
2943                 msgb_free(msg);
2944         llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
2945                 llist_del(lh);
2946                 talloc_free(lh);
2947         }
2948         llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
2949                 llist_del(lh);
2950                 talloc_free(lh);
2951         }
2952         llist_for_each_safe(lh, lh2, &cs->ba_list) {
2953                 llist_del(lh);
2954                 talloc_free(lh);
2955         }
2956         return 0;
2957 }
2958
2959