[PATCH 3/7] Remove direct handling of L1 prims and bts_model_* calls from sysmo code

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/OpenBSC@lists.osmocom.org/.

Andreas Eversberg jolly at eversberg.eu
Mon Jul 15 08:35:32 UTC 2013


Instead of handling primitves itself, sysmo-bts code sends and receives
PH-/MPH-/TCH primitves to and from common code.
---
 src/osmo-bts-sysmo/l1_if.c        | 1092 +++++++++++++++++--------------------
 src/osmo-bts-sysmo/l1_if.h        |   13 +-
 src/osmo-bts-sysmo/main.c         |    3 +-
 src/osmo-bts-sysmo/oml.c          |   46 +-
 src/osmo-bts-sysmo/sysmobts_vty.c |   94 ----
 src/osmo-bts-sysmo/tch.c          |  117 +---
 6 files changed, 559 insertions(+), 806 deletions(-)

diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c
index 6efb9d6..f2e4440 100644
--- a/src/osmo-bts-sysmo/l1_if.c
+++ b/src/osmo-bts-sysmo/l1_if.c
@@ -32,21 +32,17 @@
 #include <osmocom/core/select.h>
 #include <osmocom/core/timer.h>
 #include <osmocom/core/write_queue.h>
-#include <osmocom/core/gsmtap.h>
-#include <osmocom/core/gsmtap_util.h>
 #include <osmocom/gsm/gsm_utils.h>
 #include <osmocom/gsm/lapdm.h>
 
-#include <osmocom/trau/osmo_ortp.h>
-
 #include <osmo-bts/logging.h>
 #include <osmo-bts/bts.h>
 #include <osmo-bts/oml.h>
-#include <osmo-bts/rsl.h>
 #include <osmo-bts/gsm_data.h>
 #include <osmo-bts/paging.h>
 #include <osmo-bts/measurement.h>
 #include <osmo-bts/pcu_if.h>
+#include <osmo-bts/l1sap.h>
 
 #include <sysmocom/femtobts/superfemto.h>
 #include <sysmocom/femtobts/gsml1prim.h>
@@ -63,98 +59,6 @@ extern int pcu_direct;
 #define MIN_QUAL_RACH	 5.0f	/* at least  5 dB C/I */
 #define MIN_QUAL_NORM	-0.5f	/* at least -1 dB C/I */
 
-/* mapping from femtobts L1 SAPI to GSMTAP channel type */
-static const uint8_t l1sapi2gsmtap_cht[GsmL1_Sapi_NUM] = {
-	[GsmL1_Sapi_Idle] = 255,
-	[GsmL1_Sapi_Fcch] = 255,
-	[GsmL1_Sapi_Sch] = 255,
-	[GsmL1_Sapi_Sacch] = GSMTAP_CHANNEL_SDCCH | GSMTAP_CHANNEL_ACCH,
-	[GsmL1_Sapi_Sdcch] = GSMTAP_CHANNEL_SDCCH,
-	[GsmL1_Sapi_Bcch] = GSMTAP_CHANNEL_BCCH,
-	[GsmL1_Sapi_Pch] = GSMTAP_CHANNEL_PCH,
-	[GsmL1_Sapi_Agch] = GSMTAP_CHANNEL_AGCH,
-	[GsmL1_Sapi_Cbch] = GSMTAP_CHANNEL_CBCH51,
-	[GsmL1_Sapi_Rach] = GSMTAP_CHANNEL_RACH,
-	[GsmL1_Sapi_TchF] = 255,
-	[GsmL1_Sapi_FacchF] = GSMTAP_CHANNEL_TCH_F,
-	[GsmL1_Sapi_TchH] = 255,
-	[GsmL1_Sapi_FacchH] = GSMTAP_CHANNEL_TCH_H,
-	[GsmL1_Sapi_Nch] = GSMTAP_CHANNEL_CCCH,
-	[GsmL1_Sapi_Pdtch] = GSMTAP_CHANNEL_PACCH,
-	[GsmL1_Sapi_Pacch] = GSMTAP_CHANNEL_PACCH,
-	[GsmL1_Sapi_Pbcch] = 255,
-	[GsmL1_Sapi_Pagch] = 255,
-	[GsmL1_Sapi_Ppch] = 255,
-	[GsmL1_Sapi_Pnch] = 255,
-	[GsmL1_Sapi_Ptcch] = GSMTAP_CHANNEL_PTCCH,
-	[GsmL1_Sapi_Prach] = 255,
-};
-
-static void tx_to_gsmtap(struct femtol1_hdl *fl1h, struct msgb *msg)
-{
-	struct gsm_bts_trx *trx = fl1h->priv;
-	GsmL1_Prim_t *l1p = msgb_l1prim(msg);
-	GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
-
-	if (fl1h->gsmtap) {
-		uint8_t ss, chan_type;
-		if (data_req->subCh == 0x1f)
-			ss = 0;
-		else
-			ss = data_req->subCh;
-
-		if (!(fl1h->gsmtap_sapi_mask & (1 << data_req->sapi)))
-			return;
-
-		chan_type = l1sapi2gsmtap_cht[data_req->sapi];
-		if (chan_type == 255)
-			return;
-
-		gsmtap_send(fl1h->gsmtap, trx->arfcn, data_req->u8Tn,
-				chan_type, ss, data_req->u32Fn, 0, 0,
-				data_req->msgUnitParam.u8Buffer,
-				data_req->msgUnitParam.u8Size);
-	}
-}
-
-static void ul_to_gsmtap(struct femtol1_hdl *fl1h, struct msgb *msg)
-{
-	struct gsm_bts_trx *trx = fl1h->priv;
-	GsmL1_Prim_t *l1p = msgb_l1prim(msg);
-	GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd;
-	int skip = 0;
-
-	if (fl1h->gsmtap) {
-		uint8_t ss, chan_type;
-		if (data_ind->subCh == 0x1f)
-			ss = 0;
-		else
-			ss = data_ind->subCh;
-
-		if (!(fl1h->gsmtap_sapi_mask & (1 << data_ind->sapi)))
-			return;
-
-		chan_type = l1sapi2gsmtap_cht[data_ind->sapi];
-		if (chan_type == 255)
-			return;
-		if (chan_type == GSMTAP_CHANNEL_PACCH
-		 || chan_type == GSMTAP_CHANNEL_PDCH) {
-			if (data_ind->msgUnitParam.u8Buffer[0]
-					!= GsmL1_PdtchPlType_Full)
-				return;
-			skip = 1;
-		}
-
-		gsmtap_send(fl1h->gsmtap, trx->arfcn | GSMTAP_ARFCN_F_UPLINK,
-				data_ind->u8Tn, chan_type, ss, data_ind->u32Fn,
-				data_ind->measParam.fRssi,
-				data_ind->measParam.fLinkQuality,
-				data_ind->msgUnitParam.u8Buffer + skip,
-				data_ind->msgUnitParam.u8Size - skip);
-	}
-}
-
-
 struct wait_l1_conf {
 	struct llist_head list;		/* internal linked list */
 	struct osmo_timer_list timer;	/* timer for L1 timeout */
@@ -314,74 +218,442 @@ empty_req_from_rts_ind(GsmL1_Prim_t *l1p,
 	return empty_req;
 }
 
-/* obtain a ptr to the lapdm_channel for a given hLayer2 */
-static struct lapdm_channel *
-get_lapdm_chan_by_hl2(struct gsm_bts_trx *trx, uint32_t hLayer2)
-{
-	struct gsm_lchan *lchan;
+static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
+	0x03, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
+	0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
+	0x2B, 0x2B, 0x2B
+};
 
-	lchan = l1if_hLayer_to_lchan(trx, hLayer2);
-	if (!lchan)
-		return NULL;
+static void dump_meas_res(int ll, GsmL1_MeasParam_t *m)
+{
+	LOGPC(DL1C, ll, ", Meas: RSSI %-3.2f dBm,  Qual %-3.2f dB,  "
+		"BER %-3.2f,  Timing %d\n", m->fRssi, m->fLinkQuality,
+		m->fBer, m->i16BurstTiming);
+}
 
-	return &lchan->lapdm_ch;
+static int process_meas_res(struct gsm_bts_trx *trx, uint8_t chan_nr,
+				GsmL1_MeasParam_t *m)
+{
+	struct osmo_phsap_prim l1sap;
+
+	memset(&l1sap, 0, sizeof(l1sap));
+	osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO,
+		PRIM_OP_INDICATION, NULL);
+	l1sap.u.info.type = PRIM_INFO_MEAS;
+	l1sap.u.info.u.meas_ind.chan_nr = chan_nr;
+	l1sap.u.info.u.meas_ind.ta_offs_qbits = m->i16BurstTiming;
+	l1sap.u.info.u.meas_ind.ber10k = (unsigned int) (m->fBer * 100);
+	l1sap.u.info.u.meas_ind.inv_rssi = (uint8_t) (m->fRssi * -1);
+
+	return l1sap_up(trx, &l1sap);
 }
 
