rlc: Support encoding of EGPRS header type 1 + 2

Currently only header type 3 (MCS-1 to MCS-4) is supported.

Add header structs to rlc.h and extend
Encoding::rlc_write_dl_data_header accordingly.

Sponsored-by: On-Waves ehf
diff --git a/src/encoding.cpp b/src/encoding.cpp
index e3e1245..a7d7b16 100644
--- a/src/encoding.cpp
+++ b/src/encoding.cpp
@@ -711,10 +711,14 @@
 int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
 	uint8_t *data)
 {
+	struct gprs_rlc_dl_header_egprs_1 *egprs1;
+	struct gprs_rlc_dl_header_egprs_2 *egprs2;
 	struct gprs_rlc_dl_header_egprs_3 *egprs3;
 	struct rlc_dl_header *gprs;
 	unsigned int e_fbi_header;
 	GprsCodingScheme cs = rlc->cs;
+	unsigned int offs;
+	unsigned int bsn_delta;
 
 	switch(cs.headerTypeData()) {
 	case GprsCodingScheme::HEADER_GPRS_DATA:
@@ -733,6 +737,65 @@
 		gprs->bsn   = rlc->block_info[0].bsn;
 		break;
 
+	case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1:
+		egprs1 = static_cast<struct gprs_rlc_dl_header_egprs_1 *>
+			((void *)data);
+
+		egprs1->usf    = rlc->usf;
+		egprs1->es_p   = rlc->es_p;
+		egprs1->rrbp   = rlc->rrbp;
+		egprs1->tfi_a  = rlc->tfi >> 0; /* 1 bit LSB */
+		egprs1->tfi_b  = rlc->tfi >> 1; /* 4 bits */
+		egprs1->pr     = rlc->pr;
+		egprs1->cps    = rlc->cps;
+
+		egprs1->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
+		egprs1->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
+		egprs1->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
+
+		bsn_delta = (rlc->block_info[1].bsn - rlc->block_info[0].bsn) &
+			(RLC_EGPRS_SNS - 1);
+
+		egprs1->bsn2_a = bsn_delta >> 0; /* 7 bits LSB */
+		egprs1->bsn2_b = bsn_delta >> 7; /* 3 bits */
+
+		/* first FBI/E header */
+		e_fbi_header   = rlc->block_info[0].e       ? 0x01 : 0;
+		e_fbi_header  |= rlc->block_info[0].cv == 0 ? 0x02 : 0; /* FBI */
+		e_fbi_header <<= 0;
+		data[5] = (data[5] & 0b11111100) | e_fbi_header;
+
+		/* second FBI/E header */
+		e_fbi_header   = rlc->block_info[1].e       ? 0x01 : 0;
+		e_fbi_header  |= rlc->block_info[1].cv == 0 ? 0x02 : 0; /* FBI */
+		offs = rlc->data_offs_bits[1] / 8;
+		OSMO_ASSERT(rlc->data_offs_bits[1] % 8 == 4);
+		e_fbi_header <<= 2;
+		data[offs] = (data[offs] & 0b11110011) | e_fbi_header;
+		break;
+
+	case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2:
+		egprs2 = static_cast<struct gprs_rlc_dl_header_egprs_2 *>
+			((void *)data);
+
+		egprs2->usf    = rlc->usf;
+		egprs2->es_p   = rlc->es_p;
+		egprs2->rrbp   = rlc->rrbp;
+		egprs2->tfi_a  = rlc->tfi >> 0; /* 1 bit LSB */
+		egprs2->tfi_b  = rlc->tfi >> 1; /* 4 bits */
+		egprs2->pr     = rlc->pr;
+		egprs2->cps    = rlc->cps;
+
+		egprs2->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
+		egprs2->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
+		egprs2->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
+
+		e_fbi_header   = rlc->block_info[0].e       ? 0x01 : 0;
+		e_fbi_header  |= rlc->block_info[0].cv == 0 ? 0x02 : 0; /* FBI */
+		e_fbi_header <<= 4;
+		data[3] = (data[3] & 0b11001111) | e_fbi_header;
+		break;
+
 	case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3:
 		egprs3 = static_cast<struct gprs_rlc_dl_header_egprs_3 *>
 			((void *)data);
@@ -758,10 +821,6 @@
 		data[4] = (data[4] & 0b11111110) | (e_fbi_header >> 8);
 		break;
 
-	case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1:
-	case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2:
-		/* TODO: Support both header types */
-		/* fall through */
 	default:
 		LOGP(DRLCMACDL, LOGL_ERROR,
 			"Encoding of uplink %s data blocks not yet supported.\n",
diff --git a/src/rlc.cpp b/src/rlc.cpp
index e4a9563..efe4261 100644
--- a/src/rlc.cpp
+++ b/src/rlc.cpp
@@ -327,7 +327,8 @@
 	rdbi->spb = 0;
 }
 
-unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, int punct, int with_padding)
+unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, int punct, int punct2,
+	int with_padding)
 {
 	switch (GprsCodingScheme::Scheme(cs)) {
 	case GprsCodingScheme::MCS1: return 0b1011 + punct % 2;
@@ -335,7 +336,12 @@
 	case GprsCodingScheme::MCS3: return (with_padding ? 0b0110 : 0b0011) +
 					    punct % 3;
 	case GprsCodingScheme::MCS4: return 0b0000 + punct % 3;
-	/* TODO: Add missing MCS */
+	case GprsCodingScheme::MCS5: return  0b100 + punct % 2;
+	case GprsCodingScheme::MCS6: return (with_padding ? 0b010 : 0b000) +
+					    punct % 2;
+	case GprsCodingScheme::MCS7: return 0b10100 + 3 * (punct % 3) + punct2 % 3;
+	case GprsCodingScheme::MCS8: return 0b01011 + 3 * (punct % 3) + punct2 % 3;
+	case GprsCodingScheme::MCS9: return 0b00000 + 4 * (punct % 3) + punct2 % 3;
 	default: ;
 	}
 
diff --git a/src/rlc.h b/src/rlc.h
index 3a7f1a1..8c3a412 100644
--- a/src/rlc.h
+++ b/src/rlc.h
@@ -106,7 +106,8 @@
 	GprsCodingScheme cs);
 void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
 	GprsCodingScheme cs);
-unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, int punct, int with_padding);
+unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, int punct, int punct2,
+	int with_padding);
 
 /*
  * I hold the currently transferred blocks and will provide
@@ -300,6 +301,35 @@
 		dummy:1;
 } __attribute__ ((packed));
 
+struct gprs_rlc_dl_header_egprs_1 {
+	uint8_t usf:3,
+		es_p:2,
+		rrbp:2,
+		tfi_a:1;
+	uint8_t tfi_b:4,
+		pr:2,
+		bsn1_a:2;
+	uint8_t bsn1_b:8;
+	uint8_t bsn1_c:1,
+		bsn2_a:7;
+	uint8_t bsn2_b:3,
+		cps:5;
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_2 {
+	uint8_t usf:3,
+		es_p:2,
+		rrbp:2,
+		tfi_a:1;
+	uint8_t tfi_b:4,
+		pr:2,
+		bsn1_a:2;
+	uint8_t bsn1_b:8;
+	uint8_t bsn1_c:1,
+		cps:3,
+		dummy:4;
+} __attribute__ ((packed));
+
 struct gprs_rlc_dl_header_egprs_3 {
 	uint8_t usf:3,
 		es_p:2,
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index 03f0cc4..e567f57 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -545,7 +545,8 @@
 	rlc.usf = 7; /* will be set at scheduler */
 	rlc.pr = 0; /* FIXME: power reduction */
 	rlc.tfi = m_tfi; /* TFI */
-	rlc.cps = gprs_rlc_mcs_cps(cs, 0, 0);
+	/* TODO: Use real puncturing values */
+	rlc.cps = gprs_rlc_mcs_cps(cs, 0, 0, 0);
 
 	rlc.block_info[data_block_idx] = m_rlc.block(index)->block_info;
 	rdbi = &rlc.block_info[data_block_idx];