[layer23] Updated layer23 to current L1 support and forthcomming hopping.
[osmocom-bb.git] / src / host / layer23 / src / gsm48_rr.c
index bfe297b..2754949 100644 (file)
@@ -1,3 +1,4 @@
+#warning rr on MDL error handling (as specified in 04.08 / 04.06)
 /*
  * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
  *
 #include <osmocore/gsm48.h>
 #include <osmocore/bitvec.h>
 
-#include <osmocom/logging.h>
 #include <osmocom/osmocom_data.h>
+#include <osmocom/l1l2_interface.h>
+#include <osmocom/logging.h>
+#include <osmocom/networks.h>
+#include <osmocom/l1ctl.h>
 
 static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg);
 static int gsm48_rr_dl_est(struct osmocom_ms *ms);
@@ -68,13 +72,12 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms);
 int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc,
        uint16_t *mnc, uint16_t *lac)
 {
-       *mcc = (lai->digits[0] & 0x0f) * 100
-               + (lai->digits[0] >> 4) * 10
-               + (lai->digits[1] & 0x0f);
-       *mnc = (lai->digits[2] & 0x0f) * 10
-               + (lai->digits[2] >> 4);
-       if ((lai->digits[1] >> 4) != 0xf) /* 3 digits MNC */
-               *mnc += (lai->digits[1] >> 4) * 100;
+       *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;
@@ -134,14 +137,67 @@ static int gsm48_decode_start_time(struct gsm48_rr_cd *cd,
        return 0;
 }
 
+/* decode "BA Range" (10.5.2.1a) */
+static int gsm48_decode_ba_range(const uint8_t *ba, uint8_t ba_len,
+       uint32_t *range, uint8_t *ranges, int max_ranges)
+{
+       /* ba = pointer to IE without IE type and length octets
+        * ba_len = number of octets
+        * range = pointer to store decoded range
+        * ranges = number of ranges decoded
+        * max_ranges = maximum number of decoded ranges that can be stored
+        */
+       uint16_t lower, higher;
+       int i, n, required_octets;
+       
+       /* find out how much ba ranges will be decoded */
+       n = *ba++;
+       ba_len --;
+       required_octets = 5 * (n >> 1) + 3 * (n & 1);
+       if (required_octets > ba_len) {
+               LOGP(DRR, LOGL_NOTICE, "BA range IE too short: %d ranges "
+                       "require %d octets, but only %d octets remain.\n",
+                       n, required_octets, ba_len);
+               *ranges = 0;
+               return -EINVAL;
+       }
+       if (max_ranges > n)
+               LOGP(DRR, LOGL_NOTICE, "BA range %d exceed the maximum number "
+                       "of ranges supported by this mobile (%d).\n",
+                       n, max_ranges);
+               n = max_ranges;
+
+       /* decode ranges */
+       for (i = 0; i < n; i++) {
+               if (!(i & 1)) {
+                       /* decode even range number */
+                       lower = *ba++ << 2;
+                       lower |= (*ba >> 6);
+                       higher = (*ba++ & 0x3f) << 4;
+                       higher |= *ba >> 4;
+               } else {
+                       lower = (*ba++ & 0x0f) << 6;
+                       lower |= *ba >> 2;
+                       higher = (*ba++ & 0x03) << 8;
+                       higher |= *ba++;
+                       /* decode odd range number */
+               }
+               *range++ = (higher << 16) | lower;
+       }
+       *ranges = n;
+
+       return 0;
+}
+
 /*
  * state transition
  */
 
-static const char *gsm48_rr_state_names[] = {
-       "IDLE",
-       "CONN PEND",
-       "DEDICATED",
+const char *gsm48_rr_state_names[] = {
+       "idle",
+       "connection pending",
+       "dedicated",
+       "release pending",
 };
 
 static void new_rr_state(struct gsm48_rrlayer *rr, int state)
@@ -158,6 +214,9 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state)
        if (state == GSM48_RR_ST_IDLE) {
                struct msgb *msg, *nmsg;
 
+               /* release dedicated mode, if any */
+//             tx_ph_dm_rel_req(rr->ms);
+               l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_FULL);
                /* free establish message, if any */
                rr->rr_est_req = 0;
                if (rr->rr_est_msg) {
@@ -183,6 +242,8 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state)
                        return;
                gsm322_c_event(rr->ms, nmsg);
                msgb_free(nmsg);
+               /* reset any BA range */
+               rr->ba_ranges = 0;
        }
 }
 
@@ -273,8 +334,6 @@ static int gsm48_rx_rll(struct msgb *msg, struct osmocom_ms *ms)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
 
-#warning HACK!!!!!!
-return gsm48_rcv_rsl(ms, msg);
        msgb_enqueue(&rr->rsl_upqueue, msg);
 
        return 0;
@@ -294,6 +353,7 @@ static int gsm48_rx_rsl(struct msgb *msg, struct osmocom_ms *ms)
                /* FIXME: implement this */
                LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n",
                        rslh->msg_discr);
+               msgb_free(msg);
                rc = -EINVAL;
                break;
        }
@@ -321,6 +381,41 @@ int gsm48_rsl_dequeue(struct osmocom_ms *ms)
  * timers handling
  */
 
+/* special timer to ensure that UA is sent before disconnecting channel */
+static void timeout_rr_t_rel_wait(void *arg)
+{
+       struct gsm48_rrlayer *rr = arg;
+
+       LOGP(DRR, LOGL_INFO, "L2 release timer has fired, done waiting\n");
+
+       /* return to idle now */
+       new_rr_state(rr, GSM48_RR_ST_IDLE);
+}
+
+/* 3.4.13.1.1: Timeout of T3110 */
+static void timeout_rr_t3110(void *arg)
+{
+       struct gsm48_rrlayer *rr = arg;
+       struct osmocom_ms *ms = rr->ms;
+       struct msgb *nmsg;
+       uint8_t *mode;
+
+       LOGP(DRR, LOGL_INFO, "timer T3110 has fired, release locally\n");
+
+       new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+       /* disconnect the main signalling link */
+       nmsg = gsm48_l3_msgb_alloc();
+       if (!nmsg)
+               return;
+       mode = msgb_put(nmsg, 2);
+       mode[0] = RSL_IE_RELEASE_MODE;
+       mode[1] = 1; /* local release */
+       gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
+
+       return;
+}
+
 static void timeout_rr_t3122(void *arg)
 {
        LOGP(DRR, LOGL_INFO, "timer T3122 has fired\n");
@@ -336,6 +431,7 @@ static void timeout_rr_t3126(void *arg)
                struct msgb *msg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
                struct gsm48_rr_hdr *rrh;
 
+               LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n");
                if (!msg)
                        return;
                rrh = (struct gsm48_rr_hdr *)msg->data;
@@ -346,6 +442,22 @@ static void timeout_rr_t3126(void *arg)
        new_rr_state(rr, GSM48_RR_ST_IDLE);
 }
 
