/*
 * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
 *
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

/* 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.
 *
 * Group Channel:
 *
 * A group receive/transmit mode is not documented at GSM 04.07. The group
 * receive mode is similar to the IDLE mode, except that the MS is listening
 * to a TCH. The group transmit mode is similar to the DEDICATED mode. Special
 * (undocumented) commands are used to enter group receive and transmit mode.
 *
 * There is a substate that indicates group receive/transmit mode. If the
 * substate is set to group receive, the IDLE mode becomes the group receive
 * mode. If the substate is set to group transmit, the dedicated mode becomes
 * the group transmit mode. The substate set to group receive allows to enter
 * regular dedicated mode and return back to group receive mode afterwards.
 *
 * new_rr_state(rr, GSM48_RR_ST_IDLE) is used to return to IDLE mode or to
 * group receive mode, depending on the substate.
 *
 * The Uplink access on group channel:
 *
 * If the uplink is busy, wait until it becomes free (Uplink investigation
 * procedure). Abort, if the procedure times out, if the VGCS UPLINK GRANT
 * message is recived for a different talker.
 *
 * Request uplink using access bursts on TCH until an VGCS UPLINK GRANT is
 * received. Abort, if it the procedure times out, if the uplink becomes busy,
 * if the VGCS UPLINK GRANT message references a different frame numer.
 *
 * Establish layer 2 with TALKER INDICATION. Abort, if content resolution
 * mismatches (RR_REL_IND), if link fails (MDL_ERROR), if uplink becomes free.
 *
 * Release uplink and wait until uplink becomes free. Abort, if UPLINK RELEASE
 * is received or if uplink fails.
 *
 * New primitives are invented for group/broadcast calls. They are not
 * specified in any recommendation. They are:
 *
 * GSM48_MM_EVENT_NOTIFICATION: Notify MM layer about received/ceased call.
 * GSM48_MM_EVENT_UPLINK_BUSY: Notify MM layer about uplink becoming busy.
 * GSM48_MM_EVENT_UPLINK_FREE: Notify MM layer about uplink becoming free.
 *
 * RR_GROUP_REQ: The MM layer requests group channel in receive mode.
 * RR_GROUP_CNF: The RR confirms group channel.
 * RR_GROUP_REL_REQ: The MM layer releases group channel.
 * RR_GROUP_REL_IND: The RR indicates/confirms release of group channel.
 * RR_UPLINK_REQ: The MM layer requests uplink (group transmit mode).
 * RR_UPLINK_CNF: The RR layer confirms uplink. (Uplink was granted.)
 * RR_UPLINK_REL_REQ: The MM layer requests release of uplink.
 * RR_UPLINK_REL_IND: The RR layer indicates/confirms release of uplink
 */

/* Testing delayed (immediate) assignment / handover
 *
 * When enabled, the starting time will be set by given frames in the future.
 * If a starting time is given by the network, this time is ignored.
 */
//#define TEST_STARTING_TIMER 140

/* Testing if frequency modification works correctly "after time".
 *
 * When enabled, the starting time will be set in the future.
 * A wrong channel is defined "before time", so noise is received until
 * starting time elapses.
 * If a starting time is given by the network, this time is ignored.
 * Also channel definitions "before time" are ignored.
 *
 * NOTE: TEST_STARTING_TIMER MUST be defined also.
 */
//#define TEST_FREQUENCY_MOD

#include <stdint.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>

#include <osmocom/core/msgb.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/rsl.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/core/bitvec.h>

#include <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/l1l2_interface.h>
#include <osmocom/bb/common/l23_app.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/networks.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/utils.h>
#include <osmocom/bb/common/settings.h>

#include <osmocom/bb/mobile/vty.h>
#include <osmocom/bb/mobile/gsm48_rr.h>

#include <l1ctl_proto.h>

/* Check response for the last 3 channel requests only. See TS 44.018 §3.3.1.1.3.1 and §3.3.1.1.3.2. */
#define IMM_ASS_HISTORY		3
/* Check response for up to 5 uplink requests. See TS 44.018 §3.3.1.2.1.2. */
#define UL_GRANT_HISTROY	5

static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t *ma_len);
static int gsm48_rr_activate_channel(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len);
static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro);
static void stop_rr_t_starting(struct gsm48_rrlayer *rr);
static void stop_rr_t3124(struct gsm48_rrlayer *rr);
static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg);
static int gsm48_rr_dl_est(struct osmocom_ms *ms);
static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms);
static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, uint8_t mode, uint8_t tch_flags);
static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg);
int gsm414_rcv_test(struct osmocom_ms *ms, const struct msgb *msg);
static int gsm48_rr_group_rel(struct osmocom_ms *ms, int cause);
static int gsm48_rr_uplink_access(struct osmocom_ms *ms, struct msgb *msg);
static int gsm48_rr_uplink_access_abort(struct osmocom_ms *ms, uint8_t cause);
static int gsm48_rr_uplink_status(struct osmocom_ms *ms, uint32_t event);
static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref, uint8_t hist_num);
static int gsm48_rr_tx_talker_indication(struct osmocom_ms *ms);
static int gsm48_rr_tx_rand_acc_dedicated(struct osmocom_ms *ms, uint8_t ref, uint16_t offset, int8_t uic);
static int gsm48_rr_uplink_rel_req(struct osmocom_ms *ms, struct msgb *msg);

/*
 * support
 */

/* 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;

	return 0;
}

/* 10.5.2.38 decode Starting time IE */
static int gsm48_decode_start_time(struct gsm48_rr_cd *cd,
	struct gsm48_start_time *st)
{
	cd->start = 1;
	cd->start_tm.t1 = st->t1;
	cd->start_tm.t2 = st->t2;
	cd->start_tm.t3 = (st->t3_high << 3) | st->t3_low;
	cd->start_tm.fn = gsm_gsmtime2fn(&cd->start_tm);

	return 0;
}

/* decode "BA Range" (10.5.2.1a) */
static int gsm48_decode_ba_range(const uint8_t *ba, uint8_t ba_len,
	uint32_t *range, uint8_t *ranges, int max_ranges)
{
	/* ba = pointer to IE without IE type and length octets
	 * ba_len = number of octets
	 * range = pointer to store decoded range
	 * ranges = number of ranges decoded
	 * max_ranges = maximum number of decoded ranges that can be stored
	 */
	uint16_t lower, higher;
	int i, n, required_octets;

	/* find out how much ba ranges will be decoded */
	n = *ba++;
	ba_len --;
	required_octets = 5 * (n >> 1) + 3 * (n & 1);
	if (required_octets > ba_len) {
		LOGP(DRR, LOGL_NOTICE, "BA range IE too short: %d ranges "
			"require %d octets, but only %d octets remain.\n",
			n, required_octets, ba_len);
		*ranges = 0;
		return -EINVAL;
	}

	if (n > max_ranges) {
		LOGP(DRR, LOGL_NOTICE, "BA range %d exceed the maximum number "
			"of ranges supported by this mobile (%d).\n",
			n, max_ranges);
		n = max_ranges;
	}

	/* decode ranges */
	for (i = 0; i < n; i++) {
		if (!(i & 1)) {
			/* decode even range number */
			lower = *ba++ << 2;
			lower |= (*ba >> 6);
			higher = (*ba++ & 0x3f) << 4;
			higher |= *ba >> 4;
		} else {
			lower = (*ba++ & 0x0f) << 6;
			lower |= *ba >> 2;
			higher = (*ba++ & 0x03) << 8;
			higher |= *ba++;
			/* decode odd range number */
		}
		*range++ = (higher << 16) | lower;
	}
	*ranges = n;

	return 0;
}

/* 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->arfcn_hi << 8) + cd->arfcn_lo;
	*ncc = cd->ncc;
	*bcc = cd->bcc;

	return 0;
}

/* decode "Synchronization Indication" (10.5.2.39) */
static int gsm48_decode_sync_ind(struct gsm48_rrlayer *rr,
	struct gsm48_sync_ind *si)
{
	rr->hando_sync_ind = si->si;
	rr->hando_rot = si->rot;
	rr->hando_nci = si->nci;

	return 0;
}

/* 3.1.4.3 set sequence number and increment */
static int gsm48_apply_v_sd(struct gsm48_rrlayer *rr, struct msgb *msg)
{
	struct gsm48_hdr *gh = msgb_l3(msg);
	uint8_t pdisc = gh->proto_discr & 0x0f;
	uint8_t v_sd;

	switch (pdisc) {
	case GSM48_PDISC_MM:
	case GSM48_PDISC_CC:
	case GSM48_PDISC_NC_SS:
		/* all three pdiscs share the same V(SD) */
		pdisc = GSM48_PDISC_MM;
		// fall through
	case GSM48_PDISC_GROUP_CC:
	case GSM48_PDISC_BCAST_CC:
	case GSM48_PDISC_PDSS1:
	case GSM48_PDISC_PDSS2:
		/* extract v_sd(pdisc) */
		v_sd = (rr->v_sd >> pdisc) & 1;

		/* replace bit 7 vy v_sd */
		gh->msg_type &= 0xbf;
		gh->msg_type |= (v_sd << 6);

		/* increment V(SD) */
		rr->v_sd ^= (1 << pdisc);
		LOGP(DRR, LOGL_INFO, "Using and incrementing V(SD) = %d "
			"(pdisc %x)\n", v_sd, pdisc);
		break;
	case GSM48_PDISC_RR:
	case GSM48_PDISC_SMS:
		/* no V(VSD) is required */
		break;
	default:
		LOGP(DRR, LOGL_ERROR, "Error, V(SD) of pdisc %x not handled\n",
			pdisc);
		return -ENOTSUP;
	}

	return 0;
}

/* set channel mode if supported, or return error cause */
static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr,
	uint8_t mode)
{
	struct gsm_settings *set = &ms->settings;
	uint8_t ch_type, ch_subch, ch_ts;

	if (rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRR, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, chan_nr);
		return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
	}

	/* only complain if we use TCH/F or TCH/H */
	if (ch_type != RSL_CHAN_Bm_ACCHs
	 && ch_type != RSL_CHAN_Lm_ACCHs)
		return 0;

	switch (mode) {
	case GSM48_CMODE_SIGN:
		LOGP(DRR, LOGL_INFO, "Mode: signalling\n");
		break;
	case GSM48_CMODE_SPEECH_V1:
		if (ch_type == RSL_CHAN_Bm_ACCHs) {
			if (!set->full_v1) {
				LOGP(DRR, LOGL_NOTICE, "Not supporting "
					"full-rate speech V1\n");
				return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
			}
			LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V1\n");
		} else {
			if (!set->half_v1) {
				LOGP(DRR, LOGL_NOTICE, "Not supporting "
					"half-rate speech V1\n");
				return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
			}
			LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V1\n");
		}
		break;
	case GSM48_CMODE_SPEECH_EFR:
		if (ch_type == RSL_CHAN_Bm_ACCHs) {
			if (!set->full_v2) {
				LOGP(DRR, LOGL_NOTICE, "Not supporting "
					"full-rate speech V2\n");
				return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
			}
			LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V2\n");
		} else {
			LOGP(DRR, LOGL_NOTICE, "Not supporting "
					"half-rate speech V2\n");
			return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
		}
		break;
	case GSM48_CMODE_SPEECH_AMR:
		if (ch_type == RSL_CHAN_Bm_ACCHs) {
			if (!set->full_v3) {
				LOGP(DRR, LOGL_NOTICE, "Not supporting "
					"full-rate speech V3\n");
				return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
			}
			LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V3\n");
		} else {
			if (!set->half_v3) {
				LOGP(DRR, LOGL_NOTICE, "Not supporting "
					"half-rate speech V3\n");
				return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
			}
			LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V3\n");
		}
		break;
	case GSM48_CMODE_DATA_14k5:
		if (ch_type != RSL_CHAN_Bm_ACCHs) {
			LOGP(DRR, LOGL_ERROR, "TCH/F is expected for mode %s\n",
			     gsm48_chan_mode_name(mode));
			return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
		} else if (!set->csd_tch_f144) {
			LOGP(DRR, LOGL_ERROR, "Not supporting TCH/F14.4 data (%s)\n",
			     gsm48_chan_mode_name(mode));
			return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
		}
		LOGP(DRR, LOGL_INFO, "Mode: TCH/F14.4 data (%s)\n",
		     gsm48_chan_mode_name(mode));
		break;
	case GSM48_CMODE_DATA_12k0:
		if (ch_type != RSL_CHAN_Bm_ACCHs) {
			LOGP(DRR, LOGL_ERROR, "TCH/F is expected for mode %s\n",
			     gsm48_chan_mode_name(mode));
			return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
		} else if (!set->csd_tch_f96) {
			LOGP(DRR, LOGL_ERROR, "Not supporting TCH/F9.6 data (%s)\n",
			     gsm48_chan_mode_name(mode));
			return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
		}
		LOGP(DRR, LOGL_INFO, "Mode: TCH/F9.6 data (%s)\n",
		     gsm48_chan_mode_name(mode));
		break;
	case GSM48_CMODE_DATA_6k0:
		if ((ch_type == RSL_CHAN_Bm_ACCHs && !set->csd_tch_f48)
		 || (ch_type == RSL_CHAN_Lm_ACCHs && !set->csd_tch_h48)) {
			LOGP(DRR, LOGL_ERROR, "Not supporting TCH/%c4.8 data (%s)\n",
			     ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H',
			     gsm48_chan_mode_name(mode));
			return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
		}
		LOGP(DRR, LOGL_INFO, "Mode: TCH/%c4.8 data (%s)\n",
		     ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H',
		     gsm48_chan_mode_name(mode));
		break;
	case GSM48_CMODE_DATA_3k6:
		if ((ch_type == RSL_CHAN_Bm_ACCHs && !set->csd_tch_f24)
		 || (ch_type == RSL_CHAN_Lm_ACCHs && !set->csd_tch_h24)) {
			LOGP(DRR, LOGL_ERROR, "Not supporting TCH/%c2.4 data (%s)\n",
			     ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H',
			     gsm48_chan_mode_name(mode));
			return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
		}
		LOGP(DRR, LOGL_INFO, "Mode: TCH/%c2.4 data (%s)\n",
		     ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H',
		     gsm48_chan_mode_name(mode));
		break;
	default:
		LOGP(DRR, LOGL_ERROR, "Mode 0x%02x not supported!\n", mode);
		return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
	}

	return 0;
}

/* apply new "alter_delay" in dedicated mode */
int gsm48_rr_alter_delay(struct osmocom_ms *ms)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm_settings *set = &ms->settings;

	if (rr->state != GSM48_RR_ST_DEDICATED)
		return -EINVAL;
	l1ctl_tx_param_req(ms, rr->cd_now.ind_ta - set->alter_delay,
		(set->alter_tx_power) ? set->alter_tx_power_value
					: rr->cd_now.ind_tx_power);

	return 0;
}

/*
 * state transition
 */

const char *gsm48_rr_state_names[] = {
	"idle",
	"connection pending",
	"dedicated",
	"release pending",
};

static void new_rr_state(struct gsm48_rrlayer *rr, int state)
{
	struct osmocom_ms *ms = rr->ms;

	if (state < 0 || state >=
		(sizeof(gsm48_rr_state_names) / sizeof(char *)))
		return;

	/* Check against equal state or IDLE state. */
	if (rr->state == state && state != GSM48_RR_ST_IDLE) {
		LOGP(DRR, LOGL_INFO, "equal state ? %s\n",
			gsm48_rr_state_names[rr->state]);
		return;
	}

	LOGP(DRR, LOGL_INFO, "new state %s -> %s\n",
	     gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]);

	/* abort handover, in case of release of dedicated mode */
	if (rr->state == GSM48_RR_ST_DEDICATED) {
		/* disable handover / assign state */
		rr->modify_state = GSM48_RR_MOD_NONE;
		/* stop start_time_timer */
		stop_rr_t_starting(rr);
		/* stop handover timer */
		stop_rr_t3124(rr);
	}

	rr->state = state;

	if (state != GSM48_RR_ST_IDLE)
		return;

	/* Return from dedicated/group receive/transmit mode to idle mode. (Trigger cell reselection.) */
	if (rr->vgcs.group_state == GSM48_RR_GST_OFF) {
		struct msgb *msg, *nmsg;
		struct gsm322_msg *em;

		LOGP(DRR, LOGL_INFO, "Returning to IDLE mode.\n");
		/* release dedicated mode, if any */
		l1ctl_tx_dm_rel_req(rr->ms);
		rr->ms->meas.rl_fail = 0;
		rr->dm_est = 0;
		l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_FULL);
		/* free establish message, if any */
		rr->rr_est_req = 0;
		if (rr->rr_est_msg) {
			msgb_free(rr->rr_est_msg);
			rr->rr_est_msg = NULL;
		}
		/* free all pending messages */
		while((msg = msgb_dequeue(&rr->downqueue)))
			msgb_free(msg);
		/* clear all descriptions of last channel */
		memset(&rr->cd_now, 0, sizeof(rr->cd_now));
		/* reset ciphering */
		rr->cipher_on = 0;
		/* reset audio mode */
		/* tell cell selection process to return to idle mode
		 * NOTE: this must be sent unbuffered, because it will
		 * leave camping state, so it locks against subsequent
		 * establishment of dedicated channel, before the
		 * cell selection process returned to camping state
		 * again. (after cell reselection)
		 */
		nmsg = gsm322_msgb_alloc(GSM322_EVENT_RET_IDLE);
		if (!nmsg)
			return;
		/* return to same cell after LOC.UPD. */
		if (rr->est_cause == RR_EST_CAUSE_LOC_UPD) {
			em = (struct gsm322_msg *) nmsg->data;
			em->same_cell = 1;
		}
		gsm322_c_event(rr->ms, nmsg);
		msgb_free(nmsg);
		/* reset any BA range */
		rr->ba_ranges = 0;
		return;
	}

	/* Return from dedicated mode to group receive mode.
	 * This is not used, because we never enter dedicated mode during group receive/transmit mode.
	 * It may be used later, if we support it in MM layer. */
	if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) {
		uint16_t ma[64];
		uint8_t ma_len;
		struct msgb *msg;

		LOGP(DRR, LOGL_INFO, "Returning to GROUP RECEIVE mode.\n");
		/* release dedicated mode, if any */
		l1ctl_tx_dm_rel_req(rr->ms);
		rr->ms->meas.rl_fail = 0;
		rr->dm_est = 0;
		l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_SCHED);
		/* free establish message, if any */
		rr->rr_est_req = 0;
		if (rr->rr_est_msg) {
			msgb_free(rr->rr_est_msg);
			rr->rr_est_msg = NULL;
		}
		/* free all pending messages */
		while ((msg = msgb_dequeue(&rr->downqueue)))
			msgb_free(msg);
		/* reset ciphering */
		rr->cipher_on = 0;
		/* copy channel description "group mode" */
		memcpy(&rr->cd_now, &rr->vgcs.cd_group, sizeof(rr->cd_now));
		/* render channel "group mode" */
		gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
		/* activate channel */
		gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
		return;
	}
}

const char *gsm48_sapi3_state_names[] = {
	"idle",
	"wait establishment",
	"established",
	"wait release",
};

static void new_sapi3_state(struct gsm48_rrlayer *rr, int state)
{
	if (state < 0 || state >=
		(sizeof(gsm48_sapi3_state_names) / sizeof(char *)))
		return;

	LOGP(DRR, LOGL_INFO, "new SAPI 3 link state %s -> %s\n",
		gsm48_sapi3_state_names[rr->sapi3_state],
		gsm48_sapi3_state_names[state]);

	rr->sapi3_state = state;
}

/*
 * messages
 */

/* names of RR-SAP */
static const struct value_string gsm48_rr_msg_names[] = {
	{ GSM48_RR_EST_REQ,		"RR_EST_REQ" },
	{ GSM48_RR_EST_IND,		"RR_EST_IND" },
	{ GSM48_RR_EST_CNF,		"RR_EST_CNF" },
	{ GSM48_RR_REL_IND,		"RR_REL_IND" },
	{ GSM48_RR_SYNC_IND,		"RR_SYNC_IND" },
	{ GSM48_RR_DATA_REQ,		"RR_DATA_REQ" },
	{ GSM48_RR_DATA_IND,		"RR_DATA_IND" },
	{ GSM48_RR_UNIT_DATA_IND,	"RR_UNIT_DATA_IND" },
	{ GSM48_RR_ABORT_REQ,		"RR_ABORT_REQ" },
	{ GSM48_RR_ABORT_IND,		"RR_ABORT_IND" },
	{ GSM48_RR_ACT_REQ,		"RR_ACT_REQ" },
	{ GSM48_RR_GROUP_REQ,		"RR_GROUP_REQ" },
	{ GSM48_RR_GROUP_CNF,		"RR_GROUP_CNF" },
	{ GSM48_RR_GROUP_REL_REQ,	"RR_GROUP_REL_REQ" },
	{ GSM48_RR_GROUP_REL_IND,	"RR_GROUP_REL_IND" },
	{ GSM48_RR_UPLINK_REQ,		"RR_UPLINK_REQ" },
	{ GSM48_RR_UPLINK_CNF,		"RR_UPLINK_CNF" },
	{ GSM48_RR_UPLINK_REL_REQ,	"RR_UPLINK_REL_REQ" },
	{ GSM48_RR_UPLINK_REL_IND,	"RR_UPLINK_REL_IND" },
	{ 0,				NULL }
};

const char *get_rr_name(int value)
{
	return get_value_string(gsm48_rr_msg_names, value);
}

/* allocate GSM 04.08 layer 3 message */
struct msgb *gsm48_l3_msgb_alloc(void)
{
	struct msgb *msg;

	msg = msgb_alloc_headroom(L3_ALLOC_SIZE+L3_ALLOC_HEADROOM,
		L3_ALLOC_HEADROOM, "GSM 04.08 L3");
	if (!msg)
		return NULL;
	msg->l3h = msg->data;

	return msg;
}

/* allocate GSM 04.06 layer 2 RSL message */
struct msgb *gsm48_rsl_msgb_alloc(void)
{
	struct msgb *msg;

	msg = msgb_alloc_headroom(RSL_ALLOC_SIZE+RSL_ALLOC_HEADROOM,
		RSL_ALLOC_HEADROOM, "GSM 04.06 RSL");
	if (!msg)
		return NULL;
	msg->l2h = msg->data;

	return msg;
}

/* allocate GSM 04.08 message (RR-SAP) */
struct msgb *gsm48_rr_msgb_alloc(int msg_type)
{
	struct msgb *msg;
	struct gsm48_rr_hdr *rrh;

	msg = msgb_alloc_headroom(RR_ALLOC_SIZE+RR_ALLOC_HEADROOM,
		RR_ALLOC_HEADROOM, "GSM 04.08 RR");
	if (!msg)
		return NULL;

	rrh = (struct gsm48_rr_hdr *) msgb_put(msg, sizeof(*rrh));
	rrh->msg_type = msg_type;

	return msg;
}

/* queue message (RR-SAP) */
int gsm48_rr_upmsg(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_mmlayer *mm = &ms->mmlayer;

	msgb_enqueue(&mm->rr_upqueue, msg);

	return 0;
}

/* push rsl header and send (RSL-SAP) */
int gsm48_send_rsl(struct osmocom_ms *ms, uint8_t msg_type,
		   struct msgb *msg, uint8_t link_id)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;

	if (!msg->l3h) {
		LOGP(DRR, LOGL_ERROR, "FIX l3h\n");
		return -EINVAL;
	}
	rsl_rll_push_l3(msg, msg_type, rr->cd_now.chan_nr, link_id, 1);

	return lapdm_rslms_recvmsg(msg, &ms->lapdm_channel);
}

/* push rsl header without L3 info and send (RSL-SAP) */
static int gsm48_send_rsl_nol3(struct osmocom_ms *ms, uint8_t msg_type,
				struct msgb *msg, uint8_t link_id)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;

	rsl_rll_push_hdr(msg, msg_type, rr->cd_now.chan_nr,
		link_id, 1);

	return lapdm_rslms_recvmsg(msg, &ms->lapdm_channel);
}

/* enqueue messages (RSL-SAP) */
static int rcv_rsl(struct msgb *msg, struct lapdm_entity *le, void *l3ctx)
{
	struct osmocom_ms *ms = l3ctx;
	struct gsm48_rrlayer *rr = &ms->rrlayer;

	msgb_enqueue(&rr->rsl_upqueue, msg);

	return 0;
}

/* dequeue messages (RSL-SAP) */
int gsm48_rsl_dequeue(struct osmocom_ms *ms)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct msgb *msg;
	int work = 0;

	while ((msg = msgb_dequeue(&rr->rsl_upqueue))) {
		/* msg is freed there */
		gsm48_rcv_rsl(ms, msg);
		work = 1; /* work done */
	}

	return work;
}

int gsm48_rr_start_monitor(struct osmocom_ms *ms)
{
	ms->rrlayer.monitor = 1;

	return 0;
}

int gsm48_rr_stop_monitor(struct osmocom_ms *ms)
{
	ms->rrlayer.monitor = 0;

	return 0;
}

/* release L3 link in both directions in case of main link release */
static int gsm48_release_sapi3_link(struct osmocom_ms *ms)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm48_rr_hdr *nrrh;
	struct msgb *nmsg;
	uint8_t *mode;

	if (rr->sapi3_state == GSM48_RR_SAPI3ST_IDLE)
		return 0;

	LOGP(DRR, LOGL_INFO, "Main signallin link is down, so release SAPI 3 "
		"link locally.\n");

	new_sapi3_state(rr, GSM48_RR_SAPI3ST_IDLE);

	/* disconnect the SAPI 3 signalling link */
	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	mode = msgb_put(nmsg, 2);
	mode[0] = RSL_IE_RELEASE_MODE;
	mode[1] = 1; /* local release */
	gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, rr->sapi3_link_id);

	/* send inication to upper layer */
	nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
	if (!nmsg)
		return -ENOMEM;
	nrrh = (struct gsm48_rr_hdr *)nmsg->data;
	nrrh->cause = RR_REL_CAUSE_NORMAL;
	nrrh->sapi = rr->sapi3_link_id & 7;
	gsm48_rr_upmsg(ms, nmsg);

	return 0;
}

/*
 * timers handling
 */

/* special timer to monitor measurements */
static void timeout_rr_meas(void *arg)
{
	struct gsm48_rrlayer *rr = arg;
	struct gsm322_cellsel *cs = &rr->ms->cellsel;
	struct rx_meas_stat *meas = &rr->ms->meas;
	struct gsm_settings *set = &rr->ms->settings;
	int rxlev, berr, snr;
	uint8_t ch_type, ch_subch, ch_ts;
	struct osmo_strbuf sb;
	char text[256];

	sb = (struct osmo_strbuf) { .buf = text, .len = sizeof(text) };

	/* don't monitor if no cell is selected or if we scan neighbour cells */
	if (!cs->selected || cs->neighbour) {
		OSMO_STRBUF_PRINTF(sb, "MON: not camping on serving cell");
		goto restart;
	} else if (!meas->frames) {
		OSMO_STRBUF_PRINTF(sb, "MON: no cell info");
	} else {
		rxlev = (meas->rxlev + meas->frames / 2) / meas->frames;
		berr = (meas->berr + meas->frames / 2) / meas->frames;
		snr = (meas->snr + meas->frames / 2) / meas->frames;
		OSMO_STRBUF_PRINTF(sb, "MON: f=%d lev=%s snr=%2d ber=%3d "
				   "CGI=%s", cs->sel_arfcn,
				   gsm_print_rxlev(rxlev), snr, berr,
				   osmo_cgi_name(&cs->sel_cgi));
		if (rr->state == GSM48_RR_ST_DEDICATED) {
			OSMO_STRBUF_PRINTF(sb, " TA=%d pwr=%d",
					   rr->cd_now.ind_ta - set->alter_delay,
					   (set->alter_tx_power) ? set->alter_tx_power_value
								 : rr->cd_now.ind_tx_power);
			if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) == 0) {
				OSMO_STRBUF_PRINTF(sb, " TS=%d", ch_ts);
				if (ch_type == RSL_CHAN_SDCCH8_ACCH
				 || ch_type == RSL_CHAN_SDCCH4_ACCH
				 || ch_type == RSL_CHAN_Lm_ACCHs)
					OSMO_STRBUF_PRINTF(sb, "/%d", ch_subch);
			}
		} else
			gsm322_meas(rr->ms, rxlev);
	}
	LOGP(DRR, LOGL_INFO, "%s\n", text);
	if (rr->monitor)
		l23_vty_ms_notify(rr->ms, "%s\n", text);

	if (rr->dm_est)
		gsm48_rr_tx_meas_rep(rr->ms);

restart:
	meas->frames = meas->snr = meas->berr = meas->rxlev = 0;
	start_rr_t_meas(rr, 1, 0);
}

/* special timer to assign / handover when starting time is reached */
static void timeout_rr_t_starting(void *arg)
{
	struct gsm48_rrlayer *rr = arg;
	struct msgb *nmsg;

	LOGP(DRR, LOGL_INFO, "starting timer has fired\n");

	/* open channel when starting timer of IMM.ASS has fired */
	if (rr->modify_state == GSM48_RR_MOD_IMM_ASS) {
		rr->modify_state = GSM48_RR_MOD_NONE;
		gsm48_rr_dl_est(rr->ms);
		return;
	}

	/* start suspension of current link */
	LOGP(DRR, LOGL_INFO, "request suspension of data link\n");
	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return;
	gsm48_send_rsl(rr->ms, RSL_MT_SUSP_REQ, nmsg, 0);

	/* release SAPI 3 link, if exits
	 * FIXME: suspend and resume afterward */
	gsm48_release_sapi3_link(rr->ms);
}

/* special timer to ensure that UA is sent before disconnecting channel */
static void timeout_rr_t_rel_wait(void *arg)
{
	struct gsm48_rrlayer *rr = arg;

	LOGP(DRR, LOGL_INFO, "L2 release timer has fired, done waiting\n");

	/* return to idle now */
	new_rr_state(rr, GSM48_RR_ST_IDLE);
}

