osmith has submitted this change. ( https://gerrit.osmocom.org/c/osmo-bsc/+/31547 )
Change subject: bssmap_handle_ass_req_ct_data: implement ......................................................................
bssmap_handle_ass_req_ct_data: implement
Handle assignment requests for CSD. In this initial version, the code for non-transparent data mode is a stub.
Related: OS#5763 Depends: libosmocore Ia965cdd9f53af756e5ffaff9b8f389b5ad629969 Change-Id: I350bea15fd2158eb6edc9bc92f2dca48930736e9 --- M TODO-RELEASE M include/osmocom/bsc/Makefile.am M include/osmocom/bsc/codec_pref.h A include/osmocom/bsc/data_rate_pref.h M src/osmo-bsc/Makefile.am A src/osmo-bsc/data_rate_pref.c M src/osmo-bsc/osmo_bsc_bssap.c 7 files changed, 282 insertions(+), 4 deletions(-)
Approvals: Jenkins Builder: Verified osmith: Looks good to me, approved
diff --git a/TODO-RELEASE b/TODO-RELEASE index 78fcea3..551cc18 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -10,3 +10,4 @@ osmo-bsc VTY Timeslot phys_chan_config will now write back with new dynamic timeslot names: 'DYNAMIC/OSMOCOM' instead of 'TCH/F_TCH/H_SDCCH8_PDCH' and 'DYNAMIC/IPACCESS' instead of 'TCH/F_PDCH' osmo-bsc CTRL CTRL commands like 'bts.N.channel-load' will now respond with new dynamic timeslot names: 'DYNAMIC/OSMOCOM' instead of 'TCH/F_TCH/H_SDCCH8_PDCH' and 'DYNAMIC/IPACCESS' instead of 'TCH/F_PDCH' osmo-bsc CTRL,VTY osmo_fsm instance IDs now use new dynamic timeslot names 'DYNAMIC_OSMOCOM' and 'DYNAMIC_IPACCESS' +libosmogsm >1.8.0 circuit switched data stuff (gsm0808_enc/dec_channel_type etc.) diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am index 53d45ad..8baff9c 100644 --- a/include/osmocom/bsc/Makefile.am +++ b/include/osmocom/bsc/Makefile.am @@ -19,6 +19,7 @@ chan_alloc.h \ chan_counts.h \ codec_pref.h \ + data_rate_pref.h \ ctrl.h \ debug.h \ e1_config.h \ diff --git a/include/osmocom/bsc/codec_pref.h b/include/osmocom/bsc/codec_pref.h index adefe04..5944a4a 100644 --- a/include/osmocom/bsc/codec_pref.h +++ b/include/osmocom/bsc/codec_pref.h @@ -17,6 +17,10 @@ RATE_PREF_FR, };
+int match_codec_pref_data(struct channel_mode_and_rate *ch_mode_rate, + const struct gsm0808_channel_type *ct, + const bool full_rate); + int match_codec_pref(struct channel_mode_and_rate *ch_mode_rate, const struct gsm0808_channel_type *ct, const struct gsm0808_speech_codec_list *scl, diff --git a/include/osmocom/bsc/data_rate_pref.h b/include/osmocom/bsc/data_rate_pref.h new file mode 100644 index 0000000..da3f1fd --- /dev/null +++ b/include/osmocom/bsc/data_rate_pref.h @@ -0,0 +1,11 @@ +#pragma once + +#include <stdbool.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> + +struct gsm0808_channel_type; +struct channel_mode_and_rate; + +int match_data_rate_pref(struct channel_mode_and_rate *ch_mode_rate, + const struct gsm0808_channel_type *ct, + const bool full_rate); diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am index 60a76fa..53c0912 100644 --- a/src/osmo-bsc/Makefile.am +++ b/src/osmo-bsc/Makefile.am @@ -62,6 +62,7 @@ chan_alloc.c \ chan_counts.c \ codec_pref.c \ + data_rate_pref.c \ e1_config.c \ gsm_04_08_rr.c \ gsm_data.c \ diff --git a/src/osmo-bsc/data_rate_pref.c b/src/osmo-bsc/data_rate_pref.c new file mode 100644 index 0000000..443fea7 --- /dev/null +++ b/src/osmo-bsc/data_rate_pref.c @@ -0,0 +1,152 @@ +/* + * (C) 2023 by sysmocom s.f.m.c. GmbH info@sysmocom.de + * All Rights Reserved + * + * Author: Oliver Smith + * + * 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/gsm/protocol/gsm_08_08.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> +#include <osmocom/bsc/data_rate_pref.h> +#include <osmocom/bsc/debug.h> +#include <osmocom/bsc/lchan.h> + +static int gsm0808_data_rate_transp_to_gsm0858(enum gsm0808_data_rate_transp rate) +{ + switch (rate) { + case GSM0808_DATA_RATE_TRANSP_32k0: + return RSL_CMOD_CSD_T_32k0; + case GSM0808_DATA_RATE_TRANSP_28k8: + return RSL_CMOD_CSD_T_29k0; + case GSM0808_DATA_RATE_TRANSP_14k4: + return RSL_CMOD_CSD_T_14k4; + case GSM0808_DATA_RATE_TRANSP_9k6: + return RSL_CMOD_CSD_T_9k6; + case GSM0808_DATA_RATE_TRANSP_4k8: + return RSL_CMOD_CSD_T_4k8; + case GSM0808_DATA_RATE_TRANSP_2k4: + return RSL_CMOD_CSD_T_2k4; + case GSM0808_DATA_RATE_TRANSP_1k2: + return RSL_CMOD_CSD_T_1k2; + case GSM0808_DATA_RATE_TRANSP_600: + return RSL_CMOD_CSD_T_600; + case GSM0808_DATA_RATE_TRANSP_1200_75: + return RSL_CMOD_CSD_T_1200_75; + default: + LOGP(DMSC, LOGL_ERROR, "Unsupported transparent data rate 0x%x\n", rate); + return -1; + } +} + +static int gsm0808_data_rate_transp_to_gsm0408(enum gsm0808_data_rate_transp rate) +{ + switch (rate) { + case GSM0808_DATA_RATE_TRANSP_14k4: + return GSM48_CMODE_DATA_14k5; + case GSM0808_DATA_RATE_TRANSP_9k6: + return GSM48_CMODE_DATA_12k0; + case GSM0808_DATA_RATE_TRANSP_4k8: + return GSM48_CMODE_DATA_6k0; + case GSM0808_DATA_RATE_TRANSP_2k4: + case GSM0808_DATA_RATE_TRANSP_1k2: + case GSM0808_DATA_RATE_TRANSP_600: + case GSM0808_DATA_RATE_TRANSP_1200_75: + return GSM48_CMODE_DATA_3k6; + default: + LOGP(DMSC, LOGL_ERROR, "Unsupported transparent data rate 0x%x\n", rate); + return -1; + } +} + +static int gsm0808_data_rate_non_transp_to_gsm0408(enum gsm0808_data_rate_non_transp rate) +{ + LOGP(DMSC, LOGL_ERROR, "%s is not implemented\n", __func__); /* FIXME */ + return -1; +} + +static int gsm0808_data_rate_non_transp_to_gsm0858(enum gsm0808_data_rate_non_transp rate, bool full_rate) +{ + switch (rate) { + case GSM0808_DATA_RATE_NON_TRANSP_12000_6000: + if (full_rate) + return RSL_CMOD_CSD_NT_12k0; + return RSL_CMOD_CSD_NT_6k0; + case GSM0808_DATA_RATE_NON_TRANSP_14k5: + return RSL_CMOD_CSD_NT_14k5; + case GSM0808_DATA_RATE_NON_TRANSP_12k0: + return RSL_CMOD_CSD_NT_12k0; + case GSM0808_DATA_RATE_NON_TRANSP_6k0: + return RSL_CMOD_CSD_NT_6k0; + case GSM0808_DATA_RATE_NON_TRANSP_43k5: + return RSL_CMOD_CSD_NT_43k5; + case GSM0808_DATA_RATE_NON_TRANSP_29k0: + return RSL_CMOD_CSD_NT_28k8; + default: + LOGP(DMSC, LOGL_ERROR, "Unsupported non-transparent data rate 0x%x\n", rate); + return -1; + } +} + +static enum gsm48_chan_mode match_non_transp_data_rate(const struct gsm0808_channel_type *ct, bool full_rate) +{ + /* FIXME: Handle ct->data_rate_allowed too if it is set. Find the best + * match by comparing the preferred ct->data_rate + all allowed + * ct->data_rate_allowed against what's most suitable for the BTS. */ + + return gsm0808_data_rate_non_transp_to_gsm0858(ct->data_rate, full_rate); +} + +/*! Match the GSM 08.08 channel type received from the MSC to suitable data for + * the BTS, the GSM 04.08 channel mode, channel rate (FR/HR) and GSM 08.58 + * data rate. + * \param[out] ch_mode_rate resulting channel rate, channel mode and data rate + * \param[in] ct GSM 08.08 channel type received from MSC. + * \param[in] full_rate true means FR is preferred, false means HR + * \returns 0 on success, -1 in case no match was found */ +int match_data_rate_pref(struct channel_mode_and_rate *ch_mode_rate, + const struct gsm0808_channel_type *ct, + const bool full_rate) +{ + int rc; + *ch_mode_rate = (struct channel_mode_and_rate){}; + ch_mode_rate->chan_rate = full_rate ? CH_RATE_FULL : CH_RATE_HALF; + ch_mode_rate->data_transparent = ct->data_transparent; + + if (ct->data_transparent) { + rc = gsm0808_data_rate_transp_to_gsm0858(ct->data_rate); + if (rc == -1) + return -1; + ch_mode_rate->data_rate.t = rc; + + rc = gsm0808_data_rate_transp_to_gsm0408(ct->data_rate); + if (rc == -1) + return -1; + ch_mode_rate->chan_mode = rc; + } else { + rc = match_non_transp_data_rate(ct, full_rate); + if (rc == -1) + return -1; + ch_mode_rate->data_rate.nt = rc; + + rc = gsm0808_data_rate_non_transp_to_gsm0408(ct->data_rate); + if (rc == -1) + return -1; + ch_mode_rate->chan_mode = rc; + } + + return 0; +} diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c index 221e477..59d5aa5 100644 --- a/src/osmo-bsc/osmo_bsc_bssap.c +++ b/src/osmo-bsc/osmo_bsc_bssap.c @@ -31,6 +31,7 @@ #include <osmocom/bsc/gsm_04_08_rr.h> #include <osmocom/bsc/bsc_subscr_conn_fsm.h> #include <osmocom/bsc/codec_pref.h> +#include <osmocom/bsc/data_rate_pref.h> #include <osmocom/bsc/abis_rsl.h> #include <osmocom/bsc/handover_fsm.h> #include <osmocom/bsc/bts.h> @@ -667,6 +668,67 @@ return 0; }
+/* Select a preferred and an alternative data rate depending on the available + * capabilities. This decision does not include the actual channel load yet, + * this is also the reason why the result is a preferred and an alternate + * setting. The final decision is made in assignment_fsm.c when the actual + * lchan is requested. The preferred lchan will be requested first. If we + * find an alternate setting here, this one will be tried secondly if our + * primary choice fails. */ +static int select_data_rates(struct assignment_request *req, struct gsm0808_channel_type *ct, + struct gsm_subscriber_connection *conn) +{ + int rc, i, nc = 0; + + switch (ct->ch_rate_type) { + case GSM0808_DATA_FULL_BM: + rc = match_data_rate_pref(&req->ch_mode_rate_list[nc], ct, true); + nc += (rc == 0) ? 1 : 0; + break; + case GSM0808_DATA_HALF_LM: + rc = match_data_rate_pref(&req->ch_mode_rate_list[nc], ct, false); + nc += (rc == 0) ? 1 : 0; + break; + case GSM0808_DATA_FULL_PREF_NO_CHANGE: + case GSM0808_DATA_FULL_PREF: + rc = match_data_rate_pref(&req->ch_mode_rate_list[nc], ct, true); + nc += (rc == 0) ? 1 : 0; + rc = match_data_rate_pref(&req->ch_mode_rate_list[nc], ct, false); + nc += (rc == 0) ? 1 : 0; + break; + case GSM0808_DATA_HALF_PREF_NO_CHANGE: + case GSM0808_DATA_HALF_PREF: + rc = match_data_rate_pref(&req->ch_mode_rate_list[nc], ct, false); + nc += (rc == 0) ? 1 : 0; + rc = match_data_rate_pref(&req->ch_mode_rate_list[nc], ct, true); + nc += (rc == 0) ? 1 : 0; + break; + default: + rc = -EINVAL; + break; + } + + if (!nc) { + LOGP(DMSC, LOGL_ERROR, "No supported data rate found for channel_type =" + " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[%s] }\n", + ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len)); + return -EINVAL; + } + + for (i = 0; i < nc; i++) { + DEBUGP(DMSC, "Found matching data rate (pref=%d): %s %s for channel_type =" + " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n", + i, + req->ch_mode_rate_list[i].chan_rate == CH_RATE_FULL ? "full rate" : "half rate", + get_value_string(gsm48_chan_mode_names, req->ch_mode_rate_list[i].chan_mode), + ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len)); + } + + req->n_ch_mode_rate = nc; + + return 0; +} + /* Select a preferred and an alternative codec rate depending on the available * capabilities. This decision does not include the actual lchan availability yet, * this is also the reason why the result is a preferred and an alternate @@ -924,6 +986,38 @@ return 0; }
+static int bssmap_handle_ass_req_ct_data(struct gsm_subscriber_connection *conn, struct tlv_parsed *tp, + struct gsm0808_channel_type *ct, struct assignment_request *req, + uint8_t *cause) +{ + bool aoip = gscon_is_aoip(conn); + int rc; + + *req = (struct assignment_request){ + .assign_for = ASSIGN_FOR_BSSMAP_REQ, + .aoip = aoip, + }; + + if (bssmap_handle_ass_req_tp_cic(tp, aoip, &req->msc_assigned_cic, cause) < 0) + return -1; + + if (bssmap_handle_ass_req_tp_rtp_addr(tp, aoip, req->msc_rtp_addr, sizeof(req->msc_rtp_addr), &req->msc_rtp_port, cause) < 0) + return -1; + + /* According to 3GPP TS 48.008 § 3.2.1.1 note 13, the codec list IE + * shall be included for aoip unless channel type is signalling. */ + if (bssmap_handle_ass_req_tp_codec_list(conn, tp, aoip, cause) < 0) + return -1; + + rc = select_data_rates(req, ct, conn); + if (rc < 0) { + *cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL; + return -1; + } + + return 0; +} + static int bssmap_handle_ass_req_ct_speech(struct gsm_subscriber_connection *conn, struct tlv_parsed *tp, struct gsm0808_channel_type *ct, struct assignment_request *req, uint8_t *cause) @@ -1023,12 +1117,12 @@ bssmap_handle_ass_req_lcls(conn, &tp);
/* Currently we only support a limited subset of all - * possible channel types, such as multi-slot or CSD */ + * possible channel types, such as multi-slot */ switch (ct.ch_indctr) { case GSM0808_CHAN_DATA: - LOGP(DMSC, LOGL_ERROR, "Unsupported channel type, currently only speech is supported!\n"); - cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP; - goto reject; + if (bssmap_handle_ass_req_ct_data(conn, &tp, &ct, &req, &cause) < 0) + goto reject; + break; case GSM0808_CHAN_SPEECH: if (bssmap_handle_ass_req_ct_speech(conn, &tp, &ct, &req, &cause) < 0) goto reject;