commit 037c6084ab68785666a84c89a5f5d45de6a44620
Author: Andreas Eversberg <jolly@eversberg.eu>
Date:   Sat Dec 7 18:32:28 2013 +0100

    Each BTS can be configured for speech support (other than GSM full rate)
    
    Speech codings which are not supported by BTS will be removed from the
    bearer capability information element after parsing. This way it is not
    required for the MNCC application to consider support of each BTS.
    
    Only GSM full rate is supported by default.

diff --git a/openbsc/include/openbsc/gsm_data_shared.h b/openbsc/include/openbsc/gsm_data_shared.h
index 84d15ef..4a41498 100644
--- a/openbsc/include/openbsc/gsm_data_shared.h
+++ b/openbsc/include/openbsc/gsm_data_shared.h
@@ -143,6 +143,12 @@ struct bts_ul_meas {
 	uint8_t inv_rssi;
 };
 
+struct bts_codec_conf {
+	uint8_t hr;
+	uint8_t efr;
+	uint8_t amr;
+};
+
 struct amr_mode {
 	uint8_t mode;
 	uint8_t threshold;
@@ -704,6 +710,9 @@ struct gsm_bts {
 
 	/* exclude the BTS from the global RF Lock handling */
 	int excl_from_rf_lock;
+
+	/* supported codecs beside FR */
+	struct bts_codec_conf codec;
 #endif /* ROLE_BSC */
 	void *role;
 };
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index 0066719..fd7fbcf 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -629,6 +629,15 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
 		}
 	}
 
+	vty_out(vty, "  codec-support fr");
+	if (bts->codec.hr)
+		vty_out(vty, " hr");
+	if (bts->codec.efr)
+		vty_out(vty, " efr");
+	if (bts->codec.amr)
+		vty_out(vty, " amr");
+	vty_out(vty, "%s", VTY_NEWLINE);
+
 	config_write_bts_gprs(vty, bts);
 
 	if (bts->excl_from_rf_lock)
@@ -2635,6 +2644,73 @@ DEFUN(cfg_bts_no_excl_rf_lock,
 	return CMD_SUCCESS;
 }
 
+static void _get_codec_from_arg(struct vty *vty, int argc, const char *argv[])
+{
+	struct gsm_bts *bts = vty->index;
+	struct bts_codec_conf *codec = &bts->codec;
+	int i;
+
+	codec->hr = 0;
+	codec->efr = 0;
+	codec->amr = 0;
+	for (i = 0; i < argc; i++) {
+		if (!strcmp(argv[i], "hr"))
+			codec->hr = 1;
+		if (!strcmp(argv[i], "efr"))
+			codec->efr = 1;
+		if (!strcmp(argv[i], "amr"))
+			codec->amr = 1;
+	}
+}
+
+#define CODEC_PAR_STR	" (hr|efr|amr)"
+#define CODEC_HELP_STR	"Half Rate\n" \
+			"Enhanced Full Rate\nAdaptive Multirate\n"
+
+DEFUN(cfg_bts_codec0, cfg_bts_codec0_cmd,
+	"codec-support fr",
+	"Codec Support settings\nFullrate\n")
+{
+	_get_codec_from_arg(vty, 0, argv);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_codec1, cfg_bts_codec1_cmd,
+	"codec-support fr" CODEC_PAR_STR,
+	"Codec Support settings\nFullrate\n"
+	CODEC_HELP_STR)
+{
+	_get_codec_from_arg(vty, 1, argv);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_codec2, cfg_bts_codec2_cmd,
+	"codec-support fr" CODEC_PAR_STR CODEC_PAR_STR,
+	"Codec Support settings\nFullrate\n"
+	CODEC_HELP_STR CODEC_HELP_STR)
+{
+	_get_codec_from_arg(vty, 2, argv);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_codec3, cfg_bts_codec3_cmd,
+	"codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR,
+	"Codec Support settings\nFullrate\n"
+	CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR)
+{
+	_get_codec_from_arg(vty, 3, argv);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_codec4, cfg_bts_codec4_cmd,
+	"codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR,
+	"Codec Support settings\nFullrate\n"
+	CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR)
+{
+	_get_codec_from_arg(vty, 4, argv);
+	return CMD_SUCCESS;
+}
+
 #define TRX_TEXT "Radio Transceiver\n"
 
 /* per TRX configuration */
@@ -3231,6 +3307,11 @@ int bsc_vty_init(const struct log_info *cat)
 	install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd);
 	install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd);
 	install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd);
+	install_element(BTS_NODE, &cfg_bts_codec0_cmd);
+	install_element(BTS_NODE, &cfg_bts_codec1_cmd);
+	install_element(BTS_NODE, &cfg_bts_codec2_cmd);
+	install_element(BTS_NODE, &cfg_bts_codec3_cmd);
+	install_element(BTS_NODE, &cfg_bts_codec4_cmd);
 
 	install_element(BTS_NODE, &cfg_trx_cmd);
 	install_node(&trx_node, dummy_config_write);
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
index 997e996..f5b898a 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -79,6 +79,29 @@ struct gsm_lai {
 	uint16_t lac;
 };
 
+static int apply_codec_restrictions(struct gsm_bts *bts,
+	struct gsm_mncc_bearer_cap *bcap)
+{
+	int i, j;
+
+	/* remove unsupported speech versions from list */
+	for (i = 0, j = 0; bcap->speech_ver[i] >= 0; i++) {
+		if (bcap->speech_ver[i] == GSM48_BCAP_SV_FR)
+			bcap->speech_ver[j++] = GSM48_BCAP_SV_FR;
+		if (bcap->speech_ver[i] == GSM48_BCAP_SV_EFR && bts->codec.efr)
+			bcap->speech_ver[j++] = GSM48_BCAP_SV_EFR;
+		if (bcap->speech_ver[i] == GSM48_BCAP_SV_AMR_F && bts->codec.amr)
+			bcap->speech_ver[j++] = GSM48_BCAP_SV_AMR_F;
+		if (bcap->speech_ver[i] == GSM48_BCAP_SV_HR && bts->codec.hr)
+			bcap->speech_ver[j++] = GSM48_BCAP_SV_HR;
+		if (bcap->speech_ver[i] == GSM48_BCAP_SV_AMR_H && bts->codec.amr)
+			bcap->speech_ver[j++] = GSM48_BCAP_SV_AMR_H;
+	}
+	bcap->speech_ver[j] = -1;
+
+	return 0;
+}
+
 static uint32_t new_callref = 0x80000001;
 
 void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg)
@@ -1784,6 +1807,7 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
 		setup.fields |= MNCC_F_BEARER_CAP;
 		gsm48_decode_bearer_cap(&setup.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+		apply_codec_restrictions(trans->conn->bts, &setup.bearer_cap);
 	}
 	/* facility */
 	if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
@@ -1937,6 +1961,7 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
 		call_conf.fields |= MNCC_F_BEARER_CAP;
 		gsm48_decode_bearer_cap(&call_conf.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+		apply_codec_restrictions(trans->conn->bts, &call_conf.bearer_cap);
 	}
 	/* cause */
 	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
@@ -2625,6 +2650,7 @@ static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg)
 		modify.fields |= MNCC_F_BEARER_CAP;
 		gsm48_decode_bearer_cap(&modify.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+		apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap);
 	}
 
 	new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY);
@@ -2667,6 +2693,7 @@ static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg
 		modify.fields |= MNCC_F_BEARER_CAP;
 		gsm48_decode_bearer_cap(&modify.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+		apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap);
 	}
 
 	new_cc_state(trans, GSM_CSTATE_ACTIVE);
@@ -2707,6 +2734,7 @@ static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg)
 		modify.fields |= GSM48_IE_BEARER_CAP;
 		gsm48_decode_bearer_cap(&modify.bearer_cap,
 				  TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+		apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap);
 	}
 	/* cause */
 	if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
diff --git a/openbsc/src/libmsc/mncc_builtin.c b/openbsc/src/libmsc/mncc_builtin.c
index 617cbf2..be35454 100644
--- a/openbsc/src/libmsc/mncc_builtin.c
+++ b/openbsc/src/libmsc/mncc_builtin.c
@@ -43,7 +43,7 @@ static LLIST_HEAD(call_list);
 static uint32_t new_callref = 0x00000001;
 
 struct mncc_int mncc_int = {
-	.def_codec = { GSM48_CMODE_SPEECH_EFR, GSM48_CMODE_SPEECH_V1 },
+	.def_codec = { GSM48_CMODE_SPEECH_V1, GSM48_CMODE_SPEECH_V1 },
 };
 
 static void free_call(struct gsm_call *call)
