[layer23] Fixed handling of channel 0 in certain loops
[osmocom-bb.git] / src / host / layer23 / src / mobile / gsm48_rr.c
index 125465f..d199aac 100644 (file)
 #include <stdlib.h>
 #include <arpa/inet.h>
 
-#include <osmocore/msgb.h>
-#include <osmocore/utils.h>
-#include <osmocore/rsl.h>
-#include <osmocore/gsm48.h>
-#include <osmocore/bitvec.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/core/bitvec.h>
 
 #include <osmocom/bb/common/osmocom_data.h>
 #include <osmocom/bb/common/l1l2_interface.h>
+#include <osmocom/bb/common/l23_app.h>
 #include <osmocom/bb/common/logging.h>
 #include <osmocom/bb/common/networks.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);
+#include <l1ctl_proto.h>
+
+static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro);
 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);
+static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg);
 
 /*
  * support
@@ -92,20 +98,6 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms);
 
 #define MIN(a, b) ((a < b) ? a : b)
 
-int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc,
-       uint16_t *mnc, uint16_t *lac)
-{
-       *mcc = ((lai->digits[0] & 0x0f) << 8)
-            | (lai->digits[0] & 0xf0)
-            | (lai->digits[1] & 0x0f);
-       *mnc = ((lai->digits[2] & 0x0f) << 8)
-            | (lai->digits[2] & 0xf0)
-            | ((lai->digits[1] & 0xf0) >> 4);
-       *lac = ntohs(lai->lac);
-
-       return 0;
-}
-
 int gsm48_encode_lai(struct gsm48_loc_area_id *lai, uint16_t mcc,
        uint16_t mnc, uint16_t lac)
 {
@@ -117,25 +109,6 @@ int gsm48_encode_lai(struct gsm48_loc_area_id *lai, uint16_t mcc,
        return 0;
 }
 
-static int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc, 
-       uint16_t *arfcn)
-{
-       *tsc = cd->h0.tsc;
-       *arfcn = cd->h0.arfcn_low | (cd->h0.arfcn_high << 8);
-
-       return 0;
-}
-
-static int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc,
-       uint8_t *maio, uint8_t *hsn)
-{
-       *tsc = cd->h1.tsc;
-       *maio = cd->h1.maio_low | (cd->h1.maio_high << 2);
-       *hsn = cd->h1.hsn;
-
-       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)
@@ -277,6 +250,94 @@ static int gsm48_apply_v_sd(struct gsm48_rrlayer *rr, struct msgb *msg)
        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_settings *set = &ms->settings;
+       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 (!set->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 (!set->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 (!set->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 (!set->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 (!set->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
  */
@@ -322,6 +383,8 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state)
 
                /* release dedicated mode, if any */
                l1ctl_tx_dm_rel_req(rr->ms);
+               rr->ms->meas.rl_fail = 0;
+               rr->dm_est = 0;
                l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_FULL);
                /* free establish message, if any */
                rr->rr_est_req = 0;
@@ -445,13 +508,13 @@ 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);
 
-       return rslms_recvmsg(msg, ms);
+       return lapdm_rslms_recvmsg(msg, &ms->lapdm_channel);
 }
 
 /* push rsl header + release mode and send (RSL-SAP) */
@@ -463,12 +526,13 @@ static int gsm48_send_rsl_rel(struct osmocom_ms *ms, uint8_t msg_type,
        rsl_rll_push_hdr(msg, msg_type, rr->cd_now.chan_nr,
                rr->cd_now.link_id, 1);
 
-       return rslms_recvmsg(msg, ms);
+       return lapdm_rslms_recvmsg(msg, &ms->lapdm_channel);
 }
 
 /* enqueue messages (RSL-SAP) */
-static int gsm48_rx_rsl(struct msgb *msg, struct osmocom_ms *ms)
+static int rcv_rsl(struct msgb *msg, struct lapdm_entity *le, void *l3ctx)
 {
+       struct osmocom_ms *ms = l3ctx;
        struct gsm48_rrlayer *rr = &ms->rrlayer;
 
        msgb_enqueue(&rr->rsl_upqueue, msg);
@@ -495,8 +559,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;
 }
@@ -504,8 +566,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;
 }
@@ -515,7 +575,7 @@ 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;
@@ -526,7 +586,7 @@ static void timeout_rr_monitor(void *arg)
        char text[256];
 
        if (!cs->selected) {
-               sprintf(text, "MON: no cell selected");
+               goto restart;
        } else if (!meas->frames) {
                sprintf(text, "MON: no cell info");
        } else {
@@ -550,10 +610,16 @@ static void timeout_rr_monitor(void *arg)
                                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);
+restart:
+       meas->frames = meas->snr = meas->berr = meas->rxlev = 0;
+       start_rr_t_meas(rr, 1, 0);
 }
 
 /* special timer to assign / handover when starting time is reached */
@@ -645,11 +711,11 @@ 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;
+       osmo_timer_schedule(&rr->t_meas, sec, micro);
 }
 
 static void start_rr_t_rel_wait(struct gsm48_rrlayer *rr, int sec, int micro)
@@ -658,7 +724,7 @@ static void start_rr_t_rel_wait(struct gsm48_rrlayer *rr, int sec, int micro)
                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);
+       osmo_timer_schedule(&rr->t_rel_wait, sec, micro);
 }
 
 static void start_rr_t_starting(struct gsm48_rrlayer *rr, int sec, int micro)
@@ -667,7 +733,7 @@ static void start_rr_t_starting(struct gsm48_rrlayer *rr, int sec, int micro)
                micro / 1000);
        rr->t_starting.cb = timeout_rr_t_starting;
        rr->t_starting.data = rr;
-       bsc_schedule_timer(&rr->t_starting, sec, micro);
+       osmo_timer_schedule(&rr->t_starting, sec, micro);
 }
 
 static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro)
@@ -676,7 +742,7 @@ static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro)
                micro / 1000);
        rr->t3110.cb = timeout_rr_t3110;
        rr->t3110.data = rr;
-       bsc_schedule_timer(&rr->t3110, sec, micro);
+       osmo_timer_schedule(&rr->t3110, sec, micro);
 }
 
 static void start_rr_t3122(struct gsm48_rrlayer *rr, int sec, int micro)
@@ -685,7 +751,7 @@ static void start_rr_t3122(struct gsm48_rrlayer *rr, int sec, int micro)
                micro / 1000);
        rr->t3122.cb = timeout_rr_t3122;
        rr->t3122.data = rr;
-       bsc_schedule_timer(&rr->t3122, sec, micro);
+       osmo_timer_schedule(&rr->t3122, sec, micro);
 }
 
 static void start_rr_t3124(struct gsm48_rrlayer *rr, int sec, int micro)
@@ -694,7 +760,7 @@ static void start_rr_t3124(struct gsm48_rrlayer *rr, int sec, int micro)
                micro / 1000);
        rr->t3124.cb = timeout_rr_t3124;
        rr->t3124.data = rr;
-       bsc_schedule_timer(&rr->t3124, sec, micro);
+       osmo_timer_schedule(&rr->t3124, sec, micro);
 }
 
 static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro)
@@ -703,62 +769,62 @@ static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro)
                micro / 1000);
        rr->t3126.cb = timeout_rr_t3126;
        rr->t3126.data = rr;
