[layer23] Fixed parsing of ASSIGNMENT / HANDOVER (type-value) IEs
[osmocom-bb.git] / src / host / layer23 / src / mobile / gsm48_rr.c
index 7a563ac..1b36717 100644 (file)
@@ -45,7 +45,7 @@
  * When enabled, the starting time will be set by given frames in the future.
  * If a starting time is given by the network, this time is ignored.
  */
-//#define TEST_STARTING_TIMER 40
+//#define TEST_STARTING_TIMER 140
 
 /* Testing if frequency modification works correctly "after time".
  *
 #include <osmocom/bb/common/l1ctl.h>
 #include <osmocom/bb/mobile/vty.h>
 
-static void start_rr_t_monitor(struct gsm48_rrlayer *rr, int sec, int micro);
-static void stop_rr_t_monitor(struct gsm48_rrlayer *rr);
+static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro);
+static void stop_rr_t_meas(struct gsm48_rrlayer *rr);
 static void stop_rr_t_starting(struct gsm48_rrlayer *rr);
 static void stop_rr_t3124(struct gsm48_rrlayer *rr);
 static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg);
 static int gsm48_rr_dl_est(struct osmocom_ms *ms);
+static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms);
+static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr,
+       uint8_t mode);
 
 /*
  * support
@@ -277,6 +280,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_support *sup = &ms->support;
+       uint8_t ch_type, ch_subch, ch_ts;
+
+       /* only complain if we use TCH/F or TCH/H */
+       rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts);
+       if (ch_type != RSL_CHAN_Bm_ACCHs
+        && ch_type != RSL_CHAN_Lm_ACCHs)
+               return 0;
+
+       switch (mode) {
+       case GSM48_CMODE_SIGN:
+               LOGP(DRR, LOGL_INFO, "Mode: signalling\n");
+               break;
+       case GSM48_CMODE_SPEECH_V1:
+               if (ch_type == RSL_CHAN_Bm_ACCHs) {
+                       if (!sup->full_v1) {
+                               LOGP(DRR, LOGL_NOTICE, "Not supporting "
+                                       "full-rate speech V1\n");
+                               return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+                       }
+                       LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V1\n");
+               } else {
+                       if (!sup->half_v1) {
+                               LOGP(DRR, LOGL_NOTICE, "Not supporting "
+                                       "half-rate speech V1\n");
+                               return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+                       }
+                       LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V1\n");
+               }
+               break;
+       case GSM48_CMODE_SPEECH_EFR:
+               if (ch_type == RSL_CHAN_Bm_ACCHs) {
+                       if (!sup->full_v2) {
+                               LOGP(DRR, LOGL_NOTICE, "Not supporting "
+                                       "full-rate speech V2\n");
+                               return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+                       }
+                       LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V2\n");
+               } else {
+                       LOGP(DRR, LOGL_NOTICE, "Not supporting "
+                                       "half-rate speech V2\n");
+                       return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+               }
+               break;
+       case GSM48_CMODE_SPEECH_AMR:
+               if (ch_type == RSL_CHAN_Bm_ACCHs) {
+                       if (!sup->full_v3) {
+                               LOGP(DRR, LOGL_NOTICE, "Not supporting "
+                                       "full-rate speech V3\n");
+                               return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+                       }
+                       LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V3\n");
+               } else {
+                       if (!sup->half_v3) {
+                               LOGP(DRR, LOGL_NOTICE, "Not supporting "
+                                       "half-rate speech V3\n");
+                               return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+                       }
+                       LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V3\n");
+               }
+               break;
+       default:
+               LOGP(DRR, LOGL_ERROR, "Mode 0x%02x not supported!\n", mode);
+               return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+       }
+
+       return 0;
+}
+
+/* apply new "alter_delay" in dedicated mode */
+int gsm48_rr_alter_delay(struct osmocom_ms *ms)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct gsm_settings *set = &rr->ms->settings;
+
+       if (rr->state != GSM48_RR_ST_DEDICATED)
+               return -EINVAL;
+       l1ctl_tx_param_req(ms, rr->cd_now.ind_ta - set->alter_delay,
+               (set->alter_tx_power) ? set->alter_tx_power_value
+                                       : rr->cd_now.ind_tx_power);
+
+       return 0;
+}
+
 /*
  * state transition
  */
@@ -322,6 +413,7 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state)
 
                /* release dedicated mode, if any */
                l1ctl_tx_dm_rel_req(rr->ms);
+               rr->dm_est = 0;
                l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_FULL);
                /* free establish message, if any */
                rr->rr_est_req = 0;
@@ -445,8 +537,8 @@ static int gsm48_send_rsl(struct osmocom_ms *ms, uint8_t msg_type,
        struct gsm48_rrlayer *rr = &ms->rrlayer;
 
        if (!msg->l3h) {
-               printf("FIX l3h\n");
-               exit (0);
+               LOGP(DRR, LOGL_ERROR, "FIX l3h\n");
+               return -EINVAL;
        }
        rsl_rll_push_l3(msg, msg_type, rr->cd_now.chan_nr,
                rr->cd_now.link_id, 1);
@@ -495,8 +587,6 @@ int gsm48_rsl_dequeue(struct osmocom_ms *ms)
 int gsm48_rr_start_monitor(struct osmocom_ms *ms)
 {
        ms->rrlayer.monitor = 1;
-       memset(&ms->meas, 0, sizeof(&ms->meas));
-       start_rr_t_monitor(&ms->rrlayer, 1, 0);
 
        return 0;
 }
@@ -504,8 +594,6 @@ int gsm48_rr_start_monitor(struct osmocom_ms *ms)
 int gsm48_rr_stop_monitor(struct osmocom_ms *ms)
 {
        ms->rrlayer.monitor = 0;
-       memset(&ms->meas, 0, sizeof(&ms->meas));
-       stop_rr_t_monitor(&ms->rrlayer);
 
        return 0;
 }
@@ -515,7 +603,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 +614,7 @@ static void timeout_rr_monitor(void *arg)
        char text[256];
 
        if (!cs->selected) {
-               sprintf(text, "MON: no cell selected");
+               return;
        } else if (!meas->frames) {
                sprintf(text, "MON: no cell info");
        } else {
@@ -550,10 +638,14 @@ 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);
+       start_rr_t_meas(rr, 1, 0);
 }
 
 /* special timer to assign / handover when starting time is reached */
@@ -645,11 +737,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;
+       bsc_schedule_timer(&rr->t_meas, sec, micro);
 }
 
 static void start_rr_t_rel_wait(struct gsm48_rrlayer *rr, int sec, int micro)
@@ -706,11 +798,11 @@ static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro)
        bsc_schedule_timer(&rr->t3126, sec, micro);
 }
 
-static void stop_rr_t_monitor(struct gsm48_rrlayer *rr)
+static void stop_rr_t_meas(struct gsm48_rrlayer *rr)
 {
-       if (bsc_timer_pending(&rr->t_monitor)) {
-               LOGP(DRR, LOGL_INFO, "stopping pending timer T_monitor\n");
-               bsc_del_timer(&rr->t_monitor);
+       if (bsc_timer_pending(&rr->t_meas)) {
+               LOGP(DRR, LOGL_INFO, "stopping pending timer T_meas\n");
+               bsc_del_timer(&rr->t_meas);
        }
 }
 
@@ -800,6 +892,7 @@ static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr)
        struct gsm_settings *set = &ms->settings;
        struct msgb *nmsg;
        struct gsm48_hdr *gh;
+       struct gsm48_rr_hdr *nrrh;
        uint8_t buf[11], *tlv;
 
        LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMPLETE (cr %d)\n", cr);
@@ -815,17 +908,28 @@ static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr)
        /* MI */
        if (cr) {
                gsm48_generate_mid_from_imsi(buf, set->imeisv);
+               /* alter MI type */
+               buf[2] = (buf[2] & ~GSM_MI_TYPE_MASK) | GSM_MI_TYPE_IMEISV;
                tlv = msgb_put(nmsg, 2 + buf[1]);
                memcpy(tlv, buf, 2 + buf[1]);
        }
 
-       return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+       gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+
+       /* send RR_SYNC_IND(ciphering) */
+       nmsg = gsm48_rr_msgb_alloc(GSM48_RR_SYNC_IND);
+       if (!nmsg)
+               return -ENOMEM;
+       nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+       nrrh->cause = RR_SYNC_CAUSE_CIPHERING;
+       return gsm48_rr_upmsg(ms, nmsg);
 }
 
 /* receive ciphering mode command */
 static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct gsm_subscriber *subscr = &ms->subscr;
        struct gsm_support *sup = &ms->support;
        struct gsm48_hdr *gh = msgb_l3(msg);
        struct gsm48_cip_mode_cmd *cm = (struct gsm48_cip_mode_cmd *)gh->data;
@@ -845,36 +949,49 @@ static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
        /* cipher mode response */
        cr = cm->cr;
 
-       if (sc)
-               LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, cr=%u)",
+       if (!sc)
+               LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, cr=%u)\n",
                        sc, cr);
        else
                LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, "
-                       "algo=A5/%d cr=%u)", sc, alg_id + 1, cr);
+                       "algo=A5/%d cr=%u)\n", sc, alg_id + 1, cr);
 
        /* 3.4.7.2 */
        if (rr->cipher_on && sc) {
-               LOGP(DRR, LOGL_NOTICE, "chiphering already applied.\n");
+               LOGP(DRR, LOGL_NOTICE, "chiphering already applied\n");
                return gsm48_rr_tx_rr_status(ms,
                        GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
        }
 
        /* check if we actually support this cipher */
-       if ((alg_id == GSM_CIPHER_A5_1 && !sup->a5_1)
-        || (alg_id == GSM_CIPHER_A5_2 && !sup->a5_2)
-        || (alg_id == GSM_CIPHER_A5_3 && !sup->a5_3)
-        || (alg_id == GSM_CIPHER_A5_4 && !sup->a5_4)
-        || (alg_id == GSM_CIPHER_A5_5 && !sup->a5_5)
-        || (alg_id == GSM_CIPHER_A5_6 && !sup->a5_6)
-        || (alg_id == GSM_CIPHER_A5_7 && !sup->a5_7))
+       if (sc && ((alg_id == GSM_CIPHER_A5_1 && !sup->a5_1)
+               || (alg_id == GSM_CIPHER_A5_2 && !sup->a5_2)
+               || (alg_id == GSM_CIPHER_A5_3 && !sup->a5_3)
+               || (alg_id == GSM_CIPHER_A5_4 && !sup->a5_4)
+               || (alg_id == GSM_CIPHER_A5_5 && !sup->a5_5)
+               || (alg_id == GSM_CIPHER_A5_6 && !sup->a5_6)
+               || (alg_id == GSM_CIPHER_A5_7 && !sup->a5_7))) {
+               LOGP(DRR, LOGL_NOTICE, "algo not supported\n");
+               return gsm48_rr_tx_rr_status(ms,
+                       GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+       }
+
+       /* check if we have no key */
+       if (sc && subscr->key_seq == 7) {
+               LOGP(DRR, LOGL_NOTICE, "no key available\n");
                return gsm48_rr_tx_rr_status(ms,
-                       GSM48_RR_CAUSE_CHAN_MODE_UNACCT);
+                       GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+       }
 
        /* change to ciphering */
-#warning FIXME: cyphering command
-       rr->cipher_on = sc, rr->cipher_type = alg_id;
+       rr->cipher_on = sc;
+       rr->cipher_type = alg_id;
+       if (rr->cipher_on)
+               l1ctl_tx_crypto_req(ms, rr->cipher_type + 1, subscr->key, 8);
+       else
+               l1ctl_tx_crypto_req(ms, 0, NULL, 0);
 
-       /* response */
+       /* response (using the new mode) */
        return gsm48_rr_tx_cip_mode_cpl(ms, cr);
 }
 
@@ -1005,7 +1122,7 @@ int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm)
                cm->pwr_lev = sup->pwr_lev_1800;
        else
                cm->pwr_lev = sup->pwr_lev_900;
-       cm->a5_1 = sup->a5_1;
+       cm->a5_1 = !sup->a5_1;
        cm->es_ind = sup->es_ind;
        cm->rev_lev = sup->rev_lev;
        cm->fc = (sup->r_gsm || sup->e_gsm);
@@ -1679,11 +1796,46 @@ static int gsm48_decode_si6_rest(struct gsm48_sysinfo *s, uint8_t *si,
 }
 
 /* send sysinfo event to other layers */
-static int gsm48_send_sysinfo(struct osmocom_ms *ms, uint8_t type)
+static int gsm48_new_sysinfo(struct osmocom_ms *ms, uint8_t type)
 {
+       struct gsm48_sysinfo *s = ms->cellsel.si;
        struct msgb *nmsg;
        struct gsm322_msg *em;
 
+       /* update list of measurements, if BA(SACCH) is complete and new */
+       if (s
+        && (type == GSM48_MT_RR_SYSINFO_5
+         || type == GSM48_MT_RR_SYSINFO_5bis
+         || type == GSM48_MT_RR_SYSINFO_5ter)
+        && s->si5
+        && (!s->nb_ext_ind_si5
+         || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
+         || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
+               && s->nb_ext_ind_si5bis))) {
+               struct gsm48_rr_meas *rrmeas = &ms->rrlayer.meas;
+               int n = 0, i;
+
+               LOGP(DRR, LOGL_NOTICE, "Complete set of SI5* for BA(%d)\n",
+                       s->nb_ba_ind_si5);
+               rrmeas->nc_num = 0;
+               for (i = 1; i <= 1024; i++) {
+                       if ((s->freq[i & 1023].mask & FREQ_TYPE_REP)) {
+                               if (n == 32) {
+                                       LOGP(DRR, LOGL_NOTICE, "SI5* report "
+                                               "exceeds 32 BCCHs\n");
+                                       break;
+                               }
+                               LOGP(DRR, LOGL_NOTICE, "SI5* report arfcn %d\n",
+                                       i & 1023);
+                               rrmeas->nc_arfcn[n] = i & 1023;
+                               rrmeas->nc_rxlev[n] = -128;
+                               n++;
+                       }
+               }
+               rrmeas->nc_num = n;
+       }
+
+       /* send sysinfo event to other layers */
        nmsg = gsm322_msgb_alloc(GSM322_EVENT_SYSINFO);
        if (!nmsg)
                return -ENOMEM;
