Many fixes in layer 3:
[osmocom-bb.git] / src / host / layer23 / src / gsm322.c
index 7e43f75..0d74e6b 100755 (executable)
 #include <osmocore/talloc.h>
 #include <osmocore/utils.h>
 #include <osmocore/gsm48.h>
+#include <osmocore/signal.h>
 
 #include <osmocom/logging.h>
 #include <osmocom/l1ctl.h>
 #include <osmocom/file.h>
 #include <osmocom/osmocom_data.h>
+#include <osmocom/networks.h>
 
 extern void *l23_ctx;
 
@@ -41,6 +43,20 @@ static void gsm322_cs_timeout(void *arg);
 static int gsm322_cs_select(struct osmocom_ms *ms, int any);
 static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg);
 
+
+#warning HACK to stay on one channel
+int l1ctl_tx_ccch_req_(struct osmocom_ms *ms, uint16_t arfcn)
+{
+       static int already = 0;
+
+       if (!already) {
+               already = 1;
+               return l1ctl_tx_ccch_req(ms, arfcn);
+       }
+       ms->cellsel.ccch_active = 1;
+       return 0;
+}
+
 /*
  * notes
  */
@@ -339,6 +355,21 @@ static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
        return ba_found;
 }
 
+/* search available PLMN */
+int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
+{
+       int i;
+
+       for (i = 0; i <= 1023; i++) {
+               if (cs->list[i].sysinfo->mcc == mcc
+                && cs->list[i].sysinfo->mnc == mnc)
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* del forbidden LA */
 /*
  * timer
  */
@@ -482,10 +513,12 @@ static void new_c_state(struct gsm322_cellsel *cs, int state)
        stop_cs_timer(cs);
 
        /* stop scanning of power measurement */
+       if (cs->powerscan) {
 #ifdef TODO
-       if (cs->powerscan)
                stop power scanning 
 #endif
+               cs->powerscan = 0;
+       }
 
        cs->state = state;
 }
@@ -542,8 +575,8 @@ static int gsm322_sort_list(struct osmocom_ms *ms)
                /* search if network has multiple cells */
                found = NULL;
                llist_for_each_entry(temp, &temp_list, entry) {
-                       if (temp->mcc == cs->list[i].mcc
-                        && temp->mnc == cs->list[i].mnc)
+                       if (temp->mcc == cs->list[i].sysinfo->mcc
+                        && temp->mnc == cs->list[i].sysinfo->mnc)
                                found = temp;
                                break;
                }
@@ -555,8 +588,8 @@ static int gsm322_sort_list(struct osmocom_ms *ms)
                        temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
                        if (!temp)
                                return -ENOMEM;
-                       temp->mcc = cs->list[i].mcc;
-                       temp->mnc = cs->list[i].mnc;
+                       temp->mcc = cs->list[i].sysinfo->mcc;
+                       temp->mnc = cs->list[i].sysinfo->mnc;
                        temp->rxlev_db = cs->list[i].rxlev_db;
                        llist_add_tail(&temp->entry, &temp_list);
                }
@@ -735,11 +768,12 @@ static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
        }
 
        /* select first PLMN in list */
-       plmn->mcc = cs->list[found].mcc;
-       plmn->mnc = cs->list[found].mnc;
+       plmn->mcc = cs->list[found].sysinfo->mcc;
+       plmn->mnc = cs->list[found].sysinfo->mnc;
 
-       LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%03d mnc=%02d)\n",
-               plmn->mcc, plmn->mnc);
+       LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%03d mnc=%02d  %s, %s)\n",
+               plmn->mcc, plmn->mnc,
+               gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
 
        /* indicate New PLMN */
        nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
@@ -791,8 +825,9 @@ static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
        }
 
        LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%03d "
-                       "mnc=%02d\n", plmn->plmn_curr, plmn_first->mcc,
-                       plmn_first->mnc);
+               "mnc=%02d  %s, %s)\n", plmn->plmn_curr, plmn_first->mcc,
+               plmn_first->mnc, gsm_get_mcc(plmn_first->mcc),
+               gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
 
        /* set current network */
        plmn->mcc = plmn_first->mcc;
@@ -845,8 +880,9 @@ static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
 
 
        LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%03d "
-                       "mnc=%02d\n", plmn->plmn_curr, plmn_next->mcc,
-                       plmn_next->mnc);
+               "mnc=%02d  %s, %s)\n", plmn->plmn_curr, plmn_next->mcc,
+               plmn_next->mnc,
+               gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
 
        /* set next network */
        plmn->mcc = plmn_next->mcc;
@@ -929,8 +965,12 @@ static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
 
        /* if PLMN in list */
        if (found >= 0) {
-               LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%03d mnc=%02d)\n",
-                       cs->list[found].mcc, cs->list[found].mnc);
+               LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%03d mnc=%02d  "
+                       "%s, %s)\n", cs->list[found].sysinfo->mcc,
+                       cs->list[found].sysinfo->mnc,
+                       gsm_get_mcc(cs->list[found].sysinfo->mcc),
+                       gsm_get_mnc(cs->list[found].sysinfo->mcc,
+                               cs->list[found].sysinfo->mnc));
                return gsm322_a_sel_first_plmn(ms, msg);
        }
 
@@ -963,8 +1003,10 @@ static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
                plmn->mcc = subscr->plmn_mcc;
                plmn->mnc = subscr->plmn_mnc;
 
-               LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%03d mnc=%02d)\n",
-                       plmn->mcc, plmn->mnc);
+               LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%03d mnc=%02d  "
+                       "%s, %s)\n", plmn->mcc, plmn->mnc,
+                       gsm_get_mcc(plmn->mcc),
+                       gsm_get_mnc(plmn->mcc, plmn->mnc));
 
                new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
 
@@ -991,6 +1033,12 @@ static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
        return 0;
 }
 
+static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
+{
+       LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
+       return 0;
+}
+
 /* SIM is removed */
 static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
 {
@@ -1043,12 +1091,18 @@ static int gsm322_a_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
 static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm322_plmn *plmn = &ms->plmn;
+       struct msgb *nmsg;
 
        /* restart state machine */
        gsm322_a_switch_off(ms, msg);
        plmn->mode = PLMN_MODE_MANUAL;
        gsm322_m_switch_on(ms, msg);
 
+       nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
+       if (!nmsg)
+               return -ENOMEM;
+       gsm48_mmevent_msg(ms, nmsg);
+
        return 0;
 }
 
