diff options
author | Philipp Maier <pmaier@sysmocom.de> | 2017-06-02 17:48:37 +0200 |
---|---|---|
committer | Neels Hofmeyr <nhofmeyr@sysmocom.de> | 2017-06-18 17:50:08 +0200 |
commit | 926f84a2a33432091fffc22a6166fbb6df2920e5 (patch) | |
tree | 010e7638a876292f949cceebd59d7b23833fff44 /openbsc/src | |
parent | 9f15ac5ba06c91086626eff4855848ee80b56d44 (diff) |
fixup for: aoip: signal channel type to BSC
The channel type and the speech codec element is now
signalled to the BSC. The BSC checks both fields and
select a codec by its preference. The choosen speech
codec and the choosen channel (type) is returned to
the MSC. Currently the MSC ignores the return values
Diffstat (limited to 'openbsc/src')
-rw-r--r-- | openbsc/src/libmsc/a_iface.c | 35 | ||||
-rw-r--r-- | openbsc/src/osmo-bsc/osmo_bsc_audio.c | 7 | ||||
-rw-r--r-- | openbsc/src/osmo-bsc/osmo_bsc_bssap.c | 228 |
3 files changed, 204 insertions, 66 deletions
diff --git a/openbsc/src/libmsc/a_iface.c b/openbsc/src/libmsc/a_iface.c index 93dcf07ef..a8c040eb7 100644 --- a/openbsc/src/libmsc/a_iface.c +++ b/openbsc/src/libmsc/a_iface.c @@ -25,6 +25,7 @@ #include <osmocom/gsm/gsm0808.h> #include <osmocom/gsm/protocol/gsm_08_08.h> #include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/gsm0808_utils.h> #include <openbsc/debug.h> #include <openbsc/msc_ifaces.h> #include <openbsc/a_iface.h> @@ -195,7 +196,7 @@ static uint8_t convert_Abis_prev_to_A_pref(int radio) } /* Assemble the channel type field */ -static void make_channel_type(struct gsm_mncc_bearer_cap *bc, struct gsm0808_channel_type *ct) +static void enc_channel_type(struct gsm0808_channel_type *ct, const struct gsm_mncc_bearer_cap *bc) { unsigned int i; uint8_t sv; @@ -223,18 +224,39 @@ static void make_channel_type(struct gsm_mncc_bearer_cap *bc, struct gsm0808_cha ct->perm_spch_len = count; if (only_gsm_hr) - /* Default to full rate, in case only GSM HR V1 is available */ + /* Note: We must avoid the usage of GSM HR1 as this + * codec only offers very poor audio quality. If the + * MS only supports GSM HR1 (and full rate), and has + * a preference for half rate. Then we will ignore the + * preference and assume a preference for full rate. */ ct->ch_rate_type = GSM0808_SPEECH_FULL_BM; else ct->ch_rate_type = convert_Abis_prev_to_A_pref(bc->radio); } +/* Assemble the speech codec field */ +static int enc_speeach_codec_list(struct gsm0808_speech_codec_list *scl, const struct gsm0808_channel_type *ct) +{ + unsigned int i; + int rc; + + memset(scl, 0, sizeof(*scl)); + for (i = 0; i < ct->perm_spch_len; i++) { + rc = gsm0808_extrapolate_speech_codec(&scl->codec[i], ct->perm_spch[i]); + if (rc != 0) + return -EINVAL; + } + scl->len = i; + + return 0; +} + /* Send assignment request via A-interface */ int a_assign(struct gsm_trans *trans) { struct gsm_subscriber_connection *conn; struct gsm0808_channel_type ct; - struct gsm0808_speech_codec_list *scl = NULL; + struct gsm0808_speech_codec_list scl; uint32_t *ci_ptr = NULL; struct msgb *msg; struct sockaddr_storage rtp_addr; @@ -244,7 +266,10 @@ int a_assign(struct gsm_trans *trans) OSMO_ASSERT(conn); /* Channel type */ - make_channel_type(&trans->bearer_cap, &ct); + enc_channel_type(&ct, &trans->bearer_cap); + + /* Speech codec list */ + enc_speeach_codec_list(&scl, &ct); /* Package RTP-Address data */ memset(&rtp_addr_in, 0, sizeof(rtp_addr_in)); @@ -255,7 +280,7 @@ int a_assign(struct gsm_trans *trans) memset(&rtp_addr, 0, sizeof(rtp_addr)); memcpy(&rtp_addr, &rtp_addr_in, sizeof(rtp_addr_in)); - msg = gsm0808_create_ass(&ct, NULL, &rtp_addr, scl, ci_ptr); + msg = gsm0808_create_ass(&ct, NULL, &rtp_addr, &scl, ci_ptr); LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg->data, msg->len)); return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg); diff --git a/openbsc/src/osmo-bsc/osmo_bsc_audio.c b/openbsc/src/osmo-bsc/osmo_bsc_audio.c index 79b513aa3..ffba754d3 100644 --- a/openbsc/src/osmo-bsc/osmo_bsc_audio.c +++ b/openbsc/src/osmo-bsc/osmo_bsc_audio.c @@ -27,6 +27,7 @@ #include <openbsc/debug.h> #include <openbsc/signal.h> #include <osmocom/gsm/gsm0808.h> +#include <osmocom/gsm/gsm0808_utils.h> #include <openbsc/osmo_bsc_sigtran.h> #include <arpa/inet.h> @@ -37,6 +38,7 @@ static int send_aoip_ass_compl(struct gsm_subscriber_connection *conn, struct gs struct msgb *resp; struct sockaddr_storage rtp_addr; struct sockaddr_in rtp_addr_in; + struct gsm0808_speech_codec sc; OSMO_ASSERT(lchan->abis_ip.ass_compl.valid == true); @@ -48,13 +50,16 @@ static int send_aoip_ass_compl(struct gsm_subscriber_connection *conn, struct gs memset(&rtp_addr, 0, sizeof(rtp_addr)); memcpy(&rtp_addr, &rtp_addr_in, sizeof(rtp_addr_in)); + /* Extrapolate speech codec from speech mode */ + gsm0808_extrapolate_speech_codec(&sc, lchan->abis_ip.ass_compl.speech_mode); + /* Generate message */ resp = gsm0808_create_ass_compl(lchan->abis_ip.ass_compl.rr_cause, lchan->abis_ip.ass_compl.chosen_channel, lchan->abis_ip.ass_compl.encr_alg_id, lchan->abis_ip.ass_compl.speech_mode, &rtp_addr, - NULL, + &sc, NULL); if (!resp) { diff --git a/openbsc/src/osmo-bsc/osmo_bsc_bssap.c b/openbsc/src/osmo-bsc/osmo_bsc_bssap.c index e705e708d..7d81a9ece 100644 --- a/openbsc/src/osmo-bsc/osmo_bsc_bssap.c +++ b/openbsc/src/osmo-bsc/osmo_bsc_bssap.c @@ -38,7 +38,11 @@ /* * helpers for the assignment command */ -enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio) + +/* Helper function for match_codec_pref(), looks up a matching permitted speech + * value for a given msc audio codec pref */ +enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support + *audio) { if (audio->hr) { switch (audio->ver) { @@ -52,8 +56,9 @@ enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *a return GSM0808_PERM_HR3; break; default: - LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); - return GSM0808_PERM_FR1; + LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", + audio->ver); + return GSM0808_PERM_FR1; } } else { switch (audio->ver) { @@ -67,12 +72,15 @@ enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *a return GSM0808_PERM_FR3; break; default: - LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); + LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", + audio->ver); return GSM0808_PERM_HR1; } } } +/* Helper function for match_codec_pref(), looks up a matching chan mode for + * a given permitted speech value */ enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech) { switch (speech) { @@ -88,10 +96,98 @@ enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech) case GSM0808_PERM_FR3: return GSM48_CMODE_SPEECH_AMR; break; + default: + LOGP(DMSC, LOGL_FATAL, + "Unsupported permitted speech selected, assuming AMR as channel mode...\n"); + return GSM48_CMODE_SPEECH_AMR; + } +} + +/* Helper function for match_codec_pref(), tests if a given audio support + * matches one of the permitted speech settings of the channel type element. + * The matched permitted speech value is then also compared against the + * speech codec list. (optional, only relevant for AoIP) */ +static bool test_codec_pref(const struct gsm0808_channel_type *ct, + const struct gsm0808_speech_codec_list *scl, + uint8_t perm_spch) +{ + unsigned int i; + bool match = false; + struct gsm0808_speech_codec sc; + int rc; + + /* Try to finde the given permitted speech value in the + * codec list of the channel type element */ + for (i = 0; i < ct->perm_spch_len; i++) { + if (ct->perm_spch[i] == perm_spch) { + match = true; + break; + } + } + + /* If we do not have a speech codec list to test against, + * we just exit early (will be always the case in non-AoIP networks) */ + if (!scl) + return match; + + /* If we failed to match until here, there is no + * point in testing further */ + if (match == false) + return false; + + /* Extrapolate speech codec data */ + rc = gsm0808_extrapolate_speech_codec(&sc, perm_spch); + if (rc < 0) + return false; + + /* Try to find extrapolated speech codec data in + * the speech codec list */ + for (i = 0; i < scl->len; i++) { + if (memcmp(&sc, &scl->codec[i], sizeof(sc)) == 0) + return true; + } + + return false; +} + +/* Helper function for bssmap_handle_assignm_req(), matches the codec + * preferences from the MSC with the codec preferences */ +static int match_codec_pref(int *full_rate, enum gsm48_chan_mode *chan_mode, + const struct gsm0808_channel_type *ct, + const struct gsm0808_speech_codec_list *scl, + const struct bsc_msc_data *msc) +{ + unsigned int i; + uint8_t perm_spch; + bool match = false; + + for (i = 0; msc->audio_length; i++) { + perm_spch = audio_support_to_gsm88(msc->audio_support[i]); + if (test_codec_pref(ct, scl, perm_spch)) { + match = true; + break; + } + } + + /* Exit without result, in case no match can be deteched */ + if (!match) { + *full_rate = -1; + *chan_mode = GSM48_CMODE_SIGN; + return -1; } - LOGP(DMSC, LOGL_FATAL, "Should not be reached.\n"); - return GSM48_CMODE_SPEECH_AMR; + /* Check if the result is a half or full rate codec */ + if (perm_spch == GSM0808_PERM_HR1 || perm_spch == GSM0808_PERM_HR2 + || perm_spch == GSM0808_PERM_HR3 || perm_spch == GSM0808_PERM_HR4 + || perm_spch == GSM0808_PERM_HR6) + *full_rate = 0; + else + *full_rate = 1; + + /* Lookup a channel mode for the selected codec */ + *chan_mode = gsm88_to_chan_mode(perm_spch); + + return 0; } static int bssmap_handle_reset_ack(struct bsc_msc_data *msc, @@ -308,23 +404,31 @@ static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn, struct msgb *resp; struct bsc_msc_data *msc; struct tlv_parsed tp; - uint8_t *data; uint8_t timeslot = 0; uint8_t multiplex = 0; enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN; - int i, supported, port, full_rate = -1; + int port, full_rate = -1; bool aoip = false; struct sockaddr_storage rtp_addr; struct sockaddr_in *rtp_addr_in; + struct gsm0808_channel_type ct; + struct gsm0808_speech_codec_list scl; + struct gsm0808_speech_codec_list *scl_ptr = NULL; int rc; + const uint8_t *data; + char len; if (!conn->conn) { - LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n"); + LOGP(DMSC, LOGL_ERROR, + "No lchan/msc_data in cipher mode command.\n"); return -1; } + msc = conn->msc; + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0); + /* Check for channel type element, if its missing, immediately reject */ if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) { LOGP(DMSC, LOGL_ERROR, "Mandatory channel type not present.\n"); goto reject; @@ -333,70 +437,71 @@ static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn, /* Detect if a CIC code is present, if so, we use the classic ip.access * method to calculate the RTP port */ if (TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) { - conn->cic = osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); + conn->cic = + osmo_load16be(TLVP_VAL + (&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); timeslot = conn->cic & 0x1f; multiplex = (conn->cic & ~0x1f) >> 5; - } else if(TLVP_PRESENT(&tp, GSM0808_IE_AOIP_TRASP_ADDR)) { + } else if (TLVP_PRESENT(&tp, GSM0808_IE_AOIP_TRASP_ADDR)) { /* Decode AoIP transport address element */ - rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr, TLVP_VAL(&tp, GSM0808_IE_AOIP_TRASP_ADDR), TLVP_LEN(&tp, GSM0808_IE_AOIP_TRASP_ADDR)); + data = TLVP_VAL(&tp, GSM0808_IE_AOIP_TRASP_ADDR); + len = TLVP_LEN(&tp, GSM0808_IE_AOIP_TRASP_ADDR); + rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr, data, len); if (rc < 0) { - LOGP(DMSC, LOGL_ERROR, "Unable to decode aoip transport address.\n"); + LOGP(DMSC, LOGL_ERROR, + "Unable to decode aoip transport address.\n"); goto reject; } aoip = true; } else { - LOGP(DMSC, LOGL_ERROR, "transport address missing. Audio routing will not work.\n"); + LOGP(DMSC, LOGL_ERROR, + "transport address missing. Audio routing will not work.\n"); goto reject; } - /* - * Currently we only support a limited subset of all - * possible channel types. The limitation ends by not using - * multi-slot, limiting the channel coding, speech... - */ - if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) { - LOGP(DMSC, LOGL_ERROR, "ChannelType len !=3 not supported: %d\n", - TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE)); - goto reject; - } + /* Decode speech codec list (AoIP) */ + if (aoip) { + /* Check for speech codec list element */ + if (!TLVP_PRESENT(&tp, GSM0808_IE_SPEECH_CODEC_LIST)) { + LOGP(DMSC, LOGL_ERROR, + "Mandatory speech codec list not present.\n"); + goto reject; + } - /* - * Try to figure out if we support the proposed speech codecs. For - * now we will always pick the full rate codecs. - */ + /* Decode Speech Codec list */ + data = TLVP_VAL(&tp, GSM0808_IE_SPEECH_CODEC_LIST); + len = TLVP_LEN(&tp, GSM0808_IE_SPEECH_CODEC_LIST); + rc = gsm0808_dec_speech_codec_list(&scl, data, len); + if (rc < 0) { + LOGP(DMSC, LOGL_ERROR, + "Unable to decode speech codec list\n"); + goto reject; + } + scl_ptr = &scl; + } - data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE); - if ((data[0] & 0xf) != 0x1) { - LOGP(DMSC, LOGL_ERROR, "ChannelType != speech: %d\n", data[0]); + /* Decode Channel Type element */ + data = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE); + len = TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); + rc = gsm0808_dec_channel_type(&ct, data, len); + if (rc < 0) { + LOGP(DMSC, LOGL_ERROR, "unable to decode channel type.\n"); goto reject; } - /* - * go through the list of preferred codecs of our gsm network - * and try to find it among the permitted codecs. If we found - * it we will send chan_mode to the right mode and break the - * inner loop. The outer loop will exit due chan_mode having - * the correct value. - */ - full_rate = 0; - msc = conn->msc; - for (supported = 0; - chan_mode == GSM48_CMODE_SIGN && supported < msc->audio_length; - ++supported) { - - int perm_val = audio_support_to_gsm88(msc->audio_support[supported]); - for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) { - if ((data[i] & 0x7f) == perm_val) { - chan_mode = gsm88_to_chan_mode(perm_val); - full_rate = (data[i] & 0x4) == 0; - break; - } else if ((data[i] & 0x80) == 0x00) { - break; - } - } + /* Currently we only support a limited subset of all + * possible channel types. The limitation ends by not using + * multi-slot, limiting the channel coding to speech */ + if (ct.ch_indctr != GSM0808_CHAN_SPEECH) { + LOGP(DMSC, LOGL_ERROR, + "Unsupported channel type, currently only speech is supported!\n"); + goto reject; } - if (chan_mode == GSM48_CMODE_SIGN) { + /* Match codec information from the assignment command against the + * local preferences of the BSC */ + rc = match_codec_pref(&full_rate, &chan_mode, &ct, scl_ptr, msc); + if (rc < 0) { LOGP(DMSC, LOGL_ERROR, "No supported audio type found.\n"); goto reject; } @@ -409,14 +514,15 @@ static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn, } else { /* use address / port supplied with the AoIP * transport address element */ - if(rtp_addr.ss_family == AF_INET) - { + if (rtp_addr.ss_family == AF_INET) { rtp_addr_in = (struct sockaddr_in *)&rtp_addr; conn->rtp_port = osmo_ntohs(rtp_addr_in->sin_port); - memcpy(&conn->rtp_ip, &rtp_addr_in->sin_addr.s_addr, IP_V4_ADDR_LEN); + memcpy(&conn->rtp_ip, &rtp_addr_in->sin_addr.s_addr, + IP_V4_ADDR_LEN); conn->rtp_ip = osmo_ntohl(conn->rtp_ip); } else { - LOGP(DMSC, LOGL_ERROR, "Unsopported addressing scheme. (supports only IPV4)\n"); + LOGP(DMSC, LOGL_ERROR, + "Unsopported addressing scheme. (supports only IPV4)\n"); goto reject; } } @@ -424,7 +530,9 @@ static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn, return gsm0808_assign_req(conn->conn, chan_mode, full_rate); reject: - resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); + resp = + gsm0808_create_assignment_failure + (GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); if (!resp) { LOGP(DMSC, LOGL_ERROR, "Channel allocation failure.\n"); return -1; |