Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 1 | /* 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 | |
| 39 | struct osmo_fsm mncc_call_fsm; |
| 40 | static bool mncc_call_tx_rtp_create(struct mncc_call *mncc_call); |
| 41 | |
| 42 | LLIST_HEAD(mncc_call_list); |
| 43 | |
| 44 | static const struct osmo_tdef_state_timeout mncc_call_fsm_timeouts[32] = { |
| 45 | /* TODO */ |
| 46 | }; |
| 47 | |
| 48 | struct 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 | |
| 61 | void mncc_call_fsm_init(struct gsm_network *net) |
| 62 | { |
| 63 | osmo_fsm_register(&mncc_call_fsm); |
| 64 | gsmnet = net; |
| 65 | } |
| 66 | |
| 67 | void 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. */ |
| 76 | void 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). */ |
| 85 | int 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). */ |
| 106 | int 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 | */ |
| 127 | struct 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 | |
| 156 | void 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 | */ |
| 179 | int 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. */ |
| 199 | void mncc_call_detach_rtp_stream(struct mncc_call *mncc_call) |
| 200 | { |
| 201 | struct rtp_stream *rtps = mncc_call->rtps; |
Vadim Yanitskiy | c5a8e9f | 2019-05-11 04:53:23 +0700 | [diff] [blame] | 202 | struct osmo_sockaddr_str clear = { 0 }; |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 203 | if (!rtps) |
| 204 | return; |
| 205 | mncc_call->rtps = NULL; |
| 206 | rtp_stream_set_remote_addr(rtps, &clear); |
| 207 | } |
| 208 | |
| 209 | static 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 | |
| 231 | static 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) */ |
| 250 | static 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. */ |
| 277 | uint32_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 | |
| 295 | static 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 | |
| 326 | static 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. */ |
| 350 | static 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. */ |
| 372 | static 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 | |
| 388 | static 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 | */ |
| 402 | int 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 | */ |
| 419 | int 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 | |
| 428 | static 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 | */ |
| 454 | int 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 | |
| 476 | static 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 | |
| 503 | static 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 | |
| 530 | static 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 | |
| 558 | static 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 | |
| 585 | static 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 | |
| 603 | static 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 | |
| 621 | static 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 | |
| 646 | static int mncc_call_fsm_timer_cb(struct osmo_fsm_inst *fi) |
| 647 | { |
| 648 | return 1; |
| 649 | } |
| 650 | |
| 651 | #define S(x) (1 << (x)) |
| 652 | |
| 653 | static 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 | |
| 718 | static 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 | |
| 734 | struct 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 | |
| 744 | struct 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 | |
| 754 | void 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 | } |