[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
This commit is contained in:
Harald Welte 2009-12-17 00:31:10 +01:00
parent 7a7a0d5428
commit 8d77b9540a
10 changed files with 164 additions and 9 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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 */

View File

@ -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 \

View File

@ -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));

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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 @@ 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;

View File

@ -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);
}

View File

@ -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 */