[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:
parent
7a7a0d5428
commit
8d77b9540a
|
@ -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 bs_power, u_int8_t ms_power,
|
||||||
u_int8_t ta);
|
u_int8_t ta);
|
||||||
int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
|
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_chan_mode_modify_req(struct gsm_lchan *ts);
|
||||||
int rsl_encryption_cmd(struct msgb *msg);
|
int rsl_encryption_cmd(struct msgb *msg);
|
||||||
int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len,
|
int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len,
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
#define DMGCP 0x40000
|
#define DMGCP 0x40000
|
||||||
|
|
||||||
|
#define DHO 0x80000
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args)
|
#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args)
|
||||||
#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args)
|
#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args)
|
||||||
|
|
|
@ -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 */
|
|
@ -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 \
|
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 \
|
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 \
|
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 \
|
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 \
|
mncc.c rtp_proxy.c gsm_04_08.c gsm_04_11.c transaction.c \
|
||||||
|
|
|
@ -576,7 +576,7 @@ int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
|
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 abis_rsl_dchan_hdr *dh;
|
||||||
struct msgb *msg;
|
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;
|
dh->chan_nr = chan_nr;
|
||||||
|
|
||||||
msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type);
|
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),
|
msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
|
||||||
(u_int8_t *) &cm);
|
(u_int8_t *) &cm);
|
||||||
|
/* For compatibility with Phase 1 */
|
||||||
msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4,
|
msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4,
|
||||||
(u_int8_t *) &ci);
|
(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);
|
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_BS_POWER, lchan->bs_power);
|
||||||
msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
|
msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
|
||||||
msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
|
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->bs_power = 0; /* 0dB reduction, output power = Pn */
|
||||||
lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
|
lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
|
||||||
lchan->tch_mode = GSM48_CMODE_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 */
|
/* create IMMEDIATE ASSIGN 04.08 messge */
|
||||||
memset(&ia, 0, sizeof(ia));
|
memset(&ia, 0, sizeof(ia));
|
||||||
|
|
|
@ -162,6 +162,7 @@ int main(int argc, char **argv)
|
||||||
talloc_ctx_init();
|
talloc_ctx_init();
|
||||||
on_dso_load_token();
|
on_dso_load_token();
|
||||||
on_dso_load_rrlp();
|
on_dso_load_rrlp();
|
||||||
|
on_dso_load_ho_dec();
|
||||||
|
|
||||||
/* parse options */
|
/* parse options */
|
||||||
handle_options(argc, argv);
|
handle_options(argc, argv);
|
||||||
|
|
|
@ -679,6 +679,7 @@ static int set_system_infos(struct gsm_bts_trx *trx)
|
||||||
rc = gsm_generate_si(si_tmp, trx->bts, i);
|
rc = gsm_generate_si(si_tmp, trx->bts, i);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
DEBUGP(DRR, "SI%u: %s\n", i, hexdump(si_tmp, rc));
|
||||||
rsl_bcch_info(trx, i, si_tmp, sizeof(si_tmp));
|
rsl_bcch_info(trx, i, si_tmp, sizeof(si_tmp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
#include <openbsc/debug.h>
|
#include <openbsc/debug.h>
|
||||||
|
|
||||||
unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB|DMEAS);
|
unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB);
|
||||||
|
|
||||||
struct debug_info {
|
struct debug_info {
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@ -60,6 +60,7 @@ static const struct debug_info debug_info[] = {
|
||||||
DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
|
DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
|
||||||
DEBUG_CATEGORY(DMSC, "DMSC", "", "")
|
DEBUG_CATEGORY(DMSC, "DMSC", "", "")
|
||||||
DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
|
DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
|
||||||
|
DEBUG_CATEGORY(DHO, "DHO", "", "")
|
||||||
};
|
};
|
||||||
|
|
||||||
static int use_color = 1;
|
static int use_color = 1;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -85,23 +85,40 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
|
||||||
{
|
{
|
||||||
struct gsm_lchan *new_lchan;
|
struct gsm_lchan *new_lchan;
|
||||||
struct bsc_handover *ho;
|
struct bsc_handover *ho;
|
||||||
|
static u_int8_t ho_ref;
|
||||||
int rc;
|
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);
|
new_lchan = lchan_alloc(bts, old_lchan->type);
|
||||||
if (!new_lchan)
|
if (!new_lchan) {
|
||||||
|
DEBUGPC(DHO, "No free channel\n");
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
ho = talloc_zero(NULL, struct bsc_handover);
|
ho = talloc_zero(NULL, struct bsc_handover);
|
||||||
if (!ho) {
|
if (!ho) {
|
||||||
|
DEBUGPC(DHO, "Out of Memory\n");
|
||||||
lchan_free(new_lchan);
|
lchan_free(new_lchan);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
ho->old_lchan = old_lchan;
|
ho->old_lchan = old_lchan;
|
||||||
ho->new_lchan = new_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? */
|
/* 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) {
|
if (rc < 0) {
|
||||||
|
DEBUGPC(DHO, "could not activate channel\n");
|
||||||
talloc_free(ho);
|
talloc_free(ho);
|
||||||
lchan_free(new_lchan);
|
lchan_free(new_lchan);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -118,6 +135,8 @@ static void ho_T3103_cb(void *_ho)
|
||||||
{
|
{
|
||||||
struct bsc_handover *ho = _ho;
|
struct bsc_handover *ho = _ho;
|
||||||
|
|
||||||
|
DEBUGP(DHO, "HO T3103 expired\n");
|
||||||
|
|
||||||
lchan_free(ho->new_lchan);
|
lchan_free(ho->new_lchan);
|
||||||
llist_del(&ho->list);
|
llist_del(&ho->list);
|
||||||
talloc_free(ho);
|
talloc_free(ho);
|
||||||
|
@ -129,6 +148,8 @@ static int ho_chan_activ_ack(struct gsm_lchan *new_lchan)
|
||||||
struct bsc_handover *ho;
|
struct bsc_handover *ho;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
DEBUGP(DHO, "handover activate ack, send HO Command\n");
|
||||||
|
|
||||||
ho = bsc_ho_by_new_lchan(new_lchan);
|
ho = bsc_ho_by_new_lchan(new_lchan);
|
||||||
if (!ho)
|
if (!ho)
|
||||||
return -ENODEV;
|
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
|
/* we can now send the 04.08 HANDOVER COMMAND to the MS
|
||||||
* using the old lchan */
|
* 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,
|
/* start T3103. We can continue either with T3103 expiration,
|
||||||
* 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */
|
* 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */
|
||||||
|
|
Loading…
Reference in New Issue