blob: 46962bfa5342b72f40989c045501c27fa84996a1 [file] [log] [blame]
Harald Welte065dab82022-11-29 23:13:06 +01001/* V.110 frames according to ITU-T V.110
2 *
3 * This code implements the following functionality:
4 * - parsing/encoding of osmo_v110_decoded_frame from/to actual 80-bit V.110 frame
5 * - synchronous rate adapting of user bit rate to V.110 D-bits as per Table 6
6 *
7 * It is (at least initially) a very "naive" implementation, as it first and foremost
8 * aims to be functional and correct, rather than efficient in any way. Hence it
9 * operates on unpacked bits (ubit_t, 1 bit per byte), and has various intermediate
10 * representations and indirect function calls. If needed, a more optimized variant
11 * can always be developed later on.
12 */
13
14/* (C) 2022 by Harald Welte <laforge@osmocom.org>
15 *
16 * SPDX-License-Identifier: GPL-2.0+
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 */
28
29#include <stdint.h>
30#include <errno.h>
31
32#include <osmocom/core/bits.h>
33
34#include <osmocom/isdn/v110.h>
35
36/*************************************************************************
37 * V.110 frame decoding/encoding (ubits <-> struct with D/S/X/E bits)
38 *************************************************************************/
39
40/*! Decode a 80-bit V.110 frame present as 80 ubits into a struct osmo_v110_decoded_frame.
41 * \param[out] fr caller-allocated output data structure, filled by this function
42 * \param[in] ra_bits One V.110 frame as 80 unpacked bits.
43 * \param[in] n_bits number of unpacked bits provided in ra_bits
44 * \returns 0 in case of success; negative on error. */
45int osmo_v110_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits)
46{
47 if (n_bits < 80)
48 return -EINVAL;
49
50 /* X1 .. X2 */
51 fr->x_bits[0] = ra_bits[2 * 8 + 7];
52 fr->x_bits[1] = ra_bits[7 * 8 + 7];
53
54 /* S1, S3, S4, S6, S8, S9 */
55 fr->s_bits[0] = ra_bits[1 * 8 + 7];
56 fr->s_bits[2] = ra_bits[3 * 8 + 7];
57 fr->s_bits[3] = ra_bits[4 * 8 + 7];
58 fr->s_bits[5] = ra_bits[6 * 8 + 7];
59 fr->s_bits[7] = ra_bits[8 * 8 + 7];
60 fr->s_bits[8] = ra_bits[9 * 8 + 7];
61
62 /* E1 .. E7 */
63 memcpy(fr->e_bits, ra_bits + 5 * 8 + 1, 7);
64
65 /* D-bits */
66 memcpy(fr->d_bits + 0 * 6, ra_bits + 1 * 8 + 1, 6);
67 memcpy(fr->d_bits + 1 * 6, ra_bits + 2 * 8 + 1, 6);
68 memcpy(fr->d_bits + 2 * 6, ra_bits + 3 * 8 + 1, 6);
69 memcpy(fr->d_bits + 3 * 6, ra_bits + 4 * 8 + 1, 6);
70
71 memcpy(fr->d_bits + 4 * 6, ra_bits + 6 * 8 + 1, 6);
72 memcpy(fr->d_bits + 5 * 6, ra_bits + 7 * 8 + 1, 6);
73 memcpy(fr->d_bits + 6 * 6, ra_bits + 8 * 8 + 1, 6);
74 memcpy(fr->d_bits + 7 * 6, ra_bits + 9 * 8 + 1, 6);
75
76 return 0;
77}
78
79/*! Encode a struct osmo_v110_decoded_frame into an 80-bit V.110 frame as ubits.
80 * \param[out] ra_bits caller-provided output buffer at leat 80 ubits large
81 * \param[in] n_bits length of ra_bits. Must be at least 80.
82 * \param[in] input data structure
83 * \returns number of bits written to ra_bits */
84int osmo_v110_encode_frame(ubit_t *ra_bits, size_t n_bits, const struct osmo_v110_decoded_frame *fr)
85{
86 if (n_bits < 80)
87 return -ENOSPC;
88
89 /* alignment pattern */
90 memset(ra_bits+0, 0, 8);
91 for (int i = 1; i < 10; i++)
92 ra_bits[i*8] = 1;
93
94 /* X1 .. X2 */
95 ra_bits[2 * 8 + 7] = fr->x_bits[0];
96 ra_bits[7 * 8 + 7] = fr->x_bits[1];
97
98 /* S1, S3, S4, S6, S8, S9 */
99 ra_bits[1 * 8 + 7] = fr->s_bits[0];
100 ra_bits[3 * 8 + 7] = fr->s_bits[2];
101 ra_bits[4 * 8 + 7] = fr->s_bits[3];
102 ra_bits[6 * 8 + 7] = fr->s_bits[5];
103 ra_bits[8 * 8 + 7] = fr->s_bits[7];
104 ra_bits[9 * 8 + 7] = fr->s_bits[8];
105
106 /* E1 .. E7 */
107 memcpy(ra_bits + 5 * 8 + 1, fr->e_bits, 7);
108
109 /* D-bits */
110 memcpy(ra_bits + 1 * 8 + 1, fr->d_bits + 0 * 6, 6);
111 memcpy(ra_bits + 2 * 8 + 1, fr->d_bits + 1 * 6, 6);
112 memcpy(ra_bits + 3 * 8 + 1, fr->d_bits + 2 * 6, 6);
113 memcpy(ra_bits + 4 * 8 + 1, fr->d_bits + 3 * 6, 6);
114
115 memcpy(ra_bits + 6 * 8 + 1, fr->d_bits + 4 * 6, 6);
116 memcpy(ra_bits + 7 * 8 + 1, fr->d_bits + 5 * 6, 6);
117 memcpy(ra_bits + 8 * 8 + 1, fr->d_bits + 6 * 6, 6);
118 memcpy(ra_bits + 9 * 8 + 1, fr->d_bits + 7 * 6, 6);
119
120 return 10 * 8;
121}
122
123/*! Print a encoded V.110 frame in the same table-like structure as the spec.
124 * \param outf output FILE stream to which to dump
125 * \param[in] fr unpacked bits to dump
126 * \param[in] in_len length of unpacked bits available at fr. */
127void osmo_v110_ubit_dump(FILE *outf, const ubit_t *fr, size_t in_len)
128{
129 if (in_len < 80)
130 fprintf(outf, "short input data\n");
131
132 for (unsigned int octet = 0; octet < 10; octet++) {
133 fprintf(outf, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
134 fr[octet * 8 + 0], fr[octet * 8 + 1], fr[octet * 8 + 2], fr[octet * 8 + 3],
135 fr[octet * 8 + 4], fr[octet * 8 + 5], fr[octet * 8 + 6], fr[octet * 8 + 7]);
136 }
137}
138
139/*************************************************************************
140 * RA1 synchronous rate adaptation
141 *************************************************************************/
142
143/* I actually couldn't find any reference as to the value of F(ill) bits */
144#define F 1
145
146/*! Adapt from 6 synchronous 600bit/s input bits to a decoded V.110 frame.
147 * \param[out] fr caller-allocated output frame to which E+D bits are stored
148 * \param[in] d_in input user bits
149 * \param[in] in_len number of bits in d_in. Must be 6.
150 * \returns 0 on success; negative in case of error. */
151static int v110_adapt_600_to_IR8000(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
152{
153 if (in_len != 6)
154 return -EINVAL;
155
156 /* Table 6a / V.110 */
157 fr->e_bits[0] = 1;
158 fr->e_bits[1] = 0;
159 fr->e_bits[2] = 0;
160 for (int i = 0; i < 6; i++)
161 memset(fr->d_bits + i*8, d_in[i], 8);
162
163 return 0;
164}
165
166static int v110_adapt_IR8000_to_600(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
167{
168 if (out_len < 6)
169 return -ENOSPC;
170
171 if (fr->e_bits[0] != 1 || fr->e_bits[1] != 0 || fr->e_bits[2] != 0)
172 return -EINVAL;
173
174 for (int i = 0; i < 6; i++) {
175 /* we only use one of the bits, not some kind of consistency check or majority vote */
176 d_out[i] = fr->d_bits[i*8];
177 }
178
179 return 6;
180}
181
182/*! Adapt from 12 synchronous 1200bit/s input bits to a decoded V.110 frame.
183 * \param[out] fr caller-allocated output frame to which E+D bits are stored
184 * \param[in] d_in input user bits
185 * \param[in] in_len number of bits in d_in. Must be 12.
186 * \returns 0 on success; negative in case of error. */
187static int v110_adapt_1200_to_IR8000(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
188{
189 if (in_len != 12)
190 return -EINVAL;
191
192 /* Table 6b / V.110 */
193 fr->e_bits[0] = 0;
194 fr->e_bits[1] = 1;
195 fr->e_bits[2] = 0;
196 for (int i = 0; i < 12; i++)
197 memset(fr->d_bits + i*4, d_in[i], 4);
198
199 return 0;
200}
201
202static int v110_adapt_IR8000_to_1200(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
203{
204 if (out_len < 12)
205 return -ENOSPC;
206
207 if (fr->e_bits[0] != 0 || fr->e_bits[1] != 1 || fr->e_bits[2] != 0)
208 return -EINVAL;
209
210 for (int i = 0; i < 12; i++) {
211 /* we only use one of the bits, not some kind of consistency check or majority vote */
212 d_out[i] = fr->d_bits[i*4];
213 }
214
215 return 12;
216}
217
218/*! Adapt from 24 synchronous 2400bit/s input bits to a decoded V.110 frame.
219 * \param[out] fr caller-allocated output frame to which E+D bits are stored
220 * \param[in] d_in input user bits
221 * \param[in] in_len number of bits in d_in. Must be 24.
222 * \returns 0 on success; negative in case of error. */
223static int v110_adapt_2400_to_IR8000(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
224{
225 if (in_len != 24)
226 return -EINVAL;
227
228 /* Table 6c / V.110 */
229 fr->e_bits[0] = 1;
230 fr->e_bits[1] = 1;
231 fr->e_bits[2] = 0;
232 for (int i = 0; i < 24; i++) {
233 fr->d_bits[i*2 + 0] = d_in[i];
234 fr->d_bits[i*2 + 1] = d_in[i];
235 }
236
237 return 0;
238}
239
240static int v110_adapt_IR8000_to_2400(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
241{
242 if (out_len < 24)
243 return -ENOSPC;
244
Vadim Yanitskiy25127fb2023-03-17 17:08:18 +0700245 /* Table 6c / V.110 */
246 if (fr->e_bits[0] != 1 || fr->e_bits[1] != 1 || fr->e_bits[2] != 0)
Harald Welte065dab82022-11-29 23:13:06 +0100247 return -EINVAL;
248
249 for (int i = 0; i < 24; i++) {
250 /* we only use one of the bits, not some kind of consistency check or majority vote */
251 d_out[i] = fr->d_bits[i*2];
252 }
253
254 return 24;
255}
256
257/*! Adapt from 36 synchronous N x 3600bit/s input bits to a decoded V.110 frame.
258 * \param[out] fr caller-allocated output frame to which E+D bits are stored
259 * \param[in] d_in input user bits
260 * \param[in] in_len number of bits in d_in. Must be 36.
261 * \returns 0 on success; negative in case of error. */
262static int v110_adapt_Nx3600_to_IR(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
263{
264 int d_idx = 0;
265
266 if (in_len != 36)
267 return -EINVAL;
268
269 /* Table 6d / V.110 */
270 fr->e_bits[0] = 1;
271 fr->e_bits[1] = 0;
272 fr->e_bits[2] = 1;
273
274 memcpy(fr->d_bits + d_idx, d_in + 0, 10); d_idx += 10; /* D1..D10 */
275 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
276 memcpy(fr->d_bits + d_idx, d_in + 10, 2); d_idx += 2; /* D11..D12 */
277 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
278 memcpy(fr->d_bits + d_idx, d_in + 12, 2); d_idx += 2; /* D13..D14 */
279 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
280 memcpy(fr->d_bits + d_idx, d_in + 14, 14); d_idx += 14; /* D15..D28 */
281 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
282 memcpy(fr->d_bits + d_idx, d_in + 28, 2); d_idx += 2; /* D29..D30 */
283 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
284 memcpy(fr->d_bits + d_idx, d_in + 30, 2); d_idx += 2; /* D31..D32 */
285 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
286 memcpy(fr->d_bits + d_idx, d_in + 32, 4); d_idx += 4; /* D33..D36 */
287
288 OSMO_ASSERT(d_idx == 48);
289
290 return 0;
291}
292
293static int v110_adapt_IR_to_Nx3600(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
294{
295 int d_idx = 0;
296
297 if (out_len < 36)
298 return -ENOSPC;
299
300 if (fr->e_bits[0] != 1 || fr->e_bits[1] != 0 || fr->e_bits[2] != 1)
301 return -EINVAL;
302
303 memcpy(d_out + 0, fr->d_bits + d_idx, 10); d_idx += 10; /* D1..D10 */
304 d_idx += 2;
305 memcpy(d_out + 10, fr->d_bits + d_idx, 2); d_idx += 2; /* D11..D12 */
306 d_idx += 2;
307 memcpy(d_out + 12, fr->d_bits + d_idx, 2); d_idx += 2; /* D13..D14 */
308 d_idx += 2;
309 memcpy(d_out + 14, fr->d_bits + d_idx, 14); d_idx += 14;/* D15..D28 */
310 d_idx += 2;
311 memcpy(d_out + 28, fr->d_bits + d_idx, 2); d_idx += 2; /* D29..D30 */
312 d_idx += 2;
313 memcpy(d_out + 30, fr->d_bits + d_idx, 2); d_idx += 2; /* D31..D32 */
314 d_idx += 2;
315 memcpy(d_out + 32, fr->d_bits + d_idx, 4); d_idx += 4; /* D33..D36 */
316
317 OSMO_ASSERT(d_idx == 48);
318
319 return 36;
320}
321
322
323/*! Adapt from 48 synchronous N x 4800bit/s input bits to a decoded V.110 frame.
324 * \param[out] fr caller-allocated output frame to which E+D bits are stored
325 * \param[in] d_in input user bits
326 * \param[in] in_len number of bits in d_in. Must be 48.
327 * \returns 0 on success; negative in case of error. */
328static int v110_adapt_Nx4800_to_IR(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
329{
330 if (in_len != 48)
331 return -EINVAL;
332
333 /* Table 6e / V.110 */
334 fr->e_bits[0] = 0;
335 fr->e_bits[1] = 1;
336 fr->e_bits[2] = 1;
337
338 memcpy(fr->d_bits, d_in, 48);
339
340 return 0;
341}
342
343static int v110_adapt_IR_to_Nx4800(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
344{
345 if (out_len < 48)
346 return -ENOSPC;
347
348 if (fr->e_bits[0] != 0 || fr->e_bits[1] != 1 || fr->e_bits[2] != 1)
349 return -EINVAL;
350
351 memcpy(d_out, fr->d_bits, 48);
352
353 return 48;
354}
355
356/*! Adapt from 30 synchronous N x 12000bit/s input bits to a decoded V.110 frame.
357 * \param[out] fr caller-allocated output frame to which E+D bits are stored
358 * \param[in] d_in input user bits
359 * \param[in] in_len number of bits in d_in. Must be 30.
360 * \returns 0 on success; negative in case of error. */
361static int v110_adapt_Nx12000_to_IR(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
362{
363 int d_idx = 0;
364
365 if (in_len != 30)
366 return -EINVAL;
367
368 /* Table 6f / V.110 */
369 fr->e_bits[0] = 0;
370 fr->e_bits[1] = 0;
371 fr->e_bits[2] = 1;
372
373 memcpy(fr->d_bits + d_idx, d_in + 0, 10); d_idx += 10; /* D1..D10 */
374 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
375 memcpy(fr->d_bits + d_idx, d_in + 10, 2); d_idx += 2; /* D11..D12 */
376 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
377 memcpy(fr->d_bits + d_idx, d_in + 12, 2); d_idx += 2; /* D13..D14 */
378 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
379 fr->d_bits[d_idx++] = d_in[14]; /* D15 */
380 memset(fr->d_bits + d_idx, F, 3); d_idx += 3;
381 memcpy(fr->d_bits + d_idx, d_in + 15, 10); d_idx += 10; /* D16..D25 */
382 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
383 memcpy(fr->d_bits + d_idx, d_in + 25, 2); d_idx += 2; /* D26..D27 */
384 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
385 memcpy(fr->d_bits + d_idx, d_in + 27, 2); d_idx += 2; /* D28..D29 */
386 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
387 fr->d_bits[d_idx++] = d_in[29]; /* D30 */
388 memset(fr->d_bits + d_idx, F, 3); d_idx += 3;
389
390 OSMO_ASSERT(d_idx == 48);
391
392 return 0;
393}
394
395static int v110_adapt_IR_to_Nx12000(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
396{
397 int d_idx = 0;
398
399 if (out_len < 30)
400 return -ENOSPC;
401
402 if (fr->e_bits[0] != 0 || fr->e_bits[1] != 0 || fr->e_bits[2] != 1)
403 return -EINVAL;
404
405 memcpy(d_out + 0, fr->d_bits + d_idx, 10); d_idx += 10; /* D1..D10 */
406 d_idx += 2;
407 memcpy(d_out + 10, fr->d_bits + d_idx, 2); d_idx += 2; /* D11..D12 */
408 d_idx += 2;
409 memcpy(d_out + 12, fr->d_bits + d_idx, 2); d_idx += 2; /* D13..D14 */
410 d_idx += 2;
411 d_out[14] = fr->d_bits[d_idx++]; /* D15 */
412 d_idx += 3;
413 memcpy(d_out + 15, fr->d_bits + d_idx, 10); d_idx += 10;/* D16..D25 */
414 d_idx += 2;
415 memcpy(d_out + 25, fr->d_bits + d_idx, 2); d_idx += 2; /* D26..D27 */
416 d_idx += 2;
417 memcpy(d_out + 27, fr->d_bits + d_idx, 2); d_idx += 2; /* D28..D29 */
418 d_idx += 2;
419 d_out[29] = fr->d_bits[d_idx++]; /* D30 */
420 d_idx += 3;
421
422 OSMO_ASSERT(d_idx == 48);
423
424 return 30;
425}
426
427/* definition of a synchronous V.110 RA1 rate adaptation. There is one for each supported tuple
428 * of user data rate and intermediate rate (IR). */
429struct osmo_v110_sync_ra1 {
430 unsigned int data_rate;
431 unsigned int intermediate_rate;
432 unsigned int user_data_chunk_bits;
433 /*! RA1 function in user bitrate -> intermediate rate direction */
434 int (*adapt_user_to_ir)(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len);
435 /*! RA1 function in intermediate rate -> user bitrate direction */
436 int (*adapt_ir_to_user)(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr);
437};
438
439/* all of the synchronous data signalling rates; see Table 1/V.110 */
440static const struct osmo_v110_sync_ra1 osmo_v110_sync_ra1_def[_NUM_OSMO_V110_SYNC_RA1] = {
441 [OSMO_V110_SYNC_RA1_600] = {
442 .data_rate = 600,
443 .intermediate_rate = 8000,
444 .user_data_chunk_bits = 6,
445 .adapt_user_to_ir = v110_adapt_600_to_IR8000,
446 .adapt_ir_to_user = v110_adapt_IR8000_to_600,
447 },
448 [OSMO_V110_SYNC_RA1_1200] = {
449 .data_rate = 1200,
450 .intermediate_rate = 8000,
451 .user_data_chunk_bits = 12,
452 .adapt_user_to_ir = v110_adapt_1200_to_IR8000,
453 .adapt_ir_to_user = v110_adapt_IR8000_to_1200,
454 },
455 [OSMO_V110_SYNC_RA1_2400] = {
456 .data_rate = 2400,
457 .intermediate_rate = 8000,
458 .user_data_chunk_bits = 24,
459 .adapt_user_to_ir = v110_adapt_2400_to_IR8000,
460 .adapt_ir_to_user = v110_adapt_IR8000_to_2400,
461 },
462 [OSMO_V110_SYNC_RA1_4800] = {
463 .data_rate = 4800,
464 .intermediate_rate = 8000,
465 .user_data_chunk_bits = 48,
466 .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
467 .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
468 },
469 [OSMO_V110_SYNC_RA1_7200] = {
470 .data_rate = 7200,
471 .intermediate_rate = 16000,
472 .user_data_chunk_bits = 36,
473 .adapt_user_to_ir = v110_adapt_Nx3600_to_IR,
474 .adapt_ir_to_user = v110_adapt_IR_to_Nx3600,
475 },
476 [OSMO_V110_SYNC_RA1_9600] = {
477 .data_rate = 9600,
478 .intermediate_rate = 16000,
479 .user_data_chunk_bits = 48,
480 .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
481 .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
482 },
483 [OSMO_V110_SYNC_RA1_12000] = {
484 .data_rate = 12000,
485 .intermediate_rate = 32000,
486 .user_data_chunk_bits = 30,
487 .adapt_user_to_ir = v110_adapt_Nx12000_to_IR,
488 .adapt_ir_to_user = v110_adapt_IR_to_Nx12000,
489 },
490 [OSMO_V110_SYNC_RA1_14400] = {
491 .data_rate = 14400,
492 .intermediate_rate = 32000,
493 .user_data_chunk_bits = 36,
494 .adapt_user_to_ir = v110_adapt_Nx3600_to_IR,
495 .adapt_ir_to_user = v110_adapt_IR_to_Nx3600,
496 },
497 [OSMO_V110_SYNC_RA1_19200] = {
498 .data_rate = 19200,
499 .intermediate_rate = 32000,
500 .user_data_chunk_bits = 48,
501 .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
502 .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
503 },
504 [OSMO_V110_SYNC_RA1_24000] = {
505 .data_rate = 24000,
506 .intermediate_rate = 64000,
507 .user_data_chunk_bits = 30,
508 .adapt_user_to_ir = v110_adapt_Nx12000_to_IR,
509 .adapt_ir_to_user = v110_adapt_IR_to_Nx12000,
510 },
511 [OSMO_V110_SYNC_RA1_28800] = {
512 .data_rate = 28800,
513 .intermediate_rate = 64000,
514 .user_data_chunk_bits = 36,
515 .adapt_user_to_ir = v110_adapt_Nx3600_to_IR,
516 .adapt_ir_to_user = v110_adapt_IR_to_Nx3600,
517 },
518 [OSMO_V110_SYNC_RA1_38400] = {
519 .data_rate = 38400,
520 .intermediate_rate = 64000,
521 .user_data_chunk_bits = 48,
522 .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
523 .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
524 },
525};
526
527/*! obtain the size (in number of bits) of the user data bits in one V.110
528 * frame for specified RA1 rate */
529int osmo_v110_sync_ra1_get_user_data_chunk_bitlen(enum osmo_v100_sync_ra1_rate rate)
530{
531 if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
532 return -EINVAL;
533
534 return osmo_v110_sync_ra1_def[rate].user_data_chunk_bits;
535}
536
537/*! obtain the user data rate (in bits/s) for specified RA1 rate */
538int osmo_v110_sync_ra1_get_user_data_rate(enum osmo_v100_sync_ra1_rate rate)
539{
540 if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
541 return -EINVAL;
542
543 return osmo_v110_sync_ra1_def[rate].data_rate;
544}
545
546/*! obtain the intermediate rate (in bits/s) for specified RA1 rate */
547int osmo_v110_sync_ra1_get_intermediate_rate(enum osmo_v100_sync_ra1_rate rate)
548{
549 if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
550 return -EINVAL;
551
552 return osmo_v110_sync_ra1_def[rate].intermediate_rate;
553}
554
555/*! perform V.110 RA1 function in user rate -> intermediate rate direction.
556 * \param[in] rate specification of the user bitrate
557 * \param[out] fr caller-allocated output buffer for the [decoded] V.110 frame generated
558 * \param[in] d_in input user data (unpacked bits)
559 * \param[in] in_len length of user input data (in number of bits)
560 * \returns 0 on success; negative in case of error */
561int osmo_v110_sync_ra1_user_to_ir(enum osmo_v100_sync_ra1_rate rate, struct osmo_v110_decoded_frame *fr,
562 const ubit_t *d_in, size_t in_len)
563{
564 if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
565 return -EINVAL;
566
567 return osmo_v110_sync_ra1_def[rate].adapt_user_to_ir(fr, d_in, in_len);
568}
569
570/*! perform V.110 RA1 function in intermediate rate -> user rate direction.
571 * \param[in] rate specification of the user bitrate
572 * \param[out] d_out caller-allocated output user data (unpacked bits)
573 * \param[out] out_len length of d_out output buffer
574 * \param[in] fr [decoded] V.110 frame used as input
575 * \returns number of unpacked bits written to d_out on success; negative in case of error */
576int osmo_v110_sync_ra1_ir_to_user(enum osmo_v100_sync_ra1_rate rate, ubit_t *d_out, size_t out_len,
577 const struct osmo_v110_decoded_frame *fr)
578{
579 if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
580 return -EINVAL;
581
582 return osmo_v110_sync_ra1_def[rate].adapt_ir_to_user(d_out, out_len, fr);
583}