@@ -1737,7 +1889,7 @@ static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg)
 
        s->si1 = 1;
 
-       return gsm48_send_sysinfo(ms, si->header.system_information);
+       return gsm48_new_sysinfo(ms, si->header.system_information);
 }
 
 /* receive "SYSTEM INFORMATION 2" message (9.1.32) */
@@ -1758,7 +1910,6 @@ static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg)
                        "message.\n");
                return -EINVAL;
        }
-//printf("len = %d\n", MIN(msgb_l3len(msg), sizeof(s->si2_msg)));
 
        if (!memcmp(si, s->si2_msg, MIN(msgb_l3len(msg), sizeof(s->si2_msg))))
                return 0;
@@ -1772,13 +1923,13 @@ static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg)
        decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
                sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2);
        /* NCC Permitted */
-       s->nb_ncc_permitted = si->ncc_permitted;
+       s->nb_ncc_permitted_si2 = si->ncc_permitted;
        /* RACH Control Parameter */
        gsm48_decode_rach_ctl_neigh(s, &si->rach_control);
 
        s->si2 = 1;
 
-       return gsm48_send_sysinfo(ms, si->header.system_information);
+       return gsm48_new_sysinfo(ms, si->header.system_information);
 }
 
 /* receive "SYSTEM INFORMATION 2bis" message (9.1.33) */
@@ -1812,14 +1963,13 @@ static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg)
        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);
+               sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2bis);
        /* RACH Control Parameter */
        gsm48_decode_rach_ctl_neigh(s, &si->rach_control);
 
        s->si2bis = 1;
 
-       return gsm48_send_sysinfo(ms, si->header.system_information);
+       return gsm48_new_sysinfo(ms, si->header.system_information);
 }
 
 /* receive "SYSTEM INFORMATION 2ter" message (9.1.34) */
@@ -1850,14 +2000,15 @@ static int gsm48_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg)
 
        /* Neighbor Cell Description 2 */
        s->nb_multi_rep_si2ter = (si->ext_bcch_frequency_list[0] >> 6) & 3;
+       s->nb_ba_ind_si2ter = (si->ext_bcch_frequency_list[0] >> 5) & 1;
        decode_freq_list(&ms->support, s->freq,
                si->ext_bcch_frequency_list,
                sizeof(si->ext_bcch_frequency_list), 0x8e,
-               FREQ_TYPE_NCELL_2ter);
+                       FREQ_TYPE_NCELL_2ter);
 
        s->si2ter = 1;
 
-       return gsm48_send_sysinfo(ms, si->header.system_information);
+       return gsm48_new_sysinfo(ms, si->header.system_information);
 }
 
 /* receive "SYSTEM INFORMATION 3" message (9.1.35) */
@@ -1914,7 +2065,7 @@ static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg)
                l1ctl_tx_ccch_mode_req(ms, cs->ccch_mode);
        }
 
-       return gsm48_send_sysinfo(ms, si->header.system_information);
+       return gsm48_new_sysinfo(ms, si->header.system_information);
 }
 
 /* receive "SYSTEM INFORMATION 4" message (9.1.36) */
@@ -1990,7 +2141,7 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
 
        s->si4 = 1;
 
-       return gsm48_send_sysinfo(ms, si->header.system_information);
+       return gsm48_new_sysinfo(ms, si->header.system_information);
 }
 
 /* receive "SYSTEM INFORMATION 5" message (9.1.37) */
@@ -2027,7 +2178,7 @@ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg)
 
        s->si5 = 1;
 
-       return gsm48_send_sysinfo(ms, si->system_information);
+       return gsm48_new_sysinfo(ms, si->system_information);
 }
 
 /* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */
@@ -2065,7 +2216,7 @@ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg)
 
        s->si5bis = 1;
 
-       return gsm48_send_sysinfo(ms, si->system_information);
+       return gsm48_new_sysinfo(ms, si->system_information);
 }
 
 /* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */
@@ -2096,12 +2247,14 @@ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg)
        memcpy(s->si5t_msg, si, MIN(msgb_l3len(msg), sizeof(s->si5t_msg)));
 
        /* Neighbor Cell Description */
