Add VTY command to specify default speech codec

In order to have the MNCC application reliably decide on the codec type,
it needs to know if we are running on a TCH/F or TCH/H.  Thus, we pass
lchan_mode as a new parameter to the 'struct gsm_mncc'
diff --git a/openbsc/src/libcommon/common_vty.c b/openbsc/src/libcommon/common_vty.c
index 5b4b296..e8486f4 100644
--- a/openbsc/src/libcommon/common_vty.c
+++ b/openbsc/src/libcommon/common_vty.c
@@ -86,12 +86,11 @@
 	case PGROUP_NODE:
 		vty->node = NAT_NODE;
 		break;
-	case MSC_NODE:
-		vty->node = CONFIG_NODE;
-		break;
 	case TRUNK_NODE:
 		vty->node = MGCP_NODE;
 		break;
+	case MSC_NODE:
+	case MNCC_INT_NODE:
 	default:
 		vty->node = CONFIG_NODE;
 	}
@@ -161,6 +160,7 @@
 		vty->index = NULL;
 		break;
 	case MSC_NODE:
+	case MNCC_INT_NODE:
 		vty->node = CONFIG_NODE;
 		break;
 	case TRUNK_NODE:
@@ -197,6 +197,7 @@
 	case NAT_BSC_NODE:
 	case PGROUP_NODE:
 	case MSC_NODE:
+	case MNCC_INT_NODE:
 		vty_config_unlock(vty);
 		vty->node = ENABLE_NODE;
 		vty->index = NULL;
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
index dcb6e11..0cd707d 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -1839,6 +1839,8 @@
 
 	memset(&setup, 0, sizeof(struct gsm_mncc));
 	setup.callref = trans->callref;
+	if (trans->conn && trans->conn->lchan)
+		setup.lchan_type = trans->conn->lchan->type;
 	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 	/* emergency setup is identified by msg_type */
 	if (msg_type == GSM48_MT_CC_EMERG_SETUP)
@@ -1995,6 +1997,8 @@
 
 	memset(&call_conf, 0, sizeof(struct gsm_mncc));
 	call_conf.callref = trans->callref;
+	if (trans->conn && trans->conn->lchan)
+		call_conf.lchan_type = trans->conn->lchan->type;
 	tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
 #if 0
 	/* repeat */
diff --git a/openbsc/src/libmsc/mncc_builtin.c b/openbsc/src/libmsc/mncc_builtin.c
index 105f1dd..31d9b43 100644
--- a/openbsc/src/libmsc/mncc_builtin.c
+++ b/openbsc/src/libmsc/mncc_builtin.c
@@ -30,6 +30,7 @@
 #include <openbsc/gsm_04_08.h>
 #include <openbsc/debug.h>
 #include <openbsc/mncc.h>
+#include <openbsc/mncc_int.h>
 #include <osmocom/core/talloc.h>
 #include <openbsc/gsm_data.h>
 #include <openbsc/transaction.h>
@@ -41,6 +42,10 @@
 
 static uint32_t new_callref = 0x00000001;
 
+struct mncc_int mncc_int = {
+	.def_codec = { GSM48_CMODE_SPEECH_EFR, GSM48_CMODE_SPEECH_V1 },
+};
+
 static void free_call(struct gsm_call *call)
 {
 	llist_del(&call->entry);
@@ -60,6 +65,15 @@
 	return NULL;
 }
 
+static uint8_t determine_lchan_mode(struct gsm_mncc *setup)
+{
+	/* FIXME: check codec capabilities of the phone */
+
+	if (setup->lchan_type == GSM_LCHAN_TCH_F)
+		return mncc_int.def_codec[0];
+	else
+		return mncc_int.def_codec[1];
+}
 
 /* on incoming call, look up database and send setup to remote subscr. */
 static int mncc_setup_ind(struct gsm_call *call, int msg_type,
@@ -112,7 +126,7 @@
 	/* modify mode */
 	memset(&mncc, 0, sizeof(struct gsm_mncc));
 	mncc.callref = call->callref;
-	mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR;
+	mncc.lchan_mode = determine_lchan_mode(setup);
 	DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref);
 	mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, &mncc);
 
@@ -346,7 +360,7 @@
 		break;
 	case MNCC_CALL_CONF_IND:
 		/* we now need to MODIFY the channel */
-		data->lchan_mode = GSM48_CMODE_SPEECH_EFR;
+		data->lchan_mode = determine_lchan_mode(data);
 		mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, data);
 		break;
 	case MNCC_ALERT_IND:
diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c
index 1d3ffe9..cdda7e4 100644
--- a/openbsc/src/libmsc/vty_interface_layer3.c
+++ b/openbsc/src/libmsc/vty_interface_layer3.c
@@ -45,6 +45,7 @@
 #include <openbsc/gsm_04_80.h>
 #include <openbsc/chan_alloc.h>
 #include <openbsc/sms_queue.h>
+#include <openbsc/mncc_int.h>
 
 extern struct gsm_network *gsmnet_from_vty(struct vty *v);
 
@@ -762,6 +763,69 @@
 	return CMD_SUCCESS;
 }
 
+
+DEFUN(cfg_mncc_int, cfg_mncc_int_cmd,
+      "mncc-int", "Configure internal MNCC handler")
+{
+	vty->node = MNCC_INT_NODE;
+
+	return CMD_SUCCESS;
+}
+
+static struct cmd_node mncc_int_node = {
+	MNCC_INT_NODE,
+	"%s(mncc-int)#",
+	1,
+};
+
+static const struct value_string tchf_codec_names[] = {
+	{ GSM48_CMODE_SPEECH_V1,	"fr" },
+	{ GSM48_CMODE_SPEECH_EFR,	"efr" },
+	{ GSM48_CMODE_SPEECH_AMR,	"amr" },
+	{ 0, NULL }
+};
+
+static const struct value_string tchh_codec_names[] = {
+	{ GSM48_CMODE_SPEECH_V1,	"hr" },
+	{ GSM48_CMODE_SPEECH_AMR,	"amr" },
+	{ 0, NULL }
+};
+
+static int config_write_mncc_int(struct vty *vty)
+{
+	vty_out(vty, "mncc-int%s", VTY_NEWLINE);
+	vty_out(vty, " default-codec tch-f %s%s",
+		get_value_string(tchf_codec_names, mncc_int.def_codec[0]),
+		VTY_NEWLINE);
+	vty_out(vty, " default-codec tch-h %s%s",
+		get_value_string(tchh_codec_names, mncc_int.def_codec[1]),
+		VTY_NEWLINE);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(mnccint_def_codec_f,
+      mnccint_def_codec_f_cmd,
+      "default-codec tch-f (fr|efr|amr)",
+      "Set default codec\n" "Codec for TCH/F\n"
+      "Full-Rate\n" "Enhanced Full-Rate\n" "Adaptive Multi-Rate\n")
+{
+	mncc_int.def_codec[0] = get_string_value(tchf_codec_names, argv[0]);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(mnccint_def_codec_h,
+      mnccint_def_codec_h_cmd,
+      "default-codec tch-h (hr|amr)",
+      "Set default codec\n" "Codec for TCH/H\n"
+      "Half-Rate\n" "Adaptive Multi-Rate\n")
+{
+	mncc_int.def_codec[1] = get_string_value(tchh_codec_names, argv[0]);
+
+	return CMD_SUCCESS;
+}
+
 int bsc_vty_init_extra(void)
 {
 	osmo_signal_register_handler(SS_SCALL, scall_cbfn, NULL);
@@ -794,5 +858,11 @@
 	install_element(ENABLE_NODE, &smsqueue_fail_cmd);
 	install_element(ENABLE_NODE, &subscriber_send_pending_sms_cmd);
 
+	install_element(CONFIG_NODE, &cfg_mncc_int_cmd);
+	install_node(&mncc_int_node, config_write_mncc_int);
+	install_default(MNCC_INT_NODE);
+	install_element(MNCC_INT_NODE, &mnccint_def_codec_f_cmd);
+	install_element(MNCC_INT_NODE, &mnccint_def_codec_h_cmd);
+
 	return 0;
 }