@@ -1095,15 +1149,25 @@ static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
 
        /* if there is a registered PLMN */
        if (subscr->plmn_valid) {
+               struct msgb *nmsg;
+
                /* select the registered PLMN */
                plmn->mcc = subscr->plmn_mcc;
                plmn->mnc = subscr->plmn_mnc;
 
-               LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%03d mnc=%02d)\n",
-                       plmn->mcc, plmn->mnc);
+               LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%03d mnc=%02d  "
+                       "%s, %s)\n", plmn->mcc, plmn->mnc,
+                       gsm_get_mcc(plmn->mcc),
+                       gsm_get_mnc(plmn->mcc, plmn->mnc));
 
                new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
 
+               /* indicate New PLMN */
+               nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
+               if (!nmsg)
+                       return -ENOMEM;
+               gsm322_cs_sendmsg(ms, nmsg);
+
                return 0;
        }
 
@@ -1123,6 +1187,12 @@ static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
        return 0;
 }
 
+static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
+{
+       LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
+       return 0;
+}
+
 /* SIM is removed */
 static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
 {
@@ -1176,9 +1246,23 @@ static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
 static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm322_plmn *plmn = &ms->plmn;
+       struct gsm322_cellsel *cs = &ms->cellsel;
 
        new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
 
+       if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
+               struct msgb *nmsg;
+
+               LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
+                       "selected, so start selection.\n");
+
+               /* indicate New PLMN */
+               nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
+               if (!nmsg)
+                       return -ENOMEM;
+               gsm322_cs_sendmsg(ms, nmsg);
+       }
+       
        return 0;
 }
 
@@ -1187,16 +1271,24 @@ static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm322_plmn *plmn = &ms->plmn;
        struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
+       struct msgb *nmsg;
 
        /* use user selection */
        plmn->mcc = gm->mcc;
        plmn->mnc = gm->mnc;
 
-       LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%03d mnc=%02d)\n",
-               plmn->mcc, plmn->mnc);
+       LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%03d mnc=%02d  "
+               "%s, %s)\n", plmn->mcc, plmn->mnc,
+               gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
 
        new_m_state(plmn, GSM322_M4_TRYING_PLMN);
 
+       /* indicate New PLMN */
+       nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
+       if (!nmsg)
+               return -ENOMEM;
+       gsm322_cs_sendmsg(ms, nmsg);
+
        return 0;
 }
 
@@ -1204,12 +1296,18 @@ static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
 static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm322_plmn *plmn = &ms->plmn;
+       struct msgb *nmsg;
 
        /* restart state machine */
        gsm322_m_switch_off(ms, msg);
        plmn->mode = PLMN_MODE_AUTO;
        gsm322_a_switch_on(ms, msg);
 
+       nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
+       if (!nmsg)
+               return -ENOMEM;
+       gsm48_mmevent_msg(ms, nmsg);
+
        return 0;
 }
 
@@ -1236,6 +1334,7 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any)
 {
        struct gsm322_cellsel *cs = &ms->cellsel;
        struct gsm_subscriber *subscr = &ms->subscr;
+       struct gsm48_sysinfo *s;
        int i, found = -1, power = 0;
        uint8_t flags, mask;
        uint16_t acc_class;
@@ -1261,6 +1360,7 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any)
        /* loop through all scanned frequencies and select cell */
        for (i = 0; i <= 1023; i++) {
                cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
+               s = cs->list[i].sysinfo;
 
                /* channel has no informations for us */
                if ((cs->list[i].flags & mask) != flags) {
@@ -1269,10 +1369,10 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any)
 
                /* check C1 criteria not fullfilled */
                // TODO: C1 is also dependant on power class and max power
-               if (cs->list[i].rxlev_db < cs->list[i].min_db) {
+               if (cs->list[i].rxlev_db < s->rxlev_acc_min_db) {
                        LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
                                "not met. (rxlev=%d < min=%d)\n", i,
-                               cs->list[i].rxlev_db, cs->list[i].min_db);
+                               cs->list[i].rxlev_db, s->rxlev_acc_min_db);
                        continue;
                }
 
@@ -1289,18 +1389,16 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any)
                 && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
                        LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
                                "list of forbidden LAs. (mcc=%03d mnc=%02d "
-                               "lai=%04x)\n", i, cs->list[i].mcc,
-                               cs->list[i].mnc, cs->list[i].lac);
+                               "lai=%04x)\n", i, s->mcc, s->mnc, s->lac);
                        continue;
                }
 
                /* if we have no access to the cell and we don't override */
                if (!subscr->acc_barr 
-                && !(acc_class & (cs->list[i].class_barr ^ 0xffff))) {
+                && !(acc_class & (s->class_barr ^ 0xffff))) {
                        LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
                                "barred for out access. (access=%04x "
-                               "barred=%04x)\n", i, acc_class,
-                               cs->list[i].class_barr);
+                               "barred=%04x)\n", i, acc_class, s->class_barr);
                        continue;
                }
 
@@ -1308,19 +1406,19 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any)
                cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
 
                /* if we search a specific PLMN, but it does not match */
-               if (!any && (cs->mcc != cs->list[i].mcc
-                               || cs->mnc != cs->list[i].mnc)) {
+               if (!any && (cs->mcc != s->mcc
+                               || cs->mnc != s->mnc)) {
                        LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
                                "does not match target PLMN. (mcc=%03d "
-                               "mnc=%02d)\n", i, cs->list[i].mcc,
-                               cs->list[i].mnc);
+                               "mnc=%02d)\n", i, s->mcc, s->mnc);
                        continue;
                }
 
                LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%d "
-                       "mcc=%03d mnc=%02d lac=%04x)\n", i,
-                       cs->list[i].rxlev_db, cs->list[i].mcc, cs->list[i].mnc,
-                       cs->list[i].lac);
+                       "mcc=%03d mnc=%02d lac=%04x  %s, %s)\n", i,
+                       cs->list[i].rxlev_db, s->mcc, s->mnc,
+                       s->lac, gsm_get_mcc(s->mcc),
+                       gsm_get_mnc(s->mcc, s->mnc));
 
                /* find highest power cell */
                if (found < 0 || cs->list[i].rxlev_db > power) {
@@ -1329,6 +1427,8 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any)
                }
        }
 
+       gsm322_dump_sorted_plmn(ms);
+
        if (found >= 0)
                LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
 