+       s->nb_multi_rep_si5ter = (si->bcch_frequency_list[0] >> 6) & 3;
+       s->nb_ba_ind_si5ter = (si->bcch_frequency_list[0] >> 5) & 1;
        decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
-               sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5ter);
+               sizeof(si->bcch_frequency_list), 0x8e, FREQ_TYPE_REP_5ter);
 
        s->si5ter = 1;
 
-       return gsm48_send_sysinfo(ms, si->system_information);
+       return gsm48_new_sysinfo(ms, si->system_information);
 }
 
 /* receive "SYSTEM INFORMATION 6" message (9.1.39) */
@@ -2138,7 +2291,7 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
        /* Cell Options (SACCH) */
        gsm48_decode_cellopt_sacch(s, &si->cell_options);
        /* NCC Permitted */
-       s->nb_ncc_permitted = si->ncc_permitted;
+       s->nb_ncc_permitted_si6 = si->ncc_permitted;
        /* SI 6 Rest Octets */
        if (payload_len >= 4)
                gsm48_decode_si6_rest(s, si->rest_octets, payload_len);
@@ -2149,7 +2302,7 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
 
        s->si6 = 1;
 
-       return gsm48_send_sysinfo(ms, si->system_information);
+       return gsm48_new_sysinfo(ms, si->system_information);
 }
 
 /*
@@ -2459,6 +2612,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 */
@@ -2554,7 +2708,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 */
@@ -2586,7 +2742,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;
@@ -2658,7 +2814,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));
@@ -2755,14 +2911,91 @@ static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg)
 static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
-       struct gsm48_rr_meas *meas = &rr->meas;
+       struct gsm48_sysinfo *s = ms->cellsel.si;
+       struct rx_meas_stat *meas = &rr->ms->meas;
+       struct gsm48_rr_meas *rrmeas = &rr->meas;
        struct msgb *nmsg;
        struct gsm48_hdr *gh;
        struct gsm48_meas_res *mr;
+       uint8_t serv_rxlev_full = 0, serv_rxlev_sub = 0, serv_rxqual_full = 0,
+               serv_rxqual_sub = 0;
+       uint8_t ta, tx_power;
+       uint8_t rep_ba = 0, rep_valid = 0, meas_valid = 0, multi_rep = 0;
+       uint8_t n = 0, rxlev_nc[6], bsic_nc[6], bcch_f_nc[6];
+
+       /* just in case! */
+       if (!s)
+               return -EINVAL;
+
+       /* check if SI5* is completely received, check BA-IND */
+       if (s->si5
+        && (!s->nb_ext_ind_si5
+         || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
+         || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
+                       && s->nb_ext_ind_si5bis))) {
+               rep_ba = s->nb_ba_ind_si5;
+               if ((s->si5bis && s->nb_ext_ind_si5
+                 && s->nb_ba_ind_si5bis != rep_ba)
+                || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
+                 && s->nb_ext_ind_si5bis && s->nb_ba_ind_si5ter != rep_ba)) {
+                       LOGP(DRR, LOGL_NOTICE, "BA-IND missmatch on SI5*");
+               } else
+                       rep_valid = 1;
+       }
+
+       /* check for valid measurements, any frame must exist */
+       if (meas->frames) {
+               meas_valid = 1;
+               serv_rxlev_full = serv_rxlev_sub = meas->rxlev / meas->frames;
+               serv_rxqual_full = serv_rxqual_sub = 0; // FIXME
+       }
+
+       memset(&rxlev_nc, 0, sizeof(rxlev_nc));
+       memset(&bsic_nc, 0, sizeof(bsic_nc));
+       memset(&bcch_f_nc, 0, sizeof(bcch_f_nc));
+       if (rep_valid) {
+               int8_t strongest, current;
+               uint8_t ncc;
+               int i, index;
+
+               /* multiband reporting, if not: 0 = normal reporting */
+               if (s->si5 && s->si5bis && s->si5ter && s->nb_ext_ind_si5
+                && s->nb_ext_ind_si5bis)
+                       multi_rep = s->nb_multi_rep_si5ter;
+
+               /* get 6 strongest measurements */
+               // FIXME: multiband report
+               strongest = 127; /* infinite */
+               for (n = 0; n < 6; n++) {
+                       current = -128; /* -infinite */
+                       index = 0;
+                       for (i = 0; i < rrmeas->nc_num; i++) {
+                               /* only check if NCC is permitted */
+                               ncc = rrmeas->nc_bsic[i] >> 3;
+                               if ((s->nb_ncc_permitted_si6 & (1 << ncc))
+                                && rrmeas->nc_rxlev[i] > current
+                                && rrmeas->nc_rxlev[i] < strongest) {
+                                       current = rrmeas->nc_rxlev[i];
+                                       index = i;
+                               }
+                       }
+                       if (current == -128) /* no more found */
+                               break;
+                       rxlev_nc[n] = rrmeas->nc_rxlev[index] + 110;
+                       bsic_nc[n] = rrmeas->nc_bsic[index];
+                       bcch_f_nc[n] = index;
+               }
+       }
 
        nmsg = gsm48_l3_msgb_alloc();
        if (!nmsg)
                return -ENOMEM;
