aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/handover_logic.c
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2009-11-29 22:56:14 +0100
committerHarald Welte <laforge@gnumonks.org>2009-11-29 22:56:14 +0100
commit798418a068fcc95f8f5bcad5ba923ab065d7fa86 (patch)
tree9299b2f4bb3f4adfa7845fcc6034361cc740599c /openbsc/src/handover_logic.c
parentd011e8b958f3d5a09d1c66852292974d9dc52786 (diff)
[handover] Implement handover control logic
Code to implement handover control logic. A yet-to-be-implemented handover algorithm will call bsc_handover_start(old_lchan, new_bts) to start the handover process.
Diffstat (limited to 'openbsc/src/handover_logic.c')
-rw-r--r--openbsc/src/handover_logic.c251
1 files changed, 251 insertions, 0 deletions
diff --git a/openbsc/src/handover_logic.c b/openbsc/src/handover_logic.c
new file mode 100644
index 00000000..d4a88848
--- /dev/null
+++ b/openbsc/src/handover_logic.c
@@ -0,0 +1,251 @@
+/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not
+ * actually implement the handover algorithm/decision, but executes a
+ * handover decision */
+
+/* (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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <netinet/in.h>
+
+#include <openbsc/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_utils.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/signal.h>
+#include <openbsc/talloc.h>
+#include <openbsc/transaction.h>
+
+struct bsc_handover {
+ struct llist_head list;
+
+ struct gsm_lchan *old_lchan;
+ struct gsm_lchan *new_lchan;
+
+ struct timer_list T3103;
+
+ u_int8_t ho_ref;
+};
+
+static LLIST_HEAD(bsc_handovers);
+
+static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *ho;
+
+ llist_for_each_entry(ho, &bsc_handovers, list) {
+ if (ho->new_lchan == new_lchan)
+ return ho;
+ }
+
+ return NULL;
+}
+
+static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan)
+{
+ struct bsc_handover *ho;
+
+ llist_for_each_entry(ho, &bsc_handovers, list) {
+ if (ho->old_lchan == old_lchan)
+ return ho;
+ }
+
+ return NULL;
+}
+
+/* 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)
+{
+ struct gsm_lchan *new_lchan;
+ struct bsc_handover *ho;
+ int rc;
+
+ new_lchan = lchan_alloc(bts, old_lchan->type);
+ if (!new_lchan)
+ return -ENOSPC;
+
+ ho = talloc_zero(NULL, struct bsc_handover);
+ if (!ho) {
+ lchan_free(new_lchan);
+ return -ENOMEM;
+ }
+ ho->old_lchan = old_lchan;
+ ho->new_lchan = new_lchan;
+
+ /* FIXME: do we have a better idea of the timing advance? */
+ rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0);
+ if (rc < 0) {
+ talloc_free(ho);
+ lchan_free(new_lchan);
+ return rc;
+ }
+
+ llist_add(&ho->list, &bsc_handovers);
+ /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */
+
+ return 0;
+}
+
+/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */
+static void ho_T3103_cb(void *_ho)
+{
+ struct bsc_handover *ho = _ho;
+
+ lchan_free(ho->new_lchan);
+ llist_del(&ho->list);
+ talloc_free(ho);
+}
+
+/* RSL has acknowledged activation of the new lchan */
+static int ho_chan_activ_ack(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *ho;
+ int rc;
+
+ ho = bsc_ho_by_new_lchan(new_lchan);
+ if (!ho)
+ return -ENODEV;
+
+ /* 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);
+
+ /* start T3103. We can continue either with T3103 expiration,
+ * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */
+ ho->T3103.cb = ho_T3103_cb;
+ bsc_schedule_timer(&ho->T3103, 10, 0);
+
+ return 0;
+}
+
+/* RSL has not acknowledged activation of the new lchan */
+static int ho_chan_activ_nack(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *ho;
+
+ ho = bsc_ho_by_new_lchan(new_lchan);
+ if (!ho)
+ return -ENODEV;
+
+ llist_del(&ho->list);
+ talloc_free(ho);
+
+ /* FIXME: maybe we should try to allocate a new LCHAN here? */
+
+ return 0;
+}
+
+/* GSM 04.08 HANDOVER COMPLETE has been received on new channel */
+static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *ho;
+
+ ho = bsc_ho_by_new_lchan(new_lchan);
+ if (!ho)
+ return -ENODEV;
+
+ bsc_del_timer(&ho->T3103);
+ llist_del(&ho->list);
+
+ /* do something to re-route the actual speech frames ! */
+ //tch_remap(ho->old_lchan, ho->new_lchan);
+
+ /* release old lchan */
+ put_lchan(ho->old_lchan);
+
+ talloc_free(ho);
+
+ return 0;
+}
+
+/* GSM 04.08 HANDOVER FAIL has been received */
+static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
+{
+ struct bsc_handover *ho;
+
+ ho = bsc_ho_by_old_lchan(old_lchan);
+ if (!ho)
+ return -ENODEV;
+
+ bsc_del_timer(&ho->T3103);
+ llist_del(&ho->list);
+ put_lchan(ho->new_lchan);
+ talloc_free(ho);
+
+ return 0;
+}
+
+/* GSM 08.58 HANDOVER DETECT has been received */
+static int ho_rsl_detect(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *ho;
+
+ ho = bsc_ho_by_old_lchan(new_lchan);
+ if (!ho)
+ return -ENODEV;
+
+ /* FIXME: do we actually want to do something here ? */
+
+ return 0;
+}
+
+static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct gsm_lchan *lchan;
+
+ switch (subsys) {
+ case SS_LCHAN:
+ lchan = signal_data;
+ switch (signal) {
+ case S_LCHAN_ACTIVATE_ACK:
+ return ho_chan_activ_ack(lchan);
+ case S_LCHAN_ACTIVATE_NACK:
+ return ho_chan_activ_nack(lchan);
+ case S_LCHAN_HANDOVER_DETECT:
+ return ho_rsl_detect(lchan);
+ case S_LCHAN_HANDOVER_COMPL:
+ return ho_gsm48_ho_compl(lchan);
+ case S_LCHAN_HANDOVER_FAIL:
+ return ho_gsm48_ho_fail(lchan);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static __attribute__((constructor)) void on_dso_load_ho_logic(void)
+{
+ register_signal_handler(SS_LCHAN, ho_logic_sig_cb, NULL);
+}