generalize channel activation / channel mode modify

* we only need one piece of code to calculate rsl_ie_chan_mode from
  our run-time data structures (gsm_lchan)
* add some more channel modes for TCH/H and data
* use enum's to make the compiler warn us about unhandled enum values
* make sure the caller determines the (signalling,speech,data) mode
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index 9e4d590..a2a01d3 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -266,16 +266,23 @@
 } __attribute__ ((packed));
 #define RSL_CMOD_DTXu		0x01	/* uplink */
 #define RSL_CMOD_DTXd		0x02	/* downlink */
-#define RSL_CMOD_SPD_SPEECH	0x01
-#define RSL_CMOD_SPD_DATA	0x02
-#define RSL_CMOD_SPD_SIGN	0x03
+enum rsl_cmod_spd {
+	RSL_CMOD_SPD_SPEECH	= 0x01,
+	RSL_CMOD_SPD_DATA	= 0x02,
+	RSL_CMOD_SPD_SIGN	= 0x03,
+};
 #define RSL_CMOD_CRT_SDCCH	0x01
 #define RSL_CMOD_CRT_TCH_Bm	0x08	/* full-rate */
 #define RSL_CMOD_CRT_TCH_Lm	0x09	/* half-rate */
 /* FIXME: More CRT types */
+/* Speech */
 #define RSL_CMOD_SP_GSM1	0x01
 #define RSL_CMOD_SP_GSM2	0x11
 #define RSL_CMOD_SP_GSM3	0x21
+/* Data */
+#define RSL_CMOD_SP_NT_14k5	0x58
+#define RSL_CMOD_SP_NT_12k0	0x50
+#define RSL_CMOD_SP_NT_6k0	0x51
 
 /* Chapter 9.3.5 */
 struct rsl_ie_chan_ident {
@@ -459,7 +466,7 @@
 		      u_int8_t bs_power, u_int8_t ms_power,
 		      u_int8_t ta);
 int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, 
-			    u_int8_t ta, u_int8_t mode);
+			    u_int8_t ta);
 int rsl_chan_mode_modify_req(struct gsm_lchan *ts);
 int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len,
 		   u_int8_t *ms_ident, u_int8_t chan_needed);
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index a885d5a..c93540b 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -48,14 +48,16 @@
 	u_int8_t mode;
 } __attribute__ ((packed));
 
-#define GSM48_CMODE_SIGN	0x00
-#define GSM48_CMODE_SPEECH_V1	0x01
-#define GSM48_CMODE_SPEECH_EFR	0x21
-#define GSM48_CMODE_SPEECH_AMR	0x41
-#define GSM48_CMODE_DATA_14k5	0x0f
-#define GSM48_CMODE_DATA_12k0	0x03
-#define GSM48_CMODE_DATA_6k0	0x0b
-#define GSM48_CMODE_DATA_3k6	0x23
+enum gsm48_chan_mode {
+	GSM48_CMODE_SIGN	= 0x00,
+	GSM48_CMODE_SPEECH_V1	= 0x01,
+	GSM48_CMODE_SPEECH_EFR	= 0x21,
+	GSM48_CMODE_SPEECH_AMR	= 0x41,
+	GSM48_CMODE_DATA_14k5	= 0x0f,
+	GSM48_CMODE_DATA_12k0	= 0x03,
+	GSM48_CMODE_DATA_6k0	= 0x0b,
+	GSM48_CMODE_DATA_3k6	= 0x23,
+};
 
 /* Chapter 9.1.18 */
 struct gsm48_imm_ass {
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index e6dffa8..84e3c79 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -41,6 +41,7 @@
 
 #include <openbsc/timer.h>
 #include <openbsc/gsm_04_08.h>
+#include <openbsc/abis_rsl.h>
 #include <openbsc/mncc.h>
 #include <openbsc/tlv.h>
 
@@ -165,8 +166,10 @@
 	u_int8_t nr;
 	/* The logical channel type */
 	enum gsm_chan_t type;
+	/* RSL channel mode */
+	enum rsl_cmod_spd rsl_cmode;
 	/* If TCH, traffic channel mode */
-	enum gsm_chan_t tch_mode;
+	enum gsm48_chan_mode tch_mode;
 	/* Power levels for MS and BTS */
 	u_int8_t bs_power;
 	u_int8_t ms_power;
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 2c28a5e..eb43bd5 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -530,6 +530,62 @@
 	return abis_rsl_sendmsg(msg);
 }
 