+static void start_rr_t_rel_wait(struct gsm48_rrlayer *rr, int sec, int micro)
+{
+       LOGP(DRR, LOGL_INFO, "starting T_rel_wait with %d seconds\n", sec);
+       rr->t_rel_wait.cb = timeout_rr_t_rel_wait;
+       rr->t_rel_wait.data = rr;
+       bsc_schedule_timer(&rr->t_rel_wait, sec, micro);
+}
+
+static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro)
+{
+       LOGP(DRR, LOGL_INFO, "starting T3110 with %d seconds\n", sec);
+       rr->t3110.cb = timeout_rr_t3110;
+       rr->t3110.data = rr;
+       bsc_schedule_timer(&rr->t3110, sec, micro);
+}
+
 static void start_rr_t3122(struct gsm48_rrlayer *rr, int sec, int micro)
 {
        LOGP(DRR, LOGL_INFO, "starting T3122 with %d seconds\n", sec);
@@ -362,6 +474,22 @@ 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_rel_wait(struct gsm48_rrlayer *rr)
+{
+       if (bsc_timer_pending(&rr->t_rel_wait)) {
+               LOGP(DRR, LOGL_INFO, "stopping pending timer T_rel_wait\n");
+               bsc_del_timer(&rr->t_rel_wait);
+       }
+}
+
+static void stop_rr_t3110(struct gsm48_rrlayer *rr)
+{
+       if (bsc_timer_pending(&rr->t3110)) {
+               LOGP(DRR, LOGL_INFO, "stopping pending timer T3110\n");
+               bsc_del_timer(&rr->t3110);
+       }
+}
+
 static void stop_rr_t3122(struct gsm48_rrlayer *rr)
 {
        if (bsc_timer_pending(&rr->t3122)) {
@@ -413,7 +541,7 @@ static int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause)
 /* send chiperhing mode complete */
 static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr)
 {
-       struct gsm_support *sup = &ms->support;
+       struct gsm_settings *set = &ms->settings;
        struct msgb *nmsg;
        struct gsm48_hdr *gh;
        uint8_t buf[11], *tlv;
@@ -430,7 +558,7 @@ static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr)
 
        /* MI */
        if (cr) {
-               gsm48_generate_mid_from_imsi(buf, sup->imeisv);
+               gsm48_generate_mid_from_imsi(buf, set->imeisv);
                tlv = msgb_put(nmsg, 2 + buf[1]);
                memcpy(tlv, buf, 2 + buf[1]);
        }
@@ -697,18 +825,27 @@ static int gsm48_rr_rx_cm_enq(struct osmocom_ms *ms, struct msgb *msg)
 /* TODO: turn this into a channel activation timeout, later */
 #define RSL_MT_CHAN_CNF 0x19
 #include <osmocom/l1ctl.h>
-static void temp_rach_to(void *arg)
+int gsm48_rr_rach_conf(struct osmocom_ms *ms, uint32_t fn)
 {
-       struct gsm48_rrlayer *rr = arg;
-       struct osmocom_ms *ms = rr->ms;
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
        struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm RR");
-       struct abis_rsl_rll_hdr *rllh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rllh));
+       struct abis_rsl_rll_hdr *rllh =
+               (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rllh));
+
+       if (!rr->wait_assign) {
+               LOGP(DRR, LOGL_INFO, "RACH confirm ignored, not waiting for "
+                       "assignment.\n");
+               return 0;
+       }
+       LOGP(DRR, LOGL_INFO, "RACH confirm framenr=%u\n", fn);
+       rr->cr_hist[0].valid = 2;
+       rr->cr_hist[0].fn = fn;
 
        rllh->c.msg_type = RSL_MT_CHAN_CNF;
        msg->l2h = (unsigned char *)rllh;
        gsm48_rcv_rsl(ms, msg);
 
-       return;
+       return 0;
 }
 
 /* start random access */
@@ -722,6 +859,9 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging)
        uint8_t chan_req_val, chan_req_mask;
        int rc;
 
+       LOGP(DSUM, LOGL_INFO, "Establish radio link due to %s request\n",
+               (paging) ? "paging" : "mobility management");
+
        /* ignore paging, if not camping */
        if (paging
         && (!cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY
@@ -752,6 +892,8 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging)
        /* number of retransmissions (with first transmission) */
        rr->n_chan_req = s->max_retrans + 1;
 
+#warning HACK: always request SDCCH for test
+cause = RR_EST_CAUSE_LOC_UPD;
        /* generate CHAN REQ (9.1.8) */
        switch (cause) {
        case RR_EST_CAUSE_EMERGENCY:
@@ -862,6 +1004,8 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging)
                LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: with unknown "
                        "establishment cause: %d\n", cause);
                undefined:
+               LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n");
+
                nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
                if (!nmsg)
                        return -ENOMEM;
@@ -872,17 +1016,12 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging)
                return -EINVAL;
        }
 
-// TODO: turn this into the channel activation timer
-       rr->temp_rach_ti.cb = temp_rach_to;
-       rr->temp_rach_ti.data = rr;
-       bsc_schedule_timer(&rr->temp_rach_ti, ms->support.sync_to, 0);
-
        /* store value, mask and history */
        rr->chan_req_val = chan_req_val;
        rr->chan_req_mask = chan_req_mask;
-       rr->cr_hist[2] = -1;
-       rr->cr_hist[1] = -1;
-       rr->cr_hist[0] = -1;
+       rr->cr_hist[2].valid = 0;
+       rr->cr_hist[1].valid = 0;
+       rr->cr_hist[0].valid = 0;
 
        /* if channel is already active somehow */
        if (cs->ccch_state == GSM322_CCCH_ST_DATA)
@@ -896,7 +1035,7 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
        struct gsm322_cellsel *cs = &ms->cellsel;
-       struct gsm48_sysinfo *s = ms->cellsel.si;
+       struct gsm48_sysinfo *s = &ms->cellsel.sel_si;
        struct msgb *nmsg;
        struct l1ctl_info_ul *nul;
        struct l1ctl_rach_req *nra;
@@ -907,9 +1046,11 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
                LOGP(DRR, LOGL_INFO, "CCCH channel activation failed.\n");
 
                if (rr->rr_est_req) {
-                       struct msgb *msg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+                       struct msgb *msg =
+                               gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
                        struct gsm48_rr_hdr *rrh;
 
+                       LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n");
                        if (!msg)
                                return -ENOMEM;
                        rrh = (struct gsm48_rr_hdr *)msg->data;
@@ -928,7 +1069,7 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
                return 0;
        }
 
-       LOGP(DRR, LOGL_INFO, "RANDOM ACCESS confirm (requests left %d)\n",
+       LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (requests left %d)\n",
                rr->n_chan_req);
 
        if (!rr->n_chan_req) {
@@ -952,26 +1093,31 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
                                slots = 55;
                        else
                                slots = 41;
+                       break;
                case 4: case 9: case 16:
                        if (s->ccch_conf != 1)
                                slots = 76;
                        else
                                slots = 52;
+                       break;
                case 5: case 10: case 20:
                        if (s->ccch_conf != 1)
                                slots = 109;
                        else
                                slots = 58;
+                       break;
                case 6: case 11: case 25:
                        if (s->ccch_conf != 1)
                                slots = 163;
                        else
                                slots = 86;
+                       break;
                default:
                        if (s->ccch_conf != 1)
                                slots = 217;
                        else
                                slots = 115;
+                       break;
                }
        }
 
@@ -993,25 +1139,21 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
        chan_req &= rr->chan_req_mask;
        chan_req |= rr->chan_req_val;
        nra->ra = chan_req;
-#ifdef TODO
-       at this point we require chan req to be sent at a given delay
-       also we require a confirm from radio part
-       nra->delay = (random() % s->tx_integer) + slots;
-
-       LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (ra 0x%02x delay %d)\n", nra->ra,
-               nra->delay);
-#else
-       rr->temp_rach_ti.cb = temp_rach_to;
-       rr->temp_rach_ti.data = rr;
-       bsc_schedule_timer(&rr->temp_rach_ti, 0, 900000);
+#warning TODO
+       nra->mf_off = 1; // (random() % s->tx_integer) + slots;
+       nra->fn51 = (s->ccch_conf == 1) ? 27 : 50;
 
-       LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (ra 0x%02x)\n", nra->ra);
-#endif
+       LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (Tx-integer %d combined %s "
+               "S(lots) %d ra 0x%02x offset %d)\n", s->tx_integer,
+               (s->ccch_conf == 1) ? "yes": "no", slots, nra->ra, nra->mf_off);
 
        /* shift history and store */
-       rr->cr_hist[2] = rr->cr_hist[1];
-       rr->cr_hist[1] = rr->cr_hist[0];
-       rr->cr_hist[0] = chan_req;
+       memcpy(&(rr->cr_hist[2]), &(rr->cr_hist[1]),
+               sizeof(struct gsm48_cr_hist));
+       memcpy(&(rr->cr_hist[1]), &(rr->cr_hist[0]),
+               sizeof(struct gsm48_cr_hist));
+       rr->cr_hist[0].valid = 1;
+       rr->cr_hist[0].chan_req = chan_req;
 
 #ifdef TODO
        add layer 1 conrols to RSL...
@@ -1520,7 +1662,7 @@ static int gsm48_decode_ccd(struct gsm48_sysinfo *s,
 
 /* decode "Mobile Allocation" (10.5.2.21) */
 static int gsm48_decode_mobile_alloc(struct gsm48_sysinfo *s,
-       uint8_t *ma, uint8_t len)
+       uint8_t *ma, uint8_t len, uint16_t *hopping, uint8_t *hopp_len, int si4)
 {
        int i, j = 0;
        uint16_t f[len << 3];
@@ -1530,13 +1672,17 @@ static int gsm48_decode_mobile_alloc(struct gsm48_sysinfo *s,
                return -EINVAL;
 
        /* tabula rasa */
-       s->hopp_len = 0;
-       for (i = 0; i < 1024; i++)
-               s->freq[i].mask &= ~FREQ_TYPE_HOPP;
+       *hopp_len = 0;
+       if (si4) {
+               for (i = 0; i < 1024; i++)
+                       s->freq[i].mask &= ~FREQ_TYPE_HOPP;
+       }
 
        /* generating list of all frequencies (1..1023,0) */
        for (i = 1; i <= 1024; i++) {
                if ((s->freq[i & 1023].mask & FREQ_TYPE_SERV)) {
+                       LOGP(DRR, LOGL_INFO, "Serving cell ARFCN #%d: %d\n",
+                               j, i & 1023);
                        f[j++] = i & 1023;
                        if (j == (len << 3))
                                break;
@@ -1549,6 +1695,8 @@ static int gsm48_decode_mobile_alloc(struct gsm48_sysinfo *s,
        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 "
@@ -1557,8 +1705,9 @@ static int gsm48_decode_mobile_alloc(struct gsm48_sysinfo *s,
                                        i + 1, j);
                                break;
                        }
-                       s->hopping[s->hopp_len++] = f[i];
-                       s->freq[f[i]].mask |= FREQ_TYPE_HOPP;
+                       hopping[(*hopp_len)++] = f[i];
+                       if (si4)
+                               s->freq[f[i]].mask |= FREQ_TYPE_HOPP;
                }
        }
 
@@ -1608,6 +1757,39 @@ static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, uint8_t *si,
 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;
 }
 
@@ -1654,6 +1836,12 @@ static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm48_sysinfo *s = ms->cellsel.si;
        int payload_len = msgb_l3len(msg) - sizeof(*si);
 
+       if (!s) {
+               LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 1 "
+                       "ignored\n");
+               return -EINVAL;
+       }
+
        if (payload_len < 0) {
                LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 1 "
                        "message.\n");
