blob: 4d1354783a9b53d923f33901aad1b4b43bc84692 [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
245 if (fr->e_bits[1] != 1 || fr->e_bits[1] != 1 || fr->e_bits[2] != 0)
246 return -EINVAL;
247
248 for (int i = 0; i < 24; i++) {
249 /* we only use one of the bits, not some kind of consistency check or majority vote */
250 d_out[i] = fr->d_bits[i*2];
251 }
252
253 return 24;
254}
255
256/*! Adapt from 36 synchronous N x 3600bit/s input bits to a decoded V.110 frame.
257 * \param[out] fr caller-allocated output frame to which E+D bits are stored
258 * \param[in] d_in input user bits
259 * \param[in] in_len number of bits in d_in. Must be 36.
260 * \returns 0 on success; negative in case of error. */
261static int v110_adapt_Nx3600_to_IR(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
262{
263 int d_idx = 0;
264
265 if (in_len != 36)
266 return -EINVAL;
267
268 /* Table 6d / V.110 */
269 fr->e_bits[0] = 1;
270 fr->e_bits[1] = 0;
271 fr->e_bits[2] = 1;
272
273 memcpy(fr->d_bits + d_idx, d_in + 0, 10); d_idx += 10; /* D1..D10 */
274 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
275 memcpy(fr->d_bits + d_idx, d_in + 10, 2); d_idx += 2; /* D11..D12 */
276 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
277 memcpy(fr->d_bits + d_idx, d_in + 12, 2); d_idx += 2; /* D13..D14 */
278 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
279 memcpy(fr->d_bits + d_idx, d_in + 14, 14); d_idx += 14; /* D15..D28 */
280 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
281 memcpy(fr->d_bits + d_idx, d_in + 28, 2); d_idx += 2; /* D29..D30 */
282 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
283 memcpy(fr->d_bits + d_idx, d_in + 30, 2); d_idx += 2; /* D31..D32 */
284 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
285 memcpy(fr->d_bits + d_idx, d_in + 32, 4); d_idx += 4; /* D33..D36 */
286
287 OSMO_ASSERT(d_idx == 48);
288
289 return 0;
290}
291
292static int v110_adapt_IR_to_Nx3600(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
293{
294 int d_idx = 0;
295
296 if (out_len < 36)
297 return -ENOSPC;
298
299 if (fr->e_bits[0] != 1 || fr->e_bits[1] != 0 || fr->e_bits[2] != 1)
300 return -EINVAL;
301
302 memcpy(d_out + 0, fr->d_bits + d_idx, 10); d_idx += 10; /* D1..D10 */
303 d_idx += 2;
304 memcpy(d_out + 10, fr->d_bits + d_idx, 2); d_idx += 2; /* D11..D12 */
305 d_idx += 2;
306 memcpy(d_out + 12, fr->d_bits + d_idx, 2); d_idx += 2; /* D13..D14 */
307 d_idx += 2;
308 memcpy(d_out + 14, fr->d_bits + d_idx, 14); d_idx += 14;/* D15..D28 */
309 d_idx += 2;
310 memcpy(d_out + 28, fr->d_bits + d_idx, 2); d_idx += 2; /* D29..D30 */
311 d_idx += 2;
312 memcpy(d_out + 30, fr->d_bits + d_idx, 2); d_idx += 2; /* D31..D32 */
313 d_idx += 2;
314 memcpy(d_out + 32, fr->d_bits + d_idx, 4); d_idx += 4; /* D33..D36 */
315
316 OSMO_ASSERT(d_idx == 48);
317
318 return 36;
319}
320
321
322/*! Adapt from 48 synchronous N x 4800bit/s input bits to a decoded V.110 frame.
323 * \param[out] fr caller-allocated output frame to which E+D bits are stored
324 * \param[in] d_in input user bits
325 * \param[in] in_len number of bits in d_in. Must be 48.
326 * \returns 0 on success; negative in case of error. */
327static int v110_adapt_Nx4800_to_IR(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
328{
329 if (in_len != 48)
330 return -EINVAL;
331
332 /* Table 6e / V.110 */
333 fr->e_bits[0] = 0;
334 fr->e_bits[1] = 1;
335 fr->e_bits[2] = 1;
336
337 memcpy(fr->d_bits, d_in, 48);
338
339 return 0;
340}
341
342static int v110_adapt_IR_to_Nx4800(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
343{
344 if (out_len < 48)
345 return -ENOSPC;
346
347 if (fr->e_bits[0] != 0 || fr->e_bits[1] != 1 || fr->e_bits[2] != 1)
348 return -EINVAL;
349
350 memcpy(d_out, fr->d_bits, 48);
351
352 return 48;
353}
354
355/*! Adapt from 30 synchronous N x 12000bit/s input bits to a decoded V.110 frame.
356 * \param[out] fr caller-allocated output frame to which E+D bits are stored
357 * \param[in] d_in input user bits
358 * \param[in] in_len number of bits in d_in. Must be 30.
359 * \returns 0 on success; negative in case of error. */
360static int v110_adapt_Nx12000_to_IR(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
361{
362 int d_idx = 0;
363
364 if (in_len != 30)
365 return -EINVAL;
366
367 /* Table 6f / V.110 */
368 fr->e_bits[0] = 0;
369 fr->e_bits[1] = 0;
370 fr->e_bits[2] = 1;
371
372 memcpy(fr->d_bits + d_idx, d_in + 0, 10); d_idx += 10; /* D1..D10 */
373 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
374 memcpy(fr->d_bits + d_idx, d_in + 10, 2); d_idx += 2; /* D11..D12 */
375 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
376 memcpy(fr->d_bits + d_idx, d_in + 12, 2); d_idx += 2; /* D13..D14 */
377 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
378 fr->d_bits[d_idx++] = d_in[14]; /* D15 */
379 memset(fr->d_bits + d_idx, F, 3); d_idx += 3;
380 memcpy(fr->d_bits + d_idx, d_in + 15, 10); d_idx += 10; /* D16..D25 */
381 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
382 memcpy(fr->d_bits + d_idx, d_in + 25, 2); d_idx += 2; /* D26..D27 */
383 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
384 memcpy(fr->d_bits + d_idx, d_in + 27, 2); d_idx += 2; /* D28..D29 */
385 memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
386 fr->d_bits[d_idx++] = d_in[29]; /* D30 */
387 memset(fr->d_bits + d_idx, F, 3); d_idx += 3;
388
389 OSMO_ASSERT(d_idx == 48);
390
391 return 0;
392}
393
394static int v110_adapt_IR_to_Nx12000(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
395{
396 int d_idx = 0;
397
398 if (out_len < 30)
399 return -ENOSPC;
400
401 if (fr->e_bits[0] != 0 || fr->e_bits[1] != 0 || fr->e_bits[2] != 1)
402 return -EINVAL;
403
404 memcpy(d_out + 0, fr->d_bits + d_idx, 10); d_idx += 10; /* D1..D10 */
405 d_idx += 2;
406 memcpy(d_out + 10, fr->d_bits + d_idx, 2); d_idx += 2; /* D11..D12 */
407 d_idx += 2;
408 memcpy(d_out + 12, fr->d_bits + d_idx, 2); d_idx += 2; /* D13..D14 */
409 d_idx += 2;
410 d_out[14] = fr->d_bits[d_idx++]; /* D15 */
411 d_idx += 3;
412 memcpy(d_out + 15, fr->d_bits + d_idx, 10); d_idx += 10;/* D16..D25 */
413 d_idx += 2;
414 memcpy(d_out + 25, fr->d_bits + d_idx, 2); d_idx += 2; /* D26..D27 */
415 d_idx += 2;
416 memcpy(d_out + 27, fr->d_bits + d_idx, 2); d_idx += 2; /* D28..D29 */
417 d_idx += 2;
418 d_out[29] = fr->d_bits[d_idx++]; /* D30 */
419 d_idx += 3;
420
421 OSMO_ASSERT(d_idx == 48);
422
423 return 30;
424}
425
426/* definition of a synchronous V.110 RA1 rate adaptation. There is one for each supported tuple
427 * of user data rate and intermediate rate (IR). */
428struct osmo_v110_sync_ra1 {
429 unsigned int data_rate;
430 unsigned int intermediate_rate;
431 unsigned int user_data_chunk_bits;
432 /*! RA1 function in user bitrate -> intermediate rate direction */
433 int (*adapt_user_to_ir)(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len);
434 /*! RA1 function in intermediate rate -> user bitrate direction */
435 int (*adapt_ir_to_user)(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr);
436};
437
438/* all of the synchronous data signalling rates; see Table 1/V.110 */
439static const struct osmo_v110_sync_ra1 osmo_v110_sync_ra1_def[_NUM_OSMO_V110_SYNC_RA1] = {
440 [OSMO_V110_SYNC_RA1_600] = {
441 .data_rate = 600,
442 .intermediate_rate = 8000,
443 .user_data_chunk_bits = 6,
444 .adapt_user_to_ir = v110_adapt_600_to_IR8000,
445 .adapt_ir_to_user = v110_adapt_IR8000_to_600,
446 },
447 [OSMO_V110_SYNC_RA1_1200] = {
448 .data_rate = 1200,
449 .intermediate_rate = 8000,
450 .user_data_chunk_bits = 12,
451 .adapt_user_to_ir = v110_adapt_1200_to_IR8000,
452 .adapt_ir_to_user = v110_adapt_IR8000_to_1200,
453 },
454 [OSMO_V110_SYNC_RA1_2400] = {
455 .data_rate = 2400,
456 .intermediate_rate = 8000,
457 .user_data_chunk_bits = 24,
458 .adapt_user_to_ir = v110_adapt_2400_to_IR8000,
459 .adapt_ir_to_user = v110_adapt_IR8000_to_2400,
460 },
461 [OSMO_V110_SYNC_RA1_4800] = {
462 .data_rate = 4800,
463 .intermediate_rate = 8000,
464 .user_data_chunk_bits = 48,
465 .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
466 .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
467 },
468 [OSMO_V110_SYNC_RA1_7200] = {
469 .data_rate = 7200,
470 .intermediate_rate = 16000,
471 .user_data_chunk_bits = 36,
472 .adapt_user_to_ir = v110_adapt_Nx3600_to_IR,
473 .adapt_ir_to_user = v110_adapt_IR_to_Nx3600,
474 },
475 [OSMO_V110_SYNC_RA1_9600] = {
476 .data_rate = 9600,
477 .intermediate_rate = 16000,
478 .user_data_chunk_bits = 48,
479 .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
480 .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
481 },
482 [OSMO_V110_SYNC_RA1_12000] = {
483 .data_rate = 12000,
484 .intermediate_rate = 32000,
485 .user_data_chunk_bits = 30,
486 .adapt_user_to_ir = v110_adapt_Nx12000_to_IR,
487 .adapt_ir_to_user = v110_adapt_IR_to_Nx12000,
488 },
489 [OSMO_V110_SYNC_RA1_14400] = {
490 .data_rate = 14400,
491 .intermediate_rate = 32000,
492 .user_data_chunk_bits = 36,
493 .adapt_user_to_ir = v110_adapt_Nx3600_to_IR,
494 .adapt_ir_to_user = v110_adapt_IR_to_Nx3600,
495 },
496 [OSMO_V110_SYNC_RA1_19200] = {
497 .data_rate = 19200,
498 .intermediate_rate = 32000,
499 .user_data_chunk_bits = 48,
500 .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
501 .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
502 },
503 [OSMO_V110_SYNC_RA1_24000] = {
504 .data_rate = 24000,
505 .intermediate_rate = 64000,
506 .user_data_chunk_bits = 30,
507 .adapt_user_to_ir = v110_adapt_Nx12000_to_IR,
508 .adapt_ir_to_user = v110_adapt_IR_to_Nx12000,
509 },
510 [OSMO_V110_SYNC_RA1_28800] = {
511 .data_rate = 28800,
512 .intermediate_rate = 64000,
513 .user_data_chunk_bits = 36,
514 .adapt_user_to_ir = v110_adapt_Nx3600_to_IR,
515 .adapt_ir_to_user = v110_adapt_IR_to_Nx3600,
516 },
517 [OSMO_V110_SYNC_RA1_38400] = {
518 .data_rate = 38400,
519 .intermediate_rate = 64000,
520 .user_data_chunk_bits = 48,
521 .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
522 .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
523 },
524};
525
526/*! obtain the size (in number of bits) of the user data bits in one V.110
527 * frame for specified RA1 rate */
528int osmo_v110_sync_ra1_get_user_data_chunk_bitlen(enum osmo_v100_sync_ra1_rate rate)
529{
530 if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
531 return -EINVAL;
532
533 return osmo_v110_sync_ra1_def[rate].user_data_chunk_bits;
534}
535
536/*! obtain the user data rate (in bits/s) for specified RA1 rate */
537int osmo_v110_sync_ra1_get_user_data_rate(enum osmo_v100_sync_ra1_rate rate)
538{
539 if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
540 return -EINVAL;
541
542 return osmo_v110_sync_ra1_def[rate].data_rate;
543}
544
545/*! obtain the intermediate rate (in bits/s) for specified RA1 rate */
546int osmo_v110_sync_ra1_get_intermediate_rate(enum osmo_v100_sync_ra1_rate rate)
547{
548 if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
549 return -EINVAL;
550
551 return osmo_v110_sync_ra1_def[rate].intermediate_rate;
552}
553
554/*! perform V.110 RA1 function in user rate -> intermediate rate direction.
555 * \param[in] rate specification of the user bitrate
556 * \param[out] fr caller-allocated output buffer for the [decoded] V.110 frame generated
557 * \param[in] d_in input user data (unpacked bits)
558 * \param[in] in_len length of user input data (in number of bits)
559 * \returns 0 on success; negative in case of error */
560int osmo_v110_sync_ra1_user_to_ir(enum osmo_v100_sync_ra1_rate rate, struct osmo_v110_decoded_frame *fr,
561 const ubit_t *d_in, size_t in_len)
562{
563 if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
564 return -EINVAL;
565
566 return osmo_v110_sync_ra1_def[rate].adapt_user_to_ir(fr, d_in, in_len);
567}
568
569/*! perform V.110 RA1 function in intermediate rate -> user rate direction.
570 * \param[in] rate specification of the user bitrate
571 * \param[out] d_out caller-allocated output user data (unpacked bits)
572 * \param[out] out_len length of d_out output buffer
573 * \param[in] fr [decoded] V.110 frame used as input
574 * \returns number of unpacked bits written to d_out on success; negative in case of error */
575int osmo_v110_sync_ra1_ir_to_user(enum osmo_v100_sync_ra1_rate rate, ubit_t *d_out, size_t out_len,
576 const struct osmo_v110_decoded_frame *fr)
577{
578 if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
579 return -EINVAL;
580
581 return osmo_v110_sync_ra1_def[rate].adapt_ir_to_user(d_out, out_len, fr);
582}