[layer23] Fixed ASSIGNMENT / HANDOVER failure handling
[osmocom-bb.git] / src / host / layer23 / src / mobile / gsm48_rr.c
index 332ed83..a01e7da 100644 (file)
@@ -80,7 +80,6 @@
 #include <osmocom/bb/mobile/vty.h>
 
 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);
@@ -88,6 +87,7 @@ 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
@@ -284,7 +284,7 @@ static int gsm48_apply_v_sd(struct gsm48_rrlayer *rr, struct msgb *msg)
 static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr,
        uint8_t mode)
 {
-       struct gsm_support *sup = &ms->support;
+       struct gsm_settings *set = &ms->settings;
        uint8_t ch_type, ch_subch, ch_ts;
 
        /* only complain if we use TCH/F or TCH/H */
@@ -299,14 +299,14 @@ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr,
                break;
        case GSM48_CMODE_SPEECH_V1:
                if (ch_type == RSL_CHAN_Bm_ACCHs) {
-                       if (!sup->full_v1) {
+                       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 (!sup->half_v1) {
+                       if (!set->half_v1) {
                                LOGP(DRR, LOGL_NOTICE, "Not supporting "
                                        "half-rate speech V1\n");
                                return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
@@ -316,7 +316,7 @@ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr,
                break;
        case GSM48_CMODE_SPEECH_EFR:
                if (ch_type == RSL_CHAN_Bm_ACCHs) {
-                       if (!sup->full_v2) {
+                       if (!set->full_v2) {
                                LOGP(DRR, LOGL_NOTICE, "Not supporting "
                                        "full-rate speech V2\n");
                                return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
@@ -330,14 +330,14 @@ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr,
                break;
        case GSM48_CMODE_SPEECH_AMR:
                if (ch_type == RSL_CHAN_Bm_ACCHs) {
-                       if (!sup->full_v3) {
+                       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 (!sup->half_v3) {
+                       if (!set->half_v3) {
                                LOGP(DRR, LOGL_NOTICE, "Not supporting "
                                        "half-rate speech V3\n");
                                return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
@@ -413,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->ms->meas.rl_fail = 0;
                rr->dm_est = 0;
                l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_FULL);
                /* free establish message, if any */
@@ -644,7 +645,7 @@ static void timeout_rr_meas(void *arg)
 
        if (rr->dm_est)
                gsm48_rr_tx_meas_rep(rr->ms);
-       memset(meas, 0, sizeof(*meas));
+       meas->frames = meas->snr = meas->berr = meas->rxlev = 0;
        start_rr_t_meas(rr, 1, 0);
 }
 
@@ -930,7 +931,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);
@@ -964,13 +965,13 @@ static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
        }
 
        /* check if we actually support this cipher */
-       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))) {
+       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_PROT_ERROR_UNSPC);
@@ -1003,6 +1004,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));
@@ -1012,57 +1014,57 @@ 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)) {
                /* 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);
        }
@@ -1117,22 +1119,23 @@ 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;
+               cm->pwr_lev = set->class_dcs - 1;
        else
-               cm->pwr_lev = sup->pwr_lev_900;
-       cm->a5_1 = !sup->a5_1;
+               cm->pwr_lev = set->class_900 - 1;
+       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;
@@ -1144,6 +1147,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;
@@ -1165,8 +1169,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->e_gsm || set->r_gsm
+        || set->a5_7 || set->a5_6 || set->a5_5 || set->a5_4
         || sup->ms_sup
         || sup->ucs2_treat
         || sup->ext_meas || sup->meas_cap
@@ -1195,7 +1199,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;
@@ -1297,7 +1301,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;
@@ -1315,7 +1319,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;
@@ -1584,13 +1588,13 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
  */
 
 /* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
-static int decode_freq_list(struct gsm_support *sup,
+static int decode_freq_list(struct gsm_settings *set,
        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))
+           (set->p_gsm && !set->e_gsm && !set->r_gsm && !set->dcs))
                return 0;
 
        return gsm48_decode_freq_list(f, cd, len, mask, frqt);
@@ -1878,7 +1882,7 @@ static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg)
        LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 1\n");
 
        /* Cell Channel Description */
-       decode_freq_list(&ms->support, s->freq,
+       decode_freq_list(&ms->settings, s->freq,
                si->cell_channel_description,
                sizeof(si->cell_channel_description), 0xce, FREQ_TYPE_SERV);
        /* RACH Control Parameter */
@@ -1920,7 +1924,7 @@ static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg)
        /* Neighbor Cell Description */
        s->nb_ext_ind_si2 = (si->bcch_frequency_list[0] >> 6) & 1;
        s->nb_ba_ind_si2 = (si->bcch_frequency_list[0] >> 5) & 1;
-       decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
+       decode_freq_list(&ms->settings, s->freq, si->bcch_frequency_list,
                sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2);
        /* NCC Permitted */
        s->nb_ncc_permitted_si2 = si->ncc_permitted;
@@ -1961,7 +1965,7 @@ static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg)
        /* Neighbor Cell Description */
        s->nb_ext_ind_si2bis = (si->bcch_frequency_list[0] >> 6) & 1;
        s->nb_ba_ind_si2bis = (si->bcch_frequency_list[0] >> 5) & 1;
-       decode_freq_list(&ms->support, s->freq,
+       decode_freq_list(&ms->settings, s->freq,
                si->bcch_frequency_list,
                sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2bis);
        /* RACH Control Parameter */
@@ -2001,7 +2005,7 @@ 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,
+       decode_freq_list(&ms->settings, s->freq,
                si->ext_bcch_frequency_list,
                sizeof(si->ext_bcch_frequency_list), 0x8e,
                        FREQ_TYPE_NCELL_2ter);
@@ -2173,7 +2177,7 @@ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg)
        /* Neighbor Cell Description */
        s->nb_ext_ind_si5 = (si->bcch_frequency_list[0] >> 6) & 1;
        s->nb_ba_ind_si5 = (si->bcch_frequency_list[0] >> 5) & 1;
-       decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
+       decode_freq_list(&ms->settings, s->freq, si->bcch_frequency_list,
                sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5);
 
        s->si5 = 1;
@@ -2211,7 +2215,7 @@ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg)
        /* Neighbor Cell Description */
        s->nb_ext_ind_si5bis = (si->bcch_frequency_list[0] >> 6) & 1;
        s->nb_ba_ind_si5bis = (si->bcch_frequency_list[0] >> 5) & 1;