@@ -1688,6 +1876,12 @@ static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm48_sysinfo *s = ms->cellsel.si;
        int payload_len = msgb_l3len(msg) - sizeof(*si);
 
+       if (!s) {
+               LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2 "
+                       "ignored\n");
+               return -EINVAL;
+       }
+
        if (payload_len < 0) {
                LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2 "
                        "message.\n");
@@ -1723,6 +1917,12 @@ static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm48_sysinfo *s = ms->cellsel.si;
        int payload_len = msgb_l3len(msg) - sizeof(*si);
 
+       if (!s) {
+               LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2bis"
+                       " ignored\n");
+               return -EINVAL;
+       }
+
        if (payload_len < 0) {
                LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2bis "
                        "message.\n");
@@ -1758,6 +1958,12 @@ static int gsm48_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm48_sysinfo *s = ms->cellsel.si;
        int payload_len = msgb_l3len(msg) - sizeof(*si);
 
+       if (!s) {
+               LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2ter"
+                       " ignored\n");
+               return -EINVAL;
+       }
+
        if (payload_len < 0) {
                LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2ter "
                        "message.\n");
@@ -1787,9 +1993,16 @@ static int gsm48_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg)
 static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_system_information_type_3 *si = msgb_l3(msg);
-       struct gsm48_sysinfo *s = ms->cellsel.si;
+       struct gsm322_cellsel *cs = &ms->cellsel;
+       struct gsm48_sysinfo *s = cs->si;
        int payload_len = msgb_l3len(msg) - sizeof(*si);
 
+       if (!s) {
+               LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 3 "
+                       "ignored\n");
+               return -EINVAL;
+       }
+
        if (payload_len < 0) {
                LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 3 "
                        "message.\n");
@@ -1816,11 +2029,20 @@ static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg)
        if (payload_len >= 4)
                gsm48_decode_si3_rest(s, si->rest_octets, payload_len);
 
-       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 3 (mcc %03d mnc %02d "
-               "lac 0x%04x)\n", s->mcc, s->mnc, s->lac);
+       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;
 
+       if (cs->ccch_mode == CCCH_MODE_NONE) {
+               cs->ccch_mode = (s->ccch_conf == 1) ? CCCH_MODE_COMBINED :
+                       CCCH_MODE_NON_COMBINED;
+               LOGP(DRR, LOGL_NOTICE, "Changing CCCH_MODE to %d\n",
+                       cs->ccch_mode);
+               l1ctl_tx_ccch_mode_req(ms, cs->ccch_mode);
+       }
+
        return gsm48_send_sysinfo(ms, si->header.system_information);
 }
 
@@ -1834,6 +2056,12 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
        uint8_t *data = si->data;
        struct gsm48_chan_desc *cd;
 
+       if (!s) {
+               LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 4 "
+                       "ignored\n");
+               return -EINVAL;
+       }
+
        if (payload_len < 0) {
                short_read:
                LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 4 "
@@ -1841,6 +2069,12 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
                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)));
@@ -1870,7 +2104,8 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
        if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_MOB_AL) {
                if (payload_len < 1 || payload_len < 2 + data[1])
                        goto short_read;
-               gsm48_decode_mobile_alloc(s, data + 2, si->data[1]);
+               gsm48_decode_mobile_alloc(s, data + 2, si->data[1], s->hopping,
+                       &s->hopp_len, 1);
                payload_len -= 2 + data[1];
                data += 2 + data[1];
        }
@@ -1878,8 +2113,9 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
        if (payload_len > 0)
                gsm48_decode_si4_rest(s, data, payload_len);
 
-       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4 (mcc %03d mnc %02d "
-               "lac 0x%04x)\n", s->mcc, s->mnc, s->lac);
+       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;
 
@@ -1894,6 +2130,12 @@ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm48_sysinfo *s = ms->cellsel.si;
        int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
 
+       if (!s) {
+               LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5 "
+                       "ignored\n");
+               return -EINVAL;
+       }
+
        if (payload_len < 0) {
                LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5 "
                        "message.\n");
@@ -1925,6 +2167,12 @@ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm48_sysinfo *s = ms->cellsel.si;
        int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
 
+       if (!s) {
+               LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5bis"
+                       " ignored\n");
+               return -EINVAL;
+       }
+
        if (payload_len < 0) {
                LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5bis "
                        "message.\n");
@@ -1957,6 +2205,12 @@ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm48_sysinfo *s = ms->cellsel.si;
        int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
 
+       if (!s) {
+               LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5ter"
+                       " ignored\n");
+               return -EINVAL;
+       }
+
        if (payload_len < 0) {
                LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5ter "
                        "message.\n");
@@ -1987,6 +2241,12 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm48_sysinfo *s = ms->cellsel.si;
        int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
 
+       if (!s) {
+               LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 6 "
+                       "ignored\n");
+               return -EINVAL;
+       }
+
        if (payload_len < 0) {
                LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 6 "
                        "message.\n");
@@ -2012,8 +2272,9 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
        if (payload_len >= 4)
                gsm48_decode_si6_rest(s, si->rest_octets, payload_len);
 
-       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (mcc %03d mnc %02d "
-               "lac 0x%04x)\n", s->mcc, s->mnc, s->lac);
+       LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (mcc %s mnc %s "
+               "lac 0x%04x)\n", gsm_print_mcc(s->mcc),
+               gsm_print_mnc(s->mnc), s->lac);
 
        s->si6 = 1;
 
@@ -2092,8 +2353,8 @@ static int gsm48_rr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg)
        if (rr->state != GSM48_RR_ST_IDLE || !cs->selected
         || (cs->state != GSM322_C3_CAMPED_NORMALLY
          && cs->state != GSM322_C7_CAMPED_ANY_CELL)) {
-               LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping "
-                       "normally.\n");
+               LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n");
+
                return 0;
        }
        LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 1\n");
@@ -2102,6 +2363,7 @@ static int gsm48_rr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg)
                short_read:
                LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 1 "
                        "message.\n");
+
                return -EINVAL;
        }
 
@@ -2143,8 +2405,8 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg)
        if (rr->state != GSM48_RR_ST_IDLE || !cs->selected
         || (cs->state != GSM322_C3_CAMPED_NORMALLY
          && cs->state != GSM322_C7_CAMPED_ANY_CELL)) {
-               LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping "
-                       "normally.\n");
+               LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n");
+
                return 0;
        }
        LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 2\n");
@@ -2153,6 +2415,7 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg)
                short_read:
                LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 2 "
                        "message .\n");
+
                return -EINVAL;
        }
 
@@ -2203,8 +2466,8 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
        if (rr->state != GSM48_RR_ST_IDLE || !cs->selected
         || (cs->state != GSM322_C3_CAMPED_NORMALLY
          && cs->state != GSM322_C7_CAMPED_ANY_CELL)) {
-               LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping "
-                       "normally.\n");
+               LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n");
+
                return 0;
        }
        LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 3\n");
@@ -2212,6 +2475,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
        if (payload_len < 0) { /* must include "channel needed", part of *pa */
                LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 3 "
                        "message .\n");
+
                return -EINVAL;
        }
 
@@ -2265,67 +2529,35 @@ static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
        int i;
