tbf/test: Add tests for single and two phase access

These tests cover the message exchange from receiving from the first
RACH request to the first data block when establishing an uplink TBF.

This will be used to check, whether TA and other values are passed to
an MS object correctly.

In addition, the RX RACH log message in rcv_rach is extended to
contain the single block fn.

Sponsored-by: On-Waves ehf
diff --git a/src/bts.cpp b/src/bts.cpp
index c7daf8e..6bcfea0 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -416,8 +416,10 @@
 		if (rc < 0)
 			return rc;
 		LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] RACH qbit-ta=%d "
-			"ra=0x%02x, Fn=%d (%d,%d,%d)\n", qta, ra, Fn,
-			(Fn / (26 * 51)) % 32, Fn % 51, Fn % 26);
+			"ra=0x%02x, Fn=%d (%d,%d,%d), SBFn=%d\n",
+			qta, ra,
+			Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26,
+			sb_fn);
 		LOGP(DRLCMAC, LOGL_INFO, "TX: Immediate Assignment Uplink "
 			"(AGCH)\n");
 	} else {
diff --git a/tests/tbf/TbfTest.cpp b/tests/tbf/TbfTest.cpp
index 84ecb97..e25f344 100644
--- a/tests/tbf/TbfTest.cpp
+++ b/tests/tbf/TbfTest.cpp
@@ -25,6 +25,7 @@
 #include "gprs_debug.h"
 #include "pcu_utils.h"
 #include "gprs_bssgp_pcu.h"
+#include "pcu_l1_if.h"
 
 extern "C" {
 #include "pcu_vty.h"
@@ -46,6 +47,20 @@
 	OSMO_ASSERT(tbf);
 }
 
+/*
+static unsigned inc_fn(fn)
+{
+	unsigned next_fn;
+
+	next_fn = fn + 4;
+	if ((block_nr % 3) == 2)
+		next_fn ++;
+	next_fn = next_fn % 2715648;
+
+	return next_fn;
+}
+*/
+
 static void test_tbf_tlli_update()
 {
 	BTS the_bts;
@@ -401,6 +416,131 @@
 	gprs_bssgp_destroy();
 }
 
+static void test_tbf_single_phase()
+{
+	BTS the_bts;
+	GprsMs *ms;
+	int ts_no = 7;
+	uint32_t fn = 2654167; /* 17,25,9 */
+	uint16_t qta = 31;
+	uint8_t trx_no = 0;
+	int tfi = 0;
+	gprs_rlcmac_ul_tbf *ul_tbf;
+	struct gprs_rlcmac_pdch *pdch;
+
+	printf("=== start %s ===\n", __func__);
+
+	setup_bts(&the_bts, ts_no);
+	tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
+
+	the_bts.rcv_rach(0x03, fn, qta);
+
+	ul_tbf = the_bts.ul_tbf_by_tfi(tfi, trx_no);
+	OSMO_ASSERT(ul_tbf != NULL);
+
+	fprintf(stderr, "Got '%s', TA=%d\n",
+		ul_tbf->name(), ul_tbf->ta);
+
+	OSMO_ASSERT(ul_tbf->ta == qta / 4);
+
+	uint8_t data_msg[23] = {
+		0x00, /* GPRS_RLCMAC_DATA_BLOCK << 6 */
+		uint8_t(1 | (tfi << 2)),
+		uint8_t(1), /* BSN:7, E:1 */
+		0xf1, 0x22, 0x33, 0x44, /* TLLI */
+	};
+
+	pdch = &the_bts.bts_data()->trx[trx_no].pdch[ts_no];
+	pdch->rcv_block(&data_msg[0], sizeof(data_msg), fn, 0);
+
+	ms = the_bts.ms_by_tlli(0xf1223344);
+	OSMO_ASSERT(ms != NULL);
+	fprintf(stderr, "Got MS: TLLI = 0x%08x\n", ms->tlli());
+
+	printf("=== end %s ===\n", __func__);
+}
+
+static void test_tbf_two_phase()
+{
+	BTS the_bts;
+	GprsMs *ms;
+	int ts_no = 7;
+	uint32_t rach_fn = 2654167; /* 17,25,9 */
+	uint32_t rts_fn = 2654218;
+	uint8_t rts_bn = 8;
+	uint16_t qta = 31;
+	uint8_t trx_no = 0;
+	int tfi = 0;
+	const uint32_t tlli = 0xf1223344;
+	gprs_rlcmac_ul_tbf *ul_tbf;
+	struct gprs_rlcmac_pdch *pdch;
+	gprs_rlcmac_bts *bts;
+	RlcMacUplink_t ulreq = {0};
+	bitvec *rlc_block;
+	uint8_t buf[64];
+	int num_bytes;
+
+	printf("=== start %s ===\n", __func__);
+
+	setup_bts(&the_bts, ts_no);
+	bts = the_bts.bts_data();
+
+	/* needed to set last_rts_fn in the PDCH object */
+	send_rlc_block(bts, trx_no, ts_no, 0, &rts_fn, &rts_bn);
+
+	/* simulate RACH, this sends an Immediate Assignment Uplink on the AGCH */
+	the_bts.rcv_rach(0x73, rach_fn, qta);
+
+	/* get next free TFI */
+	tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
+
+	/* fake a resource request */
+	rlc_block = bitvec_alloc(23);
+
+	ulreq.u.MESSAGE_TYPE = MT_PACKET_RESOURCE_REQUEST;
+	ulreq.u.Packet_Resource_Request.PayloadType = GPRS_RLCMAC_CONTROL_BLOCK;
+	ulreq.u.Packet_Resource_Request.ID.UnionType = 1; /* != 0 */
+	ulreq.u.Packet_Resource_Request.ID.u.TLLI = tlli;
+
+	encode_gsm_rlcmac_uplink(rlc_block, &ulreq);
+	num_bytes = bitvec_pack(rlc_block, &buf[0]);
+	OSMO_ASSERT(size_t(num_bytes) < sizeof(buf));
+	bitvec_free(rlc_block);
+
+	pdch = &the_bts.bts_data()->trx[trx_no].pdch[ts_no];
+	pdch->rcv_block(&buf[0], num_bytes, 2654270, 31);
+
+	/* check the TBF */
+	ul_tbf = the_bts.ul_tbf_by_tfi(tfi, trx_no);
+	OSMO_ASSERT(ul_tbf != NULL);
+
+	fprintf(stderr, "Got '%s', TA=%d\n",
+		ul_tbf->name(), ul_tbf->ta);
+
+	OSMO_ASSERT(ul_tbf->ta == qta / 4);
+
+	/* send packet uplink assignment */
+	rts_fn += 52;
+	rts_bn += 12;
+	send_rlc_block(bts, trx_no, ts_no, 0, &rts_fn, &rts_bn);
+
+	/* send fake data */
+	uint8_t data_msg[23] = {
+		0x00, /* GPRS_RLCMAC_DATA_BLOCK << 6 */
+		uint8_t(0 | (tfi << 2)),
+		uint8_t(1), /* BSN:7, E:1 */
+	};
+
+	pdch->rcv_block(&data_msg[0], sizeof(data_msg), rts_fn, 31);
+
+	ms = the_bts.ms_by_tlli(0xf1223344);
+	OSMO_ASSERT(ms != NULL);
+	fprintf(stderr, "Got MS: TLLI = 0x%08x\n", ms->tlli());
+
+	printf("=== end %s ===\n", __func__);
+}
+
+
 static const struct log_info_cat default_categories[] = {
         {"DCSN1", "\033[1;31m", "Concrete Syntax Notation One (CSN1)", LOGL_INFO, 0},
         {"DL1IF", "\033[1;32m", "GPRS PCU L1 interface (L1IF)", LOGL_DEBUG, 1},
@@ -449,6 +589,8 @@
 	test_tbf_delayed_release();
 	test_tbf_imsi();
 	test_tbf_exhaustion();
+	test_tbf_single_phase();
+	test_tbf_two_phase();
 
 	if (getenv("TALLOC_REPORT_FULL"))
 		talloc_report_full(tall_pcu_ctx, stderr);
diff --git a/tests/tbf/TbfTest.err b/tests/tbf/TbfTest.err
index 03921c5..20b1a72 100644
--- a/tests/tbf/TbfTest.err
+++ b/tests/tbf/TbfTest.err
@@ -1102,3 +1102,99 @@
 No TFI available.
 No PDCH resource
 BSSGP (BVCI=1234) Tx LLC-DISCARDED TLLI=0xc0000020, FRAMES=1, OCTETS=256
+Searching for first unallocated TFI: TRX=0 first TS=7
+ Found TFI=0.
+MS requests UL TBF on RACH, so we provide one:
+Searching for first unallocated TFI: TRX=0 first TS=7
+ Found TFI=0.
+********** TBF starts here **********
+Allocating UL TBF: TFI=0 TRX=0 MS_CLASS=0
+Slot Allocation (Algorithm A) for class 0
+- Skipping TS 0, because not enabled
+- Skipping TS 1, because not enabled
+- Skipping TS 2, because not enabled
+- Skipping TS 3, because not enabled
+- Skipping TS 4, because not enabled
+- Skipping TS 5, because not enabled
+- Skipping TS 6, because not enabled
+- Assign uplink TS=7 USF=0
+- Setting Control TS 7
+TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL) changes state from NULL to FLOW
+TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=FLOW) starting timer 3169.
+TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=FLOW) [UPLINK] START
+TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=FLOW) RX: [PCU <- BTS] RACH qbit-ta=31 ra=0x03, Fn=2654167 (17,25,9)
+TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=FLOW) TX: START Immediate Assignment Uplink (AGCH)
+Sending data request: trx=0 ts=0 sapi=2 arfcn=0 fn=0 block=0 data=2d 06 3f 10 0f 00 00 03 8b 29 07 00 c8 00 70 0b 2b 2b 2b 2b 2b 2b 2b 
+Got 'TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=FLOW)', TA=7
+UL DATA TFI=0 received (V(Q)=0 .. V(R)=0)
+Creating MS object, TLLI = 0x00000000
+Modifying MS object, UL TLLI: 0x00000000 -> 0xf1223344, not yet confirmed
+Attaching TBF to MS object, TLLI = 0xf1223344, TBF = TBF(TFI=0 TLLI=0xf1223344 DIR=UL STATE=FLOW)
+Decoded premier TLLI=0xf1223344 of UL DATA TFI=0.
+TBF(TFI=0 TLLI=0xf1223344 DIR=UL STATE=FLOW) restarting timer 3169 while old timer 3169 pending 
+- BSN 0 storing in window (0..63)
+- Raising V(R) to 1
+- Taking block 0 out, raising V(Q) to 1
+- Assembling frames: (len=23)
+-- Frame 1 starts at offset 0
+- Data length after length fields: 20
+- Length after skipping TLLI: 16
+TBF(TFI=0 TLLI=0xf1223344 DIR=UL STATE=FLOW) complete UL frame that fits precisely in last block: len=16
+LLC [PCU -> SGSN] TBF(TFI=0 TLLI=0xf1223344 DIR=UL STATE=FLOW) len=16
+No bctx
+- No gaps in received block, last block: BSN=0 CV=0
+- Finished with UL TBF
+TBF(TFI=0 TLLI=0xf1223344 DIR=UL STATE=FLOW) changes state from FLOW to FINISHED
+- Scheduling Ack/Nack, because TLLI is included.
+- Scheduling Ack/Nack, because last block has CV==0.
+Got MS: TLLI = 0xf1223344
+Sending data request: trx=0 ts=7 sapi=5 arfcn=0 fn=2654218 block=8 data=47 94 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 
+MS requests UL TBF on RACH, so we provide one:
+MS requests single block allocation
+RX: [PCU <- BTS] RACH qbit-ta=31 ra=0x73, Fn=2654167 (17,25,9), SBFn=2654270
+TX: Immediate Assignment Uplink (AGCH)
+Sending data request: trx=0 ts=0 sapi=2 arfcn=0 fn=0 block=0 data=2d 06 3f 10 0f 00 00 73 8b 29 07 00 c0 0c 5a 43 2b 2b 2b 2b 2b 2b 2b 
+Searching for first unallocated TFI: TRX=0 first TS=7
+ Found TFI=0.
++++++++++++++++++++++++++ RX : Uplink Control Block +++++++++++++++++++++++++
+------------------------- RX : Uplink Control Block -------------------------
+MS requests UL TBF in packet resource request of single block, so we provide one:
+MS does not give us a class.
+Searching for first unallocated TFI: TRX=0 first TS=7
+ Found TFI=0.
+********** TBF starts here **********
+Allocating UL TBF: TFI=0 TRX=0 MS_CLASS=0
+Slot Allocation (Algorithm A) for class 0
+- Skipping TS 0, because not enabled
+- Skipping TS 1, because not enabled
+- Skipping TS 2, because not enabled
+- Skipping TS 3, because not enabled
+- Skipping TS 4, because not enabled
+- Skipping TS 5, because not enabled
+- Skipping TS 6, because not enabled
+- Assign uplink TS=7 USF=0
+- Setting Control TS 7
+TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL) changes state from NULL to ASSIGN
+TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=ASSIGN) starting timer 3169.
+Creating MS object, TLLI = 0x00000000
+Modifying MS object, UL TLLI: 0x00000000 -> 0xf1223344, not yet confirmed
+Attaching TBF to MS object, TLLI = 0xf1223344, TBF = TBF(TFI=0 TLLI=0xf1223344 DIR=UL STATE=ASSIGN)
+Change control TS to 7 until assinment is complete.
+Got 'TBF(TFI=0 TLLI=0xf1223344 DIR=UL STATE=ASSIGN)', TA=7
+TBF(TFI=0 TLLI=0xf1223344 DIR=UL STATE=ASSIGN)s start Packet Uplink Assignment (PACCH)
++++++++++++++++++++++++++ TX : Packet Uplink Assignment +++++++++++++++++++++++++
+------------------------- TX : Packet Uplink Assignment -------------------------
+Scheduling control message at RTS for TBF(TFI=0 TLLI=0xf1223344 DIR=UL STATE=ASSIGN) (TRX=0, TS=7)
+Sending data request: trx=0 ts=7 sapi=5 arfcn=0 fn=2654275 block=21 data=4f 28 5e 24 46 68 8f 1d 00 00 88 00 08 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 
+UL DATA TFI=0 received (V(Q)=0 .. V(R)=0)
+TBF(TFI=0 TLLI=0xf1223344 DIR=UL STATE=ASSIGN) restarting timer 3169 while old timer 3169 pending 
+- BSN 0 storing in window (0..63)
+- Raising V(R) to 1
+- Taking block 0 out, raising V(Q) to 1
+- Assembling frames: (len=23)
+-- Frame 1 starts at offset 0
+- Data length after length fields: 20
+TBF(TFI=0 TLLI=0xf1223344 DIR=UL STATE=ASSIGN) complete UL frame that fits precisely in last block: len=20
+LLC [PCU -> SGSN] TBF(TFI=0 TLLI=0xf1223344 DIR=UL STATE=ASSIGN) len=20
+No bctx
+Got MS: TLLI = 0xf1223344
diff --git a/tests/tbf/TbfTest.ok b/tests/tbf/TbfTest.ok
index 984e735..831a3dd 100644
--- a/tests/tbf/TbfTest.ok
+++ b/tests/tbf/TbfTest.ok
@@ -4,3 +4,7 @@
 === end test_tbf_imsi ===
 === start test_tbf_exhaustion ===
 === end test_tbf_exhaustion ===
+=== start test_tbf_single_phase ===
+=== end test_tbf_single_phase ===
+=== start test_tbf_two_phase ===
+=== end test_tbf_two_phase ===