From 09e716190581413c6a0dd282418a43f1fe932cf8 Mon Sep 17 00:00:00 2001
From: Neels Hofmeyr <neels@hofmeyr.de>
Date: Mon, 10 Aug 2020 19:13:27 +0200
Subject: hodec2: consider congestion resolution across TCH/F<->TCH/H

Change-Id: Id2af93f6e09a938d69e4a8f101235028d488616e
---
 src/osmo-bsc/handover_decision_2.c | 102 ++++++++++++++-----------------------
 tests/handover/handover_test.c     |   9 ++++
 2 files changed, 46 insertions(+), 65 deletions(-)

diff --git a/src/osmo-bsc/handover_decision_2.c b/src/osmo-bsc/handover_decision_2.c
index c818dbbde..e4edfe6e0 100644
--- a/src/osmo-bsc/handover_decision_2.c
+++ b/src/osmo-bsc/handover_decision_2.c
@@ -447,76 +447,47 @@ static uint8_t check_requirements(struct gsm_lchan *lchan, struct gsm_bts *bts,
 		return 0;
 	}
 
-	/* compatibility check for codecs.
-	 * if so, the candidates for full rate and half rate are selected */
-	switch (lchan->tch_mode) {
-	case GSM48_CMODE_SPEECH_V1:
-		switch (lchan->type) {
-		case GSM_LCHAN_TCH_F: /* mandatory */
-			requirement |= REQUIREMENT_A_TCHF;
-			break;
-		case GSM_LCHAN_TCH_H:
-			if (!bts->codec.hr) {
-				LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
-						 "tch_mode='%s' type='%s' not supported\n",
-						 get_value_string(gsm48_chan_mode_names,
-								  lchan->tch_mode),
-						 gsm_lchant_name(lchan->type));
-				break;
-			}
-			if (codec_type_is_supported(lchan->conn, GSM0808_SCT_HR1))
-				requirement |= REQUIREMENT_A_TCHH;
-			break;
-		default:
-			LOGPHOLCHAN(lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n",
-				    get_value_string(gsm48_chan_mode_names, lchan->tch_mode));
-			return 0;
-		}
-		break;
-	case GSM48_CMODE_SPEECH_EFR:
-		if (!bts->codec.efr) {
-			LOGPHOBTS(bts, LOGL_DEBUG, "EFR not supported\n");
-			break;
-		}
-		if (codec_type_is_supported(lchan->conn, GSM0808_SCT_FR2))
-			requirement |= REQUIREMENT_A_TCHF;
-		break;
-	case GSM48_CMODE_SPEECH_AMR:
-		if (!bts->codec.amr) {
-			LOGPHOBTS(bts, LOGL_DEBUG, "AMR not supported\n");
-			break;
-		}
-		if (codec_type_is_supported(lchan->conn, GSM0808_SCT_FR3))
-			requirement |= REQUIREMENT_A_TCHF;
-		if (codec_type_is_supported(lchan->conn, GSM0808_SCT_HR3))
-			requirement |= REQUIREMENT_A_TCHH;
-		break;
-	default:
-		LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "Not even considering: src is not a SPEECH mode lchan\n");
-		/* FIXME: should allow handover of non-speech lchans */
-		return 0;
+	/* To which TCH type and codec can we handover? Consider targets independently from the current lchan's TCH kind
+	 * and codec, to support TCH/F <-> TCH/H handovers and those that switch the codec type. */
+
+	/* TCH/F target? */
+	if (tchf_count) {
+		/* TCH/F timeslots are available, and FR1 is always supported. Could also end up using EFR or AMR codecs. */
+		LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "considering HO to TCH/F (%d available)\n", tchf_count);
+		requirement |= REQUIREMENT_A_TCHF;
+	} else {
+		LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not considering HO to TCH/F (0 available)\n");
 	}
+	/* If the EFR (FR2) codec is supported, we'd add requirement |= TCH/F. Since TCH/F is already always supported,
+	 * there is no need to check for EFR here. */
 