/* 3.4.13.1.1: Timeout of T3110 */
static void timeout_rr_t3110(void *arg)
{
	struct gsm48_rrlayer *rr = arg;
	struct osmocom_ms *ms = rr->ms;
	struct msgb *nmsg;
	uint8_t *mode;

	LOGP(DRR, LOGL_INFO, "timer T3110 has fired, release locally\n");

	new_rr_state(rr, GSM48_RR_ST_REL_PEND);

	/* disconnect the main signalling link */
	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return;
	mode = msgb_put(nmsg, 2);
	mode[0] = RSL_IE_RELEASE_MODE;
	mode[1] = 1; /* local release */
	gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0);

	/* release SAPI 3 link, if exits */
	gsm48_release_sapi3_link(ms);

	return;
}

static void timeout_rr_t3122(void *arg)
{
	LOGP(DRR, LOGL_INFO, "timer T3122 has fired\n");
}

static void timeout_rr_t3126(void *arg)
{
	struct gsm48_rrlayer *rr = arg;
	struct osmocom_ms *ms = rr->ms;

	LOGP(DRR, LOGL_INFO, "timer T3126 has fired\n");
	if (rr->rr_est_req) {
		struct msgb *msg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
		struct gsm48_rr_hdr *rrh;

		LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n");
		if (!msg)
			return;
		rrh = (struct gsm48_rr_hdr *)msg->data;
		rrh->cause = RR_REL_CAUSE_RA_FAILURE;
		gsm48_rr_upmsg(ms, msg);
	}

	new_rr_state(rr, GSM48_RR_ST_IDLE);
}

static void timeout_rr_t3128(void *arg)
{
	struct gsm48_rrlayer *rr = arg;
	struct osmocom_ms *ms = rr->ms;

	LOGP(DRR, LOGL_INFO, "timer T3128 has fired\n");

	LOGP(DRR, LOGL_NOTICE, "Failed to access uplink, uplink did not become free.\n");
	gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY);
}

static void timeout_rr_t3130(void *arg)
{
	struct gsm48_rrlayer *rr = arg;
	struct osmocom_ms *ms = rr->ms;

	LOGP(DRR, LOGL_INFO, "timer T3130 has fired\n");

	gsm48_rr_uplink_access(ms, NULL);
}

static void timeout_rr_t_ul_free(void *arg)
{
	struct gsm48_rrlayer *rr = arg;

	LOGP(DRR, LOGL_INFO, "uplink free timer has fired\n");
	rr->vgcs.uplink_free = false;
	gsm48_rr_uplink_status(rr->ms, GSM48_MM_EVENT_UPLINK_BUSY);
}

static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro)
{
	rr->t_meas.cb = timeout_rr_meas;
	rr->t_meas.data = rr;
	osmo_timer_schedule(&rr->t_meas, sec, micro);
}

static void start_rr_t_rel_wait(struct gsm48_rrlayer *rr, int sec, int micro)
{
	LOGP(DRR, LOGL_INFO, "starting T_rel_wait with %d.%03d seconds\n", sec,
		micro / 1000);
	rr->t_rel_wait.cb = timeout_rr_t_rel_wait;
	rr->t_rel_wait.data = rr;
	osmo_timer_schedule(&rr->t_rel_wait, sec, micro);
}

static void start_rr_t_starting(struct gsm48_rrlayer *rr, int sec, int micro)
{
	LOGP(DRR, LOGL_INFO, "starting T_starting with %d.%03d seconds\n", sec,
		micro / 1000);
	rr->t_starting.cb = timeout_rr_t_starting;
	rr->t_starting.data = rr;
	osmo_timer_schedule(&rr->t_starting, sec, micro);
}

static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro)
{
	LOGP(DRR, LOGL_INFO, "starting T3110 with %d.%03d seconds\n", sec,
		micro / 1000);
	rr->t3110.cb = timeout_rr_t3110;
	rr->t3110.data = rr;
	osmo_timer_schedule(&rr->t3110, sec, micro);
}

static void start_rr_t3122(struct gsm48_rrlayer *rr, int sec, int micro)
{
	LOGP(DRR, LOGL_INFO, "starting T3122 with %d.%03d seconds\n", sec,
		micro / 1000);
	rr->t3122.cb = timeout_rr_t3122;
	rr->t3122.data = rr;
	osmo_timer_schedule(&rr->t3122, sec, micro);
}

static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro)
{
	LOGP(DRR, LOGL_INFO, "starting T3126 with %d.%03d seconds\n", sec,
		micro / 1000);
	rr->t3126.cb = timeout_rr_t3126;
	rr->t3126.data = rr;
	osmo_timer_schedule(&rr->t3126, sec, micro);
}

static void start_rr_t3128(struct gsm48_rrlayer *rr, int sec, int micro)
{
	LOGP(DRR, LOGL_INFO, "starting T3128 with %d.%03d seconds\n", sec,
		micro / 1000);
	rr->vgcs.t3128.cb = timeout_rr_t3128;
	rr->vgcs.t3128.data = rr;
	osmo_timer_schedule(&rr->vgcs.t3128, sec, micro);
}

static void start_rr_t3130(struct gsm48_rrlayer *rr, int sec, int micro)
{
	LOGP(DRR, LOGL_INFO, "starting T3130 with %d.%03d seconds\n", sec,
		micro / 1000);
	rr->vgcs.t3130.cb = timeout_rr_t3130;
	rr->vgcs.t3130.data = rr;
	osmo_timer_schedule(&rr->vgcs.t3130, sec, micro);
}

static void start_rr_t_ul_free(struct gsm48_rrlayer *rr)
{
	if (!osmo_timer_pending(&rr->vgcs.t_ul_free))
		LOGP(DRR, LOGL_INFO, "starting uplink free timer\n");
	rr->vgcs.t_ul_free.cb = timeout_rr_t_ul_free;
	rr->vgcs.t_ul_free.data = rr;
	osmo_timer_schedule(&rr->vgcs.t_ul_free, 0, 480000);
}

static void stop_rr_t_meas(struct gsm48_rrlayer *rr)
{
	if (osmo_timer_pending(&rr->t_meas)) {
		LOGP(DRR, LOGL_INFO, "stopping pending timer T_meas\n");
		osmo_timer_del(&rr->t_meas);
	}
}

static void stop_rr_t_starting(struct gsm48_rrlayer *rr)
{
	if (osmo_timer_pending(&rr->t_starting)) {
		LOGP(DRR, LOGL_INFO, "stopping pending timer T_starting\n");
		osmo_timer_del(&rr->t_starting);
	}
}

static void stop_rr_t_rel_wait(struct gsm48_rrlayer *rr)
{
	if (osmo_timer_pending(&rr->t_rel_wait)) {
		LOGP(DRR, LOGL_INFO, "stopping pending timer T_rel_wait\n");
		osmo_timer_del(&rr->t_rel_wait);
	}
}

static void stop_rr_t3110(struct gsm48_rrlayer *rr)
{
	if (osmo_timer_pending(&rr->t3110)) {
		LOGP(DRR, LOGL_INFO, "stopping pending timer T3110\n");
		osmo_timer_del(&rr->t3110);
	}
}

static void stop_rr_t3122(struct gsm48_rrlayer *rr)
{
	if (osmo_timer_pending(&rr->t3122)) {
		LOGP(DRR, LOGL_INFO, "stopping pending timer T3122\n");
		osmo_timer_del(&rr->t3122);
	}
}

static void stop_rr_t3124(struct gsm48_rrlayer *rr)
{
	if (osmo_timer_pending(&rr->t3124)) {
		LOGP(DRR, LOGL_INFO, "stopping pending timer T3124\n");
		osmo_timer_del(&rr->t3124);
	}
}

static void stop_rr_t3126(struct gsm48_rrlayer *rr)
{
	if (osmo_timer_pending(&rr->t3126)) {
		LOGP(DRR, LOGL_INFO, "stopping pending timer T3126\n");
		osmo_timer_del(&rr->t3126);
	}
}

static void stop_rr_t3128(struct gsm48_rrlayer *rr)
{
	if (osmo_timer_pending(&rr->vgcs.t3128)) {
		LOGP(DRR, LOGL_INFO, "stopping pending timer T3128\n");
		osmo_timer_del(&rr->vgcs.t3128);
	}
}

static void stop_rr_t3130(struct gsm48_rrlayer *rr)
{
	if (osmo_timer_pending(&rr->vgcs.t3130)) {
		LOGP(DRR, LOGL_INFO, "stopping pending timer T3130\n");
		osmo_timer_del(&rr->vgcs.t3130);
	}
}

static void stop_rr_t_ul_free(struct gsm48_rrlayer *rr)
{
	if (osmo_timer_pending(&rr->vgcs.t_ul_free)) {
		LOGP(DRR, LOGL_INFO, "stopping pending uplink free timer\n");
		osmo_timer_del(&rr->vgcs.t_ul_free);
	}
}

/*
 * status
 */

/* send rr status request */
int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause)
{
	struct msgb *nmsg;
	struct gsm48_hdr *gh;
	struct gsm48_rr_status *st;

	LOGP(DRR, LOGL_INFO, "RR STATUS (cause #%d)\n", cause);

	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
	st = (struct gsm48_rr_status *) msgb_put(nmsg, sizeof(*st));

	gh->proto_discr = GSM48_PDISC_RR;
	gh->msg_type = GSM48_MT_RR_STATUS;

	/* rr cause */
	st->rr_cause = cause;

	return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0);
}

/*
 * ciphering
 */

/* send chiperhing mode complete */
static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr)
{
	struct msgb *nmsg;
	struct gsm48_hdr *gh;
	struct gsm48_rr_hdr *nrrh;

	LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMPLETE (cr %d)\n", cr);

	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));

	gh->proto_discr = GSM48_PDISC_RR;
	gh->msg_type = GSM48_MT_RR_CIPH_M_COMPL;

	/* MI */
	if (cr)
		gsm48_encode_mi_tlv(ms, nmsg, GSM_MI_TYPE_IMEISV, false);

	gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0);

	/* send RR_SYNC_IND(ciphering) */
	nmsg = gsm48_rr_msgb_alloc(GSM48_RR_SYNC_IND);
	if (!nmsg)
		return -ENOMEM;
	nrrh = (struct gsm48_rr_hdr *)nmsg->data;
	nrrh->cause = RR_SYNC_CAUSE_CIPHERING;
	return gsm48_rr_upmsg(ms, nmsg);
}

/* receive ciphering mode command */
static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm_subscriber *subscr = &ms->subscr;
	struct gsm_settings *set = &ms->settings;
	struct gsm48_hdr *gh = msgb_l3(msg);
	struct gsm48_cip_mode_cmd *cm = (struct gsm48_cip_mode_cmd *)gh->data;
	int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm);
	uint8_t sc, alg_id, cr;

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of CIPHERING MODE COMMAND "
			"message.\n");
		return gsm48_rr_tx_rr_status(ms,
			GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}

	/* cipher mode setting */
	sc = cm->sc;
	alg_id = cm->alg_id;
	/* cipher mode response */
	cr = cm->cr;

	if (!sc)
		LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, cr=%u)\n",
			sc, cr);
	else
		LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, "
			"algo=A5/%d cr=%u)\n", sc, alg_id + 1, cr);

	/* 3.4.7.2 */
	if (rr->cipher_on && sc) {
		LOGP(DRR, LOGL_NOTICE, "chiphering already applied\n");
		return gsm48_rr_tx_rr_status(ms,
			GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}

	/* check if we actually support this cipher */
	if (sc && ((alg_id == GSM_CIPHER_A5_1 && !set->a5_1)
		|| (alg_id == GSM_CIPHER_A5_2 && !set->a5_2)
		|| (alg_id == GSM_CIPHER_A5_3 && !set->a5_3)
		|| (alg_id == GSM_CIPHER_A5_4 && !set->a5_4)
		|| (alg_id == GSM_CIPHER_A5_5 && !set->a5_5)
		|| (alg_id == GSM_CIPHER_A5_6 && !set->a5_6)
		|| (alg_id == GSM_CIPHER_A5_7 && !set->a5_7))) {
		LOGP(DRR, LOGL_NOTICE, "algo not supported\n");
		return gsm48_rr_tx_rr_status(ms,
			GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}

	/* check if we have no key */
	if (sc && subscr->key_seq == 7) {
		LOGP(DRR, LOGL_NOTICE, "no key available\n");
		return gsm48_rr_tx_rr_status(ms,
			GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}

	/* change to ciphering */
	rr->cipher_on = sc;
	rr->cipher_type = alg_id;
	if (rr->cipher_on)
		l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr,
			rr->cipher_type + 1, subscr->key, 8);
	else
		l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr,
			0, NULL, 0);

	/* response (using the new mode) */
	return gsm48_rr_tx_cip_mode_cpl(ms, cr);
}

/*
 * classmark
 */

/* Encode  "Classmark 3" (10.5.1.7) */
static int gsm48_rr_enc_cm3(struct osmocom_ms *ms, uint8_t *buf, uint8_t *len)
{
	struct gsm_support *sup = &ms->support;
	struct gsm_settings *set = &ms->settings;
	struct bitvec bv;

	memset(&bv, 0, sizeof(bv));
	bv.data = buf;
	bv.data_len = 12;

	/* spare bit */
	bitvec_set_bit(&bv, 0);
	/* band 3 supported */
	if (set->dcs)
		bitvec_set_bit(&bv, ONE);
	else
		bitvec_set_bit(&bv, ZERO);
	/* band 2 supported */
	if (set->e_gsm || set->r_gsm)
		bitvec_set_bit(&bv, ONE);
	else
		bitvec_set_bit(&bv, ZERO);
	/* band 1 supported */
	if (set->p_gsm && !(set->e_gsm || set->r_gsm))
		bitvec_set_bit(&bv, ONE);
	else
		bitvec_set_bit(&bv, ZERO);
	/* a5 bits */
	if (set->a5_7)
		bitvec_set_bit(&bv, ONE);
	else
		bitvec_set_bit(&bv, ZERO);
	if (set->a5_6)
		bitvec_set_bit(&bv, ONE);
	else
		bitvec_set_bit(&bv, ZERO);
	if (set->a5_5)
		bitvec_set_bit(&bv, ONE);
	else
		bitvec_set_bit(&bv, ZERO);
	if (set->a5_4)
		bitvec_set_bit(&bv, ONE);
	else
		bitvec_set_bit(&bv, ZERO);
	/* radio capability */
	if (!set->dcs && !set->p_gsm && !(set->e_gsm || set->r_gsm)) {
		/* Fig. 10.5.7 / TS 24.0008: none of dcs, p, e, r */
	} else
	if (set->dcs && !set->p_gsm && !(set->e_gsm || set->r_gsm)) {
		/* dcs only */
		bitvec_set_uint(&bv, 0, 4);
		bitvec_set_uint(&bv, set->class_dcs, 4);
	} else
	if (set->dcs && (set->p_gsm || (set->e_gsm || set->r_gsm))) {
		/* dcs */
		bitvec_set_uint(&bv, set->class_dcs, 4);
		/* low band */
		bitvec_set_uint(&bv, set->class_900, 4);
	} else {
		/* low band only */
		bitvec_set_uint(&bv, 0, 4);
		bitvec_set_uint(&bv, set->class_900, 4);
	}
	/* r support */
	if (set->r_gsm) {
		bitvec_set_bit(&bv, ONE);
		bitvec_set_uint(&bv, set->class_900, 3);
	} else {
		bitvec_set_bit(&bv, ZERO);
	}
	/* multi slot support */
	if (sup->ms_sup) {
		bitvec_set_bit(&bv, ONE);
		bitvec_set_uint(&bv, sup->ms_sup, 5);
	} else {
		bitvec_set_bit(&bv, ZERO);
	}
	/* ucs2 treatment */
	if (sup->ucs2_treat) {
		bitvec_set_bit(&bv, ONE);
	} else {
		bitvec_set_bit(&bv, ZERO);
	}
	/* support extended measurements */
	if (sup->ext_meas) {
		bitvec_set_bit(&bv, ONE);
	} else {
		bitvec_set_bit(&bv, ZERO);
	}
	/* support measurement capability */
	if (sup->meas_cap) {
		bitvec_set_bit(&bv, ONE);
		bitvec_set_uint(&bv, sup->sms_val, 4);
		bitvec_set_uint(&bv, sup->sm_val, 4);
	} else {
		bitvec_set_bit(&bv, ZERO);
	}
	/* positioning method capability */
	if (sup->loc_serv) {
		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 {
		bitvec_set_bit(&bv, ZERO);
	}
	/* The following bits are described in TS 24.008 */
	/* EDGE multi slot support */
	if (set->edge_ms_sup) {
		bitvec_set_bit(&bv, ONE);
		bitvec_set_uint(&bv, set->edge_ms_sup, 5);
	} else {
		bitvec_set_bit(&bv, ZERO);
	}
	/* EDGE support */
	if (set->edge_psk_sup) {
		bitvec_set_bit(&bv, ONE);
		bitvec_set_bit(&bv, set->edge_psk_uplink == 1);
		if (set->p_gsm || (set->e_gsm || set->r_gsm)) {
			bitvec_set_bit(&bv, ONE);
			bitvec_set_uint(&bv, set->class_900_edge, 2);
		} else {
			bitvec_set_bit(&bv, ZERO);
		}
		if (set->dcs || set->pcs) {
			bitvec_set_bit(&bv, ONE);
			bitvec_set_uint(&bv, set->class_dcs_pcs_edge, 2);
		} else {
			bitvec_set_bit(&bv, ZERO);
		}
	} else {
		bitvec_set_bit(&bv, ZERO);
	}
	/* GSM 400 Bands */
	if (set->gsm_480 || set->gsm_450) {
		bitvec_set_bit(&bv, ONE);
		bitvec_set_bit(&bv, set->gsm_480 == 1);
		bitvec_set_bit(&bv, set->gsm_450 == 1);
		bitvec_set_uint(&bv, set->class_400, 4);
	} else {
		bitvec_set_bit(&bv, ZERO);
	}
	/* GSM 850 Band */
	if (set->gsm_850) {
		bitvec_set_bit(&bv, ONE);
		bitvec_set_uint(&bv, set->class_850, 4);
	} else {
		bitvec_set_bit(&bv, ZERO);
	}
	/* PCS Band */
	if (set->pcs) {
		bitvec_set_bit(&bv, ONE);
		bitvec_set_uint(&bv, set->class_pcs, 4);
	} else {
		bitvec_set_bit(&bv, ZERO);
	}
	/* RAT Capability */
	bitvec_set_bit(&bv, set->umts_fdd == 1);
	bitvec_set_bit(&bv, set->umts_tdd == 1);
	bitvec_set_bit(&bv, set->cdma_2000 == 1);
	/* DTM */
	if (set->dtm) {
		bitvec_set_bit(&bv, ONE);
		bitvec_set_uint(&bv, set->class_dtm, 2);
		bitvec_set_bit(&bv, set->dtm_mac == 1);
		bitvec_set_bit(&bv, set->dtm_egprs == 1);
	} else {
		bitvec_set_bit(&bv, ZERO);
	}
	/* info: The max number of bits are about 80. */

	/* partial bytes will be padded with zero */
	*len = (bv.cur_bit + 7) >> 3;
	bitvec_fill(&bv, (*len*8) - bv.cur_bit, ZERO);

	return 0;
}

/* encode classmark 2 */
int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm,
	uint16_t arfcn)
{
	struct gsm_support *sup = &ms->support;
	struct gsm_settings *set = &ms->settings;

	cm->pwr_lev = gsm48_current_pwr_lev(set, arfcn);
	cm->a5_1 = !set->a5_1;
	cm->es_ind = sup->es_ind;
	cm->rev_lev = sup->rev_lev;
	cm->fc = (set->r_gsm || set->e_gsm);
	cm->vgcs = sup->vgcs;
	cm->vbs = sup->vbs;
	cm->sm_cap = set->sms_ptp;
	cm->ss_scr = sup->ss_ind;
	cm->ps_cap = sup->ps_cap;
	cm->a5_2 = set->a5_2;
	cm->a5_3 = set->a5_3;
	cm->cmsp = sup->cmsp;
	cm->solsa = sup->solsa;
	cm->lcsva_cap = sup->lcsva;

	return 0;
}

/* send classmark change */
static int gsm48_rr_tx_cm_change(struct osmocom_ms *ms)
{
	struct gsm_support *sup = &ms->support;
	struct gsm_settings *set = &ms->settings;
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct msgb *nmsg;
	struct gsm48_hdr *gh;
	struct gsm48_cm_change *cc;
	uint8_t cm3[14], *tlv;

	LOGP(DRR, LOGL_INFO, "CLASSMARK CHANGE\n");

	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
	cc = (struct gsm48_cm_change *) msgb_put(nmsg, sizeof(*cc));

	gh->proto_discr = GSM48_PDISC_RR;
	gh->msg_type = GSM48_MT_RR_CLSM_CHG;

	/* classmark 2 */
	cc->cm2_len = sizeof(cc->cm2);
	gsm48_rr_enc_cm2(ms, &cc->cm2, rr->cd_now.arfcn);

	/* classmark 3 */
	if (set->dcs || set->pcs || set->e_gsm || set->r_gsm || set->gsm_850
	 || set->a5_7 || set->a5_6 || set->a5_5 || set->a5_4
	 || sup->ms_sup
	 || sup->ucs2_treat
	 || sup->ext_meas || sup->meas_cap
	 || sup->loc_serv) {
		cc->cm2.cm3 = 1;
		cm3[0] = GSM48_IE_CLASSMARK3;
		gsm48_rr_enc_cm3(ms, cm3 + 2, &cm3[1]);
		tlv = msgb_put(nmsg, 2 + cm3[1]);
		memcpy(tlv, cm3, 2 + cm3[1]);
	}

	return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0);
}

/* receiving classmark enquiry */
static int gsm48_rr_rx_cm_enq(struct osmocom_ms *ms, struct msgb *msg)
{
	/* send classmark */
	return gsm48_rr_tx_cm_change(ms);
}

/*
 * ASCI notification
 */

struct asci_notif {
	struct llist_head	entry;
	struct gsm48_rrlayer	*rr;
	uint8_t			gcr[5];
	bool			ch_desc_present;
	struct gsm48_chan_desc	ch_desc;
	struct osmo_timer_list	timer;
};

/* When does a notification received from NCH expires. */
#define NOTIFICATION_TIMEOUT	5

static void asci_notif_timeout(void *arg);

/* Add new notification to list. */
static struct asci_notif *asci_notif_alloc(struct gsm48_rrlayer *rr, const uint8_t *gcr)
{
	struct asci_notif *notif;

	notif = talloc_zero(rr->ms, struct asci_notif);
	if (!notif)
		return NULL;
	notif->rr = rr;
	memcpy(notif->gcr, gcr, sizeof(notif->gcr));
	llist_add_tail(&notif->entry, &rr->vgcs.notif_list);

	notif->timer.cb = asci_notif_timeout;
	notif->timer.data = notif;

	return notif;
}

/* Remove notification from list. */
static void asci_notif_free(struct asci_notif *notif)
{
	osmo_timer_del(&notif->timer);
	llist_del(&notif->entry);
	talloc_free(notif);
}

/* Remove all ASCI notifications from list. */
static void asci_notif_list_free(struct gsm48_rrlayer *rr)
{
	struct asci_notif *notif, *notif2;

	llist_for_each_entry_safe(notif, notif2, &rr->vgcs.notif_list, entry)
		asci_notif_free(notif);
}

/* Notification timed out. */
static void asci_notif_timeout(void *arg)
{
	struct asci_notif *notif = arg;
	struct gsm48_rrlayer *rr = notif->rr;
	struct msgb *nmsg;
	struct gsm48_mm_event *mme;

	/* Send notification of ceased call to MM layer. */
	LOGP(DRR, LOGL_INFO, "Notify MM layer about ceased group call.\n");
	nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NOTIFICATION);
	if (!nmsg)
		return;
	mme = (struct gsm48_mm_event *) nmsg->data;
	memcpy(mme->notification.gcr, notif->gcr, sizeof(mme->notification.gcr));
	mme->notification.gone = true;
	gsm48_mmevent_msg(rr->ms, nmsg);

	asci_notif_free(notif);
}

/* Find notification in list. */
static struct asci_notif *asci_notif_find(struct gsm48_rrlayer *rr, const uint8_t *gcr)
{
	struct asci_notif *notif;

	llist_for_each_entry(notif, &rr->vgcs.notif_list, entry) {
		if (!memcmp(&notif->gcr, gcr, sizeof(notif->gcr)))
			return notif;
	}

	return NULL;
}

static int gsm48_rr_rx_group_call(struct osmocom_ms *ms, const uint8_t *gcr, const uint8_t *ch_desc, bool nch)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct asci_notif *notif;
	bool update_call = false;
	struct msgb *nmsg;
	struct gsm48_mm_event *mme;

	/* Find or create notification entry. Only create entries for notifications on NCH */
	notif = asci_notif_find(rr, gcr);
	if (!notif) {
		update_call = true;
		if (nch) {
			notif = asci_notif_alloc(rr, gcr);
			if (!notif)
				return -ENOMEM;
		}
	}

	/* Update channel description. */
	if (notif) {
		if (ch_desc) {
			if (!notif->ch_desc_present)
				update_call = true;
			notif->ch_desc_present = true;
			memcpy(&notif->ch_desc, ch_desc, sizeof(notif->ch_desc));
		} else {
			notif->ch_desc_present = false;
			if (!notif->ch_desc_present)
				update_call = true;
		}
		/* (Re-)Start timer. */
		osmo_timer_schedule(&notif->timer, NOTIFICATION_TIMEOUT, 0);
	} else
		update_call = true;

	/* Send notification of new or updated call to MM layer. */
	if (update_call) {
		LOGP(DRR, LOGL_INFO, "Notify MM layer about new/updated group call.\n");
		nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NOTIFICATION);
		if (!nmsg)
			return -ENOMEM;
		mme = (struct gsm48_mm_event *) nmsg->data;
		memcpy(mme->notification.gcr, gcr, sizeof(mme->notification.gcr));
		if (ch_desc) {
			mme->notification.ch_desc_present = true;
			memcpy(&mme->notification.ch_desc, ch_desc, sizeof(mme->notification.ch_desc));
		}
		gsm48_mmevent_msg(ms, nmsg);
	}

	return 0;
}

/* Common function to decode Group Call Information */
static int gsm48_rr_decode_group_call_info(struct osmocom_ms *ms, struct bitvec *bv, bool nch)
{
	struct bitvec *gcr_bv = NULL, *chd_bv = NULL;
	int rc = 0;
	int i;

	/* <Group Call Reference : bit(36)> */
	gcr_bv = bitvec_alloc(OSMO_BYTES_FOR_BITS(36), NULL);
	OSMO_ASSERT(gcr_bv);
	for (i = 0; i < 36; i++)
		bitvec_set_bit(gcr_bv, bitvec_get_bit_pos(bv, bv->cur_bit++));
	/* Group Channel Description */
	if (bitvec_get_bit_pos(bv, bv->cur_bit++) == 1) {
		chd_bv = bitvec_alloc(OSMO_BYTES_FOR_BITS(24), NULL);
		OSMO_ASSERT(chd_bv);
		for (i = 0; i < 24; i++)
			bitvec_set_bit(chd_bv, bitvec_get_bit_pos(bv, bv->cur_bit++));
		/* FIXME: hopping */
		if (bitvec_get_bit_pos(bv, bv->cur_bit++) == 1) {
			LOGP(DRR, LOGL_ERROR, "Hopping not supported on VGCS/VBS channel, please fix!\n");
			rc = -ENOTSUP;
			goto out;
		}
	}

	rc = gsm48_rr_rx_group_call(ms, gcr_bv->data, (chd_bv) ? chd_bv->data : NULL, nch);

out:
	bitvec_free(chd_bv);
	bitvec_free(gcr_bv);

	return rc;
}

/* Notification/FACCH (9.1.21a) */
static int gsm48_rr_rx_notif_facch(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_hdr_sh *sgh = msgb_l3(msg);
	int payload_len = msgb_l3len(msg) - sizeof(*sgh);
	struct bitvec bv;
	int rc;

	LOGP(DRR, LOGL_INFO, "NOTIFICATION/FACCH\n");

	bv = (struct bitvec) {
		.data = sgh->data,
		.data_len = payload_len,
	};

	/* Group Call Information */
	if (bitvec_get_bit_pos(&bv, bv.cur_bit++) == 0) {
		rc = gsm48_rr_decode_group_call_info(ms, &bv, false);
		return rc;
	}

	/* Note: Other information are not used. */
	return -ENOTSUP;
}

/* Notification/NCH (9.1.21b) */
static int gsm48_rr_rx_notif_nch(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_notification_nch *nn = msgb_l3(msg);
	int payload_len = msgb_l3len(msg) - sizeof(*nn);
	struct bitvec bv;
	int rc;

	LOGP(DRR, LOGL_INFO, "NOTIFICATION/NCH\n");

	bv = (struct bitvec) {
		.data = nn->data,
		.data_len = payload_len,
	};

	/* 0 | 1 <NLN(NCH) : bit (2) > */
	if (bitvec_get_bit_pos(&bv, bv.cur_bit++) == 1) {
		/* NLN not used
		nln = bitvec_get_uint(&bv, 2);
		nln_present = true;
		*/
		bv.cur_bit += 2;
	}

	/* < list of Group Call NCH information > */
	while (bitvec_get_bit_pos(&bv, bv.cur_bit++) == 1) {
		rc = gsm48_rr_decode_group_call_info(ms, &bv, true);
		if (rc < 0)
			break;
	}

	return 0;
}

/*
 * VGCS uplink control
 */

/* Send uplink status to upper layer. */
static int gsm48_rr_uplink_status(struct osmocom_ms *ms, uint32_t event)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct msgb *nmsg;

	if (rr->vgcs.group_state != GSM48_RR_GST_RECEIVE)
		return -EINVAL;

	/* Send notification of uplink state to MM layer. */
	LOGP(DRR, LOGL_INFO, "Notify MM layer about uplink state.\n");
	nmsg = gsm48_mmevent_msgb_alloc(event);
	if (!nmsg)
		return -ENOMEM;
	gsm48_mmevent_msg(rr->ms, nmsg);

	return 0;
}