@@ -1339,32 +1439,9 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int any)
 static int gsm322_cs_scan(struct osmocom_ms *ms)
 {
        struct gsm322_cellsel *cs = &ms->cellsel;
-       struct gsm_subscriber *subscr = &ms->subscr;
-       struct gsm48_sysinfo *s = &ms->sysinfo;
-       struct gsm48_rrlayer *rr = &ms->rrlayer;
-       int i;
+       int i, j;
        uint8_t mask, flags;
-       uint32_t max = 0, weight = cs->scan_state;
-
-       if (rr->state != GSM48_RR_ST_IDLE) {
-               LOGP(DCS, LOGL_FATAL, "This must only happen in IDLE mode, "
-                       "please fix!\n");
-               return -EINVAL;
-       }
-
-       /* special prositive case for HPLMN search */
-       if (cs->state == GSM322_HPLMN_SEARCH && s->mcc == subscr->mcc
-        && s->mnc == subscr->mnc) {
-               struct msgb *nmsg;
-
-               nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_FOUND);
-               LOGP(DCS, LOGL_INFO, "HPLMN cell available.\n");
-               if (!nmsg)
-                       return -ENOMEM;
-               gsm322_plmn_sendmsg(ms, nmsg);
-
-               return 0;
-       }
+       uint32_t weight = 0, test = cs->scan_state;
 
        /* search for strongest unscanned cell */
        mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
@@ -1373,20 +1450,40 @@ static int gsm322_cs_scan(struct osmocom_ms *ms)
                mask |= GSM322_CS_FLAG_BA;
        flags = mask; /* all masked flags are requied */
        for (i = 0; i <= 1023; i++) {
+               /* skip if band has enough frequencies scanned (3.2.1) */
+               for (j = 0; gsm_sup_smax[j].max; j++) {
+                       if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
+                               if (gsm_sup_smax[j].start >= i
+                                && gsm_sup_smax[j].end <= i)
+                                       break;
+                       } else {
+                               if (gsm_sup_smax[j].end <= i
+                                || gsm_sup_smax[j].start >= i)
+                                       break;
+                       }
+               }
+               if (gsm_sup_smax[j].max) {
+                       if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
+                               continue;
+               }
+               /* search for unscanned frequency */
                if ((cs->list[i].flags & mask) == flags) {
                        /* weight depends on the power level
                         * if it is the same, it depends on arfcn
                         */
-                       weight = cs->list[i].rxlev_db + 128;
-                       weight = (weight << 16) | i;
-                       if (weight >= cs->scan_state)
+                       test = cs->list[i].rxlev_db + 128;
+                       test = (test << 16) | i;
+                       if (test >= cs->scan_state)
                                continue;
-                       if (weight > max)
-                               max = weight;
+                       if (test > weight)
+                               weight = test;
                }
        }
        cs->scan_state = weight;
 
+       if (!weight)
+               gsm322_dump_cs_list(ms);
+
        /* special negative case for HPLMN search */
        if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
                struct msgb *nmsg;
@@ -1398,7 +1495,8 @@ static int gsm322_cs_scan(struct osmocom_ms *ms)
                gsm322_plmn_sendmsg(ms, nmsg);
 
                /* re-tune back to current VPLMN */
-               l1ctl_tx_ccch_req(ms, cs->arfcn);
+               cs->ccch_active = 0;
+               l1ctl_tx_ccch_req_(ms, cs->arfcn);
 
                return 0;
        }
@@ -1420,22 +1518,65 @@ static int gsm322_cs_scan(struct osmocom_ms *ms)
 
                /* if found */
                if (found >= 0) {
+                       struct gsm322_plmn *plmn = &ms->plmn;
+
                        LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
                        /* tune */
                        cs->arfcn = found;
-                       l1ctl_tx_ccch_req(ms, cs->arfcn);
-       
-                       /* Clear system information. */
-                       gsm48_sysinfo_init(ms);
+                       cs->si = cs->list[cs->arfcn].sysinfo;
+                       cs->ccch_active = 0;
+                       l1ctl_tx_ccch_req_(ms, cs->arfcn);
+
+                       /* selected PLMN (manual) or any PLMN (auto) */
+                       switch (plmn->mode) {
+                       case PLMN_MODE_AUTO:
+                               if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
+                                       /* PLMN becomes available */
+                                       nmsg = gsm322_msgb_alloc(
+                                               GSM322_EVENT_PLMN_AVAIL);
+                                       if (!nmsg)
+                                               return -ENOMEM;
+                                       gsm322_plmn_sendmsg(ms, nmsg);
+                               }
+                               break;
+                       case PLMN_MODE_MANUAL:
+                               if (plmn->state == GSM322_M3_NOT_ON_PLMN
+                                 && gsm322_is_plmn_avail(cs, plmn->mcc,
+                                       plmn->mnc)) {
+                                       /* PLMN becomes available */
+                                       nmsg = gsm322_msgb_alloc(
+                                               GSM322_EVENT_PLMN_AVAIL);
+                                       if (!nmsg)
+                                               return -ENOMEM;
+                                       gsm322_plmn_sendmsg(ms, nmsg);
+                               }
+                               break;
+                       }
+
+                       /* set selected cell */
+                       cs->selected = 1;
+                       cs->sel_arfcn = cs->arfcn;
+                       memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
+                       cs->sel_mcc = cs->si->mcc;
+                       cs->sel_mnc = cs->si->mnc;
+                       cs->sel_lac = cs->si->lac;
 
+                       /* tell CS process about available cell */
                        nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
+                       LOGP(DCS, LOGL_INFO, "Cell available.\n");
                } else {
+                       /* unset selected cell */
+                       cs->selected = 0;
+                       memset(&cs->sel_si, 0, sizeof(cs->sel_si));
+                       cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
+
+                       /* tell CS process about no cell available */
                        nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
                        LOGP(DCS, LOGL_INFO, "No cell available.\n");
                }
                if (!nmsg)
                        return -ENOMEM;
-               gsm322_plmn_sendmsg(ms, nmsg);
+               gsm322_cs_sendmsg(ms, nmsg);
 
                return 0;
        }
@@ -1447,14 +1588,19 @@ static int gsm322_cs_scan(struct osmocom_ms *ms)
        /* Tune to frequency for a while, to receive broadcasts. */
        cs->arfcn = weight & 1023;
        LOGP(DCS, LOGL_INFO, "Scanning frequency %d.\n", cs->arfcn);
