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