[handover] first functional handover implementation

With this commit, we can successfully hand over a channel from one cell to
another cell.  We implement asynchronous intra-BSC (but inter-BTS) handover.

Changes:
* introduce new DHO log category
* extend rsl_chan_activate_lchan() with argument for HO reference
* introduce actual minimal handover decision making in handover_decision.c
* various fixes to bsc_handover_start() in  handover_logic.c
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index b76d0fa..6d0ab61 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -497,7 +497,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 ta, u_int8_t ho_ref);
 int rsl_chan_mode_modify_req(struct gsm_lchan *ts);
 int rsl_encryption_cmd(struct msgb *msg);
 int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len,
diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h
index 447c358..c1098a5 100644
--- a/openbsc/include/openbsc/debug.h
+++ b/openbsc/include/openbsc/debug.h
@@ -25,6 +25,8 @@
 
 #define DMGCP		0x40000
 
+#define DHO		0x80000
+
 #ifdef DEBUG
 #define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args)
 #define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args)
diff --git a/openbsc/include/openbsc/handover.h b/openbsc/include/openbsc/handover.h
new file mode 100644
index 0000000..8ab1b06
--- /dev/null
+++ b/openbsc/include/openbsc/handover.h
@@ -0,0 +1,8 @@
+#ifndef _HANDOVER_H
+#define _HANDOVER_H
+/* Hand over the specified logical channel to the specified new BTS.
+ * This is the main entry point for the actual handover algorithm,
+ * after it has decided it wants to initiate HO to a specific BTS */
+int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts);
+
+#endif /* _HANDOVER_H */
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index f225821..5692ac4 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -11,7 +11,8 @@
 		gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.c \
 		trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
 		input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \
-		talloc_ctx.c system_information.c bitvec.c rest_octets.c
+		talloc_ctx.c system_information.c bitvec.c rest_octets.c \
+		handover_decision.c
 
 libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \
 		mncc.c rtp_proxy.c gsm_04_08.c gsm_04_11.c transaction.c \
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 1fbea83..d42daf5 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -576,7 +576,7 @@
 #endif
 
 int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, 
-			    u_int8_t ta)
+			    u_int8_t ta, u_int8_t ho_ref)
 {
 	struct abis_rsl_dchan_hdr *dh;
 	struct msgb *msg;
@@ -603,9 +603,9 @@
 	dh->chan_nr = chan_nr;
 
 	msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type);
-	/* For compatibility with Phase 1 */
 	msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
 		     (u_int8_t *) &cm);
+	/* For compatibility with Phase 1 */
 	msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4,
 		     (u_int8_t *) &ci);
 
@@ -616,6 +616,15 @@
 			msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
 	}
 
+	switch (act_type) {
+	case RSL_ACT_INTER_ASYNC:
+	case RSL_ACT_INTER_SYNC:
+		msgb_tv_put(msg, RSL_IE_HANDO_REF, ho_ref);
+		break;
+	default:
+		break;
+	}
+
 	msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power);
 	msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
 	msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
@@ -1258,7 +1267,7 @@
 	lchan->bs_power = 0; /* 0dB reduction, output power = Pn */
 	lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
 	lchan->tch_mode = GSM48_CMODE_SIGN;
-	rsl_chan_activate_lchan(lchan, 0x00, rqd_ta);
+	rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, 0);
 
 	/* create IMMEDIATE ASSIGN 04.08 messge */
 	memset(&ia, 0, sizeof(ia));
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index 1dd5e10..b0f8d6c 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -162,6 +162,7 @@
 	talloc_ctx_init();
 	on_dso_load_token();
 	on_dso_load_rrlp();
+	on_dso_load_ho_dec();
 
 	/* parse options */
 	handle_options(argc, argv);
diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c
index aed0dad..ce3d0b4 100644
--- a/openbsc/src/bsc_init.c
+++ b/openbsc/src/bsc_init.c
@@ -679,6 +679,7 @@
 			rc = gsm_generate_si(si_tmp, trx->bts, i);
 			if (rc < 0)
 				goto err_out;
+			DEBUGP(DRR, "SI%u: %s\n", i, hexdump(si_tmp, rc));
 			rsl_bcch_info(trx, i, si_tmp, sizeof(si_tmp));
 		}
 	}
diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c
index 5dc2e0f..9c6cb49 100644
--- a/openbsc/src/debug.c
+++ b/openbsc/src/debug.c
@@ -28,7 +28,7 @@
 
 #include <openbsc/debug.h>
 
-unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB|DMEAS);
+unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB);
 
 struct debug_info {
 	const char *name;
@@ -60,6 +60,7 @@
 	DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
 	DEBUG_CATEGORY(DMSC, "DMSC", "", "")
 	DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
+	DEBUG_CATEGORY(DHO, "DHO", "", "")
 };
 
 static int use_color = 1;
