diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index 12be383..633a5a5 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -91,6 +91,19 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
 	DEBUGP(DL1P, "Rx PH-RTS.ind %02u/%02u/%02u chan_nr=%d link_id=%d\n",
 		g_time.t1, g_time.t2, g_time.t3, chan_nr, link_id);
 
+	if (trx->ts[tn].pchan == GSM_PCHAN_PDCH) {
+		if (L1SAP_IS_PTCCH(rts_ind->fn)) {
+			pcu_tx_rts_req(&trx->ts[tn], 1, fn, 1 /* ARFCN */,
+				L1SAP_FN2PTCCHBLOCK(fn));
+
+			return 0;
+		}
+		pcu_tx_rts_req(&trx->ts[tn], 0, fn, 0 /* ARFCN */,
+			L1SAP_FN2MACBLOCK(fn));
+
+		return 0;
+	}
+
 	/* reuse PH-RTS.ind for PH-DATA.req */
 	if (!msg) {
 		LOGP(DL1P, LOGL_FATAL, "RTS without msg to be reused. Please "
@@ -155,6 +168,54 @@ static int l1sap_handover_rach(struct gsm_bts_trx *trx,
 	return 0;
 }
 
+/* DATA received from bts model */
+static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
+	 struct osmo_phsap_prim *l1sap, struct ph_data_param *data_ind)
+{
+	struct msgb *msg = l1sap->oph.msg;
+	struct gsm_time g_time;
+	uint8_t *data = msg->l2h;
+	int len = msgb_l2len(msg);
+	uint8_t chan_nr, link_id;
+	uint8_t tn, ss;
+	uint32_t fn;
+	int8_t rssi;
+
+	rssi = data_ind->rssi;
+	chan_nr = data_ind->chan_nr;
+	link_id = data_ind->link_id;
+	fn = data_ind->fn;
+	tn = L1SAP_CHAN2TS(chan_nr);
+	ss = l1sap_chan2ss(chan_nr);
+
+	gsm_fn2gsmtime(&g_time, fn);
+
+	DEBUGP(DL1P, "Rx PH-DATA.ind %02u/%02u/%02u chan_nr=%d link_id=%d\n",
+		g_time.t1, g_time.t2, g_time.t3, chan_nr, link_id);
+
+	if (trx->ts[tn].pchan == GSM_PCHAN_PDCH) {
+		if (len == 0)
+			return -EINVAL;
+		if (L1SAP_IS_PTCCH(fn)) {
+			pcu_tx_data_ind(&trx->ts[tn], 1, fn,
+				0 /* ARFCN */, L1SAP_FN2PTCCHBLOCK(fn),
+				data, len, rssi);
+
+			return 0;
+		}
+		/* drop incomplete UL block */
+		if (data[0] != 7)
+			return 0;
+		/* PDTCH / PACCH frame handling */
+		pcu_tx_data_ind(&trx->ts[tn], 0, fn, 0 /* ARFCN */,
+			L1SAP_FN2MACBLOCK(fn), data + 1, len - 1, rssi);
+
+		return 0;
+	}
+
+	return 0;
+}
+
 /* RACH received from bts model */
 static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx,
 	 struct osmo_phsap_prim *l1sap, struct ph_rach_ind_param *rach_ind)
@@ -204,6 +265,9 @@ int l1sap_up(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
 	case OSMO_PRIM(PRIM_PH_RTS, PRIM_OP_INDICATION):
 		rc = l1sap_ph_rts_ind(trx, l1sap, &l1sap->u.data);
 		break;
+	case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_INDICATION):
+		rc = l1sap_ph_data_ind(trx, l1sap, &l1sap->u.data);
+		break;
 	case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION):
 		rc = l1sap_ph_rach_ind(trx, l1sap, &l1sap->u.rach_ind);
 		break;
@@ -226,3 +290,29 @@ static int l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
 	return bts_model_l1sap_down(trx, l1sap);
 }
 
+/* pcu (socket interface) sends us a data request primitive */
+int l1sap_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 msgb *msg;
+	struct osmo_phsap_prim *l1sap;
+	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 = l1sap_msgb_alloc(len);
+	l1sap = msgb_l1sap_prim(msg);
+	osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_REQUEST,
+		msg);
+	l1sap->u.data.chan_nr = 0x08 | ts->nr;
+	l1sap->u.data.link_id = 0x00;
+	l1sap->u.data.fn = fn;
+	msg->l2h = msgb_put(msg, len);
+	memcpy(msg->l2h, data, len);
+
+	return l1sap_down(ts->trx, l1sap);
+}
diff --git a/src/common/pcu_sock.c b/src/common/pcu_sock.c
index a978e46..515993e 100644
--- a/src/common/pcu_sock.c
+++ b/src/common/pcu_sock.c
@@ -40,6 +40,7 @@
 #include <osmo-bts/rsl.h>
 #include <osmo-bts/signal.h>
 #include <osmo-bts/bts_model.h>
