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