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