+       struct gsm_time tm;
+       uint8_t ia_t1, ia_t2, ia_t3;
 
        for (i = 0; i < 3; i++) {
-               if (rr->cr_hist[i] >= 0
-                && ref->ra == rr->cr_hist[i]) {
-                       LOGP(DRR, LOGL_INFO, "request %02x matches\n", ref->ra);
-                       // todo: match timeslot
-                       return 1;
+               /* filter confirmed RACH requests only */
+               if (rr->cr_hist[i].valid == 2
+                && ref->ra == rr->cr_hist[i].chan_req) {
+                       ia_t1 = ref->t1;
+                       ia_t2 = ref->t2;
+                       ia_t3 = (ref->t3_high << 3) | ref->t3_low;
+                       gsm_fn2gsmtime(&tm, rr->cr_hist[i].fn);
+                       if (ia_t1 == (tm.t1 & 0x1f) && ia_t2 == tm.t2
+                        && ia_t3 == tm.t3) {
+                               LOGP(DRR, LOGL_INFO, "request %02x matches "
+                                       "(fn=%d,%d,%d)\n", ref->ra, ia_t1,
+                                       ia_t2, ia_t3);
+                               return 1;
+                       } else
+                               LOGP(DRR, LOGL_INFO, "request %02x matches "
+                                       "but not frame number (IMM.ASS "
+                                       "fn=%d,%d,%d != RACH fn=%d,%d,%d)\n",
+                                       ref->ra, ia_t1, ia_t2, ia_t3,
+                                       tm.t1 & 0x1f, tm.t2, tm.t3);
                }
        }
 
        return 0;
 }
 
-/* 9.1.3 sending ASSIGNMENT COMPLETE */
-static int gsm48_rr_tx_ass_cpl(struct osmocom_ms *ms, uint8_t cause)
-{
-       struct msgb *nmsg;
-       struct gsm48_hdr *gh;
-       struct gsm48_ass_cpl *ac;
-
-       LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMPLETE (cause #%d)\n", cause);
-
-       nmsg = gsm48_l3_msgb_alloc();
-       if (!nmsg)
-               return -ENOMEM;
-       gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
-       ac = (struct gsm48_ass_cpl *) msgb_put(nmsg, sizeof(*ac));
-
-       gh->proto_discr = GSM48_PDISC_RR;
-       gh->msg_type = GSM48_MT_RR_ASS_COMPL;
-
-       /* RR_CAUSE */
-       ac->rr_cause = cause;
-
-       return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
-}
-
-/* 9.1.4 sending ASSIGNMENT FAILURE */
-static int gsm48_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause)
-{
-       struct msgb *nmsg;
-       struct gsm48_hdr *gh;
-       struct gsm48_ass_fail *af;
-
-       LOGP(DRR, LOGL_INFO, "ASSIGNMENT FAILURE (cause #%d)\n", cause);
-
-       nmsg = gsm48_l3_msgb_alloc();
-       if (!nmsg)
-               return -ENOMEM;
-       gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
-       af = (struct gsm48_ass_fail *) msgb_put(nmsg, sizeof(*af));
-
-       gh->proto_discr = GSM48_PDISC_RR;
-       gh->msg_type = GSM48_MT_RR_ASS_COMPL;
-
-       /* RR_CAUSE */
-       af->rr_cause = cause;
-
-       return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
-}
-
 /* 9.1.18 IMMEDIATE ASSIGNMENT is received */
 static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
 {
@@ -2357,7 +2589,7 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
                gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1));
 
        /* decode channel description */
-       LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:");
+       LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:\n");
        cd.chan_nr = ia->chan_desc.chan_nr;
        rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts);
        if (ia->chan_desc.h0.h) {
@@ -2658,31 +2890,96 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
  * link establishment and release
  */
 
+/* process "Loss Of Signal" */
+int gsm48_rr_los(struct osmocom_ms *ms)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+       uint8_t *mode;
+       struct msgb *nmsg;
+       struct gsm48_rr_hdr *nrrh;
+
+       LOGP(DSUM, LOGL_INFO, "Radio link lost signal\n");
+
+       /* stop T3211 if running */
+       stop_rr_t3110(rr);
+
+       switch(rr->state) {
+       case GSM48_RR_ST_CONN_PEND:
+               LOGP(DRR, LOGL_INFO, "LOS during RACH request\n");
+
+               /* stop pending RACH timer */
+               stop_rr_t3126(rr);
+               break;
+       case GSM48_RR_ST_DEDICATED:
+               LOGP(DRR, LOGL_INFO, "LOS during dedicated mode, release "
+                       "locally\n");
+
+               new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+               /* release message */
+               rel_local:
+               nmsg = gsm48_l3_msgb_alloc();
+               if (!nmsg)
+                       return -ENOMEM;
+               mode = msgb_put(nmsg, 2);
+               mode[0] = RSL_IE_RELEASE_MODE;
+               mode[1] = 1; /* local release */
+               /* start release */
+               return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
+       case GSM48_RR_ST_REL_PEND:
+               LOGP(DRR, LOGL_INFO, "LOS during RR release procedure, release "
+                       "locally\n");
+
+               /* stop pending RACH timer */
+               stop_rr_t3110(rr);
+
+               /* release locally */
+               goto rel_local;
+       default:
+               /* this should not happen */
+               LOGP(DRR, LOGL_ERROR, "LOS in IDLE state, ignoring\n");
+               return -EINVAL;
+       }
+
+       /* send inication to upper layer */
+       nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+       if (!nmsg)
+               return -ENOMEM;
+       nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+       nrrh->cause = RR_REL_CAUSE_LOST_SIGNAL;
+       gsm48_rr_upmsg(ms, nmsg);
+
+       /* return idle */
+       new_rr_state(rr, GSM48_RR_ST_IDLE);
+       return 0;
+}
+
 /* activate link and send establish request */
 static int gsm48_rr_dl_est(struct osmocom_ms *ms)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
        struct gsm_subscriber *subscr = &ms->subscr;
+       struct gsm322_cellsel *cs = &ms->cellsel;
+       struct gsm48_sysinfo *s = cs->si;
        struct msgb *nmsg;
        struct gsm48_hdr *gh;
        struct gsm48_pag_rsp *pr;
        uint8_t mi[11];
        uint8_t ch_type, ch_subch, ch_ts;
+       uint16_t ma[64];
+       uint8_t ma_len;
 
        /* 3.3.1.1.3.1 */
        stop_rr_t3126(rr);
 
-       /* flush pending RACH requests */
-#ifdef TODO
-       rr->n_chan_req = 0; // just to be safe
-       nmsg = msgb_alloc_headroom(20, 16, "RAND_FLUSH");
-       if (!nmsg)
-               return -ENOMEM;
-       gsm48_send_rsl(ms, RSL_MT_RAND_ACC_FLSH, msg);
-#else
-       if (bsc_timer_pending(&rr->temp_rach_ti))
-               bsc_del_timer(&rr->temp_rach_ti);
-#endif
+       if (rr->cd_now.h) {
+               gsm48_decode_mobile_alloc(s, rr->cd_now.mob_alloc_lv + 1,
+                       rr->cd_now.mob_alloc_lv[0], ma, &ma_len, 0);
+               if (ma_len < 1) {
+                       LOGP(DRR, LOGL_NOTICE, "Hopping, but no allocation\n");
+                       return -EINVAL;
+               }
+       }
 
        /* send DL_EST_REQ */
        if (rr->rr_est_msg) {
@@ -2726,11 +3023,18 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms)
        RSL_MT_ to activate channel with all the cd_now informations
 #else
        rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts);
-       if (ch_type == RSL_CHAN_SDCCH4_ACCH || ch_ts != 0) {
-               printf("Channel type not supported, exitting.\n");
+       if ((ch_type != RSL_CHAN_SDCCH8_ACCH
+         && ch_type != RSL_CHAN_SDCCH4_ACCH) || ch_ts > 4) {
+               printf("Channel type %d, subch %d, ts %d not supported, "
+                       "exitting.\n", ch_type, ch_subch, ch_ts);
                exit(-ENOTSUP);
        }
-       tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr);
+       if (rr->cd_now.h)
+               tx_ph_dm_est_req_h1(ms, rr->cd_now.maio, rr->cd_now.hsn,
+                       ma, ma_len, rr->cd_now.chan_nr, rr->cd_now.tsc, 0);
+       else
+               tx_ph_dm_est_req_h0(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr,
+                       rr->cd_now.tsc, 0);
 #endif
 
        /* start establishmnet */
