[layer23] Fixed parsing of ASSIGNMENT / HANDOVER (type-value) IEs
[osmocom-bb.git] / src / host / layer23 / src / mobile / gsm48_rr.c
index f34cdcf..1b36717 100644 (file)
@@ -1,4 +1,3 @@
-#warning rr on MDL error handling (as specified in 04.08 / 04.06)
 /*
  * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
  *
  *
  */
 
+/* Testing delayed (immediate) assigment / handover
+ *
+ * When enabled, the starting time will be set by given frames in the future.
+ * If a starting time is given by the network, this time is ignored.
+ */
+//#define TEST_STARTING_TIMER 140
+
+/* Testing if frequency modification works correctly "after time".
+ *
+ * When enabled, the starting time will be set in the future.
+ * A wrong channel is defined "before time", so noise is received until
+ * starting time elapses.
+ * If a starting time is given by the network, this time is ignored.
+ * Also channel definitions "before time" are ignored.
+ *
+ * NOTE: TEST_STARTING_TIMER MUST be defined also.
+ */
+//#define TEST_FREQUENCY_MOD
+
 #include <stdint.h>
 #include <errno.h>
 #include <stdio.h>
 #include <osmocom/bb/common/l1ctl.h>
 #include <osmocom/bb/mobile/vty.h>
 
-static void start_rr_t_monitor(struct gsm48_rrlayer *rr, int sec, int micro);
-static void stop_rr_t_monitor(struct gsm48_rrlayer *rr);
+static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro);
+static void stop_rr_t_meas(struct gsm48_rrlayer *rr);
+static void stop_rr_t_starting(struct gsm48_rrlayer *rr);
+static void stop_rr_t3124(struct gsm48_rrlayer *rr);
 static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg);
 static int gsm48_rr_dl_est(struct osmocom_ms *ms);
+static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms);
+static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr,
+       uint8_t mode);
 
 /*
  * support
@@ -86,30 +109,17 @@ int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc,
        return 0;
 }
 
-static int gsm48_encode_chan_h0(struct gsm48_chan_desc *cd, uint8_t tsc,
-       uint16_t arfcn)
-{
-       cd->h0.tsc = tsc;
-       cd->h0.h = 0;
-       cd->h0.arfcn_low = arfcn & 0xff;
-       cd->h0.arfcn_high = arfcn >> 8;
-
-       return 0;
-}
-
-static int gsm48_encode_chan_h1(struct gsm48_chan_desc *cd, uint8_t tsc,
-       uint8_t maio, uint8_t hsn)
+int gsm48_encode_lai(struct gsm48_loc_area_id *lai, uint16_t mcc,
+       uint16_t mnc, uint16_t lac)
 {
-       cd->h1.tsc = tsc;
-       cd->h1.h = 1;
-       cd->h1.maio_low = maio & 0x03;
-       cd->h1.maio_high = maio >> 2;
-       cd->h1.hsn = hsn;
+       lai->digits[0] = (mcc >> 8) | (mcc & 0xf0);
+       lai->digits[1] = (mcc & 0x0f) | (mnc << 4);
+       lai->digits[2] = (mnc >> 8) | (mnc & 0xf0);
+       lai->lac = htons(lac);
 
        return 0;
 }
 
-
 static int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc, 
        uint16_t *arfcn)
 {
@@ -129,13 +139,26 @@ static int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc,
        return 0;
 }
 
+/* decode "Power Command" (10.5.2.28) and (10.5.2.28a) */
+static int gsm48_decode_power_cmd_acc(struct gsm48_power_cmd *pc,
+       uint8_t *power_level, uint8_t *atc)
+{
+       *power_level = pc->power_level;
+       if (atc) /* only in case of 10.5.2.28a */
+               *atc = pc->atc;
+
+       return 0;
+}
+
 /* 10.5.2.38 decode Starting time IE */
 static int gsm48_decode_start_time(struct gsm48_rr_cd *cd,
        struct gsm48_start_time *st)
 {
-       cd->start_t1 = st->t1;
-       cd->start_t2 = st->t2;
-       cd->start_t3 = (st->t3_high << 3) | st->t3_low;
+       cd->start = 1;
+       cd->start_tm.t1 = st->t1;
+       cd->start_tm.t2 = st->t2;
+       cd->start_tm.t3 = (st->t3_high << 3) | st->t3_low;
+       cd->start_tm.fn = gsm_gsmtime2fn(&cd->start_tm);
 
        return 0;
 }
@@ -192,6 +215,159 @@ static int gsm48_decode_ba_range(const uint8_t *ba, uint8_t ba_len,
        return 0;
 }
 
+/* decode "Cell Description" (10.5.2.2) */
+static int gsm48_decode_cell_desc(struct gsm48_cell_desc *cd, uint16_t *arfcn,
+       uint8_t *ncc, uint8_t *bcc)
+{
+       *arfcn = (cd->arfcn_hi << 8) + cd->arfcn_lo;
+       *ncc = cd->ncc;
+       *bcc = cd->bcc;
+
+       return 0;
+}
+
+/* decode "Synchronization Indication" (10.5.2.39) */
+static int gsm48_decode_sync_ind(struct gsm48_rrlayer *rr,
+       struct gsm48_sync_ind *si)
+{
+       rr->hando_sync_ind = si->si;
+       rr->hando_rot = si->rot;
+       rr->hando_nci = si->nci;
+
+       return 0;
+}
+
+/* 3.1.4.3 set sequence number and increment */
+static int gsm48_apply_v_sd(struct gsm48_rrlayer *rr, struct msgb *msg)
+{
+       struct gsm48_hdr *gh = msgb_l3(msg);
+       uint8_t pdisc = gh->proto_discr & 0x0f;
+       uint8_t v_sd;
+
+       switch (pdisc) {
+       case GSM48_PDISC_MM:
+       case GSM48_PDISC_CC:
+       case GSM48_PDISC_NC_SS:
+               /* all thre pdiscs share the same V(SD) */
+               pdisc = GSM48_PDISC_MM;
+               // fall through
+       case GSM48_PDISC_GROUP_CC:
+       case GSM48_PDISC_BCAST_CC:
+       case GSM48_PDISC_PDSS1:
+       case GSM48_PDISC_PDSS2:
+               /* extract v_sd(pdisc) */
+               v_sd = (rr->v_sd >> pdisc) & 1;
+
+               /* replace bit 7 vy v_sd */
+               gh->msg_type &= 0xbf;
+               gh->msg_type |= (v_sd << 6);
+
+               /* increment V(SD) */
+               rr->v_sd ^= (1 << pdisc);
+               LOGP(DRR, LOGL_INFO, "Using and incrementing V(SD) = %d "
+                       "(pdisc %x)\n", v_sd, pdisc);
+               break;
+       case GSM48_PDISC_RR:
+       case GSM48_PDISC_SMS:
+               /* no V(VSD) is required */
+               break;
+       default:
+               LOGP(DRR, LOGL_ERROR, "Error, V(SD) of pdisc %x not handled\n",
+                       pdisc);
+               return -ENOTSUP;
+       }
+
+       return 0;
+}
+
+/* set channel mode if supported, or return error cause */
+static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr,
+       uint8_t mode)
+{
+       struct gsm_support *sup = &ms->support;
+       uint8_t ch_type, ch_subch, ch_ts;
+
+       /* only complain if we use TCH/F or TCH/H */
+       rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts);
+       if (ch_type != RSL_CHAN_Bm_ACCHs
+        && ch_type != RSL_CHAN_Lm_ACCHs)
+               return 0;
+
+       switch (mode) {
+       case GSM48_CMODE_SIGN:
+               LOGP(DRR, LOGL_INFO, "Mode: signalling\n");
+               break;
+       case GSM48_CMODE_SPEECH_V1:
+               if (ch_type == RSL_CHAN_Bm_ACCHs) {
+                       if (!sup->full_v1) {
+                               LOGP(DRR, LOGL_NOTICE, "Not supporting "
+                                       "full-rate speech V1\n");
+                               return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+                       }
+                       LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V1\n");
+               } else {
+                       if (!sup->half_v1) {
+                               LOGP(DRR, LOGL_NOTICE, "Not supporting "
+                                       "half-rate speech V1\n");
+                               return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+                       }
+                       LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V1\n");
+               }
+               break;
+       case GSM48_CMODE_SPEECH_EFR:
+               if (ch_type == RSL_CHAN_Bm_ACCHs) {
+                       if (!sup->full_v2) {
+                               LOGP(DRR, LOGL_NOTICE, "Not supporting "
+                                       "full-rate speech V2\n");
+                               return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+                       }
+                       LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V2\n");
+               } else {
+                       LOGP(DRR, LOGL_NOTICE, "Not supporting "
+                                       "half-rate speech V2\n");
+                       return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+               }
+               break;
+       case GSM48_CMODE_SPEECH_AMR:
+               if (ch_type == RSL_CHAN_Bm_ACCHs) {
+                       if (!sup->full_v3) {
+                               LOGP(DRR, LOGL_NOTICE, "Not supporting "
+                                       "full-rate speech V3\n");
+                               return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+                       }
+                       LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V3\n");
+               } else {
+                       if (!sup->half_v3) {
+                               LOGP(DRR, LOGL_NOTICE, "Not supporting "
+                                       "half-rate speech V3\n");
+                               return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+                       }
+                       LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V3\n");
+               }
+               break;
+       default:
+               LOGP(DRR, LOGL_ERROR, "Mode 0x%02x not supported!\n", mode);
+               return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+       }
+
+       return 0;
+}
+
+/* apply new "alter_delay" in dedicated mode */
+int gsm48_rr_alter_delay(struct osmocom_ms *ms)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct gsm_settings *set = &rr->ms->settings;
+
+       if (rr->state != GSM48_RR_ST_DEDICATED)
+               return -EINVAL;
+       l1ctl_tx_param_req(ms, rr->cd_now.ind_ta - set->alter_delay,
+               (set->alter_tx_power) ? set->alter_tx_power_value
+                                       : rr->cd_now.ind_tx_power);
+
+       return 0;
+}
+
 /*
  * state transition
  */
@@ -219,6 +395,16 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state)
        LOGP(DRR, LOGL_INFO, "new state %s -> %s\n",
                gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]);
 
+       /* abort handover, in case of release of dedicated mode */
+       if (rr->state == GSM48_RR_ST_DEDICATED) {
+               /* disable handover / assign state */
+               rr->modify_state = GSM48_RR_MOD_NONE;
+               /* stop start_time_timer */
+               stop_rr_t_starting(rr);
+               /* stop handover timer */
+               stop_rr_t3124(rr);
+       }
+
        rr->state = state;
 
        if (state == GSM48_RR_ST_IDLE) {
@@ -227,6 +413,7 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state)
 
                /* release dedicated mode, if any */
                l1ctl_tx_dm_rel_req(rr->ms);
+               rr->dm_est = 0;
                l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_FULL);
                /* free establish message, if any */
                rr->rr_est_req = 0;
@@ -239,7 +426,7 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state)
                        msgb_free(msg);
                /* clear all descriptions of last channel */
                memset(&rr->cd_now, 0, sizeof(rr->cd_now));
-               /* reset cipering */
+               /* reset ciphering */
                rr->cipher_on = 0;
                /* tell cell selection process to return to idle mode
                 * NOTE: this must be sent unbuffered, because it will
@@ -350,8 +537,8 @@ static int gsm48_send_rsl(struct osmocom_ms *ms, uint8_t msg_type,
        struct gsm48_rrlayer *rr = &ms->rrlayer;
 
        if (!msg->l3h) {
-               printf("FIX l3h\n");
-               exit (0);
+               LOGP(DRR, LOGL_ERROR, "FIX l3h\n");
+               return -EINVAL;
        }
        rsl_rll_push_l3(msg, msg_type, rr->cd_now.chan_nr,
                rr->cd_now.link_id, 1);
@@ -400,8 +587,6 @@ int gsm48_rsl_dequeue(struct osmocom_ms *ms)
 int gsm48_rr_start_monitor(struct osmocom_ms *ms)
 {
        ms->rrlayer.monitor = 1;
-       memset(&ms->meas, 0, sizeof(&ms->meas));
-       start_rr_t_monitor(&ms->rrlayer, 1, 0);
 
        return 0;
 }
@@ -409,8 +594,6 @@ int gsm48_rr_start_monitor(struct osmocom_ms *ms)
 int gsm48_rr_stop_monitor(struct osmocom_ms *ms)
 {
        ms->rrlayer.monitor = 0;
-       memset(&ms->meas, 0, sizeof(&ms->meas));
-       stop_rr_t_monitor(&ms->rrlayer);
 
        return 0;
 }
@@ -420,43 +603,72 @@ int gsm48_rr_stop_monitor(struct osmocom_ms *ms)
  */
 
 /* special timer to monitor measurements */
-static void timeout_rr_monitor(void *arg)
+static void timeout_rr_meas(void *arg)
 {
        struct gsm48_rrlayer *rr = arg;
        struct gsm322_cellsel *cs = &rr->ms->cellsel;
        struct rx_meas_stat *meas = &rr->ms->meas;
        struct gsm_settings *set = &rr->ms->settings;
-       int rxlev, berr;
+       int rxlev, berr, snr;
        uint8_t ch_type, ch_subch, ch_ts;
        char text[256];
 
        if (!cs->selected) {
-               sprintf(text, "MON: no cell selected");
+               return;
        } else if (!meas->frames) {
                sprintf(text, "MON: no cell info");
        } else {
                rxlev = meas->rxlev / meas->frames;
                berr = meas->berr / meas->frames;
-               sprintf(text, "MON: arfcn=%d lev=%s ber=%2d LAI=%s %s %04x "
-                       "ID=%04x", cs->sel_arfcn, gsm_print_rxlev(rxlev),
-                       berr, gsm_print_mcc(cs->sel_mcc),
+               snr = meas->snr / meas->frames;
+               sprintf(text, "MON: f=%d lev=%s snr=%2d ber=%3d "
+                       "LAI=%s %s %04x ID=%04x", cs->sel_arfcn,
+                       gsm_print_rxlev(rxlev), berr, snr, 
+                       gsm_print_mcc(cs->sel_mcc),
                        gsm_print_mnc(cs->sel_mnc), cs->sel_lac, cs->sel_id);
                if (rr->state == GSM48_RR_ST_DEDICATED) {
                        rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type,
                                &ch_subch, &ch_ts);
                        sprintf(text + strlen(text), " TA=%d pwr=%d TS=%d",
-                       rr->ind_ta - set->alter_delay,
+                       rr->cd_now.ind_ta - set->alter_delay,
                        (set->alter_tx_power) ? set->alter_tx_power_value
-                                               : rr->ind_tx_power, ch_ts);
+                                       : rr->cd_now.ind_tx_power, ch_ts);
                        if (ch_type == RSL_CHAN_SDCCH8_ACCH
                         || ch_type == RSL_CHAN_SDCCH4_ACCH)
                                sprintf(text + strlen(text), "/%d", ch_subch);
                }
        }
-       vty_notify(rr->ms, "%s\n", text);
+       LOGP(DRR, LOGL_INFO, "%s\n", text);
+       if (rr->monitor)
+               vty_notify(rr->ms, "%s\n", text);
 
+       if (rr->dm_est)
+               gsm48_rr_tx_meas_rep(rr->ms);
        memset(meas, 0, sizeof(*meas));
-       start_rr_t_monitor(rr, 1, 0);
+       start_rr_t_meas(rr, 1, 0);
+}
+
+/* special timer to assign / handover when starting time is reached */
+static void timeout_rr_t_starting(void *arg)
+{
+       struct gsm48_rrlayer *rr = arg;
+       struct msgb *nmsg;
+
+       LOGP(DRR, LOGL_INFO, "starting timer has fired\n");
+
+       /* open channel when starting timer of IMM.ASS has fired */
+       if (rr->modify_state == GSM48_RR_MOD_IMM_ASS) {
+               rr->modify_state = GSM48_RR_MOD_NONE;
+               gsm48_rr_dl_est(rr->ms);
+               return;
+       }
+
+       /* start suspension of current link */
+       LOGP(DRR, LOGL_INFO, "request suspension of data link\n");
+       nmsg = gsm48_l3_msgb_alloc();
+       if (!nmsg)
+               return;
+       gsm48_send_rsl(rr->ms, RSL_MT_SUSP_REQ, nmsg);
 }
 
 /* special timer to ensure that UA is sent before disconnecting channel */
@@ -499,6 +711,11 @@ static void timeout_rr_t3122(void *arg)
        LOGP(DRR, LOGL_INFO, "timer T3122 has fired\n");
 }
 
+static void timeout_rr_t3124(void *arg)
+{
+       LOGP(DRR, LOGL_INFO, "timer T3124 has fired\n");
+}
+
 static void timeout_rr_t3126(void *arg)
 {
        struct gsm48_rrlayer *rr = arg;
@@ -520,24 +737,35 @@ static void timeout_rr_t3126(void *arg)
        new_rr_state(rr, GSM48_RR_ST_IDLE);
 }
 
-static void start_rr_t_monitor(struct gsm48_rrlayer *rr, int sec, int micro)
+static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro)
 {
-       rr->t_monitor.cb = timeout_rr_monitor;
-       rr->t_monitor.data = rr;
-       bsc_schedule_timer(&rr->t_monitor, sec, micro);
+       rr->t_meas.cb = timeout_rr_meas;
+       rr->t_meas.data = rr;
+       bsc_schedule_timer(&rr->t_meas, sec, micro);
 }
 
 static void start_rr_t_rel_wait(struct gsm48_rrlayer *rr, int sec, int micro)
 {
-       LOGP(DRR, LOGL_INFO, "starting T_rel_wait with %d seconds\n", sec);
+       LOGP(DRR, LOGL_INFO, "starting T_rel_wait with %d.%03d seconds\n", sec,
+               micro / 1000);
        rr->t_rel_wait.cb = timeout_rr_t_rel_wait;
        rr->t_rel_wait.data = rr;
        bsc_schedule_timer(&rr->t_rel_wait, sec, micro);
 }
 
+static void start_rr_t_starting(struct gsm48_rrlayer *rr, int sec, int micro)
+{
+       LOGP(DRR, LOGL_INFO, "starting T_starting with %d.%03d seconds\n", sec,
+               micro / 1000);
+       rr->t_starting.cb = timeout_rr_t_starting;
+       rr->t_starting.data = rr;
+       bsc_schedule_timer(&rr->t_starting, sec, micro);
+}
+
 static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro)
 {
-       LOGP(DRR, LOGL_INFO, "starting T3110 with %d seconds\n", sec);
+       LOGP(DRR, LOGL_INFO, "starting T3110 with %d.%03d seconds\n", sec,
+               micro / 1000);
        rr->t3110.cb = timeout_rr_t3110;
        rr->t3110.data = rr;
        bsc_schedule_timer(&rr->t3110, sec, micro);
@@ -545,25 +773,44 @@ static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro)
 
 static void start_rr_t3122(struct gsm48_rrlayer *rr, int sec, int micro)
 {
-       LOGP(DRR, LOGL_INFO, "starting T3122 with %d seconds\n", sec);
+       LOGP(DRR, LOGL_INFO, "starting T3122 with %d.%03d seconds\n", sec,
+               micro / 1000);
        rr->t3122.cb = timeout_rr_t3122;
        rr->t3122.data = rr;
        bsc_schedule_timer(&rr->t3122, sec, micro);
 }
 