-/* check if the message is a GSM48_MT_RR_CIPH_M_CMD, and if yes, enable
- * uni-directional de-cryption on the uplink. We need this ugly layering
- * violation as we have no way of passing down L3 metadata (RSL CIPHERING CMD)
- * to this point in L1 */
-static int check_for_ciph_cmd(struct femtol1_hdl *fl1h,
-			      struct msgb *msg, struct gsm_lchan *lchan)
+/* primitive from common part */
+int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
 {
+	struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx);
+	struct msgb *msg = l1sap->oph.msg;
+	uint32_t u32Fn;
+	uint8_t u8Tn, subCh, u8BlockNbr = 0, sapi, ss;
+	uint8_t chan_nr, link_id;
+	int rc = 0;
+	struct msgb *nmsg = NULL;
+	GsmL1_Prim_t *l1p;
+	struct gsm_lchan *lchan;
+
+	switch (OSMO_PRIM_HDR(&l1sap->oph)) {
+	case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST):
+		if (!msg) {
+			LOGP(DL1C, LOGL_FATAL, "PH-DATA.req without msg. "
+				"Please fix!\n");
+			abort();
+		}
+		chan_nr = l1sap->u.data.chan_nr;
+		link_id = l1sap->u.data.link_id;
+		u32Fn = l1sap->u.data.fn;
+		u8Tn = L1SAP_CHAN2TS(chan_nr);
+		subCh = 0x1f;
+		if (L1SAP_IS_LINK_SACCH(link_id)) {
+			sapi = GsmL1_Sapi_Sacch;
+			if (!L1SAP_IS_CHAN_TCHF(chan_nr))
+				subCh = l1sap_chan2ss(chan_nr);
+		} else if (L1SAP_IS_CHAN_TCHF(chan_nr)) {
+			if (trx->ts[u8Tn].pchan == GSM_PCHAN_PDCH) {
+				if (L1SAP_IS_PTCCH(u32Fn)) {
+					sapi = GsmL1_Sapi_Ptcch;
+					u8BlockNbr = L1SAP_FN2PTCCHBLOCK(u32Fn);
+				} else {
+					sapi = GsmL1_Sapi_Pdtch;
+					u8BlockNbr = L1SAP_FN2MACBLOCK(u32Fn);
+				}
+			} else {
+				sapi = GsmL1_Sapi_FacchF;
+				u8BlockNbr = (u32Fn % 13) >> 2;
+			}
+		} else if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
+			subCh = L1SAP_CHAN2SS_TCHH(chan_nr);
+			sapi = GsmL1_Sapi_FacchH;
+			u8BlockNbr = (u32Fn % 26) >> 3;
+		} else if (L1SAP_IS_CHAN_SDCCH4(chan_nr)) {
+			subCh = L1SAP_CHAN2SS_SDCCH4(chan_nr);
+			sapi = GsmL1_Sapi_Sdcch;
+		} else if (L1SAP_IS_CHAN_SDCCH8(chan_nr)) {
+			subCh = L1SAP_CHAN2SS_SDCCH8(chan_nr);
+			sapi = GsmL1_Sapi_Sdcch;
+		} else if (L1SAP_IS_CHAN_BCCH(chan_nr)) {
+			sapi = GsmL1_Sapi_Bcch;
+		} else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) {
+#warning Set BS_AG_BLKS_RES
+			/* The sapi depends on DSP configuration, not
+			 * on the actual SYSTEM INFORMATION 3. */
+			u8BlockNbr = L1SAP_FN2CCCHBLOCK(u32Fn);
+			if (u8BlockNbr >= 1)
+				sapi = GsmL1_Sapi_Pch;
+			else
+				sapi = GsmL1_Sapi_Agch;
+		} else {
+			LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d "
+				"chan_nr %d link_id %d\n", l1sap->oph.primitive,
+				l1sap->oph.operation, chan_nr, link_id);
+			rc = -EINVAL;
+			goto done;
+		}
+
+		msgb_pull(msg, sizeof(*l1sap));
+
+		/* create new message */
+		nmsg = l1p_msgb_alloc();
+		l1p = msgb_l1prim(nmsg);
+		if (msg->len) {
+			/* data request */
+			GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
+			GsmL1_MsgUnitParam_t *msu_param;
+
+			l1p->id = GsmL1_PrimId_PhDataReq;
+
+			data_req->hLayer1 = fl1->hLayer1;
+			data_req->u8Tn = u8Tn;
+			data_req->u32Fn = u32Fn;
+			data_req->sapi = sapi;
+			data_req->subCh = subCh;
+			data_req->u8BlockNbr = u8BlockNbr;
+			msu_param = &data_req->msgUnitParam;
+			msu_param->u8Size = msg->len;
+			memcpy(msu_param->u8Buffer, msg->data, msg->len);
+		} else {
+			/* empty frame */
+			GsmL1_PhEmptyFrameReq_t *empty_req =
+							&l1p->u.phEmptyFrameReq;
+
+			l1p->id = GsmL1_PrimId_PhEmptyFrameReq;
+
+			empty_req->hLayer1 = fl1->hLayer1;
+			empty_req->u8Tn = u8Tn;
+			empty_req->u32Fn = u32Fn;
+			empty_req->sapi = sapi;
+			empty_req->subCh = subCh;
+			empty_req->u8BlockNbr = u8BlockNbr;
+		}
+
+		/* send message to DSP's queue */
+		osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg);
+		break;
+	case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
+		chan_nr = l1sap->u.tch.chan_nr;
+		u32Fn = l1sap->u.tch.fn;
+		u8Tn = L1SAP_CHAN2TS(chan_nr);
+		u8BlockNbr = (u32Fn % 13) >> 2;
+		if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
+			ss = subCh = L1SAP_CHAN2SS_TCHH(chan_nr);
+			sapi = GsmL1_Sapi_TchH;
+		} else {
+			subCh = 0x1f;
+			ss = 0;
+			sapi = GsmL1_Sapi_TchF;
+		}
+
+		lchan = &trx->ts[u8Tn].lchan[ss];
+
+		/* create new message and fill data */
+		if (msg) {
+			msgb_pull(msg, sizeof(*l1sap));
+			/* create new message */
+			nmsg = l1p_msgb_alloc();
+			if (!nmsg) {
+				rc = -ENOMEM;
+				goto done;
+			}
+			l1p = msgb_l1prim(nmsg);
+			l1if_tch_encode(lchan,
+				l1p->u.phDataReq.msgUnitParam.u8Buffer,
+				&l1p->u.phDataReq.msgUnitParam.u8Size,
+				msg->data, msg->len);
+		}
+
+		/* no message/data, we generate an empty traffic msg */
+		if (!nmsg)
+			nmsg = gen_empty_tch_msg(lchan);
+
+		/* no traffic message, we generate an empty msg */
+		if (!nmsg) {
+			nmsg = l1p_msgb_alloc();
+			if (!nmsg) {
+				rc = -ENOMEM;
+				goto done;
+			}
+		}
+
+		l1p = msgb_l1prim(nmsg);
 
-	/* only do this if we are in the right state */
-	switch (lchan->ciph_state) {
-	case LCHAN_CIPH_NONE:
-	case LCHAN_CIPH_RX_REQ:
+		/* if we provide data, or if data is already in nmsg */
+		if (l1p->u.phDataReq.msgUnitParam.u8Size) {
+			/* data request */
+			GsmL1_PhDataReq_t *data_req = &l1p->u.phDataReq;
+
+			l1p->id = GsmL1_PrimId_PhDataReq;
+
+			data_req->hLayer1 = fl1->hLayer1;
+			data_req->u8Tn = u8Tn;
+			data_req->u32Fn = u32Fn;
+			data_req->sapi = sapi;
+			data_req->subCh = subCh;
+			data_req->u8BlockNbr = u8BlockNbr;
+		} else {
+			/* empty frame */
+			GsmL1_PhEmptyFrameReq_t *empty_req =
+							&l1p->u.phEmptyFrameReq;
+
+			l1p->id = GsmL1_PrimId_PhEmptyFrameReq;
+
+			empty_req->hLayer1 = fl1->hLayer1;
+			empty_req->u8Tn = u8Tn;
+			empty_req->u32Fn = u32Fn;
+			empty_req->sapi = sapi;
+			empty_req->subCh = subCh;
+			empty_req->u8BlockNbr = u8BlockNbr;
+		}
+		/* send message to DSP's queue */
+		osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg);
+		break;
+	case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
+		switch (l1sap->u.info.type) {
+		case PRIM_INFO_ACT_CIPH:
+			chan_nr = l1sap->u.info.u.ciph_req.chan_nr;
+			u8Tn = L1SAP_CHAN2TS(chan_nr);
+			ss = l1sap_chan2ss(chan_nr);
+			lchan = &trx->ts[u8Tn].lchan[ss];
+			if (l1sap->u.info.u.ciph_req.downlink) {
+				l1if_set_ciphering(fl1, lchan, 1);
+				lchan->ciph_state = LCHAN_CIPH_RX_REQ;
+			}
+			if (l1sap->u.info.u.ciph_req.uplink) {
+				l1if_set_ciphering(fl1, lchan, 0);
+				lchan->ciph_state = LCHAN_CIPH_TXRX_REQ;
+			}
+			break;
+		case PRIM_INFO_ACTIVATE:
+		case PRIM_INFO_DEACTIVATE:
+		case PRIM_INFO_MODIFY:
+			chan_nr = l1sap->u.info.u.act_req.chan_nr;
+			u8Tn = L1SAP_CHAN2TS(chan_nr);
+			ss = l1sap_chan2ss(chan_nr);
+			lchan = &trx->ts[u8Tn].lchan[ss];
+			if (l1sap->u.info.type == PRIM_INFO_ACTIVATE)
+				l1if_rsl_chan_act(lchan);
+			else if (l1sap->u.info.type == PRIM_INFO_MODIFY)
+				l1if_rsl_mode_modify(lchan);
+			else if (l1sap->u.info.u.act_req.sacch_only)
+				l1if_rsl_deact_sacch(lchan);
+			else
+				l1if_rsl_chan_rel(lchan);
+			break;
+		default:
+			LOGP(DL1C, LOGL_NOTICE, "unknown MPH-INFO.req %d\n",
+				l1sap->u.info.type);
+			rc = -EINVAL;
+			goto done;
+		}
 		break;
 	default:
-		return 0;
+		LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d\n",
+			l1sap->oph.primitive, l1sap->oph.operation);
+		rc = -EINVAL;
+		goto done;
 	}
 
-	/* First byte (Address Field) of LAPDm header) */
-	if (msg->data[0] != 0x03)
-		return 0;
-	/* First byte (protocol discriminator) of RR */
-	if ((msg->data[3] & 0xF) != GSM48_PDISC_RR)
-		return 0;
-	/* 2nd byte (msg type) of RR */
-	if ((msg->data[4] & 0x3F) != GSM48_MT_RR_CIPH_M_CMD)
+done:
+	if (msg)
+		msgb_free(msg);
+	return rc;
+}
+
+static int handle_mph_time_ind(struct femtol1_hdl *fl1,
+				GsmL1_MphTimeInd_t *time_ind)
+{
+	struct gsm_bts_trx *trx = fl1->priv;
+	struct gsm_bts *bts = trx->bts;
+	struct osmo_phsap_prim l1sap;
+	uint32_t fn;
+
+	/* increment the primitive count for the alive timer */
+	fl1->alive_prim_cnt++;
+
+	/* ignore every time indication, except for c0 */
+	if (trx != bts->c0) {
 		return 0;
+	}
 
-	lchan->ciph_state = LCHAN_CIPH_RX_REQ;
-	l1if_set_ciphering(fl1h, lchan, 0);
+	fn = time_ind->u32Fn;
 
-	return 1;
+	memset(&l1sap, 0, sizeof(l1sap));
+	osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO,
+		PRIM_OP_INDICATION, NULL);
+	l1sap.u.info.type = PRIM_INFO_TIME;
+	l1sap.u.info.u.time_ind.fn = fn;
+
+	return l1sap_up(trx, &l1sap);
 }
 
-static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
-	0x03, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
-	0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
-	0x2B, 0x2B, 0x2B
-};
+static uint8_t chan_nr_by_sapi(enum gsm_phys_chan_config pchan,
+			       GsmL1_Sapi_t sapi, GsmL1_SubCh_t subCh,
+			       uint8_t u8Tn, uint32_t u32Fn)
+{
+	uint8_t cbits = 0;
+	switch (sapi) {
+	case GsmL1_Sapi_Bcch:
+		cbits = 0x10;
+		break;
+	case GsmL1_Sapi_Sacch:
+		switch(pchan) {
+		case GSM_PCHAN_TCH_F:
+			cbits = 0x01;
+			break;
+		case GSM_PCHAN_TCH_H:
+			cbits = 0x02 + subCh;
+			break;
+		case GSM_PCHAN_CCCH_SDCCH4:
+			cbits = 0x04 + subCh;
+			break;
+		case GSM_PCHAN_SDCCH8_SACCH8C:
+			cbits = 0x08 + subCh;
+			break;
+		default:
+			LOGP(DL1C, LOGL_ERROR, "SACCH for pchan %d?\n",
+				pchan);
+			return 0;
+		}
+		break;
+	case GsmL1_Sapi_Sdcch:
+		switch(pchan) {
+		case GSM_PCHAN_CCCH_SDCCH4:
+			cbits = 0x04 + subCh;
+			break;
+		case GSM_PCHAN_SDCCH8_SACCH8C:
+			cbits = 0x08 + subCh;
+			break;
+		default:
+			LOGP(DL1C, LOGL_ERROR, "SDCCH for pchan %d?\n",
+				pchan);
+			return 0;
+		}
+		break;
+	case GsmL1_Sapi_Agch:
+	case GsmL1_Sapi_Pch:
+		cbits = 0x12;
+		break;
+	case GsmL1_Sapi_TchF:
+		cbits = 0x01;
+		break;
+	case GsmL1_Sapi_TchH:
+		cbits = 0x02 + subCh;
+		break;
+	case GsmL1_Sapi_FacchF:
+		cbits = 0x01;
+		break;
+	case GsmL1_Sapi_FacchH:
+		cbits = 0x02 + subCh;
+		break;
+	case GsmL1_Sapi_Pdtch:
+	case GsmL1_Sapi_Pacch:
+		switch(pchan) {
+		case GSM_PCHAN_PDCH:
+			cbits = 0x01;
+			break;
+		default:
+			LOGP(DL1C, LOGL_ERROR, "PDTCH for pchan %d?\n",
+				pchan);
+			return 0;
+		}
+		break;
+	case GsmL1_Sapi_Ptcch:
+		if (!L1SAP_IS_PTCCH(u32Fn)) {
+			LOGP(DL1C, LOGL_FATAL, "Not expecting PTCCH at frame "
+				"number other than 12, got it at %u (%u). "
+				"Please fix!\n", u32Fn % 52, u32Fn);
+			abort();
+		}
+		switch(pchan) {
+		case GSM_PCHAN_PDCH:
+			cbits = 0x01;
+			break;
+		default:
+			LOGP(DL1C, LOGL_ERROR, "PTCCH for pchan %d?\n",
+				pchan);
+			return 0;
+		}
+		break;
+	default:
+		return 0;
+	}
+
+	return (cbits << 3) | u8Tn;
+}
 
 static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
-				     GsmL1_PhReadyToSendInd_t *rts_ind)
+				     GsmL1_PhReadyToSendInd_t *rts_ind,
+				     struct msgb *l1p_msg)
 {
 	struct gsm_bts_trx *trx = fl1->priv;
 	struct gsm_bts *bts = trx->bts;
-	struct gsm_bts_role_bts *btsb = bts->role;
+	struct osmo_phsap_prim *l1sap;
+	struct gsm_time g_time;
+	uint8_t chan_nr, link_id;
+	uint32_t fn;
+	int rc;
 	struct msgb *resp_msg;
 	GsmL1_PhDataReq_t *data_req;
 	GsmL1_MsgUnitParam_t *msu_param;
-	struct lapdm_entity *le;
-	struct gsm_lchan *lchan;
-	struct gsm_time g_time;
 	uint32_t t3p;
-	uint8_t *si;
-	struct osmo_phsap_prim pp;
-	int rc;
+
+	/* in case we need to forward primitive to common part*/
+	chan_nr = chan_nr_by_sapi(trx->ts[rts_ind->u8Tn].pchan, rts_ind->sapi,
+		rts_ind->subCh, rts_ind->u8Tn, rts_ind->u32Fn);
+	if (chan_nr) {
+		fn = rts_ind->u32Fn;
+		if (rts_ind->sapi == GsmL1_Sapi_Sacch)
+			link_id = 0x40;
+		else
+			link_id = 0;
+		rc = msgb_trim(l1p_msg, sizeof(*l1sap));
+		if (rc < 0)
+			MSGB_ABORT(l1p_msg, "No room for primitive\n");
+		l1sap = msgb_l1sap_prim(l1p_msg);
+		if (rts_ind->sapi == GsmL1_Sapi_TchF
+		 || rts_ind->sapi == GsmL1_Sapi_TchH) {
+			osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS,
+				PRIM_OP_INDICATION, l1p_msg);
+			l1sap->u.tch.chan_nr = chan_nr;
+			l1sap->u.tch.fn = fn;
+		} else {
+			osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS,
+				PRIM_OP_INDICATION, l1p_msg);
+			l1sap->u.data.link_id = link_id;
+			l1sap->u.data.chan_nr = chan_nr;
+			l1sap->u.data.fn = fn;
+		}
+
+		return l1sap_up(trx, l1sap);
+	}
 
 	gsm_fn2gsmtime(&g_time, rts_ind->u32Fn);
 
@@ -389,57 +661,6 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
 		g_time.t1, g_time.t2, g_time.t3,
 		get_value_string(femtobts_l1sapi_names, rts_ind->sapi));
 
-	/* In case of TCH downlink trasnmission, we already have a l1
-	 * primitive msgb pre-allocated and pre-formatted in the
-	 * dl_tch_queue.  All we need to do is to pull it off the queue
-	 * and transmit it */
-	switch (rts_ind->sapi) {
-	case GsmL1_Sapi_TchF:
-	case GsmL1_Sapi_TchH:
-		/* resolve the L2 entity using rts_ind->hLayer2 */
-		lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2);
-		if (!lchan)
-			break;
-
-		if (!lchan->loopback && lchan->abis_ip.rtp_socket) {
-			osmo_rtp_socket_poll(lchan->abis_ip.rtp_socket);
-			/* FIXME: we _assume_ that we never miss TDMA
-			 * frames and that we always get to this point
-			 * for every to-be-transmitted voice frame.  A
-			 * better solution would be to compute
-			 * rx_user_ts based on how many TDMA frames have
-			 * elapsed since the last call */
-			lchan->abis_ip.rtp_socket->rx_user_ts += GSM_RTP_DURATION;
-		}
-		/* get a msgb from the dl_tx_queue */
-		resp_msg = msgb_dequeue(&lchan->dl_tch_queue);
-		/* if there is none, try to generate empty TCH frame
-		 * like AMR SID_BAD */
-		if (!resp_msg) {
-			LOGP(DL1C, LOGL_DEBUG, "%s DL TCH Tx queue underrun\n",
-				gsm_lchan_name(lchan));
-			resp_msg = gen_empty_tch_msg(lchan);
-			/* if there really is none, break here and send empty */
-			if (!resp_msg)
-				break;
-		}
-
-		/* fill header */
-		data_req_from_rts_ind(msgb_l1prim(resp_msg), rts_ind);
-		/* actually transmit it */
-		goto tx;
-		break;
-	case GsmL1_Sapi_Pdtch:
-	case GsmL1_Sapi_Pacch:
-		return pcu_tx_rts_req(&trx->ts[rts_ind->u8Tn], 0,
-			rts_ind->u32Fn, rts_ind->u16Arfcn, rts_ind->u8BlockNbr);
-	case GsmL1_Sapi_Ptcch:
-		return pcu_tx_rts_req(&trx->ts[rts_ind->u8Tn], 1,
-			rts_ind->u32Fn, rts_ind->u16Arfcn, rts_ind->u8BlockNbr);
-	default:
-		break;
-	}
-
 	/* in all other cases, we need to allocate a new PH-DATA.ind
 	 * primitive msgb and start to fill it */
 	resp_msg = l1p_msgb_alloc();
