bts: Verify SI3 GPRS indication reflects PCU interface status

When no PCU is connected to the BTS, the BTS should mask the GPRS
indicator from the SI3 rest octets to indicate "no GPRS support"
in the cell.  This will cause phones not even trying to send us
RACH requests for GPRS ATTACH, RAU, etc.

Change-Id: I37efb712ee0c513aea230beeb35e7dabce698a34
Related: OS#4023
Related: OS#3075
diff --git a/bts/BTS_Tests.ttcn b/bts/BTS_Tests.ttcn
index f60da24..0506e64 100644
--- a/bts/BTS_Tests.ttcn
+++ b/bts/BTS_Tests.ttcn
@@ -245,7 +245,7 @@
 			},
 			cell_sel_par := ts_CellSelPar_default,
 			rach_control := ts_RachCtrl_default,
-			rest_octets := '2B2B2B2B'O
+			rest_octets := '2C2B2B2B'O /* GPRS present */
 		}
 	}
 }
@@ -3983,6 +3983,174 @@
 	Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
 }
 
+/* Ensure that GPRS capability is not advertised before PCU socket conncet */
+private function f_get_si3(L1CTL_PT pt) runs on test_CT return SystemInformationType3 {
+	var L1ctlDlMessage l1_dl;
+	var SystemInformation si;
+	timer T := 5.0;
+	T.start;
+	alt {
+	[] pt.receive(tr_L1CTL_DATA_IND(t_RslChanNr_BCCH(0), ?)) -> value l1_dl {
+		/* somehow dec_SystemInformation will try to decode even non-RR as SI */
+		if (not (l1_dl.payload.data_ind.payload[1] ==  '06'O)) {
+			log("Ignoring non-RR SI ", l1_dl);
+			repeat;
+		}
+		si := dec_SystemInformation(l1_dl.payload.data_ind.payload)
+		if (not ischosen(si.payload.si3)) {
+			repeat;
+		}
+		}
+	[] pt.receive {
+		repeat;
+		}
+	[] T.timeout {
+		setverdict(fail, "Timeout waiting for SI3");
+		}
+	}
+	return si.payload.si3;
+}
+
+/* CSN.1 L/H logic: is the bit at the current position using "inverted logic" (true) or not? */
+private function f_bitpos_is_inv(integer idx) return boolean {
+	select (idx mod 8) {
+	case ((2, 4, 6, 7)) { return true; } /* 1-bits of 0x2B */
+	case else { return false; } /* 0-bits of 0x2B */
+	}
+}
+/* determine if the bit at position 'idx' in 'str' is a CSN.1 'H' (true) or 'L' (false) */
+private function f_bit_is_high(bitstring str, integer idx) return boolean {
+	var boolean invert := f_bitpos_is_inv(idx);
+	if (invert) {
+		if (str[idx] == '1'B) {
+			return false;
+		} else {
+			return true;
+		}
+	} else {
+		if (str[idx] == '1'B) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+}
+/* As the TITAN RAW codec cannot expres the CSN.1 L/H concept yet, we have to do this
+   manually.  Let's hope https://www.eclipse.org/forums/index.php/t/1099087/ takes off and
+   we can replace this piece of code soon ... */
+private function f_si3_has_gprs_indicator(OCT4 si3_restoctets) return boolean {
+	var bitstring bits := oct2bit(si3_restoctets);
+	var integer idx := 0;
+
+	if (f_bit_is_high(bits, idx)) {
+		/* skip Optional selection parameters */
+		idx := idx + 16;
+	} else {
+		idx := idx + 1;
+	}
+
+	if (f_bit_is_high(bits, idx)) {
+		/* skip Optional power offset */
+		idx := idx + 3;
+	} else {
+		idx := idx + 1;
+	}
+
+	/* skip SI2ter Indicator */
+	idx := idx + 1;
+
+	/* skip Early CM Sending Control */
+	idx := idx + 1;
+
+	/* skip Scheduling if and where */
+	if (f_bit_is_high(bits, idx)) {
+		idx := idx + 4;
+	} else {
+		idx := idx + 1;
+	}
+
+	return f_bit_is_high(bits, idx);
+}
+
+testcase TC_pcu_socket_noconnect_nosi3gprs() runs on test_CT {
+	var SystemInformationType3 si3;
+	timer T := 5.0;
+
+	/* don't call f_init() as this would connect PCU socket */
+	f_init_rsl(testcasename());
+	T.start;
+	alt {
+	[] RSL_CCHAN.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_UP});
+	[] T.timeout {
+		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for ASP_IPA_EVENT_UP");
+		}
+	}
+	f_rsl_bcch_fill(RSL_SYSTEM_INFO_3, ts_SI3_default);
+
+	f_init_l1ctl();
+	f_l1_tune(L1CTL);
+
+	f_sleep(2.0);
+	L1CTL.clear;
+	si3 := f_get_si3(L1CTL);
+	if (f_si3_has_gprs_indicator(si3.rest_octets)) {
+		setverdict(fail, "SI3 indicates GPRS even before PCU socket connected");
+	} else {
+		setverdict(pass);
+	}
+	Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
+}
+
+/* Ensure that GPRS capability is advertised after PCU socket connect */
+testcase TC_pcu_socket_connect_si3gprs() runs on test_CT {
+	var SystemInformationType3 si3;
+
+	/* this (among other things) establishes the first connection to the PCUIF socket */
+	f_init();
+	f_init_l1ctl();
+	f_l1_tune(L1CTL);
+
+	f_sleep(2.0);
+	L1CTL.clear;
+	si3 := f_get_si3(L1CTL);
+	if (not f_si3_has_gprs_indicator(si3.rest_octets)) {
+		setverdict(fail, "SI3 indicates no GPRS despite PCU socket connected");
+	} else {
+		setverdict(pass);
+	}
+	Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
+}
+
+/* Ensure that GPRS capability is no longer advertised after PCU socket disconnect */
+testcase TC_pcu_socket_disconnect_nosi3gprs() runs on test_CT {
+	var SystemInformationType3 si3;
+
+	/* this (among other things) establishes the first connection to the PCUIF socket */
+	f_init();
+	f_init_l1ctl();
+	f_l1_tune(L1CTL);
+
+	f_pcuif_close(PCU, g_pcu_conn_id);
+	g_pcu_conn_id := -1;
+
+	f_sleep(1.0);
+
+	/* re-connect */
+	PCU.clear;
+	f_init_pcu(PCU, testcasename(), g_pcu_conn_id, g_pcu_last_info);
+
+	f_sleep(2.0);
+	L1CTL.clear;
+	si3 := f_get_si3(L1CTL);
+	if (f_si3_has_gprs_indicator(si3.rest_octets)) {
+		setverdict(fail, "SI3 indicates GPRS after PCU socket disconnected");
+	} else {
+		setverdict(pass);
+	}
+
+	Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
+}
+
 
 /***********************************************************************
  * Osmocom Style Dynamic Timeslot Support
@@ -5360,6 +5528,9 @@
 		execute( TC_pcu_rr_suspend() );
 		execute( TC_pcu_socket_connect_multi() );
 		execute( TC_pcu_socket_reconnect() );
+		execute( TC_pcu_socket_noconnect_nosi3gprs() );
+		execute( TC_pcu_socket_connect_si3gprs() );
+		execute( TC_pcu_socket_disconnect_nosi3gprs() );
 	} else {
 		log("PCU socket path not available, skipping PCU tests");
 	}