-	/* no candidate, because new cell is incompatible */
-	if (!requirement) {
-		LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, because codec of MS and BTS are incompatible\n");
-		return 0;
-	}
+	/* TCH/H target? */
+	if (tchh_count) {
+		/* TCH/H timeslots are available, check whether any half rate codec is supported. */
+		bool hr1_ms = codec_type_is_supported(lchan->conn, GSM0808_SCT_HR1);
+		bool hr1_bts = (bts->codec.hr != 0);
+		bool hr3_ms = codec_type_is_supported(lchan->conn, GSM0808_SCT_HR3);
+		bool hr3_bts = (bts->codec.amr != 0);
+		bool add_tchh = (hr1_ms && hr1_bts) || (hr3_ms && hr3_bts);
 
-	/* remove slot types that are not available */
-	if (!tchf_count && requirement & REQUIREMENT_A_TCHF) {
-		LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
-				 "removing TCH/F, since all TCH/F lchans are in use\n");
-		requirement &= ~(REQUIREMENT_A_TCHF);
-	}
-	if (!tchh_count && requirement & REQUIREMENT_A_TCHH) {
-		LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
-				 "removing TCH/H, since all TCH/H lchans are in use\n");
-		requirement &= ~(REQUIREMENT_A_TCHH);
+		if (add_tchh)
+			requirement |= REQUIREMENT_A_TCHH;
+
+		LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "%s HO to TCH/H"
+				 " (support by MS/BTS for HR1: %s/%s, HR3(AMR): %s/%s)\n",
+				 add_tchh ? "considering" : "not considering",
+				 hr1_ms ? "yes" : "no",
+				 hr1_bts ? "yes" : "no",
+				 hr3_ms ? "yes" : "no",
+				 hr3_bts ? "yes" : "no");
+	} else {
+		LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not considering HO to TCH/H, none available\n");
 	}
 
+	/* no candidate, because new cell is incompatible */
 	if (!requirement) {
-		LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, because no suitable slots available\n");
+		LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG,
+				 "Not a candidate, no lchans matching codecs supported by both MS and BTS are available\n");
 		return 0;
 	}
 
@@ -607,7 +578,8 @@ static uint8_t check_requirements(struct gsm_lchan *lchan, struct gsm_bts *bts,
 	 * free slots of the current cell _after_ handover/assignment */
 	count = bts_count_free_ts(current_bts,
 				  (lchan->type == GSM_LCHAN_TCH_H) ?
-				   GSM_PCHAN_TCH_H : GSM_PCHAN_TCH_F);
+				  GSM_PCHAN_TCH_H : GSM_PCHAN_TCH_F);
+
 	if (requirement & REQUIREMENT_A_TCHF) {
 		if (tchf_count - 1 >= count + 1)
 			requirement |= REQUIREMENT_C_TCHF;
diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c
index 6fb145ee6..3be51f164 100644
--- a/tests/handover/handover_test.c
+++ b/tests/handover/handover_test.c
@@ -571,6 +571,7 @@ static char *test_case_4[] = {
 
 	"create-bts", "2",
 	"set-min-free", "1", "TCH/H", "4",
+	"set-min-free", "1", "TCH/F", "4",
 	"create-ms", "0", "TCH/H", "HR",
 	"meas-rep", "0", "20","0", "1","0","30",
 	"expect-no-chan",
@@ -592,6 +593,7 @@ static char *test_case_5[] = {
 	"place.\n",
 
 	"create-bts", "2",
+	"set-min-free", "1", "TCH/H", "4",
 	"set-min-free", "1", "TCH/F", "4",
 	"create-ms", "0", "TCH/F", "FR",
 	"meas-rep", "0", "20","0", "1","0","30",
@@ -615,6 +617,7 @@ static char *test_case_6[] = {
 
 	"create-bts", "2",
 	"set-min-free", "1", "TCH/F", "4",
+	"set-min-free", "1", "TCH/H", "4",
 	"create-ms", "0", "TCH/F", "EFR",
 	"meas-rep", "0", "20","0", "1","0","30",
 	"expect-no-chan",
@@ -961,10 +964,16 @@ static char *test_case_19[] = {
 
 	"create-bts", "2",
 	"set-min-free", "0", "TCH/F", "4",
+	"set-min-free", "0", "TCH/H", "0",
 	"set-min-free", "1", "TCH/F", "4",
+	"set-min-free", "1", "TCH/H", "4",
 	"create-ms", "0", "TCH/F", "FR",
 	"create-ms", "0", "TCH/F", "FR",
 	"create-ms", "0", "TCH/F", "FR",
+	"create-ms", "0", "TCH/H", "AMR",
+	"create-ms", "0", "TCH/H", "AMR",
+	"create-ms", "0", "TCH/H", "AMR",
+	"create-ms", "0", "TCH/H", "AMR",
 	"create-ms", "1", "TCH/F", "FR",
 	"meas-rep", "0", "30","0", "1","0","20",
 	"expect-no-chan",
-- 
cgit v1.2.1

