blob: 2322f20947213f57866c8eed004dee6de61a1b92 [file] [log] [blame]
Oliver Smith10632132023-05-12 12:14:22 +02001/* 3GPP TS 122.002 Bearer Services */
2/*
3 * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
4 * All Rights Reserved
5 *
6 * Author: Oliver Smith
7 *
8 * SPDX-License-Identifier: AGPL-3.0+
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
19 *
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23#include <errno.h>
24
25#include <osmocom/msc/csd_bs.h>
26#include <osmocom/msc/debug.h>
27
28/* csd_bs related below */
29
30struct csd_bs_map {
31 /* BS number (20, 21, ...) */
32 unsigned int num;
33 /* Access Structure (1: asynchronous, 0: synchronous) */
34 bool async;
35 /* QoS Attribute (1: transparent, 0: non-transparent) */
36 bool transp;
37 /* Rate Adaption (V110, V120 etc.) */
38 enum gsm48_bcap_ra ra;
39 /* Fixed Network User Rate */
40 unsigned int rate;
41};
42
43static const struct csd_bs_map bs_map[] = {
44 /* 3.1.1.1.2 */
45 [CSD_BS_21_T_V110_0k3] = {
46 .num = 21,
47 .async = true,
48 .transp = true,
49 .ra = GSM48_BCAP_RA_V110_X30,
50 .rate = 300,
51 },
52 [CSD_BS_22_T_V110_1k2] = {
53 .num = 22,
54 .async = true,
55 .transp = true,
56 .ra = GSM48_BCAP_RA_V110_X30,
57 .rate = 1200,
58 },
59 [CSD_BS_24_T_V110_2k4] = {
60 .num = 24,
61 .async = true,
62 .transp = true,
63 .ra = GSM48_BCAP_RA_V110_X30,
64 .rate = 2400,
65 },
66 [CSD_BS_25_T_V110_4k8] = {
67 .num = 25,
68 .async = true,
69 .transp = true,
70 .ra = GSM48_BCAP_RA_V110_X30,
71 .rate = 4800,
72 },
73 [CSD_BS_26_T_V110_9k6] = {
74 .num = 26,
75 .async = true,
76 .transp = true,
77 .ra = GSM48_BCAP_RA_V110_X30,
78 .rate = 9600,
79 },
80
81 /* 3.1.1.2.2 */
82 [CSD_BS_21_NT_V110_0k3] = {
83 .num = 21,
84 .async = true,
85 .transp = false,
86 .ra = GSM48_BCAP_RA_V110_X30,
87 .rate = 300,
88 },
89 [CSD_BS_22_NT_V110_1k2] = {
90 .num = 22,
91 .async = true,
92 .transp = false,
93 .ra = GSM48_BCAP_RA_V110_X30,
94 .rate = 1200,
95 },
96 [CSD_BS_24_NT_V110_2k4] = {
97 .num = 24,
98 .async = true,
99 .transp = false,
100 .ra = GSM48_BCAP_RA_V110_X30,
101 .rate = 2400,
102 },
103 [CSD_BS_25_NT_V110_4k8] = {
104 .num = 25,
105 .async = true,
106 .transp = false,
107 .ra = GSM48_BCAP_RA_V110_X30,
108 .rate = 4800,
109 },
110 [CSD_BS_26_NT_V110_9k6] = {
111 .num = 26,
112 .async = true,
113 .transp = false,
114 .ra = GSM48_BCAP_RA_V110_X30,
115 .rate = 9600,
116 },
117
118 /* 3.1.2.1.2 */
119 [CSD_BS_31_T_V110_1k2] = {
120 .num = 31,
121 .async = false,
122 .transp = true,
123 .ra = GSM48_BCAP_RA_V110_X30,
124 .rate = 1200,
125 },
126 [CSD_BS_32_T_V110_2k4] = {
127 .num = 32,
128 .async = false,
129 .transp = true,
130 .ra = GSM48_BCAP_RA_V110_X30,
131 .rate = 2400,
132 },
133 [CSD_BS_33_T_V110_4k8] = {
134 .num = 33,
135 .async = false,
136 .transp = true,
137 .ra = GSM48_BCAP_RA_V110_X30,
138 .rate = 4800,
139 },
140 [CSD_BS_34_T_V110_9k6] = {
141 .num = 34,
142 .async = false,
143 .transp = true,
144 .ra = GSM48_BCAP_RA_V110_X30,
145 .rate = 9600,
146 },
147};
148
149osmo_static_assert(ARRAY_SIZE(bs_map) == CSD_BS_MAX, _invalid_size_bs_map);
150
151bool csd_bs_is_transp(enum csd_bs bs)
152{
153 return bs_map[bs].transp;
154}
155
156/* Short single-line representation, convenient for logging.
157 * Like "BS25NT" */
158int csd_bs_to_str_buf(char *buf, size_t buflen, enum csd_bs bs)
159{
160 struct osmo_strbuf sb = { .buf = buf, .len = buflen };
161 const struct csd_bs_map *map = &bs_map[bs];
162
163 OSMO_STRBUF_PRINTF(sb, "BS%u%s",
164 map->num,
165 map->transp ? "T" : "NT");
166
167 if (map->ra != GSM48_BCAP_RA_V110_X30)
168 OSMO_STRBUF_PRINTF(sb, "-RA=%d", map->ra);
169
170 return sb.chars_needed;
171}
172
173char *csd_bs_to_str_c(void *ctx, enum csd_bs bs)
174{
175 OSMO_NAME_C_IMPL(ctx, 32, "csd_bs_to_str_c-ERROR", csd_bs_to_str_buf, bs)
176}
177
178const char *csd_bs_to_str(enum csd_bs bs)
179{
180 return csd_bs_to_str_c(OTC_SELECT, bs);
181}
182
183static int csd_bs_to_gsm0808_data_rate_transp(enum csd_bs bs)
184{
185 switch (bs_map[bs].rate) {
186 case 1200:
187 return GSM0808_DATA_RATE_TRANSP_1k2;
188 case 2400:
189 return GSM0808_DATA_RATE_TRANSP_2k4;
190 case 4800:
191 return GSM0808_DATA_RATE_TRANSP_4k8;
192 case 9600:
193 return GSM0808_DATA_RATE_TRANSP_9k6;
194 }
195 return -EINVAL;
196}
197
198static int csd_bs_to_gsm0808_data_rate_non_transp(enum csd_bs bs)
199{
200 uint16_t rate = bs_map[bs].rate;
201
202 if (rate < 6000)
203 return GSM0808_DATA_RATE_NON_TRANSP_6k0;
204 if (rate < 12000)
205 return GSM0808_DATA_RATE_NON_TRANSP_12k0;
206
207 return -EINVAL;
208}
209
210static int csd_bs_to_gsm0808_data_rate_non_transp_allowed(enum csd_bs bs)
211{
212 uint16_t rate = bs_map[bs].rate;
213
214 if (rate < 6000)
215 return GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_6k0;
216 if (rate < 12000)
217 return GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_12k0;
218
219 return -EINVAL;
220}
221
222enum csd_bs csd_bs_from_bearer_cap(const struct gsm_mncc_bearer_cap *cap, bool transp)
223{
224 enum gsm48_bcap_ra ra = cap->data.rate_adaption;
225 enum gsm48_bcap_user_rate rate = cap->data.user_rate;
226 bool async = cap->data.async;
227
228 if (ra == GSM48_BCAP_RA_V110_X30 && async && transp) {
229 switch (rate) {
230 case GSM48_BCAP_UR_300:
231 return CSD_BS_21_T_V110_0k3;
232 case GSM48_BCAP_UR_1200:
233 return CSD_BS_22_T_V110_1k2;
234 case GSM48_BCAP_UR_2400:
235 return CSD_BS_24_T_V110_2k4;
236 case GSM48_BCAP_UR_4800:
237 return CSD_BS_25_T_V110_4k8;
238 case GSM48_BCAP_UR_9600:
239 return CSD_BS_26_T_V110_9k6;
240 default:
241 return CSD_BS_NONE;
242 }
243 }
244
245 if (ra == GSM48_BCAP_RA_V110_X30 && async && !transp) {
246 switch (rate) {
247 case GSM48_BCAP_UR_300:
248 return CSD_BS_21_NT_V110_0k3;
249 case GSM48_BCAP_UR_1200:
250 return CSD_BS_22_NT_V110_1k2;
251 case GSM48_BCAP_UR_2400:
252 return CSD_BS_24_NT_V110_2k4;
253 case GSM48_BCAP_UR_4800:
254 return CSD_BS_25_NT_V110_4k8;
255 case GSM48_BCAP_UR_9600:
256 return CSD_BS_26_NT_V110_9k6;
257 default:
258 return CSD_BS_NONE;
259 }
260 }
261
262 if (ra == GSM48_BCAP_RA_V110_X30 && !async && transp) {
263 switch (rate) {
264 case GSM48_BCAP_UR_1200:
265 return CSD_BS_31_T_V110_1k2;
266 case GSM48_BCAP_UR_2400:
267 return CSD_BS_32_T_V110_2k4;
268 case GSM48_BCAP_UR_4800:
269 return CSD_BS_33_T_V110_4k8;
270 case GSM48_BCAP_UR_9600:
271 return CSD_BS_34_T_V110_9k6;
272 default:
273 return CSD_BS_NONE;
274 }
275 }
276
277 return CSD_BS_NONE;
278}
279
280/* csd_bs_list related below */
281
282int csd_bs_list_to_str_buf(char *buf, size_t buflen, const struct csd_bs_list *list)
283{
284 struct osmo_strbuf sb = { .buf = buf, .len = buflen };
285 int i;
286
287 if (!list->count)
288 OSMO_STRBUF_PRINTF(sb, "(no-bearer-services)");
289
290 for (i = 0; i < list->count; i++) {
291 if (i)
292 OSMO_STRBUF_PRINTF(sb, ",");
293
294 OSMO_STRBUF_APPEND(sb, csd_bs_to_str_buf, list->bs[i]);
295 }
296 return sb.chars_needed;
297}
298
299char *csd_bs_list_to_str_c(void *ctx, const struct csd_bs_list *list)
300{
301 OSMO_NAME_C_IMPL(ctx, 128, "csd_bs_list_to_str_c-ERROR", csd_bs_list_to_str_buf, list)
302}
303
304const char *csd_bs_list_to_str(const struct csd_bs_list *list)
305{
306 return csd_bs_list_to_str_c(OTC_SELECT, list);
307}
308
309bool csd_bs_list_has_bs(const struct csd_bs_list *list, enum csd_bs bs)
310{
311 int i;
312
313 for (i = 0; i < list->count; i++) {
314 if (list->bs[i] == bs)
315 return true;
316 }
317
318 return false;
319}
320
321void csd_bs_list_add_bs(struct csd_bs_list *list, enum csd_bs bs)
322{
323 int i;
324
325 if (!bs)
326 return;
327
328 for (i = 0; i < list->count; i++) {
329 if (list->bs[i] == bs)
330 return;
331 }
332
333 list->bs[i] = bs;
334 list->count++;
335}
336
337void csd_bs_list_remove(struct csd_bs_list *list, enum csd_bs bs)
338{
339 int i;
340 bool found = false;
341
342 for (i = 0; i < list->count; i++) {
343 if (list->bs[i] == bs) {
344 found = true;
345 list->count--;
346 continue;
347 }
348 if (i && found)
349 list->bs[i-1] = list->bs[i];
350 }
351}
352
353void csd_bs_list_intersection(struct csd_bs_list *dest, const struct csd_bs_list *other)
354{
355 int i;
356
357 for (i = 0; i < dest->count; i++) {
358 if (csd_bs_list_has_bs(other, dest->bs[i]))
359 continue;
360 csd_bs_list_remove(dest, dest->bs[i]);
361 i--;
362 }
363}
364
365int csd_bs_list_to_gsm0808_channel_type(struct gsm0808_channel_type *ct, const struct csd_bs_list *list)
366{
367 int i;
368 int rc;
369
370 *ct = (struct gsm0808_channel_type){
371 .ch_indctr = GSM0808_CHAN_DATA,
372 };
373
374 OSMO_ASSERT(list->count);
375
376 if (csd_bs_is_transp(list->bs[0])) {
377 ct->data_transparent = true;
Oliver Smith17399802023-06-22 10:39:24 +0200378 rc = csd_bs_to_gsm0808_data_rate_transp(list->bs[0]);
Oliver Smith10632132023-05-12 12:14:22 +0200379 } else {
Oliver Smith17399802023-06-22 10:39:24 +0200380 rc = csd_bs_to_gsm0808_data_rate_non_transp(list->bs[0]);
Oliver Smith10632132023-05-12 12:14:22 +0200381 }
382
Oliver Smith17399802023-06-22 10:39:24 +0200383 if (rc < 0)
Oliver Smith10632132023-05-12 12:14:22 +0200384 return -EINVAL;
385
Oliver Smith17399802023-06-22 10:39:24 +0200386 ct->data_rate = rc;
387
Oliver Smith10632132023-05-12 12:14:22 +0200388 /* Other possible data rates allowed (3GPP TS 48.008 ยง 3.2.2.11, 5a) */
389 if (!ct->data_transparent && list->count > 1) {
390 for (i = 1; i < list->count; i++) {
391 if (!csd_bs_is_transp(list->bs[i]))
392 continue;
393
394 rc = csd_bs_to_gsm0808_data_rate_non_transp_allowed(list->bs[i]);
395 if (rc < 0) {
396 LOGP(DMSC, LOGL_DEBUG, "Failed to convert %s to allowed r i/f rate\n",
397 csd_bs_to_str(list->bs[i]));
398 continue;
399 }
400
401 ct->data_rate_allowed |= rc;
402 }
403 if (ct->data_rate_allowed)
404 ct->data_rate_allowed_is_set = true;
405 }
406
407 ct->ch_rate_type = GSM0808_SPEECH_FULL_BM;
408
409 return 0;
410}
411
412int csd_bs_list_to_bearer_cap(struct gsm_mncc_bearer_cap *cap, const struct csd_bs_list *list)
413{
414 *cap = (struct gsm_mncc_bearer_cap){
415 .transfer = GSM_MNCC_BCAP_UNR_DIG,
416 };
417 enum csd_bs bs;
418 int i;
419
420 for (i = 0; i < list->count; i++) {
421 bs = list->bs[i];
422
423 cap->data.rate_adaption = GSM48_BCAP_RA_V110_X30;
424 cap->data.async = bs_map[bs].async;
425 cap->data.transp = bs_map[bs].transp;
426
427 switch (bs_map[bs].rate) {
428 case 300:
429 cap->data.user_rate = GSM48_BCAP_UR_300;
430 break;
431 case 1200:
432 cap->data.user_rate = GSM48_BCAP_UR_1200;
433 break;
434 case 2400:
435 cap->data.user_rate = GSM48_BCAP_UR_2400;
436 break;
437 case 4800:
438 cap->data.user_rate = GSM48_BCAP_UR_4800;
439 break;
440 case 9600:
441 cap->data.user_rate = GSM48_BCAP_UR_9600;
442 break;
443 }
444
445 /* FIXME: handle more than one list entry */
446 return 1;
447 }
448
449 return 0;
450}
451
452void csd_bs_list_from_bearer_cap(struct csd_bs_list *list, const struct gsm_mncc_bearer_cap *cap)
453{
454 *list = (struct csd_bs_list){};
455
456 switch (cap->data.transp) {
457 case GSM48_BCAP_TR_TRANSP:
458 csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, true));
459 break;
460 case GSM48_BCAP_TR_RLP: /* NT */
461 csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, false));
462 break;
463 case GSM48_BCAP_TR_TR_PREF:
464 csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, true));
465 csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, false));
466 break;
467 case GSM48_BCAP_TR_RLP_PREF:
468 csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, false));
469 csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, true));
470 break;
471 }
472
473 if (!list->count) {
474 LOGP(DMSC, LOGL_ERROR, "Failed to get bearer service from bearer capabilities ra=%d, async=%d,"
475 " transp=%d, user_rate=%d\n", cap->data.rate_adaption, cap->data.async, cap->data.transp,
476 cap->data.user_rate);
477 return;
478 }
479}