/* UPLINK BUSY (9.1.46) */
static int gsm48_rr_rx_uplink_busy(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;

	LOGP(DRR, LOGL_INFO, "UPLINK BUSY\n");

	/* Only allow when in some group mode. */
	if (rr->vgcs.group_state == GSM48_RR_GST_OFF)
		return 0;

	/* Uplink is busy now. */
	if (rr->vgcs.uplink_free) {
		rr->vgcs.uplink_free = false;
		gsm48_rr_uplink_status(ms, GSM48_MM_EVENT_UPLINK_BUSY);
	}
	stop_rr_t_ul_free(rr);

	/* Abort during uplink investigation or access procedure. */
	if (osmo_timer_pending(&rr->vgcs.t3128) || osmo_timer_pending(&rr->vgcs.t3130)) {
		LOGP(DRR, LOGL_NOTICE, "Abort uplink access, due to busy uplink.\n");
		return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY);
	}

	return 0;
}

/* UPLINK FREE (9.1.47) */
static int gsm48_rr_rx_uplink_free(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct bitvec bv;
	bool uplink_access = false;
	uint8_t uic = 0xff;
	uint8_t *mode;

	bv = (struct bitvec) {
		.data_len = msgb_l3len(msg),
		.data = msgb_l3(msg),
		.cur_bit = 8,
	};

	/* Uplink Access */
	if (bitvec_get_bit_high(&bv) == H)
		uplink_access = true;

	/* UIC */
	if (bitvec_get_bit_high(&bv) == H)
		uic = bitvec_get_uint(&bv, 6);

	/* Note: Emergency Indicator not used. */

	/* Do not flood the logging with UPLINK FREE messages. Log only on the fist received message. */
	if (!osmo_timer_pending(&rr->vgcs.t_ul_free))
		LOGP(DRR, LOGL_INFO, "UPLINK FREE (uplink access=%s, uic=0x%02x)\n", (uplink_access) ? "true" : "false",
		     uic);

	/* Uplink is free now. */
	if (!rr->vgcs.uplink_free) {
		rr->vgcs.uplink_free = true;
		gsm48_rr_uplink_status(ms, GSM48_MM_EVENT_UPLINK_FREE);
	}
	rr->vgcs.uic = uic;
	rr->vgcs.uplink_access = uplink_access;
	start_rr_t_ul_free(rr);

	/* We can be in group mode or in dedicated mode. When we are in dedicated mode and we receive UPLINK FREE,
	 * we know that we are actually on a group channel. This is the actual confirm to the UPLINK RELEASE message
	 * on a group channel. We must then release layer 2 locally and indicate channel release toward upper layer.
	 *
	 * When we are in group transmit mode, we return to group receive mode and also release layer 2 locally and
	 * indicate uplink release towards upper layer.
	 *
	 * When we are waiting for a free uplink (T3128 is running), we start uplink access. While accessing the
	 * uplink, we ignore further UPLINK FREE messages.
	 */
	if (rr->vgcs.group_state != GSM48_RR_GST_OFF) {
		/* Start uplink access. */
		if (osmo_timer_pending(&rr->vgcs.t3128)) {
			/* Stop timer, because uplink is now free. */
			stop_rr_t3128(rr);
			rr->vgcs.uplink_tries = 3;
			return gsm48_rr_uplink_access(ms, NULL);
		}

		/* Ignore uplink free messages while accessing uplink. */
		if (osmo_timer_pending(&rr->vgcs.t3130))
			return 0;
	}

	/* Any time in group transmit mode or dedicated mode, release on uplink free. */
	if (rr->state == GSM48_RR_ST_DEDICATED || rr->state == GSM48_RR_ST_REL_PEND) {
		struct msgb *nmsg;

		LOGP(DRR, LOGL_INFO, "Uplink becomes free, send (local) release to layer 2.\n");

		/* Go into pending release state if not already.
		 * We are already in pending relese state when we release uplink normally.
		 * If we receive UPLINK FREE while we release normally, we abort layer 2. */
		if (rr->state != GSM48_RR_ST_REL_PEND)
			new_rr_state(rr, GSM48_RR_ST_REL_PEND);

		/* release message */
		nmsg = gsm48_l3_msgb_alloc();
		if (!nmsg)
			return -ENOMEM;
		mode = msgb_put(nmsg, 2);
		mode[0] = RSL_IE_RELEASE_MODE;
		mode[1] = RSL_REL_LOCAL_END;
		gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0);

		return 0;
	}

	return 0;
}

/* UPLINK RELEASE (9.1.48) */
static int gsm48_rr_rx_uplink_release(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm48_hdr *gh = msgb_l3(msg);
	struct gsm48_uplink_release *ur = (struct gsm48_uplink_release *)gh->data;
	int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ur);

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short UPLINK RELEASE message.\n");
		return -EINVAL;
	}

	LOGP(DRR, LOGL_INFO, "UPLINK RELEASE with cause 0x%02x\n", ur->rr_cause);

	/* Only allow when in some group mode. */
	if (rr->vgcs.group_state == GSM48_RR_GST_OFF)
		return 0;

	if (rr->state == GSM48_RR_ST_DEDICATED && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT)
		return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_NORMAL);

	return 0;
}

/* VGCS UPLINK GRANT (9.1.49) */
static int gsm48_rr_rx_vgcs_uplink_grant(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm48_sysinfo *s = &ms->cellsel.sel_si;
	struct gsm_settings *set = &ms->settings;
	struct gsm0408_vgcs_ul_grant *ug = msgb_l3(msg);
	int ug_len = msgb_l3len(msg) - sizeof(*ug);

	LOGP(DRR, LOGL_INFO, "VGCS UPLINK GRANT\n");

	if (ug_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of VGCS UPLINK GRANT message.\n");
		return -EINVAL;
	}

	/* Only allow when in some group mode. */
	if (rr->vgcs.group_state == GSM48_RR_GST_OFF)
		return 0;

	/* Uplink is busy now. */
	if (rr->vgcs.uplink_free) {
		rr->vgcs.uplink_free = false;
		gsm48_rr_uplink_status(rr->ms, GSM48_MM_EVENT_UPLINK_BUSY);
	}
	stop_rr_t_ul_free(rr);

	/* Abort during uplink investigation or access procedure. */
	if (osmo_timer_pending(&rr->vgcs.t3128)) {
		LOGP(DRR, LOGL_NOTICE, "Abort uplink access, other phone accessing the uplink.\n");
		return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY);
	}

	/* We are not waiting for the uplink to be granted. */
	if (!osmo_timer_pending(&rr->vgcs.t3130))
		return 0;

	/* Stop timer. */
	stop_rr_t3130(rr);

	/* Check if message is for our request. */
	if (!gsm48_match_ra(ms, &ug->req_ref, 5)) {
		LOGP(DRR, LOGL_NOTICE, "Abort uplink access, other phone gets uplink access granted.\n");
		return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY);
	}

	LOGP(DRR, LOGL_INFO, "Access to uplink has been granted.\n");

	/* Set initial power and timing advance. */
	rr->cd_now.ind_tx_power = s->ms_txpwr_max_cch;
	rr->cd_now.ind_ta = ug->ta;
	LOGP(DRR, LOGL_INFO, "Applying initial ta and tx_power\n");
	l1ctl_tx_param_req(ms, rr->cd_now.ind_ta - set->alter_delay,
			   (set->alter_tx_power) ? set->alter_tx_power_value : s->ms_txpwr_max_cch);

	/* Turn on transmitter. */
	rr->cd_now.tch_flags &= ~(L1CTL_TCH_FLAG_RXONLY);
	gsm48_rr_set_mode(ms, rr->cd_now.chan_nr, rr->cd_now.mode, rr->cd_now.tch_flags);

	/* Complete group transmit mode. */
	new_rr_state(rr, GSM48_RR_ST_DEDICATED);

	/* Establish layer 2 connection. */
	return gsm48_rr_tx_talker_indication(ms);
}

/* send rr uplink release */
static int gsm48_rr_tx_uplink_release(struct osmocom_ms *ms, uint8_t cause)
{
	struct msgb *nmsg;
	struct gsm48_hdr *gh;
	struct gsm48_uplink_release *ur;

	LOGP(DRR, LOGL_INFO, "UPLINK RELEASE (cause #%d)\n", cause);

	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
	ur = (struct gsm48_uplink_release *) msgb_put(nmsg, sizeof(*ur));

	gh->proto_discr = GSM48_PDISC_RR;
	gh->msg_type = GSM48_MT_RR_UPLINK_RELEASE;
	ur->rr_cause = cause;

	return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0);
}

/* Start uplink access procedure. (3.3.1.2.1.2) */
static int gsm48_rr_uplink_access(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;

	/* store frame number */
	if (msg) {
		struct abis_rsl_cchan_hdr *ch = msgb_l2(msg);
		struct gsm48_req_ref *ref =
				(struct gsm48_req_ref *) (ch->data + 1);

		if (msgb_l2len(msg) < sizeof(*ch) + sizeof(*ref)) {
			LOGP(DRR, LOGL_ERROR, "CHAN_CNF too slort\n");
			return -EINVAL;
		}

		/* Store to history buffer. */
		/* shift history and store */
		memcpy(&(rr->cr_hist[4]), &(rr->cr_hist[3]),
			sizeof(struct gsm48_cr_hist));
		memcpy(&(rr->cr_hist[3]), &(rr->cr_hist[2]),
			sizeof(struct gsm48_cr_hist));
		memcpy(&(rr->cr_hist[2]), &(rr->cr_hist[1]),
			sizeof(struct gsm48_cr_hist));
		memcpy(&(rr->cr_hist[1]), &(rr->cr_hist[0]),
			sizeof(struct gsm48_cr_hist));
		rr->cr_hist[0].valid = 1;
		rr->cr_hist[0].ref.ra = rr->cr_ra;
		rr->cr_hist[0].ref.t1 = ref->t1;
		rr->cr_hist[0].ref.t2 = ref->t2;
		rr->cr_hist[0].ref.t3_low = ref->t3_low;
		rr->cr_hist[0].ref.t3_high = ref->t3_high;
	}

	if (!osmo_timer_pending(&rr->vgcs.t3130)) {
		uint8_t uplink_ref;

		/* Only try up to 3 times. */
		if (!rr->vgcs.uplink_tries) {
			LOGP(DRR, LOGL_NOTICE, "Abort uplink access, due uplink access timeout.\n");
			return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_LINK_FAILURE);
		}

		LOGP(DRR, LOGL_INFO, "Trying to access uplink.\n");

		rr->vgcs.uplink_tries--;

		/* See Table 9.1.45.1 */
		uplink_ref = layer23_random();
		uplink_ref &= 0x1f;
		uplink_ref |= 0xc0;

		/* store value, mask and history */
		rr->cr_ra = uplink_ref;
		rr->cr_hist[4].valid = 0;
		rr->cr_hist[3].valid = 0;
		rr->cr_hist[2].valid = 0;
		rr->cr_hist[1].valid = 0;
		rr->cr_hist[0].valid = 0;

		/* Reset counter. */
		rr->vgcs.uplink_counter = 0;

		/* Start T3130. */
		start_rr_t3130(rr, GSM_T3130_MS);
	}

	/* Send random access bursts up to 5 times. */
	if (rr->vgcs.uplink_counter < 5) {
		int delay_ms;

		/* The first UPLINK ACCESS message shall be delayed between 0..20ms.
		 * Subsequent UPLINK ACCESS messages shall be delayed 100ms + 0..20ms. */
		delay_ms = (layer23_random() & 0xffff) / 3277;
		if (rr->vgcs.uplink_counter)
			delay_ms += 100;

		gsm48_rr_tx_rand_acc_dedicated(ms, rr->cr_ra, delay_ms * 26 / 120, rr->vgcs.uic);
		rr->vgcs.uplink_counter++;
	}

	return 0;
}

/* Whenever uplink access is released or failed for some reason. */
static int gsm48_rr_uplink_access_abort(struct osmocom_ms *ms, uint8_t cause)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;

	/* Stop group transmit mode timers. */
	stop_rr_t3128(rr);
	stop_rr_t3130(rr);

	/* Turn off transmitter. */
	rr->cd_now.tch_flags |= L1CTL_TCH_FLAG_RXONLY;
	gsm48_rr_set_mode(ms, rr->cd_now.chan_nr, rr->cd_now.mode, rr->cd_now.tch_flags);

	/* Only return IDLE without changing channel, because we are still in group transmit mode. */
	new_rr_state(rr, GSM48_RR_ST_IDLE);

	/* Set group state to receive mode. */
	rr->vgcs.group_state = GSM48_RR_GST_RECEIVE;

	nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_REL_IND);
	if (!nmsg)
		return -ENOMEM;
	nrrh = (struct gsm48_rr_hdr *)nmsg->data;
	nrrh->cause = cause;
	return gsm48_rr_upmsg(ms, nmsg);
}

/* send talker indication */
static int gsm48_rr_tx_talker_indication(struct osmocom_ms *ms)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm_subscriber *subscr = &ms->subscr;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct msgb *nmsg;
	struct gsm48_hdr *gh;
	struct gsm48_talker_indication *ti;

	LOGP(DRR, LOGL_INFO, "TALKER INDICATION\n");

	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
	ti = (struct gsm48_talker_indication *) msgb_put(nmsg, sizeof(*ti));

	gh->proto_discr = GSM48_PDISC_RR;
	gh->msg_type = GSM48_MT_RR_TALKER_IND;

	/* classmark 2 */
	ti->cm2_len = sizeof(ti->cm2);
	gsm48_rr_enc_cm2(ms, &ti->cm2, rr->cd_now.arfcn);

	/* mobile identity */
	if (ms->subscr.tmsi != GSM_RESERVED_TMSI && (osmo_lai_cmp(&subscr->lai, &cs->sel_cgi.lai) == 0)) {
		gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false);
		LOGP(DRR, LOGL_INFO, "Sending TALKER INDICATION with TMSI.\n");
	} else if (subscr->imsi[0]) {
		gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false);
		LOGP(DRR, LOGL_INFO, "Sending TALKER INDICATION with IMSI.\n");
	} else {
		gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_NONE, false);
		LOGP(DRR, LOGL_INFO, "Sending TALKER INDICATION without TMSI/IMSI.\n");
	}

	/* start establishmnet */
	return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg, 0);
}

/*
 * random access
 */

/* start random access */
static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging,
	int paging_mi_type)
{
	struct gsm_settings *set = &ms->settings;
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_sysinfo *s = cs->si;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;
	uint8_t chan_req_val, chan_req_mask;
	int rc;

	LOGP(DSUM, LOGL_INFO, "Establish radio link due to %s request\n",
		(paging) ? "paging" : "mobility management");

	/* ignore paging, if not camping */
	if (paging
	 && (!cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY
			    && cs->state != GSM322_C7_CAMPED_ANY_CELL))) {
		LOGP(DRR, LOGL_INFO, "Paging, but not camping, ignore.\n");
		return -EINVAL;
	}

	/* ignore channel request while not camping on a cell */
	if (!cs->selected) {
		LOGP(DRR, LOGL_INFO, "Channel request rejected, we did not "
			"properly select the serving cell.\n");

		goto rel_ind;
	}

	/* tell cell selection process to leave idle mode
	 * NOTE: this must be sent unbuffered, because the state may not
	 * change until idle mode is left
	 */
	nmsg = gsm322_msgb_alloc(GSM322_EVENT_LEAVE_IDLE);
	if (!nmsg)
		return -ENOMEM;
	rc = gsm322_c_event(ms, nmsg);
	msgb_free(nmsg);
	if (rc) {
		if (paging)
			return rc;
		LOGP(DRR, LOGL_INFO, "Failed to leave IDLE mode.\n");
		goto undefined;
	}

	/* 3.3.1.1.2 */
	new_rr_state(rr, GSM48_RR_ST_CONN_PEND);

	/* set assignment state */
	rr->wait_assign = 0;

	/* number of retransmissions (with first transmission) */
	rr->n_chan_req = s->max_retrans + 1;

	/* generate CHAN REQ (9.1.8) */
	switch (cause) {
	case RR_EST_CAUSE_EMERGENCY:
		/* 101xxxxx */
		chan_req_mask = 0x1f;
		chan_req_val = 0xa0;
		LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Emergency call)\n",
			chan_req_val);
		break;
	case RR_EST_CAUSE_REESTAB_TCH_F:
		chan_req_mask = 0x1f;
		chan_req_val = 0xc0;
		LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (re-establish "
			"TCH/F)\n", chan_req_val);
		break;
	case RR_EST_CAUSE_REESTAB_TCH_H:
		if (s->neci) {
			chan_req_mask = 0x03;
			chan_req_val = 0x68;
			LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x "
				"(re-establish TCH/H with NECI)\n",
				chan_req_val);
		} else {
			chan_req_mask = 0x1f;
			chan_req_val = 0xc0;
			LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x "
				"(re-establish TCH/H no NECI)\n", chan_req_val);
		}
		break;
	case RR_EST_CAUSE_REESTAB_2_TCH_H:
		if (s->neci) {
			chan_req_mask = 0x03;
			chan_req_val = 0x6c;
			LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x "
				"(re-establish TCH/H+TCH/H with NECI)\n",
				chan_req_val);
		} else {
			chan_req_mask = 0x1f;
			chan_req_val = 0xc0;
			LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x "
				"(re-establish TCH/H+TCH/H no NECI)\n",
				chan_req_val);
		}
		break;
	case RR_EST_CAUSE_ANS_PAG_ANY:
		chan_req_mask = 0x1f;
		chan_req_val = 0x80;
		LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING "
			"Any channel)\n", chan_req_val);
		break;
	case RR_EST_CAUSE_ANS_PAG_SDCCH:
		chan_req_mask = 0x0f;
		chan_req_val = 0x10;
		LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING SDCCH)\n",
			chan_req_val);
		break;
	case RR_EST_CAUSE_ANS_PAG_TCH_F:
		switch (set->ch_cap) {
		case GSM_CAP_SDCCH:
			chan_req_mask = 0x0f;
			chan_req_val = 0x10;
			break;
		case GSM_CAP_SDCCH_TCHF:
			chan_req_mask = 0x1f;
			chan_req_val = 0x80;
			break;
		default:
			chan_req_mask = 0x0f;
			chan_req_val = 0x20;
			break;
		}
		LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING TCH/F)\n",
			chan_req_val);
		break;
	case RR_EST_CAUSE_ANS_PAG_TCH_ANY:
		switch (set->ch_cap) {
		case GSM_CAP_SDCCH:
			chan_req_mask = 0x0f;
			chan_req_val = 0x10;
			break;
		case GSM_CAP_SDCCH_TCHF:
			chan_req_mask = 0x1f;
			chan_req_val = 0x80;
			break;
		default:
			chan_req_mask = 0x0f;
			chan_req_val = 0x30;
			break;
		}
		LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING TCH/H or "
				"TCH/F)\n", chan_req_val);
		break;
	case RR_EST_CAUSE_ORIG_TCHF:
		/* ms supports no dual rate */
		chan_req_mask = 0x1f;
		chan_req_val = 0xe0;
		LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Orig TCH/F)\n",
			chan_req_val);
		break;
	case RR_EST_CAUSE_LOC_UPD:
		if (s->neci) {
			chan_req_mask = 0x0f;
			chan_req_val = 0x00;
			LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Location "
				"Update with NECI)\n", chan_req_val);
		} else {
			chan_req_mask = 0x1f;
			chan_req_val = 0x00;
			LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Location "
				"Update no NECI)\n", chan_req_val);
		}
		break;
	case RR_EST_CAUSE_OTHER_SDCCH:
		if (s->neci) {
			chan_req_mask = 0x0f;
			chan_req_val = 0x10;
			LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OTHER "
				"with NECI)\n", chan_req_val);
		} else {
			chan_req_mask = 0x1f;
			chan_req_val = 0xe0;
			LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OTHER "
				"no NECI)\n", chan_req_val);
		}
		break;
	default:
		if (!rr->rr_est_req) /* no request from MM */
			return -EINVAL;

		LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: with unknown "
			"establishment cause: %d\n", cause);
		undefined:
		LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n");

rel_ind:
		nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
		if (!nmsg)
			return -ENOMEM;
		nrrh = (struct gsm48_rr_hdr *)nmsg->data;
		nrrh->cause = RR_REL_CAUSE_UNDEFINED;
		gsm48_rr_upmsg(ms, nmsg);
		new_rr_state(rr, GSM48_RR_ST_IDLE);
		return -EINVAL;
	}

	/* store value, mask and clear history */
	rr->chan_req_val = chan_req_val;
	rr->chan_req_mask = chan_req_mask;
	memset(rr->cr_hist, 0, sizeof(rr->cr_hist));

	/* store establishment cause, so 'choose cell' selects the last cell
	 * after location updating */
	rr->est_cause = cause;

	/* store paging mobile identity type, if we respond to paging */
	rr->paging_mi_type = paging_mi_type;

	/* if channel is already active somehow */
	if (cs->ccch_state == GSM322_CCCH_ST_DATA)
		return gsm48_rr_tx_rand_acc(ms, NULL);

	return 0;
}

/* send first/next channel request in conn pend state */
int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_sysinfo *s = &ms->cellsel.sel_si;
	struct gsm_settings *set = &ms->settings;
	struct msgb *nmsg;
	struct abis_rsl_cchan_hdr *ncch;
	int slots;
	uint8_t chan_req;
	uint8_t tx_power;
	enum gsm_band band;

	if (gsm_arfcn2band_rc(cs->arfcn, &band) != 0) {
		LOGP(DRR, LOGL_ERROR, "gsm_arfcn2band_rc() failed\n");
		return -EINVAL;
	}

	/* already assigned */
	if (rr->wait_assign == 2)
		return 0;

	/* store frame number */
	if (msg) {
		struct abis_rsl_cchan_hdr *ch = msgb_l2(msg);
		struct gsm48_req_ref *ref =
				(struct gsm48_req_ref *) (ch->data + 1);

		if (msgb_l2len(msg) < sizeof(*ch) + sizeof(*ref)) {
			LOGP(DRR, LOGL_ERROR, "CHAN_CNF too slort\n");
			return -EINVAL;
		}

		/* shift history and store */
		memmove(rr->cr_hist + 1, rr->cr_hist, sizeof(rr->cr_hist) - sizeof(rr->cr_hist[0]));
		rr->cr_hist[0].valid = 1;
		rr->cr_hist[0].ref.ra = rr->cr_ra;
		rr->cr_hist[0].ref.t1 = ref->t1;
		rr->cr_hist[0].ref.t2 = ref->t2;
		rr->cr_hist[0].ref.t3_low = ref->t3_low;
		rr->cr_hist[0].ref.t3_high = ref->t3_high;
	}

	if (cs->ccch_state != GSM322_CCCH_ST_DATA) {
		LOGP(DRR, LOGL_INFO, "CCCH channel activation failed.\n");
fail:
		if (rr->rr_est_req) {
			struct msgb *msg =
				gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
			struct gsm48_rr_hdr *rrh;

			LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n");
			if (!msg)
				return -ENOMEM;
			rrh = (struct gsm48_rr_hdr *)msg->data;
			rrh->cause = RR_REL_CAUSE_RA_FAILURE;
			gsm48_rr_upmsg(ms, msg);
		}

		new_rr_state(rr, GSM48_RR_ST_IDLE);

		return 0;
	}

	if (!s || !s->si3 || !s->tx_integer) {
		LOGP(DRR, LOGL_NOTICE, "Not enough SYSINFO\n");
		goto fail;
	}

	if (rr->state == GSM48_RR_ST_IDLE) {
		LOGP(DRR, LOGL_INFO, "MM already released RR.\n");

		return 0;
	}

	LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (requests left %d)\n",
		rr->n_chan_req);

	if (!rr->n_chan_req) {
		LOGP(DRR, LOGL_INFO, "Done with sending RANDOM ACCESS "
			"bursts\n");
		if (!osmo_timer_pending(&rr->t3126))
			start_rr_t3126(rr, 5, 0); /* TODO improve! */
		return 0;
	}
	rr->n_chan_req--;

	if (rr->wait_assign == 0) {
		/* first random access, without delay of slots */
		slots = 0;
		rr->wait_assign = 1;
	} else {
		/* subsequent random access, with slots from table 3.1 */
		switch(s->tx_integer) {
		case 3: case 8: case 14: case 50:
			if (s->ccch_conf != 1) /* not combined CCCH */
				slots = 55;
			else
				slots = 41;
			break;
		case 4: case 9: case 16:
			if (s->ccch_conf != 1)
				slots = 76;
			else
				slots = 52;
			break;
		case 5: case 10: case 20:
			if (s->ccch_conf != 1)
				slots = 109;
			else
				slots = 58;
			break;
		case 6: case 11: case 25:
			if (s->ccch_conf != 1)
				slots = 163;
			else
				slots = 86;
			break;
		default:
			if (s->ccch_conf != 1)
				slots = 217;
			else
				slots = 115;
			break;
		}
	}

	chan_req = layer23_random();
	chan_req &= rr->chan_req_mask;
	chan_req |= rr->chan_req_val;

	LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (Tx-integer %d combined %s "
		"S(lots) %d ra 0x%02x)\n", s->tx_integer,
		(s->ccch_conf == 1) ? "yes": "no", slots, chan_req);

	slots = (random() % s->tx_integer) + slots;

	/* (re)send CHANNEL RQD with new randiom */
	nmsg = gsm48_rsl_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	ncch = (struct abis_rsl_cchan_hdr *) msgb_put(nmsg, sizeof(*ncch)
							+ 4 + 2 + 2);
	rsl_init_cchan_hdr(ncch, RSL_MT_CHAN_RQD);
	ncch->chan_nr = RSL_CHAN_RACH;
	ncch->data[0] = RSL_IE_REQ_REFERENCE;
	ncch->data[1] = chan_req;
	ncch->data[2] = (slots >> 8) | ((s->ccch_conf == 1) << 7);
	ncch->data[3] = slots;
	ncch->data[4] = RSL_IE_ACCESS_DELAY;
	ncch->data[5] = set->alter_delay; /* (-)=earlier (+)=later */
	ncch->data[6] = RSL_IE_MS_POWER;
	if (set->alter_tx_power) {
		tx_power = set->alter_tx_power_value;
		LOGP(DRR, LOGL_INFO, "Use alternative tx-power %d (%d dBm)\n",
			tx_power, ms_pwr_dbm(band, tx_power));
	} else {
		tx_power = s->ms_txpwr_max_cch;
		/* power offset in case of DCS1800 */
		if (s->po && (cs->arfcn & 1023) >= 512
		 && (cs->arfcn & 1023) <= 885) {
			LOGP(DRR, LOGL_INFO, "Use MS-TXPWR-MAX-CCH power value "
				"%d (%d dBm) with offset %d dBm\n", tx_power,
				ms_pwr_dbm(band, tx_power), s->po_value * 2);
			/* use reserved bits 7,8 for offset (+ X * 2dB) */
			tx_power |= s->po_value << 6;
		} else
			LOGP(DRR, LOGL_INFO, "Use MS-TXPWR-MAX-CCH power value "
				"%d (%d dBm)\n", tx_power, ms_pwr_dbm(band, tx_power));
	}
	ncch->data[7] = tx_power;

	/* set initial indications */
	rr->cd_now.ind_tx_power = s->ms_txpwr_max_cch;
	rr->cd_now.ind_ta = set->alter_delay;

	/* store ra until confirmed, then copy it with time into cr_hist */
	rr->cr_ra = chan_req;

	return lapdm_rslms_recvmsg(nmsg, &ms->lapdm_channel);
}

/*
 * system information
 */

/* send sysinfo event to other layers */
static int gsm48_new_sysinfo(struct osmocom_ms *ms, uint8_t type)
{
	struct gsm48_sysinfo *s = ms->cellsel.si;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct msgb *nmsg;
	struct gsm322_msg *em;

	/* update list of measurements, if BA(SACCH) is complete and new */
	if (s
	 && (type == GSM48_MT_RR_SYSINFO_5
	  || type == GSM48_MT_RR_SYSINFO_5bis
	  || type == GSM48_MT_RR_SYSINFO_5ter)
	 && s->si5
	 && (!s->nb_ext_ind_si5 || s->si5bis)) {
		struct gsm48_rr_meas *rrmeas = &ms->rrlayer.meas;
		int i;
		bool refer_pcs;
		int16_t arfcn;

		LOGP(DRR, LOGL_NOTICE, "Complete set of SI5* for BA(%d)\n",
			s->nb_ba_ind_si5);
		rrmeas->nc_num = 0;
		refer_pcs = gsm_refer_pcs(cs->arfcn, s);

		/* Collect channels from freq list in correct order. */
		for (i = 0; i < 32; i++) {
			arfcn = arfcn_from_freq_index(s, i);
			if (arfcn < 0)
				break;
			if (refer_pcs && arfcn >= 512 && arfcn <= 810)
				rrmeas->nc_arfcn[i] = arfcn | ARFCN_PCS;
			else
				rrmeas->nc_arfcn[i] = arfcn;
			rrmeas->nc_rxlev_dbm[i] = -128;
			LOGP(DRR, LOGL_NOTICE, "SI5/SI5bis report arfcn %s (index %d)\n",
			     gsm_print_arfcn(rrmeas->nc_arfcn[i]), i);
		}
		rrmeas->nc_num = i;
		if (i == 32 && arfcn_from_freq_index(s, i) >= 0)
			LOGP(DRR, LOGL_NOTICE, "SI5/SI5bis/SI5ter define more than 32 channels.\n");
	}

	/* send sysinfo event to other layers */
	nmsg = gsm322_msgb_alloc(GSM322_EVENT_SYSINFO);
	if (!nmsg)
		return -ENOMEM;
	em = (struct gsm322_msg *) nmsg->data;
	em->sysinfo = type;
	gsm322_cs_sendmsg(ms, nmsg);

	/* if not camping, we don't care about SI */
	if (ms->cellsel.neighbour
	 || (ms->cellsel.state != GSM322_C3_CAMPED_NORMALLY
	  && ms->cellsel.state != GSM322_C7_CAMPED_ANY_CELL))
		return 0;

	/* send timer info to location update process */
	nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_SYSINFO);
	if (!nmsg)
		return -ENOMEM;
	gsm48_mmevent_msg(ms, nmsg);

	return 0;
}

/* receive "SYSTEM INFORMATION 1" message (9.1.31) */
static int gsm48_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->cellsel.si;
	int payload_len = msgb_l3len(msg) - sizeof(*si);

	if (!s) {
		LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 1 "
			"ignored\n");
		return -EINVAL;
	}

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 1 "
			"message.\n");
		return -EINVAL;
	}

	if (!memcmp(si, s->si1_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si1_msg))))
		return 0;

	gsm48_decode_sysinfo1(s, si, msgb_l3len(msg));

	LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 1\n");

	return gsm48_new_sysinfo(ms, si->header.system_information);
}

/* receive "SYSTEM INFORMATION 2" message (9.1.32) */
static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_system_information_type_2 *si = msgb_l3(msg);
	struct gsm48_sysinfo *s = ms->cellsel.si;
	int payload_len = msgb_l3len(msg) - sizeof(*si);

	if (!s) {
		LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2 "
			"ignored\n");
		return -EINVAL;
	}

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2 "
			"message.\n");
		return -EINVAL;
	}

	if (!memcmp(si, s->si2_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si2_msg))))
		return 0;

	gsm48_decode_sysinfo2(s, si, msgb_l3len(msg));

	LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2\n");

	return gsm48_new_sysinfo(ms, si->header.system_information);
}

/* receive "SYSTEM INFORMATION 2bis" message (9.1.33) */
static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_system_information_type_2bis *si = msgb_l3(msg);
	struct gsm48_sysinfo *s = ms->cellsel.si;
	int payload_len = msgb_l3len(msg) - sizeof(*si);

	if (!s) {
		LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2bis"
			" ignored\n");
		return -EINVAL;
	}

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2bis "
			"message.\n");
		return -EINVAL;
	}

	if (!memcmp(si, s->si2b_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si2b_msg))))
		return 0;

	gsm48_decode_sysinfo2bis(s, si, msgb_l3len(msg));

	LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2bis\n");

	return gsm48_new_sysinfo(ms, si->header.system_information);
}

/* receive "SYSTEM INFORMATION 2ter" message (9.1.34) */
static int gsm48_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->cellsel.si;
	int payload_len = msgb_l3len(msg) - sizeof(*si);

	if (!s) {
		LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2ter"
			" ignored\n");
		return -EINVAL;
	}

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2ter "
			"message.\n");
		return -EINVAL;
	}

	if (!memcmp(si, s->si2t_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si2t_msg))))
		return 0;

	gsm48_decode_sysinfo2ter(s, si, msgb_l3len(msg));

	LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2ter\n");

	return gsm48_new_sysinfo(ms, si->header.system_information);
}

/* receive "SYSTEM INFORMATION 3" message (9.1.35) */
static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_system_information_type_3 *si = msgb_l3(msg);
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_sysinfo *s = cs->si;
	int payload_len = msgb_l3len(msg) - sizeof(*si);

	if (!s) {
		LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 3 "
			"ignored\n");
		return -EINVAL;
	}

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 3 "
			"message.\n");
		return -EINVAL;
	}

	if (!memcmp(si, s->si3_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si3_msg))))
		return 0;

	gsm48_decode_sysinfo3(s, si, msgb_l3len(msg));

	if (cs->ccch_mode == CCCH_MODE_NONE) {
		cs->ccch_mode = (s->ccch_conf == 1) ? CCCH_MODE_COMBINED :
			CCCH_MODE_NON_COMBINED;
		LOGP(DRR, LOGL_NOTICE, "Changing CCCH_MODE to %d\n",
			cs->ccch_mode);
		l1ctl_tx_ccch_mode_req(ms, cs->ccch_mode);
	}

	return gsm48_new_sysinfo(ms, si->header.system_information);
}

/* receive "SYSTEM INFORMATION 4" message (9.1.36) */
static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_system_information_type_4 *si = msgb_l3(msg);
	struct gsm48_sysinfo *s = ms->cellsel.si;
	int payload_len = msgb_l3len(msg) - sizeof(*si);

	if (!s) {
		LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 4 "
			"ignored\n");
		return -EINVAL;
	}

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 4 "
			"message.\n");
		return -EINVAL;
	}

	if (!memcmp(si, s->si4_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si4_msg))))
		return 0;

	gsm48_decode_sysinfo4(s, si, msgb_l3len(msg));

	LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4 (lai=%s)\n", osmo_lai_name(&s->lai));

	return gsm48_new_sysinfo(ms, si->header.system_information);
}

/* receive "SYSTEM INFORMATION 5" message (9.1.37) */
static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg)
{
	/* NOTE: pseudo length is not in this structure, so we skip */
	struct gsm48_system_information_type_5 *si = msgb_l3(msg) + 1;
	struct gsm48_sysinfo *s = ms->cellsel.si;
	int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;

	if (!s) {
		LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5 "
			"ignored\n");
		return -EINVAL;
	}

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5 "
			"message.\n");
		return -EINVAL;
	}

	if (!memcmp(si, s->si5_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si5_msg))))
		return 0;

	gsm48_decode_sysinfo5(s, si, msgb_l3len(msg));

	LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5\n");

	return gsm48_new_sysinfo(ms, si->system_information);
}

/* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */
static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg)
{
	/* NOTE: pseudo length is not in this structure, so we skip */
	struct gsm48_system_information_type_5bis *si = msgb_l3(msg) + 1;
	struct gsm48_sysinfo *s = ms->cellsel.si;
	int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;

	if (!s) {
		LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5bis"
			" ignored\n");
		return -EINVAL;
	}

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5bis "
			"message.\n");
		return -EINVAL;
	}

	if (!memcmp(si, s->si5b_msg, OSMO_MIN(msgb_l3len(msg),
			sizeof(s->si5b_msg))))
		return 0;

	gsm48_decode_sysinfo5bis(s, si, msgb_l3len(msg));

	LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5bis\n");

	return gsm48_new_sysinfo(ms, si->system_information);
}

/* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */
static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg)
{
	/* NOTE: pseudo length is not in this structure, so we skip */
	struct gsm48_system_information_type_5ter *si = msgb_l3(msg) + 1;
	struct gsm48_sysinfo *s = ms->cellsel.si;
	int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;

	if (!s) {
		LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5ter"
			" ignored\n");
		return -EINVAL;
	}

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5ter "
			"message.\n");
		return -EINVAL;
	}

	if (!memcmp(si, s->si5t_msg, OSMO_MIN(msgb_l3len(msg),
			sizeof(s->si5t_msg))))
		return 0;

	gsm48_decode_sysinfo5ter(s, si, msgb_l3len(msg));

	LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5ter\n");

	return gsm48_new_sysinfo(ms, si->system_information);
}

/* receive "SYSTEM INFORMATION 6" message (9.1.39) */
static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
{
	/* NOTE: pseudo length is not in this structure, so we skip */
	struct gsm48_system_information_type_6 *si = msgb_l3(msg) + 1;
	struct gsm48_sysinfo *s = ms->cellsel.si;
	struct rx_meas_stat *meas = &ms->meas;
	int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;

	if (!s) {
		LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 6 "
			"ignored\n");
		return -EINVAL;
	}

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 6 "
			"message.\n");
		return -EINVAL;
	}

	if (!memcmp(si, s->si6_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si6_msg))))
		return 0;

	gsm48_decode_sysinfo6(s, si, msgb_l3len(msg));

	LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (lai=%s SACCH-timeout %d)\n",
	     osmo_lai_name(&s->lai), s->sacch_radio_link_timeout);

	meas->rl_fail = meas->s = s->sacch_radio_link_timeout;
	LOGP(DRR, LOGL_INFO, "using (new) SACCH timeout %d\n", meas->rl_fail);

	return gsm48_new_sysinfo(ms, si->system_information);
}

/* Receive "SYSTEM INFORMATION 10" message (9.1.50). */
static int gsm48_rr_rx_sysinfo_10(struct osmocom_ms *ms, struct msgb *msg)
{
	/* NOTE: Short L2 header is included in this structure */
	struct gsm48_system_information_type_10 *si = msgb_l3(msg);
	struct gsm48_sysinfo *s = ms->cellsel.si;
	int payload_len = msgb_l3len(msg) - sizeof(*si);

	if (!s) {
		LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 10 ignored.\n");
		return -EINVAL;
	}

	if (payload_len < 20) {
		LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 10 message.\n");
		return -EINVAL;
	}

	/* No complete SI5, cannot decode yet. */
	if (!s->si5 || !(s->si5bis || !s->nb_ext_ind_si5))
		return 0;

	/* We decode when changed or when SI10 could not decoded, due to missing neighbor cell infos. */
	if (!memcmp(si, s->si10_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si10_msg))) && s->si10)
		return 0;

	LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 10\n");

	gsm48_decode_sysinfo10(s, si, msgb_l3len(msg));

	/* We cannot call gsm48_new_sysinfo, because it requires regular message types. */
	return 0;
}

/* receive "SYSTEM INFORMATION 13" message (9.1.43a) */
static int gsm48_rr_rx_sysinfo13(struct osmocom_ms *ms, struct msgb *msg)
{
	const struct gsm48_system_information_type_13 *si = msgb_l3(msg);
	int rest_octets_len = msgb_l3len(msg) - sizeof(si->header);
	struct gsm48_sysinfo *s = ms->cellsel.si;

	if (!s) {
		LOGP(DRR, LOGL_INFO,
		     "No cell selected, SYSTEM INFORMATION 13 ignored\n");
		return -EINVAL;
	}

	if (rest_octets_len < 0) {
		LOGP(DRR, LOGL_NOTICE,
		     "Short read of SYSTEM INFORMATION 13 message.\n");
		return -EINVAL;
	}

	if (!memcmp(si, s->si13_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si6_msg))))
		return 0;

	gsm48_decode_sysinfo13(s, si, msgb_l3len(msg));

	LOGP(DRR, LOGL_INFO,
	     "New SYSTEM INFORMATION 13 (%s, RAC 0x%02x, NCO %u, MNO %u)\n",
	     s->gprs.egprs_supported ? "EGPRS" : "GPRS only",
	     s->gprs.rac, s->gprs.nco, s->gprs.nmo);

	return gsm48_new_sysinfo(ms, si->header.system_information);
}

/*
 * paging
 */

/* paging channel request */
static int gsm48_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 against ms */
static uint8_t gsm_match_mi(struct osmocom_ms *ms, const uint8_t *mi_lv)
{
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct osmo_mobile_identity mi;
	char buf[32];
	int rc;

	rc = osmo_mobile_identity_decode(&mi, mi_lv+1, mi_lv[0], false);
	if (rc < 0)
		return rc;
	osmo_mobile_identity_to_str_buf(buf, sizeof(buf), &mi);

	switch (mi.type) {
	case GSM_MI_TYPE_TMSI:
		if ((ms->subscr.tmsi == mi.tmsi)
		 && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
			LOGP(DPAG, LOGL_INFO, " %s matches\n", buf);
			return mi.type;
		} else
			LOGP(DPAG, LOGL_INFO, " %s (not for us)\n", buf);
		break;
	case GSM_MI_TYPE_IMSI:
		if (!strcmp(mi.imsi, ms->subscr.imsi)) {
			LOGP(DPAG, LOGL_INFO, " %s matches\n", buf);
			return mi.type;
		} else
			LOGP(DPAG, LOGL_INFO, "  %s (not for us)\n", buf);
		break;
	default:
		LOGP(DPAG, LOGL_NOTICE, "Paging with unsupported MI type %d.\n",
			mi.type);
	}

	return 0;
}

/* 9.1.22 PAGING REQUEST 1 message received */
static int gsm48_rr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_paging1 *pa = msgb_l3(msg);
	int payload_len = msgb_l3len(msg) - sizeof(*pa);
	int chan_1, chan_2;
	uint8_t *mi, mi_type;

	/* empty paging request */
	if (payload_len >= 2 && (pa->data[1] & GSM_MI_TYPE_MASK) == 0)
		return 0;

	/* 3.3.1.1.2: ignore paging while not camping on a cell */
	if (rr->state != GSM48_RR_ST_IDLE || !cs->selected
	 || (cs->state != GSM322_C3_CAMPED_NORMALLY
	  && cs->state != GSM322_C7_CAMPED_ANY_CELL)
	 || cs->neighbour) {
		LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n");

		return 0;
	}
	LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 1\n");

	if (payload_len < 2) {
		short_read:
		LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 1 "
			"message.\n");

		return -EINVAL;
	}

	/* channel needed */
	chan_1 = pa->cneed1;
	chan_2 = pa->cneed2;
	/* first MI */
	mi = pa->data;
	if (payload_len < mi[0] + 1)
		goto short_read;
	if ((mi_type = gsm_match_mi(ms, mi)) > 0)
		return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1,
			mi_type);
	/* 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_read;
	if ((mi_type = gsm_match_mi(ms, mi + 1)) > 0)
		return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1,
			mi_type);

	return 0;
}

/* 9.1.23 PAGING REQUEST 2 message received */
static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_paging2 *pa = msgb_l3(msg);
	int payload_len = msgb_l3len(msg) - sizeof(*pa);
	uint8_t *mi, mi_type;
	int chan_1, chan_2, chan_3;

	/* 3.3.1.1.2: ignore paging while not camping on a cell */
	if (rr->state != GSM48_RR_ST_IDLE || !cs->selected
	 || (cs->state != GSM322_C3_CAMPED_NORMALLY
	  && cs->state != GSM322_C7_CAMPED_ANY_CELL)
	 || cs->neighbour) {
		LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n");

		return 0;
	}
	LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 2\n");

	if (payload_len < 0) {
		short_read:
		LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 2 "
			"message .\n");

		return -EINVAL;
	}

	/* channel needed */
	chan_1 = pa->cneed1;
	chan_2 = pa->cneed2;
	/* first MI */
	if (ms->subscr.tmsi == ntohl(pa->tmsi1)
	 && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
		LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1));
		return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1,
			GSM_MI_TYPE_TMSI);
	} else
		LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
			ntohl(pa->tmsi1));
	/* second MI */
	if (ms->subscr.tmsi == ntohl(pa->tmsi2)
	 && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
		LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2));
		return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1,
			GSM_MI_TYPE_TMSI);
	} else
		LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
			ntohl(pa->tmsi2));
	/* 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_read;
	chan_3 = mi[mi[1] + 2] & 0x03; /* channel needed */
	if ((mi_type = gsm_match_mi(ms, mi + 1)) > 0)
		return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1,
			mi_type);

	return 0;
}

/* 9.1.24 PAGING REQUEST 3 message received */
static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_paging3 *pa = msgb_l3(msg);
	int payload_len = msgb_l3len(msg) - sizeof(*pa);
	int chan_1, chan_2, chan_3, chan_4;

	/* 3.3.1.1.2: ignore paging while not camping on a cell */
	if (rr->state != GSM48_RR_ST_IDLE || !cs->selected
	 || (cs->state != GSM322_C3_CAMPED_NORMALLY
	  && cs->state != GSM322_C7_CAMPED_ANY_CELL)
	 || cs->neighbour) {
		LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n");

		return 0;
	}
	LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 3\n");

	if (payload_len < 0) { /* must include "channel needed", part of *pa */
		LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 3 "
			"message .\n");

		return -EINVAL;
	}

	/* channel needed */
	chan_1 = pa->cneed1;
	chan_2 = pa->cneed2;
	chan_3 = pa->cneed3;
	chan_4 = pa->cneed4;
	/* first MI */
	if (ms->subscr.tmsi == ntohl(pa->tmsi1)
	 && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
		LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1));
		return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1,
			GSM_MI_TYPE_TMSI);
	} else
		LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
			ntohl(pa->tmsi1));
	/* second MI */
	if (ms->subscr.tmsi == ntohl(pa->tmsi2)
	 && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
		LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2));
		return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1,
			GSM_MI_TYPE_TMSI);
	} else
		LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
			ntohl(pa->tmsi2));
	/* third MI */
	if (ms->subscr.tmsi == ntohl(pa->tmsi3)
	 && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
		LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi3));
		return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1,
			GSM_MI_TYPE_TMSI);
	} else
		LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
			ntohl(pa->tmsi3));
	/* fourth MI */
	if (ms->subscr.tmsi == ntohl(pa->tmsi4)
	 && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
		LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi4));
		return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_4], 1,
			GSM_MI_TYPE_TMSI);
	} else
		LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
			ntohl(pa->tmsi4));

	return 0;
}

/*
 * (immediate) assignment
 */

/* match request reference against request history */
static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref, uint8_t hist_num)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	int i;
	uint8_t ia_t1, ia_t2, ia_t3;
	uint8_t cr_t1, cr_t2, cr_t3;

	for (i = 0; i < hist_num; i++) {
		/* filter confirmed RACH requests only */
		if (rr->cr_hist[i].valid && ref->ra == rr->cr_hist[i].ref.ra) {
			ia_t1 = ref->t1;
			ia_t2 = ref->t2;
			ia_t3 = (ref->t3_high << 3) | ref->t3_low;
			cr_t1 = rr->cr_hist[i].ref.t1;
			cr_t2 = rr->cr_hist[i].ref.t2;
			cr_t3 = (rr->cr_hist[i].ref.t3_high << 3) | rr->cr_hist[i].ref.t3_low;
			if (ia_t1 == cr_t1 && ia_t2 == cr_t2
			 && ia_t3 == cr_t3) {
		 		LOGP(DRR, LOGL_INFO, "request %02x matches "
					"(fn=%d,%d,%d)\n", ref->ra, ia_t1,
					ia_t2, ia_t3);
				return 1;
			} else
		 		LOGP(DRR, LOGL_INFO, "request %02x matches "
					"but not frame number (IMM.ASS "
					"fn=%d,%d,%d != RACH fn=%d,%d,%d)\n",
					ref->ra, ia_t1, ia_t2, ia_t3,
					cr_t1, cr_t2, cr_t3);
		}
	}

	return 0;
}

/* 9.1.18 IMMEDIATE ASSIGNMENT is received */
static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm48_imm_ass *ia = msgb_l3(msg);
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_sysinfo *s = cs->si;
	int ma_len = msgb_l3len(msg) - sizeof(*ia);
	uint8_t ch_type, ch_subch, ch_ts;
	struct gsm48_rr_cd cd;
#ifndef TEST_STARTING_TIMER
	uint8_t *st, st_len;
#endif

	/* ignore imm.ass. while not camping on a cell */
	if (!cs->selected || cs->neighbour || !s) {
		LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT ignored, we are "
			"have not proper selected the serving cell.\n");

		return 0;
	}

	memset(&cd, 0, sizeof(cd));
	cd.ind_tx_power = rr->cd_now.ind_tx_power;

	if (ma_len < 0 /* mobile allocation IE must be included */
	 || ia->mob_alloc_len > ma_len) { /* short read of IE */
		LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT "
			"message.\n");
		return -EINVAL;
	}
	if (ia->mob_alloc_len > 8) {
		LOGP(DRR, LOGL_NOTICE, "Moble allocation in IMMEDIATE "
			"ASSIGNMENT too large.\n");
		return -EINVAL;
	}

	/* starting time */
#ifdef TEST_STARTING_TIMER
	cd.start = 1;
	cd.start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432;
	LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n");
#else
	st_len = ma_len - ia->mob_alloc_len;
	st = ia->mob_alloc + ia->mob_alloc_len;
	if (st_len >= 3 && st[0] == GSM48_IE_START_TIME)
		gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1));
#endif

	/* decode channel description */
	LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:\n");
	cd.chan_nr = ia->chan_desc.chan_nr;
	if (rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRR, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, cd.chan_nr);
		return -EINVAL;
	}
	if (ia->chan_desc.h0.h) {
		cd.h = 1;
		gsm48_decode_chan_h1(&ia->chan_desc, &cd.tsc, &cd.maio,
			&cd.hsn);
		LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x "
			"MAIO %u HSN %u TS %u SS %u TSC %u)\n",
			ia->timing_advance,
			ia->timing_advance * GSM_TA_CM / 100,
			ia->req_ref.ra, ia->chan_desc.chan_nr, cd.maio,
			cd.hsn, ch_ts, ch_subch, cd.tsc);
	} else {
		cd.h = 0;
		gsm48_decode_chan_h0(&ia->chan_desc, &cd.tsc, &cd.arfcn);
		cd.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd.arfcn);
		LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x "
			"ARFCN %s TS %u SS %u TSC %u)\n",
			ia->timing_advance,
			ia->timing_advance * GSM_TA_CM / 100,
			ia->req_ref.ra, ia->chan_desc.chan_nr,
			gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc);
	}

	/* 3.3.1.1.2: ignore assignment while idle */
	if (rr->state != GSM48_RR_ST_CONN_PEND || rr->wait_assign == 0) {
		LOGP(DRR, LOGL_INFO, "Not for us, no request.\n");
		return 0;
	}

	if (rr->wait_assign == 2) {
		LOGP(DRR, LOGL_INFO, "Ignoring, channel already assigned.\n");
		return 0;
	}

	/* request ref */
	if (gsm48_match_ra(ms, &ia->req_ref, IMM_ASS_HISTORY)) {
		/* channel description */
		memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now));
		/* timing advance */
		rr->cd_now.ind_ta = ia->timing_advance;
		/* mobile allocation */
		memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len,
			ia->mob_alloc_len + 1);
		rr->wait_assign = 2;
		/* reset scheduler */
		LOGP(DRR, LOGL_INFO, "resetting scheduler\n");
		l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);

		return gsm48_rr_dl_est(ms);
	}
	LOGP(DRR, LOGL_INFO, "Request, but not for us.\n");

	return 0;
}

/* 9.1.19 IMMEDIATE ASSIGNMENT EXTENDED is received */
static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_sysinfo *s = cs->si;
	struct gsm48_imm_ass_ext *ia = msgb_l3(msg);
	int ma_len = msgb_l3len(msg) - sizeof(*ia);
	uint8_t ch_type, ch_subch, ch_ts;
	struct gsm48_rr_cd cd1, cd2;
#ifndef TEST_STARTING_TIMER
	uint8_t *st, st_len;
#endif

	/* ignore imm.ass.ext while not camping on a cell */
	if (!cs->selected || cs->neighbour || !s) {
		LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT ignored, we are "
			"have not proper selected the serving cell.\n");

		return 0;
	}

	memset(&cd1, 0, sizeof(cd1));
	cd1.ind_tx_power = rr->cd_now.ind_tx_power;
	memset(&cd2, 0, sizeof(cd2));
	cd2.ind_tx_power = rr->cd_now.ind_tx_power;

	if (ma_len < 0 /* mobile allocation IE must be included */
	 || ia->mob_alloc_len > ma_len) { /* short read of IE */
		LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT "
			"EXTENDED message.\n");
		return -EINVAL;
	}
	if (ia->mob_alloc_len > 4) {
		LOGP(DRR, LOGL_NOTICE, "Moble allocation in IMMEDIATE "
			"ASSIGNMENT EXTENDED too large.\n");
		return -EINVAL;
	}

#ifdef TEST_STARTING_TIMER
	cd1.start = 1;
	cd2.start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432;
	memcpy(&cd2, &cd1, sizeof(cd2));
	LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n");
#else
	/* starting time */
	st_len = ma_len - ia->mob_alloc_len;
	st = ia->mob_alloc + ia->mob_alloc_len;
	if (st_len >= 3 && st[0] == GSM48_IE_START_TIME) {
		gsm48_decode_start_time(&cd1,
			(struct gsm48_start_time *)(st+1));
		memcpy(&cd2, &cd1, sizeof(cd2));
	}
#endif

	/* decode channel description */
	LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT EXTENDED:\n");
	cd1.chan_nr = ia->chan_desc1.chan_nr;
	if (rsl_dec_chan_nr(cd1.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRR, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, cd1.chan_nr);
		return -EINVAL;
	}
	if (ia->chan_desc1.h0.h) {
		cd1.h = 1;
		gsm48_decode_chan_h1(&ia->chan_desc1, &cd1.tsc, &cd1.maio,
			&cd1.hsn);
		LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x "
			"chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n",
			ia->timing_advance1,
			ia->timing_advance1 * GSM_TA_CM / 100,
			ia->req_ref1.ra, ia->chan_desc1.chan_nr, cd1.maio,
			cd1.hsn, ch_ts, ch_subch, cd1.tsc);
	} else {
		cd1.h = 0;
		gsm48_decode_chan_h0(&ia->chan_desc1, &cd1.tsc, &cd1.arfcn);
		cd1.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd1.arfcn);
		LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x "
			"chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n",
			ia->timing_advance1,
			ia->timing_advance1 * GSM_TA_CM / 100,
			ia->req_ref1.ra, ia->chan_desc1.chan_nr,
			gsm_print_arfcn(cd1.arfcn), ch_ts, ch_subch, cd1.tsc);
	}
	cd2.chan_nr = ia->chan_desc2.chan_nr;
	if (rsl_dec_chan_nr(cd2.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRR, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, cd2.chan_nr);
		return -EINVAL;
	}
	if (ia->chan_desc2.h0.h) {
		cd2.h = 1;
		gsm48_decode_chan_h1(&ia->chan_desc2, &cd2.tsc, &cd2.maio,
			&cd2.hsn);
		LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x "
			"chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n",
			ia->timing_advance2,
			ia->timing_advance2 * GSM_TA_CM / 100,
			ia->req_ref2.ra, ia->chan_desc2.chan_nr, cd2.maio,
			cd2.hsn, ch_ts, ch_subch, cd2.tsc);
	} else {
		cd2.h = 0;
		gsm48_decode_chan_h0(&ia->chan_desc2, &cd2.tsc, &cd2.arfcn);
		cd2.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd2.arfcn);
		LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x "
			"chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n",
			ia->timing_advance2,
			ia->timing_advance2 * GSM_TA_CM / 100,
			ia->req_ref2.ra, ia->chan_desc2.chan_nr,
			gsm_print_arfcn(cd2.arfcn), ch_ts, ch_subch, cd2.tsc);
	}

	/* 3.3.1.1.2: ignore assignment while idle */
	if (rr->state != GSM48_RR_ST_CONN_PEND || rr->wait_assign == 0) {
		LOGP(DRR, LOGL_INFO, "Not for us, no request.\n");
		return 0;
	}

	if (rr->wait_assign == 2) {
		LOGP(DRR, LOGL_INFO, "Ignoring, channel already assigned.\n");
		return 0;
	}

	/* request ref 1 */
	if (gsm48_match_ra(ms, &ia->req_ref1, IMM_ASS_HISTORY)) {
		/* channel description */
		memcpy(&rr->cd_now, &cd1, sizeof(rr->cd_now));
		/* timing advance */
		rr->cd_now.ind_ta = ia->timing_advance1;
		/* mobile allocation */
		memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len,
			ia->mob_alloc_len + 1);
		rr->wait_assign = 2;
		/* reset scheduler */
		LOGP(DRR, LOGL_INFO, "resetting scheduler\n");
		l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);

		return gsm48_rr_dl_est(ms);
	}
	/* request ref 2 */
	if (gsm48_match_ra(ms, &ia->req_ref2, IMM_ASS_HISTORY)) {
		/* channel description */
		memcpy(&rr->cd_now, &cd2, sizeof(rr->cd_now));
		/* timing advance */
		rr->cd_now.ind_ta = ia->timing_advance2;
		/* mobile allocation */
		memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len,
			ia->mob_alloc_len + 1);
		rr->wait_assign = 2;
		/* reset scheduler */
		LOGP(DRR, LOGL_INFO, "resetting scheduler\n");
		l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);

		return gsm48_rr_dl_est(ms);
	}
	LOGP(DRR, LOGL_INFO, "Request, but not for us.\n");

	return 0;
}

/* 9.1.20 IMMEDIATE ASSIGNMENT REJECT is received */
static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_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 != GSM48_RR_ST_CONN_PEND || rr->wait_assign == 0)
		return 0;

	if (rr->wait_assign == 2) {
		return 0;
	}

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT "
			"REJECT message.\n");
		return -EINVAL;
	}

	for (i = 0; i < 4; i++) {
		/* request reference */
		req_ref = (struct gsm48_req_ref *)
				(((uint8_t *)&ia->req_ref1) + i * 4);
		LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT REJECT "
			"(ref 0x%02x)\n", req_ref->ra);
		if (gsm48_match_ra(ms, req_ref, IMM_ASS_HISTORY)) {
			/* 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 (!osmo_timer_pending(&rr->t3126))
				start_rr_t3126(rr, 5, 0); /* TODO improve! */
			/* stop assignment requests */
			rr->n_chan_req = 0;

			/* wait until timer 3126 expires, then release
			 * or wait for channel assignment */
			return 0;
		}
	}

	return 0;
}

/* 9.1.1 ADDITIONAL ASSIGNMENT is received */
static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg)
{
	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);
	struct tlv_parsed tp;

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of ADDITIONAL ASSIGNMENT message\n");
		return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}
	if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, aa->data, payload_len, 0, 0) < 0) {
		LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
		return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}

	return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
}

/*
 * measturement reports
 */

