Add initial CSD support with external MNCC
Implement and use CSD bearer service logic (with similar audio codec code): * csd_filter (codec_filter) * csd_bs (sdp_audio_codec) * csd_bs_list (sdp_audio_codecs) Related: OS#4394 Change-Id: Ide8b8321e0401dcbe35da2ec9cee0abca821d99a
This commit is contained in:
parent
64f3930c72
commit
1063213b38
|
@ -3,6 +3,8 @@ noinst_HEADERS = \
|
|||
cell_id_list.h \
|
||||
codec_filter.h \
|
||||
codec_mapping.h \
|
||||
csd_bs.h \
|
||||
csd_filter.h \
|
||||
db.h \
|
||||
debug.h \
|
||||
e_link.h \
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/* 3GPP TS 122.002 Bearer Services */
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/gsm/mncc.h>
|
||||
#include <osmocom/gsm/protocol/gsm_08_08.h>
|
||||
|
||||
enum csd_bs {
|
||||
CSD_BS_NONE,
|
||||
|
||||
/* 3.1.1.1.2 */
|
||||
CSD_BS_21_T_V110_0k3,
|
||||
CSD_BS_22_T_V110_1k2,
|
||||
CSD_BS_24_T_V110_2k4,
|
||||
CSD_BS_25_T_V110_4k8,
|
||||
CSD_BS_26_T_V110_9k6,
|
||||
|
||||
/* 3.1.1.2.2 */
|
||||
CSD_BS_21_NT_V110_0k3,
|
||||
CSD_BS_22_NT_V110_1k2,
|
||||
CSD_BS_24_NT_V110_2k4,
|
||||
CSD_BS_25_NT_V110_4k8,
|
||||
CSD_BS_26_NT_V110_9k6,
|
||||
|
||||
/* 3.1.2.1.2 */
|
||||
CSD_BS_31_T_V110_1k2,
|
||||
CSD_BS_32_T_V110_2k4,
|
||||
CSD_BS_33_T_V110_4k8,
|
||||
CSD_BS_34_T_V110_9k6,
|
||||
|
||||
CSD_BS_MAX,
|
||||
};
|
||||
|
||||
struct csd_bs_list {
|
||||
unsigned int count;
|
||||
enum csd_bs bs[CSD_BS_MAX];
|
||||
};
|
||||
|
||||
void csd_bs_list_add_bs(struct csd_bs_list *list, enum csd_bs bs);
|
||||
int csd_bs_list_to_bearer_cap(struct gsm_mncc_bearer_cap *cap, const struct csd_bs_list *list);
|
||||
void csd_bs_list_from_bearer_cap(struct csd_bs_list *list, const struct gsm_mncc_bearer_cap *cap);
|
||||
|
||||
int csd_bs_to_str_buf(char *buf, size_t buflen, enum csd_bs bs);
|
||||
char *csd_bs_to_str_c(void *ctx, enum csd_bs bs);
|
||||
const char *csd_bs_to_str(enum csd_bs bs);
|
||||
|
||||
int csd_bs_list_to_str_buf(char *buf, size_t buflen, const struct csd_bs_list *list);
|
||||
char *csd_bs_list_to_str_c(void *ctx, const struct csd_bs_list *list);
|
||||
const char *csd_bs_list_to_str(const struct csd_bs_list *list);
|
||||
|
||||
void csd_bs_list_add_bs(struct csd_bs_list *list, enum csd_bs bs);
|
||||
void csd_bs_list_remove(struct csd_bs_list *list, enum csd_bs bs);
|
||||
void csd_bs_list_intersection(struct csd_bs_list *dest, const struct csd_bs_list *other);
|
||||
|
||||
int csd_bs_list_to_gsm0808_channel_type(struct gsm0808_channel_type *ct, const struct csd_bs_list *list);
|
|
@ -0,0 +1,53 @@
|
|||
/* Filter/overlay data rates for CSD, across MS, RAN and CN limitations */
|
||||
/*
|
||||
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Oliver Smith
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/gsm/mncc.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
|
||||
#include <osmocom/msc/csd_bs.h>
|
||||
#include <osmocom/msc/sdp_msg.h>
|
||||
|
||||
/* Combine various data rate selections to obtain a resulting set allowed by
|
||||
* all of them. Members reflect the different entities/stages that select data
|
||||
* rates in CSD. Call csd_filter_run() and obtain the resulting set in
|
||||
* csd_filter.result. */
|
||||
struct csd_filter {
|
||||
/* The fixed set available on the RAN type, per definition. */
|
||||
struct csd_bs_list ran;
|
||||
/* The services advertised by the MS Bearer Capabilities */
|
||||
struct csd_bs_list ms;
|
||||
/* If known, the set 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 csd_bs_list bss;
|
||||
|
||||
/* After a channel was assigned, this reflects the chosen BS. */
|
||||
enum csd_bs assignment;
|
||||
};
|
||||
|
||||
void csd_filter_set_ran(struct csd_filter *filter, enum osmo_rat_type ran_type);
|
||||
int csd_filter_run(struct csd_filter *filter, struct sdp_msg *result, const struct sdp_msg *remote);
|
||||
|
||||
int csd_filter_to_str_buf(char *buf, size_t buflen, const struct csd_filter *filter,
|
||||
const struct sdp_msg *result, const struct sdp_msg *remote);
|
||||
char *csd_filter_to_str_c(void *ctx, const struct csd_filter *filter, const struct sdp_msg *result, const struct sdp_msg *remote);
|
||||
const char *csd_filter_to_str(const struct csd_filter *filter, const struct sdp_msg *result, const struct sdp_msg *remote);
|
|
@ -4,6 +4,8 @@
|
|||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
|
||||
#include <osmocom/msc/csd_bs.h>
|
||||
|
||||
extern const struct value_string sdp_msg_payload_type_names[];
|
||||
static inline const char *sdp_msg_payload_type_name(unsigned int payload_type)
|
||||
{ return get_value_string(sdp_msg_payload_type_names, payload_type); }
|
||||
|
@ -36,6 +38,7 @@ struct sdp_msg {
|
|||
unsigned int ptime;
|
||||
enum sdp_mode_e mode;
|
||||
struct sdp_audio_codecs audio_codecs;
|
||||
struct csd_bs_list bearer_services;
|
||||
};
|
||||
|
||||
#define foreach_sdp_audio_codec(/* struct sdp_audio_codec* */ CODEC, \
|
||||
|
@ -80,3 +83,5 @@ const char *sdp_audio_codecs_to_str(const struct sdp_audio_codecs *ac);
|
|||
int sdp_msg_to_str_buf(char *buf, size_t buflen, const struct sdp_msg *sdp);
|
||||
char *sdp_msg_to_str_c(void *ctx, const struct sdp_msg *sdp);
|
||||
const char *sdp_msg_to_str(const struct sdp_msg *sdp);
|
||||
|
||||
void sdp_audio_codecs_set_csd(struct sdp_audio_codecs *ac);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <osmocom/msc/msc_a.h>
|
||||
#include <osmocom/msc/debug.h>
|
||||
#include <osmocom/msc/codec_filter.h>
|
||||
#include <osmocom/msc/csd_filter.h>
|
||||
#include <osmocom/gsm/gsm0411_smc.h>
|
||||
#include <osmocom/gsm/gsm0411_smr.h>
|
||||
|
||||
|
@ -107,8 +108,9 @@ struct gsm_trans {
|
|||
struct osmo_lcls *lcls;
|
||||
/* SDP as last received from the remote call leg. */
|
||||
struct sdp_msg remote;
|
||||
/* Track codec choices from BSS and remote call leg */
|
||||
/* Track codec/CSD choices from BSS and remote call leg */
|
||||
struct codec_filter codecs;
|
||||
struct csd_filter csd;
|
||||
/* Resulting choice from codecs/bearer services and the
|
||||
* local RTP address to be sent to the remote call leg. */
|
||||
struct sdp_msg local;
|
||||
|
|
|
@ -29,6 +29,8 @@ libmsc_a_SOURCES = \
|
|||
cell_id_list.c \
|
||||
codec_filter.c \
|
||||
codec_mapping.c \
|
||||
csd_bs.c \
|
||||
csd_filter.c \
|
||||
sccp_ran.c \
|
||||
msc_vty.c \
|
||||
db.c \
|
||||
|
|
|
@ -0,0 +1,477 @@
|
|||
/* 3GPP TS 122.002 Bearer Services */
|
||||
/*
|
||||
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Oliver Smith
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/msc/csd_bs.h>
|
||||
#include <osmocom/msc/debug.h>
|
||||
|
||||
/* csd_bs related below */
|
||||
|
||||
struct csd_bs_map {
|
||||
/* BS number (20, 21, ...) */
|
||||
unsigned int num;
|
||||
/* Access Structure (1: asynchronous, 0: synchronous) */
|
||||
bool async;
|
||||
/* QoS Attribute (1: transparent, 0: non-transparent) */
|
||||
bool transp;
|
||||
/* Rate Adaption (V110, V120 etc.) */
|
||||
enum gsm48_bcap_ra ra;
|
||||
/* Fixed Network User Rate */
|
||||
unsigned int rate;
|
||||
};
|
||||
|
||||
static const struct csd_bs_map bs_map[] = {
|
||||
/* 3.1.1.1.2 */
|
||||
[CSD_BS_21_T_V110_0k3] = {
|
||||
.num = 21,
|
||||
.async = true,
|
||||
.transp = true,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 300,
|
||||
},
|
||||
[CSD_BS_22_T_V110_1k2] = {
|
||||
.num = 22,
|
||||
.async = true,
|
||||
.transp = true,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 1200,
|
||||
},
|
||||
[CSD_BS_24_T_V110_2k4] = {
|
||||
.num = 24,
|
||||
.async = true,
|
||||
.transp = true,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 2400,
|
||||
},
|
||||
[CSD_BS_25_T_V110_4k8] = {
|
||||
.num = 25,
|
||||
.async = true,
|
||||
.transp = true,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 4800,
|
||||
},
|
||||
[CSD_BS_26_T_V110_9k6] = {
|
||||
.num = 26,
|
||||
.async = true,
|
||||
.transp = true,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 9600,
|
||||
},
|
||||
|
||||
/* 3.1.1.2.2 */
|
||||
[CSD_BS_21_NT_V110_0k3] = {
|
||||
.num = 21,
|
||||
.async = true,
|
||||
.transp = false,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 300,
|
||||
},
|
||||
[CSD_BS_22_NT_V110_1k2] = {
|
||||
.num = 22,
|
||||
.async = true,
|
||||
.transp = false,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 1200,
|
||||
},
|
||||
[CSD_BS_24_NT_V110_2k4] = {
|
||||
.num = 24,
|
||||
.async = true,
|
||||
.transp = false,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 2400,
|
||||
},
|
||||
[CSD_BS_25_NT_V110_4k8] = {
|
||||
.num = 25,
|
||||
.async = true,
|
||||
.transp = false,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 4800,
|
||||
},
|
||||
[CSD_BS_26_NT_V110_9k6] = {
|
||||
.num = 26,
|
||||
.async = true,
|
||||
.transp = false,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 9600,
|
||||
},
|
||||
|
||||
/* 3.1.2.1.2 */
|
||||
[CSD_BS_31_T_V110_1k2] = {
|
||||
.num = 31,
|
||||
.async = false,
|
||||
.transp = true,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 1200,
|
||||
},
|
||||
[CSD_BS_32_T_V110_2k4] = {
|
||||
.num = 32,
|
||||
.async = false,
|
||||
.transp = true,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 2400,
|
||||
},
|
||||
[CSD_BS_33_T_V110_4k8] = {
|
||||
.num = 33,
|
||||
.async = false,
|
||||
.transp = true,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 4800,
|
||||
},
|
||||
[CSD_BS_34_T_V110_9k6] = {
|
||||
.num = 34,
|
||||
.async = false,
|
||||
.transp = true,
|
||||
.ra = GSM48_BCAP_RA_V110_X30,
|
||||
.rate = 9600,
|
||||
},
|
||||
};
|
||||
|
||||
osmo_static_assert(ARRAY_SIZE(bs_map) == CSD_BS_MAX, _invalid_size_bs_map);
|
||||
|
||||
bool csd_bs_is_transp(enum csd_bs bs)
|
||||
{
|
||||
return bs_map[bs].transp;
|
||||
}
|
||||
|
||||
/* Short single-line representation, convenient for logging.
|
||||
* Like "BS25NT" */
|
||||
int csd_bs_to_str_buf(char *buf, size_t buflen, enum csd_bs bs)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
const struct csd_bs_map *map = &bs_map[bs];
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "BS%u%s",
|
||||
map->num,
|
||||
map->transp ? "T" : "NT");
|
||||
|
||||
if (map->ra != GSM48_BCAP_RA_V110_X30)
|
||||
OSMO_STRBUF_PRINTF(sb, "-RA=%d", map->ra);
|
||||
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *csd_bs_to_str_c(void *ctx, enum csd_bs bs)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 32, "csd_bs_to_str_c-ERROR", csd_bs_to_str_buf, bs)
|
||||
}
|
||||
|
||||
const char *csd_bs_to_str(enum csd_bs bs)
|
||||
{
|
||||
return csd_bs_to_str_c(OTC_SELECT, bs);
|
||||
}
|
||||
|
||||
static int csd_bs_to_gsm0808_data_rate_transp(enum csd_bs bs)
|
||||
{
|
||||
switch (bs_map[bs].rate) {
|
||||
case 1200:
|
||||
return GSM0808_DATA_RATE_TRANSP_1k2;
|
||||
case 2400:
|
||||
return GSM0808_DATA_RATE_TRANSP_2k4;
|
||||
case 4800:
|
||||
return GSM0808_DATA_RATE_TRANSP_4k8;
|
||||
case 9600:
|
||||
return GSM0808_DATA_RATE_TRANSP_9k6;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int csd_bs_to_gsm0808_data_rate_non_transp(enum csd_bs bs)
|
||||
{
|
||||
uint16_t rate = bs_map[bs].rate;
|
||||
|
||||
if (rate < 6000)
|
||||
return GSM0808_DATA_RATE_NON_TRANSP_6k0;
|
||||
if (rate < 12000)
|
||||
return GSM0808_DATA_RATE_NON_TRANSP_12k0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int csd_bs_to_gsm0808_data_rate_non_transp_allowed(enum csd_bs bs)
|
||||
{
|
||||
uint16_t rate = bs_map[bs].rate;
|
||||
|
||||
if (rate < 6000)
|
||||
return GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_6k0;
|
||||
if (rate < 12000)
|
||||
return GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_12k0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
enum csd_bs csd_bs_from_bearer_cap(const struct gsm_mncc_bearer_cap *cap, bool transp)
|
||||
{
|
||||
enum gsm48_bcap_ra ra = cap->data.rate_adaption;
|
||||
enum gsm48_bcap_user_rate rate = cap->data.user_rate;
|
||||
bool async = cap->data.async;
|
||||
|
||||
if (ra == GSM48_BCAP_RA_V110_X30 && async && transp) {
|
||||
switch (rate) {
|
||||
case GSM48_BCAP_UR_300:
|
||||
return CSD_BS_21_T_V110_0k3;
|
||||
case GSM48_BCAP_UR_1200:
|
||||
return CSD_BS_22_T_V110_1k2;
|
||||
case GSM48_BCAP_UR_2400:
|
||||
return CSD_BS_24_T_V110_2k4;
|
||||
case GSM48_BCAP_UR_4800:
|
||||
return CSD_BS_25_T_V110_4k8;
|
||||
case GSM48_BCAP_UR_9600:
|
||||
return CSD_BS_26_T_V110_9k6;
|
||||
default:
|
||||
return CSD_BS_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ra == GSM48_BCAP_RA_V110_X30 && async && !transp) {
|
||||
switch (rate) {
|
||||
case GSM48_BCAP_UR_300:
|
||||
return CSD_BS_21_NT_V110_0k3;
|
||||
case GSM48_BCAP_UR_1200:
|
||||
return CSD_BS_22_NT_V110_1k2;
|
||||
case GSM48_BCAP_UR_2400:
|
||||
return CSD_BS_24_NT_V110_2k4;
|
||||
case GSM48_BCAP_UR_4800:
|
||||
return CSD_BS_25_NT_V110_4k8;
|
||||
case GSM48_BCAP_UR_9600:
|
||||
return CSD_BS_26_NT_V110_9k6;
|
||||
default:
|
||||
return CSD_BS_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ra == GSM48_BCAP_RA_V110_X30 && !async && transp) {
|
||||
switch (rate) {
|
||||
case GSM48_BCAP_UR_1200:
|
||||
return CSD_BS_31_T_V110_1k2;
|
||||
case GSM48_BCAP_UR_2400:
|
||||
return CSD_BS_32_T_V110_2k4;
|
||||
case GSM48_BCAP_UR_4800:
|
||||
return CSD_BS_33_T_V110_4k8;
|
||||
case GSM48_BCAP_UR_9600:
|
||||
return CSD_BS_34_T_V110_9k6;
|
||||
default:
|
||||
return CSD_BS_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
return CSD_BS_NONE;
|
||||
}
|
||||
|
||||
/* csd_bs_list related below */
|
||||
|
||||
int csd_bs_list_to_str_buf(char *buf, size_t buflen, const struct csd_bs_list *list)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
int i;
|
||||
|
||||
if (!list->count)
|
||||
OSMO_STRBUF_PRINTF(sb, "(no-bearer-services)");
|
||||
|
||||
for (i = 0; i < list->count; i++) {
|
||||
if (i)
|
||||
OSMO_STRBUF_PRINTF(sb, ",");
|
||||
|
||||
OSMO_STRBUF_APPEND(sb, csd_bs_to_str_buf, list->bs[i]);
|
||||
}
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *csd_bs_list_to_str_c(void *ctx, const struct csd_bs_list *list)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 128, "csd_bs_list_to_str_c-ERROR", csd_bs_list_to_str_buf, list)
|
||||
}
|
||||
|
||||
const char *csd_bs_list_to_str(const struct csd_bs_list *list)
|
||||
{
|
||||
return csd_bs_list_to_str_c(OTC_SELECT, list);
|
||||
}
|
||||
|
||||
bool csd_bs_list_has_bs(const struct csd_bs_list *list, enum csd_bs bs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < list->count; i++) {
|
||||
if (list->bs[i] == bs)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void csd_bs_list_add_bs(struct csd_bs_list *list, enum csd_bs bs)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!bs)
|
||||
return;
|
||||
|
||||
for (i = 0; i < list->count; i++) {
|
||||
if (list->bs[i] == bs)
|
||||
return;
|
||||
}
|
||||
|
||||
list->bs[i] = bs;
|
||||
list->count++;
|
||||
}
|
||||
|
||||
void csd_bs_list_remove(struct csd_bs_list *list, enum csd_bs bs)
|
||||
{
|
||||
int i;
|
||||
bool found = false;
|
||||
|
||||
for (i = 0; i < list->count; i++) {
|
||||
if (list->bs[i] == bs) {
|
||||
found = true;
|
||||
list->count--;
|
||||
continue;
|
||||
}
|
||||
if (i && found)
|
||||
list->bs[i-1] = list->bs[i];
|
||||
}
|
||||
}
|
||||
|
||||
void csd_bs_list_intersection(struct csd_bs_list *dest, const struct csd_bs_list *other)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dest->count; i++) {
|
||||
if (csd_bs_list_has_bs(other, dest->bs[i]))
|
||||
continue;
|
||||
csd_bs_list_remove(dest, dest->bs[i]);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
int csd_bs_list_to_gsm0808_channel_type(struct gsm0808_channel_type *ct, const struct csd_bs_list *list)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
*ct = (struct gsm0808_channel_type){
|
||||
.ch_indctr = GSM0808_CHAN_DATA,
|
||||
};
|
||||
|
||||
OSMO_ASSERT(list->count);
|
||||
|
||||
if (csd_bs_is_transp(list->bs[0])) {
|
||||
ct->data_transparent = true;
|
||||
ct->data_rate = csd_bs_to_gsm0808_data_rate_transp(list->bs[0]);
|
||||
} else {
|
||||
ct->data_rate = csd_bs_to_gsm0808_data_rate_non_transp(list->bs[0]);
|
||||
}
|
||||
|
||||
if (ct->data_rate < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Other possible data rates allowed (3GPP TS 48.008 § 3.2.2.11, 5a) */
|
||||
if (!ct->data_transparent && list->count > 1) {
|
||||
for (i = 1; i < list->count; i++) {
|
||||
if (!csd_bs_is_transp(list->bs[i]))
|
||||
continue;
|
||||
|
||||
rc = csd_bs_to_gsm0808_data_rate_non_transp_allowed(list->bs[i]);
|
||||
if (rc < 0) {
|
||||
LOGP(DMSC, LOGL_DEBUG, "Failed to convert %s to allowed r i/f rate\n",
|
||||
csd_bs_to_str(list->bs[i]));
|
||||
continue;
|
||||
}
|
||||
|
||||
ct->data_rate_allowed |= rc;
|
||||
}
|
||||
if (ct->data_rate_allowed)
|
||||
ct->data_rate_allowed_is_set = true;
|
||||
}
|
||||
|
||||
ct->ch_rate_type = GSM0808_SPEECH_FULL_BM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int csd_bs_list_to_bearer_cap(struct gsm_mncc_bearer_cap *cap, const struct csd_bs_list *list)
|
||||
{
|
||||
*cap = (struct gsm_mncc_bearer_cap){
|
||||
.transfer = GSM_MNCC_BCAP_UNR_DIG,
|
||||
};
|
||||
enum csd_bs bs;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < list->count; i++) {
|
||||
bs = list->bs[i];
|
||||
|
||||
cap->data.rate_adaption = GSM48_BCAP_RA_V110_X30;
|
||||
cap->data.async = bs_map[bs].async;
|
||||
cap->data.transp = bs_map[bs].transp;
|
||||
|
||||
switch (bs_map[bs].rate) {
|
||||
case 300:
|
||||
cap->data.user_rate = GSM48_BCAP_UR_300;
|
||||
break;
|
||||
case 1200:
|
||||
cap->data.user_rate = GSM48_BCAP_UR_1200;
|
||||
break;
|
||||
case 2400:
|
||||
cap->data.user_rate = GSM48_BCAP_UR_2400;
|
||||
break;
|
||||
case 4800:
|
||||
cap->data.user_rate = GSM48_BCAP_UR_4800;
|
||||
break;
|
||||
case 9600:
|
||||
cap->data.user_rate = GSM48_BCAP_UR_9600;
|
||||
break;
|
||||
}
|
||||
|
||||
/* FIXME: handle more than one list entry */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void csd_bs_list_from_bearer_cap(struct csd_bs_list *list, const struct gsm_mncc_bearer_cap *cap)
|
||||
{
|
||||
*list = (struct csd_bs_list){};
|
||||
|
||||
switch (cap->data.transp) {
|
||||
case GSM48_BCAP_TR_TRANSP:
|
||||
csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, true));
|
||||
break;
|
||||
case GSM48_BCAP_TR_RLP: /* NT */
|
||||
csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, false));
|
||||
break;
|
||||
case GSM48_BCAP_TR_TR_PREF:
|
||||
csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, true));
|
||||
csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, false));
|
||||
break;
|
||||
case GSM48_BCAP_TR_RLP_PREF:
|
||||
csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, false));
|
||||
csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, true));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!list->count) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Failed to get bearer service from bearer capabilities ra=%d, async=%d,"
|
||||
" transp=%d, user_rate=%d\n", cap->data.rate_adaption, cap->data.async, cap->data.transp,
|
||||
cap->data.user_rate);
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/* Filter/overlay bearer service selections across MS, RAN and CN limitations */
|
||||
/*
|
||||
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Oliver Smith
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
|
||||
#include <osmocom/msc/csd_filter.h>
|
||||
|
||||
static void add_all_geran_bs(struct csd_bs_list *list)
|
||||
{
|
||||
/* See 3GPP TS 122.002 Bearer Services */
|
||||
/* In order of preference. TODO: make configurable */
|
||||
|
||||
/* GSM-R */
|
||||
csd_bs_list_add_bs(list, CSD_BS_24_T_V110_2k4);
|
||||
csd_bs_list_add_bs(list, CSD_BS_25_T_V110_4k8);
|
||||
csd_bs_list_add_bs(list, CSD_BS_26_T_V110_9k6);
|
||||
|
||||
/* Other */
|
||||
csd_bs_list_add_bs(list, CSD_BS_21_T_V110_0k3);
|
||||
csd_bs_list_add_bs(list, CSD_BS_22_T_V110_1k2);
|
||||
csd_bs_list_add_bs(list, CSD_BS_21_NT_V110_0k3);
|
||||
csd_bs_list_add_bs(list, CSD_BS_22_NT_V110_1k2);
|
||||
csd_bs_list_add_bs(list, CSD_BS_24_NT_V110_2k4);
|
||||
csd_bs_list_add_bs(list, CSD_BS_25_NT_V110_4k8);
|
||||
csd_bs_list_add_bs(list, CSD_BS_26_NT_V110_9k6);
|
||||
csd_bs_list_add_bs(list, CSD_BS_31_T_V110_1k2);
|
||||
csd_bs_list_add_bs(list, CSD_BS_32_T_V110_2k4);
|
||||
csd_bs_list_add_bs(list, CSD_BS_33_T_V110_4k8);
|
||||
csd_bs_list_add_bs(list, CSD_BS_34_T_V110_9k6);
|
||||
}
|
||||
|
||||
static void add_all_utran_bs(struct csd_bs_list *list)
|
||||
{
|
||||
/* See 3GPP TS 122.002 Bearer Services */
|
||||
/* In order of preference. TODO: make configurable */
|
||||
csd_bs_list_add_bs(list, CSD_BS_21_NT_V110_0k3);
|
||||
csd_bs_list_add_bs(list, CSD_BS_22_NT_V110_1k2);
|
||||
csd_bs_list_add_bs(list, CSD_BS_24_NT_V110_2k4);
|
||||
csd_bs_list_add_bs(list, CSD_BS_25_NT_V110_4k8);
|
||||
csd_bs_list_add_bs(list, CSD_BS_26_NT_V110_9k6);
|
||||
}
|
||||
|
||||
void csd_filter_set_ran(struct csd_filter *filter, enum osmo_rat_type ran_type)
|
||||
{
|
||||
filter->ran = (struct csd_bs_list){};
|
||||
|
||||
switch (ran_type) {
|
||||
default:
|
||||
case OSMO_RAT_GERAN_A:
|
||||
add_all_geran_bs(&filter->ran);
|
||||
break;
|
||||
case OSMO_RAT_UTRAN_IU:
|
||||
add_all_utran_bs(&filter->ran);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int csd_filter_run(struct csd_filter *filter, struct sdp_msg *result, const struct sdp_msg *remote)
|
||||
{
|
||||
struct csd_bs_list *r = &result->bearer_services;
|
||||
enum csd_bs a = filter->assignment;
|
||||
|
||||
*r = filter->ran;
|
||||
|
||||
if (filter->ms.count)
|
||||
csd_bs_list_intersection(r, &filter->ms);
|
||||
if (filter->bss.count)
|
||||
csd_bs_list_intersection(r, &filter->bss);
|
||||
if (remote->bearer_services.count)
|
||||
csd_bs_list_intersection(r, &remote->bearer_services);
|
||||
|
||||
/* Future: If osmo-msc were able to trigger a re-assignment [...] see
|
||||
* comment in codec_filter_run(). */
|
||||
|
||||
if (a) {
|
||||
*r = (struct csd_bs_list){};
|
||||
csd_bs_list_add_bs(r, a);
|
||||
}
|
||||
|
||||
result->audio_codecs.count = 1;
|
||||
result->audio_codecs.codec[0] = (struct sdp_audio_codec){
|
||||
.payload_type = CODEC_CLEARMODE,
|
||||
.subtype_name = "CLEARMODE",
|
||||
.rate = 8000,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int csd_filter_to_str_buf(char *buf, size_t buflen, const struct csd_filter *filter,
|
||||
const struct sdp_msg *result, const struct sdp_msg *remote)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_APPEND(sb, sdp_msg_to_str_buf, result);
|
||||
OSMO_STRBUF_PRINTF(sb, " (from:");
|
||||
|
||||
if (filter->assignment) {
|
||||
OSMO_STRBUF_PRINTF(sb, " assigned=");
|
||||
OSMO_STRBUF_APPEND(sb, csd_bs_to_str_buf, filter->assignment);
|
||||
}
|
||||
|
||||
if (remote->bearer_services.count || osmo_sockaddr_str_is_nonzero(&remote->rtp)) {
|
||||
OSMO_STRBUF_PRINTF(sb, " remote=");
|
||||
OSMO_STRBUF_APPEND(sb, sdp_msg_to_str_buf, remote);
|
||||
}
|
||||
|
||||
if (filter->ms.count) {
|
||||
OSMO_STRBUF_PRINTF(sb, " MS={");
|
||||
OSMO_STRBUF_APPEND(sb, csd_bs_list_to_str_buf, &filter->ms);
|
||||
OSMO_STRBUF_PRINTF(sb, "}");
|
||||
}
|
||||
|
||||
if (filter->bss.count) {
|
||||
OSMO_STRBUF_PRINTF(sb, " bss={");
|
||||
OSMO_STRBUF_APPEND(sb, csd_bs_list_to_str_buf, &filter->bss);
|
||||
OSMO_STRBUF_PRINTF(sb, "}");
|
||||
}
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, " RAN={");
|
||||
OSMO_STRBUF_APPEND(sb, csd_bs_list_to_str_buf, &filter->ran);
|
||||
OSMO_STRBUF_PRINTF(sb, "}");
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, ")");
|
||||
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *csd_filter_to_str_c(void *ctx, const struct csd_filter *filter, const struct sdp_msg *result, const struct sdp_msg *remote)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 128, "csd_filter_to_str_c-ERROR", csd_filter_to_str_buf, filter, result, remote)
|
||||
}
|
||||
|
||||
const char *csd_filter_to_str(const struct csd_filter *filter, const struct sdp_msg *result, const struct sdp_msg *remote)
|
||||
{
|
||||
return csd_filter_to_str_c(OTC_SELECT, filter, result, remote);
|
||||
}
|
|
@ -808,55 +808,86 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
|
|||
trans_cc_filter_set_bss(trans, trans->msc_a);
|
||||
if (setup->fields & MNCC_F_BEARER_CAP)
|
||||
trans->bearer_cap.transfer = setup->bearer_cap.transfer;
|
||||
/* sdp.remote: if SDP is included in the MNCC, take that as definitive list of remote audio codecs. */
|
||||
rx_mncc_sdp(trans, setup->msg_type, setup->sdp);
|
||||
/* 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.remote.audio_codecs.count && (setup->fields & MNCC_F_BEARER_CAP)) {
|
||||
trans->cc.remote = (struct sdp_msg){};
|
||||
trans_cc_set_remote_from_bc(trans, &setup->bearer_cap);
|
||||
LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s Bearer Cap: remote=%s\n",
|
||||
get_mncc_name(setup->msg_type), sdp_msg_to_str(&trans->cc.remote));
|
||||
|
||||
switch (trans->bearer_cap.transfer) {
|
||||
case GSM48_BCAP_ITCAP_SPEECH:
|
||||
/* sdp.remote: if SDP is included in the MNCC, take that as definitive list of remote audio codecs. */
|
||||
rx_mncc_sdp(trans, setup->msg_type, setup->sdp);
|
||||
/* 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.remote.audio_codecs.count && (setup->fields & MNCC_F_BEARER_CAP)) {
|
||||
trans->cc.remote = (struct sdp_msg){};
|
||||
trans_cc_set_remote_from_bc(trans, &setup->bearer_cap);
|
||||
LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s Bearer Cap: remote=%s\n",
|
||||
get_mncc_name(setup->msg_type), sdp_msg_to_str(&trans->cc.remote));
|
||||
}
|
||||
if (!trans->cc.remote.audio_codecs.count)
|
||||
LOG_TRANS(trans, LOGL_INFO,
|
||||
"Got no information of remote audio codecs: neither SDP nor Bearer Capability. Trying anyway.\n");
|
||||
break;
|
||||
case GSM48_BCAP_ITCAP_UNR_DIG_INF:
|
||||
sdp_audio_codecs_set_csd(&trans->cc.codecs.ms);
|
||||
break;
|
||||
default:
|
||||
LOG_TRANS(trans, LOGL_ERROR, "Handling of information transfer capability %d not implemented\n",
|
||||
trans->bearer_cap.transfer);
|
||||
}
|
||||
if (!trans->cc.remote.audio_codecs.count)
|
||||
LOG_TRANS(trans, LOGL_INFO,
|
||||
"Got no information of remote audio codecs: neither SDP nor Bearer Capability. Trying anyway.\n");
|
||||
|
||||
trans_cc_filter_run(trans);
|
||||
|
||||
/* Compose Bearer Capability information that reflects only the codecs (Speech Versions) remaining after
|
||||
* intersecting MS, BSS and remote call leg restrictions. To store in trans for later use, and to include in
|
||||
* the outgoing CC Setup message. */
|
||||
bearer_cap = (struct gsm_mncc_bearer_cap){
|
||||
.speech_ver = { -1 },
|
||||
};
|
||||
sdp_audio_codecs_to_bearer_cap(&bearer_cap, &trans->cc.local.audio_codecs);
|
||||
rc = bearer_cap_set_radio(&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;
|
||||
/* Compose Bearer Capability information that reflects only the codecs (Speech Versions) / CSD bearer services
|
||||
* remaining after intersecting MS, BSS and remote call leg restrictions. To store in trans for later use, and
|
||||
* to include in the outgoing CC Setup message. */
|
||||
switch (trans->bearer_cap.transfer) {
|
||||
case GSM48_BCAP_ITCAP_SPEECH:
|
||||
bearer_cap = (struct gsm_mncc_bearer_cap){
|
||||
.speech_ver = { -1 },
|
||||
};
|
||||
sdp_audio_codecs_to_bearer_cap(&bearer_cap, &trans->cc.local.audio_codecs);
|
||||
rc = bearer_cap_set_radio(&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. We cannot find a codec that matches both call legs. If the MGW were
|
||||
* able to transcode, we could use non-identical codecs on each conn of the MGW endpoint, but we are aiming for
|
||||
* finding a matching codec. */
|
||||
if (bearer_cap.speech_ver[0] == -1) {
|
||||
LOG_TRANS(trans, LOGL_ERROR, "%s: no codec match possible: %s\n",
|
||||
get_mncc_name(setup->msg_type),
|
||||
codec_filter_to_str(&trans->cc.codecs, &trans->cc.local, &trans->cc.remote));
|
||||
|
||||
/* 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->callref = 0;
|
||||
trans_free(trans);
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
break;
|
||||
case GSM48_BCAP_ITCAP_UNR_DIG_INF:
|
||||
if (csd_bs_list_to_bearer_cap(&bearer_cap, &trans->cc.local.bearer_services) == 0) {
|
||||
LOG_TRANS(trans, LOGL_ERROR, "Error composing Bearer Capability for CC Setup\n");
|
||||
|
||||
/* 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->callref = 0;
|
||||
trans_free(trans);
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Create a copy of the bearer capability in the transaction struct, so we can use this information later */
|
||||
trans->bearer_cap = bearer_cap;
|
||||
/* If no resulting codecs remain, error out. We cannot find a codec that matches both call legs. If the MGW were
|
||||
* able to transcode, we could use non-identical codecs on each conn of the MGW endpoint, but we are aiming for
|
||||
* finding a matching codec. */
|
||||
if (bearer_cap.speech_ver[0] == -1) {
|
||||
LOG_TRANS(trans, LOGL_ERROR, "%s: no codec match possible: %s\n",
|
||||
get_mncc_name(setup->msg_type),
|
||||
codec_filter_to_str(&trans->cc.codecs, &trans->cc.local, &trans->cc.remote));
|
||||
|
||||
/* 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->callref = 0;
|
||||
trans_free(trans);
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
gsm48_encode_bearer_cap(msg, 0, &bearer_cap);
|
||||
|
||||
/* facility */
|
||||
|
|
|
@ -640,23 +640,48 @@ static void msc_a_call_leg_ran_local_addr_available(struct msc_a *msc_a)
|
|||
trans_cc_filter_run(cc_trans);
|
||||
LOG_TRANS(cc_trans, LOGL_DEBUG, "Sending Assignment Command\n");
|
||||
|
||||
if (!cc_trans->cc.local.audio_codecs.count) {
|
||||
LOG_TRANS(cc_trans, LOGL_ERROR, "Assignment not possible, no matching codec: %s\n",
|
||||
codec_filter_to_str(&cc_trans->cc.codecs, &cc_trans->cc.local, &cc_trans->cc.remote));
|
||||
switch (cc_trans->bearer_cap.transfer) {
|
||||
case GSM48_BCAP_ITCAP_SPEECH:
|
||||
if (!cc_trans->cc.local.audio_codecs.count) {
|
||||
LOG_TRANS(cc_trans, LOGL_ERROR, "Assignment not possible, no matching codec: %s\n",
|
||||
codec_filter_to_str(&cc_trans->cc.codecs, &cc_trans->cc.local, &cc_trans->cc.remote));
|
||||
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.local.audio_codecs)) {
|
||||
LOG_MSC_A(msc_a, LOGL_ERROR, "Cannot compose Channel Type (Permitted Speech) from codecs: %s\n",
|
||||
codec_filter_to_str(&cc_trans->cc.codecs, &cc_trans->cc.local, &cc_trans->cc.remote));
|
||||
trans_free(cc_trans);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case GSM48_BCAP_ITCAP_UNR_DIG_INF:
|
||||
if (!cc_trans->cc.local.bearer_services.count) {
|
||||
LOG_TRANS(cc_trans, LOGL_ERROR, "Assignment not possible, no matching bearer service: %s\n",
|
||||
csd_filter_to_str(&cc_trans->cc.csd, &cc_trans->cc.local, &cc_trans->cc.remote));
|
||||
call_leg_release(msc_a->cc.call_leg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compose 48.008 Channel Type from the current set of bearer
|
||||
* services determined from local and remote capabilities. */
|
||||
if (csd_bs_list_to_gsm0808_channel_type(&channel_type, &cc_trans->cc.local.bearer_services)) {
|
||||
LOG_MSC_A(msc_a, LOGL_ERROR, "Cannot compose channel type from: %s\n",
|
||||
csd_filter_to_str(&cc_trans->cc.csd, &cc_trans->cc.local, &cc_trans->cc.remote));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_TRANS(cc_trans, LOGL_ERROR, "Assignment not possible for information transfer capability %d\n",
|
||||
cc_trans->bearer_cap.transfer);
|
||||
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.local.audio_codecs)) {
|
||||
LOG_MSC_A(msc_a, LOGL_ERROR, "Cannot compose Channel Type (Permitted Speech) from codecs: %s\n",
|
||||
codec_filter_to_str(&cc_trans->cc.codecs, &cc_trans->cc.local, &cc_trans->cc.remote));
|
||||
trans_free(cc_trans);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The RAN side RTP address is known, so the voice Assignment can commence. */
|
||||
/* The RAN side RTP address is known, so the voice/CSD Assignment can commence. */
|
||||
msg = (struct ran_msg){
|
||||
.msg_type = RAN_MSG_ASSIGNMENT_COMMAND,
|
||||
.assignment_command = {
|
||||
|
|
|
@ -657,6 +657,10 @@ int sdp_msg_to_str_buf(char *buf, size_t buflen, const struct sdp_msg *sdp)
|
|||
OSMO_STRBUF_PRINTF(sb, OSMO_SOCKADDR_STR_FMT, OSMO_SOCKADDR_STR_FMT_ARGS(&sdp->rtp));
|
||||
OSMO_STRBUF_PRINTF(sb, "{");
|
||||
OSMO_STRBUF_APPEND(sb, sdp_audio_codecs_to_str_buf, &sdp->audio_codecs);
|
||||
if (sdp->bearer_services.count) {
|
||||
OSMO_STRBUF_PRINTF(sb, ",");
|
||||
OSMO_STRBUF_APPEND(sb, csd_bs_list_to_str_buf, &sdp->bearer_services);
|
||||
}
|
||||
OSMO_STRBUF_PRINTF(sb, "}");
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
@ -670,3 +674,15 @@ const char *sdp_msg_to_str(const struct sdp_msg *sdp)
|
|||
{
|
||||
return sdp_msg_to_str_c(OTC_SELECT, sdp);
|
||||
}
|
||||
|
||||
void sdp_audio_codecs_set_csd(struct sdp_audio_codecs *ac)
|
||||
{
|
||||
*ac = (struct sdp_audio_codecs){
|
||||
.count = 1,
|
||||
.codec = {{
|
||||
.payload_type = 120,
|
||||
.subtype_name = "CLEARMODE",
|
||||
.rate = 8000,
|
||||
}},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -24,32 +24,52 @@
|
|||
|
||||
#include <osmocom/msc/transaction_cc.h>
|
||||
#include <osmocom/msc/codec_filter.h>
|
||||
#include <osmocom/msc/csd_filter.h>
|
||||
|
||||
void trans_cc_filter_init(struct gsm_trans *trans)
|
||||
{
|
||||
trans->cc.codecs = (struct codec_filter){};
|
||||
trans->cc.csd = (struct csd_filter){};
|
||||
}
|
||||
|
||||
void trans_cc_filter_set_ran(struct gsm_trans *trans, enum osmo_rat_type ran_type)
|
||||
{
|
||||
codec_filter_set_ran(&trans->cc.codecs, ran_type);
|
||||
csd_filter_set_ran(&trans->cc.csd, ran_type);
|
||||
}
|
||||
|
||||
void trans_cc_filter_set_bss(struct gsm_trans *trans, struct msc_a *msc_a)
|
||||
{
|
||||
codec_filter_set_bss(&trans->cc.codecs, &msc_a->cc.compl_l3_codec_list_bss_supported);
|
||||
|
||||
/* For CSD, there is no list of supported bearer services passed in
|
||||
* Complete Layer 3. TODO: make it configurable? */
|
||||
}
|
||||
|
||||
void trans_cc_filter_run(struct gsm_trans *trans)
|
||||
{
|
||||
codec_filter_run(&trans->cc.codecs, &trans->cc.local, &trans->cc.remote);
|
||||
LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n",
|
||||
codec_filter_to_str(&trans->cc.codecs, &trans->cc.local, &trans->cc.remote));
|
||||
switch (trans->bearer_cap.transfer) {
|
||||
case GSM48_BCAP_ITCAP_SPEECH:
|
||||
codec_filter_run(&trans->cc.codecs, &trans->cc.local, &trans->cc.remote);
|
||||
LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n",
|
||||
codec_filter_to_str(&trans->cc.codecs, &trans->cc.local, &trans->cc.remote));
|
||||
break;
|
||||
case GSM48_BCAP_ITCAP_UNR_DIG_INF:
|
||||
csd_filter_run(&trans->cc.csd, &trans->cc.local, &trans->cc.remote);
|
||||
LOG_TRANS(trans, LOGL_DEBUG, "codec/BS: %s\n",
|
||||
csd_filter_to_str(&trans->cc.csd, &trans->cc.local, &trans->cc.remote));
|
||||
break;
|
||||
default:
|
||||
LOG_TRANS(trans, LOGL_ERROR, "Handling of information transfer capability %d not implemented\n",
|
||||
trans->bearer_cap.transfer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void trans_cc_filter_set_ms_from_bc(struct gsm_trans *trans, const struct gsm_mncc_bearer_cap *bcap)
|
||||
{
|
||||
trans->cc.codecs.ms = (struct sdp_audio_codecs){0};
|
||||
trans->cc.csd.ms = (struct csd_bs_list){0};
|
||||
|
||||
if (!bcap)
|
||||
return;
|
||||
|
@ -58,6 +78,10 @@ void trans_cc_filter_set_ms_from_bc(struct gsm_trans *trans, const struct gsm_mn
|
|||
case GSM48_BCAP_ITCAP_SPEECH:
|
||||
sdp_audio_codecs_from_bearer_cap(&trans->cc.codecs.ms, bcap);
|
||||
break;
|
||||
case GSM48_BCAP_ITCAP_UNR_DIG_INF:
|
||||
sdp_audio_codecs_set_csd(&trans->cc.codecs.ms);
|
||||
csd_bs_list_from_bearer_cap(&trans->cc.csd.ms, bcap);
|
||||
break;
|
||||
default:
|
||||
LOG_TRANS(trans, LOGL_ERROR, "Handling of information transfer capability %d not implemented\n",
|
||||
bcap->transfer);
|
||||
|
@ -68,6 +92,7 @@ void trans_cc_filter_set_ms_from_bc(struct gsm_trans *trans, const struct gsm_mn
|
|||
void trans_cc_set_remote_from_bc(struct gsm_trans *trans, const struct gsm_mncc_bearer_cap *bcap)
|
||||
{
|
||||
trans->cc.remote.audio_codecs = (struct sdp_audio_codecs){0};
|
||||
trans->cc.remote.bearer_services = (struct csd_bs_list){0};
|
||||
|
||||
if (!bcap)
|
||||
return;
|
||||
|
@ -76,6 +101,10 @@ void trans_cc_set_remote_from_bc(struct gsm_trans *trans, const struct gsm_mncc_
|
|||
case GSM48_BCAP_ITCAP_SPEECH:
|
||||
sdp_audio_codecs_from_bearer_cap(&trans->cc.remote.audio_codecs, bcap);
|
||||
break;
|
||||
case GSM48_BCAP_ITCAP_UNR_DIG_INF:
|
||||
sdp_audio_codecs_set_csd(&trans->cc.remote.audio_codecs);
|
||||
csd_bs_list_from_bearer_cap(&trans->cc.remote.bearer_services, bcap);
|
||||
break;
|
||||
default:
|
||||
LOG_TRANS(trans, LOGL_ERROR, "Handling of information transfer capability %d not implemented\n",
|
||||
bcap->transfer);
|
||||
|
|
Loading…
Reference in New Issue