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


Reply via email to