blob: dcc612420f79e23d7febe4b09b70937dfe332047 [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
Oliver Smith80654ed2023-07-06 10:59:57 +0200183static int csd_bs_to_gsm0808_data_rate_transp(enum csd_bs bs, uint8_t *ch_rate_type)
Oliver Smith10632132023-05-12 12:14:22 +0200184{
185 switch (bs_map[bs].rate) {
Oliver Smith80654ed2023-07-06 10:59:57 +0200186 case 300:
187 *ch_rate_type = GSM0808_DATA_FULL_PREF;
188 return GSM0808_DATA_RATE_TRANSP_600;
Oliver Smith10632132023-05-12 12:14:22 +0200189 case 1200:
Oliver Smith80654ed2023-07-06 10:59:57 +0200190 *ch_rate_type = GSM0808_DATA_FULL_PREF;
Oliver Smith10632132023-05-12 12:14:22 +0200191 return GSM0808_DATA_RATE_TRANSP_1k2;
192 case 2400:
Oliver Smith80654ed2023-07-06 10:59:57 +0200193 *ch_rate_type = GSM0808_DATA_FULL_PREF;
Oliver Smith10632132023-05-12 12:14:22 +0200194 return GSM0808_DATA_RATE_TRANSP_2k4;
195 case 4800:
Oliver Smith80654ed2023-07-06 10:59:57 +0200196 *ch_rate_type = GSM0808_DATA_FULL_PREF;
Oliver Smith10632132023-05-12 12:14:22 +0200197 return GSM0808_DATA_RATE_TRANSP_4k8;
198 case 9600:
Oliver Smith80654ed2023-07-06 10:59:57 +0200199 *ch_rate_type = GSM0808_DATA_FULL_BM;
Oliver Smith10632132023-05-12 12:14:22 +0200200 return GSM0808_DATA_RATE_TRANSP_9k6;
201 }
202 return -EINVAL;
203}
204
Oliver Smith80654ed2023-07-06 10:59:57 +0200205static int csd_bs_to_gsm0808_data_rate_non_transp(enum csd_bs bs, uint8_t *ch_rate_type)
Oliver Smith10632132023-05-12 12:14:22 +0200206{
207 uint16_t rate = bs_map[bs].rate;
208
Oliver Smith80654ed2023-07-06 10:59:57 +0200209 if (rate < 6000) {
210 *ch_rate_type = GSM0808_DATA_FULL_PREF;
Oliver Smith10632132023-05-12 12:14:22 +0200211 return GSM0808_DATA_RATE_NON_TRANSP_6k0;
Oliver Smith80654ed2023-07-06 10:59:57 +0200212 }
213 if (rate < 12000) {
214 *ch_rate_type = GSM0808_DATA_FULL_BM;
Oliver Smith10632132023-05-12 12:14:22 +0200215 return GSM0808_DATA_RATE_NON_TRANSP_12k0;
Oliver Smith80654ed2023-07-06 10:59:57 +0200216 }
Oliver Smith10632132023-05-12 12:14:22 +0200217
218 return -EINVAL;
219}
220
221static int csd_bs_to_gsm0808_data_rate_non_transp_allowed(enum csd_bs bs)
222{
223 uint16_t rate = bs_map[bs].rate;
224
225 if (rate < 6000)
226 return GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_6k0;
227 if (rate < 12000)
228 return GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_12k0;
229
230 return -EINVAL;
231}
232
233enum csd_bs csd_bs_from_bearer_cap(const struct gsm_mncc_bearer_cap *cap, bool transp)
234{
235 enum gsm48_bcap_ra ra = cap->data.rate_adaption;
236 enum gsm48_bcap_user_rate rate = cap->data.user_rate;
237 bool async = cap->data.async;
238
239 if (ra == GSM48_BCAP_RA_V110_X30 && async && transp) {
240 switch (rate) {
241 case GSM48_BCAP_UR_300:
242 return CSD_BS_21_T_V110_0k3;
243 case GSM48_BCAP_UR_1200:
244 return CSD_BS_22_T_V110_1k2;
245 case GSM48_BCAP_UR_2400:
246 return CSD_BS_24_T_V110_2k4;
247 case GSM48_BCAP_UR_4800:
248 return CSD_BS_25_T_V110_4k8;
249 case GSM48_BCAP_UR_9600:
250 return CSD_BS_26_T_V110_9k6;
251 default:
252 return CSD_BS_NONE;
253 }
254 }
255
256 if (ra == GSM48_BCAP_RA_V110_X30 && async && !transp) {
257 switch (rate) {
258 case GSM48_BCAP_UR_300:
259 return CSD_BS_21_NT_V110_0k3;
260 case GSM48_BCAP_UR_1200:
261 return CSD_BS_22_NT_V110_1k2;
262 case GSM48_BCAP_UR_2400:
263 return CSD_BS_24_NT_V110_2k4;
264 case GSM48_BCAP_UR_4800:
265 return CSD_BS_25_NT_V110_4k8;
266 case GSM48_BCAP_UR_9600:
267 return CSD_BS_26_NT_V110_9k6;
268 default:
269 return CSD_BS_NONE;
270 }
271 }
272
273 if (ra == GSM48_BCAP_RA_V110_X30 && !async && transp) {
274 switch (rate) {
275 case GSM48_BCAP_UR_1200:
276 return CSD_BS_31_T_V110_1k2;
277 case GSM48_BCAP_UR_2400:
278 return CSD_BS_32_T_V110_2k4;
279 case GSM48_BCAP_UR_4800:
280 return CSD_BS_33_T_V110_4k8;
281 case GSM48_BCAP_UR_9600:
282 return CSD_BS_34_T_V110_9k6;
283 default:
284 return CSD_BS_NONE;
285 }
286 }
287
288 return CSD_BS_NONE;
289}
290
291/* csd_bs_list related below */
292
293int csd_bs_list_to_str_buf(char *buf, size_t buflen, const struct csd_bs_list *list)
294{
295 struct osmo_strbuf sb = { .buf = buf, .len = buflen };
296 int i;
297
298 if (!list->count)
299 OSMO_STRBUF_PRINTF(sb, "(no-bearer-services)");
300
301 for (i = 0; i < list->count; i++) {
302 if (i)
303 OSMO_STRBUF_PRINTF(sb, ",");
304
305 OSMO_STRBUF_APPEND(sb, csd_bs_to_str_buf, list->bs[i]);
306 }
307 return sb.chars_needed;
308}
309
310char *csd_bs_list_to_str_c(void *ctx, const struct csd_bs_list *list)
311{
312 OSMO_NAME_C_IMPL(ctx, 128, "csd_bs_list_to_str_c-ERROR", csd_bs_list_to_str_buf, list)
313}
314
315const char *csd_bs_list_to_str(const struct csd_bs_list *list)
316{
317 return csd_bs_list_to_str_c(OTC_SELECT, list);
318}
319
320bool csd_bs_list_has_bs(const struct csd_bs_list *list, enum csd_bs bs)
321{
322 int i;
323
324 for (i = 0; i < list->count; i++) {
325 if (list->bs[i] == bs)
326 return true;
327 }
328
329 return false;
330}
331
332void csd_bs_list_add_bs(struct csd_bs_list *list, enum csd_bs bs)
333{
334 int i;
335
336 if (!bs)
337 return;
338
339 for (i = 0; i < list->count; i++) {
340 if (list->bs[i] == bs)
341 return;
342 }
343
344 list->bs[i] = bs;
345 list->count++;
346}
347
348void csd_bs_list_remove(struct csd_bs_list *list, enum csd_bs bs)
349{
350 int i;
351 bool found = false;
352
353 for (i = 0; i < list->count; i++) {
Oliver Smithfa006b82023-07-06 13:40:15 +0200354 if (list->bs[i] == bs)
Oliver Smith10632132023-05-12 12:14:22 +0200355 found = true;
Oliver Smithfa006b82023-07-06 13:40:15 +0200356 if (found && i + 1 < list->count)
357 list->bs[i] = list->bs[i + 1];
Oliver Smith10632132023-05-12 12:14:22 +0200358 }
Oliver Smithfa006b82023-07-06 13:40:15 +0200359
360 if (found)
361 list->count--;
Oliver Smith10632132023-05-12 12:14:22 +0200362}
363
364void csd_bs_list_intersection(struct csd_bs_list *dest, const struct csd_bs_list *other)
365{
366 int i;
367
368 for (i = 0; i < dest->count; i++) {
369 if (csd_bs_list_has_bs(other, dest->bs[i]))
370 continue;
371 csd_bs_list_remove(dest, dest->bs[i]);
372 i--;
373 }
374}
375
376int csd_bs_list_to_gsm0808_channel_type(struct gsm0808_channel_type *ct, const struct csd_bs_list *list)
377{
378 int i;
379 int rc;
380
381 *ct = (struct gsm0808_channel_type){
382 .ch_indctr = GSM0808_CHAN_DATA,
383 };
384
385 OSMO_ASSERT(list->count);
386
387 if (csd_bs_is_transp(list->bs[0])) {
388 ct->data_transparent = true;
Oliver Smith80654ed2023-07-06 10:59:57 +0200389 rc = csd_bs_to_gsm0808_data_rate_transp(list->bs[0], &ct->ch_rate_type);
Oliver Smith10632132023-05-12 12:14:22 +0200390 } else {
Oliver Smith80654ed2023-07-06 10:59:57 +0200391 rc = csd_bs_to_gsm0808_data_rate_non_transp(list->bs[0], &ct->ch_rate_type);
Oliver Smith10632132023-05-12 12:14:22 +0200392 }
393
Oliver Smith17399802023-06-22 10:39:24 +0200394 if (rc < 0)
Oliver Smith10632132023-05-12 12:14:22 +0200395 return -EINVAL;
396
Oliver Smith17399802023-06-22 10:39:24 +0200397 ct->data_rate = rc;
398
Oliver Smith10632132023-05-12 12:14:22 +0200399 /* Other possible data rates allowed (3GPP TS 48.008 ยง 3.2.2.11, 5a) */
400 if (!ct->data_transparent && list->count > 1) {
401 for (i = 1; i < list->count; i++) {
402 if (!csd_bs_is_transp(list->bs[i]))
403 continue;
404
405 rc = csd_bs_to_gsm0808_data_rate_non_transp_allowed(list->bs[i]);
406 if (rc < 0) {
407 LOGP(DMSC, LOGL_DEBUG, "Failed to convert %s to allowed r i/f rate\n",
408 csd_bs_to_str(list->bs[i]));
409 continue;
410 }
411
412 ct->data_rate_allowed |= rc;
413 }
414 if (ct->data_rate_allowed)
415 ct->data_rate_allowed_is_set = true;
416 }
417
Oliver Smith10632132023-05-12 12:14:22 +0200418 return 0;
419}
420
421int csd_bs_list_to_bearer_cap(struct gsm_mncc_bearer_cap *cap, const struct csd_bs_list *list)
422{
423 *cap = (struct gsm_mncc_bearer_cap){
424 .transfer = GSM_MNCC_BCAP_UNR_DIG,
Vadim Yanitskiyab2845a2023-07-26 02:45:24 +0700425 .mode = GSM48_BCAP_TMOD_CIRCUIT,
426 .coding = GSM48_BCAP_CODING_GSM_STD,
427 .radio = GSM48_BCAP_RRQ_FR_ONLY,
Oliver Smith10632132023-05-12 12:14:22 +0200428 };
429 enum csd_bs bs;
430 int i;
431
432 for (i = 0; i < list->count; i++) {
433 bs = list->bs[i];
434
435 cap->data.rate_adaption = GSM48_BCAP_RA_V110_X30;
Vadim Yanitskiyab2845a2023-07-26 02:45:24 +0700436 cap->data.sig_access = GSM48_BCAP_SA_I440_I450;
Oliver Smith10632132023-05-12 12:14:22 +0200437 cap->data.async = bs_map[bs].async;
Vadim Yanitskiyab2845a2023-07-26 02:45:24 +0700438 if (bs_map[bs].transp)
439 cap->data.transp = GSM48_BCAP_TR_TRANSP;
440 else
441 cap->data.transp = GSM48_BCAP_TR_RLP;
442
443 /* FIXME: proper values for sync/async (current: 8N1) */
444 cap->data.nr_data_bits = 8;
445 cap->data.parity = GSM48_BCAP_PAR_NONE;
446 cap->data.nr_stop_bits = 1;
447 cap->data.modem_type = GSM48_BCAP_MT_NONE;
Oliver Smith10632132023-05-12 12:14:22 +0200448
449 switch (bs_map[bs].rate) {
450 case 300:
451 cap->data.user_rate = GSM48_BCAP_UR_300;
Vadim Yanitskiyab2845a2023-07-26 02:45:24 +0700452 cap->data.interm_rate = GSM48_BCAP_IR_8k;
Oliver Smith10632132023-05-12 12:14:22 +0200453 break;
454 case 1200:
455 cap->data.user_rate = GSM48_BCAP_UR_1200;
Vadim Yanitskiyab2845a2023-07-26 02:45:24 +0700456 cap->data.interm_rate = GSM48_BCAP_IR_8k;
Oliver Smith10632132023-05-12 12:14:22 +0200457 break;
458 case 2400:
459 cap->data.user_rate = GSM48_BCAP_UR_2400;
Vadim Yanitskiyab2845a2023-07-26 02:45:24 +0700460 cap->data.interm_rate = GSM48_BCAP_IR_8k;
Oliver Smith10632132023-05-12 12:14:22 +0200461 break;
462 case 4800:
463 cap->data.user_rate = GSM48_BCAP_UR_4800;
Vadim Yanitskiyab2845a2023-07-26 02:45:24 +0700464 cap->data.interm_rate = GSM48_BCAP_IR_8k;
Oliver Smith10632132023-05-12 12:14:22 +0200465 break;
466 case 9600:
467 cap->data.user_rate = GSM48_BCAP_UR_9600;
Vadim Yanitskiyab2845a2023-07-26 02:45:24 +0700468 cap->data.interm_rate = GSM48_BCAP_IR_16k;
Oliver Smith10632132023-05-12 12:14:22 +0200469 break;
470 }
471
472 /* FIXME: handle more than one list entry */
473 return 1;
474 }
475
476 return 0;
477}
478
479void csd_bs_list_from_bearer_cap(struct csd_bs_list *list, const struct gsm_mncc_bearer_cap *cap)
480{
481 *list = (struct csd_bs_list){};
482
483 switch (cap->data.transp) {
484 case GSM48_BCAP_TR_TRANSP:
485 csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, true));
486 break;
487 case GSM48_BCAP_TR_RLP: /* NT */
488 csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, false));
489 break;
490 case GSM48_BCAP_TR_TR_PREF:
491 csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, true));
492 csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, false));
493 break;
494 case GSM48_BCAP_TR_RLP_PREF:
495 csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, false));
496 csd_bs_list_add_bs(list, csd_bs_from_bearer_cap(cap, true));
497 break;
498 }
499
500 if (!list->count) {
501 LOGP(DMSC, LOGL_ERROR, "Failed to get bearer service from bearer capabilities ra=%d, async=%d,"
502 " transp=%d, user_rate=%d\n", cap->data.rate_adaption, cap->data.async, cap->data.transp,
503 cap->data.user_rate);
504 return;
505 }
506}