blob: 1cf0c3d2d44d927f4e80f132cbd13b1c07ed2295 [file] [log] [blame]
Neels Hofmeyrc4628a32018-12-07 14:47:34 +01001/* Handle an MNCC managed call (external MNCC). */
2/* At the time of writing, this is only used for inter-MSC handover: forward a voice stream to a remote MSC.
3 * Maybe it makes sense to also use it for all "normal" external call management at some point. */
4/*
5 * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
6 * All Rights Reserved
7 *
8 * SPDX-License-Identifier: AGPL-3.0+
9 *
10 * Author: Neels Hofmeyr
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Affero General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Affero General Public License for more details.
21 *
22 * You should have received a copy of the GNU Affero General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include <string.h>
27
28#include <osmocom/core/msgb.h>
29#include <osmocom/core/fsm.h>
30#include <osmocom/core/tdef.h>
31
32#include <osmocom/msc/mncc_call.h>
33#include <osmocom/msc/debug.h>
34#include <osmocom/msc/gsm_data.h>
35#include <osmocom/msc/rtp_stream.h>
36#include <osmocom/msc/msub.h>
37#include <osmocom/msc/vlr.h>
Neels Hofmeyra001a702022-10-31 17:57:30 +010038#include <osmocom/msc/codec_mapping.h>
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010039
40struct osmo_fsm mncc_call_fsm;
41static bool mncc_call_tx_rtp_create(struct mncc_call *mncc_call);
42
43LLIST_HEAD(mncc_call_list);
44
45static const struct osmo_tdef_state_timeout mncc_call_fsm_timeouts[32] = {
46 /* TODO */
47};
48
49struct gsm_network *gsmnet = NULL;
50
51/* Transition to a state, using the T timer defined in msc_a_fsm_timeouts.
52 * The actual timeout value is in turn obtained from network->T_defs.
53 * Assumes local variable fi exists. */
54#define mncc_call_fsm_state_chg(MNCC, STATE) \
55 osmo_tdef_fsm_inst_state_chg((MNCC)->fi, STATE, mncc_call_fsm_timeouts, gsmnet->mncc_tdefs, 5)
56
57#define mncc_call_error(MNCC, FMT, ARGS...) do { \
58 LOG_MNCC_CALL(MNCC, LOGL_ERROR, FMT, ##ARGS); \
59 osmo_fsm_inst_term((MNCC)->fi, OSMO_FSM_TERM_REGULAR, 0); \
60 } while(0)
61
62void mncc_call_fsm_init(struct gsm_network *net)
63{
Harald Welte34a8cc32019-12-01 15:32:09 +010064 OSMO_ASSERT(osmo_fsm_register(&mncc_call_fsm) == 0);
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010065 gsmnet = net;
66}
67
68void mncc_call_fsm_update_id(struct mncc_call *mncc_call)
69{
70 osmo_fsm_inst_update_id_f_sanitize(mncc_call->fi, '-', "%s:callref-0x%x%s%s",
71 vlr_subscr_name(mncc_call->vsub), mncc_call->callref,
72 mncc_call->remote_msisdn_present ? ":to-msisdn-" : "",
73 mncc_call->remote_msisdn_present ? mncc_call->remote_msisdn.number : "");
74}
75
76/* Invoked by the socket read callback in case the given MNCC call instance is responsible for the given callref. */
77void mncc_call_rx(struct mncc_call *mncc_call, const union mncc_msg *mncc_msg)
78{
79 if (!mncc_call)
80 return;
81 LOG_MNCC_CALL(mncc_call, LOGL_DEBUG, "Rx %s\n", get_mncc_name(mncc_msg->msg_type));
82 osmo_fsm_inst_dispatch(mncc_call->fi, MNCC_CALL_EV_RX_MNCC_MSG, (void*)mncc_msg);
83}
84
85/* Send an MNCC message (associated with this MNCC call). */
86int mncc_call_tx(struct mncc_call *mncc_call, union mncc_msg *mncc_msg)
87{
88 struct msgb *msg;
89 unsigned char *data;
90
91 LOG_MNCC_CALL(mncc_call, LOGL_DEBUG, "tx %s\n", get_mncc_name(mncc_msg->msg_type));
92
93 msg = msgb_alloc(sizeof(*mncc_msg), "MNCC-tx");
94 OSMO_ASSERT(msg);
95
96 data = msgb_put(msg, sizeof(*mncc_msg));
97 memcpy(data, mncc_msg, sizeof(*mncc_msg));
98
99 if (gsmnet->mncc_recv(gsmnet, msg)) {
100 mncc_call_error(mncc_call, "Failed to send MNCC message %s\n", get_mncc_name(mncc_msg->msg_type));
101 return -EIO;
102 }
103 return 0;
104}
105
106/* Send a trivial MNCC message with just a message type (associated with this MNCC call). */
107int mncc_call_tx_msgt(struct mncc_call *mncc_call, uint32_t msg_type)
108{
109 union mncc_msg mncc_msg = {
110 .signal = {
111 .msg_type = msg_type,
112 .callref = mncc_call->callref,
113 },
114 };
115 return mncc_call_tx(mncc_call, &mncc_msg);
116}
117
118/* Allocate an MNCC FSM as child of the given MSC role FSM.
119 * parent_event_call_released is mandatory and is passed as the parent_term_event.
120 * parent_event_call_setup_complete is dispatched when the MNCC FSM enters the MNCC_CALL_ST_TALKING state.
121 * parent_event_call_setup_complete is optional, pass a negative number to avoid dispatching.
122 *
123 * If non-NULL, message_cb is invoked whenever an MNCC message is received from the the MNCC socket, which is useful to
124 * forward things like DTMF to CC or to another MNCC call.
125 *
126 * After mncc_call_alloc(), call either mncc_call_outgoing_start() or mncc_call_incoming_start().
127 */
128struct mncc_call *mncc_call_alloc(struct vlr_subscr *vsub,
129 struct osmo_fsm_inst *parent,
130 int parent_event_call_setup_complete,
131 uint32_t parent_event_call_released,
132 mncc_call_message_cb_t message_cb, void *forward_cb_data)
133{
134 struct mncc_call *mncc_call;
135 struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc_child(&mncc_call_fsm, parent, parent_event_call_released);
136 OSMO_ASSERT(fi);
137 OSMO_ASSERT(vsub);
138
139 mncc_call = talloc(fi, struct mncc_call);
140 OSMO_ASSERT(mncc_call);
141 fi->priv = mncc_call;
142
143 *mncc_call = (struct mncc_call){
144 .fi = fi,
145 .vsub = vsub,
146 .parent_event_call_setup_complete = parent_event_call_setup_complete,
147 .message_cb = message_cb,
148 .forward_cb_data = forward_cb_data,
149 };
150
151 llist_add(&mncc_call->entry, &mncc_call_list);
152 mncc_call_fsm_update_id(mncc_call);
153
154 return mncc_call;
155}
156
157void mncc_call_reparent(struct mncc_call *mncc_call,
158 struct osmo_fsm_inst *new_parent,
159 int parent_event_call_setup_complete,
160 uint32_t parent_event_call_released,
161 mncc_call_message_cb_t message_cb, void *forward_cb_data)
162{
163 LOG_MNCC_CALL(mncc_call, LOGL_DEBUG, "Reparenting from parent %s to parent %s\n",
164 mncc_call->fi->proc.parent->name, new_parent->name);
165 osmo_fsm_inst_change_parent(mncc_call->fi, new_parent, parent_event_call_released);
166 talloc_steal(new_parent, mncc_call->fi);
167 mncc_call->parent_event_call_setup_complete = parent_event_call_setup_complete;
168 mncc_call->message_cb = message_cb;
169 mncc_call->forward_cb_data = forward_cb_data;
170}
171
172/* Associate an rtp_stream with this MNCC call instance (optional).
173 * Can be called directly after mncc_call_alloc(). If an rtp_stream is set, upon receiving the MNCC_RTP_CONNECT containing
174 * the PBX's RTP IP and port, pass the IP:port information to rtp_stream_set_remote_addr() and rtp_stream_commit() to
175 * update the MGW connection. If no rtp_stream is associated, the caller is responsible to manually extract the RTP
176 * IP:port from the MNCC_RTP_CONNECT message forwarded to mncc_call_message_cb_t (see mncc_call_alloc()).
177 * When an rtp_stream is set, call rtp_stream_release() when the MNCC call ends; call mncc_call_detach_rtp_stream() before
178 * the MNCC call releases if that is not desired.
179 */
180int mncc_call_set_rtp_stream(struct mncc_call *mncc_call, struct rtp_stream *rtps)
181{
182 if (mncc_call->rtps && mncc_call->rtps != rtps) {
183 LOG_MNCC_CALL(mncc_call, LOGL_ERROR,
184 "Cannot associate with RTP stream %s, already associated with %s\n",
185 rtps ? rtps->fi->name : "NULL", mncc_call->rtps->fi->name);
186 return -ENOSPC;
187 }
188
189 mncc_call->rtps = rtps;
190 LOG_MNCC_CALL(mncc_call, LOGL_DEBUG, "Associated with RTP stream %s\n", mncc_call->rtps->fi->name);
191 return 0;
192}
193
194/* Disassociate the rtp_stream from this MNCC call instance, and clear the remote RTP IP:port info.
195 * When the MNCC FSM ends for any reason, it will release the RTP stream (which usually triggers complete tear down of
Martin Hauke3f07dac2019-11-14 17:49:08 +0100196 * the call_leg and CC transaction). If the RTP stream should still remain in use, e.g. during Subsequent inter-MSC
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100197 * Handover where this MNCC was a forwarding to a remote MSC that is no longer needed, this function must be called
198 * before the MNCC FSM instance terminates. Call this *before* setting a new remote RTP address on the rtp_stream, since
199 * this clears the rtp_stream->remote ip:port information. */
200void mncc_call_detach_rtp_stream(struct mncc_call *mncc_call)
201{
202 struct rtp_stream *rtps = mncc_call->rtps;
Vadim Yanitskiyc5a8e9f2019-05-11 04:53:23 +0700203 struct osmo_sockaddr_str clear = { 0 };
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100204 if (!rtps)
205 return;
206 mncc_call->rtps = NULL;
207 rtp_stream_set_remote_addr(rtps, &clear);
208}
209
210static void mncc_call_tx_setup_ind(struct mncc_call *mncc_call)
211{
Philipp Maier8c472bd2020-09-18 18:01:32 +0200212 union mncc_msg mncc_msg;
213 mncc_msg.signal = mncc_call->outgoing_req;
214 mncc_msg.signal.msg_type = MNCC_SETUP_IND;
215 mncc_msg.signal.callref = mncc_call->callref;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100216
Philipp Maier8c472bd2020-09-18 18:01:32 +0200217 OSMO_STRLCPY_ARRAY(mncc_msg.signal.imsi, mncc_call->vsub->imsi);
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100218
219 if (!(mncc_call->outgoing_req.fields & MNCC_F_CALLING)) {
220 /* No explicit calling number set, use the local subscriber */
Philipp Maier8c472bd2020-09-18 18:01:32 +0200221 mncc_msg.signal.fields |= MNCC_F_CALLING;
222 OSMO_STRLCPY_ARRAY(mncc_msg.signal.calling.number, mncc_call->vsub->msisdn);
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100223
224 }
225 mncc_call->local_msisdn_present = true;
Philipp Maier8c472bd2020-09-18 18:01:32 +0200226 mncc_call->local_msisdn = mncc_msg.signal.calling;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100227
Pau Espin Pedrol2e21a682021-06-04 16:45:44 +0200228 rate_ctr_inc(rate_ctr_group_get_ctr(gsmnet->msc_ctrs, MSC_CTR_CALL_MO_SETUP));
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100229
Philipp Maier8c472bd2020-09-18 18:01:32 +0200230 mncc_call_tx(mncc_call, &mncc_msg);
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100231}
232
233static void mncc_call_rx_setup_req(struct mncc_call *mncc_call, const struct gsm_mncc *incoming_req)
234{
235 mncc_call->callref = incoming_req->callref;
236
237 if (incoming_req->fields & MNCC_F_CALLED) {
238 mncc_call->local_msisdn_present = true;
239 mncc_call->local_msisdn = incoming_req->called;
240 }
241
242 if (incoming_req->fields & MNCC_F_CALLING) {
243 mncc_call->remote_msisdn_present = true;
244 mncc_call->remote_msisdn = incoming_req->calling;
245 }
246
247 mncc_call_fsm_update_id(mncc_call);
248}
249
250/* Remote PBX asks for RTP_CREATE. This merely asks us to create an RTP stream, and does not actually contain any useful
251 * information like the remote RTP IP:port (these follow in the RTP_CONNECT from the SIP side) */
252static bool mncc_call_rx_rtp_create(struct mncc_call *mncc_call)
253{
254 mncc_call->received_rtp_create = true;
255
256 if (!mncc_call->rtps) {
257 LOG_MNCC_CALL(mncc_call, LOGL_DEBUG, "Got RTP_CREATE, but no RTP stream associated\n");
258 return true;
259 }
260
Neels Hofmeyr84ce2062019-10-05 05:15:25 +0200261 if (!osmo_sockaddr_str_is_nonzero(&mncc_call->rtps->local)) {
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100262 LOG_MNCC_CALL(mncc_call, LOGL_DEBUG, "Got RTP_CREATE, but RTP stream has no local address\n");
263 return true;
264 }
265
Neels Hofmeyr62bfa372022-10-31 18:51:07 +0100266 if (!mncc_call->rtps->codecs_known) {
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100267 LOG_MNCC_CALL(mncc_call, LOGL_DEBUG, "Got RTP_CREATE, but RTP stream has no codec set\n");
268 return true;
269 }
270
271 LOG_MNCC_CALL(mncc_call, LOGL_DEBUG, "Got RTP_CREATE, responding with " OSMO_SOCKADDR_STR_FMT " %s\n",
272 OSMO_SOCKADDR_STR_FMT_ARGS(&mncc_call->rtps->local),
Neels Hofmeyr62bfa372022-10-31 18:51:07 +0100273 sdp_audio_codecs_to_str(&mncc_call->rtps->codecs));
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100274 /* Already know what RTP IP:port to tell the MNCC. Send it. */
275 return mncc_call_tx_rtp_create(mncc_call);
276}
277
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100278static bool mncc_call_tx_rtp_create(struct mncc_call *mncc_call)
279{
Neels Hofmeyr84ce2062019-10-05 05:15:25 +0200280 if (!mncc_call->rtps || !osmo_sockaddr_str_is_nonzero(&mncc_call->rtps->local)) {
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100281 mncc_call_error(mncc_call, "Cannot send RTP_CREATE, no local RTP address set up\n");
282 return false;
283 }
284 struct osmo_sockaddr_str *rtp_local = &mncc_call->rtps->local;
285 union mncc_msg mncc_msg = {
286 .rtp = {
287 .msg_type = MNCC_RTP_CREATE,
288 .callref = mncc_call->callref,
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100289 },
290 };
291
Pau Espin Pedroleeda9e12020-09-03 22:11:03 +0200292 if (osmo_sockaddr_str_to_sockaddr(rtp_local, &mncc_msg.rtp.addr)) {
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100293 mncc_call_error(mncc_call, "Failed to compose IP address " OSMO_SOCKADDR_STR_FMT "\n",
294 OSMO_SOCKADDR_STR_FMT_ARGS(rtp_local));
295 return false;
296 }
297
Neels Hofmeyr62bfa372022-10-31 18:51:07 +0100298 if (mncc_call->rtps->codecs_known) {
299 struct sdp_audio_codec *codec = &mncc_call->rtps->codecs.codec[0];
300 const struct codec_mapping *m = codec_mapping_by_subtype_name(codec->subtype_name);
Neels Hofmeyra001a702022-10-31 17:57:30 +0100301
302 if (!m) {
303 mncc_call_error(mncc_call, "Failed to resolve audio codec '%s'\n",
Neels Hofmeyr62bfa372022-10-31 18:51:07 +0100304 sdp_audio_codec_to_str(codec));
Neels Hofmeyra001a702022-10-31 17:57:30 +0100305 return false;
306 }
Neels Hofmeyr62bfa372022-10-31 18:51:07 +0100307 mncc_msg.rtp.payload_type = codec->payload_type;
Neels Hofmeyra001a702022-10-31 17:57:30 +0100308 mncc_msg.rtp.payload_msg_type = m->mncc_payload_msg_type;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100309 }
310
311 if (mncc_call_tx(mncc_call, &mncc_msg))
312 return false;
313 return true;
314}
315
316static bool mncc_call_rx_rtp_connect(struct mncc_call *mncc_call, const struct gsm_mncc_rtp *mncc_msg)
317{
318 struct osmo_sockaddr_str rtp;
319
320 if (!mncc_call->rtps) {
321 /* The user has not associated an RTP stream, hence we're not supposed to take any action here. */
322 return true;
323 }
324
Pau Espin Pedroleeda9e12020-09-03 22:11:03 +0200325 if (osmo_sockaddr_str_from_sockaddr(&rtp, &mncc_msg->addr)) {
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100326 mncc_call_error(mncc_call, "Cannot RTP-CONNECT, invalid RTP IP:port in incoming MNCC message\n");
327 return false;
328 }
329
330 rtp_stream_set_remote_addr(mncc_call->rtps, &rtp);
331 if (rtp_stream_commit(mncc_call->rtps)) {
332 mncc_call_error(mncc_call, "RTP-CONNECT, failed, RTP stream is not properly set up: %s\n",
333 mncc_call->rtps->fi->id);
334 return false;
335 }
336 return true;
337}
338
339/* Return true if the FSM instance still exists after this call, false if it was terminated. */
340static bool mncc_call_rx_release_msg(struct mncc_call *mncc_call, const union mncc_msg *mncc_msg)
341{
342 switch (mncc_msg->msg_type) {
343 case MNCC_DISC_REQ:
344 /* Remote call leg ended the call, MNCC tells us to DISC. We ack with a REL. */
345 mncc_call_tx_msgt(mncc_call, MNCC_REL_IND);
346 osmo_fsm_inst_term(mncc_call->fi, OSMO_FSM_TERM_REGULAR, 0);
347 return false;
348
349 case MNCC_REL_REQ:
350 /* MNCC acks with a REL to a previous DISC IND we have (probably) sent.
351 * We ack with a REL CNF. */
352 mncc_call_tx_msgt(mncc_call, MNCC_REL_CNF);
353 osmo_fsm_inst_term(mncc_call->fi, OSMO_FSM_TERM_REGULAR, 0);
354 return false;
355
356 default:
357 return true;
358 }
359}
360
361/* Return true if the FSM instance still exists after this call, false if it was terminated. */
362static bool mncc_call_rx_common_msg(struct mncc_call *mncc_call, const union mncc_msg *mncc_msg)
363{
364 switch (mncc_msg->msg_type) {
365 case MNCC_RTP_CREATE:
366 mncc_call_rx_rtp_create(mncc_call);
367 return true;
368
369 case MNCC_RTP_CONNECT:
370 mncc_call_rx_rtp_connect(mncc_call, &mncc_msg->rtp);
371 return true;
372
373 default:
374 return mncc_call_rx_release_msg(mncc_call, mncc_msg);
375 }
376}
377
378static void mncc_call_forward(struct mncc_call *mncc_call, const union mncc_msg *mncc_msg)
379{
380 if (!mncc_call || !mncc_call->message_cb)
381 return;
382 mncc_call->message_cb(mncc_call, mncc_msg, mncc_call->forward_cb_data);
383}
384
385/* Initiate an outgoing call.
386 * The outgoing_req represents the details for the MNCC_SETUP_IND message sent to initiate the outgoing call. Pass at
387 * least a called number (set outgoing_req->fields |= MNCC_F_CALLED and populate outgoing_req->called). All other items
388 * are optional and can be included if required. The message type, callref and IMSI from this struct are ignored,
389 * instead they are determined internally upon sending the MNCC message. If no calling number is set in the message
390 * struct, it will be set from mncc_call->vsub->msisdn.
391 */
392int mncc_call_outgoing_start(struct mncc_call *mncc_call, const struct gsm_mncc *outgoing_req)
393{
394 if (!mncc_call)
395 return -EINVAL;
396 /* By dispatching an event instead of taking direct action, make sure that the FSM permits starting an outgoing
397 * call. */
398 return osmo_fsm_inst_dispatch(mncc_call->fi, MNCC_CALL_EV_OUTGOING_START, (void*)outgoing_req);
399}
400
401/* Handle an incoming call.
402 * When the MNCC recv callback (not included in this mncc_call_fsm API) detects an incoming call (MNCC_SETUP_REQ), take over
403 * handling of the incoming call by the given mncc_call instance.
404 * In incoming_req->setup_req_msg, pass the struct gsm_mncc message containing the received MNCC_SETUP_REQ.
405 * mncc_call_incoming_start() will immediately respond with a MNCC_CALL_CONF_IND; in incoming_req->bearer_cap, pass the
406 * bearer capabilities that should be included in this MNCC_CALL_CONF_IND message; in incoming_req->cccap, pass the
407 * CCCAP to be sent, if any.
408 */
409int mncc_call_incoming_start(struct mncc_call *mncc_call, const struct mncc_call_incoming_req *incoming_req)
410{
411 if (!mncc_call)
412 return -EINVAL;
413 /* By dispatching an event instead of taking direct action, make sure that the FSM permits starting an incoming
414 * call. */
415 return osmo_fsm_inst_dispatch(mncc_call->fi, MNCC_CALL_EV_INCOMING_START, (void*)incoming_req);
416}
417
418static void mncc_call_incoming_tx_call_conf_ind(struct mncc_call *mncc_call, const struct gsm_mncc_bearer_cap *bearer_cap)
419{
420 if (mncc_call->fi->state != MNCC_CALL_ST_INCOMING_WAIT_COMPLETE) {
421 LOG_MNCC_CALL(mncc_call, LOGL_ERROR, "%s not allowed in this state\n", __func__);
422 return;
423 }
424
425 union mncc_msg mncc_msg = {
426 .signal = {
427 .msg_type = MNCC_CALL_CONF_IND,
428 .callref = mncc_call->callref,
429 },
430 };
431
432 if (bearer_cap) {
433 mncc_msg.signal.fields |= MNCC_F_BEARER_CAP;
434 mncc_msg.signal.bearer_cap = *bearer_cap;
435 }
436
437 mncc_call_tx(mncc_call, &mncc_msg);
438}
439
440/* Send an MNCC_SETUP_CNF message. Typically after the local side is ready to receive the call and RTP (e.g. for a GSM
441 * CC call, the lchan and RTP should be ready and the CC call should have been confirmed and alerting).
442 * For inter-MSC call forwarding, this can happen immediately upon the MNCC_RTP_CREATE.
443 */
444int mncc_call_incoming_tx_setup_cnf(struct mncc_call *mncc_call, const struct gsm_mncc_number *connected_number)
445{
446 if (mncc_call->fi->state != MNCC_CALL_ST_INCOMING_WAIT_COMPLETE) {
447 LOG_MNCC_CALL(mncc_call, LOGL_ERROR, "%s not allowed in this state\n", __func__);
448 return -EINVAL;
449 }
450
451 union mncc_msg mncc_msg = {
452 .signal = {
453 .msg_type = MNCC_SETUP_CNF,
454 .callref = mncc_call->callref,
455 },
456 };
457
458 if (connected_number) {
459 mncc_msg.signal.fields |= MNCC_F_CONNECTED;
460 mncc_msg.signal.connected = *connected_number;
461 }
462
463 return mncc_call_tx(mncc_call, &mncc_msg);
464}
465
466static void mncc_call_fsm_not_started(struct osmo_fsm_inst *fi, uint32_t event, void *data)
467{
468 struct mncc_call *mncc_call = fi->priv;
469 const struct gsm_mncc *outgoing_req;
470 const struct mncc_call_incoming_req *incoming_req;
471
472 switch (event) {
473 case MNCC_CALL_EV_OUTGOING_START:
474 outgoing_req = data;
475 mncc_call->outgoing_req = *outgoing_req;
476 mncc_call->callref = msc_cc_next_outgoing_callref();
477 mncc_call_fsm_state_chg(mncc_call, MNCC_CALL_ST_OUTGOING_WAIT_PROCEEDING);
478 mncc_call_tx_setup_ind(mncc_call);
479 return;
480
481 case MNCC_CALL_EV_INCOMING_START:
482 incoming_req = data;
483 mncc_call_rx_setup_req(mncc_call, &incoming_req->setup_req_msg);
484 mncc_call_fsm_state_chg(mncc_call, MNCC_CALL_ST_INCOMING_WAIT_COMPLETE);
485 mncc_call_incoming_tx_call_conf_ind(mncc_call, incoming_req->bearer_cap_present ? &incoming_req->bearer_cap : NULL);
486 return;
487
488 default:
489 OSMO_ASSERT(false);
490 }
491}
492
493static void mncc_call_fsm_outgoing_wait_proceeding(struct osmo_fsm_inst *fi, uint32_t event, void *data)
494{
495 struct mncc_call *mncc_call = fi->priv;
496 const union mncc_msg *mncc_msg;
497
498 switch (event) {
499 case MNCC_CALL_EV_RX_MNCC_MSG:
500 mncc_msg = data;
501 if (!mncc_call_rx_common_msg(mncc_call, mncc_msg))
502 return;
503
504 switch (mncc_msg->msg_type) {
505 case MNCC_CALL_PROC_REQ:
506 mncc_call_fsm_state_chg(mncc_call, MNCC_CALL_ST_OUTGOING_WAIT_COMPLETE);
507 break;
508 default:
509 break;
510 }
511
512 mncc_call_forward(mncc_call, mncc_msg);
513 return;
514
515 default:
516 OSMO_ASSERT(false);
517 };
518}
519
520static void mncc_call_fsm_outgoing_wait_complete(struct osmo_fsm_inst *fi, uint32_t event, void *data)
521{
522 struct mncc_call *mncc_call = fi->priv;
523 const union mncc_msg *mncc_msg;
524
525 switch (event) {
526 case MNCC_CALL_EV_RX_MNCC_MSG:
527 mncc_msg = data;
528 if (!mncc_call_rx_common_msg(mncc_call, mncc_msg))
529 return;
530
531 switch (mncc_msg->msg_type) {
532 case MNCC_SETUP_RSP:
533 mncc_call_fsm_state_chg(mncc_call, MNCC_CALL_ST_TALKING);
534 mncc_call_tx_msgt(mncc_call, MNCC_SETUP_COMPL_IND);
535 break;
536 default:
537 break;
538 }
539
540 mncc_call_forward(mncc_call, mncc_msg);
541 return;
542
543 default:
544 OSMO_ASSERT(false);
545 };
546}
547
548static void mncc_call_fsm_incoming_wait_complete(struct osmo_fsm_inst *fi, uint32_t event, void *data)
549{
550 struct mncc_call *mncc_call = fi->priv;
551 const union mncc_msg *mncc_msg;
552
553 switch (event) {
554 case MNCC_CALL_EV_RX_MNCC_MSG:
555 mncc_msg = data;
556 if (!mncc_call_rx_common_msg(mncc_call, mncc_msg))
557 return;
558
559 switch (mncc_msg->msg_type) {
560 case MNCC_SETUP_COMPL_REQ:
561 mncc_call_fsm_state_chg(mncc_call, MNCC_CALL_ST_TALKING);
562 break;
563 default:
564 break;
565 }
566
567 mncc_call_forward(mncc_call, mncc_msg);
568 return;
569
570 default:
571 OSMO_ASSERT(false);
572 };
573}
574
575static void mncc_call_fsm_talking(struct osmo_fsm_inst *fi, uint32_t event, void *data)
576{
577 struct mncc_call *mncc_call = fi->priv;
578 const union mncc_msg *mncc_msg;
579
580 switch (event) {
581 case MNCC_CALL_EV_RX_MNCC_MSG:
582 mncc_msg = data;
583 if (!mncc_call_rx_common_msg(mncc_call, mncc_msg))
584 return;
585 mncc_call_forward(mncc_call, mncc_msg);
586 return;
587
588 default:
589 OSMO_ASSERT(false);
590 };
591}
592
593static void mncc_call_fsm_wait_release_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
594{
595 struct mncc_call *mncc_call = fi->priv;
596 const union mncc_msg *mncc_msg;
597
598 switch (event) {
599 case MNCC_CALL_EV_RX_MNCC_MSG:
600 mncc_msg = data;
601 if (!mncc_call_rx_release_msg(mncc_call, mncc_msg))
602 return;
603 mncc_call_forward(mncc_call, mncc_msg);
604 return;
605
606 default:
607 OSMO_ASSERT(false);
608 };
609}
610
611static void mncc_call_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
612{
613 struct mncc_call *mncc_call = fi->priv;
614
615 switch (fi->state) {
616 case MNCC_CALL_ST_NOT_STARTED:
617 case MNCC_CALL_ST_WAIT_RELEASE_ACK:
618 break;
619 default:
620 /* Make sure we did indicate some sort of release */
621 mncc_call_tx_msgt(mncc_call, MNCC_REL_IND);
622 break;
623 }
624
625 /* Releasing the RTP stream should trigger completely tearing down the call leg as well as the CC transaction.
626 * In case of an inter-MSC handover where this MNCC connection is replaced by another MNCC / another BSC
627 * connection, the caller needs to detach the RTP stream from this FSM before terminating it. */
628 if (mncc_call->rtps) {
629 rtp_stream_release(mncc_call->rtps);
630 mncc_call->rtps = NULL;
631 }
632
633 llist_del(&mncc_call->entry);
634}
635
636static int mncc_call_fsm_timer_cb(struct osmo_fsm_inst *fi)
637{
638 return 1;
639}
640
641#define S(x) (1 << (x))
642
643static const struct osmo_fsm_state mncc_call_fsm_states[] = {
644 [MNCC_CALL_ST_NOT_STARTED] = {
645 .name = "NOT_STARTED",
646 .in_event_mask = 0
647 | S(MNCC_CALL_EV_OUTGOING_START)
648 | S(MNCC_CALL_EV_INCOMING_START)
649 ,
650 .out_state_mask = 0
651 | S(MNCC_CALL_ST_OUTGOING_WAIT_PROCEEDING)
652 | S(MNCC_CALL_ST_INCOMING_WAIT_COMPLETE)
653 ,
654 .action = mncc_call_fsm_not_started,
655 },
656 [MNCC_CALL_ST_OUTGOING_WAIT_PROCEEDING] = {
657 .name = "OUTGOING_WAIT_PROCEEDING",
658 .in_event_mask = 0
659 | S(MNCC_CALL_EV_RX_MNCC_MSG)
660 ,
661 .out_state_mask = 0
662 | S(MNCC_CALL_ST_OUTGOING_WAIT_COMPLETE)
663 | S(MNCC_CALL_ST_WAIT_RELEASE_ACK)
664 ,
665 .action = mncc_call_fsm_outgoing_wait_proceeding,
666 },
667 [MNCC_CALL_ST_OUTGOING_WAIT_COMPLETE] = {
668 .name = "OUTGOING_WAIT_COMPLETE",
669 .in_event_mask = 0
670 | S(MNCC_CALL_EV_RX_MNCC_MSG)
671 ,
672 .out_state_mask = 0
673 | S(MNCC_CALL_ST_TALKING)
674 | S(MNCC_CALL_ST_WAIT_RELEASE_ACK)
675 ,
676 .action = mncc_call_fsm_outgoing_wait_complete,
677 },
678 [MNCC_CALL_ST_INCOMING_WAIT_COMPLETE] = {
679 .name = "INCOMING_WAIT_COMPLETE",
680 .in_event_mask = 0
681 | S(MNCC_CALL_EV_RX_MNCC_MSG)
682 ,
683 .out_state_mask = 0
684 | S(MNCC_CALL_ST_TALKING)
685 | S(MNCC_CALL_ST_WAIT_RELEASE_ACK)
686 ,
687 .action = mncc_call_fsm_incoming_wait_complete,
688 },
689 [MNCC_CALL_ST_TALKING] = {
690 .name = "TALKING",
691 .in_event_mask = 0
692 | S(MNCC_CALL_EV_RX_MNCC_MSG)
693 ,
694 .out_state_mask = 0
695 | S(MNCC_CALL_ST_WAIT_RELEASE_ACK)
696 ,
697 .action = mncc_call_fsm_talking,
698 },
699 [MNCC_CALL_ST_WAIT_RELEASE_ACK] = {
700 .name = "WAIT_RELEASE_ACK",
701 .in_event_mask = 0
702 | S(MNCC_CALL_EV_RX_MNCC_MSG)
703 ,
704 .action = mncc_call_fsm_wait_release_ack,
705 },
706};
707
708static const struct value_string mncc_call_fsm_event_names[] = {
709 OSMO_VALUE_STRING(MNCC_CALL_EV_RX_MNCC_MSG),
710
711 OSMO_VALUE_STRING(MNCC_CALL_EV_OUTGOING_START),
712 OSMO_VALUE_STRING(MNCC_CALL_EV_OUTGOING_ALERTING),
713 OSMO_VALUE_STRING(MNCC_CALL_EV_OUTGOING_SETUP_COMPLETE),
714
715 OSMO_VALUE_STRING(MNCC_CALL_EV_INCOMING_START),
716 OSMO_VALUE_STRING(MNCC_CALL_EV_INCOMING_SETUP),
717 OSMO_VALUE_STRING(MNCC_CALL_EV_INCOMING_SETUP_COMPLETE),
718
719 OSMO_VALUE_STRING(MNCC_CALL_EV_CN_RELEASE),
720 OSMO_VALUE_STRING(MNCC_CALL_EV_MS_RELEASE),
721 {}
722};
723
724struct osmo_fsm mncc_call_fsm = {
725 .name = "mncc_call",
726 .states = mncc_call_fsm_states,
727 .num_states = ARRAY_SIZE(mncc_call_fsm_states),
728 .log_subsys = DMNCC,
729 .event_names = mncc_call_fsm_event_names,
730 .timer_cb = mncc_call_fsm_timer_cb,
731 .cleanup = mncc_call_fsm_cleanup,
732};
733
734struct mncc_call *mncc_call_find_by_callref(uint32_t callref)
735{
736 struct mncc_call *mncc_call;
737 llist_for_each_entry(mncc_call, &mncc_call_list, entry) {
738 if (mncc_call->callref == callref)
739 return mncc_call;
740 }
741 return NULL;
742}
743
744void mncc_call_release(struct mncc_call *mncc_call)
745{
746 if (!mncc_call)
747 return;
748 mncc_call_tx_msgt(mncc_call, MNCC_DISC_IND);
749 mncc_call_fsm_state_chg(mncc_call, MNCC_CALL_ST_WAIT_RELEASE_ACK);
750}