@@ -2741,15 +3045,19 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms)
 static int gsm48_rr_estab_cnf(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
+       uint8_t *mode;
        struct msgb *nmsg;
 
        /* if MM has releases before confirm, we start release */
-       if (rr->state == GSM48_RR_ST_IDLE) {
+       if (rr->state == GSM48_RR_ST_REL_PEND) {
                LOGP(DRR, LOGL_INFO, "MM already released RR.\n");
                /* release message */
                nmsg = gsm48_l3_msgb_alloc();
                if (!nmsg)
                        return -ENOMEM;
+               mode = msgb_put(nmsg, 2);
+               mode[0] = RSL_IE_RELEASE_MODE;
+               mode[1] = 0; /* normal release */
                /* start release */
                return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
        }
@@ -2765,25 +3073,215 @@ static int gsm48_rr_estab_cnf(struct osmocom_ms *ms, struct msgb *msg)
        return gsm48_rr_upmsg(ms, nmsg);
 }
 
-/* the link is released */
-static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
+/* the link is released in pending state (by l2) */
+static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct msgb *nmsg;
+       struct gsm48_rr_hdr *nrrh;
 
-       /* deactivate channel */
-       LOGP(DRR, LOGL_INFO, "deactivating channel (arfcn %d)\n",
-               rr->cd_now.arfcn);
-#ifdef TODO
-       release and give new arfcn
-       tx_ph_dm_rel_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
-#else
-       l1ctl_tx_ccch_req(ms, rr->cd_now.arfcn);
-#endif
+       LOGP(DSUM, LOGL_INFO, "Radio link is released\n");
 
-       /* do nothing, because we aleady IDLE
-        * or we received the rel cnf of the last connection
-        * while already requesting a new one (CONN PEND)
-        */
+       /* send inication to upper layer */
+       nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+       if (!nmsg)
+               return -ENOMEM;
+       nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+       nrrh->cause = RR_REL_CAUSE_NORMAL;
+       gsm48_rr_upmsg(ms, nmsg);
+
+       /* start release timer, so UA will be transmitted */
+       start_rr_t_rel_wait(rr, 1, 500000);
+
+       /* pending release */
+       new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+       return 0;
+}
+
+/* 9.1.7 CHANNEL RELEASE is received  */
+static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct gsm48_hdr *gh = msgb_l3(msg);
+       struct gsm48_chan_rel *cr = (struct gsm48_chan_rel *)gh->data;
+       int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cr);
+       struct tlv_parsed tp;
+       struct msgb *nmsg;
+       uint8_t *mode;
+
+       if (payload_len < 0) {
+               LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE "
+                       "message.\n");
+               return gsm48_rr_tx_rr_status(ms,
+                       GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+       }
+       tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0);
+
+       LOGP(DRR, LOGL_INFO, "channel release request with cause 0x%02x)\n",
+               cr->rr_cause);
+
+       /* BA range */
+       if (TLVP_PRESENT(&tp, GSM48_IE_BA_RANGE)) {
+               gsm48_decode_ba_range(TLVP_VAL(&tp, GSM48_IE_BA_RANGE),
+                       *(TLVP_VAL(&tp, GSM48_IE_BA_RANGE) - 1), rr->ba_range,
+                       &rr->ba_ranges,
+                       sizeof(rr->ba_range) / sizeof(rr->ba_range[0]));
+               /* NOTE: the ranges are kept until IDLE state is returned
+                * (see new_rr_state)
+                */
+       }
+
+       new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+       /* start T3110, so that two DISCs can be sent due to T200 timeout */
+       start_rr_t3110(rr, 1, 500000);
+
+       /* disconnect the main signalling link */
+       nmsg = gsm48_l3_msgb_alloc();
+       if (!nmsg)
+               return -ENOMEM;
+       mode = msgb_put(nmsg, 2);
+       mode[0] = RSL_IE_RELEASE_MODE;
+       mode[1] = 0; /* normal release */
+       return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
+}
+
+/*
+ * assignment and handover
+ */
+
+/* 9.1.3 sending ASSIGNMENT COMPLETE */
+static int gsm48_rr_tx_ass_cpl(struct osmocom_ms *ms, uint8_t cause)
+{
+       struct msgb *nmsg;
+       struct gsm48_hdr *gh;
+       struct gsm48_ass_cpl *ac;
+
+       LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMPLETE (cause #%d)\n", cause);
+
+       nmsg = gsm48_l3_msgb_alloc();
+       if (!nmsg)
+               return -ENOMEM;
+       gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+       ac = (struct gsm48_ass_cpl *) msgb_put(nmsg, sizeof(*ac));
+
+       gh->proto_discr = GSM48_PDISC_RR;
+       gh->msg_type = GSM48_MT_RR_ASS_COMPL;
+
+       /* RR_CAUSE */
+       ac->rr_cause = cause;
+
+       return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+}
+
+/* 9.1.4 sending ASSIGNMENT FAILURE */
+static int gsm48_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause)
+{
+       struct msgb *nmsg;
+       struct gsm48_hdr *gh;
+       struct gsm48_ass_fail *af;
+
+       LOGP(DRR, LOGL_INFO, "ASSIGNMENT FAILURE (cause #%d)\n", cause);
+
+       nmsg = gsm48_l3_msgb_alloc();
+       if (!nmsg)
+               return -ENOMEM;
+       gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+       af = (struct gsm48_ass_fail *) msgb_put(nmsg, sizeof(*af));
+
+       gh->proto_discr = GSM48_PDISC_RR;
+       gh->msg_type = GSM48_MT_RR_ASS_COMPL;
+
+       /* RR_CAUSE */
+       af->rr_cause = cause;
+
+       return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, 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 gsm48_hdr *gh = msgb_l3(msg);
+       struct gsm48_ass_cmd *ac = (struct gsm48_ass_cmd *)gh->data;
+       int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ac);
+       struct tlv_parsed tp;
+       struct gsm48_rr_cd cd;
+
+       LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMMAND\n");
+
+       memset(&cd, 0, sizeof(cd));
+
+       if (payload_len < 0) {
+               LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND message.\n");
+               return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+       }
+       tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0);
+
+#if 0
+       /* channel description */
+       memcpy(&cd.chan_desc, &ac->chan_desc, sizeof(chan_desc));
+       /* power command */
+       cd.power_command = ac->power_command;
+       /* frequency list, after timer */
+       tlv_copy(&cd.fl, sizeof(fl_after), &tp, GSM48_IE_FRQLIST_AFTER);
+       /* cell channel description */
+       tlv_copy(&cd.ccd, sizeof(ccd), &tp, GSM48_IE_CELL_CH_DESC);
+       /* multislot allocation */
+       tlv_copy(&cd.multia, sizeof(ma), &tp, GSM48_IE_MSLOT_DESC);
+       /* channel mode */
+       tlv_copy(&cd.chanmode, sizeof(chanmode), &tp, GSM48_IE_CHANMODE_1);
+       /* mobile allocation, after time */
+       tlv_copy(&cd.moba_after, sizeof(moba_after), &tp, GSM48_IE_MOB_AL_AFTER);
+       /* starting time */
+       tlv_copy(&cd.start, sizeof(start), &tp, GSM_IE_START_TIME);
+       /* frequency list, before time */
+       tlv_copy(&cd.fl_before, sizeof(fl_before), &tp, GSM48_IE_FRQLIST_BEFORE);
+       /* channel description, before time */
+       tlv_copy(&cd.chan_desc_before, sizeof(cd_before), &tp, GSM48_IE_CHDES_1_BEFORE);
+       /* frequency channel sequence, before time */
+       tlv_copy(&cd.fcs_before, sizeof(fcs_before), &tp, GSM48_IE_FRQSEQ_BEFORE);
+       /* mobile allocation, before time */
+       tlv_copy(&cd.moba_before, sizeof(moba_before), &tp, GSM48_IE_MOB_AL_BEFORE);
+       /* cipher mode setting */
+       if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET))
+               cd.cipher = *TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET);
+       else
+               cd.cipher = 0;
+
+       if (no CA) {
+               LOGP(DRR, LOGL_INFO, "No current cell allocation available.\n");
+               return gsm48_rr_tx_ass_fail(ms, GSM48_GSM48_RR_CAUSE_NO_CELL_ALLOC_A);
+       }
+       
+       if (not supported) {
+               LOGP(DRR, LOGL_INFO, "New channel is not supported.\n");
+               return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_CHAN_MODE_UNACCEPT);
+       }
+
+       if (freq not supported) {
+               LOGP(DRR, LOGL_INFO, "New frequency is not supported.\n");
+               return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_FREQ_NOT_IMPL);
+       }
+
+       /* store current channel descriptions, to return in case of failure */
+       memcpy(&rr->chan_last, &rr->chan_desc, sizeof(*cd));
+       /* copy new description */
+       memcpy(&rr->chan_desc, cd, sizeof(cd));
+
+       /* start suspension of current link */
+       nmsg = gsm48_l3_msgb_alloc();
+       if (!nmsg)
+               return -ENOMEM;
+       gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, msg);
+
+       /* change into special assignment suspension state */
+       rr->assign_susp_state = 1;
+       rr->resume_last_state = 0;
+#else
+       return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_FREQ_NOT_IMPL);
+#endif
 
        return 0;
 }
@@ -2812,6 +3310,8 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
                        LOGP(DRR, LOGL_INFO, "T3122 running, rejecting!\n");
                        cause = RR_REL_CAUSE_T3122;
                        reject:
+                       LOGP(DSUM, LOGL_INFO, "Establishing radio link not "
+                               "possible\n");
                        nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
                        if (!nmsg)
                                return -ENOMEM;
@@ -2823,6 +3323,13 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
                stop_rr_t3122(rr);
        }
 
+       /* if state is not idle */
+       if (rr->state != GSM48_RR_ST_IDLE) {
+               LOGP(DRR, LOGL_INFO, "We are not IDLE yet, rejecting!\n");
+               cause = RR_REL_CAUSE_TRY_LATER;
+               goto reject;
+       }
+
        /* cell selected */
        if (!cs->selected) {
                LOGP(DRR, LOGL_INFO, "No cell selected, rejecting!\n");
@@ -2947,10 +3454,10 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
                case GSM48_MT_RR_ADD_ASS:
                        rc = gsm48_rr_rx_add_ass(ms, msg);
                        break;
-#if 0
                case GSM48_MT_RR_ASS_CMD:
                        rc = gsm48_rr_rx_ass_cmd(ms, msg);
                        break;
+#if 0
                case GSM48_MT_RR_CIP_MODE_CMD:
                        rc = gsm48_rr_rx_cip_mode_cmd(ms, msg);
                        break;
@@ -2966,6 +3473,9 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
                        rc = gsm48_rr_rx_freq_redef(ms, msg);
                        break;
 #endif
+               case GSM48_MT_RR_CHAN_REL:
+                       rc = gsm48_rr_rx_chan_rel(ms, msg);
+                       break;
                default:
                        LOGP(DRR, LOGL_NOTICE, "Message type 0x%02x unknown.\n",
                                gh->msg_type);
@@ -3022,15 +3532,6 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm48_system_information_type_header *sih = msgb_l3(msg);
 
        switch (sih->system_information) {
-       case GSM48_MT_RR_SYSINFO_5:
-               return gsm48_rr_rx_sysinfo5(ms, msg);
-       case GSM48_MT_RR_SYSINFO_5bis:
-               return gsm48_rr_rx_sysinfo5bis(ms, msg);
-       case GSM48_MT_RR_SYSINFO_5ter:
-               return gsm48_rr_rx_sysinfo5ter(ms, msg);
-       case GSM48_MT_RR_SYSINFO_6:
-               return gsm48_rr_rx_sysinfo6(ms, msg);
-
        case GSM48_MT_RR_PAG_REQ_1:
                return gsm48_rr_rx_pag_req_1(ms, msg);
        case GSM48_MT_RR_PAG_REQ_2:
@@ -3045,10 +3546,29 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg)
        case GSM48_MT_RR_IMM_ASS_REJ:
                return gsm48_rr_rx_imm_ass_rej(ms, msg);
        default:
-#if 0
                LOGP(DRR, LOGL_NOTICE, "CCCH message type 0x%02x unknown.\n",
                        sih->system_information);
-#endif
+               return -EINVAL;
+       }
+}
+
+/* receive ACCH at RR layer */
+static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm48_system_information_type_header *sih = msgb_l3(msg);
+
+       switch (sih->system_information) {
+       case GSM48_MT_RR_SYSINFO_5:
+               return gsm48_rr_rx_sysinfo5(ms, msg);
+       case GSM48_MT_RR_SYSINFO_5bis:
+               return gsm48_rr_rx_sysinfo5bis(ms, msg);
+       case GSM48_MT_RR_SYSINFO_5ter:
+               return gsm48_rr_rx_sysinfo5ter(ms, msg);
+       case GSM48_MT_RR_SYSINFO_6:
+               return gsm48_rr_rx_sysinfo6(ms, msg);
+       default:
+               LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n",
+                       sih->system_information);
                return -EINVAL;
        }
 }
@@ -3074,6 +3594,18 @@ 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
+               start_loss_timer(cs, 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");
@@ -3097,24 +3629,107 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
                                // TODO: timer depends on BCCH config
        }
 
-       switch (rllh->chan_nr) {
-       case RSL_CHAN_PCH_AGCH:
+       if (rllh->chan_nr == RSL_CHAN_PCH_AGCH)
                return gsm48_rr_rx_pch_agch(ms, msg);
-       case RSL_CHAN_BCCH:
-#if 0
-#warning testing corrupt frames
-{int i;
-if (ms->cellsel.state == GSM322_C7_CAMPED_ANY_CELL)
-for(i=0;i<msgb_l3len(msg);i++)
- msg->l3h[i] = random();
- }
-#endif
+       if (rllh->chan_nr == RSL_CHAN_BCCH)
                return gsm48_rr_rx_bcch(ms, msg);
-       default:
-               LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n",
-                       rllh->chan_nr);
-               return -EINVAL;
+       if ((rllh->chan_nr & 0xf0) == RSL_CHAN_SDCCH4_ACCH)
+               return gsm48_rr_rx_acch(ms, msg);
+       if ((rllh->chan_nr & 0xf0) == RSL_CHAN_SDCCH8_ACCH)
+               return gsm48_rr_rx_acch(ms, msg);
+       LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n",
+               rllh->chan_nr);
+       return -EINVAL;
+}
+
+/* 3.4.13.3 RR abort in dedicated mode (also in conn. pending mode) */
+static int gsm48_rr_abort_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+       uint8_t *mode;
+
+       /* stop pending RACH timer */
+       stop_rr_t3126(rr);
+
+       /* release "normally" if we are in dedicated mode */
+       if (rr->state == GSM48_RR_ST_DEDICATED) {
+               struct msgb *nmsg;
+
+               LOGP(DRR, LOGL_INFO, "Abort in dedicated state, send release "
+                       "to layer 2.\n");
+
+               new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+               /* release message */
+               nmsg = gsm48_l3_msgb_alloc();
+               if (!nmsg)
+                       return -ENOMEM;
+               mode = msgb_put(nmsg, 2);
+               mode[0] = RSL_IE_RELEASE_MODE;
+               mode[1] = 0; /* normal release */
+               return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
+       }
+
+       LOGP(DRR, LOGL_INFO, "Abort in connection pending state, return to "
+               "idle state.\n");
+       /* return idle */
+       new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+       return 0;
+}
+
+/* release confirm in dedicated mode */
+static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+       if (rr->hando_susp_state || rr->assign_susp_state) {
+               struct msgb *nmsg;
+
+               /* change radio to new channel */
+//todo         tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr,
+//                              rr->cd_now.tsc);
+
+               /* send DL-ESTABLISH REQUEST */
+               nmsg = gsm48_l3_msgb_alloc();
+               if (!nmsg)
+                       return -ENOMEM;
+               gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg);
+
+#ifdef TODO
+               /* trigger RACH */
+               if (rr->hando_susp_state) {
+                       gsm48_rr_tx_hando_access(ms);
+                       rr->hando_acc_left = 3;
+               }
+#endif
        }
+       return 0;
+}
+
+/* release confirm */
+static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm48_rrlayer *rr = &ms->rrlayer;
+       struct msgb *nmsg;
+       struct gsm48_rr_hdr *nrrh;
+
+       LOGP(DSUM, LOGL_INFO, "Requesting channel aborted\n");
+
+       /* stop T3211 if running */
+       stop_rr_t3110(rr);
+
+       /* send release indication */
+       nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+       if (!nmsg)
+               return -ENOMEM;
+       nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+       nrrh->cause = RR_REL_CAUSE_NORMAL;
+       gsm48_rr_upmsg(ms, nmsg);
+
+       /* return idle */
+       new_rr_state(rr, GSM48_RR_ST_IDLE);
+       return 0;
 }
 
 /*
@@ -3125,36 +3740,54 @@ for(i=0;i<msgb_l3len(msg);i++)
 static struct dldatastate {
        uint32_t        states;
        int             type;
-       const char      *type_name;
        int             (*rout) (struct osmocom_ms *ms, struct msgb *msg);
 } dldatastatelist[] = {
-       {SBIT(GSM48_RR_ST_IDLE) | SBIT(GSM48_RR_ST_CONN_PEND),
-        RSL_MT_UNIT_DATA_IND, "UNIT_DATA_IND", gsm48_rr_unit_data_ind},
+       /* data transfer */
+       {SBIT(GSM48_RR_ST_IDLE) |
+        SBIT(GSM48_RR_ST_CONN_PEND) |
+        SBIT(GSM48_RR_ST_DEDICATED) |
+        SBIT(GSM48_RR_ST_REL_PEND),
+        RSL_MT_UNIT_DATA_IND, gsm48_rr_unit_data_ind},
+
        {SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */
-        RSL_MT_DATA_IND, "DATA_IND", gsm48_rr_data_ind},
-       {SBIT(GSM48_RR_ST_IDLE) | SBIT(GSM48_RR_ST_CONN_PEND),
-        RSL_MT_EST_CONF, "EST_CONF", gsm48_rr_estab_cnf},
+        RSL_MT_DATA_IND, gsm48_rr_data_ind},
+
+       /* esablish */
+       {SBIT(GSM48_RR_ST_CONN_PEND), /* 3.3.1.1.2 */
+        RSL_MT_CHAN_CNF, gsm48_rr_tx_rand_acc},
+
+       {SBIT(GSM48_RR_ST_IDLE) |
+        SBIT(GSM48_RR_ST_CONN_PEND) |
+        SBIT(GSM48_RR_ST_REL_PEND),
+        RSL_MT_EST_CONF, gsm48_rr_estab_cnf},
+
 #if 0
        {SBIT(GSM48_RR_ST_DEDICATED),
-        RSL_MT_EST_CONF, "EST_CONF", gsm48_rr_estab_cnf_dedicated},
-       {SBIT(GSM_RRSTATE),
-        RSL_MT_CONNECT_CNF, "CONNECT_CNF", gsm48_rr_connect_cnf},
+        RSL_MT_EST_CONF, gsm48_rr_estab_cnf_dedicated},
+
        {SBIT(GSM_RRSTATE),
-        RSL_MT_RELEASE_IND, "REL_IND", gsm48_rr_rel_ind},
+        RSL_MT_CONNECT_CNF, gsm48_rr_connect_cnf},
+
 #endif
-       {SBIT(GSM48_RR_ST_IDLE) | SBIT(GSM48_RR_ST_CONN_PEND),
-        RSL_MT_REL_CONF, "REL_CONF", gsm48_rr_rel_cnf},
-#if 0
+
+       /* release */
+       {SBIT(GSM48_RR_ST_CONN_PEND) |
+        SBIT(GSM48_RR_ST_DEDICATED),
+        RSL_MT_REL_IND, gsm48_rr_rel_ind},
+
+       {SBIT(GSM48_RR_ST_REL_PEND),
+        RSL_MT_REL_CONF, gsm48_rr_rel_cnf},
+
+       /* suspenion */
        {SBIT(GSM48_RR_ST_DEDICATED),
-        RSL_MT_REL_CONF, "REL_CONF", gsm48_rr_rel_cnf_dedicated},
-#endif
-       {SBIT(GSM48_RR_ST_CONN_PEND), /* 3.3.1.1.2 */
-        RSL_MT_CHAN_CNF, "CHAN_CNF", gsm48_rr_tx_rand_acc},
+        RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated},
+
 #if 0
        {SBIT(GSM48_RR_ST_DEDICATED),
-        RSL_MT_CHAN_CNF, "CHAN_CNF", gsm48_rr_rand_acc_cnf_dedicated},
+        RSL_MT_CHAN_CNF, gsm48_rr_rand_acc_cnf_dedicated},
+
        {SBIT(GSM_RRSTATE),
-        RSL_MT_MDL_ERROR_IND, "MDL_ERROR_IND", gsm48_rr_mdl_error_ind},
+        RSL_MT_MDL_ERROR_IND, gsm48_rr_mdl_error_ind},
 #endif
 };
 
@@ -3169,27 +3802,27 @@ static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg)
        int i;
        int rc;
 
+       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),
+                       gsm48_rr_state_names[rr->state]);
+       }
+
        /* find function for current state and message */
        for (i = 0; i < DLDATASLLEN; i++)
                if ((msg_type == dldatastatelist[i].type)
                 && ((1 << rr->state) & dldatastatelist[i].states))
                        break;
        if (i == DLDATASLLEN) {
-               LOGP(DRSL, LOGL_NOTICE, "RSLms message 0x%02x unhandled at "
-               "state %s.\n", msg_type, gsm48_rr_state_names[rr->state]);
+               LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n");
                msgb_free(msg);
                return 0;
        }
-       LOGP(DRSL, LOGL_INFO, "(ms %s) Received 'RSL_MT_%s' from RSL in state "
-               "%s\n", ms->name, dldatastatelist[i].type_name,
-               gsm48_rr_state_names[rr->state]);
 
        rc = dldatastatelist[i].rout(ms, msg);
 
        /* free msgb unless it is forwarded */
        if (dldatastatelist[i].rout != gsm48_rr_data_ind)
-#warning HACK!!!!!!
-return rc;
                msgb_free(msg);
 
        return rc;
@@ -3201,13 +3834,18 @@ static struct rrdownstate {
        int             type;
        int             (*rout) (struct osmocom_ms *ms, struct msgb *msg);
 } rrdownstatelist[] = {
-       {SBIT(GSM48_RR_ST_IDLE), /* 3.3.1.1 */
+       /* NOTE: If not IDLE, it is rejected there. */
+       {ALL_STATES, /* 3.3.1.1 */
         GSM48_RR_EST_REQ, gsm48_rr_est_req},
+
        {SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */
         GSM48_RR_DATA_REQ, gsm48_rr_data_req},
-#if 0
-       {SBIT(GSM48_RR_ST_CONN_PEND) | SBIT(GSM48_RR_ST_DEDICATED),
+
+       {SBIT(GSM48_RR_ST_CONN_PEND) |
+        SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.13.3 */
         GSM48_RR_ABORT_REQ, gsm48_rr_abort_req},
+
+#if 0
        {SBIT(GSM48_RR_ST_DEDICATED),
         GSM48_RR_ACT_REQ, gsm48_rr_act_req},
 #endif
@@ -3288,6 +3926,8 @@ int gsm48_rr_exit(struct osmocom_ms *ms)
                rr->rr_est_msg = NULL;
        }
 
+       stop_rr_t_rel_wait(rr);
+       stop_rr_t3110(rr);
        stop_rr_t3122(rr);
        stop_rr_t3126(rr);
 
@@ -3320,42 +3960,13 @@ incomplete
 
 todo:
 
-add support structure
-initialize support structure
-
-queue messages (rslms_data_req) if channel changes
-
-flush rach msg in all cases: during sending, after its done, and when aborted
 stop timers on abort
-debugging. (wenn dies todo erledigt ist, bitte in den anderen code moven)
 wird beim abbruch immer der gepufferte cm-service-request entfernt, auch beim verschicken?:
 measurement reports
 todo rr_sync_ind when receiving ciph, re ass, channel mode modify
 
 todo change procedures, release procedure
 
-during procedures, like "channel assignment" or "handover", rr requests must be queued
-they must be dequeued when complete
-they queue must be flushed when rr fails
-
-#include <osmocore/protocol/gsm_04_08.h>
-#include <osmocore/msgb.h>
-#include <osmocore/utils.h>
-#include <osmocore/gsm48.h>
-
-static int gsm48_rr_abort_req(struct osmocom_ms *ms, struct gsm48_rr *rrmsg)
-{
-       struct gsm48_rrlayer *rr = ms->rrlayer;
-       stop_rr_t3126(rr);
-       if (rr->state == GSM48_RR_ST_DEDICATED) {
-               struct gsm_dl dlmsg;
-
-               memset(&dlmsg, 0, sizeof(dlmsg));
-               return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
-       }
-       new_rr_state(rr, GSM48_RR_ST_IDLE);
-}
-
 static int gsm48_rr_act_req(struct osmocom_ms *ms, struct gsm48_rr *rrmsg)
 {
 }
@@ -3386,133 +3997,6 @@ static int tlv_copy(void *dest, int dest_len, struct tlv_parsed *tp, uint8_t ie)
        return 0;
 }
 
-static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
-{
-       struct gsm48_rrlayer *rr = ms->rrlayer;
-       struct gsm48_hdr *gh = msgb_l3(msg);
-       struct gsm48_ass_cmd *ac = (struct gsm48_ass_cmd *)gh->data;
-       int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ac);
-       struct tlv_parsed tp;
-       struct gsm48_rr_chan_desc cd;
-       struct msgb *nmsg;
-
-       memset(&cd, 0, sizeof(cd));
-
-       if (payload_len < 0) {
-               LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND message.\n");
-               return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
-       }
-       tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0);
-
-       /* channel description */
-       memcpy(&cd.chan_desc, &ac->chan_desc, sizeof(chan_desc));
-       /* power command */
-       cd.power_command = ac->power_command;
-       /* frequency list, after timer */
-       tlv_copy(&cd.fl, sizeof(fl_after), &tp, GSM48_IE_FRQLIST_AFTER);
-       /* cell channel description */
-       tlv_copy(&cd.ccd, sizeof(ccd), &tp, GSM48_IE_CELL_CH_DESC);
-       /* multislot allocation */
-       tlv_copy(&cd.multia, sizeof(ma), &tp, GSM48_IE_MSLOT_DESC);
-       /* channel mode */
-       tlv_copy(&cd.chanmode, sizeof(chanmode), &tp, GSM48_IE_CHANMODE_1);
-       /* mobile allocation, after time */
-       tlv_copy(&cd.moba_after, sizeof(moba_after), &tp, GSM48_IE_MOB_AL_AFTER);
-       /* starting time */
-       tlv_copy(&cd.start, sizeof(start), &tp, GSM_IE_START_TIME);
-       /* frequency list, before time */
-       tlv_copy(&cd.fl_before, sizeof(fl_before), &tp, GSM48_IE_FRQLIST_BEFORE);
-       /* channel description, before time */
-       tlv_copy(&cd.chan_desc_before, sizeof(cd_before), &tp, GSM48_IE_CHDES_1_BEFORE);
-       /* frequency channel sequence, before time */
-       tlv_copy(&cd.fcs_before, sizeof(fcs_before), &tp, GSM48_IE_FRQSEQ_BEFORE);
-       /* mobile allocation, before time */
-       tlv_copy(&cd.moba_before, sizeof(moba_before), &tp, GSM48_IE_MOB_AL_BEFORE);
-       /* cipher mode setting */
-       if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET))
-               cd.cipher = *TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET);
-       else
-               cd.cipher = 0;
-
-       if (no CA) {
-               LOGP(DRR, LOGL_INFO, "No current cell allocation available.\n");
-               return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_NO_CELL_ALLOC_A);
-       }
-       
-       if (not supported) {
-               LOGP(DRR, LOGL_INFO, "New channel is not supported.\n");
-               return gsm48_rr_tx_rr_status(ms, RR_CAUSE_CHAN_MODE_UNACCEPT);
-       }
-
-       if (freq not supported) {
-               LOGP(DRR, LOGL_INFO, "New frequency is not supported.\n");
-               return gsm48_rr_tx_rr_status(ms, RR_CAUSE_FREQ_NOT_IMPLEMENTED);
-       }
-
-       /* store current channel descriptions, to return in case of failure */
-       memcpy(&rr->chan_last, &rr->chan_desc, sizeof(*cd));
-       /* copy new description */
-       memcpy(&rr->chan_desc, cd, sizeof(cd));
-
-       /* start suspension of current link */
-       nmsg = gsm48_l3_msgb_alloc();
-       if (!nmsg)
-               return -ENOMEM;
-       gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, msg);
-
-       /* change into special assignment suspension state */
-       rr->assign_susp_state = 1;
-       rr->resume_last_state = 0;
-
-       return 0;
-}
-
-/* decode "BA Range" (10.5.2.1a) */
-static int gsm48_decode_ba_range(uint8_t *ba, uint8_t, ba_len, uint32_t *range,
-       uint8_t *ranges, int max_ranges)
-{
-       /* ba = pointer to IE without IE type and length octets
-        * ba_len = number of octets
-        * range = pointer to store decoded range
-        * ranges = number of ranges decoded
-        * max_ranges = maximum number of decoded ranges that can be stored
-        */
-       uint16_t lower, higher;
-       int i, n, required_octets;
-       
-       /* find out how much ba ranges will be decoded */
-       n = *ba++;
-       ba_len --;
-       required_octets = 5 * (n >> 1) + 3 * (n & 1);
-       if (required_octets > n) {
-               *ranges = 0;
-               return -EINVAL;
-       }
-       if (max_ranges > n)
-               n = max_ranges;
-
-       /* decode ranges */
-       for (i = 0; i < n; i++) {
-               if (!(i & 1)) {
-                       /* decode even range number */
-                       lower = *ba++ << 2;
-                       lower |= (*ba >> 6);
-                       higher = (*ba++ & 0x3f) << 4;
-                       higher |= *ba >> 4;
-               } else {
-                       lower = (*ba++ & 0x0f) << 6;
-                       lower |= *ba >> 2;
-                       higher = (*ba++ & 0x03) << 8;
-                       higher |= *ba++;
-                       /* decode odd range number */
-               }
-               *range++ = (higher << 16) | lower;
-       }
-       *ranges = n;
-
-       return 0;
-}
-
 
 /* decode "Cell Description" (10.5.2.2) */
 static int gsm48_decode_cell_desc(struct gsm48_cell_desc *cd, uint16_t *arfcn, uint8_t *ncc uint8_t *bcc)