-       l1ctl_tx_ccch_req(ms, cs->arfcn);
-
-       /* Clear system information. */
-       gsm48_sysinfo_init(ms);
-
-       /* set timer for reading BCCH */
-       start_cs_timer(cs, 4, 0); // TODO: timer depends on BCCH config
-
+       cs->ccch_active = 0;
+       l1ctl_tx_ccch_req_(ms, cs->arfcn);
+
+       /* Allocate system information. */
+       cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
+                                       struct gsm48_sysinfo);
+       if (!cs->list[cs->arfcn].sysinfo)
+               exit(-ENOMEM);
+       cs->si = cs->list[cs->arfcn].sysinfo;
+
+       /* increase scan counter for each maximum scan range */
+       if (gsm_sup_smax[j].max)
+               gsm_sup_smax[j].temp++;
        return 0;
 }
 
@@ -1462,8 +1608,9 @@ static int gsm322_cs_scan(struct osmocom_ms *ms)
 static int gsm322_cs_store(struct osmocom_ms *ms)
 {
        struct gsm322_cellsel *cs = &ms->cellsel;
-       struct gsm48_sysinfo *s = &ms->sysinfo;
+       struct gsm_subscriber *subscr = &ms->subscr;
        int i = cs->scan_state & 1023;
+       struct gsm48_sysinfo *s = cs->list[i].sysinfo;
 
        if (cs->state != GSM322_C2_STORED_CELL_SEL
         && cs->state != GSM322_C1_NORMAL_CELL_SEL
@@ -1483,15 +1630,20 @@ static int gsm322_cs_store(struct osmocom_ms *ms)
                cs->list[i].flags |= GSM322_CS_FLAG_BARRED;
        else
                cs->list[i].flags &= ~GSM322_CS_FLAG_BARRED;
+
+#if 0
        cs->list[i].min_db = s->rxlev_acc_min_db;
        cs->list[i].class_barr = s->class_barr;
        cs->list[i].max_pwr = s->ms_txpwr_max_ccch;
+#endif
 
        /* store selected network */
        if (s->mcc) {
+#if 0
                cs->list[i].mcc = s->mcc;
                cs->list[i].mnc = s->mnc;
                cs->list[i].lac = s->lac;
+#endif
 
                if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
                        cs->list[i].flags |= GSM322_CS_FLAG_FORBIDD;
@@ -1501,7 +1653,21 @@ static int gsm322_cs_store(struct osmocom_ms *ms)
 
        LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev=%d "
                "mcc=%03d mnc=%02d lac=%04x)\n", i, cs->list[i].rxlev_db,
-               cs->list[i].mcc, cs->list[i].mnc, cs->list[i].lac);
+               s->mcc, s->mnc, s->lac);
+
+       /* special prositive case for HPLMN search */
+       if (cs->state == GSM322_HPLMN_SEARCH && s->mcc == subscr->mcc
+        && s->mnc == subscr->mnc) {
+               struct msgb *nmsg;
+
+               nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_FOUND);
+               LOGP(DCS, LOGL_INFO, "HPLMN cell available.\n");
+               if (!nmsg)
+                       return -ENOMEM;
+               gsm322_plmn_sendmsg(ms, nmsg);
+
+               return 0;
+       }
 
        /* tune to next cell */
        return gsm322_cs_scan(ms);
@@ -1511,7 +1677,7 @@ static int gsm322_cs_store(struct osmocom_ms *ms)
 struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
 {
        struct gsm322_cellsel *cs = &ms->cellsel;
-       struct gsm48_sysinfo *s = &ms->sysinfo;
+       struct gsm48_sysinfo *s = cs->si;
        struct gsm322_ba_list *ba = NULL;
        int i;
        uint8_t freq[128];
@@ -1539,8 +1705,10 @@ struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
                                freq[i >> 3] |= (1 << (i & 7));
                }
                if (!!memcmp(freq, ba->freq, sizeof(freq))) {
-                       LOGP(DCS, LOGL_INFO, "New BA list (mcc=%d mnc=%d).\n",
-                               ba->mcc, ba->mnc);
+                       LOGP(DCS, LOGL_INFO, "New BA list (mcc=%d mnc=%d  "
+                               "%s, %s).\n", ba->mcc, ba->mnc,
+                               gsm_get_mcc(ba->mcc),
+                               gsm_get_mnc(ba->mcc, ba->mnc));
                        memcpy(ba->freq, freq, sizeof(freq));
                }
        }
@@ -1548,15 +1716,83 @@ struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
        return ba;
 }
 
+/* store BA whenever a system informations changes */
+static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
+       struct gsm48_sysinfo *s)
+{
+       struct gsm322_ba_list *ba;
+       int i;
+       uint8_t freq[128];
+
+       /* find or create ba list */
+       ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
+       if (!ba) {
+               ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
+               if (!ba)
+                       return -ENOMEM;
+               ba->mcc = s->mcc;
+               ba->mnc = s->mnc;
+               llist_add_tail(&ba->entry, &cs->ba_list);
+       }
+       /* update ba list */
+       memset(freq, 0, sizeof(freq));
+       freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
+       for (i = 0; i <= 1023; i++) {
+               if ((s->freq[i].mask &
+                       (FREQ_TYPE_SERV | FREQ_TYPE_NCELL)))
+                       freq[i >> 3] |= (1 << (i & 7));
+       }
+       if (!!memcmp(freq, ba->freq, sizeof(freq))) {
+               LOGP(DCS, LOGL_INFO, "New BA list (mcc=%d mnc=%d  "
+                       "%s, %s).\n", ba->mcc, ba->mnc,
+                       gsm_get_mcc(ba->mcc),
+                       gsm_get_mnc(ba->mcc, ba->mnc));
+               memcpy(ba->freq, freq, sizeof(freq));
+       }
+
+       return 0;
+}
+
 /* process system information during camping on a cell */
 static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm322_cellsel *cs = &ms->cellsel;
-       struct gsm48_sysinfo *s = &ms->sysinfo;
+       struct gsm48_sysinfo *s = cs->si;
        struct gsm_subscriber *subscr = &ms->subscr;
        struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
        struct msgb *nmsg;
 