-       bsc_schedule_timer(&rr->t3126, sec, micro);
+       osmo_timer_schedule(&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_monitor)) {
-               LOGP(DRR, LOGL_INFO, "stopping pending timer T_monitor\n");
-               bsc_del_timer(&rr->t_monitor);
+       if (osmo_timer_pending(&rr->t_meas)) {
+               LOGP(DRR, LOGL_INFO, "stopping pending timer T_meas\n");
+               osmo_timer_del(&rr->t_meas);
        }
 }
 
 static void stop_rr_t_starting(struct gsm48_rrlayer *rr)
 {
-       if (bsc_timer_pending(&rr->t_starting)) {
+       if (osmo_timer_pending(&rr->t_starting)) {
                LOGP(DRR, LOGL_INFO, "stopping pending timer T_starting\n");
-               bsc_del_timer(&rr->t_starting);
+               osmo_timer_del(&rr->t_starting);
        }
 }
 
 static void stop_rr_t_rel_wait(struct gsm48_rrlayer *rr)
 {
-       if (bsc_timer_pending(&rr->t_rel_wait)) {
+       if (osmo_timer_pending(&rr->t_rel_wait)) {
                LOGP(DRR, LOGL_INFO, "stopping pending timer T_rel_wait\n");
-               bsc_del_timer(&rr->t_rel_wait);
+               osmo_timer_del(&rr->t_rel_wait);
        }
 }
 
 static void stop_rr_t3110(struct gsm48_rrlayer *rr)
 {
-       if (bsc_timer_pending(&rr->t3110)) {
+       if (osmo_timer_pending(&rr->t3110)) {
                LOGP(DRR, LOGL_INFO, "stopping pending timer T3110\n");
-               bsc_del_timer(&rr->t3110);
+               osmo_timer_del(&rr->t3110);
        }
 }
 
 static void stop_rr_t3122(struct gsm48_rrlayer *rr)
 {
-       if (bsc_timer_pending(&rr->t3122)) {
+       if (osmo_timer_pending(&rr->t3122)) {
                LOGP(DRR, LOGL_INFO, "stopping pending timer T3122\n");
-               bsc_del_timer(&rr->t3122);
+               osmo_timer_del(&rr->t3122);
        }
 }
 
 static void stop_rr_t3124(struct gsm48_rrlayer *rr)
 {
-       if (bsc_timer_pending(&rr->t3124)) {
+       if (osmo_timer_pending(&rr->t3124)) {
                LOGP(DRR, LOGL_INFO, "stopping pending timer T3124\n");
-               bsc_del_timer(&rr->t3124);
+               osmo_timer_del(&rr->t3124);
        }
 }
 
 static void stop_rr_t3126(struct gsm48_rrlayer *rr)
 {
-       if (bsc_timer_pending(&rr->t3126)) {
+       if (osmo_timer_pending(&rr->t3126)) {
                LOGP(DRR, LOGL_INFO, "stopping pending timer T3126\n");
-               bsc_del_timer(&rr->t3126);
+               osmo_timer_del(&rr->t3126);
        }
 }
 
@@ -800,6 +866,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);
@@ -821,7 +888,15 @@ static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr)
                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 */
@@ -829,7 +904,7 @@ 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 gsm_settings *set = &ms->settings;
        struct gsm48_hdr *gh = msgb_l3(msg);
        struct gsm48_cip_mode_cmd *cm = (struct gsm48_cip_mode_cmd *)gh->data;
        int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm);
@@ -848,12 +923,12 @@ 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) {
@@ -863,15 +938,17 @@ static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
        }
 
        /* 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 && !set->a5_1)
+               || (alg_id == GSM_CIPHER_A5_2 && !set->a5_2)
+               || (alg_id == GSM_CIPHER_A5_3 && !set->a5_3)
+               || (alg_id == GSM_CIPHER_A5_4 && !set->a5_4)
+               || (alg_id == GSM_CIPHER_A5_5 && !set->a5_5)
+               || (alg_id == GSM_CIPHER_A5_6 && !set->a5_6)
+               || (alg_id == GSM_CIPHER_A5_7 && !set->a5_7))) {
+               LOGP(DRR, LOGL_NOTICE, "algo not supported\n");
                return gsm48_rr_tx_rr_status(ms,
-                       GSM48_RR_CAUSE_CHAN_MODE_UNACCT);
+                       GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+       }
 
        /* check if we have no key */
        if (sc && subscr->key_seq == 7) {
@@ -881,9 +958,10 @@ static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
        }
 
        /* change to ciphering */
-       rr->cipher_on = sc, rr->cipher_type = alg_id;
-       if (sc)
-               l1ctl_tx_crypto_req(ms, alg_id + 1, subscr->key, 8);
+       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);
 
@@ -899,6 +977,7 @@ static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
 static int gsm48_rr_enc_cm3(struct osmocom_ms *ms, uint8_t *buf, uint8_t *len)
 {
        struct gsm_support *sup = &ms->support;
+       struct gsm_settings *set = &ms->settings;
        struct bitvec bv;
 
        memset(&bv, 0, sizeof(bv));
@@ -908,57 +987,60 @@ static int gsm48_rr_enc_cm3(struct osmocom_ms *ms, uint8_t *buf, uint8_t *len)
        /* spare bit */
        bitvec_set_bit(&bv, 0);
        /* band 3 supported */
-       if (sup->dcs_1800)
+       if (set->dcs)
                bitvec_set_bit(&bv, ONE);
        else
                bitvec_set_bit(&bv, ZERO);
        /* band 2 supported */
-       if (sup->e_gsm || sup->r_gsm)
+       if (set->e_gsm || set->r_gsm)
                bitvec_set_bit(&bv, ONE);
        else
                bitvec_set_bit(&bv, ZERO);
        /* band 1 supported */
-       if (sup->p_gsm && !(sup->e_gsm || sup->r_gsm))
+       if (set->p_gsm && !(set->e_gsm || set->r_gsm))
                bitvec_set_bit(&bv, ONE);
        else
                bitvec_set_bit(&bv, ZERO);
        /* a5 bits */
-       if (sup->a5_7)
+       if (set->a5_7)
                bitvec_set_bit(&bv, ONE);
        else
                bitvec_set_bit(&bv, ZERO);
-       if (sup->a5_6)
+       if (set->a5_6)
                bitvec_set_bit(&bv, ONE);
        else
                bitvec_set_bit(&bv, ZERO);
-       if (sup->a5_5)
+       if (set->a5_5)
                bitvec_set_bit(&bv, ONE);
        else
                bitvec_set_bit(&bv, ZERO);
-       if (sup->a5_4)
+       if (set->a5_4)
                bitvec_set_bit(&bv, ONE);
        else
                bitvec_set_bit(&bv, ZERO);
        /* radio capability */
-       if (sup->dcs_1800 && !sup->p_gsm && !(sup->e_gsm || sup->r_gsm)) {
+       if (!set->dcs && !set->p_gsm && !(set->e_gsm || set->r_gsm)) {
+               /* Fig. 10.5.7 / TS 24.0008: none of dcs, p, e, r */
+       } else
+       if (set->dcs && !set->p_gsm && !(set->e_gsm || set->r_gsm)) {
                /* dcs only */
                bitvec_set_uint(&bv, 0, 4);
-               bitvec_set_uint(&bv, sup->dcs_capa, 4);
+               bitvec_set_uint(&bv, set->class_dcs, 4);
        } else
-       if (sup->dcs_1800 && (sup->p_gsm || (sup->e_gsm || sup->r_gsm))) {
+       if (set->dcs && (set->p_gsm || (set->e_gsm || set->r_gsm))) {
                /* dcs */
-               bitvec_set_uint(&bv, sup->dcs_capa, 4);
+               bitvec_set_uint(&bv, set->class_dcs, 4);
                /* low band */
-               bitvec_set_uint(&bv, sup->low_capa, 4);
+               bitvec_set_uint(&bv, set->class_900, 4);
        } else {
                /* low band only */
                bitvec_set_uint(&bv, 0, 4);
-               bitvec_set_uint(&bv, sup->low_capa, 4);
+               bitvec_set_uint(&bv, set->class_900, 4);
        }
        /* r support */
-       if (sup->r_gsm) {
+       if (set->r_gsm) {
                bitvec_set_bit(&bv, ONE);
-               bitvec_set_uint(&bv, sup->r_capa, 3);
+               bitvec_set_uint(&bv, set->class_900, 3);
        } else {
                bitvec_set_bit(&bv, ZERO);
        }
@@ -1000,6 +1082,70 @@ static int gsm48_rr_enc_cm3(struct osmocom_ms *ms, uint8_t *buf, uint8_t *len)
        } else {
                bitvec_set_bit(&bv, ZERO);
        }
+       /* The following bits are described in TS 24.008 */
+       /* EDGE multi slot support */
+       if (set->edge_ms_sup) {
+               bitvec_set_bit(&bv, ONE);
+               bitvec_set_uint(&bv, set->edge_ms_sup, 5);
+       } else {
+               bitvec_set_bit(&bv, ZERO);
+       }
+       /* EDGE support */
+       if (set->edge_psk_sup) {
+               bitvec_set_bit(&bv, ONE);
+               bitvec_set_bit(&bv, set->edge_psk_uplink == 1);
+               if (set->p_gsm || (set->e_gsm || set->r_gsm)) {
+                       bitvec_set_bit(&bv, ONE);
+                       bitvec_set_uint(&bv, set->class_900_edge, 2);
+               } else {
+                       bitvec_set_bit(&bv, ZERO);
+               }
+               if (set->dcs || set->pcs) {
+                       bitvec_set_bit(&bv, ONE);
+                       bitvec_set_uint(&bv, set->class_dcs_pcs_edge, 2);
+               } else {
+                       bitvec_set_bit(&bv, ZERO);
+               }
+       } else {
+               bitvec_set_bit(&bv, ZERO);
+       }
+       /* GSM 400 Bands */
+       if (set->gsm_480 || set->gsm_450) {
+               bitvec_set_bit(&bv, ONE);
+               bitvec_set_bit(&bv, set->gsm_480 == 1);
+               bitvec_set_bit(&bv, set->gsm_450 == 1);
+               bitvec_set_uint(&bv, set->class_400, 4);
+       } else {
+               bitvec_set_bit(&bv, ZERO);
+       }
+       /* GSM 850 Band */
+       if (set->gsm_850) {
+               bitvec_set_bit(&bv, ONE);
+               bitvec_set_uint(&bv, set->class_850, 4);
+       } else {
+               bitvec_set_bit(&bv, ZERO);
+       }
+       /* PCS Band */
+       if (set->pcs) {
+               bitvec_set_bit(&bv, ONE);
+               bitvec_set_uint(&bv, set->class_pcs, 4);
+       } else {
+               bitvec_set_bit(&bv, ZERO);
+       }
+       /* RAT Capability */
+       bitvec_set_bit(&bv, set->umts_fdd == 1);
+       bitvec_set_bit(&bv, set->umts_tdd == 1);
+       bitvec_set_bit(&bv, set->cdma_2000 == 1);
+       /* DTM */
+       if (set->dtm) {
+               bitvec_set_bit(&bv, ONE);
+               bitvec_set_uint(&bv, set->class_dtm, 2);
+               bitvec_set_bit(&bv, set->dtm_mac == 1);
+               bitvec_set_bit(&bv, set->dtm_egprs == 1);
+       } else {
+               bitvec_set_bit(&bv, ZERO);
+       }
+       /* info: The max number of bits are about 80. */
 
        /* partitial bytes will be completed */
        *len = (bv.cur_bit + 7) >> 3;
@@ -1013,22 +1159,20 @@ int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
        struct gsm_support *sup = &ms->support;
+       struct gsm_settings *set = &ms->settings;
 
-       if (rr->cd_now.arfcn >= 512 && rr->cd_now.arfcn <= 885)
-               cm->pwr_lev = sup->pwr_lev_1800;
-       else
-               cm->pwr_lev = sup->pwr_lev_900;
-       cm->a5_1 = sup->a5_1;
+       cm->pwr_lev = gsm48_current_pwr_lev(set, rr->cd_now.arfcn);
+       cm->a5_1 = !set->a5_1;
        cm->es_ind = sup->es_ind;
        cm->rev_lev = sup->rev_lev;
-       cm->fc = (sup->r_gsm || sup->e_gsm);
+       cm->fc = (set->r_gsm || set->e_gsm);
        cm->vgcs = sup->vgcs;
        cm->vbs = sup->vbs;
-       cm->sm_cap = sup->sms_ptp;
+       cm->sm_cap = set->sms_ptp;
        cm->ss_scr = sup->ss_ind;
        cm->ps_cap = sup->ps_cap;
-       cm->a5_2 = sup->a5_2;
-       cm->a5_3 = sup->a5_3;
+       cm->a5_2 = set->a5_2;
+       cm->a5_3 = set->a5_3;
        cm->cmsp = sup->cmsp;
        cm->solsa = sup->solsa;
        cm->lcsva_cap = sup->lcsva;
@@ -1040,6 +1184,7 @@ int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm)
 static int gsm48_rr_tx_cm_change(struct osmocom_ms *ms)
 {
        struct gsm_support *sup = &ms->support;
+       struct gsm_settings *set = &ms->settings;
        struct msgb *nmsg;
        struct gsm48_hdr *gh;
        struct gsm48_cm_change *cc;
@@ -1061,8 +1206,8 @@ static int gsm48_rr_tx_cm_change(struct osmocom_ms *ms)
        gsm48_rr_enc_cm2(ms, &cc->cm2);
 
        /* classmark 3 */
-       if (sup->dcs_1800 || sup->e_gsm || sup->r_gsm
-        || sup->a5_7 || sup->a5_6 || sup->a5_5 || sup->a5_4
+       if (set->dcs || set->pcs || set->e_gsm || set->r_gsm || set->gsm_850
+        || set->a5_7 || set->a5_6 || set->a5_5 || set->a5_4
         || sup->ms_sup
         || sup->ucs2_treat
         || sup->ext_meas || sup->meas_cap
@@ -1091,7 +1236,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 gsm_settings *set = &ms->settings;
        struct gsm48_rrlayer *rr = &ms->rrlayer;
        struct gsm322_cellsel *cs = &ms->cellsel;
        struct gsm48_sysinfo *s = cs->si;
@@ -1193,7 +1338,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging)
                        chan_req_val);
                break;
        case RR_EST_CAUSE_ANS_PAG_TCH_F:
-               switch (sup->ch_cap) {
+               switch (set->ch_cap) {
                case GSM_CAP_SDCCH:
                        chan_req_mask = 0x0f;
                        chan_req_val = 0x10;
@@ -1211,7 +1356,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging)
                        chan_req_val);
                break;
        case RR_EST_CAUSE_ANS_PAG_TCH_ANY:
-               switch (sup->ch_cap) {
+               switch (set->ch_cap) {
                case GSM_CAP_SDCCH:
                        chan_req_mask = 0x0f;
                        chan_req_val = 0x10;
@@ -1251,7 +1396,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging)
        case RR_EST_CAUSE_OTHER_SDCCH:
                if (s->neci) {
                        chan_req_mask = 0x0f;
-                       chan_req_val = 0x01;
+                       chan_req_val = 0x10;
                        LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OHTER "
                                "with NECI)\n", chan_req_val);
                } else {
@@ -1341,7 +1486,7 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
 
        if (cs->ccch_state != GSM322_CCCH_ST_DATA) {
                LOGP(DRR, LOGL_INFO, "CCCH channel activation failed.\n");
-
+fail:
                if (rr->rr_est_req) {
                        struct msgb *msg =
                                gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
@@ -1360,6 +1505,11 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
                return 0;
        }
 
+       if (!s || !s->si3 || !s->tx_integer) {
+               LOGP(DRR, LOGL_NOTICE, "Not enough SYSINFO\n");
+               goto fail;
+       }
+
        if (rr->state == GSM48_RR_ST_IDLE) {
                LOGP(DRR, LOGL_INFO, "MM already released RR.\n");
 
@@ -1372,7 +1522,7 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
        if (!rr->n_chan_req) {
                LOGP(DRR, LOGL_INFO, "Done with sending RANDOM ACCESS "
                        "bursts\n");
-               if (!bsc_timer_pending(&rr->t3126))
+               if (!osmo_timer_pending(&rr->t3126))
                        start_rr_t3126(rr, 5, 0); /* TODO improve! */
                return 0;
        }
@@ -1426,6 +1576,8 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
                "S(lots) %d ra 0x%02x)\n", s->tx_integer,
                (s->ccch_conf == 1) ? "yes": "no", slots, chan_req);
 
+       slots = (random() % s->tx_integer) + slots;
+
        /* (re)send CHANNEL RQD with new randiom */
        nmsg = gsm48_rsl_msgb_alloc();
        if (!nmsg)
@@ -1436,9 +1588,8 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
        ncch->chan_nr = RSL_CHAN_RACH;
        ncch->data[0] = RSL_IE_REQ_REFERENCE;
        ncch->data[1] = chan_req;
-#warning HACK: fn51 and fn_off
-       ncch->data[2] = (s->ccch_conf == 1) ? 27 : 50;
-       ncch->data[3] = 1 + ((random() % s->tx_integer) + slots) / 51;
+       ncch->data[2] = (slots >> 8) | ((s->ccch_conf == 1) << 7);
+       ncch->data[3] = slots;
        ncch->data[4] = RSL_IE_ACCESS_DELAY;
        ncch->data[5] = set->alter_delay; /* (-)=earlier (+)=later */
        ncch->data[6] = RSL_IE_MS_POWER;
@@ -1450,7 +1601,8 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
        } else {
                tx_power = s->ms_txpwr_max_cch;
                /* power offset in case of DCS1800 */
-               if (s->po && cs->arfcn >= 512 && cs->arfcn <= 885) {
+               if (s->po && (cs->arfcn & 1023) >= 512
+                && (cs->arfcn & 1023) <= 885) {
                        LOGP(DRR, LOGL_INFO, "Use MS-TXPWR-MAX-CCH power value "
                                "%d (%d dBm) with offset %d dBm\n", tx_power,
                                ms_pwr_dbm(gsm_arfcn2band(cs->arfcn), tx_power),
@@ -1472,231 +1624,58 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
        /* store ra until confirmed, then copy it with time into cr_hist */
        rr->cr_ra = chan_req;
 
-       return rslms_recvmsg(nmsg, ms);
+       return lapdm_rslms_recvmsg(nmsg, &ms->lapdm_channel);
 }
 
 /*
  * system information
  */
 
-/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
-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)
-{
-       /* only Bit map 0 format for P-GSM */
-       if ((cd[0] & 0xc0 & mask) != 0x00 &&
-           (sup->p_gsm && !sup->e_gsm && !sup->r_gsm && !sup->dcs_1800))
-               return 0;
-
-       return gsm48_decode_freq_list(f, cd, len, mask, frqt);
-}
-
-/* decode "Cell Selection Parameters" (10.5.2.4) */
-static int gsm48_decode_cell_sel_param(struct gsm48_sysinfo *s,
-       struct gsm48_cell_sel_par *cs)
-{
-#ifdef TODO
-       convert ms_txpwr_max_ccch dependant on the current frequenc and support
-       to the right powe level
-#endif
-       s->ms_txpwr_max_cch = cs->ms_txpwr_max_ccch;
-       s->cell_resel_hyst_db = cs->cell_resel_hyst * 2;
-       s->rxlev_acc_min_db = cs->rxlev_acc_min - 110;
-       s->neci = cs->neci;
-       s->acs = cs->acs;
-
-       return 0;
-}
-
-/* decode "Cell Options (BCCH)" (10.5.2.3) */
-static int gsm48_decode_cellopt_bcch(struct gsm48_sysinfo *s,
-       struct gsm48_cell_options *co)
-{
-       s->bcch_radio_link_timeout = (co->radio_link_timeout + 1) * 4;
-       s->bcch_dtx = co->dtx;
-       s->bcch_pwrc = co->pwrc;
-
-       return 0;
-}
-
-/* decode "Cell Options (SACCH)" (10.5.2.3a) */
-static int gsm48_decode_cellopt_sacch(struct gsm48_sysinfo *s,
-       struct gsm48_cell_options *co)
-{
-       s->sacch_radio_link_timeout = (co->radio_link_timeout + 1) * 4;
-       s->sacch_dtx = co->dtx;
-       s->sacch_pwrc = co->pwrc;
-
-       return 0;
-}
-
-/* decode "Control Channel Description" (10.5.2.11) */
-static int gsm48_decode_ccd(struct gsm48_sysinfo *s,
-       struct gsm48_control_channel_descr *cc)
-{
-       s->ccch_conf = cc->ccch_conf;
-       s->bs_ag_blks_res = cc->bs_ag_blks_res;
-       s->att_allowed = cc->att;
-       s->pag_mf_periods = cc->bs_pa_mfrms + 2;
-       s->t3212 = cc->t3212 * 360; /* convert deci-hours to seconds */
-
-       return 0;
-}
-
-/* decode "Mobile Allocation" (10.5.2.21) */
-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)
+/* send sysinfo event to other layers */
+static int gsm48_new_sysinfo(struct osmocom_ms *ms, uint8_t type)
 {
-       int i, j = 0;
-       uint16_t f[len << 3];
-
-       /* not more than 64 hopping indexes allowed in IE */
-       if (len > 8)
-               return -EINVAL;
-
-       /* tabula rasa */
-       *hopp_len = 0;
-       if (si4) {
-               for (i = 0; i < 1024; i++)
-                       freq[i].mask &= ~FREQ_TYPE_HOPP;
-       }
-
-       /* generating list of all frequencies (1..1023,0) */
-       for (i = 1; i <= 1024; i++) {
-               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;
-                       if (j == (len << 3))
-                               break;
-               }
-       }
+       struct gsm48_sysinfo *s = ms->cellsel.si;
+       struct gsm322_cellsel *cs = &ms->cellsel;
+       struct msgb *nmsg;
+       struct gsm322_msg *em;
 
-       /* fill hopping table with frequency index given by IE
-        * and set hopping type bits
-        */
-       for (i = 0; i < (len << 3); i++) {
-               /* if bit is set, this frequency index is used for hopping */
-               if ((ma[len - 1 - (i >> 3)] & (1 << (i & 7)))) {
-                       LOGP(DRR, LOGL_INFO, "Hopping ARFCN: %d (bit %d)\n",
-                               i, f[i]);
-                       /* index higher than entries in list ? */
-                       if (i >= j) {
-                               LOGP(DRR, LOGL_NOTICE, "Mobile Allocation "
-                                       "hopping index %d exceeds maximum "
-                                       "number of cell frequencies. (%d)\n",
-                                       i + 1, j);
-                               break;
+       /* 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)) {
+               struct gsm48_rr_meas *rrmeas = &ms->rrlayer.meas;
+               int n = 0, i, refer_pcs;
+
+               LOGP(DRR, LOGL_NOTICE, "Complete set of SI5* for BA(%d)\n",
+                       s->nb_ba_ind_si5);
+               rrmeas->nc_num = 0;
+               refer_pcs = gsm_refer_pcs(cs->arfcn, s);
+
+               /* collect channels from freq list (1..1023,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;
+                               }
+                               if (refer_pcs && i >= 512 && i <= 810)
+                                       rrmeas->nc_arfcn[n] = i | ARFCN_PCS;
+                               else
+                                       rrmeas->nc_arfcn[n] = i & 1023;
+                               rrmeas->nc_rxlev[n] = -128;
+                               LOGP(DRR, LOGL_NOTICE, "SI5* report arfcn %s\n",
+                                       gsm_print_arfcn(rrmeas->nc_arfcn[n]));
+                               n++;
                        }
-                       hopping[(*hopp_len)++] = f[i];
-                       if (si4)
-                               freq[f[i]].mask |= FREQ_TYPE_HOPP;
                }
+               rrmeas->nc_num = n;
        }
 
-       return 0;
-}
-
-/* Rach Control decode tables */
-static uint8_t gsm48_max_retrans[4] = {
-       1, 2, 4, 7
-};
-static uint8_t gsm48_tx_integer[16] = {
-       3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 25, 32, 50
-};
-
-/* decode "RACH Control Parameter" (10.5.2.29) */
-static int gsm48_decode_rach_ctl_param(struct gsm48_sysinfo *s,
-       struct gsm48_rach_control *rc)
-{
-       s->reest_denied = rc->re;
-       s->cell_barr = rc->cell_bar;
-       s->tx_integer = gsm48_tx_integer[rc->tx_integer];
-       s->max_retrans = gsm48_max_retrans[rc->max_trans];
-       s->class_barr = (rc->t2 << 8) | rc->t3;
-
-       return 0;
-}
-static int gsm48_decode_rach_ctl_neigh(struct gsm48_sysinfo *s,
-       struct gsm48_rach_control *rc)
-{
-       s->nb_reest_denied = rc->re;
-       s->nb_cell_barr = rc->cell_bar;
-       s->nb_tx_integer = gsm48_tx_integer[rc->tx_integer];
-       s->nb_max_retrans = gsm48_max_retrans[rc->max_trans];
-       s->nb_class_barr = (rc->t2 << 8) | rc->t3;
-
-       return 0;
-}
-
-/* decode "SI 1 Rest Octets" (10.5.2.32) */
-static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, uint8_t *si,
-       uint8_t len)
-{
-       return 0;
-}
-
-/* decode "SI 3 Rest Octets" (10.5.2.34) */
-static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s, uint8_t *si,
-       uint8_t len)
-{
-       struct bitvec bv;
-
-       memset(&bv, 0, sizeof(bv));
-       bv.data_len = len;
-       bv.data = si;
-
-       /* Optional Selection Parameters */
-       if (bitvec_get_bit_high(&bv) == H) {
-               s->sp = 1;
-               s->sp_cbq = bitvec_get_uint(&bv, 1);
-               s->sp_cro = bitvec_get_uint(&bv, 6);
-               s->sp_to = bitvec_get_uint(&bv, 3);
-               s->sp_pt = bitvec_get_uint(&bv, 5);
-       }
-       /* Optional Power Offset */
-       if (bitvec_get_bit_high(&bv) == H) {
-               s->po = 1;
-               s->po_value = bitvec_get_uint(&bv, 3);
-       }
-       /* System Onformation 2ter Indicator */
-       if (bitvec_get_bit_high(&bv) == H)
-               s->si2ter_ind = 1;
-       /* Early Classark Sending Control */
-       if (bitvec_get_bit_high(&bv) == H)
-               s->ecsm = 1;
-       /* Scheduling if and where */
-       if (bitvec_get_bit_high(&bv) == H) {
-               s->sched = 1;
-               s->sched_where = bitvec_get_uint(&bv, 3);
-       }
-       /* GPRS Indicator */
-       s->gi_ra_colour = bitvec_get_uint(&bv, 3);
-       s->gi_si13_pos = bitvec_get_uint(&bv, 1);
-       return 0;
-}
-
-/* decode "SI 4 Rest Octets" (10.5.2.35) */
-static int gsm48_decode_si4_rest(struct gsm48_sysinfo *s, uint8_t *si,
-       uint8_t len)
-{
-       return 0;
-}
-
-/* decode "SI 6 Rest Octets" (10.5.2.35a) */
-static int gsm48_decode_si6_rest(struct gsm48_sysinfo *s, uint8_t *si,
-       uint8_t len)
-{
-       return 0;
-}
-
-/* send sysinfo event to other layers */
-static int gsm48_send_sysinfo(struct osmocom_ms *ms, uint8_t type)
-{
-       struct msgb *nmsg;
-       struct gsm322_msg *em;
-
+       /* send sysinfo event to other layers */
        nmsg = gsm322_msgb_alloc(GSM322_EVENT_SYSINFO);
        if (!nmsg)
                return -ENOMEM;
@@ -1734,23 +1713,12 @@ static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg)
 
        if (!memcmp(si, s->si1_msg, MIN(msgb_l3len(msg), sizeof(s->si1_msg))))
                return 0;
-       memcpy(s->si1_msg, si, MIN(msgb_l3len(msg), sizeof(s->si1_msg)));
-
-       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 1\n");
 
-       /* Cell Channel Description */
-       decode_freq_list(&ms->support, s->freq,
-               si->cell_channel_description,
-               sizeof(si->cell_channel_description), 0xce, FREQ_TYPE_SERV);
-       /* RACH Control Parameter */
-       gsm48_decode_rach_ctl_param(s, &si->rach_control);
-       /* SI 1 Rest Octets */
-       if (payload_len)
-               gsm48_decode_si1_rest(s, si->rest_octets, payload_len);
+       gsm48_decode_sysinfo1(s, si, msgb_l3len(msg));
 
-       s->si1 = 1;
+       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 1\n");
 
-       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) */
@@ -1771,27 +1739,15 @@ 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;
-       memcpy(s->si2_msg, si, MIN(msgb_l3len(msg), sizeof(s->si2_msg)));
-
-       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2\n");
 
-       /* 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;
-       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;
-       /* RACH Control Parameter */
-       gsm48_decode_rach_ctl_neigh(s, &si->rach_control);
+       gsm48_decode_sysinfo2(s, si, msgb_l3len(msg));
 
-       s->si2 = 1;
+       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2\n");
 
-       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) */
@@ -1813,26 +1769,14 @@ static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg)
                return -EINVAL;
        }
 
-       if (!memcmp(si, s->si2b_msg, MIN(msgb_l3len(msg),
-                       sizeof(s->si2b_msg))))
+       if (!memcmp(si, s->si2b_msg, MIN(msgb_l3len(msg), sizeof(s->si2b_msg))))
                return 0;
-       memcpy(s->si2b_msg, si, MIN(msgb_l3len(msg), sizeof(s->si2b_msg)));
-
-       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2bis\n");
 
-       /* 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;
-       decode_freq_list(&ms->support, s->freq,
-               si->bcch_frequency_list,
-               sizeof(si->bcch_frequency_list), 0x8e,
-               FREQ_TYPE_NCELL_2bis);
-       /* RACH Control Parameter */
-       gsm48_decode_rach_ctl_neigh(s, &si->rach_control);
+       gsm48_decode_sysinfo2bis(s, si, msgb_l3len(msg));
 
-       s->si2bis = 1;
+       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2bis\n");
 
-       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) */
@@ -1854,23 +1798,14 @@ static int gsm48_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg)
                return -EINVAL;
        }
 