+static void start_rr_t3124(struct gsm48_rrlayer *rr, int sec, int micro)
+{
+       LOGP(DRR, LOGL_INFO, "starting T3124 with %d.%03d seconds\n", sec,
+               micro / 1000);
+       rr->t3124.cb = timeout_rr_t3124;
+       rr->t3124.data = rr;
+       bsc_schedule_timer(&rr->t3124, sec, micro);
+}
+
 static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro)
 {
-       LOGP(DRR, LOGL_INFO, "starting T3126 with %d seconds\n", sec);
+       LOGP(DRR, LOGL_INFO, "starting T3126 with %d.%03d seconds\n", sec,
+               micro / 1000);
        rr->t3126.cb = timeout_rr_t3126;
        rr->t3126.data = rr;
        bsc_schedule_timer(&rr->t3126, sec, micro);
 }
 
-static void stop_rr_t_monitor(struct gsm48_rrlayer *rr)
+static void stop_rr_t_meas(struct gsm48_rrlayer *rr)
+{
+       if (bsc_timer_pending(&rr->t_meas)) {
+               LOGP(DRR, LOGL_INFO, "stopping pending timer T_meas\n");
+               bsc_del_timer(&rr->t_meas);
+       }
+}
+
+static void stop_rr_t_starting(struct gsm48_rrlayer *rr)
 {
-       if (bsc_timer_pending(&rr->t_monitor)) {
-               LOGP(DRR, LOGL_INFO, "stopping pending timer T_monitor\n");
-               bsc_del_timer(&rr->t_monitor);
+       if (bsc_timer_pending(&rr->t_starting)) {
+               LOGP(DRR, LOGL_INFO, "stopping pending timer T_starting\n");
+               bsc_del_timer(&rr->t_starting);
        }
 }
 
@@ -591,6 +838,14 @@ static void stop_rr_t3122(struct gsm48_rrlayer *rr)
        }
 }
 
+static void stop_rr_t3124(struct gsm48_rrlayer *rr)
+{
+       if (bsc_timer_pending(&rr->t3124)) {
+               LOGP(DRR, LOGL_INFO, "stopping pending timer T3124\n");
+               bsc_del_timer(&rr->t3124);
+       }
+}
+
 static void stop_rr_t3126(struct gsm48_rrlayer *rr)
 {
        if (bsc_timer_pending(&rr->t3126)) {
@@ -637,6 +892,7 @@ static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr)
        struct gsm_settings *set = &ms->settings;
        struct msgb *nmsg;
        struct gsm48_hdr *gh;
+       struct gsm48_rr_hdr *nrrh;
        uint8_t buf[11], *tlv;
 
        LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMPLETE (cr %d)\n", cr);
@@ -652,17 +908,28 @@ static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr)
        /* MI */
        if (cr) {
                gsm48_generate_mid_from_imsi(buf, set->imeisv);
+               /* alter MI type */
+               buf[2] = (buf[2] & ~GSM_MI_TYPE_MASK) | GSM_MI_TYPE_IMEISV;
                tlv = msgb_put(nmsg, 2 + buf[1]);
                memcpy(tlv, buf, 2 + buf[1]);
        }
 
-       return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+       gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+
+       /* send RR_SYNC_IND(ciphering) */
+       nmsg = gsm48_rr_msgb_alloc(GSM48_RR_SYNC_IND);
+       if (!nmsg)
+               return -ENOMEM;
+       nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+       nrrh->cause = RR_SYNC_CAUSE_CIPHERING;
+       return gsm48_rr_upmsg(ms, nmsg);
 }
 
 /* receive ciphering mode command */
 static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct gsm_subscriber *subscr = &ms->subscr;
        struct gsm_support *sup = &ms->support;
        struct gsm48_hdr *gh = msgb_l3(msg);
        struct gsm48_cip_mode_cmd *cm = (struct gsm48_cip_mode_cmd *)gh->data;
@@ -682,38 +949,49 @@ static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
        /* cipher mode response */
        cr = cm->cr;
 
-       if (sc)
-               LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, cr=%u)",
+       if (!sc)
+               LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, cr=%u)\n",
                        sc, cr);
        else
                LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, "
-                       "algo=A5/%d cr=%u)", sc, alg_id + 1, cr);
+                       "algo=A5/%d cr=%u)\n", sc, alg_id + 1, cr);
 
        /* 3.4.7.2 */
        if (rr->cipher_on && sc) {
-               LOGP(DRR, LOGL_INFO, "cipering already applied.\n");
+               LOGP(DRR, LOGL_NOTICE, "chiphering already applied\n");
                return gsm48_rr_tx_rr_status(ms,
                        GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
        }
 
        /* check if we actually support this cipher */
-       if ((alg_id == GSM_CIPHER_A5_1 && !sup->a5_1)
-        || (alg_id == GSM_CIPHER_A5_2 && !sup->a5_2)
-        || (alg_id == GSM_CIPHER_A5_3 && !sup->a5_3)
-        || (alg_id == GSM_CIPHER_A5_4 && !sup->a5_4)
-        || (alg_id == GSM_CIPHER_A5_5 && !sup->a5_5)
-        || (alg_id == GSM_CIPHER_A5_6 && !sup->a5_6)
-        || (alg_id == GSM_CIPHER_A5_7 && !sup->a5_7))
+       if (sc && ((alg_id == GSM_CIPHER_A5_1 && !sup->a5_1)
+               || (alg_id == GSM_CIPHER_A5_2 && !sup->a5_2)
+               || (alg_id == GSM_CIPHER_A5_3 && !sup->a5_3)
+               || (alg_id == GSM_CIPHER_A5_4 && !sup->a5_4)
+               || (alg_id == GSM_CIPHER_A5_5 && !sup->a5_5)
+               || (alg_id == GSM_CIPHER_A5_6 && !sup->a5_6)
+               || (alg_id == GSM_CIPHER_A5_7 && !sup->a5_7))) {
+               LOGP(DRR, LOGL_NOTICE, "algo not supported\n");
+               return gsm48_rr_tx_rr_status(ms,
+                       GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+       }
+
+       /* check if we have no key */
+       if (sc && subscr->key_seq == 7) {
+               LOGP(DRR, LOGL_NOTICE, "no key available\n");
                return gsm48_rr_tx_rr_status(ms,
-                       GSM48_RR_CAUSE_CHAN_MODE_UNACCT);
+                       GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+       }
 
        /* change to ciphering */
-#ifdef TODO
-       rsl command to activate ciperhing
-#endif
-       rr->cipher_on = sc, rr->cipher_type = alg_id;
+       rr->cipher_on = sc;
+       rr->cipher_type = alg_id;
+       if (rr->cipher_on)
+               l1ctl_tx_crypto_req(ms, rr->cipher_type + 1, subscr->key, 8);
+       else
+               l1ctl_tx_crypto_req(ms, 0, NULL, 0);
 
-       /* response */
+       /* response (using the new mode) */
        return gsm48_rr_tx_cip_mode_cpl(ms, cr);
 }
 
@@ -844,7 +1122,7 @@ int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm)
                cm->pwr_lev = sup->pwr_lev_1800;
        else
                cm->pwr_lev = sup->pwr_lev_900;
-       cm->a5_1 = sup->a5_1;
+       cm->a5_1 = !sup->a5_1;
        cm->es_ind = sup->es_ind;
        cm->rev_lev = sup->rev_lev;
        cm->fc = (sup->r_gsm || sup->e_gsm);
@@ -917,6 +1195,7 @@ static int gsm48_rr_rx_cm_enq(struct osmocom_ms *ms, struct msgb *msg)
 /* start random access */
 static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging)
 {
+       struct gsm_support *sup = &ms->support;
        struct gsm48_rrlayer *rr = &ms->rrlayer;
        struct gsm322_cellsel *cs = &ms->cellsel;
        struct gsm48_sysinfo *s = cs->si;
@@ -961,8 +1240,6 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging)
        /* number of retransmissions (with first transmission) */
        rr->n_chan_req = s->max_retrans + 1;
 
-#warning HACK: always request SDCCH for test
-cause = RR_EST_CAUSE_LOC_UPD;
        /* generate CHAN REQ (9.1.8) */
        switch (cause) {
        case RR_EST_CAUSE_EMERGENCY:
@@ -1020,16 +1297,38 @@ cause = RR_EST_CAUSE_LOC_UPD;
                        chan_req_val);
                break;
        case RR_EST_CAUSE_ANS_PAG_TCH_F:
-               /* ms supports no dual rate */
-               chan_req_mask = 0x1f;
-               chan_req_val = 0x80;
+               switch (sup->ch_cap) {
+               case GSM_CAP_SDCCH:
+                       chan_req_mask = 0x0f;
+                       chan_req_val = 0x10;
+                       break;
+               case GSM_CAP_SDCCH_TCHF:
+                       chan_req_mask = 0x1f;
+                       chan_req_val = 0x80;
+                       break;
+               default:
+                       chan_req_mask = 0x0f;
+                       chan_req_val = 0x20;
+                       break;
+               }
                LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING TCH/F)\n",
                        chan_req_val);
                break;
        case RR_EST_CAUSE_ANS_PAG_TCH_ANY:
-               /* ms supports no dual rate */
-               chan_req_mask = 0x1f;
-               chan_req_val = 0x80;
+               switch (sup->ch_cap) {
+               case GSM_CAP_SDCCH:
+                       chan_req_mask = 0x0f;
+                       chan_req_val = 0x10;
+                       break;
+               case GSM_CAP_SDCCH_TCHF:
+                       chan_req_mask = 0x1f;
+                       chan_req_val = 0x80;
+                       break;
+               default:
+                       chan_req_mask = 0x0f;
+                       chan_req_val = 0x30;
+                       break;
+               }
                LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING TCH/H or "
                                "TCH/F)\n", chan_req_val);
                break;
@@ -1271,8 +1570,8 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
        ncch->data[7] = tx_power;
 
        /* set initial indications */
-       rr->ind_tx_power = s->ms_txpwr_max_cch;
-       rr->ind_ta = set->alter_delay;
+       rr->cd_now.ind_tx_power = s->ms_txpwr_max_cch;
+       rr->cd_now.ind_ta = set->alter_delay;
 
        /* store ra until confirmed, then copy it with time into cr_hist */
        rr->cr_ra = chan_req;
@@ -1285,443 +1584,16 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
  */
 
 /* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
-static int gsm48_decode_freq_list(struct gsm_support *sup,
+static int decode_freq_list(struct gsm_support *sup,
        struct gsm_sysinfo_freq *f, uint8_t *cd, uint8_t len, uint8_t mask,
        uint8_t frqt)
 {
-       int i;
-
-       /* NOTES:
-        *
-        * The Range format uses "SMOD" computation.
-        * e.g. "n SMOD m" equals "((n - 1) % m) + 1"
-        * A cascade of multiple SMOD computations is simpified:
-        * "(n SMOD m) SMOD o" equals "(((n - 1) % m) % o) + 1"
-        *
-        * The Range format uses 16 octets of data in SYSTEM INFORMATION.
-        * When used in dedicated messages, the length can be less.
-        * In this case the ranges are decoded for all frequencies that
-        * fit in the block of given length.
-        */
-
-       /* tabula rasa */
-       for (i = 0; i < 1024; i++)
-               f[i].mask &= ~frqt;
-
-       /* 00..XXX. */
-       if ((cd[0] & 0xc0 & mask) == 0x00) {
-               /* Bit map 0 format */
-               if (len < 16)
-                       return -EINVAL;
-               for (i = 1; i <= 124; i++)
-                       if ((cd[15 - ((i-1) >> 3)] & (1 << ((i-1) & 7))))
-                               f[i].mask |= frqt;
-
-               return 0;
-       }
-
        /* only Bit map 0 format for P-GSM */
-       if (sup->p_gsm && !sup->e_gsm && !sup->r_gsm && !sup->dcs_1800)
+       if ((cd[0] & 0xc0 & mask) != 0x00 &&
+           (sup->p_gsm && !sup->e_gsm && !sup->r_gsm && !sup->dcs_1800))
                return 0;
 
-       /* 10..0XX. */
-       if ((cd[0] & 0xc8 & mask) == 0x80) {
-               /* Range 1024 format */
-               uint16_t w[17]; /* 1..16 */
-               struct gsm48_range_1024 *r = (struct gsm48_range_1024 *)cd;
-
-               if (len < 2)
-                       return -EINVAL;
-               memset(w, 0, sizeof(w));
-               if (r->f0)
-                       f[0].mask |= frqt;
-               w[1] = (r->w1_hi << 8) | r->w1_lo;
-               if (len >= 4)
-                       w[2] = (r->w2_hi << 1) | r->w2_lo;
-               if (len >= 5)
-                       w[3] = (r->w3_hi << 2) | r->w3_lo;
-               if (len >= 6)
-                       w[4] = (r->w4_hi << 2) | r->w4_lo;
-               if (len >= 7)
-                       w[5] = (r->w5_hi << 2) | r->w5_lo;
-               if (len >= 8)
-                       w[6] = (r->w6_hi << 2) | r->w6_lo;
-               if (len >= 9)
-                       w[7] = (r->w7_hi << 2) | r->w7_lo;
-               if (len >= 10)
-                       w[8] = (r->w8_hi << 1) | r->w8_lo;
-               if (len >= 10)
-                       w[9] = r->w9;
-               if (len >= 11)
-                       w[10] = r->w10;
-               if (len >= 12)
-                       w[11] = (r->w11_hi << 6) | r->w11_lo;
-               if (len >= 13)
-                       w[12] = (r->w12_hi << 5) | r->w12_lo;
-               if (len >= 14)
-                       w[13] = (r->w13_hi << 4) | r->w13_lo;
-               if (len >= 15)
-                       w[14] = (r->w14_hi << 3) | r->w14_lo;
-               if (len >= 16)
-                       w[15] = (r->w15_hi << 2) | r->w15_lo;
-               if (len >= 16)
-                       w[16] = r->w16;
-               if (w[1])
-                       f[w[1]].mask |= frqt;
-               if (w[2])
-                       f[((w[1] - 512 + w[2] - 1) % 1023) + 1].mask |= frqt;
-               if (w[3])
-                       f[((w[1]       + w[3] - 1) % 1023) + 1].mask |= frqt;
-               if (w[4])
-                       f[((w[1] - 512 + ((w[2] - 256 + w[4] - 1) % 511)) % 1023) + 1].mask |= frqt;
-               if (w[5])
-                       f[((w[1]       + ((w[3] - 256 - w[5] - 1) % 511)) % 1023) + 1].mask |= frqt;
-               if (w[6])
-                       f[((w[1] - 512 + ((w[2]       + w[6] - 1) % 511)) % 1023) + 1].mask |= frqt;
-               if (w[7])
-                       f[((w[1]       + ((w[3]       + w[7] - 1) % 511)) % 1023) + 1].mask |= frqt;
-               if (w[8])
-                       f[((w[1] - 512 + ((w[2] - 256 + ((w[4] - 128 + w[8] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
-               if (w[9])
-                       f[((w[1]       + ((w[3] - 256 + ((w[5] - 128 + w[9] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
-               if (w[10])
-                       f[((w[1] - 512 + ((w[2]       + ((w[6] - 128 + w[10] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
-               if (w[11])
-                       f[((w[1]       + ((w[3]       + ((w[7] - 128 + w[11] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
-               if (w[12])
-                       f[((w[1] - 512 + ((w[2] - 256 + ((w[4]       + w[12] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
-               if (w[13])
-                       f[((w[1]       + ((w[3] - 256 + ((w[5]       + w[13] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
-               if (w[14])
-                       f[((w[1] - 512 + ((w[2]       + ((w[6]       + w[14] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
-               if (w[15])
-                       f[((w[1]       + ((w[3]       + ((w[7]       + w[15] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
-               if (w[16])
-                       f[((w[1] - 512 + ((w[2] - 256 + ((w[4] - 128 + ((w[8] - 64 + w[16] - 1) % 127)) % 255)) % 511)) % 1023) + 1].mask |= frqt;
-
-               return 0;
-       }
-       /* 10..100. */
-       if ((cd[0] & 0xce & mask) == 0x88) {
-               /* Range 512 format */
-               uint16_t w[18]; /* 1..17 */
-               struct gsm48_range_512 *r = (struct gsm48_range_512 *)cd;
-
-               if (len < 4)
-                       return -EINVAL;
-               memset(w, 0, sizeof(w));
-               w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
-               w[1] = (r->w1_hi << 2) | r->w1_lo;
-               if (len >= 5)
-                       w[2] = (r->w2_hi << 2) | r->w2_lo;
-               if (len >= 6)
-                       w[3] = (r->w3_hi << 2) | r->w3_lo;
-               if (len >= 7)
-                       w[4] = (r->w4_hi << 1) | r->w4_lo;
-               if (len >= 7)
-                       w[5] = r->w5;
-               if (len >= 8)
-                       w[6] = r->w6;
-               if (len >= 9)
-                       w[7] = (r->w7_hi << 6) | r->w7_lo;
-               if (len >= 10)
-                       w[8] = (r->w8_hi << 4) | r->w8_lo;
-               if (len >= 11)
-                       w[9] = (r->w9_hi << 2) | r->w9_lo;
-               if (len >= 11)
-                       w[10] = r->w10;
-               if (len >= 12)
-                       w[11] = r->w11;
-               if (len >= 13)
-                       w[12] = (r->w12_hi << 4) | r->w12_lo;
-               if (len >= 14)
-                       w[13] = (r->w13_hi << 2) | r->w13_lo;
-               if (len >= 14)
-                       w[14] = r->w14;
-               if (len >= 15)
-                       w[15] = r->w15;
-               if (len >= 16)
-                       w[16] = (r->w16_hi << 3) | r->w16_lo;
-               if (len >= 16)
-                       w[17] = r->w17;
-               f[w[0]].mask |= frqt;
-               if (w[1])
-                       f[(w[0] + w[1]) % 1024].mask |= frqt;
-               if (w[2])
-                       f[(w[0] + ((w[1] - 256 + w[2] - 1) % 511) + 1) % 1024].mask |= frqt;
-               if (w[3])
-                       f[(w[0] + ((w[1]       + w[3] - 1) % 511) + 1) % 1024].mask |= frqt;
-               if (w[4])
-                       f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + w[4] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[5])
-                       f[(w[0] + ((w[1]       + ((w[3] - 128 + w[5] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[6])
-                       f[(w[0] + ((w[1] - 256 + ((w[2]       + w[6] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[7])
-                       f[(w[0] + ((w[1]       + ((w[3]       + w[7] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[8])
-                       f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] - 64 + w[8] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[9])
-                       f[(w[0] + ((w[1]       + ((w[3] - 128 + ((w[5] - 64 + w[9] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[10])
-                       f[(w[0] + ((w[1] - 256 + ((w[2]       + ((w[6] - 64 + w[10] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[11])
-                       f[(w[0] + ((w[1]       + ((w[3]       + ((w[7] - 64 + w[11] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[12])
-                       f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4]      + w[12] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[13])
-                       f[(w[0] + ((w[1]       + ((w[3] - 128 + ((w[5]      + w[13] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[14])
-                       f[(w[0] + ((w[1] - 256 + ((w[2]       + ((w[6]      + w[14] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[15])
-                       f[(w[0] + ((w[1]       + ((w[3]       + ((w[7]      + w[15] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[16])
-                       f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] - 64 + ((w[8] - 32 + w[16] - 1) % 63)) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
-               if (w[17])
-                       f[(w[0] + ((w[1]       + ((w[3] - 128 + ((w[5] - 64 + ((w[9] - 32 + w[17] - 1) % 63)) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
-
-               return 0;
-       }
-       /* 10..101. */
-       if ((cd[0] & 0xce & mask) == 0x8a) {
-               /* Range 256 format */
-               uint16_t w[22]; /* 1..21 */
-               struct gsm48_range_256 *r = (struct gsm48_range_256 *)cd;
-
-               if (len < 4)
-                       return -EINVAL;
-               memset(w, 0, sizeof(w));
-               w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
-               w[1] = (r->w1_hi << 1) | r->w1_lo;
-               if (len >= 4)
-                       w[2] = r->w2;
-               if (len >= 5)
-                       w[3] = r->w3;
-               if (len >= 6)
-                       w[4] = (r->w4_hi << 5) | r->w4_lo;
-               if (len >= 7)
-                       w[5] = (r->w5_hi << 3) | r->w5_lo;
-               if (len >= 8)
-                       w[6] = (r->w6_hi << 1) | r->w6_lo;
-               if (len >= 8)
-                       w[7] = r->w7;
-               if (len >= 9)
-                       w[8] = (r->w8_hi << 4) | r->w8_lo;
-               if (len >= 10)
-                       w[9] = (r->w9_hi << 1) | r->w9_lo;
-               if (len >= 10)
-                       w[10] = r->w10;
-               if (len >= 11)
-                       w[11] = (r->w11_hi << 3) | r->w11_lo;
-               if (len >= 11)
-                       w[12] = r->w12;
-               if (len >= 12)
-                       w[13] = r->w13;
-               if (len >= 13)
-                       w[14] = r->w15;
-               if (len >= 13)
-                       w[15] = (r->w14_hi << 2) | r->w14_lo;
-               if (len >= 14)
-                       w[16] = (r->w16_hi << 3) | r->w16_lo;
-               if (len >= 14)
-                       w[17] = r->w17;
-               if (len >= 15)
-                       w[18] = r->w19;
-               if (len >= 15)
-                       w[19] = (r->w18_hi << 3) | r->w18_lo;
-               if (len >= 16)
-                       w[20] = (r->w20_hi << 3) | r->w20_lo;
-               if (len >= 16)
-                       w[21] = r->w21;
-               f[w[0]].mask |= frqt;
-               if (w[1])
-                       f[(w[0] + w[1]) % 1024].mask |= frqt;
-               if (w[2])
-                       f[(w[0] + ((w[1] - 128 + w[2] - 1) % 255) + 1) % 1024].mask |= frqt;
-               if (w[3])
-                       f[(w[0] + ((w[1]       + w[3] - 1) % 255) + 1) % 1024].mask |= frqt;
-               if (w[4])
-                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + w[4] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[5])
-                       f[(w[0] + ((w[1]       + ((w[3] - 64 + w[5] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[6])
-                       f[(w[0] + ((w[1] - 128 + ((w[2]      + w[6] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[7])
-                       f[(w[0] + ((w[1]       + ((w[3]      + w[7] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[8])
-                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] - 32 + w[8] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[9])
-                       f[(w[0] + ((w[1]       + ((w[3] - 64 + ((w[5] - 32 + w[9] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[10])
-                       f[(w[0] + ((w[1] - 128 + ((w[2]      + ((w[6] - 32 + w[10] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[11])
-                       f[(w[0] + ((w[1]       + ((w[3]      + ((w[7] - 32 + w[11] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[12])
-                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4]      + w[12] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[13])
-                       f[(w[0] + ((w[1]       + ((w[3] - 64 + ((w[5]      + w[13] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[14])
-                       f[(w[0] + ((w[1] - 128 + ((w[2]      + ((w[6]      + w[14] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[15])
-                       f[(w[0] + ((w[1]       + ((w[3]      + ((w[7]      + w[15] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[16])
-                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] - 32 + ((w[8] - 16 + w[16] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[17])
-                       f[(w[0] + ((w[1]       + ((w[3] - 64 + ((w[5] - 32 + ((w[9] - 16 + w[17] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[18])
-                       f[(w[0] + ((w[1] - 128 + ((w[2]      + ((w[6] - 32 + ((w[10] - 16 + w[18] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[19])
-                       f[(w[0] + ((w[1]       + ((w[3]      + ((w[7] - 32 + ((w[11] - 16 + w[19] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[20])
-                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4]      + ((w[12] - 16 + w[20] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-               if (w[21])
-                       f[(w[0] + ((w[1]       + ((w[3] - 64 + ((w[5]      + ((w[13] - 16 + w[21] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
-
-               return 0;
-       }
-       /* 10..110. */
-       if ((cd[0] & 0xce & mask) == 0x8c) {
-               /* Range 128 format */
-               uint16_t w[29]; /* 1..28 */
-               struct gsm48_range_128 *r = (struct gsm48_range_128 *)cd;
-
-               if (len < 3)
-                       return -EINVAL;
-               memset(w, 0, sizeof(w));
-               w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
-               w[1] = r->w1;
-               if (len >= 4)
-                       w[2] = r->w2;
-               if (len >= 5)
-                       w[3] = (r->w3_hi << 4) | r->w3_lo;
-               if (len >= 6)
-                       w[4] = (r->w4_hi << 1) | r->w4_lo;
-               if (len >= 6)
-                       w[5] = r->w5;
-               if (len >= 7)
-                       w[6] = (r->w6_hi << 3) | r->w6_lo;
-               if (len >= 7)
-                       w[7] = r->w7;
-               if (len >= 8)
-                       w[8] = r->w8;
-               if (len >= 8)
-                       w[9] = r->w9;
-               if (len >= 9)
-                       w[10] = r->w10;
-               if (len >= 9)
-                       w[11] = r->w11;
-               if (len >= 10)
-                       w[12] = r->w12;
-               if (len >= 10)
-                       w[13] = r->w13;
-               if (len >= 11)
-                       w[14] = r->w14;
-               if (len >= 11)
-                       w[15] = r->w15;
-               if (len >= 12)
-                       w[16] = r->w16;
-               if (len >= 12)
-                       w[17] = r->w17;
-               if (len >= 13)
-                       w[18] = (r->w18_hi << 1) | r->w18_lo;
-               if (len >= 13)
-                       w[19] = r->w19;
-               if (len >= 13)
-                       w[20] = r->w20;
-               if (len >= 14)
-                       w[21] = (r->w21_hi << 2) | r->w21_lo;
-               if (len >= 14)
-                       w[22] = r->w22;
-               if (len >= 14)
-                       w[23] = r->w23;
-               if (len >= 15)
-                       w[24] = r->w24;
-               if (len >= 15)
-                       w[25] = r->w25;
-               if (len >= 16)
-                       w[26] = (r->w26_hi << 1) | r->w26_lo;
-               if (len >= 16)
-                       w[27] = r->w27;
-               if (len >= 16)
-                       w[28] = r->w28;
-               f[w[0]].mask |= frqt;
-               if (w[1])
-                       f[(w[0] + w[1]) % 1024].mask |= frqt;
-               if (w[2])
-                       f[(w[0] + ((w[1] - 64 + w[2] - 1) % 127) + 1) % 1024].mask |= frqt;
-               if (w[3])
-                       f[(w[0] + ((w[1]      + w[3] - 1) % 127) + 1) % 1024].mask |= frqt;
-               if (w[4])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + w[4] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[5])
-                       f[(w[0] + ((w[1]      + ((w[3] - 32 + w[5] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[6])
-                       f[(w[0] + ((w[1] - 64 + ((w[2]      + w[6] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[7])
-                       f[(w[0] + ((w[1]      + ((w[3]      + w[7] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[8])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + w[8] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[9])
-                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5] - 16 + w[9] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[10])
-                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6] - 16 + w[10] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[11])
-                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7] - 16 + w[11] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[12])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4]      + w[12] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[13])
-                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5]      + w[13] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[14])
-                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6]      + w[14] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[15])
-                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7]      + w[15] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[16])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + ((w[8] - 8 + w[16] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[17])
-                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5] - 16 + ((w[9] - 8 + w[17] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[18])
-                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6] - 16 + ((w[10] - 8 + w[18] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[19])
-                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7] - 16 + ((w[11] - 8 + w[19] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[20])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4]      + ((w[12] - 8 + w[20] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[21])
-                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5]      + ((w[13] - 8 + w[21] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[22])
-                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6]      + ((w[14] - 8 + w[22] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[23])
-                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7]      + ((w[15] - 8 + w[23] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[24])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + ((w[8]     + w[24] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[25])
-                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5] - 16 + ((w[9]     + w[25] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[26])
-                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6] - 16 + ((w[10]     + w[26] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[27])
-                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7] - 16 + ((w[11]     + w[27] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-               if (w[28])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4]      + ((w[12]     + w[28] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
-
-               return 0;
-       }
-       /* 10..111. */
-       if ((cd[0] & 0xce & mask) == 0x8e) {
-               /* Variable bitmap format (can be any length >= 3) */
-               uint16_t orig = 0;
-               struct gsm48_var_bit *r = (struct gsm48_var_bit *)cd;
-
-               if (len < 3)
-                       return -EINVAL;
-               orig = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
-               f[orig].mask |= frqt;
-               for (i = 1; 2 + (i >> 3) < len; i++)
-                       if ((cd[2 + (i >> 3)] & (0x80 >> (i & 7))))
-                               f[(orig + i) % 1024].mask |= frqt;
-
-               return 0;
-       }
-
-       return 0;
+       return gsm48_decode_freq_list(f, cd, len, mask, frqt);
 }
 
 /* decode "Cell Selection Parameters" (10.5.2.4) */
@@ -1777,7 +1649,7 @@ static int gsm48_decode_ccd(struct gsm48_sysinfo *s,
 }
 
 /* decode "Mobile Allocation" (10.5.2.21) */
-static int gsm48_decode_mobile_alloc(struct gsm48_sysinfo *s,
+static int gsm48_decode_mobile_alloc(struct gsm_sysinfo_freq *freq,
        uint8_t *ma, uint8_t len, uint16_t *hopping, uint8_t *hopp_len, int si4)
 {
        int i, j = 0;
@@ -1791,12 +1663,12 @@ static int gsm48_decode_mobile_alloc(struct gsm48_sysinfo *s,
        *hopp_len = 0;
        if (si4) {
                for (i = 0; i < 1024; i++)
-                       s->freq[i].mask &= ~FREQ_TYPE_HOPP;
+                       freq[i].mask &= ~FREQ_TYPE_HOPP;
        }
 
        /* generating list of all frequencies (1..1023,0) */
        for (i = 1; i <= 1024; i++) {
-               if ((s->freq[i & 1023].mask & FREQ_TYPE_SERV)) {
+               if ((freq[i & 1023].mask & FREQ_TYPE_SERV)) {
                        LOGP(DRR, LOGL_INFO, "Serving cell ARFCN #%d: %d\n",
                                j, i & 1023);
                        f[j++] = i & 1023;
@@ -1823,7 +1695,7 @@ static int gsm48_decode_mobile_alloc(struct gsm48_sysinfo *s,
                        }
                        hopping[(*hopp_len)++] = f[i];
                        if (si4)
-                               s->freq[f[i]].mask |= FREQ_TYPE_HOPP;
+                               freq[f[i]].mask |= FREQ_TYPE_HOPP;
                }
        }
 
@@ -1924,11 +1796,46 @@ static int gsm48_decode_si6_rest(struct gsm48_sysinfo *s, uint8_t *si,
 }
 
 /* send sysinfo event to other layers */
-static int gsm48_send_sysinfo(struct osmocom_ms *ms, uint8_t type)
+static int gsm48_new_sysinfo(struct osmocom_ms *ms, uint8_t type)
 {
+       struct gsm48_sysinfo *s = ms->cellsel.si;
        struct msgb *nmsg;
        struct gsm322_msg *em;
 
+       /* update list of measurements, if BA(SACCH) is complete and new */
+       if (s
+        && (type == GSM48_MT_RR_SYSINFO_5
+         || type == GSM48_MT_RR_SYSINFO_5bis
+         || type == GSM48_MT_RR_SYSINFO_5ter)
+        && s->si5
+        && (!s->nb_ext_ind_si5
+         || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
+         || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
+               && s->nb_ext_ind_si5bis))) {
+               struct gsm48_rr_meas *rrmeas = &ms->rrlayer.meas;
+               int n = 0, i;
+
+               LOGP(DRR, LOGL_NOTICE, "Complete set of SI5* for BA(%d)\n",
+                       s->nb_ba_ind_si5);
+               rrmeas->nc_num = 0;
+               for (i = 1; i <= 1024; i++) {
+                       if ((s->freq[i & 1023].mask & FREQ_TYPE_REP)) {
+                               if (n == 32) {
+                                       LOGP(DRR, LOGL_NOTICE, "SI5* report "
+                                               "exceeds 32 BCCHs\n");
+                                       break;
+                               }
+                               LOGP(DRR, LOGL_NOTICE, "SI5* report arfcn %d\n",
+                                       i & 1023);
+                               rrmeas->nc_arfcn[n] = i & 1023;
+                               rrmeas->nc_rxlev[n] = -128;
+                               n++;
+                       }
+               }
+               rrmeas->nc_num = n;
+       }
+
+       /* send sysinfo event to other layers */
        nmsg = gsm322_msgb_alloc(GSM322_EVENT_SYSINFO);
        if (!nmsg)
                return -ENOMEM;
@@ -1971,7 +1878,7 @@ static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg)
        LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 1\n");
 
        /* Cell Channel Description */
-       gsm48_decode_freq_list(&ms->support, s->freq,
+       decode_freq_list(&ms->support, s->freq,
                si->cell_channel_description,
                sizeof(si->cell_channel_description), 0xce, FREQ_TYPE_SERV);
        /* RACH Control Parameter */
@@ -1982,7 +1889,7 @@ static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg)
 
        s->si1 = 1;
 
-       return gsm48_send_sysinfo(ms, si->header.system_information);
+       return gsm48_new_sysinfo(ms, si->header.system_information);
 }
 
 /* receive "SYSTEM INFORMATION 2" message (9.1.32) */
@@ -2003,7 +1910,6 @@ static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg)
                        "message.\n");
                return -EINVAL;
        }
-//printf("len = %d\n", MIN(msgb_l3len(msg), sizeof(s->si2_msg)));
 
        if (!memcmp(si, s->si2_msg, MIN(msgb_l3len(msg), sizeof(s->si2_msg))))
                return 0;
@@ -2014,16 +1920,16 @@ static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg)
        /* Neighbor Cell Description */
        s->nb_ext_ind_si2 = (si->bcch_frequency_list[0] >> 6) & 1;
        s->nb_ba_ind_si2 = (si->bcch_frequency_list[0] >> 5) & 1;
-       gsm48_decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
+       decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
                sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2);
        /* NCC Permitted */
-       s->nb_ncc_permitted = si->ncc_permitted;
+       s->nb_ncc_permitted_si2 = si->ncc_permitted;
        /* RACH Control Parameter */
        gsm48_decode_rach_ctl_neigh(s, &si->rach_control);
 
        s->si2 = 1;
 
-       return gsm48_send_sysinfo(ms, si->header.system_information);
+       return gsm48_new_sysinfo(ms, si->header.system_information);
 }
 
 /* receive "SYSTEM INFORMATION 2bis" message (9.1.33) */
@@ -2055,16 +1961,15 @@ static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg)
        /* Neighbor Cell Description */
        s->nb_ext_ind_si2bis = (si->bcch_frequency_list[0] >> 6) & 1;
        s->nb_ba_ind_si2bis = (si->bcch_frequency_list[0] >> 5) & 1;
-       gsm48_decode_freq_list(&ms->support, s->freq,
+       decode_freq_list(&ms->support, s->freq,
                si->bcch_frequency_list,
-               sizeof(si->bcch_frequency_list), 0x8e,
-               FREQ_TYPE_NCELL_2bis);
+               sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2bis);
        /* RACH Control Parameter */
        gsm48_decode_rach_ctl_neigh(s, &si->rach_control);
 
        s->si2bis = 1;
 
-       return gsm48_send_sysinfo(ms, si->header.system_information);
+       return gsm48_new_sysinfo(ms, si->header.system_information);
 }
 
 /* receive "SYSTEM INFORMATION 2ter" message (9.1.34) */
@@ -2095,14 +2000,15 @@ static int gsm48_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg)
 
        /* Neighbor Cell Description 2 */
        s->nb_multi_rep_si2ter = (si->ext_bcch_frequency_list[0] >> 6) & 3;
-       gsm48_decode_freq_list(&ms->support, s->freq,
+       s->nb_ba_ind_si2ter = (si->ext_bcch_frequency_list[0] >> 5) & 1;
+       decode_freq_list(&ms->support, s->freq,
                si->ext_bcch_frequency_list,
                sizeof(si->ext_bcch_frequency_list), 0x8e,
-               FREQ_TYPE_NCELL_2ter);
+                       FREQ_TYPE_NCELL_2ter);
 
        s->si2ter = 1;
 
-       return gsm48_send_sysinfo(ms, si->header.system_information);
+       return gsm48_new_sysinfo(ms, si->header.system_information);
 }
 
 /* receive "SYSTEM INFORMATION 3" message (9.1.35) */
@@ -2159,7 +2065,7 @@ static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg)
                l1ctl_tx_ccch_mode_req(ms, cs->ccch_mode);
        }
 
-       return gsm48_send_sysinfo(ms, si->header.system_information);
+       return gsm48_new_sysinfo(ms, si->header.system_information);
 }
 
 /* receive "SYSTEM INFORMATION 4" message (9.1.36) */
@@ -2220,8 +2126,8 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
        if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_MOB_AL) {
                if (payload_len < 1 || payload_len < 2 + data[1])
                        goto short_read;
-               gsm48_decode_mobile_alloc(s, data + 2, si->data[1], s->hopping,
-                       &s->hopp_len, 1);
+               gsm48_decode_mobile_alloc(s->freq, data + 2, si->data[1],
+                       s->hopping, &s->hopp_len, 1);
                payload_len -= 2 + data[1];
                data += 2 + data[1];
        }
@@ -2235,7 +2141,7 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
 
        s->si4 = 1;
 
-       return gsm48_send_sysinfo(ms, si->header.system_information);
+       return gsm48_new_sysinfo(ms, si->header.system_information);
 }
 
 /* receive "SYSTEM INFORMATION 5" message (9.1.37) */
@@ -2267,12 +2173,12 @@ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg)
        /* Neighbor Cell Description */
        s->nb_ext_ind_si5 = (si->bcch_frequency_list[0] >> 6) & 1;
        s->nb_ba_ind_si5 = (si->bcch_frequency_list[0] >> 5) & 1;
-       gsm48_decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
+       decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
                sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5);
 
        s->si5 = 1;
 
-       return gsm48_send_sysinfo(ms, si->system_information);
+       return gsm48_new_sysinfo(ms, si->system_information);
 }
 
 /* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */
@@ -2305,12 +2211,12 @@ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg)
        /* Neighbor Cell Description */
        s->nb_ext_ind_si5bis = (si->bcch_frequency_list[0] >> 6) & 1;
        s->nb_ba_ind_si5bis = (si->bcch_frequency_list[0] >> 5) & 1;
-       gsm48_decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
+       decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
                sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5bis);
 
        s->si5bis = 1;
 
-       return gsm48_send_sysinfo(ms, si->system_information);
+       return gsm48_new_sysinfo(ms, si->system_information);
 }
 
 /* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */
@@ -2341,12 +2247,14 @@ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg)
        memcpy(s->si5t_msg, si, MIN(msgb_l3len(msg), sizeof(s->si5t_msg)));
 
        /* Neighbor Cell Description */
-       gsm48_decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
-               sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5ter);
+       s->nb_multi_rep_si5ter = (si->bcch_frequency_list[0] >> 6) & 3;
+       s->nb_ba_ind_si5ter = (si->bcch_frequency_list[0] >> 5) & 1;
+       decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
+               sizeof(si->bcch_frequency_list), 0x8e, FREQ_TYPE_REP_5ter);
 
        s->si5ter = 1;
 
-       return gsm48_send_sysinfo(ms, si->system_information);
+       return gsm48_new_sysinfo(ms, si->system_information);
 }
 
 /* receive "SYSTEM INFORMATION 6" message (9.1.39) */
@@ -2383,7 +2291,7 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
        /* Cell Options (SACCH) */
        gsm48_decode_cellopt_sacch(s, &si->cell_options);
        /* NCC Permitted */
-       s->nb_ncc_permitted = si->ncc_permitted;
+       s->nb_ncc_permitted_si6 = si->ncc_permitted;
        /* SI 6 Rest Octets */
        if (payload_len >= 4)
                gsm48_decode_si6_rest(s, si->rest_octets, payload_len);
@@ -2394,7 +2302,7 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
 
        s->si6 = 1;
 
-       return gsm48_send_sysinfo(ms, si->system_information);
+       return gsm48_new_sysinfo(ms, si->system_information);
 }
 
 /*
@@ -2412,6 +2320,7 @@ static int gsm48_rr_chan2cause[4] = {
 /* given LV of mobile identity is checked agains ms */
 static int gsm_match_mi(struct osmocom_ms *ms, uint8_t *mi)
 {
+       struct gsm322_cellsel *cs = &ms->cellsel;
        char imsi[16];
        uint32_t tmsi;
        uint8_t mi_type;
@@ -2425,7 +2334,9 @@ static int gsm_match_mi(struct osmocom_ms *ms, uint8_t *mi)
                        return 0;
                memcpy(&tmsi, mi+2, 4);
                if (ms->subscr.tmsi == ntohl(tmsi)
-                && ms->subscr.tmsi_valid) {
+                && ms->subscr.mcc == cs->sel_mcc
+                && ms->subscr.mnc == cs->sel_mnc
+                && ms->subscr.lac == cs->sel_lac) {
                        LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n",
                                ntohl(tmsi));
 
@@ -2540,7 +2451,9 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg)
        chan_2 = pa->cneed2;
        /* first MI */
        if (ms->subscr.tmsi == ntohl(pa->tmsi1)
-        && ms->subscr.tmsi_valid) {
+        && ms->subscr.mcc == cs->sel_mcc
+        && ms->subscr.mnc == cs->sel_mnc
+        && ms->subscr.lac == cs->sel_lac) {
                LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi1));
                return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1);
        } else
@@ -2548,7 +2461,9 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg)
                        ntohl(pa->tmsi1));
        /* second MI */
        if (ms->subscr.tmsi == ntohl(pa->tmsi2)
-        && ms->subscr.tmsi_valid) {
+        && ms->subscr.mcc == cs->sel_mcc
+        && ms->subscr.mnc == cs->sel_mnc
+        && ms->subscr.lac == cs->sel_lac) {
                LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi2));
                return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1);
        } else
@@ -2602,7 +2517,9 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
        chan_4 = pa->cneed4;
        /* first MI */
        if (ms->subscr.tmsi == ntohl(pa->tmsi1)
-        && ms->subscr.tmsi_valid) {
+        && ms->subscr.mcc == cs->sel_mcc
+        && ms->subscr.mnc == cs->sel_mnc
+        && ms->subscr.lac == cs->sel_lac) {
                LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi1));
                return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1);
        } else
@@ -2610,7 +2527,9 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
                        ntohl(pa->tmsi1));
        /* second MI */
        if (ms->subscr.tmsi == ntohl(pa->tmsi2)
-        && ms->subscr.tmsi_valid) {
+        && ms->subscr.mcc == cs->sel_mcc
+        && ms->subscr.mnc == cs->sel_mnc
+        && ms->subscr.lac == cs->sel_lac) {
                LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi2));
                return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1);
        } else
@@ -2618,7 +2537,9 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
                        ntohl(pa->tmsi2));
        /* thrid MI */
        if (ms->subscr.tmsi == ntohl(pa->tmsi3)
-        && ms->subscr.tmsi_valid) {
+        && ms->subscr.mcc == cs->sel_mcc
+        && ms->subscr.mnc == cs->sel_mnc
+        && ms->subscr.lac == cs->sel_lac) {
                LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi3));
                return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1);
        } else
@@ -2626,7 +2547,9 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
                        ntohl(pa->tmsi3));
        /* fourth MI */
        if (ms->subscr.tmsi == ntohl(pa->tmsi4)
-        && ms->subscr.tmsi_valid) {
+        && ms->subscr.mcc == cs->sel_mcc
+        && ms->subscr.mnc == cs->sel_mnc
+        && ms->subscr.lac == cs->sel_lac) {
                LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi4));
                return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_4], 1);
        } else
@@ -2684,9 +2607,12 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
        int ma_len = msgb_l3len(msg) - sizeof(*ia);
        uint8_t ch_type, ch_subch, ch_ts;
        struct gsm48_rr_cd cd;
+#ifndef TEST_STARTING_TIMER
        uint8_t *st, st_len;
+#endif
 
        memset(&cd, 0, sizeof(cd));
+       cd.ind_tx_power = rr->cd_now.ind_tx_power;
 
        if (ma_len < 0 /* mobile allocation IE must be included */
         || ia->mob_alloc_len > ma_len) { /* short read of IE */
@@ -2701,10 +2627,16 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
        }
 
        /* starting time */
+#ifdef TEST_STARTING_TIMER
+       cd.start = 1;
+       cd.start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432;
+       LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n");
+#else
        st_len = ma_len - ia->mob_alloc_len;
        st = ia->mob_alloc + ia->mob_alloc_len;
        if (st_len >= 3 && st[0] == GSM48_IE_START_TIME)
                gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1));
+#endif
 
        /* decode channel description */
        LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:\n");
@@ -2747,11 +2679,15 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
                /* channel description */
                memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now));
                /* timing advance */
-               rr->cd_now.ta = ia->timing_advance;
+               rr->cd_now.ind_ta = ia->timing_advance;
                /* mobile allocation */
                memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len, 
                        ia->mob_alloc_len + 1);
                rr->wait_assign = 2;
+               /* reset scheduler */
+               LOGP(DRR, LOGL_INFO, "resetting scheduler\n");
+               l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
+
                return gsm48_rr_dl_est(ms);
        }
        LOGP(DRR, LOGL_INFO, "Request, but not for us.\n");
@@ -2767,10 +2703,14 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
        int ma_len = msgb_l3len(msg) - sizeof(*ia);
        uint8_t ch_type, ch_subch, ch_ts;
        struct gsm48_rr_cd cd1, cd2;
+#ifndef TEST_STARTING_TIMER
        uint8_t *st, st_len;
+#endif
 
        memset(&cd1, 0, sizeof(cd1));
+       cd1.ind_tx_power = rr->cd_now.ind_tx_power;
        memset(&cd2, 0, sizeof(cd2));
+       cd2.ind_tx_power = rr->cd_now.ind_tx_power;
 
        if (ma_len < 0 /* mobile allocation IE must be included */
         || ia->mob_alloc_len > ma_len) { /* short read of IE */
@@ -2784,6 +2724,12 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
                return -EINVAL;
        }
 
+#ifdef TEST_STARTING_TIMER
+       cd1.start = 1;
+       cd2.start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432;
+       memcpy(&cd2, &cd1, sizeof(cd2));
+       LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n");
+#else
        /* starting time */
        st_len = ma_len - ia->mob_alloc_len;
        st = ia->mob_alloc + ia->mob_alloc_len;
@@ -2792,10 +2738,11 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
                        (struct gsm48_start_time *)(st+1));
                memcpy(&cd2, &cd1, sizeof(cd2));
        }
+#endif
 
        /* decode channel description */
        LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT EXTENDED:\n");
-       cd2.chan_nr = ia->chan_desc1.chan_nr;
+       cd1.chan_nr = ia->chan_desc1.chan_nr;
        rsl_dec_chan_nr(cd1.chan_nr, &ch_type, &ch_subch, &ch_ts);
        if (ia->chan_desc1.h0.h) {
                cd1.h = 1;
@@ -2856,23 +2803,31 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
                /* channel description */
                memcpy(&rr->cd_now, &cd1, sizeof(rr->cd_now));
                /* timing advance */
-               rr->cd_now.ta = ia->timing_advance1;
+               rr->cd_now.ind_ta = ia->timing_advance1;
                /* mobile allocation */
                memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len,
                        ia->mob_alloc_len + 1);
                rr->wait_assign = 2;
+               /* reset scheduler */
+               LOGP(DRR, LOGL_INFO, "resetting scheduler\n");
+               l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
+
                return gsm48_rr_dl_est(ms);
        }
-       /* request ref 1 */
+       /* request ref 2 */
        if (gsm48_match_ra(ms, &ia->req_ref2)) {
                /* channel description */
                memcpy(&rr->cd_now, &cd2, sizeof(rr->cd_now));
                /* timing advance */
-               rr->cd_now.ta = ia->timing_advance2;
+               rr->cd_now.ind_ta = ia->timing_advance2;
                /* mobile allocation */
                memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len,
                        ia->mob_alloc_len + 1);
                rr->wait_assign = 2;
+               /* reset scheduler */
+               LOGP(DRR, LOGL_INFO, "resetting scheduler\n");
+               l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
+
                return gsm48_rr_dl_est(ms);
        }
        LOGP(DRR, LOGL_INFO, "Request, but not for us.\n");
@@ -2930,7 +2885,7 @@ static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg)
        return 0;
 }
 
-/* 9.1.1 ADDITIONAL ASSIGMENT is received  */
+/* 9.1.1 ADDITIONAL ASSIGMENT is received */
 static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_hdr *gh = msgb_l3(msg);
@@ -2956,14 +2911,91 @@ static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg)
 static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
-       struct gsm48_rr_meas *meas = &rr->meas;
+       struct gsm48_sysinfo *s = ms->cellsel.si;
+       struct rx_meas_stat *meas = &rr->ms->meas;
+       struct gsm48_rr_meas *rrmeas = &rr->meas;
        struct msgb *nmsg;
        struct gsm48_hdr *gh;
        struct gsm48_meas_res *mr;
+       uint8_t serv_rxlev_full = 0, serv_rxlev_sub = 0, serv_rxqual_full = 0,
+               serv_rxqual_sub = 0;
+       uint8_t ta, tx_power;
+       uint8_t rep_ba = 0, rep_valid = 0, meas_valid = 0, multi_rep = 0;
+       uint8_t n = 0, rxlev_nc[6], bsic_nc[6], bcch_f_nc[6];
+
+       /* just in case! */
+       if (!s)
+               return -EINVAL;
+
+       /* check if SI5* is completely received, check BA-IND */
+       if (s->si5
+        && (!s->nb_ext_ind_si5
+         || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
+         || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
+                       && s->nb_ext_ind_si5bis))) {
+               rep_ba = s->nb_ba_ind_si5;
+               if ((s->si5bis && s->nb_ext_ind_si5
+                 && s->nb_ba_ind_si5bis != rep_ba)
+                || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
+                 && s->nb_ext_ind_si5bis && s->nb_ba_ind_si5ter != rep_ba)) {
+                       LOGP(DRR, LOGL_NOTICE, "BA-IND missmatch on SI5*");
+               } else
+                       rep_valid = 1;
+       }
+
+       /* check for valid measurements, any frame must exist */
+       if (meas->frames) {
+               meas_valid = 1;
+               serv_rxlev_full = serv_rxlev_sub = meas->rxlev / meas->frames;
+               serv_rxqual_full = serv_rxqual_sub = 0; // FIXME
+       }
+
+       memset(&rxlev_nc, 0, sizeof(rxlev_nc));
+       memset(&bsic_nc, 0, sizeof(bsic_nc));
+       memset(&bcch_f_nc, 0, sizeof(bcch_f_nc));
+       if (rep_valid) {
+               int8_t strongest, current;
+               uint8_t ncc;
+               int i, index;
+
+               /* multiband reporting, if not: 0 = normal reporting */
+               if (s->si5 && s->si5bis && s->si5ter && s->nb_ext_ind_si5
+                && s->nb_ext_ind_si5bis)
+                       multi_rep = s->nb_multi_rep_si5ter;
+
+               /* get 6 strongest measurements */
+               // FIXME: multiband report
+               strongest = 127; /* infinite */
+               for (n = 0; n < 6; n++) {
+                       current = -128; /* -infinite */
+                       index = 0;
+                       for (i = 0; i < rrmeas->nc_num; i++) {
+                               /* only check if NCC is permitted */
+                               ncc = rrmeas->nc_bsic[i] >> 3;
+                               if ((s->nb_ncc_permitted_si6 & (1 << ncc))
+                                && rrmeas->nc_rxlev[i] > current
+                                && rrmeas->nc_rxlev[i] < strongest) {
+                                       current = rrmeas->nc_rxlev[i];
+                                       index = i;
+                               }
+                       }
+                       if (current == -128) /* no more found */
+                               break;
+                       rxlev_nc[n] = rrmeas->nc_rxlev[index] + 110;
+                       bsic_nc[n] = rrmeas->nc_bsic[index];
+                       bcch_f_nc[n] = index;
+               }
+       }
 
        nmsg = gsm48_l3_msgb_alloc();
        if (!nmsg)
                return -ENOMEM;
+
+       /* use indicated tx-power and TA (not the altered ones) */
+       tx_power = rr->cd_now.ind_tx_power;
+       // FIXME: degrade power to the max supported level
+       ta = rr->cd_now.ind_ta;
+
        gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
        mr = (struct gsm48_meas_res *) msgb_put(nmsg, sizeof(*mr));
 
@@ -2971,51 +3003,68 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
        gh->msg_type = GSM48_MT_RR_MEAS_REP;
 
        /* measurement results */
-       mr->rxlev_full = meas->rxlev_full;
-       mr->rxlev_sub = meas->rxlev_sub;
-       mr->rxqual_full = meas->rxqual_full;
-       mr->rxqual_sub = meas->rxqual_sub;
-       mr->dtx_used = meas->dtx;
-       mr->ba_used = meas->ba;
-       mr->meas_valid = meas->meas_valid;
-       if (meas->ncell_na) {
+       mr->rxlev_full = serv_rxlev_full;
+       mr->rxlev_sub = serv_rxlev_sub;
+       mr->rxqual_full = serv_rxqual_full;
+       mr->rxqual_sub = serv_rxqual_sub;
+       mr->dtx_used = 0; // FIXME: no DTX yet
+       mr->ba_used = rep_ba;
+       mr->meas_valid = !meas_valid; /* 0 = valid */
+       if (rep_valid) {
+               mr->no_nc_n_hi = n >> 2;
+               mr->no_nc_n_lo = n & 3;
+       } else {
                /* no results for serving cells */
                mr->no_nc_n_hi = 1;
                mr->no_nc_n_lo = 3;
-       } else {
-               mr->no_nc_n_hi = meas->count >> 2;
-               mr->no_nc_n_lo = meas->count & 3;
-       }
-       mr->rxlev_nc1 = meas->rxlev_nc[0];
-       mr->rxlev_nc2_hi = meas->rxlev_nc[1] >> 1;
-       mr->rxlev_nc2_lo = meas->rxlev_nc[1] & 1;
-       mr->rxlev_nc3_hi = meas->rxlev_nc[2] >> 2;
-       mr->rxlev_nc3_lo = meas->rxlev_nc[2] & 3;
-       mr->rxlev_nc4_hi = meas->rxlev_nc[3] >> 3;
-       mr->rxlev_nc4_lo = meas->rxlev_nc[3] & 7;
-       mr->rxlev_nc5_hi = meas->rxlev_nc[4] >> 4;
-       mr->rxlev_nc5_lo = meas->rxlev_nc[4] & 15;
-       mr->rxlev_nc6_hi = meas->rxlev_nc[5] >> 5;
-       mr->rxlev_nc6_lo = meas->rxlev_nc[5] & 31;
-       mr->bsic_nc1_hi = meas->bsic_nc[0] >> 3;
-       mr->bsic_nc1_lo = meas->bsic_nc[0] & 7;
-       mr->bsic_nc2_hi = meas->bsic_nc[1] >> 4;
-       mr->bsic_nc2_lo = meas->bsic_nc[1] & 15;
-       mr->bsic_nc3_hi = meas->bsic_nc[2] >> 5;
-       mr->bsic_nc3_lo = meas->bsic_nc[2] & 31;
-       mr->bsic_nc4 = meas->bsic_nc[3];
-       mr->bsic_nc5 = meas->bsic_nc[4];
-       mr->bsic_nc6 = meas->bsic_nc[5];
-       mr->bcch_f_nc1 = meas->bcch_f_nc[0];
-       mr->bcch_f_nc2 = meas->bcch_f_nc[1];
-       mr->bcch_f_nc3 = meas->bcch_f_nc[2];
-       mr->bcch_f_nc4 = meas->bcch_f_nc[3];
-       mr->bcch_f_nc5_hi = meas->bcch_f_nc[4] >> 1;
-       mr->bcch_f_nc5_lo = meas->bcch_f_nc[4] & 1;
-       mr->bcch_f_nc6_hi = meas->bcch_f_nc[5] >> 2;
-       mr->bcch_f_nc6_lo = meas->bcch_f_nc[5] & 3;
-
-       return gsm48_send_rsl(ms, RSL_MT_UNIT_DATA_REQ, nmsg);
+       }
+       mr->rxlev_nc1 = rxlev_nc[0];
+       mr->rxlev_nc2_hi = rxlev_nc[1] >> 1;
+       mr->rxlev_nc2_lo = rxlev_nc[1] & 1;
+       mr->rxlev_nc3_hi = rxlev_nc[2] >> 2;
+       mr->rxlev_nc3_lo = rxlev_nc[2] & 3;
+       mr->rxlev_nc4_hi = rxlev_nc[3] >> 3;
+       mr->rxlev_nc4_lo = rxlev_nc[3] & 7;
+       mr->rxlev_nc5_hi = rxlev_nc[4] >> 4;
+       mr->rxlev_nc5_lo = rxlev_nc[4] & 15;
+       mr->rxlev_nc6_hi = rxlev_nc[5] >> 5;
+       mr->rxlev_nc6_lo = rxlev_nc[5] & 31;
+       mr->bsic_nc1_hi = bsic_nc[0] >> 3;
+       mr->bsic_nc1_lo = bsic_nc[0] & 7;
+       mr->bsic_nc2_hi = bsic_nc[1] >> 4;
+       mr->bsic_nc2_lo = bsic_nc[1] & 15;
+       mr->bsic_nc3_hi = bsic_nc[2] >> 5;
+       mr->bsic_nc3_lo = bsic_nc[2] & 31;
+       mr->bsic_nc4 = bsic_nc[3];
+       mr->bsic_nc5 = bsic_nc[4];
+       mr->bsic_nc6 = bsic_nc[5];
+       mr->bcch_f_nc1 = bcch_f_nc[0];
+       mr->bcch_f_nc2 = bcch_f_nc[1];
+       mr->bcch_f_nc3 = bcch_f_nc[2];
+       mr->bcch_f_nc4 = bcch_f_nc[3];
+       mr->bcch_f_nc5_hi = bcch_f_nc[4] >> 1;
+       mr->bcch_f_nc5_lo = bcch_f_nc[4] & 1;
+       mr->bcch_f_nc6_hi = bcch_f_nc[5] >> 2;
+       mr->bcch_f_nc6_lo = bcch_f_nc[5] & 3;
+
+       LOGP(DRR, LOGL_INFO, "MEAS REP: pwr=%d TA=%d meas-invalid=%d "
+               "rxlev-full=%d rxlev-sub=%d rxqual-full=%d rxqual-sub=%d "
+               "dtx %d ba %d no-ncell-n %d\n", tx_power, ta, mr->meas_valid,
+               mr->rxlev_full - 110, mr->rxlev_sub - 110,
+               mr->rxqual_full, mr->rxqual_sub, mr->dtx_used, mr->ba_used,
+               (mr->no_nc_n_hi << 2) | mr->no_nc_n_lo);
+
+       msgb_tv16_push(nmsg, RSL_IE_L3_INFO,
+               nmsg->tail - (uint8_t *)msgb_l3(nmsg));
+       msgb_push(nmsg, 2 + 2);
+       nmsg->data[0] = RSL_IE_TIMING_ADVANCE;
+       nmsg->data[1] = ta;
+       nmsg->data[2] = RSL_IE_MS_POWER;
+       nmsg->data[3] = tx_power;
+       rsl_rll_push_hdr(nmsg, RSL_MT_UNIT_DATA_REQ, rr->cd_now.chan_nr,
+               0x40, 1);
+
+       return rslms_recvmsg(nmsg, ms);
 }
 
 /*
@@ -3086,40 +3135,264 @@ int gsm48_rr_los(struct osmocom_ms *ms)
        return 0;
 }
 
-/* activate link and send establish request */
-static int gsm48_rr_dl_est(struct osmocom_ms *ms)
+/* activation of channel in dedicated mode */
+static int gsm48_rr_activate_channel(struct osmocom_ms *ms,
+       struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len)
 {
-       struct gsm48_rrlayer *rr = &ms->rrlayer;
        struct gsm_subscriber *subscr = &ms->subscr;
-       struct gsm322_cellsel *cs = &ms->cellsel;
-       struct gsm48_sysinfo *s = cs->si;
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
        struct gsm_settings *set = &ms->settings;
-       struct msgb *nmsg;
-       struct gsm48_hdr *gh;
-       struct gsm48_pag_rsp *pr;
-       uint8_t mi[11];
+       struct gsm48_sysinfo *s = ms->cellsel.si;
+       struct rx_meas_stat *meas = &ms->meas;
        uint8_t ch_type, ch_subch, ch_ts;
-       uint16_t ma[64];
-       uint8_t ma_len;
 
-       if (rr->cd_now.h) {
-               gsm48_decode_mobile_alloc(s, rr->cd_now.mob_alloc_lv + 1,
-                       rr->cd_now.mob_alloc_lv[0], ma, &ma_len, 0);
-               if (ma_len < 1) {
-                       LOGP(DRR, LOGL_NOTICE, "Hopping, but no allocation\n");
-                       return -EINVAL;
-               }
-       }
+       /* setting (new) timing advance */
+       LOGP(DRR, LOGL_INFO, "setting indicated TA %d (actual TA %d)\n",
+               cd->ind_ta, cd->ind_ta - set->alter_delay);
+       l1ctl_tx_param_req(ms, cd->ind_ta - set->alter_delay,
+                       (set->alter_tx_power) ? set->alter_tx_power_value
+                                               : cd->ind_tx_power);
+       /* setting initial (invalid) measurement report, resetting SI5* */
+       if (s) {
+               memset(s->si5_msg, 0, sizeof(s->si5_msg));
+               memset(s->si5b_msg, 0, sizeof(s->si5b_msg));
+               memset(s->si5t_msg, 0, sizeof(s->si5t_msg));
+               memset(meas, 0, sizeof(*meas));
+       }
+       rr->meas.nc_num = 0;
+       stop_rr_t_meas(rr);
+       start_rr_t_meas(rr, 1, 0);
+       gsm48_rr_tx_meas_rep(ms);
+
+       /* establish */
+       LOGP(DRR, LOGL_INFO, "establishing channel in dedicated mode\n");
+       rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts);
+       LOGP(DRR, LOGL_INFO, " Channel type %d, subch %d, ts %d, mode %d\n",
+               ch_type, ch_subch, ch_ts, cd->mode);
+       if (cd->h)
+               l1ctl_tx_dm_est_req_h1(ms, cd->maio, cd->hsn,
+                       ma, ma_len, cd->chan_nr, cd->tsc);
+       else
+               l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc);
+       rr->dm_est = 1;
 
-       /* 3.3.1.1.3.1 */
-       stop_rr_t3126(rr);
+       if (rr->cipher_on)
+               l1ctl_tx_crypto_req(ms, rr->cipher_type + 1, subscr->key, 8);
 
-       /* send DL_EST_REQ */
+       gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode);
+
+       return 0;
+}
+
+/* frequency change of channel "after time" */
+static int gsm48_rr_channel_after_time(struct osmocom_ms *ms,
+       struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len, uint16_t fn)
+{
+       struct gsm_subscriber *subscr = &ms->subscr;
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+       if (cd->h)
+               l1ctl_tx_dm_freq_req_h1(ms, cd->maio, cd->hsn,
+                       ma, ma_len, cd->tsc, fn);
+       else
+               l1ctl_tx_dm_freq_req_h0(ms, cd->arfcn, cd->tsc, fn);
+
+       if (rr->cipher_on)
+               l1ctl_tx_crypto_req(ms, rr->cipher_type + 1, subscr->key, 8);
+
+       gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode);
+
+       return 0;
+}
+
+/* render list of hopping channels from channel description elements */
+static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd,
+       uint16_t *ma, uint8_t *ma_len)
+{
+       struct gsm48_sysinfo *s = ms->cellsel.si;
+       struct gsm_support *sup = &ms->support;
+       int i;
+       uint16_t arfcn;
+
+       /* no hopping (no MA), channel description is valid */
+       if (!cd->h) {
+               ma_len = 0;
+               return 0;
+       }
+
+       /* decode mobile allocation */
+       if (cd->mob_alloc_lv[0]) {
+               struct gsm_sysinfo_freq *freq = s->freq;
+       
+               LOGP(DRR, LOGL_INFO, "decoding mobile allocation\n");
+
+               if (cd->cell_desc_lv[0]) {
+                       LOGP(DRR, LOGL_INFO, "using cell channel descr.\n");
+                       if (cd->cell_desc_lv[0] != 16) {
+                               LOGP(DRR, LOGL_ERROR, "cell channel descr. "
+                                       "has invalid lenght\n");
+                               return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
+                       }
+                       gsm48_decode_freq_list(freq, cd->cell_desc_lv + 1, 16,
+                               0xce, FREQ_TYPE_SERV);
+               }
+
+               gsm48_decode_mobile_alloc(freq, cd->mob_alloc_lv + 1,
+                       cd->mob_alloc_lv[0], ma, ma_len, 0);
+               if (*ma_len < 1) {
+                       LOGP(DRR, LOGL_NOTICE, "mobile allocation with no "
+                               "frequency available\n");
+                       return GSM48_RR_CAUSE_NO_CELL_ALLOC_A;
+
+               }
+       } else
+       /* decode frequency list */
+       if (cd->freq_list_lv[0]) {
+               struct gsm_sysinfo_freq f[1024];
+               int j = 0;
+
+               LOGP(DRR, LOGL_INFO, "decoding frequency list\n");
+
+               /* get bitmap */
+               if (gsm48_decode_freq_list(f, cd->freq_list_lv + 1,
+                       cd->freq_list_lv[0], 0xce, FREQ_TYPE_SERV)) {
+                       LOGP(DRR, LOGL_NOTICE, "frequency list invalid\n");
+                       return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
+               }
+
+               /* collect channels from bitmap (1..1023,0) */
+               for (i = 1; i <= 1024; i++) {
+                       if ((f[i & 1023].mask & FREQ_TYPE_SERV)) {
+                               LOGP(DRR, LOGL_INFO, "Listed ARFCN #%d: %d\n",
+                                       j, i);
+                               if (j == 64) {
+                                       LOGP(DRR, LOGL_NOTICE, "frequency list "
+                                               "exceeds 64 entries!\n");
+                                       return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
+                               }
+                               ma[j++] = i;
+                       }
+               }
+               *ma_len = j;
+               return 0;
+       } else
+       /* decode frequency channel sequence */
+       if (cd->freq_seq_lv[0]) {
+               int j = 0, inc;
+
+               LOGP(DRR, LOGL_INFO, "decoding frequency channel sequence\n");
+
+               if (cd->freq_seq_lv[0] != 9) {
+                       LOGP(DRR, LOGL_NOTICE, "invalid frequency channel "
+                               "sequence\n");
+                       return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
+               }
+               arfcn = cd->freq_seq_lv[1] & 0x7f;
+               LOGP(DRR, LOGL_INFO, "Listed Sequence ARFCN #%d: %d\n", j,
+                       arfcn);
+               ma[j++] = arfcn;
+               for (i = 0; i <= 16; i++) {
+                       if ((i & 1))
+                               inc = cd->freq_seq_lv[2 + (i >> 1)] & 0x0f;
+                       else
+                               inc = cd->freq_seq_lv[2 + (i >> 1)] >> 4;
+                       if (inc) {
+                               arfcn += inc;
+                               LOGP(DRR, LOGL_INFO, "Listed Sequence ARFCN "
+                                       "#%d: %d\n", j, arfcn);
+                               ma[j++] = arfcn;
+                       } else
+                               arfcn += 15;
+               }
+               *ma_len = j;
+               return 0;
+       } else {
+               LOGP(DRR, LOGL_NOTICE, "hopping, but nothing that tells us "
+                       "a sequence\n");
+               return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
+       }
+
+       /* check for unsported frequency */
+       for (i = 0; i < *ma_len; i++) {
+               arfcn = ma[i];
+               if (!(sup->freq_map[arfcn >> 3] & (1 << (arfcn & 7)))) {
+                       LOGP(DRR, LOGL_NOTICE, "Hopping frequency %d not "
+                               "supported\n", arfcn);
+                       return GSM48_RR_CAUSE_FREQ_NOT_IMPL;
+               }
+       }
+
+       return 0;
+}
+
+/* activate link and send establish request */
+static int gsm48_rr_dl_est(struct osmocom_ms *ms)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct gsm322_cellsel *cs = &ms->cellsel;
+       struct gsm_subscriber *subscr = &ms->subscr;
+       struct msgb *nmsg;
+       struct gsm48_hdr *gh;
+       struct gsm48_pag_rsp *pr;
+       uint8_t mi[11];
+       uint16_t ma[64];
+       uint8_t ma_len;
+
+       /* check if we have to change channel at starting time (we delay) */
+       if (rr->cd_now.start) {
+               int32_t now, start, diff;
+               uint32_t start_mili = 0;
+
+               /* how much time do we have left? */
+               now = ms->meas.last_fn % 42432;
+               start = rr->cd_now.start_tm.fn % 42432;
+               diff = start - now;
+               if (diff < 0)
+                       diff += 42432;
+               LOGP(DRR, LOGL_INFO, " (Tnow %d Tstart %d diff %d)\n",
+                       now, start, diff);
+               start_mili = (uint32_t)diff * 19580 / 42432 * 10;
+               if (diff >= 32024 || !start_mili) {
+                       LOGP(DRR, LOGL_INFO, " -> Start time already "
+                               "elapsed\n");
+                       rr->cd_now.start = 0;
+               } else {
+                       LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the "
+                               "future\n", start_mili);
+               }
+
+#ifndef TEST_FREQUENCY_MOD
+               /* schedule start of IMM.ASS */
+               rr->modify_state = GSM48_RR_MOD_IMM_ASS;
+               start_rr_t_starting(rr, start_mili / 1000,
+                       (start_mili % 1000) * 1000);
+               /* when timer fires, start time is already elapsed */
+               rr->cd_now.start = 0;
+
+               return 0;
+#endif
+       }
+
+       /* get hopping sequence, if required */
+       if (gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len))
+               return -EINVAL;
+
+       /* 3.3.1.1.3.1 */
+       stop_rr_t3126(rr);
+
+       /* clear all sequence numbers for all possible PDs */
+       rr->v_sd = 0;
+
+       /* send DL_EST_REQ */
        if (rr->rr_est_msg) {
+               LOGP(DRR, LOGL_INFO, "sending establish message\n");
+
                /* use queued message */
                nmsg = rr->rr_est_msg;
                rr->rr_est_msg = 0;
-               LOGP(DRR, LOGL_INFO, "sending establish message\n");
+
+               /* set sequence number and increment */
+               gsm48_apply_v_sd(rr, nmsg);
        } else {
                /* create paging response */
                nmsg = gsm48_l3_msgb_alloc();
@@ -3135,7 +3408,10 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms)
                pr->cm2_len = sizeof(pr->cm2);
                gsm48_rr_enc_cm2(ms, &pr->cm2);
                /* mobile identity */
-               if (ms->subscr.tmsi_valid) {
+               if (ms->subscr.tmsi != 0xffffffff
+                && ms->subscr.mcc == cs->sel_mcc
+                && ms->subscr.mnc == cs->sel_mnc
+                && ms->subscr.lac == cs->sel_lac) {
                        gsm48_generate_mid_from_tmsi(mi, subscr->tmsi);
                        LOGP(DRR, LOGL_INFO, "sending paging response with "
                                "TMSI\n");
@@ -3153,36 +3429,21 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms)
                memcpy(pr->data, mi + 1, 1 + mi[1]);
        }
 
-       /* reset scheduler */
-       LOGP(DRR, LOGL_INFO, "resetting scheduler\n");
-       l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
-
-       /* setting (new) timing advance */
-       rr->ind_ta = rr->cd_now.ta;
-       LOGP(DRR, LOGL_INFO, "setting indicated TA %d (actual TA %d)\n",
-               rr->ind_ta, rr->ind_ta - set->alter_delay);
-       l1ctl_tx_param_req(ms, rr->ind_ta - set->alter_delay,
-                       (set->alter_tx_power) ? set->alter_tx_power_value
-                                               : rr->ind_tx_power);
-
+#ifdef TEST_FREQUENCY_MOD
+       LOGP(DRR, LOGL_INFO, " TESTING: frequency modify IMM.ASS\n");
+       memcpy(&rr->cd_before, &rr->cd_now, sizeof(rr->cd_before));
+       rr->cd_before.h = 0;
+       rr->cd_before.arfcn = 0;
        /* activate channel */
-       LOGP(DRR, LOGL_INFO, "activating channel\n");
-#ifdef TODO
-       RSL_MT_ to activate channel with all the cd_now informations
+       gsm48_rr_activate_channel(ms, &rr->cd_before, ma, ma_len);
+       /* render channel "after time" */
+       gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
+       /* schedule change of channel */
+       gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len,
+               rr->cd_now.start_tm.fn);
 #else
-       rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts);
-       if ((ch_type != RSL_CHAN_SDCCH8_ACCH
-         && ch_type != RSL_CHAN_SDCCH4_ACCH) || ch_ts > 4 || ch_subch >= 4) {
-               printf("Channel type %d, subch %d, ts %d not supported, "
-                       "exitting.\n", ch_type, ch_subch, ch_ts);
-               exit(-ENOTSUP);
-       }
-       if (rr->cd_now.h)
-               l1ctl_tx_dm_est_req_h1(ms, rr->cd_now.maio, rr->cd_now.hsn,
-                       ma, ma_len, rr->cd_now.chan_nr, rr->cd_now.tsc);
-       else
-               l1ctl_tx_dm_est_req_h0(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr,
-                       rr->cd_now.tsc);
+       /* activate channel */
+       gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
 #endif
 
        /* start establishmnet */
@@ -3296,9 +3557,107 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg)
 }
 
 /*
- * chanel mode modify, assignment, and handover
+ * frequency redefition, chanel mode modify, assignment, and handover
  */
 
+/* set channel mode in case of TCH */
+static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr,
+       uint8_t mode)
+{
+       uint8_t ch_type, ch_subch, ch_ts;
+
+       /* only apply mode to TCH/F or TCH/H */
+       rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts);
+       if (ch_type != RSL_CHAN_Bm_ACCHs
+        && ch_type != RSL_CHAN_Lm_ACCHs)
+               return -ENOTSUP;
+
+       /* setting (new) timing advance */
+       LOGP(DRR, LOGL_INFO, "setting TCH mode to %d\n", mode);
+       l1ctl_tx_tch_mode_req(ms, mode);
+
+       return 0;
+}
+
+/* 9.1.13 FREQUENCY REDEFINITION is received */
+static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct gsm48_frq_redef *fr = msgb_l3(msg);
+       int mob_al_len = msgb_l3len(msg) - sizeof(*fr);
+       uint8_t ch_type, ch_subch, ch_ts;
+       struct gsm48_rr_cd cd;
+       uint8_t cause;
+       uint8_t *st;
+       uint16_t ma[64];
+       uint8_t ma_len;
+
+       memcpy(&cd, &rr->cd_now, sizeof(cd));
+
+       if (mob_al_len < 0 /* mobile allocation IE must be included */
+        || fr->mob_alloc_len + 2 > mob_al_len) { /* short read of IE */
+               LOGP(DRR, LOGL_NOTICE, "Short read of FREQUENCY REDEFINITION "
+                       "message.\n");
+               return -EINVAL;
+       }
+       if (fr->mob_alloc_len > 8) {
+               LOGP(DRR, LOGL_NOTICE, "Moble allocation in FREQUENCY "
+                       "REDEFINITION too large.\n");
+               return -EINVAL;
+       }
+
+       /* decode channel description */
+       LOGP(DRR, LOGL_INFO, "FREQUENCY REDEFINITION:\n");
+       cd.chan_nr = fr->chan_desc.chan_nr;
+       rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts);
+       if (fr->chan_desc.h0.h) {
+               cd.h = 1;
+               gsm48_decode_chan_h1(&fr->chan_desc, &cd.tsc, &cd.maio,
+                       &cd.hsn);
+               LOGP(DRR, LOGL_INFO, " (MAIO %u HSN %u TS %u SS %u TSC %u)\n",
+                       cd.maio, cd.hsn, ch_ts, ch_subch, cd.tsc);
+       } else {
+               cd.h = 0;
+               gsm48_decode_chan_h0(&fr->chan_desc, &cd.tsc, &cd.arfcn);
+               LOGP(DRR, LOGL_INFO, " (ARFCN %u TS %u SS %u TSC %u)\n",
+                       cd.arfcn, ch_ts, ch_subch, cd.tsc);
+       }
+
+       /* mobile allocation */
+       memcpy(&rr->cd_now.mob_alloc_lv, &fr->mob_alloc_len, 
+               fr->mob_alloc_len + 1);
+
+       /* starting time */
+       st = fr->mob_alloc + fr->mob_alloc_len;
+       gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1));
+
+       /* cell channel description */
+       if (mob_al_len >= fr->mob_alloc_len + 2 + 17
+        && fr->mob_alloc[fr->mob_alloc_len + 2] == GSM48_IE_CELL_CH_DESC) {
+               const uint8_t *v = fr->mob_alloc + fr->mob_alloc_len + 3 + 1;
+
+               LOGP(DRR, LOGL_INFO, " using cell channel description)\n");
+               memcpy(&cd.cell_desc_lv + 1, v, 17);
+               cd.cell_desc_lv[0] = 16;
+       }
+
+       /* render channel "after time" */
+       cause = gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
+       if (cause)
+               return gsm48_rr_tx_rr_status(ms, cause);
+
+       /* update to new channel data */
+       memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now));
+
+       /* schedule change of channel */
+       gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len,
+               rr->cd_now.start_tm.fn);
+
+       rr->cd_now.start = 0;
+
+       return 0;
+}
+
 /* 9.1.6 sending CHANNEL MODE MODIFY ACKNOWLEDGE */
 static int gsm48_rr_tx_chan_modify_ack(struct osmocom_ms *ms,
                struct gsm48_chan_desc *cd, uint8_t mode)
@@ -3336,7 +3695,7 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg)
        int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm);
        struct gsm48_rr_cd *cd = &rr->cd_now;
        uint8_t ch_type, ch_subch, ch_ts;
-       uint8_t mode;
+       uint8_t cause;
 
        LOGP(DRR, LOGL_INFO, "CHANNEL MODE MODIFY\n");
 
@@ -3366,19 +3725,13 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg)
                        ch_ts, ch_subch, cd->tsc, cm->mode);
        }
        /* mode */
-       mode = cm->mode;
-       switch (mode) {
-       case GSM48_CMODE_SIGN:
-               LOGP(DRR, LOGL_INFO, "Mode set to signalling.\n");
-               break;
-       case GSM48_CMODE_SPEECH_V1:
-               LOGP(DRR, LOGL_INFO, "Mode set to GSM full-rate codec.\n");
-               break;
-       default:
-               LOGP(DRR, LOGL_ERROR, "Mode %u not supported!\n", mode);
-       }
+       cause = gsm48_rr_check_mode(ms, cd->chan_nr, cm->mode);
+       if (cause)
+               return gsm48_rr_tx_rr_status(ms, cause);
+       cd->mode = cm->mode;
+       gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode);
 
-       return gsm48_rr_tx_chan_modify_ack(ms, &cm->chan_desc, mode);
+       return gsm48_rr_tx_chan_modify_ack(ms, &cm->chan_desc, cm->mode);
 }
 
 /* 9.1.3 sending ASSIGNMENT COMPLETE */
@@ -3432,16 +3785,28 @@ static int gsm48_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause)
 /* 9.1.2 ASSIGNMENT COMMAND is received */
 static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
 {
-//     struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
        struct gsm48_hdr *gh = msgb_l3(msg);
        struct gsm48_ass_cmd *ac = (struct gsm48_ass_cmd *)gh->data;
        int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ac);
        struct tlv_parsed tp;
-       struct gsm48_rr_cd cd;
+       struct gsm48_rr_cd *cda = &rr->cd_after;
+       struct gsm48_rr_cd *cdb = &rr->cd_before;
+       uint8_t ch_type, ch_subch, ch_ts;
+       uint8_t before_time = 0;
+       uint16_t ma[64];
+       uint8_t ma_len;
+       uint32_t start_mili = 0;
+       uint8_t cause;
+       struct msgb *nmsg;
+
 
        LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMMAND\n");
 
-       memset(&cd, 0, sizeof(cd));
+       memset(cda, 0, sizeof(*cda));
+       cda->ind_tx_power = rr->cd_now.ind_tx_power;
+       memset(cdb, 0, sizeof(*cdb));
+       cdb->ind_tx_power = rr->cd_now.ind_tx_power;
 
        if (payload_len < 0) {
                LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND "
@@ -3451,70 +3816,767 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
        }
        tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0);
 
-#if 0
-       /* channel description */
-       memcpy(&cd.chan_desc, &ac->chan_desc, sizeof(chan_desc));
-       /* power command */
-       cd.power_command = ac->power_command;
-       /* frequency list, after timer */
-       tlv_copy(&cd.fl, sizeof(fl_after), &tp, GSM48_IE_FRQLIST_AFTER);
+       /* decode channel description (before time) */
+       if (TLVP_PRESENT(&tp, GSM48_IE_CH_DESC_1_BEFORE)) {
+               struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *)
+                       TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE);
+               cdb->chan_nr = ccd->chan_nr;
+               rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts);
+               if (ccd->h0.h) {
+                       cdb->h = 1;
+                       gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio,
+                               &cdb->hsn);
+                       LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x MAIO %u "
+                               "HSN %u TS %u SS %u TSC %u)\n", ccd->chan_nr,
+                               cdb->maio, cdb->hsn, ch_ts, ch_subch, cdb->tsc);
+               } else {
+                       cdb->h = 0;
+                       gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn);
+                       LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x "
+                               "ARFCN %u TS %u SS %u TSC %u)\n", ccd->chan_nr,
+                               cdb->arfcn, ch_ts, ch_subch, cdb->tsc);
+               }
+               before_time = 1;
+       }
+
+       /* decode channel description (after time) */
+       cda->chan_nr = ac->chan_desc.chan_nr;
+       rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts);
+       if (ac->chan_desc.h0.h) {
+               cda->h = 1;
+               gsm48_decode_chan_h1(&ac->chan_desc, &cda->tsc, &cda->maio,
+                       &cda->hsn);
+               LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x MAIO %u HSN %u "
+                       "TS %u SS %u TSC %u)\n", ac->chan_desc.chan_nr,
+                       cda->maio, cda->hsn, ch_ts, ch_subch, cda->tsc);
+       } else {
+               cda->h = 0;
+               gsm48_decode_chan_h0(&ac->chan_desc, &cda->tsc, &cda->arfcn);
+               LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %u TS %u "
+                       "SS %u TSC %u)\n", ac->chan_desc.chan_nr,
+                       cda->arfcn, ch_ts, ch_subch, cda->tsc);
+       }
+
+       /* starting time */
+#ifdef TEST_STARTING_TIMER
+       cda->start = 1;
+       cda->start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432;
+       LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n");
+#else
+       if (TLVP_PRESENT(&tp, GSM48_IE_START_TIME)) {   
+               gsm48_decode_start_time(cda, (struct gsm48_start_time *) 
+                       TLVP_VAL(&tp, GSM48_IE_START_TIME));
+               /* 9.1.2.5 "... before time IE is not present..." */
+               if (!before_time) {
+                       LOGP(DRR, LOGL_INFO, " -> channel description after "
+                               "time only, but starting time\n");
+               } else
+                       LOGP(DRR, LOGL_INFO, " -> channel description before "
+                               "and after time\n");
+       } else {
+               /* 9.1.2.5 "... IEs unnecessary in this message." */
+               if (before_time) {
+                       before_time = 0;
+                       LOGP(DRR, LOGL_INFO, " -> channel description before "
+                               "time, but no starting time, ignoring!\n");
+               }
+       }
+#endif
+
+       /* mobile allocation / frequency list after time */
+       if (cda->h) {
+               if (TLVP_PRESENT(&tp, GSM48_IE_MA_AFTER)) {
+                       const uint8_t *lv =
+                               TLVP_VAL(&tp, GSM48_IE_MA_AFTER) - 1;
+
+                       LOGP(DRR, LOGL_INFO, " after: hopping required and "
+                               "mobile allocation available\n");
+                       if (*lv + 1 > sizeof(cda->mob_alloc_lv)) {
+                               LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+                               return -ENOMEM;
+                       }
+                       memcpy(&cda->mob_alloc_lv, lv, *lv + 1);
+               } else
+               if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_AFTER)) {
+                       const uint8_t *lv =
+                               TLVP_VAL(&tp, GSM48_IE_FREQ_L_AFTER) - 1;
+
+                       LOGP(DRR, LOGL_INFO, " after: hopping required and "
+                               "frequency list available\n");
+                       if (*lv + 1 > sizeof(cda->freq_list_lv)) {
+                               LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+                               return -ENOMEM;
+                       }
+                       memcpy(&cda->freq_list_lv, lv, *lv + 1);
+               } else {
+                       LOGP(DRR, LOGL_NOTICE, " after: hopping required, but "
+                               "no mobile allocation / frequency list\n");
+               }
+       }
+
+       /* mobile allocation / frequency list before time */
+       if (cdb->h) {
+               if (TLVP_PRESENT(&tp, GSM48_IE_MA_BEFORE)) {
+                       const uint8_t *lv =
+                               TLVP_VAL(&tp, GSM48_IE_MA_BEFORE) - 1;
+
+                       LOGP(DRR, LOGL_INFO, " before: hopping required and "
+                               "mobile allocation available\n");
+                       if (*lv + 1 > sizeof(cdb->mob_alloc_lv)) {
+                               LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+                               return -ENOMEM;
+                       }
+                       memcpy(&cdb->mob_alloc_lv, lv, *lv + 1);
+               } else
+               if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_BEFORE)) {
+                       const uint8_t *lv =
+                               TLVP_VAL(&tp, GSM48_IE_FREQ_L_BEFORE) - 1;
+
+                       LOGP(DRR, LOGL_INFO, " before: hopping required and "
+                               "frequency list available\n");
+                       if (*lv + 1 > sizeof(cdb->freq_list_lv)) {
+                               LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+                               return -ENOMEM;
+                       }
+                       memcpy(&cdb->freq_list_lv, lv, *lv + 1);
+               } else
+               if (TLVP_PRESENT(&tp, GSM48_IE_F_CH_SEQ_BEFORE)) {
+                       const uint8_t *v =
+                               TLVP_VAL(&tp, GSM48_IE_F_CH_SEQ_BEFORE);
+                       uint8_t len = TLVP_LEN(&tp, GSM48_IE_F_CH_SEQ_BEFORE);
+
+                       LOGP(DRR, LOGL_INFO, " before: hopping required and "
+                               "frequency channel sequence available\n");
+                       if (len + 1 > sizeof(cdb->freq_seq_lv)) {
+                               LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+                               return -ENOMEM;
+                       }
+                       cdb->freq_seq_lv[0] = len;
+                       memcpy(&cdb->freq_seq_lv + 1, v, len);
+               } else
+               if (cda->mob_alloc_lv[0]) {
+                       LOGP(DRR, LOGL_INFO, " before: hopping required and "
+                               "mobile allocation not available, using "
+                               "mobile allocation after time\n");
+                       memcpy(&cdb->mob_alloc_lv, &cda->mob_alloc_lv,
+                               sizeof(cdb->mob_alloc_lv));
+               } else
+               if (cda->freq_list_lv[0]) {
+                       LOGP(DRR, LOGL_INFO, " before: hopping required and "
+                               "frequency list not available, using "
+                               "frequency list after time\n");
+                       memcpy(&cdb->freq_list_lv, &cda->freq_list_lv,
+                               sizeof(cdb->freq_list_lv));
+               } else {
+                       LOGP(DRR, LOGL_NOTICE, " before: hopping required, but "
+                               "no mobile allocation / frequency list\n");
+               }
+       }
+
        /* cell channel description */
-       tlv_copy(&cd.ccd, sizeof(ccd), &tp, GSM48_IE_CELL_CH_DESC);
-       /* multislot allocation */
-       tlv_copy(&cd.multia, sizeof(ma), &tp, GSM48_IE_MSLOT_DESC);
+       if (TLVP_PRESENT(&tp, GSM48_IE_CELL_CH_DESC)) {
+               const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_CELL_CH_DESC);
+               uint8_t len = TLVP_LEN(&tp, GSM48_IE_CELL_CH_DESC);
+
+               LOGP(DRR, LOGL_INFO, " both: using cell channel description "
+                       "in case of mobile allocation\n");
+               if (len + 1 > sizeof(cdb->cell_desc_lv)) {
+                       LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+                       return -ENOMEM;
+               }
+               cdb->cell_desc_lv[0] = len;
+               memcpy(&cdb->cell_desc_lv + 1, v, len);
+               cda->cell_desc_lv[0] = len;
+               memcpy(&cda->cell_desc_lv + 1, v, len);
+       } else {
+               /* keep old */
+               memcpy(&cdb->cell_desc_lv, &rr->cd_now.cell_desc_lv,
+                       sizeof(cdb->cell_desc_lv));
+               memcpy(&cda->cell_desc_lv, &rr->cd_now.cell_desc_lv,
+                       sizeof(cda->cell_desc_lv));
+       }
+
        /* channel mode */
-       tlv_copy(&cd.chanmode, sizeof(chanmode), &tp, GSM48_IE_CHANMODE_1);
-       /* mobile allocation, after time */
-       tlv_copy(&cd.moba_after, sizeof(moba_after), &tp, GSM48_IE_MOB_AL_AFTER);
+       if (TLVP_PRESENT(&tp, GSM48_IE_CHANMODE_1)) {
+               cda->mode = cdb->mode = *TLVP_VAL(&tp, GSM48_IE_CHANMODE_1);
+               LOGP(DRR, LOGL_INFO, " both: changing channel mode 0x%02x\n",
+                       cda->mode);
+       } else
+               cda->mode = cdb->mode = rr->cd_now.mode;
+
+       /* cipher mode setting */
+       if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET)) {
+               cda->cipher = cdb->cipher =
+                       *TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET);
+               LOGP(DRR, LOGL_INFO, " both: changing cipher mode 0x%02x\n",
+                       cda->cipher);
+       } else
+               cda->cipher = cdb->cipher = rr->cd_now.cipher;
+
+       /* power command and TA (before and after time) */
+       gsm48_decode_power_cmd_acc(
+               (struct gsm48_power_cmd *) &ac->power_command,
+               &cda->ind_tx_power, NULL);
+       cdb->ind_tx_power = cda->ind_tx_power;
+       cda->ind_ta = cdb->ind_ta = rr->cd_now.ind_ta; /* same cell */
+       LOGP(DRR, LOGL_INFO, " both: (tx_power %d TA %d)\n", cda->ind_tx_power,
+               cda->ind_ta);
+
+       /* check if we have to change channel at starting time */
+       if (cda->start) {
+               int32_t now, start, diff;
+
+               /* how much time do we have left? */
+               now = ms->meas.last_fn % 42432;
+               start = cda->start_tm.fn % 42432;
+               diff = start - now;
+               if (diff < 0)
+                       diff += 42432;
+               LOGP(DRR, LOGL_INFO, " after: (Tnow %d Tstart %d diff %d)\n",
+                       now, start, diff);
+               start_mili = (uint32_t)diff * 19580 / 42432 * 10;
+               if (diff >= 32024 || !start_mili) {
+                       LOGP(DRR, LOGL_INFO, " -> Start time already "
+                               "elapsed\n");
+                       before_time = 0;
+                       cda->start = 0;
+               } else {
+                       LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the "
+                               "future\n", start_mili);
+               }
+       }
+
+       /* check if channels are valid */
+       cause = gsm48_rr_check_mode(ms, cda->chan_nr, cda->mode);
+       if (cause)
+               return gsm48_rr_tx_ass_fail(ms, cause);
+       if (before_time) {
+               cause = gsm48_rr_render_ma(ms, cdb, ma, &ma_len);
+               if (cause)
+                       return gsm48_rr_tx_ass_fail(ms, cause);
+       }
+       cause = gsm48_rr_render_ma(ms, cda, ma, &ma_len);
+       if (cause)
+               return gsm48_rr_tx_ass_fail(ms, cause);
+
+
+#if 0
+       if (not supported) {
+               LOGP(DRR, LOGL_NOTICE, "New channel is not supported.\n");
+               return GSM48_RR_CAUSE_CHAN_MODE_UNACCEPT;
+       }
+#endif
+
+#ifdef TEST_FREQUENCY_MOD
+       LOGP(DRR, LOGL_INFO, " TESTING: frequency modify ASS.CMD\n");
+       before_time = 1;
+       memcpy(cdb, cda, sizeof(*cdb));
+       cdb->h = 0;
+       cdb->arfcn = 0;
+#endif
+
+       /* schedule start of assignment */
+       rr->modify_state = GSM48_RR_MOD_ASSIGN;
+       if (!before_time && cda->start) {
+               start_rr_t_starting(rr, start_mili / 1000, start_mili % 1000);
+               /* when timer fires, start time is already elapsed */
+               cda->start = 0;
+
+               return 0;
+       }
+
+       /* if no starting time, start suspension of current link directly */
+       LOGP(DRR, LOGL_INFO, "request suspension of data link\n");
+       nmsg = gsm48_l3_msgb_alloc();
+       if (!nmsg)
+               return -ENOMEM;
+       gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, nmsg);
+
+       return 0;
+}
+
+/* 9.1.16 sending HANDOVER COMPLETE */
+static int gsm48_rr_tx_hando_cpl(struct osmocom_ms *ms, uint8_t cause)
+{
+       struct msgb *nmsg;
+       struct gsm48_hdr *gh;
+       struct gsm48_ho_cpl *hc;
+
+       LOGP(DRR, LOGL_INFO, "HANDOVER COMPLETE (cause #%d)\n", cause);
+
+       nmsg = gsm48_l3_msgb_alloc();
+       if (!nmsg)
+               return -ENOMEM;
+       gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+       hc = (struct gsm48_ho_cpl *) msgb_put(nmsg, sizeof(*hc));
+
+       gh->proto_discr = GSM48_PDISC_RR;
+       gh->msg_type = GSM48_MT_RR_HANDO_COMPL;
+
+       /* RR_CAUSE */
+       hc->rr_cause = cause;
+
+       // FIXME: mobile observed time
+
+       return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+}
+
+/* 9.1.4 sending HANDOVER FAILURE */
+static int gsm48_rr_tx_hando_fail(struct osmocom_ms *ms, uint8_t cause)
+{
+       struct msgb *nmsg;
+       struct gsm48_hdr *gh;
+       struct gsm48_ho_fail *hf;
+
+       LOGP(DRR, LOGL_INFO, "HANDOVER FAILURE (cause #%d)\n", cause);
+
+       nmsg = gsm48_l3_msgb_alloc();
+       if (!nmsg)
+               return -ENOMEM;
+       gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+       hf = (struct gsm48_ho_fail *) msgb_put(nmsg, sizeof(*hf));
+
+       gh->proto_discr = GSM48_PDISC_RR;
+       gh->msg_type = GSM48_MT_RR_ASS_COMPL;
+
+       /* RR_CAUSE */
+       hf->rr_cause = cause;
+
+       return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+}
+
+/* receiving HANDOVER COMMAND message (9.1.15) */
+static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct gsm48_hdr *gh = msgb_l3(msg);
+       struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *)gh->data;
+       int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ho);
+       struct tlv_parsed tp;
+       struct gsm48_rr_cd *cda = &rr->cd_after;
+       struct gsm48_rr_cd *cdb = &rr->cd_before;
+       uint16_t arfcn;
+       uint8_t bcc, ncc;
+       uint8_t ch_type, ch_subch, ch_ts;
+       uint8_t before_time = 0;
+       uint16_t ma[64];
+       uint8_t ma_len;
+       uint32_t start_mili = 0;
+       uint8_t cause;
+       struct msgb *nmsg;
+
+       LOGP(DRR, LOGL_INFO, "HANDOVER COMMAND\n");
+
+       memset(cda, 0, sizeof(*cda));
+       cda->ind_tx_power = rr->cd_now.ind_tx_power;
+       memset(cdb, 0, sizeof(*cdb));
+       cdb->ind_tx_power = rr->cd_now.ind_tx_power;
+
+       if (payload_len < 0) {
+               LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND "
+                       "message.\n");
+               return gsm48_rr_tx_rr_status(ms,
+                       GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+       }
+
+       /* cell description */
+       gsm48_decode_cell_desc(&ho->cell_desc, &arfcn, &ncc, &bcc);
+
+       /* handover reference */
+       rr->chan_req_val = ho->ho_ref;
+       rr->chan_req_mask = 0x00;
+
+       /* sync ind */
+       if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND)) {     
+               gsm48_decode_sync_ind(rr, (struct gsm48_sync_ind *) 
+                       TLVP_VAL(&tp, GSM48_IE_SYNC_IND));
+               LOGP(DRR, LOGL_INFO, " (sync_ind=%d rot=%d nci=%d)\n",
+                       rr->hando_sync_ind, rr->hando_rot, rr->hando_nci);
+       }
+
+       tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0);
+
+       /* decode channel description (before time) */
+       if (TLVP_PRESENT(&tp, GSM48_IE_CH_DESC_1_BEFORE)) {
+               struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *)
+                       TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE);
+               cdb->chan_nr = ccd->chan_nr;
+               rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts);
+               if (ccd->h0.h) {
+                       cdb->h = 1;
+                       gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio,
+                               &cdb->hsn);
+                       LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x MAIO %u "
+                               "HSN %u TS %u SS %u TSC %u)\n", ccd->chan_nr,
+                               cdb->maio, cdb->hsn, ch_ts, ch_subch, cdb->tsc);
+               } else {
+                       cdb->h = 0;
+                       gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn);
+                       LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x "
+                               "ARFCN %u TS %u SS %u TSC %u)\n", ccd->chan_nr,
+                               cdb->arfcn, ch_ts, ch_subch, cdb->tsc);
+               }
+               before_time = 1;
+       }
+
+       /* decode channel description (after time) */
+       cda->chan_nr = ho->chan_desc.chan_nr;
+       rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts);
+       if (ho->chan_desc.h0.h) {
+               cda->h = 1;
+               gsm48_decode_chan_h1(&ho->chan_desc, &cda->tsc, &cda->maio,
+                       &cda->hsn);
+               LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x MAIO %u HSN %u "
+                       "TS %u SS %u TSC %u)\n", ho->chan_desc.chan_nr,
+                       cda->maio, cda->hsn, ch_ts, ch_subch, cda->tsc);
+       } else {
+               cda->h = 0;
+               gsm48_decode_chan_h0(&ho->chan_desc, &cda->tsc, &cda->arfcn);
+               LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %u TS %u "
+                       "SS %u TSC %u)\n", ho->chan_desc.chan_nr,
+                       cda->arfcn, ch_ts, ch_subch, cda->tsc);
+       }
+
        /* starting time */
-       tlv_copy(&cd.start, sizeof(start), &tp, GSM_IE_START_TIME);
-       /* frequency list, before time */
-       tlv_copy(&cd.fl_before, sizeof(fl_before), &tp, GSM48_IE_FRQLIST_BEFORE);
-       /* channel description, before time */
-       tlv_copy(&cd.chan_desc_before, sizeof(cd_before), &tp, GSM48_IE_CHDES_1_BEFORE);
-       /* frequency channel sequence, before time */
-       tlv_copy(&cd.fcs_before, sizeof(fcs_before), &tp, GSM48_IE_FRQSEQ_BEFORE);
-       /* mobile allocation, before time */
-       tlv_copy(&cd.moba_before, sizeof(moba_before), &tp, GSM48_IE_MOB_AL_BEFORE);
+#ifdef TEST_STARTING_TIMER
+       cda->start = 1;
+       cda->start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432;
+       LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n");
+#else
+       if (TLVP_PRESENT(&tp, GSM48_IE_START_TIME)) {   
+               gsm48_decode_start_time(cda, (struct gsm48_start_time *) 
+                       TLVP_VAL(&tp, GSM48_IE_START_TIME));
+               /* 9.1.2.5 "... before time IE is not present..." */
+               if (!before_time) {
+                       LOGP(DRR, LOGL_INFO, " -> channel description after "
+                               "time only, but starting time\n");
+               } else
+                       LOGP(DRR, LOGL_INFO, " -> channel description before "
+                               "and after time\n");
+       } else {
+               /* 9.1.2.5 "... IEs unnecessary in this message." */
+               if (before_time) {
+                       before_time = 0;
+                       LOGP(DRR, LOGL_INFO, " -> channel description before "
+                               "time, but no starting time, ignoring!\n");
+               }
+       }
+#endif
+
+       /* mobile allocation / frequency list after time */
+       if (cda->h) {
+               if (TLVP_PRESENT(&tp, GSM48_IE_MA_AFTER)) {
+                       const uint8_t *lv =
+                               TLVP_VAL(&tp, GSM48_IE_MA_AFTER) - 1;
+
+                       LOGP(DRR, LOGL_INFO, " after: hopping required and "
+                               "mobile allocation available\n");
+                       if (*lv + 1 > sizeof(cda->mob_alloc_lv)) {
+                               LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+                               return -ENOMEM;
+                       }
+                       memcpy(&cda->mob_alloc_lv, lv, *lv + 1);
+               } else
+               if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_AFTER)) {
+                       const uint8_t *lv =
+                               TLVP_VAL(&tp, GSM48_IE_FREQ_L_AFTER) - 1;
+
+                       LOGP(DRR, LOGL_INFO, " after: hopping required and "
+                               "frequency list available\n");
+                       if (*lv + 1 > sizeof(cda->freq_list_lv)) {
+                               LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+                               return -ENOMEM;
+                       }
+                       memcpy(&cda->freq_list_lv, lv, *lv + 1);
+               } else {
+                       LOGP(DRR, LOGL_NOTICE, " after: hopping required, but "
+                               "no mobile allocation / frequency list\n");
+               }
+       }
+
+       /* mobile allocation / frequency list before time */
+       if (cdb->h) {
+               if (TLVP_PRESENT(&tp, GSM48_IE_MA_BEFORE)) {
+                       const uint8_t *lv =
+                               TLVP_VAL(&tp, GSM48_IE_MA_BEFORE) - 1;
+
+                       LOGP(DRR, LOGL_INFO, " before: hopping required and "
+                               "mobile allocation available\n");
+                       if (*lv + 1 > sizeof(cdb->mob_alloc_lv)) {
+                               LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+                               return -ENOMEM;
+                       }
+                       memcpy(&cdb->mob_alloc_lv, lv, *lv + 1);
+               } else
+               if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_BEFORE)) {
+                       const uint8_t *lv =
+                               TLVP_VAL(&tp, GSM48_IE_FREQ_L_BEFORE) - 1;
+
+                       LOGP(DRR, LOGL_INFO, " before: hopping required and "
+                               "frequency list available\n");
+                       if (*lv + 1 > sizeof(cdb->freq_list_lv)) {
+                               LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+                               return -ENOMEM;
+                       }
+                       memcpy(&cdb->freq_list_lv, lv, *lv + 1);
+               } else
+               if (TLVP_PRESENT(&tp, GSM48_IE_F_CH_SEQ_BEFORE)) {
+                       const uint8_t *v =
+                               TLVP_VAL(&tp, GSM48_IE_F_CH_SEQ_BEFORE);
+                       uint8_t len = TLVP_LEN(&tp, GSM48_IE_F_CH_SEQ_BEFORE);
+
+                       LOGP(DRR, LOGL_INFO, " before: hopping required and "
+                               "frequency channel sequence available\n");
+                       if (len + 1 > sizeof(cdb->freq_seq_lv)) {
+                               LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+                               return -ENOMEM;
+                       }
+                       cdb->freq_seq_lv[0] = len;
+                       memcpy(&cdb->freq_seq_lv, v + 1, *v);
+               } else
+               if (cda->mob_alloc_lv[0]) {
+                       LOGP(DRR, LOGL_INFO, " before: hopping required and "
+                               "mobile allocation not available, using "
+                               "mobile allocation after time\n");
+                       memcpy(&cdb->mob_alloc_lv, &cda->mob_alloc_lv,
+                               sizeof(cdb->mob_alloc_lv));
+               } else
+               if (cda->freq_list_lv[0]) {
+                       LOGP(DRR, LOGL_INFO, " before: hopping required and "
+                               "frequency list not available, using "
+                               "frequency list after time\n");
+                       memcpy(&cdb->freq_list_lv, &cda->freq_list_lv,
+                               sizeof(cdb->freq_list_lv));
+               } else {
+                       LOGP(DRR, LOGL_NOTICE, " before: hopping required, but "
+                               "no mobile allocation / frequency list\n");
+               }
+       }
+
+       /* cell channel description */
+       if (TLVP_PRESENT(&tp, GSM48_IE_CELL_CH_DESC)) {
+               const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_CELL_CH_DESC);
+               uint8_t len = TLVP_LEN(&tp, GSM48_IE_CELL_CH_DESC);
+
+               LOGP(DRR, LOGL_INFO, " both: using cell channel description "
+                       "in case of mobile allocation\n");
+               if (len + 1 > sizeof(cdb->cell_desc_lv)) {
+                       LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
+                       return -ENOMEM;
+               }
+               cdb->cell_desc_lv[0] = len;
+               memcpy(&cdb->cell_desc_lv + 1, v, len);
+               cda->cell_desc_lv[0] = len;
+               memcpy(&cda->cell_desc_lv + 1, v, len);
+       } else {
+               /* keep old */
+               memcpy(&cdb->cell_desc_lv, &rr->cd_now.cell_desc_lv,
+                       sizeof(cdb->cell_desc_lv));
+               memcpy(&cda->cell_desc_lv, &rr->cd_now.cell_desc_lv,
+                       sizeof(cda->cell_desc_lv));
+       }
+
+       /* channel mode */
+       if (TLVP_PRESENT(&tp, GSM48_IE_CHANMODE_1)) {
+               cda->mode = cdb->mode = *TLVP_VAL(&tp, GSM48_IE_CHANMODE_1);
+               LOGP(DRR, LOGL_INFO, " both: changing channel mode 0x%02x\n",
+                       cda->mode);
+       } else
+               cda->mode = cdb->mode = rr->cd_now.mode;
+
        /* cipher mode setting */
-       if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET))
-               cd.cipher = *TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET);
-       else
-               cd.cipher = 0;
+       if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET)) {
+               cda->cipher = cdb->cipher =
+                       *TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET);
+               LOGP(DRR, LOGL_INFO, " both: changing cipher mode 0x%02x\n",
+                       cda->cipher);
+       } else
+               cda->cipher = cdb->cipher = rr->cd_now.cipher;
+
+       /* power command and TA (before and after time) */
+       gsm48_decode_power_cmd_acc(
+               (struct gsm48_power_cmd *) &ho->power_command,
+               &cda->ind_tx_power, &rr->hando_act);
+       cdb->ind_tx_power = cda->ind_tx_power;
+       cda->ind_ta = cdb->ind_ta = rr->cd_now.ind_ta; /* same cell */
+       LOGP(DRR, LOGL_INFO, " both: (tx_power %d TA %d access=%s)\n",
+               cda->ind_tx_power, cda->ind_ta,
+               (rr->hando_act) ? "optional" : "mandatory");
+
+       /* check if we have to change channel at starting time */
+       if (cda->start) {
+               int32_t now, start, diff;
+
+               /* how much time do we have left? */
+               now = ms->meas.last_fn % 42432;
+               start = cda->start_tm.fn % 42432;
+               diff = start - now;
+               if (diff < 0)
+                       diff += 42432;
+               LOGP(DRR, LOGL_INFO, " after: (Tnow %d Tstart %d diff %d)\n",
+                       now, start, diff);
+               start_mili = (uint32_t)diff * 19580 / 42432 * 10;
+               if (diff >= 32024 || !start_mili) {
+                       LOGP(DRR, LOGL_INFO, " -> Start time already "
+                               "elapsed\n");
+                       before_time = 0;
+                       cda->start = 0;
+               } else {
+                       LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the "
+                               "future\n", start_mili);
+               }
+       }
+
+       /* check if channels are valid */
+       if (before_time) {
+               cause = gsm48_rr_render_ma(ms, cdb, ma, &ma_len);
+               if (cause)
+                       return gsm48_rr_tx_hando_fail(ms, cause);
+       }
+       cause = gsm48_rr_render_ma(ms, cda, ma, &ma_len);
+       if (cause)
+               return gsm48_rr_tx_hando_fail(ms, cause);
+
+
+#if 0
+       if (not supported) {
+               LOGP(DRR, LOGL_NOTICE, "New channel is not supported.\n");
+               return GSM48_RR_CAUSE_CHAN_MODE_UNACCEPT;
+       }
+#endif
+
+#ifdef TEST_FREQUENCY_MOD
+       LOGP(DRR, LOGL_INFO, " TESTING: frequency modify HANDO.CMD\n");
+       before_time = 1;
+       memcpy(cdb, cda, sizeof(*cdb));
+       cdb->h = 0;
+       cdb->arfcn = 0;
+#endif
+
+       /* schedule start of handover */
+       rr->modify_state = GSM48_RR_MOD_HANDO;
+       if (!before_time && cda->start) {
+               start_rr_t_starting(rr, start_mili / 1000, start_mili % 1000);
+               /* when timer fires, start time is already elapsed */
+               cda->start = 0;
+
+               return 0;
+       }
+
+       /* if no starting time, start suspension of current link directly */
+       LOGP(DRR, LOGL_INFO, "request suspension of data link\n");
+       nmsg = gsm48_l3_msgb_alloc();
+       if (!nmsg)
+               return -ENOMEM;
+       gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, nmsg);
+
+       return 0;
+}
+
+/* send all queued messages down to layer 2 */
+static int gsm48_rr_dequeue_down(struct osmocom_ms *ms)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct msgb *msg;
+
+       while((msg = msgb_dequeue(&rr->downqueue))) {
+               LOGP(DRR, LOGL_INFO, "Sending queued message.\n");
+               gsm48_send_rsl(ms, RSL_MT_DATA_REQ, msg);
+       }
+
+       return 0;
+}
+
+/* channel is resumed in dedicated mode */
+static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+       LOGP(DRR, LOGL_INFO, "data link is resumed\n");
+
+       switch (rr->modify_state) {
+       case GSM48_RR_MOD_ASSIGN:
+               gsm48_rr_tx_ass_cpl(ms, GSM48_RR_CAUSE_NORMAL);
+               break;
+       case GSM48_RR_MOD_HANDO:
+               gsm48_rr_tx_hando_cpl(ms, GSM48_RR_CAUSE_NORMAL);
+               break;
+       case GSM48_RR_MOD_ASSIGN_RESUME:
+               gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+               break;
+       case GSM48_RR_MOD_HANDO_RESUME:
+               gsm48_rr_tx_hando_fail(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+               break;
+       }
+
+       /* transmit queued frames during ho / ass transition */
+       gsm48_rr_dequeue_down(ms);
+
+       rr->modify_state = GSM48_RR_MOD_NONE;
+
+       return 0;
+}
+
+/* suspend confirm in dedicated mode */
+static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+       if (rr->modify_state) {
+               struct msgb *nmsg;
+               uint16_t ma[64];
+               uint8_t ma_len;
+
+               /* deactivating dedicated mode */
+               LOGP(DRR, LOGL_INFO, "suspension coplete, leaving dedicated "
+                       "mode\n");
+               l1ctl_tx_dm_rel_req(ms);
+               rr->dm_est = 0;
+               l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
+
+               /* store current channel descriptions */
+               memcpy(&rr->cd_last, &rr->cd_now, sizeof(rr->cd_last));
 
-       if (no CA) {
-               LOGP(DRR, LOGL_INFO, "No current cell allocation available.\n");
-               return gsm48_rr_tx_ass_fail(ms, GSM48_GSM48_RR_CAUSE_NO_CELL_ALLOC_A);
-       }
-       
-       if (not supported) {
-               LOGP(DRR, LOGL_INFO, "New channel is not supported.\n");
-               return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_CHAN_MODE_UNACCEPT);
-       }
+               /* copy channel description "after time" */
+               memcpy(&rr->cd_now, &rr->cd_after, sizeof(rr->cd_now));
 
-       if (freq not supported) {
-               LOGP(DRR, LOGL_INFO, "New frequency is not supported.\n");
-               return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_FREQ_NOT_IMPL);
-       }
+               if (rr->cd_after.start) {
+                       /* render channel "before time" */
+                       gsm48_rr_render_ma(ms, &rr->cd_before, ma, &ma_len);
 
-       /* store current channel descriptions, to return in case of failure */
-       memcpy(&rr->chan_last, &rr->chan_desc, sizeof(*cd));
-       /* copy new description */
-       memcpy(&rr->chan_desc, cd, sizeof(cd));
+                       /* activate channel */
+                       gsm48_rr_activate_channel(ms, &rr->cd_before, ma,
+                               ma_len);
 
-       /* start suspension of current link */
-       nmsg = gsm48_l3_msgb_alloc();
-       if (!nmsg)
-               return -ENOMEM;
-       gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, msg);
+                       /* render channel "after time" */
+                       gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
 
-       /* change into special assignment suspension state */
-       rr->assign_susp_state = 1;
-       rr->resume_last_state = 0;
-#else
-       return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_FREQ_NOT_IMPL);
-#endif
+                       /* schedule change of channel */
+                       gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len,
+                               rr->cd_now.start_tm.fn);
+               } else {
+                       /* render channel "after time" */
+                       gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
+
+                       /* activate channel */
+                       gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
+               }
+
+               /* send DL-RESUME REQUEST */
+               LOGP(DRR, LOGL_INFO, "request resume of data link\n");
+               nmsg = gsm48_l3_msgb_alloc();
+               if (!nmsg)
+                       return -ENOMEM;
+               gsm48_send_rsl(ms, RSL_MT_RES_REQ, nmsg);
 
+#ifdef TODO
+               /* trigger RACH */
+               if (rr->modify_state == GSM48_RR_MOD_HANDO) {
+                       gsm48_rr_tx_hando_access(ms);
+                       rr->hando_acc_left = 3;
+               }
+#endif
+       }
        return 0;
 }
 
@@ -3625,20 +4687,6 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
        return gsm48_rr_chan_req(ms, rrh->cause, 0);
 }
 
-/* send all queued messages down to layer 2 */
-static int gsm48_rr_dequeue_down(struct osmocom_ms *ms)
-{
-       struct gsm48_rrlayer *rr = &ms->rrlayer;
-       struct msgb *msg;
-
-       while((msg = msgb_dequeue(&rr->downqueue))) {
-               LOGP(DRR, LOGL_INFO, "Sending queued message.\n");
-               gsm48_send_rsl(ms, RSL_MT_DATA_REQ, msg);
-       }
-
-       return 0;
-}
-
 /* 3.4.2 transfer data in dedicated mode */
 static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg)
 {
@@ -3652,8 +4700,12 @@ static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg)
        /* pull RR header */
        msgb_pull(msg, sizeof(struct gsm48_rr_hdr));
 
+       /* set sequence number and increment */
+       gsm48_apply_v_sd(rr, msg);
+
        /* queue message, during handover or assignment procedure */
-       if (rr->hando_susp_state || rr->assign_susp_state) {
+       if (rr->modify_state == GSM48_RR_MOD_ASSIGN
+        || rr->modify_state == GSM48_RR_MOD_HANDO) {
                LOGP(DRR, LOGL_INFO, "Queueing message during suspend.\n");
                msgb_enqueue(&rr->downqueue, msg);
                return 0;
@@ -3689,28 +4741,27 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
                case GSM48_MT_RR_ASS_CMD:
                        rc = gsm48_rr_rx_ass_cmd(ms, msg);
                        break;
-#if 0
-               case GSM48_MT_RR_CIP_MODE_CMD:
+               case GSM48_MT_RR_CIPH_M_CMD:
                        rc = gsm48_rr_rx_cip_mode_cmd(ms, msg);
                        break;
-#endif
                case GSM48_MT_RR_CLSM_ENQ:
                        rc = gsm48_rr_rx_cm_enq(ms, msg);
                        break;
                case GSM48_MT_RR_CHAN_MODE_MODIF:
                        rc = gsm48_rr_rx_chan_modify(ms, msg);
                        break;
-#if 0
                case GSM48_MT_RR_HANDO_CMD:
                        rc = gsm48_rr_rx_hando_cmd(ms, msg);
                        break;
                case GSM48_MT_RR_FREQ_REDEF:
-                       rc = gsm48_rr_rx_freq_redef(ms, msg);
+                       rc = gsm48_rr_rx_frq_redef(ms, msg);
                        break;
-#endif
                case GSM48_MT_RR_CHAN_REL:
                        rc = gsm48_rr_rx_chan_rel(ms, msg);
                        break;
+               case GSM48_MT_RR_APP_INFO:
+                       LOGP(DRR, LOGL_NOTICE, "APP INFO not supported!\n");
+                       break;
                default:
                        LOGP(DRR, LOGL_NOTICE, "Message type 0x%02x unknown.\n",
                                gh->msg_type);
@@ -3809,13 +4860,14 @@ static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg)
                ind_ta, ind_ta - set->alter_delay);
        LOGP(DRR, LOGL_INFO, "Indicated tx_power %d\n",
                ind_tx_power);
-       if (ind_ta != rr->ind_ta || ind_tx_power != rr->ind_tx_power) {
+       if (ind_ta != rr->cd_now.ind_ta
+        || ind_tx_power != rr->cd_now.ind_tx_power) {
                LOGP(DRR, LOGL_INFO, "setting new ta and tx_power\n");
                l1ctl_tx_param_req(ms, ind_ta - set->alter_delay,
                        (set->alter_tx_power) ? set->alter_tx_power_value
                                                : ind_tx_power);
-               rr->ind_ta = ind_ta;
-               rr->ind_tx_power = ind_tx_power;
+               rr->cd_now.ind_ta = ind_ta;
+               rr->cd_now.ind_tx_power = ind_tx_power;
        }
 
        switch (sih->system_information) {
@@ -3948,53 +5000,100 @@ static int gsm48_rr_abort_req(struct osmocom_ms *ms, struct msgb *msg)
        return 0;
 }
 
-/* release confirm in dedicated mode */
-static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
+/* release confirm */
+static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct msgb *nmsg;
+       struct gsm48_rr_hdr *nrrh;
 
-       if (rr->hando_susp_state || rr->assign_susp_state) {
-               struct msgb *nmsg;
+       LOGP(DSUM, LOGL_INFO, "Requesting channel aborted\n");
 
-               /* change radio to new channel */
-//todo         tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr,
-//                              rr->cd_now.tsc);
+       /* stop T3211 if running */
+       stop_rr_t3110(rr);
 
-               /* send DL-ESTABLISH REQUEST */
-               nmsg = gsm48_l3_msgb_alloc();
-               if (!nmsg)
-                       return -ENOMEM;
-               gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg);
+       /* send release indication */
+       nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+       if (!nmsg)
+               return -ENOMEM;
+       nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+       nrrh->cause = RR_REL_CAUSE_NORMAL;
+       gsm48_rr_upmsg(ms, nmsg);
 
-#ifdef TODO
-               /* trigger RACH */
-               if (rr->hando_susp_state) {
-                       gsm48_rr_tx_hando_access(ms);
-                       rr->hando_acc_left = 3;
-               }
-#endif
-       }
+       /* return idle */
+       new_rr_state(rr, GSM48_RR_ST_IDLE);
        return 0;
 }
 
-/* release confirm */
-static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
+/* MDL-ERROR */
+static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
        struct msgb *nmsg;
        struct gsm48_rr_hdr *nrrh;
+       uint8_t *mode;
+       uint8_t cause = rllh->data[2];
+       uint16_t ma[64];
+       uint8_t ma_len;
 
-       LOGP(DSUM, LOGL_INFO, "Requesting channel aborted\n");
+       switch (cause) {
+       case RLL_CAUSE_SEQ_ERR:
+       case RLL_CAUSE_UNSOL_DM_RESP_MF:
+               break;
+       default:
+               LOGP(DRR, LOGL_NOTICE, "MDL-Error (cause %d) ignoring\n",
+                       cause);
+       }
 
-       /* stop T3211 if running */
-       stop_rr_t3110(rr);
+       LOGP(DRR, LOGL_NOTICE, "MDL-Error (cause %d) aborting\n", cause);
 
-       /* send release indication */
-       nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+       /* disconnect the main signalling link */
+       nmsg = gsm48_l3_msgb_alloc();
+       if (!nmsg)
+               return -ENOMEM;
+       mode = msgb_put(nmsg, 2);
+       mode[0] = RSL_IE_RELEASE_MODE;
+       mode[1] = 1; /* local release */
+       gsm48_send_rsl_rel(ms, RSL_MT_REL_REQ, nmsg);
+
+       /* deactivate channel */
+       l1ctl_tx_dm_rel_req(ms);
+       rr->dm_est = 0;
+       l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
+
+       switch (rr->modify_state) {
+       case GSM48_RR_MOD_ASSIGN:
+       case GSM48_RR_MOD_HANDO:
+               if (rr->modify_state == GSM48_RR_MOD_ASSIGN)
+                       rr->modify_state = GSM48_RR_MOD_ASSIGN_RESUME;
+               else
+                       rr->modify_state = GSM48_RR_MOD_HANDO_RESUME;
+
+               /* get old channel description */
+               memcpy(&rr->cd_now, &rr->cd_last, sizeof(rr->cd_now));
+
+               /* render and change radio to old channel */
+               gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
+               gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
+
+               /* re-establish old link */
+               nmsg = gsm48_l3_msgb_alloc();
+               if (!nmsg)
+                       return -ENOMEM;
+               return gsm48_send_rsl(ms, RSL_MT_RECON_REQ, nmsg);
+       case GSM48_RR_MOD_ASSIGN_RESUME:
+       case GSM48_RR_MOD_HANDO_RESUME:
+               rr->modify_state = GSM48_RR_MOD_NONE;
+               break;
+       }
+
+       /* send abort ind to upper layer */
+       nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_IND);
        if (!nmsg)
                return -ENOMEM;
        nrrh = (struct gsm48_rr_hdr *)nmsg->data;
-       nrrh->cause = RR_REL_CAUSE_NORMAL;
+       nrrh->cause = RR_REL_CAUSE_LINK_FAILURE;
        gsm48_rr_upmsg(ms, nmsg);
 
        /* return idle */
@@ -4028,15 +5127,10 @@ static struct dldatastate {
         SBIT(GSM48_RR_ST_REL_PEND),
         RSL_MT_EST_CONF, gsm48_rr_estab_cnf},
 
-#if 0
+       /* resume */
        {SBIT(GSM48_RR_ST_DEDICATED),
         RSL_MT_EST_CONF, gsm48_rr_estab_cnf_dedicated},
 
-       {SBIT(GSM_RRSTATE),
-        RSL_MT_CONNECT_CNF, gsm48_rr_connect_cnf},
-
-#endif
-
        /* release */
        {SBIT(GSM48_RR_ST_CONN_PEND) |
         SBIT(GSM48_RR_ST_DEDICATED),
@@ -4052,10 +5146,10 @@ static struct dldatastate {
 #if 0
        {SBIT(GSM48_RR_ST_DEDICATED),
         RSL_MT_CHAN_CNF, gsm48_rr_rand_acc_cnf_dedicated},
-
-       {SBIT(GSM_RRSTATE),
-        RSL_MT_MDL_ERROR_IND, gsm48_rr_mdl_error_ind},
 #endif
+
+       {SBIT(GSM48_RR_ST_DEDICATED),
+        RSL_MT_ERROR_IND, gsm48_rr_mdl_error_ind},
 };
 
 #define DLDATASLLEN \
@@ -4160,11 +5254,6 @@ static struct rrdownstate {
        {SBIT(GSM48_RR_ST_CONN_PEND) |
         SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.13.3 */
         GSM48_RR_ABORT_REQ, gsm48_rr_abort_req},
-
-#if 0
-       {SBIT(GSM48_RR_ST_DEDICATED),
-        GSM48_RR_ACT_REQ, gsm48_rr_act_req},
-#endif
 };
 
 #define RRDOWNSLLEN \
@@ -4221,6 +5310,8 @@ int gsm48_rr_init(struct osmocom_ms *ms)
 
        osmol2_register_handler(ms, &gsm48_rx_rsl);
 
+       start_rr_t_meas(rr, 1, 0);
+
        return 0;
 }
 
@@ -4242,10 +5333,12 @@ int gsm48_rr_exit(struct osmocom_ms *ms)
                rr->rr_est_msg = NULL;
        }
 
-       stop_rr_t_monitor(rr);
+       stop_rr_t_meas(rr);
+       stop_rr_t_starting(rr);
        stop_rr_t_rel_wait(rr);
        stop_rr_t3110(rr);
        stop_rr_t3122(rr);
+       stop_rr_t3124(rr);
        stop_rr_t3126(rr);
 
        return 0;
@@ -4253,221 +5346,8 @@ int gsm48_rr_exit(struct osmocom_ms *ms)
 
 #if 0
 
-the process above is complete
-------------------------------------------------------------------------------
-incomplete
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-todo:
-
-stop timers on abort
-wird beim abbruch immer der gepufferte cm-service-request entfernt, auch beim verschicken?:
-measurement reports
 todo rr_sync_ind when receiving ciph, re ass, channel mode modify
 
-todo change procedures, release procedure
-
-static int gsm48_rr_act_req(struct osmocom_ms *ms, struct gsm48_rr *rrmsg)
-{
-}
-
-
-}
-
-/* memcopy of LV of given IE from tlv_parsed structure */
-static int tlv_copy(void *dest, int dest_len, struct tlv_parsed *tp, uint8_t ie)
-{
-       uint8_t *lv = dest;
-       uint8_t len;
-
-       if (dest_len < 1)
-               return -EINVAL;
-       lv[0] = 0;
-
-       if (!TLVP_PRESENT(tp, ie))
-               return 0;
-
-       len = TLVP_LEN(tp, ie);
-       if (len < 1)
-               return 0;
-       if (len + 1 > dest_len)
-               return -ENOMEM;
-
-       memcpy(dest, TLVP_VAL(tp, ie) - 1, len + 1);
-       return 0;
-}
-
-
-/* decode "Cell Description" (10.5.2.2) */
-static int gsm48_decode_cell_desc(struct gsm48_cell_desc *cd, uint16_t *arfcn, uint8_t *ncc uint8_t *bcc)
-{
-       *arfcn = (cd->bcch_hi << 8) + cd->bcch_lo;
-       *ncc = cd->ncc;
-       *bcc = cd->bcc;
-}
-
-/* decode "Power Command" (10.5.2.28) and (10.5.2.28a) */
-static int gsm48_decode_power_cmd_acc(struct gsm48_power_cmd *pc, uint8_t *power_level uint8_t *atc)
-{
-       *power_level = pc->power_level;
-       if (atc) /* only in case of 10.5.2.28a */
-               *atc = pc->atc;
-}
-
-/* decode "Synchronization Indication" (10.5.2.39) */
-static int gsm48_decode_power_cmd_acc(struct gsm48_rrlayer *rr, struct gsm48_rr_sync_ind *si)
-{
-       rr->ho_sync_ind = si->si;
-       rr->ho_rot = si->rot;
-       rr->ho_nci = si->nci;
-}
-
-/* receiving HANDOVER COMMAND message (9.1.15) */
-static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
-{
-       struct gsm48_rrlayer *rr = ms->rrlayer;
-       struct gsm48_hdr *gh = msgb_l3(msg);
-       struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *)gh->data;
-       int payload_len = msgb_l3len(msg) - sizeof(*gh) - wirklich sizeof(*ho);
-       struct tlv_parsed tp;
-       struct gsm48_rr_cd cd;
-       struct msgb *nmsg;
-
-       memset(&cd, 0, sizeof(cd));
-
-       if (payload_len < 0) {
-               LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND message.\n");
-               return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
-       }
-       tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0);
-
-       /* decode Cell Description */
-       gsm_decode_cell_desc(&ho->cell_desc, &cd.bcch_arfcn, &cd.ncc, &cd.bcc);
-       /* Channel Description */
-       memcpy(&rr->chan_desc.chan_desc, ho->chan_desc, 3);
-       /* Handover Reference */
-       rr->hando_ref = ho->ho_ref;
-       /* Power Command and access type */
-       gsm_decode_power_cmd_acc((struct gsm48_power_cmd *)&ho->power_command,
-               &cd.power_level, cd.atc);
-       /* Synchronization Indication */
-       if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND))
-               gsm48_decode_sync_ind(rr,
-                       TLVP_VAL(&tp, GSM48_IE_SYNC_IND)-1, &cd);
-       /* Frequency Sort List */
-       if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_SHORT_LIST))
-               gsm48_decode_freq_list(&ms->support, s->freq,
-                       TLVP_VAL(&tp, GSM48_IE_FREQ_SHORT_LIST),
-                       *(TLVP_VAL(&tp, GSM48_IE_FREQ_SHORT_LIST)-1),
-                               0xce, FREQ_TYPE_SERV);
-
-
-today: more IE parsing
-
-       /* store current channel descriptions, to return in case of failure */
-       memcpy(&rr->chan_last, &rr->chan_desc, sizeof(*cd));
-       /* copy new description */
-       memcpy(&rr->chan_desc, cd, sizeof(cd));
-
-       /* start suspension of current link */
-       nmsg = gsm48_l3_msgb_alloc();
-       if (!nmsg)
-               return -ENOMEM;
-       gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, msg);
-
-       /* change into special handover suspension state */
-       rr->hando_susp_state = 1;
-       rr->resume_last_state = 0;
-
-       return 0;
-}
-
-static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
-{
-       if (rr->hando_susp_state || rr->assign_susp_state) {
-               if (rr->resume_last_state) {
-                       rr->resume_last_state = 0;
-                       gsm48_rr_tx_ass_cpl(ms, GSM48_RR_CAUSE_NORMAL);
-               } else {
-                       gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_PROTO_ERR_UNSPEC);
-               }
-               /* transmit queued frames during ho / ass transition */
-               gsm48_rr_dequeue_down(ms);
-       }
-
-       return 0;
-}
-
-static int gsm48_rr_connect_cnf(struct osmocom_ms *ms, struct msgbl *msg)
-{
-}
-
-static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
-{
-       struct gsm48_rrlayer *rr = ms->rrlayer;
-       struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
-       struct msgb *nmsg;
-       uint8_t cause = rllh->data[2];
-
-       printing of the cause
-
-       switch (cause) {
-       case RLL_CAUSE_SEQ_ERR:
-       case RLL_CAUSE_UNSOL_DM_RESP_MF:
-       einige muessen ignoriert werden
-       andere gelten als release
-       }
-
-       if (rr->hando_susp_state || rr->assign_susp_state) {
-               if (!rr->resume_last_state) {
-                       rr->resume_last_state = 1;
-
-                       /* get old channel description */
-                       memcpy(&rr->chan_desc, &rr->chan_last, sizeof(*cd));
-
-                       /* change radio to old channel */
-                       tx_ph_dm_est_req(ms, rr->cd_now.arfcn,
-                               rr->cd_now.chan_nr, rr->cd_now.tsc);
-
-                       /* re-establish old link */
-                       nmsg = gsm48_l3_msgb_alloc();
-                       if (!nmsg)
-                               return -ENOMEM;
-                       return gsm48_send_rsl(ms, RSL_MT_RECON_REQ, nmsg);
-               }
-               rr->resume_last_state = 0;
-       }
-
-       /* deactivate channel */
-       l1ctl_tx_dm_rel_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
-
-       /* send abort ind to upper layer */
-       nmsg = gsm48_mm_msgb_alloc();
-
-       if (!msg)
-               return -ENOMEM;
-       nrrh = (struct gsm_mm_hdr *)nmsg->data;
-       nrrh->msg_type = RR_ABORT_IND;
-       nrrh->cause = GSM_MM_CAUSE_LINK_FAILURE;
-       return gsm48_rr_upmsg(ms, msg);
-}
 
 static void timeout_rr_t3124(void *arg)
 {
@@ -4478,11 +5358,12 @@ static void timeout_rr_t3124(void *arg)
        hando_acc_left = 0;
 
        /* get old channel description */
-       memcpy(&rr->chan_desc, &rr->chan_last, sizeof(*cd));
+       memcpy(&rr->chan_desc, &rr->chan_last, sizeof(rr->chan_desc));
 
        /* change radio to old channel */
        tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr,
                         rr->cd_now.tsc);
+       rr->dm_est = 1;
 
        /* re-establish old link */
        nmsg = gsm48_l3_msgb_alloc();
@@ -4511,7 +5392,7 @@ static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *m
        struct msgb *nmsg;
        int s;
 
-       if (!rr->hando_susp_state) {
+       if (rr->modify_state != GSM48_RR_MOD_HANDO) {
                LOGP(DRR, LOGL_NOTICE, "Random acces confirm, but not in handover state.\n");
                return 0;
        }
@@ -4529,6 +5410,7 @@ static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *m
                        start_rr_t3124(rr, GSM_T3124_675);
                else
                        start_rr_t3124(rr, GSM_T3124_320);
+       }
        if (!rr->n_chan_req) {
                start_rr_t3126(rr, 5, 0); /* TODO improve! */
                return 0;
@@ -4542,4 +5424,3 @@ static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *m
 
 #endif
 
-