+       /* Store BA if we have full system info about cells and neigbor cells.
+        * Depending on the extended bit in the channel description,
+        * we require more or less system informations about neighbor cells
+        */
+       if (s->mcc
+        && s->mnc
+        && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
+         || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
+         || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
+         || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
+        && s->si1
+        && s->si2
+        && (!s->nb_ext_ind_si2 
+         || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
+         || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
+               && s->nb_ext_ind_si2bis)))
+               gsm322_store_ba_list(cs, s);
+
+       /* update sel_si, if all relevant system informations received */
+       if (s->si1 && s->si2 && s->si3
+        && (!s->nb_ext_ind_si2
+         || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
+         || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
+               && s->nb_ext_ind_si2bis))) {
+               if (cs->selected) {
+                       LOGP(DCS, LOGL_INFO, "Selected sysinfo is updated.\n");
+                       memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
+               }
+       }
+
+       /* check for barred cell */
        if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
                /* check if cell becomes barred */
                if (!subscr->acc_barr && s->cell_barr) {
@@ -1574,7 +1810,7 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
                }
                /* check if cell access becomes barred */
                if (!((subscr->acc_class & 0xfbff)
-                       & (cs->list[cs->arfcn].class_barr ^ 0xffff))) {
+                       & (s->class_barr ^ 0xffff))) {
                        LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
                        goto trigger_resel;
                }
@@ -1587,18 +1823,8 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
 static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm322_cellsel *cs = &ms->cellsel;
-       struct gsm48_rrlayer *rr = &ms->rrlayer;
-       struct gsm48_sysinfo *s = &ms->sysinfo;
+       struct gsm48_sysinfo *s = cs->si;
        struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
-       struct gsm322_ba_list *ba;
-       int i;
-       uint8_t freq[128];
-
-       if (rr->state != GSM48_RR_ST_IDLE) {
-               LOGP(DCS, LOGL_FATAL, "This must only happen in IDLE mode, "
-                       "please fix!\n");
-               return -EINVAL;
-       }
 
        /* no sysinfo if we are not done with power scan */
        if (cs->powerscan) {
@@ -1621,31 +1847,8 @@ static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
         && (!s->nb_ext_ind_si2 
          || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
          || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
-               && s->nb_ext_ind_si2bis))) {
-               /* find or create ba list */
-               ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
-               if (!ba) {
-                       ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
-                       if (!ba)
-                               return -ENOMEM;
-                       ba->mcc = s->mcc;
-                       ba->mnc = s->mnc;
-                       llist_add_tail(&ba->entry, &cs->ba_list);
-               }
-               /* update ba list */
-               memset(freq, 0, sizeof(freq));
-               freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
-               for (i = 0; i <= 1023; i++) {
-                       if ((s->freq[i].mask &
-                               (FREQ_TYPE_SERV | FREQ_TYPE_NCELL)))
-                               freq[i >> 3] |= (1 << (i & 7));
-               }
-               if (!!memcmp(freq, ba->freq, sizeof(freq))) {
-                       LOGP(DCS, LOGL_INFO, "New BA list (mcc=%d mnc=%d).\n",
-                               ba->mcc, ba->mnc);
-                       memcpy(ba->freq, freq, sizeof(freq));
-               }
-       }
+               && s->nb_ext_ind_si2bis)))
+               gsm322_store_ba_list(cs, s);
 
        /* all relevant system informations received */
        if (s->si1 && s->si2 && s->si3
@@ -1656,6 +1859,8 @@ static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
                /* stop timer */
                stop_cs_timer(cs);
 
+               gsm48_sysinfo_dump(ms, s);
+
                /* store sysinfo and continue scan */
                return gsm322_cs_store(ms);
        }
@@ -1676,6 +1881,10 @@ static void gsm322_cs_timeout(void *arg)
 
        /* remove system information */
        cs->list[i].flags &= ~GSM322_CS_FLAG_SYSINFO; 
+       if (cs->list[i].sysinfo) {
+               talloc_free(cs->list[i].sysinfo);
+               cs->list[i].sysinfo = NULL;
+       }
 
        /* tune to next cell */
        gsm322_cs_scan(ms);
@@ -1691,25 +1900,20 @@ static void gsm322_cs_timeout(void *arg)
 static int gsm322_cs_powerscan(struct osmocom_ms *ms)
 {
        struct gsm322_cellsel *cs = &ms->cellsel;
-       struct gsm48_rrlayer *rr = &ms->rrlayer;
        int i, s = -1, e;
        uint8_t mask, flags;
 
-       if (rr->state != GSM48_RR_ST_IDLE) {
-               LOGP(DCS, LOGL_FATAL, "This must only happen in IDLE mode, "
-                       "please fix!\n");
-               return -EINVAL;
-       }
-
        again:
 
        /* search for first frequency to scan */
        mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
        flags = GSM322_CS_FLAG_SUPPORT;
        if (cs->state == GSM322_C2_STORED_CELL_SEL) {
+               LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA list.\n");
                mask |= GSM322_CS_FLAG_BA;
                flags |= GSM322_CS_FLAG_BA;
-       }
+       } else
+               LOGP(DCS, LOGL_FATAL, "Scanning power for all frequencies.\n");
        for (i = 0; i <= 1023; i++) {
                if ((cs->list[i].flags & mask) == flags) {
                        s = e = i;
@@ -1717,6 +1921,11 @@ static int gsm322_cs_powerscan(struct osmocom_ms *ms)
                }
        }
 
+#warning testing
+cs->list[10].rxlev_db = -50;
+cs->list[10].flags |= GSM322_CS_FLAG_POWER;
+cs->list[10].flags |= GSM322_CS_FLAG_SIGNAL;
+s = -1;
        /* if there is no more frequency, we can tune to that cell */
        if (s < 0) {
                int found = 0;
@@ -1741,6 +1950,11 @@ static int gsm322_cs_powerscan(struct osmocom_ms *ms)
                                                ~(GSM322_CS_FLAG_POWER
                                                | GSM322_CS_FLAG_SIGNAL
                                                | GSM322_CS_FLAG_SYSINFO);
+                                       if (cs->list[i].sysinfo) {
+                                               talloc_free(
+                                                       cs->list[i].sysinfo);
+                                               cs->list[i].sysinfo = NULL;
+                                       }
                                }
                                goto again;
                        }
@@ -1759,6 +1973,9 @@ static int gsm322_cs_powerscan(struct osmocom_ms *ms)
                }
                LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
                cs->scan_state = 0xffffffff; /* higher than high */
+               /* clear counter of scanned frequencies of each range */
+               for (i = 0; gsm_sup_smax[i].max; i++)
+                       gsm_sup_smax[i].temp = 0;
                return gsm322_cs_scan(ms);
        }
 
@@ -1773,30 +1990,69 @@ static int gsm322_cs_powerscan(struct osmocom_ms *ms)
 
        LOGP(DCS, LOGL_INFO, "Scanning frequecies. (%d..%d)\n", s, e);
 