-       decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
+       decode_freq_list(&ms->settings, s->freq, si->bcch_frequency_list,
                sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5bis);
 
        s->si5bis = 1;
@@ -2249,7 +2253,7 @@ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *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,
+       decode_freq_list(&ms->settings, s->freq, si->bcch_frequency_list,
                sizeof(si->bcch_frequency_list), 0x8e, FREQ_TYPE_REP_5ter);
 
        s->si5ter = 1;
@@ -2263,6 +2267,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) {
@@ -2300,6 +2305,8 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
                "lac 0x%04x SACCH-timeout %d)\n", gsm_print_mcc(s->mcc),
                gsm_print_mnc(s->mnc), s->lac, s->sacch_radio_link_timeout);
 
+       meas->rl_fail = meas->s = s->sacch_radio_link_timeout;
+       LOGP(DRR, LOGL_INFO, "using (new) SACCH timeout %d\n", meas->rl_fail);
        s->si6 = 1;
 
        return gsm48_new_sysinfo(ms, si->system_information);
@@ -3145,6 +3152,7 @@ static int gsm48_rr_activate_channel(struct osmocom_ms *ms,
        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",
@@ -3152,13 +3160,29 @@ 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);
+
+       /* 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));
-               memset(meas, 0, sizeof(*meas));
        }
+       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);
@@ -3167,20 +3191,20 @@ static int gsm48_rr_activate_channel(struct osmocom_ms *ms,
        /* establish */
        LOGP(DRR, LOGL_INFO, "establishing channel in dedicated mode\n");
        rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts);
-       LOGP(DRR, LOGL_INFO, " Channel type %d, subch %d, ts %d, mode %d\n",
-               ch_type, ch_subch, ch_ts, cd->mode);
+       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;
 
        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;
 }
 
@@ -3489,6 +3513,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 */
@@ -3755,11 +3791,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;
@@ -3779,7 +3816,7 @@ 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 */
@@ -4049,23 +4086,15 @@ 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);
+               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");
@@ -4118,11 +4147,12 @@ 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);
+       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)
 {
        struct msgb *nmsg;
        struct gsm48_hdr *gh;
@@ -4142,7 +4172,7 @@ 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);
+       return gsm48_send_rsl(ms, rsl_prim, nmsg);
 }
 
 /* receiving HANDOVER COMMAND message (9.1.15) */
@@ -4186,6 +4216,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 *) 
@@ -4194,8 +4226,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 *)
@@ -4431,11 +4461,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
@@ -4494,21 +4524,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);
 
@@ -4523,7 +4538,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;
 
@@ -4531,6 +4545,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);
+               ms->meas.rl_fail = 0;
                rr->dm_est = 0;
                l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
 
@@ -4564,10 +4579,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 */
@@ -4634,13 +4653,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;
        }
@@ -4889,7 +4910,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;
@@ -4909,21 +4929,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");
@@ -4953,8 +4958,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:
@@ -5006,8 +5012,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;
+
+       /* 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, "Requesting channel aborted\n");
+       LOGP(DSUM, LOGL_INFO, "Requested channel aborted\n");
 
        /* stop T3211 if running */
        stop_rr_t3110(rr);
@@ -5017,7 +5066,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 */
@@ -5034,8 +5083,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:
@@ -5057,36 +5104,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);
-       rr->dm_est = 0;
-       l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);
-
-       switch (rr->modify_state) {
-       case GSM48_RR_MOD_ASSIGN:
-       case GSM48_RR_MOD_HANDO:
-               if (rr->modify_state == GSM48_RR_MOD_ASSIGN)
-                       rr->modify_state = GSM48_RR_MOD_ASSIGN_RESUME;
-               else
-                       rr->modify_state = GSM48_RR_MOD_HANDO_RESUME;
-
-               /* get old channel description */
-               memcpy(&rr->cd_now, &rr->cd_last, sizeof(rr->cd_now));
-
-               /* render and change radio to old channel */
-               gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
-               gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
-
-               /* re-establish old link */
-               nmsg = gsm48_l3_msgb_alloc();
-               if (!nmsg)
-                       return -ENOMEM;
-               return gsm48_send_rsl(ms, RSL_MT_RECON_REQ, nmsg);
-       case GSM48_RR_MOD_ASSIGN_RESUME:
-       case GSM48_RR_MOD_HANDO_RESUME:
-               rr->modify_state = GSM48_RR_MOD_NONE;
-               break;
-       }
+       /* 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);
@@ -5139,6 +5159,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},