add full SDP codec information to the MNCC socket
This way osmo-msc can benefit from the complete codec information received via SIP, which was so far terminated at osmo-sip-connector. osmo-sip-connector could/should have translated the received SDP to MNCC bearer_cap, but this was never implemented properly. Since osmo-msc already handles SDP towards the MGW, it makes most sense to pass SDP to osmo-msc transparently. To be able to send a valid RTP IP:port in the SDP upon the first MNCC_SETUP_IND going out, move the CN side CRCX to the very start of establishing a voice call. As a result, first create MGW conns for both RAN and CN before starting. The voice_call_full.msc chart shows the change in message sequence for MO and MT voice calls. Implement cc_sdp.c, which accumulates codec information from various sources (MS, BSS, Assignment, remote call leg) and provides filtering to get the available set of codecs at any point in time. Implement codec_sdp_cc_t9n.c, to translate between SDP and the various libosmo-mgcp-client, CC and BSSMAP representations of codecs: - Speech Version, - Permitted Speech, - Speech Codec Type, - default Payload Type numbers, - enum mgcp_codecs, - FR/HR compatibility - SDP audio codec names, - various AMR configurations. A codec_map lists these relations in one large data record. Various functions provide conversions by traversing this map. Add trans->cc.mnccc_release_sent: so far, avoiding to send an MNCC release during trans_free() was done by setting the callref = 0. But that also skips CC Release. On codec mismatch, we send a specific MNCC error code but still want a normal CC Release: hence send the MNCC message, set mnccc_release_sent = true and do normal CC Release in trans_free(). (A better way to do this would be to adopt the mncc_call FSM from inter-MSC handover also for local voice calls, but that is out of scope for now. I want to try that soon, as time permits.) Change-Id: I8c3b2de53ffae4ec3a66b9dabf308c290a2c999f
This commit is contained in:
parent
02dd265d68
commit
f31a1ccd9a
|
@ -11,22 +11,22 @@ moms =>> momsc [label="RR CIPH_M_COMPL"];
|
|||
moms =>> momsc [label="CC SETUP"];
|
||||
momsc note momsc [label="CC starts guard timer (180s)"];
|
||||
momsc abox momsc [label="CC state:\nINITIATED"];
|
||||
momgw <<= momsc [label="for CN: CRCX\nrtpbridge/*@msc"];
|
||||
momgw =>> momsc [label="for CN: CRCX OK\nEP-1 CI-1"];
|
||||
momgw <<= momsc [label="for RAN: CRCX\nEP-1"];
|
||||
momsc =>> sip [label="MNCC_SETUP_IND"];
|
||||
momsc <<= sip [label="MNCC_RTP_CREATE"];
|
||||
momgw <<= momsc [label="for RAN: CRCX\nrtpbridge/*@msc"];
|
||||
momgw =>> momsc [label="for RAN: CRCX OK\nEP-1 CI-1"];
|
||||
momgw =>> momsc [label="for RAN: CRCX OK\nEP-1 CI-2"];
|
||||
moms <<= momsc [label="(BSSMAP) ASSIGNMENT_COMMAND"];
|
||||
momsc <<= sip [label="MNCC_RTP_CREATE"];
|
||||
moms =>> momsc [label="(BSSMAP) Assignment Complete"];
|
||||
momgw <<= momsc [label="for RAN: MDCX\nEP-1 CI-1"];
|
||||
momgw =>> momsc [label="for RAN: MDCX OK\nEP-1 CI-1"];
|
||||
momgw <<= momsc [label="for CN: CRCX\nEP-1"];
|
||||
momgw =>> momsc [label="for CN: CRCX OK\nEP-1 CI-2"];
|
||||
momgw <<= momsc [label="for RAN: MDCX\nEP-1 CI-2"];
|
||||
momsc =>> sip [label="MNCC_RTP_CREATE\nIP:port-1"];
|
||||
momsc <<= sip [label="MNCC_CALL_PROC_REQ"];
|
||||
momsc note momsc [label="CC stops guard timer"];
|
||||
momsc note momsc [label="CC starts guard timer (180s)"];
|
||||
momsc abox momsc [label="CC state:\nMO_CALL_PROC"];
|
||||
moms <<= momsc [label="CC CALL_PROC"];
|
||||
momgw =>> momsc [label="for RAN: MDCX OK\nEP-1 CI-2"];
|
||||
mtmsc <<= sip [label="MNCC_SETUP_REQ"];
|
||||
mtms <<= mtmsc [label="Paging"];
|
||||
mtms =>> mtmsc [label="RR PAG_RESP"];
|
||||
|
@ -36,24 +36,25 @@ mtms =>> mtmsc [label="MM AUTH_RESP"];
|
|||
mtms <<= mtmsc [label="(RANAP) SecurityModeCommand"];
|
||||
mtms =>> mtmsc [label="(RANAP) SecurityModeControl successfulOutcome"];
|
||||
mtms <<= mtmsc [label="(RANAP) CommonId"];
|
||||
mtmsc note mtmsc [label="CC starts timer T303 (30s)"];
|
||||
mtmsc abox mtmsc [label="CC state:\nCALL_PRESENT"];
|
||||
mtmsc note mtmsc [label="CC starts timer T303 (30s)"];
|
||||
mtms <<= mtmsc [label="CC SETUP"];
|
||||
mtms =>> mtmsc [label="CC CALL_CONF"];
|
||||
mtmsc note mtmsc [label="CC stops timer T303"];
|
||||
mtmsc note mtmsc [label="CC starts timer T310 (30s)"];
|
||||
mtmgw <<= mtmsc [label="for CN: CRCX\nrtpbridge/*@msc"];
|
||||
mtmsc abox mtmsc [label="CC state:\nMO_TERM_CALL_CONF"];
|
||||
mtmgw <<= mtmsc [label="for RAN: CRCX\nrtpbridge/*@msc"];
|
||||
mtmsc =>> sip [label="MNCC_CALL_CONF_IND"];
|
||||
mtmsc <<= sip [label="MNCC_RTP_CREATE"];
|
||||
mtmgw =>> mtmsc [label="for RAN: CRCX OK\nEP-2 CI-3"];
|
||||
mtmgw =>> mtmsc [label="for CN: CRCX OK\nEP-2 CI-3"];
|
||||
mtmgw <<= mtmsc [label="for RAN: CRCX\nEP-2"];
|
||||
mtmsc =>> sip [label="MNCC_RTP_CREATE\nIP:port-2"];
|
||||
mtmgw =>> mtmsc [label="for RAN: CRCX OK\nEP-2 CI-4"];
|
||||
mtms <<= mtmsc [label="(RANAP) RAB AssignmentRequest"];
|
||||
mtms =>> mtmsc [label="(RANAP) RAB Assignment Response"];
|
||||
mtmgw <<= mtmsc [label="for RAN: MDCX\nEP-2 CI-3"];
|
||||
mtmgw =>> mtmsc [label="for RAN: MDCX OK\nEP-2 CI-3"];
|
||||
mtmgw <<= mtmsc [label="for CN: CRCX\nEP-2"];
|
||||
mtmgw =>> mtmsc [label="for CN: CRCX OK\nEP-2 CI-4"];
|
||||
mtmgw <<= mtmsc [label="for RAN: MDCX\nEP-2 CI-4"];
|
||||
mtmsc =>> sip [label="MNCC_RTP_CREATE\nIP:port-2"];
|
||||
mtmgw =>> mtmsc [label="for RAN: MDCX OK\nEP-2 CI-4"];
|
||||
mtms =>> mtmsc [label="CC ALERTING"];
|
||||
mtmsc note mtmsc [label="CC stops timer T310"];
|
||||
mtmsc note mtmsc [label="CC starts timer T301 (180s)"];
|
||||
|
@ -68,55 +69,55 @@ mtms =>> mtmsc [label="CC CONNECT"];
|
|||
mtmsc note mtmsc [label="CC stops timer T301"];
|
||||
mtmsc abox mtmsc [label="CC state:\nCONNECT_REQUEST"];
|
||||
mtmsc =>> sip [label="MNCC_SETUP_CNF"];
|
||||
mtmsc <<= sip [label="MNCC_RTP_CONNECT\nIP:port-1"];
|
||||
mtmgw <<= mtmsc [label="for CN: MDCX\nEP-2 CI-4"];
|
||||
mtmsc <<= sip [label="MNCC_RTP_CONNECT"];
|
||||
mtmgw <<= mtmsc [label="for CN: MDCX\nEP-2 CI-3"];
|
||||
mtmsc <<= sip [label="MNCC_SETUP_COMPL_REQ"];
|
||||
mtmsc note mtmsc [label="CC starts guard timer (180s)"];
|
||||
mtmsc abox mtmsc [label="CC state:\nACTIVE"];
|
||||
mtmsc note mtmsc [label="CC stops guard timer"];
|
||||
mtms <<= mtmsc [label="CC CONNECT_ACK"];
|
||||
mtmgw =>> mtmsc [label="for CN: MDCX OK\nEP-2 CI-4"];
|
||||
momsc <<= sip [label="MNCC_RTP_CONNECT\nIP:port-2"];
|
||||
momgw <<= momsc [label="for CN: MDCX\nEP-1 CI-2"];
|
||||
mtmgw =>> mtmsc [label="for CN: MDCX OK\nEP-2 CI-3"];
|
||||
momsc <<= sip [label="MNCC_RTP_CONNECT"];
|
||||
momgw <<= momsc [label="for CN: MDCX\nEP-1 CI-1"];
|
||||
momsc <<= sip [label="MNCC_SETUP_RSP"];
|
||||
momsc note momsc [label="CC stops guard timer"];
|
||||
momsc note momsc [label="CC starts guard timer (180s)"];
|
||||
momsc note momsc [label="CC starts timer T313 (30s)"];
|
||||
momsc abox momsc [label="CC state:\nCONNECT_IND"];
|
||||
moms <<= momsc [label="CC CONNECT"];
|
||||
momgw =>> momsc [label="for CN: MDCX OK\nEP-1 CI-2"];
|
||||
momgw =>> momsc [label="for CN: MDCX OK\nEP-1 CI-1"];
|
||||
moms =>> momsc [label="CC CONNECT_ACK"];
|
||||
momsc note momsc [label="CC stops timer T313"];
|
||||
momsc abox momsc [label="CC state:\nACTIVE"];
|
||||
momsc note momsc [label="CC stops guard timer"];
|
||||
momsc =>> sip [label="MNCC_SETUP_COMPL_IND"];
|
||||
moms =>> momsc [label="CC DISCONNECT"];
|
||||
momsc abox momsc [label="CC state:\nDISCONNECT_IND"];
|
||||
momsc =>> sip [label="MNCC_DISC_IND"];
|
||||
momsc <<= sip [label="MNCC_REL_REQ"];
|
||||
momsc note momsc [label="CC starts guard timer (180s)"];
|
||||
momsc note momsc [label="CC starts timer T308 (10s)"];
|
||||
momsc abox momsc [label="CC state:\nRELEASE_REQ"];
|
||||
moms <<= momsc [label="CC RELEASE"];
|
||||
mtmsc <<= sip [label="MNCC_DISC_REQ"];
|
||||
mtmsc note mtmsc [label="CC starts guard timer (180s)"];
|
||||
mtmsc note mtmsc [label="CC starts timer T306 (30s)"];
|
||||
mtms =>> mtmsc [label="CC DISCONNECT"];
|
||||
mtmsc abox mtmsc [label="CC state:\nDISCONNECT_IND"];
|
||||
mtms <<= mtmsc [label="CC DISCONNECT"];
|
||||
moms =>> momsc [label="CC RELEASE_COMPL"];
|
||||
momsc note momsc [label="CC stops timer T308"];
|
||||
mtmsc =>> sip [label="MNCC_DISC_IND"];
|
||||
mtmsc <<= sip [label="MNCC_REL_REQ"];
|
||||
mtmsc note mtmsc [label="CC starts guard timer (180s)"];
|
||||
mtmsc note mtmsc [label="CC starts timer T308 (10s)"];
|
||||
mtmsc abox mtmsc [label="CC state:\nRELEASE_REQ"];
|
||||
mtms <<= mtmsc [label="CC RELEASE"];
|
||||
momsc <<= sip [label="MNCC_DISC_REQ"];
|
||||
momsc note momsc [label="CC starts guard timer (180s)"];
|
||||
momsc note momsc [label="CC starts timer T306 (30s)"];
|
||||
momsc abox momsc [label="CC state:\nDISCONNECT_IND"];
|
||||
moms <<= momsc [label="CC DISCONNECT"];
|
||||
moms =>> momsc [label="CC RELEASE"];
|
||||
momsc note momsc [label="CC stops timer T306"];
|
||||
moms <<= momsc [label="CC RELEASE_COMPL"];
|
||||
momsc abox momsc [label="CC state:\nNULL"];
|
||||
momsc note momsc [label="CC stops guard timer"];
|
||||
moms <<= momsc [label="(BSSMAP) CLEAR_COMMAND"];
|
||||
momsc =>> sip [label="MNCC_REL_CNF"];
|
||||
momsc =>> sip [label="MNCC_REL_IND"];
|
||||
moms =>> momsc [label="(BSSMAP) Clear Complete"];
|
||||
mtms =>> mtmsc [label="CC RELEASE"];
|
||||
mtmsc note mtmsc [label="CC stops timer T306"];
|
||||
mtms <<= mtmsc [label="CC RELEASE_COMPL"];
|
||||
mtms =>> mtmsc [label="CC RELEASE_COMPL"];
|
||||
mtmsc note mtmsc [label="CC stops timer T308"];
|
||||
mtmsc abox mtmsc [label="CC state:\nNULL"];
|
||||
mtmsc note mtmsc [label="CC stops guard timer"];
|
||||
mtms <<= mtmsc [label="(RANAP) Iu Release"];
|
||||
mtmsc =>> sip [label="MNCC_REL_IND"];
|
||||
mtmsc =>> sip [label="MNCC_REL_CNF"];
|
||||
mtms =>> mtmsc [label="(RANAP) Iu Release successfulOutcome"];
|
||||
moms =>> momsc [label="IMSI Detach"];
|
||||
mtms =>> mtmsc [label="IMSI Detach"];
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
noinst_HEADERS = \
|
||||
call_leg.h \
|
||||
cc_sdp.h \
|
||||
cell_id_list.h \
|
||||
codec_sdp_cc_t9n.h \
|
||||
db.h \
|
||||
debug.h \
|
||||
e_link.h \
|
||||
|
|
|
@ -12,6 +12,7 @@ struct gsm_network;
|
|||
struct gsm_trans;
|
||||
struct rtp_stream;
|
||||
enum rtp_direction;
|
||||
struct sdp_audio_codecs;
|
||||
|
||||
extern struct osmo_tdef g_mgw_tdefs[];
|
||||
|
||||
|
@ -74,7 +75,8 @@ int call_leg_local_bridge(struct call_leg *cl1, uint32_t call_id1, struct gsm_tr
|
|||
int call_leg_ensure_rtp_alloc(struct call_leg *cl, enum rtp_direction dir, uint32_t call_id,
|
||||
struct gsm_trans *for_trans);
|
||||
int call_leg_ensure_ci(struct call_leg *cl, enum rtp_direction dir, uint32_t call_id, struct gsm_trans *for_trans,
|
||||
const enum mgcp_codecs *codec_if_known, const struct osmo_sockaddr_str *remote_addr_if_known);
|
||||
const struct sdp_audio_codecs *codecs_if_known,
|
||||
const struct osmo_sockaddr_str *remote_addr_if_known);
|
||||
struct osmo_sockaddr_str *call_leg_local_ip(struct call_leg *cl, enum rtp_direction dir);
|
||||
|
||||
void call_leg_rtp_stream_gone(struct call_leg *cl, struct rtp_stream *rtps);
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/gsm/mncc.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
|
||||
#include <osmocom/msc/sdp_msg.h>
|
||||
|
||||
struct gsm0808_speech_codec_list;
|
||||
|
||||
struct cc_sdp {
|
||||
/* The fixed set of codecs available on the RAN type, per definition. */
|
||||
struct sdp_audio_codecs ran;
|
||||
/* The codecs advertised by the MS Bearer Capabilities */
|
||||
struct sdp_audio_codecs ms;
|
||||
/* If known, the set of codecs the current RAN cell allows / has available.
|
||||
* This may not be available if the BSC does not issue this information early enough.
|
||||
* Should be ignored if empty. */
|
||||
struct sdp_audio_codecs cell;
|
||||
|
||||
/* SDP as last received from the remote call leg. */
|
||||
struct sdp_msg remote;
|
||||
|
||||
/* After a channel was assigned, this reflects the chosen codec. */
|
||||
struct sdp_audio_codec assignment;
|
||||
|
||||
/* Resulting choice of supported codecs, usually the intersection of the above,
|
||||
* and the local RTP address to be sent to the remote call leg. */
|
||||
struct sdp_msg result;
|
||||
};
|
||||
|
||||
void cc_sdp_init(struct cc_sdp *cc_sdp,
|
||||
enum osmo_rat_type ran_type,
|
||||
const struct gsm_mncc_bearer_cap *ms_bearer_cap,
|
||||
const struct gsm0808_speech_codec_list *codec_list_bss_supported);
|
||||
void cc_sdp_set_cell(struct cc_sdp *cc_sdp,
|
||||
const struct gsm0808_speech_codec_list *codec_list_bss_supported);
|
||||
int cc_sdp_filter(struct cc_sdp *cc_sdp);
|
||||
|
||||
int cc_sdp_name_buf(char *buf, size_t buflen, const struct cc_sdp *cc_sdp);
|
||||
char *cc_sdp_name_c(void *ctx, const struct cc_sdp *cc_sdp);
|
||||
const char *cc_sdp_name(const struct cc_sdp *cc_sdp);
|
|
@ -0,0 +1,58 @@
|
|||
/* Routines for translation ("t9n") between SDP codec names and CC/BSSMAP codec constants */
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
#include <osmocom/gsm/protocol/gsm_08_08.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/msc/sdp_msg.h>
|
||||
#include <osmocom/gsm/mncc.h>
|
||||
|
||||
#define NO_MGCP_CODEC 0xffffffff
|
||||
|
||||
extern const struct gsm_mncc_bearer_cap bearer_cap_empty;
|
||||
|
||||
enum codec_frhr {
|
||||
CODEC_FRHR_NONE = 0,
|
||||
CODEC_FRHR_FR,
|
||||
CODEC_FRHR_HR,
|
||||
};
|
||||
|
||||
struct codec_mapping {
|
||||
struct sdp_audio_codec sdp;
|
||||
enum mgcp_codecs mgcp;
|
||||
unsigned int speech_ver_count;
|
||||
enum gsm48_bcap_speech_ver speech_ver[8];
|
||||
uint32_t mncc_payload_msg_type;
|
||||
/* gsm0808_speech_codec_type corresponds to gsm0808_speech_codec[_list]->type */
|
||||
bool has_gsm0808_speech_codec_type;
|
||||
enum gsm0808_speech_codec_type gsm0808_speech_codec_type;
|
||||
enum gsm0808_permitted_speech perm_speech;
|
||||
enum codec_frhr frhr;
|
||||
};
|
||||
|
||||
extern const struct codec_mapping codec_map[];
|
||||
#define foreach_codec_mapping(CODEC_MAPPING) \
|
||||
for ((CODEC_MAPPING) = codec_map; (CODEC_MAPPING) < codec_map + ARRAY_SIZE(codec_map); (CODEC_MAPPING)++)
|
||||
|
||||
const struct codec_mapping *codec_mapping_by_speech_ver(enum gsm48_bcap_speech_ver speech_ver);
|
||||
const struct codec_mapping *codec_mapping_by_gsm0808_speech_codec_type(enum gsm0808_speech_codec_type sct,
|
||||
uint16_t cfg);
|
||||
const struct codec_mapping *codec_mapping_by_perm_speech(enum gsm0808_permitted_speech perm_speech);
|
||||
const struct codec_mapping *codec_mapping_by_subtype_name(const char *subtype_name);
|
||||
const struct codec_mapping *codec_mapping_by_mgcp_codec(enum mgcp_codecs mgcp);
|
||||
|
||||
int bearer_cap_add_speech_ver(struct gsm_mncc_bearer_cap *bearer_cap, enum gsm48_bcap_speech_ver speech_ver);
|
||||
int sdp_audio_codec_add_to_bearer_cap(struct gsm_mncc_bearer_cap *bearer_cap, const struct sdp_audio_codec *codec);
|
||||
int sdp_audio_codecs_to_bearer_cap(struct gsm_mncc_bearer_cap *bearer_cap, const struct sdp_audio_codecs *ac);
|
||||
int bearer_cap_set_radio(struct gsm_mncc_bearer_cap *bearer_cap);
|
||||
|
||||
struct sdp_audio_codec *sdp_audio_codecs_add_speech_ver(struct sdp_audio_codecs *ac,
|
||||
enum gsm48_bcap_speech_ver speech_ver);
|
||||
struct sdp_audio_codec *sdp_audio_codecs_add_mgcp_codec(struct sdp_audio_codecs *ac, enum mgcp_codecs mgcp_codec);
|
||||
void sdp_audio_codecs_from_bearer_cap(struct sdp_audio_codecs *ac, const struct gsm_mncc_bearer_cap *bc);
|
||||
|
||||
void sdp_audio_codecs_from_speech_codec_list(struct sdp_audio_codecs *ac, const struct gsm0808_speech_codec_list *cl);
|
||||
|
||||
int sdp_audio_codecs_to_gsm0808_channel_type(struct gsm0808_channel_type *ct, const struct sdp_audio_codecs *ac);
|
||||
|
||||
enum mgcp_codecs sdp_audio_codec_to_mgcp_codec(const struct sdp_audio_codec *codec);
|
|
@ -45,6 +45,11 @@ int gsm48_send_rr_app_info(struct msc_a *msc_a, uint8_t apdu_id, uint8_t apdu_le
|
|||
int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_class);
|
||||
int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, uint8_t power_command, uint8_t ho_ref);
|
||||
|
||||
void gsm48_cc_rx_setup_cn_local_rtp_port_known(struct gsm_trans *trans);
|
||||
void gsm48_cc_rx_call_conf_cn_local_rtp_port_known(struct gsm_trans *trans);
|
||||
int cc_cn_local_rtp_port_known(struct gsm_trans *trans);
|
||||
int cc_assignment_done(struct gsm_trans *trans);
|
||||
|
||||
int mncc_tx_to_cc(struct gsm_network *net, void *arg);
|
||||
|
||||
/* convert a ASCII phone number to call-control BCD */
|
||||
|
|
|
@ -121,6 +121,9 @@ struct msc_a {
|
|||
* \-------RTP--> (ISUP) <--RTP--> <--RTP-->
|
||||
*/
|
||||
struct {
|
||||
/* Codec List (BSS Supported) as received during Complete Layer 3 Information */
|
||||
struct gsm0808_speech_codec_list codec_list_bss_supported;
|
||||
|
||||
/* All of the RTP stream handling */
|
||||
struct call_leg *call_leg;
|
||||
struct mncc_call *mncc_forwarding_to_remote_ran;
|
||||
|
@ -203,6 +206,7 @@ void msc_a_up_ciph_res(struct msc_a *msc_a, bool success, const char *imeisv);
|
|||
bool msc_a_is_accepted(const struct msc_a *msc_a);
|
||||
bool msc_a_is_establishing_auth_ciph(const struct msc_a *msc_a);
|
||||
|
||||
int msc_a_ensure_cn_local_rtp(struct msc_a *msc_a, struct gsm_trans *cc_trans);
|
||||
int msc_a_try_call_assignment(struct gsm_trans *cc_trans);
|
||||
|
||||
const char *msc_a_cm_service_type_to_use(enum osmo_cm_service_type cm_service_type);
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#include <osmocom/msc/neighbor_ident.h>
|
||||
#include <osmocom/msc/ran_msg.h>
|
||||
#include <osmocom/msc/mncc_call.h>
|
||||
|
||||
#include <osmocom/msc/sdp_msg.h>
|
||||
|
||||
struct gsm0808_handover_required;
|
||||
|
||||
|
@ -92,7 +92,7 @@ struct msc_ho_state {
|
|||
struct {
|
||||
/* Saved RTP IP:port and codec in case we need to roll back */
|
||||
struct osmo_sockaddr_str ran_remote_rtp;
|
||||
enum mgcp_codecs codec;
|
||||
struct sdp_audio_codecs codecs;
|
||||
} old_cell;
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/msc/sdp_msg.h>
|
||||
|
||||
struct gsm_trans;
|
||||
|
||||
|
@ -37,9 +38,9 @@ struct rtp_stream {
|
|||
struct osmo_sockaddr_str remote;
|
||||
bool remote_sent_to_mgw;
|
||||
|
||||
bool codec_known;
|
||||
enum mgcp_codecs codec;
|
||||
bool codec_sent_to_mgw;
|
||||
bool codecs_known;
|
||||
struct sdp_audio_codecs codecs;
|
||||
bool codecs_sent_to_mgw;
|
||||
|
||||
struct osmo_mgcpc_ep_ci *ci;
|
||||
|
||||
|
@ -64,8 +65,11 @@ struct rtp_stream *rtp_stream_alloc(struct call_leg *parent_call_leg, enum rtp_d
|
|||
int rtp_stream_ensure_ci(struct rtp_stream *rtps, struct osmo_mgcpc_ep *at_endpoint);
|
||||
int rtp_stream_do_mdcx(struct rtp_stream *rtps);
|
||||
|
||||
void rtp_stream_set_codec(struct rtp_stream *rtps, enum mgcp_codecs codec);
|
||||
void rtp_stream_set_codecs(struct rtp_stream *rtps, const struct sdp_audio_codecs *codecs);
|
||||
void rtp_stream_set_one_codec(struct rtp_stream *rtps, const struct sdp_audio_codec *codec);
|
||||
bool rtp_stream_set_codecs_from_mgcp_codec(struct rtp_stream *rtps, enum mgcp_codecs codec);
|
||||
void rtp_stream_set_remote_addr(struct rtp_stream *rtps, const struct osmo_sockaddr_str *r);
|
||||
void rtp_stream_set_remote_addr_and_codecs(struct rtp_stream *rtps, const struct sdp_msg *sdp);
|
||||
void rtp_stream_set_remote_osmux_cid(struct rtp_stream *rtps, uint8_t osmux_cid);
|
||||
int rtp_stream_commit(struct rtp_stream *rtps);
|
||||
|
||||
|
|
|
@ -36,7 +36,10 @@ struct sdp_msg {
|
|||
|
||||
const char *sdp_msg_line_end(const char *src);
|
||||
|
||||
int sdp_audio_codec_cmp(const struct sdp_audio_codec *a, const struct sdp_audio_codec *b);
|
||||
int sdp_audio_codec_cmp(const struct sdp_audio_codec *a, const struct sdp_audio_codec *b,
|
||||
bool cmp_fmtp, bool cmp_payload_type);
|
||||
int sdp_audio_codecs_cmp(const struct sdp_audio_codecs *a, const struct sdp_audio_codecs *b,
|
||||
bool cmp_fmtp, bool cmp_payload_type);
|
||||
|
||||
struct sdp_audio_codec *sdp_audio_codec_add(struct sdp_audio_codecs *ac, unsigned int payload_type,
|
||||
const char *subtype_name, unsigned int rate, const char *fmtp);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <osmocom/msc/debug.h>
|
||||
#include <osmocom/gsm/gsm0411_smc.h>
|
||||
#include <osmocom/gsm/gsm0411_smr.h>
|
||||
#include <osmocom/msc/cc_sdp.h>
|
||||
|
||||
struct vty;
|
||||
|
||||
|
@ -87,6 +88,10 @@ struct gsm_trans {
|
|||
/* bearer capabilities (rate and codec) */
|
||||
struct gsm_mncc_bearer_cap bearer_cap;
|
||||
|
||||
/* if true, TCH_RTP_CREATE is sent after the
|
||||
* assignment is done */
|
||||
bool tch_rtp_create;
|
||||
|
||||
union {
|
||||
struct {
|
||||
|
||||
|
@ -100,6 +105,8 @@ struct gsm_trans {
|
|||
struct osmo_timer_list timer_guard;
|
||||
struct gsm_mncc msg; /* stores setup/disconnect/release message */
|
||||
bool mncc_initiated; /* Whether an MNCC Release is necessary on failure */
|
||||
bool mncc_release_sent; /* Mark when special error handling already did MNCC rel */
|
||||
struct cc_sdp sdp; /* Track SDP codec info from BSS and remote call leg */
|
||||
} cc;
|
||||
struct {
|
||||
struct gsm411_smc_inst smc_inst;
|
||||
|
|
|
@ -29,7 +29,9 @@ noinst_LIBRARIES = \
|
|||
|
||||
libmsc_a_SOURCES = \
|
||||
call_leg.c \
|
||||
cc_sdp.c \
|
||||
cell_id_list.c \
|
||||
codec_sdp_cc_t9n.c \
|
||||
sccp_ran.c \
|
||||
msc_vty.c \
|
||||
db.c \
|
||||
|
|
|
@ -316,7 +316,8 @@ struct osmo_sockaddr_str *call_leg_local_ip(struct call_leg *cl, enum rtp_direct
|
|||
* MDCX.
|
||||
*/
|
||||
int call_leg_ensure_ci(struct call_leg *cl, enum rtp_direction dir, uint32_t call_id, struct gsm_trans *for_trans,
|
||||
const enum mgcp_codecs *codec_if_known, const struct osmo_sockaddr_str *remote_addr_if_known)
|
||||
const struct sdp_audio_codecs *codecs_if_known,
|
||||
const struct osmo_sockaddr_str *remote_addr_if_known)
|
||||
{
|
||||
if (call_leg_ensure_rtp_alloc(cl, dir, call_id, for_trans))
|
||||
return -EIO;
|
||||
|
@ -325,8 +326,8 @@ int call_leg_ensure_ci(struct call_leg *cl, enum rtp_direction dir, uint32_t cal
|
|||
cl->rtp[dir]->use_osmux = true;
|
||||
cl->rtp[dir]->remote_osmux_cid = -1; /* wildcard */
|
||||
}
|
||||
if (codec_if_known)
|
||||
rtp_stream_set_codec(cl->rtp[dir], *codec_if_known);
|
||||
if (codecs_if_known)
|
||||
rtp_stream_set_codecs(cl->rtp[dir], codecs_if_known);
|
||||
if (remote_addr_if_known && osmo_sockaddr_str_is_nonzero(remote_addr_if_known))
|
||||
rtp_stream_set_remote_addr(cl->rtp[dir], remote_addr_if_known);
|
||||
return rtp_stream_ensure_ci(cl->rtp[dir], cl->mgw_endpoint);
|
||||
|
@ -335,22 +336,22 @@ int call_leg_ensure_ci(struct call_leg *cl, enum rtp_direction dir, uint32_t cal
|
|||
int call_leg_local_bridge(struct call_leg *cl1, uint32_t call_id1, struct gsm_trans *trans1,
|
||||
struct call_leg *cl2, uint32_t call_id2, struct gsm_trans *trans2)
|
||||
{
|
||||
enum mgcp_codecs codec;
|
||||
struct sdp_audio_codecs *codecs;
|
||||
|
||||
cl1->local_bridge = cl2;
|
||||
cl2->local_bridge = cl1;
|
||||
|
||||
/* We may just copy the codec info we have for the RAN side of the first leg to the CN side of both legs. This
|
||||
* also means that if both legs use different codecs the MGW must perform transcoding on the second leg. */
|
||||
if (!cl1->rtp[RTP_TO_RAN] || !cl1->rtp[RTP_TO_RAN]->codec_known) {
|
||||
if (!cl1->rtp[RTP_TO_RAN] || !cl1->rtp[RTP_TO_RAN]->codecs_known) {
|
||||
LOG_CALL_LEG(cl1, LOGL_ERROR, "RAN-side RTP stream codec is not known, not ready for bridging\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
codec = cl1->rtp[RTP_TO_RAN]->codec;
|
||||
codecs = &cl1->rtp[RTP_TO_RAN]->codecs;
|
||||
|
||||
call_leg_ensure_ci(cl1, RTP_TO_CN, call_id1, trans1,
|
||||
&codec, &cl2->rtp[RTP_TO_CN]->local);
|
||||
codecs, &cl2->rtp[RTP_TO_CN]->local);
|
||||
call_leg_ensure_ci(cl2, RTP_TO_CN, call_id2, trans2,
|
||||
&codec, &cl1->rtp[RTP_TO_CN]->local);
|
||||
codecs, &cl1->rtp[RTP_TO_CN]->local);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
#include <osmocom/gsm/protocol/gsm_08_08.h>
|
||||
|
||||
#include <osmocom/msc/cc_sdp.h>
|
||||
#include <osmocom/msc/codec_sdp_cc_t9n.h>
|
||||
#include <osmocom/msc/debug.h>
|
||||
|
||||
/* Add all known payload types encountered in GSM networks */
|
||||
static void sdp_add_all_mobile_codecs(struct sdp_audio_codecs *ac)
|
||||
{
|
||||
/* In order of preference. TODO: make configurable */
|
||||
static const enum gsm48_bcap_speech_ver mobile_codecs[] = {
|
||||
GSM48_BCAP_SV_AMR_F /*!< 4 GSM FR V3 (FR AMR) */,
|
||||
GSM48_BCAP_SV_AMR_H /*!< 5 GSM HR V3 (HR_AMR) */,
|
||||
GSM48_BCAP_SV_EFR /*!< 2 GSM FR V2 (GSM EFR) */,
|
||||
GSM48_BCAP_SV_FR /*!< 0 GSM FR V1 (GSM FR) */,
|
||||
GSM48_BCAP_SV_HR /*!< 1 GSM HR V1 (GSM HR) */,
|
||||
};
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(mobile_codecs); i++)
|
||||
sdp_audio_codecs_add_speech_ver(ac, mobile_codecs[i]);
|
||||
}
|
||||
|
||||
/* Add all known AMR payload types encountered in UTRAN networks */
|
||||
static void sdp_add_all_utran_codecs(struct sdp_audio_codecs *ac)
|
||||
{
|
||||
/* In order of preference. TODO: make configurable */
|
||||
static const enum gsm48_bcap_speech_ver utran_codecs[] = {
|
||||
GSM48_BCAP_SV_AMR_F /*!< 4 GSM FR V3 (FR AMR) */,
|
||||
GSM48_BCAP_SV_AMR_H /*!< 5 GSM HR V3 (HR_AMR) */,
|
||||
GSM48_BCAP_SV_AMR_OH /*!< 11 GSM HR V6 (OHR AMR) */,
|
||||
GSM48_BCAP_SV_AMR_FW /*!< 8 GSM FR V5 (FR AMR-WB) */,
|
||||
GSM48_BCAP_SV_AMR_OFW /*!< 6 GSM FR V4 (OFR AMR-WB) */,
|
||||
GSM48_BCAP_SV_AMR_OHW /*!< 7 GSM HR V4 (OHR AMR-WB) */,
|
||||
};
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(utran_codecs); i++)
|
||||
sdp_audio_codecs_add_speech_ver(ac, utran_codecs[i]);
|
||||
}
|
||||
|
||||
static void cc_sdp_set_ran(struct cc_sdp *cc_sdp, enum osmo_rat_type ran_type)
|
||||
{
|
||||
cc_sdp->ran = (struct sdp_audio_codecs){};
|
||||
|
||||
switch (ran_type) {
|
||||
default:
|
||||
case OSMO_RAT_GERAN_A:
|
||||
sdp_add_all_mobile_codecs(&cc_sdp->ran);
|
||||
break;
|
||||
|
||||
case OSMO_RAT_UTRAN_IU:
|
||||
sdp_add_all_utran_codecs(&cc_sdp->ran);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void cc_sdp_init(struct cc_sdp *cc_sdp,
|
||||
enum osmo_rat_type ran_type,
|
||||
const struct gsm_mncc_bearer_cap *ms_bearer_cap,
|
||||
const struct gsm0808_speech_codec_list *codec_list_bss_supported)
|
||||
{
|
||||
*cc_sdp = (struct cc_sdp){};
|
||||
cc_sdp_set_ran(cc_sdp, ran_type);
|
||||
|
||||
if (ms_bearer_cap)
|
||||
sdp_audio_codecs_from_bearer_cap(&cc_sdp->ms, ms_bearer_cap);
|
||||
|
||||
if (codec_list_bss_supported)
|
||||
cc_sdp_set_cell(cc_sdp, codec_list_bss_supported);
|
||||
}
|
||||
|
||||
void cc_sdp_set_cell(struct cc_sdp *cc_sdp,
|
||||
const struct gsm0808_speech_codec_list *codec_list_bss_supported)
|
||||
{
|
||||
cc_sdp->cell = (struct sdp_audio_codecs){};
|
||||
if (codec_list_bss_supported)
|
||||
sdp_audio_codecs_from_speech_codec_list(&cc_sdp->cell, codec_list_bss_supported);
|
||||
}
|
||||
|
||||
/* Render intersections of all known audio codec constraints to reach a resulting choice of favorite audio codec, plus
|
||||
* possible set of alternative audio codecs, in cc_sdp->result. (The result.rtp address remains unchanged.) */
|
||||
int cc_sdp_filter(struct cc_sdp *cc_sdp)
|
||||
{
|
||||
struct sdp_audio_codecs *r = &cc_sdp->result.audio_codecs;
|
||||
struct sdp_audio_codec *a = &cc_sdp->assignment;
|
||||
*r = cc_sdp->ran;
|
||||
if (cc_sdp->ms.count)
|
||||
sdp_audio_codecs_intersection(r, &cc_sdp->ms, false);
|
||||
if (cc_sdp->cell.count)
|
||||
sdp_audio_codecs_intersection(r, &cc_sdp->cell, false);
|
||||
if (cc_sdp->remote.audio_codecs.count)
|
||||
sdp_audio_codecs_intersection(r, &cc_sdp->remote.audio_codecs, true);
|
||||
|
||||
#if ALLOW_REASSIGNMENT
|
||||
/* If osmo-msc were able to trigger a re-assignment after the remote side has picked a codec mismatching the
|
||||
* initial Assignment, then this code here would make sense: keep the other codecs as available to choose from,
|
||||
* but put the currently assigned codec in the first position. */
|
||||
if (a->subtype_name[0]) {
|
||||
/* Assignment has completed, the chosen codec should be the first of the resulting SDP.
|
||||
* Make sure this is actually listed in the result SDP and move to first place. */
|
||||
struct sdp_audio_codec *select = sdp_audio_codec_by_descr(r, a);
|
||||
|
||||
if (!select) {
|
||||
/* Not present. Add. */
|
||||
if (sdp_audio_codec_by_payload_type(r, a->payload_type, false)) {
|
||||
/* Oh crunch, that payload type number is already in use.
|
||||
* Find an unused one. */
|
||||
for (a->payload_type = 96; a->payload_type <= 127; a->payload_type++) {
|
||||
if (!sdp_audio_codec_by_payload_type(r, a->payload_type, false))
|
||||
break;
|
||||
}
|
||||
|
||||
if (a->payload_type > 127)
|
||||
return -ENOSPC;
|
||||
}
|
||||
select = sdp_audio_codec_add_copy(r, a);
|
||||
}
|
||||
|
||||
sdp_audio_codecs_select(r, select);
|
||||
}
|
||||
#else
|
||||
/* Currently, osmo-msc does not trigger re-assignment if the remote side has picked a codec that the local side
|
||||
* would also support, but the local side has already assigned a mismatching codec before. Mismatching codecs
|
||||
* means call failure. So, currently, if locally, Assignment has already happened, it makes sense to send only
|
||||
* the assigned codec as available choice to the remote side. */
|
||||
if (a->subtype_name[0]) {
|
||||
/* Assignment has completed, the chosen codec should be the the only possible one. */
|
||||
struct sdp_audio_codecs assigned_codec = {};
|
||||
sdp_audio_codec_add_copy(&assigned_codec, a);
|
||||
sdp_audio_codecs_intersection(r, &assigned_codec, false);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cc_sdp_name_buf(char *buf, size_t buflen, const struct cc_sdp *cc_sdp)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_PRINTF(sb, "RAN={");
|
||||
OSMO_STRBUF_APPEND(sb, sdp_audio_codecs_name_buf, &cc_sdp->ran);
|
||||
OSMO_STRBUF_PRINTF(sb, "}");
|
||||
|
||||
if (cc_sdp->cell.count) {
|
||||
OSMO_STRBUF_PRINTF(sb, " cell={");
|
||||
OSMO_STRBUF_APPEND(sb, sdp_audio_codecs_name_buf, &cc_sdp->cell);
|
||||
OSMO_STRBUF_PRINTF(sb, "}");
|
||||
}
|
||||
|
||||
if (cc_sdp->ms.count) {
|
||||
OSMO_STRBUF_PRINTF(sb, " MS={");
|
||||
OSMO_STRBUF_APPEND(sb, sdp_audio_codecs_name_buf, &cc_sdp->ms);
|
||||
OSMO_STRBUF_PRINTF(sb, "}");
|
||||
}
|
||||
|
||||
if (cc_sdp->remote.audio_codecs.count
|
||||
|| osmo_sockaddr_str_is_nonzero(&cc_sdp->remote.rtp)) {
|
||||
OSMO_STRBUF_PRINTF(sb, " remote=");
|
||||
OSMO_STRBUF_APPEND(sb, sdp_msg_name_buf, &cc_sdp->remote);
|
||||
}
|
||||
|
||||
if (cc_sdp->assignment.subtype_name[0]) {
|
||||
OSMO_STRBUF_PRINTF(sb, " assigned=");
|
||||
OSMO_STRBUF_APPEND(sb, sdp_audio_codec_name_buf, &cc_sdp->assignment);
|
||||
}
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, " result=");
|
||||
OSMO_STRBUF_APPEND(sb, sdp_msg_name_buf, &cc_sdp->result);
|
||||
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *cc_sdp_name_c(void *ctx, const struct cc_sdp *cc_sdp)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 128, "cc_sdp_name_c-ERROR", cc_sdp_name_buf, cc_sdp)
|
||||
}
|
||||
|
||||
const char *cc_sdp_name(const struct cc_sdp *cc_sdp)
|
||||
{
|
||||
return cc_sdp_name_c(OTC_SELECT, cc_sdp);
|
||||
}
|
|
@ -0,0 +1,424 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <osmocom/gsm/mncc.h>
|
||||
|
||||
#include <osmocom/msc/sdp_msg.h>
|
||||
#include <osmocom/msc/codec_sdp_cc_t9n.h>
|
||||
#include <osmocom/msc/mncc.h>
|
||||
|
||||
const struct codec_mapping codec_map[] = {
|
||||
/* FIXME: I'm not sure about OFR, OHR -- O means octet-aligned?? */
|
||||
{
|
||||
.sdp = {
|
||||
.payload_type = 0,
|
||||
.subtype_name = "PCMU",
|
||||
.rate = 8000,
|
||||
},
|
||||
.mgcp = CODEC_PCMU_8000_1,
|
||||
},
|
||||
{
|
||||
.sdp = {
|
||||
.payload_type = 3,
|
||||
.subtype_name = "GSM",
|
||||
.rate = 8000,
|
||||
},
|
||||
.mgcp = CODEC_GSM_8000_1,
|
||||
.speech_ver_count = 1,
|
||||
.speech_ver = { GSM48_BCAP_SV_FR },
|
||||
.mncc_payload_msg_type = GSM_TCHF_FRAME,
|
||||
.has_gsm0808_speech_codec_type = true,
|
||||
.gsm0808_speech_codec_type = GSM0808_SCT_FR1,
|
||||
.perm_speech = GSM0808_PERM_FR1,
|
||||
.frhr = CODEC_FRHR_FR,
|
||||
},
|
||||
{
|
||||
.sdp = {
|
||||
.payload_type = 8,
|
||||
.subtype_name = "PCMA",
|
||||
.rate = 8000,
|
||||
},
|
||||
.mgcp = CODEC_PCMA_8000_1,
|
||||
},
|
||||
{
|
||||
.sdp = {
|
||||
.payload_type = 18,
|
||||
.subtype_name = "G729",
|
||||
.rate = 8000,
|
||||
},
|
||||
.mgcp = CODEC_G729_8000_1,
|
||||
},
|
||||
{
|
||||
.sdp = {
|
||||
.payload_type = 110,
|
||||
.subtype_name = "GSM-EFR",
|
||||
.rate = 8000,
|
||||
},
|
||||
.mgcp = CODEC_GSMEFR_8000_1,
|
||||
.speech_ver_count = 1,
|
||||
.speech_ver = { GSM48_BCAP_SV_EFR },
|
||||
.mncc_payload_msg_type = GSM_TCHF_FRAME_EFR,
|
||||
.has_gsm0808_speech_codec_type = true,
|
||||
.gsm0808_speech_codec_type = GSM0808_SCT_FR2,
|
||||
.perm_speech = GSM0808_PERM_FR2,
|
||||
.frhr = CODEC_FRHR_FR,
|
||||
},
|
||||
{
|
||||
.sdp = {
|
||||
.payload_type = 111,
|
||||
.subtype_name = "GSM-HR-08",
|
||||
.rate = 8000,
|
||||
},
|
||||
.mgcp = CODEC_GSMHR_8000_1,
|
||||
.speech_ver_count = 1,
|
||||
.speech_ver = { GSM48_BCAP_SV_HR },
|
||||
.mncc_payload_msg_type = GSM_TCHH_FRAME,
|
||||
.has_gsm0808_speech_codec_type = true,
|
||||
.gsm0808_speech_codec_type = GSM0808_SCT_HR1,
|
||||
.perm_speech = GSM0808_PERM_HR1,
|
||||
.frhr = CODEC_FRHR_HR,
|
||||
},
|
||||
{
|
||||
.sdp = {
|
||||
.payload_type = 112,
|
||||
.subtype_name = "AMR",
|
||||
.rate = 8000,
|
||||
/* It is important to send this fmtp parameter to a SIP peer in SDP,
|
||||
* otherwise the voice audio is broken noise.
|
||||
* However, a SIP peer may offer AMR without this parameter set in its SDP, so fmtp must be
|
||||
* ignored during codec matching: otherwise an incoming AMR codec without this parameter fails
|
||||
* to match this entry, and it ends in an aborted call due to no codec match.
|
||||
* If the peer offers plain "AMR/8000" and we reply with "AMR/8000 fmtp:octet-align=1",
|
||||
* then everything works out happily, */
|
||||
.fmtp = "octet-align=1",
|
||||
},
|
||||
.mgcp = CODEC_AMR_8000_1,
|
||||
.speech_ver_count = 1,
|
||||
.speech_ver = { GSM48_BCAP_SV_AMR_F },
|
||||
.mncc_payload_msg_type = GSM_TCH_FRAME_AMR,
|
||||
.has_gsm0808_speech_codec_type = true,
|
||||
.gsm0808_speech_codec_type = GSM0808_SCT_FR3,
|
||||
.perm_speech = GSM0808_PERM_FR3,
|
||||
.frhr = CODEC_FRHR_FR,
|
||||
},
|
||||
{
|
||||
.sdp = {
|
||||
.payload_type = 112,
|
||||
.subtype_name = "AMR",
|
||||
.rate = 8000,
|
||||
.fmtp = "octet-align=1;mode-set=0,1,2,3",
|
||||
},
|
||||
.mgcp = CODEC_AMR_8000_1,
|
||||
.speech_ver_count = 2,
|
||||
.speech_ver = { GSM48_BCAP_SV_AMR_H, GSM48_BCAP_SV_AMR_OH },
|
||||
.mncc_payload_msg_type = GSM_TCH_FRAME_AMR,
|
||||
.has_gsm0808_speech_codec_type = true,
|
||||
.gsm0808_speech_codec_type = GSM0808_SCT_HR3,
|
||||
.perm_speech = GSM0808_PERM_HR3,
|
||||
.frhr = CODEC_FRHR_HR,
|
||||
},
|
||||
{
|
||||
.sdp = {
|
||||
.payload_type = 113,
|
||||
.subtype_name = "AMR-WB",
|
||||
.rate = 16000,
|
||||
.fmtp = "octet-align=1",
|
||||
},
|
||||
.mgcp = CODEC_AMRWB_16000_1,
|
||||
.speech_ver_count = 2,
|
||||
.speech_ver = { GSM48_BCAP_SV_AMR_OFW, GSM48_BCAP_SV_AMR_FW },
|
||||
.mncc_payload_msg_type = GSM_TCH_FRAME_AMR,
|
||||
.has_gsm0808_speech_codec_type = true,
|
||||
.gsm0808_speech_codec_type = GSM0808_SCT_FR5,
|
||||
.perm_speech = GSM0808_PERM_FR5,
|
||||
.frhr = CODEC_FRHR_FR,
|
||||
},
|
||||
{
|
||||
.sdp = {
|
||||
.payload_type = 113,
|
||||
.subtype_name = "AMR-WB",
|
||||
.rate = 16000,
|
||||
.fmtp = "octet-align=1;mode-set=0,1,2,3", /* TODO: does this make sense?? */
|
||||
},
|
||||
.mgcp = CODEC_AMRWB_16000_1,
|
||||
.speech_ver_count = 1,
|
||||
.speech_ver = { GSM48_BCAP_SV_AMR_OHW },
|
||||
.mncc_payload_msg_type = GSM_TCH_FRAME_AMR,
|
||||
.has_gsm0808_speech_codec_type = true,
|
||||
.gsm0808_speech_codec_type = GSM0808_SCT_HR4,
|
||||
.perm_speech = GSM0808_PERM_HR4,
|
||||
.frhr = CODEC_FRHR_HR,
|
||||
},
|
||||
};
|
||||
|
||||
const struct gsm_mncc_bearer_cap bearer_cap_empty = {
|
||||
.speech_ver = { -1 },
|
||||
};
|
||||
|
||||
const struct codec_mapping *codec_mapping_by_speech_ver(enum gsm48_bcap_speech_ver speech_ver)
|
||||
{
|
||||
const struct codec_mapping *m;
|
||||
foreach_codec_mapping(m) {
|
||||
int i;
|
||||
for (i = 0; i < m->speech_ver_count; i++)
|
||||
if (m->speech_ver[i] == speech_ver)
|
||||
return m;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const struct codec_mapping *codec_mapping_by_gsm0808_speech_codec_type(enum gsm0808_speech_codec_type sct, uint16_t cfg)
|
||||
{
|
||||
const struct codec_mapping *m;
|
||||
foreach_codec_mapping(m) {
|
||||
if (!m->has_gsm0808_speech_codec_type)
|
||||
continue;
|
||||
if (m->gsm0808_speech_codec_type == sct)
|
||||
return m;
|
||||
/* TODO: evaluate cfg bits? */
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct codec_mapping *codec_mapping_by_perm_speech(enum gsm0808_permitted_speech perm_speech)
|
||||
{
|
||||
const struct codec_mapping *m;
|
||||
foreach_codec_mapping(m) {
|
||||
if (m->perm_speech == perm_speech)
|
||||
return m;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct codec_mapping *codec_mapping_by_subtype_name(const char *subtype_name)
|
||||
{
|
||||
const struct codec_mapping *m;
|
||||
foreach_codec_mapping(m) {
|
||||
if (!strcmp(m->sdp.subtype_name, subtype_name))
|
||||
return m;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct codec_mapping *codec_mapping_by_mgcp_codec(enum mgcp_codecs mgcp)
|
||||
{
|
||||
const struct codec_mapping *m;
|
||||
foreach_codec_mapping(m) {
|
||||
if (m->mgcp == mgcp)
|
||||
return m;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Append given Speech Version to the end of the Bearer Capabilities Speech Version array. Return 1 if added, zero
|
||||
* otherwise (as in, return the number of items added). */
|
||||
int bearer_cap_add_speech_ver(struct gsm_mncc_bearer_cap *bearer_cap, enum gsm48_bcap_speech_ver speech_ver)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(bearer_cap->speech_ver) - 1; i++) {
|
||||
if (bearer_cap->speech_ver[i] == speech_ver)
|
||||
return 0;
|
||||
if (bearer_cap->speech_ver[i] == -1) {
|
||||
bearer_cap->speech_ver[i] = speech_ver;
|
||||
bearer_cap->speech_ver[i+1] = -1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* From the current speech_ver list present in the bearer_cap, set the bearer_cap.radio.
|
||||
* If a HR speech_ver is present, set to GSM48_BCAP_RRQ_DUAL_FR, otherwise set to GSM48_BCAP_RRQ_FR_ONLY. */
|
||||
int bearer_cap_set_radio(struct gsm_mncc_bearer_cap *bearer_cap)
|
||||
{
|
||||
bool hr_present;
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(bearer_cap->speech_ver) - 1; i++) {
|
||||
const struct codec_mapping *m = codec_mapping_by_speech_ver(bearer_cap->speech_ver[i]);
|
||||
|
||||
if (!m)
|
||||
continue;
|
||||
|
||||
if (m->frhr == CODEC_FRHR_HR)
|
||||
hr_present = true;
|
||||
}
|
||||
|
||||
if (hr_present)
|
||||
bearer_cap->radio = GSM48_BCAP_RRQ_DUAL_FR;
|
||||
else
|
||||
bearer_cap->radio = GSM48_BCAP_RRQ_FR_ONLY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to convert the SDP audio codec name to Speech Versions to append to Bearer Capabilities.
|
||||
* Return the number of Speech Version entries added (some may add more than one, others may be unknown/unapplicable and
|
||||
* return 0). */
|
||||
int sdp_audio_codec_add_to_bearer_cap(struct gsm_mncc_bearer_cap *bearer_cap, const struct sdp_audio_codec *codec)
|
||||
{
|
||||
const struct codec_mapping *m;
|
||||
int added = 0;
|
||||
foreach_codec_mapping(m) {
|
||||
int i;
|
||||
if (strcmp(m->sdp.subtype_name, codec->subtype_name))
|
||||
continue;
|
||||
/* TODO also match rate and fmtp? */
|
||||
for (i = 0; i < m->speech_ver_count; i++) {
|
||||
added += bearer_cap_add_speech_ver(bearer_cap, m->speech_ver[i]);
|
||||
}
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
/* Append all audio codecs found in given sdp_msg to Bearer Capability, by traversing all codec entries with
|
||||
* sdp_audio_codec_add_to_bearer_cap(). Return the number of Speech Version entries added.
|
||||
* Note that Speech Version entries are only appended, no previous entries are removed.
|
||||
* Note that only the Speech Version entries are modified; to make a valid Bearer Capabiliy, at least bearer_cap->radio
|
||||
* must also be set (before or after this function); see also bearer_cap_set_radio(). */
|
||||
int sdp_audio_codecs_to_bearer_cap(struct gsm_mncc_bearer_cap *bearer_cap, const struct sdp_audio_codecs *ac)
|
||||
{
|
||||
const struct sdp_audio_codec *codec;
|
||||
int added = 0;
|
||||
|
||||
foreach_sdp_audio_codec(codec, ac) {
|
||||
added += sdp_audio_codec_add_to_bearer_cap(bearer_cap, codec);
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
/* Convert Speech Version to SDP audio codec and append to SDP message struct. */
|
||||
struct sdp_audio_codec *sdp_audio_codecs_add_speech_ver(struct sdp_audio_codecs *ac,
|
||||
enum gsm48_bcap_speech_ver speech_ver)
|
||||
{
|
||||
const struct codec_mapping *m;
|
||||
struct sdp_audio_codec *ret = NULL;
|
||||
foreach_codec_mapping(m) {
|
||||
int i;
|
||||
for (i = 0; i < m->speech_ver_count; i++) {
|
||||
if (m->speech_ver[i] == speech_ver) {
|
||||
ret = sdp_audio_codec_add_copy(ac, &m->sdp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sdp_audio_codec *sdp_audio_codecs_add_mgcp_codec(struct sdp_audio_codecs *ac, enum mgcp_codecs mgcp_codec)
|
||||
{
|
||||
const struct codec_mapping *m = codec_mapping_by_mgcp_codec(mgcp_codec);
|
||||
if (!m)
|
||||
return NULL;
|
||||
return sdp_audio_codec_add_copy(ac, &m->sdp);
|
||||
}
|
||||
|
||||
void sdp_audio_codecs_from_bearer_cap(struct sdp_audio_codecs *ac, const struct gsm_mncc_bearer_cap *bc)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bc->speech_ver); i++) {
|
||||
if (bc->speech_ver[i] == -1)
|
||||
break;
|
||||
sdp_audio_codecs_add_speech_ver(ac, bc->speech_ver[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void sdp_audio_codecs_from_speech_codec_list(struct sdp_audio_codecs *ac, const struct gsm0808_speech_codec_list *cl)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < cl->len; i++) {
|
||||
const struct gsm0808_speech_codec *sc = &cl->codec[i];
|
||||
const struct codec_mapping *m = codec_mapping_by_gsm0808_speech_codec_type(sc->type, sc->cfg);
|
||||
if (!m)
|
||||
continue;
|
||||
sdp_audio_codec_add_copy(ac, &m->sdp);
|
||||
}
|
||||
}
|
||||
|
||||
int sdp_audio_codecs_to_gsm0808_channel_type(struct gsm0808_channel_type *ct, const struct sdp_audio_codecs *ac)
|
||||
{
|
||||
const struct sdp_audio_codec *codec;
|
||||
bool fr_present = false;
|
||||
int first_fr_idx = -1;
|
||||
bool hr_present = false;
|
||||
int first_hr_idx = -1;
|
||||
int idx = -1;
|
||||
|
||||
*ct = (struct gsm0808_channel_type){
|
||||
.ch_indctr = GSM0808_CHAN_SPEECH,
|
||||
};
|
||||
|
||||
foreach_sdp_audio_codec(codec, ac) {
|
||||
const struct codec_mapping *m;
|
||||
int i;
|
||||
bool dup;
|
||||
idx++;
|
||||
foreach_codec_mapping(m) {
|
||||
if (strcmp(m->sdp.subtype_name, codec->subtype_name))
|
||||
continue;
|
||||
|
||||
switch (m->perm_speech) {
|
||||
default:
|
||||
continue;
|
||||
|
||||
case GSM0808_PERM_FR1:
|
||||
case GSM0808_PERM_FR2:
|
||||
case GSM0808_PERM_FR3:
|
||||
case GSM0808_PERM_FR4:
|
||||
case GSM0808_PERM_FR5:
|
||||
fr_present = true;
|
||||
if (first_fr_idx < 0)
|
||||
first_fr_idx = idx;
|
||||
break;
|
||||
|
||||
case GSM0808_PERM_HR1:
|
||||
case GSM0808_PERM_HR2:
|
||||
case GSM0808_PERM_HR3:
|
||||
case GSM0808_PERM_HR4:
|
||||
case GSM0808_PERM_HR6:
|
||||
hr_present = true;
|
||||
if (first_hr_idx < 0)
|
||||
first_hr_idx = idx;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Avoid duplicates */
|
||||
dup = false;
|
||||
for (i = 0; i < ct->perm_spch_len; i++) {
|
||||
if (ct->perm_spch[i] == m->perm_speech) {
|
||||
dup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dup)
|
||||
continue;
|
||||
|
||||
ct->perm_spch[ct->perm_spch_len] = m->perm_speech;
|
||||
ct->perm_spch_len++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fr_present && hr_present) {
|
||||
if (first_fr_idx <= first_hr_idx)
|
||||
ct->ch_rate_type = GSM0808_SPEECH_FULL_PREF;
|
||||
else
|
||||
ct->ch_rate_type = GSM0808_SPEECH_HALF_PREF;
|
||||
} else if (fr_present && !hr_present)
|
||||
ct->ch_rate_type = GSM0808_SPEECH_FULL_BM;
|
||||
else if (!fr_present && hr_present)
|
||||
ct->ch_rate_type = GSM0808_SPEECH_HALF_LM;
|
||||
else
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum mgcp_codecs sdp_audio_codec_to_mgcp_codec(const struct sdp_audio_codec *codec)
|
||||
{
|
||||
const struct codec_mapping *m;
|
||||
foreach_codec_mapping(m) {
|
||||
if (!sdp_audio_codec_cmp(&m->sdp, codec, false, false))
|
||||
return m->mgcp;
|
||||
}
|
||||
return NO_MGCP_CODEC;
|
||||
}
|
|
@ -55,6 +55,9 @@
|
|||
#include <osmocom/msc/rtp_stream.h>
|
||||
#include <osmocom/msc/mncc_call.h>
|
||||
#include <osmocom/msc/msc_t.h>
|
||||
#include <osmocom/msc/sdp_msg.h>
|
||||
#include <osmocom/msc/cc_sdp.h>
|
||||
#include <osmocom/msc/codec_sdp_cc_t9n.h>
|
||||
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
#include <osmocom/gsm/gsm0480.h>
|
||||
|
@ -254,8 +257,12 @@ static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans,
|
|||
int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans,
|
||||
uint32_t callref, int location, int value)
|
||||
{
|
||||
/* BEWARE: trans may be passed as NULL to reply to invalid MNCC requests */
|
||||
struct gsm_mncc rel;
|
||||
|
||||
if (trans && trans->cc.mncc_release_sent)
|
||||
return 0;
|
||||
|
||||
memset(&rel, 0, sizeof(rel));
|
||||
rel.callref = callref;
|
||||
mncc_set_cause(&rel, location, value);
|
||||
|
@ -498,6 +505,8 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
|
|||
memset(&setup, 0, sizeof(struct gsm_mncc));
|
||||
setup.callref = trans->callref;
|
||||
|
||||
OSMO_ASSERT(trans->msc_a);
|
||||
|
||||
tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
|
||||
/* emergency setup is identified by msg_type */
|
||||
if (msg_type == GSM48_MT_CC_EMERG_SETUP) {
|
||||
|
@ -567,25 +576,77 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
|
|||
TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1);
|
||||
}
|
||||
|
||||
new_cc_state(trans, GSM_CSTATE_INITIATED);
|
||||
cc_sdp_init(&trans->cc.sdp, trans->msc_a->c.ran->type,
|
||||
setup.fields & MNCC_F_BEARER_CAP ? &trans->bearer_cap : NULL,
|
||||
&trans->msc_a->cc.codec_list_bss_supported);
|
||||
cc_sdp_filter(&trans->cc.sdp);
|
||||
|
||||
LOG_TRANS(trans, setup.emergency ? LOGL_NOTICE : LOGL_INFO, "%sSETUP to %s\n",
|
||||
setup.emergency ? "EMERGENCY_" : "", setup.called.number);
|
||||
LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n", cc_sdp_name(&trans->cc.sdp));
|
||||
|
||||
rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MO_SETUP]);
|
||||
|
||||
new_cc_state(trans, GSM_CSTATE_INITIATED);
|
||||
|
||||
/* To complete the MNCC_SETUP_IND, we need to provide an RTP address and port. First instruct the MGW to create
|
||||
* a CN-side RTP conn, and continue with MNCC_SETUP_IND once that is done. Leave trans.cc in GSM_CSTATE_NULL and
|
||||
* note down the msg_type to indicate that we indeed composed an MNCC_SETUP_IND for later. */
|
||||
setup.msg_type = MNCC_SETUP_IND;
|
||||
trans->cc.msg = setup;
|
||||
return msc_a_try_call_assignment(trans);
|
||||
/* continue in gsm48_cc_rx_setup_cn_local_rtp_port_known() */
|
||||
}
|
||||
|
||||
/* Callback for MNCC_SETUP_IND waiting for the core network RTP port to be established by the MGW (via msc_a) */
|
||||
void gsm48_cc_rx_setup_cn_local_rtp_port_known(struct gsm_trans *trans)
|
||||
{
|
||||
struct msc_a *msc_a = trans->msc_a;
|
||||
struct gsm_mncc setup = trans->cc.msg;
|
||||
struct osmo_sockaddr_str *rtp_cn_local;
|
||||
struct sdp_msg *sdp;
|
||||
|
||||
if (trans->cc.state != GSM_CSTATE_INITIATED
|
||||
|| setup.msg_type != MNCC_SETUP_IND) {
|
||||
LOG_TRANS(trans, LOGL_ERROR,
|
||||
"Unexpected CC state. Expected GSM_CSTATE_NULL and a buffered MNCC_SETUP_IND message,"
|
||||
" found CC state %d and msg_type %s\n",
|
||||
trans->cc.state, get_mncc_name(setup.msg_type));
|
||||
trans->callref = 0;
|
||||
trans_free(trans);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!msc_a) {
|
||||
LOG_TRANS(trans, LOGL_ERROR, "No connection for CC trans\n");
|
||||
trans->callref = 0;
|
||||
trans_free(trans);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 'setup' above has taken the value of trans->cc.msg, we can now clear that. */
|
||||
trans->cc.msg = (struct gsm_mncc){};
|
||||
|
||||
/* Insert the CN side RTP port now available into SDP and compose SDP string */
|
||||
rtp_cn_local = call_leg_local_ip(msc_a->cc.call_leg, RTP_TO_CN);
|
||||
if (!osmo_sockaddr_str_is_nonzero(rtp_cn_local)) {
|
||||
LOG_TRANS(trans, LOGL_ERROR, "Cannot compose SDP for MNCC_SETUP_IND: no RTP set up for the CN side\n");
|
||||
trans_free(trans);
|
||||
return;
|
||||
}
|
||||
|
||||
cc_sdp_filter(&trans->cc.sdp);
|
||||
sdp = &trans->cc.sdp.result;
|
||||
sdp->rtp = *rtp_cn_local;
|
||||
sdp_msg_to_str(setup.sdp, sizeof(setup.sdp), sdp);
|
||||
|
||||
/* indicate setup to MNCC */
|
||||
mncc_recvmsg(trans->net, trans, MNCC_SETUP_IND, &setup);
|
||||
|
||||
/* MNCC code will modify the channel asynchronously, we should
|
||||
* ipaccess-bind only after the modification has been made to the
|
||||
* lchan->tch_mode */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
|
||||
{
|
||||
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC STUP");
|
||||
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC SETUP");
|
||||
struct gsm48_hdr *gh;
|
||||
struct gsm_mncc *setup = arg;
|
||||
int rc, trans_id;
|
||||
|
@ -622,15 +683,65 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
|
|||
|
||||
gh->msg_type = GSM48_MT_CC_SETUP;
|
||||
|
||||
gsm48_start_cc_timer(trans, 0x303, GSM48_T303);
|
||||
/* We must not pass bearer_cap to cc_sdp_init(), because we haven't received the MS's Bearer Capabilities yet;
|
||||
* the Bearer Capabilities handled here are actually the remote call leg's Bearer Capabilities to be passed on
|
||||
* during the CC Setup. */
|
||||
cc_sdp_init(&trans->cc.sdp, trans->msc_a->c.ran->type, NULL,
|
||||
&trans->msc_a->cc.codec_list_bss_supported);
|
||||
|
||||
/* bearer capability */
|
||||
if (setup->fields & MNCC_F_BEARER_CAP) {
|
||||
/* Create a copy of the bearer capability in the transaction struct, so we
|
||||
* can use this information later */
|
||||
memcpy(&trans->bearer_cap, &setup->bearer_cap, sizeof(trans->bearer_cap));
|
||||
gsm48_encode_bearer_cap(msg, 0, &setup->bearer_cap);
|
||||
/* sdp.remote: if SDP is included in the MNCC, take that as definitive list of remote audio codecs. */
|
||||
if (setup->sdp[0]) {
|
||||
rc = sdp_msg_from_str(&trans->cc.sdp.remote, setup->sdp);
|
||||
if (rc)
|
||||
LOG_TRANS(trans, LOGL_ERROR, "Failed to parse remote call leg SDP: %d\n", rc);
|
||||
}
|
||||
|
||||
/* sdp.remote: if there is no SDP information or we failed to parse it, try using the Bearer Capability from
|
||||
* MNCC, if any. */
|
||||
if (!trans->cc.sdp.remote.audio_codecs.count && (setup->fields & MNCC_F_BEARER_CAP)) {
|
||||
trans->cc.sdp.remote = (struct sdp_msg){};
|
||||
sdp_audio_codecs_from_bearer_cap(&trans->cc.sdp.remote.audio_codecs,
|
||||
&setup->bearer_cap);
|
||||
}
|
||||
|
||||
if (!trans->cc.sdp.remote.audio_codecs.count)
|
||||
LOG_TRANS(trans, LOGL_ERROR,
|
||||
"Got no information of remote audio codecs: neither SDP nor Bearer Capability. Trying anyway.\n");
|
||||
|
||||
/* Translate SDP to bearer capability Speech Version entries.
|
||||
* If we supported transcoding, this could add arbitrary speech versions.
|
||||
* For now add speech_ver entries for each codec in the SDP that matches a GSM speech_ver constant. */
|
||||
cc_sdp_filter(&trans->cc.sdp);
|
||||
LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n", cc_sdp_name(&trans->cc.sdp));
|
||||
trans->bearer_cap = (struct gsm_mncc_bearer_cap){
|
||||
.speech_ver = { -1 },
|
||||
};
|
||||
sdp_audio_codecs_to_bearer_cap(&trans->bearer_cap, &trans->cc.sdp.result.audio_codecs);
|
||||
rc = bearer_cap_set_radio(&trans->bearer_cap);
|
||||
if (rc) {
|
||||
LOG_TRANS(trans, LOGL_ERROR, "Error composing Bearer Capability for CC Setup\n");
|
||||
trans_free(trans);
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* If no resulting codecs remain, error out. If the MGW were able to transcode, we would just use unidentical
|
||||
* codecs on each conn of the MGW endpoint. */
|
||||
if (trans->bearer_cap.speech_ver[0] == -1) {
|
||||
LOG_TRANS(trans, LOGL_ERROR, "%s: no codec match possible: %s\n",
|
||||
get_mncc_name(setup->msg_type), cc_sdp_name(&trans->cc.sdp));
|
||||
|
||||
/* incompatible codecs */
|
||||
rc = mncc_release_ind(trans->net, trans, trans->callref,
|
||||
GSM48_CAUSE_LOC_PRN_S_LU,
|
||||
GSM48_CC_CAUSE_INCOMPAT_DEST /* TODO: correct cause code? */);
|
||||
trans->cc.mncc_release_sent = true;
|
||||
trans_free(trans);
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
gsm48_encode_bearer_cap(msg, 0, &trans->bearer_cap);
|
||||
|
||||
/* facility */
|
||||
if (setup->fields & MNCC_F_FACILITY)
|
||||
gsm48_encode_facility(msg, 0, &setup->facility);
|
||||
|
@ -657,6 +768,8 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
|
|||
|
||||
rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MT_SETUP]);
|
||||
|
||||
gsm48_start_cc_timer(trans, 0x303, GSM48_T303);
|
||||
|
||||
return trans_tx_gsm48(trans, msg);
|
||||
}
|
||||
|
||||
|
@ -691,9 +804,14 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
|
|||
/* Create a copy of the bearer capability
|
||||
* in the transaction struct, so we can use
|
||||
* this information later */
|
||||
memcpy(&trans->bearer_cap,&call_conf.bearer_cap,
|
||||
memcpy(&trans->bearer_cap, &call_conf.bearer_cap,
|
||||
sizeof(trans->bearer_cap));
|
||||
|
||||
/* Note MS codec capabilities for codec negotiation */
|
||||
trans->cc.sdp.ms = (struct sdp_audio_codecs){};
|
||||
sdp_audio_codecs_from_bearer_cap(&trans->cc.sdp.ms, &call_conf.bearer_cap);
|
||||
}
|
||||
|
||||
/* cause */
|
||||
if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
|
||||
call_conf.fields |= MNCC_F_CAUSE;
|
||||
|
@ -710,8 +828,6 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
|
|||
/* IMSI of called subscriber */
|
||||
OSMO_STRLCPY_ARRAY(call_conf.imsi, trans->vsub->imsi);
|
||||
|
||||
new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
|
||||
|
||||
/* Assign call (if not done yet) */
|
||||
rc = msc_a_try_call_assignment(trans);
|
||||
|
||||
|
@ -720,8 +836,53 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND,
|
||||
&call_conf);
|
||||
/* Directly ack with MNCC_CALL_CONF_IND, not yet containing SDP or RTP IP:port information. */
|
||||
new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
|
||||
return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND, &call_conf);
|
||||
}
|
||||
|
||||
static int mncc_recv_rtp(struct gsm_network *net, struct gsm_trans *trans, uint32_t callref,
|
||||
int cmd, struct osmo_sockaddr_str *rtp_addr, uint32_t payload_type,
|
||||
uint32_t payload_msg_type, const struct sdp_msg *sdp);
|
||||
|
||||
int gsm48_cc_mt_rtp_port_and_codec_known(struct gsm_trans *trans)
|
||||
{
|
||||
struct msc_a *msc_a = trans->msc_a;
|
||||
struct osmo_sockaddr_str *rtp_cn_local;
|
||||
struct rtp_stream *rtp_ran;
|
||||
struct gsm_mncc_rtp;
|
||||
|
||||
if (!msc_a) {
|
||||
LOG_TRANS(trans, LOGL_ERROR, "No connection for CC trans\n");
|
||||
trans->callref = 0;
|
||||
trans_free(trans);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set chosen codec in SDP. This is the result of the Assignment, the actual codec the BSS has chosen for this
|
||||
* MT side. */
|
||||
rtp_ran = msc_a->cc.call_leg->rtp[RTP_TO_RAN];
|
||||
if (!rtp_ran->codecs_known) {
|
||||
LOG_TRANS(trans, LOGL_ERROR, "RAN codecs not known but should be, cannot continue.\n");
|
||||
trans_free(trans);
|
||||
return -EINVAL;
|
||||
}
|
||||
trans->cc.sdp.assignment = rtp_ran->codecs.codec[0];
|
||||
|
||||
/* Insert the CN side RTP port now available into SDP */
|
||||
rtp_cn_local = call_leg_local_ip(msc_a->cc.call_leg, RTP_TO_CN);
|
||||
if (!rtp_cn_local) {
|
||||
LOG_TRANS(trans, LOGL_ERROR, "Cannot compose SDP for MNCC_RTP_CREATE: no RTP set up for the CN side\n");
|
||||
trans_free(trans);
|
||||
return -EINVAL;
|
||||
}
|
||||
trans->cc.sdp.result.rtp = *rtp_cn_local;
|
||||
|
||||
cc_sdp_filter(&trans->cc.sdp);
|
||||
LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n", cc_sdp_name(&trans->cc.sdp));
|
||||
|
||||
return mncc_recv_rtp(msc_a_net(msc_a), trans, trans->callref, MNCC_RTP_CREATE, rtp_cn_local, 0, 0,
|
||||
&trans->cc.sdp.result);
|
||||
}
|
||||
|
||||
static int gsm48_cc_tx_call_proc_and_assign(struct gsm_trans *trans, void *arg)
|
||||
|
@ -790,6 +951,10 @@ static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg)
|
|||
|
||||
new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED);
|
||||
|
||||
cc_sdp_filter(&trans->cc.sdp);
|
||||
LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n", cc_sdp_name(&trans->cc.sdp));
|
||||
sdp_msg_to_str(alerting.sdp, sizeof(alerting.sdp), &trans->cc.sdp.result);
|
||||
|
||||
return mncc_recvmsg(trans->net, trans, MNCC_ALERT_IND,
|
||||
&alerting);
|
||||
}
|
||||
|
@ -814,6 +979,19 @@ static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg)
|
|||
|
||||
new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED);
|
||||
|
||||
if (alerting->sdp[0]) {
|
||||
struct call_leg *cl = trans->msc_a->cc.call_leg;
|
||||
struct rtp_stream *rtp_cn = cl ? cl->rtp[RTP_TO_CN] : NULL;
|
||||
sdp_msg_from_str(&trans->cc.sdp.remote, alerting->sdp);
|
||||
LOG_TRANS(trans, LOGL_DEBUG, "%s codecs: %s\n",
|
||||
get_mncc_name(alerting->msg_type),
|
||||
cc_sdp_name(&trans->cc.sdp));
|
||||
if (rtp_cn) {
|
||||
rtp_stream_set_remote_addr_and_codecs(rtp_cn, &trans->cc.sdp.remote);
|
||||
rtp_stream_commit(rtp_cn);
|
||||
}
|
||||
}
|
||||
|
||||
return trans_tx_gsm48(trans, msg);
|
||||
}
|
||||
|
||||
|
@ -860,6 +1038,20 @@ static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg)
|
|||
|
||||
new_cc_state(trans, GSM_CSTATE_CONNECT_IND);
|
||||
|
||||
/* Received an MNCC_SETUP_RSP with the remote leg's SDP information. Apply codec choice. */
|
||||
if (connect->sdp[0]) {
|
||||
struct call_leg *cl = trans->msc_a->cc.call_leg;
|
||||
struct rtp_stream *rtp_cn = cl ? cl->rtp[RTP_TO_CN] : NULL;
|
||||
sdp_msg_from_str(&trans->cc.sdp.remote, connect->sdp);
|
||||
LOG_TRANS(trans, LOGL_DEBUG, "%s codecs: %s\n",
|
||||
get_mncc_name(connect->msg_type),
|
||||
cc_sdp_name(&trans->cc.sdp));
|
||||
if (rtp_cn) {
|
||||
rtp_stream_set_remote_addr_and_codecs(rtp_cn, &trans->cc.sdp.remote);
|
||||
rtp_stream_commit(rtp_cn);
|
||||
}
|
||||
}
|
||||
|
||||
return trans_tx_gsm48(trans, msg);
|
||||
}
|
||||
|
||||
|
@ -902,6 +1094,8 @@ static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg)
|
|||
new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST);
|
||||
rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MT_CONNECT]);
|
||||
|
||||
cc_sdp_filter(&trans->cc.sdp);
|
||||
sdp_msg_to_str(connect.sdp, sizeof(connect.sdp), &trans->cc.sdp.result);
|
||||
return mncc_recvmsg(trans->net, trans, MNCC_SETUP_CNF, &connect);
|
||||
}
|
||||
|
||||
|
@ -1027,7 +1221,6 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
|
|||
unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
|
||||
struct tlv_parsed tp;
|
||||
struct gsm_mncc rel;
|
||||
int rc;
|
||||
|
||||
gsm48_stop_cc_timer(trans);
|
||||
|
||||
|
@ -1059,14 +1252,16 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
|
|||
TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
|
||||
}
|
||||
|
||||
if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) {
|
||||
/* release collision 5.4.5 */
|
||||
rc = mncc_recvmsg(trans->net, trans, MNCC_REL_CNF, &rel);
|
||||
} else {
|
||||
rc = gsm48_tx_simple(trans->msc_a,
|
||||
GSM48_PDISC_CC | (trans->transaction_id << 4),
|
||||
GSM48_MT_CC_RELEASE_COMPL);
|
||||
rc = mncc_recvmsg(trans->net, trans, MNCC_REL_IND, &rel);
|
||||
if (!trans->cc.mncc_release_sent) {
|
||||
if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) {
|
||||
/* release collision 5.4.5 */
|
||||
mncc_recvmsg(trans->net, trans, MNCC_REL_CNF, &rel);
|
||||
} else {
|
||||
gsm48_tx_simple(trans->msc_a,
|
||||
GSM48_PDISC_CC | (trans->transaction_id << 4),
|
||||
GSM48_MT_CC_RELEASE_COMPL);
|
||||
mncc_recvmsg(trans->net, trans, MNCC_REL_IND, &rel);
|
||||
}
|
||||
}
|
||||
|
||||
new_cc_state(trans, GSM_CSTATE_NULL);
|
||||
|
@ -1074,7 +1269,7 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
|
|||
trans->callref = 0;
|
||||
trans_free(trans);
|
||||
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg)
|
||||
|
@ -1153,19 +1348,21 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
|
|||
TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
|
||||
}
|
||||
|
||||
if (trans->callref) {
|
||||
switch (trans->cc.state) {
|
||||
case GSM_CSTATE_CALL_PRESENT:
|
||||
rc = mncc_recvmsg(trans->net, trans,
|
||||
MNCC_REJ_IND, &rel);
|
||||
break;
|
||||
case GSM_CSTATE_RELEASE_REQ:
|
||||
rc = mncc_recvmsg(trans->net, trans,
|
||||
MNCC_REL_CNF, &rel);
|
||||
break;
|
||||
default:
|
||||
rc = mncc_recvmsg(trans->net, trans,
|
||||
MNCC_REL_IND, &rel);
|
||||
if (!trans->cc.mncc_release_sent) {
|
||||
if (trans->callref) {
|
||||
switch (trans->cc.state) {
|
||||
case GSM_CSTATE_CALL_PRESENT:
|
||||
rc = mncc_recvmsg(trans->net, trans,
|
||||
MNCC_REJ_IND, &rel);
|
||||
break;
|
||||
case GSM_CSTATE_RELEASE_REQ:
|
||||
rc = mncc_recvmsg(trans->net, trans,
|
||||
MNCC_REL_CNF, &rel);
|
||||
break;
|
||||
default:
|
||||
rc = mncc_recvmsg(trans->net, trans,
|
||||
MNCC_REL_IND, &rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1612,7 +1809,7 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
|
|||
|
||||
static int mncc_recv_rtp(struct gsm_network *net, struct gsm_trans *trans, uint32_t callref,
|
||||
int cmd, struct osmo_sockaddr_str *rtp_addr, uint32_t payload_type,
|
||||
uint32_t payload_msg_type)
|
||||
uint32_t payload_msg_type, const struct sdp_msg *sdp)
|
||||
{
|
||||
uint8_t data[sizeof(struct gsm_mncc)];
|
||||
struct gsm_mncc_rtp *rtp;
|
||||
|
@ -1628,12 +1825,18 @@ static int mncc_recv_rtp(struct gsm_network *net, struct gsm_trans *trans, uint3
|
|||
}
|
||||
rtp->payload_type = payload_type;
|
||||
rtp->payload_msg_type = payload_msg_type;
|
||||
if (sdp) {
|
||||
LOG_TRANS(trans, LOGL_DEBUG, "%s SDP: %s\n",
|
||||
get_mncc_name(rtp->msg_type),
|
||||
sdp_msg_name(sdp));
|
||||
sdp_msg_to_str(rtp->sdp, sizeof(rtp->sdp), sdp);
|
||||
}
|
||||
return mncc_recvmsg(net, trans, cmd, (struct gsm_mncc *)data);
|
||||
}
|
||||
|
||||
static void mncc_recv_rtp_err(struct gsm_network *net, struct gsm_trans *trans, uint32_t callref, int cmd)
|
||||
{
|
||||
mncc_recv_rtp(net, trans, callref, cmd, NULL, 0, 0);
|
||||
mncc_recv_rtp(net, trans, callref, cmd, NULL, 0, 0, NULL);
|
||||
}
|
||||
|
||||
static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
|
||||
|
@ -1659,6 +1862,57 @@ static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
|
|||
return msc_a_try_call_assignment(trans);
|
||||
}
|
||||
|
||||
int cc_cn_local_rtp_port_known(struct gsm_trans *cc_trans)
|
||||
{
|
||||
switch(cc_trans->cc.state) {
|
||||
case GSM_CSTATE_INITIATED:
|
||||
if (cc_trans->cc.msg.msg_type != MNCC_SETUP_IND) {
|
||||
LOG_TRANS(cc_trans, LOGL_ERROR, "Assuming MO call, expected MNCC_SETUP_IND to be prepared\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* This is the MO call leg, waiting for a CN RTP be able to send initial MNCC_SETUP_IND. */
|
||||
gsm48_cc_rx_setup_cn_local_rtp_port_known(cc_trans);
|
||||
return 0;
|
||||
|
||||
case GSM_CSTATE_MO_TERM_CALL_CONF:
|
||||
/* This is the MT call leg, waiting for a CN RTP to be able to send MNCC_CALL_CONF_IND. */
|
||||
return gsm48_cc_mt_rtp_port_and_codec_known(cc_trans);
|
||||
|
||||
default:
|
||||
LOG_TRANS(cc_trans, LOGL_ERROR, "CN RTP address available, but in unexpected state %d\n",
|
||||
cc_trans->cc.state);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int cc_assignment_done(struct gsm_trans *trans)
|
||||
{
|
||||
struct msc_a *msc_a = trans->msc_a;
|
||||
|
||||
switch (trans->cc.state) {
|
||||
case GSM_CSTATE_INITIATED:
|
||||
case GSM_CSTATE_MO_CALL_PROC:
|
||||
/* MO call */
|
||||
break;
|
||||
|
||||
case GSM_CSTATE_CALL_RECEIVED:
|
||||
case GSM_CSTATE_MO_TERM_CALL_CONF:
|
||||
/* MT call */
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_TRANS(trans, LOGL_ERROR, "Assignment done in unexpected CC state: %d\n", trans->cc.state);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!call_leg_local_ip(msc_a->cc.call_leg, RTP_TO_CN)) {
|
||||
LOG_TRANS(trans, LOGL_DEBUG,
|
||||
"Assignment complete, but still waiting for the CRCX OK on the CN side RTP\n");
|
||||
return 0;
|
||||
}
|
||||
return gsm48_tch_rtp_create(trans);
|
||||
}
|
||||
|
||||
/* Trigger TCH_RTP_CREATE acknowledgement */
|
||||
int gsm48_tch_rtp_create(struct gsm_trans *trans)
|
||||
{
|
||||
|
@ -1670,30 +1924,32 @@ int gsm48_tch_rtp_create(struct gsm_trans *trans)
|
|||
struct call_leg *cl = msc_a->cc.call_leg;
|
||||
struct osmo_sockaddr_str *rtp_cn_local;
|
||||
struct rtp_stream *rtp_cn = cl ? cl->rtp[RTP_TO_CN] : NULL;
|
||||
uint32_t payload_type;
|
||||
int payload_msg_type;
|
||||
const struct mgcp_conn_peer *mgcp_info;
|
||||
int mncc_payload_msg_type;
|
||||
struct sdp_audio_codec *codec;
|
||||
const struct codec_mapping *m;
|
||||
|
||||
if (!rtp_cn) {
|
||||
LOG_TRANS_CAT(trans, DMNCC, LOGL_ERROR, "Cannot RTP CREATE to MNCC, no RTP set up for the CN side\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!rtp_cn->codec_known) {
|
||||
cc_sdp_filter(&trans->cc.sdp);
|
||||
LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n", cc_sdp_name(&trans->cc.sdp));
|
||||
|
||||
if (!trans->cc.sdp.result.audio_codecs.count) {
|
||||
LOG_TRANS_CAT(trans, DMNCC, LOGL_ERROR,
|
||||
"Cannot RTP CREATE to MNCC, no codec set up for the RTP CN side\n");
|
||||
"Cannot RTP CREATE to MNCC, there is no codec available\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Codec */
|
||||
payload_msg_type = mgcp_codec_to_mncc_payload_msg_type(rtp_cn->codec);
|
||||
/* Modify the MGW endpoint if necessary, usually this should already match and not cause MGCP. */
|
||||
rtp_stream_set_codecs(rtp_cn, &trans->cc.sdp.result.audio_codecs);
|
||||
rtp_stream_commit(rtp_cn);
|
||||
|
||||
/* Payload Type number */
|
||||
mgcp_info = osmo_mgcpc_ep_ci_get_rtp_info(rtp_cn->ci);
|
||||
if (mgcp_info && mgcp_info->ptmap_len)
|
||||
payload_type = map_codec_to_pt(mgcp_info->ptmap, mgcp_info->ptmap_len, rtp_cn->codec);
|
||||
else
|
||||
payload_type = rtp_cn->codec;
|
||||
/* Populate the legacy MNCC codec elements: payload_type and payload_msg_type */
|
||||
codec = &rtp_cn->codecs.codec[0];
|
||||
m = codec_mapping_by_subtype_name(codec->subtype_name);
|
||||
mncc_payload_msg_type = m ? m->mncc_payload_msg_type : 0;
|
||||
|
||||
rtp_cn_local = call_leg_local_ip(cl, RTP_TO_CN);
|
||||
if (!rtp_cn_local) {
|
||||
|
@ -1701,7 +1957,9 @@ int gsm48_tch_rtp_create(struct gsm_trans *trans)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
return mncc_recv_rtp(net, trans, trans->callref, MNCC_RTP_CREATE, rtp_cn_local, payload_type, payload_msg_type);
|
||||
return mncc_recv_rtp(net, trans, trans->callref, MNCC_RTP_CREATE, rtp_cn_local,
|
||||
codec->payload_type, mncc_payload_msg_type,
|
||||
&trans->cc.sdp.result);
|
||||
}
|
||||
|
||||
static int tch_rtp_connect(struct gsm_network *net, const struct gsm_mncc_rtp *rtp)
|
||||
|
@ -1709,7 +1967,6 @@ static int tch_rtp_connect(struct gsm_network *net, const struct gsm_mncc_rtp *r
|
|||
struct gsm_trans *trans;
|
||||
struct call_leg *cl;
|
||||
struct rtp_stream *rtps;
|
||||
struct osmo_sockaddr_str rtp_addr;
|
||||
|
||||
/* FIXME: in *rtp we should get the codec information of the remote
|
||||
* leg. We will have to populate trans->conn->rtp.codec_cn with a
|
||||
|
@ -1735,7 +1992,7 @@ static int tch_rtp_connect(struct gsm_network *net, const struct gsm_mncc_rtp *r
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s\n", get_mncc_name(MNCC_RTP_CONNECT));
|
||||
LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s\n", get_mncc_name(rtp->msg_type));
|
||||
|
||||
cl = trans->msc_a->cc.call_leg;
|
||||
rtps = cl ? cl->rtp[RTP_TO_CN] : NULL;
|
||||
|
@ -1746,8 +2003,21 @@ static int tch_rtp_connect(struct gsm_network *net, const struct gsm_mncc_rtp *r
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
osmo_sockaddr_str_from_32n(&rtp_addr, rtp->ip, rtp->port);
|
||||
rtp_stream_set_remote_addr(rtps, &rtp_addr);
|
||||
if (rtp->sdp[0]) {
|
||||
sdp_msg_from_str(&trans->cc.sdp.remote, rtp->sdp);
|
||||
LOG_TRANS(trans, LOGL_DEBUG, "%s contained SDP %s\n",
|
||||
get_mncc_name(rtp->msg_type),
|
||||
sdp_msg_name(&trans->cc.sdp.remote));
|
||||
}
|
||||
rtp_stream_set_remote_addr_and_codecs(rtps, &trans->cc.sdp.remote);
|
||||
|
||||
if (!osmo_sockaddr_str_is_nonzero(&rtps->remote)) {
|
||||
/* Didn't get an IP address from SDP. Try legacy MNCC IP address */
|
||||
struct osmo_sockaddr_str rtp_addr;
|
||||
osmo_sockaddr_str_from_32n(&rtp_addr, rtp->ip, rtp->port);
|
||||
rtp_stream_set_remote_addr(rtps, &rtp_addr);
|
||||
}
|
||||
|
||||
rtp_stream_commit(rtps);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1929,6 +2199,19 @@ static int mncc_tx_to_gsm_cc(struct gsm_network *net, const union mncc_msg *msg)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Remember remote SDP, if any */
|
||||
if (data->sdp[0]) {
|
||||
if (sdp_msg_from_str(&trans->cc.sdp.remote, data->sdp)) {
|
||||
LOG_TRANS(trans, LOGL_ERROR, "Failed to parse incoming SDP: %s\n",
|
||||
osmo_quote_str(data->sdp, -1));
|
||||
vlr_subscr_put(vsub, __func__);
|
||||
mncc_release_ind(net, NULL, data->callref,
|
||||
GSM48_CAUSE_LOC_PRN_S_LU,
|
||||
GSM48_CC_CAUSE_NORMAL_UNSPEC);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* If subscriber has no conn */
|
||||
if (!msc_a) {
|
||||
/* This condition will return before the common logging of the received MNCC message below, so
|
||||
|
@ -1976,6 +2259,7 @@ static int mncc_tx_to_gsm_cc(struct gsm_network *net, const union mncc_msg *msg)
|
|||
LOG_TRANS(trans, LOGL_DEBUG, "rx %s in paging state\n", get_mncc_name(msg->msg_type));
|
||||
mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
|
||||
GSM48_CC_CAUSE_NORM_CALL_CLEAR);
|
||||
trans->cc.mncc_release_sent = true;
|
||||
if (msg->msg_type == MNCC_REL_REQ)
|
||||
rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel);
|
||||
else
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <osmocom/msc/rtp_stream.h>
|
||||
#include <osmocom/msc/msub.h>
|
||||
#include <osmocom/msc/vlr.h>
|
||||
#include <osmocom/msc/codec_sdp_cc_t9n.h>
|
||||
|
||||
struct osmo_fsm mncc_call_fsm;
|
||||
static bool mncc_call_tx_rtp_create(struct mncc_call *mncc_call);
|
||||
|
@ -261,37 +262,18 @@ static bool mncc_call_rx_rtp_create(struct mncc_call *mncc_call)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!mncc_call->rtps->codec_known) {
|
||||
if (!mncc_call->rtps->codecs_known) {
|
||||
LOG_MNCC_CALL(mncc_call, LOGL_DEBUG, "Got RTP_CREATE, but RTP stream has no codec set\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_MNCC_CALL(mncc_call, LOGL_DEBUG, "Got RTP_CREATE, responding with " OSMO_SOCKADDR_STR_FMT " %s\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&mncc_call->rtps->local),
|
||||
osmo_mgcpc_codec_name(mncc_call->rtps->codec));
|
||||
sdp_audio_codecs_name(&mncc_call->rtps->codecs));
|
||||
/* Already know what RTP IP:port to tell the MNCC. Send it. */
|
||||
return mncc_call_tx_rtp_create(mncc_call);
|
||||
}
|
||||
|
||||
/* Convert enum mgcp_codecs to an gsm_mncc_rtp->payload_msg_type value. */
|
||||
uint32_t mgcp_codec_to_mncc_payload_msg_type(enum mgcp_codecs codec)
|
||||
{
|
||||
switch (codec) {
|
||||
default:
|
||||
/* disclaimer: i have no idea what i'm doing. */
|
||||
case CODEC_GSM_8000_1:
|
||||
return GSM_TCHF_FRAME;
|
||||
case CODEC_GSMEFR_8000_1:
|
||||
return GSM_TCHF_FRAME_EFR;
|
||||
case CODEC_GSMHR_8000_1:
|
||||
return GSM_TCHH_FRAME;
|
||||
case CODEC_AMR_8000_1:
|
||||
case CODEC_AMRWB_16000_1:
|
||||
//return GSM_TCHF_FRAME;
|
||||
return GSM_TCH_FRAME_AMR;
|
||||
}
|
||||
}
|
||||
|
||||
static bool mncc_call_tx_rtp_create(struct mncc_call *mncc_call)
|
||||
{
|
||||
if (!mncc_call->rtps || !osmo_sockaddr_str_is_nonzero(&mncc_call->rtps->local)) {
|
||||
|
@ -313,9 +295,16 @@ static bool mncc_call_tx_rtp_create(struct mncc_call *mncc_call)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (mncc_call->rtps->codec_known) {
|
||||
mncc_msg.rtp.payload_type = 0; /* ??? */
|
||||
mncc_msg.rtp.payload_msg_type = mgcp_codec_to_mncc_payload_msg_type(mncc_call->rtps->codec);
|
||||
if (mncc_call->rtps->codecs_known) {
|
||||
struct sdp_audio_codec *codec = &mncc_call->rtps->codecs.codec[0];
|
||||
const struct codec_mapping *m = codec_mapping_by_subtype_name(codec->subtype_name);
|
||||
|
||||
if (!m) {
|
||||
mncc_call_error(mncc_call, "Failed to resolve audio codec '%s'\n", sdp_audio_codec_name(codec));
|
||||
return false;
|
||||
}
|
||||
mncc_msg.rtp.payload_type = codec->payload_type;
|
||||
mncc_msg.rtp.payload_msg_type = m->mncc_payload_msg_type;
|
||||
}
|
||||
|
||||
if (mncc_call_tx(mncc_call, &mncc_msg))
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include <osmocom/msc/call_leg.h>
|
||||
#include <osmocom/msc/rtp_stream.h>
|
||||
#include <osmocom/msc/msc_ho.h>
|
||||
#include <osmocom/msc/codec_sdp_cc_t9n.h>
|
||||
|
||||
#define MSC_A_USE_WAIT_CLEAR_COMPLETE "wait-Clear-Complete"
|
||||
|
||||
|
@ -511,12 +512,87 @@ static void msc_a_fsm_authenticated(struct osmo_fsm_inst *fi, uint32_t event, vo
|
|||
}
|
||||
}
|
||||
|
||||
static struct call_leg *msc_a_ensure_call_leg(struct msc_a *msc_a, struct gsm_trans *for_cc_trans)
|
||||
{
|
||||
struct call_leg *cl = msc_a->cc.call_leg;
|
||||
struct gsm_network *net = msc_a_net(msc_a);
|
||||
|
||||
/* Ensure that events about RTP endpoints coming from the msc_a->cc.call_leg know which gsm_trans to abort on
|
||||
* error */
|
||||
if (!msc_a->cc.active_trans)
|
||||
msc_a->cc.active_trans = for_cc_trans;
|
||||
if (msc_a->cc.active_trans != for_cc_trans) {
|
||||
LOG_TRANS(for_cc_trans, LOGL_ERROR,
|
||||
"Cannot create call leg, another trans is already active for this conn\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!cl) {
|
||||
cl = msc_a->cc.call_leg = call_leg_alloc(msc_a->c.fi,
|
||||
MSC_EV_CALL_LEG_TERM,
|
||||
MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE,
|
||||
MSC_EV_CALL_LEG_RTP_COMPLETE);
|
||||
OSMO_ASSERT(cl);
|
||||
|
||||
/* HACK: We put the connection in loopback mode at the beginnig to
|
||||
* trick the hNodeB into doing the IuUP negotiation with itself.
|
||||
* This is a hack we need because osmo-mgw does not support IuUP yet, see OS#2459. */
|
||||
if (msc_a->c.ran->type == OSMO_RAT_UTRAN_IU)
|
||||
cl->crcx_conn_mode[RTP_TO_RAN] = MGCP_CONN_LOOPBACK;
|
||||
|
||||
if (net->use_osmux != OSMUX_USAGE_OFF) {
|
||||
struct msc_i *msc_i = msc_a_msc_i(msc_a);
|
||||
if (msc_i->c.remote_to) {
|
||||
/* TODO: investigate what to do in this case */
|
||||
LOG_MSC_A(msc_a, LOGL_ERROR, "Osmux not yet supported for inter-MSC");
|
||||
} else {
|
||||
cl->ran_peer_supports_osmux = msc_i->ran_conn->ran_peer->remote_supports_osmux;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
|
||||
int msc_a_ensure_cn_local_rtp(struct msc_a *msc_a, struct gsm_trans *cc_trans)
|
||||
{
|
||||
struct call_leg *cl;
|
||||
struct rtp_stream *rtp_to_ran;
|
||||
|
||||
cl = msc_a_ensure_call_leg(msc_a, cc_trans);
|
||||
if (!cl)
|
||||
return -EINVAL;
|
||||
rtp_to_ran = cl->rtp[RTP_TO_RAN];
|
||||
|
||||
if (call_leg_local_ip(cl, RTP_TO_CN)) {
|
||||
/* Already has an RTP address and port towards the CN, continue right away. */
|
||||
return osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, cl->rtp[RTP_TO_CN]);
|
||||
}
|
||||
|
||||
/* No CN RTP address available yet, ask the MGW to create one.
|
||||
* Set a codec to be used: if Assignment on the RAN side is already done, take the same codec as the RTP_TO_RAN.
|
||||
* If no RAN side RTP is established, try to guess a preliminary codec from SDP -- before Assignment, picking a
|
||||
* codec from the SDP is more politeness/avoiding confusion than necessity. The actual codec to be used would be
|
||||
* determined later. If no codec could be determined, pass none for the time being. */
|
||||
return call_leg_ensure_ci(cl, RTP_TO_CN, cc_trans->callref, cc_trans,
|
||||
rtp_to_ran->codecs_known ? &rtp_to_ran->codecs : NULL, NULL);
|
||||
}
|
||||
|
||||
static void msc_a_call_leg_cn_local_addr_available(struct msc_a *msc_a, struct gsm_trans *cc_trans)
|
||||
{
|
||||
cc_cn_local_rtp_port_known(cc_trans);
|
||||
}
|
||||
|
||||
|
||||
/* The MGW has given us a local IP address for the RAN side. Ready to start the Assignment of a voice channel. */
|
||||
static void msc_a_call_leg_ran_local_addr_available(struct msc_a *msc_a)
|
||||
{
|
||||
struct ran_msg msg;
|
||||
struct gsm_trans *cc_trans = msc_a->cc.active_trans;
|
||||
struct gsm0808_channel_type channel_type;
|
||||
struct gsm0808_channel_type channel_type = {
|
||||
.ch_indctr = GSM0808_CHAN_SPEECH,
|
||||
.ch_rate_type = GSM0808_SPEECH_FULL_PREF,
|
||||
};
|
||||
|
||||
if (!cc_trans) {
|
||||
LOG_MSC_A(msc_a, LOGL_ERROR, "No CC transaction active\n");
|
||||
|
@ -526,9 +602,21 @@ static void msc_a_call_leg_ran_local_addr_available(struct msc_a *msc_a)
|
|||
|
||||
/* Once a CI is known, we could also CRCX the CN side of the MGW endpoint, but it makes sense to wait for the
|
||||
* codec to be determined by the Assignment Complete message, first. */
|
||||
cc_sdp_filter(&cc_trans->cc.sdp);
|
||||
LOG_TRANS(cc_trans, LOGL_DEBUG, "Sending Assignment Command with codecs: %s\n", cc_sdp_name(&cc_trans->cc.sdp));
|
||||
|
||||
if (mncc_bearer_cap_to_channel_type(&channel_type, &cc_trans->bearer_cap)) {
|
||||
LOG_MSC_A(msc_a, LOGL_ERROR, "Cannot compose Channel Type from bearer capabilities\n");
|
||||
if (!cc_trans->cc.sdp.result.audio_codecs.count) {
|
||||
LOG_TRANS(cc_trans, LOGL_ERROR, "Assignment not possible, no matching codec: %s\n",
|
||||
cc_sdp_name(&cc_trans->cc.sdp));
|
||||
call_leg_release(msc_a->cc.call_leg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compose 48.008 Channel Type from the current set of codecs determined from both local and remote codec
|
||||
* capabilities. */
|
||||
if (sdp_audio_codecs_to_gsm0808_channel_type(&channel_type, &cc_trans->cc.sdp.result.audio_codecs)) {
|
||||
LOG_MSC_A(msc_a, LOGL_ERROR, "Cannot compose Channel Type (Permitted Speech) from codecs: %s\n",
|
||||
cc_sdp_name(&cc_trans->cc.sdp));
|
||||
trans_free(cc_trans);
|
||||
return;
|
||||
}
|
||||
|
@ -550,15 +638,6 @@ static void msc_a_call_leg_ran_local_addr_available(struct msc_a *msc_a)
|
|||
}
|
||||
}
|
||||
|
||||
static void msc_a_call_leg_cn_local_addr_available(struct msc_a *msc_a, struct gsm_trans *cc_trans)
|
||||
{
|
||||
if (gsm48_tch_rtp_create(cc_trans)) {
|
||||
LOG_MSC_A(msc_a, LOGL_ERROR, "Cannot inform MNCC of RTP address\n");
|
||||
trans_free(cc_trans);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static struct gsm_trans *find_waiting_call(struct msc_a *msc_a)
|
||||
{
|
||||
struct gsm_trans *trans;
|
||||
|
@ -1264,6 +1343,7 @@ static void msc_a_up_call_assignment_complete(struct msc_a *msc_a, const struct
|
|||
{
|
||||
struct gsm_trans *cc_trans = msc_a->cc.active_trans;
|
||||
struct rtp_stream *rtps_to_ran = msc_a->cc.call_leg ? msc_a->cc.call_leg->rtp[RTP_TO_RAN] : NULL;
|
||||
const struct codec_mapping *m;
|
||||
|
||||
if (!rtps_to_ran) {
|
||||
LOG_MSC_A(msc_a, LOGL_ERROR, "Rx Assignment Complete, but no RTP stream is set up\n");
|
||||
|
@ -1281,24 +1361,30 @@ static void msc_a_up_call_assignment_complete(struct msc_a *msc_a, const struct
|
|||
return;
|
||||
}
|
||||
|
||||
m = codec_mapping_by_mgcp_codec(ac->assignment_complete.codec);
|
||||
if (!m) {
|
||||
LOG_TRANS(cc_trans, LOGL_ERROR, "Unknown codec in Assignment Complete: %s\n",
|
||||
osmo_mgcpc_codec_name(ac->assignment_complete.codec));
|
||||
call_leg_release(msc_a->cc.call_leg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update RAN-side endpoint CI: */
|
||||
rtp_stream_set_codec(rtps_to_ran, ac->assignment_complete.codec);
|
||||
rtp_stream_set_one_codec(rtps_to_ran, &m->sdp);
|
||||
rtp_stream_set_remote_addr(rtps_to_ran, &ac->assignment_complete.remote_rtp);
|
||||
if (rtps_to_ran->use_osmux)
|
||||
rtp_stream_set_remote_osmux_cid(rtps_to_ran,
|
||||
ac->assignment_complete.osmux_cid);
|
||||
|
||||
rtp_stream_commit(rtps_to_ran);
|
||||
|
||||
/* Setup CN side endpoint CI:
|
||||
* Now that
|
||||
* - the first CI has been created and a definitive endpoint name is assigned to the call_leg's MGW
|
||||
* endpoint,
|
||||
* - the Assignment has chosen a speech codec
|
||||
* go on to create the CN side RTP stream's CI. */
|
||||
if (call_leg_ensure_ci(msc_a->cc.call_leg, RTP_TO_CN, cc_trans->callref, cc_trans,
|
||||
&ac->assignment_complete.codec, NULL)) {
|
||||
LOG_MSC_A_CAT(msc_a, DCC, LOGL_ERROR, "Error creating MGW CI towards CN\n");
|
||||
/* Remember the Codec List (BSS Supported) */
|
||||
if (ac->assignment_complete.codec_list_bss_supported)
|
||||
cc_sdp_set_cell(&cc_trans->cc.sdp, ac->assignment_complete.codec_list_bss_supported);
|
||||
|
||||
cc_trans->cc.sdp.assignment = m->sdp;
|
||||
|
||||
if (cc_assignment_done(cc_trans)) {
|
||||
/* If an error occured, it was logged in cc_assignment_done() */
|
||||
call_leg_release(msc_a->cc.call_leg);
|
||||
return;
|
||||
}
|
||||
|
@ -1377,6 +1463,15 @@ int msc_a_ran_dec_from_msc_i(struct msc_a *msc_a, struct msc_a_ran_dec_data *d)
|
|||
.lai.plmn = msc_a_net(msc_a)->plmn,
|
||||
};
|
||||
gsm0808_cell_id_to_cgi(&msc_a->via_cell, msg->compl_l3.cell_id);
|
||||
if (msg->compl_l3.codec_list_bss_supported) {
|
||||
msc_a->cc.codec_list_bss_supported = *msg->compl_l3.codec_list_bss_supported;
|
||||
if (log_check_level(msc_a->c.ran->log_subsys, LOGL_DEBUG)) {
|
||||
struct sdp_audio_codecs ac = {};
|
||||
sdp_audio_codecs_from_speech_codec_list(&ac, &msc_a->cc.codec_list_bss_supported);
|
||||
LOG_MSC_A(msc_a, LOGL_DEBUG, "Complete Layer 3: Codec List (BSS Supported): %s\n",
|
||||
sdp_audio_codecs_name(&ac));
|
||||
}
|
||||
}
|
||||
rc = msc_a_up_l3(msc_a, msg->compl_l3.msg);
|
||||
if (!rc) {
|
||||
struct ran_conn *conn = msub_ran_conn(msc_a->c.msub);
|
||||
|
@ -1643,45 +1738,42 @@ int msc_tx_common_id(struct msc_a *msc_a, enum msc_role to_role)
|
|||
|
||||
static int msc_a_start_assignment(struct msc_a *msc_a, struct gsm_trans *cc_trans)
|
||||
{
|
||||
struct call_leg *cl = msc_a->cc.call_leg;
|
||||
struct msc_i *msc_i = msc_a_msc_i(msc_a);
|
||||
struct gsm_network *net = msc_a_net(msc_a);
|
||||
struct call_leg *cl;
|
||||
bool cn_rtp_available;
|
||||
bool ran_rtp_available;
|
||||
struct sdp_audio_codecs *codecs;
|
||||
|
||||
OSMO_ASSERT(!msc_a->cc.active_trans);
|
||||
msc_a->cc.active_trans = cc_trans;
|
||||
|
||||
OSMO_ASSERT(cc_trans && cc_trans->type == TRANS_CC);
|
||||
cl = msc_a_ensure_call_leg(msc_a, cc_trans);
|
||||
if (!cl)
|
||||
return -EINVAL;
|
||||
|
||||
if (!cl) {
|
||||
cl = msc_a->cc.call_leg = call_leg_alloc(msc_a->c.fi,
|
||||
MSC_EV_CALL_LEG_TERM,
|
||||
MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE,
|
||||
MSC_EV_CALL_LEG_RTP_COMPLETE);
|
||||
OSMO_ASSERT(cl);
|
||||
/* See if we can set a preliminary codec. If not, pass none for the time being. */
|
||||
cc_sdp_filter(&cc_trans->cc.sdp);
|
||||
codecs = cc_trans->cc.sdp.result.audio_codecs.count ? &cc_trans->cc.sdp.result.audio_codecs : NULL;
|
||||
|
||||
/* HACK: We put the connection in loopback mode at the beginning to
|
||||
* trick the hNodeB into doing the IuUP negotiation with itself.
|
||||
* This is a hack we need because osmo-mgw does not support IuUP yet, see OS#2459. */
|
||||
if (msc_a->c.ran->type == OSMO_RAT_UTRAN_IU)
|
||||
cl->crcx_conn_mode[RTP_TO_RAN] = MGCP_CONN_LOOPBACK;
|
||||
}
|
||||
cn_rtp_available = call_leg_local_ip(cl, RTP_TO_CN);
|
||||
ran_rtp_available = call_leg_local_ip(cl, RTP_TO_RAN);
|
||||
|
||||
if (net->use_osmux != OSMUX_USAGE_OFF) {
|
||||
msc_i = msc_a_msc_i(msc_a);
|
||||
if (msc_i->c.remote_to) {
|
||||
/* TODO: investigate what to do in this case */
|
||||
LOG_MSC_A(msc_a, LOGL_ERROR, "Osmux not yet supported for inter-MSC");
|
||||
} else {
|
||||
cl->ran_peer_supports_osmux = msc_i->ran_conn->ran_peer->remote_supports_osmux;
|
||||
}
|
||||
}
|
||||
/* Set up RTP ports for both RAN and CN side. Even though we ask for both at the same time, the
|
||||
* osmo_mgcpc_ep_fsm automagically waits for the first CRCX to complete before firing the second CRCX. The one
|
||||
* issued first here will also be the first CRCX sent to the MGW. Usually both still need to be set up. */
|
||||
if (!cn_rtp_available)
|
||||
call_leg_ensure_ci(cl, RTP_TO_CN, cc_trans->callref, cc_trans, codecs, NULL);
|
||||
if (!ran_rtp_available)
|
||||
call_leg_ensure_ci(cl, RTP_TO_RAN, cc_trans->callref, cc_trans, codecs, NULL);
|
||||
|
||||
/* This will lead to either MSC_EV_CALL_LEG_LOCAL_ADDR_AVAILABLE or MSC_EV_CALL_LEG_TERM.
|
||||
* If the local address is already known, then immediately trigger. */
|
||||
if (call_leg_local_ip(cl, RTP_TO_RAN))
|
||||
/* Should these already be set up, immediately continue by retriggering the events signalling that the RTP
|
||||
* ports are available. The ordering is: first CN, then RAN. */
|
||||
if (cn_rtp_available && ran_rtp_available)
|
||||
return osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, cl->rtp[RTP_TO_RAN]);
|
||||
else
|
||||
return call_leg_ensure_ci(msc_a->cc.call_leg, RTP_TO_RAN, cc_trans->callref, cc_trans, NULL, NULL);
|
||||
else if (cn_rtp_available)
|
||||
return osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, cl->rtp[RTP_TO_CN]);
|
||||
/* Otherwise wait for MGCP response and continue from there. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msc_a_try_call_assignment(struct gsm_trans *cc_trans)
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <osmocom/msc/call_leg.h>
|
||||
#include <osmocom/msc/rtp_stream.h>
|
||||
#include <osmocom/msc/mncc_call.h>
|
||||
#include <osmocom/msc/codec_sdp_cc_t9n.h>
|
||||
|
||||
struct osmo_fsm msc_ho_fsm;
|
||||
|
||||
|
@ -563,7 +564,7 @@ static int msc_ho_start_inter_msc_call_forwarding(struct msc_a *msc_a, struct ms
|
|||
|
||||
/* Backup old cell's RTP IP:port and codec data */
|
||||
msc_a->ho.old_cell.ran_remote_rtp = rtp_to_ran->remote;
|
||||
msc_a->ho.old_cell.codec = rtp_to_ran->codec;
|
||||
msc_a->ho.old_cell.codecs = rtp_to_ran->codecs;
|
||||
|
||||
/* Blindly taken over from an MNCC trace of existing code: send an all-zero CCCAP: */
|
||||
outgoing_call_req.fields |= MNCC_F_CCCAP;
|
||||
|
@ -700,7 +701,7 @@ static void msc_ho_rtp_switch_to_new_cell(struct msc_a *msc_a)
|
|||
|
||||
/* Backup old cell's RTP IP:port and codec data */
|
||||
msc_a->ho.old_cell.ran_remote_rtp = rtp_to_ran->remote;
|
||||
msc_a->ho.old_cell.codec = rtp_to_ran->codec;
|
||||
msc_a->ho.old_cell.codecs = rtp_to_ran->codecs;
|
||||
|
||||
LOG_HO(msc_a, LOGL_DEBUG, "Switching RTP stream to new cell: from " OSMO_SOCKADDR_STR_FMT " to " OSMO_SOCKADDR_STR_FMT "\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(&msc_a->ho.old_cell.ran_remote_rtp),
|
||||
|
@ -719,9 +720,14 @@ static void msc_ho_rtp_switch_to_new_cell(struct msc_a *msc_a)
|
|||
|
||||
/* Switch over to the new peer */
|
||||
rtp_stream_set_remote_addr(rtp_to_ran, &msc_a->ho.new_cell.ran_remote_rtp);
|
||||
if (msc_a->ho.new_cell.codec_present)
|
||||
rtp_stream_set_codec(rtp_to_ran, msc_a->ho.new_cell.codec);
|
||||
else
|
||||
if (msc_a->ho.new_cell.codec_present) {
|
||||
struct sdp_audio_codecs codecs = {};
|
||||
if (!sdp_audio_codecs_add_mgcp_codec(&codecs, msc_a->ho.new_cell.codec)) {
|
||||
LOG_HO(msc_a, LOGL_ERROR,
|
||||
"Cannot resolve codec: %s\n", osmo_mgcpc_codec_name(msc_a->ho.new_cell.codec));
|
||||
} else
|
||||
rtp_stream_set_codecs(rtp_to_ran, &codecs);
|
||||
} else
|
||||
LOG_HO(msc_a, LOGL_ERROR, "No codec is set\n");
|
||||
rtp_stream_commit(rtp_to_ran);
|
||||
}
|
||||
|
@ -761,7 +767,7 @@ static void msc_ho_rtp_rollback_to_old_cell(struct msc_a *msc_a)
|
|||
|
||||
/* Switch back to the old cell */
|
||||
rtp_stream_set_remote_addr(rtp_to_ran, &msc_a->ho.old_cell.ran_remote_rtp);
|
||||
rtp_stream_set_codec(rtp_to_ran, msc_a->ho.old_cell.codec);
|
||||
rtp_stream_set_codecs(rtp_to_ran, &msc_a->ho.old_cell.codecs);
|
||||
rtp_stream_commit(rtp_to_ran);
|
||||
}
|
||||
|
||||
|
|
|
@ -454,9 +454,12 @@ static int msc_t_patch_and_send_ho_request_ack(struct msc_t *msc_t, const struct
|
|||
if (r->codec_present) {
|
||||
LOG_MSC_T(msc_t, LOGL_DEBUG, "From Handover Request Ack, got %s\n",
|
||||
osmo_mgcpc_codec_name(r->codec));
|
||||
rtp_stream_set_codec(rtp_ran, r->codec);
|
||||
if (!rtp_stream_set_codecs_from_mgcp_codec(rtp_ran, r->codec)) {
|
||||
LOG_MSC_T(msc_t, LOGL_ERROR, "Cannot resolve codec in Handover Request Ack: %s\n",
|
||||
osmo_mgcpc_codec_name(r->codec));
|
||||
}
|
||||
if (rtp_cn)
|
||||
rtp_stream_set_codec(rtp_cn, r->codec);
|
||||
rtp_stream_set_codecs_from_mgcp_codec(rtp_cn, r->codec);
|
||||
} else {
|
||||
LOG_MSC_T(msc_t, LOGL_DEBUG, "No codec in Handover Request Ack\n");
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <osmocom/msc/transaction.h>
|
||||
#include <osmocom/msc/call_leg.h>
|
||||
#include <osmocom/msc/rtp_stream.h>
|
||||
#include <osmocom/msc/codec_sdp_cc_t9n.h>
|
||||
|
||||
#define LOG_RTPS(rtps, level, fmt, args...) \
|
||||
LOGPFSML(rtps->fi, level, fmt, ##args)
|
||||
|
@ -78,10 +79,10 @@ void rtp_stream_update_id(struct rtp_stream *rtps)
|
|||
OSMO_STRBUF_PRINTF(sb, ":no-remote-port");
|
||||
else if (!rtps->remote_sent_to_mgw)
|
||||
OSMO_STRBUF_PRINTF(sb, ":remote-port-not-sent");
|
||||
if (!rtps->codec_known)
|
||||
OSMO_STRBUF_PRINTF(sb, ":no-codec");
|
||||
else if (!rtps->codec_sent_to_mgw)
|
||||
OSMO_STRBUF_PRINTF(sb, ":codec-not-sent");
|
||||
if (!rtps->codecs_known)
|
||||
OSMO_STRBUF_PRINTF(sb, ":no-codecs");
|
||||
else if (!rtps->codecs_sent_to_mgw)
|
||||
OSMO_STRBUF_PRINTF(sb, ":codecs-not-sent");
|
||||
if (rtps->use_osmux) {
|
||||
if (rtps->remote_osmux_cid < 0)
|
||||
OSMO_STRBUF_PRINTF(sb, ":no-remote-osmux-cid");
|
||||
|
@ -141,7 +142,7 @@ static void check_established(struct rtp_stream *rtps)
|
|||
&& osmo_sockaddr_str_is_nonzero(&rtps->remote)
|
||||
&& rtps->remote_sent_to_mgw
|
||||
&& (!rtps->use_osmux || rtps->remote_osmux_cid_sent_to_mgw)
|
||||
&& rtps->codec_known)
|
||||
&& rtps->codecs_known)
|
||||
rtp_stream_state_chg(rtps, RTP_STREAM_ST_ESTABLISHED);
|
||||
}
|
||||
|
||||
|
@ -171,14 +172,14 @@ static void rtp_stream_fsm_establishing_established(struct osmo_fsm_inst *fi, ui
|
|||
osmo_fsm_inst_dispatch(fi->proc.parent, CALL_LEG_EV_RTP_STREAM_ADDR_AVAILABLE, rtps);
|
||||
check_established(rtps);
|
||||
|
||||
if ((!rtps->remote_sent_to_mgw || !rtps->codec_sent_to_mgw)
|
||||
if ((!rtps->remote_sent_to_mgw || !rtps->codecs_sent_to_mgw)
|
||||
&& osmo_sockaddr_str_is_nonzero(&rtps->remote)
|
||||
&& (!rtps->use_osmux || rtps->remote_osmux_cid_sent_to_mgw)
|
||||
&& rtps->codec_known) {
|
||||
&& rtps->codecs_known) {
|
||||
LOG_RTPS(rtps, LOGL_DEBUG,
|
||||
"local ip:port set;%s%s%s triggering MDCX to send the new settings\n",
|
||||
(!rtps->remote_sent_to_mgw)? " remote ip:port not yet sent," : "",
|
||||
(!rtps->codec_sent_to_mgw)? " codec not yet sent," : "",
|
||||
(!rtps->codecs_sent_to_mgw)? " codecs not yet sent," : "",
|
||||
(rtps->use_osmux && !rtps->remote_osmux_cid_sent_to_mgw) ? "Osmux CID not yet sent,": "");
|
||||
rtp_stream_do_mdcx(rtps);
|
||||
}
|
||||
|
@ -192,7 +193,7 @@ static void rtp_stream_fsm_establishing_established(struct osmo_fsm_inst *fi, ui
|
|||
case RTP_STREAM_EV_CRCX_FAIL:
|
||||
case RTP_STREAM_EV_MDCX_FAIL:
|
||||
rtps->remote_sent_to_mgw = false;
|
||||
rtps->codec_sent_to_mgw = false;
|
||||
rtps->codecs_sent_to_mgw = false;
|
||||
rtps->remote_osmux_cid_sent_to_mgw = false;
|
||||
rtp_stream_update_id(rtps);
|
||||
rtp_stream_state_chg(rtps, RTP_STREAM_ST_DISCARDING);
|
||||
|
@ -310,10 +311,25 @@ static int rtp_stream_do_mgcp_verb(struct rtp_stream *rtps, enum mgcp_verb verb,
|
|||
if (verb == MGCP_VERB_CRCX)
|
||||
verb_info.conn_mode = rtps->crcx_conn_mode;
|
||||
|
||||
if (rtps->codec_known) {
|
||||
verb_info.codecs[0] = rtps->codec;
|
||||
verb_info.codecs_len = 1;
|
||||
rtps->codec_sent_to_mgw = true;
|
||||
if (rtps->codecs_known) {
|
||||
/* Send the list of codecs to the MGW. Ideally we would just feed the SDP directly, but for legacy
|
||||
* reasons we still need to translate to a struct mgcp_conn_peer representation to send it. */
|
||||
struct sdp_audio_codec *codec;
|
||||
int i = 0;
|
||||
foreach_sdp_audio_codec(codec, &rtps->codecs) {
|
||||
const struct codec_mapping *m = codec_mapping_by_subtype_name(codec->subtype_name);
|
||||
if (!m)
|
||||
continue;
|
||||
verb_info.codecs[i] = m->mgcp;
|
||||
verb_info.ptmap[i] = (struct ptmap){
|
||||
.codec = m->mgcp,
|
||||
.pt = codec->payload_type,
|
||||
};
|
||||
i++;
|
||||
verb_info.codecs_len = i;
|
||||
verb_info.ptmap_len = i;
|
||||
}
|
||||
rtps->codecs_sent_to_mgw = true;
|
||||
}
|
||||
if (osmo_sockaddr_str_is_nonzero(&rtps->remote)) {
|
||||
int rc = osmo_strlcpy(verb_info.addr, rtps->remote.ip, sizeof(verb_info.addr));
|
||||
|
@ -361,43 +377,75 @@ void rtp_stream_release(struct rtp_stream *rtps)
|
|||
* least one of them has not yet been sent to the MGW in a previous CRCX or MDCX. */
|
||||
int rtp_stream_commit(struct rtp_stream *rtps)
|
||||
{
|
||||
if (!rtps->ci) {
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "Not committing: no MGW endpoint CI set up\n");
|
||||
return -1;
|
||||
}
|
||||
if (!osmo_sockaddr_str_is_nonzero(&rtps->remote)) {
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "Not committing: no remote RTP address known\n");
|
||||
return -1;
|
||||
}
|
||||
if (!rtps->codec_known) {
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "Not committing: no codec known\n");
|
||||
if (!rtps->codecs_known) {
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "Not committing: no codecs known\n");
|
||||
return -1;
|
||||
}
|
||||
if (rtps->remote_sent_to_mgw && rtps->codec_sent_to_mgw) {
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "Not committing: both remote RTP address and codec already set up at MGW\n");
|
||||
if (rtps->remote_sent_to_mgw && rtps->codecs_sent_to_mgw) {
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "Not committing: both remote RTP address and codecs already set up at MGW\n");
|
||||
return 0;
|
||||
}
|
||||
if (!rtps->ci) {
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "Not committing: no MGW endpoint CI set up\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "Committing: Tx MDCX to update the MGW: updating%s%s%s\n",
|
||||
rtps->remote_sent_to_mgw ? "" : " remote-RTP-IP-port",
|
||||
rtps->codec_sent_to_mgw ? "" : " codec",
|
||||
rtps->codecs_sent_to_mgw ? "" : " codecs",
|
||||
(!rtps->use_osmux || rtps->remote_osmux_cid_sent_to_mgw) ? "" : " remote-Osmux-CID");
|
||||
return rtp_stream_do_mdcx(rtps);
|
||||
}
|
||||
|
||||
void rtp_stream_set_codec(struct rtp_stream *rtps, enum mgcp_codecs codec)
|
||||
void rtp_stream_set_codecs(struct rtp_stream *rtps, const struct sdp_audio_codecs *codecs)
|
||||
{
|
||||
if (!codecs || !codecs->count)
|
||||
return;
|
||||
if (sdp_audio_codecs_cmp(&rtps->codecs, codecs, false, true) == 0) {
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "no change: codecs already set to %s\n",
|
||||
sdp_audio_codecs_name(&rtps->codecs));
|
||||
return;
|
||||
}
|
||||
if (rtps->fi->state == RTP_STREAM_ST_ESTABLISHED)
|
||||
rtp_stream_state_chg(rtps, RTP_STREAM_ST_ESTABLISHING);
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "setting codec to %s\n", osmo_mgcpc_codec_name(codec));
|
||||
rtps->codec = codec;
|
||||
rtps->codec_known = true;
|
||||
rtps->codec_sent_to_mgw = false;
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "setting codecs to %s\n", sdp_audio_codecs_name(codecs));
|
||||
rtps->codecs = *codecs;
|
||||
rtps->codecs_known = true;
|
||||
rtps->codecs_sent_to_mgw = false;
|
||||
rtp_stream_update_id(rtps);
|
||||
}
|
||||
|
||||
/* Convenience shortcut to call rtp_stream_set_codecs() with a list of only one sdp_audio_codec record. */
|
||||
void rtp_stream_set_one_codec(struct rtp_stream *rtps, const struct sdp_audio_codec *codec)
|
||||
{
|
||||
struct sdp_audio_codecs codecs = {};
|
||||
sdp_audio_codec_add_copy(&codecs, codec);
|
||||
rtp_stream_set_codecs(rtps, &codecs);
|
||||
}
|
||||
|
||||
/* For legacy, rather use rtp_stream_set_codecs() with a full codecs list. */
|
||||
bool rtp_stream_set_codecs_from_mgcp_codec(struct rtp_stream *rtps, enum mgcp_codecs codec)
|
||||
{
|
||||
struct sdp_audio_codecs codecs = {};
|
||||
if (!sdp_audio_codecs_add_mgcp_codec(&codecs, codec))
|
||||
return false;
|
||||
rtp_stream_set_codecs(rtps, &codecs);
|
||||
return true;
|
||||
}
|
||||
|
||||
void rtp_stream_set_remote_addr(struct rtp_stream *rtps, const struct osmo_sockaddr_str *r)
|
||||
{
|
||||
if (!strcmp(rtps->remote.ip, r->ip)
|
||||
&& rtps->remote.port == r->port
|
||||
&& rtps->remote.af == r->af) {
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "remote addr already " OSMO_SOCKADDR_STR_FMT ", no change\n",
|
||||
OSMO_SOCKADDR_STR_FMT_ARGS(r));
|
||||
return;
|
||||
}
|
||||
if (rtps->fi->state == RTP_STREAM_ST_ESTABLISHED)
|
||||
rtp_stream_state_chg(rtps, RTP_STREAM_ST_ESTABLISHING);
|
||||
LOG_RTPS(rtps, LOGL_DEBUG, "setting remote addr to " OSMO_SOCKADDR_STR_FMT "\n", OSMO_SOCKADDR_STR_FMT_ARGS(r));
|
||||
|
@ -406,6 +454,13 @@ void rtp_stream_set_remote_addr(struct rtp_stream *rtps, const struct osmo_socka
|
|||
rtp_stream_update_id(rtps);
|
||||
}
|
||||
|
||||
void rtp_stream_set_remote_addr_and_codecs(struct rtp_stream *rtps, const struct sdp_msg *sdp)
|
||||
{
|
||||
rtp_stream_set_codecs(rtps, &sdp->audio_codecs);
|
||||
if (osmo_sockaddr_str_is_nonzero(&sdp->rtp))
|
||||
rtp_stream_set_remote_addr(rtps, &sdp->rtp);
|
||||
}
|
||||
|
||||
void rtp_stream_set_remote_osmux_cid(struct rtp_stream *rtps, uint8_t osmux_cid)
|
||||
{
|
||||
if (rtps->fi->state == RTP_STREAM_ST_ESTABLISHED)
|
||||
|
@ -425,7 +480,7 @@ bool rtp_stream_is_established(struct rtp_stream *rtps)
|
|||
if (rtps->fi->state != RTP_STREAM_ST_ESTABLISHED)
|
||||
return false;
|
||||
if (!rtps->remote_sent_to_mgw
|
||||
|| !rtps->codec_sent_to_mgw
|
||||
|| !rtps->codecs_sent_to_mgw
|
||||
|| (rtps->use_osmux && !rtps->remote_osmux_cid_sent_to_mgw))
|
||||
return false;
|
||||
return true;
|
||||
|
|
|
@ -30,31 +30,78 @@
|
|||
#include <osmocom/msc/debug.h>
|
||||
#include <osmocom/msc/sdp_msg.h>
|
||||
|
||||
#define CMP(a,b) (a < b? -1 : (a > b? 1 : 0))
|
||||
|
||||
/* Compare name, rate and fmtp, returning typical cmp result: 0 on match, and -1 / 1 on mismatch.
|
||||
* Do *not* compare the payload_type number.
|
||||
* If cmp_fmtp is false, do *not* compare the fmtp string; if true, compare fmtp 1:1 as strings.
|
||||
* If cmp_payload_type is false, do *not* compare the payload_type number.
|
||||
* The fmtp is only string-compared -- e.g. if AMR parameters appear in a different order, it amounts to a mismatch even
|
||||
* though all parameters are the same. */
|
||||
int sdp_audio_codec_cmp(const struct sdp_audio_codec *a, const struct sdp_audio_codec *b)
|
||||
int sdp_audio_codec_cmp(const struct sdp_audio_codec *a, const struct sdp_audio_codec *b,
|
||||
bool cmp_fmtp, bool cmp_payload_type)
|
||||
{
|
||||
int rc;
|
||||
int cmp;
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
rc = strncmp(a->subtype_name, b->subtype_name, sizeof(a->subtype_name));
|
||||
if (rc)
|
||||
return rc;
|
||||
cmp = strncmp(a->subtype_name, b->subtype_name, sizeof(a->subtype_name));
|
||||
if (cmp)
|
||||
return cmp;
|
||||
cmp = CMP(a->rate, b->rate);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
if (cmp_fmtp) {
|
||||
cmp = strncmp(a->fmtp, b->fmtp, sizeof(a->fmtp));
|
||||
if (cmp)
|
||||
return cmp;
|
||||
}
|
||||
if (cmp_payload_type) {
|
||||
cmp = CMP(a->payload_type, b->payload_type);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->rate < b->rate)
|
||||
int sdp_audio_codecs_cmp(const struct sdp_audio_codecs *a, const struct sdp_audio_codecs *b,
|
||||
bool cmp_fmtp, bool cmp_payload_type)
|
||||
{
|
||||
const struct sdp_audio_codec *codec_a;
|
||||
const struct sdp_audio_codec *codec_b;
|
||||
int cmp;
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (a->rate > b->rate)
|
||||
if (!b)
|
||||
return 1;
|
||||
|
||||
rc = strncmp(a->fmtp, b->fmtp, sizeof(a->fmtp));
|
||||
if (rc)
|
||||
return rc;
|
||||
/* The first codec is the "chosen" codec and should match. The others may appear in different order. */
|
||||
if (a->count && b->count) {
|
||||
cmp = sdp_audio_codec_cmp(&a->codec[0], &b->codec[0], cmp_fmtp, cmp_payload_type);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
}
|
||||
|
||||
cmp = CMP(a->count, b->count);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
/* See if each codec in a is also present in b */
|
||||
foreach_sdp_audio_codec(codec_a, a) {
|
||||
bool match_found = false;
|
||||
foreach_sdp_audio_codec(codec_b, b) {
|
||||
if (!sdp_audio_codec_cmp(codec_a, codec_b, cmp_fmtp, cmp_payload_type)) {
|
||||
match_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match_found)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -130,13 +177,13 @@ struct sdp_audio_codec *sdp_audio_codec_by_payload_type(struct sdp_audio_codecs
|
|||
return codec;
|
||||
}
|
||||
|
||||
/* Return a given sdp_msg's codec entry that matches the subtype_name, rate and fmtp of the given codec, or NULL if no
|
||||
* match is found. Comparison is made by sdp_audio_codec_cmp(). */
|
||||
/* Return a given sdp_msg's codec entry that matches the subtype_name and rate of the given codec, or NULL if no
|
||||
* match is found. Comparison is made by sdp_audio_codec_cmp(cmp_payload_type=false). */
|
||||
struct sdp_audio_codec *sdp_audio_codec_by_descr(struct sdp_audio_codecs *ac, const struct sdp_audio_codec *codec)
|
||||
{
|
||||
struct sdp_audio_codec *i;
|
||||
foreach_sdp_audio_codec(i, ac) {
|
||||
if (!sdp_audio_codec_cmp(i, codec))
|
||||
if (!sdp_audio_codec_cmp(i, codec, false, false))
|
||||
return i;
|
||||
}
|
||||
return NULL;
|
||||
|
@ -451,8 +498,8 @@ next_line:
|
|||
}
|
||||
|
||||
/* Leave only those codecs in 'ac_dest' that are also present in 'ac_other'.
|
||||
* The matching is made by sdp_audio_codec_cmp(), i.e. payload_type numbers are not compared and fmtp parameters are
|
||||
* compared 1:1 as plain strings.
|
||||
* The matching is made by sdp_audio_codec_cmp(cmp_payload_type=false), i.e. payload_type numbers are not compared and
|
||||
* fmtp parameters are compared 1:1 as plain strings.
|
||||
* If translate_payload_type_numbers has an effect if ac_dest and ac_other have mismatching payload_type numbers for the
|
||||
* same SDP codec descriptions. If translate_payload_type_numbers is true, take the payload_type numbers from ac_other.
|
||||
* If false, keep payload_type numbers in ac_dest unchanged. */
|
||||
|
@ -508,8 +555,11 @@ int sdp_audio_codec_name_buf(char *buf, size_t buflen, const struct sdp_audio_co
|
|||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_PRINTF(sb, "%s", codec->subtype_name);
|
||||
if (codec->rate != 8000)
|
||||
OSMO_STRBUF_PRINTF(sb, "/%u", codec->rate);
|
||||
if (codec->fmtp[0])
|
||||
OSMO_STRBUF_PRINTF(sb, ":%s", codec->fmtp);
|
||||
OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -41,6 +41,7 @@
|
|||
#include <osmocom/msc/msc_t.h>
|
||||
#include <osmocom/msc/call_leg.h>
|
||||
#include <osmocom/msc/rtp_stream.h>
|
||||
#include <osmocom/msc/codec_sdp_cc_t9n.h>
|
||||
|
||||
#include "msc_vlr_tests.h"
|
||||
|
||||
|
@ -75,6 +76,7 @@ bool bssap_clear_sent = false;
|
|||
|
||||
bool bssap_assignment_expected = false;
|
||||
bool bssap_assignment_sent = false;
|
||||
struct gsm0808_channel_type bssap_assignment_command_last_channel_type;
|
||||
bool iu_rab_assignment_expected = false;
|
||||
bool iu_rab_assignment_sent = false;
|
||||
|
||||
|
@ -82,9 +84,10 @@ uint32_t cc_to_mncc_tx_expected_msg_type = 0;
|
|||
const char *cc_to_mncc_tx_expected_imsi = NULL;
|
||||
bool cc_to_mncc_tx_confirmed = false;
|
||||
uint32_t cc_to_mncc_tx_got_callref = 0;
|
||||
char cc_to_mncc_tx_last_sdp[1024] = {};
|
||||
|
||||
enum rtp_direction expecting_crcx = -1;
|
||||
bool got_crcx = false;
|
||||
bool expecting_crcx[2] = {};
|
||||
bool got_crcx[2] = {};
|
||||
|
||||
extern int ran_dec_dtap_undup_pdisc_ctr_bin(uint8_t pdisc);
|
||||
|
||||
|
@ -306,14 +309,18 @@ static int bssap_validate_cipher_mode_cmd(const struct ran_cipher_mode_command *
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void bssap_validate_assignment_cmd()
|
||||
static void bssap_validate_assignment_cmd(const struct ran_assignment_command *assignment_command)
|
||||
{
|
||||
OSMO_ASSERT(bssap_assignment_expected);
|
||||
bssap_assignment_expected = false;
|
||||
bssap_assignment_sent = true;
|
||||
if (assignment_command->channel_type)
|
||||
bssap_assignment_command_last_channel_type = *assignment_command->channel_type;
|
||||
else
|
||||
bssap_assignment_command_last_channel_type = (struct gsm0808_channel_type){};
|
||||
}
|
||||
|
||||
static void iucs_validate_assignment_cmd()
|
||||
static void iucs_validate_assignment_cmd(const struct ran_assignment_command *assignment_command)
|
||||
{
|
||||
OSMO_ASSERT(iu_rab_assignment_expected);
|
||||
iu_rab_assignment_expected = false;
|
||||
|
@ -373,10 +380,10 @@ struct msgb *dont_ran_encode(struct osmo_fsm_inst *caller_fi, const struct ran_m
|
|||
case RAN_MSG_ASSIGNMENT_COMMAND:
|
||||
switch (ran_type) {
|
||||
case OSMO_RAT_GERAN_A:
|
||||
bssap_validate_assignment_cmd();
|
||||
bssap_validate_assignment_cmd(&ran_enc_msg->assignment_command);
|
||||
break;
|
||||
case OSMO_RAT_UTRAN_IU:
|
||||
iucs_validate_assignment_cmd();
|
||||
iucs_validate_assignment_cmd(&ran_enc_msg->assignment_command);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
|
@ -418,7 +425,7 @@ static int fake_msc_a_ran_dec(const struct ran_msg *ran_dec_msg)
|
|||
return msc_a_ran_decode_cb(g_msub->role[MSC_ROLE_A], &d, ran_dec_msg);
|
||||
}
|
||||
|
||||
void rx_from_ms(struct msgb *msg)
|
||||
void rx_from_ms(struct msgb *msg, const struct gsm0808_speech_codec_list *codec_list_bss_supported)
|
||||
{
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
struct ran_msg ran_dec_msg;
|
||||
|
@ -451,6 +458,7 @@ void rx_from_ms(struct msgb *msg)
|
|||
.compl_l3 = {
|
||||
.cell_id = &cell_id,
|
||||
.msg = msg,
|
||||
.codec_list_bss_supported = codec_list_bss_supported,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
|
@ -478,7 +486,30 @@ void ms_sends_msg(const char *hex)
|
|||
|
||||
msg = msgb_from_hex("ms_sends_msg", 1024, hex);
|
||||
msg->l1h = msg->l2h = msg->l3h = msg->data;
|
||||
rx_from_ms(msg);
|
||||
rx_from_ms(msg, NULL);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
void ms_sends_msgf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *hex;
|
||||
|
||||
va_start(ap, fmt);
|
||||
hex = talloc_vasprintf(msc_vlr_tests_ctx, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
ms_sends_msg(hex);
|
||||
talloc_free(hex);
|
||||
}
|
||||
|
||||
void ms_sends_compl_l3(const char *hex, const struct gsm0808_speech_codec_list *codec_list_bss_supported)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
msg = msgb_from_hex("ms_sends_msg", 1024, hex);
|
||||
msg->l1h = msg->l2h = msg->l3h = msg->data;
|
||||
rx_from_ms(msg, codec_list_bss_supported);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
|
@ -639,8 +670,8 @@ void clear_vlr()
|
|||
|
||||
osmo_gettimeofday_override = false;
|
||||
|
||||
expecting_crcx = -1;
|
||||
got_crcx = false;
|
||||
memset(expecting_crcx, 0, sizeof(expecting_crcx));
|
||||
memset(got_crcx, 0, sizeof(got_crcx));
|
||||
|
||||
bssap_assignment_expected = false;
|
||||
bssap_assignment_sent = false;
|
||||
|
@ -721,8 +752,18 @@ struct gsm_mncc *on_call_release_mncc_sends_to_cc_data = NULL;
|
|||
int mncc_recv(struct gsm_network *net, struct msgb *msg)
|
||||
{
|
||||
struct gsm_mncc *mncc = (void*)msg->data;
|
||||
log("MSC --> MNCC: callref 0x%x: %s", mncc->callref,
|
||||
get_mncc_name(mncc->msg_type));
|
||||
if (mncc->msg_type == MNCC_RTP_CREATE) {
|
||||
struct gsm_mncc_rtp *rtp = (void*)msg->data;
|
||||
log("MSC --> MNCC: callref 0x%x: %s\n%s", rtp->callref,
|
||||
get_mncc_name(rtp->msg_type),
|
||||
rtp->sdp);
|
||||
OSMO_STRLCPY_ARRAY(cc_to_mncc_tx_last_sdp, rtp->sdp);
|
||||
} else {
|
||||
log("MSC --> MNCC: callref 0x%x: %s\n%s", mncc->callref,
|
||||
get_mncc_name(mncc->msg_type),
|
||||
mncc->sdp);
|
||||
OSMO_STRLCPY_ARRAY(cc_to_mncc_tx_last_sdp, mncc->sdp);
|
||||
}
|
||||
|
||||
if (mncc->msg_type == MNCC_REL_IND && on_call_release_mncc_sends_to_cc_data) {
|
||||
|
||||
|
@ -834,27 +875,35 @@ struct rtp_stream fake_rtp[2] = {
|
|||
|
||||
void expect_crcx(enum rtp_direction towards)
|
||||
{
|
||||
OSMO_ASSERT(expecting_crcx == -1);
|
||||
expecting_crcx = towards;
|
||||
got_crcx = false;
|
||||
OSMO_ASSERT(!expecting_crcx[towards]);
|
||||
expecting_crcx[towards] = true;
|
||||
got_crcx[towards] = false;
|
||||
}
|
||||
|
||||
bool crcx_scheduled(enum rtp_direction towards)
|
||||
{
|
||||
return got_crcx[towards];
|
||||
}
|
||||
|
||||
/* override, requires '-Wl,--wrap=call_leg_ensure_ci' */
|
||||
int __real_call_leg_ensure_ci(struct call_leg *cl, enum rtp_direction dir, uint32_t call_id, struct gsm_trans *for_trans,
|
||||
const enum mgcp_codecs *codec_if_known, const struct osmo_sockaddr_str *remote_addr_if_known);
|
||||
const struct sdp_audio_codecs *codecs_if_known,
|
||||
const struct osmo_sockaddr_str *remote_addr_if_known);
|
||||
int __wrap_call_leg_ensure_ci(struct call_leg *cl, enum rtp_direction dir, uint32_t call_id, struct gsm_trans *for_trans,
|
||||
const enum mgcp_codecs *codec_if_known, const struct osmo_sockaddr_str *remote_addr_if_known)
|
||||
const struct sdp_audio_codecs *codecs_if_known,
|
||||
const struct osmo_sockaddr_str *remote_addr_if_known)
|
||||
{
|
||||
if (!cl->rtp[dir]) {
|
||||
log("MGW <--CRCX to %s-- MSC: callref=0x%x", rtp_direction_name(dir), call_id);
|
||||
log("MGW <--CRCX to %s-- MSC: callref=0x%x codecs=%s", rtp_direction_name(dir), call_id,
|
||||
codecs_if_known ? sdp_audio_codecs_name(codecs_if_known) : "unset");
|
||||
|
||||
OSMO_ASSERT(expecting_crcx == dir);
|
||||
expecting_crcx = -1;
|
||||
got_crcx = true;
|
||||
OSMO_ASSERT(expecting_crcx[dir]);
|
||||
expecting_crcx[dir] = false;
|
||||
got_crcx[dir] = true;
|
||||
|
||||
call_leg_ensure_rtp_alloc(cl, dir, call_id, for_trans);
|
||||
if (codec_if_known)
|
||||
rtp_stream_set_codec(cl->rtp[dir], *codec_if_known);
|
||||
if (codecs_if_known)
|
||||
rtp_stream_set_codecs(cl->rtp[dir], codecs_if_known);
|
||||
if (remote_addr_if_known && osmo_sockaddr_str_is_nonzero(remote_addr_if_known))
|
||||
rtp_stream_set_remote_addr(cl->rtp[dir], remote_addr_if_known);
|
||||
}
|
||||
|
@ -998,9 +1047,12 @@ void ms_sends_security_mode_complete()
|
|||
g_msub = NULL;
|
||||
}
|
||||
|
||||
void ms_sends_assignment_complete(enum mgcp_codecs assigned_codec)
|
||||
void ms_sends_assignment_complete(const char *sdp_codec_name)
|
||||
{
|
||||
struct ran_msg ran_dec;
|
||||
const struct codec_mapping *m = codec_mapping_by_subtype_name(sdp_codec_name);
|
||||
OSMO_ASSERT(m);
|
||||
enum mgcp_codecs assigned_codec = m->mgcp;
|
||||
|
||||
ran_dec = (struct ran_msg){
|
||||
.msg_type = RAN_MSG_ASSIGNMENT_COMPLETE,
|
||||
|
|
|
@ -55,6 +55,8 @@ extern bool _log_lines;
|
|||
#define comment_start() fprintf(stderr, "===== %s\n", __func__);
|
||||
#define comment_end() fprintf(stderr, "===== %s: SUCCESS\n\n", __func__);
|
||||
|
||||
extern void *msc_vlr_tests_ctx;
|
||||
|
||||
extern struct msub *g_msub;
|
||||
extern struct gsm_network *net;
|
||||
extern void *msgb_ctx;
|
||||
|
@ -115,6 +117,7 @@ extern uint32_t cc_to_mncc_tx_expected_msg_type;
|
|||
extern const char *cc_to_mncc_tx_expected_imsi;
|
||||
extern bool cc_to_mncc_tx_confirmed;
|
||||
extern uint32_t cc_to_mncc_tx_got_callref;
|
||||
extern char cc_to_mncc_tx_last_sdp[1024];
|
||||
|
||||
extern struct gsm_mncc *on_call_release_mncc_sends_to_cc_data;
|
||||
|
||||
|
@ -147,6 +150,7 @@ static inline void expect_release_clear(enum osmo_rat_type via_ran)
|
|||
|
||||
extern bool bssap_assignment_expected;
|
||||
extern bool bssap_assignment_sent;
|
||||
extern struct gsm0808_channel_type bssap_assignment_command_last_channel_type;
|
||||
extern bool iu_rab_assignment_expected;
|
||||
extern bool iu_rab_assignment_sent;
|
||||
|
||||
|
@ -182,10 +186,12 @@ void paging_expect_imsi(const char *imsi);
|
|||
void paging_expect_tmsi(uint32_t tmsi);
|
||||
|
||||
void ms_sends_msg(const char *hex);
|
||||
void ms_sends_msgf(const char *fmt, ...);
|
||||
void ms_sends_compl_l3(const char *hex, const struct gsm0808_speech_codec_list *codec_list_bss_supported);
|
||||
void ms_sends_classmark_update(const struct osmo_gsm48_classmark *classmark);
|
||||
void ms_sends_ciphering_mode_complete(const char *inner_nas_msg);
|
||||
void ms_sends_security_mode_complete();
|
||||
void ms_sends_assignment_complete(enum mgcp_codecs assigned_codec);
|
||||
void ms_sends_assignment_complete(const char *sdp_codec_name);
|
||||
void gsup_rx(const char *rx_hex, const char *expect_tx_hex);
|
||||
void send_sms(struct vlr_subscr *receiver,
|
||||
struct vlr_subscr *sender,
|
||||
|
@ -274,5 +280,5 @@ extern const struct timeval fake_time_start_time;
|
|||
#define EUSE_TO_MSC_USSD "0a0103"
|
||||
#define MSC_USSD_TO_EUSE "0a0103"
|
||||
|
||||
extern bool got_crcx;
|
||||
void expect_crcx(enum rtp_direction towards);
|
||||
bool crcx_scheduled(enum rtp_direction towards);
|
||||
|
|
|
@ -566,25 +566,25 @@ sdp_msg_intersection(a,b): a=ptime:20\r\n
|
|||
test_select
|
||||
|
||||
[0]
|
||||
SDP: AMR:octet-align=1,GSM,GSM-HR-08,GSM-EFR
|
||||
Select: AMR:octet-align=1
|
||||
SDP: AMR:octet-align=1,GSM,GSM-HR-08,GSM-EFR
|
||||
SDP: AMR:octet-align=1#112,GSM#3,GSM-HR-08#111,GSM-EFR#110
|
||||
Select: AMR:octet-align=1#112
|
||||
SDP: AMR:octet-align=1#112,GSM#3,GSM-HR-08#111,GSM-EFR#110
|
||||
[0] ok
|
||||
|
||||
[1]
|
||||
SDP: AMR:octet-align=1,GSM,GSM-HR-08,GSM-EFR
|
||||
Select: GSM
|
||||
SDP: GSM,AMR:octet-align=1,GSM-HR-08,GSM-EFR
|
||||
SDP: AMR:octet-align=1#112,GSM#3,GSM-HR-08#111,GSM-EFR#110
|
||||
Select: GSM#3
|
||||
SDP: GSM#3,AMR:octet-align=1#112,GSM-HR-08#111,GSM-EFR#110
|
||||
[1] ok
|
||||
|
||||
[2]
|
||||
SDP: AMR:octet-align=1,GSM,GSM-HR-08,GSM-EFR
|
||||
Select: GSM-HR-08
|
||||
SDP: GSM-HR-08,AMR:octet-align=1,GSM,GSM-EFR
|
||||
SDP: AMR:octet-align=1#112,GSM#3,GSM-HR-08#111,GSM-EFR#110
|
||||
Select: GSM-HR-08#111
|
||||
SDP: GSM-HR-08#111,AMR:octet-align=1#112,GSM#3,GSM-EFR#110
|
||||
[2] ok
|
||||
|
||||
[3]
|
||||
SDP: AMR:octet-align=1,GSM,GSM-HR-08,GSM-EFR
|
||||
Select: GSM-EFR
|
||||
SDP: GSM-EFR,AMR:octet-align=1,GSM,GSM-HR-08
|
||||
SDP: AMR:octet-align=1#112,GSM#3,GSM-HR-08#111,GSM-EFR#110
|
||||
Select: GSM-EFR#110
|
||||
SDP: GSM-EFR#110,AMR:octet-align=1#112,GSM#3,GSM-HR-08#111
|
||||
[3] ok
|
||||
|
|
Loading…
Reference in New Issue