-#ifdef TODO
-       start scan on radio interface
-
+       /* start scan on radio interface */
        cs->powerscan = 1;
-
-       also stop on state change
-
-#else
-       printf("scan not supported... exitting\n");
-       exit(-1);
-#endif
+       return l1ctl_tx_pm_req_range(ms, s, e);
 }
 
-#ifdef TODO
+static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
+                    void *handler_data, void *signal_data)
 {
+       struct osmocom_ms *ms;
+       struct gsm322_cellsel *cs;
+       struct osmobb_meas_res *mr;
+       int i;
+       int8_t rxlev_db;
 
-       LOGP(DCS, LOGL_INFO, "Found frequency %d.\n", i);
+       if (subsys != SS_L1CTL)
+               return 0;
 
-       /* set power scan flag */
-       cs->list[i].flags |= GSM322_CS_FLAG_POWER;
-       if (...)
-               cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
+       switch (signal) {
+       case S_L1CTL_PM_RES:
+               mr = signal_data;
+               ms = mr->ms;
+               cs = &ms->cellsel;
+               if (!cs->powerscan)
+                       return -EINVAL;
+               i = mr->band_arfcn & 1023;
+               rxlev_db = mr->rx_lev - 110;
+               cs->list[i].rxlev_db = rxlev_db;
+               cs->list[i].flags |= GSM322_CS_FLAG_POWER;
+               if (rxlev_db >= ms->support.min_rxlev_db) {
+                       cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
+                       LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
+                               "rxlev %d)\n", i, cs->list[i].rxlev_db);
+               }
+               break;
+       case S_L1CTL_PM_DONE:
+               LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
+               ms = signal_data;
+               cs = &ms->cellsel;
+               if (!cs->powerscan)
+                       return -EINVAL;
+               gsm322_cs_powerscan(ms);
+               break;
+       case S_L1CTL_CCCH_RESP:
+               ms = signal_data;
+               cs = &ms->cellsel;
+               if (!cs->ccch_active) {
+                       LOGP(DCS, LOGL_INFO, "Channel activated.\n");
+                       cs->ccch_active = 1;
+                       /* set timer for reading BCCH */
+                       if (cs->state == GSM322_C2_STORED_CELL_SEL
+                        || cs->state == GSM322_C1_NORMAL_CELL_SEL
+                        || cs->state == GSM322_C6_ANY_CELL_SEL
+                        || cs->state == GSM322_C4_NORMAL_CELL_RESEL
+                        || cs->state == GSM322_C8_ANY_CELL_RESEL
+                        || cs->state == GSM322_C5_CHOOSE_CELL
+                        || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
+                               start_cs_timer(cs, 8, 0);
+                                       // TODO: timer depends on BCCH config
+               }
+               break;
+       }
+       return 0;
 }
-#endif
 
 /*
  * handler for cell selection process
@@ -1813,12 +2069,22 @@ static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
        /* mark all frequencies except our own BA to be scanned */
        for (i = 0; i <= 1023; i++) {
                if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
-                && !(cs->list[i].flags & GSM322_CS_FLAG_BA))
+                && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
                        cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
                                                | GSM322_CS_FLAG_SIGNAL
                                                | GSM322_CS_FLAG_SYSINFO);
+                       if (cs->list[i].sysinfo) {
+                               talloc_free(cs->list[i].sysinfo);
+                               cs->list[i].sysinfo = NULL;
+                       }
+               }
        }
 
+       /* unset selected cell */
+       cs->selected = 0;
+       memset(&cs->sel_si, 0, sizeof(cs->sel_si));
+       cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
+
        /* start power scan */
        return gsm322_cs_powerscan(ms);
 }
@@ -1839,15 +2105,11 @@ static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list
                        cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
        }
 
-#ifdef TODO
-       remove this:
-#else
-       /* use hacked frequency */
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_POWER;
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_SIGNAL;
-       cs->list[ms->test_arfcn].rxlev_db = -40;
-#endif
-       
+       /* unset selected cell */
+       cs->selected = 0;
+       memset(&cs->sel_si, 0, sizeof(cs->sel_si));
+       cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
+
        /* start power scan */
        return gsm322_cs_powerscan(ms);
 }
@@ -1860,22 +2122,23 @@ static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
 
        /* except for stored cell selection state, we weed to rescan ?? */
        if (cs->state != GSM322_C2_STORED_CELL_SEL) {
-               for (i = 0; i <= 1023; i++)
+               for (i = 0; i <= 1023; i++) {
                        cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
                                                | GSM322_CS_FLAG_SIGNAL
                                                | GSM322_CS_FLAG_SYSINFO);
+                       if (cs->list[i].sysinfo) {
+                               talloc_free(cs->list[i].sysinfo);
+                               cs->list[i].sysinfo = NULL;
+                       }
+               }
        }
 
        new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
 
-#ifdef TODO
-       remove this:
-#else
-       /* use hacked frequency */
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_POWER;
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_SIGNAL;
-       cs->list[ms->test_arfcn].rxlev_db = -40;
-#endif
+       /* unset selected cell */
+       cs->selected = 0;
+       memset(&cs->sel_si, 0, sizeof(cs->sel_si));
+       cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
 
        /* start power scan */
        return gsm322_cs_powerscan(ms);
@@ -1889,24 +2152,33 @@ static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
 
        /* in case we already tried any cell selection, power scan again */
        if (cs->state == GSM322_C6_ANY_CELL_SEL) {
-               for (i = 0; i <= 1023; i++)
+               struct msgb *nmsg;
+
+               /* tell that we have no cell found */
+               nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
+               if (!nmsg)
+                       return -ENOMEM;
+               gsm48_mmevent_msg(ms, nmsg);
+
+               for (i = 0; i <= 1023; i++) {
                        cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
                                                | GSM322_CS_FLAG_SIGNAL
                                                | GSM322_CS_FLAG_SYSINFO);
+                       if (cs->list[i].sysinfo) {
+                               talloc_free(cs->list[i].sysinfo);
+                               cs->list[i].sysinfo = NULL;
+                       }
+               }
        } else { 
                new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
        }
 
        cs->mcc = cs->mnc = 0;
 
-#ifdef TODO
-       remove this:
-#else
-       /* use hacked frequency */
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_POWER;
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_SIGNAL;
-       cs->list[ms->test_arfcn].rxlev_db = -40;
-#endif
+       /* unset selected cell */
+       cs->selected = 0;
+       memset(&cs->sel_si, 0, sizeof(cs->sel_si));
+       cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
 
        /* start power scan */
        return gsm322_cs_powerscan(ms);
