pcu_l1_if.cpp: handle TLLI based IMMEDIATE ASSIGNMENT confirmation

The IMMEDIATE ASSIGNMENT for downlink TBFs must be confirmed by the
receiving end (BSC/BTS) in order to set the timers in the PCU correctly.

When the PCU is used in a BSC co-located scheme (Ericsson RBS). The TLLI
is used as an identifier to confirm a specific IMMEDIATE ASSIGNMENT.

Change-Id: Icf7ca34500984239ee877ee71fd9c126b5eb3480
Related: OS#5198
diff --git a/src/bts.cpp b/src/bts.cpp
index d06c9d0..33ad4a6 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -668,13 +668,10 @@
 	return best_first_tfi;
 }
 
-int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t fn)
+static int tlli_from_imm_ass(uint32_t *tlli, const uint8_t *data, uint32_t fn)
 {
-	struct gprs_rlcmac_dl_tbf *dl_tbf;
 	const struct gsm48_imm_ass *imm_ass = (struct gsm48_imm_ass *)data;
 	uint8_t plen;
-	uint32_t tlli;
-	GprsMs *ms;
 
 	/* Move to IA Rest Octets: TS 44.018 9.1.18 "The L2 pseudo length of
 	 * this message is the sum of lengths of all information elements
@@ -692,12 +689,41 @@
 	}
 
 	/* get TLLI from downlink assignment */
-	tlli = (uint32_t)((*data++) & 0xf) << 28;
-	tlli |= (*data++) << 20;
-	tlli |= (*data++) << 12;
-	tlli |= (*data++) << 4;
-	tlli |= (*data++) >> 4;
+	*tlli = (uint32_t)((*data++) & 0xf) << 28;
+	*tlli |= (*data++) << 20;
+	*tlli |= (*data++) << 12;
+	*tlli |= (*data++) << 4;
+	*tlli |= (*data++) >> 4;
 
+	return 0;
+}
+
+int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t tlli, uint32_t fn)
+{
+	struct gprs_rlcmac_dl_tbf *dl_tbf;
+	GprsMs *ms;
+	int rc;
+
+	/* NOTE: A confirmation for a downlink IMMEDIATE ASSIGNMENT can be received using two different methods.
+	 * One way is to send the whole IMMEDIATE ASSIGNMENT back to the PCU and the TLLI, which we use as
+	 * reference is extracted from the rest octets of this message. Alternatively the TLLI may be sent as
+	 * confirmation directly. */
+
+	/* Extract TLLI from the presented IMMEDIATE ASSIGNMENT
+	 * (if present and only when TLLI that is supplied as function parameter is valid.) */
+	if (data && tlli == GSM_RESERVED_TMSI) {
+		rc = tlli_from_imm_ass(&tlli, data, fn);
+		if (rc != 0)
+			return -EINVAL;
+	}
+
+	/* Make sure TLLI is valid */
+	if (tlli == GSM_RESERVED_TMSI) {
+		LOGP(DTBFDL, LOGL_ERROR, "FN=%u Got IMM.ASS confirm, but TLLI is invalid!\n", fn);
+		return -EINVAL;
+	}
+
+	/* Find related TBF and send confirmation signal to FSM */
 	ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 	if (!ms) {
 		LOGP(DTBFDL, LOGL_ERROR, "FN=%u Got IMM.ASS confirm for unknown MS with TLLI=%08x\n", fn, tlli);
diff --git a/src/bts.h b/src/bts.h
index 61c5e43..5efbdad 100644
--- a/src/bts.h
+++ b/src/bts.h
@@ -315,7 +315,7 @@
 
 int bts_rcv_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip);
 int bts_rcv_ptcch_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip);
-int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t fn);
+int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t tlli, uint32_t fn);
 
 void bts_send_gsmtap(struct gprs_rlcmac_bts *bts,
 		     enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
diff --git a/src/pcu_l1_if.cpp b/src/pcu_l1_if.cpp
index 69629db..02f56ac 100644
--- a/src/pcu_l1_if.cpp
+++ b/src/pcu_l1_if.cpp
@@ -512,7 +512,7 @@
 	switch (data_cnf->sapi) {
 	case PCU_IF_SAPI_PCH:
 		if (data_cnf->data[2] == GSM48_MT_RR_IMM_ASS)
-			bts_rcv_imm_ass_cnf(bts, data_cnf->data, data_cnf->fn);
+			bts_rcv_imm_ass_cnf(bts, data_cnf->data, GSM_RESERVED_TMSI, data_cnf->fn);
 		break;
 	default:
 		LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with "
@@ -523,6 +523,26 @@
 	return rc;
 }
 
+static int pcu_rx_data_cnf_dt(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_data_cnf_dt *data_cnf_dt)
+{
+	int rc = 0;
+	int current_fn = bts_current_frame_number(bts);
+
+	LOGP(DL1IF, LOGL_DEBUG, "Data confirm received: sapi=%d fn=%d cur_fn=%d\n",
+	     data_cnf_dt->sapi, data_cnf_dt->fn, current_fn);
+
+	switch (data_cnf_dt->sapi) {
+	case PCU_IF_SAPI_PCH:
+		bts_rcv_imm_ass_cnf(bts, NULL, data_cnf_dt->tlli, data_cnf_dt->fn);
+		break;
+	default:
+		LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with unsupported sapi %d\n", data_cnf_dt->sapi);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
 // FIXME: remove this, when changed from c++ to c.
 int pcu_rx_rts_req_pdtch(struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts,
 	uint32_t fn, uint8_t block_nr)
@@ -1133,6 +1153,10 @@
 		CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.data_cnf);
 		rc = pcu_rx_data_cnf(bts, &pcu_prim->u.data_cnf);
 		break;
+	case PCU_IF_MSG_DATA_CNF_DT:
+		CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.data_cnf_dt);
+		rc = pcu_rx_data_cnf_dt(bts, &pcu_prim->u.data_cnf_dt);
+		break;
 	case PCU_IF_MSG_RTS_REQ:
 		CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.rts_req);
 		rc = pcu_rx_rts_req(bts, &pcu_prim->u.rts_req);