edge: Add max_mcs_ul and max_mcs_dl config

This sets the maximum MCS encoding used for EGPRS RLC data blocks
in either direction.

The following VTY command are added to node config-pcu:

 - mcs max <1-9>         set maximum for both, uplink and downlink
 - mcs max <1-9> <1-9>   set maximum for downlink and uplink (in that
                         order)
 - no mcs max            do not limit

Note that using a value of 4 or below for each direction implies
that a GMSK-only TBF may be assumed, which for instance would allow
the use of the GPRS MS class instead of the possibly more restrictive
EGPRS MS class.

Sponsored-by: On-Waves ehf
diff --git a/src/bts.h b/src/bts.h
index 704a5be..1498ced 100644
--- a/src/bts.h
+++ b/src/bts.h
@@ -156,6 +156,7 @@
 	uint8_t cs4;
 	uint8_t initial_cs_dl, initial_cs_ul;
 	uint8_t max_cs_dl, max_cs_ul;
+	uint8_t max_mcs_dl, max_mcs_ul;
 	uint8_t force_cs;	/* 0=use from BTS 1=use from VTY */
 	uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
 	uint32_t llc_discard_csec;
diff --git a/src/pcu_main.cpp b/src/pcu_main.cpp
index 77e1082..4156c4b 100644
--- a/src/pcu_main.cpp
+++ b/src/pcu_main.cpp
@@ -179,6 +179,8 @@
 	bts->cs_adj_lower_limit = 10; /* Increase CS if the error rate is below */
 	bts->max_cs_ul = 4;
 	bts->max_cs_dl = 4;
+	bts->max_mcs_ul = 4;
+	bts->max_mcs_dl = 4;
 	/* CS-1 to CS-4 */
 	bts->cs_lqual_ranges[0].low = -256;
 	bts->cs_lqual_ranges[0].high = 6;
diff --git a/src/pcu_vty.c b/src/pcu_vty.c
index ce9db29..ee3116f 100644
--- a/src/pcu_vty.c
+++ b/src/pcu_vty.c
@@ -111,6 +111,15 @@
 		bts->cs_lqual_ranges[3].low,
 		VTY_NEWLINE);
 
+	if (bts->max_mcs_dl && bts->max_mcs_ul) {
+		if (bts->max_mcs_ul == bts->max_mcs_dl)
+			vty_out(vty, " mcs max %d%s", bts->max_mcs_dl,
+				VTY_NEWLINE);
+		else
+			vty_out(vty, " mcs max %d %d%s", bts->max_mcs_dl,
+				bts->max_mcs_ul, VTY_NEWLINE);
+	}
+
 	if (bts->force_llc_lifetime == 0xffff)
 		vty_out(vty, " queue lifetime infinite%s", VTY_NEWLINE);
 	else if (bts->force_llc_lifetime)
@@ -361,11 +370,12 @@
 	return CMD_SUCCESS;
 }
 
+#define CS_MAX_STR "Set maximum values for adaptive CS selection (overrides BTS config)\n"
 DEFUN(cfg_pcu_cs_max,
       cfg_pcu_cs_max_cmd,
       "cs max <1-4> [<1-4>]",
       CS_STR
-      "Set maximum values for adaptive CS selection (overrides BTS config)\n"
+      CS_MAX_STR
       "Maximum CS value to be used\n"
       "Use a different maximum CS value for the uplink")
 {
@@ -384,8 +394,7 @@
 DEFUN(cfg_pcu_no_cs_max,
       cfg_pcu_no_cs_max_cmd,
       "no cs max",
-      NO_STR CS_STR
-      "Set maximum values for adaptive CS selection (overrides BTS config)\n")
+      NO_STR CS_STR CS_MAX_STR)
 {
 	struct gprs_rlcmac_bts *bts = bts_main_data();
 
@@ -395,6 +404,41 @@
 	return CMD_SUCCESS;
 }
 
+#define MCS_STR "Modulation and Coding Scheme configuration (EGPRS)\n"
+
+DEFUN(cfg_pcu_mcs_max,
+      cfg_pcu_mcs_max_cmd,
+      "mcs max <1-4> [<1-4>]",
+      MCS_STR
+      CS_MAX_STR
+      "Maximum MCS value to be used\n"
+      "Use a different maximum MCS value for the uplink")
+{
+	struct gprs_rlcmac_bts *bts = bts_main_data();
+	uint8_t mcs = atoi(argv[0]);
+
+	bts->max_mcs_dl = mcs;
+	if (argc > 1)
+		bts->max_mcs_ul = atoi(argv[1]);
+	else
+		bts->max_mcs_ul = mcs;
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_pcu_no_mcs_max,
+      cfg_pcu_no_mcs_max_cmd,
+      "no mcs max",
+      NO_STR MCS_STR CS_MAX_STR)
+{
+	struct gprs_rlcmac_bts *bts = bts_main_data();
+
+	bts->max_mcs_dl = 0;
+	bts->max_mcs_ul = 0;
+
+	return CMD_SUCCESS;
+}
+
 #define QUEUE_STR "Packet queue options\n"
 #define LIFETIME_STR "Set lifetime limit of LLC frame in centi-seconds " \
 	"(overrides the value given by SGSN)\n"
@@ -848,6 +892,8 @@
 	install_element(PCU_NODE, &cfg_pcu_cs_downgrade_thrsh_cmd);
 	install_element(PCU_NODE, &cfg_pcu_no_cs_downgrade_thrsh_cmd);
 	install_element(PCU_NODE, &cfg_pcu_cs_lqual_ranges_cmd);
+	install_element(PCU_NODE, &cfg_pcu_mcs_max_cmd);
+	install_element(PCU_NODE, &cfg_pcu_no_mcs_max_cmd);
 	install_element(PCU_NODE, &cfg_pcu_queue_lifetime_cmd);
 	install_element(PCU_NODE, &cfg_pcu_queue_lifetime_inf_cmd);
 	install_element(PCU_NODE, &cfg_pcu_no_queue_lifetime_cmd);