blob: 960bf6993424fbadee730594292a3d470331037b [file] [log] [blame]
Harald Welte798418a2009-11-29 22:56:14 +01001/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not
2 * actually implement the handover algorithm/decision, but executes a
3 * handover decision */
4
5/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
6 *
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
Harald Welte9af6ddf2011-01-01 15:25:50 +010010 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
Harald Welte798418a2009-11-29 22:56:14 +010012 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte9af6ddf2011-01-01 15:25:50 +010017 * GNU Affero General Public License for more details.
Harald Welte798418a2009-11-29 22:56:14 +010018 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010019 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Harald Welte798418a2009-11-29 22:56:14 +010021 *
22 */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <errno.h>
28#include <time.h>
29#include <netinet/in.h>
30
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010031#include <osmocom/core/msgb.h>
Neels Hofmeyrc0164792017-09-04 15:15:32 +020032#include <osmocom/bsc/debug.h>
33#include <osmocom/bsc/gsm_data.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010034#include <osmocom/gsm/gsm_utils.h>
Neels Hofmeyrc0164792017-09-04 15:15:32 +020035#include <osmocom/bsc/abis_rsl.h>
36#include <osmocom/bsc/chan_alloc.h>
37#include <osmocom/bsc/signal.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010038#include <osmocom/core/talloc.h>
Neels Hofmeyrc0164792017-09-04 15:15:32 +020039#include <osmocom/bsc/bsc_subscriber.h>
40#include <osmocom/bsc/gsm_04_08_utils.h>
Neels Hofmeyr6dff51d2018-02-12 16:56:41 +010041#include <osmocom/bsc/handover.h>
Neels Hofmeyr45e46d22018-02-15 14:10:12 +010042#include <osmocom/bsc/handover_cfg.h>
Harald Welte3561bd42018-01-28 03:04:16 +010043#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
Harald Welte798418a2009-11-29 22:56:14 +010044
45static LLIST_HEAD(bsc_handovers);
Neels Hofmeyr45e46d22018-02-15 14:10:12 +010046static LLIST_HEAD(handover_decision_callbacks);
Harald Welte798418a2009-11-29 22:56:14 +010047
Holger Hans Peter Freytherd30ed6b2014-12-17 21:21:36 +010048static void handover_free(struct bsc_handover *ho)
49{
50 osmo_timer_del(&ho->T3103);
51 llist_del(&ho->list);
52 talloc_free(ho);
53}
54
Harald Welte798418a2009-11-29 22:56:14 +010055static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan)
56{
57 struct bsc_handover *ho;
58
59 llist_for_each_entry(ho, &bsc_handovers, list) {
60 if (ho->new_lchan == new_lchan)
61 return ho;
62 }
63
64 return NULL;
65}
66
67static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan)
68{
69 struct bsc_handover *ho;
70
71 llist_for_each_entry(ho, &bsc_handovers, list) {
72 if (ho->old_lchan == old_lchan)
73 return ho;
74 }
75
76 return NULL;
77}
78
Neels Hofmeyr6dff51d2018-02-12 16:56:41 +010079/*! Hand over the specified logical channel to the specified new BTS and possibly change the lchan type.
80 * This is the main entry point for the actual handover algorithm, after the decision whether to initiate
81 * HO to a specific BTS. To not change the lchan type, pass old_lchan->type. */
Neels Hofmeyr45e46d22018-02-15 14:10:12 +010082int bsc_handover_start(enum hodec_id from_hodec_id, struct gsm_lchan *old_lchan, struct gsm_bts *new_bts,
Neels Hofmeyr6dff51d2018-02-12 16:56:41 +010083 enum gsm_chan_t new_lchan_type)
Harald Welte798418a2009-11-29 22:56:14 +010084{
Harald Welte3561bd42018-01-28 03:04:16 +010085 struct gsm_subscriber_connection *conn;
Harald Welte798418a2009-11-29 22:56:14 +010086 struct bsc_handover *ho;
Neels Hofmeyrc766c9e2017-11-27 17:58:04 +010087 static uint8_t ho_ref = 0;
Harald Welte3561bd42018-01-28 03:04:16 +010088 bool do_assignment;
89
90 OSMO_ASSERT(old_lchan);
Harald Welte798418a2009-11-29 22:56:14 +010091
Harald Welte66706812009-12-17 22:23:21 +010092 /* don't attempt multiple handovers for the same lchan at
93 * the same time */
94 if (bsc_ho_by_old_lchan(old_lchan))
95 return -EBUSY;
96
Harald Welte3561bd42018-01-28 03:04:16 +010097 conn = old_lchan->conn;
98 if (!conn) {
Holger Hans Peter Freythere071ab72010-06-30 12:40:10 +080099 LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n");
100 return -ENOSPC;
101 }
102
Harald Welte3561bd42018-01-28 03:04:16 +0100103 if (!new_bts)
104 new_bts = old_lchan->ts->trx->bts;
105 OSMO_ASSERT(new_bts);
106
107 do_assignment = (new_bts == old_lchan->ts->trx->bts);
108
109 ho = talloc_zero(conn, struct bsc_handover);
110 if (!ho) {
111 LOGP(DHO, LOGL_FATAL, "Out of Memory\n");
112 return -ENOMEM;
113 }
114 ho->from_hodec_id = from_hodec_id;
115 ho->old_lchan = old_lchan;
116 ho->new_bts = new_bts;
117 ho->new_lchan_type = new_lchan_type;
118 ho->ho_ref = ho_ref++;
119 ho->inter_cell = !do_assignment;
120 ho->async = true;
121 llist_add(&ho->list, &bsc_handovers);
122
123 conn->ho = ho;
124
125 DEBUGP(DHO, "(BTS %u trx %u ts %u lchan %u %s)->(BTS %u lchan %s) Initiating %s...\n",
Neels Hofmeyrcbc999c2018-02-12 17:25:04 +0100126 old_lchan->ts->trx->bts->nr,
127 old_lchan->ts->trx->nr,
128 old_lchan->ts->nr,
129 old_lchan->nr,
130 gsm_pchan_name(old_lchan->ts->pchan),
131 new_bts->nr,
Harald Welte3561bd42018-01-28 03:04:16 +0100132 gsm_lchant_name(new_lchan_type),
133 do_assignment ? "Assignment" : "Handover");
Neels Hofmeyrcbc999c2018-02-12 17:25:04 +0100134
Harald Welte3561bd42018-01-28 03:04:16 +0100135 return osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_HO_START, NULL);
136}
137
138/*! Start actual handover. Call bsc_handover_start() instead; The only legal caller is the GSCON FSM in
139 * bsc_subscr_conn_fsm.c. */
140int bsc_handover_start_gscon(struct gsm_subscriber_connection *conn)
141{
142 int rc;
143 struct gsm_network *network = conn->network;
144 struct bsc_handover *ho = conn->ho;
145 struct gsm_lchan *old_lchan;
146 struct gsm_lchan *new_lchan;
147
148 if (!ho) {
149 LOGP(DHO, LOGL_ERROR, "%s: Requested to start handover, but conn->ho is NULL\n",
150 bsc_subscr_name(conn->bsub));
151 return -EINVAL;
152 }
153
154 OSMO_ASSERT(ho->old_lchan && ho->new_bts);
155
156 if (ho->old_lchan->conn != conn) {
157 LOGP(DHO, LOGL_ERROR,
158 "%s: Requested to start handover, but the lchan does not belong to this conn\n",
159 bsc_subscr_name(conn->bsub));
160 return -EINVAL;
161 }
162
163 rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED]);
164
165 ho->new_lchan = lchan_alloc(ho->new_bts, ho->new_lchan_type, 0);
166 if (!ho->new_lchan) {
167 LOGP(DHO, LOGL_NOTICE, "No free channel for %s\n", gsm_lchant_name(ho->new_lchan_type));
Neels Hofmeyr4e3db632018-02-12 17:25:04 +0100168 rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL]);
Harald Welte798418a2009-11-29 22:56:14 +0100169 return -ENOSPC;
Harald Welte8d77b952009-12-17 00:31:10 +0100170 }
Harald Welte798418a2009-11-29 22:56:14 +0100171
Harald Welte3561bd42018-01-28 03:04:16 +0100172 LOGPHO(ho, LOGL_INFO, "Triggering %s\n", ho->inter_cell? "Handover" : "Assignment");
Neels Hofmeyrcbc999c2018-02-12 17:25:04 +0100173
Harald Welte8d77b952009-12-17 00:31:10 +0100174 /* copy some parameters from old lchan */
Harald Welte3561bd42018-01-28 03:04:16 +0100175 old_lchan = ho->old_lchan;
176 new_lchan = ho->new_lchan;
Harald Welte8d77b952009-12-17 00:31:10 +0100177 memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr));
Harald Welte3561bd42018-01-28 03:04:16 +0100178 if (!ho->inter_cell) {
Neels Hofmeyr05a3a2d2018-02-12 17:25:04 +0100179 new_lchan->ms_power = old_lchan->ms_power;
180 new_lchan->rqd_ta = old_lchan->rqd_ta;
181 } else {
182 new_lchan->ms_power =
Harald Welte3561bd42018-01-28 03:04:16 +0100183 ms_pwr_ctl_lvl(ho->new_bts->band, ho->new_bts->ms_max_power);
Neels Hofmeyr05a3a2d2018-02-12 17:25:04 +0100184 /* FIXME: do we have a better idea of the timing advance? */
185 //new_lchan->rqd_ta = old_lchan->rqd_ta;
186 }
Harald Welte8d77b952009-12-17 00:31:10 +0100187 new_lchan->bs_power = old_lchan->bs_power;
188 new_lchan->rsl_cmode = old_lchan->rsl_cmode;
189 new_lchan->tch_mode = old_lchan->tch_mode;
Neels Hofmeyref497b82018-02-12 17:28:07 +0100190 memcpy(&new_lchan->mr_ms_lv, &old_lchan->mr_ms_lv, sizeof(new_lchan->mr_ms_lv));
191 memcpy(&new_lchan->mr_bts_lv, &old_lchan->mr_bts_lv, sizeof(new_lchan->mr_bts_lv));
Holger Hans Peter Freythere071ab72010-06-30 12:40:10 +0800192
Harald Welte3561bd42018-01-28 03:04:16 +0100193 new_lchan->conn = conn;
Harald Welte798418a2009-11-29 22:56:14 +0100194
Neels Hofmeyr5eaa4fb2017-11-27 17:57:15 +0100195 rc = rsl_chan_activate_lchan(new_lchan,
Neels Hofmeyr063868e2018-02-15 14:18:22 +0100196 ho->async ? RSL_ACT_INTER_ASYNC : RSL_ACT_INTER_SYNC,
Neels Hofmeyr5eaa4fb2017-11-27 17:57:15 +0100197 ho->ho_ref);
Harald Welte798418a2009-11-29 22:56:14 +0100198 if (rc < 0) {
Neels Hofmeyrcbc999c2018-02-12 17:25:04 +0100199 LOGPHO(ho, LOGL_INFO, "%s Failure: activate lchan rc = %d\n",
Harald Welte3561bd42018-01-28 03:04:16 +0100200 ho->inter_cell? "Handover" : "Assignment", rc);
Harald Welte798418a2009-11-29 22:56:14 +0100201 lchan_free(new_lchan);
Harald Welte3561bd42018-01-28 03:04:16 +0100202 ho->new_lchan = NULL;
203 bsc_clear_handover(conn, 0);
Harald Welte798418a2009-11-29 22:56:14 +0100204 return rc;
205 }
206
Holger Hans Peter Freyther5eec9d92010-04-10 00:16:04 +0200207 rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ);
Harald Welte798418a2009-11-29 22:56:14 +0100208 /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */
209
210 return 0;
211}
212
Neels Hofmeyra2733ae2017-11-28 00:29:25 +0100213/* clear any operation for this connection */
Holger Hans Peter Freytherebd50a62010-12-27 13:46:48 +0100214void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan)
Holger Hans Peter Freytherf2553a62010-06-30 12:58:14 +0800215{
Harald Welte3561bd42018-01-28 03:04:16 +0100216 struct bsc_handover *ho = conn->ho;
Holger Hans Peter Freytherf2553a62010-06-30 12:58:14 +0800217
Harald Welte3561bd42018-01-28 03:04:16 +0100218 if (!ho)
Holger Hans Peter Freytherf2553a62010-06-30 12:58:14 +0800219 return;
Harald Welte3561bd42018-01-28 03:04:16 +0100220
221 if (ho->new_lchan) {
222 ho->new_lchan->conn = NULL;
223 if (free_lchan)
224 lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END);
225 ho->new_lchan = NULL;
Holger Hans Peter Freytherf2553a62010-06-30 12:58:14 +0800226 }
227
Holger Hans Peter Freytherd30ed6b2014-12-17 21:21:36 +0100228 handover_free(ho);
Harald Welte3561bd42018-01-28 03:04:16 +0100229 conn->ho = NULL;
Holger Hans Peter Freytherf2553a62010-06-30 12:58:14 +0800230}
231
Harald Welte798418a2009-11-29 22:56:14 +0100232/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */
233static void ho_T3103_cb(void *_ho)
234{
235 struct bsc_handover *ho = _ho;
Harald Welteffa55a42009-12-22 19:07:32 +0100236 struct gsm_network *net = ho->new_lchan->ts->trx->bts->network;
Harald Welte798418a2009-11-29 22:56:14 +0100237
Harald Welte8d77b952009-12-17 00:31:10 +0100238 DEBUGP(DHO, "HO T3103 expired\n");
Alexander Couzensb847a212016-08-02 11:34:11 +0200239 rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT]);
Harald Welte8d77b952009-12-17 00:31:10 +0100240
Harald Welte3561bd42018-01-28 03:04:16 +0100241 /* Inform the GSCON FSM about the timed out handover */
242 osmo_fsm_inst_dispatch(ho->old_lchan->conn->fi, GSCON_EV_HO_TIMEOUT, NULL);
243
244 bsc_clear_handover(ho->old_lchan->conn, 1);
Harald Welte798418a2009-11-29 22:56:14 +0100245}
246
247/* RSL has acknowledged activation of the new lchan */
248static int ho_chan_activ_ack(struct gsm_lchan *new_lchan)
249{
250 struct bsc_handover *ho;
Harald Welte798418a2009-11-29 22:56:14 +0100251
Harald Weltea9fa8dc2009-12-18 14:50:08 +0100252 /* we need to check if this channel activation is related to
253 * a handover at all (and if, which particular handover) */
Harald Welte798418a2009-11-29 22:56:14 +0100254 ho = bsc_ho_by_new_lchan(new_lchan);
Harald Weltea9fa8dc2009-12-18 14:50:08 +0100255 if (!ho)
Harald Welte798418a2009-11-29 22:56:14 +0100256 return -ENODEV;
Harald Weltea9fa8dc2009-12-18 14:50:08 +0100257
Neels Hofmeyrcbc999c2018-02-12 17:25:04 +0100258 LOGPHO(ho, LOGL_INFO, "Channel Activate Ack, send %s COMMAND\n", ho->inter_cell? "HANDOVER" : "ASSIGNMENT");
Harald Welte798418a2009-11-29 22:56:14 +0100259
260 /* we can now send the 04.08 HANDOVER COMMAND to the MS
261 * using the old lchan */
262
Neels Hofmeyrb0370532018-02-15 14:18:58 +0100263 gsm48_send_ho_cmd(ho->old_lchan, new_lchan, new_lchan->ms_power, ho->ho_ref);
Harald Welte798418a2009-11-29 22:56:14 +0100264
265 /* start T3103. We can continue either with T3103 expiration,
266 * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */
Pablo Neira Ayuso51215762017-05-08 20:57:52 +0200267 osmo_timer_setup(&ho->T3103, ho_T3103_cb, ho);
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200268 osmo_timer_schedule(&ho->T3103, 10, 0);
Harald Welte798418a2009-11-29 22:56:14 +0100269
Harald Weltea0379022009-12-20 17:04:40 +0100270 /* create a RTP connection */
271 if (is_ipaccess_bts(new_lchan->ts->trx->bts))
272 rsl_ipacc_crcx(new_lchan);
273
Harald Welte798418a2009-11-29 22:56:14 +0100274 return 0;
275}
276
277/* RSL has not acknowledged activation of the new lchan */
278static int ho_chan_activ_nack(struct gsm_lchan *new_lchan)
279{
280 struct bsc_handover *ho;
Neels Hofmeyr45e46d22018-02-15 14:10:12 +0100281 struct handover_decision_callbacks *hdc;
Harald Welte798418a2009-11-29 22:56:14 +0100282
283 ho = bsc_ho_by_new_lchan(new_lchan);
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100284 if (!ho) {
Neels Hofmeyr8489b462018-02-15 14:19:38 +0100285 /* This lchan is not involved in a handover. */
286 return 0;
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100287 }
Harald Welte798418a2009-11-29 22:56:14 +0100288
Neels Hofmeyr45e46d22018-02-15 14:10:12 +0100289 hdc = handover_decision_callbacks_get(ho->from_hodec_id);
290 if (hdc && hdc->on_ho_chan_activ_nack)
291 hdc->on_ho_chan_activ_nack(ho);
292
Harald Welte3561bd42018-01-28 03:04:16 +0100293 bsc_clear_handover(new_lchan->conn, 0);
Harald Welte798418a2009-11-29 22:56:14 +0100294 return 0;
295}
296
297/* GSM 04.08 HANDOVER COMPLETE has been received on new channel */
298static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
299{
Holger Hans Peter Freyther9d3e2ec2010-12-26 20:34:26 +0100300 struct gsm_network *net;
Harald Welte798418a2009-11-29 22:56:14 +0100301 struct bsc_handover *ho;
302
303 ho = bsc_ho_by_new_lchan(new_lchan);
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100304 if (!ho) {
305 LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
Harald Welte798418a2009-11-29 22:56:14 +0100306 return -ENODEV;
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100307 }
Harald Welte798418a2009-11-29 22:56:14 +0100308
Holger Hans Peter Freyther9d3e2ec2010-12-26 20:34:26 +0100309 net = new_lchan->ts->trx->bts->network;
Neels Hofmeyrcbc999c2018-02-12 17:25:04 +0100310
311 LOGPHO(ho, LOGL_INFO, "%s Complete\n", ho->inter_cell ? "Handover" : "Assignment");
Harald Welte7c639a02009-12-25 23:02:50 +0100312
Alexander Couzensb847a212016-08-02 11:34:11 +0200313 rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_COMPLETED]);
Harald Welte24ff6ee2009-12-22 00:41:05 +0100314
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200315 osmo_timer_del(&ho->T3103);
Harald Welte798418a2009-11-29 22:56:14 +0100316
Holger Hans Peter Freythere071ab72010-06-30 12:40:10 +0800317 /* Replace the ho lchan with the primary one */
318 if (ho->old_lchan != new_lchan->conn->lchan)
Neels Hofmeyrcbc999c2018-02-12 17:25:04 +0100319 LOGPHO(ho, LOGL_ERROR, "Primary lchan changed during handover.\n");
Holger Hans Peter Freythere071ab72010-06-30 12:40:10 +0800320
Harald Welte3561bd42018-01-28 03:04:16 +0100321 if (new_lchan->conn->ho != ho)
Neels Hofmeyrcbc999c2018-02-12 17:25:04 +0100322 LOGPHO(ho, LOGL_ERROR, "Handover channel changed during this handover.\n");
Holger Hans Peter Freythere071ab72010-06-30 12:40:10 +0800323
Holger Hans Peter Freythere071ab72010-06-30 12:40:10 +0800324 new_lchan->conn->lchan = new_lchan;
325 ho->old_lchan->conn = NULL;
Harald Weltefe18d5c2009-12-17 17:14:43 +0100326
Holger Hans Peter Freytherd66777f2012-12-06 12:20:56 +0100327 lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END);
Harald Welteade773f2009-12-21 13:29:19 +0100328
Holger Hans Peter Freytherd30ed6b2014-12-17 21:21:36 +0100329 handover_free(ho);
Harald Welte3561bd42018-01-28 03:04:16 +0100330 new_lchan->conn->ho = NULL;
331
332 /* Inform the GSCON FSM that the handover is complete */
333 osmo_fsm_inst_dispatch(new_lchan->conn->fi, GSCON_EV_HO_COMPL, NULL);
Harald Welte798418a2009-11-29 22:56:14 +0100334 return 0;
335}
336
337/* GSM 04.08 HANDOVER FAIL has been received */
338static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
339{
Harald Welteffa55a42009-12-22 19:07:32 +0100340 struct gsm_network *net = old_lchan->ts->trx->bts->network;
Harald Welte798418a2009-11-29 22:56:14 +0100341 struct bsc_handover *ho;
Neels Hofmeyr45e46d22018-02-15 14:10:12 +0100342 struct handover_decision_callbacks *hdc;
Harald Welte798418a2009-11-29 22:56:14 +0100343
344 ho = bsc_ho_by_old_lchan(old_lchan);
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100345 if (!ho) {
346 LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
Harald Welte798418a2009-11-29 22:56:14 +0100347 return -ENODEV;
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100348 }
Harald Welte798418a2009-11-29 22:56:14 +0100349
Neels Hofmeyr45e46d22018-02-15 14:10:12 +0100350 hdc = handover_decision_callbacks_get(ho->from_hodec_id);
351 if (hdc && hdc->on_ho_failure)
352 hdc->on_ho_failure(ho);
353
Alexander Couzensb847a212016-08-02 11:34:11 +0200354 rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED]);
Harald Welte24ff6ee2009-12-22 00:41:05 +0100355
Harald Welte3561bd42018-01-28 03:04:16 +0100356 bsc_clear_handover(ho->new_lchan->conn, 1);
Holger Hans Peter Freythere071ab72010-06-30 12:40:10 +0800357
Harald Welte3561bd42018-01-28 03:04:16 +0100358 /* Inform the GSCON FSM that the handover failed */
359 osmo_fsm_inst_dispatch(old_lchan->conn->fi, GSCON_EV_HO_FAIL, NULL);
Harald Welte798418a2009-11-29 22:56:14 +0100360 return 0;
361}
362
363/* GSM 08.58 HANDOVER DETECT has been received */
364static int ho_rsl_detect(struct gsm_lchan *new_lchan)
365{
366 struct bsc_handover *ho;
367
Harald Welte6f7a5a72009-12-18 11:52:03 +0100368 ho = bsc_ho_by_new_lchan(new_lchan);
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100369 if (!ho) {
370 LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
Harald Welte798418a2009-11-29 22:56:14 +0100371 return -ENODEV;
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100372 }
Harald Welte798418a2009-11-29 22:56:14 +0100373
Neels Hofmeyrcbc999c2018-02-12 17:25:04 +0100374 LOGPHO(ho, LOGL_DEBUG, "Handover RACH detected\n");
Neels Hofmeyrbe1131d2018-01-19 01:23:35 +0100375
376 /* This is just for logging on the DHO category. The actual MGCP switchover happens in
377 * osmo_bsc_mgcp.c by receiving the same S_LCHAN_HANDOVER_DETECT signal.
378 * (Calling mgcp_handover() directly currently breaks linking in utils/...) */
Harald Welte798418a2009-11-29 22:56:14 +0100379
380 return 0;
381}
382
Neels Hofmeyr45e46d22018-02-15 14:10:12 +0100383static int ho_meas_rep(struct gsm_meas_rep *mr)
384{
385 struct handover_decision_callbacks *hdc;
386 enum hodec_id hodec_id = ho_get_algorithm(mr->lchan->ts->trx->bts->ho);
387
388 hdc = handover_decision_callbacks_get(hodec_id);
389 if (!hdc || !hdc->on_measurement_report)
390 return 0;
391 hdc->on_measurement_report(mr);
392 return 0;
393}
394
Harald Welte798418a2009-11-29 22:56:14 +0100395static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal,
396 void *handler_data, void *signal_data)
397{
Holger Hans Peter Freyther08eebd52010-12-27 13:28:20 +0100398 struct lchan_signal_data *lchan_data;
Harald Welte798418a2009-11-29 22:56:14 +0100399 struct gsm_lchan *lchan;
400
Holger Hans Peter Freyther08eebd52010-12-27 13:28:20 +0100401 lchan_data = signal_data;
Harald Welte798418a2009-11-29 22:56:14 +0100402 switch (subsys) {
403 case SS_LCHAN:
Holger Hans Peter Freyther08eebd52010-12-27 13:28:20 +0100404 lchan = lchan_data->lchan;
Harald Welte798418a2009-11-29 22:56:14 +0100405 switch (signal) {
406 case S_LCHAN_ACTIVATE_ACK:
407 return ho_chan_activ_ack(lchan);
408 case S_LCHAN_ACTIVATE_NACK:
409 return ho_chan_activ_nack(lchan);
410 case S_LCHAN_HANDOVER_DETECT:
411 return ho_rsl_detect(lchan);
412 case S_LCHAN_HANDOVER_COMPL:
413 return ho_gsm48_ho_compl(lchan);
414 case S_LCHAN_HANDOVER_FAIL:
415 return ho_gsm48_ho_fail(lchan);
Neels Hofmeyr45e46d22018-02-15 14:10:12 +0100416 case S_LCHAN_MEAS_REP:
417 return ho_meas_rep(lchan_data->mr);
Harald Welte798418a2009-11-29 22:56:14 +0100418 }
419 break;
420 default:
421 break;
422 }
423
424 return 0;
425}
426
Neels Hofmeyra2733ae2017-11-28 00:29:25 +0100427/* Return the old lchan or NULL. This is meant for audio handling */
Holger Hans Peter Freytherc121bb32012-12-26 10:17:42 +0100428struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan)
429{
430 struct bsc_handover *ho;
431 ho = bsc_ho_by_new_lchan(new_lchan);
432 if (!ho)
433 return NULL;
434 return ho->old_lchan;
435}
436
Harald Welte798418a2009-11-29 22:56:14 +0100437static __attribute__((constructor)) void on_dso_load_ho_logic(void)
438{
Pablo Neira Ayusobbc5b992011-05-06 12:12:31 +0200439 osmo_signal_register_handler(SS_LCHAN, ho_logic_sig_cb, NULL);
Harald Welte798418a2009-11-29 22:56:14 +0100440}
Andreas Eversberga9180002013-05-30 11:14:31 +0200441
442/* Count number of currently ongoing handovers
443 * inter_cell: if true, count only handovers between two cells. If false, count only handovers within one
444 * cell. */
445int bsc_ho_count(struct gsm_bts *bts, bool inter_cell)
446{
447 struct bsc_handover *ho;
448 int count = 0;
449
450 llist_for_each_entry(ho, &bsc_handovers, list) {
451 if (ho->inter_cell != inter_cell)
452 continue;
453 if (ho->new_lchan->ts->trx->bts == bts)
454 count++;
455 }
456
457 return count;
458}
Neels Hofmeyr45e46d22018-02-15 14:10:12 +0100459
460void handover_decision_callbacks_register(struct handover_decision_callbacks *hdc)
461{
462 llist_add_tail(&hdc->entry, &handover_decision_callbacks);
463}
464
465struct handover_decision_callbacks *handover_decision_callbacks_get(int hodec_id)
466{
467 struct handover_decision_callbacks *hdc;
468 llist_for_each_entry(hdc, &handover_decision_callbacks, entry) {
469 if (hdc->hodec_id == hodec_id)
470 return hdc;
471 }
472 return NULL;
473}