RR: add VAMOS channel modes

Also add functions to convert between VAMOS and non-VAMOS speech modes.

Related: SYS#4895 SYS#5315
Change-Id: Ie0ea592da5610ae70290106d004e549cf3212a89
diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h
index fdaa572..c1ca630 100644
--- a/include/osmocom/gsm/gsm48.h
+++ b/include/osmocom/gsm/gsm48.h
@@ -118,3 +118,6 @@
 
 #define gsm48_push_l3hdr_tid(msg, pdisc, tid, msg_type) \
 	gsm48_push_l3hdr(msg, (pdisc & 0x0f) | (tid << 4), msg_type)
+
+enum gsm48_chan_mode gsm48_chan_mode_to_vamos(enum gsm48_chan_mode mode);
+enum gsm48_chan_mode gsm48_chan_mode_to_non_vamos(enum gsm48_chan_mode mode);
diff --git a/include/osmocom/gsm/protocol/gsm_04_08.h b/include/osmocom/gsm/protocol/gsm_04_08.h
index a103c32..c9cc667 100644
--- a/include/osmocom/gsm/protocol/gsm_04_08.h
+++ b/include/osmocom/gsm/protocol/gsm_04_08.h
@@ -749,6 +749,10 @@
 	GSM48_CMODE_DATA_12k0	= 0x03,
 	GSM48_CMODE_DATA_6k0	= 0x0b,
 	GSM48_CMODE_DATA_3k6	= 0x13,
+	GSM48_CMODE_SPEECH_V1_VAMOS	= 0xc1,
+	GSM48_CMODE_SPEECH_V2_VAMOS	= 0xc2,
+	GSM48_CMODE_SPEECH_V3_VAMOS	= 0xc3,
+	GSM48_CMODE_SPEECH_V5_VAMOS	= 0xc5,
 };
 
 extern const struct value_string gsm48_chan_mode_names[];
diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c
index e12fda5..927e639 100644
--- a/src/gsm/gsm48.c
+++ b/src/gsm/gsm48.c
@@ -432,9 +432,55 @@
 	{ GSM48_CMODE_DATA_12k0,	"DATA_12k0" },
 	{ GSM48_CMODE_DATA_6k0,		"DATA_6k0" },
 	{ GSM48_CMODE_DATA_3k6,		"DATA_3k6" },
+	{ GSM48_CMODE_SPEECH_V1_VAMOS,	"SPEECH_V1_VAMOS" },
+	{ GSM48_CMODE_SPEECH_V2_VAMOS,	"SPEECH_V2_VAMOS" },
+	{ GSM48_CMODE_SPEECH_V3_VAMOS,	"SPEECH_V3_VAMOS" },
+	{ GSM48_CMODE_SPEECH_V5_VAMOS,	"SPEECH_V5_VAMOS" },
 	{ 0,				NULL },
 };
 
+/*! Translate GSM48_CMODE_SPEECH_* to its corresponding GSM48_CMODE_SPEECH_*_VAMOS mode.
+ * If the mode has no equivalent VAMOS mode, return a negative value.
+ */
+enum gsm48_chan_mode gsm48_chan_mode_to_vamos(enum gsm48_chan_mode mode)
+{
+	switch (mode) {
+	case GSM48_CMODE_SPEECH_V1:
+	case GSM48_CMODE_SPEECH_V1_VAMOS:
+		return GSM48_CMODE_SPEECH_V1_VAMOS;
+	case GSM48_CMODE_SPEECH_EFR:
+	case GSM48_CMODE_SPEECH_V2_VAMOS:
+		return GSM48_CMODE_SPEECH_V2_VAMOS;
+	case GSM48_CMODE_SPEECH_AMR:
+	case GSM48_CMODE_SPEECH_V3_VAMOS:
+		return GSM48_CMODE_SPEECH_V3_VAMOS;
+	case GSM48_CMODE_SPEECH_V5_VAMOS:
+		return GSM48_CMODE_SPEECH_V5_VAMOS;
+	default:
+		return -1;
+	}
+}
+
+/*! Translate GSM48_CMODE_SPEECH_*_VAMOS to its corresponding GSM48_CMODE_SPEECH_* non-vamos mode.
+ * If the mode has no equivalent non-VAMOS mode, return a negative value.
+ */
+enum gsm48_chan_mode gsm48_chan_mode_to_non_vamos(enum gsm48_chan_mode mode)
+{
+	switch (mode) {
+	case GSM48_CMODE_SPEECH_V1_VAMOS:
+	case GSM48_CMODE_SPEECH_V1:
+		return GSM48_CMODE_SPEECH_V1;
+	case GSM48_CMODE_SPEECH_V2_VAMOS:
+	case GSM48_CMODE_SPEECH_EFR:
+		return GSM48_CMODE_SPEECH_EFR;
+	case GSM48_CMODE_SPEECH_V3_VAMOS:
+	case GSM48_CMODE_SPEECH_AMR:
+		return GSM48_CMODE_SPEECH_AMR;
+	default:
+		return -1;
+	}
+}
+
 const struct value_string gsm_chan_t_names[] = {
 	{ GSM_LCHAN_NONE,	"NONE" },
 	{ GSM_LCHAN_SDCCH,	"SDCCH" },
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 7b2c18f..56a57b8 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -372,6 +372,8 @@
 gsm48_generate_mid_from_imsi;
 gsm48_generate_mid_from_tmsi;
 gsm48_mi_to_string;
+gsm48_chan_mode_to_vamos;
+gsm48_chan_mode_to_non_vamos;
 osmo_mobile_identity_to_str_buf;
 osmo_mobile_identity_to_str_c;
 osmo_mobile_identity_cmp;