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