Support puncturing scheme selection for EGPRS DL

Adds support to find the puncturing scheme for retransmission
with MCS change, retransmission with no MCS change, transmission
case. Puncturing scheme selection for retransmission case with
MCS change is aligned with TS 44.060 9.3.2.1. Puncturing scheme
selection for retransmission without MCS change, fresh transmission
is aligned with TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
diff --git a/src/rlc.cpp b/src/rlc.cpp
index 6ea1597..6770043 100644
--- a/src/rlc.cpp
+++ b/src/rlc.cpp
@@ -399,3 +399,77 @@
 	default: ;
 	}
 }
+
+/*
+ * Finds the PS value for retransmission with MCS change,
+ * retransmission with no MCS change, fresh transmission cases.
+ * The return value shall be used for current transmission only
+ * 44.060 9.3.2.1 defines the PS selection for MCS change case
+ * cs_current is the output of MCS selection algorithm for retx
+ * cs is coding scheme of previous transmission of RLC data block
+ */
+enum egprs_puncturing_values gprs_get_punct_scheme(
+	enum egprs_puncturing_values punct,
+	const GprsCodingScheme &cs,
+	const GprsCodingScheme &cs_current)
+{
+	/* TS  44.060 9.3.2.1.1 */
+	if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS9) &&
+	(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS6)) {
+		if ((punct == EGPRS_PS_1) || (punct == EGPRS_PS_3))
+			return EGPRS_PS_1;
+		else if (punct == EGPRS_PS_2)
+			return EGPRS_PS_2;
+	} else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS6) &&
+	(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS9)) {
+		if (punct == EGPRS_PS_1)
+			return EGPRS_PS_3;
+		else if (punct == EGPRS_PS_2)
+			return EGPRS_PS_2;
+	} else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS7) &&
+	(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS5))
+		return EGPRS_PS_1;
+	else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS5) &&
+	(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS7))
+		return EGPRS_PS_2;
+	else if (cs != cs_current)
+		return EGPRS_PS_1;
+	/* TS  44.060 9.3.2.1.1 ends here */
+	/*
+	 * Below else will handle fresh transmission, retransmission with no
+	 * MCS change case
+	 */
+	else
+		return punct;
+	return EGPRS_PS_INVALID;
+}
+
+/*
+ * This function calculates puncturing scheme for retransmission of a RLC
+ * block with same MCS. The computed value shall be used for next transmission
+ * of the same RLC block
+ * TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
+ */
+void gprs_update_punct_scheme(enum egprs_puncturing_values *punct,
+	const GprsCodingScheme &cs)
+{
+	switch (GprsCodingScheme::Scheme(cs)) {
+	case GprsCodingScheme::MCS1 :
+	case GprsCodingScheme::MCS2 :
+	case GprsCodingScheme::MCS5 :
+	case GprsCodingScheme::MCS6 :
+		*punct = ((enum egprs_puncturing_values)((*punct + 1) %
+			EGPRS_MAX_PS_NUM_2));
+		break;
+	case GprsCodingScheme::MCS3 :
+	case GprsCodingScheme::MCS4 :
+	case GprsCodingScheme::MCS7 :
+	case GprsCodingScheme::MCS8 :
+	case GprsCodingScheme::MCS9 :
+		*punct = ((enum egprs_puncturing_values)((*punct + 1) %
+			EGPRS_MAX_PS_NUM_3));
+		break;
+	default:
+		break;
+	}
+}
diff --git a/src/rlc.h b/src/rlc.h
index 6a8fd29..8f75588 100644
--- a/src/rlc.h
+++ b/src/rlc.h
@@ -136,7 +136,11 @@
 	punct, enum egprs_puncturing_values punct2, int with_padding);
 void gprs_rlc_mcs_cps_decode(unsigned int cps, GprsCodingScheme cs,
 	int *punct, int *punct2, int *with_padding);
-
+enum egprs_puncturing_values gprs_get_punct_scheme(enum egprs_puncturing_values
+	punct, const GprsCodingScheme &cs,
+	const GprsCodingScheme &cs_current_trans);
+void gprs_update_punct_scheme(enum egprs_puncturing_values *punct,
+	const GprsCodingScheme &cs);
 /*
  * I hold the currently transferred blocks and will provide
  * the routines to manipulate these arrays.
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index 56dedd0..9e4d078 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -587,6 +587,7 @@
 	bool is_final = false;
 	gprs_rlc_data_info rlc;
 	GprsCodingScheme cs;
+	GprsCodingScheme cs_current_trans;
 	int bsns[ARRAY_SIZE(rlc.block_info)];
 	unsigned num_bsns;
 	enum egprs_puncturing_values punct[ARRAY_SIZE(rlc.block_info)];
@@ -638,6 +639,7 @@
 		GprsCodingScheme cs_enc;
 		uint8_t *block_data;
 		gprs_rlc_data_block_info *rdbi, *block_info;
+		enum egprs_puncturing_values punct_scheme;
 
 		/* Check if there are more blocks than BSNs */
 		if (data_block_idx < num_bsns)
@@ -650,9 +652,19 @@
 		/* get data and header from current block */
 		block_data = m_rlc.block(bsn)->block;
 
-		/* TODO: Use real puncturing values */
-		punct[data_block_idx] =
-			(enum egprs_puncturing_values) data_block_idx;
+		/* TODO: Need to support MCS change during retx */
+		cs_current_trans = cs;
+
+		/* Get current puncturing scheme from block */
+		punct_scheme = gprs_get_punct_scheme(
+			m_rlc.block(bsn)->next_ps,
+			cs, cs_current_trans);
+
+		if (cs.isEgprs()) {
+			OSMO_ASSERT(punct_scheme >= EGPRS_PS_1);
+			OSMO_ASSERT(punct_scheme <= EGPRS_PS_3);
+		}
+		punct[data_block_idx] = punct_scheme;
 
 		rdbi = &rlc.block_info[data_block_idx];
 		block_info = &m_rlc.block(bsn)->block_info;
@@ -665,6 +677,13 @@
 				data_block_idx, bsn, cs_enc.name());
 			OSMO_ASSERT(rdbi->data_len == m_rlc.block(bsn)->len);
 		}
+
+		/* TODO: Need to handle 2 same bsns
+		 * in header type 1
+		 */
+		gprs_update_punct_scheme(&m_rlc.block(bsn)->next_ps,
+					cs_current_trans);
+
 		rdbi->e   = block_info->e;
 		rdbi->cv  = block_info->cv;
 		rdbi->bsn = bsn;