@@ -1922,15 +2194,6 @@ static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
        /* NOTE: We keep our scan info we have so far.
         * This may cause a skip in power scan. */
 
-#ifdef TODO
-       remove this:
-#else
-       /* use hacked frequency */
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_POWER;
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_SIGNAL;
-       cs->list[ms->test_arfcn].rxlev_db = -40;
-#endif
-
        /* start power scan */
        return gsm322_cs_powerscan(ms);
 }
@@ -1945,15 +2208,6 @@ static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
        /* NOTE: We keep our scan info we have so far.
         * This may cause a skip in power scan. */
 
-#ifdef TODO
-       remove this:
-#else
-       /* use hacked frequency */
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_POWER;
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_SIGNAL;
-       cs->list[ms->test_arfcn].rxlev_db = -40;
-#endif
-
        /* start power scan */
        return gsm322_cs_powerscan(ms);
 }
@@ -1992,6 +2246,8 @@ static int gsm322_cs_choose(struct osmocom_ms *ms)
        int i;
 
 #ifdef TODO
+what we have todo here:
+if we return from dedicated mode and we have a ba range, we can use that for cell reselection
        if (message->ranges)
                ba = gsm322_cs_ba_range(ms, message->range, message->ranges);
        else {
@@ -1999,6 +2255,9 @@ static int gsm322_cs_choose(struct osmocom_ms *ms)
 #endif
                /* get and update BA of last received sysinfo 5* */
                ba = gsm322_cs_sysinfo_sacch(ms);
+               if (!ba)
+                       ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
+                               cs->sel_si.mnc);
 #ifdef TODO
        }
 #endif
@@ -2006,7 +2265,7 @@ static int gsm322_cs_choose(struct osmocom_ms *ms)
        if (!ba) {
                struct msgb *nmsg;
 
-               LOGP(DCS, LOGL_INFO, "No BA list.\n");
+               LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
 
                /* tell CS to start over */
                nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
@@ -2026,15 +2285,16 @@ static int gsm322_cs_choose(struct osmocom_ms *ms)
                cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
                                        | GSM322_CS_FLAG_SIGNAL
                                        | GSM322_CS_FLAG_SYSINFO);
+               if (cs->list[i].sysinfo) {
+                       talloc_free(cs->list[i].sysinfo);
+                       cs->list[i].sysinfo = NULL;
+               }
        }
-#ifdef TODO
-       remove this:
-#else
-       /* use hacked frequency */
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_POWER;
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_SIGNAL;
-       cs->list[ms->test_arfcn].rxlev_db = -40;
-#endif
+
+       /* unset selected cell */
+       cs->selected = 0;
+       memset(&cs->sel_si, 0, sizeof(cs->sel_si));
+       cs->sel_mcc = cs->sel_mnc = cs->sel_lac = 0;
 
        /* start power scan */
        return gsm322_cs_powerscan(ms);
@@ -2088,7 +2348,8 @@ static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm322_cellsel *cs = &ms->cellsel;
        struct msgb *nmsg;
 
-       nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NEW_LAI);
+       /* tell that we have selected a (new) cell */
+       nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
        if (!nmsg)
                return -ENOMEM;
        gsm48_mmevent_msg(ms, nmsg);
@@ -2102,6 +2363,13 @@ static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
 static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm322_cellsel *cs = &ms->cellsel;
+       struct msgb *nmsg;
+
+       /* tell that we have selected a (new) cell */
+       nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
+       if (!nmsg)
+               return -ENOMEM;
+       gsm48_mmevent_msg(ms, nmsg);
 
        new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
 
@@ -2113,11 +2381,16 @@ static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm322_cellsel *cs = &ms->cellsel;
 
-       /* stop camping process */
+       /* check for error */
+       if (!cs->selected)
+               return -EINVAL;
+       cs->arfcn = cs->sel_arfcn;
 
        /* be sure to go to current camping frequency on return */
        LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
-       l1ctl_tx_ccch_req(ms, cs->arfcn);
+       cs->ccch_active = 0;
+       l1ctl_tx_ccch_req_(ms, cs->arfcn);
+       cs->si = cs->list[cs->arfcn].sysinfo;
 
        return 0;
 }
@@ -2126,11 +2399,16 @@ static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm322_cellsel *cs = &ms->cellsel;
 
-       /* stop camping process */
+       /* check for error */
+       if (!cs->selected)
+               return -EINVAL;
+       cs->arfcn = cs->sel_arfcn;
 
        /* be sure to go to current camping frequency on return */
        LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
-       l1ctl_tx_ccch_req(ms, cs->arfcn);
+       cs->ccch_active = 0;
+       l1ctl_tx_ccch_req_(ms, cs->arfcn);
+       cs->si = cs->list[cs->arfcn].sysinfo;
 
        return 0;
 }
@@ -2141,9 +2419,11 @@ static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm_subscriber *subscr = &ms->subscr;
 
        /* if no SIM is is MS */
-       if (!subscr->sim_valid)
+       if (!subscr->sim_valid) {
                LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
                return gsm322_c_any_cell_sel(ms, msg);
+       LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
+       }
 
        /* stay in NULL state until PLMN is selected */
 