@@ -3546,7 +4030,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
        struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *)gh->data;
        int payload_len = msgb_l3len(msg) - sizeof(*gh) - wirklich sizeof(*ho);
        struct tlv_parsed tp;
-       struct gsm48_rr_chan_desc cd;
+       struct gsm48_rr_cd cd;
        struct msgb *nmsg;
 
        memset(&cd, 0, sizeof(cd));
@@ -3569,12 +4053,12 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
        /* Synchronization Indication */
        if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND))
                gsm48_decode_sync_ind(rr,
-                       TLVP_VAL(&tp, GSM48_IE_MOBILE_ALLOC)-1, &cd);
+                       TLVP_VAL(&tp, GSM48_IE_SYNC_IND)-1, &cd);
        /* Frequency Sort List */
        if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_SHORT_LIST))
                gsm48_decode_freq_list(&ms->support, s->freq,
-                       TLVP_VAL(&tp, GSM48_IE_MOBILE_ALLOC),
-                       *(TLVP_VAL(&tp, GSM48_IE_MOBILE_ALLOC)-1),
+                       TLVP_VAL(&tp, GSM48_IE_FREQ_SHORT_LIST),
+                       *(TLVP_VAL(&tp, GSM48_IE_FREQ_SHORT_LIST)-1),
                                0xce, FREQ_TYPE_SERV);
 
 
@@ -3605,7 +4089,7 @@ static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
                        rr->resume_last_state = 0;
                        gsm48_rr_tx_ass_cpl(ms, GSM48_RR_CAUSE_NORMAL);
                } else {
-                       gsm48_rr_tx_ass_fail(ms, RR_CAUSE_PROTO_ERR_UNSPEC);
+                       gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_PROTO_ERR_UNSPEC);
                }
                /* transmit queued frames during ho / ass transition */
                gsm48_rr_dequeue_down(ms);
@@ -3618,40 +4102,20 @@ static int gsm48_rr_connect_cnf(struct osmocom_ms *ms, struct msgbl *msg)
 {
 }
 
-static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg)
-{
-}
-
-static int gsm48_rr_rel_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
+static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm48_rrlayer *rr = ms->rrlayer;
        struct msgb *nmsg;
+       struct gsm_rr_hdr *nrrh;
 
-       if (rr->hando_susp_state || rr->assign_susp_state) {
-               struct msgb *msg;
-
-               /* change radio to new channel */
-               tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr);
+       printing of the cause
 
-               nmsg = gsm48_l3_msgb_alloc();
-               if (!nmsg)
-                       return -ENOMEM;
-               /* send DL-ESTABLISH REQUEST */
-               gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg);
-
-       }
-       if (rr->hando_susp_state) {
-               gsm48_rr_tx_hando_access(ms);
-               rr->hando_acc_left = 3;
+       switch (msg->l3h[0]) {
+       case RLL_CAUSE_SEQ_ERR:
+       case RLL_CAUSE_UNSOL_DM_RESP_MF:
+       einige muessen ignoriert werden
+       andere gelten als release
        }
-       return 0;
-}
-
-static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
-{
-       struct gsm48_rrlayer *rr = ms->rrlayer;
-       struct msgb *nmsg;
-       struct gsm_rr_hdr *nrrh;
 
        if (rr->hando_susp_state || rr->assign_susp_state) {
                if (!rr->resume_last_state) {
@@ -3662,13 +4126,13 @@ static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
 
                        /* change radio to old channel */
                        tx_ph_dm_est_req(ms, rr->cd_now.arfcn,
-                               rr->cd_now.chan_nr);
+                               rr->cd_now.chan_nr, rr->cd_now.tsc);
 
                        /* re-establish old link */
                        nmsg = gsm48_l3_msgb_alloc();
                        if (!nmsg)
                                return -ENOMEM;
-                       return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg);
+                       return gsm48_send_rsl(ms, RSL_MT_RECON_REQ, nmsg);
                }
                rr->resume_last_state = 0;
        }
@@ -3699,13 +4163,14 @@ static void timeout_rr_t3124(void *arg)
        memcpy(&rr->chan_desc, &rr->chan_last, sizeof(*cd));
 
        /* change radio to old channel */
-       tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr);
+       tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr,
+                        rr->cd_now.tsc);
 
        /* re-establish old link */
        nmsg = gsm48_l3_msgb_alloc();
        if (!nmsg)
                return -ENOMEM;
-       return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg);
+       return gsm48_send_rsl(ms, RSL_MT_REEST_REQ, nmsg);
 
        todo
 }