static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm48_sysinfo *s = ms->cellsel.si;
	struct rx_meas_stat *meas = &rr->ms->meas;
	struct gsm48_rr_meas *rrmeas = &rr->meas;
	struct msgb *nmsg;
	struct gsm48_hdr *gh;
	struct gsm48_meas_res *mr;
	uint8_t serv_rxlev_full = 0, serv_rxlev_sub = 0, serv_rxqual_full = 0,
		serv_rxqual_sub = 0;
	uint8_t ta, tx_power;
	uint8_t rep_ba = 0, rep_valid = 0, meas_valid = 0;
	uint8_t n = 0, rxlev_nc[6], bsic_nc[6], bcch_f_nc[6];

	/* just in case! */
	if (!s)
		return -EINVAL;

	/* check if SI5* is completely received, check BA-IND */
	if (s->si5
	 && (!s->nb_ext_ind_si5 || s->si5bis)) {
		rep_ba = s->nb_ba_ind_si5;
		if ((s->si5bis && s->nb_ext_ind_si5
		  && s->nb_ba_ind_si5bis != rep_ba)
		 || (s->si5ter && s->nb_ba_ind_si5ter != rep_ba)) {
			LOGP(DRR, LOGL_NOTICE, "BA-IND mismatch on SI5*");
		} else
			rep_valid = 1;
	}

	/* check for valid measurements, any frame must exist */
	if (meas->frames) {
		meas_valid = 1;
		serv_rxlev_full = serv_rxlev_sub =
			(meas->rxlev + (meas->frames / 2)) / meas->frames;
		serv_rxqual_full = serv_rxqual_sub = 0; // FIXME
	}

	memset(&rxlev_nc, 0, sizeof(rxlev_nc));
	memset(&bsic_nc, 0, sizeof(bsic_nc));
	memset(&bcch_f_nc, 0, sizeof(bcch_f_nc));
	if (rep_valid) {
		int8_t strongest, current;
		uint8_t ncc;
		int i, index, strongest_i;

#if 0
		/* FIXME: multi-band reporting, if not: 0 = normal reporting */
		uint8_t multi_rep = s->si5ter ?
			s->nb_multi_rep_si5ter : 0;
#endif

		/* get 6 strongest measurements */
		strongest = 127; /* infinite */
		for (n = 0; n < 6; n++) {
			current = -128; /* -infinite */
			index = 0;
			for (i = 0; i < rrmeas->nc_num; i++) {
				/* Skip stronger cells that have been added to measurement report so far. */
				if (rrmeas->nc_rxlev_dbm[i] > strongest)
					continue;
				/* Skip cells with equal strength that have been added so far. */
				if (rrmeas->nc_rxlev_dbm[i] == strongest && i <= strongest_i)
					continue;
				/* only check if NCC is permitted */
				ncc = rrmeas->nc_bsic[i] >> 3;
				if ((s->nb_ncc_permitted_si6 & (1 << ncc))
				 && rrmeas->nc_rxlev_dbm[i] > current) {
					current = rrmeas->nc_rxlev_dbm[i];
					strongest = current;
					index = i;
					strongest_i = i;
				}
			}
			if (current == -128) /* no more found */
				break;
			rxlev_nc[n] = rrmeas->nc_rxlev_dbm[index] + 110;
			bsic_nc[n] = rrmeas->nc_bsic[index];
			bcch_f_nc[n] = index;
		}
	}

	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;

	/* use indicated tx-power and TA (not the altered ones) */
	tx_power = rr->cd_now.ind_tx_power;
	// FIXME: degrade power to the max supported level
	ta = rr->cd_now.ind_ta;

	gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
	mr = (struct gsm48_meas_res *) msgb_put(nmsg, sizeof(*mr));

	gh->proto_discr = GSM48_PDISC_RR;
	gh->msg_type = GSM48_MT_RR_MEAS_REP;

	/* measurement results */
	mr->rxlev_full = serv_rxlev_full;
	mr->rxlev_sub = serv_rxlev_sub;
	mr->rxqual_full = serv_rxqual_full;
	mr->rxqual_sub = serv_rxqual_sub;
	mr->dtx_used = 0; // FIXME: no DTX yet
	mr->ba_used = rep_ba;
	mr->meas_valid = !meas_valid; /* 0 = valid */
	if (rep_valid) {
		mr->no_nc_n_hi = n >> 2;
		mr->no_nc_n_lo = n & 3;
	} else {
		/* no results for serving cells */
		mr->no_nc_n_hi = 1;
		mr->no_nc_n_lo = 3;
	}
	mr->rxlev_nc1 = rxlev_nc[0];
	mr->rxlev_nc2_hi = rxlev_nc[1] >> 1;
	mr->rxlev_nc2_lo = rxlev_nc[1] & 1;
	mr->rxlev_nc3_hi = rxlev_nc[2] >> 2;
	mr->rxlev_nc3_lo = rxlev_nc[2] & 3;
	mr->rxlev_nc4_hi = rxlev_nc[3] >> 3;
	mr->rxlev_nc4_lo = rxlev_nc[3] & 7;
	mr->rxlev_nc5_hi = rxlev_nc[4] >> 4;
	mr->rxlev_nc5_lo = rxlev_nc[4] & 15;
	mr->rxlev_nc6_hi = rxlev_nc[5] >> 5;
	mr->rxlev_nc6_lo = rxlev_nc[5] & 31;
	mr->bsic_nc1_hi = bsic_nc[0] >> 3;
	mr->bsic_nc1_lo = bsic_nc[0] & 7;
	mr->bsic_nc2_hi = bsic_nc[1] >> 4;
	mr->bsic_nc2_lo = bsic_nc[1] & 15;
	mr->bsic_nc3_hi = bsic_nc[2] >> 5;
	mr->bsic_nc3_lo = bsic_nc[2] & 31;
	mr->bsic_nc4 = bsic_nc[3];
	mr->bsic_nc5 = bsic_nc[4];
	mr->bsic_nc6 = bsic_nc[5];
	mr->bcch_f_nc1 = bcch_f_nc[0];
	mr->bcch_f_nc2 = bcch_f_nc[1];
	mr->bcch_f_nc3 = bcch_f_nc[2];
	mr->bcch_f_nc4 = bcch_f_nc[3];
	mr->bcch_f_nc5_hi = bcch_f_nc[4] >> 1;
	mr->bcch_f_nc5_lo = bcch_f_nc[4] & 1;
	mr->bcch_f_nc6_hi = bcch_f_nc[5] >> 2;
	mr->bcch_f_nc6_lo = bcch_f_nc[5] & 3;

	LOGP(DRR, LOGL_INFO, "MEAS REP: pwr=%d TA=%d meas-invalid=%d "
		"rxlev-full=%d rxlev-sub=%d rxqual-full=%d rxqual-sub=%d "
		"dtx %d ba %d no-ncell-n %d\n", tx_power, ta, mr->meas_valid,
		mr->rxlev_full - 110, mr->rxlev_sub - 110,
		mr->rxqual_full, mr->rxqual_sub, mr->dtx_used, mr->ba_used,
		(mr->no_nc_n_hi << 2) | mr->no_nc_n_lo);

	msgb_tv16_push(nmsg, RSL_IE_L3_INFO,
		nmsg->tail - (uint8_t *)msgb_l3(nmsg));
	msgb_push(nmsg, 2 + 2);
	nmsg->data[0] = RSL_IE_TIMING_ADVANCE;
	nmsg->data[1] = ta;
	nmsg->data[2] = RSL_IE_MS_POWER;
	nmsg->data[3] = tx_power;
	rsl_rll_push_hdr(nmsg, RSL_MT_UNIT_DATA_REQ, rr->cd_now.chan_nr,
		0x40, 1);

	return lapdm_rslms_recvmsg(nmsg, &ms->lapdm_channel);
}

/*
 * link establishment and release
 */

/* process "Loss Of Signal" */
int gsm48_rr_los(struct osmocom_ms *ms)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	uint8_t *mode;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;

	LOGP(DSUM, LOGL_INFO, "Radio link lost signal\n");

	if (rr->vgcs.group_state != GSM48_RR_GST_OFF)
		return gsm48_rr_group_rel(ms, RR_REL_CAUSE_LOST_SIGNAL);

	/* stop T3211 if running */
	stop_rr_t3110(rr);

	switch(rr->state) {
	case GSM48_RR_ST_CONN_PEND:
		LOGP(DRR, LOGL_INFO, "LOS during RACH request\n");

		/* stop pending RACH timer */
		stop_rr_t3126(rr);
		break;
	case GSM48_RR_ST_DEDICATED:
		LOGP(DRR, LOGL_INFO, "LOS during dedicated mode, release "
			"locally\n");

		new_rr_state(rr, GSM48_RR_ST_REL_PEND);

		/* release message */
		rel_local:
		nmsg = gsm48_l3_msgb_alloc();
		if (!nmsg)
			return -ENOMEM;
		mode = msgb_put(nmsg, 2);
		mode[0] = RSL_IE_RELEASE_MODE;
		mode[1] = 1; /* local release */
		/* start release */
		gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0);
		/* release SAPI 3 link, if exits */
		gsm48_release_sapi3_link(ms);
		return 0;
	case GSM48_RR_ST_REL_PEND:
		LOGP(DRR, LOGL_INFO, "LOS during RR release procedure, release "
			"locally\n");

		/* stop pending RACH timer */
		stop_rr_t3110(rr);

		/* release locally */
		goto rel_local;
	default:
		/* this should not happen */
		LOGP(DRR, LOGL_ERROR, "LOS in IDLE state, ignoring\n");
		return -EINVAL;
	}

	/* send inication to upper layer */
	nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
	if (!nmsg)
		return -ENOMEM;
	nrrh = (struct gsm48_rr_hdr *)nmsg->data;
	nrrh->cause = RR_REL_CAUSE_LOST_SIGNAL;
	gsm48_rr_upmsg(ms, nmsg);

	/* return idle */
	new_rr_state(rr, GSM48_RR_ST_IDLE);
	return 0;
}

/* activation of channel in dedicated mode */
static int gsm48_rr_activate_channel(struct osmocom_ms *ms,
	struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len)
{
	struct gsm_subscriber *subscr = &ms->subscr;
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm_settings *set = &ms->settings;
 	struct gsm48_sysinfo *s = ms->cellsel.si;
 	struct rx_meas_stat *meas = &ms->meas;
	uint8_t ch_type, ch_subch, ch_ts;
	uint8_t timeout = 64;

	/* setting (new) timing advance */
	LOGP(DRR, LOGL_INFO, "setting indicated TA %d (actual TA %d)\n",
		cd->ind_ta, cd->ind_ta - set->alter_delay);
	l1ctl_tx_param_req(ms, cd->ind_ta - set->alter_delay,
			(set->alter_tx_power) ? set->alter_tx_power_value
						: cd->ind_tx_power);

	/* reset measurement and link timeout */
	meas->ds_fail = 0;
	if (s) {
		if (s->sacch_radio_link_timeout) {
			timeout = s->sacch_radio_link_timeout;
			LOGP(DRR, LOGL_INFO, "using last SACCH timeout %d\n",
				timeout);
		} else if (s->bcch_radio_link_timeout) {
			timeout = s->bcch_radio_link_timeout;
			LOGP(DRR, LOGL_INFO, "using last BCCH timeout %d\n",
				timeout);
		}
	}
	meas->rl_fail = meas->s = timeout;

	/* setting initial (invalid) measurement report, resetting SI5* */
	if (s) {
		memset(s->si5_msg, 0, sizeof(s->si5_msg));
		memset(s->si5b_msg, 0, sizeof(s->si5b_msg));
		memset(s->si5t_msg, 0, sizeof(s->si5t_msg));
	}
	meas->frames = meas->snr = meas->berr = meas->rxlev = 0;
	rr->meas.nc_num = 0;
	stop_rr_t_meas(rr);
	start_rr_t_meas(rr, 1, 0);
	gsm48_rr_tx_meas_rep(ms);

	/* establish */
	LOGP(DRR, LOGL_INFO, "establishing channel in dedicated/group mode\n");

	if (rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRR, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, cd->chan_nr);
		return -EINVAL;
	}

	LOGP(DRR, LOGL_INFO, " Channel type %d, subch %d, ts %d, mode %d, audio-mode %d, flags 0x%02x, cipher %d\n",
	     ch_type, ch_subch, ch_ts, cd->mode, rr->audio_mode, cd->tch_flags, rr->cipher_type + 1);
	if (cd->h)
		l1ctl_tx_dm_est_req_h1(ms, cd->maio, cd->hsn, ma, ma_len, cd->chan_nr, cd->tsc, cd->mode,
				       rr->audio_mode, cd->tch_flags);
	else
		l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc, cd->mode, rr->audio_mode, cd->tch_flags);
	rr->dm_est = 1;

	/* old SI 5/6 are not valid on a new dedicated channel */
	s->si5 = s->si5bis = s->si5ter = s->si6 = 0;

	if (rr->cipher_on)
		l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr,
			rr->cipher_type + 1, subscr->key, 8);

	return 0;
}

/* frequency change of channel "after time" */
static int gsm48_rr_channel_after_time(struct osmocom_ms *ms,
	struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len, uint16_t fn)
{
	struct gsm_subscriber *subscr = &ms->subscr;
	struct gsm48_rrlayer *rr = &ms->rrlayer;

	if (cd->h)
		l1ctl_tx_dm_freq_req_h1(ms, cd->maio, cd->hsn,
			ma, ma_len, cd->tsc, fn);
	else
		l1ctl_tx_dm_freq_req_h0(ms, cd->arfcn, cd->tsc, fn);

	if (rr->cipher_on)
		l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr,
			rr->cipher_type + 1, subscr->key, 8);

	gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode, cd->tch_flags);

	return 0;
}

/* render list of hopping channels from channel description elements */
static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd,
	uint16_t *ma, uint8_t *ma_len)
{
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_sysinfo *s = cs->si;
	struct gsm_settings *set = &ms->settings;
	int i, index;
	uint16_t arfcn, pcs;

	pcs = gsm_refer_pcs(cs->arfcn, s) ? ARFCN_PCS : 0;

	/* no hopping (no MA), channel description is valid */
	if (!cd->h) {
		*ma_len = 0;
		return 0;
	}

	/* decode mobile allocation */
	if (cd->mob_alloc_lv[0]) {
		struct gsm_sysinfo_freq *freq = s->freq;

		LOGP(DRR, LOGL_INFO, "decoding mobile allocation\n");

		if (cd->cell_desc_lv[0]) {
			LOGP(DRR, LOGL_INFO, "using cell channel descr.\n");
			if (cd->cell_desc_lv[0] != 16) {
				LOGP(DRR, LOGL_ERROR, "cell channel descr. "
					"has invalid length\n");
				return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
			}
			gsm48_decode_freq_list(freq, cd->cell_desc_lv + 1, 16,
				0xce, FREQ_TYPE_SERV);
		}

		gsm48_decode_mobile_alloc(freq, cd->mob_alloc_lv + 1,
			cd->mob_alloc_lv[0], ma, ma_len, 0);
		if (*ma_len < 1) {
			LOGP(DRR, LOGL_NOTICE, "mobile allocation with no "
				"frequency available\n");
			return GSM48_RR_CAUSE_NO_CELL_ALLOC_A;

		}
	} else
	/* decode frequency list */
	if (cd->freq_list_lv[0]) {
		struct gsm_sysinfo_freq f[1024];
		int j = 0;

		LOGP(DRR, LOGL_INFO, "decoding frequency list\n");

		/* get bitmap */
		if (gsm48_decode_freq_list(f, cd->freq_list_lv + 1,
			cd->freq_list_lv[0], 0xce, FREQ_TYPE_SERV)) {
			LOGP(DRR, LOGL_NOTICE, "frequency list invalid\n");
			return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
		}

		/* collect channels from bitmap (1..1023,0) */
		for (i = 1; i <= 1024; i++) {
			if ((f[i & 1023].mask & FREQ_TYPE_SERV)) {
				LOGP(DRR, LOGL_INFO, "Listed ARFCN #%d: %s\n",
					j, gsm_print_arfcn((i & 1023) | pcs));
				if (j == 64) {
					LOGP(DRR, LOGL_NOTICE, "frequency list "
						"exceeds 64 entries!\n");
					return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
				}
				ma[j++] = i & 1023;
			}
		}
		*ma_len = j;
	} else
	/* decode frequency channel sequence */
	if (cd->freq_seq_lv[0]) {
		int j = 0, inc;

		LOGP(DRR, LOGL_INFO, "decoding frequency channel sequence\n");

		if (cd->freq_seq_lv[0] != 9) {
			LOGP(DRR, LOGL_NOTICE, "invalid frequency channel "
				"sequence\n");
			return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
		}
		arfcn = cd->freq_seq_lv[1] & 0x7f;
		LOGP(DRR, LOGL_INFO, "Listed Sequence ARFCN #%d: %s\n", j,
			gsm_print_arfcn(arfcn | pcs));
		ma[j++] = arfcn;
		for (i = 0; i < 16; i++) {
			if ((i & 1))
				inc = cd->freq_seq_lv[2 + (i >> 1)] & 0x0f;
			else
				inc = cd->freq_seq_lv[2 + (i >> 1)] >> 4;
			if (inc) {
				arfcn += inc;
				LOGP(DRR, LOGL_INFO, "Listed Sequence ARFCN "
					"#%d: %s\n", j,
					gsm_print_arfcn(i | pcs));
				ma[j++] = arfcn;
			} else
				arfcn += 15;
		}
		*ma_len = j;
	} else {
		LOGP(DRR, LOGL_NOTICE, "hopping, but nothing that tells us "
			"a sequence\n");
		return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
	}

	/* convert to band_arfcn and check for unsported frequency */
	for (i = 0; i < *ma_len; i++) {
		arfcn = ma[i];
		if (arfcn >= 512 && arfcn <= 810)
			arfcn |= pcs;
		ma[i] = arfcn;
		index = arfcn2index(arfcn);
		if (!(set->freq_map[index >> 3] & (1 << (index & 7)))) {
			LOGP(DRR, LOGL_NOTICE, "Hopping ARFCN %s not "
				"supported\n", gsm_print_arfcn(arfcn));
			return GSM48_RR_CAUSE_FREQ_NOT_IMPL;
		}
	}

	return 0;
}

/* activate link and send establish request */
static int gsm48_rr_dl_est(struct osmocom_ms *ms)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm_subscriber *subscr = &ms->subscr;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct msgb *nmsg;
	struct gsm48_hdr *gh;
	struct gsm48_pag_rsp *pr;
	uint16_t ma[64];
	uint8_t ma_len;

	/* 3.3.1.1.3.1 */
	stop_rr_t3126(rr);

	/* check if we have to change channel at starting time (we delay) */
	if (rr->cd_now.start) {
		int32_t now, start, diff;
		uint32_t start_mili = 0;

		/* how much time do we have left? */
		now = ms->meas.last_fn % 42432;
		start = rr->cd_now.start_tm.fn % 42432;
		diff = start - now;
		if (diff < 0)
			diff += 42432;
		LOGP(DRR, LOGL_INFO, " (Tnow %d Tstart %d diff %d)\n",
			now, start, diff);
		start_mili = (uint32_t)diff * 19580 / 42432 * 10;
		if (diff >= 32024 || !start_mili) {
			LOGP(DRR, LOGL_INFO, " -> Start time already "
				"elapsed\n");
			rr->cd_now.start = 0;
		} else {
			LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the "
				"future\n", start_mili);
		}

#ifndef TEST_FREQUENCY_MOD
		/* schedule start of IMM.ASS */
		rr->modify_state = GSM48_RR_MOD_IMM_ASS;
		start_rr_t_starting(rr, start_mili / 1000,
			(start_mili % 1000) * 1000);
		/* when timer fires, start time is already elapsed */
		rr->cd_now.start = 0;

		return 0;
#endif
	}

	/* get hopping sequence, if required */
	if (gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len))
		return -EINVAL;

	/* clear all sequence numbers for all possible PDs */
	rr->v_sd = 0;

	/* send DL_EST_REQ */
	if (rr->rr_est_msg) {
		LOGP(DRR, LOGL_INFO, "sending establish message\n");

		/* use queued message */
		nmsg = rr->rr_est_msg;
		rr->rr_est_msg = 0;

		/* set sequence number and increment */
		gsm48_apply_v_sd(rr, nmsg);
	} else {
		/* create paging response */
		nmsg = gsm48_l3_msgb_alloc();
		if (!nmsg)
			return -ENOMEM;
		gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
		gh->proto_discr = GSM48_PDISC_RR;
		gh->msg_type = GSM48_MT_RR_PAG_RESP;
		pr = (struct gsm48_pag_rsp *) msgb_put(nmsg, sizeof(*pr));
		/* key sequence */
		pr->key_seq = gsm_subscr_get_key_seq(ms, subscr);
		/* classmark 2 */
		pr->cm2_len = sizeof(pr->cm2);
		gsm48_rr_enc_cm2(ms, &pr->cm2, rr->cd_now.arfcn);
		/* mobile identity */
		if (ms->subscr.tmsi != GSM_RESERVED_TMSI
		 && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)
		 && rr->paging_mi_type == GSM_MI_TYPE_TMSI) {
			gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false);
			LOGP(DRR, LOGL_INFO, "sending paging response with "
				"TMSI\n");
		} else if (subscr->imsi[0]) {
			gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false);
			LOGP(DRR, LOGL_INFO, "sending paging response with "
				"IMSI\n");
		} else {
			gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_NONE, false);
			LOGP(DRR, LOGL_INFO, "sending paging response without "
				"TMSI/IMSI\n");
		}
	}

#ifdef TEST_FREQUENCY_MOD
	LOGP(DRR, LOGL_INFO, " TESTING: frequency modify IMM.ASS\n");
	memcpy(&rr->cd_before, &rr->cd_now, sizeof(rr->cd_before));
	rr->cd_before.h = 0;
	rr->cd_before.arfcn = 0;
	/* activate channel */
	gsm48_rr_activate_channel(ms, &rr->cd_before, ma, ma_len);
	/* render channel "after time" */
	gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
	/* schedule change of channel */
	gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len,
		rr->cd_now.start_tm.fn);
#else
	/* activate channel */
	gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
#endif

	/* set T200 of SAPI 0 */
	ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_sec =
		T200_DCCH;
	ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_usec = 0;

	/* start establishmnet */
	return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg, 0);
}

/* the link is established */
static int gsm48_rr_estab_cnf(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm_support *sup = &ms->support;
	struct gsm322_cellsel *cs = &ms->cellsel;
	uint8_t *mode;
	struct msgb *nmsg;

	/* if MM has releases before confirm, we start release */
	if (rr->state == GSM48_RR_ST_REL_PEND) {
		LOGP(DRR, LOGL_INFO, "MM already released RR.\n");
		/* release message */
		nmsg = gsm48_l3_msgb_alloc();
		if (!nmsg)
			return -ENOMEM;
		mode = msgb_put(nmsg, 2);
		mode[0] = RSL_IE_RELEASE_MODE;
		mode[1] = 0; /* normal release */
		/* start release */
		return gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0);
	}

	/* 3.3.1.1.4 */
	new_rr_state(rr, GSM48_RR_ST_DEDICATED);

	/* early classmark sending */
	if (cs->si->ecsm && sup->es_ind)
		gsm48_rr_tx_cm_change(ms);

	/* send confirm to upper layer */
	nmsg = gsm48_rr_msgb_alloc(
		(rr->rr_est_req) ? GSM48_RR_EST_CNF : GSM48_RR_EST_IND);
	if (!nmsg)
		return -ENOMEM;
	return gsm48_rr_upmsg(ms, nmsg);
}

/* the link is released in pending state (by l2) */
static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;

	/* Handle on group channel. */
	if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) {
		LOGP(DRR, LOGL_INFO, "Returning to group receive mode, due to link release indication.\n");
		return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_NORMAL);
	}

	/* switch back to old channel, if modify/ho failed */
	switch (rr->modify_state) {
	case GSM48_RR_MOD_ASSIGN:
	case GSM48_RR_MOD_HANDO:
		/* channel is deactivate there */
		return gsm48_rr_rel_cnf(ms, msg);
	case GSM48_RR_MOD_ASSIGN_RESUME:
	case GSM48_RR_MOD_HANDO_RESUME:
		rr->modify_state = GSM48_RR_MOD_NONE;
		break;
	}

	LOGP(DSUM, LOGL_INFO, "Radio link is released\n");

	/* send inication to upper layer */
	nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
	if (!nmsg)
		return -ENOMEM;
	nrrh = (struct gsm48_rr_hdr *)nmsg->data;
	nrrh->cause = RR_REL_CAUSE_NORMAL;
	gsm48_rr_upmsg(ms, nmsg);

	/* start release timer, so UA will be transmitted */
	start_rr_t_rel_wait(rr, 1, 500000);

	/* pending release */
	new_rr_state(rr, GSM48_RR_ST_REL_PEND);

	/* also release SAPI 3 link, if exists */
	gsm48_release_sapi3_link(ms);

	return 0;
}

/* 9.1.7 CHANNEL RELEASE is received  */
static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm48_hdr *gh = msgb_l3(msg);
	struct gsm48_chan_rel *cr = (struct gsm48_chan_rel *)gh->data;
	int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cr);
	struct tlv_parsed tp;
	struct msgb *nmsg;
	uint8_t *mode;

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE message\n");
		return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}
	if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0) < 0) {
		LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
		return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}

	LOGP(DRR, LOGL_INFO, "channel release request with cause 0x%02x\n",
		cr->rr_cause);

	/* BA range */
	if (TLVP_PRESENT(&tp, GSM48_IE_BA_RANGE)) {
		gsm48_decode_ba_range(TLVP_VAL(&tp, GSM48_IE_BA_RANGE),
			*(TLVP_VAL(&tp, GSM48_IE_BA_RANGE) - 1), rr->ba_range,
			&rr->ba_ranges,
			sizeof(rr->ba_range) / sizeof(rr->ba_range[0]));
		/* NOTE: the ranges are kept until IDLE state is returned
		 * (see new_rr_state)
		 */
	}

	new_rr_state(rr, GSM48_RR_ST_REL_PEND);

	/* When we receive a channel release, we are not just releasing the transmit mode. */
	rr->vgcs.group_state = GSM48_RR_GST_OFF;

	/* start T3110, so that two DISCs can be sent due to T200 timeout */
	start_rr_t3110(rr, 1, 500000);

	/* disconnect the main signalling link */
	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	mode = msgb_put(nmsg, 2);
	mode[0] = RSL_IE_RELEASE_MODE;
	mode[1] = 0; /* normal release */
	gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0);

	/* release SAPI 3 link, if exits */
	gsm48_release_sapi3_link(ms);
	return 0;
}

/* Release of channel on VGCS/VBS. */
static int gsm48_rr_rx_chan_rel_ui(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm48_hdr *gh = msgb_l3(msg);
	struct gsm48_chan_rel *cr = (struct gsm48_chan_rel *)gh->data;
	int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cr);
	struct tlv_parsed tp;

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE message\n");
		return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}
	if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0) < 0) {
		LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
		return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}

	LOGP(DRR, LOGL_INFO, "CHANNEL RELESE via UI frame with cause 0x%02x\n", cr->rr_cause);

	/* Only allow when in group receive mode. */
	if (rr->vgcs.group_state != GSM48_RR_GST_RECEIVE)
		return 0;

	return gsm48_rr_group_rel(ms, RR_REL_CAUSE_NORMAL);
}

/*
 * frequency redefition, channel mode modify, assignment, and handover
 */

/* set channel mode in case of TCH */
static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, uint8_t mode, uint8_t tch_flags)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	uint8_t ch_type, ch_subch, ch_ts;

	if (rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRR, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, chan_nr);
		return -EINVAL;
	}

	/* only apply mode to TCH/F or TCH/H */
	if (ch_type != RSL_CHAN_Bm_ACCHs
	 && ch_type != RSL_CHAN_Lm_ACCHs)
		return -ENOTSUP;

	/* Apply indicated channel mode */
	LOGP(DRR, LOGL_INFO, "setting TCH mode to %s, audio mode to %d, tch flags to %d\n",
	     get_value_string(gsm48_chan_mode_names, mode), rr->audio_mode, tch_flags);
	l1ctl_tx_tch_mode_req(ms, mode, rr->audio_mode, tch_flags, rr->tch_loop_mode);

	return 0;
}

/* 9.1.13 FREQUENCY REDEFINITION is received */
static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_sysinfo *s = cs->si;
	struct gsm48_frq_redef *fr = msgb_l3(msg);
	int mob_al_len = msgb_l3len(msg) - sizeof(*fr);
	uint8_t ch_type, ch_subch, ch_ts;
	struct gsm48_rr_cd cd;
	uint8_t cause;
	uint8_t *st;
	uint16_t ma[64];
	uint8_t ma_len;

	memcpy(&cd, &rr->cd_now, sizeof(cd));

	if (mob_al_len < 0 /* mobile allocation IE must be included */
	 || fr->mob_alloc_len + 2 > mob_al_len) { /* short read of IE */
		LOGP(DRR, LOGL_NOTICE, "Short read of FREQUENCY REDEFINITION "
			"message.\n");
		return -EINVAL;
	}
	if (fr->mob_alloc_len > 8) {
		LOGP(DRR, LOGL_NOTICE, "Moble allocation in FREQUENCY "
			"REDEFINITION too large.\n");
		return -EINVAL;
	}

	/* decode channel description */
	LOGP(DRR, LOGL_INFO, "FREQUENCY REDEFINITION:\n");
	cd.chan_nr = fr->chan_desc.chan_nr;
	if (rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRR, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, cd.chan_nr);
		return -EINVAL;
	}
	if (fr->chan_desc.h0.h) {
		cd.h = 1;
		gsm48_decode_chan_h1(&fr->chan_desc, &cd.tsc, &cd.maio,
			&cd.hsn);
		LOGP(DRR, LOGL_INFO, " (MAIO %u HSN %u TS %u SS %u TSC %u)\n",
			cd.maio, cd.hsn, ch_ts, ch_subch, cd.tsc);
	} else {
		cd.h = 0;
		gsm48_decode_chan_h0(&fr->chan_desc, &cd.tsc, &cd.arfcn);
		cd.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd.arfcn);
		LOGP(DRR, LOGL_INFO, " (ARFCN %s TS %u SS %u TSC %u)\n",
			gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc);
	}

	/* mobile allocation */
	memcpy(rr->cd_now.mob_alloc_lv, &fr->mob_alloc_len,
		fr->mob_alloc_len + 1);

	/* starting time */
	st = fr->mob_alloc + fr->mob_alloc_len;
	gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1));

	/* cell channel description */
	if (mob_al_len >= fr->mob_alloc_len + 2 + 17
	 && fr->mob_alloc[fr->mob_alloc_len + 2] == GSM48_IE_CELL_CH_DESC) {
		const uint8_t *v = fr->mob_alloc + fr->mob_alloc_len + 2 + 1;

		LOGP(DRR, LOGL_INFO, " using cell channel description)\n");
		cd.cell_desc_lv[0] = 16;
		memcpy(cd.cell_desc_lv + 1, v, 17);
	}

	/* render channel "after time" */
	cause = gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
	if (cause)
		return gsm48_rr_tx_rr_status(ms, cause);

	/* update to new channel data */
	memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now));

	/* schedule change of channel */
	gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len,
		rr->cd_now.start_tm.fn);

	rr->cd_now.start = 0;

	return 0;
}

/* 9.1.6 sending CHANNEL MODE MODIFY ACKNOWLEDGE */
static int gsm48_rr_tx_chan_modify_ack(struct osmocom_ms *ms,
		struct gsm48_chan_desc *cd, uint8_t mode)
{
	struct msgb *nmsg;
	struct gsm48_hdr *gh;
	struct gsm48_chan_mode_modify *cm;