-       if (!memcmp(si, s->si2t_msg, MIN(msgb_l3len(msg),
-                       sizeof(s->si2t_msg))))
+       if (!memcmp(si, s->si2t_msg, MIN(msgb_l3len(msg), sizeof(s->si2t_msg))))
                return 0;
-       memcpy(s->si2t_msg, si, MIN(msgb_l3len(msg), sizeof(s->si2t_msg)));
-
-       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2ter\n");
 
-       /* Neighbor Cell Description 2 */
-       s->nb_multi_rep_si2ter = (si->ext_bcch_frequency_list[0] >> 6) & 3;
-       decode_freq_list(&ms->support, s->freq,
-               si->ext_bcch_frequency_list,
-               sizeof(si->ext_bcch_frequency_list), 0x8e,
-               FREQ_TYPE_NCELL_2ter);
+       gsm48_decode_sysinfo2ter(s, si, msgb_l3len(msg));
 
-       s->si2ter = 1;
+       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2ter\n");
 
-       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) */
@@ -1895,29 +1830,8 @@ static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg)
 
        if (!memcmp(si, s->si3_msg, MIN(msgb_l3len(msg), sizeof(s->si3_msg))))
                return 0;
-       memcpy(s->si3_msg, si, MIN(msgb_l3len(msg), sizeof(s->si3_msg)));
-
-       /* Cell Identity */
-       s->cell_id = ntohs(si->cell_identity);
-       /* LAI */
-       gsm48_decode_lai(&si->lai, &s->mcc, &s->mnc, &s->lac);
-       /* Control Channel Description */
-       gsm48_decode_ccd(s, &si->control_channel_desc);
-       /* Cell Options (BCCH) */
-       gsm48_decode_cellopt_bcch(s, &si->cell_options);
-       /* Cell Selection Parameters */
-       gsm48_decode_cell_sel_param(s, &si->cell_sel_par);
-       /* RACH Control Parameter */
-       gsm48_decode_rach_ctl_param(s, &si->rach_control);
-       /* SI 3 Rest Octets */
-       if (payload_len >= 4)
-               gsm48_decode_si3_rest(s, si->rest_octets, payload_len);
-
-       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 3 (mcc %s mnc %s "
-               "lac 0x%04x)\n", gsm_print_mcc(s->mcc),
-               gsm_print_mnc(s->mnc), s->lac);
 
-       s->si3 = 1;
+       gsm48_decode_sysinfo3(s, si, msgb_l3len(msg));
 
        if (cs->ccch_mode == CCCH_MODE_NONE) {
                cs->ccch_mode = (s->ccch_conf == 1) ? CCCH_MODE_COMBINED :
@@ -1927,7 +1841,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) */
@@ -1937,8 +1851,6 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm48_system_information_type_4 *si = msgb_l3(msg);
        struct gsm48_sysinfo *s = ms->cellsel.si;
        int payload_len = msgb_l3len(msg) - sizeof(*si);
-       uint8_t *data = si->data;
-       struct gsm48_chan_desc *cd;
 
        if (!s) {
                LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 4 "
@@ -1947,63 +1859,21 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
        }
 
        if (payload_len < 0) {
-               short_read:
                LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 4 "
                        "message.\n");
                return -EINVAL;
        }
 
-       if (!s->si1) {
-               LOGP(DRR, LOGL_NOTICE, "Ignoring SYSTEM INFORMATION 4 "
-                       "until SI 1 is received.\n");
-               return -EBUSY;
-       }
-
        if (!memcmp(si, s->si4_msg, MIN(msgb_l3len(msg), sizeof(s->si4_msg))))
                return 0;
-       memcpy(s->si4_msg, si, MIN(msgb_l3len(msg), sizeof(s->si4_msg)));
-
-       /* LAI */
-       gsm48_decode_lai(&si->lai, &s->mcc, &s->mnc, &s->lac);
-       /* Cell Selection Parameters */
-       gsm48_decode_cell_sel_param(s, &si->cell_sel_par);
-       /* RACH Control Parameter */
-       gsm48_decode_rach_ctl_param(s, &si->rach_control);
-       /* CBCH Channel Description */
-       if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_CHAN_DESC) {
-               if (payload_len < 4)
-                       goto short_read;
-               cd = (struct gsm48_chan_desc *) (data + 1);
-               if (cd->h0.h) {
-                       s->h = 1;
-                       gsm48_decode_chan_h1(cd, &s->tsc, &s->maio, &s->hsn);
-               } else {
-                       s->h = 0;
-                       gsm48_decode_chan_h0(cd, &s->tsc, &s->arfcn);
-               }
-               payload_len -= 4;
-               data += 4;
-       }
-       /* CBCH Mobile Allocation */
-       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->freq, data + 2, si->data[1],
-                       s->hopping, &s->hopp_len, 1);
-               payload_len -= 2 + data[1];
-               data += 2 + data[1];
-       }
-       /* SI 4 Rest Octets */
-       if (payload_len > 0)
-               gsm48_decode_si4_rest(s, data, payload_len);
+
+       gsm48_decode_sysinfo4(s, si, msgb_l3len(msg));
 
        LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4 (mcc %s mnc %s "
                "lac 0x%04x)\n", gsm_print_mcc(s->mcc),
                gsm_print_mnc(s->mnc), s->lac);
 
-       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) */
@@ -2028,19 +1898,12 @@ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg)
 
        if (!memcmp(si, s->si5_msg, MIN(msgb_l3len(msg), sizeof(s->si5_msg))))
                return 0;