@@ -451,6 +672,7 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
 
 	switch (rts_ind->sapi) {
 	case GsmL1_Sapi_Sch:
+		gsm_fn2gsmtime(&g_time, rts_ind->u32Fn);
 		/* compute T3prime */
 		t3p = (g_time.t3 - 1) / 10;
 		/* fill SCH burst with data */
@@ -460,87 +682,6 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
 		msu_param->u8Buffer[2] = (g_time.t1 << 7) | (g_time.t2 << 2) | (t3p >> 1);
 		msu_param->u8Buffer[3] = (t3p & 1);
 		break;
-	case GsmL1_Sapi_Bcch:
-		/* get them from bts->si_buf[] */
-		si = bts_sysinfo_get(bts, &g_time);
-		if (si)
-			memcpy(msu_param->u8Buffer, si, GSM_MACBLOCK_LEN);
-		else
-			memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
-		break;
-	case GsmL1_Sapi_Sacch:
-		/* resolve the L2 entity using rts_ind->hLayer2 */
-		lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2);
-		le = &lchan->lapdm_ch.lapdm_acch;
-		/* if the DSP is taking care of power control
-		 * (ul_power_target==0), then this value will be
-		 * overridden. */
-		msu_param->u8Buffer[0] = lchan->ms_power;
-		rc = lapdm_phsap_dequeue_prim(le, &pp);
-		if (rc < 0) {
-			/* No SACCH data from LAPDM pending, send SACCH filling */
-			uint8_t *si = lchan_sacch_get(lchan, &g_time);
-			if (si) {
-				/* The +2 is empty space where the DSP inserts the L1 hdr */
-				memcpy(msu_param->u8Buffer+2, si, GSM_MACBLOCK_LEN-2);
-			} else
-				memcpy(msu_param->u8Buffer+2, fill_frame, GSM_MACBLOCK_LEN-2);
-		} else {
-			/* The +2 is empty space where the DSP inserts the L1 hdr */
-			memcpy(msu_param->u8Buffer+2, pp.oph.msg->data, GSM_MACBLOCK_LEN-2);
-			msgb_free(pp.oph.msg);
-		}
-		break;
-	case GsmL1_Sapi_Sdcch:
-		/* resolve the L2 entity using rts_ind->hLayer2 */
-		lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2);
-		le = &lchan->lapdm_ch.lapdm_dcch;
-		rc = lapdm_phsap_dequeue_prim(le, &pp);
-		if (rc < 0)
-			memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
-		else {
-			memcpy(msu_param->u8Buffer, pp.oph.msg->data, GSM_MACBLOCK_LEN);
-			/* check if it is a RR CIPH MODE CMD. if yes, enable RX ciphering */
-			check_for_ciph_cmd(fl1, pp.oph.msg, lchan);
-			msgb_free(pp.oph.msg);
-		}
-		break;
-	case GsmL1_Sapi_Agch:
-		/* special queue of messages from IMM ASS CMD */
-		{
-			struct msgb *msg = bts_agch_dequeue(bts);
-			if (!msg)
-				memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
-			else {
-				memcpy(msu_param->u8Buffer, msg->data, msg->len);
-				msgb_free(msg);
-			}
-		}
-		break;
-	case GsmL1_Sapi_Pch:
-		rc = paging_gen_msg(btsb->paging_state, msu_param->u8Buffer, &g_time);
-		break;
-	case GsmL1_Sapi_TchF:
-	case GsmL1_Sapi_TchH:
-		/* only hit in case we have a RTP underflow, as real TCH
-		 * frames are handled way above */
-		goto empty_frame;
-		break;
-	case GsmL1_Sapi_FacchF:
-	case GsmL1_Sapi_FacchH:
-		/* resolve the L2 entity using rts_ind->hLayer2 */
-		lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2);
-		le = &lchan->lapdm_ch.lapdm_dcch;
-		rc = lapdm_phsap_dequeue_prim(le, &pp);
-		if (rc < 0)
-			goto empty_frame;
-		else {
-			memcpy(msu_param->u8Buffer, pp.oph.msg->data, GSM_MACBLOCK_LEN);
-			/* check if it is a RR CIPH MODE CMD. if yes, enable RX ciphering */
-			check_for_ciph_cmd(fl1, pp.oph.msg, lchan);
-			msgb_free(pp.oph.msg);
-		}
-		break;
 	case GsmL1_Sapi_Prach:
 		goto empty_frame;
 		break;
@@ -548,13 +689,12 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
 		memcpy(msu_param->u8Buffer, fill_frame, GSM_MACBLOCK_LEN);
 		break;
 	}
-tx:
-
-	tx_to_gsmtap(fl1, resp_msg);
 
+tx:
 	/* transmit */
 	osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], resp_msg);
 
+	msgb_free(l1p_msg);
 	return 0;
 
 empty_frame:
@@ -564,143 +704,38 @@ empty_frame:
 	goto tx;
 }
 
-static int handle_mph_time_ind(struct femtol1_hdl *fl1,
-				GsmL1_MphTimeInd_t *time_ind)
-{
-	struct gsm_bts_trx *trx = fl1->priv;
-	struct gsm_bts *bts = trx->bts;
-	struct gsm_bts_role_bts *btsb = bts->role;
-
-	int frames_expired = time_ind->u32Fn - fl1->gsm_time.fn;
-
-	/* update time on PCU interface */
-	pcu_tx_time_ind(time_ind->u32Fn);
-
-	/* Update our data structures with the current GSM time */
-	gsm_fn2gsmtime(&fl1->gsm_time, time_ind->u32Fn);
-
-	/* check if the measurement period of some lchan has ended
-	 * and pre-compute the respective measurement */
-	trx_meas_check_compute(fl1->priv, time_ind->u32Fn -1);
-
-	/* increment the primitive count for the alive timer */
-	fl1->alive_prim_cnt++;
-
-	/* increment number of RACH slots that have passed by since the
-	 * last time indication */
-	if (trx == bts->c0) {
-		unsigned int num_rach_per_frame;
-		/* 27 / 51 taken from TS 05.01 Figure 3 */
-		if (bts->c0->ts[0].pchan == GSM_PCHAN_CCCH_SDCCH4)
-			num_rach_per_frame = 27;
-		else
-			num_rach_per_frame = 51;
-
-		btsb->load.rach.total += frames_expired * num_rach_per_frame;
-	}
-
-	return 0;
-}
-
-/* determine LAPDm entity inside LAPDm channel for given L1 sapi */
-static struct lapdm_entity *le_by_l1_sapi(struct lapdm_channel *lc, GsmL1_Sapi_t sapi)
-{
-	switch (sapi) {
-	case GsmL1_Sapi_Sacch:
-		return &lc->lapdm_acch;
-	default:
-		return &lc->lapdm_dcch;
-	}
-}
-
-static uint8_t gen_link_id(GsmL1_Sapi_t l1_sapi, uint8_t lapdm_sapi)
-{
-	uint8_t c_bits = 0;
-
-	if (l1_sapi == GsmL1_Sapi_Sacch)
-		c_bits = 0x40;
-
-	return c_bits | (lapdm_sapi & 7);
-}
-
-static void dump_meas_res(int ll, GsmL1_MeasParam_t *m)
-{
-	LOGPC(DL1C, ll, ", Meas: RSSI %-3.2f dBm,  Qual %-3.2f dB,  "
-		"BER %-3.2f,  Timing %d\n", m->fRssi, m->fLinkQuality,
-		m->fBer, m->i16BurstTiming);
-}
-
-static int process_meas_res(struct gsm_lchan *lchan, GsmL1_MeasParam_t *m)
-{
-	struct bts_ul_meas ulm;
-
-	/* in the GPRS case we are not interested in measurement
-	 * processing.  The PCU will take care of it */
-	if (lchan->type == GSM_LCHAN_PDTCH)
-		return 0;
-
-	ulm.ta_offs_qbits = m->i16BurstTiming;
-	ulm.ber10k = (unsigned int) (m->fBer * 100);
-	ulm.inv_rssi = (uint8_t) (m->fRssi * -1);
-
-	return lchan_new_ul_meas(lchan, &ulm);
-}
-
-/* process radio link timeout counter S */
-static void radio_link_timeout(struct gsm_lchan *lchan, int bad_frame)
-{
-	struct gsm_bts_role_bts *btsb = lchan->ts->trx->bts->role;
-
-	/* if link loss criterion already reached */
-	if (lchan->s == 0) {
-		DEBUGP(DMEAS, "%s radio link counter S already 0.\n",
-			gsm_lchan_name(lchan));
-		return;
-	}
-
-	if (bad_frame) {
-		/* count down radio link counter S */
-		lchan->s--;
-		DEBUGP(DMEAS, "%s counting down radio link counter S=%d\n",
-			gsm_lchan_name(lchan), lchan->s);
-		if (lchan->s == 0)
-			rsl_tx_conn_fail(lchan, RSL_ERR_RADIO_LINK_FAIL);
-		return;
-	}
-
-	if (lchan->s < btsb->radio_link_timeout) {
-		/* count up radio link counter S */
-		lchan->s += 2;
-		if (lchan->s > btsb->radio_link_timeout)
-			lchan->s = btsb->radio_link_timeout;
-		DEBUGP(DMEAS, "%s counting up radio link counter S=%d\n",
-			gsm_lchan_name(lchan), lchan->s);
-	}
-}
-
 static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_ind,
 			      struct msgb *l1p_msg)
 {
 	struct gsm_bts_trx *trx = fl1->priv;
-	struct osmo_phsap_prim pp;
-	struct gsm_lchan *lchan;
-	struct lapdm_entity *le;
-	struct msgb *msg;
-	int rc = 0;
-
-	ul_to_gsmtap(fl1, l1p_msg);
+	struct osmo_phsap_prim *l1sap;
+	uint8_t chan_nr, link_id;
+	uint32_t fn;
+	uint8_t *data, len;
+	int rc;
 
-	lchan = l1if_hLayer_to_lchan(fl1->priv, data_ind->hLayer2);
-	if (!lchan) {
-		LOGP(DL1C, LOGL_ERROR, "unable to resolve lchan by hLayer2\n");
-		return -ENODEV;
+	/* chan_nr and link_id */
+	chan_nr = chan_nr_by_sapi(trx->ts[data_ind->u8Tn].pchan, data_ind->sapi,
+		data_ind->subCh, data_ind->u8Tn, data_ind->u32Fn);
+	if (!chan_nr) {
+		LOGP(DL1C, LOGL_ERROR, "PH-DATA-INDICATION for unknown sapi "
+			"%d\n", data_ind->sapi);
+		return ENOTSUP;
 	}
+	fn = data_ind->u32Fn;
+	if (data_ind->sapi == GsmL1_Sapi_Sacch)
+		link_id = 0x40;
+	else
+		link_id = 0;
 
-	process_meas_res(lchan, &data_ind->measParam);
+	/* uplink measurement */
+	process_meas_res(trx, chan_nr, &data_ind->measParam);
 
 	if (data_ind->measParam.fLinkQuality < fl1->min_qual_norm
-	 && data_ind->msgUnitParam.u8Size != 0)
-		return 0;
+	 && data_ind->msgUnitParam.u8Size != 0) {
+		msgb_free(l1p_msg);
+ 		return 0;
+	}
 
 	DEBUGP(DL1C, "Rx PH-DATA.ind %s (hL2 %08x): %s",
 		get_value_string(femtobts_l1sapi_names, data_ind->sapi),
@@ -709,165 +744,85 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
 			     data_ind->msgUnitParam.u8Size));
 	dump_meas_res(LOGL_DEBUG, &data_ind->measParam);
 
-	switch (data_ind->sapi) {
-	case GsmL1_Sapi_Sacch:
-		radio_link_timeout(lchan, (data_ind->msgUnitParam.u8Size == 0));
-		if (data_ind->msgUnitParam.u8Size == 0)
-			break;
-		/* save the SACCH L1 header in the lchan struct for RSL MEAS RES */
-		if (data_ind->msgUnitParam.u8Size < 2) {
-			LOGP(DL1C, LOGL_NOTICE, "SACCH with size %u<2 !?!\n",
-				data_ind->msgUnitParam.u8Size);
-			break;
-		}
-		/* Some brilliant engineer decided that the ordering of
-		 * fields on the Um interface is different from the
-		 * order of fields in RLS. See TS 04.04 (Chapter 7.2)
-		 * vs. TS 08.58 (Chapter 9.3.10). */
-		lchan->meas.l1_info[0] = data_ind->msgUnitParam.u8Buffer[0] << 3;
-		lchan->meas.l1_info[0] |= ((data_ind->msgUnitParam.u8Buffer[0] >> 5) & 1) << 2;
-		lchan->meas.l1_info[1] = data_ind->msgUnitParam.u8Buffer[1];
-		lchan->meas.flags |= LC_UL_M_F_L1_VALID;
-		/* fall-through */
-	case GsmL1_Sapi_Sdcch:
-	case GsmL1_Sapi_FacchF:
-	case GsmL1_Sapi_FacchH:
-		/* Check and Re-check for the SACCH */
-		if (data_ind->msgUnitParam.u8Size == 0) {
-			LOGP(DL1C, LOGL_NOTICE, "%s %s data is null.\n",
-				gsm_lchan_name(lchan),
-				get_value_string(femtobts_l1sapi_names, data_ind->sapi));
-			break;
-		}
-
-		/* if this is the first valid message after enabling Rx
-		 * decryption, we have to enable Tx encryption */
-		if (lchan->ciph_state == LCHAN_CIPH_RX_CONF) {
-			/* HACK: check if it's an I frame, in order to
-			 * ignore some still buffered/queued UI frames received
-			 * before decryption was enabled */
-			if (data_ind->msgUnitParam.u8Buffer[0] == 0x01 &&
-			    (data_ind->msgUnitParam.u8Buffer[1] & 0x01) == 0) {
-				l1if_set_ciphering(fl1, lchan, 1);
-				lchan->ciph_state = LCHAN_CIPH_TXRX_REQ;
-			}
-		}
-
-		/* SDCCH, SACCH and FACCH all go to LAPDm */
-		le = le_by_l1_sapi(&lchan->lapdm_ch, data_ind->sapi);
-		/* allocate and fill LAPDm primitive */
-		msg = msgb_alloc_headroom(128, 64, "PH-DATA.ind");
-		osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
-				PRIM_OP_INDICATION, msg);
-
-		/* copy over actual MAC block */
-		msg->l2h = msgb_put(msg, data_ind->msgUnitParam.u8Size);
-		memcpy(msg->l2h, data_ind->msgUnitParam.u8Buffer,
-			data_ind->msgUnitParam.u8Size);
-
-		/* LAPDm requires those... */
-		pp.u.data.chan_nr = gsm_lchan2chan_nr(lchan);
-		pp.u.data.link_id = gen_link_id(data_ind->sapi, 0);
-
-		/* feed into the LAPDm code of libosmogsm */
-		rc = lapdm_phsap_up(&pp.oph, le);
-		break;
-	case GsmL1_Sapi_TchF:
-	case GsmL1_Sapi_TchH:
-		/* TCH speech frame handling */
-		rc = l1if_tch_rx(lchan, l1p_msg);
-		break;
-	case GsmL1_Sapi_Pdtch:
-	case GsmL1_Sapi_Pacch:
-		/* drop incomplete UL block */
-		if (!data_ind->msgUnitParam.u8Size
-		 || data_ind->msgUnitParam.u8Buffer[0]
-			!= GsmL1_PdtchPlType_Full)
-			break;
-		/* PDTCH / PACCH frame handling */
-		rc = pcu_tx_data_ind(&trx->ts[data_ind->u8Tn], 0,
-			data_ind->u32Fn, data_ind->u16Arfcn,
-			data_ind->u8BlockNbr,
-			data_ind->msgUnitParam.u8Buffer + 1,
-			data_ind->msgUnitParam.u8Size - 1,
-			(int8_t) (data_ind->measParam.fRssi));
-		break;
-	case GsmL1_Sapi_Ptcch:
-		/* PTCCH frame handling */
-		rc = pcu_tx_data_ind(&trx->ts[data_ind->u8Tn], 1,
-			data_ind->u32Fn, data_ind->u16Arfcn,
-			data_ind->u8BlockNbr,
-			data_ind->msgUnitParam.u8Buffer,
-			data_ind->msgUnitParam.u8Size,
-			(int8_t) (data_ind->measParam.fRssi));
-		break;
-	default:
-		LOGP(DL1C, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
-			get_value_string(femtobts_l1sapi_names, data_ind->sapi));
-		break;
-	}
-
-	return rc;
+	/* check for TCH */
+	if (data_ind->sapi == GsmL1_Sapi_TchF
+	 || data_ind->sapi == GsmL1_Sapi_TchH) {
+ 		/* TCH speech frame handling */
+		return l1if_tch_rx(trx, chan_nr, l1p_msg);
+ 	}
+
+	/* get data pointer and length */
+	data = data_ind->msgUnitParam.u8Buffer;
+	len = data_ind->msgUnitParam.u8Size;
+	/* pull lower header part before data */
+	msgb_pull(l1p_msg, data - l1p_msg->data);
+	/* trim remaining data to it's size, to get rid of upper header part */
+	rc = msgb_trim(l1p_msg, len);
+	if (rc < 0)
+		MSGB_ABORT(l1p_msg, "No room for primitive data\n");
+	l1p_msg->l2h = l1p_msg->data;
+	/* push new l1 header */
+	l1p_msg->l1h = msgb_push(l1p_msg, sizeof(*l1sap));
+	/* fill header */
+	l1sap = msgb_l1sap_prim(l1p_msg);
+	osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_INDICATION,
+		l1p_msg);
+	l1sap->u.data.link_id = link_id;
+	l1sap->u.data.chan_nr = chan_nr;
+	l1sap->u.data.fn = fn;
+
+	return l1sap_up(trx, l1sap);
 }
 
 
-static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind)
+static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind,
+			    struct msgb *l1p_msg)
 {
 	struct gsm_bts_trx *trx = fl1->priv;
 	struct gsm_bts *bts = trx->bts;
 	struct gsm_bts_role_bts *btsb = bts->role;
-	struct osmo_phsap_prim pp;
-	struct lapdm_channel *lc;
-	uint8_t acc_delay;
+	struct osmo_phsap_prim *l1sap;
+	uint32_t fn;
+	uint8_t ra, acc_delay;
+	int rc;
 
 	/* increment number of busy RACH slots, if required */
 	if (trx == bts->c0 &&
 	    ra_ind->measParam.fRssi >= btsb->load.rach.busy_thresh)
 		btsb->load.rach.busy++;
 
-	if (ra_ind->measParam.fLinkQuality < fl1->min_qual_rach)
+	if (ra_ind->measParam.fLinkQuality < fl1->min_qual_rach) {
+		msgb_free(l1p_msg);
 		return 0;
+	}
 
-	/* increment number of RACH slots with valid RACH burst */
-	if (trx == bts->c0)
-		btsb->load.rach.access++;
-
-	DEBUGP(DL1C, "Rx PH-RA.ind");
 	dump_meas_res(LOGL_DEBUG, &ra_ind->measParam);
 
-	lc = get_lapdm_chan_by_hl2(fl1->priv, ra_ind->hLayer2);
-	if (!lc) {
-		LOGP(DL1C, LOGL_ERROR, "unable to resolve LAPD channel by hLayer2\n");
-		return -ENODEV;
+	if (ra_ind->msgUnitParam.u8Size != 1) {
+		LOGP(DL1C, LOGL_ERROR, "PH-RACH-INDICATION has %d bits\n",
+			ra_ind->sapi);
+		msgb_free(l1p_msg);
+		return 0;
 	}
 
+	fn = ra_ind->u32Fn;
+	ra = ra_ind->msgUnitParam.u8Buffer[0];
 	/* check for under/overflow / sign */
 	if (ra_ind->measParam.i16BurstTiming < 0)
 		acc_delay = 0;
 	else
 		acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
-	if (acc_delay > btsb->max_ta) {
-		LOGP(DL1C, LOGL_INFO, "ignoring RACH request %u > max_ta(%u)\n",
-		     acc_delay, btsb->max_ta);
-		return 0;
-	}
-
-	/* check for packet access */
-	if (trx == bts->c0
-	 && (ra_ind->msgUnitParam.u8Buffer[0] & 0xf0) == 0x70) {
-		LOGP(DL1C, LOGL_INFO, "RACH for packet access\n");
-		return pcu_tx_rach_ind(bts, ra_ind->measParam.i16BurstTiming,
-			ra_ind->msgUnitParam.u8Buffer[0], ra_ind->u32Fn);
-	}
-
-	osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_RACH,
-			PRIM_OP_INDICATION, NULL);
-
-	pp.u.rach_ind.ra = ra_ind->msgUnitParam.u8Buffer[0];
-	pp.u.rach_ind.fn = ra_ind->u32Fn;
-	pp.u.rach_ind.acc_delay = acc_delay;
-
-	return lapdm_phsap_up(&pp.oph, &lc->lapdm_dcch);
+	rc = msgb_trim(l1p_msg, sizeof(*l1sap));
+	if (rc < 0)
+		MSGB_ABORT(l1p_msg, "No room for primitive data\n");
+	l1sap = msgb_l1sap_prim(l1p_msg);
+	osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION,
+		l1p_msg);
+	l1sap->u.rach_ind.ra = ra;
+	l1sap->u.rach_ind.acc_delay = acc_delay;
+	l1sap->u.rach_ind.fn = fn;
+
+	return l1sap_up(trx, l1sap);
 }
 
 /* handle any random indication from the L1 */
@@ -885,21 +840,19 @@ static int l1if_handle_ind(struct femtol1_hdl *fl1, struct msgb *msg)
 	case GsmL1_PrimId_PhConnectInd:
 		break;
 	case GsmL1_PrimId_PhReadyToSendInd:
-		rc = handle_ph_readytosend_ind(fl1, &l1p->u.phReadyToSendInd);
+		return handle_ph_readytosend_ind(fl1, &l1p->u.phReadyToSendInd, msg);
 		break;
 	case GsmL1_PrimId_PhDataInd:
-		rc = handle_ph_data_ind(fl1, &l1p->u.phDataInd, msg);
+		return handle_ph_data_ind(fl1, &l1p->u.phDataInd, msg);
 		break;
 	case GsmL1_PrimId_PhRaInd:
-		rc = handle_ph_ra_ind(fl1, &l1p->u.phRaInd);
+		return handle_ph_ra_ind(fl1, &l1p->u.phRaInd, msg);
 		break;
 	default:
 		break;
 	}
 
-	/* Special return value '1' means: do not free */
-	if (rc != 1)
-		msgb_free(msg);
+	msgb_free(msg);
 
 	return rc;
 }
@@ -1203,46 +1156,6 @@ int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags)
 	return osmo_wqueue_enqueue(&hdl->write_q[MQ_SYS_WRITE], msg);
 }
 
-/* send packet data request to L1 */
-int l1if_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
-	uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
-{
-	struct gsm_bts_trx *trx = ts->trx;
-	struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
-	struct msgb *msg;
-	GsmL1_Prim_t *l1p;
-	GsmL1_PhDataReq_t *data_req;
-	GsmL1_MsgUnitParam_t *msu_param;
-	struct gsm_time g_time;
-
-	gsm_fn2gsmtime(&g_time, fn);
-
-	DEBUGP(DL1P, "TX packet data %02u/%02u/%02u is_ptcch=%d trx=%d ts=%d "
-		"block_nr=%d, arfcn=%d, len=%d\n", g_time.t1, g_time.t2,
-		g_time.t3, is_ptcch, ts->trx->nr, ts->nr, block_nr, arfcn, len);
-
-	msg = l1p_msgb_alloc();
-	l1p = msgb_l1prim(msg);
-	l1p->id = GsmL1_PrimId_PhDataReq;
-	data_req = &l1p->u.phDataReq;
-	data_req->hLayer1 = fl1h->hLayer1;
-	data_req->sapi = (is_ptcch) ? GsmL1_Sapi_Ptcch : GsmL1_Sapi_Pdtch;
-	data_req->subCh = GsmL1_SubCh_NA;
-	data_req->u8BlockNbr = block_nr;
-	data_req->u8Tn = ts->nr;
-	data_req->u32Fn = fn;
-	msu_param = &data_req->msgUnitParam;
-	msu_param->u8Size = len;
-	memcpy(msu_param->u8Buffer, data, len);
-
-	tx_to_gsmtap(fl1h, msg);
-
-	/* transmit */
-	osmo_wqueue_enqueue(&fl1h->write_q[MQ_L1_WRITE], msg);
-
-	return 0;
-}
-
 struct femtol1_hdl *l1if_open(void *priv)
 {
 	struct femtol1_hdl *fl1h;
@@ -1290,10 +1203,6 @@ struct femtol1_hdl *l1if_open(void *priv)
 		return NULL;
 	}
 
-	fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
-	if (fl1h->gsmtap)
-		gsmtap_source_add_sink(fl1h->gsmtap);
-
 	return fl1h;
 }
 
@@ -1304,8 +1213,3 @@ int l1if_close(struct femtol1_hdl *fl1h)
 	return 0;
 }
 
-/* temporary stub to make this patch compile */
-int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
-{
-	return -ENOTSUP;
-}
diff --git a/src/osmo-bts-sysmo/l1_if.h b/src/osmo-bts-sysmo/l1_if.h
index 7947ea2..388f1fb 100644
--- a/src/osmo-bts-sysmo/l1_if.h
+++ b/src/osmo-bts-sysmo/l1_if.h
@@ -50,9 +50,6 @@ struct femtol1_hdl {
 	char *calib_path;
 	struct llist_head wlc_list;
 
-	struct gsmtap_inst *gsmtap;
-	uint32_t gsmtap_sapi_mask;
-
 	void *priv;			/* user reference */
 
 	struct osmo_timer_list alive_timer;
@@ -97,7 +94,9 @@ uint32_t l1if_lchan_to_hLayer(struct gsm_lchan *lchan);
 struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer);
 
 /* tch.c */
-int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
+void l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
+	const uint8_t *rtp_pl, unsigned int rtp_pl_len);
+int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg);
 int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
 struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
 
@@ -106,6 +105,12 @@ int l1if_set_ciphering(struct femtol1_hdl *fl1h,
 			  struct gsm_lchan *lchan,
 			  int dir_downlink);
 
+/* channel control */
+int l1if_rsl_chan_act(struct gsm_lchan *lchan);
+int l1if_rsl_chan_rel(struct gsm_lchan *lchan);
+int l1if_rsl_deact_sacch(struct gsm_lchan *lchan);
+int l1if_rsl_mode_modify(struct gsm_lchan *lchan);
+
 /* calibration loading */
 int calib_load(struct femtol1_hdl *fl1h);
 
diff --git a/src/osmo-bts-sysmo/main.c b/src/osmo-bts-sysmo/main.c
index 7e7f761..465f99d 100644
--- a/src/osmo-bts-sysmo/main.c
+++ b/src/osmo-bts-sysmo/main.c
@@ -45,6 +45,7 @@
 #include <osmo-bts/vty.h>
 #include <osmo-bts/bts_model.h>
 #include <osmo-bts/pcu_if.h>
+#include <osmo-bts/l1sap.h>
 
 #define SYSMOBTS_RF_LOCK_PATH	"/var/lock/bts_rf_lock"
 
@@ -254,6 +255,7 @@ int main(int argc, char **argv)
 
 	bts_log_init(NULL);
 
+	bts = gsm_bts_alloc(tall_bts_ctx);
 	vty_init(&bts_vty_info);
 	bts_vty_init(bts, &bts_log_info);
 
@@ -271,7 +273,6 @@ int main(int argc, char **argv)
 		}
 	}
 
-	bts = gsm_bts_alloc(tall_bts_ctx);
 	if (bts_init(bts) < 0) {
 		fprintf(stderr, "unable to to open bts\n");
 		exit(1);
diff --git a/src/osmo-bts-sysmo/oml.c b/src/osmo-bts-sysmo/oml.c
index aeddad7..399576e 100644
--- a/src/osmo-bts-sysmo/oml.c
+++ b/src/osmo-bts-sysmo/oml.c
@@ -35,11 +35,27 @@
 #include <osmo-bts/amr.h>
 #include <osmo-bts/bts.h>
 #include <osmo-bts/bts_model.h>
+#include <osmo-bts/l1sap.h>
 
 #include "l1_if.h"
 #include "femtobts.h"
 #include "utils.h"
 
+static int mph_info_chan_confirm(struct gsm_lchan *lchan,
+			enum osmo_mph_info_type type, uint8_t cause)
+{
+	struct osmo_phsap_prim l1sap;
+
+	memset(&l1sap, 0, sizeof(l1sap));
+	osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM,
+		NULL);
+	l1sap.u.info.type = type;
+	l1sap.u.info.u.act_cnf.chan_nr = gsm_lchan2chan_nr(lchan);
+	l1sap.u.info.u.act_cnf.cause = cause;
+
+	return l1sap_up(lchan->ts->trx, &l1sap);
+}
+
 enum sapi_cmd_type {
 	SAPI_CMD_ACTIVATE,
 	SAPI_CMD_CONFIG_CIPHERING,
@@ -858,7 +874,7 @@ static int sapi_activate_cb(struct gsm_lchan *lchan, int status)
 	if (status != GsmL1_Status_Success) {
 		lchan_set_state(lchan, LCHAN_S_BROKEN);
 		sapi_clear_queue(&lchan->sapi_cmds);
-		rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL);
+		mph_info_chan_confirm(lchan, PRIM_INFO_ACTIVATE, RSL_ERR_EQUIPMENT_FAIL);
 		return -1;
 	}
 
@@ -869,7 +885,7 @@ static int sapi_activate_cb(struct gsm_lchan *lchan, int status)
 		return 0;
 
 	lchan_set_state(lchan, LCHAN_S_ACTIVE);
-	rsl_tx_chan_act_ack(lchan);
+	mph_info_chan_confirm(lchan, PRIM_INFO_ACTIVATE, 0);
 
 	/* set the initial ciphering parameters for both directions */
 	l1if_set_ciphering(fl1h, lchan, 0);
@@ -891,7 +907,6 @@ static void enqueue_sapi_act_cmd(struct gsm_lchan *lchan, int sapi, int dir)
 
 int lchan_activate(struct gsm_lchan *lchan, enum gsm_lchan_state lchan_state)
 {
-	struct gsm_bts_role_bts *btsb = lchan->ts->trx->bts->role;
 	struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
 	const struct lchan_sapis *s4l = &sapis_for_lchan[lchan->type];
 	unsigned int i;
@@ -920,8 +935,6 @@ int lchan_activate(struct gsm_lchan *lchan, enum gsm_lchan_state lchan_state)
 #warning "FIXME: Should this be in sapi_activate_cb?"
 	lchan_init_lapdm(lchan);
 
-	lchan->s = btsb->radio_link_timeout;
-
 	return 0;
 }
 
@@ -1177,7 +1190,7 @@ int l1if_set_ciphering(struct femtol1_hdl *fl1h,
 	return 0;
 }
 
-int bts_model_rsl_mode_modify(struct gsm_lchan *lchan)
+int l1if_rsl_mode_modify(struct gsm_lchan *lchan)
 {
 	if (lchan->state != LCHAN_S_ACTIVE)
 		return -1;
@@ -1286,7 +1299,7 @@ static int sapi_deactivate_cb(struct gsm_lchan *lchan, int status)
 			gsm_lchan_name(lchan));
 		lchan_set_state(lchan, LCHAN_S_BROKEN);
 		sapi_clear_queue(&lchan->sapi_cmds);
-		rsl_tx_rf_rel_ack(lchan);
+		mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
 		return -1;
 	}
 
@@ -1298,7 +1311,7 @@ static int sapi_deactivate_cb(struct gsm_lchan *lchan, int status)
 		return 0;
 
 	lchan_set_state(lchan, LCHAN_S_NONE);
-	rsl_tx_rf_rel_ack(lchan);
+	mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
 	return 0;
 }
 
@@ -1358,7 +1371,7 @@ static int lchan_deactivate_sapis(struct gsm_lchan *lchan)
 		LOGP(DL1C, LOGL_ERROR, "%s all SAPIs already released?\n",
 			gsm_lchan_name(lchan));
 		lchan_set_state(lchan, LCHAN_S_BROKEN);
-		rsl_tx_rf_rel_ack(lchan);
+		mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
 	}
 
 	return res;
@@ -1398,13 +1411,6 @@ static int lchan_deactivate_sacch(struct gsm_lchan *lchan)
 	return 0;
 }
 
-struct gsm_time *bts_model_get_time(struct gsm_bts *bts)
-{
-	struct femtol1_hdl *fl1h = trx_femtol1_hdl(bts->c0);
-
-	return &fl1h->gsm_time;
-}
-
 /* callback from OML */
 int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
 			struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
@@ -1457,17 +1463,17 @@ int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
 	mo->nm_state.administrative = adm_state;
 	return oml_mo_statechg_ack(mo);
 }
-int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp)
+
+int l1if_rsl_chan_act(struct gsm_lchan *lchan)
 {
 	//uint8_t mode = *TLVP_VAL(tp, RSL_IE_CHAN_MODE);
 	//uint8_t type = *TLVP_VAL(tp, RSL_IE_ACT_TYPE);
 
-	lchan->sacch_deact = 0;
 	lchan_activate(lchan, LCHAN_S_ACT_REQ);
 	return 0;
 }
 
-int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
+int l1if_rsl_chan_rel(struct gsm_lchan *lchan)
 {
 	/* A duplicate RF Release Request, ignore it */
 	if (lchan->state == LCHAN_S_REL_REQ)
@@ -1476,7 +1482,7 @@ int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
 	return 0;
 }
 
-int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan)
+int l1if_rsl_deact_sacch(struct gsm_lchan *lchan)
 {
 	/* Only de-activate the SACCH if the lchan is active */
 	if (lchan->state != LCHAN_S_ACTIVE)
diff --git a/src/osmo-bts-sysmo/sysmobts_vty.c b/src/osmo-bts-sysmo/sysmobts_vty.c
index 61deda4..998ed80 100644
--- a/src/osmo-bts-sysmo/sysmobts_vty.c
+++ b/src/osmo-bts-sysmo/sysmobts_vty.c
@@ -83,34 +83,6 @@ DEFUN(cfg_bts_no_auto_band, cfg_bts_no_auto_band_cmd,
 	return CMD_SUCCESS;
 }
 
-DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
-	"HIDDEN", "HIDDEN")
-{
-	struct gsm_bts_trx *trx = vty->index;
-	struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
-	int sapi;
-
-	sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
-
-	fl1h->gsmtap_sapi_mask |= (1 << sapi);
-
-	return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_no_gsmtap_sapi, cfg_trx_no_gsmtap_sapi_cmd,
-	"HIDDEN", "HIDDEN")
-{
-	struct gsm_bts_trx *trx = vty->index;
-	struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
-	int sapi;
-
-	sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
-
-	fl1h->gsmtap_sapi_mask &= ~(1 << sapi);
-
-	return CMD_SUCCESS;
-}
-
 DEFUN(cfg_trx_clkcal_def, cfg_trx_clkcal_def_cmd,
 	"clock-calibration default",
 	"Set the clock calibration value\n" "Default Clock DAC value\n")
@@ -397,44 +369,6 @@ DEFUN(set_tx_power, set_tx_power_cmd,
 	return CMD_SUCCESS;
 }
 
-DEFUN(loopback, loopback_cmd,
-	"trx <0-0> <0-7> loopback <0-1>",
-	TRX_STR
-	"Timeslot number\n"
-	"Set TCH loopback\n"
-	"Logical Channel Number\n")
-{
-	int trx_nr = atoi(argv[0]);
-	int ts_nr = atoi(argv[1]);
-	int lchan_nr = atoi(argv[2]);
-	struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
-	struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
-	struct gsm_lchan *lchan = &ts->lchan[lchan_nr];
-
-	lchan->loopback = 1;
-
-	return CMD_SUCCESS;
-}
-
-DEFUN(no_loopback, no_loopback_cmd,
-	"no trx <0-0> <0-7> loopback <0-1>",
-	NO_STR TRX_STR
-	"Timeslot number\n"
-	"Set TCH loopback\n"
-	"Logical Channel Number\n")
-{
-	int trx_nr = atoi(argv[0]);
-	int ts_nr = atoi(argv[1]);
-	int lchan_nr = atoi(argv[2]);
-	struct gsm_bts_trx *trx = gsm_bts_trx_num(vty_bts, trx_nr);
-	struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
-	struct gsm_lchan *lchan = &ts->lchan[lchan_nr];
-
-	lchan->loopback = 0;
-
-	return CMD_SUCCESS;
-}
-
 
 void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
 {
@@ -447,7 +381,6 @@ void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
 void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
 {
 	struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
-	int i;
 
 	vty_out(vty, "  clock-calibration %d%s", fl1h->clk_cal,
 			VTY_NEWLINE);
@@ -463,14 +396,6 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
 		VTY_NEWLINE);
 	vty_out(vty, "  min-qual-norm %.0f%s", fl1h->min_qual_norm * 10.0f,
 		VTY_NEWLINE);
-
-	for (i = 0; i < 32; i++) {
-		if (fl1h->gsmtap_sapi_mask & (1 << i)) {
-			const char *name = get_value_string(femtobts_l1sapi_names, i);
-			vty_out(vty, "  gsmtap-sapi %s%s", osmo_str_tolower(name),
-				VTY_NEWLINE);
-		}
-	}
 }
 
 int bts_model_vty_init(struct gsm_bts *bts)
@@ -492,20 +417,6 @@ int bts_model_vty_init(struct gsm_bts *bts)
 						NO_STR TRX_STR DSP_TRACE_F_STR,
 						"\n", "", 0);
 
-	cfg_trx_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
-						"gsmtap-sapi (",
-						"|",")", VTY_DO_LOWER);
-	cfg_trx_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
-						"GSMTAP SAPI\n",
-						"\n", "", 0);
-
-	cfg_trx_no_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
-						"no gsmtap-sapi (",
-						"|",")", VTY_DO_LOWER);
-	cfg_trx_no_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
-						NO_STR "GSMTAP SAPI\n",
-						"\n", "", 0);
-
 	install_element_ve(&show_dsp_trace_f_cmd);
 	install_element_ve(&show_sys_info_cmd);
 	install_element_ve(&show_trx_clksrc_cmd);
@@ -515,9 +426,6 @@ int bts_model_vty_init(struct gsm_bts *bts)
 	install_element(ENABLE_NODE, &activate_lchan_cmd);
 	install_element(ENABLE_NODE, &set_tx_power_cmd);
 
-	install_element(ENABLE_NODE, &loopback_cmd);
-	install_element(ENABLE_NODE, &no_loopback_cmd);
-
 	install_element(BTS_NODE, &cfg_bts_auto_band_cmd);
 	install_element(BTS_NODE, &cfg_bts_no_auto_band_cmd);
 
@@ -525,8 +433,6 @@ int bts_model_vty_init(struct gsm_bts *bts)
 	install_element(TRX_NODE, &cfg_trx_clkcal_def_cmd);
 	install_element(TRX_NODE, &cfg_trx_clksrc_cmd);
 	install_element(TRX_NODE, &cfg_trx_cal_path_cmd);
-	install_element(TRX_NODE, &cfg_trx_gsmtap_sapi_cmd);
-	install_element(TRX_NODE, &cfg_trx_no_gsmtap_sapi_cmd);
 	install_element(TRX_NODE, &cfg_trx_ul_power_target_cmd);
 	install_element(TRX_NODE, &cfg_trx_min_qual_rach_cmd);
 	install_element(TRX_NODE, &cfg_trx_min_qual_norm_cmd);
diff --git a/src/osmo-bts-sysmo/tch.c b/src/osmo-bts-sysmo/tch.c
index c6c782f..da62c84 100644
--- a/src/osmo-bts-sysmo/tch.c
+++ b/src/osmo-bts-sysmo/tch.c
@@ -39,6 +39,7 @@
 #include <osmo-bts/bts.h>
 #include <osmo-bts/gsm_data.h>
 #include <osmo-bts/measurement.h>
+#include <osmo-bts/l1sap.h>
 
 #include <sysmocom/femtobts/superfemto.h>
 #include <sysmocom/femtobts/gsml1prim.h>
@@ -416,7 +417,7 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
 
 #define RTP_MSGB_ALLOC_SIZE	512
 
-/*! \brief call-back function for incoming RTP 
+/*! \brief function for incoming RTP via TCH.req
  *  \param rs RTP Socket
  *  \param[in] rtp_pl buffer containing RTP payload
  *  \param[in] rtp_pl_len length of \a rtp_pl
@@ -428,14 +429,9 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
  * yet, as things like the frame number, etc. are unknown at the time we
  * pre-fill the primtive.
  */
-void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
-			 unsigned int rtp_pl_len)
+void l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
+	const uint8_t *rtp_pl, unsigned int rtp_pl_len)
 {
-	struct gsm_lchan *lchan = rs->priv;
-	struct msgb *msg;
-	GsmL1_Prim_t *l1p;
-	GsmL1_PhDataReq_t *data_req;
-	GsmL1_MsgUnitParam_t *msu_param;
 	uint8_t *payload_type;
 	uint8_t *l1_payload;
 	int rc;
@@ -443,22 +439,8 @@ void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
 	DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
 		osmo_hexdump(rtp_pl, rtp_pl_len));
 
-	/* skip processing of incoming RTP frames if we are in loopback mode */
-	if (lchan->loopback)
-		return;
-
-	msg = l1p_msgb_alloc();
-	if (!msg) {
-		LOGP(DRTP, LOGL_ERROR, "%s: Failed to allocate Rx payload.\n",
-			gsm_lchan_name(lchan));
-		return;
-	}
-
-	l1p = msgb_l1prim(msg);
-	data_req = &l1p->u.phDataReq;
-	msu_param = &data_req->msgUnitParam;
-	payload_type = &msu_param->u8Buffer[0];
-	l1_payload = &msu_param->u8Buffer[1];
+	payload_type = &data[0];
+	l1_payload = &data[1];
 
 	switch (lchan->tch_mode) {
 	case GSM48_CMODE_SPEECH_V1:
@@ -493,40 +475,17 @@ void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
 	if (rc < 0) {
 		LOGP(DRTP, LOGL_ERROR, "%s unable to parse RTP payload\n",
 		     gsm_lchan_name(lchan));
-		msgb_free(msg);
 		return;
 	}
 
-	msu_param->u8Size = rc + 1;
+	*len = rc + 1;
 
 	DEBUGP(DRTP, "%s RTP->L1: %s\n", gsm_lchan_name(lchan),
-		osmo_hexdump(msu_param->u8Buffer, msu_param->u8Size));
-
-	/* make sure the number of entries in the dl_tch_queue is never
-	 * more than 3 */
-	{
-		struct msgb *tmp;
-		int count = 0;
-
-		llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
-			count++;
-
-		DEBUGP(DL1C, "%s DL TCH queue length = %u\n",
-			gsm_lchan_name(lchan), count);
-
-		while (count >= 2) {
-			tmp = msgb_dequeue(&lchan->dl_tch_queue);
-			msgb_free(tmp);
-			count--;
-		}
-	}
-
-	/* enqueue msgb to be transmitted to L1 */
-	msgb_enqueue(&lchan->dl_tch_queue, msg);
+		osmo_hexdump(data, *len));
 }
 
 /*! \brief receive a traffic L1 primitive for a given lchan */
-int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
+int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg)
 {
 	GsmL1_Prim_t *l1p = msgb_l1prim(l1p_msg);
 	GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd;
@@ -534,50 +493,15 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
 	uint8_t *payload = data_ind->msgUnitParam.u8Buffer + 1;
 	uint8_t payload_len;
 	struct msgb *rmsg = NULL;
+	struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)].lchan[l1sap_chan2ss(chan_nr)];
 
 	if (data_ind->msgUnitParam.u8Size < 1) {
-		LOGP(DL1C, LOGL_ERROR, "%s Rx Payload size 0\n",
-			gsm_lchan_name(lchan));
+		LOGP(DL1C, LOGL_ERROR, "chan_nr %d Rx Payload size 0\n",
+			chan_nr);
 		return -EINVAL;
 	}
 	payload_len = data_ind->msgUnitParam.u8Size - 1;
 
-	if (lchan->loopback) {
-		GsmL1_Prim_t *rl1p;
-		GsmL1_PhDataReq_t *data_req;
-		GsmL1_MsgUnitParam_t *msu_param;
-
-		struct msgb *tmp;
-		int count = 0;
-
-		/* generate a new msgb from the paylaod */
-		rmsg = l1p_msgb_alloc();
-		if (!rmsg)
-			return -ENOMEM;
-
-		rl1p = msgb_l1prim(rmsg);
-		data_req = &rl1p->u.phDataReq;
-		msu_param = &data_req->msgUnitParam;
-
-		memcpy(msu_param->u8Buffer,
-			data_ind->msgUnitParam.u8Buffer,
-			data_ind->msgUnitParam.u8Size);
-		msu_param->u8Size = data_ind->msgUnitParam.u8Size;
-
-		/* make sure the queue doesn't get too long */
-		llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
-			count++;
-		while (count >= 1) {
-			tmp = msgb_dequeue(&lchan->dl_tch_queue);
-			msgb_free(tmp);
-			count--;
-		}
-
-		msgb_enqueue(&lchan->dl_tch_queue, rmsg);
-
-		return 0;
-	}
-
 	switch (payload_type) {
 	case GsmL1_TchPlType_Fr:
 #if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
@@ -624,13 +548,20 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
 	}
 
 	if (rmsg) {
+		struct osmo_phsap_prim *l1sap;
+
 		LOGP(DL1C, LOGL_DEBUG, "%s Rx -> RTP: %s\n",
 			gsm_lchan_name(lchan), osmo_hexdump(rmsg->data, rmsg->len));
-		/* hand rmsg to RTP code for transmission */
-		if (lchan->abis_ip.rtp_socket)
-			osmo_rtp_send_frame(lchan->abis_ip.rtp_socket,
-					    rmsg->data, rmsg->len, 160);
-		msgb_free(rmsg);
+
+		/* add l1sap header */
+		rmsg->l2h = rmsg->data;
+		msgb_push(rmsg, sizeof(*l1sap));
+		rmsg->l1h = rmsg->data;
+		l1sap = msgb_l1sap_prim(rmsg);
+		osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_INDICATION, rmsg);
+		l1sap->u.tch.chan_nr = chan_nr;
+
+		return l1sap_up(trx, l1sap);
 	}
 
 	return 0;
-- 
1.8.1.5





More information about the OpenBSC mailing list