Work on various L3 processes.
[osmocom-bb.git] / src / host / gsm48-andreas / gsm48_rr.c
index ebac35c..bb11856 100644 (file)
  *
  */
 
+/* Very short description of some of the procedures:
+ *
+ * A radio ressource request causes sendig a channel request on RACH.
+ * After receiving of an immediate assignment the link will be establised.
+ * After the link is established, the dedicated mode is entered and confirmed.
+ *
+ * A Paging request also triggers the channel request as above...
+ * After the link is established, the dedicated mode is entered and indicated.
+ *
+ * During dedicated mode, messages are transferred.
+ *
+ * When an assignment command or a handover command is received, the current
+ * link is released. After release, the new channel is activated and the
+ * link is established again. After link is establised, pending messages from
+ * radio ressource are sent.
+ *
+ * When the assignment or handover fails, the old channel is activate and the
+ * link is established again. Also pending messages are sent.
+ *
+ */
+
 /*
  * state transition
  */
 
-char *rr_state_names[] = {
+const char *rr_state_names[] = {
        "IDLE",
        "CONN PEND",
        "DEDICATED",
@@ -54,6 +75,31 @@ static void new_rr_state(struct gsm_rrlayer *rr, int state)
        rr->state = state;
 }
 
+/*
+ * messages
+ */
+
+/* allocate GSM 04.08 radio ressource message (RR to L2) */
+static struct msgb *gsm48_rr_msgb_alloc(void)
+{
+       struct msgb *msg;
+
+       msg = msgb_alloc_headroom(GSM48_RR_ALLOC_SIZE, GSM48_RR_ALLOC_HEADROOM,
+               "GSM 04.08 RR");
+       if (!msg)
+               return NULL;
+
+       return msg;
+}
+
+/* queue message L2 -> RR */
+int gsm48_rr_upmsg(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm_rrlayer *rr = ms->rrlayer;
+
+       msgb_enqueue(&rr->up_queue, msg);
+}
+
 /*
  * timers handling
  */
@@ -108,7 +154,7 @@ static void timeout_rr_t3126(void *arg)
                mmh = (struct gsm_mm_hdr *)msg->data;
                mmh->msg_type RR_REL_IND;
                mmh->cause = GSM_MM_CAUSE_RA_FAILURE;
-               rr_rcvmsg(ms, msg);
+               gsm48_mm_upmsg(ms, msg);
        }
 
        new_rr_state(rr, GSM_RRSTATE_IDLE);
@@ -122,10 +168,11 @@ static void timeout_rr_t3126(void *arg)
 static int gsm_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause)
 {
        struct gsm_rrlayer *rr = ms->rrlayer;
-       struct msgb *msg = gsm48_rr_msgb_alloc();
+       struct msgb *msg;
        struct gsm48_hdr *gh;
        struct gsm48_rr_status *st;
 
+       msg = gsm48_rr_msgb_alloc();
        if (!msg)
                return -ENOMEM;
        gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
@@ -149,10 +196,11 @@ static int gsm_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr)
 {
        struct gsm_rrlayer *rr = ms->rrlayer;
        struct gsm_subscriber *subcr = ms->subscr;
-       struct msgb *msg = gsm48_rr_msgb_alloc();
+       struct msgb *msg;
        struct gsm48_hdr *gh;
        u_int8_t buf[11], *ie;
 
+       msg = gsm48_rr_msgb_alloc();
        if (!msg)
                return -ENOMEM;
        gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
@@ -206,152 +254,115 @@ static int gsm_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
  * classmark
  */
 
-/* encode classmark 3 */
-#define ADD_BIT(a) \
-{ \
-       if (bit-- == 0) { \
-               bit = 7; \
-               buf[byte] = 0; \
-       } \
-       buf[byte] |= ((a) << bit); \
-       if (bit == 0) \
-               byte++; \
-}
+/* Encode  "Classmark 3" (10.5.2.20) */
 static int gsm_rr_enc_cm3(struct osmocom_sm *ms, uint8_t *buf, uint8_t *len)
 {
        struct gsm_support *sup = ms->support;
-       uint8_t bit = 0, byte = 0;
+       struct bitvec bv;
+
+       memset(&bv, 0, sizeof(bv));
+       bv.data = data;
+       bv.data_len = 12;
 
        /* spare bit */
-       ADD_BIT(0)
+       bitvec_set_bit(&bv, 0);
        /* band 3 supported */
        if (sup->dcs_1800)
-               ADD_BIT(1)
+               bitvec_set_bit(&bv, ONE);
        else
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        /* band 2 supported */
        if (sup->e_gsm || sup->r_gsm)
-               ADD_BIT(1)
+               bitvec_set_bit(&bv, ONE);
        else
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        /* band 1 supported */
        if (sup->p_gsm && !(sup->e_gsm || sup->r_gsm))
-               ADD_BIT(1)
+               bitvec_set_bit(&bv, ONE);
        else
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        /* a5 bits */
        if (sup->a5_7)
-               ADD_BIT(1)
+               bitvec_set_bit(&bv, ONE);
        else
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        if (sup->a5_6)
-               ADD_BIT(1)
+               bitvec_set_bit(&bv, ONE);
        else
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        if (sup->a5_5)
-               ADD_BIT(1)
+               bitvec_set_bit(&bv, ONE);
        else
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        if (sup->a5_4)
-               ADD_BIT(1)
+               bitvec_set_bit(&bv, ONE);
        else
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        /* radio capability */
        if (sup->dcs_1800 && !sup->p_gsm && !(sup->e_gsm || sup->r_gsm)) {
                /* dcs only */
-               ADD_BIT(0)
-               ADD_BIT(0)
-               ADD_BIT(0)
-               ADD_BIT(0)
-               ADD_BIT((sup->dcs_capa >> 3) & 1)
-               ADD_BIT((sup->dcs_capa >> 2) & 1)
-               ADD_BIT((sup->dcs_capa >> 1) & 1)
-               ADD_BIT(sup->dcs_capa & 1)
+               bitvec_set_uint(&bv, 0, 4);
+               bitvec_set_uint(&bv, sup->dcs_capa, 4);
        } else
        if (sup->dcs_1800 && (sup->p_gsm || (sup->e_gsm || sup->r_gsm))) {
                /* dcs */
-               ADD_BIT((sup->dcs_capa >> 3) & 1)
-               ADD_BIT((sup->dcs_capa >> 2) & 1)
-               ADD_BIT((sup->dcs_capa >> 1) & 1)
-               ADD_BIT(sup->dcs_capa & 1)
+               bitvec_set_uint(&bv, sup->dcs_capa, 4);
                /* low band */
-               ADD_BIT((sup->low_capa >> 3) & 1)
-               ADD_BIT((sup->low_capa >> 2) & 1)
-               ADD_BIT((sup->low_capa >> 1) & 1)
-               ADD_BIT(sup->low_capa & 1)
+               bitvec_set_uint(&bv, sup->low_capa, 4);
        } else {
                /* low band only */
-               ADD_BIT(0)
-               ADD_BIT(0)
-               ADD_BIT(0)
-               ADD_BIT(0)
-               ADD_BIT((sup->low_capa >> 3) & 1)
-               ADD_BIT((sup->low_capa >> 2) & 1)
-               ADD_BIT((sup->low_capa >> 1) & 1)
-               ADD_BIT(sup->low_capa & 1)
+               bitvec_set_uint(&bv, 0, 4);
+               bitvec_set_uint(&bv, sup->low_capa, 4);
        }
        /* r support */
        if (sup->r_gsm) {
-               ADD_BIT(1)
-               ADD_BIT((sup->r_capa >> 2) & 1)
-               ADD_BIT((sup->r_capa >> 1) & 1)
-               ADD_BIT(sup->r_capa & 1)
+               bitvec_set_bit(&bv, ONE);
+               bitvec_set_uint(&bv, sup->r_capa, 3);
        } else {
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        }
        /* multi slot support */
        if (sup->ms_sup) {
-               ADD_BIT(1)
-               ADD_BIT((sup->ms_capa >> 4) & 1)
-               ADD_BIT((sup->ms_capa >> 3) & 1)
-               ADD_BIT((sup->ms_capa >> 2) & 1)
-               ADD_BIT((sup->ms_capa >> 1) & 1)
-               ADD_BIT(sup->ms_capa & 1)
+               bitvec_set_bit(&bv, ONE);
+               bitvec_set_uint(&bv, sup->ms_capa, 5);
        } else {
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        }
        /* ucs2 treatment */
        if (sup->ucs2_treat) {
-               ADD_BIT(1)
+               bitvec_set_bit(&bv, ONE);
        } else {
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        }
        /* support extended measurements */
        if (sup->ext_meas) {
-               ADD_BIT(1)
+               bitvec_set_bit(&bv, ONE);
        } else {
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        }
        /* support measurement capability */
        if (sup->meas_cap) {
-               ADD_BIT(1)
-               ADD_BIT((sup->sms_val >> 3) & 1)
-               ADD_BIT((sup->sms_val >> 2) & 1)
-               ADD_BIT((sup->sms_val >> 1) & 1)
-               ADD_BIT(sup->sms_val & 1)
-               ADD_BIT((sup->sm_val >> 3) & 1)
-               ADD_BIT((sup->sm_val >> 2) & 1)
-               ADD_BIT((sup->sm_val >> 1) & 1)
-               ADD_BIT(sup->sm_val & 1)
+               bitvec_set_bit(&bv, ONE);
+               bitvec_set_uint(&bv, sup->sms_val, 4);
+               bitvec_set_uint(&bv, sup->sm_val, 4);
        } else {
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        }
        /* positioning method capability */
        if (sup->loc_serv) {
-               ADD_BIT(1)
-               ADD_BIT(sup->e_otd_ass == 1)
-               ADD_BIT(sup->e_otd_based == 1)
-               ADD_BIT(sup->gps_ass == 1)
-               ADD_BIT(sup->gps_based == 1)
-               ADD_BIT(sup->gps_conv == 1)
+               bitvec_set_bit(&bv, ONE);
+               bitvec_set_bit(&bv, sup->e_otd_ass == 1);
+               bitvec_set_bit(&bv, sup->e_otd_based == 1);
+               bitvec_set_bit(&bv, sup->gps_ass == 1);
+               bitvec_set_bit(&bv, sup->gps_based == 1);
+               bitvec_set_bit(&bv, sup->gps_conv == 1);
        } else {
-               ADD_BIT(0)
+               bitvec_set_bit(&bv, ZERO);
        }
 
        /* partitial bytes will be completed */
-       if (bit)
-               byte++;
-       *len = byte;
+       *len = (bv.cur_bit + 7) >> 3;
+       bitvec_spare_padding(&bv, (*len * 8) - 1);
 
        return 0;
 }
@@ -383,12 +394,13 @@ static int gsm_rr_tx_cm_change(struct osmocom_ms *ms)
 {
        struct gsm_rrlayer *rr = ms->rrlayer;
        struct gsm_support *sup = ms->support;
-       struct msgb *msg = gsm48_rr_msgb_alloc();
+       struct msgb *msg;
        struct gsm48_hdr *gh;
        struct gsm48_cm_change *cc;
        int len;
        uint8_t buf[14];
 
+       msg = gsm48_rr_msgb_alloc();
        if (!msg)
                return -ENOMEM;
        gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
@@ -410,7 +422,7 @@ static int gsm_rr_tx_cm_change(struct osmocom_ms *ms)
         || sup->loc_serv) {
                cm->cm2.cm3 = 1;
                buf[0] = GSM48_IE_CLASSMARK2;
-               gsm_rr_enc_cm3(ms, buf + 1, &buf[0]);
+               gsm_rr_enc_cm3(ms, buf + 2, &buf[1]);
        }
 
        return rslms_data_req(ms, msg, 0);
@@ -542,7 +554,7 @@ static int gsm_rr_tx_chan_req(struct osmocom_ms *ms, int cause)
                mmh = (struct gsm_mm_hdr *)msg->data;
                mmh->msg_type RR_REL_IND;
                mmh->cause = GSM_MM_CAUSE_UNDEFINED;
-               rr_rcvmsg(ms, msg);
+               gsm48_mm_upmsg(ms, msg);
                new_rr_state(rr, GSM_RRSTATE_IDLE);
                return -EINVAL;
        }
@@ -570,7 +582,7 @@ static int gsm_rr_tx_chan_req(struct osmocom_ms *ms, int cause)
 static int gsm_rr_rand_acc_cnf(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm_rrlayer *rr = ms->rrlayer;
-       struct msgb *newmsg;
+       struct msgb *nmsg;
        int s;
 
        if (!rr->n_chan_req) {
@@ -609,1463 +621,1704 @@ static int gsm_rr_rand_acc_cnf(struct osmocom_ms *ms, struct msgb *msg)
                        s = 115;
 
        /* resend chan_req */
-       newmsg = msgb_alloc_headroom(20, 16, "CHAN_REQ");
-       if (!newmsg)
+       nmsg = msgb_alloc_headroom(20, 16, "CHAN_REQ");
+       if (!nmsg)
                return -ENOMEM;
-       *msgb_put(newmsg, 1) = rr->chan_req;
-       *msgb_put(newmsg, 1) = (random() % ms->si.tx_integer) + s; /* delay */
+       *msgb_put(nmsg, 1) = rr->chan_req;
+       *msgb_put(nmsg, 1) = (random() % ms->si.tx_integer) + s; /* delay */
        rr->cr_hist[3] = rr->cr_hist[2];
        rr->cr_hist[2] = rr->cr_hist[1];
        rr->cr_hist[1] = chan_req;
-       return rslms_tx_rll_req_l3(ms, RSL_MT_RAND_ACC_REQ, chan_nr, 0, newmsg);
+       return rslms_tx_rll_req_l3(ms, RSL_MT_RAND_ACC_REQ, chan_nr, 0, nmsg);
 }
 
 /*
- * paging
+ * system information
  */
 
-/* paging channel request */
-static int gsm_rr_chan2cause[4] = {
-       RR_EST_CAUSE_ANS_PAG_ANY,
-       RR_EST_CAUSE_ANS_PAG_SDCCH,
-       RR_EST_CAUSE_ANS_PAG_TCH_F,
-       RR_EST_CAUSE_ANS_PAG_TCH_ANY
-};
-
-/* given LV of mobile identity is checked agains ms */
-static int gsm_match_mi(struct osmocom_ms *ms, u_int8_t mi)
-{
-       char imsi[16];
-       u_int32_t tmsi;
-
-       if (mi[0] < 1)
-               return 0;
-       mi_type = mi[1] & GSM_MI_TYPE_MASK;
-       switch (mi_type) {
-       case GSM_MI_TYPE_TMSI:
-               if (mi[0] < 5)
-                       return;
-               memcpy(&tmsi, mi+2, 4);
-               if (ms->subscr.tmsi == ntohl(tmsi)
-                && ms->subscr.tmsi_valid)
-                       return 1;
-               break;
-       case GSM_MI_TYPE_IMSI:
-               gsm48_mi_to_string(imsi, sizeof(imsi), mi + 1, mi[0]);
-               if (!strcmp(imsi, ms->subscr.imsi))
-                       return 1;
-               break;
-       default:
-               DEBUGP(DRR, "paging with unsupported MI type %d.\n", mi_type);
-       }
-
-       return 0;
-}
-
-/* paging request 1 message */
-static int gsm_rr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg)
+/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
+static int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd, uint8_t len, uint8_t mask, uint8_t frqt)
 {
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct gsm48_rr_paging1 *pa = msgb_l3(msg);
-       int payload_len = msgb_l3len(msg) - sizeof(*pa);
-       int chan_first, chan_second;
-       uint8_t mi;
-
-       /* 3.3.1.1.2: ignore paging while establishing */
-       if (rr->state != GSM_RRSTATE_IDLE)
-               return 0;
-
-       if (payload_len < 2) {
-               short:
-               DEBUGP(DRR, "Short read of paging request 1 message .\n");
-               return -EINVAL;
-       }
+       int i;
 
-       /* channel needed */
-       chan_first = pa->cneed1;
-       chan_second = pa->cneed2;
-       /* first MI */
-       mi = pa->data + 1;
-       if (payload_len < mi[0] + 1)
-               goto short;
-       if (gsm_match_mi(ms, mi) > 0)
-               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_first]);
-       /* second MI */
-       payload_len -= mi[0] + 1;
-       mi = pa->data + mi[0] + 1;
-       if (payload_len < 2)
-               return 0;
-       if (mi[0] != GSM48_IE_MOBILE_ID)
-               return 0;
-       if (payload_len < mi[1] + 2)
-               goto short;
-       if (gsm_match_mi(ms, mi + 1) > 0)
-               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_second]);
+       /* NOTES:
+        *
+        * The Range format uses "SMOD" computation.
+        * e.g. "n SMOD m" equals "((n - 1) % m) + 1"
+        * A cascade of multiple SMOD computations is simpified:
+        * "(n SMOD m) SMOD o" equals "(((n - 1) % m) % o) + 1"
+        *
+        * The Range format uses 16 octets of data in SYSTEM INFORMATION.
+        * When used in dedicated messages, the length can be less.
+        * In this case the ranges are decoded for all frequencies that
+        * fit in the block of given length.
+        */
 
-       return 0;
-}
+       /* tabula rasa */
+       for (i = 0; i < 1024; i++)
+               f[i].mask &= ~frqt;
 
-/* paging request 2 message */
-static int gsm_rr_rx_pag_req_2(struct osmocom_ms *ms, struct gsm_msgb *msg)
-{
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct gsm48_rr_paging2 *pa = msgb_l3(msg);
-       int payload_len = msgb_l3len(msg) - sizeof(*pa);
-       u_int32_t tmsi;
-       int chan_first, chan_second, chan_third;
+       /* 00..XXX. */
+       if ((cd[0] & 0xc0 & mask) == 0x00) {
+               /* Bit map 0 format */
+               if (len < 16)
+                       return -EINVAL;
+               for (i = 1; i <= 124; i++)
+                       if ((cd[15 - ((i-1) >> 3)] & (1 << ((i-1) & 7))))
+                               f[i].mask |= frqt;
 
-       /* 3.3.1.1.2: ignore paging while establishing */
-       if (rr->state != GSM_RRSTATE_IDLE)
                return 0;
-
-       if (payload_len < 0) {
-               short:
-               DEBUGP(DRR, "Short read of paging request 2 message .\n");
-               return -EINVAL;
        }
 
-       /* channel needed */
-       chan_first = pa->cneed1;
-       chan_second = pa->cneed2;
-       /* first MI */
-       if (ms->subscr.tmsi == ntohl(pa->tmsi1)
-        && ms->subscr.tmsi_valid)
-               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_first]);
-       /* second MI */
-       if (ms->subscr.tmsi == ntohl(pa->tmsi2)
-        && ms->subscr.tmsi_valid)
-               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_second]);
-       /* third MI */
-       mi = pa->data;
-       if (payload_len < 2)
-               return 0;
-       if (mi[0] != GSM48_IE_MOBILE_ID)
-               return 0;
-       if (payload_len < mi[1] + 2 + 1) /* must include "channel needed" */
-               goto short;
-       chan_third = mi[mi[1] + 2] & 0x03; /* channel needed */
-       if (gsm_match_mi(ms, mi + 1) > 0)
-               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_third]);
+       /* only Bit map 0 format for P-GSM */
+       if (ms->support.p_gsm && !ms->support.e_gsm
+        && !ms->support.r_gsm && !ms->support.dcs_1800)
+               return 0;
 
-       return 0;
-}
+       /* 10..0XX. */
+       if ((cd[0] & 0xc8 & mask) == 0x80) {
+               /* Range 1024 format */
+               uint16_t w[17]; /* 1..16 */
+               struct gsm_range_1024 *r = (struct gsm_range_1024 *)cd;
 
-/* paging request 3 message */
-static int gsm_rr_rx_pag_req_3(struct osmocom_ms *ms, struct gsm_msgb *msg)
-{
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct gsm48_rr_paging3 *pa = msgb_l3(msg);
-       int payload_len = msgb_l3len(msg) - sizeof(*pa);
-       u_int32_t tmsi;
-       int chan_first, chan_second, chan_third, chan_fourth;
+               if (len < 2)
+                       return -EINVAL;
+               memset(w, 0, sizeof(w));
+               if (r->f0)
+                       f[0].mask |= frqt;
+               w[1] = (r->w1_hi << 8) | r->w1_lo;
+               if (len >= 4)
+                       w[2] = (r->w2_hi << 1) | r->w2_lo;
+               if (len >= 5)
+                       w[3] = (r->w3_hi << 2) | r->w3_lo;
+               if (len >= 6)
+                       w[4] = (r->w4_hi << 2) | r->w4_lo;
+               if (len >= 7)
+                       w[5] = (r->w5_hi << 2) | r->w5_lo;
+               if (len >= 8)
+                       w[6] = (r->w6_hi << 2) | r->w6_lo;
+               if (len >= 9)
+                       w[7] = (r->w7_hi << 2) | r->w7_lo;
+               if (len >= 10)
+                       w[8] = (r->w8_hi << 1) | r->w8_lo;
+               if (len >= 10)
+                       w[9] = r->w9;
+               if (len >= 11)
+                       w[10] = r->w10;
+               if (len >= 12)
+                       w[11] = (r->w11_hi << 6) | r->w11_lo;
+               if (len >= 13)
+                       w[12] = (r->w12_hi << 5) | r->w12_lo;
+               if (len >= 14)
+                       w[13] = (r->w13_hi << 4) | r->w13_lo;
+               if (len >= 15)
+                       w[14] = (r->w14_hi << 3) | r->w14_lo;
+               if (len >= 16)
+                       w[15] = (r->w15_hi << 2) | r->w15_lo;
+               if (len >= 16)
+                       w[16] = r->w16;
+               if (w[1])
+                       f[w[1]].mask |= frqt;
+               if (w[2])
+                       f[((w[1] - 512 + w[2] - 1) % 1023) + 1].mask |= frqt;
+               if (w[3])
+                       f[((w[1]       + w[3] - 1) % 1023) + 1].mask |= frqt;
+               if (w[4])
+                       f[((w[1] - 512 + ((w[2] - 256 + w[4] - 1) % 511)) % 1023) + 1].mask |= frqt;
+               if (w[5])
+                       f[((w[1]       + ((w[3] - 256 - w[5] - 1) % 511)) % 1023) + 1].mask |= frqt;
+               if (w[6])
+                       f[((w[1] - 512 + ((w[2]       + w[6] - 1) % 511)) % 1023) + 1].mask |= frqt;
+               if (w[7])
+                       f[((w[1]       + ((w[3]       + w[7] - 1) % 511)) % 1023) + 1].mask |= frqt;
+               if (w[8])
+                       f[((w[1] - 512 + ((w[2] - 256 + ((w[4] - 128 + w[8] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+               if (w[9])
+                       f[((w[1]       + ((w[3] - 256 + ((w[5] - 128 + w[9] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+               if (w[10])
+                       f[((w[1] - 512 + ((w[2]       + ((w[6] - 128 + w[10] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+               if (w[11])
+                       f[((w[1]       + ((w[3]       + ((w[7] - 128 + w[11] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+               if (w[12])
+                       f[((w[1] - 512 + ((w[2] - 256 + ((w[4]       + w[12] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+               if (w[13])
+                       f[((w[1]       + ((w[3] - 256 + ((w[5]       + w[13] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+               if (w[14])
+                       f[((w[1] - 512 + ((w[2]       + ((w[6]       + w[14] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+               if (w[15])
+                       f[((w[1]       + ((w[3]       + ((w[7]       + w[15] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+               if (w[16])
+                       f[((w[1] - 512 + ((w[2] - 256 + ((w[4] - 128 + ((w[8] - 64 + w[16] - 1) % 127)) % 255)) % 511)) % 1023) + 1].mask |= frqt;
 
-       /* 3.3.1.1.2: ignore paging while establishing */
-       if (rr->state != GSM_RRSTATE_IDLE)
                return 0;
-
-       if (payload_len < 0) { /* must include "channel needed", part of *pa */
-               short:
-               DEBUGP(DRR, "Short read of paging request 3 message .\n");
-               return -EINVAL;
        }
+       /* 10..100. */
+       if ((cd[0] & 0xce & mask) == 0x88) {
+               /* Range 512 format */
+               uint16_t w[18]; /* 1..17 */
+               struct gsm_range_512 *r = (struct gsm_range_512 *)cd;
 
-       /* channel needed */
-       chan_first = pa->cneed1;
-       chan_second = pa->cneed2;
-       chan_third = pa->cneed3;
-       chan_fourth = pa->cneed4;
-       /* first MI */
-       if (ms->subscr.tmsi == ntohl(pa->tmsi1)
-        && ms->subscr.tmsi_valid)
-               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_first]);
-       /* second MI */
-       if (ms->subscr.tmsi == ntohl(pa->tmsi2)
-        && ms->subscr.tmsi_valid)
-               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_second]);
-       /* thrid MI */
-       if (ms->subscr.tmsi == ntohl(pa->tmsi3)
-        && ms->subscr.tmsi_valid)
-               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_third]);
-       /* fourth MI */
-       if (ms->subscr.tmsi == ntohl(pa->tmsi4)
-        && ms->subscr.tmsi_valid)
-               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_fourth]);
-
-       return 0;
-}
+               if (len < 4)
+                       return -EINVAL;
+               memset(w, 0, sizeof(w));
+               w[0] = (r->orig_arfcn_hi << 9) || (r->orig_arfcn_mid << 1) || r->orig_arfcn_lo;
+               w[1] = (r->w1_hi << 2) || r->w1_lo;
+               if (len >= 5)
+                       w[2] = (r->w2_hi << 2) || r->w2_lo;
+               if (len >= 6)
+                       w[3] = (r->w3_hi << 2) || r->w3_lo;
+               if (len >= 7)
+                       w[4] = (r->w4_hi << 1) || r->w4_lo;
+               if (len >= 7)
+                       w[5] = r->w5;
+               if (len >= 8)
+                       w[6] = r->w6;
+               if (len >= 9)
+                       w[7] = (r->w7_hi << 6) || r->w7_lo;
+               if (len >= 10)
+                       w[8] = (r->w8_hi << 4) || r->w8_lo;
+               if (len >= 11)
+                       w[9] = (r->w9_hi << 2) || r->w9_lo;
+               if (len >= 11)
+                       w[10] = r->w10;
+               if (len >= 12)
+                       w[11] = r->w11;
+               if (len >= 13)
+                       w[12] = (r->w12_hi << 4) || r->w12_lo;
+               if (len >= 14)
+                       w[13] = (r->w13_hi << 2) || r->w13_lo;
+               if (len >= 14)
+                       w[14] = r->w14;
+               if (len >= 15)
+                       w[15] = r->w15;
+               if (len >= 16)
+                       w[16] = (r->w16_hi << 3) || r->w16_lo;
+               if (len >= 16)
+                       w[17] = r->w17;
+               if (w[0])
+                       f[w[0]].mask |= frqt;
+               if (w[1])
+                       f[(w[0] + w[1]) % 1024].mask |= frqt;
+               if (w[2])
+                       f[(w[0] + ((w[1] - 256 + w[2] - 1) % 511) + 1) % 1024].mask |= frqt;
+               if (w[3])
+                       f[(w[0] + ((w[1]       + w[3] - 1) % 511) + 1) % 1024].mask |= frqt;
+               if (w[4])
+                       f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + w[4] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[5])
+                       f[(w[0] + ((w[1]       + ((w[3] - 128 + w[5] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[6])
+                       f[(w[0] + ((w[1] - 256 + ((w[2]       + w[6] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[7])
+                       f[(w[0] + ((w[1]       + ((w[3]       + w[7] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[8])
+                       f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] - 64 + w[8] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[9])
+                       f[(w[0] + ((w[1]       + ((w[3] - 128 + ((w[5] - 64 + w[9] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[10])
+                       f[(w[0] + ((w[1] - 256 + ((w[2]       + ((w[6] - 64 + w[10] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[11])
+                       f[(w[0] + ((w[1]       + ((w[3]       + ((w[7] - 64 + w[11] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[12])
+                       f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4]      + w[12] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[13])
+                       f[(w[0] + ((w[1]       + ((w[3] - 128 + ((w[5]      + w[13] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[14])
+                       f[(w[0] + ((w[1] - 256 + ((w[2]       + ((w[6]      + w[14] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[15])
+                       f[(w[0] + ((w[1]       + ((w[3]       + ((w[7]      + w[15] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[16])
+                       f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] - 64 + ((w[8] - 32 + w[16] - 1) % 63)) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+               if (w[17])
+                       f[(w[0] + ((w[1]       + ((w[3] - 128 + ((w[5] - 64 + ((w[9] - 32 + w[17] - 1) % 63)) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
 
-/*
- * (immediate) assignment
- */
+               return 0;
+       }
+       /* 10..101. */
+       if ((cd[0] & & mask 0xce) == 0x8a) {
+               /* Range 256 format */
+               uint16_t w[22]; /* 1..21 */
+               struct gsm_range_256 *r = (struct gsm_range_256 *)cd;
 
-/* match request reference agains request history */
-static int gsm_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *req)
-{
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       int i;
+               if (len < 4)
+                       return -EINVAL;
+               memset(w, 0, sizeof(w));
+               w[0] = (r->orig_arfcn_hi << 9) || (r->orig_arfcn_mid << 1) || r->orig_arfcn_lo;
+               w[1] = (r->w1_hi << 1) || r->w1_lo;
+               if (len >= 4)
+                       w[2] = r->w2;
+               if (len >= 5)
+                       w[3] = r->w3;
+               if (len >= 6)
+                       w[4] = (r->w4_hi << 5) || r->w4_lo;
+               if (len >= 7)
+                       w[5] = (r->w5_hi << 3) || r->w5_lo;
+               if (len >= 8)
+                       w[6] = (r->w6_hi << 1) || r->w6_lo;
+               if (len >= 8)
+                       w[7] = r->w7;
+               if (len >= 9)
+                       w[8] = (r->w8_hi << 4) || r->w8_lo;
+               if (len >= 10)
+                       w[9] = (r->w9_hi << 1) || r->w9_lo;
+               if (len >= 10)
+                       w[10] = r->w10;
+               if (len >= 11)
+                       w[11] = (r->w11_hi << 3) || r->w11_lo;
+               if (len >= 11)
+                       w[12] = r->w12;
+               if (len >= 12)
+                       w[13] = r->w13;
+               if (len >= 13)
+                       w[14] = r->w15;
+               if (len >= 13)
+                       w[15] = (r->w14_hi << 2) || r->w14_lo;
+               if (len >= 14)
+                       w[16] = (r->w16_hi << 3) || r->w16_lo;
+               if (len >= 14)
+                       w[17] = r->w17;
+               if (len >= 15)
+                       w[18] = r->w19;
+               if (len >= 15)
+                       w[19] = (r->w18_hi << 3) || r->w18_lo;
+               if (len >= 16)
+                       w[20] = (r->w20_hi << 3) || r->w20_lo;
+               if (len >= 16)
+                       w[21] = r->w21;
+               if (w[0])
+                       f[w[0]].mask |= frqt;
+               if (w[1])
+                       f[(w[0] + w[1]) % 1024].mask |= frqt;
+               if (w[2])
+                       f[(w[0] + ((w[1] - 128 + w[2] - 1) % 255) + 1) % 1024].mask |= frqt;
+               if (w[3])
+                       f[(w[0] + ((w[1]       + w[3] - 1) % 255) + 1) % 1024].mask |= frqt;
+               if (w[4])
+                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + w[4] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[5])
+                       f[(w[0] + ((w[1]       + ((w[3] - 64 + w[5] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[6])
+                       f[(w[0] + ((w[1] - 128 + ((w[2]      + w[6] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[7])
+                       f[(w[0] + ((w[1]       + ((w[3]      + w[7] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[8])
+                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] - 32 + w[8] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[9])
+                       f[(w[0] + ((w[1]       + ((w[3] - 64 + ((w[5] - 32 + w[9] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[10])
+                       f[(w[0] + ((w[1] - 128 + ((w[2]      + ((w[6] - 32 + w[10] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[11])
+                       f[(w[0] + ((w[1]       + ((w[3]      + ((w[7] - 32 + w[11] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[12])
+                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4]      + w[12] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[13])
+                       f[(w[0] + ((w[1]       + ((w[3] - 64 + ((w[5]      + w[13] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[14])
+                       f[(w[0] + ((w[1] - 128 + ((w[2]      + ((w[6]      + w[14] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[15])
+                       f[(w[0] + ((w[1]       + ((w[3]      + ((w[7]      + w[15] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[16])
+                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] - 32 + ((w[8] - 16 + w[16] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[17])
+                       f[(w[0] + ((w[1]       + ((w[3] - 64 + ((w[5] - 32 + ((w[9] - 16 + w[17] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[18])
+                       f[(w[0] + ((w[1] - 128 + ((w[2]      + ((w[6] - 32 + ((w[10] - 16 + w[18] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[19])
+                       f[(w[0] + ((w[1]       + ((w[3]      + ((w[7] - 32 + ((w[11] - 16 + w[19] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[20])
+                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4]      + ((w[12] - 16 + w[20] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+               if (w[21])
+                       f[(w[0] + ((w[1]       + ((w[3] - 64 + ((w[5]      + ((w[13] - 16 + w[21] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
 
-       for (i = 0; i < 3; i++) {
-               if (rr->cr_hist[i] >= 0
-                && ref->ra == rr->cr_hist[i]) {
-                       // todo: match timeslot
-                       return 1;
-               }
+               return 0;
        }
+       /* 10..110. */
+       if ((cd[0] & 0xce & mask) == 0x8c) {
+               /* Range 128 format */
+               uint16_t w[29]; /* 1..28 */
+               struct gsm_range_128 *r = (struct gsm_range_128 *)cd;
 
-       return 0;
-}
-
-/* transmit assignment complete after establishing link */
-static int gsm_rr_tx_ass_cpl(struct osmocom_ms *ms, uint8_t cause)
-{
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct msgb *msg = gsm48_rr_msgb_alloc();
-       struct gsm48_hdr *gh;
-       struct gsm48_ass_cpl *ac;
+               if (len < 3)
+                       return -EINVAL;
+               memset(w, 0, sizeof(w));
+               w[0] = (r->orig_arfcn_hi << 9) || (r->orig_arfcn_mid << 1) || r->orig_arfcn_lo;
+               w[1] = r->w1;
+               if (len >= 4)
+                       w[2] = r->w2;
+               if (len >= 5)
+                       w[3] = (r->w3_hi << 4) || r->w3_lo;
+               if (len >= 6)
+                       w[4] = (r->w4_hi << 1) || r->w4_lo;
+               if (len >= 6)
+                       w[5] = r->w5;
+               if (len >= 7)
+                       w[6] = (r->w6_hi << 3) || r->w6_lo;
+               if (len >= 7)
+                       w[7] = r->w7;
+               if (len >= 8)
+                       w[8] = r->w8;
+               if (len >= 8)
+                       w[9] = r->w9;
+               if (len >= 9)
+                       w[10] = r->w10;
+               if (len >= 9)
+                       w[11] = r->w11;
+               if (len >= 10)
+                       w[12] = r->w12;
+               if (len >= 10)
+                       w[13] = r->w13;
+               if (len >= 11)
+                       w[14] = r->w14;
+               if (len >= 11)
+                       w[15] = r->w15;
+               if (len >= 12)
+                       w[16] = r->w16;
+               if (len >= 12)
+                       w[17] = r->w17;
+               if (len >= 13)
+                       w[18] = (r->w18_hi << 1) || r->w18_lo;
+               if (len >= 13)
+                       w[19] = r->w19;
+               if (len >= 13)
+                       w[20] = r->w20;
+               if (len >= 14)
+                       w[21] = (r->w21_hi << 2) || r->w21_lo;
+               if (len >= 14)
+                       w[22] = r->w22;
+               if (len >= 14)
+                       w[23] = r->w23;
+               if (len >= 15)
+                       w[24] = r->w24;
+               if (len >= 15)
+                       w[25] = r->w25;
+               if (len >= 16)
+                       w[26] = (r->w26_hi << 1) || r->w26_lo;
+               if (len >= 16)
+                       w[27] = r->w27;
+               if (len >= 16)
+                       w[28] = r->w28;
+               if (w[0])
+                       f[w[0]].mask |= frqt;
+               if (w[1])
+                       f[(w[0] + w[1]) % 1024].mask |= frqt;
+               if (w[2])
+                       f[(w[0] + ((w[1] - 64 + w[2] - 1) % 127) + 1) % 1024].mask |= frqt;
+               if (w[3])
+                       f[(w[0] + ((w[1]      + w[3] - 1) % 127) + 1) % 1024].mask |= frqt;
+               if (w[4])
+                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + w[4] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[5])
+                       f[(w[0] + ((w[1]      + ((w[3] - 32 + w[5] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[6])
+                       f[(w[0] + ((w[1] - 64 + ((w[2]      + w[6] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[7])
+                       f[(w[0] + ((w[1]      + ((w[3]      + w[7] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[8])
+                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + w[8] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[9])
+                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5] - 16 + w[9] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[10])
+                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6] - 16 + w[10] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[11])
+                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7] - 16 + w[11] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[12])
+                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4]      + w[12] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[13])
+                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5]      + w[13] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[14])
+                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6]      + w[14] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[15])
+                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7]      + w[15] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[16])
+                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + ((w[8] - 8 + w[16] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[17])
+                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5] - 16 + ((w[9] - 8 + w[17] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[18])
+                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6] - 16 + ((w[10] - 8 + w[18] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[19])
+                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7] - 16 + ((w[11] - 8 + w[19] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[20])
+                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4]      + ((w[12] - 8 + w[20] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[21])
+                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5]      + ((w[13] - 8 + w[21] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[22])
+                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6]      + ((w[14] - 8 + w[22] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[23])
+                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7]      + ((w[15] - 8 + w[23] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[24])
+                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + ((w[8]     + w[24] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[25])
+                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5] - 16 + ((w[9]     + w[25] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[26])
+                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6] - 16 + ((w[10]     + w[26] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[27])
+                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7] - 16 + ((w[11]     + w[27] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+               if (w[28])
+                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4]      + ((w[12]     + w[28] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
 
-       if (!msg)
-               return -ENOMEM;
-       gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
-       ac = (struct gsm48_ass_cpl *) msgb_put(msg, sizeof(*ac));
+               return 0;
+       }
+       /* 10..111. */
+       if ((cd[0] & 0xce & mask) == 0x8e) {
+               /* Variable bitmap format (can be any length >= 3) */
+               uint16_t orig = 0;
+               struct gsm_var_bit *r = (struct gsm_var_bit *)cd;
 
-       gh->proto = GSM48_PDISC_RR;
-       gh->msg_type = GSM48_MT_RR_ASS_COMPL;
+               if (len < 3)
+                       return -EINVAL;
+               orig = (r->orig_arfcn_hi << 9) || (r->orig_arfcn_mid << 1) || r->orig_arfcn_lo;
+               f[orig].mask |= frqt;
+               for (i = 1; 2 + (i >> 3) < len; i++)
+                       if ((cd[2 + (i >> 3)] & (0x80 >> (i & 7))))
+                               f[(orig + 1) % 1024].mask |= frqt;
 
-       /* RR_CAUSE */
-       ac->rr_cause = cause;
+               return 0;
+       }
 
-       return rslms_data_req(ms, msg, 0);
 }
 
-/* transmit failure to old link */
-static int gsm_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause)
+/* decode "Cell Options (BCCH)" (10.5.2.3) */
+static int gsm48_decode_cell_sel_param(struct gsm48_sysinfo *s, struct gsm48_cell_sel_par *cs)
 {
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct msgb *msg = gsm48_rr_msgb_alloc();
-       struct gsm48_hdr *gh;
-       struct gsm48_ass_fail *ac;
-
-       if (!msg)
-               return -ENOMEM;
-       gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
-       af = (struct gsm48_ass_fail *) msgb_put(msg, sizeof(*af));
-
-       gh->proto = GSM48_PDISC_RR;
-       gh->msg_type = GSM48_MT_RR_ASS_COMPL;
+       s->ms_txpwr_max_ccch = cs->ms_txpwr_max_ccch;
+       s->cell_resel_hyst_db = cs->cell_resel_hyst * 2;
+       s->rxlev_acc_min_db = cs->rxlev_acc_min - 110;
+       s->neci = cs->neci;
+       s->acs = cs->acs;
+}
 
-       /* RR_CAUSE */
-       af->rr_cause = cause;
+/* decode "Cell Options (BCCH)" (10.5.2.3) */
+static int gsm48_decode_cellopt_bcch(struct gsm48_sysinfo *s, struct gsm48_cell_options *co)
+{
+       s->bcch_radio_link_timeout = (co->radio_link_timeout + 1) * 4;
+       s->bcch_dtx = co->dtx;
+       s->bcch_pwrc = co->pwrc;
+}
 
-       return rslms_data_req(ms, msg, 0);
+/* decode "Cell Options (SACCH)" (10.5.2.3a) */
+static int gsm48_decode_cellopt_sacch(struct gsm48_sysinfo *s, struct gsm48_cell_options *co)
+{
+       s->sacch_radio_link_timeout = (co->radio_link_timeout + 1) * 4;
+       s->sacch_dtx = co->dtx;
+       s->sacch_pwrc = co->pwrc;
 }
 
-/* receive immediate assignment */
-static int gsm_rr_rx_imm_ass(struct osmocom_ms *ms, struct gsm_msgb *msg)
+/* decode "Cell Channel Description" (10.5.2.11) */
+static int gsm48_decode_ccd(struct gsm48_sysinfo *s, struct gsm48_control_channel_desc *cc)
 {
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct gsm48_imm_ass *ia = msgb_l3(msg);
-       int payload_len = msgb_l3len(msg) - sizeof(*ia);
+       s->ccch_conf = cc->ccch_conf;
+       s->bs_ag_blks_res = cc->bs_ag_blks_res;
+       s->att_allowed = cc->att;
+       s->pag_mf_periods = cc->bs_pa_mfrms + 2;
+       s->t3212 = cc->t3212 * 360; /* convert deci-hours to seconds */
+}
 
-       /* 3.3.1.1.2: ignore assignment while idle */
-       if (rr->state != GSM_RRSTATE_CONN_PEND || !rr->wait_assign)
-               return 0;
+/* decode "Mobile Allocation" (10.5.2.21) */
+static int gsm48_decode_mobile_alloc(struct gsm48_sysinfo *s, uint8_t *ma, uint8_t len)
+{
+       int i, j = 0;
+       uint16_t f[len << 3];
 
-       if (payload_len < 1 /* mobile allocation IE must be included */
-        || *gh->data + 1 > payload_len) { /* short read of IE */
-               DEBUGP(DRR, "Short read of immediate assignment message.\n");
-               return -EINVAL;
-       }
-       if (*gh->data > 8) {
-               DEBUGP(DRR, "moble allocation in immediate assignment too large.\n");
+       /* not more than 64 hopping indexes allowed in IE */
+       if (len > 8)
                return -EINVAL;
+
+       /* tabula rasa */
+       s->hopp_len = 0;
+       for (i = 0; i < 1024; i++)
+               s->freq[i] &= ~FREQ_TYPE_HOPP;
+
+       /* generating list of all frequencies (1..1023,0) */
+       for (i = 1; i <= 1024; i++) {
+               if ((s->freq[i & 1023] & FREQ_TYPE_SERV)) {
+                       f[j++] = i & 1023;
+                       if (j == (len << 3))
+                               break;
+               }
        }
 
-       /* request ref */
-       if (gsm_match_ra(ms, ia->req_ref)) {
-               /* channel description */
-               memset(&rr->chan_desc, 0, sizeof(cd));
-               memcpy(rr->chan_desc.chan_desc, ia->chan_desc, 3);
-               /* timing advance */
-               rr->timing_advance = ia->timing_advance;
-               /* mobile allocation */
-               memcpy(rr->mobile_alloc_lv, gh->data, *gh->data + 1);
-               rr->wait_assing = 0;
-               return gsm_rr_dl_est(ms);
+       /* fill hopping table with frequency index given by IE
+        * and set hopping type bits
+        */
+       for (i = 0, i < (len << 3), i++) {
+               /* if bit is set, this frequency index is used for hopping */
+               if ((ma[len - 1 - (i >> 3)] & (1 << (i & 7)))) {
+                       /* index higher than entries in list ? */
+                       if (i >= j) {
+                               DEBUGP(DRR, "Mobile Allocation hopping index "
+                                       "%d exceeds maximum number of cell "
+                                       "frequencies. (%d)\n", i + 1, j);
+                               break;
+                       }
+                       hopping[s->hopp_len++] = f[i];
+                       s->freq[f[i]] |= FREQ_TYPE_HOPP;
+               }
        }
 
        return 0;
 }
 
-/* receive immediate assignment extended */
-static int gsm_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct gsm_msgb *msg)
-{
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct gsm48_imm_ass_ext *ia = msgb_l3(msg);
-       int payload_len = msgb_l3len(msg) - sizeof(*ia);
-
-       /* 3.3.1.1.2: ignore assignment while idle */
-       if (rr->state != GSM_RRSTATE_CONN_PEND || !rr->wait_assign)
-               return 0;
+/* Rach Control decode tables */
+static uint8_t gsm48_max_retrans[4] = {
+       1, 2, 4, 7
+}
+static uint8_t gsm48_tx_integer[16] = {
+       3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 25, 32, 50
+}
 
-       if (payload_len < 1 /* mobile allocation IE must be included */
-        || *gh->data + 1 > payload_len) { /* short read of IE */
-               DEBUGP(DRR, "Short read of immediate assignment extended message.\n");
-               return -EINVAL;
-       }
-       if (*gh->data > 4) {
-               DEBUGP(DRR, "moble allocation in immediate assignment extended too large.\n");
-               return -EINVAL;
-       }
+/* decode "RACH Control Parameter" (10.5.2.29) */
+static int gsm48_decode_rach_ctl_param(struct gsm48_sysinfo *s, struct gsm48_rach_ctl *rc)
+{
+       int i;
 
-       /* request ref 1 */
-       if (gsm_match_ra(ms, ia->req_ref1)) {
-               /* channel description */
-               memset(&rr->chan_desc, 0, sizeof(cd));
-               memcpy(rr->chan_desc.chan_desc, ia->chan_desc1, 3);
-               /* timing advance */
-               rr->timing_advance = ia->timing_advance1;
-               /* mobile allocation */
-               memcpy(rr->mobile_alloc_lv, gh->data, *gh->data + 1);
-               rr->wait_assing = 0;
-               return gsm_rr_dl_est(ms);
-       }
-       /* request ref 1 */
-       if (gsm_match_ra(ms, ia->req_ref2)) {
-               /* channel description */
-               memset(&rr->chan_desc, 0, sizeof(cd));
-               memcpy(rr->chan_desc.chan_desc, ia->chan_desc2, 3);
-               /* timing advance */
-               rr->timing_advance = ia->timing_advance2;
-               /* mobile allocation */
-               memcpy(rr->mobile_alloc_lv, gh->data, *gh->data + 1);
-               rr->wait_assing = 0;
-               return gsm_rr_dl_est(ms);
-       }
+       s->reest_denied = rc->re;
+       s->cell_barred = rc->cell_barr;
+       s->tx_integer = gsm48_tx_integer[rc->tx_int];
+       s->max_retrans = gsm48_max_retrans[rc->max_retr];
+       for (i = 0, i <= 15, i++)
+               if ((rc->ac[1 - (i >> 3)] & (1 << (i & 7))))
+                       s->class_barr[i] = 1;
+               else
+                       s->class_barr[i] = 0;
 
        return 0;
 }
-
-/* receive immediate assignment reject */
-static int gsm_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct gsm_msgb *msg)
+static int gsm48_decode_rach_ctl_neigh(struct gsm48_sysinfo *s, struct gsm48_rach_ctl *rc)
 {
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct gsm48_imm_ass_rej *ia = msgb_l3(msg);
-       int payload_len = msgb_l3len(msg) - sizeof(*ia);
        int i;
-       struct gsm48_req_ref *req_ref;
-       uint8_t t3122_value;
 
-       /* 3.3.1.1.2: ignore assignment while idle */
-       if (rr->state != GSM_RRSTATE_CONN_PEND || !rr->wait_assign)
-               return 0;
+       s->nb_reest_denied = rc->re;
+       s->nb_cell_barred = rc->cell_barr;
+       s->nb_tx_integer = gsm48_tx_integer[rc->tx_int];
+       s->nb_max_retrans = gsm48_max_retrans[rc->max_retr];
+       for (i = 0, i <= 15, i++)
+               if ((rc->ac[1 - (i >> 3)] & (1 << (i & 7))))
+                       s->nb_class_barr[i] = 1;
+               else
+                       s->nb_class_barr[i] = 0;
 
-       if (payload_len < 0) {
-               short:
-               DEBUGP(DRR, "Short read of immediate assignment reject message.\n");
-               return -EINVAL;
-       }
+       return 0;
+}
 
-       for (i = 0; i < 4; i++) {
-               /* request reference */
-               req_ref = (struct gsm48_req_ref *)(((uint8_t *)&ia->req_ref1) + i * 4);
-               if (gsm_match_ra(ms, req_ref)) {
-                       /* wait indication */
-                       t3122_value = ((uint8_t *)&ia->wait_ind1) + i * 4;
-                       if (t3122_value)
-                               start_rr_t3122(rr, t3122_value, 0);
-                       /* start timer 3126 if not already */
-                       if (!timer_pending(rr->t3126))
-                               start_rr_t3126(rr, GSM_T3126_MS);
-                       /* stop assignmnet requests */
-                       rr->n_chan_req = 0;
+/* decode "SI 1 Rest Octets" (10.5.2.32) */
+static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, uint8_t *si, uint8_t len)
+{
+}
 
-                       /* wait until timer 3126 expires, then release
-                        * or wait for channel assignment */
-                       return 0;
-               }
-       }
+/* decode "SI 3 Rest Octets" (10.5.2.34) */
+static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s, uint8_t *si, uint8_t len)
+{
+}
 
-       return 0;
+/* decode "SI 4 Rest Octets" (10.5.2.35) */
+static int gsm48_decode_si4_rest(struct gsm48_sysinfo *s, uint8_t *si, uint8_t len)
+{
 }
 
-/* receive additional assignment */
-static int gsm_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg)
+/* decode "SI 6 Rest Octets" (10.5.2.35a) */
+static int gsm48_decode_si6_rest(struct gsm48_sysinfo *s, uint8_t *si, uint8_t len)
 {
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct gsm48_hdr *gh = msgb_l3(msg);
-       struct gsm48_add_ass *aa = (struct gsm48_add_ass *)gh->data;
-       int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*aa);
+}
+
+/* receive "SYSTEM INFORMATION 1" message (9.1.31) */
+static int gsm_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm48_system_information_type_1 *si = msgb_l3(msg);
+       struct gsm48_sysinfo *s = ms->sysinfo;
+       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct msgb *nmsg;
 
        if (payload_len < 0) {
-               DEBUGP(DRR, "Short read of ADDITIONAL ASSIGNMENT message.\n");
-               return gsm_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 1 message.\n");
+               return -EINVAL;
        }
-       tlv_parse(&tp, &rsl_att_tlvdef, aa->data, payload_len, 0, 0);
+       /* Cell Channel Description */
+       gsm48_decode_freq_list(s->freq, si->cell_channel_description,
+               sizeof(si->cell_channel_description), 0xce, FREQ_TYPE_SERV);
+       /* RACH Control Parameter */
+       gsm48_decode_rach_ctl_param(s, si->rach_control);
+       /* SI 1 Rest Octets */
+       if (payload_len)
+               gsm48_decode_si1_rest(si->rest_octets, payload_len);
+
+       si->si1 = 1;
+
+       nmsg = gsm58_msgb_alloc(GSM58_EVENT_SYSINFO);
+       if (!nmsg)
+               return -ENOMEM;
+       gsm322_sendmsg(ms, nmsg);
 
-       return gsm_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+       return 0;
 }
 
-/*
- * measturement reports
- */
-
-static int gsm_rr_tx_meas_rep(struct osmocom_ms *ms)
+/* receive "SYSTEM INFORMATION 2" message (9.1.32) */
+static int gsm_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg)
 {
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct gsm_rr_meas *meas = &rr->meas;
-       struct msgb *msg = gsm48_rr_msgb_alloc();
-       struct gsm48_hdr *gh;
-       struct gsm48_meas_res *mr;
+       struct gsm48_system_information_type_2 *si = msgb_l3(msg);
+       struct gsm48_sysinfo *s = ms->sysinfo;
+       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct msgb *nmsg;
 
-       if (!msg)
-               return -ENOMEM;
-       gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
-       mr = (struct gsm48_meas_res *) msgb_put(msg, sizeof(*mr));
+       if (payload_len < 0) {
+               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 2 message.\n");
+               return -EINVAL;
+       }
+       /* Neighbor Cell Description */
+       gsm48_decode_freq_list(s->freq, si->bcch_frequency_list,
+               sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2);
+       /* NCC Permitted */
+       s->nb_ncc_permitted = si->ncc_permitted;
+       /* RACH Control Parameter */
+       gsm48_decode_rach_ctl_neigh(s, si->rach_control);
 
-       gh->proto = GSM48_PDISC_RR;
-       gh->msg_type = GSM48_MT_RR_MEAS_RES;
+       si->si2 = 1;
 
-       /* measurement results */
-       mr->rxlev_full = meas->rxlev_full;
-       mr->rxlev_sub = meas->rxlev_sub;
-       mr->rxqual_full = meas->rxqual_full;
-       mr->rxqual_sub = meas->rxqual_sub;
-       mr->dtx = meas->dtx;
-       mr->ba = meas->ba;
-       mr->meas_valid = meas->meas_valid;
-       if (meas->ncell_na) {
-               /* no results for serving cells */
-               mr->no_n_hi = 1;
-               mr->no_n_lo = 3;
-       } else {
-               mr->no_n_hi = meas->count >> 2;
-               mr->no_n_lo = meas->count & 3;
-       }
-       rxlev_nc1 = meas->rxlev_nc[0];
-       rxlev_nc2_hi = meas->rxlev_nc[1] >> 1;
-       rxlev_nc2_lo = meas->rxlev_nc[1] & 1;
-       rxlev_nc3_hi = meas->rxlev_nc[2] >> 2;
-       rxlev_nc3_lo = meas->rxlev_nc[2] & 3;
-       rxlev_nc4_hi = meas->rxlev_nc[3] >> 3;
-       rxlev_nc4_lo = meas->rxlev_nc[3] & 7;
-       rxlev_nc5_hi = meas->rxlev_nc[4] >> 4;
-       rxlev_nc5_lo = meas->rxlev_nc[4] & 15;
-       rxlev_nc6_hi = meas->rxlev_nc[5] >> 5;
-       rxlev_nc6_lo = meas->rxlev_nc[5] & 31;
-       bsic_nc1_hi = meas->bsic_nc[0] >> 3;
-       bsic_nc1_lo = meas->bsic_nc[0] & 7;
-       bsic_nc2_hi = meas->bsic_nc[1] >> 4;
-       bsic_nc2_lo = meas->bsic_nc[1] & 15;
-       bsic_nc3_hi = meas->bsic_nc[2] >> 5;
-       bsic_nc3_lo = meas->bsic_nc[2] & 31;
-       bsic_nc4 = meas->bsic_nc[3];
-       bsic_nc5 = meas->bsic_nc[4];
-       bsic_nc6 = meas->bsic_nc[5];
-       bcch_f_nc1 = meas->bcch_f_nc[0];
-       bcch_f_nc2 = meas->bcch_f_nc[1];
-       bcch_f_nc3 = meas->bcch_f_nc[2];
-       bcch_f_nc4 = meas->bcch_f_nc[3];
-       bcch_f_nc5_hi = meas->bcch_f_nc[4] >> 1;
-       bcch_f_nc5_lo = meas->bcch_f_nc[4] & 1;
-       bcch_f_nc6_hi = meas->bcch_f_nc[5] >> 2;
-       bcch_f_nc6_lo = meas->bcch_f_nc[5] & 3;
+       nmsg = gsm58_msgb_alloc(GSM58_EVENT_SYSINFO);
+       if (!nmsg)
+               return -ENOMEM;
+       gsm322_sendmsg(ms, nmsg);
 
-       //todo return rslms_data_req(ms, msg, 0);
+       return 0;
 }
 
-/*
- * link establishment and release
- */
-
-/* activate link and send establish request */
-static int gsm_rr_dl_est(struct osmocom_ms *ms)
+/* receive "SYSTEM INFORMATION 2bis" message (9.1.33) */
+static int gsm_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg)
 {
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct gsm_subscriber *subcr = ms->subscr;
-       struct msgb *msg;
-       struct gsm48_hdr *gh;
-       struct gsm48_pag_rsp *pa;
+       struct gsm48_system_information_type_2bis *si = msgb_l3(msg);
+       struct gsm48_sysinfo *s = ms->sysinfo;
+       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct msgb *nmsg;
 
-       /* 3.3.1.1.3.1 */
-       stop_rr_t3126(rr);
+       if (payload_len < 0) {
+               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 2bis message.\n");
+               return -EINVAL;
+       }
+       /* Neighbor Cell Description */
+       s->nb_ext_ind = (si->bcch_frequency_list[0] >> 6) & 1;
+       s->nb_ba_ind = (si->bcch_frequency_list[0] >> 5) & 1;
+       gsm48_decode_freq_list(s->freq, si->ext_bcch_frequency_list,
+               sizeof(si->ext_bcch_frequency_list), 0x8e, FREQ_TYPE_NCELL_2bis);
+       /* RACH Control Parameter */
+       gsm48_decode_rach_ctl_neigh(s, si->rach_control);
 
-       /* flush pending RACH requests */
-       rr->n_chan_req = 0; // just to be safe
-       msg = msgb_alloc_headroom(20, 16, "RAND_FLUSH");
-       if (!msg)
+       si->si2bis = 1;
+
+       nmsg = gsm58_msgb_alloc(GSM58_EVENT_SYSINFO);
+       if (!nmsg)
                return -ENOMEM;
-       rslms_tx_rll_req_l3(ms, RSL_MT_RAND_ACC_FLSH, chan_nr, 0, msg);
+       gsm322_sendmsg(ms, nmsg);
 
-       /* send DL_EST_REQ */
-       if (rr->rr_est_msg) {
-               /* use queued message */
-               msg = rr->rr_est_msg;
-               rr->rr_est_msg = 0;
-       } else {
-               uint8_t mi[11];
+       return 0;
+}
 
-               /* create paging response */
-               msg = gsm48_rr_msgb_alloc();
-               if (!msg)
-                       return -ENOMEM;
-               gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
-               pr = (struct gsm48_pag_rsp *) msgb_put(msg, sizeof(*pr));
-               /* key sequence */
-               if (subscr->key_valid)
-                       pr->key_seq = subscr->key_seq;
-               else
-                       pr->key_seq = 7;
-               /* classmark 2 */
-               cc->cm_len = sizeof(cm->cm2);
-               gsm_rr_enc_cm2(ms, &cc->cm2)
-               /* mobile identity */
-               if (ms->subscr.tmsi_valid) {
-                       gsm48_generate_mid_from_tmsi(mi, subscr->tmsi);
-               } else if (subscr->imsi[0])
-                       gsm48_generate_mid_from_imsi(mi, subscr->imsi);
-               else {
-                       mi[1] = 1;
-                       mi[2] = 0xf0 | GSM_MI_TYPE_NONE;
-               }
-               msgb_put(msg, 1 + mi[1]);
-               memcpy(cm->data, mi + 1, 1 + mi[1]);
+/* receive "SYSTEM INFORMATION 2ter" message (9.1.34) */
+static int gsm_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm48_system_information_type_2ter *si = msgb_l3(msg);
+       struct gsm48_sysinfo *s = ms->sysinfo;
+       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct msgb *nmsg;
+
+       if (payload_len < 0) {
+               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 2ter message.\n");
+               return -EINVAL;
        }
+       /* Neighbor Cell Description 2 */
+       s->nb_multi_rep = (si->bcch_frequency_list[0] >> 6) & 3;
+       gsm48_decode_freq_list(s->freq, si->ext_bcch_frequency_list,
+               sizeof(si->ext_bcch_frequency_list), 0x8e, FREQ_TYPE_NCELL_2ter);
 
-       /* activate channel */
-       tx_ph_dm_est_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
+       si->si2ter = 1;
 
-       /* start establishmnet */
-       return rslms_tx_rll_req_l3(ms, RSL_MT_EST_REQ, rr->chan_desc.chan_desc.chan_nr, 0, msg);
+       nmsg = gsm58_msgb_alloc(GSM58_EVENT_SYSINFO);
+       if (!nmsg)
+               return -ENOMEM;
+       gsm322_sendmsg(ms, nmsg);
+
+       return 0;
 }
 
-/* the link is established */
-static int gsm_rr_estab_cnf(struct osmocom_ms *ms, struct msgb *msg)
+/* receive "SYSTEM INFORMATION 3" message (9.1.35) */
+static int gsm_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg)
 {
-       struct msgb *newmsg;
-       struct gsm_mm_hdr *newmmh;
+       struct gsm48_system_information_type_3 *si = msgb_l3(msg);
+       struct gsm48_sysinfo *s = ms->sysinfo;
+       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct msgb *nmsg;
 
-       /* if MM has releases before confirm, we start release */
-       if (rr->state == GSM_RRSTATE_IDLE) {
-               /* release message */
-               newmsg = gsm48_rr_msgb_alloc();
-               if (!newmsg)
-                       return -ENOMEM;
-               /* start release */
-               return rslms_tx_rll_req_l3(ms, RSL_MT_REL_REQ, 0, 0, newmsg);
+       if (payload_len < 0) {
+               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 3 message.\n");
+               return -EINVAL;
        }
+       /* Cell Identity */
+       s->cell_identity = ntohl(si->cell_identity);
+       /* LAI */
+       gsm48_decode_lai(si->lai, &s->mcc, &s->mnc, &s->lac);
+       /* Control Channel Description */
+       gsm48_decode_ccd(s, si->control_channel_desc);
+       /* Cell Options (BCCH) */
+       gsm48_decode_cellopt_bcch(s, si->control_channel_desc);
+       /* Cell Selection Parameters */
+       gsm48_decode_cell_sel_param(s, si->cell_sel_par);
+       /* RACH Control Parameter */
+       gsm48_decode_rach_ctl_param(s, si->rach_control);
+       /* SI 3 Rest Octets */
+       if (payload_len >= 4)
+               gsm48_decode_si3_rest(si->rest_octets, payload_len);
 
-       /* 3.3.1.1.4 */
-       new_rr_state(rr, GSM_RRSTATE_DEDICATED);
+       si->si3 = 1;
 
-       /* send confirm to upper layer */
-       newmsg = gsm48_mm_msgb_alloc();
-       if (!newmsg)
+       nmsg = gsm58_msgb_alloc(GSM58_EVENT_SYSINFO);
+       if (!nmsg)
                return -ENOMEM;
-       newmmh = (struct gsm_mm_hdr *)newmsg->data;
-       newmmh->msg_type = (rr->rr_est_req) ? RR_EST_CNF : RR_EST_IND;
-       return rr_rcvmsg(ms, newmsg);
+       gsm322_sendmsg(ms, nmsg);
+
+       return 0;
 }
 
-/* the link is released */
-static int gsm_rr_rel_cnf(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+/* receive "SYSTEM INFORMATION 4" message (9.1.36) */
+static int gsm_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
 {
-       /* deactivate channel */
-       tx_ph_dm_rel_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
+       struct gsm48_system_information_type_4 *si = msgb_l3(msg);
+       struct gsm48_sysinfo *s = ms->sysinfo;
+       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       uint8_t *data = si->data;
+       struct msgb *nmsg;
+todo: si has different header in structures
 
-       /* do nothing, because we aleady IDLE
-        * or we received the rel cnf of the last connection
-        * while already requesting a new one (CONN PEND)
-        */
+       if (payload_len < 0) {
+               short_read:
+               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 4 message.\n");
+               return -EINVAL;
+       }
+       /* LAI */
+       gsm48_decode_lai(si->lai, &s->mcc, &s->mnc, &s->lac);
+       /* Cell Selection Parameters */
+       gsm48_decode_cell_sel_param(s, si->cell_sel_par);
+       /* RACH Control Parameter */
+       gsm48_decode_rach_ctl_param(s, si->rach_control);
+       /* CBCH Channel Description */
+       if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_CHAN_DES) {
+               if (payload_len < 4)
+                       goto short_read;
+               memcpy(&s->chan_desc, data + 1, sizeof(s->chan_desc));
+               payload_len -= 4;
+               data += 4;
+       }
+       /* CBCH Mobile Allocation */
+       if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_MOB_ALLOC) {
+               if (payload_len < 1 || payload_len < 2 + data[1])
+                       goto short_read;
+               gsm48_decode_mobile_alloc(&s, data + 2, si->data[1]);
+               payload_len -= 2 + data[1];
+               data += 2 + data[1];
+       }
+       /* SI 4 Rest Octets */
+       if (payload_len > 0)
+               gsm48_decode_si3_rest(data, payload_len);
+
+       si->si4 = 1;
+
+       nmsg = gsm58_msgb_alloc(GSM58_EVENT_SYSINFO);
+       if (!nmsg)
+               return -ENOMEM;
+       gsm322_sendmsg(ms, nmsg);
 
        return 0;
 }
 
-/*
- * radio ressource requests 
- */
-
-/* establish request for dedicated mode */
-static int gsm_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
+/* receive "SYSTEM INFORMATION 5" message (9.1.37) */
+static int gsm_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg)
 {
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct gsm_mm_hdr *mmh = msgb->data;
-       struct gsm48_hdr *gh = msgb_l3(msg);
-
-       /* 3.3.1.1.3.2 */
-       if (timer_pending(rr->t3122)) {
-               if (rrmsg->cause != RR_EST_CAUSE_EMERGENCY) {
-                       struct msgb *newmsg;
-                       struct gsm_mm_hdr *newmmh;
+       struct gsm48_system_information_type_5 *si = msgb_l3(msg);
+       struct gsm48_sysinfo *s = ms->sysinfo;
+       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct msgb *nmsg;
 
-                       newmsg = gsm48_mm_msgb_alloc();
-                       if (!newmsg)
-                               return -ENOMEM;
-                       newmmh = (struct gsm_mm_hdr *)newmsg->data;
-                       newmmh->msg_type RR_REL_IND;
-                       newmmh->cause = GSM_MM_CAUSE_T3122_PEND;
-                       return rr_rcvmsg(ms, newmsg);
-               } else
-                       stop_rr_t3122(rr);
+       if (payload_len < 0) {
+               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 5 message.\n");
+               return -EINVAL;
        }
+       /* Neighbor Cell Description */
+       gsm48_decode_freq_list(s->freq, si->bcch_frequency_list,
+               sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5);
 
-       /* 3.3.1.1.1 */
-       if (rrmsg->cause != RR_EST_CAUSE_EMERGENCY) {
-               if (!(ms->access_class & ms->si.access_class)) {
-                       reject:
-                       if (!ms->opt.access_class_override) {
-                               struct msgb *newmsg;
-                               struct gsm_mm_hdr *newmmh;
+       si->si5 = 1;
 
-                               newmsg = gsm48_mm_msgb_alloc();
-                               if (!newmsg)
-                                       return -ENOMEM;
-                               newmmh = (struct gsm_mm_hdr *)newmsg->data;
-                               newmmh->msg_type RR_REL_IND;
-                               newmmh->cause = GSM_MM_CAUSE_NOT_AUTHORIZED;
-                               return rr_rcvmsg(ms, newmsg);
-                       }
-               }
-       } else {
-               if (!(ms->access_class & ms->si.access_class)
-                && !ms->si.emergency)
-                       goto reject;
-       }
+       nmsg = gsm58_msgb_alloc(GSM58_EVENT_SYSINFO);
+       if (!nmsg)
+               return -ENOMEM;
+       gsm322_sendmsg(ms, nmsg);
 
-       /* requested by RR */
-       rr->rr_est_req = 1;
+       return 0;
+}
+
+/* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */
+static int gsm_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm48_system_information_type_5bis *si = msgb_l3(msg);
+       struct gsm48_sysinfo *s = ms->sysinfo;
+       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct msgb *nmsg;
 
-       /* clone and store REQUEST message */
-       if (!gh) {
-               printf("Error, missing l3 message\n");
+       if (payload_len < 0) {
+               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 5bis message.\n");
                return -EINVAL;
        }
-       rr->rr_est_msg = msgb_alloc_headroom(256, 16, "EST_REQ");
-       if (!rr->rr_est_msg)
+       /* Neighbor Cell Description */
+       gsm48_decode_freq_list(s->freq, si->bcch_frequency_list,
+               sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5bis);
+
+       si->si5bis = 1;
+
+       nmsg = gsm58_msgb_alloc(GSM58_EVENT_SYSINFO);
+       if (!nmsg)
                return -ENOMEM;
-       memcpy(msgb_put(rr_est_msg, msgb_l3len(msg)),
-               msgb_l3(msg), msgb_l3len(msg));
+       gsm322_sendmsg(ms, nmsg);
 
-       /* request channel */
-       return gsm_rr_tx_chan_req(ms, mmh->cause);
+       return 0;
 }
 
-/* send all queued messages down to layer 2 */
-static int gsm_rr_dequeue_down(struct osmocom_ms *ms)
+/* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */
+static int gsm_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg)
 {
-       struct gsm_rrlayer *rr = ms->rrlayer;
-       struct msgb *msg;
+       struct gsm48_system_information_type_5ter *si = msgb_l3(msg);
+       struct gsm48_sysinfo *s = ms->sysinfo;
+       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct msgb *nmsg;
 
-       while((msg = msgb_dequeue(&rr->downqueue))) {
-               rslms_tx_rll_req_l3(ms, RSL_MT_DATA_REQ, chan_nr, 0, msg);
+       if (payload_len < 0) {
+               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 5ter message.\n");
+               return -EINVAL;
        }
+       /* Neighbor Cell Description */
+       gsm48_decode_freq_list(s->freq, si->bcch_frequency_list,
+               sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5ter);
+
+       si->si5ter = 1;
+
+       nmsg = gsm58_msgb_alloc(GSM58_EVENT_SYSINFO);
+       if (!nmsg)
+               return -ENOMEM;
+       gsm322_sendmsg(ms, nmsg);
 
        return 0;
 }
 
-/* 3.4.2 transfer data in dedicated mode */
-static int gsm_rr_data_req(struct osmocom_ms *ms, struct msgb *msg)
+/* receive "SYSTEM INFORMATION 6" message (9.1.39) */
+static int gsm_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
 {
-       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct gsm48_system_information_type_6 *si = msgb_l3(msg);
+       struct gsm48_sysinfo *s = ms->sysinfo;
+       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct msgb *nmsg;
 
-       if (rr->state != GSM_RRSTATE_DEDICATED) {
-               msgb_free(msg)
+       if (payload_len < 0) {
+               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 6 message.\n");
                return -EINVAL;
        }
-       
-       /* pull header */
-       msgb_pull(msg, sizeof(struct gsm_mm_hdr));
+       /* Cell Identity */
+       s->cell_identity = ntohl(si->cell_identity);
+       /* LAI */
+       gsm48_decode_lai(si->lai, &s->mcc, &s->mnc, &s->lac);
+       /* Cell Options (SACCH) */
+       gsm48_decode_cellopt_sacch(s, si->control_channel_desc);
+       /* NCC Permitted */
+       s->nb_ncc_permitted = si->ncc_permitted;
+       /* SI 6 Rest Octets */
+       if (payload_len >= 4)
+               gsm48_decode_si6_rest(si->rest_octets, payload_len);
 
-       /* queue message, during handover or assignment procedure */
-       if (rr->hando_susp_state || rr->assign_susp_state) {
-               msgb_enqueue(&rr->downqueue, msg);
-               return 0;
-       }
+       si->si6 = 1;
 
-       /* forward message */
-       return rslms_tx_rll_req_l3(ms, RSL_MT_DATA_REQ, chan_nr, 0, msg);
+       nmsg = gsm58_msgb_alloc(GSM58_EVENT_SYSINFO);
+       if (!nmsg)
+               return -ENOMEM;
+       gsm322_sendmsg(ms, nmsg);
+
+       return 0;
 }
 
 /*
- * data indications from data link
+ * paging
  */
 
-/* 3.4.2 data from layer 2 to RR and upper layer*/
-static int gsm_rr_data_ind(struct osmocom_ms *ms, struct msbg *msg)
-{
-       struct gsm48_hdr *gh = msgb_l3(msg);
-       u_int8_t pdisc = gh->proto_discr & 0x0f;
-
-       if (pdisc == GSM48_PDISC_RR) {
-               int rc = -EINVAL;
+/* paging channel request */
+static int gsm_rr_chan2cause[4] = {
+       RR_EST_CAUSE_ANS_PAG_ANY,
+       RR_EST_CAUSE_ANS_PAG_SDCCH,
+       RR_EST_CAUSE_ANS_PAG_TCH_F,
+       RR_EST_CAUSE_ANS_PAG_TCH_ANY
+};
 
-               switch(gh->msg_type) {
-               case GSM48_MT_RR_ADD_ASS:
-                       rc = gsm_rr_rx_add_ass(ms, msg);
-                       break;
-               case GSM48_MT_RR_ASS_CMD:
-                       rc = gsm_rr_rx_ass_cmd(ms, msg);
-                       break;
-               case GSM48_MT_RR_CIP_MODE_CMD:
-                       rc = gsm_rr_rx_cip_mode_cmd(ms, msg);
-                       break;
-               case GSM48_MT_RR_CLSM_ENQ:
-                       rc = gsm_rr_rx_cm_enq(ms, msg);
-                       break;
-               case GSM48_MT_RR_HANDO_CMD:
-                       rc = gsm_rr_rx_hando_cmd(ms, msg);
-                       break;
-               case GSM48_MT_RR_FREQ_REDEF:
-                       rc = gsm_rr_rx_freq_redef(ms, msg);
-                       break;
-               default:
-                       DEBUGP(DRR, "Message type 0x%02x unknown.\n", gh->msg_type);
-               }
+/* given LV of mobile identity is checked agains ms */
+static int gsm_match_mi(struct osmocom_ms *ms, u_int8_t mi)
+{
+       char imsi[16];
+       uint32_t tmsi;
 
-               free_msgb(msg);
-               return rc;
+       if (mi[0] < 1)
+               return 0;
+       mi_type = mi[1] & GSM_MI_TYPE_MASK;
+       switch (mi_type) {
+       case GSM_MI_TYPE_TMSI:
+               if (mi[0] < 5)
+                       return;
+               memcpy(&tmsi, mi+2, 4);
+               if (ms->subscr.tmsi == ntohl(tmsi)
+                && ms->subscr.tmsi_valid)
+                       return 1;
+               break;
+       case GSM_MI_TYPE_IMSI:
+               gsm48_mi_to_string(imsi, sizeof(imsi), mi + 1, mi[0]);
+               if (!strcmp(imsi, ms->subscr.imsi))
+                       return 1;
+               break;
+       default:
+               DEBUGP(DRR, "paging with unsupported MI type %d.\n", mi_type);
        }
 
-       /* push header */
-       msgb_push(msg, sizeof(struct gsm_mm_hdr));
-       mmh = (struct gsm_mm_hdr *)msg->data;
-       mmh->msg_type = RR_DATA_IND;
-       /* forward message */
-       return rr_rcvmsg(ms, msg);
+       return 0;
 }
 
-/* unit data from layer 2 to RR layer */
-static int gsm_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
+/* paging request 1 message */
+static int gsm_rr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg)
 {
-       struct gsm48_hdr *gh = msgb_l3(msg);
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct gsm48_rr_paging1 *pa = msgb_l3(msg);
+       int payload_len = msgb_l3len(msg) - sizeof(*pa);
+       int chan_first, chan_second;
+       uint8_t mi;
 
-       switch (gh->msg_type) {
-       case GSM48_MT_RR_PAG_REQ_1:
-               return gsm_rr_rx_pag_req_1(ms, dlmsg->msg);
-       case GSM48_MT_RR_PAG_REQ_2:
-               return gsm_rr_rx_pag_req_2(ms, dlmsg->msg);
-       case GSM48_MT_RR_PAG_REQ_3:
-               return gsm_rr_rx_pag_req_3(ms, dlmsg->msg);
-       case GSM48_MT_RR_IMM_ASS:
-               return gsm_rr_rx_imm_ass(ms, dlmsg->msg);
-       case GSM48_MT_RR_IMM_ASS_EXT:
-               return gsm_rr_rx_imm_ass_ext(ms, dlmsg->msg);
-       case GSM48_MT_RR_IMM_ASS_REJ:
-               return gsm_rr_rx_imm_ass_rej(ms, dlmsg->msg);
-       default:
-               DEBUGP(DRR, "Message type 0x%02x unknown.\n", gh->msg_type);
+       /* 3.3.1.1.2: ignore paging while establishing */
+       if (rr->state != GSM_RRSTATE_IDLE)
+               return 0;
+
+       if (payload_len < 2) {
+               short:
+               DEBUGP(DRR, "Short read of paging request 1 message .\n");
                return -EINVAL;
        }
+
+       /* channel needed */
+       chan_first = pa->cneed1;
+       chan_second = pa->cneed2;
+       /* first MI */
+       mi = pa->data + 1;
+       if (payload_len < mi[0] + 1)
+               goto short;
+       if (gsm_match_mi(ms, mi) > 0)
+               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_first]);
+       /* second MI */
+       payload_len -= mi[0] + 1;
+       mi = pa->data + mi[0] + 1;
+       if (payload_len < 2)
+               return 0;
+       if (mi[0] != GSM48_IE_MOBILE_ID)
+               return 0;
+       if (payload_len < mi[1] + 2)
+               goto short;
+       if (gsm_match_mi(ms, mi + 1) > 0)
+               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_second]);
+
+       return 0;
 }
 
+/* paging request 2 message */
+static int gsm_rr_rx_pag_req_2(struct osmocom_ms *ms, struct gsm_msgb *msg)
+{
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct gsm48_rr_paging2 *pa = msgb_l3(msg);
+       int payload_len = msgb_l3len(msg) - sizeof(*pa);
+       uint32_t tmsi;
+       int chan_first, chan_second, chan_third;
+
+       /* 3.3.1.1.2: ignore paging while establishing */
+       if (rr->state != GSM_RRSTATE_IDLE)
+               return 0;
 
+       if (payload_len < 0) {
+               short:
+               DEBUGP(DRR, "Short read of paging request 2 message .\n");
+               return -EINVAL;
+       }
 
-complete
--------------------------------------------------------------------------------
-uncomplete
+       /* channel needed */
+       chan_first = pa->cneed1;
+       chan_second = pa->cneed2;
+       /* first MI */
+       if (ms->subscr.tmsi == ntohl(pa->tmsi1)
+        && ms->subscr.tmsi_valid)
+               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_first]);
+       /* second MI */
+       if (ms->subscr.tmsi == ntohl(pa->tmsi2)
+        && ms->subscr.tmsi_valid)
+               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_second]);
+       /* third MI */
+       mi = pa->data;
+       if (payload_len < 2)
+               return 0;
+       if (mi[0] != GSM48_IE_MOBILE_ID)
+               return 0;
+       if (payload_len < mi[1] + 2 + 1) /* must include "channel needed" */
+               goto short;
+       chan_third = mi[mi[1] + 2] & 0x03; /* channel needed */
+       if (gsm_match_mi(ms, mi + 1) > 0)
+               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_third]);
 
+       return 0;
+}
 
+/* paging request 3 message */
+static int gsm_rr_rx_pag_req_3(struct osmocom_ms *ms, struct gsm_msgb *msg)
+{
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct gsm48_rr_paging3 *pa = msgb_l3(msg);
+       int payload_len = msgb_l3len(msg) - sizeof(*pa);
+       uint32_t tmsi;
+       int chan_first, chan_second, chan_third, chan_fourth;
 
+       /* 3.3.1.1.2: ignore paging while establishing */
+       if (rr->state != GSM_RRSTATE_IDLE)
+               return 0;
 
+       if (payload_len < 0) { /* must include "channel needed", part of *pa */
+               short:
+               DEBUGP(DRR, "Short read of paging request 3 message .\n");
+               return -EINVAL;
+       }
 
+       /* channel needed */
+       chan_first = pa->cneed1;
+       chan_second = pa->cneed2;
+       chan_third = pa->cneed3;
+       chan_fourth = pa->cneed4;
+       /* first MI */
+       if (ms->subscr.tmsi == ntohl(pa->tmsi1)
+        && ms->subscr.tmsi_valid)
+               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_first]);
+       /* second MI */
+       if (ms->subscr.tmsi == ntohl(pa->tmsi2)
+        && ms->subscr.tmsi_valid)
+               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_second]);
+       /* thrid MI */
+       if (ms->subscr.tmsi == ntohl(pa->tmsi3)
+        && ms->subscr.tmsi_valid)
+               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_third]);
+       /* fourth MI */
+       if (ms->subscr.tmsi == ntohl(pa->tmsi4)
+        && ms->subscr.tmsi_valid)
+               return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_fourth]);
 
+       return 0;
+}
 
+/*
+ * (immediate) assignment
+ */
 
+/* match request reference agains request history */
+static int gsm_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *req)
+{
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       int i;
 
+       for (i = 0; i < 3; i++) {
+               if (rr->cr_hist[i] >= 0
+                && ref->ra == rr->cr_hist[i]) {
+                       // todo: match timeslot
+                       return 1;
+               }
+       }
 
+       return 0;
+}
 
+/* transmit assignment complete after establishing link */
+static int gsm_rr_tx_ass_cpl(struct osmocom_ms *ms, uint8_t cause)
+{
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct msgb *msg;
+       struct gsm48_hdr *gh;
+       struct gsm48_ass_cpl *ac;
 
+       msg = gsm48_rr_msgb_alloc();
+       if (!msg)
+               return -ENOMEM;
+       gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+       ac = (struct gsm48_ass_cpl *) msgb_put(msg, sizeof(*ac));
 
+       gh->proto = GSM48_PDISC_RR;
+       gh->msg_type = GSM48_MT_RR_ASS_COMPL;
 
+       /* RR_CAUSE */
+       ac->rr_cause = cause;
 
+       return rslms_data_req(ms, msg, 0);
+}
 
+/* transmit failure to old link */
+static int gsm_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause)
+{
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct msgb *msg;
+       struct gsm48_hdr *gh;
+       struct gsm48_ass_fail *ac;
 
-/*
- * system information
- */
+       msg = gsm48_rr_msgb_alloc();
+       if (!msg)
+               return -ENOMEM;
+       gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+       af = (struct gsm48_ass_fail *) msgb_put(msg, sizeof(*af));
 
-/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
-static int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd, uint8_t len, uint8_t mask)
-{
-       int i;
+       gh->proto = GSM48_PDISC_RR;
+       gh->msg_type = GSM48_MT_RR_ASS_COMPL;
 
-       /* NOTES:
-        *
-        * The Range format uses "SMOD" computation.
-        * e.g. "n SMOD m" equals "((n - 1) % m) + 1"
-        * A cascade of multiple SMOD computations is simpified:
-        * "(n SMOD m) SMOD o" equals "(((n - 1) % m) % o) + 1"
-        *
-        * The Range format uses 16 octets of data in SYSTEM INFORMATION.
-        * When used in dedicated messages, the length can be less.
-        * In this case the ranges are decoded for all frequencies that
-        * fit in the block of given length.
-        */
+       /* RR_CAUSE */
+       af->rr_cause = cause;
 
-       /* tabula rasa */
-       for (i = 0; i < 1024; i++)
-               f[i].used = 0;
+       return rslms_data_req(ms, msg, 0);
+}
 
-       /* 00..XXX. */
-       if ((cd[0] & 0xc0 & mask) == 0x00) {
-               /* Bit map 0 format */
-               if (len < 16)
-                       return -EINVAL;
-               for (i = 1; i <= 124; i++)
-                       if ((cd[15 - ((i-1) >> 3)] & (1 << ((i-1) & 7))))
-                               f[i].used = 1;
+/* receive immediate assignment */
+static int gsm_rr_rx_imm_ass(struct osmocom_ms *ms, struct gsm_msgb *msg)
+{
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct gsm48_imm_ass *ia = msgb_l3(msg);
+       int payload_len = msgb_l3len(msg) - sizeof(*ia);
 
+       /* 3.3.1.1.2: ignore assignment while idle */
+       if (rr->state != GSM_RRSTATE_CONN_PEND || !rr->wait_assign)
                return 0;
+
+       if (payload_len < 1 /* mobile allocation IE must be included */
+        || *gh->data + 1 > payload_len) { /* short read of IE */
+               DEBUGP(DRR, "Short read of immediate assignment message.\n");
+               return -EINVAL;
+       }
+       if (*gh->data > 8) {
+               DEBUGP(DRR, "moble allocation in immediate assignment too large.\n");
+               return -EINVAL;
        }
 
-       /* only Bit map 0 format for P-GSM */
-       if (ms->support.p_gsm && !ms->support.e_gsm
-        && !ms->support.r_gsm && !ms->support.dcs_1800)
-               return 0;
+       /* request ref */
+       if (gsm_match_ra(ms, ia->req_ref)) {
+               /* channel description */
+todo channel structure and right management of channel IEs
+               memset(&rr->chan_desc, 0, sizeof(cd));
+               /* timing advance */
+               rr->timing_advance = ia->timing_advance;
+               /* mobile allocation */
+               memcpy(rr->mobile_alloc_lv, gh->data, *gh->data + 1);
+               rr->wait_assing = 0;
+               return gsm_rr_dl_est(ms);
+       }
 
-       /* 10..0XX. */
-       if ((cd[0] & 0xc8 & mask) == 0x80) {
-               /* Range 1024 format */
-               uint16_t w[17]; /* 1..16 */
-               struct gsm_range_1024 *r = (struct gsm_range_1024 *)cd;
+       return 0;
+}
 
-               if (len < 2)
-                       return -EINVAL;
-               memset(w, 0, sizeof(w));
-               if (r->f0)
-                       f[0].used = 1;
-               w[1] = (r->w1_hi << 8) | r->w1_lo;
-               if (len >= 4)
-                       w[2] = (r->w2_hi << 1) | r->w2_lo;
-               if (len >= 5)
-                       w[3] = (r->w3_hi << 2) | r->w3_lo;
-               if (len >= 6)
-                       w[4] = (r->w4_hi << 2) | r->w4_lo;
-               if (len >= 7)
-                       w[5] = (r->w5_hi << 2) | r->w5_lo;
-               if (len >= 8)
-                       w[6] = (r->w6_hi << 2) | r->w6_lo;
-               if (len >= 9)
-                       w[7] = (r->w7_hi << 2) | r->w7_lo;
-               if (len >= 10)
-                       w[8] = (r->w8_hi << 1) | r->w8_lo;
-               if (len >= 10)
-                       w[9] = r->w9;
-               if (len >= 11)
-                       w[10] = r->w10;
-               if (len >= 12)
-                       w[11] = (r->w11_hi << 6) | r->w11_lo;
-               if (len >= 13)
-                       w[12] = (r->w12_hi << 5) | r->w12_lo;
-               if (len >= 14)
-                       w[13] = (r->w13_hi << 4) | r->w13_lo;
-               if (len >= 15)
-                       w[14] = (r->w14_hi << 3) | r->w14_lo;
-               if (len >= 16)
-                       w[15] = (r->w15_hi << 2) | r->w15_lo;
-               if (len >= 16)
-                       w[16] = r->w16;
-               if (w[1])
-                       f[w[1]].used = 1;
-               if (w[2])
-                       f[((w[1] - 512 + w[2] - 1) % 1023) + 1].used = 1;
-               if (w[3])
-                       f[((w[1]       + w[3] - 1) % 1023) + 1].used = 1;
-               if (w[4])
-                       f[((w[1] - 512 + ((w[2] - 256 + w[4] - 1) % 511)) % 1023) + 1].used = 1;
-               if (w[5])
-                       f[((w[1]       + ((w[3] - 256 - w[5] - 1) % 511)) % 1023) + 1].used = 1;
-               if (w[6])
-                       f[((w[1] - 512 + ((w[2]       + w[6] - 1) % 511)) % 1023) + 1].used = 1;
-               if (w[7])
-                       f[((w[1]       + ((w[3]       + w[7] - 1) % 511)) % 1023) + 1].used = 1;
-               if (w[8])
-                       f[((w[1] - 512 + ((w[2] - 256 + ((w[4] - 128 + w[8] - 1) % 255)) % 511)) % 1023) + 1].used = 1;
-               if (w[9])
-                       f[((w[1]       + ((w[3] - 256 + ((w[5] - 128 + w[9] - 1) % 255)) % 511)) % 1023) + 1].used = 1;
-               if (w[10])
-                       f[((w[1] - 512 + ((w[2]       + ((w[6] - 128 + w[10] - 1) % 255)) % 511)) % 1023) + 1].used = 1;
-               if (w[11])
-                       f[((w[1]       + ((w[3]       + ((w[7] - 128 + w[11] - 1) % 255)) % 511)) % 1023) + 1].used = 1;
-               if (w[12])
-                       f[((w[1] - 512 + ((w[2] - 256 + ((w[4]       + w[12] - 1) % 255)) % 511)) % 1023) + 1].used = 1;
-               if (w[13])
-                       f[((w[1]       + ((w[3] - 256 + ((w[5]       + w[13] - 1) % 255)) % 511)) % 1023) + 1].used = 1;
-               if (w[14])
-                       f[((w[1] - 512 + ((w[2]       + ((w[6]       + w[14] - 1) % 255)) % 511)) % 1023) + 1].used = 1;
-               if (w[15])
-                       f[((w[1]       + ((w[3]       + ((w[7]       + w[15] - 1) % 255)) % 511)) % 1023) + 1].used = 1;
-               if (w[16])
-                       f[((w[1] - 512 + ((w[2] - 256 + ((w[4] - 128 + ((w[8] - 64 + w[16] - 1) % 127)) % 255)) % 511)) % 1023) + 1].used = 1;
+/* receive immediate assignment extended */
+static int gsm_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct gsm_msgb *msg)
+{
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct gsm48_imm_ass_ext *ia = msgb_l3(msg);
+       int payload_len = msgb_l3len(msg) - sizeof(*ia);
 
+       /* 3.3.1.1.2: ignore assignment while idle */
+       if (rr->state != GSM_RRSTATE_CONN_PEND || !rr->wait_assign)
                return 0;
+
+       if (payload_len < 1 /* mobile allocation IE must be included */
+        || *gh->data + 1 > payload_len) { /* short read of IE */
+               DEBUGP(DRR, "Short read of immediate assignment extended message.\n");
+               return -EINVAL;
+       }
+       if (*gh->data > 4) {
+               DEBUGP(DRR, "moble allocation in immediate assignment extended too large.\n");
+               return -EINVAL;
        }
-       /* 10..100. */
-       if ((cd[0] & 0xce & mask) == 0x88) {
-               /* Range 512 format */
-               uint16_t w[18]; /* 1..17 */
-               struct gsm_range_512 *r = (struct gsm_range_512 *)cd;
 
-               if (len < 4)
-                       return -EINVAL;
-               memset(w, 0, sizeof(w));
-               w[0] = (r->orig_arfcn_hi << 9) || (r->orig_arfcn_mid << 1) || r->orig_arfcn_lo;
-               w[1] = (r->w1_hi << 2) || r->w1_lo;
-               if (len >= 5)
-                       w[2] = (r->w2_hi << 2) || r->w2_lo;
-               if (len >= 6)
-                       w[3] = (r->w3_hi << 2) || r->w3_lo;
-               if (len >= 7)
-                       w[4] = (r->w4_hi << 1) || r->w4_lo;
-               if (len >= 7)
-                       w[5] = r->w5;
-               if (len >= 8)
-                       w[6] = r->w6;
-               if (len >= 9)
-                       w[7] = (r->w7_hi << 6) || r->w7_lo;
-               if (len >= 10)
-                       w[8] = (r->w8_hi << 4) || r->w8_lo;
-               if (len >= 11)
-                       w[9] = (r->w9_hi << 2) || r->w9_lo;
-               if (len >= 11)
-                       w[10] = r->w10;
-               if (len >= 12)
-                       w[11] = r->w11;
-               if (len >= 13)
-                       w[12] = (r->w12_hi << 4) || r->w12_lo;
-               if (len >= 14)
-                       w[13] = (r->w13_hi << 2) || r->w13_lo;
-               if (len >= 14)
-                       w[14] = r->w14;
-               if (len >= 15)
-                       w[15] = r->w15;
-               if (len >= 16)
-                       w[16] = (r->w16_hi << 3) || r->w16_lo;
-               if (len >= 16)
-                       w[17] = r->w17;
-               if (w[0])
-                       f[w[0]].used = 1;
-               if (w[1])
-                       f[(w[0] + w[1]) % 1024].used = 1;
-               if (w[2])
-                       f[(w[0] + ((w[1] - 256 + w[2] - 1) % 511) + 1) % 1024].used = 1;
-               if (w[3])
-                       f[(w[0] + ((w[1]       + w[3] - 1) % 511) + 1) % 1024].used = 1;
-               if (w[4])
-                       f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + w[4] - 1) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[5])
-                       f[(w[0] + ((w[1]       + ((w[3] - 128 + w[5] - 1) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[6])
-                       f[(w[0] + ((w[1] - 256 + ((w[2]       + w[6] - 1) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[7])
-                       f[(w[0] + ((w[1]       + ((w[3]       + w[7] - 1) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[8])
-                       f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] - 64 + w[8] - 1) % 127)) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[9])
-                       f[(w[0] + ((w[1]       + ((w[3] - 128 + ((w[5] - 64 + w[9] - 1) % 127)) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[10])
-                       f[(w[0] + ((w[1] - 256 + ((w[2]       + ((w[6] - 64 + w[10] - 1) % 127)) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[11])
-                       f[(w[0] + ((w[1]       + ((w[3]       + ((w[7] - 64 + w[11] - 1) % 127)) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[12])
-                       f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4]      + w[12] - 1) % 127)) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[13])
-                       f[(w[0] + ((w[1]       + ((w[3] - 128 + ((w[5]      + w[13] - 1) % 127)) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[14])
-                       f[(w[0] + ((w[1] - 256 + ((w[2]       + ((w[6]      + w[14] - 1) % 127)) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[15])
-                       f[(w[0] + ((w[1]       + ((w[3]       + ((w[7]      + w[15] - 1) % 127)) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[16])
-                       f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] - 64 + ((w[8] - 32 + w[16] - 1) % 63)) % 127)) % 255)) % 511) + 1) % 1024].used = 1;
-               if (w[17])
-                       f[(w[0] + ((w[1]       + ((w[3] - 128 + ((w[5] - 64 + ((w[9] - 32 + w[17] - 1) % 63)) % 127)) % 255)) % 511) + 1) % 1024].used = 1;
+       /* request ref 1 */
+       if (gsm_match_ra(ms, ia->req_ref1)) {
+               /* channel description */
+               memset(&rr->chan_desc, 0, sizeof(cd));
+               memcpy(rr->chan_desc.chan_desc, ia->chan_desc1, 3);
+               /* timing advance */
+               rr->timing_advance = ia->timing_advance1;
+               /* mobile allocation */
+               memcpy(rr->mobile_alloc_lv, gh->data, *gh->data + 1);
+               rr->wait_assing = 0;
+               return gsm_rr_dl_est(ms);
+       }
+       /* request ref 1 */
+       if (gsm_match_ra(ms, ia->req_ref2)) {
+               /* channel description */
+               memset(&rr->chan_desc, 0, sizeof(cd));
+               memcpy(rr->chan_desc.chan_desc, ia->chan_desc2, 3);
+               /* timing advance */
+               rr->timing_advance = ia->timing_advance2;
+               /* mobile allocation */
+               memcpy(rr->mobile_alloc_lv, gh->data, *gh->data + 1);
+               rr->wait_assing = 0;
+               return gsm_rr_dl_est(ms);
+       }
+
+       return 0;
+}
+
+/* receive immediate assignment reject */
+static int gsm_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct gsm_msgb *msg)
+{
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct gsm48_imm_ass_rej *ia = msgb_l3(msg);
+       int payload_len = msgb_l3len(msg) - sizeof(*ia);
+       int i;
+       struct gsm48_req_ref *req_ref;
+       uint8_t t3122_value;
 
+       /* 3.3.1.1.2: ignore assignment while idle */
+       if (rr->state != GSM_RRSTATE_CONN_PEND || !rr->wait_assign)
                return 0;
+
+       if (payload_len < 0) {
+               short:
+               DEBUGP(DRR, "Short read of immediate assignment reject message.\n");
+               return -EINVAL;
        }
-       /* 10..101. */
-       if ((cd[0] & & mask 0xce) == 0x8a) {
-               /* Range 256 format */
-               uint16_t w[22]; /* 1..21 */
-               struct gsm_range_256 *r = (struct gsm_range_256 *)cd;
 
-               if (len < 4)
-                       return -EINVAL;
-               memset(w, 0, sizeof(w));
-               w[0] = (r->orig_arfcn_hi << 9) || (r->orig_arfcn_mid << 1) || r->orig_arfcn_lo;
-               w[1] = (r->w1_hi << 1) || r->w1_lo;
-               if (len >= 4)
-                       w[2] = r->w2;
-               if (len >= 5)
-                       w[3] = r->w3;
-               if (len >= 6)
-                       w[4] = (r->w4_hi << 5) || r->w4_lo;
-               if (len >= 7)
-                       w[5] = (r->w5_hi << 3) || r->w5_lo;
-               if (len >= 8)
-                       w[6] = (r->w6_hi << 1) || r->w6_lo;
-               if (len >= 8)
-                       w[7] = r->w7;
-               if (len >= 9)
-                       w[8] = (r->w8_hi << 4) || r->w8_lo;
-               if (len >= 10)
-                       w[9] = (r->w9_hi << 1) || r->w9_lo;
-               if (len >= 10)
-                       w[10] = r->w10;
-               if (len >= 11)
-                       w[11] = (r->w11_hi << 3) || r->w11_lo;
-               if (len >= 11)
-                       w[12] = r->w12;
-               if (len >= 12)
-                       w[13] = r->w13;
-               if (len >= 13)
-                       w[14] = r->w15;
-               if (len >= 13)
-                       w[15] = (r->w14_hi << 2) || r->w14_lo;
-               if (len >= 14)
-                       w[16] = (r->w16_hi << 3) || r->w16_lo;
-               if (len >= 14)
-                       w[17] = r->w17;
-               if (len >= 15)
-                       w[18] = r->w19;
-               if (len >= 15)
-                       w[19] = (r->w18_hi << 3) || r->w18_lo;
-               if (len >= 16)
-                       w[20] = (r->w20_hi << 3) || r->w20_lo;
-               if (len >= 16)
-                       w[21] = r->w21;
-               if (w[0])
-                       f[w[0]].used = 1;
-               if (w[1])
-                       f[(w[0] + w[1]) % 1024].used = 1;
-               if (w[2])
-                       f[(w[0] + ((w[1] - 128 + w[2] - 1) % 255) + 1) % 1024].used = 1;
-               if (w[3])
-                       f[(w[0] + ((w[1]       + w[3] - 1) % 255) + 1) % 1024].used = 1;
-               if (w[4])
-                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + w[4] - 1) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[5])
-                       f[(w[0] + ((w[1]       + ((w[3] - 64 + w[5] - 1) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[6])
-                       f[(w[0] + ((w[1] - 128 + ((w[2]      + w[6] - 1) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[7])
-                       f[(w[0] + ((w[1]       + ((w[3]      + w[7] - 1) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[8])
-                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] - 32 + w[8] - 1) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[9])
-                       f[(w[0] + ((w[1]       + ((w[3] - 64 + ((w[5] - 32 + w[9] - 1) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[10])
-                       f[(w[0] + ((w[1] - 128 + ((w[2]      + ((w[6] - 32 + w[10] - 1) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[11])
-                       f[(w[0] + ((w[1]       + ((w[3]      + ((w[7] - 32 + w[11] - 1) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[12])
-                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4]      + w[12] - 1) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[13])
-                       f[(w[0] + ((w[1]       + ((w[3] - 64 + ((w[5]      + w[13] - 1) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[14])
-                       f[(w[0] + ((w[1] - 128 + ((w[2]      + ((w[6]      + w[14] - 1) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[15])
-                       f[(w[0] + ((w[1]       + ((w[3]      + ((w[7]      + w[15] - 1) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[16])
-                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] - 32 + ((w[8] - 16 + w[16] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[17])
-                       f[(w[0] + ((w[1]       + ((w[3] - 64 + ((w[5] - 32 + ((w[9] - 16 + w[17] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[18])
-                       f[(w[0] + ((w[1] - 128 + ((w[2]      + ((w[6] - 32 + ((w[10] - 16 + w[18] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[19])
-                       f[(w[0] + ((w[1]       + ((w[3]      + ((w[7] - 32 + ((w[11] - 16 + w[19] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[20])
-                       f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4]      + ((w[12] - 16 + w[20] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
-               if (w[21])
-                       f[(w[0] + ((w[1]       + ((w[3] - 64 + ((w[5]      + ((w[13] - 16 + w[21] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].used = 1;
+       for (i = 0; i < 4; i++) {
+               /* request reference */
+               req_ref = (struct gsm48_req_ref *)(((uint8_t *)&ia->req_ref1) + i * 4);
+               if (gsm_match_ra(ms, req_ref)) {
+                       /* wait indication */
+                       t3122_value = ((uint8_t *)&ia->wait_ind1) + i * 4;
+                       if (t3122_value)
+                               start_rr_t3122(rr, t3122_value, 0);
+                       /* start timer 3126 if not already */
+                       if (!timer_pending(rr->t3126))
+                               start_rr_t3126(rr, GSM_T3126_MS);
+                       /* stop assignmnet requests */
+                       rr->n_chan_req = 0;
 
-               return 0;
+                       /* wait until timer 3126 expires, then release
+                        * or wait for channel assignment */
+                       return 0;
+               }
        }
-       /* 10..110. */
-       if ((cd[0] & 0xce & mask) == 0x8c) {
-               /* Range 128 format */
-               uint16_t w[29]; /* 1..28 */
-               struct gsm_range_128 *r = (struct gsm_range_128 *)cd;
 
-               if (len < 3)
-                       return -EINVAL;
-               memset(w, 0, sizeof(w));
-               w[0] = (r->orig_arfcn_hi << 9) || (r->orig_arfcn_mid << 1) || r->orig_arfcn_lo;
-               w[1] = r->w1;
-               if (len >= 4)
-                       w[2] = r->w2;
-               if (len >= 5)
-                       w[3] = (r->w3_hi << 4) || r->w3_lo;
-               if (len >= 6)
-                       w[4] = (r->w4_hi << 1) || r->w4_lo;
-               if (len >= 6)
-                       w[5] = r->w5;
-               if (len >= 7)
-                       w[6] = (r->w6_hi << 3) || r->w6_lo;
-               if (len >= 7)
-                       w[7] = r->w7;
-               if (len >= 8)
-                       w[8] = r->w8;
-               if (len >= 8)
-                       w[9] = r->w9;
-               if (len >= 9)
-                       w[10] = r->w10;
-               if (len >= 9)
-                       w[11] = r->w11;
-               if (len >= 10)
-                       w[12] = r->w12;
-               if (len >= 10)
-                       w[13] = r->w13;
-               if (len >= 11)
-                       w[14] = r->w14;
-               if (len >= 11)
-                       w[15] = r->w15;
-               if (len >= 12)
-                       w[16] = r->w16;
-               if (len >= 12)
-                       w[17] = r->w17;
-               if (len >= 13)
-                       w[18] = (r->w18_hi << 1) || r->w18_lo;
-               if (len >= 13)
-                       w[19] = r->w19;
-               if (len >= 13)
-                       w[20] = r->w20;
-               if (len >= 14)
-                       w[21] = (r->w21_hi << 2) || r->w21_lo;
-               if (len >= 14)
-                       w[22] = r->w22;
-               if (len >= 14)
-                       w[23] = r->w23;
-               if (len >= 15)
-                       w[24] = r->w24;
-               if (len >= 15)
-                       w[25] = r->w25;
-               if (len >= 16)
-                       w[26] = (r->w26_hi << 1) || r->w26_lo;
-               if (len >= 16)
-                       w[27] = r->w27;
-               if (len >= 16)
-                       w[28] = r->w28;
-               if (w[0])
-                       f[w[0]].used = 1;
-               if (w[1])
-                       f[(w[0] + w[1]) % 1024].used = 1;
-               if (w[2])
-                       f[(w[0] + ((w[1] - 64 + w[2] - 1) % 127) + 1) % 1024].used = 1;
-               if (w[3])
-                       f[(w[0] + ((w[1]      + w[3] - 1) % 127) + 1) % 1024].used = 1;
-               if (w[4])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + w[4] - 1) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[5])
-                       f[(w[0] + ((w[1]      + ((w[3] - 32 + w[5] - 1) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[6])
-                       f[(w[0] + ((w[1] - 64 + ((w[2]      + w[6] - 1) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[7])
-                       f[(w[0] + ((w[1]      + ((w[3]      + w[7] - 1) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[8])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + w[8] - 1) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[9])
-                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5] - 16 + w[9] - 1) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[10])
-                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6] - 16 + w[10] - 1) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[11])
-                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7] - 16 + w[11] - 1) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[12])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4]      + w[12] - 1) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[13])
-                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5]      + w[13] - 1) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[14])
-                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6]      + w[14] - 1) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[15])
-                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7]      + w[15] - 1) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[16])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + ((w[8] - 8 + w[16] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[17])
-                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5] - 16 + ((w[9] - 8 + w[17] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[18])
-                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6] - 16 + ((w[10] - 8 + w[18] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[19])
-                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7] - 16 + ((w[11] - 8 + w[19] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[20])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4]      + ((w[12] - 8 + w[20] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[21])
-                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5]      + ((w[13] - 8 + w[21] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[22])
-                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6]      + ((w[14] - 8 + w[22] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[23])
-                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7]      + ((w[15] - 8 + w[23] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[24])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + ((w[8]     + w[24] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[25])
-                       f[(w[0] + ((w[1]      + ((w[3] - 32 + ((w[5] - 16 + ((w[9]     + w[25] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[26])
-                       f[(w[0] + ((w[1] - 64 + ((w[2]      + ((w[6] - 16 + ((w[10]     + w[26] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[27])
-                       f[(w[0] + ((w[1]      + ((w[3]      + ((w[7] - 16 + ((w[11]     + w[27] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
-               if (w[28])
-                       f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4]      + ((w[12]     + w[28] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].used = 1;
+       return 0;
+}
 
-               return 0;
+/* receive additional assignment */
+static int gsm_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg)
+{
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct gsm48_hdr *gh = msgb_l3(msg);
+       struct gsm48_add_ass *aa = (struct gsm48_add_ass *)gh->data;
+       int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*aa);
+
+       if (payload_len < 0) {
+               DEBUGP(DRR, "Short read of ADDITIONAL ASSIGNMENT message.\n");
+               return gsm_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
        }
-       /* 10..111. */
-       if ((cd[0] & 0xce & mask) == 0x8e) {
-               /* Variable bitmap format (can be any length >= 3) */
-               uint16_t orig = 0;
-               struct gsm_var_bit *r = (struct gsm_var_bit *)cd;
+       tlv_parse(&tp, &rsl_att_tlvdef, aa->data, payload_len, 0, 0);
+
+       return gsm_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+}
+
+/*
+ * measturement reports
+ */
+
+static int gsm_rr_tx_meas_rep(struct osmocom_ms *ms)
+{
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct gsm_rr_meas *meas = &rr->meas;
+       struct msgb *msg;
+       struct gsm48_hdr *gh;
+       struct gsm48_meas_res *mr;
+
+       msg = gsm48_rr_msgb_alloc();
+       if (!msg)
+               return -ENOMEM;
+       gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+       mr = (struct gsm48_meas_res *) msgb_put(msg, sizeof(*mr));
 
-               if (len < 3)
-                       return -EINVAL;
-               orig = (r->orig_arfcn_hi << 9) || (r->orig_arfcn_mid << 1) || r->orig_arfcn_lo;
-               f[orig].used = 1;
-               for (i = 1; 2 + (i >> 3) < len; i++)
-                       if ((cd[2 + (i >> 3)] & (0x80 >> (i & 7))))
-                               f[(orig + 1) % 1024].used = 1;
+       gh->proto = GSM48_PDISC_RR;
+       gh->msg_type = GSM48_MT_RR_MEAS_RES;
 
-               return 0;
+       /* measurement results */
+       mr->rxlev_full = meas->rxlev_full;
+       mr->rxlev_sub = meas->rxlev_sub;
+       mr->rxqual_full = meas->rxqual_full;
+       mr->rxqual_sub = meas->rxqual_sub;
+       mr->dtx = meas->dtx;
+       mr->ba = meas->ba;
+       mr->meas_valid = meas->meas_valid;
+       if (meas->ncell_na) {
+               /* no results for serving cells */
+               mr->no_n_hi = 1;
+               mr->no_n_lo = 3;
+       } else {
+               mr->no_n_hi = meas->count >> 2;
+               mr->no_n_lo = meas->count & 3;
        }
+       rxlev_nc1 = meas->rxlev_nc[0];
+       rxlev_nc2_hi = meas->rxlev_nc[1] >> 1;
+       rxlev_nc2_lo = meas->rxlev_nc[1] & 1;
+       rxlev_nc3_hi = meas->rxlev_nc[2] >> 2;
+       rxlev_nc3_lo = meas->rxlev_nc[2] & 3;
+       rxlev_nc4_hi = meas->rxlev_nc[3] >> 3;
+       rxlev_nc4_lo = meas->rxlev_nc[3] & 7;
+       rxlev_nc5_hi = meas->rxlev_nc[4] >> 4;
+       rxlev_nc5_lo = meas->rxlev_nc[4] & 15;
+       rxlev_nc6_hi = meas->rxlev_nc[5] >> 5;
+       rxlev_nc6_lo = meas->rxlev_nc[5] & 31;
+       bsic_nc1_hi = meas->bsic_nc[0] >> 3;
+       bsic_nc1_lo = meas->bsic_nc[0] & 7;
+       bsic_nc2_hi = meas->bsic_nc[1] >> 4;
+       bsic_nc2_lo = meas->bsic_nc[1] & 15;
+       bsic_nc3_hi = meas->bsic_nc[2] >> 5;
+       bsic_nc3_lo = meas->bsic_nc[2] & 31;
+       bsic_nc4 = meas->bsic_nc[3];
+       bsic_nc5 = meas->bsic_nc[4];
+       bsic_nc6 = meas->bsic_nc[5];
+       bcch_f_nc1 = meas->bcch_f_nc[0];
+       bcch_f_nc2 = meas->bcch_f_nc[1];
+       bcch_f_nc3 = meas->bcch_f_nc[2];
+       bcch_f_nc4 = meas->bcch_f_nc[3];
+       bcch_f_nc5_hi = meas->bcch_f_nc[4] >> 1;
+       bcch_f_nc5_lo = meas->bcch_f_nc[4] & 1;
+       bcch_f_nc6_hi = meas->bcch_f_nc[5] >> 2;
+       bcch_f_nc6_lo = meas->bcch_f_nc[5] & 3;
 
+       //todo return rslms_data_req(ms, msg, 0);
 }
 
-/* decode "Cell Options (BCCH)" (10.5.2.3) */
-static int gsm48_decode_cell_sel_param(struct gsm48_sysinfo *s, struct gsm48_cell_sel_par *cs)
-{
-       s->radio_link_timeout = (cs->radio_link_timeout + 1) * 4;
-       s->dtx = cs->dtx;
-       s->pwrc = cs->pwrc;
-}
+/*
+ * link establishment and release
+ */
 
-/* decode "Cell Options (BCCH)" (10.5.2.3) */
-static int gsm48_decode_cellopt(struct gsm48_sysinfo *s, struct gsm48_cell_options *co)
+/* activate link and send establish request */
+static int gsm_rr_dl_est(struct osmocom_ms *ms)
 {
-       s->ms_txpwr_max_ccch = co->ms_txpwr_max_ccch;
-       s->cell_resel_hyst_db = co->cell_resel_hyst * 2;
-       s->rxlev_acc_min_db = co->rxlev_acc_min - 110;
-       s->neci = co->neci;
-       s->acs = co->acs;
-}
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct gsm_subscriber *subcr = ms->subscr;
+       struct msgb *msg;
+       struct gsm48_hdr *gh;
+       struct gsm48_pag_rsp *pa;
 
-/* decode "Cell Channel Description" (10.5.2.11) */
-static int gsm48_decode_ccd(struct gsm48_sysinfo *s, struct gsm48_control_channel_desc *cc)
-{
-       s->ccch_conf = cc->ccch_conf;
-       s->bs_ag_blks_res = cc->bs_ag_blks_res;
-       s->att_allowed = cc->att;
-       s->pag_mf_periods = cc->bs_pa_mfrms + 2;
-       s->t3212 = cc->t3212 * 360; /* convert deci-hours to seconds */
-}
+       /* 3.3.1.1.3.1 */
+       stop_rr_t3126(rr);
 
-/* Rach Control decode tables */
-static uint8_t gsm48_max_retrans[4] = {
-       1, 2, 4, 7
-}
-static uint8_t gsm48_tx_integer[16] = {
-       3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 25, 32, 50
-}
+       /* flush pending RACH requests */
+       rr->n_chan_req = 0; // just to be safe
+       msg = msgb_alloc_headroom(20, 16, "RAND_FLUSH");
+       if (!msg)
+               return -ENOMEM;
+       rslms_tx_rll_req_l3(ms, RSL_MT_RAND_ACC_FLSH, chan_nr, 0, msg);
 
-/* decode "RACH Control Parameter" (10.5.2.29) */
-static int gsm48_decode_rach_ctl_param(struct gsm48_sysinfo *s, struct gsm48_rach_ctl *rc)
-{
-       int i;
+       /* send DL_EST_REQ */
+       if (rr->rr_est_msg) {
+               /* use queued message */
+               msg = rr->rr_est_msg;
+               rr->rr_est_msg = 0;
+       } else {
+               uint8_t mi[11];
 
-       s->reest_denied = rc->re;
-       s->cell_barred = rc->cell_barr;
-       s->tx_integer = gsm48_tx_integer[rc->tx_int];
-       s->max_retrans = gsm48_max_retrans[rc->max_retr];
-       for (i = 0, i <= 15, i++)
-               if ((rc->ac[1 - (i >> 3)] & (1 << (i & 7))))
-                       s->class_barr[i] = 1;
+               /* create paging response */
+               msg = gsm48_rr_msgb_alloc();
+               if (!msg)
+                       return -ENOMEM;
+               gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+               pr = (struct gsm48_pag_rsp *) msgb_put(msg, sizeof(*pr));
+               /* key sequence */
+               if (subscr->key_valid)
+                       pr->key_seq = subscr->key_seq;
                else
-                       s->class_barr[i] = 0;
+                       pr->key_seq = 7;
+               /* classmark 2 */
+               cc->cm_len = sizeof(cm->cm2);
+               gsm_rr_enc_cm2(ms, &cc->cm2)
+               /* mobile identity */
+               if (ms->subscr.tmsi_valid) {
+                       gsm48_generate_mid_from_tmsi(mi, subscr->tmsi);
+               } else if (subscr->imsi[0])
+                       gsm48_generate_mid_from_imsi(mi, subscr->imsi);
+               else {
+                       mi[1] = 1;
+                       mi[2] = 0xf0 | GSM_MI_TYPE_NONE;
+               }
+               msgb_put(msg, 1 + mi[1]);
+               memcpy(cm->data, mi + 1, 1 + mi[1]);
+       }
 
-       return 0;
+       /* activate channel */
+       tx_ph_dm_est_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
+
+       /* start establishmnet */
+       return rslms_tx_rll_req_l3(ms, RSL_MT_EST_REQ, rr->chan_desc.chan_desc.chan_nr, 0, msg);
 }
-static int gsm48_decode_rach_ctl_neigh(struct gsm48_sysinfo *s, struct gsm48_rach_ctl *rc)
+
+/* the link is established */
+static int gsm_rr_estab_cnf(struct osmocom_ms *ms, struct msgb *msg)
 {
-       int i;
+       struct msgb *nmsg;
+       struct gsm_mm_hdr *nmmh;
 
-       s->nb_reest_denied = rc->re;
-       s->nb_cell_barred = rc->cell_barr;
-       s->nb_tx_integer = gsm48_tx_integer[rc->tx_int];
-       s->nb_max_retrans = gsm48_max_retrans[rc->max_retr];
-       for (i = 0, i <= 15, i++)
-               if ((rc->ac[1 - (i >> 3)] & (1 << (i & 7))))
-                       s->nb_class_barr[i] = 1;
-               else
-                       s->nb_class_barr[i] = 0;
+       /* if MM has releases before confirm, we start release */
+       if (rr->state == GSM_RRSTATE_IDLE) {
+               /* release message */
+               nmsg = gsm48_rr_msgb_alloc();
+               if (!nmsg)
+                       return -ENOMEM;
+               /* start release */
+               return rslms_tx_rll_req_l3(ms, RSL_MT_REL_REQ, 0, 0, nmsg);
+       }
 
-       return 0;
-}
+       /* 3.3.1.1.4 */
+       new_rr_state(rr, GSM_RRSTATE_DEDICATED);
 
-/* decode "SI 1 Rest Octets" (10.5.2.32) */
-static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, uint8_t *si, uint8_t len)
-{
+       /* send confirm to upper layer */
+       nmsg = gsm48_mm_msgb_alloc();
+       if (!nmsg)
+               return -ENOMEM;
+       nmmh = (struct gsm_mm_hdr *)nmsg->data;
+       nmmh->msg_type = (rr->rr_est_req) ? RR_EST_CNF : RR_EST_IND;
+       return gsm48_mm_upmsg(ms, nmsg);
 }
 
-/* decode "SI 3 Rest Octets" (10.5.2.34) */
-static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s, uint8_t *si, uint8_t len)
+/* the link is released */
+static int gsm_rr_rel_cnf(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
 {
+       /* deactivate channel */
+       tx_ph_dm_rel_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
+
+       /* do nothing, because we aleady IDLE
+        * or we received the rel cnf of the last connection
+        * while already requesting a new one (CONN PEND)
+        */
+
+       return 0;
 }
 
+/*
+ * radio ressource requests 
+ */
 
-todo: add to unit data ind switch-case state
-/* receive "SYSTEM INFORMATION 1" message (9.1.31) */
-static int gsm_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg)
+/* establish request for dedicated mode */
+static int gsm_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
 {
-       struct gsm48_system_information_type_1 *si = msgb_l3(msg);
-       struct gsm48_sysinfo *s = ms->sysinfo;
-       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct gsm_mm_hdr *mmh = msgb->data;
+       struct gsm48_hdr *gh = msgb_l3(msg);
 
-       if (payload_len < 0) {
-               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 1 message.\n");
+       /* 3.3.1.1.3.2 */
+       if (timer_pending(rr->t3122)) {
+               if (rrmsg->cause != RR_EST_CAUSE_EMERGENCY) {
+                       struct msgb *nmsg;
+                       struct gsm_mm_hdr *nmmh;
+
+                       nmsg = gsm48_mm_msgb_alloc();
+                       if (!nmsg)
+                               return -ENOMEM;
+                       nmmh = (struct gsm_mm_hdr *)nmsg->data;
+                       nmmh->msg_type RR_REL_IND;
+                       nmmh->cause = GSM_MM_CAUSE_T3122_PEND;
+                       return gsm48_mm_upmsg(ms, nmsg);
+               } else
+                       stop_rr_t3122(rr);
+       }
+
+       /* 3.3.1.1.1 */
+       if (rrmsg->cause != RR_EST_CAUSE_EMERGENCY) {
+               if (!(ms->access_class & ms->si.access_class)) {
+                       reject:
+                       if (!ms->opt.access_class_override) {
+                               struct msgb *nmsg;
+                               struct gsm_mm_hdr *nmmh;
+
+                               nmsg = gsm48_mm_msgb_alloc();
+                               if (!nmsg)
+                                       return -ENOMEM;
+                               nmmh = (struct gsm_mm_hdr *)nmsg->data;
+                               nmmh->msg_type RR_REL_IND;
+                               nmmh->cause = GSM_MM_CAUSE_NOT_AUTHORIZED;
+                               return gsm48_mm_upmsg(ms, nmsg);
+                       }
+               }
+       } else {
+               if (!(ms->access_class & ms->si.access_class)
+                && !ms->si.emergency)
+                       goto reject;
+       }
+
+       /* requested by RR */
+       rr->rr_est_req = 1;
+
+       /* clone and store REQUEST message */
+       if (!gh) {
+               printf("Error, missing l3 message\n");
                return -EINVAL;
        }
-       /* Cell Channel Description */
-       gsm48_decode_freq_list(s->freq, si->cell_channel_description,
-               sizeof(si->cell_channel_description), 0xce);
-       /* RACH Control Parameter */
-       gsm48_decode_rach_ctl_param(s, si->rach_control);
-       /* SI 1 Rest Octets */
-       if (payload_len)
-               gsm48_decode_si1_rest(si->rest_octets, payload_len);
+       rr->rr_est_msg = msgb_alloc_headroom(256, 16, "EST_REQ");
+       if (!rr->rr_est_msg)
+               return -ENOMEM;
+       memcpy(msgb_put(rr_est_msg, msgb_l3len(msg)),
+               msgb_l3(msg), msgb_l3len(msg));
 
-       return 0;
+       /* request channel */
+       return gsm_rr_tx_chan_req(ms, mmh->cause);
 }
 
-
-todo: add to unit data ind switch-case state
-/* receive "SYSTEM INFORMATION 2" message (9.1.32) */
-static int gsm_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg)
+/* send all queued messages down to layer 2 */
+static int gsm_rr_dequeue_down(struct osmocom_ms *ms)
 {
-       struct gsm48_system_information_type_2 *si = msgb_l3(msg);
-       struct gsm48_sysinfo *s = ms->sysinfo;
-       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct msgb *msg;
 
-       if (payload_len < 0) {
-               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 2 message.\n");
-               return -EINVAL;
+       while((msg = msgb_dequeue(&rr->downqueue))) {
+               rslms_tx_rll_req_l3(ms, RSL_MT_DATA_REQ, chan_nr, 0, msg);
        }
-       /* Neighbor Cell Description */
-       gsm48_decode_freq_list(s->nb_freq, si->bcch_frequency_list,
-               sizeof(si->bcch_frequency_list), 0xce);
-       /* NCC Permitted */
-       s->ncc_permitted = si->ncc_permitted;
-       /* RACH Control Parameter */
-       gsm48_decode_rach_ctl_neigh(s, si->rach_control);
 
        return 0;
 }
 
-todo: tabula rasa?:
-todo: add to unit data ind switch-case state
-/* receive "SYSTEM INFORMATION 2bis" message (9.1.33) */
-static int gsm_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg)
+/* 3.4.2 transfer data in dedicated mode */
+static int gsm_rr_data_req(struct osmocom_ms *ms, struct msgb *msg)
 {
-       struct gsm48_system_information_type_2bis *si = msgb_l3(msg);
-       struct gsm48_sysinfo *s = ms->sysinfo;
-       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct gsm_rrlayer *rr = ms->rrlayer;
 
-       if (payload_len < 0) {
-               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 2bis message.\n");
+       if (rr->state != GSM_RRSTATE_DEDICATED) {
+               msgb_free(msg)
                return -EINVAL;
        }
-       /* Neighbor Cell Description */
-       s->ext_ind = (si->bcch_frequency_list[0] >> 6) & 1;
-       s->ba_ind = (si->bcch_frequency_list[0] >> 5) & 1;
-       gsm48_decode_freq_list(s->nb_freq, si->ext_bcch_frequency_list,
-               sizeof(si->ext_bcch_frequency_list), 0x8e);
-       /* RACH Control Parameter */
-       gsm48_decode_rach_ctl_neigh(s, si->rach_control);
+       
+       /* pull header */
+       msgb_pull(msg, sizeof(struct gsm_mm_hdr));
 
-       return 0;
+       /* queue message, during handover or assignment procedure */
+       if (rr->hando_susp_state || rr->assign_susp_state) {
+               msgb_enqueue(&rr->downqueue, msg);
+               return 0;
+       }
+
+       /* forward message */
+       return rslms_tx_rll_req_l3(ms, RSL_MT_DATA_REQ, chan_nr, 0, msg);
 }
 
+/*
+ * data indications from data link
+ */
 
-todo: add to unit data ind switch-case state
-/* receive "SYSTEM INFORMATION 2ter" message (9.1.34) */
-static int gsm_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg)
+/* 3.4.2 data from layer 2 to RR and upper layer*/
+static int gsm_rr_data_ind(struct osmocom_ms *ms, struct msbg *msg)
 {
-       struct gsm48_system_information_type_2ter *si = msgb_l3(msg);
-       struct gsm48_sysinfo *s = ms->sysinfo;
-       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct gsm48_hdr *gh = msgb_l3(msg);
+       u_int8_t pdisc = gh->proto_discr & 0x0f;
 
-       if (payload_len < 0) {
-               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 2ter message.\n");
-               return -EINVAL;
+       if (pdisc == GSM48_PDISC_RR) {
+               int rc = -EINVAL;
+
+               switch(gh->msg_type) {
+               case GSM48_MT_RR_ADD_ASS:
+                       rc = gsm_rr_rx_add_ass(ms, msg);
+                       break;
+               case GSM48_MT_RR_ASS_CMD:
+                       rc = gsm_rr_rx_ass_cmd(ms, msg);
+                       break;
+               case GSM48_MT_RR_CIP_MODE_CMD:
+                       rc = gsm_rr_rx_cip_mode_cmd(ms, msg);
+                       break;
+               case GSM48_MT_RR_CLSM_ENQ:
+                       rc = gsm_rr_rx_cm_enq(ms, msg);
+                       break;
+               case GSM48_MT_RR_HANDO_CMD:
+                       rc = gsm_rr_rx_hando_cmd(ms, msg);
+                       break;
+               case GSM48_MT_RR_FREQ_REDEF:
+                       rc = gsm_rr_rx_freq_redef(ms, msg);
+                       break;
+               default:
+                       DEBUGP(DRR, "Message type 0x%02x unknown.\n", gh->msg_type);
+               }
+
+               free_msgb(msg);
+               return rc;
        }
-       /* Neighbor Cell Description 2 */
-       s->multi_rep = (si->bcch_frequency_list[0] >> 6) & 3;
-       gsm48_decode_freq_list(s->nb_freq, si->ext_bcch_frequency_list,
-               sizeof(si->ext_bcch_frequency_list), 0x8e);
 
-       return 0;
+       /* push header */
+       msgb_push(msg, sizeof(struct gsm48_mm_hdr));
+       mmh = (struct gsm48_mm_hdr *)msg->data;
+       mmh->msg_type = RR_DATA_IND;
+
+       return gsm48_mm_upmsg(ms, msg);
 }
 
-todo: add to unit data ind switch-case state
-/* receive "SYSTEM INFORMATION 3" message (9.1.35) */
-static int gsm_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg)
+/* unit data from layer 2 to RR layer */
+static int gsm_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
 {
-       struct gsm48_system_information_type_3 *si = msgb_l3(msg);
-       struct gsm48_sysinfo *s = ms->sysinfo;
-       int payload_len = msgb_l3len(msg) - sizeof(*si);
+       struct gsm48_hdr *gh = msgb_l3(msg);
 
-       if (payload_len < 0) {
-               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 3 message.\n");
+       switch (gh->msg_type) {
+       case GSM48_MT_RR_SYSINFO_1:
+               return gsm_rr_rx_sysinfo1(ms, dlmsg->msg);
+       case GSM48_MT_RR_SYSINFO_2:
+               return gsm_rr_rx_sysinfo2(ms, dlmsg->msg);
+       case GSM48_MT_RR_SYSINFO_2bis:
+               return gsm_rr_rx_sysinfo2bis(ms, dlmsg->msg);
+       case GSM48_MT_RR_SYSINFO_2ter:
+               return gsm_rr_rx_sysinfo2ter(ms, dlmsg->msg);
+       case GSM48_MT_RR_SYSINFO_3:
+               return gsm_rr_rx_sysinfo3(ms, dlmsg->msg);
+       case GSM48_MT_RR_SYSINFO_4:
+               return gsm_rr_rx_sysinfo4(ms, dlmsg->msg);
+       case GSM48_MT_RR_SYSINFO_5:
+               return gsm_rr_rx_sysinfo5(ms, dlmsg->msg);
+       case GSM48_MT_RR_SYSINFO_5bis:
+               return gsm_rr_rx_sysinfo5bis(ms, dlmsg->msg);
+       case GSM48_MT_RR_SYSINFO_5ter:
+               return gsm_rr_rx_sysinfo5ter(ms, dlmsg->msg);
+       case GSM48_MT_RR_SYSINFO_6:
+               return gsm_rr_rx_sysinfo6(ms, dlmsg->msg);
+       case GSM48_MT_RR_PAG_REQ_1:
+               return gsm_rr_rx_pag_req_1(ms, dlmsg->msg);
+       case GSM48_MT_RR_PAG_REQ_2:
+               return gsm_rr_rx_pag_req_2(ms, dlmsg->msg);
+       case GSM48_MT_RR_PAG_REQ_3:
+               return gsm_rr_rx_pag_req_3(ms, dlmsg->msg);
+       case GSM48_MT_RR_IMM_ASS:
+               return gsm_rr_rx_imm_ass(ms, dlmsg->msg);
+       case GSM48_MT_RR_IMM_ASS_EXT:
+               return gsm_rr_rx_imm_ass_ext(ms, dlmsg->msg);
+       case GSM48_MT_RR_IMM_ASS_REJ:
+               return gsm_rr_rx_imm_ass_rej(ms, dlmsg->msg);
+       default:
+               DEBUGP(DRR, "Message type 0x%02x unknown.\n", gh->msg_type);
                return -EINVAL;
        }
-       /* Cell Identity */
-       s->cell_identity = ntohl(si->cell_identity);
-       /* LAI */
-       gsm48_decode_lai(si->lai, s->mcc, s->mnc, s->lac);
-       /* Control Channel Description */
-       gsm48_decode_ccd(s, si->control_channel_desc);
-       /* Cell Options (BCCH) */
-       gsm48_decode_cellopt(s, si->control_channel_desc);
-       /* Cell Selection Parameters */
-       gsm48_decode_cell_sel_param(s, si->cell_sel_par);
-       /* RACH Control Parameter */
-       gsm48_decode_rach_ctl_param(s, si->rach_control);
-       /* SI 1 Rest Octets */
-       if (payload_len >= 4)
-               gsm48_decode_si3_rest(si->rest_octets, payload_len);
-
-       return 0;
 }
 
-todo: add to unit data ind switch-case state
-/* receive "SYSTEM INFORMATION 4" message (9.1.36) */
-static int gsm_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
-{
-       struct gsm48_system_information_type_4 *si = msgb_l3(msg);
-       struct gsm48_sysinfo *s = ms->sysinfo;
-       int payload_len = msgb_l3len(msg) - sizeof(*si);
 
-       if (payload_len < 0) {
-               DEBUGP(DRR, "Short read of SYSTEM INFORMATION 4 message.\n");
-               return -EINVAL;
-       }
-       /* LAI */
-       gsm48_decode_lai(si->lai, s->mcc, s->mnc, s->lac);
-       /* Cell Selection Parameters */
-       gsm48_decode_cell_sel_param(s, si->cell_sel_par);
-       /* RACH Control Parameter */
-       gsm48_decode_rach_ctl_param(s, si->rach_control);
-       /* CBCH Channel Description */
-       if (payload_len >= 4 && si->data[0] == GSM48_IE_CBCH_CHAN_DES) {
-               memcpy(&s->chan_desc, si->data + 1, sizeof(s->chan_desc));
-               /* CBCH Mobile Allocation */
-               if (payload_len >= 6 && si->data[4] == GSM48_IE_CBCH_MOB_ALLOC)
-                       && payload_len >= 6 + si->data[5])
-                       gsm48_decode_mobile_alloc(&ma, si->data + 5);
-               }
-       }
-       /* Cell Options (BCCH) */
-       gsm48_decode_cellopt(s, si->control_channel_desc);
-       /* SI 1 Rest Octets */
-       if (payload_len >= 4)
-               gsm48_decode_si3_rest(si->rest_octets, payload_len);
 
-       return 0;
-}
+the process above is complete
+------------------------------------------------------------------------------
+incomplete
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 
-today: decode mobile alloc 1-8 binary masks
 
 
 
@@ -2092,46 +2345,39 @@ 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 struct rr_names {
-       char *name;
-       int value;
-} rr_names[] = {
-       { "RR_EST_REQ",         RR_EST_REQ },
-       { "RR_EST_IND",         RR_EST_IND },
-       { "RR_EST_CNF",         RR_EST_CNF },
-       { "RR_REL_IND",         RR_REL_IND },
-       { "RR_SYNC_IND",        RR_SYNC_IND },
-       { "RR_DATA_REQ",        RR_DATA_REQ },
-       { "RR_DATA_IND",        RR_DATA_IND },
-       { "RR_UNIT_DATA_IND",   RR_UNIT_DATA_IND },
-       { "RR_ABORT_REQ",       RR_ABORT_REQ },
-       { "RR_ABORT_IND",       RR_ABORT_IND },
-       { "RR_ACT_REQ",         RR_ACT_REQ },
-
-       {NULL, 0}
+static const struct value_string rr_names[] = {
+       { RR_EST_REQ,           "RR_EST_REQ" },
+       { RR_EST_IND,           "RR_EST_IND" },
+       { RR_EST_CNF,           "RR_EST_CNF" },
+       { RR_REL_IND,           "RR_REL_IND" },
+       { RR_SYNC_IND,          "RR_SYNC_IND" },
+       { RR_DATA_REQ,          "RR_DATA_REQ" },
+       { RR_DATA_IND,          "RR_DATA_IND" },
+       { RR_UNIT_DATA_IND,     "RR_UNIT_DATA_IND" },
+       { RR_ABORT_REQ,         "RR_ABORT_REQ" },
+       { RR_ABORT_IND,         "RR_ABORT_IND" },
+       { RR_ACT_REQ,           "RR_ACT_REQ" },
+       { 0,                    NULL }
 };
 
-char *get_rr_name(int value)
+const char *get_rr_name(int value)
 {
-       int i;
-
-       for (i = 0; rr_names[i].name; i++) {
-               if (rr_names[i].value == value)
-                       return rr_names[i].name;
-       }
-
-       return "RR_Unknown";
+       return get_value_string(rr_names, value);
 }
 
-static int rr_rcvmsg(struct osmocom_ms *ms,
+move to mm
+static int gsm48_mm_upmsg(struct osmocom_ms *ms,
                        int msg_type, struct gsm_mncc *rrmsg)
 {
        struct msgb *msg;
 
+#if 0
        DEBUGP(DRR, "(MS %s) Sending '%s' to MM.\n", ms->name,
                get_rr_name(msg_type));
+#endif
 
        rrmsg->msg_type = msg_type;
        
@@ -2163,7 +2409,7 @@ static int gsm_rr_act_req(struct osmocom_ms *ms, struct gsm_rr *rrmsg)
 
 /* state trasitions for radio ressource messages (upper layer) */
 static struct rrdownstate {
-       u_int32_t       states;
+       uint32_t        states;
        int             type;
        int             (*rout) (struct osmocom_ms *ms, struct gsm_dl *rrmsg);
 } rrdownstatelist[] = {
@@ -2180,13 +2426,13 @@ static struct rrdownstate {
 #define RRDOWNSLLEN \
        (sizeof(rrdownstatelist) / sizeof(struct rrdownstate))
 
-static int gsm_send_rr(struct osmocom_ms *ms, struct gsm_rr *msg)
+static int gsm48_rr_sendmsg(struct osmocom_ms *ms, struct gsm_rr *msg)
 {
        struct gsm_mm_hdr *mmh = msgb->data;
        int msg_type = mmh->msg_type;
 
        DEBUGP(DRR, "(ms %s) Sending '%s' to DL in state %s\n", ms->name,
-               gsm0408_rr_msg_names[msg_type], mm_state_names[mm->state]);
+               gsm48_rr_msg_name(msg_type), mm_state_names[mm->state]);
 
        /* find function for current state and message */
        for (i = 0; i < RRDOWNSLLEN; i++)
@@ -2195,6 +2441,8 @@ static int gsm_send_rr(struct osmocom_ms *ms, struct gsm_rr *msg)
                        break;
        if (i == RRDOWNSLLEN) {
                DEBUGP(DRR, "Message unhandled at this state.\n");
+               free_msgb(msg);
+todo: in all functions of this type: free_msgb must be called if unhandled.
                return 0;
        }
 
@@ -2253,7 +2501,6 @@ static int tlv_copy(void *dest, int dest_len, struct tlv_parsed *tp, uint8_t ie)
        return 0;
 }
 
-        - flush/send when leaving this state (or completion or if back on old channel)
 static int gsm_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm_rrlayer *rr = ms->rrlayer;
@@ -2322,8 +2569,8 @@ static int gsm_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
        memcpy(&rr->chan_desc, cd, sizeof(cd));
 
        /* start suspension of current link */
-       newmsg = gsm48_rr_msgb_alloc();
-       if (!newmsg)
+       nmsg = gsm48_rr_msgb_alloc();
+       if (!nmsg)
                return -ENOMEM;
        rslms_tx_rll_req_l3(ms, RSL_MT_SUSP_REQ, rr->chan_desc.chan_nr, 0, msg);
 
@@ -2334,21 +2581,87 @@ static int gsm_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
        return 0;
 }
 
-        - queue messages during this state
-        - flush/send when leaving this state
+/* decode "Cell Description" (10.5.2.2) */
+static int gsm48_decode_cell_desc(struct gsm48_cell_desc *cd, uint16_t *arfcn, uint8_t *ncc uint8_t *bcc)
+{
+       *arfcn = (cd->bcch_hi << 8) + cd->bcch_lo;
+       *ncc = cd->ncc;
+       *bcc = cd->bcc;
+}
+
+/* decode "Power Command" (10.5.2.28) and (10.5.2.28a) */
+static int gsm48_decode_power_cmd_acc(struct gsm48_power_cmd *pc, uint8_t *power_level uint8_t *atc)
+{
+       *power_level = pc->power_level;
+       if (atc) /* only in case of 10.5.2.28a */
+               *atc = pc->atc;
+}
+
+/* decode "Synchronization Indication" (10.5.2.39) */
+static int gsm48_decode_power_cmd_acc(struct gsm_rrlayer *rr, struct gsm_rr_sync_ind *si)
+{
+       rr->ho_sync_ind = si->si;
+       rr->ho_rot = si->rot;
+       rr->ho_nci = si->nci;
+}
+
+/* receiving HANDOVER COMMAND message (9.1.15) */
 static int gsm_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm_rrlayer *rr = ms->rrlayer;
        struct gsm48_hdr *gh = msgb_l3(msg);
-       int payload_len = msgb_l3len(msg) - sizeof(*gh);
+       struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *)gh->data;
+       int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ho);
+       struct tlv_parsed tp;
+       struct gsm_rr_chan_desc cd;
+
+       memset(&cd, 0, sizeof(cd));
+
+       if (payload_len < 0) {
+               DEBUGP(DRR, "Short read of HANDOVER COMMAND message.\n");
+               return gsm_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+       }
+       tlv_parse(&tp, &rsl_att_tlvdef, ho->data, payload_len, 0, 0);
+
+       /* decode Cell Description */
+       gsm_decode_cell_desc(&ho->cell_desc, &cd.bcch_arfcn, &cd.ncc, &cd.bcc);
+       /* Channel Description */
+       memcpy(&rr->chan_desc.chan_desc, ho->chan_desc, 3);
+       /* Handover Reference */
+       rr->hando_ref = ho->ho_ref;
+       /* Power Command and access type */
+       gsm_decode_power_cmd_acc((struct gsm48_power_cmd *)&ho->power_command,
+               &cd.power_level, cd.atc);
+       /* Synchronization Indication */
+       if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND))
+               gsm48_decode_sync_ind(rr,
+                       TLVP_VAL(&tp, GSM48_IE_MOBILE_ALLOC)-1, &cd);
+       /* Frequency Sort List */
+       if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_SHORT_LIST))
+               gsm48_decode_freq_list(s->freq,
+                       TLVP_VAL(&tp, GSM48_IE_MOBILE_ALLOC),
+                       *(TLVP_VAL(&tp, GSM48_IE_MOBILE_ALLOC)-1),
+                               0xce, FREQ_TYPE_SERV);
+
+
+today: more IE parsing
 
-       parsing
+       /* store current channel descriptions, to return in case of failure */
+       memcpy(&rr->chan_last, &rr->chan_desc, sizeof(*cd));
+       /* copy new description */
+       memcpy(&rr->chan_desc, cd, sizeof(cd));
 
-       send dl suspend req
+       /* start suspension of current link */
+       nmsg = gsm48_rr_msgb_alloc();
+       if (!nmsg)
+               return -ENOMEM;
+       rslms_tx_rll_req_l3(ms, RSL_MT_SUSP_REQ, rr->chan_desc.chan_nr, 0, msg);
 
        /* change into special handover suspension state */
        rr->hando_susp_state = 1;
        rr->resume_last_state = 0;
+
+       return 0;
 }
 
 static int gsm_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
@@ -2362,15 +2675,15 @@ static int gsm_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
        if (rr->hando_susp_state || rr->assign_susp_state) {
                if (rr->resume_last_state) {
                        rr->resume_last_state = 0;
-                       gsm_rr_tx_ass_cpl(ms, cause);
-                       flush queued radio ressource messages
-
-                       return 0;
+                       gsm_rr_tx_ass_cpl(ms, GSM48_RR_CAUSE_NORMAL);
                } else {
                        gsm_rr_tx_ass_fail(ms, RR_CAUSE_PROTO_ERR_UNSPEC);
-                       return 0;
                }
+               /* transmit queued frames during ho / ass transition */
+               gsm_rr_dequeue_down(ms);
        }
+
+       return 0;
 }
 
 static int gsm_rr_connect_cnf(struct osmocom_ms *ms, struct msgbl *msg)
@@ -2391,15 +2704,15 @@ static int gsm_rr_rel_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
                /* change radio to new channel */
                tx_ph_dm_est_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
 
-               newmsg = gsm48_rr_msgb_alloc();
-               if (!newmsg)
+               nmsg = gsm48_rr_msgb_alloc();
+               if (!nmsg)
                        return -ENOMEM;
                /* send DL-ESTABLISH REQUEST */
-               rslms_tx_rll_req_l3(ms, RSL_MT_EST_REQ, rr->chan_desc.chan_desc.chan_nr, 0, newmsg);
+               rslms_tx_rll_req_l3(ms, RSL_MT_EST_REQ, rr->chan_desc.chan_desc.chan_nr, 0, nmsg);
 
        }
        if (rr->hando_susp_state) {
-               send HANDOVER ACCESS via DL_RANDOM_ACCESS_REQ
+               gsm_rr_tx_hando_access(ms);
                rr->hando_acc_left = 3;
        }
        return 0;
@@ -2408,8 +2721,8 @@ static int gsm_rr_rel_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
 static int gsm_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm_rrlayer *rr = ms->rrlayer;
-       struct msgb *newmsg;
-       struct gsm_mm_hdr *newmmh;
+       struct msgb *nmsg;
+       struct gsm_mm_hdr *nmmh;
 
        if (rr->hando_susp_state || rr->assign_susp_state) {
                if (!rr->resume_last_state) {
@@ -2421,7 +2734,11 @@ static int gsm_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
                        /* change radio to old channel */
                        tx_ph_dm_est_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
 
-                       return 0;
+                       /* re-establish old link */
+                       nmsg = gsm48_rr_msgb_alloc();
+                       if (!nmsg)
+                               return -ENOMEM;
+                       return rslms_tx_rll_req_l3(ms, RSL_MT_EST_REQ, rr->chan_desc.chan_desc.chan_nr, 0, nmsg);
                }
                rr->resume_last_state = 0;
        }
@@ -2430,19 +2747,19 @@ static int gsm_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
        tx_ph_dm_rel_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
 
        /* send abort ind to upper layer */
-       newmsg = gsm48_mm_msgb_alloc();
+       nmsg = gsm48_mm_msgb_alloc();
 
        if (!msg)
                return -ENOMEM;
-       newmmh = (struct gsm_mm_hdr *)newmsg->data;
-       newmmh->msg_type = RR_ABORT_IND;
-       newmmh->cause = GSM_MM_CAUSE_LINK_FAILURE;
-       return rr_rcvmsg(ms, msg);
+       nmmh = (struct gsm_mm_hdr *)nmsg->data;
+       nmmh->msg_type = RR_ABORT_IND;
+       nmmh->cause = GSM_MM_CAUSE_LINK_FAILURE;
+       return gsm48_mm_upmsg(ms, msg);
 }
 
 /* state trasitions for link layer messages (lower layer) */
 static struct dldatastate {
-       u_int32_t       states;
+       uint32_t        states;
        int             type;
        int             (*rout) (struct osmocom_ms *ms, struct gsm_dl *dlmsg);
 } dldatastatelist[] = {
@@ -2473,12 +2790,12 @@ static struct dldatastate {
 #define DLDATASLLEN \
        (sizeof(dldatastatelist) / sizeof(struct dldatastate))
 
-static int gsm_rcv_dl(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+static int gsm48_rcv_dl(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
 {
        int msg_type = dlmsg->msg_type;
 
        DEBUGP(DRR, "(ms %s) Received '%s' from DL in state %s\n", ms->name,
-               gsm0408_dl_msg_names[msg_type], mm_state_names[mm->state]);
+               gsm48_dl_msg_name(msg_type), mm_state_names[mm->state]);
 
        /* find function for current state and message */
        for (i = 0; i < DLDATASLLEN; i++)
@@ -2487,6 +2804,7 @@ static int gsm_rcv_dl(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
                        break;
        if (i == DLDATASLLEN) {
                DEBUGP(DRR, "Message unhandled at this state.\n");
+               free_msgb(msg);
                return 0;
        }
 
@@ -2499,10 +2817,41 @@ static int gsm_rcv_dl(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
        return rc;
 }
 
+/* dequeue messages from dl */
+int gsm48_rr_queue(struct osmocom_ms *ms)
+{
+       struct gsm_rrlayer *rr = ms->rrlayer;
+       struct msgb *msg;
+       int work = 0;
+       
+       while ((msg = msgb_dequeue(&rr->up_queue))) {
+               /* msg is freed there */
+               gsm48_rcv_dl(ms, msg);
+               work = 1; /* work done */
+       }
+       
+       return work;
+}
+
 static void timeout_rr_t3124(void *arg)
 {
        struct gsm_rrlayer *rr = arg;
 
+       /* stop sending more access bursts when timer expired */
+       hando_acc_left = 0;
+
+       /* get old channel description */
+       memcpy(&rr->chan_desc, &rr->chan_last, sizeof(*cd));
+
+       /* change radio to old channel */
+       tx_ph_dm_est_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
+
+       /* re-establish old link */
+       msg = gsm48_rr_msgb_alloc();
+       if (!msg)
+               return -ENOMEM;
+       return rslms_tx_rll_req_l3(ms, RSL_MT_EST_REQ, rr->chan_desc.chan_desc.chan_nr, 0, msg);
+
        todo
 }
 
@@ -2515,9 +2864,7 @@ struct gsm_rrlayer *gsm_new_rr(struct osmocom_ms *ms)
                return NULL;
        rr->ms = ms;
 
-       init queues
-
-       init timers
+       INIT_LLIST_HEAD(&rr->up_queue);
 
        return;
 }
@@ -2537,11 +2884,21 @@ todo stop t3122 when cell change
        return;
 }
 
+/* send HANDOVER ACCESS burst (9.1.14) */
+static int gsm_rr_tx_hando_access(struct osmocom_ms *ms)
+{
+       nmsg = msgb_alloc_headroom(20, 16, "HAND_ACCESS");
+       if (!nmsg)
+               return -ENOMEM;
+       *msgb_put(nmsg, 1) = rr->hando_ref;
+       return rslms_tx_rll_req_l3(ms, RSL_MT_RAND_ACC_REQ, chan_nr, 0, nmsg);
+}
+
 /* send next channel request in dedicated state */
 static int gsm_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
 {
        struct gsm_rrlayer *rr = ms->rrlayer;
-       struct msgb *newmsg;
+       struct msgb *nmsg;
        int s;
 
        if (!rr->hando_susp_state) {
@@ -2552,11 +2909,12 @@ static int gsm_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg
        /* send up to four handover access bursts */
        if (rr->hando_acc_left) {
                rr->hando_acc_left--;
-               send HANDOVER ACCESS via DL_RANDOM_ACCESS_REQ;
+               gsm_rr_tx_hando_access(ms);
                return;
        }
 
-       if (!timer 3124 running) {
+       /* start timer for sending next HANDOVER ACCESS bursts afterwards */
+       if (!timer_pending(&rr->t3124)) {
                if (allocated channel is SDCCH)
                        start_rr_t3124(rr, GSM_T3124_675);
                else
@@ -2567,43 +2925,8 @@ static int gsm_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg
        }
        rr->n_chan_req--;
 
-       /* table 3.1 */
-       switch(ms->si.tx_integer) {
-       case 3: case 8: case 14: case 50:
-               if (ms->si.bcch_type == GSM_NON_COMBINED_CCCH)
-                       s = 55;
-               else
-                       s = 41;
-       case 4: case 9: case 16:
-               if (ms->si.bcch_type == GSM_NON_COMBINED_CCCH)
-                       s = 76;
-               else
-                       s = 52;
-       case 5: case 10: case 20:
-               if (ms->si.bcch_type == GSM_NON_COMBINED_CCCH)
-                       s = 109;
-               else
-                       s = 58;
-       case 6: case 11: case 25:
-               if (ms->si.bcch_type == GSM_NON_COMBINED_CCCH)
-                       s = 163;
-               else
-                       s = 86;
-       default:
-               if (ms->si.bcch_type == GSM_NON_COMBINED_CCCH)
-                       s = 217;
-               else
-                       s = 115;
+       /* wait for PHYSICAL INFORMATION message or T3124 timeout */
+       return 0;
 
-       /* resend chan_req */
-       newmsg = msgb_alloc_headroom(20, 16, "CHAN_REQ");
-       if (!newmsg)
-               return -ENOMEM;
-       *msgb_put(newmsg, 1) = rr->chan_req;
-       *msgb_put(newmsg, 1) = (random() % ms->si.tx_integer) + s; /* delay */
-       rr->cr_hist[3] = rr->cr_hist[2];
-       rr->cr_hist[2] = rr->cr_hist[1];
-       rr->cr_hist[1] = chan_req;
-       return rslms_tx_rll_req_l3(ms, RSL_MT_RAND_ACC_REQ, chan_nr, 0, newmsg);
 }