+
+       /* use indicated tx-power and TA (not the altered ones) */
+       tx_power = rr->cd_now.ind_tx_power;
+       // FIXME: degrade power to the max supported level
+       ta = rr->cd_now.ind_ta;
+
        gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
        mr = (struct gsm48_meas_res *) msgb_put(nmsg, sizeof(*mr));
 
@@ -2770,51 +3003,68 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
        gh->msg_type = GSM48_MT_RR_MEAS_REP;
 
        /* measurement results */
-       mr->rxlev_full = meas->rxlev_full;
-       mr->rxlev_sub = meas->rxlev_sub;
-       mr->rxqual_full = meas->rxqual_full;
-       mr->rxqual_sub = meas->rxqual_sub;
-       mr->dtx_used = meas->dtx;
-       mr->ba_used = meas->ba;
-       mr->meas_valid = meas->meas_valid;
-       if (meas->ncell_na) {
+       mr->rxlev_full = serv_rxlev_full;
+       mr->rxlev_sub = serv_rxlev_sub;
+       mr->rxqual_full = serv_rxqual_full;
+       mr->rxqual_sub = serv_rxqual_sub;
+       mr->dtx_used = 0; // FIXME: no DTX yet
+       mr->ba_used = rep_ba;
+       mr->meas_valid = !meas_valid; /* 0 = valid */
+       if (rep_valid) {
+               mr->no_nc_n_hi = n >> 2;
+               mr->no_nc_n_lo = n & 3;
+       } else {
                /* no results for serving cells */
                mr->no_nc_n_hi = 1;
                mr->no_nc_n_lo = 3;
-       } else {
-               mr->no_nc_n_hi = meas->count >> 2;
-               mr->no_nc_n_lo = meas->count & 3;
-       }
-       mr->rxlev_nc1 = meas->rxlev_nc[0];
-       mr->rxlev_nc2_hi = meas->rxlev_nc[1] >> 1;
-       mr->rxlev_nc2_lo = meas->rxlev_nc[1] & 1;
-       mr->rxlev_nc3_hi = meas->rxlev_nc[2] >> 2;
-       mr->rxlev_nc3_lo = meas->rxlev_nc[2] & 3;
-       mr->rxlev_nc4_hi = meas->rxlev_nc[3] >> 3;
-       mr->rxlev_nc4_lo = meas->rxlev_nc[3] & 7;
-       mr->rxlev_nc5_hi = meas->rxlev_nc[4] >> 4;
-       mr->rxlev_nc5_lo = meas->rxlev_nc[4] & 15;
-       mr->rxlev_nc6_hi = meas->rxlev_nc[5] >> 5;
-       mr->rxlev_nc6_lo = meas->rxlev_nc[5] & 31;
-       mr->bsic_nc1_hi = meas->bsic_nc[0] >> 3;
-       mr->bsic_nc1_lo = meas->bsic_nc[0] & 7;
-       mr->bsic_nc2_hi = meas->bsic_nc[1] >> 4;
-       mr->bsic_nc2_lo = meas->bsic_nc[1] & 15;
-       mr->bsic_nc3_hi = meas->bsic_nc[2] >> 5;
-       mr->bsic_nc3_lo = meas->bsic_nc[2] & 31;
-       mr->bsic_nc4 = meas->bsic_nc[3];
-       mr->bsic_nc5 = meas->bsic_nc[4];
-       mr->bsic_nc6 = meas->bsic_nc[5];
-       mr->bcch_f_nc1 = meas->bcch_f_nc[0];
-       mr->bcch_f_nc2 = meas->bcch_f_nc[1];
-       mr->bcch_f_nc3 = meas->bcch_f_nc[2];
-       mr->bcch_f_nc4 = meas->bcch_f_nc[3];
-       mr->bcch_f_nc5_hi = meas->bcch_f_nc[4] >> 1;
-       mr->bcch_f_nc5_lo = meas->bcch_f_nc[4] & 1;
-       mr->bcch_f_nc6_hi = meas->bcch_f_nc[5] >> 2;
-       mr->bcch_f_nc6_lo = meas->bcch_f_nc[5] & 3;
-
-       return gsm48_send_rsl(ms, RSL_MT_UNIT_DATA_REQ, nmsg);
+       }
+       mr->rxlev_nc1 = rxlev_nc[0];
+       mr->rxlev_nc2_hi = rxlev_nc[1] >> 1;
+       mr->rxlev_nc2_lo = rxlev_nc[1] & 1;
+       mr->rxlev_nc3_hi = rxlev_nc[2] >> 2;
+       mr->rxlev_nc3_lo = rxlev_nc[2] & 3;
+       mr->rxlev_nc4_hi = rxlev_nc[3] >> 3;
+       mr->rxlev_nc4_lo = rxlev_nc[3] & 7;
+       mr->rxlev_nc5_hi = rxlev_nc[4] >> 4;
+       mr->rxlev_nc5_lo = rxlev_nc[4] & 15;
+       mr->rxlev_nc6_hi = rxlev_nc[5] >> 5;
+       mr->rxlev_nc6_lo = rxlev_nc[5] & 31;
+       mr->bsic_nc1_hi = bsic_nc[0] >> 3;
+       mr->bsic_nc1_lo = bsic_nc[0] & 7;
+       mr->bsic_nc2_hi = bsic_nc[1] >> 4;
+       mr->bsic_nc2_lo = bsic_nc[1] & 15;
+       mr->bsic_nc3_hi = bsic_nc[2] >> 5;
+       mr->bsic_nc3_lo = bsic_nc[2] & 31;
+       mr->bsic_nc4 = bsic_nc[3];
+       mr->bsic_nc5 = bsic_nc[4];
+       mr->bsic_nc6 = bsic_nc[5];
+       mr->bcch_f_nc1 = bcch_f_nc[0];
+       mr->bcch_f_nc2 = bcch_f_nc[1];
+       mr->bcch_f_nc3 = bcch_f_nc[2];
+       mr->bcch_f_nc4 = bcch_f_nc[3];
+       mr->bcch_f_nc5_hi = bcch_f_nc[4] >> 1;
+       mr->bcch_f_nc5_lo = bcch_f_nc[4] & 1;
+       mr->bcch_f_nc6_hi = bcch_f_nc[5] >> 2;
+       mr->bcch_f_nc6_lo = bcch_f_nc[5] & 3;
+
+       LOGP(DRR, LOGL_INFO, "MEAS REP: pwr=%d TA=%d meas-invalid=%d "
+               "rxlev-full=%d rxlev-sub=%d rxqual-full=%d rxqual-sub=%d "
+               "dtx %d ba %d no-ncell-n %d\n", tx_power, ta, mr->meas_valid,
+               mr->rxlev_full - 110, mr->rxlev_sub - 110,
+               mr->rxqual_full, mr->rxqual_sub, mr->dtx_used, mr->ba_used,
+               (mr->no_nc_n_hi << 2) | mr->no_nc_n_lo);
+
+       msgb_tv16_push(nmsg, RSL_IE_L3_INFO,
+               nmsg->tail - (uint8_t *)msgb_l3(nmsg));
+       msgb_push(nmsg, 2 + 2);
+       nmsg->data[0] = RSL_IE_TIMING_ADVANCE;
+       nmsg->data[1] = ta;
+       nmsg->data[2] = RSL_IE_MS_POWER;
+       nmsg->data[3] = tx_power;
+       rsl_rll_push_hdr(nmsg, RSL_MT_UNIT_DATA_REQ, rr->cd_now.chan_nr,
+               0x40, 1);
+
+       return rslms_recvmsg(nmsg, ms);
 }
 
 /*
@@ -2889,7 +3139,11 @@ 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;
 
        /* setting (new) timing advance */
@@ -2898,24 +3152,34 @@ static int gsm48_rr_activate_channel(struct osmocom_ms *ms,
        l1ctl_tx_param_req(ms, cd->ind_ta - set->alter_delay,
                        (set->alter_tx_power) ? set->alter_tx_power_value
                                                : cd->ind_tx_power);
+       /* setting initial (invalid) measurement report, resetting SI5* */
+       if (s) {
+               memset(s->si5_msg, 0, sizeof(s->si5_msg));
+               memset(s->si5b_msg, 0, sizeof(s->si5b_msg));
+               memset(s->si5t_msg, 0, sizeof(s->si5t_msg));
+               memset(meas, 0, sizeof(*meas));
+       }
+       rr->meas.nc_num = 0;
+       stop_rr_t_meas(rr);
+       start_rr_t_meas(rr, 1, 0);
+       gsm48_rr_tx_meas_rep(ms);
 
        /* establish */
        LOGP(DRR, LOGL_INFO, "establishing channel in dedicated mode\n");
        rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts);
-       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\n",
+               ch_type, ch_subch, ch_ts, cd->mode);
        if (cd->h)
                l1ctl_tx_dm_est_req_h1(ms, cd->maio, cd->hsn,
                        ma, ma_len, cd->chan_nr, cd->tsc);
        else
                l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc);