	LOGP(DRR, LOGL_INFO, "CHAN.MODE.MOD ACKNOWLEDGE\n");

	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
	cm = (struct gsm48_chan_mode_modify *) msgb_put(nmsg, sizeof(*cm));

	gh->proto_discr = GSM48_PDISC_RR;
	gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF_ACK;

	/* CD */
	memcpy(&cm->chan_desc, cd, sizeof(struct gsm48_chan_desc));
	/* mode */
	cm->mode = mode;

	return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0);
}

/* 9.1.5 CHANNEL MODE MODIFY is received */
static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_sysinfo *s = cs->si;
	struct gsm48_hdr *gh = msgb_l3(msg);
	struct gsm48_chan_mode_modify *cm =
		(struct gsm48_chan_mode_modify *)gh->data;
	int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm);
	struct gsm48_rr_cd *cd = &rr->cd_now;
	uint8_t ch_type, ch_subch, ch_ts;
	int rc;

	LOGP(DRR, LOGL_INFO, "CHANNEL MODE MODIFY\n");


	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL MODE MODIFY "
			"message.\n");
		return gsm48_rr_tx_rr_status(ms,
			GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}

	/* decode channel description */
	cd->chan_nr = cm->chan_desc.chan_nr;
	if (rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRR, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, cd->chan_nr);
		return -EINVAL;
	}
	if (cm->chan_desc.h0.h) {
		cd->h = 1;
		gsm48_decode_chan_h1(&cm->chan_desc, &cd->tsc, &cd->maio,
			&cd->hsn);
		LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x MAIO %u HSN %u TS %u "
			"SS %u TSC %u mode %u)\n", cm->chan_desc.chan_nr,
			cd->maio, cd->hsn, ch_ts, ch_subch, cd->tsc, cm->mode);
	} else {
		cd->h = 0;
		gsm48_decode_chan_h0(&cm->chan_desc, &cd->tsc, &cd->arfcn);
		cd->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd->arfcn);
		LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x ARFCN %s TS %u SS %u "
			"TSC %u mode %u)\n", cm->chan_desc.chan_nr,
			gsm_print_arfcn(cd->arfcn), ch_ts, ch_subch, cd->tsc,
			cm->mode);
	}

	/**
	 * According to 3GPP TS 04.08, section 3.4.6.1.3
	 * "Abnormal cases" of "channel mode modify procedure",
	 * if the MS doesn't support the indicated channel mode,
	 * it shall retain the old mode and return the associated
	 * channel mode information in the ACKNOWLEDGE message.
	 */

	/* Check if we support this channel mode */
	rc = gsm48_rr_check_mode(ms, cd->chan_nr, cm->mode);
	if (rc)
		goto ack;

	/* Attempt to apply this mode */
	rc = gsm48_rr_set_mode(ms, cd->chan_nr, cm->mode, cd->tch_flags);
	if (rc)
		goto ack;

	/* Finally set (a new) mode */
	cd->mode = cm->mode;

ack:
	return gsm48_rr_tx_chan_modify_ack(ms, &cm->chan_desc, cd->mode);
}

/* 9.1.3 sending ASSIGNMENT COMPLETE */
static int gsm48_rr_tx_ass_cpl(struct osmocom_ms *ms, uint8_t cause)
{
	struct msgb *nmsg;
	struct gsm48_hdr *gh;
	struct gsm48_ass_cpl *ac;

	LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMPLETE (cause #%d)\n", cause);

	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
	ac = (struct gsm48_ass_cpl *) msgb_put(nmsg, sizeof(*ac));

	gh->proto_discr = GSM48_PDISC_RR;
	gh->msg_type = GSM48_MT_RR_ASS_COMPL;

	/* RR_CAUSE */
	ac->rr_cause = cause;

	/* set T200 of SAPI 0 */
	ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_sec =
		T200_DCCH;
	ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_usec = 0;

	return gsm48_send_rsl(ms, RSL_MT_RES_REQ, nmsg, 0);
}

/* 9.1.4 sending ASSIGNMENT FAILURE */
static int gsm48_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause,
	uint8_t rsl_prim)
{
	struct msgb *nmsg;
	struct gsm48_hdr *gh;
	struct gsm48_ass_fail *af;

	LOGP(DRR, LOGL_INFO, "ASSIGNMENT FAILURE (cause #%d)\n", cause);

	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
	af = (struct gsm48_ass_fail *) msgb_put(nmsg, sizeof(*af));

	gh->proto_discr = GSM48_PDISC_RR;
	gh->msg_type = GSM48_MT_RR_ASS_COMPL;

	/* RR_CAUSE */
	af->rr_cause = cause;

	return gsm48_send_rsl(ms, rsl_prim, nmsg, 0);
}

/* 9.1.2 ASSIGNMENT COMMAND is received */
static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_sysinfo *s = cs->si;
	struct gsm48_hdr *gh = msgb_l3(msg);
	struct gsm48_ass_cmd *ac = (struct gsm48_ass_cmd *)gh->data;
	int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ac);
	struct tlv_parsed tp;
	struct gsm48_rr_cd *cda = &rr->cd_after;
	struct gsm48_rr_cd *cdb = &rr->cd_before;
	uint8_t ch_type, ch_subch, ch_ts;
	uint8_t before_time = 0;
	uint16_t ma[64];
	uint8_t ma_len;
	uint32_t start_mili = 0;
	uint8_t cause;
	struct msgb *nmsg;


	LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMMAND\n");

	memset(cda, 0, sizeof(*cda));
	cda->ind_tx_power = rr->cd_now.ind_tx_power;
	memset(cdb, 0, sizeof(*cdb));
	cdb->ind_tx_power = rr->cd_now.ind_tx_power;

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND message\n");
		return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}
	if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0) < 0) {
		LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
		return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}

	/* decode channel description (before time) */
	if (TLVP_PRESENT(&tp, GSM48_IE_CH_DESC_1_BEFORE)) {
		struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *)
			TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE);
		cdb->chan_nr = ccd->chan_nr;
		if (rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
			LOGP(DRR, LOGL_ERROR,
			     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
			     __func__, cdb->chan_nr);
			return -EINVAL;
		}
		if (ccd->h0.h) {
			cdb->h = 1;
			gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio,
				&cdb->hsn);
			LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x MAIO %u "
				"HSN %u TS %u SS %u TSC %u)\n", ccd->chan_nr,
				cdb->maio, cdb->hsn, ch_ts, ch_subch, cdb->tsc);
		} else {
			cdb->h = 0;
			gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn);
			cdb->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cdb->arfcn);
			LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x "
				"ARFCN %s TS %u SS %u TSC %u)\n", ccd->chan_nr,
				gsm_print_arfcn(cdb->arfcn),
				ch_ts, ch_subch, cdb->tsc);
		}
		before_time = 1;
	}

	/* decode channel description (after time) */
	cda->chan_nr = ac->chan_desc.chan_nr;
	if (rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRR, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, cda->chan_nr);
		return -EINVAL;
	}
	if (ac->chan_desc.h0.h) {
		cda->h = 1;
		gsm48_decode_chan_h1(&ac->chan_desc, &cda->tsc, &cda->maio,
			&cda->hsn);
		LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x MAIO %u HSN %u "
			"TS %u SS %u TSC %u)\n", ac->chan_desc.chan_nr,
			cda->maio, cda->hsn, ch_ts, ch_subch, cda->tsc);
	} else {
		cda->h = 0;
		gsm48_decode_chan_h0(&ac->chan_desc, &cda->tsc, &cda->arfcn);
		cda->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cda->arfcn);
		LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %s TS %u "
			"SS %u TSC %u)\n", ac->chan_desc.chan_nr,
			gsm_print_arfcn(cda->arfcn), ch_ts, ch_subch, cda->tsc);
	}

	/* starting time */
#ifdef TEST_STARTING_TIMER
	cda->start = 1;
	cda->start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432;
	LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n");
#else
	if (TLVP_PRESENT(&tp, GSM48_IE_START_TIME)) {
		gsm48_decode_start_time(cda, (struct gsm48_start_time *)
			TLVP_VAL(&tp, GSM48_IE_START_TIME));
		/* 9.1.2.5 "... before time IE is not present..." */
		if (!before_time) {
			LOGP(DRR, LOGL_INFO, " -> channel description after "
				"time only, but starting time\n");
		} else
			LOGP(DRR, LOGL_INFO, " -> channel description before "
				"and after time\n");
	} else {
		/* 9.1.2.5 "... IEs unnecessary in this message." */
		if (before_time) {
			before_time = 0;
			LOGP(DRR, LOGL_INFO, " -> channel description before "
				"time, but no starting time, ignoring!\n");
		}
	}
#endif

	/* mobile allocation / frequency list after time */
	if (cda->h) {
		if (TLVP_PRESENT(&tp, GSM48_IE_MA_AFTER)) {
			const uint8_t *lv =
				TLVP_VAL(&tp, GSM48_IE_MA_AFTER) - 1;

			LOGP(DRR, LOGL_INFO, " after: hopping required and "
				"mobile allocation available\n");
			if (*lv + 1 > sizeof(cda->mob_alloc_lv)) {
				LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
				return -ENOMEM;
			}
			memcpy(cda->mob_alloc_lv, lv, *lv + 1);
		} else
		if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_AFTER)) {
			const uint8_t *lv =
				TLVP_VAL(&tp, GSM48_IE_FREQ_L_AFTER) - 1;

			LOGP(DRR, LOGL_INFO, " after: hopping required and "
				"frequency list available\n");
			if (*lv + 1 > sizeof(cda->freq_list_lv)) {
				LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
				return -ENOMEM;
			}
			memcpy(cda->freq_list_lv, lv, *lv + 1);
		} else {
			LOGP(DRR, LOGL_NOTICE, " after: hopping required, but "
				"no mobile allocation / frequency list\n");
		}
	}

	/* mobile allocation / frequency list before time */
	if (cdb->h) {
		if (TLVP_PRESENT(&tp, GSM48_IE_MA_BEFORE)) {
			const uint8_t *lv =
				TLVP_VAL(&tp, GSM48_IE_MA_BEFORE) - 1;

			LOGP(DRR, LOGL_INFO, " before: hopping required and "
				"mobile allocation available\n");
			if (*lv + 1 > sizeof(cdb->mob_alloc_lv)) {
				LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
				return -ENOMEM;
			}
			memcpy(cdb->mob_alloc_lv, lv, *lv + 1);
		} else
		if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_BEFORE)) {
			const uint8_t *lv =
				TLVP_VAL(&tp, GSM48_IE_FREQ_L_BEFORE) - 1;

			LOGP(DRR, LOGL_INFO, " before: hopping required and "
				"frequency list available\n");
			if (*lv + 1 > sizeof(cdb->freq_list_lv)) {
				LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
				return -ENOMEM;
			}
			memcpy(cdb->freq_list_lv, lv, *lv + 1);
		} else
		if (TLVP_PRESENT(&tp, GSM48_IE_F_CH_SEQ_BEFORE)) {
			const uint8_t *v =
				TLVP_VAL(&tp, GSM48_IE_F_CH_SEQ_BEFORE);
			uint8_t len = TLVP_LEN(&tp, GSM48_IE_F_CH_SEQ_BEFORE);

			LOGP(DRR, LOGL_INFO, " before: hopping required and "
				"frequency channel sequence available\n");
			if (len + 1 > sizeof(cdb->freq_seq_lv)) {
				LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
				return -ENOMEM;
			}
			cdb->freq_seq_lv[0] = len;
			memcpy(cdb->freq_seq_lv + 1, v, len);
		} else
		if (cda->mob_alloc_lv[0]) {
			LOGP(DRR, LOGL_INFO, " before: hopping required and "
				"mobile allocation not available, using "
				"mobile allocation after time\n");
			memcpy(cdb->mob_alloc_lv, cda->mob_alloc_lv,
				sizeof(cdb->mob_alloc_lv));
		} else
		if (cda->freq_list_lv[0]) {
			LOGP(DRR, LOGL_INFO, " before: hopping required and "
				"frequency list not available, using "
				"frequency list after time\n");
			memcpy(cdb->freq_list_lv, cda->freq_list_lv,
				sizeof(cdb->freq_list_lv));
		} else {
			LOGP(DRR, LOGL_NOTICE, " before: hopping required, but "
				"no mobile allocation / frequency list\n");
		}
	}

	/* cell channel description */
	if (TLVP_PRESENT(&tp, GSM48_IE_CELL_CH_DESC)) {
		const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_CELL_CH_DESC);
		uint8_t len = TLVP_LEN(&tp, GSM48_IE_CELL_CH_DESC);

		LOGP(DRR, LOGL_INFO, " both: using cell channel description "
			"in case of mobile allocation\n");
		if (len + 1 > sizeof(cdb->cell_desc_lv)) {
			LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
			return -ENOMEM;
		}
		cdb->cell_desc_lv[0] = len;
		memcpy(cdb->cell_desc_lv + 1, v, len);
		cda->cell_desc_lv[0] = len;
		memcpy(cda->cell_desc_lv + 1, v, len);
	} else {
		/* keep old */
		memcpy(cdb->cell_desc_lv, rr->cd_now.cell_desc_lv,
			sizeof(cdb->cell_desc_lv));
		memcpy(cda->cell_desc_lv, rr->cd_now.cell_desc_lv,
			sizeof(cda->cell_desc_lv));
	}

	/* channel mode */
	if (TLVP_PRESENT(&tp, GSM48_IE_CHANMODE_1)) {
		cda->mode = cdb->mode = *TLVP_VAL(&tp, GSM48_IE_CHANMODE_1);
		LOGP(DRR, LOGL_INFO, " both: changing channel mode 0x%02x\n",
			cda->mode);
	} else
		cda->mode = cdb->mode = rr->cd_now.mode;

	/* cipher mode setting */
	if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET)) {
		cda->cipher = cdb->cipher =
			*TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET);
		LOGP(DRR, LOGL_INFO, " both: changing cipher mode 0x%02x\n",
			cda->cipher);
	} else
		cda->cipher = cdb->cipher = rr->cd_now.cipher;

	/* power command and TA (before and after time) */
	gsm48_decode_power_cmd_acc(
		(struct gsm48_power_cmd *) &ac->power_command,
		&cda->ind_tx_power, NULL);
	cdb->ind_tx_power = cda->ind_tx_power;
	cda->ind_ta = cdb->ind_ta = rr->cd_now.ind_ta; /* same cell */
	LOGP(DRR, LOGL_INFO, " both: (tx_power %d TA %d)\n", cda->ind_tx_power,
		cda->ind_ta);

	/* check if we have to change channel at starting time */
	if (cda->start) {
		int32_t now, start, diff;

		/* how much time do we have left? */
		now = ms->meas.last_fn % 42432;
		start = cda->start_tm.fn % 42432;
		diff = start - now;
		if (diff < 0)
			diff += 42432;
		LOGP(DRR, LOGL_INFO, " after: (Tnow %d Tstart %d diff %d)\n",
			now, start, diff);
		start_mili = (uint32_t)diff * 19580 / 42432 * 10;
		if (diff >= 32024 || !start_mili) {
			LOGP(DRR, LOGL_INFO, " -> Start time already "
				"elapsed\n");
			before_time = 0;
			cda->start = 0;
		} else {
			LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the "
				"future\n", start_mili);
		}
	}

	/* check if channels are valid */
	cause = gsm48_rr_check_mode(ms, cda->chan_nr, cda->mode);
	if (cause)
		return gsm48_rr_tx_ass_fail(ms, cause, RSL_MT_DATA_REQ);
	if (before_time) {
		cause = gsm48_rr_render_ma(ms, cdb, ma, &ma_len);
		if (cause)
			return gsm48_rr_tx_ass_fail(ms, cause, RSL_MT_DATA_REQ);
	}
	cause = gsm48_rr_render_ma(ms, cda, ma, &ma_len);
	if (cause)
		return gsm48_rr_tx_ass_fail(ms, cause, RSL_MT_DATA_REQ);

#ifdef TEST_FREQUENCY_MOD
	LOGP(DRR, LOGL_INFO, " TESTING: frequency modify ASS.CMD\n");
	before_time = 1;
	memcpy(cdb, cda, sizeof(*cdb));
	cdb->h = 0;
	cdb->arfcn = 0;
#endif

	/* schedule start of assignment */
	rr->modify_state = GSM48_RR_MOD_ASSIGN;
	if (!before_time && cda->start) {
		start_rr_t_starting(rr, start_mili / 1000, start_mili % 1000);
		/* when timer fires, start time is already elapsed */
		cda->start = 0;

		return 0;
	}

	/* if no starting time, start suspension of current link directly */
	LOGP(DRR, LOGL_INFO, "request suspension of data link\n");
	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, nmsg, 0);

	/* release SAPI 3 link, if exits
	 * FIXME: suspend and resume afterward */
	gsm48_release_sapi3_link(ms);

	return 0;
}

/* 9.1.16 sending HANDOVER COMPLETE */
static int gsm48_rr_tx_hando_cpl(struct osmocom_ms *ms, uint8_t cause)
{
	struct msgb *nmsg;
	struct gsm48_hdr *gh;
	struct gsm48_ho_cpl *hc;

	LOGP(DRR, LOGL_INFO, "HANDOVER COMPLETE (cause #%d)\n", cause);

	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
	hc = (struct gsm48_ho_cpl *) msgb_put(nmsg, sizeof(*hc));

	gh->proto_discr = GSM48_PDISC_RR;
	gh->msg_type = GSM48_MT_RR_HANDO_COMPL;

	/* RR_CAUSE */
	hc->rr_cause = cause;

	// FIXME: mobile observed time

	/* set T200 of SAPI 0 */
	ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_sec =
		T200_DCCH;
	ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_usec = 0;

	return gsm48_send_rsl(ms, RSL_MT_RES_REQ, nmsg, 0);
}

/* 9.1.4 sending HANDOVER FAILURE */
static int gsm48_rr_tx_hando_fail(struct osmocom_ms *ms, uint8_t cause,
	uint8_t rsl_prim)
{
	struct msgb *nmsg;
	struct gsm48_hdr *gh;
	struct gsm48_ho_fail *hf;

	LOGP(DRR, LOGL_INFO, "HANDOVER FAILURE (cause #%d)\n", cause);

	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
	hf = (struct gsm48_ho_fail *) msgb_put(nmsg, sizeof(*hf));

	gh->proto_discr = GSM48_PDISC_RR;
	gh->msg_type = GSM48_MT_RR_ASS_COMPL;

	/* RR_CAUSE */
	hf->rr_cause = cause;

	return gsm48_send_rsl(ms, rsl_prim, nmsg, 0);
}

/* receiving HANDOVER COMMAND message (9.1.15) */
static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_sysinfo *s = cs->si;
	struct gsm48_hdr *gh = msgb_l3(msg);
	struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *)gh->data;
	int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ho);
	struct tlv_parsed tp;
	struct gsm48_rr_cd *cda = &rr->cd_after;
	struct gsm48_rr_cd *cdb = &rr->cd_before;
	uint16_t arfcn;
	uint8_t bcc, ncc;
	uint8_t ch_type, ch_subch, ch_ts;
	uint8_t before_time = 0;
	uint16_t ma[64];
	uint8_t ma_len;
	uint32_t start_mili = 0;
	uint8_t cause;
	struct msgb *nmsg;

	LOGP(DRR, LOGL_INFO, "HANDOVER COMMAND\n");

	memset(cda, 0, sizeof(*cda));
	cda->ind_tx_power = rr->cd_now.ind_tx_power;
	memset(cdb, 0, sizeof(*cdb));
	cdb->ind_tx_power = rr->cd_now.ind_tx_power;

	if (payload_len < 0) {
		LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND message\n");
		return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}
	if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0) < 0) {
		LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
		return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
	}

	/* cell description */
	gsm48_decode_cell_desc(&ho->cell_desc, &arfcn, &ncc, &bcc);

	/* handover reference */
	rr->chan_req_val = ho->ho_ref;
	rr->chan_req_mask = 0x00;

	/* sync ind */
	if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND)) {
		gsm48_decode_sync_ind(rr, (struct gsm48_sync_ind *)
			TLVP_VAL(&tp, GSM48_IE_SYNC_IND));
		LOGP(DRR, LOGL_INFO, " (sync_ind=%d rot=%d nci=%d)\n",
			rr->hando_sync_ind, rr->hando_rot, rr->hando_nci);
	}

	/* decode channel description (before time) */
	if (TLVP_PRESENT(&tp, GSM48_IE_CH_DESC_1_BEFORE)) {
		struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *)
			TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE);
		cdb->chan_nr = ccd->chan_nr;
		if (rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
			LOGP(DRR, LOGL_ERROR,
			     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
			     __func__, cdb->chan_nr);
			return -EINVAL;
		}
		if (ccd->h0.h) {
			cdb->h = 1;
			gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio,
				&cdb->hsn);
			LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x MAIO %u "
				"HSN %u TS %u SS %u TSC %u)\n", ccd->chan_nr,
				cdb->maio, cdb->hsn, ch_ts, ch_subch, cdb->tsc);
		} else {
			cdb->h = 0;
			gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn);
			cdb->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cdb->arfcn);
			LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x "
				"ARFCN %s TS %u SS %u TSC %u)\n", ccd->chan_nr,
				gsm_print_arfcn(cdb->arfcn),
				ch_ts, ch_subch, cdb->tsc);
		}
		before_time = 1;
	}

	/* decode channel description (after time) */
	cda->chan_nr = ho->chan_desc.chan_nr;
	if (rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRR, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, cda->chan_nr);
		return -EINVAL;
	}
	if (ho->chan_desc.h0.h) {
		cda->h = 1;
		gsm48_decode_chan_h1(&ho->chan_desc, &cda->tsc, &cda->maio,
			&cda->hsn);
		LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x MAIO %u HSN %u "
			"TS %u SS %u TSC %u)\n", ho->chan_desc.chan_nr,
			cda->maio, cda->hsn, ch_ts, ch_subch, cda->tsc);
	} else {
		cda->h = 0;
		gsm48_decode_chan_h0(&ho->chan_desc, &cda->tsc, &cda->arfcn);
		cda->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cda->arfcn);
		LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %s TS %u "
			"SS %u TSC %u)\n", ho->chan_desc.chan_nr,
			gsm_print_arfcn(cda->arfcn), ch_ts, ch_subch, cda->tsc);
	}

	/* starting time */
#ifdef TEST_STARTING_TIMER
	cda->start = 1;
	cda->start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432;
	LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n");
#else
	if (TLVP_PRESENT(&tp, GSM48_IE_START_TIME)) {
		gsm48_decode_start_time(cda, (struct gsm48_start_time *)
			TLVP_VAL(&tp, GSM48_IE_START_TIME));
		/* 9.1.2.5 "... before time IE is not present..." */
		if (!before_time) {
			LOGP(DRR, LOGL_INFO, " -> channel description after "
				"time only, but starting time\n");
		} else
			LOGP(DRR, LOGL_INFO, " -> channel description before "
				"and after time\n");
	} else {
		/* 9.1.2.5 "... IEs unnecessary in this message." */
		if (before_time) {
			before_time = 0;
			LOGP(DRR, LOGL_INFO, " -> channel description before "
				"time, but no starting time, ignoring!\n");
		}
	}
#endif

	/* mobile allocation / frequency list after time */
	if (cda->h) {
		if (TLVP_PRESENT(&tp, GSM48_IE_MA_AFTER)) {
			const uint8_t *lv =
				TLVP_VAL(&tp, GSM48_IE_MA_AFTER) - 1;

			LOGP(DRR, LOGL_INFO, " after: hopping required and "
				"mobile allocation available\n");
			if (*lv + 1 > sizeof(cda->mob_alloc_lv)) {
				LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
				return -ENOMEM;
			}
			memcpy(cda->mob_alloc_lv, lv, *lv + 1);
		} else
		if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_AFTER)) {
			const uint8_t *lv =
				TLVP_VAL(&tp, GSM48_IE_FREQ_L_AFTER) - 1;

			LOGP(DRR, LOGL_INFO, " after: hopping required and "
				"frequency list available\n");
			if (*lv + 1 > sizeof(cda->freq_list_lv)) {
				LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
				return -ENOMEM;
			}
			memcpy(cda->freq_list_lv, lv, *lv + 1);
		} else {
			LOGP(DRR, LOGL_NOTICE, " after: hopping required, but "
				"no mobile allocation / frequency list\n");
		}
	}

	/* mobile allocation / frequency list before time */
	if (cdb->h) {
		if (TLVP_PRESENT(&tp, GSM48_IE_MA_BEFORE)) {
			const uint8_t *lv =
				TLVP_VAL(&tp, GSM48_IE_MA_BEFORE) - 1;

			LOGP(DRR, LOGL_INFO, " before: hopping required and "
				"mobile allocation available\n");
			if (*lv + 1 > sizeof(cdb->mob_alloc_lv)) {
				LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
				return -ENOMEM;
			}
			memcpy(cdb->mob_alloc_lv, lv, *lv + 1);
		} else
		if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_BEFORE)) {
			const uint8_t *lv =
				TLVP_VAL(&tp, GSM48_IE_FREQ_L_BEFORE) - 1;

			LOGP(DRR, LOGL_INFO, " before: hopping required and "
				"frequency list available\n");
			if (*lv + 1 > sizeof(cdb->freq_list_lv)) {
				LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
				return -ENOMEM;
			}
			memcpy(cdb->freq_list_lv, lv, *lv + 1);
		} else
		if (TLVP_PRESENT(&tp, GSM48_IE_F_CH_SEQ_BEFORE)) {
			const uint8_t *v =
				TLVP_VAL(&tp, GSM48_IE_F_CH_SEQ_BEFORE);
			uint8_t len = TLVP_LEN(&tp, GSM48_IE_F_CH_SEQ_BEFORE);

			LOGP(DRR, LOGL_INFO, " before: hopping required and "
				"frequency channel sequence available\n");
			if (len + 1 > sizeof(cdb->freq_seq_lv)) {
				LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
				return -ENOMEM;
			}
			cdb->freq_seq_lv[0] = len;
			memcpy(cdb->freq_seq_lv, v + 1, *v);
		} else
		if (cda->mob_alloc_lv[0]) {
			LOGP(DRR, LOGL_INFO, " before: hopping required and "
				"mobile allocation not available, using "
				"mobile allocation after time\n");
			memcpy(cdb->mob_alloc_lv, cda->mob_alloc_lv,
				sizeof(cdb->mob_alloc_lv));
		} else
		if (cda->freq_list_lv[0]) {
			LOGP(DRR, LOGL_INFO, " before: hopping required and "
				"frequency list not available, using "
				"frequency list after time\n");
			memcpy(cdb->freq_list_lv, cda->freq_list_lv,
				sizeof(cdb->freq_list_lv));
		} else {
			LOGP(DRR, LOGL_NOTICE, " before: hopping required, but "
				"no mobile allocation / frequency list\n");
		}
	}

	/* cell channel description */
	if (TLVP_PRESENT(&tp, GSM48_IE_CELL_CH_DESC)) {
		const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_CELL_CH_DESC);
		uint8_t len = TLVP_LEN(&tp, GSM48_IE_CELL_CH_DESC);

		LOGP(DRR, LOGL_INFO, " both: using cell channel description "
			"in case of mobile allocation\n");
		if (len + 1 > sizeof(cdb->cell_desc_lv)) {
			LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n");
			return -ENOMEM;
		}
		cdb->cell_desc_lv[0] = len;
		memcpy(cdb->cell_desc_lv + 1, v, len);
		cda->cell_desc_lv[0] = len;
		memcpy(cda->cell_desc_lv + 1, v, len);
	} else {
		/* keep old */
		memcpy(cdb->cell_desc_lv, rr->cd_now.cell_desc_lv,
			sizeof(cdb->cell_desc_lv));
		memcpy(cda->cell_desc_lv, rr->cd_now.cell_desc_lv,
			sizeof(cda->cell_desc_lv));
	}

	/* channel mode */
	if (TLVP_PRESENT(&tp, GSM48_IE_CHANMODE_1)) {
		cda->mode = cdb->mode = *TLVP_VAL(&tp, GSM48_IE_CHANMODE_1);
		LOGP(DRR, LOGL_INFO, " both: changing channel mode 0x%02x\n",
			cda->mode);
	} else
		cda->mode = cdb->mode = rr->cd_now.mode;

	/* cipher mode setting */
	if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET)) {
		cda->cipher = cdb->cipher =
			*TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET);
		LOGP(DRR, LOGL_INFO, " both: changing cipher mode 0x%02x\n",
			cda->cipher);
	} else
		cda->cipher = cdb->cipher = rr->cd_now.cipher;

	/* power command and TA (before and after time) */
	gsm48_decode_power_cmd_acc(
		(struct gsm48_power_cmd *) &ho->power_command,
		&cda->ind_tx_power, &rr->hando_act);
	cdb->ind_tx_power = cda->ind_tx_power;
	cda->ind_ta = cdb->ind_ta = rr->cd_now.ind_ta; /* same cell */
	LOGP(DRR, LOGL_INFO, " both: (tx_power %d TA %d access=%s)\n",
		cda->ind_tx_power, cda->ind_ta,
		(rr->hando_act) ? "optional" : "mandatory");

	/* check if we have to change channel at starting time */
	if (cda->start) {
		int32_t now, start, diff;

		/* how much time do we have left? */
		now = ms->meas.last_fn % 42432;
		start = cda->start_tm.fn % 42432;
		diff = start - now;
		if (diff < 0)
			diff += 42432;
		LOGP(DRR, LOGL_INFO, " after: (Tnow %d Tstart %d diff %d)\n",
			now, start, diff);
		start_mili = (uint32_t)diff * 19580 / 42432 * 10;
		if (diff >= 32024 || !start_mili) {
			LOGP(DRR, LOGL_INFO, " -> Start time already "
				"elapsed\n");
			before_time = 0;
			cda->start = 0;
		} else {
			LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the "
				"future\n", start_mili);
		}
	}

	/* check if channels are valid */
	if (before_time) {
		cause = gsm48_rr_render_ma(ms, cdb, ma, &ma_len);
		if (cause)
			return gsm48_rr_tx_hando_fail(ms, cause, RSL_MT_DATA_REQ);
	}
	cause = gsm48_rr_render_ma(ms, cda, ma, &ma_len);
	if (cause)
		return gsm48_rr_tx_hando_fail(ms, cause, RSL_MT_DATA_REQ);


#if 0
	if (not supported) {
		LOGP(DRR, LOGL_NOTICE, "New channel is not supported.\n");
		return GSM48_RR_CAUSE_CHAN_MODE_UNACCEPT;
	}
#endif

#ifdef TEST_FREQUENCY_MOD
	LOGP(DRR, LOGL_INFO, " TESTING: frequency modify HANDO.CMD\n");
	before_time = 1;
	memcpy(cdb, cda, sizeof(*cdb));
	cdb->h = 0;
	cdb->arfcn = 0;
#endif

	/* schedule start of handover */
	rr->modify_state = GSM48_RR_MOD_HANDO;
	if (!before_time && cda->start) {
		start_rr_t_starting(rr, start_mili / 1000, start_mili % 1000);
		/* when timer fires, start time is already elapsed */
		cda->start = 0;

		return 0;
	}

	/* if no starting time, start suspension of current link directly */
	LOGP(DRR, LOGL_INFO, "request suspension of data link\n");
	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, nmsg, 0);

	/* release SAPI 3 link, if exits
	 * FIXME: suspend and resume afterward */
	gsm48_release_sapi3_link(ms);

	return 0;
}

/* send HANDOVER/UPLINK ACCESS burst (9.1.14) */
static int gsm48_rr_tx_rand_acc_dedicated(struct osmocom_ms *ms, uint8_t ref, uint16_t offset, int8_t uic)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;

	LOGP(DRR, LOGL_INFO, "send access burst on DCCH (ref 0x%02x)\n", ref);

	return l1ctl_tx_rach_req(ms, rr->cd_now.chan_nr, 0x00, ref, 0, offset, uic);
}

/* send all queued messages down to layer 2 */
static int gsm48_rr_dequeue_down(struct osmocom_ms *ms)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct msgb *msg;

	while((msg = msgb_dequeue(&rr->downqueue))) {
		struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data;
		uint8_t sapi = rrh->sapi;

		LOGP(DRR, LOGL_INFO, "Sending queued message.\n");
		if (sapi && rr->sapi3_state != GSM48_RR_SAPI3ST_ESTAB) {
			LOGP(DRR, LOGL_INFO, "Dropping SAPI 3 msg, no link!\n");
			msgb_free(msg);
			return 0;
		}
		gsm48_send_rsl(ms, RSL_MT_DATA_REQ, msg, 0);
	}

	return 0;
}

/* Channel is resumed in dedicated mode or uplink is estabished. */
static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct msgb *nmsg;

	if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) {
		LOGP(DRR, LOGL_INFO, "data link established on uplink\n");

		/* Confirm uplink access. */
		nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_CNF);
		if (nmsg)
			gsm48_rr_upmsg(ms, nmsg);
	} else {
		LOGP(DRR, LOGL_INFO, "data link is resumed\n");

		/* transmit queued frames during ho / ass transition */
		gsm48_rr_dequeue_down(ms);

		rr->modify_state = GSM48_RR_MOD_NONE;
	}

	return 0;
}

/* suspend confirm in dedicated mode */
static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;

	if (rr->modify_state) {
		uint16_t ma[64];
		uint8_t ma_len;

		/* deactivating dedicated mode */
		LOGP(DRR, LOGL_INFO, "suspension coplete, leaving dedicated "
			"mode\n");
		l1ctl_tx_dm_rel_req(ms);
		ms->meas.rl_fail = 0;
		rr->dm_est = 0;
		l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);

		/* store current channel descriptions */
		memcpy(&rr->cd_last, &rr->cd_now, sizeof(rr->cd_last));

		/* copy channel description "after time" */
		memcpy(&rr->cd_now, &rr->cd_after, sizeof(rr->cd_now));

		if (rr->cd_after.start) {
			/* render channel "before time" */
			gsm48_rr_render_ma(ms, &rr->cd_before, ma, &ma_len);

			/* activate channel */
			gsm48_rr_activate_channel(ms, &rr->cd_before, ma,
				ma_len);

			/* render channel "after time" */
			gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);

			/* schedule change of channel */
			gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len,
				rr->cd_now.start_tm.fn);
		} else {
			/* render channel "after time" */
			gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);

			/* activate channel */
			gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
		}

		/* send DL-RESUME REQUEST */
		LOGP(DRR, LOGL_INFO, "request resume of data link\n");
		switch (rr->modify_state) {
		case GSM48_RR_MOD_ASSIGN:
			gsm48_rr_tx_ass_cpl(ms, GSM48_RR_CAUSE_NORMAL);
			break;
		case GSM48_RR_MOD_HANDO:
			gsm48_rr_tx_hando_cpl(ms, GSM48_RR_CAUSE_NORMAL);
			break;
		}

#ifdef TODO
		/* trigger RACH */
		if (rr->modify_state == GSM48_RR_MOD_HANDO) {
			gsm48_rr_tx_hando_access(ms);
			rr->hando_acc_left = 3;
		}
#endif
	}
	return 0;
}

/*
 * radio ressource requests
 */

/* establish request for dedicated mode */
static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_sysinfo *s = &cs->sel_si;
	struct gsm_subscriber *subscr = &ms->subscr;
	struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data;
	struct gsm48_hdr *gh = msgb_l3(msg);
	uint8_t cause;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;
	uint16_t acc_class;

	/* Reject during group call. */
	if (rr->vgcs.group_state != GSM48_RR_GST_OFF) {
		LOGP(DRR, LOGL_INFO, "We have a group call, rejecting!\n");
		cause = RR_REL_CAUSE_TRY_LATER;
		goto reject;
	}

	/* 3.3.1.1.3.2 */
	if (osmo_timer_pending(&rr->t3122)) {
		if (rrh->cause != RR_EST_CAUSE_EMERGENCY) {
			LOGP(DRR, LOGL_INFO, "T3122 running, rejecting!\n");
			cause = RR_REL_CAUSE_T3122;
			reject:
			LOGP(DSUM, LOGL_INFO, "Establishing radio link not "
				"possible\n");
			nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
			if (!nmsg)
				return -ENOMEM;
			nrrh = (struct gsm48_rr_hdr *)nmsg->data;
			nrrh->cause = cause;
			return gsm48_rr_upmsg(ms, nmsg);
		}
		LOGP(DRR, LOGL_INFO, "T3122 running, but emergency call\n");
		stop_rr_t3122(rr);
	}

	/* if state is not idle */
	if (rr->state != GSM48_RR_ST_IDLE) {
		LOGP(DRR, LOGL_INFO, "We are not IDLE yet, rejecting!\n");
		cause = RR_REL_CAUSE_TRY_LATER;
	 	goto reject;
	}

	/* cell selected */
	if (!cs->selected) {
		LOGP(DRR, LOGL_INFO, "No cell selected, rejecting!\n");
		cause = RR_REL_CAUSE_TRY_LATER;
	 	goto reject;
	}

	/* check if camping */
	if (cs->state != GSM322_C3_CAMPED_NORMALLY
	 && rrh->cause != RR_EST_CAUSE_EMERGENCY) {
		LOGP(DRR, LOGL_INFO, "Not camping normally, rejecting! "
			"(cs->state = %d)\n", cs->state);
		cause = RR_REL_CAUSE_EMERGENCY_ONLY;
	 	goto reject;
	}
	if (cs->state != GSM322_C3_CAMPED_NORMALLY
	 && cs->state != GSM322_C7_CAMPED_ANY_CELL) {
		LOGP(DRR, LOGL_INFO, "Not camping, rejecting! "
			"(cs->state = %d)\n", cs->state);
		cause = RR_REL_CAUSE_TRY_LATER;
	 	goto reject;
	}

	/* check for relevant information */
	if (!s->si3) {
		LOGP(DRR, LOGL_INFO, "Not enough SI, rejecting!\n");
		cause = RR_REL_CAUSE_TRY_LATER;
	 	goto reject;
	}

	/* 3.3.1.1.1 */
	if (!subscr->acc_barr && s->cell_barr) {
		LOGP(DRR, LOGL_INFO, "Cell barred, rejecting!\n");
	 	cause = RR_REL_CAUSE_NOT_AUTHORIZED;
		goto reject;
	}
	if (rrh->cause == RR_EST_CAUSE_EMERGENCY)
		acc_class = subscr->acc_class | 0x0400;
	else
		acc_class = subscr->acc_class & 0xfbff;
	if (!subscr->acc_barr && !(acc_class & (s->class_barr ^ 0xffff))) {
		LOGP(DRR, LOGL_INFO, "Cell barred for our access class (access "
			"%04x barred %04x)!\n", acc_class, s->class_barr);
	 	cause = RR_REL_CAUSE_NOT_AUTHORIZED;
		goto reject;
	}

	/* requested by RR */
	rr->rr_est_req = 1;

	/* clone and store REQUEST message */
	if (!gh) {
		LOGP(DRR, LOGL_ERROR, "Error, missing l3 message\n");
		return -EINVAL;
	}
	rr->rr_est_msg = gsm48_l3_msgb_alloc();
	if (!rr->rr_est_msg)
		return -ENOMEM;
	memcpy(msgb_put(rr->rr_est_msg, msgb_l3len(msg)),
		msgb_l3(msg), msgb_l3len(msg));

	if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) {
		/* Release group receive mode. */
		l1ctl_tx_dm_rel_req(rr->ms);
		rr->ms->meas.rl_fail = 0;
		l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_SCHED);
	}

	/* request channel */
	return gsm48_rr_chan_req(ms, rrh->cause, 0, 0);
}

/* 3.3.3.2 Request for group receive mode. */
static int gsm48_rr_group_req(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm48_sysinfo *s = &cs->sel_si;
	struct gsm48_chan_desc *ch_desc = (struct gsm48_chan_desc *)(msg->data + sizeof(struct gsm48_rr_hdr));
	uint8_t cause;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;
	struct gsm48_rr_cd cd;
	uint8_t ch_type, ch_subch, ch_ts;
	uint16_t ma[64];
	uint8_t ma_len;
	int rc;

	/* if state is not idle */
	if (rr->state != GSM48_RR_ST_IDLE || rr->vgcs.group_state != GSM48_RR_GST_OFF) {
		LOGP(DRR, LOGL_INFO, "We are not IDLE yet, rejecting!\n");
		cause = RR_REL_CAUSE_TRY_LATER;
reject:
		LOGP(DSUM, LOGL_INFO, "Joining group call channel not possible\n");
		nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND);
		if (!nmsg)
			return -ENOMEM;
		nrrh = (struct gsm48_rr_hdr *)nmsg->data;
		nrrh->cause = cause;
		return gsm48_rr_upmsg(ms, nmsg);
	}

	/* cell selected */
	if (!cs->selected) {
		LOGP(DRR, LOGL_INFO, "No cell selected, rejecting!\n");
		cause = RR_REL_CAUSE_TRY_LATER;
		goto reject;
	}

	/* check if camping */
	if (cs->state != GSM322_C3_CAMPED_NORMALLY
	 && cs->state != GSM322_C7_CAMPED_ANY_CELL) {
		LOGP(DRR, LOGL_INFO, "Not camping, rejecting! "
			"(cs->state = %d)\n", cs->state);
		cause = RR_REL_CAUSE_TRY_LATER;
		goto reject;
	}

	/* get channel description */
	memset(&cd, 0, sizeof(cd));
	cd.chan_nr = ch_desc->chan_nr;
	if (rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRR, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, cd.chan_nr);
		cause = GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
		goto reject;
	}
	if (ch_desc->h0.h) {
		LOGP(DRR, LOGL_ERROR, "HOPPING NOT SUPPORTED, PLEASE FIX!\n");
		cd.h = 1;
		gsm48_decode_chan_h1(ch_desc, &cd.tsc, &cd.maio,
			&cd.hsn);
		LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n",
		     ch_desc->chan_nr, cd.maio, cd.hsn, ch_ts, ch_subch, cd.tsc);
	} else {
		cd.h = 0;
		gsm48_decode_chan_h0(ch_desc, &cd.tsc, &cd.arfcn);
		cd.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd.arfcn);
		LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n",
		     ch_desc->chan_nr, gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc);
	}
	if (gsm48_rr_render_ma(ms, &rr->vgcs.cd_group, ma, &ma_len)) {
		cause = GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
		goto reject;
	}

	/* Turn off transmitter. */
	cd.tch_flags |= L1CTL_TCH_FLAG_RXONLY;

	/* Set mode to Speech V1. FIXME: Add AMR support. */
	cd.mode = GSM48_CMODE_SPEECH_V1;

	/* Set current channel and also store as 'vgcs.cd_group', used when leaving dedicated mode. */
	memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now));
	memcpy(&rr->vgcs.cd_group, &cd, sizeof(rr->vgcs.cd_group));

	/* tell cell selection process to leave idle mode
	 * NOTE: this must be sent unbuffered, because the state may not
	 * change until idle mode is left
	 */
	nmsg = gsm322_msgb_alloc(GSM322_EVENT_LEAVE_IDLE);
	if (!nmsg)
		return -ENOMEM;
	rc = gsm322_c_event(ms, nmsg);
	msgb_free(nmsg);
	if (rc) {
		LOGP(DRR, LOGL_INFO, "Failed to leave IDLE mode.\n");
		cause = GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
		goto reject;
	}

	/* Initial uplink state. */
	rr->vgcs.uplink_free = false;

	/* Set group state to receive mode. */
	rr->vgcs.group_state = GSM48_RR_GST_RECEIVE;

	/* Wait until synced to the CCCH ... */
	return 0;
}
/* ... Continue after synced to the CCCH. */
static int gsm48_rr_group_req_continue(struct osmocom_ms *ms)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	uint16_t ma[64];
	uint8_t ma_len;
	struct msgb *nmsg;

	/* get hopping sequence, if required */
	gsm48_rr_render_ma(ms, &rr->vgcs.cd_group, ma, &ma_len);
	/* activate channel */
	gsm48_rr_activate_channel(ms, &rr->vgcs.cd_group, ma, ma_len);

	/* Confirm group call channel. */
	nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_CNF);
	if (!nmsg)
		return -ENOMEM;
	return gsm48_rr_upmsg(ms, nmsg);
}

/* After "loss of signal"/release in group transmit or receive mode, return IDLE and release towards MM. */
static int gsm48_rr_group_rel(struct osmocom_ms *ms, int cause)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;
	uint8_t *mode;

	/* Stop group receive and transmit timers. */
	stop_rr_t_ul_free(rr);
	stop_rr_t3128(rr);
	stop_rr_t3130(rr);

	if (rr->state == GSM48_RR_ST_DEDICATED || rr->state == GSM48_RR_ST_REL_PEND) {
		struct msgb *nmsg;

		LOGP(DRR, LOGL_INFO, "Channel lost, send (local) release to layer 2.\n");

		/* Go into group receive mode, so that the channel gets released on LAPD release confirm. */
		rr->vgcs.group_state = GSM48_RR_GST_RECEIVE;
		if (rr->state != GSM48_RR_ST_REL_PEND)
			new_rr_state(rr, GSM48_RR_ST_REL_PEND);

		/* release message */
		nmsg = gsm48_l3_msgb_alloc();
		if (!nmsg)
			return -ENOMEM;
		mode = msgb_put(nmsg, 2);
		mode[0] = RSL_IE_RELEASE_MODE;
		mode[1] = RSL_REL_LOCAL_END;
		gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0);

		/* release SAPI 3 link, if exits */
		gsm48_release_sapi3_link(ms);

		/* Wait for LAPD to confirm. Then return idle. */
		return 0;
	}

	/* Go back to IDLE mode. */
	rr->vgcs.group_state = GSM48_RR_GST_OFF;
	new_rr_state(rr, GSM48_RR_ST_IDLE);

	/* Indicate release to MM. */
	nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND);
	if (!nmsg)
		return -ENOMEM;
	nrrh = (struct gsm48_rr_hdr *)nmsg->data;
	nrrh->cause = cause;
	return gsm48_rr_upmsg(ms, nmsg);
}

/* Release group channel, return to IDLE. */
static int gsm48_rr_group_rel_req(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;

	/* Only in group receive/transmit mode. */
	if (rr->vgcs.group_state == GSM48_RR_GST_OFF)
		return 0;

	if (rr->state == GSM48_RR_ST_CONN_PEND || rr->state == GSM48_RR_ST_DEDICATED) {
		LOGP(DRR, LOGL_INFO, "Connot release group channel yet, perform uplink release first.\n");
		gsm48_rr_uplink_rel_req(ms, msg);
		if (rr->state == GSM48_RR_ST_REL_PEND) {
			/* Leave the group call state, so that we change to IDLE when we released the uplink. */
			rr->vgcs.group_state = GSM48_RR_GST_OFF;
			return 0;
		}
	}

	/* Stop group receive and transmit timers. */
	stop_rr_t_ul_free(rr);
	stop_rr_t3128(rr);
	stop_rr_t3130(rr);

	/* Unset group state. Wait, if release is pending. */
	rr->vgcs.group_state = GSM48_RR_GST_OFF;
	if (rr->state != GSM48_RR_ST_IDLE) {
		LOGP(DRR, LOGL_INFO, "Cannot release group channel yet, wait to return to IDLE state.\n");
		return 0;
	}

	LOGP(DRR, LOGL_INFO, "Release Group channel.\n");
	new_rr_state(rr, GSM48_RR_ST_IDLE);
	return 0;
}

/* Request uplink in group receive mode. */
static int gsm48_rr_uplink_req(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;

	/* Only in group receive mode */
	if (rr->state != GSM48_RR_ST_IDLE || rr->vgcs.group_state != GSM48_RR_GST_RECEIVE) {
		LOGP(DRR, LOGL_INFO, "We are not in group receive mode yet, rejecting!\n");
		nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_REL_IND);
		if (!nmsg)
			return -ENOMEM;
		nrrh = (struct gsm48_rr_hdr *)nmsg->data;
		nrrh->cause = RR_REL_CAUSE_TRY_LATER;
		return gsm48_rr_upmsg(ms, nmsg);
	}

	LOGP(DRR, LOGL_INFO, "Changing from group receive mode to group transmit mode.\n");

	/* Enter uplink access procedure. */
	new_rr_state(rr, GSM48_RR_ST_CONN_PEND);

	/* Set group state to transmit mode. */
	rr->vgcs.group_state = GSM48_RR_GST_TRANSMIT;

	/* Uplink investigation procedure (3.3.1.2.1.1) */
	if (!rr->vgcs.uplink_free) {
		start_rr_t3128(rr, GSM_T3128_MS);
		return 0;
	}

	rr->vgcs.uplink_tries = 3;
	return gsm48_rr_uplink_access(ms, NULL);
}

/* Leave uplink, also called when leaving group channel. */
static int gsm48_rr_uplink_rel_req(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm_settings *set = &ms->settings;
	struct msgb *nmsg;
	uint8_t *mode;

	/* Only in group transmit mode */
	if (rr->vgcs.group_state != GSM48_RR_GST_TRANSMIT)
		return -EINVAL;

	/* Stop group transmit mode timers. */
	stop_rr_t3128(rr);
	stop_rr_t3130(rr);

	/* Continue if in dedicated mode, so we release and wait for uplink to become free. */
	if (rr->state != GSM48_RR_ST_DEDICATED)
		return 0;

	LOGP(DRR, LOGL_INFO, "Returning from group transmit to group receive mode.\n");

	/* Leave uplink, wait for uplink being free, channel release or channel/link failure. */
	gsm48_rr_tx_uplink_release(ms, GSM48_RR_CAUSE_LEAVE_GROUP_CA);

	/* Go into release pending mode. */
	new_rr_state(rr, GSM48_RR_ST_REL_PEND);

	/* Special setting where we release locally. This means we wait for free uplink an then release. */
	if (set->uplink_release_local) {
		LOGP(DRR, LOGL_INFO, "Release L2 locally as specified via VTY. Wait for UPLINK FREE.\n");
		return 0;
	}

	/* Release dedicated mode. */
	/* disconnect the main signalling link */
	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	mode = msgb_put(nmsg, 2);
	mode[0] = RSL_IE_RELEASE_MODE;
	mode[1] = RSL_REL_NORMAL;
	gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0);

	return 0;
}

/* 3.4.2 transfer data in dedicated mode */
static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data;
	uint8_t sapi = rrh->sapi;

	if (rr->state != GSM48_RR_ST_DEDICATED) {
		msgb_free(msg);
		return -EINVAL;
	}

	/* pull RR header */
	msgb_pull(msg, sizeof(struct gsm48_rr_hdr));

	/* set sequence number and increment */
	gsm48_apply_v_sd(rr, msg);

	/* queue message, during handover or assignment procedure */
	if (rr->modify_state == GSM48_RR_MOD_ASSIGN
	 || rr->modify_state == GSM48_RR_MOD_HANDO) {
		LOGP(DRR, LOGL_INFO, "Queueing message during suspend.\n");
		msgb_enqueue(&rr->downqueue, msg);
		return 0;
	}

	if (sapi && rr->sapi3_state != GSM48_RR_SAPI3ST_ESTAB) {
		LOGP(DRR, LOGL_INFO, "Dropping SAPI 3 msg, no link!\n");
		msgb_free(msg);
		return 0;
	}

	/* forward message */
	return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, msg,
					sapi ? rr->sapi3_link_id : 0);
}

/*
 * data indications from data link
 */

/* 3.4.2 data from layer 2 to RR and upper layer*/
static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
{
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	uint8_t sapi = rllh->link_id & 7;
	struct gsm48_hdr *gh = msgb_l3(msg);
	struct gsm48_rr_hdr *rrh;
	uint8_t pdisc = gh->proto_discr & 0x0f;
	int rc = -EINVAL;

	if (pdisc == GSM48_PDISC_RR) {
		uint8_t skip_ind = (gh->proto_discr & 0xf0) >> 4;

		/* ignore if skip indicator is not B'0000' */
		if (skip_ind) {
			msgb_free(msg);
			return 0;
		}

		switch(gh->msg_type) {
		case GSM48_MT_RR_ADD_ASS:
			rc = gsm48_rr_rx_add_ass(ms, msg);
			break;
		case GSM48_MT_RR_ASS_CMD:
			rc = gsm48_rr_rx_ass_cmd(ms, msg);
			break;
		case GSM48_MT_RR_CIPH_M_CMD:
			rc = gsm48_rr_rx_cip_mode_cmd(ms, msg);
			break;
		case GSM48_MT_RR_CLSM_ENQ:
			rc = gsm48_rr_rx_cm_enq(ms, msg);
			break;
		case GSM48_MT_RR_CHAN_MODE_MODIF:
			rc = gsm48_rr_rx_chan_modify(ms, msg);
			break;
		case GSM48_MT_RR_HANDO_CMD:
			rc = gsm48_rr_rx_hando_cmd(ms, msg);
			break;
		case GSM48_MT_RR_FREQ_REDEF:
			rc = gsm48_rr_rx_frq_redef(ms, msg);
			break;
		case GSM48_MT_RR_CHAN_REL:
			rc = gsm48_rr_rx_chan_rel(ms, msg);
			break;
		case GSM48_MT_RR_UPLINK_RELEASE:
			rc = gsm48_rr_rx_uplink_release(ms, msg);
			break;
		case GSM48_MT_RR_APP_INFO:
			LOGP(DRR, LOGL_NOTICE, "APP INFO not supported!\n");
			break;
		default:
			LOGP(DRR, LOGL_NOTICE, "Message type 0x%02x unknown.\n",
				gh->msg_type);

			/* status message */
			gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N);
		}

		msgb_free(msg);
		return rc;
	} else if (pdisc == GSM48_PDISC_TEST) {
		rc = gsm414_rcv_test(ms, msg);
		msgb_free(msg);
		return rc;
	}

	/* pull off RSL header up to L3 message */
	msgb_pull(msg, (long)msgb_l3(msg) - (long)msg->data);

	/* push RR header */
	msgb_push(msg, sizeof(struct gsm48_rr_hdr));
	rrh = (struct gsm48_rr_hdr *)msg->data;
	rrh->msg_type = GSM48_RR_DATA_IND;
	rrh->sapi = sapi;

	return gsm48_rr_upmsg(ms, msg);
}

/* receive BCCH at RR layer */
static int gsm48_rr_rx_bcch(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_system_information_type_header *sih = msgb_l3(msg);

	switch (sih->system_information) {
	case GSM48_MT_RR_SYSINFO_1:
		return gsm48_rr_rx_sysinfo1(ms, msg);
	case GSM48_MT_RR_SYSINFO_2:
		return gsm48_rr_rx_sysinfo2(ms, msg);
	case GSM48_MT_RR_SYSINFO_2bis:
		return gsm48_rr_rx_sysinfo2bis(ms, msg);
	case GSM48_MT_RR_SYSINFO_2ter:
		return gsm48_rr_rx_sysinfo2ter(ms, msg);
	case GSM48_MT_RR_SYSINFO_3:
		return gsm48_rr_rx_sysinfo3(ms, msg);
	case GSM48_MT_RR_SYSINFO_4:
		return gsm48_rr_rx_sysinfo4(ms, msg);
	case GSM48_MT_RR_SYSINFO_13:
		return gsm48_rr_rx_sysinfo13(ms, msg);
	default:
#if 0
		LOGP(DRR, LOGL_NOTICE, "BCCH message type 0x%02x not sup.\n",
			sih->system_information);
#endif
		return -EINVAL;
	}
}

/* receive CCCH at RR layer */
static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_system_information_type_header *sih = msgb_l3(msg);

	if (msgb_l3len(msg) < sizeof(*sih)) {
		LOGP(DRR, LOGL_NOTICE, "Short read of CCCH message.\n");
		return -EINVAL;
	}

	switch (sih->system_information) {
	case GSM48_MT_RR_PAG_REQ_1:
		return gsm48_rr_rx_pag_req_1(ms, msg);
	case GSM48_MT_RR_PAG_REQ_2:
		return gsm48_rr_rx_pag_req_2(ms, msg);
	case GSM48_MT_RR_PAG_REQ_3:
		return gsm48_rr_rx_pag_req_3(ms, msg);

	case GSM48_MT_RR_IMM_ASS:
		return gsm48_rr_rx_imm_ass(ms, msg);
	case GSM48_MT_RR_IMM_ASS_EXT:
		return gsm48_rr_rx_imm_ass_ext(ms, msg);
	case GSM48_MT_RR_IMM_ASS_REJ:
		return gsm48_rr_rx_imm_ass_rej(ms, msg);

	case GSM48_MT_RR_NOTIF_NCH:
		return gsm48_rr_rx_notif_nch(ms, msg);

	default:
#if 0
		LOGP(DRR, LOGL_NOTICE, "CCCH message type 0x%02x unknown.\n",
			sih->system_information);
#endif
		return -EINVAL;
	}
}

#define N201_Bter_SACCH		21
#define N201_Bter_SDCCH_FACCH	23
#define N201_B4			19

/* receive ACCH at RR layer */
static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg)
{
	const struct gsm48_system_information_type_header *sih;
	const struct gsm48_hdr_sh *sgh;
	const struct gsm48_hdr *gh;

	/* Bter frame (SACCH or SDCCH/FACCH) */
	if (msgb_l3len(msg) == N201_Bter_SACCH || msgb_l3len(msg) == N201_Bter_SDCCH_FACCH) {
		sgh = msgb_l3(msg);
		if (sgh->rr_short_pd != GSM48_PDISC_SH_RR) {
			LOGP(DRR, LOGL_NOTICE, "Short header message is not an RR message.\n");
			return -EINVAL;
		}
		switch (sgh->msg_type) {
		case GSM48_MT_RR_SH_UL_FREE:
			return gsm48_rr_rx_uplink_free(ms, msg);
		case GSM48_MT_RR_SH_FACCH:
			return gsm48_rr_rx_notif_facch(ms, msg);
		case GSM48_MT_RR_SH_SI10:
			return gsm48_rr_rx_sysinfo_10(ms, msg);
		default:
			LOGP(DRR, LOGL_NOTICE, "Short header message type 0x%02x unsupported.\n", sgh->msg_type);
			return -EINVAL;
		}
	}

	/* B4 frame (SACCH) */
	if ((msg->cb[0] & 0x40) && msgb_l3len(msg) == N201_B4) {
		sih = msgb_l3(msg);
		switch (sih->system_information) {
		case GSM48_MT_RR_SYSINFO_5:
			return gsm48_rr_rx_sysinfo5(ms, msg);
		case GSM48_MT_RR_SYSINFO_5bis:
			return gsm48_rr_rx_sysinfo5bis(ms, msg);
		case GSM48_MT_RR_SYSINFO_5ter:
			return gsm48_rr_rx_sysinfo5ter(ms, msg);
		case GSM48_MT_RR_SYSINFO_6:
			return gsm48_rr_rx_sysinfo6(ms, msg);
		default:
			LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", sih->system_information);
			return -EINVAL;
		}
	}

	/* B frame (UI frame on VGCS) */
	if (msgb_l3len(msg) < sizeof(*gh)) {
		LOGP(DRR, LOGL_NOTICE, "ACCH message too short.\n");
		return -EINVAL;
	}
	gh = msgb_l3(msg);
	switch (gh->msg_type) {
	case GSM48_MT_RR_VGCS_UPL_GRANT:
		return gsm48_rr_rx_vgcs_uplink_grant(ms, msg);
	case GSM48_MT_RR_UPLINK_BUSY:
		return gsm48_rr_rx_uplink_busy(ms, msg);
	case GSM48_MT_RR_CHAN_REL:
		return gsm48_rr_rx_chan_rel_ui(ms, msg);
	default:
		LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", gh->msg_type);
		return -EINVAL;
	}
}

/* unit data from layer 2 to RR layer */
static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm322_cellsel *cs = &ms->cellsel;
	struct gsm_settings *set = &ms->settings;
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	struct tlv_parsed tv;
	int ind_ta = -1, ind_tx_power = -1;
	uint8_t ch_type, ch_subch, ch_ts;

	DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n",
		rllh->chan_nr, rllh->link_id);

	if (rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) {
		LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
		return -EINVAL;
	}

	/* Update TX power and timing advance, if included in message. */
	if (TLVP_PRES_LEN(&tv, RSL_IE_TIMING_ADVANCE, 1)) {
		ind_ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE);
		LOGP(DRR, LOGL_INFO, "DL SACCH indicates ta %d (actual ta %d)\n", ind_ta, ind_ta - set->alter_delay);
	}
	if (TLVP_PRES_LEN(&tv, RSL_IE_MS_POWER, 1)) {
		ind_tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER);
		LOGP(DRR, LOGL_INFO, "DL SACCH indicates tx_power %d\n", ind_tx_power);
	}
	if ((ind_ta >= 0 && ind_ta != rr->cd_now.ind_ta) ||
	    (ind_tx_power >= 0 && ind_tx_power != rr->cd_now.ind_tx_power)) {
		LOGP(DRR, LOGL_INFO, "Applying new ta and tx_power\n");
		l1ctl_tx_param_req(ms, ind_ta - set->alter_delay,
			(set->alter_tx_power) ? set->alter_tx_power_value : ind_tx_power);
		rr->cd_now.ind_ta = ind_ta;
		rr->cd_now.ind_tx_power = ind_tx_power;
	}

	if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
		DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n");
		return -EIO;
	}
	msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);

	if (cs->ccch_state != GSM322_CCCH_ST_SYNC
	 && cs->ccch_state != GSM322_CCCH_ST_DATA)
	 	return -EINVAL;

	/* temporary moved here until confirm is fixed */
	if (cs->ccch_state != GSM322_CCCH_ST_DATA) {
		LOGP(DCS, LOGL_INFO, "Channel provides data.\n");
		cs->ccch_state = GSM322_CCCH_ST_DATA;

		/* in group receive mode */
		if (ms->rrlayer.vgcs.group_state == GSM48_RR_GST_RECEIVE)
			return gsm48_rr_group_req_continue(ms);

		/* in dedicated mode */
		if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
			return gsm48_rr_tx_rand_acc(ms, NULL);

		/* set timer for reading BCCH */
		if (cs->state == GSM322_C2_STORED_CELL_SEL
		 || cs->state == GSM322_C1_NORMAL_CELL_SEL
		 || cs->state == GSM322_C6_ANY_CELL_SEL
		 || cs->state == GSM322_C4_NORMAL_CELL_RESEL
		 || cs->state == GSM322_C8_ANY_CELL_RESEL
		 || cs->state == GSM322_C5_CHOOSE_CELL
		 || cs->state == GSM322_C9_CHOOSE_ANY_CELL
		 || cs->state == GSM322_PLMN_SEARCH
		 || cs->state == GSM322_HPLMN_SEARCH)
			start_cs_timer(cs, ms->support.scan_to, 0);
				// TODO: timer depends on BCCH config
	}

	if (rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRSL, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, rllh->chan_nr);
		return -EINVAL;
	}

	switch (ch_type) {
	case RSL_CHAN_PCH_AGCH:
		return gsm48_rr_rx_pch_agch(ms, msg);
	case RSL_CHAN_BCCH:
		return gsm48_rr_rx_bcch(ms, msg);
	case RSL_CHAN_Bm_ACCHs:
	case RSL_CHAN_Lm_ACCHs:
	case RSL_CHAN_SDCCH4_ACCH:
	case RSL_CHAN_SDCCH8_ACCH:
		msg->cb[0] = rllh->link_id;
		return gsm48_rr_rx_acch(ms, msg);
	default:
		LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n",
			rllh->chan_nr);
		return -EINVAL;
	}
}

/* 3.4.13.3 RR abort in dedicated mode (also in conn. pending mode) */
static int gsm48_rr_abort_req(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	uint8_t *mode;

	/* stop pending RACH timer */
	stop_rr_t3126(rr);

	/* release "normally" if we are in dedicated mode */
	if (rr->state == GSM48_RR_ST_DEDICATED) {
		struct msgb *nmsg;

		LOGP(DRR, LOGL_INFO, "Abort in dedicated state, send release "
			"to layer 2.\n");

		new_rr_state(rr, GSM48_RR_ST_REL_PEND);

		/* release message */
		nmsg = gsm48_l3_msgb_alloc();
		if (!nmsg)
			return -ENOMEM;
		mode = msgb_put(nmsg, 2);
		mode[0] = RSL_IE_RELEASE_MODE;
		mode[1] = 0; /* normal release */
		gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0);

		/* release SAPI 3 link, if exits */
		gsm48_release_sapi3_link(ms);
		return 0;
	}

	LOGP(DRR, LOGL_INFO, "Abort in connection pending state, return to "
		"idle state.\n");
	/* return idle */
	new_rr_state(rr, GSM48_RR_ST_IDLE);

	return 0;
}

/* release confirm */
static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;
	uint8_t cause = RR_REL_CAUSE_NORMAL;
	uint16_t ma[64];
	uint8_t ma_len;

	/* Handle on group channel. */
	if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) {
		LOGP(DRR, LOGL_INFO, "Returning to group receive mode, due to link release confirm.\n");
		return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_NORMAL);
	}

	/* switch back to old channel, if modify/ho failed */
	switch (rr->modify_state) {
	case GSM48_RR_MOD_ASSIGN:
	case GSM48_RR_MOD_HANDO:
		/* deactivate channel */
		l1ctl_tx_dm_rel_req(ms);
		ms->meas.rl_fail = 0;
		rr->dm_est = 0;
		l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED);

		/* get old channel description */
		memcpy(&rr->cd_now, &rr->cd_last, sizeof(rr->cd_now));

		/* render and change radio to old channel */
		gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
		gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);

		/* re-establish old link */
		nmsg = gsm48_l3_msgb_alloc();
		if (!nmsg)
			return -ENOMEM;
		if (rr->modify_state == GSM48_RR_MOD_ASSIGN) {
			rr->modify_state = GSM48_RR_MOD_ASSIGN_RESUME;
			return gsm48_rr_tx_ass_fail(ms,
				GSM48_RR_CAUSE_ABNORMAL_UNSPEC,
				RSL_MT_RECON_REQ);
		} else {
			rr->modify_state = GSM48_RR_MOD_HANDO_RESUME;
			return gsm48_rr_tx_hando_fail(ms,
				GSM48_RR_CAUSE_ABNORMAL_UNSPEC,
				RSL_MT_RECON_REQ);
		}
		/* returns above */
	case GSM48_RR_MOD_ASSIGN_RESUME:
	case GSM48_RR_MOD_HANDO_RESUME:
		rr->modify_state = GSM48_RR_MOD_NONE;
		cause = RR_REL_CAUSE_LINK_FAILURE;
		break;
	}

	LOGP(DSUM, LOGL_INFO, "Requested channel aborted\n");

	/* stop T3211 if running */
	stop_rr_t3110(rr);

	/* send release indication */
	if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) {
		nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND);
		rr->vgcs.group_state = GSM48_RR_GST_OFF;
	} else
		nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
	if (!nmsg)
		return -ENOMEM;
	nrrh = (struct gsm48_rr_hdr *)nmsg->data;
	nrrh->cause = cause;
	gsm48_rr_upmsg(ms, nmsg);

	/* return idle */
	new_rr_state(rr, GSM48_RR_ST_IDLE);
	return 0;
}

/* MDL-ERROR */
static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;
	uint8_t *mode;
	uint8_t cause = rllh->data[2];
	uint8_t link_id = rllh->link_id;

	switch (cause) {
	case RLL_CAUSE_SEQ_ERR:
	case RLL_CAUSE_UNSOL_DM_RESP_MF:
		break;
	default:
		LOGP(DRR, LOGL_NOTICE, "MDL-Error (cause %d) ignoring\n",
			cause);
		return 0;
	}

	LOGP(DRR, LOGL_NOTICE, "MDL-Error (cause %d) aborting\n", cause);

	/* disconnect the (main) signalling link */
	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	mode = msgb_put(nmsg, 2);
	mode[0] = RSL_IE_RELEASE_MODE;
	mode[1] = 1; /* local release */
	gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, link_id);

	/* in case of modify/hando: wait for confirm */
	if (rr->modify_state)
		return 0;

	/* Handle on group channel. */
	if ((link_id & 7) == 0 && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) {
		LOGP(DRR, LOGL_INFO, "Returning to group receive mode, due to link failure.\n");
		return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_LINK_FAILURE);
	}

	/* send abort ind to upper layer */
	nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_IND);
	if (!nmsg)
		return -ENOMEM;
	nrrh = (struct gsm48_rr_hdr *)nmsg->data;
	nrrh->cause = RR_REL_CAUSE_LINK_FAILURE;
	nrrh->sapi = link_id & 7;
	gsm48_rr_upmsg(ms, nmsg);

	/* only for main signalling link */
	if ((link_id & 7) == 0) {
		/* return idle */
		new_rr_state(rr, GSM48_RR_ST_IDLE);
		/* release SAPI 3 link, if exits */
		gsm48_release_sapi3_link(ms);
	} else {
		new_sapi3_state(rr, GSM48_RR_SAPI3ST_IDLE);
		LOGP(DSUM, LOGL_INFO, "Radio link SAPI3 failed\n");
	}
	return 0;
}

static int gsm48_rr_estab_ind_sapi3(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	uint8_t link_id = rllh->link_id;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;

	if (rr->state != GSM48_RR_ST_DEDICATED) {
		/* disconnect sapi 3 link */
		gsm48_release_sapi3_link(ms);
		return -EINVAL;
	}

	new_sapi3_state(rr, GSM48_RR_SAPI3ST_ESTAB);
	rr->sapi3_link_id = link_id; /* set link ID */

	LOGP(DSUM, LOGL_INFO, "Radio link SAPI3 is established\n");

	if ((link_id & 0xf8) == 0x00) {
		/* raise T200 of SAPI 0 */
		ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_sec =
			T200_DCCH_SHARED;
		ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_usec= 0;
	}

	/* send inication to upper layer */
	nmsg = gsm48_rr_msgb_alloc(GSM48_RR_EST_IND);
	if (!nmsg)
		return -ENOMEM;
	nrrh = (struct gsm48_rr_hdr *)nmsg->data;
	nrrh->sapi = link_id & 7;

	return gsm48_rr_upmsg(ms, nmsg);
}

static int gsm48_rr_estab_cnf_sapi3(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	uint8_t link_id = rllh->link_id;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;

	if (rr->state != GSM48_RR_ST_DEDICATED) {
		gsm48_release_sapi3_link(ms);
		return -EINVAL;
	}

	new_sapi3_state(rr, GSM48_RR_SAPI3ST_ESTAB);
	rr->sapi3_link_id = link_id; /* set link ID, just to be sure */

	LOGP(DSUM, LOGL_INFO, "Radio link SAPI3 is established\n");

	/* send inication to upper layer */
	nmsg = gsm48_rr_msgb_alloc(GSM48_RR_EST_CNF);
	if (!nmsg)
		return -ENOMEM;
	nrrh = (struct gsm48_rr_hdr *)nmsg->data;
	nrrh->sapi = link_id & 7;

	return gsm48_rr_upmsg(ms, nmsg);
}

/* 3.4.2 data from layer 2 to RR and upper layer (sapi 3)*/
static int gsm48_rr_data_ind_sapi3(struct osmocom_ms *ms, struct msgb *msg)
{
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	uint8_t sapi = rllh->link_id & 7;
	struct gsm48_hdr *gh = msgb_l3(msg);
	struct gsm48_rr_hdr *rrh;
	uint8_t pdisc = gh->proto_discr & 0x0f;

	if (pdisc == GSM48_PDISC_RR) {
		msgb_free(msg);
		return -EINVAL;
	}

	/* pull off RSL header up to L3 message */
	msgb_pull(msg, (long)msgb_l3(msg) - (long)msg->data);

	/* push RR header */
	msgb_push(msg, sizeof(struct gsm48_rr_hdr));
	rrh = (struct gsm48_rr_hdr *)msg->data;
	rrh->msg_type = GSM48_RR_DATA_IND;
	rrh->sapi = sapi;

	return gsm48_rr_upmsg(ms, msg);
}

static int gsm48_rr_rel_ind_sapi3(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	uint8_t link_id = rllh->link_id;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;

	new_sapi3_state(rr, GSM48_RR_SAPI3ST_IDLE);

	LOGP(DSUM, LOGL_INFO, "Radio link SAPI3 is released\n");

	/* lower T200 of SAPI 0 */
	ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_sec =
		T200_DCCH;
	ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_usec = 0;

	/* send inication to upper layer */
	nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
	if (!nmsg)
		return -ENOMEM;
	nrrh = (struct gsm48_rr_hdr *)nmsg->data;
	nrrh->cause = RR_REL_CAUSE_NORMAL;
	nrrh->sapi = link_id & 7;

	return gsm48_rr_upmsg(ms, nmsg);
}

/* request SAPI 3 establishment */
static int gsm48_rr_est_req_sapi3(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	uint8_t ch_type, ch_subch, ch_ts;
	struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data;
	uint8_t sapi = rrh->sapi;
	struct msgb *nmsg;

	if (rr->state != GSM48_RR_ST_DEDICATED) {
		struct gsm48_rr_hdr *nrrh;

		/* send inication to upper layer */
		nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
		if (!nmsg)
			return -ENOMEM;
		nrrh = (struct gsm48_rr_hdr *)nmsg->data;
		nrrh->cause = RR_REL_CAUSE_NORMAL;
		nrrh->sapi = sapi;
		return gsm48_rr_upmsg(ms, nmsg);
	}

	if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRSL, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, rr->cd_now.chan_nr);
		return -EINVAL;
	}

	if (ch_type != RSL_CHAN_Bm_ACCHs
	 && ch_type != RSL_CHAN_Lm_ACCHs) {
		LOGP(DRR, LOGL_INFO, "Requesting DCCH link, because no TCH "
			"(sapi %d)\n", sapi);
		rr->sapi3_link_id = 0x00 | sapi; /* SAPI 3, DCCH */
		/* raise T200 of SAPI 0 */
		ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_sec =
			T200_DCCH_SHARED;
		ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_usec= 0;
	} else {
		LOGP(DRR, LOGL_INFO, "Requesting ACCH link, because TCH "
			"(sapi %d)\n", sapi);
		rr->sapi3_link_id = 0x40 | sapi; /* SAPI 3, ACCH */
	}

	/* already established */
	new_sapi3_state(rr, GSM48_RR_SAPI3ST_WAIT_EST);

	/* send message */
	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	return gsm48_send_rsl_nol3(ms, RSL_MT_EST_REQ, nmsg, rr->sapi3_link_id);
}

static int gsm48_rr_est_req_estab_sapi3(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data;
	uint8_t sapi = rrh->sapi;
	struct msgb *nmsg;
	struct gsm48_rr_hdr *nrrh;

	LOGP(DRR, LOGL_INFO, "Radio link SAPI3 already established\n");

	/* send inication to upper layer */
	nmsg = gsm48_rr_msgb_alloc(GSM48_RR_EST_CNF);
	if (!nmsg)
		return -ENOMEM;
	nrrh = (struct gsm48_rr_hdr *)nmsg->data;
	nrrh->sapi = sapi;

	return gsm48_rr_upmsg(ms, nmsg);
}

/*
 * state machines
 */

/* state trasitions for link layer messages (lower layer) */
static struct dldatastate {
	uint32_t	states;
	int		type;
	int		(*rout) (struct osmocom_ms *ms, struct msgb *msg);
} dldatastatelist[] = {
	/* SAPI 0 on DCCH */

	/* data transfer */
	{SBIT(GSM48_RR_ST_IDLE) |
	 SBIT(GSM48_RR_ST_CONN_PEND) |
	 SBIT(GSM48_RR_ST_DEDICATED) |
	 SBIT(GSM48_RR_ST_REL_PEND),
	 RSL_MT_UNIT_DATA_IND, gsm48_rr_unit_data_ind},

	{SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */
	 RSL_MT_DATA_IND, gsm48_rr_data_ind},

	/* esablish */
	{SBIT(GSM48_RR_ST_IDLE) |
	 SBIT(GSM48_RR_ST_CONN_PEND) |
	 SBIT(GSM48_RR_ST_REL_PEND),
	 RSL_MT_EST_CONF, gsm48_rr_estab_cnf},

	/* resume */
	{SBIT(GSM48_RR_ST_DEDICATED),
	 RSL_MT_EST_CONF, gsm48_rr_estab_cnf_dedicated},

	/* release */
	{SBIT(GSM48_RR_ST_CONN_PEND) |
	 SBIT(GSM48_RR_ST_DEDICATED),
	 RSL_MT_REL_IND, gsm48_rr_rel_ind},

	{SBIT(GSM48_RR_ST_REL_PEND),
	 RSL_MT_REL_CONF, gsm48_rr_rel_cnf},

	/* reconnect */
	{SBIT(GSM48_RR_ST_CONN_PEND) |
	 SBIT(GSM48_RR_ST_DEDICATED),
	 RSL_MT_REL_CONF, gsm48_rr_rel_cnf},

	/* suspenion */
	{SBIT(GSM48_RR_ST_DEDICATED),
	 RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated},

	{SBIT(GSM48_RR_ST_DEDICATED),
	 RSL_MT_ERROR_IND, gsm48_rr_mdl_error_ind},
};

#define DLDATASLLEN \
	(sizeof(dldatastatelist) / sizeof(struct dldatastate))

static struct dldatastate dldatastatelists3[] = {
	/* SAPI 3 on DCCH */

	/* establish */
	{SBIT(GSM48_RR_SAPI3ST_IDLE),
	 RSL_MT_EST_IND, gsm48_rr_estab_ind_sapi3},

	/* establish */
	{SBIT(GSM48_RR_SAPI3ST_IDLE) | SBIT(GSM48_RR_SAPI3ST_WAIT_EST),
	 RSL_MT_EST_CONF, gsm48_rr_estab_cnf_sapi3},

	/* data transfer */
	{SBIT(GSM48_RR_SAPI3ST_ESTAB),
	 RSL_MT_DATA_IND, gsm48_rr_data_ind_sapi3},

	/* release */
	{SBIT(GSM48_RR_SAPI3ST_WAIT_EST) | SBIT(GSM48_RR_SAPI3ST_ESTAB),
	 RSL_MT_REL_IND, gsm48_rr_rel_ind_sapi3},

	{ALL_STATES,
	 RSL_MT_ERROR_IND, gsm48_rr_mdl_error_ind},
};

#define DLDATASLLENS3 \
	(sizeof(dldatastatelists3) / sizeof(struct dldatastate))

static int gsm48_rcv_rll(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
	int msg_type = rllh->c.msg_type;
	int link_id = rllh->link_id;
	int i;
	int rc;

	LOGP(DRSL, LOGL_INFO, "(ms %s) Received '%s' from L2 in state "
		"%s (link_id 0x%x)\n", ms->name, rsl_msg_name(msg_type),
		gsm48_rr_state_names[rr->state], link_id);

	/* find function for current state and message */
	if (!(link_id & 7)) {
		/* SAPI 0 */
		for (i = 0; i < DLDATASLLEN; i++)
			if ((msg_type == dldatastatelist[i].type)
			 && ((1 << rr->state) & dldatastatelist[i].states))
				break;
		if (i == DLDATASLLEN) {
			LOGP(DRSL, LOGL_NOTICE, "RSLms message '%s' "
				"unhandled\n", rsl_msg_name(msg_type));
			msgb_free(msg);
			return 0;
		}

		rc = dldatastatelist[i].rout(ms, msg);
	} else {
		/* SAPI 3 */
		for (i = 0; i < DLDATASLLENS3; i++)
			if ((msg_type == dldatastatelists3[i].type)
			 && ((1 << rr->sapi3_state) &
			     dldatastatelists3[i].states))
				break;
		if (i == DLDATASLLENS3) {
			LOGP(DRSL, LOGL_NOTICE, "RSLms message '%s' "
				"unhandled\n", rsl_msg_name(msg_type));
			msgb_free(msg);
			return 0;
		}

		rc = dldatastatelists3[i].rout(ms, msg);
	}

	/* free msgb unless it is forwarded */
	if (msg_type != RSL_MT_DATA_IND)
		msgb_free(msg);

	return rc;
}

static int gsm48_rcv_cch(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct abis_rsl_cchan_hdr *ch = msgb_l2(msg);
	int msg_type = ch->c.msg_type;
	int rc;

	LOGP(DRSL, LOGL_INFO, "(ms %s) Received '%s' from L2 in state "
		"%s\n", ms->name, rsl_msg_name(msg_type),
		gsm48_rr_state_names[rr->state]);

	if (msg_type == RSL_MT_CHAN_CONF) {
		/* Recevie confirm to get the FN when the access burst was transmitted on VGCS channel. */
		if (rr->state == GSM48_RR_ST_CONN_PEND && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) {
			rc = gsm48_rr_uplink_access(ms, msg);
			msgb_free(msg);
			return rc;
		}
		/* Ignore subsequent access bursts in dedicated group transmit mode. */
		if (rr->state == GSM48_RR_ST_DEDICATED && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) {
			msgb_free(msg);
			return 0;
		}
		/* Recevie confirm to get the FN when the access burst was transmitted on CCCH. */
		if (rr->state == GSM48_RR_ST_CONN_PEND) {
			rc = gsm48_rr_tx_rand_acc(ms, msg);
			msgb_free(msg);
			return rc;
		}
	}

	LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n");
	msgb_free(msg);
	return 0;
}


/* input function for L2 messages up to L3 */
static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg)
{
	struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
	int rc = 0;

	switch (rslh->msg_discr & 0xfe) {
	case ABIS_RSL_MDISC_RLL:
		rc = gsm48_rcv_rll(ms, msg);
		break;
	case ABIS_RSL_MDISC_COM_CHAN:
		rc = gsm48_rcv_cch(ms, msg);
		break;
	default:
		/* FIXME: implement this */
		LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n",
			rslh->msg_discr);
		msgb_free(msg);
		rc = -EINVAL;
		break;
	}

	return rc;
}

/* state trasitions for RR-SAP messages from up (main link) */
static struct rrdownstate {
	uint32_t	states;
	int		type;
	int		(*rout) (struct osmocom_ms *ms, struct msgb *msg);
} rrdownstatelist[] = {
	/* SAPI 0 */

	/* NOTE: If not IDLE, it is rejected there. */
	{ALL_STATES, /* 3.3.1.1 */
	 GSM48_RR_EST_REQ, gsm48_rr_est_req},

	{SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */
	 GSM48_RR_DATA_REQ, gsm48_rr_data_req},

	{SBIT(GSM48_RR_ST_CONN_PEND) |
	 SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.13.3 */
	 GSM48_RR_ABORT_REQ, gsm48_rr_abort_req},

	/* NOTE: If not IDLE, it is rejected there. */
	{ALL_STATES, /* 3.3.3.2 */
	 GSM48_RR_GROUP_REQ, gsm48_rr_group_req},

	{ALL_STATES,
	 GSM48_RR_GROUP_REL_REQ, gsm48_rr_group_rel_req},

	/* NOTE: If not IDLE, it is rejected there. */
	{ALL_STATES, /* 3.3.1.2 */
	 GSM48_RR_UPLINK_REQ, gsm48_rr_uplink_req},

	{ALL_STATES, /* 3.4.13.4 */
	 GSM48_RR_UPLINK_REL_REQ, gsm48_rr_uplink_rel_req},
};

#define RRDOWNSLLEN \
	(sizeof(rrdownstatelist) / sizeof(struct rrdownstate))

/* state trasitions for RR-SAP messages from up with (SAPI 3) */
static struct rrdownstate rrdownstatelists3[] = {
	/* SAPI 3 */

	{SBIT(GSM48_RR_SAPI3ST_IDLE),
	 GSM48_RR_EST_REQ, gsm48_rr_est_req_sapi3},

	{SBIT(GSM48_RR_SAPI3ST_ESTAB),
	 GSM48_RR_EST_REQ, gsm48_rr_est_req_estab_sapi3},

	{SBIT(GSM48_RR_SAPI3ST_ESTAB),
	 GSM48_RR_DATA_REQ, gsm48_rr_data_req}, /* handles SAPI 3 too */
};

#define RRDOWNSLLENS3 \
	(sizeof(rrdownstatelists3) / sizeof(struct rrdownstate))

int gsm48_rr_downmsg(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data;
	int msg_type = rrh->msg_type;
	int sapi = rrh->sapi;
	int i;
	int rc;

	LOGP(DRR, LOGL_INFO, "(ms %s) Message '%s' received in state %s "
		"(sapi %d)\n", ms->name, get_rr_name(msg_type),
		gsm48_rr_state_names[rr->state], sapi);

	if (!sapi) {
		/* SAPI 0: find function for current state and message */
		for (i = 0; i < RRDOWNSLLEN; i++)
			if ((msg_type == rrdownstatelist[i].type)
			 && ((1 << rr->state) & rrdownstatelist[i].states))
				break;
		if (i == RRDOWNSLLEN) {
			LOGP(DRR, LOGL_NOTICE, "Message unhandled at this "
				"state.\n");
			msgb_free(msg);
			return 0;
		}

		rc = rrdownstatelist[i].rout(ms, msg);
	} else {
		/* SAPI 3: find function for current state and message */
		for (i = 0; i < RRDOWNSLLENS3; i++)
			if ((msg_type == rrdownstatelists3[i].type)
			 && ((1 << rr->sapi3_state)
			     & rrdownstatelists3[i].states))
				break;
		if (i == RRDOWNSLLENS3) {
			LOGP(DRR, LOGL_NOTICE, "Message unhandled at this "
				"state.\n");
			msgb_free(msg);
			return 0;
		}

		rc = rrdownstatelists3[i].rout(ms, msg);
	}

	/* free msgb unless it is forwarded */
	if (msg_type != GSM48_RR_DATA_REQ)
		msgb_free(msg);

	return rc;
}

/*
 * init/exit
 */

int gsm48_rr_init(struct osmocom_ms *ms)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;

	memset(rr, 0, sizeof(*rr));
	rr->ms = ms;

	LOGP(DRR, LOGL_INFO, "init Radio Ressource process\n");

	INIT_LLIST_HEAD(&rr->rsl_upqueue);
	INIT_LLIST_HEAD(&rr->downqueue);
	/* downqueue is handled here, so don't add_work */

	lapdm_channel_set_l3(&ms->lapdm_channel, &rcv_rsl, ms);

	start_rr_t_meas(rr, 1, 0);

	rr->tch_loop_mode = L1CTL_TCH_LOOP_OPEN;
	rr->audio_mode = 0x00; /* set in tch_{voice,data}_state_init() */

	/* List of notifications about ongoing ASCI calls */
	INIT_LLIST_HEAD(&rr->vgcs.notif_list);

	return 0;
}

int gsm48_rr_exit(struct osmocom_ms *ms)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	struct msgb *msg;

	LOGP(DRR, LOGL_INFO, "exit Radio Ressource process\n");

	/* flush queues */
	while ((msg = msgb_dequeue(&rr->rsl_upqueue)))
		msgb_free(msg);
	while ((msg = msgb_dequeue(&rr->downqueue)))
		msgb_free(msg);

	if (rr->rr_est_msg) {
		msgb_free(rr->rr_est_msg);
		rr->rr_est_msg = NULL;
	}

	stop_rr_t_meas(rr);
	stop_rr_t_starting(rr);
	stop_rr_t_rel_wait(rr);
	stop_rr_t3110(rr);
	stop_rr_t3122(rr);
	stop_rr_t3124(rr);
	stop_rr_t3126(rr);
	stop_rr_t_ul_free(rr);
	stop_rr_t3128(rr);
	stop_rr_t3130(rr);

	/* Free pending list entries. */
	asci_notif_list_free(rr);

	return 0;
}

#if 0

todo rr_sync_ind when receiving ciph, re ass, channel mode modify


static void timeout_rr_t3124(void *arg)
{
	struct gsm48_rrlayer *rr = arg;
	struct msgb *nmsg;

	/* stop sending more access bursts when timer expired */
	hando_acc_left = 0;

	/* get old channel description */
	memcpy(&rr->chan_desc, &rr->chan_last, sizeof(rr->chan_desc));

	/* change radio to old channel */
	tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr,
			 rr->cd_now.tsc);
	rr->dm_est = 1;

	/* re-establish old link */
	nmsg = gsm48_l3_msgb_alloc();
	if (!nmsg)
		return -ENOMEM;
	return gsm48_send_rsl(ms, RSL_MT_REEST_REQ, nmsg, 0);

	todo
}

static void start_rr_t3124(struct gsm48_rrlayer *rr, int sec, int micro)
{
	LOGP(DRR, LOGL_INFO, "starting T3124 with %d.%03d seconds\n", sec,
		micro / 1000);
	rr->t3124.cb = timeout_rr_t3124;
	rr->t3124.data = rr;
	osmo_timer_schedule(&rr->t3124, sec, micro);
}

#endif

int gsm48_rr_tx_traffic(struct osmocom_ms *ms, struct msgb *msg)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	uint8_t ch_type, ch_subch, ch_ts;

	if (!rr->dm_est) {
		LOGP(DRR, LOGL_INFO, "Current channel is not active\n");
		goto error;
	}

	if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRSL, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, rr->cd_now.chan_nr);
		goto error;
	}

	if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) {
		LOGP(DRR, LOGL_INFO, "Current channel is not (yet) TCH\n");
		goto error;
	}

	return l1ctl_tx_traffic_req(ms, msg, rr->cd_now.chan_nr, 0);
error:
	msgb_free(msg);
	return -ENOTSUP;
}

int gsm48_rr_audio_mode(struct osmocom_ms *ms, uint8_t mode)
{
	struct gsm48_rrlayer *rr = &ms->rrlayer;
	uint8_t ch_type, ch_subch, ch_ts;

	LOGP(DRR, LOGL_INFO, "setting audio mode to %d\n", mode);

	rr->audio_mode = mode;

	if (!rr->dm_est)
		return 0;

	if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
		LOGP(DRSL, LOGL_ERROR,
		     "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
		     __func__, rr->cd_now.chan_nr);
		return -EINVAL;
	}

	if (ch_type != RSL_CHAN_Bm_ACCHs
	 && ch_type != RSL_CHAN_Lm_ACCHs)
		return 0;

	return l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, mode, rr->cd_now.tch_flags, rr->tch_loop_mode);
}
