From 8d77b9540a907fb36afbb324df549c9261e1ca02 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 17 Dec 2009 00:31:10 +0100 Subject: [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 --- openbsc/include/openbsc/abis_rsl.h | 2 +- openbsc/include/openbsc/debug.h | 2 + openbsc/include/openbsc/handover.h | 8 +++ openbsc/src/Makefile.am | 3 +- openbsc/src/abis_rsl.c | 15 ++++- openbsc/src/bsc_hack.c | 1 + openbsc/src/bsc_init.c | 1 + openbsc/src/debug.c | 3 +- openbsc/src/handover_decision.c | 111 +++++++++++++++++++++++++++++++++++++ openbsc/src/handover_logic.c | 27 ++++++++- 10 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 openbsc/include/openbsc/handover.h create mode 100644 openbsc/src/handover_decision.c (limited to 'openbsc') diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h index b76d0facc..6d0ab6182 100644 --- a/openbsc/include/openbsc/abis_rsl.h +++ b/openbsc/include/openbsc/abis_rsl.h @@ -497,7 +497,7 @@ int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr, 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 447c3584f..c1098a53c 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 000000000..8ab1b0642 --- /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 f22582122..5692ac4e4 100644 --- a/openbsc/src/Makefile.am +++ b/openbsc/src/Makefile.am @@ -11,7 +11,8 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \ 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 1fbea837d..d42daf5f2 100644 --- a/openbsc/src/abis_rsl.c +++ b/openbsc/src/abis_rsl.c @@ -576,7 +576,7 @@ int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr, #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 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, 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 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, 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 @@ static int rsl_rx_chan_rqd(struct msgb *msg) 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 1dd5e1020..b0f8d6c3f 100644 --- a/openbsc/src/bsc_hack.c +++ b/openbsc/src/bsc_hack.c @@ -162,6 +162,7 @@ int main(int argc, char **argv) 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 aed0dad42..ce3d0b4f8 100644 --- a/openbsc/src/bsc_init.c +++ b/openbsc/src/bsc_init.c @@ -679,6 +679,7 @@ static int set_system_infos(struct gsm_bts_trx *trx) 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 5dc2e0ff7..9c6cb4963 100644 --- a/openbsc/src/debug.c +++ b/openbsc/src/debug.c @@ -28,7 +28,7 @@ #include -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 @@ static const struct debug_info debug_info[] = { 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 000000000..06eb86507 --- /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 + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +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 d4a888487..99b3c0139 100644 --- a/openbsc/src/handover_logic.c +++ b/openbsc/src/handover_logic.c @@ -85,23 +85,40 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) { 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 @@ static void ho_T3103_cb(void *_ho) { 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 @@ static int ho_chan_activ_ack(struct gsm_lchan *new_lchan) 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 @@ static int ho_chan_activ_ack(struct gsm_lchan *new_lchan) /* 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 */ -- cgit v1.2.3