+       rr->dm_est = 1;
 
-#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;
 }
@@ -2924,13 +3188,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;
 }
@@ -3290,11 +3560,28 @@ 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 gsm48_frq_redef *fr = msgb_l3(msg);
        int mob_al_len = msgb_l3len(msg) - sizeof(*fr);
@@ -3368,7 +3655,6 @@ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg)
 
        rr->cd_now.start = 0;
 
-#endif
        return 0;
 }
 
@@ -3409,7 +3695,7 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg)
        int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm);
        struct gsm48_rr_cd *cd = &rr->cd_now;
        uint8_t ch_type, ch_subch, ch_ts;
-       uint8_t mode;
+       uint8_t cause;
 
        LOGP(DRR, LOGL_INFO, "CHANNEL MODE MODIFY\n");
 
@@ -3439,21 +3725,13 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg)
                        ch_ts, ch_subch, cd->tsc, cm->mode);
        }
        /* mode */
-       mode = cm->mode;
-       switch (mode) {
-       case GSM48_CMODE_SIGN:
-               LOGP(DRR, LOGL_INFO, "Mode set to signalling.\n");
-               break;
-       case GSM48_CMODE_SPEECH_V1:
-               LOGP(DRR, LOGL_INFO, "Mode set to GSM full-rate codec.\n");
-               break;
-       default:
-               LOGP(DRR, LOGL_ERROR, "Mode %u not supported!\n", mode);
-       }
-       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 */
@@ -3526,7 +3804,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 "
@@ -3661,16 +3941,18 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
                        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 "
@@ -3678,6 +3960,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
                                "mobile allocation after time\n");
                        memcpy(&cdb->mob_alloc_lv, &cda->mob_alloc_lv,
                                sizeof(cdb->mob_alloc_lv));
+               } else
                if (cda->freq_list_lv[0]) {
                        LOGP(DRR, LOGL_INFO, " before: hopping required and "
                                "frequency list not available, using "
@@ -3692,16 +3975,19 @@ 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,
@@ -3719,7 +4005,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",
@@ -3761,6 +4047,9 @@ 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);
        if (before_time) {
                cause = gsm48_rr_render_ma(ms, cdb, ma, &ma_len);
                if (cause)
@@ -3809,8 +4098,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;
@@ -3832,14 +4119,11 @@ 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
 }
 
 /* 9.1.4 sending HANDOVER FAILURE */
 static int gsm48_rr_tx_hando_fail(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_fail *hf;
@@ -3859,7 +4143,6 @@ static int gsm48_rr_tx_hando_fail(struct osmocom_ms *ms, uint8_t cause)
        hf->rr_cause = cause;
 
        return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
-#endif
 }
 
 /* receiving HANDOVER COMMAND message (9.1.15) */
@@ -3885,7 +4168,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 "
@@ -4036,16 +4321,18 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
                        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 "
@@ -4053,6 +4340,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
                                "mobile allocation after time\n");
                        memcpy(&cdb->mob_alloc_lv, &cda->mob_alloc_lv,
                                sizeof(cdb->mob_alloc_lv));
+               } else
                if (cda->freq_list_lv[0]) {
                        LOGP(DRR, LOGL_INFO, " before: hopping required and "
                                "frequency list not available, using "
@@ -4067,16 +4355,19 @@ 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,
@@ -4094,7 +4385,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",
@@ -4240,6 +4531,7 @@ 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);
+               rr->dm_est = 0;
                l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
 
                /* store current channel descriptions */
@@ -4767,6 +5059,7 @@ static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
 
        /* deactivate channel */
        l1ctl_tx_dm_rel_req(ms);
+       rr->dm_est = 0;
        l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
 
        switch (rr->modify_state) {
@@ -5017,6 +5310,8 @@ int gsm48_rr_init(struct osmocom_ms *ms)
 
        osmol2_register_handler(ms, &gsm48_rx_rsl);
 
+       start_rr_t_meas(rr, 1, 0);
+
        return 0;
 }
 
@@ -5038,7 +5333,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);
@@ -5068,6 +5363,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();
@@ -5114,6 +5410,7 @@ static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *m
                        start_rr_t3124(rr, GSM_T3124_675);
                else
                        start_rr_t3124(rr, GSM_T3124_320);
+       }
        if (!rr->n_chan_req) {
                start_rr_t3126(rr, 5, 0); /* TODO improve! */
                return 0;
@@ -5127,4 +5424,3 @@ static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *m
 
 #endif
 
-