blob: 7467287624fe191309667fe9d4826929edda3370 [file] [log] [blame]
Neels Hofmeyrc4628a32018-12-07 14:47:34 +01001/* MSC Handover implementation */
2/*
Vadim Yanitskiy999a5932023-05-18 17:22:26 +07003 * (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
Neels Hofmeyrc4628a32018-12-07 14:47:34 +01004 * All Rights Reserved
5 *
6 * Author: Neels Hofmeyr
7 *
8 * SPDX-License-Identifier: GPL-2.0+
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010019 */
20
21#include <osmocom/core/fsm.h>
22#include <osmocom/gsm/protocol/gsm_08_08.h>
23#include <osmocom/sigtran/sccp_helpers.h>
24
25#include <osmocom/msc/msc_ho.h>
26#include <osmocom/msc/ran_msg.h>
27#include <osmocom/msc/msc_a.h>
28#include <osmocom/msc/msc_i.h>
29#include <osmocom/msc/msc_t.h>
30#include <osmocom/msc/e_link.h>
31#include <osmocom/msc/msc_i_remote.h>
32#include <osmocom/msc/msc_t_remote.h>
33#include <osmocom/msc/neighbor_ident.h>
34#include <osmocom/msc/gsm_data.h>
35#include <osmocom/msc/ran_peer.h>
36#include <osmocom/msc/vlr.h>
37#include <osmocom/msc/transaction.h>
38#include <osmocom/msc/gsm_04_08.h>
39#include <osmocom/msc/call_leg.h>
40#include <osmocom/msc/rtp_stream.h>
41#include <osmocom/msc/mncc_call.h>
Neels Hofmeyr62bfa372022-10-31 18:51:07 +010042#include <osmocom/msc/codec_mapping.h>
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010043
44struct osmo_fsm msc_ho_fsm;
45
46#define MSC_A_USE_HANDOVER "Handover"
47
48static const struct osmo_tdef_state_timeout msc_ho_fsm_timeouts[32] = {
49 [MSC_HO_ST_REQUIRED] = { .keep_timer = true, .T = -3 },
50 [MSC_HO_ST_WAIT_REQUEST_ACK] = { .keep_timer = true },
51 [MSC_HO_ST_WAIT_COMPLETE] = { .T = -3 },
52};
53
54/* Transition to a state, using the T timer defined in msc_a_fsm_timeouts.
55 * The actual timeout value is in turn obtained from network->T_defs.
56 * Assumes local variable fi exists. */
57#define msc_ho_fsm_state_chg(msc_a, state) \
58 osmo_tdef_fsm_inst_state_chg((msc_a)->ho.fi, state, msc_ho_fsm_timeouts, (msc_a)->c.ran->tdefs, 5)
59
60static __attribute__((constructor)) void msc_ho_fsm_init()
61{
Harald Welte34a8cc32019-12-01 15:32:09 +010062 OSMO_ASSERT(osmo_fsm_register(&msc_ho_fsm) == 0);
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010063}
64
65void msc_ho_down_required_reject(struct msc_a *msc_a, enum gsm0808_cause cause)
66{
Vadim Yanitskiydb4839c2019-12-01 18:52:58 +070067 struct msc_i *msc_i;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010068 uint32_t event;
69
Vadim Yanitskiydb4839c2019-12-01 18:52:58 +070070 msc_i = msc_a_msc_i(msc_a);
71 OSMO_ASSERT(msc_i);
72
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010073 struct ran_msg ran_enc_msg = {
74 .msg_type = RAN_MSG_HANDOVER_REQUIRED_REJECT,
75 .handover_required_reject = {
76 .cause = cause,
77 },
78 };
79
80 if (msc_i->c.remote_to)
81 event = MSC_I_EV_FROM_A_PREPARE_SUBSEQUENT_HANDOVER_ERROR;
82 else
83 event = MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST;
84
85 msc_a_msg_down(msc_a, MSC_ROLE_I, event, &ran_enc_msg);
86}
87
88/* Even though this is using the 3GPP TS 48.008 definitions and naming, the intention is to be RAN implementation agnostic.
89 * For other RAN types, the 48.008 items shall be translated to their respective counterparts. */
90void msc_ho_start(struct msc_a *msc_a, const struct ran_handover_required *ho_req)
91{
92 if (msc_a->ho.fi) {
93 LOG_HO(msc_a, LOGL_ERROR, "Rx Handover Required, but Handover is still ongoing\n");
94 msc_ho_down_required_reject(msc_a, GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC);
95 return;
96 }
97
98 if (!ho_req->cil.id_list_len) {
99 LOG_HO(msc_a, LOGL_ERROR, "Rx Handover Required without a Cell Identifier List\n");
100 msc_ho_down_required_reject(msc_a, GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING);
101 return;
102 }
103
104 if (msc_a_msc_t(msc_a)) {
105 LOG_HO(msc_a, LOGL_ERROR,
106 "Rx Handover Required, but this subscriber still has an active MSC-T role: %s\n",
107 msc_a_msc_t(msc_a)->c.fi->id);
108 /* Protocol error because the BSS is not supposed to send another Handover Required before the previous
109 * attempt has concluded. */
110 msc_ho_down_required_reject(msc_a, GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC);
111 return;
112 }
113
114 /* Paranoia: make sure we start with clean state */
115 msc_a->ho = (struct msc_ho_state){};
116
117 msc_a->ho.fi = osmo_fsm_inst_alloc_child(&msc_ho_fsm, msc_a->c.fi, MSC_A_EV_HANDOVER_END);
118 OSMO_ASSERT(msc_a->ho.fi);
119
120 msc_a->ho.fi->priv = msc_a;
121 msc_a->ho.info = *ho_req;
122 msc_a->ho.next_cil_idx = 0;
123
124 /* Start the timeout */
125 msc_ho_fsm_state_chg(msc_a, MSC_HO_ST_REQUIRED);
126}
127
128static void msc_ho_rtp_rollback_to_old_cell(struct msc_a *msc_a);
129
130static void msc_ho_end(struct msc_a *msc_a, bool success, enum gsm0808_cause cause)
131{
132 struct msc_i *msc_i;
133 struct msc_t *msc_t = msc_a_msc_t(msc_a);
134
135 if (!success) {
136 msc_ho_rtp_rollback_to_old_cell(msc_a);
137 msc_ho_down_required_reject(msc_a, cause);
138 }
139
140 if (success) {
141 /* Any previous call forwarding to a remote MSC becomes obsolete. */
142 if (msc_a->cc.mncc_forwarding_to_remote_ran) {
143 mncc_call_release(msc_a->cc.mncc_forwarding_to_remote_ran);
144 msc_a->cc.mncc_forwarding_to_remote_ran = NULL;
145 }
146
147 /* Replace MSC-I with new MSC-T */
148 if (msc_t->c.remote_to) {
149 /* Inter-MSC Handover. */
150
151 /* The MNCC forwarding set up for inter-MSC handover, so far transitional in msc_a->ho now
152 * becomes the "officially" active MNCC forwarding for this call. */
153 msc_a->cc.mncc_forwarding_to_remote_ran = msc_a->ho.new_cell.mncc_forwarding_to_remote_ran;
154 msc_a->ho.new_cell.mncc_forwarding_to_remote_ran = NULL;
155 mncc_call_reparent(msc_a->cc.mncc_forwarding_to_remote_ran,
156 msc_a->c.fi, -1, MSC_MNCC_EV_CALL_ENDED, NULL, NULL);
157
158 /* inter-MSC link. msc_i_remote_alloc() properly "steals" the e_link from msc_t. */
159 msc_i = msc_i_remote_alloc(msc_a->c.msub, msc_t->c.ran, msc_t->c.remote_to);
160 OSMO_ASSERT(msc_t->c.remote_to == NULL);
161 } else {
162 /* local BSS */
163 msc_i = msc_i_alloc(msc_a->c.msub, msc_t->c.ran);
164 /* msc_i_set_ran_conn() properly "steals" the ran_conn from msc_t */
165 msc_i_set_ran_conn(msc_i, msc_t->ran_conn);
166 }
167 }
168
169 osmo_fsm_inst_term(msc_a->ho.fi, OSMO_FSM_TERM_REGULAR, NULL);
170}
171
172#define msc_ho_failed(msc_a, cause, fmt, args...) do { \
173 LOG_HO(msc_a, LOGL_ERROR, fmt, ##args); \
174 msc_ho_end(msc_a, false, cause); \
175 } while(0)
176#define msc_ho_try_next_cell(msc_a, fmt, args...) do {\
177 LOG_HO(msc_a, LOGL_ERROR, fmt, ##args); \
178 msc_ho_fsm_state_chg(msc_a, MSC_HO_ST_REQUIRED); \
179 } while(0)
180#define msc_ho_success(msc_a) msc_ho_end(msc_a, true, 0)
181
182enum msc_neighbor_type msc_ho_find_target_cell(struct msc_a *msc_a, const struct gsm0808_cell_id *cid,
183 const struct neighbor_ident_entry **remote_msc,
184 struct ran_peer **ran_peer_from_neighbor_ident,
185 struct ran_peer **ran_peer_from_seen_cells)
186{
187 struct gsm_network *net = msc_a_net(msc_a);
188 const struct neighbor_ident_entry *e;
189 struct sccp_ran_inst *sri;
190 struct ran_peer *rp_from_neighbor_ident = NULL;
191 struct ran_peer *rp_from_cell_id = NULL;
192 struct ran_peer *rp;
193 int i;
194
195 OSMO_ASSERT(remote_msc);
196 OSMO_ASSERT(ran_peer_from_neighbor_ident);
197 OSMO_ASSERT(ran_peer_from_seen_cells);
198
199 e = neighbor_ident_find_by_cell(&net->neighbor_ident_list, msc_a->c.ran->type, cid);
200
201 if (e && e->addr.type == MSC_NEIGHBOR_TYPE_REMOTE_MSC) {
202 *remote_msc = e;
203 return MSC_NEIGHBOR_TYPE_REMOTE_MSC;
204 }
205
206 /* It is not a remote MSC target. Figure out local RAN peers. */
207
208 if (e && e->addr.type == MSC_NEIGHBOR_TYPE_LOCAL_RAN_PEER) {
209 /* Find local RAN peer in neighbor config. If anything is wrong with that, just keep
210 * rp_from_neighbor_ident == NULL. */
211
212 struct sccp_ran_inst *sri_from_neighbor_ident = NULL;
213 struct osmo_ss7_instance *ss7 = NULL;
214
215 /* Get the sccp_ran_inst with sanity checkin. If anything is fishy, just keep
216 * sri_from_neighbor_ident == NULL and below code will notice the error. */
217 if (e->addr.ran_type < msc_ran_infra_len) {
218 sri_from_neighbor_ident = msc_ran_infra[e->addr.ran_type].sri;
219 ss7 = osmo_sccp_get_ss7(sri_from_neighbor_ident->sccp);
220 if (!ss7)
221 sri_from_neighbor_ident = NULL;
222 }
223
224 if (!sri_from_neighbor_ident) {
225 LOG_HO(msc_a, LOGL_ERROR, "Cannot handover to RAN type %s\n", osmo_rat_type_name(e->addr.ran_type));
226 } else {
227 /* Interpret the point-code string placed in the neighbors config. */
228 int pc = osmo_ss7_pointcode_parse(ss7, e->addr.local_ran_peer_pc_str);
229
230 if (pc < 0) {
231 LOG_HO(msc_a, LOGL_ERROR, "Invalid point code string: %s\n",
232 osmo_quote_str(e->addr.local_ran_peer_pc_str, -1));
233 } else {
234 struct osmo_sccp_addr addr = {};
235 osmo_sccp_make_addr_pc_ssn(&addr, pc, sri_from_neighbor_ident->ran->ssn);
236 rp_from_neighbor_ident = ran_peer_find_by_addr(sri_from_neighbor_ident, &addr);
237 }
238 }
239
240 if (!rp_from_neighbor_ident) {
241 LOG_HO(msc_a, LOGL_ERROR, "Target RAN peer from neighbor config is not connected:"
242 " Cell ID %s resolves to target address %s\n",
243 gsm0808_cell_id_name(cid), e->addr.local_ran_peer_pc_str);
244 } else if (rp_from_neighbor_ident->fi->state != RAN_PEER_ST_READY) {
245 LOG_HO(msc_a, LOGL_ERROR, "Target RAN peer in invalid state: %s (%s)\n",
246 osmo_fsm_inst_state_name(rp_from_neighbor_ident->fi),
247 rp_from_neighbor_ident->fi->id);
248 rp_from_neighbor_ident = NULL;
249 }
250 }
251
252 /* Figure out actually connected RAN peers for this cell ID.
253 * If no cell has been found yet at all, this might determine a Handover target,
254 * otherwise this is for sanity checking. If none is found, just keep rp_from_cell_id == NULL. */
255
256 /* Iterate all connected RAN peers. Possibly, more than one RAN peer has advertised a match for this Cell ID.
257 * For example, if the handover target is identified as LAC=23 but there are multiple cells with distinct CIs
258 * serving in LAC=23, we have an ambiguity. It's up to the user to configure correctly, help with logging. */
259 for (i = 0; i < msc_ran_infra_len; i++) {
260 sri = msc_ran_infra[i].sri;
261 if (!sri)
262 continue;
263
264 rp = ran_peer_find_by_cell_id(sri, cid, true);
265 if (rp && rp->fi && rp->fi->state == RAN_PEER_ST_READY) {
266 if (rp_from_cell_id) {
267 LOG_HO(msc_a, LOGL_ERROR,
268 "Ambiguous match for cell ID %s: more than one RAN type is serving this cell"
269 " ID: %s and %s\n",
270 gsm0808_cell_id_name(cid),
271 rp_from_cell_id->fi->id,
272 rp->fi->id);
273 /* But logging is all we're going to do about it. */
274 }
275
276 /* Use the first found RAN peer, but if multiple matches are found, favor the one that matches
277 * the current RAN type. */
278 if (!rp_from_cell_id || rp->sri == msc_a->c.ran->sri)
279 rp_from_cell_id = rp;
280 }
281 }
282
283 /* Did we find mismatching targets from neighbor config and from connected cells? */
284 if (rp_from_neighbor_ident && rp_from_cell_id
285 && rp_from_neighbor_ident != rp_from_cell_id) {
286 LOG_HO(msc_a, LOGL_ERROR, "Ambiguous match for cell ID %s:"
287 " neighbor config points at %s; a matching cell is also served by connected RAN peer %s\n",
288 gsm0808_cell_id_name(cid), rp_from_neighbor_ident->fi->id, rp_from_cell_id->fi->id);
289 /* But logging is all we're going to do about it. */
290 }
291
292 if (rp_from_neighbor_ident && rp_from_neighbor_ident->sri != msc_a->c.ran->sri) {
293 LOG_HO(msc_a, LOGL_ERROR,
294 "Neighbor config indicates inter-RAT Handover, which is not implemented. Ignoring target %s\n",
295 rp_from_neighbor_ident->fi->id);
296 rp_from_neighbor_ident = NULL;
297 }
298
299 if (rp_from_cell_id && rp_from_cell_id->sri != msc_a->c.ran->sri) {
300 LOG_HO(msc_a, LOGL_ERROR,
301 "Target RAN peer indicates inter-RAT Handover, which is not implemented. Ignoring target %s\n",
302 rp_from_cell_id->fi->id);
303 rp_from_cell_id = NULL;
304 }
305
306 *ran_peer_from_neighbor_ident = rp_from_neighbor_ident;
307 *ran_peer_from_seen_cells = rp_from_cell_id;
308
309 return rp_from_neighbor_ident || rp_from_cell_id ? MSC_NEIGHBOR_TYPE_LOCAL_RAN_PEER : MSC_NEIGHBOR_TYPE_NONE;
310}
311
312static bool msc_ho_find_next_target_cell(struct msc_a *msc_a)
313{
314 struct vlr_subscr *vsub = msc_a_vsub(msc_a);
315 struct ran_handover_required *info = &msc_a->ho.info;
316 struct gsm0808_cell_id *cid = &msc_a->ho.new_cell.cid;
317 const struct neighbor_ident_entry *e;
318 struct ran_peer *rp_from_neighbor_ident = NULL;
319 struct ran_peer *rp_from_cell_id = NULL;
320 struct ran_peer *rp;
321
322 unsigned int cil_idx = msc_a->ho.next_cil_idx;
323 msc_a->ho.next_cil_idx++;
324
325 msc_a->ho.new_cell.type = MSC_NEIGHBOR_TYPE_NONE;
326
327 if (cil_idx >= info->cil.id_list_len)
328 return false;
329
330 *cid = (struct gsm0808_cell_id){
331 .id_discr = info->cil.id_discr,
332 .id = info->cil.id_list[cil_idx],
333 };
334
335 msc_a->ho.new_cell.cgi = (struct osmo_cell_global_id){
336 .lai = vsub->cgi.lai,
337 };
338 gsm0808_cell_id_to_cgi(&msc_a->ho.new_cell.cgi, cid);
339
340 switch (msc_ho_find_target_cell(msc_a, cid, &e, &rp_from_neighbor_ident, &rp_from_cell_id)) {
341 case MSC_NEIGHBOR_TYPE_REMOTE_MSC:
342 OSMO_ASSERT(e);
343 msc_a->ho.new_cell.ran_type = e->addr.ran_type;
344 msc_a->ho.new_cell.type = MSC_NEIGHBOR_TYPE_REMOTE_MSC;
345 msc_a->ho.new_cell.msc_ipa_name = e->addr.remote_msc_ipa_name.buf;
346 return true;
347
348 case MSC_NEIGHBOR_TYPE_LOCAL_RAN_PEER:
349 rp = rp_from_neighbor_ident ? : rp_from_cell_id;
350 OSMO_ASSERT(rp);
351 msc_a->ho.new_cell.type = MSC_NEIGHBOR_TYPE_LOCAL_RAN_PEER;
352 msc_a->ho.new_cell.ran_peer = rp;
353 return true;
354
355 default:
356 break;
357 }
358
359 LOG_HO(msc_a, LOGL_DEBUG, "Cannot find target peer for cell ID %s\n", gsm0808_cell_id_name(cid));
360 /* Try the next cell id, if any. */
361 return msc_ho_find_next_target_cell(msc_a);
362}
363
364static void msc_ho_fsm_required_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
365{
366 struct msc_a *msc_a = fi->priv;
367
368 if (!msc_ho_find_next_target_cell(msc_a)) {
369 int tried = msc_a->ho.next_cil_idx - 1;
370 msc_ho_failed(msc_a, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
371 "Attempted Handover to %u cells without success\n", tried);
372 return;
373 }
374
375 msc_ho_fsm_state_chg(msc_a, MSC_HO_ST_WAIT_REQUEST_ACK);
376}
377
378static void msc_ho_send_handover_request(struct msc_a *msc_a)
379{
380 struct vlr_subscr *vsub = msc_a_vsub(msc_a);
381 struct gsm_network *net = msc_a_net(msc_a);
382 struct gsm0808_channel_type channel_type;
Neels Hofmeyrb091d212022-08-08 19:11:51 +0200383 struct gsm0808_speech_codec_list scl;
Philipp Maier7da956e2020-06-09 14:34:40 +0200384 struct gsm_trans *cc_trans = msc_a->cc.active_trans;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100385 struct ran_msg ran_enc_msg = {
386 .msg_type = RAN_MSG_HANDOVER_REQUEST,
387 .handover_request = {
388 .imsi = vsub->imsi,
389 .classmark = &vsub->classmark,
390 .geran = {
391 .chosen_encryption = &msc_a->geran_encr,
392 .a5_encryption_mask = net->a5_encryption_mask,
393 },
394 .bssap_cause = GSM0808_CAUSE_BETTER_CELL,
395 .current_channel_type_1_present = msc_a->ho.info.current_channel_type_1_present,
396 .current_channel_type_1 = msc_a->ho.info.current_channel_type_1,
397 .speech_version_used = msc_a->ho.info.speech_version_used,
398 .old_bss_to_new_bss_info_raw = msc_a->ho.info.old_bss_to_new_bss_info_raw,
399 .old_bss_to_new_bss_info_raw_len = msc_a->ho.info.old_bss_to_new_bss_info_raw_len,
400
401 /* Don't send AoIP Transport Layer Address for inter-MSC Handover */
402 .rtp_ran_local = (msc_a->ho.new_cell.type == MSC_NEIGHBOR_TYPE_LOCAL_RAN_PEER)
403 ? call_leg_local_ip(msc_a->cc.call_leg, RTP_TO_RAN) : NULL,
Philipp Maier7da956e2020-06-09 14:34:40 +0200404 .call_id_present = true,
405 .call_id = cc_trans->callref,
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100406 },
407 };
408
Neels Hofmeyr73d093a2021-06-23 23:54:43 +0200409 if (msc_a->geran_encr.key_len)
410 LOG_MSC_A(msc_a, LOGL_DEBUG, "HO Request with ciphering: A5/%d kc %s kc128 %s\n",
411 msc_a->geran_encr.alg_id - 1,
412 osmo_hexdump_nospc_c(OTC_SELECT, msc_a->geran_encr.key, msc_a->geran_encr.key_len),
413 msc_a->geran_encr.kc128_present ?
414 osmo_hexdump_nospc_c(OTC_SELECT, msc_a->geran_encr.kc128, sizeof(msc_a->geran_encr.kc128))
415 : "-");
416
Neels Hofmeyrb6c11c42022-08-08 18:15:32 +0200417 if (cc_trans) {
418 if (sdp_audio_codecs_to_gsm0808_channel_type(&channel_type,
419 &cc_trans->cc.codecs.result.audio_codecs)) {
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100420 msc_ho_failed(msc_a, GSM0808_CAUSE_EQUIPMENT_FAILURE,
Neels Hofmeyrb6c11c42022-08-08 18:15:32 +0200421 "Failed to determine Channel Type for Handover Request message\n");
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100422 return;
423 }
424 ran_enc_msg.handover_request.geran.channel_type = &channel_type;
Neels Hofmeyrb091d212022-08-08 19:11:51 +0200425
426 sdp_audio_codecs_to_speech_codec_list(&scl, &cc_trans->cc.codecs.result.audio_codecs);
427 if (!scl.len) {
428 msc_ho_failed(msc_a, GSM0808_CAUSE_EQUIPMENT_FAILURE, "Failed to compose"
429 " Codec List (MSC Preferred) for Handover Request message\n");
430 return;
431 }
432 ran_enc_msg.handover_request.codec_list_msc_preferred = &scl;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100433 }
434
435 gsm0808_cell_id_from_cgi(&ran_enc_msg.handover_request.cell_id_serving, CELL_IDENT_WHOLE_GLOBAL, &vsub->cgi);
436 ran_enc_msg.handover_request.cell_id_target = msc_a->ho.new_cell.cid;
437
438 if (msc_a_msg_down(msc_a, MSC_ROLE_T, MSC_T_EV_FROM_A_PREPARE_HANDOVER_REQUEST, &ran_enc_msg))
439 msc_ho_try_next_cell(msc_a, "Failed to send Handover Request message\n");
440}
441
442static void msc_ho_fsm_wait_request_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
443{
444 struct msc_a *msc_a = fi->priv;
445 struct msc_i *msc_i = msc_a_msc_i(msc_a);
446 struct msc_t *msc_t;
447 struct ran_peer *rp;
448 const char *ipa_name;
449
450 msc_t = msc_a_msc_t(msc_a);
451 if (msc_t) {
452 /* All the other code should prevent this from happening, ever. */
453 msc_ho_failed(msc_a, GSM0808_CAUSE_EQUIPMENT_FAILURE,
454 "Cannot initiate Handover Request, there still is an active MSC-T role: %s\n",
455 msc_t->c.fi->id);
456 return;
457 }
458
459 if (!msc_i) {
460 msc_ho_failed(msc_a, GSM0808_CAUSE_EQUIPMENT_FAILURE,
461 "Cannot initiate Handover Request, there is no MSC-I role\n");
462 return;
463 }
464
465 if (!msc_i->c.remote_to
466 && !(msc_i->ran_conn && msc_i->ran_conn->ran_peer)) {
467 msc_ho_failed(msc_a, GSM0808_CAUSE_EQUIPMENT_FAILURE,
468 "Cannot initiate Handover Request, MSC-I role has no connection\n");
469 return;
470 }
471
472 switch (msc_a->ho.new_cell.type) {
473 case MSC_NEIGHBOR_TYPE_LOCAL_RAN_PEER:
474 rp = msc_a->ho.new_cell.ran_peer;
475 OSMO_ASSERT(rp && rp->fi);
476
477 if (msc_i->c.remote_to) {
478 LOG_HO(msc_a, LOGL_INFO,
479 "Starting inter-MSC Subsequent Handover from remote MSC %s to local %s\n",
480 msc_i->c.remote_to->remote_name, rp->fi->id);
481 msc_a->ho.subsequent_ho = true;
482 } else {
483 LOG_HO(msc_a, LOGL_INFO, "Starting inter-BSC Handover from %s to %s\n",
484 msc_i->ran_conn->ran_peer->fi->id, rp->fi->id);
485 }
486
Vadim Yanitskiya870faf2019-05-11 02:45:46 +0700487 msc_t = msc_t_alloc(msc_a->c.msub, rp);
488 break;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100489
490 case MSC_NEIGHBOR_TYPE_REMOTE_MSC:
491 ipa_name = msc_a->ho.new_cell.msc_ipa_name;
492 OSMO_ASSERT(ipa_name);
493
494 if (msc_i->c.remote_to) {
495 LOG_HO(msc_a, LOGL_INFO,
496 "Starting inter-MSC Subsequent Handover from remote MSC %s to remote MSC at %s\n",
497 msc_i->c.remote_to->remote_name, osmo_quote_str(ipa_name, -1));
498 msc_a->ho.subsequent_ho = true;
499 } else {
500 LOG_HO(msc_a, LOGL_INFO, "Starting inter-MSC Handover from local %s to remote MSC at %s\n",
501 msc_i->ran_conn->ran_peer->fi->id,
502 osmo_quote_str(ipa_name, -1));
503 }
504
Vadim Yanitskiya870faf2019-05-11 02:45:46 +0700505 msc_t = msc_t_remote_alloc(msc_a->c.msub, msc_a->c.ran,
506 (const uint8_t *) ipa_name,
507 strlen(ipa_name));
508 break;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100509
510 default:
511 msc_ho_try_next_cell(msc_a, "unknown Handover target type %d\n", msc_a->ho.new_cell.type);
512 return;
513 }
514
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100515 if (!msc_t) {
516 /* There should definitely be one now. */
517 msc_ho_failed(msc_a, GSM0808_CAUSE_EQUIPMENT_FAILURE,
518 "Cannot initiate Handover Request, failed to set up a target MSC-T\n");
519 return;
520 }
Vadim Yanitskiya870faf2019-05-11 02:45:46 +0700521
522 msc_ho_send_handover_request(msc_a);
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100523}
524
525static void msc_ho_rx_request_ack(struct msc_a *msc_a, struct msc_a_ran_dec_data *hra);
526
527static void msc_ho_fsm_wait_request_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
528{
529 struct msc_a *msc_a = fi->priv;
530
531 switch (event) {
532
533 case MSC_HO_EV_RX_REQUEST_ACK:
534 msc_ho_rx_request_ack(msc_a, (struct msc_a_ran_dec_data*)data);
535 return;
536
537 case MSC_HO_EV_RX_FAILURE:
538 msc_ho_failed(msc_a, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
539 "Received Handover Failure message\n");
540 return;
541
542 default:
543 OSMO_ASSERT(false);
544 }
545}
546
547static void msc_ho_rtp_switch_to_new_cell(struct msc_a *msc_a);
548
549void msc_ho_mncc_forward_cb(struct mncc_call *mncc_call, const union mncc_msg *mncc_msg, void *data)
550{
551 struct msc_a *msc_a = data;
552 switch (mncc_msg->msg_type) {
553 case MNCC_RTP_CONNECT:
554 msc_a->ho.rtp_switched_to_new_cell = true;
555 return;
556 default:
557 return;
558 }
559}
560
561/* Initiate call forwarding via MNCC: call the Handover Number that the other MSC assigned. */
562static int msc_ho_start_inter_msc_call_forwarding(struct msc_a *msc_a, struct msc_t *msc_t,
563 const struct msc_a_ran_dec_data *hra)
564{
565 const struct osmo_gsup_message *e_info = hra->an_apdu->e_info;
566 struct gsm_mncc outgoing_call_req = {};
567 struct call_leg *cl = msc_a->cc.call_leg;
568 struct rtp_stream *rtp_to_ran = cl ? cl->rtp[RTP_TO_RAN] : NULL;
569 struct mncc_call *mncc_call;
570
571 if (!e_info || !e_info->msisdn_enc || !e_info->msisdn_enc_len) {
572 msc_ho_try_next_cell(msc_a,
573 "No Handover Number in Handover Request Acknowledge from remote MSC\n");
574 return -EINVAL;
575 }
576
Neels Hofmeyrda3ce712019-05-09 14:16:26 +0200577 if (!rtp_to_ran) {
578 msc_ho_failed(msc_a, GSM0808_CAUSE_EQUIPMENT_FAILURE, "Unexpected: no RTP stream is set up\n");
579 return -EINVAL;
580 }
581
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100582 /* Backup old cell's RTP IP:port and codec data */
583 msc_a->ho.old_cell.ran_remote_rtp = rtp_to_ran->remote;
Neels Hofmeyr62bfa372022-10-31 18:51:07 +0100584 msc_a->ho.old_cell.codecs = rtp_to_ran->codecs;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100585
586 /* Blindly taken over from an MNCC trace of existing code: send an all-zero CCCAP: */
587 outgoing_call_req.fields |= MNCC_F_CCCAP;
588
589 /* Called number */
590 outgoing_call_req.fields |= MNCC_F_CALLED;
591 outgoing_call_req.called.plan = 1; /* Empirical magic number. There seem to be no enum or defines for this.
592 * The only other place setting this apparently is gsm48_decode_called(). */
593 if (gsm48_decode_bcd_number2(outgoing_call_req.called.number, sizeof(outgoing_call_req.called.number),
594 e_info->msisdn_enc, e_info->msisdn_enc_len, 0)) {
595 msc_ho_try_next_cell(msc_a,
596 "Failed to decode Handover Number in Handover Request Acknowledge"
597 " from remote MSC\n");
598 return -EINVAL;
599 }
600
601 if (msc_a->cc.active_trans) {
602 outgoing_call_req.fields |= MNCC_F_BEARER_CAP;
603 outgoing_call_req.bearer_cap = msc_a->cc.active_trans->bearer_cap;
604 }
605
606 mncc_call = mncc_call_alloc(msc_a_vsub(msc_a),
607 msc_a->ho.fi,
608 MSC_HO_EV_MNCC_FORWARDING_COMPLETE,
609 MSC_HO_EV_MNCC_FORWARDING_FAILED,
610 msc_ho_mncc_forward_cb, msc_a);
611
612 mncc_call_set_rtp_stream(mncc_call, rtp_to_ran);
613 msc_a->ho.new_cell.mncc_forwarding_to_remote_ran = mncc_call;
614 return mncc_call_outgoing_start(mncc_call, &outgoing_call_req);
615}
616
617static void msc_ho_rx_request_ack(struct msc_a *msc_a, struct msc_a_ran_dec_data *hra)
618{
619 struct msc_t *msc_t = msc_a_msc_t(msc_a);
620 struct ran_msg ran_enc_msg;
621
622 OSMO_ASSERT(hra->ran_dec);
623 OSMO_ASSERT(hra->an_apdu);
624
625 if (!msc_t) {
626 msc_ho_failed(msc_a, GSM0808_CAUSE_EQUIPMENT_FAILURE, "MSC-T role missing\n");
627 return;
628 }
629
630 if (!hra->ran_dec->handover_request_ack.rr_ho_command
631 || !hra->ran_dec->handover_request_ack.rr_ho_command_len) {
632 msc_ho_try_next_cell(msc_a, "Missing mandatory IE in Handover Request Acknowledge:"
633 " L3 Info (RR Handover Command)\n");
634 return;
635 }
636
637 if (!hra->ran_dec->handover_request_ack.chosen_channel_present) {
638 LOG_HO(msc_a, LOGL_DEBUG, "No 'Chosen Channel' IE in Handover Request Ack\n");
639 msc_t->geran.chosen_channel = 0;
640 } else
641 msc_t->geran.chosen_channel = hra->ran_dec->handover_request_ack.chosen_channel;
642
643 if (!hra->ran_dec->handover_request_ack.chosen_encr_alg) {
644 LOG_HO(msc_a, LOGL_DEBUG, "No 'Chosen Encryption Algorithm' IE in Handover Request Ack\n");
645 msc_t->geran.chosen_encr_alg = 0;
646 } else {
647 msc_t->geran.chosen_encr_alg = hra->ran_dec->handover_request_ack.chosen_encr_alg;
648 if (msc_t->geran.chosen_encr_alg < 1 || msc_t->geran.chosen_encr_alg > 8) {
649 msc_ho_try_next_cell(msc_a, "Handover Request Ack: Invalid 'Chosen Encryption Algorithm': %u\n",
650 msc_t->geran.chosen_encr_alg);
651 return;
652 }
653 }
654
655 msc_t->geran.chosen_speech_version = hra->ran_dec->handover_request_ack.chosen_speech_version;
656 if (!msc_t->geran.chosen_speech_version)
657 LOG_HO(msc_a, LOGL_DEBUG, "No 'Chosen Speech Version' IE in Handover Request Ack\n");
658
659 /* Inter-MSC call forwarding? */
660 if (msc_a->ho.new_cell.type == MSC_NEIGHBOR_TYPE_REMOTE_MSC) {
661 if (msc_ho_start_inter_msc_call_forwarding(msc_a, msc_t, hra))
662 return;
663 }
664
665 msc_ho_fsm_state_chg(msc_a, MSC_HO_ST_WAIT_COMPLETE);
666
667 /* Forward the RR Handover Command composed by the new RAN peer down to the old RAN peer */
668 ran_enc_msg = (struct ran_msg){
669 .msg_type = RAN_MSG_HANDOVER_COMMAND,
670 .handover_command = {
671 .rr_ho_command = hra->ran_dec->handover_request_ack.rr_ho_command,
672 .rr_ho_command_len = hra->ran_dec->handover_request_ack.rr_ho_command_len,
673 },
674 };
675
676 if (msc_a_msg_down(msc_a, MSC_ROLE_I,
677 msc_a->ho.subsequent_ho ? MSC_I_EV_FROM_A_PREPARE_SUBSEQUENT_HANDOVER_RESULT
678 : MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST,
679 &ran_enc_msg)) {
680 msc_ho_failed(msc_a, GSM0808_CAUSE_EQUIPMENT_FAILURE, "Failed to send Handover Command\n");
681 return;
682 }
683
684 msc_a->ho.new_cell.ran_remote_rtp = hra->ran_dec->handover_request_ack.remote_rtp;
Neels Hofmeyr84ce2062019-10-05 05:15:25 +0200685 if (osmo_sockaddr_str_is_nonzero(&msc_a->ho.new_cell.ran_remote_rtp)) {
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100686 LOG_HO(msc_a, LOGL_DEBUG, "Request Ack contains cell's RTP address " OSMO_SOCKADDR_STR_FMT "\n",
687 OSMO_SOCKADDR_STR_FMT_ARGS(&msc_a->ho.new_cell.ran_remote_rtp));
688 }
689
690 msc_a->ho.new_cell.codec_present = hra->ran_dec->handover_request_ack.codec_present;
691 msc_a->ho.new_cell.codec = hra->ran_dec->handover_request_ack.codec;
692 if (hra->ran_dec->handover_request_ack.codec_present) {
693 LOG_HO(msc_a, LOGL_DEBUG, "Request Ack contains codec %s\n",
Neels Hofmeyr7934e0d2022-10-31 18:13:47 +0100694 gsm0808_speech_codec_type_name(msc_a->ho.new_cell.codec.type));
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100695 }
696}
697
698static void msc_ho_rtp_switch_to_new_cell(struct msc_a *msc_a)
699{
700 struct call_leg *cl = msc_a->cc.call_leg;
701 struct rtp_stream *rtp_to_ran = cl ? cl->rtp[RTP_TO_RAN] : NULL;
702
703 if (!rtp_to_ran) {
704 LOG_HO(msc_a, LOGL_DEBUG, "No RTP stream, nothing to switch\n");
705 return;
706 }
707
Neels Hofmeyr84ce2062019-10-05 05:15:25 +0200708 if (!osmo_sockaddr_str_is_nonzero(&msc_a->ho.new_cell.ran_remote_rtp)) {
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100709 LOG_HO(msc_a, LOGL_DEBUG, "New cell's RTP IP:port not yet known, not switching RTP stream\n");
710 return;
711 }
712
713 if (msc_a->ho.rtp_switched_to_new_cell) {
714 LOG_HO(msc_a, LOGL_DEBUG, "Already switched RTP to new cell\n");
715 return;
716 }
717 msc_a->ho.rtp_switched_to_new_cell = true;
718
719 /* Backup old cell's RTP IP:port and codec data */
720 msc_a->ho.old_cell.ran_remote_rtp = rtp_to_ran->remote;
Neels Hofmeyr62bfa372022-10-31 18:51:07 +0100721 msc_a->ho.old_cell.codecs = rtp_to_ran->codecs;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100722
723 LOG_HO(msc_a, LOGL_DEBUG, "Switching RTP stream to new cell: from " OSMO_SOCKADDR_STR_FMT " to " OSMO_SOCKADDR_STR_FMT "\n",
724 OSMO_SOCKADDR_STR_FMT_ARGS(&msc_a->ho.old_cell.ran_remote_rtp),
725 OSMO_SOCKADDR_STR_FMT_ARGS(&msc_a->ho.new_cell.ran_remote_rtp));
726
727 /* If a previous forwarding to a remote MSC is still active, this now becomes no longer responsible for the RTP
728 * stream. */
729 if (msc_a->cc.mncc_forwarding_to_remote_ran) {
730 if (msc_a->cc.mncc_forwarding_to_remote_ran->rtps != rtp_to_ran) {
731 LOG_HO(msc_a, LOGL_ERROR,
732 "Unexpected state: previous MNCC forwarding not using RTP-to-RAN stream\n");
733 /* That would be weird, but carry on anyway... */
734 }
735 mncc_call_detach_rtp_stream(msc_a->cc.mncc_forwarding_to_remote_ran);
736 }
737
738 /* Switch over to the new peer */
739 rtp_stream_set_remote_addr(rtp_to_ran, &msc_a->ho.new_cell.ran_remote_rtp);
Neels Hofmeyr62bfa372022-10-31 18:51:07 +0100740 if (msc_a->ho.new_cell.codec_present) {
Neels Hofmeyr7934e0d2022-10-31 18:13:47 +0100741 const struct codec_mapping *m;
742 m = codec_mapping_by_gsm0808_speech_codec_type(msc_a->ho.new_cell.codec.type);
743 /* TODO: use codec_mapping_by_gsm0808_speech_codec() to also match on codec.cfg */
744 if (!m)
745 LOG_HO(msc_a, LOGL_ERROR, "Cannot resolve codec: %s\n",
746 gsm0808_speech_codec_type_name(msc_a->ho.new_cell.codec.type));
747 else
748 rtp_stream_set_one_codec(rtp_to_ran, &m->sdp);
Neels Hofmeyr62bfa372022-10-31 18:51:07 +0100749 } else {
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100750 LOG_HO(msc_a, LOGL_ERROR, "No codec is set\n");
Neels Hofmeyr62bfa372022-10-31 18:51:07 +0100751 }
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100752 rtp_stream_commit(rtp_to_ran);
753}
754
755static void msc_ho_rtp_rollback_to_old_cell(struct msc_a *msc_a)
756{
757 struct call_leg *cl = msc_a->cc.call_leg;
758 struct rtp_stream *rtp_to_ran = cl ? cl->rtp[RTP_TO_RAN] : NULL;
759
760 if (!msc_a->ho.rtp_switched_to_new_cell) {
761 LOG_HO(msc_a, LOGL_DEBUG, "Not switched RTP to new cell yet, no need to roll back\n");
762 return;
763 }
764
765 if (!rtp_to_ran) {
766 LOG_HO(msc_a, LOGL_DEBUG, "No RTP stream, nothing to switch\n");
767 return;
768 }
769
Neels Hofmeyr84ce2062019-10-05 05:15:25 +0200770 if (!osmo_sockaddr_str_is_nonzero(&msc_a->ho.old_cell.ran_remote_rtp)) {
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100771 LOG_HO(msc_a, LOGL_DEBUG, "Have no RTP IP:port for the old cell, not switching back to\n");
772 return;
773 }
774
775 /* The new call forwarding to a remote MSC is no longer needed because the handover failed */
776 if (msc_a->ho.new_cell.mncc_forwarding_to_remote_ran)
777 mncc_call_detach_rtp_stream(msc_a->ho.new_cell.mncc_forwarding_to_remote_ran);
778
779 /* If before this handover, there was a call forwarding to a remote MSC in place, this now goes back into
780 * responsibility. */
781 if (msc_a->cc.mncc_forwarding_to_remote_ran)
782 mncc_call_set_rtp_stream(msc_a->cc.mncc_forwarding_to_remote_ran, rtp_to_ran);
783
784 msc_a->ho.rtp_switched_to_new_cell = false;
785 msc_a->ho.ready_to_switch_rtp = false;
786 LOG_HO(msc_a, LOGL_NOTICE, "Switching RTP back to old cell\n");
787
788 /* Switch back to the old cell */
789 rtp_stream_set_remote_addr(rtp_to_ran, &msc_a->ho.old_cell.ran_remote_rtp);
Neels Hofmeyr62bfa372022-10-31 18:51:07 +0100790 rtp_stream_set_codecs(rtp_to_ran, &msc_a->ho.old_cell.codecs);
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100791 rtp_stream_commit(rtp_to_ran);
792}
793
794static void msc_ho_send_handover_succeeded(struct msc_a *msc_a)
795{
796 struct ran_msg ran_enc_msg = {
797 .msg_type = RAN_MSG_HANDOVER_SUCCEEDED,
798 };
799
800 if (msc_a_msg_down(msc_a, MSC_ROLE_I, MSC_I_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST, &ran_enc_msg))
801 msc_ho_failed(msc_a, GSM0808_CAUSE_EQUIPMENT_FAILURE, "Failed to send Handover Succeeded message\n");
802}
803
804static void msc_ho_fsm_wait_complete(struct osmo_fsm_inst *fi, uint32_t event, void *data)
805{
806 struct msc_a *msc_a = fi->priv;
807
808 switch (event) {
809
810 case MSC_HO_EV_RX_DETECT:
811 msc_a->ho.ready_to_switch_rtp = true;
812 /* For inter-MSC, the mncc_fsm switches the rtp_stream upon MNCC_RTP_CONNECT.
813 * For inter-BSC, need to switch here to the address obtained from Handover Request Ack. */
814 if (msc_a->ho.new_cell.type == MSC_NEIGHBOR_TYPE_LOCAL_RAN_PEER)
815 msc_ho_rtp_switch_to_new_cell(msc_a);
816 msc_ho_send_handover_succeeded(msc_a);
817 return;
818
819 case MSC_HO_EV_RX_COMPLETE:
820 msc_ho_success(msc_a);
821 return;
822
823 case MSC_HO_EV_RX_FAILURE:
824 msc_ho_failed(msc_a, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
825 "Received Handover Failure message\n");
826 return;
827
828 case MSC_HO_EV_MNCC_FORWARDING_FAILED:
829 msc_ho_failed(msc_a, GSM0808_CAUSE_EQUIPMENT_FAILURE, "MNCC Forwarding failed\n");
830 return;
831
832 case MSC_HO_EV_MNCC_FORWARDING_COMPLETE:
833 return;
834
835 default:
836 OSMO_ASSERT(false);
837 }
838}
839
840static void msc_ho_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
841{
842 struct msc_a *msc_a = fi->priv;
843 struct msc_t *msc_t = msc_a_msc_t(msc_a);
844
845 /* paranoia */
846 if (msc_a->ho.fi != fi)
847 return;
848
849 /* Completely clear all handover state */
850 msc_a->ho = (struct msc_ho_state){};
851
852 if (msc_t)
853 msc_t_clear(msc_t);
854}
855
856static int msc_ho_fsm_timer_cb(struct osmo_fsm_inst *fi)
857{
858 return 1;
859}
860
861#define S(x) (1 << (x))
862
863static const struct osmo_fsm_state msc_ho_fsm_states[] = {
864 [MSC_HO_ST_REQUIRED] = {
865 .name = OSMO_STRINGIFY(MSC_HO_ST_REQUIRED),
866 .out_state_mask = 0
867 | S(MSC_HO_ST_REQUIRED)
868 | S(MSC_HO_ST_WAIT_REQUEST_ACK)
869 ,
870 .onenter = msc_ho_fsm_required_onenter,
871 },
872 [MSC_HO_ST_WAIT_REQUEST_ACK] = {
873 .name = OSMO_STRINGIFY(MSC_HO_ST_WAIT_REQUEST_ACK),
874 .in_event_mask = 0
875 | S(MSC_HO_EV_RX_REQUEST_ACK)
876 | S(MSC_HO_EV_RX_FAILURE)
877 ,
878 .out_state_mask = 0
879 | S(MSC_HO_ST_REQUIRED)
880 | S(MSC_HO_ST_WAIT_COMPLETE)
881 ,
882 .onenter = msc_ho_fsm_wait_request_ack_onenter,
883 .action = msc_ho_fsm_wait_request_ack,
884 },
885 [MSC_HO_ST_WAIT_COMPLETE] = {
886 .name = OSMO_STRINGIFY(MSC_HO_ST_WAIT_COMPLETE),
887 .in_event_mask = 0
888 | S(MSC_HO_EV_RX_DETECT)
889 | S(MSC_HO_EV_RX_COMPLETE)
890 | S(MSC_HO_EV_RX_FAILURE)
891 | S(MSC_HO_EV_MNCC_FORWARDING_COMPLETE)
892 | S(MSC_HO_EV_MNCC_FORWARDING_FAILED)
893 ,
894 .action = msc_ho_fsm_wait_complete,
895 },
896};
897
898static const struct value_string msc_ho_fsm_event_names[] = {
899 OSMO_VALUE_STRING(MSC_HO_EV_RX_REQUEST_ACK),
900 OSMO_VALUE_STRING(MSC_HO_EV_RX_DETECT),
901 OSMO_VALUE_STRING(MSC_HO_EV_RX_COMPLETE),
902 OSMO_VALUE_STRING(MSC_HO_EV_RX_FAILURE),
903 {}
904};
905
906struct osmo_fsm msc_ho_fsm = {
907 .name = "handover",
908 .states = msc_ho_fsm_states,
909 .num_states = ARRAY_SIZE(msc_ho_fsm_states),
910 .log_subsys = DHO,
911 .event_names = msc_ho_fsm_event_names,
912 .timer_cb = msc_ho_fsm_timer_cb,
913 .cleanup = msc_ho_fsm_cleanup,
914};