osmo-bsc/src/osmo-bsc/handover_logic.c

199 lines
5.3 KiB
C

/* 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 Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <netinet/in.h>
#include <osmocom/core/msgb.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/lchan_select.h>
#include <osmocom/bsc/signal.h>
#include <osmocom/core/talloc.h>
#include <osmocom/bsc/bsc_subscriber.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
#include <osmocom/bsc/handover.h>
#include <osmocom/bsc/handover_cfg.h>
#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/bsc/neighbor_ident.h>
#include <osmocom/bsc/abis_nm.h>
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/gsm0808_utils.h>
const struct value_string handover_scope_names[] = {
{ HO_NO_HANDOVER, "HO-none" },
{ HO_INTRA_CELL, "AS" },
{ HO_INTRA_BSC, "HO-intraBSC" },
{ HO_INTER_BSC_OUT, "HO-interBSC-Out" },
{ HO_INTER_BSC_IN, "HO-interBSC-In" },
{ HO_SCOPE_ALL, "HO-any" },
{}
};
const struct value_string handover_result_names[] = {
{ HO_RESULT_OK, "Complete" },
{ HO_RESULT_FAIL_NO_CHANNEL, "Failure (no channel could be allocated)" },
{ HO_RESULT_FAIL_RR_HO_FAIL, "Failure (MS sent RR Handover Failure)" },
{ HO_RESULT_FAIL_TIMEOUT, "Failure (timeout)" },
{ HO_RESULT_CONN_RELEASE, "Connection released" },
{ HO_RESULT_ERROR, "Failure" },
{}
};
static LLIST_HEAD(handover_decision_callbacks);
void handover_decision_callbacks_register(struct handover_decision_callbacks *hdc)
{
llist_add_tail(&hdc->entry, &handover_decision_callbacks);
}
struct handover_decision_callbacks *handover_decision_callbacks_get(int hodec_id)
{
struct handover_decision_callbacks *hdc;
llist_for_each_entry(hdc, &handover_decision_callbacks, entry) {
if (hdc->hodec_id == hodec_id)
return hdc;
}
return NULL;
}
static void ho_meas_rep(struct gsm_meas_rep *mr)
{
struct handover_decision_callbacks *hdc;
enum hodec_id hodec_id = ho_get_algorithm(mr->lchan->ts->trx->bts->ho);
hdc = handover_decision_callbacks_get(hodec_id);
if (!hdc || !hdc->on_measurement_report)
return;
hdc->on_measurement_report(mr);
}
/* Count ongoing handovers within the given BTS.
* ho_scopes is an OR'd combination of enum handover_scope values to include in the count. */
int bts_handover_count(struct gsm_bts *bts, int ho_scopes)
{
struct gsm_bts_trx *trx;
int count = 0;
llist_for_each_entry(trx, &bts->trx_list, list) {
int i;
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
struct gsm_bts_trx_ts *ts = &trx->ts[i];
struct gsm_lchan *lchan;
/* skip administratively deactivated timeslots */
if (!nm_is_running(&ts->mo.nm_state))
continue;
ts_for_each_lchan(lchan, ts) {
if (!lchan->conn)
continue;
if (!lchan->conn->ho.fi)
continue;
if (lchan->conn->ho.scope & ho_scopes)
count++;
}
}
}
return count;
}
struct gsm_bts *bts_by_neighbor_ident(const struct gsm_network *net,
const struct neighbor_ident_key *search_for)
{
struct gsm_bts *found = NULL;
struct gsm_bts *bts;
struct gsm_bts *wildcard_match = NULL;
llist_for_each_entry(bts, &net->bts_list, list) {
struct neighbor_ident_key entry = {
.from_bts = NEIGHBOR_IDENT_KEY_ANY_BTS,
.arfcn = bts->c0->arfcn,
.bsic = bts->bsic,
};
if (neighbor_ident_key_match(&entry, search_for, true)) {
if (found) {
LOGP(DHO, LOGL_ERROR, "CONFIG ERROR: Multiple BTS match %s: %d and %d\n",
neighbor_ident_key_name(search_for),
found->nr, bts->nr);
return found;
}
found = bts;
}
if (neighbor_ident_key_match(&entry, search_for, false))
wildcard_match = bts;
}
if (found)
return found;
return wildcard_match;
}
struct neighbor_ident_key *bts_ident_key(const struct gsm_bts *bts)
{
static struct neighbor_ident_key key;
key = (struct neighbor_ident_key){
.arfcn = bts->c0->arfcn,
.bsic = bts->bsic,
};
return &key;
}
static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
struct lchan_signal_data *lchan_data;
struct gsm_lchan *lchan;
lchan_data = signal_data;
switch (subsys) {
case SS_LCHAN:
OSMO_ASSERT(lchan_data);
lchan = lchan_data->lchan;
OSMO_ASSERT(lchan);
switch (signal) {
case S_LCHAN_MEAS_REP:
ho_meas_rep(lchan_data->mr);
break;
}
default:
break;
}
return 0;
}
static __attribute__((constructor)) void on_dso_load_ho_logic(void)
{
osmo_signal_register_handler(SS_LCHAN, ho_logic_sig_cb, NULL);
}