+#include <osmo-bts/l1sap.h>
 
 uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
 
@@ -57,10 +58,6 @@ static const char *sapi_string[] = {
 	[PCU_IF_SAPI_PTCCH] = 	"PTCCH",
 };
 
-/* FIXME: common l1if include ? */
-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);
-
 static int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
 /* FIXME: move this to libosmocore */
 int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path);
@@ -512,7 +509,7 @@ static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
 		}
 		ts = &trx->ts[data_req->ts_nr];
 		is_ptcch = (data_req->sapi == PCU_IF_SAPI_PTCCH);
-		rc = l1if_pdch_req(ts, is_ptcch, data_req->fn, data_req->arfcn,
+		rc = l1sap_pdch_req(ts, is_ptcch, data_req->fn, data_req->arfcn,
 			data_req->block_nr, data_req->data, data_req->len);
 		break;
 	default:
diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c
index a3fd64e..d4861e2 100644
--- a/src/osmo-bts-sysmo/l1_if.c
+++ b/src/osmo-bts-sysmo/l1_if.c
@@ -466,7 +466,17 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg,
 	u32Fn = l1sap->u.data.fn;
 	u8Tn = L1SAP_CHAN2TS(chan_nr);
 	subCh = 0x1f;
-	if (L1SAP_IS_CHAN_BCCH(chan_nr)) {
+	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 if (L1SAP_IS_CHAN_BCCH(chan_nr)) {
 		sapi = GsmL1_Sapi_Bcch;
 	} else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) {
 		/* The sapi depends on DSP configuration, not
@@ -556,6 +566,35 @@ static uint8_t chan_nr_by_sapi(enum gsm_phys_chan_config pchan,
 	case GsmL1_Sapi_Pch:
 		cbits = 0x12;
 		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;
 	}
@@ -648,13 +687,6 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1,
 		/* 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;
 	}
@@ -886,6 +918,10 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
 	struct gsm_lchan *lchan;
 	struct lapdm_entity *le;
 	struct msgb *msg;
+	uint8_t chan_nr, link_id;
+	struct osmo_phsap_prim *l1sap;
+	uint32_t fn;
+	uint8_t *data, len;
 	int rc = 0;
 
 	ul_to_gsmtap(fl1, l1p_msg);
@@ -895,14 +931,22 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
 		LOGP(DL1C, LOGL_ERROR,
 			"unable to resolve lchan by hLayer2 for 0x%x\n",
 			data_ind->hLayer2);
+		msgb_free(l1p_msg);
 		return -ENODEV;
 	}
 
+	chan_nr = chan_nr_by_sapi(trx->ts[data_ind->u8Tn].pchan, data_ind->sapi,
+		data_ind->subCh, data_ind->u8Tn, data_ind->u32Fn);
+	fn = data_ind->u32Fn;
+	link_id =  (data_ind->sapi == GsmL1_Sapi_Sacch) ? 0x40 : 0x00;
+
 	process_meas_res(lchan, &data_ind->measParam);
 
 	if (data_ind->measParam.fLinkQuality < fl1->min_qual_norm
-	 && data_ind->msgUnitParam.u8Size != 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),
@@ -973,27 +1017,7 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
 		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;
 	case GsmL1_Sapi_Idle:
 		/* nothing to send */
@@ -1004,7 +1028,32 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i
 		break;
 	}
 
-	return rc;
+	if (!chan_nr) {
+		msgb_free(l1p_msg);
+		return rc;
+	}
+
+	/* 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,
@@ -1084,8 +1133,7 @@ static int l1if_handle_ind(struct femtol1_hdl *fl1, struct msgb *msg)
 		return handle_ph_readytosend_ind(fl1, &l1p->u.phReadyToSendInd,
 					       msg);
 	case GsmL1_PrimId_PhDataInd:
-		rc = handle_ph_data_ind(fl1, &l1p->u.phDataInd, msg);
-		break;
+		return handle_ph_data_ind(fl1, &l1p->u.phDataInd, msg);
 	case GsmL1_PrimId_PhRaInd:
 		return handle_ph_ra_ind(fl1, &l1p->u.phRaInd, msg);
 		break;
@@ -1538,46 +1586,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;
-}
-
 /* get those femtol1_hdl.hw_info elements that sre in EEPROM */
 static int get_hwinfo_eeprom(struct femtol1_hdl *fl1h)
 {