@@ -2166,6 +2446,8 @@ static struct plmnastatelist {
         GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
        {SBIT(GSM322_A6_NO_SIM),
         GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
+       {ALL_STATES,
+        GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
        {ALL_STATES,
         GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
        {ALL_STATES,
@@ -2241,6 +2523,8 @@ static struct plmnmstatelist {
         GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
        {SBIT(GSM322_M5_NO_SIM),
         GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
+       {ALL_STATES,
+        GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
        {ALL_STATES,
         GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
        {SBIT(GSM322_M1_TRYING_RPLMN),
@@ -2340,7 +2624,8 @@ static struct cellselstatelist {
         SBIT(GSM322_C4_NORMAL_CELL_RESEL),
         GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
        {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
-        SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL),
+        SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
+        SBIT(GSM322_C0_NULL),
         GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
        {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
         SBIT(GSM322_C4_NORMAL_CELL_RESEL),
@@ -2361,9 +2646,10 @@ static struct cellselstatelist {
         GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
        {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
         SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
-        SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL),
+        SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
+        SBIT(GSM322_C6_ANY_CELL_SEL),
         GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
-       {SBIT(GSM322_C3_CAMPED_NORMALLY),
+       {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
         GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
        {SBIT(GSM322_C3_CAMPED_NORMALLY),
         GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search}
@@ -2389,7 +2675,7 @@ int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
                 && ((1 << cs->state) & cellselstatelist[i].states))
                        break;
        if (i == CELLSELSLLEN) {
-               LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
+               LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
                return 0;
        }
 
@@ -2448,8 +2734,8 @@ int gsm322_dump_cs_list(struct osmocom_ms *ms)
                        continue;
                printf("%4d   |", cs->list[i].rxlev_db);
                if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
-                       printf("%03d    |%02d     |", cs->list[i].mcc,
-                               cs->list[i].mnc);
+                       struct gsm48_sysinfo *s = cs->list[i].sysinfo;
+                       printf("%03d    |%02d     |", s->mcc, s->mnc);
                        if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
                                printf("yes    |");
                        else
@@ -2459,16 +2745,16 @@ int gsm322_dump_cs_list(struct osmocom_ms *ms)
                        else
                                printf("no     ");
                        for (j = 0; j < 16; j++) {
-                               if ((cs->list[i].class_barr & (1 << j)))
+                               if ((s->class_barr & (1 << j)))
                                        printf("*");
                                else
                                        printf(" ");
                        }
-                       printf("|%4d   |%4d", cs->list[i].min_db,
-                               cs->list[i].max_pwr);
+                       printf("|%4d   |%4d\n", s->rxlev_acc_min_db,
+                               s->ms_txpwr_max_ccch);
                } else
-                       printf("-      |-      |                       |"
-                               "-      |-");
+                       printf("n/a    |n/a    |       |                       "
+                               "|n/a    |n/a\n");
        }
 
        return 0;
@@ -2526,7 +2812,7 @@ int gsm322_init(struct osmocom_ms *ms)
        OSMOCOM_FILE *fp;
        char suffix[] = ".ba";
        char filename[sizeof(ms->name) + strlen(suffix) + 1];
-//     int i;
+       int i;
        struct gsm322_ba_list *ba;
        uint8_t buf[4];
 
@@ -2550,36 +2836,45 @@ int gsm322_init(struct osmocom_ms *ms)
        INIT_LLIST_HEAD(&plmn->forbidden_la);
        INIT_LLIST_HEAD(&cs->ba_list);
 
-#ifdef TODO
        /* set supported frequencies in cell selection list */
        for (i = 0; i <= 1023; i++)
-               if ((s->freq_map[i >> 3] & (1 << (i & 7))))
+               if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
                        cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
-#else
-       /* set cell selection list to given test frequency only */
-       cs->list[ms->test_arfcn].flags |= GSM322_CS_FLAG_SUPPORT;
-#endif
 
        /* read BA list */
        strcpy(filename, ms->name);
        strcat(filename, suffix);
        fp = osmocom_fopen(filename, "r");
        if (fp) {
-               while(!feof(fp)) {
+               int rc;
+
+               while(!osmocom_feof(fp)) {
                        ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
                        if (!ba)
                                return -ENOMEM;
-                       osmocom_fread(buf, 4, 1, fp);
+                       rc = osmocom_fread(buf, 4, 1, fp);
+                       if (!rc) {
+                               talloc_free(ba);
+                               break;
+                       }
                        ba->mcc = (buf[0] << 8) | buf[1];
                        ba->mnc = (buf[2] << 8) | buf[3];
-                       osmocom_fread(ba->freq, sizeof(ba->freq), 1, fp);
+                       rc = osmocom_fread(ba->freq, sizeof(ba->freq), 1, fp);
+                       if (!rc) {
+                               talloc_free(ba);
+                               break;
+                       }
                        llist_add_tail(&ba->entry, &cs->ba_list);
-                       LOGP(DPLMN, LOGL_INFO, "Read stored BA list (mcc=%d "
-                               "mnc=%d)\n", ba->mcc, ba->mnc);
+                       LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%d "
+                               "mnc=%d  %s, %s)\n", ba->mcc, ba->mnc,
+                               gsm_get_mcc(ba->mcc),
+                               gsm_get_mnc(ba->mcc, ba->mnc));
                }
                osmocom_fclose(fp);
        } else
-               LOGP(DCS, LOGL_NOTICE, "No stored BA list\n");
+               LOGP(DCS, LOGL_INFO, "No stored BA list\n");
+
+       register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
 
        return 0;
 }
@@ -2595,10 +2890,13 @@ int gsm322_exit(struct osmocom_ms *ms)
        char filename[sizeof(ms->name) + strlen(suffix) + 1];
        struct gsm322_ba_list *ba;
        uint8_t buf[4];
+       int i;
 
        LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
        LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
 
+       unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
+
        /* stop cell selection process (if any) */
        new_c_state(cs, GSM322_C0_NULL);
 
@@ -2606,20 +2904,33 @@ int gsm322_exit(struct osmocom_ms *ms)
        stop_cs_timer(cs);
        stop_plmn_timer(plmn);
 
+       /* flush sysinfo */
+       for (i = 0; i <= 1023; i++) {
+               if (cs->list[i].sysinfo) {
+                       talloc_free(cs->list[i].sysinfo);
+                       cs->list[i].sysinfo = NULL;
+               }
+               cs->list[i].flags = 0;
+       }
+
        /* store BA list */
        strcpy(filename, ms->name);
        strcat(filename, suffix);
        fp = osmocom_fopen(filename, "w");
        if (fp) {
+               int rc;
+
                llist_for_each_entry(ba, &cs->ba_list, entry) {
                        buf[0] = ba->mcc >> 8;
-                       buf[1] = ba->mcc | 0xff;
+                       buf[1] = ba->mcc & 0xff;
                        buf[2] = ba->mnc >> 8;
-                       buf[3] = ba->mnc | 0xff;
-                       osmocom_fwrite(buf, 4, 1, fp);
-                       osmocom_fwrite(ba->freq, sizeof(ba->freq), 1, fp);
-                       LOGP(DPLMN, LOGL_INFO, "Write stored BA list (mcc=%d "
-                               "mnc=%d)\n", ba->mcc, ba->mnc);
+                       buf[3] = ba->mnc & 0xff;
+                       rc = osmocom_fwrite(buf, 4, 1, fp);
+                       rc = osmocom_fwrite(ba->freq, sizeof(ba->freq), 1, fp);
+                       LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%d "
+                               "mnc=%d  %s, %s)\n", ba->mcc, ba->mnc,
+                               gsm_get_mcc(ba->mcc),
+                               gsm_get_mnc(ba->mcc, ba->mnc));
                }
                osmocom_fclose(fp);
        } else