Introduce BS and MS power control related functions

* add bts->band field plus corresponding VTY and commandline argument
* add trx->nominal_power and trx->max_power_red fields
* add rsl_chan_bs_power_ctrl() to control TRX RF power for a given TS
* add rsl_chan_ms_power_ctrl() to control MS RF power for a given lchan.
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index 532595b..c928ffa 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -411,5 +411,8 @@
 int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf);
 int rsl_number_of_paging_subchannels(struct gsm_bts *bts);
 
+int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db);
+int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm);
+
 #endif /* RSL_MT_H */
 
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index a401a47..8c44ba5 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -3,6 +3,14 @@
 
 #include <sys/types.h>
 
+enum gsm_band {
+	GSM_BAND_400,
+	GSM_BAND_850,
+	GSM_BAND_900,
+	GSM_BAND_1800,
+	GSM_BAND_1900,
+};
+
 enum gsm_phys_chan_config {
 	GSM_PCHAN_NONE,
 	GSM_PCHAN_CCCH,
@@ -223,6 +231,8 @@
 	} bb_transc;
 
 	u_int16_t arfcn;
+	int nominal_power;		/* in dBm */
+	unsigned int max_power_red;	/* in actual dB */
 
 	union {
 		struct {
@@ -301,6 +311,7 @@
 	u_int8_t bsic;
 	/* type of BTS */
 	enum gsm_bts_type type;
+	enum gsm_band band;
 	/* how do we talk OML with this TRX? */
 	struct e1inp_sign_link *oml_link;
 
@@ -396,6 +407,9 @@
 struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
 				struct gsm_bts *start_bts);
 
+char *gsm_band_name(enum gsm_band band);
+enum gsm_band gsm_band_parse(int mhz);
+
 static inline int is_ipaccess_bts(struct gsm_bts *bts)
 {
 	switch (bts->type) {
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 4b2a7fc..5c1967d 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -356,6 +356,95 @@
 	return abis_rsl_sendmsg(msg);
 }
 
+int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db)
+{
+	struct abis_rsl_dchan_hdr *dh;
+	struct msgb *msg = rsl_msgb_alloc();
+	u_int8_t chan_nr = lchan2chan_nr(lchan);
+
+	db = abs(db);
+	if (db > 30)
+		return -EINVAL;
+
+	lchan->bs_power = db/2;
+	if (fpc)
+		lchan->bs_power |= 0x10;
+	
+	dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+	init_dchan_hdr(dh, RSL_MT_BS_POWER_CONTROL);
+	dh->chan_nr = chan_nr;
+
+	msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power);
+
+	msg->trx = lchan->ts->trx;
+
+	return abis_rsl_sendmsg(msg);
+}
+
+/* determine power control level for given dBm value, as indicated
+ * by the tables in chapter 4.1.1 of GSM TS 05.05 */
+static int ms_pwr_ctl_lvl(struct gsm_bts *bts, unsigned int dbm)
+{
+	switch (bts->band) {
+	case GSM_BAND_400:
+	case GSM_BAND_900:
+	case GSM_BAND_850:
+		if (dbm >= 39)
+			return 0;
+		else if (dbm < 5)
+			return 19;
+		else
+			return 2 + ((39 - dbm) / 2);
+		break;
+	case GSM_BAND_1800:
+		if (dbm >= 36)
+			return 29;
+		else if (dbm >= 34)	
+			return 30;
+		else if (dbm >= 32)
+			return 31;
+		else
+			return (30 - dbm) / 2;
+		break;
+	case GSM_BAND_1900:
+		if (dbm >= 33)
+			return 30;
+		else if (dbm >= 32)
+			return 31;
+		else
+			return (30 - dbm) / 2;
+		break;
+	}
+	return -EINVAL;
+}
+
+int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm)
+{
+	struct abis_rsl_dchan_hdr *dh;
+	struct msgb *msg = rsl_msgb_alloc();
+	u_int8_t chan_nr = lchan2chan_nr(lchan);
+	int ctl_lvl;
+
+	ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts, dbm);
+	if (ctl_lvl < 0)
+		return ctl_lvl;
+
+	lchan->ms_power = ctl_lvl;
+
+	if (fpc)
+		lchan->ms_power |= 0x20;
+	
+	dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+	init_dchan_hdr(dh, RSL_MT_MS_POWER_CONTROL);
+	dh->chan_nr = chan_nr;
+
+	msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
+
+	msg->trx = lchan->ts->trx;
+
+	return abis_rsl_sendmsg(msg);
+}
+
 /* Chapter 8.4.1 */
 #if 0
 int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr,
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index 0322ff7..43f1011 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -59,6 +59,7 @@
 static int cardnr = 0;
 static int release_l2 = 0;
 static enum gsm_bts_type BTS_TYPE = GSM_BTS_TYPE_BS11;
+static enum gsm_band BAND = GSM_BAND_900;
 static const char *database_name = "hlr.sqlite3";
 
 /* The following definitions are for OM and NM packets that we cannot yet
@@ -927,6 +928,7 @@
 
 static int bootstrap_bts(struct gsm_bts *bts)
 {
+	bts->band = BAND;
 	bts->location_area_code = LAC;
 	bts->trx[0].arfcn = ARFCN;
 
@@ -1027,6 +1029,7 @@
 		/* FIXME: do this dynamic */
 		bts->ip_access.site_id = 1801;
 		bts->ip_access.bts_id = 0;
+
 		bts = &gsmnet->bts[1];
 		bootstrap_bts(bts);
 		bts->ip_access.site_id = 1800;
@@ -1075,7 +1078,7 @@
 static void handle_options(int argc, char** argv)
 {
 	while (1) {
-		int option_index = 0, c;
+		int tmp, option_index = 0, c;
 		static struct option long_options[] = {
 			{"help", 0, 0, 'h'},
 			{"debug", 1, 0, 'd'},
@@ -1092,10 +1095,11 @@
 			{"cardnr", 1, 0, 'C'},
 			{"release-l2", 0, 0, 'R'},
 			{"timestamp", 0, 0, 'T'},
+			{"band", 0, 0, 'b'},
 			{0, 0, 0, 0}
 		};
 
-		c = getopt_long(argc, argv, "hc:n:d:sar:p:f:t:C:RL:l:T",
+		c = getopt_long(argc, argv, "hc:n:d:sar:p:f:t:C:RL:l:Tb:",
 				long_options, &option_index);
 		if (c == -1)
 			break;
@@ -1147,6 +1151,9 @@
 		case 'T':
 			debug_timestamp(1);
 			break;
+		case 'b':
+			BAND = gsm_band_parse(atoi(optarg));
+			break;
 		default:
 			/* ignore */
 			break;
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index e71a6cb..80020e5 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <errno.h>
 
 #include <openbsc/gsm_data.h>
 
@@ -213,3 +214,39 @@
 	}
 	return NULL;
 }
+
+char *gsm_band_name(enum gsm_band band)
+{
+	switch (band) {
+	case GSM_BAND_400:
+		return "GSM 400";
+	case GSM_BAND_850:
+		return "GSM 850";
+	case GSM_BAND_900:
+		return "GSM 900";
+	case GSM_BAND_1800:
+		return "DCS 1800";
+	case GSM_BAND_1900:
+		return "PCS 1900";
+	}
+	return "invalid";
+}
+
+enum gsm_band gsm_band_parse(int mhz)
+{
+	switch (mhz) {
+	case 400:
+		return GSM_BAND_400;
+	case 850:
+		return GSM_BAND_850;
+	case 900:
+		return GSM_BAND_900;
+	case 1800:
+		return GSM_BAND_1800;
+	case 1900:
+		return GSM_BAND_1900;
+	default:
+		return -EINVAL;
+	}
+}
+
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index 6597e44..a2b3e56 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -112,9 +112,11 @@
 
 static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
 {
-	vty_out(vty, "BTS %u is of %s type, has LAC %u, BSIC %u, TSC %u and %u TRX%s",
-		bts->nr, btstype2str(bts->type), bts->location_area_code,
-		bts->bsic, bts->tsc, bts->num_trx, VTY_NEWLINE);
+	vty_out(vty, "BTS %u is of %s type in band %s, has LAC %u, "
+		"BSIC %u, TSC %u and %u TRX%s",
+		bts->nr, btstype2str(bts->type), gsm_band_name(bts->band),
+		bts->location_area_code, bts->bsic, bts->tsc, 
+		bts->num_trx, VTY_NEWLINE);
 	if (is_ipaccess_bts(bts))
 		vty_out(vty, "  Unit ID: %u/%u/0%s",
 			bts->ip_access.site_id, bts->ip_access.bts_id,
@@ -159,6 +161,10 @@
 {
 	vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s",
 		trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE);
+	vty_out(vty, "  RF Nominal Power: %d dBm, reduced by %u dB, "
+		"resulting BS power: %d dBm\n",
+		trx->nominal_power, trx->max_power_red,
+		trx->nominal_power - trx->max_power_red);
 	vty_out(vty, "  NM State: ");
 	net_dump_nmstate(vty, &trx->nm_state);
 	vty_out(vty, "  Baseband Transceiver NM State: ");
@@ -297,7 +303,7 @@
 
 static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
 {
-	vty_out(vty, "    ID: %lu, Authorized: %d%s", subscr->id,
+	vty_out(vty, "    ID: %llu, Authorized: %d%s", subscr->id,
 		subscr->authorized, VTY_NEWLINE);
 	if (subscr->name)
 		vty_out(vty, "    Name: '%s'%s", subscr->name, VTY_NEWLINE);
@@ -628,10 +634,30 @@
 {
 	struct gsm_bts *bts = vty->index;
 
+	/* FIXME: implementation */
 	//bts->type =
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_bts_band,
+      cfg_bts_band_cmd,
+      "band BAND",
+      "Set the frequency band of this BTS\n")
+{
+	struct gsm_bts *bts = vty->index;
+	int band = gsm_band_parse(atoi(argv[0]));
+
+	if (band < 0) {
+		vty_out(vty, "%% BAND %d is not a valid GSM band%s",
+			band, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	bts->band = band;
+
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_bts_lac,
       cfg_bts_lac_cmd,
       "location_area_code <0-255>",
@@ -758,6 +784,34 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_trx_max_power_red,
+      cfg_trx_max_power_red_cmd,
+      "max_power_red <0-100>",
+      "Reduction of maximum BS RF Power in dB\n")
+{
+	int maxpwr_r = atoi(argv[0]);
+	struct gsm_bts_trx *trx = vty->index;
+	int upper_limit = 12;	/* default 12.21 max power red. */
+
+	/* FIXME: check if our BTS type supports more than 12 */
+	if (maxpwr_r < 0 || maxpwr_r > upper_limit) {
+		vty_out(vty, "%% Power %d dB is not in the valid range%s",
+			maxpwr_r, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+	if (maxpwr_r & 1) {
+		vty_out(vty, "%% Power %d dB is not an even value%s",
+			maxpwr_r, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	trx->max_power_red = maxpwr_r;
+
+	/* FIXME: make sure we update this using OML */
+
+	return CMD_SUCCESS;
+}
+
 /* per TS configuration */
 DEFUN(cfg_ts,
       cfg_ts_cmd,
@@ -884,6 +938,7 @@
 	install_node(&bts_node, dummy_config_write);
 	install_default(BTS_NODE);
 	install_element(BTS_NODE, &cfg_bts_type_cmd);
+	install_element(BTS_NODE, &cfg_bts_band_cmd);
 	install_element(BTS_NODE, &cfg_bts_lac_cmd);
 	install_element(BTS_NODE, &cfg_bts_tsc_cmd);
 	install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
@@ -892,6 +947,7 @@
 	install_node(&trx_node, dummy_config_write);
 	install_default(TRX_NODE);
 	install_element(TRX_NODE, &cfg_trx_arfcn_cmd);
+	install_element(TRX_NODE, &cfg_trx_max_power_red);
 
 	install_element(TRX_NODE, &cfg_ts_cmd);
 	install_node(&ts_node, dummy_config_write);