-       memcpy(s->si5_msg, si, MIN(msgb_l3len(msg), sizeof(s->si5_msg)));
 
-       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5\n");
-
-       /* 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;
-       decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
-               sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5);
+       gsm48_decode_sysinfo5(s, si, msgb_l3len(msg));
 
-       s->si5 = 1;
+       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5\n");
 
-       return gsm48_send_sysinfo(ms, si->system_information);
+       return gsm48_new_sysinfo(ms, si->system_information);
 }
 
 /* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */
@@ -2063,22 +1926,15 @@ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg)
                return -EINVAL;
        }
 
-       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5bis\n");
-
        if (!memcmp(si, s->si5b_msg, MIN(msgb_l3len(msg),
                        sizeof(s->si5b_msg))))
                return 0;
-       memcpy(s->si5b_msg, si, MIN(msgb_l3len(msg), sizeof(s->si5b_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;
-       decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
-               sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5bis);
+       gsm48_decode_sysinfo5bis(s, si, msgb_l3len(msg));
 
-       s->si5bis = 1;
+       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5bis\n");
 
-       return gsm48_send_sysinfo(ms, si->system_information);
+       return gsm48_new_sysinfo(ms, si->system_information);
 }
 
 /* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */
@@ -2101,20 +1957,15 @@ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg)
                return -EINVAL;
        }
 
-       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5ter\n");
-
        if (!memcmp(si, s->si5t_msg, MIN(msgb_l3len(msg),
                        sizeof(s->si5t_msg))))
                return 0;
-       memcpy(s->si5t_msg, si, MIN(msgb_l3len(msg), sizeof(s->si5t_msg)));
 
-       /* Neighbor Cell Description */
-       decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
-               sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5ter);
+       gsm48_decode_sysinfo5ter(s, si, msgb_l3len(msg));
 
-       s->si5ter = 1;
+       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5ter\n");
 
-       return gsm48_send_sysinfo(ms, si->system_information);
+       return gsm48_new_sysinfo(ms, si->system_information);
 }
 
 /* receive "SYSTEM INFORMATION 6" message (9.1.39) */
@@ -2123,6 +1974,7 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
        /* NOTE: pseudo length is not in this structure, so we skip */
        struct gsm48_system_information_type_6 *si = msgb_l3(msg) + 1;
        struct gsm48_sysinfo *s = ms->cellsel.si;
+       struct rx_meas_stat *meas = &ms->meas;
        int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
 
        if (!s) {
@@ -2139,30 +1991,17 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
 
        if (!memcmp(si, s->si6_msg, MIN(msgb_l3len(msg), sizeof(s->si6_msg))))
                return 0;
-       memcpy(s->si6_msg, si, MIN(msgb_l3len(msg), sizeof(s->si6_msg)));
-
-       /* Cell Identity */
-       if (s->si6 && s->cell_id != ntohs(si->cell_identity))
-               LOGP(DRR, LOGL_INFO, "Cell ID on SI 6 differs from previous "
-                       "read.\n");
-       s->cell_id = ntohs(si->cell_identity);
-       /* LAI */
-       gsm48_decode_lai(&si->lai, &s->mcc, &s->mnc, &s->lac);
-       /* Cell Options (SACCH) */
-       gsm48_decode_cellopt_sacch(s, &si->cell_options);
-       /* NCC Permitted */
-       s->nb_ncc_permitted = si->ncc_permitted;
-       /* SI 6 Rest Octets */
-       if (payload_len >= 4)
-               gsm48_decode_si6_rest(s, si->rest_octets, payload_len);
+
+       gsm48_decode_sysinfo6(s, si, msgb_l3len(msg));
 
        LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (mcc %s mnc %s "
                "lac 0x%04x SACCH-timeout %d)\n", gsm_print_mcc(s->mcc),
                gsm_print_mnc(s->mnc), s->lac, s->sacch_radio_link_timeout);
 
-       s->si6 = 1;
+       meas->rl_fail = meas->s = s->sacch_radio_link_timeout;
+       LOGP(DRR, LOGL_INFO, "using (new) SACCH timeout %d\n", meas->rl_fail);
 
-       return gsm48_send_sysinfo(ms, si->system_information);
+       return gsm48_new_sysinfo(ms, si->system_information);
 }
 
 /*
@@ -2197,22 +2036,22 @@ static int gsm_match_mi(struct osmocom_ms *ms, uint8_t *mi)
                 && 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",
+                       LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n",
                                ntohl(tmsi));
 
                        return 1;
                } else
-                       LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+                       LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
                                ntohl(tmsi));
                break;
        case GSM_MI_TYPE_IMSI:
                gsm48_mi_to_string(imsi, sizeof(imsi), mi + 1, mi[0]);
                if (!strcmp(imsi, ms->subscr.imsi)) {
-                       LOGP(DPAG, LOGL_INFO, "IMSI %s matches\n", imsi);
+                       LOGP(DPAG, LOGL_INFO, " IMSI %s matches\n", imsi);
 
                        return 1;
                } else
-                       LOGP(DPAG, LOGL_INFO, "IMSI %s (not for us)\n", imsi);
+                       LOGP(DPAG, LOGL_INFO, " IMSI %s (not for us)\n", imsi);
                break;
        default:
                LOGP(DPAG, LOGL_NOTICE, "Paging with unsupported MI type %d.\n",
@@ -2314,20 +2153,20 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg)
         && 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));
+               LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1));
                return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1);
        } else
-               LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+               LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
                        ntohl(pa->tmsi1));
        /* second MI */
        if (ms->subscr.tmsi == ntohl(pa->tmsi2)
         && 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));
+               LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2));
                return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1);
        } else
-               LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+               LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
                        ntohl(pa->tmsi2));
        /* third MI */
        mi = pa->data;
@@ -2380,40 +2219,40 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
         && 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));
+               LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1));
                return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1);
        } else
-               LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+               LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
                        ntohl(pa->tmsi1));
        /* second MI */
        if (ms->subscr.tmsi == ntohl(pa->tmsi2)
         && 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));
+               LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2));
                return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1);
        } else
-               LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+               LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
                        ntohl(pa->tmsi2));
        /* thrid MI */
        if (ms->subscr.tmsi == ntohl(pa->tmsi3)
         && 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));
+               LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi3));
                return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1);
        } else
-               LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+               LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
                        ntohl(pa->tmsi3));
        /* fourth MI */
        if (ms->subscr.tmsi == ntohl(pa->tmsi4)
         && 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));
+               LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi4));
                return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_4], 1);
        } else
-               LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+               LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
                        ntohl(pa->tmsi4));
 
        return 0;
@@ -2464,6 +2303,8 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
        struct gsm48_imm_ass *ia = msgb_l3(msg);
+       struct gsm322_cellsel *cs = &ms->cellsel;
+       struct gsm48_sysinfo *s = cs->si;
        int ma_len = msgb_l3len(msg) - sizeof(*ia);
        uint8_t ch_type, ch_subch, ch_ts;
        struct gsm48_rr_cd cd;
@@ -2472,6 +2313,7 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
 #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 */
@@ -2514,12 +2356,14 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
        } else {
                cd.h = 0;
                gsm48_decode_chan_h0(&ia->chan_desc, &cd.tsc, &cd.arfcn);
+               if (gsm_refer_pcs(cs->arfcn, s))
+                       cd.arfcn |= ARFCN_PCS;
                LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x "
-                       "ARFCN %u TS %u SS %u TSC %u)\n",
+                       "ARFCN %s TS %u SS %u TSC %u)\n",
                        ia->timing_advance,
                        ia->timing_advance * GSM_TA_CM / 100,
-                       ia->req_ref.ra, ia->chan_desc.chan_nr, cd.arfcn,
-                       ch_ts, ch_subch, cd.tsc);
+                       ia->req_ref.ra, ia->chan_desc.chan_nr,
+                       gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc);
        }
 
        /* 3.3.1.1.2: ignore assignment while idle */
@@ -2558,6 +2402,8 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
 static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct gsm322_cellsel *cs = &ms->cellsel;
+       struct gsm48_sysinfo *s = cs->si;
        struct gsm48_imm_ass_ext *ia = msgb_l3(msg);
        int ma_len = msgb_l3len(msg) - sizeof(*ia);
        uint8_t ch_type, ch_subch, ch_ts;
@@ -2567,7 +2413,9 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
 #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 */
@@ -2599,7 +2447,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
 
        /* 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;
@@ -2614,12 +2462,14 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
        } else {
                cd1.h = 0;
                gsm48_decode_chan_h0(&ia->chan_desc1, &cd1.tsc, &cd1.arfcn);
+               if (gsm_refer_pcs(cs->arfcn, s))
+                       cd1.arfcn |= ARFCN_PCS;
                LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x "
-                       "chan_nr 0x%02x ARFCN %u TS %u SS %u TSC %u)\n",
+                       "chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n",
                        ia->timing_advance1,
                        ia->timing_advance1 * GSM_TA_CM / 100,
-                       ia->req_ref1.ra, ia->chan_desc1.chan_nr, cd1.arfcn,
-                       ch_ts, ch_subch, cd1.tsc);
+                       ia->req_ref1.ra, ia->chan_desc1.chan_nr,
+                       gsm_print_arfcn(cd1.arfcn), ch_ts, ch_subch, cd1.tsc);
        }
        cd2.chan_nr = ia->chan_desc2.chan_nr;
        rsl_dec_chan_nr(cd2.chan_nr, &ch_type, &ch_subch, &ch_ts);
@@ -2636,12 +2486,14 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
        } else {
                cd2.h = 0;
                gsm48_decode_chan_h0(&ia->chan_desc2, &cd2.tsc, &cd2.arfcn);
+               if (gsm_refer_pcs(cs->arfcn, s))
+                       cd2.arfcn |= ARFCN_PCS;
                LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x "
-                       "chan_nr 0x%02x ARFCN %u TS %u SS %u TSC %u)\n",
+                       "chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n",
                        ia->timing_advance2,
                        ia->timing_advance2 * GSM_TA_CM / 100,
-                       ia->req_ref2.ra, ia->chan_desc2.chan_nr, cd2.arfcn,
-                       ch_ts, ch_subch, cd2.tsc);
+                       ia->req_ref2.ra, ia->chan_desc2.chan_nr,
+                       gsm_print_arfcn(cd2.arfcn), ch_ts, ch_subch, cd2.tsc);
        }
 
        /* 3.3.1.1.2: ignore assignment while idle */
@@ -2671,7 +2523,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
 
                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));
@@ -2728,7 +2580,7 @@ static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg)
                        if (t3122_value)
                                start_rr_t3122(rr, t3122_value, 0);
                        /* start timer 3126 if not already */
-                       if (!bsc_timer_pending(&rr->t3126))
+                       if (!osmo_timer_pending(&rr->t3126))
                                start_rr_t3126(rr, 5, 0); /* TODO improve! */
                        /* stop assignmnet requests */
                        rr->n_chan_req = 0;
@@ -2768,14 +2620,86 @@ 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)) {
+               rep_ba = s->nb_ba_ind_si5;
+               if ((s->si5bis && s->nb_ext_ind_si5
+                 && s->nb_ba_ind_si5bis != rep_ba)
+                || (s->si5ter && 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->si5ter)
+                       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));
 
@@ -2783,51 +2707,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 lapdm_rslms_recvmsg(nmsg, &ms->lapdm_channel);
 }
 
 /*
@@ -2902,8 +2843,13 @@ int gsm48_rr_los(struct osmocom_ms *ms)
 static int gsm48_rr_activate_channel(struct osmocom_ms *ms,
        struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len)
 {
+       struct gsm_subscriber *subscr = &ms->subscr;
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
        struct gsm_settings *set = &ms->settings;
+       struct gsm48_sysinfo *s = ms->cellsel.si;
+       struct rx_meas_stat *meas = &ms->meas;
        uint8_t ch_type, ch_subch, ch_ts;
+       uint8_t timeout = 64;
 
        /* setting (new) timing advance */
        LOGP(DRR, LOGL_INFO, "setting indicated TA %d (actual TA %d)\n",
@@ -2912,23 +2858,52 @@ static int gsm48_rr_activate_channel(struct osmocom_ms *ms,
                        (set->alter_tx_power) ? set->alter_tx_power_value
                                                : cd->ind_tx_power);
 
+       /* reset measurement and link timeout */
+       meas->ds_fail = 0;
+       if (s) {
+               if (s->sacch_radio_link_timeout) {
+                       timeout = s->sacch_radio_link_timeout;
+                       LOGP(DRR, LOGL_INFO, "using last SACCH timeout %d\n",
+                               timeout);
+               } else if (s->bcch_radio_link_timeout) {
+                       timeout = s->bcch_radio_link_timeout;
+                       LOGP(DRR, LOGL_INFO, "using last BCCH timeout %d\n",
+                               timeout);
+               }
+       }
+       meas->rl_fail = meas->s = timeout;
+
+       /* 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));
+       }
+       meas->frames = meas->snr = meas->berr = meas->rxlev = 0;
+       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);
-       if ((ch_type != RSL_CHAN_SDCCH8_ACCH
-         && ch_type != RSL_CHAN_SDCCH4_ACCH
-         && ch_type != RSL_CHAN_Bm_ACCHs) /*|| 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);
-       }
+       LOGP(DRR, LOGL_INFO, " Channel type %d, subch %d, ts %d, mode %d, "
+               "cipher %d\n", ch_type, ch_subch, ch_ts, cd->mode,
+               rr->cipher_type + 1);
        if (cd->h)
                l1ctl_tx_dm_est_req_h1(ms, cd->maio, cd->hsn,
-                       ma, ma_len, cd->chan_nr, cd->tsc);
+                       ma, ma_len, cd->chan_nr, cd->tsc, cd->mode);
        else
-               l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc);
+               l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc,
+                       cd->mode);
+       rr->dm_est = 1;
+
+       /* old SI 5/6 are not valid on a new dedicated channel */
+       s->si5 = s->si5bis = s->si5ter = s->si6 = 0;
 
-#warning FIXME: channel mode, cyphering command
+       if (rr->cipher_on)
+               l1ctl_tx_crypto_req(ms, rr->cipher_type + 1, subscr->key, 8);
 
        return 0;
 }
@@ -2937,13 +2912,19 @@ static int gsm48_rr_activate_channel(struct osmocom_ms *ms,
 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);
 
-#warning FIXME: channel mode, cyphering command
+       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;
 }
@@ -2952,14 +2933,17 @@ static int gsm48_rr_channel_after_time(struct osmocom_ms *ms,
 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;
+       struct gsm322_cellsel *cs = &ms->cellsel;
+       struct gsm48_sysinfo *s = cs->si;
+       struct gsm_settings *set = &ms->settings;
+       int i, pcs, index;
        uint16_t arfcn;
 
+       pcs = gsm_refer_pcs(cs->arfcn, s) ? ARFCN_PCS : 0;
+
        /* no hopping (no MA), channel description is valid */
        if (!cd->h) {
-               ma_len = 0;
+               *ma_len = 0;
                return 0;
        }
 
@@ -3006,18 +2990,17 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd,
                /* 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);
+                               LOGP(DRR, LOGL_INFO, "Listed ARFCN #%d: %s\n",
+                                       j, gsm_print_arfcn((i & 1023) | pcs));
                                if (j == 64) {
                                        LOGP(DRR, LOGL_NOTICE, "frequency list "
                                                "exceeds 64 entries!\n");
                                        return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
                                }
-                               ma[j++] = i;
+                               ma[j++] = i & 1023;
                        }
                }
                *ma_len = j;
-               return 0;
        } else
        /* decode frequency channel sequence */
        if (cd->freq_seq_lv[0]) {
@@ -3031,10 +3014,10 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd,
                        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);
+               LOGP(DRR, LOGL_INFO, "Listed Sequence ARFCN #%d: %s\n", j,
+                       gsm_print_arfcn(arfcn | pcs));
                ma[j++] = arfcn;
-               for (i = 0; i <= 16; i++) {
+               for (i = 0; i < 16; i++) {
                        if ((i & 1))
                                inc = cd->freq_seq_lv[2 + (i >> 1)] & 0x0f;
                        else
@@ -3042,25 +3025,27 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd,
                        if (inc) {
                                arfcn += inc;
                                LOGP(DRR, LOGL_INFO, "Listed Sequence ARFCN "
-                                       "#%d: %d\n", j, arfcn);
+                                       "#%d: %s\n", j,
+                                       gsm_print_arfcn(i | pcs));
                                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 */
+       /* convert to band_arfcn and 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);
+               arfcn = ma[i] | pcs;
+               ma[i] = arfcn;
+               index = arfcn2index(arfcn);
+               if (!(set->freq_map[index >> 3] & (1 << (index & 7)))) {
+                       LOGP(DRR, LOGL_NOTICE, "Hopping ARFCN %s not "
+                               "supported\n", gsm_print_arfcn(arfcn));
                        return GSM48_RR_CAUSE_FREQ_NOT_IMPL;
                }
        }
@@ -3081,6 +3066,9 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms)
        uint16_t ma[64];
        uint8_t ma_len;
 
+       /* 3.3.1.1.3.1 */
+       stop_rr_t3126(rr);
+
        /* check if we have to change channel at starting time (we delay) */
        if (rr->cd_now.start) {
                int32_t now, start, diff;
@@ -3120,9 +3108,6 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms)
        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;
 
@@ -3232,6 +3217,18 @@ static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg)
        struct msgb *nmsg;
        struct gsm48_rr_hdr *nrrh;
 
+       /* switch back to old channel, if modify/ho failed */
+       switch (rr->modify_state) {
+       case GSM48_RR_MOD_ASSIGN:
+       case GSM48_RR_MOD_HANDO:
+               /* channel is deactivate there */
+               return gsm48_rr_rel_cnf(ms, msg);
+       case GSM48_RR_MOD_ASSIGN_RESUME:
+       case GSM48_RR_MOD_HANDO_RESUME:
+               rr->modify_state = GSM48_RR_MOD_NONE;
+               break;
+       }
+
        LOGP(DSUM, LOGL_INFO, "Radio link is released\n");
 
        /* send inication to upper layer */
@@ -3303,12 +3300,31 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg)
  * 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)
 {
-#warning disabled, until added to libosmocore
-#if 0
        struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct gsm322_cellsel *cs = &ms->cellsel;
+       struct gsm48_sysinfo *s = cs->si;
        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;
@@ -3345,12 +3361,14 @@ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg)
        } 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);
+               if (gsm_refer_pcs(cs->arfcn, s))
+                       cd.arfcn |= ARFCN_PCS;
+               LOGP(DRR, LOGL_INFO, " (ARFCN %s TS %u SS %u TSC %u)\n",
+                       gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc);
        }
 
        /* mobile allocation */
-       memcpy(&rr->cd_now.mob_alloc_lv, &fr->mob_alloc_len, 
+       memcpy(rr->cd_now.mob_alloc_lv, &fr->mob_alloc_len, 
                fr->mob_alloc_len + 1);
 
        /* starting time */
@@ -3360,11 +3378,11 @@ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg)
        /* 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;
+               const uint8_t *v = fr->mob_alloc + fr->mob_alloc_len + 2 + 1;
 
                LOGP(DRR, LOGL_INFO, " using cell channel description)\n");
-               memcpy(&cd.cell_desc_lv + 1, v, 17);
                cd.cell_desc_lv[0] = 16;
+               memcpy(cd.cell_desc_lv + 1, v, 17);
        }
 
        /* render channel "after time" */
@@ -3381,7 +3399,6 @@ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg)
 
        rr->cd_now.start = 0;
 
-#endif
        return 0;
 }
 
@@ -3416,13 +3433,15 @@ static int gsm48_rr_tx_chan_modify_ack(struct osmocom_ms *ms,
 static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct gsm322_cellsel *cs = &ms->cellsel;
+       struct gsm48_sysinfo *s = cs->si;
        struct gsm48_hdr *gh = msgb_l3(msg);
        struct gsm48_chan_mode_modify *cm =
                (struct gsm48_chan_mode_modify *)gh->data;
        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");
 
@@ -3447,26 +3466,21 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg)
        } else {
                cd->h = 0;
                gsm48_decode_chan_h0(&cm->chan_desc, &cd->tsc, &cd->arfcn);
-               LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x ARFCN %u TS %u SS %u "
-                       "TSC %u mode %u)\n", cm->chan_desc.chan_nr, cd->arfcn,
-                       ch_ts, ch_subch, cd->tsc, cm->mode);
+               if (gsm_refer_pcs(cs->arfcn, s))
+                       cd->arfcn |= ARFCN_PCS;
+               LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x ARFCN %s TS %u SS %u "
+                       "TSC %u mode %u)\n", cm->chan_desc.chan_nr,
+                       gsm_print_arfcn(cd->arfcn), 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);
-       }
-       rr->cd_now.mode = mode;
-#warning FIXME: channel 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 */
@@ -3490,11 +3504,12 @@ static int gsm48_rr_tx_ass_cpl(struct osmocom_ms *ms, uint8_t cause)
        /* RR_CAUSE */
        ac->rr_cause = cause;
 
-       return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+       return gsm48_send_rsl(ms, RSL_MT_RES_REQ, nmsg);
 }
 
 /* 9.1.4 sending ASSIGNMENT FAILURE */
-static int gsm48_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause)
+static int gsm48_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause,
+       uint8_t rsl_prim)
 {
        struct msgb *nmsg;
        struct gsm48_hdr *gh;
@@ -3514,13 +3529,15 @@ static int gsm48_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause)
        /* RR_CAUSE */
        af->rr_cause = cause;
 
-       return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+       return gsm48_send_rsl(ms, rsl_prim, nmsg);
 }
 
 /* 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 gsm322_cellsel *cs = &ms->cellsel;
+       struct gsm48_sysinfo *s = cs->si;
        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);
@@ -3539,7 +3556,9 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
        LOGP(DRR, LOGL_INFO, "ASSIGNMENT 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 ASSIGNMENT COMMAND "
@@ -3565,9 +3584,12 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
                } else {
                        cdb->h = 0;
                        gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn);
+                       if (gsm_refer_pcs(cs->arfcn, s))
+                               cdb->arfcn |= ARFCN_PCS;
                        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);
+                               "ARFCN %s TS %u SS %u TSC %u)\n", ccd->chan_nr,
+                               gsm_print_arfcn(cdb->arfcn),
+                               ch_ts, ch_subch, cdb->tsc);
                }
                before_time = 1;
        }
@@ -3585,9 +3607,11 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
        } 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 "
+               if (gsm_refer_pcs(cs->arfcn, s))
+                       cda->arfcn |= ARFCN_PCS;
+               LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %s TS %u "
                        "SS %u TSC %u)\n", ac->chan_desc.chan_nr,
-                       cda->arfcn, ch_ts, ch_subch, cda->tsc);
+                       gsm_print_arfcn(cda->arfcn), ch_ts, ch_subch, cda->tsc);
        }
 
        /* starting time */
@@ -3628,7 +3652,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
                                LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
                                return -ENOMEM;
                        }
-                       memcpy(&cda->mob_alloc_lv, lv, *lv + 1);
+                       memcpy(cda->mob_alloc_lv, lv, *lv + 1);
                } else
                if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_AFTER)) {
                        const uint8_t *lv =
@@ -3640,7 +3664,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
                                LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
                                return -ENOMEM;
                        }
-                       memcpy(&cda->freq_list_lv, lv, *lv + 1);
+                       memcpy(cda->freq_list_lv, lv, *lv + 1);
                } else {
                        LOGP(DRR, LOGL_NOTICE, " after: hopping required, but "
                                "no mobile allocation / frequency list\n");
@@ -3659,7 +3683,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
                                LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
                                return -ENOMEM;
                        }
-                       memcpy(&cdb->mob_alloc_lv, lv, *lv + 1);
+                       memcpy(cdb->mob_alloc_lv, lv, *lv + 1);
                } else
                if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_BEFORE)) {
                        const uint8_t *lv =
@@ -3671,31 +3695,34 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
                                LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
                                return -ENOMEM;
                        }
-                       memcpy(&cdb->freq_list_lv, lv, *lv + 1);
+                       memcpy(cdb->freq_list_lv, lv, *lv + 1);
                } else
                if (TLVP_PRESENT(&tp, GSM48_IE_F_CH_SEQ_BEFORE)) {
-                       const uint8_t *lv =
-                               TLVP_VAL(&tp, GSM48_IE_F_CH_SEQ_BEFORE) - 1;
+                       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 (*lv + 1 > sizeof(cdb->freq_seq_lv)) {
+                       if (len + 1 > sizeof(cdb->freq_seq_lv)) {
                                LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
                                return -ENOMEM;
                        }
-                       memcpy(&cdb->freq_seq_lv, lv, *lv + 1);
+                       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,
+                       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,
+                       memcpy(cdb->freq_list_lv, cda->freq_list_lv,
                                sizeof(cdb->freq_list_lv));
                } else {
                        LOGP(DRR, LOGL_NOTICE, " before: hopping required, but "
@@ -3705,21 +3732,24 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
 
        /* cell channel description */
        if (TLVP_PRESENT(&tp, GSM48_IE_CELL_CH_DESC)) {
-               const uint8_t *lv = TLVP_VAL(&tp, GSM48_IE_CELL_CH_DESC) - 1;
+               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 (*lv + 1 > sizeof(cdb->cell_desc_lv)) {
+               if (len + 1 > sizeof(cdb->cell_desc_lv)) {
                        LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
                        return -ENOMEM;
                }
-               memcpy(&cdb->cell_desc_lv, lv, *lv + 1);
-               memcpy(&cda->cell_desc_lv, lv, *lv + 1);
+               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,
+               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,
+               memcpy(cda->cell_desc_lv, rr->cd_now.cell_desc_lv,
                        sizeof(cda->cell_desc_lv));
        }
 
@@ -3732,7 +3762,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
                cda->mode = cdb->mode = rr->cd_now.mode;
 
        /* cipher mode setting */
-       if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET))
+       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",
@@ -3774,22 +3804,17 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
        }
 
        /* 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, RSL_MT_DATA_REQ);
        if (before_time) {
                cause = gsm48_rr_render_ma(ms, cdb, ma, &ma_len);
                if (cause)
-                       return gsm48_rr_tx_ass_fail(ms, cause);
+                       return gsm48_rr_tx_ass_fail(ms, cause, RSL_MT_DATA_REQ);
        }
        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
+               return gsm48_rr_tx_ass_fail(ms, cause, RSL_MT_DATA_REQ);
 
 #ifdef TEST_FREQUENCY_MOD
        LOGP(DRR, LOGL_INFO, " TESTING: frequency modify ASS.CMD\n");
@@ -3822,8 +3847,6 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
 /* 9.1.16 sending HANDOVER COMPLETE */
 static int gsm48_rr_tx_hando_cpl(struct osmocom_ms *ms, uint8_t cause)
 {
-#warning disabled, until added to libosmocore
-#if 0
        struct msgb *nmsg;
        struct gsm48_hdr *gh;
        struct gsm48_ho_cpl *hc;
@@ -3844,15 +3867,13 @@ static int gsm48_rr_tx_hando_cpl(struct osmocom_ms *ms, uint8_t cause)
 
        // FIXME: mobile observed time
 
-       return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
-#endif
+       return gsm48_send_rsl(ms, RSL_MT_RES_REQ, nmsg);
 }
 
 /* 9.1.4 sending HANDOVER FAILURE */
-static int gsm48_rr_tx_hando_fail(struct osmocom_ms *ms, uint8_t cause)
+static int gsm48_rr_tx_hando_fail(struct osmocom_ms *ms, uint8_t cause,
+       uint8_t rsl_prim)
 {
-#warning disabled, until added to libosmocore
-#if 0
        struct msgb *nmsg;
        struct gsm48_hdr *gh;
        struct gsm48_ho_fail *hf;
@@ -3871,14 +3892,15 @@ static int gsm48_rr_tx_hando_fail(struct osmocom_ms *ms, uint8_t cause)
        /* RR_CAUSE */
        hf->rr_cause = cause;
 
-       return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
-#endif
+       return gsm48_send_rsl(ms, rsl_prim, 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 gsm322_cellsel *cs = &ms->cellsel;
+       struct gsm48_sysinfo *s = cs->si;
        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);
@@ -3898,7 +3920,9 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
        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 "
@@ -3914,6 +3938,8 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
        rr->chan_req_val = ho->ho_ref;
        rr->chan_req_mask = 0x00;
 
+       tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0);
+
        /* sync ind */
        if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND)) {     
                gsm48_decode_sync_ind(rr, (struct gsm48_sync_ind *) 
@@ -3922,8 +3948,6 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
                        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 *)
@@ -3940,9 +3964,12 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
                } else {
                        cdb->h = 0;
                        gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn);
+                       if (gsm_refer_pcs(cs->arfcn, s))
+                               cdb->arfcn |= ARFCN_PCS;
                        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);
+                               "ARFCN %s TS %u SS %u TSC %u)\n", ccd->chan_nr,
+                               gsm_print_arfcn(cdb->arfcn),
+                               ch_ts, ch_subch, cdb->tsc);
                }
                before_time = 1;
        }
@@ -3960,9 +3987,11 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
        } 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 "
+               if (gsm_refer_pcs(cs->arfcn, s))
+                       cda->arfcn |= ARFCN_PCS;
+               LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %s TS %u "
                        "SS %u TSC %u)\n", ho->chan_desc.chan_nr,
-                       cda->arfcn, ch_ts, ch_subch, cda->tsc);
+                       gsm_print_arfcn(cda->arfcn), ch_ts, ch_subch, cda->tsc);
        }
 
        /* starting time */
@@ -4003,7 +4032,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
                                LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
                                return -ENOMEM;
                        }
-                       memcpy(&cda->mob_alloc_lv, lv, *lv + 1);
+                       memcpy(cda->mob_alloc_lv, lv, *lv + 1);
                } else
                if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_AFTER)) {
                        const uint8_t *lv =
@@ -4015,7 +4044,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
                                LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
                                return -ENOMEM;
                        }
-                       memcpy(&cda->freq_list_lv, lv, *lv + 1);
+                       memcpy(cda->freq_list_lv, lv, *lv + 1);
                } else {
                        LOGP(DRR, LOGL_NOTICE, " after: hopping required, but "
                                "no mobile allocation / frequency list\n");
@@ -4034,7 +4063,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
                                LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
                                return -ENOMEM;
                        }
-                       memcpy(&cdb->mob_alloc_lv, lv, *lv + 1);
+                       memcpy(cdb->mob_alloc_lv, lv, *lv + 1);
                } else
                if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_BEFORE)) {
                        const uint8_t *lv =
@@ -4046,31 +4075,34 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
                                LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
                                return -ENOMEM;
                        }
-                       memcpy(&cdb->freq_list_lv, lv, *lv + 1);
+                       memcpy(cdb->freq_list_lv, lv, *lv + 1);
                } else
                if (TLVP_PRESENT(&tp, GSM48_IE_F_CH_SEQ_BEFORE)) {
-                       const uint8_t *lv =
-                               TLVP_VAL(&tp, GSM48_IE_F_CH_SEQ_BEFORE) - 1;
+                       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 (*lv + 1 > sizeof(cdb->freq_seq_lv)) {
+                       if (len + 1 > sizeof(cdb->freq_seq_lv)) {
                                LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
                                return -ENOMEM;
                        }
-                       memcpy(&cdb->freq_seq_lv, lv, *lv + 1);
+                       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,
+                       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,
+                       memcpy(cdb->freq_list_lv, cda->freq_list_lv,
                                sizeof(cdb->freq_list_lv));
                } else {
                        LOGP(DRR, LOGL_NOTICE, " before: hopping required, but "
@@ -4080,21 +4112,24 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
 
        /* cell channel description */
        if (TLVP_PRESENT(&tp, GSM48_IE_CELL_CH_DESC)) {
-               const uint8_t *lv = TLVP_VAL(&tp, GSM48_IE_CELL_CH_DESC) - 1;
+               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 (*lv + 1 > sizeof(cdb->cell_desc_lv)) {
+               if (len + 1 > sizeof(cdb->cell_desc_lv)) {
                        LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
                        return -ENOMEM;
                }
-               memcpy(&cdb->cell_desc_lv, lv, *lv + 1);
-               memcpy(&cda->cell_desc_lv, lv, *lv + 1);
+               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,
+               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,
+               memcpy(cda->cell_desc_lv, rr->cd_now.cell_desc_lv,
                        sizeof(cda->cell_desc_lv));
        }
 
@@ -4107,7 +4142,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
                cda->mode = cdb->mode = rr->cd_now.mode;
 
        /* cipher mode setting */
-       if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET))
+       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",
@@ -4153,11 +4188,11 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
        if (before_time) {
                cause = gsm48_rr_render_ma(ms, cdb, ma, &ma_len);
                if (cause)
-                       return gsm48_rr_tx_hando_fail(ms, cause);
+                       return gsm48_rr_tx_hando_fail(ms, cause, RSL_MT_DATA_REQ);
        }
        cause = gsm48_rr_render_ma(ms, cda, ma, &ma_len);
        if (cause)
-               return gsm48_rr_tx_hando_fail(ms, cause);
+               return gsm48_rr_tx_hando_fail(ms, cause, RSL_MT_DATA_REQ);
 
 
 #if 0
@@ -4216,21 +4251,6 @@ static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
 
        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);
 
@@ -4245,7 +4265,6 @@ 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;
 
@@ -4253,6 +4272,8 @@ static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
                LOGP(DRR, LOGL_INFO, "suspension coplete, leaving dedicated "
                        "mode\n");
                l1ctl_tx_dm_rel_req(ms);
+               ms->meas.rl_fail = 0;
+               rr->dm_est = 0;
                l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
 
                /* store current channel descriptions */
@@ -4285,10 +4306,14 @@ static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
 
                /* 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);
+               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;
+               }
 
 #ifdef TODO
                /* trigger RACH */
@@ -4320,7 +4345,7 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
        uint16_t acc_class;
 
        /* 3.3.1.1.3.2 */
-       if (bsc_timer_pending(&rr->t3122)) {
+       if (osmo_timer_pending(&rr->t3122)) {
                if (rrh->cause != RR_EST_CAUSE_EMERGENCY) {
                        LOGP(DRR, LOGL_INFO, "T3122 running, rejecting!\n");
                        cause = RR_REL_CAUSE_T3122;
@@ -4355,13 +4380,15 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
        /* check if camping */
        if (cs->state != GSM322_C3_CAMPED_NORMALLY
         && rrh->cause != RR_EST_CAUSE_EMERGENCY) {
-               LOGP(DRR, LOGL_INFO, "Not camping normally, rejecting!\n");
+               LOGP(DRR, LOGL_INFO, "Not camping normally, rejecting! "
+                       "(cs->state = %d)\n", cs->state);
                cause = RR_REL_CAUSE_EMERGENCY_ONLY;
                goto reject;
        }
        if (cs->state != GSM322_C3_CAMPED_NORMALLY
         && cs->state != GSM322_C7_CAMPED_ANY_CELL) {
-               LOGP(DRR, LOGL_INFO, "Not camping, rejecting!\n");
+               LOGP(DRR, LOGL_INFO, "Not camping, rejecting! "
+                       "(cs->state = %d)\n", cs->state);
                cause = RR_REL_CAUSE_TRY_LATER;
                goto reject;
        }
@@ -4610,7 +4637,6 @@ static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg)
 /* unit data from layer 2 to RR layer */
 static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
 {
-       struct gsm48_rrlayer *rr = &ms->rrlayer;
        struct gsm322_cellsel *cs = &ms->cellsel;
        struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
        struct tlv_parsed tv;
@@ -4630,21 +4656,6 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
         && cs->ccch_state != GSM322_CCCH_ST_DATA)
                return -EINVAL;
 
-       /* when camping, start/reset loss timer */
-       if (cs->state == GSM322_C3_CAMPED_NORMALLY
-        || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
-               struct gsm48_sysinfo *s = &ms->cellsel.sel_si;
-#ifdef TODO
-       set radio link timeout on layer 1
-       it is the number of subsequent BCCH blocks. (about 1/4 seconds)
-#else
-               /* use maximu loss timer, if to value is not available yet */
-               start_loss_timer(cs, ((rr->state == GSM48_RR_ST_DEDICATED)
-                       ? ((s->sacch_radio_link_timeout) ? : 64)
-                       : s->bcch_radio_link_timeout) / 4, 0);
-#endif
-       }
-
        /* temporary moved here until confirm is fixed */
        if (cs->ccch_state != GSM322_CCCH_ST_DATA) {
                LOGP(DCS, LOGL_INFO, "Channel provides data.\n");
@@ -4674,8 +4685,9 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
                return gsm48_rr_rx_pch_agch(ms, msg);
        case RSL_CHAN_BCCH:
                return gsm48_rr_rx_bcch(ms, msg);
+       case RSL_CHAN_Bm_ACCHs:
+       case RSL_CHAN_Lm_ACCHs:
        case RSL_CHAN_SDCCH4_ACCH:
-               return gsm48_rr_rx_acch(ms, msg);
        case RSL_CHAN_SDCCH8_ACCH:
                return gsm48_rr_rx_acch(ms, msg);
        default:
@@ -4727,8 +4739,51 @@ 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;
+       uint8_t cause = RR_REL_CAUSE_NORMAL;
+       uint16_t ma[64];
+       uint8_t ma_len;
 
-       LOGP(DSUM, LOGL_INFO, "Requesting channel aborted\n");
+       /* switch back to old channel, if modify/ho failed */
+       switch (rr->modify_state) {
+       case GSM48_RR_MOD_ASSIGN:
+       case GSM48_RR_MOD_HANDO:
+               /* deactivate channel */
+               l1ctl_tx_dm_rel_req(ms);
+               ms->meas.rl_fail = 0;
+               rr->dm_est = 0;
+               l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
+
+               /* 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;
+               if (rr->modify_state == GSM48_RR_MOD_ASSIGN) {
+                       rr->modify_state = GSM48_RR_MOD_ASSIGN_RESUME;
+                       return gsm48_rr_tx_ass_fail(ms,
+                               GSM48_RR_CAUSE_ABNORMAL_UNSPEC,
+                               RSL_MT_RECON_REQ);
+               } else {
+                       rr->modify_state = GSM48_RR_MOD_HANDO_RESUME;
+                       return gsm48_rr_tx_hando_fail(ms,
+                               GSM48_RR_CAUSE_ABNORMAL_UNSPEC,
+                               RSL_MT_RECON_REQ);
+               }
+               /* returns above */
+       case GSM48_RR_MOD_ASSIGN_RESUME:
+       case GSM48_RR_MOD_HANDO_RESUME:
+               rr->modify_state = GSM48_RR_MOD_NONE;
+               cause = RR_REL_CAUSE_LINK_FAILURE;
+               break;
+       }
+
+       LOGP(DSUM, LOGL_INFO, "Requested channel aborted\n");
 
        /* stop T3211 if running */
        stop_rr_t3110(rr);
@@ -4738,7 +4793,7 @@ static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
        if (!nmsg)
                return -ENOMEM;
        nrrh = (struct gsm48_rr_hdr *)nmsg->data;
-       nrrh->cause = RR_REL_CAUSE_NORMAL;
+       nrrh->cause = cause;
        gsm48_rr_upmsg(ms, nmsg);
 
        /* return idle */
@@ -4755,8 +4810,6 @@ static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm48_rr_hdr *nrrh;
        uint8_t *mode;
        uint8_t cause = rllh->data[2];
-       uint16_t ma[64];
-       uint8_t ma_len;
 
        switch (cause) {
        case RLL_CAUSE_SEQ_ERR:
@@ -4778,35 +4831,9 @@ static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
        mode[1] = 1; /* local release */
        gsm48_send_rsl_rel(ms, RSL_MT_REL_REQ, nmsg);
 
-       /* deactivate channel */
-       l1ctl_tx_dm_rel_req(ms);
-       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;
-       }
+       /* in case of modify/hando: wait for confirm */
+       if (rr->modify_state)
+               return 0;
 
        /* send abort ind to upper layer */
        nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_IND);
@@ -4859,6 +4886,11 @@ static struct dldatastate {
        {SBIT(GSM48_RR_ST_REL_PEND),
         RSL_MT_REL_CONF, gsm48_rr_rel_cnf},
 
+       /* reconnect */
+       {SBIT(GSM48_RR_ST_CONN_PEND) |
+        SBIT(GSM48_RR_ST_DEDICATED),
+        RSL_MT_REL_CONF, gsm48_rr_rel_cnf},
+
        /* suspenion */
        {SBIT(GSM48_RR_ST_DEDICATED),
         RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated},
@@ -4885,7 +4917,7 @@ static int gsm48_rcv_rll(struct osmocom_ms *ms, struct msgb *msg)
 
        if (msg_type != RSL_MT_UNIT_DATA_IND) {
                LOGP(DRSL, LOGL_INFO, "(ms %s) Received '%s' from L2 in state "
-                       "%s\n", ms->name, get_rsl_name(msg_type),
+                       "%s\n", ms->name, rsl_msg_name(msg_type),
                        gsm48_rr_state_names[rr->state]);
        }
 
@@ -4917,7 +4949,7 @@ static int gsm48_rcv_cch(struct osmocom_ms *ms, struct msgb *msg)
        int rc;
 
        LOGP(DRSL, LOGL_INFO, "(ms %s) Received '%s' from L2 in state "
-               "%s\n", ms->name, get_rsl_name(msg_type),
+               "%s\n", ms->name, rsl_msg_name(msg_type),
                gsm48_rr_state_names[rr->state]);
 
        if (rr->state == GSM48_RR_ST_CONN_PEND
@@ -5028,7 +5060,9 @@ int gsm48_rr_init(struct osmocom_ms *ms)
        INIT_LLIST_HEAD(&rr->downqueue);
        /* downqueue is handled here, so don't add_work */
 
-       osmol2_register_handler(ms, &gsm48_rx_rsl);
+       lapdm_channel_set_l3(&ms->lapdm_channel, &rcv_rsl, ms);
+
+       start_rr_t_meas(rr, 1, 0);
 
        return 0;
 }
@@ -5051,7 +5085,7 @@ 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);
@@ -5081,6 +5115,7 @@ static void timeout_rr_t3124(void *arg)
        /* 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();
@@ -5122,11 +5157,12 @@ static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *m
        }
 
        /* start timer for sending next HANDOVER ACCESS bursts afterwards */
-       if (!bsc_timer_pending(&rr->t3124)) {
+       if (!osmo_timer_pending(&rr->t3124)) {
                if (allocated channel is SDCCH)
                        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;
@@ -5140,4 +5176,3 @@ static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *m
 
 #endif
 
-