diff --git a/openbsc/src/handover_decision.c b/openbsc/src/handover_decision.c
new file mode 100644
index 0000000..06eb865
--- /dev/null
+++ b/openbsc/src/handover_decision.c
@@ -0,0 +1,111 @@
+/* Handover Decision making for Inter-BTS (Intra-BSC) Handover.  This
+ * only implements the handover algorithm/decision, but not execution
+ * of it */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <openbsc/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/meas_rep.h>
+#include <openbsc/signal.h>
+#include <openbsc/talloc.h>
+#include <openbsc/handover.h>
+
+static int handover_to_arfcn_bsic(struct gsm_lchan *lchan,
+				  u_int16_t arfcn, u_int8_t bsic)
+{
+	struct gsm_bts *new_bts;
+
+	/* resolve the gsm_bts structure for the best neighbor */
+	new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic);
+	if (!new_bts) {
+		DEBUGP(DHO, "unable to determine neighbor BTS for ARFCN %u BSIC %u ?!?\n", arfcn, bsic);
+		return -EINVAL;
+	}
+
+	/* and actually try to handover to that cell */
+	return bsc_handover_start(lchan, new_bts);
+}
+
+#define RXLEV_HYST 3
+
+/* process an already parsed measurement report */
+static int process_meas_rep(struct gsm_meas_rep *mr)
+{
+	struct gsm_meas_rep_cell *mr_cell = NULL;
+	unsigned int best_better_db;
+	int i;
+
+	DEBUGP(DHO, "process meas res: ");
+
+	/* FIXME: implement actual averaging over multiple measurement
+	 * reports */
+
+	/* find the best cell in this report that is at least RXLEV_HYST
+	 * better than the current serving cell */
+	for (i = 0; i < mr->num_cell; i++) {
+		unsigned int better;
+		if (mr->cell[i].rxlev < mr->dl.full.rx_lev + RXLEV_HYST)
+			continue;
+
+		better = mr->cell[i].rxlev - mr->dl.full.rx_lev;
+		if (better > best_better_db) {
+			mr_cell = &mr->cell[i];
+			best_better_db = better;
+		}
+	}
+
+	if (mr_cell) {
+		DEBUGPC(DHO, "Cell on ARFCN %u is better, starting handover\n", mr_cell->arfcn);
+		return handover_to_arfcn_bsic(mr->lchan, mr_cell->arfcn,
+						mr_cell->bsic);
+	}
+
+	DEBUGPC(DHO, "No better cell\n");
+	return 0;
+}
+
+static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal,
+			   void *handler_data, void *signal_data)
+{
+	struct gsm_meas_rep *mr;
+
+	if (subsys != SS_LCHAN)
+		return 0;
+
+	switch (signal) {
+	case S_LCHAN_MEAS_REP:
+		mr = signal_data;
+		process_meas_rep(mr);
+		break;
+	}
+
+	return 0;
+}
+
+void on_dso_load_ho_dec(void)
+{
+	register_signal_handler(SS_LCHAN, ho_dec_sig_cb, NULL);
+}
diff --git a/openbsc/src/handover_logic.c b/openbsc/src/handover_logic.c
index d4a8884..99b3c01 100644
--- a/openbsc/src/handover_logic.c
+++ b/openbsc/src/handover_logic.c
@@ -85,23 +85,40 @@
 {
 	struct gsm_lchan *new_lchan;
 	struct bsc_handover *ho;
+	static u_int8_t ho_ref;
 	int rc;
 
+	DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u): ",
+		old_lchan->ts->trx->bts->nr, bts->nr);
+
 	new_lchan = lchan_alloc(bts, old_lchan->type);
-	if (!new_lchan)
+	if (!new_lchan) {
+		DEBUGPC(DHO, "No free channel\n");
 		return -ENOSPC;
+	}
 
 	ho = talloc_zero(NULL, struct bsc_handover);
 	if (!ho) {
+		DEBUGPC(DHO, "Out of Memory\n");
 		lchan_free(new_lchan);
 		return -ENOMEM;
 	}
 	ho->old_lchan = old_lchan;
 	ho->new_lchan = new_lchan;
+	ho->ho_ref = ho_ref++;
+
+	/* copy some parameters from old lchan */
+	memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr));
+	new_lchan->ms_power = old_lchan->ms_power;
+	new_lchan->bs_power = old_lchan->bs_power;
+	new_lchan->rsl_cmode = old_lchan->rsl_cmode;
+	new_lchan->tch_mode = old_lchan->tch_mode;
 
 	/* FIXME: do we have a better idea of the timing advance? */
-	rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0);
+	rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0,
+				     ho->ho_ref);
 	if (rc < 0) {
+		DEBUGPC(DHO, "could not activate channel\n");
 		talloc_free(ho);
 		lchan_free(new_lchan);
 		return rc;
@@ -118,6 +135,8 @@
 {
 	struct bsc_handover *ho = _ho;
 
+	DEBUGP(DHO, "HO T3103 expired\n");
+
 	lchan_free(ho->new_lchan);
 	llist_del(&ho->list);
 	talloc_free(ho);
@@ -129,6 +148,8 @@
 	struct bsc_handover *ho;
 	int rc;
 
+	DEBUGP(DHO, "handover activate ack, send HO Command\n");
+
 	ho = bsc_ho_by_new_lchan(new_lchan);
 	if (!ho)
 		return -ENODEV;
@@ -136,7 +157,7 @@
 	/* we can now send the 04.08 HANDOVER COMMAND to the MS
 	 * using the old lchan */
 
-	rc = gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0);
+	rc = gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref);
 
 	/* start T3103.  We can continue either with T3103 expiration,
 	 * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */