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