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