+static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
+				   struct gsm_lchan *lchan)
+{
+	memset(cm, 0, sizeof(cm));
+
+	/* FIXME: what to do with data calls ? */
+	cm->dtx_dtu = 0x00;
+
+	/* set TCH Speech/Data */
+	cm->spd_ind = lchan->rsl_cmode;
+
+	switch (lchan->type) {
+	case GSM_LCHAN_SDCCH:
+		cm->chan_rt = RSL_CMOD_CRT_SDCCH;
+		break;
+	case GSM_LCHAN_TCH_F:
+		cm->chan_rt = RSL_CMOD_CRT_TCH_Bm;
+		break;
+	case GSM_LCHAN_TCH_H:
+		cm->chan_rt = RSL_CMOD_CRT_TCH_Lm;
+		break;
+	case GSM_LCHAN_NONE:
+	case GSM_LCHAN_UNKNOWN:
+	default:
+		return -EINVAL;
+	}
+
+	switch (lchan->tch_mode) {
+	case GSM48_CMODE_SIGN:
+		cm->chan_rate = 0;
+		break;
+	case GSM48_CMODE_SPEECH_V1:
+		cm->chan_rate = RSL_CMOD_SP_GSM1;
+		break;
+	case GSM48_CMODE_SPEECH_EFR:
+		cm->chan_rate = RSL_CMOD_SP_GSM2;
+		break;
+	case GSM48_CMODE_SPEECH_AMR:
+		cm->chan_rate = RSL_CMOD_SP_GSM3;
+		break;
+	case GSM48_CMODE_DATA_14k5:
+		cm->chan_rate = RSL_CMOD_SP_NT_14k5;
+		break;
+	case GSM48_CMODE_DATA_12k0:
+		cm->chan_rate = RSL_CMOD_SP_NT_12k0;
+		break;
+	case GSM48_CMODE_DATA_6k0:
+		cm->chan_rate = RSL_CMOD_SP_NT_6k0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 /* Chapter 8.4.1 */
 #if 0
 int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr,
@@ -567,45 +623,20 @@
 #endif
 
 int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, 
-			    u_int8_t ta, u_int8_t mode)
+			    u_int8_t ta)
 {
 	struct abis_rsl_dchan_hdr *dh;
 	struct msgb *msg;
+	int rc;
 
 	u_int8_t chan_nr = lchan2chan_nr(lchan);
 	u_int16_t arfcn = lchan->ts->trx->arfcn;
 	struct rsl_ie_chan_mode cm;
 	struct rsl_ie_chan_ident ci;
 
-	memset(&cm, 0, sizeof(cm));
-	/* FIXME: what to do with data calls ? */
-	cm.dtx_dtu = 0x00;
-	switch (lchan->type) {
-	case GSM_LCHAN_SDCCH:
-		cm.spd_ind = RSL_CMOD_SPD_SIGN;
-		cm.chan_rt = RSL_CMOD_CRT_SDCCH;
-		cm.chan_rate = 0x00;
-		break;
-	case GSM_LCHAN_TCH_F:
-		cm.chan_rt = RSL_CMOD_CRT_TCH_Bm;
-		switch (mode) {
-		case RSL_CMOD_SPD_SIGN:
-			cm.spd_ind = RSL_CMOD_SPD_SIGN;
-			cm.chan_rate = 0x00;
-			break;
-		case RSL_CMOD_SPD_SPEECH:
-			cm.spd_ind = RSL_CMOD_SPD_SPEECH;
-			cm.chan_rate = RSL_CMOD_SP_GSM2;
-			break;
-		}
-		break;
-	case GSM_LCHAN_TCH_H:
-		DEBUGP(DRSL, "Unimplemented TCH_H activation\n");
-		return -1;
-	case GSM_LCHAN_UNKNOWN:
-	case GSM_LCHAN_NONE:
-		return -1;
-	}
+	rc = channel_mode_from_lchan(&cm, lchan);
+	if (rc < 0)
+		return rc;
 
 	memset(&ci, 0, sizeof(ci));
 	ci.chan_desc.iei = 0x64;
@@ -642,54 +673,14 @@
 {
 	struct abis_rsl_dchan_hdr *dh;
 	struct msgb *msg;
+	int rc;
 
 	u_int8_t chan_nr = lchan2chan_nr(lchan);
 	struct rsl_ie_chan_mode cm;
 
-	memset(&cm, 0, sizeof(cm));
-
-	/* FIXME: what to do with data calls ? */
-	cm.dtx_dtu = 0x00;
-	switch (lchan->type) {
-	/* todo more modes */
-	case GSM_LCHAN_TCH_F:
-		cm.spd_ind = RSL_CMOD_SPD_SPEECH;
-		cm.chan_rt = RSL_CMOD_CRT_TCH_Bm;
-		switch(lchan->tch_mode) {
-		case GSM48_CMODE_SPEECH_V1:
-			cm.chan_rate = RSL_CMOD_SP_GSM1;
-			break;
-		case GSM48_CMODE_SPEECH_EFR:
-			cm.chan_rate = RSL_CMOD_SP_GSM2;
-			break;
-		case GSM48_CMODE_SPEECH_AMR:
-			cm.chan_rate = RSL_CMOD_SP_GSM3;
-			break;
-		default:
-			DEBUGP(DRSL, "Unimplemented channel modification\n");
-			return -1;
-		}
-		break;
-	case GSM_LCHAN_TCH_H:
-		cm.spd_ind = RSL_CMOD_SPD_SPEECH;
-		cm.chan_rt = RSL_CMOD_CRT_TCH_Lm;
-		switch (lchan->tch_mode) {
-		case GSM48_CMODE_SPEECH_V1:
-			cm.chan_rate = RSL_CMOD_SP_GSM1;
-			break;
-		/* Half-rate has no V2 */
-		case GSM48_CMODE_SPEECH_AMR:
-			cm.chan_rate = RSL_CMOD_SP_GSM3;
-			break;
-		default:
-			DEBUGP(DRSL, "Unimplemented channel modification\n");
-			return -1;
-		}
-		break;
-	default:
-		DEBUGP(DRSL, "Unimplemented channel modification\n");
-		return -1;
-	}
+	rc = channel_mode_from_lchan(&cm, lchan);
+	if (rc < 0)
+		return rc;
 
 	msg = rsl_msgb_alloc();
 	dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
@@ -1099,7 +1090,8 @@
 	subch = lchan->nr;
 	
 	lchan->ms_power = lchan->bs_power = 0x0f; /* 30dB reduction */
-	rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, RSL_CMOD_SPD_SIGN);
+	lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
+	rsl_chan_activate_lchan(lchan, 0x00, rqd_ta);
 
 	/* create IMMEDIATE ASSIGN 04.08 messge */
 	memset(&ia, 0, sizeof(ia));
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index a7577d2..078e95f 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -1746,6 +1746,7 @@
 		DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n");
 		/* We've successfully modified the MS side of the channel,
 		 * now go on to modify the BTS side of the channel */
+		msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
 		rc = rsl_chan_mode_modify_req(msg->lchan);
 		break;
 	case GSM48_MT_RR_STATUS: