blob: 9c7303f10470246d0ca43a5e84d6f9b7a0f332b5 [file] [log] [blame]
Harald Welte78861c02020-05-14 13:28:07 +02001/* GSM A-bis TRAU frame synchronization as per TS 48.060 / 48.061 */
2
3/* (C) 2020 by Harald Welte <laforge@gnumonks.org>
4 * All Rights Reserved
5 *
6 * SPDX-License-Identifier: GPL-2.0+
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#include <stdint.h>
24
25#include <osmocom/core/msgb.h>
26#include <osmocom/core/bits.h>
27#include <osmocom/core/fsm.h>
28
29#include "ubit_buf.h"
30#include <osmocom/trau/trau_sync.h>
31
32#define S(x) (1 << (x))
33
34#define MAX_TRAU_BYTES 40
35
36#define T_SYNC 1
37
38struct sync_pattern {
39 /* provided by user */
40 const char *name; /*!< human-readable name */
41 const uint8_t byte_pattern[MAX_TRAU_BYTES]; /*!< bytes to match against */
42 const uint8_t byte_mask[MAX_TRAU_BYTES]; /*!< mask applied before matching */
43 uint8_t byte_len; /*!< length of mask in bytes */
44
45 /* generated by code */
46 ubit_t ubit_pattern[MAX_TRAU_BYTES*8]; /*!< bits to match against */
47 ubit_t ubit_mask[MAX_TRAU_BYTES*8]; /*!< mask applied before matching */
48 uint8_t bitcount; /*!< number of high bits in mask */
49};
50
51static struct sync_pattern sync_patterns[] = {
52 [OSMO_TRAU_SYNCP_16_FR_EFR] = {
53 /* TS 08.60 Section 4.8.1 */
54 .name = "FR/EFR",
55 .byte_pattern = {
56 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
57 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
58 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
59 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
60 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
61 },
62 .byte_mask = {
63 0xff, 0xff, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
64 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
65 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
66 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
67 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
68 },
69 .byte_len = 40,
70 },
71 [OSMO_TRAU_SYNCP_8_HR] = {
72 /* TS 08.61 Section 6.8.2.1.1 */
73 .name = "HR8",
74 .byte_pattern = {
75 0x00, 0x80, 0x40, 0x80,
76 0x80, 0x80, 0x80, 0x80,
77 0x80, 0x80, 0x80, 0x80,
78 0x80, 0x80, 0x80, 0x80,
79 0x80, 0x80, 0x80, 0x80,
80 },
81 .byte_mask = {
82 0xff, 0x80, 0xC0, 0x80,
83 0x80, 0x80, 0x80, 0x80,
84 0x80, 0x80, 0x80, 0x80,
85 0x80, 0x80, 0x80, 0x80,
86 0x80, 0x80, 0x80, 0x80,
87 },
88 .byte_len = 20,
89 },
90 [OSMO_TRAU_SYNCP_8_AMR_LOW] = {
91 /* TS 08.61 Section 6.8.2.1.2 */
92 /* The frame synchronisation for No_Speech frames and the speech frames of the three lower codec modes */
93 .name = "AMR8_LOW",
94 .byte_pattern = {
95 0x00, 0x80, 0x80, 0x40,
96 0x80, 0x80, 0x80, 0x80,
97 0x80, 0x80, 0x80, 0x80,
98 0x80, 0x80, 0x80, 0x80,
99 0x80, 0x80, 0x80, 0x80,
100 },
101 .byte_mask = {
102 0xff, 0x80, 0x80, 0xC0,
103 0x80, 0x80, 0x80, 0x80,
104 0x80, 0x80, 0x80, 0x80,
105 0x80, 0x80, 0x80, 0x80,
106 0x80, 0x80, 0x80, 0x80,
107 },
108 .byte_len = 20,
109 },
110 [OSMO_TRAU_SYNCP_8_AMR_6K7] = {
111 /* The frame synchronisation for the speech frames for codec mode 6,70 kBit/s */
112 .name = "AMR8_67",
113 .byte_pattern = {
114 0x00, 0x80, 0x80, 0x80,
115 0x80, 0x00, 0x80, 0x00,
116 0x80, 0x00, 0x80, 0x00,
117 0x80, 0x00, 0x80, 0x00,
118 0x80, 0x00, 0x80, 0x00,
119 },
120 .byte_mask = {
121 0xff, 0x80, 0x80, 0x80,
122 0x80, 0x80, 0x80, 0x00,
123 0x80, 0x00, 0x80, 0x00,
124 0x80, 0x00, 0x80, 0x00,
125 0x80, 0x00, 0x80, 0x00,
126 },
127 .byte_len = 20
128 },
129 [OSMO_TRAU_SYNCP_8_AMR_7K4] = {
130 /* The frame synchronisation for the speech frames for codec mode 7,40 kBit/s */
131 .name = "AMR8_74",
132 .byte_pattern = {
133 0x20, 0x00, 0x80, 0x00,
134 0x00, 0x00, 0x00, 0x00,
135 0x00, 0x00, 0x00, 0x00,
136 0x00, 0x00, 0x00, 0x00,
137 0x00, 0x00, 0x00, 0x00,
138 },
139 .byte_mask = {
140 0xe0, 0x80, 0x80, 0x80,
141 0x00, 0x00, 0x00, 0x00,
142 0x00, 0x00, 0x00, 0x00,
143 0x00, 0x00, 0x00, 0x00,
144 0x00, 0x00, 0x00, 0x00,
145 },
146 .byte_len = 20,
147 },
Harald Welte9d90eb92022-11-29 20:46:52 +0100148 [OSMO_TRAU_SYNCP_V110] = {
149 /* See Table 2 of ITU-T V.110 */
150 .name = "V110",
151 .byte_pattern = {
152 0x00, 0x80, 0x80, 0x80, 0x80,
153 0x80, 0x80, 0x80, 0x80, 0x80,
154 },
155 .byte_mask = {
156 0xff, 0x80, 0x80, 0x80, 0x80,
157 0x80, 0x80, 0x80, 0x80, 0x80,
158 },
159 .byte_len = 10,
160 },
Philipp Maiercf890392022-09-01 18:48:14 +0200161 [OSMO_TRAU_SYNCP_16_ER_CCU] = {
162 .name = "Ericsson CCU 16 kbps",
163 .byte_pattern = {
164 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169 },
170 .byte_mask = {
171 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176 },
177 .byte_len = 40,
Harald Welte78861c02020-05-14 13:28:07 +0200178 },
179};
180
Harald Welte78861c02020-05-14 13:28:07 +0200181static void expand_sync_pattern(struct sync_pattern *pat)
182{
183 osmo_pbit2ubit(pat->ubit_pattern, pat->byte_pattern, pat->byte_len*8);
184 osmo_pbit2ubit(pat->ubit_mask, pat->byte_mask, pat->byte_len*8);
185}
186
187static unsigned int count_one_bits(const ubit_t *in, unsigned int in_bits)
188{
189 unsigned int i, count = 0;
190
191 for (i = 0; i < in_bits; i++) {
192 if (in[i])
193 count++;
194 }
195 return count;
196}
197
198static void sync_pattern_register(struct sync_pattern *p)
199{
200 expand_sync_pattern(p);
201 p->bitcount = count_one_bits(p->ubit_mask, p->byte_len*8);
202}
203
204#if 0
205/*! correlate pattern with unpacked bits from buffer.
206 * \param[in] pattern sync_pattern against which we shall compare
207 * \param[in] bits unpacked bits to compare against pattern
208 * \param[in] num_bits number of unpacked bits
209 * \returns number of bits not matching pattern; -1 if insufficient bits available. */
210static int correlate_pattern_ubits(const struct sync_pattern *pattern,
211 const ubit_t *bits, size_t num_bits)
212{
213 int i, num_wrong = 0;
214
215 if (num_bits < pattern->byte_len*8)
216 return -1; /* insufficient data */
217
218 for (i = 0; i < pattern->byte_len *8; i++) {
219 /* if mask doesn't contain '1', we can skip this octet */
220 if (!pattern->ubit_mask)
221 continue;
222 if (bits[i] != pattern->ubit_pattern[i])
223 num_wrong++;
224 }
225
226 return num_wrong;
227}
228#endif
229
230struct trau_rx_sync_state {
231 /*! call-back to be called for every TRAU frame (called with
232 * bits=NULL in case of frame sync loss */
233 frame_out_cb_t out_cb;
234 /*! opaque user data; passed to out_cb */
235 void *user_data;
236
237 /*! history of received bits */
238 ubit_t history[MAX_TRAU_BYTES*8+1]; /* +1 not required, but helps to expose bugs */
239 /*! index of next-to-be-written ubit in history */
240 unsigned int history_idx;
241 /*! the pattern we are trying to sync to */
242 const struct sync_pattern *pattern;
243 /*! number of consecutive frames without sync */
244 unsigned int num_consecutive_errors;
245};
246
247/* correlate the history (up to the last received bit) against the pattern */
248static int correlate_history_against_pattern(struct trau_rx_sync_state *tss)
249{
250 const struct sync_pattern *pattern = tss->pattern;
251 int i, start, num_wrong = 0;
252
253 /* compute index of first bit in history array */
254 start = (ARRAY_SIZE(tss->history) + tss->history_idx - pattern->byte_len*8)
255 % ARRAY_SIZE(tss->history);
256
257 OSMO_ASSERT(ARRAY_SIZE(tss->history) >= pattern->byte_len*8);
258
259 for (i = 0; i < pattern->byte_len*8; i++) {
260 unsigned int pos = (start + i) % ARRAY_SIZE(tss->history);
261
262 /* if mask doesn't contain '1', we can skip this octet */
263 if (!pattern->ubit_mask[i])
264 continue;
265 if (tss->history[pos] != pattern->ubit_pattern[i])
266 num_wrong++;
267 }
268
269 return num_wrong;
270}
271
272/* add (append) one ubit to the history; wrap as needed */
273static void rx_history_add_bit(struct trau_rx_sync_state *tss, ubit_t bit)
274{
275 tss->history[tss->history_idx] = bit;
276 /* simply wrap around at the end */
277 tss->history_idx = (tss->history_idx + 1) % ARRAY_SIZE(tss->history);
278}
279
280/* append bits to history. We assume that this does NOT wrap */
281static void rx_history_add_bits(struct trau_rx_sync_state *tss, const ubit_t *bits, size_t n_bits)
282{
283 unsigned int frame_bits_remaining = tss->pattern->byte_len*8 - tss->history_idx;
284 OSMO_ASSERT(frame_bits_remaining >= n_bits);
285 memcpy(&tss->history[tss->history_idx], bits, n_bits);
286 tss->history_idx = tss->history_idx + n_bits;
287}
288
289/* align the history, i.e. next received bit is start of frame */
290static void rx_history_align(struct trau_rx_sync_state *tss)
291{
292 ubit_t tmp[sizeof(tss->history)];
293 size_t history_size = sizeof(tss->history);
294 size_t pattern_bits = tss->pattern->byte_len*8;
295 size_t first_bit = (history_size + tss->history_idx - pattern_bits) % history_size;
296 int i;
297
298 /* we need to shift the last received frame to the start of the history buffer;
299 * do this in two steps: First copy to a local buffer on the stack, using modulo-arithmetic
300 * as index into the history. Second, copy it back to history */
301
302 for (i = 0; i < pattern_bits; i++)
303 tmp[i] = tss->history[(first_bit + i) % history_size];
304
305 memcpy(tss->history, tmp, history_size);
306 tss->history_idx = 0;
307}
308
309enum trau_sync_state {
310 WAIT_FRAME_ALIGN,
311 FRAME_ALIGNED,
312 /* if at least 3 consecutive frames with each at least one framing error have been received */
313 FRAME_ALIGNMENT_LOST,
314};
315
316enum trau_sync_event {
317 TRAUSYNC_E_RESET,
318 /*! a buffer of bits was received (msgb with ubits) */
319 TRAUSYNC_E_RX_BITS,
320};
321
322static const struct value_string trau_sync_event_names[] = {
323 { TRAUSYNC_E_RESET, "RESET" },
324 { TRAUSYNC_E_RX_BITS, "RX_BITS" },
325 { 0, NULL }
326};
327
328
329static void trau_sync_wait_align(struct osmo_fsm_inst *fi, uint32_t event, void *data)
330{
331 struct trau_rx_sync_state *tss = (struct trau_rx_sync_state *) fi->priv;
332 struct ubit_buf *ubb;
333
334 switch (event) {
335 case TRAUSYNC_E_RX_BITS:
336 ubb = data;
337 /* append every bit individually + check if we have sync */
338 while (ubb_length(ubb) > 0) {
339 ubit_t bit = ubb_pull_ubit(ubb);
340 int rc;
341
342 rx_history_add_bit(tss, bit);
343 rc = correlate_history_against_pattern(tss);
344 if (!rc) {
345 osmo_fsm_inst_state_chg(fi, FRAME_ALIGNED, 0, 0);
346 /* treat remainder of input bits in correct state */
347 osmo_fsm_inst_dispatch(fi, TRAUSYNC_E_RX_BITS, ubb);
348 return;
349 }
350 }
351 break;
352 default:
353 OSMO_ASSERT(0);
354 }
355}
356
357static void trau_sync_aligned_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
358{
359 struct trau_rx_sync_state *tss = (struct trau_rx_sync_state *) fi->priv;
360 /* dispatch aligned frame to user */
361 rx_history_align(tss);
362 tss->out_cb(tss->user_data, tss->history, tss->pattern->byte_len*8);
363}
364
365static void trau_sync_aligned(struct osmo_fsm_inst *fi, uint32_t event, void *data)
366{
367 struct trau_rx_sync_state *tss = (struct trau_rx_sync_state *) fi->priv;
368 struct ubit_buf *ubb;
369 int rc;
370
371 switch (event) {
372 case TRAUSYNC_E_RX_BITS:
373 ubb = data;
374 while (ubb_length(ubb)) {
375 unsigned int frame_bits_remaining = tss->pattern->byte_len*8 - tss->history_idx;
376 if (ubb_length(ubb) < frame_bits_remaining) {
377 /* frame not filled by this message; just add data */
378 rx_history_add_bits(tss, ubb_data(ubb), ubb_length(ubb));
379 ubb_pull(ubb, ubb_length(ubb));
380 } else {
381 /* append as many bits as are missing in the current frame */
382 rx_history_add_bits(tss, ubb_data(ubb), frame_bits_remaining);
383 ubb_pull(ubb, frame_bits_remaining);
384
385 /* check if we still have frame sync */
386 rc = correlate_history_against_pattern(tss);
387 if (rc > 0) {
388 tss->num_consecutive_errors++;
389 if (tss->num_consecutive_errors >= 3) {
390 tss->history_idx = 0;
391 /* send NULL frame to user */
392 tss->out_cb(tss->user_data, NULL, 0);
393 osmo_fsm_inst_state_chg(fi, FRAME_ALIGNMENT_LOST, 1, T_SYNC);
394 osmo_fsm_inst_dispatch(fi, TRAUSYNC_E_RX_BITS, ubb);
395 return;
396 }
397 } else
398 tss->num_consecutive_errors = 0;
399
400 /* dispatch aligned frame to user */
401 tss->out_cb(tss->user_data, tss->history, tss->history_idx);
402 tss->history_idx = 0;
403 }
404 }
405 break;
406 default:
407 OSMO_ASSERT(0);
408 }
409}
410
411static void trau_sync_alignment_lost(struct osmo_fsm_inst *fi, uint32_t event, void *data)
412{
413 /* we try to restore sync for some amount of time before generating an error */
414
415 switch (event) {
416 case TRAUSYNC_E_RX_BITS:
417 trau_sync_wait_align(fi, event, data);
418 break;
419 default:
420 OSMO_ASSERT(0);
421 }
422}
423
424static void trau_sync_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
425{
426 switch (event) {
427 case TRAUSYNC_E_RESET:
428 osmo_fsm_inst_state_chg(fi, WAIT_FRAME_ALIGN, 0, 0);
429 break;
430 default:
431 OSMO_ASSERT(0);
432 }
433}
434
435static int trau_sync_timeout(struct osmo_fsm_inst *fi)
436{
437 switch (fi->T) {
438 case T_SYNC:
439 /* if Tsync expires before frame synchronization is
440 * again obtained the TRAU initiates sending of the
441 * urgent alarm pattern described in clause 4.10.2. */
442 osmo_fsm_inst_state_chg(fi, WAIT_FRAME_ALIGN, 0, 0);
443 break;
444 default:
445 OSMO_ASSERT(0);
446 }
447 return 0;
448}
449
450static const struct osmo_fsm_state trau_sync_states[] = {
451 [WAIT_FRAME_ALIGN] = {
452 .name = "WAIT_FRAME_ALIGN",
453 .in_event_mask = S(TRAUSYNC_E_RX_BITS),
454 .out_state_mask = S(FRAME_ALIGNED),
455 .action = trau_sync_wait_align,
456 },
457 [FRAME_ALIGNED] = {
458 .name = "FRAME_ALIGNED",
459 .in_event_mask = S(TRAUSYNC_E_RX_BITS),
460 .out_state_mask = S(FRAME_ALIGNMENT_LOST) | S(WAIT_FRAME_ALIGN),
461 .action = trau_sync_aligned,
462 .onenter = trau_sync_aligned_onenter,
463 },
464 [FRAME_ALIGNMENT_LOST] = {
465 .name = "FRAME_ALIGNMENT_LOST",
466 .in_event_mask = S(TRAUSYNC_E_RX_BITS),
467 .out_state_mask = S(WAIT_FRAME_ALIGN) | S(FRAME_ALIGNED),
468 .action = trau_sync_alignment_lost,
469 },
470};
471
472static struct osmo_fsm trau_sync_fsm = {
473 .name = "trau_sync",
474 .states = trau_sync_states,
475 .num_states = ARRAY_SIZE(trau_sync_states),
476 .allstate_event_mask = S(TRAUSYNC_E_RESET),
477 .allstate_action = trau_sync_allstate,
478 .timer_cb = trau_sync_timeout,
479 .log_subsys = DLGLOBAL,
480 .event_names = trau_sync_event_names,
481};
482
483
484struct osmo_fsm_inst *
485osmo_trau_sync_alloc(void *ctx, const char *name, frame_out_cb_t frame_out_cb,
Harald Weltece700742022-11-30 18:07:36 +0100486 enum osmo_trau_sync_pat_id pat_id, void *user_data)
Harald Welte78861c02020-05-14 13:28:07 +0200487{
488 struct trau_rx_sync_state *tss;
489 struct osmo_fsm_inst *fi;
490
491 if (pat_id >= ARRAY_SIZE(sync_patterns))
492 return NULL;
493
Keithbbff3042021-04-28 21:33:00 -0500494 fi = osmo_fsm_inst_alloc(&trau_sync_fsm, ctx, NULL, LOGL_INFO, name);
Harald Welte78861c02020-05-14 13:28:07 +0200495 if (!fi)
496 return NULL;
497 tss = talloc_zero(fi, struct trau_rx_sync_state);
498 if (!tss) {
499 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
500 return NULL;
501 }
502 fi->priv = tss;
503
504 tss->out_cb = frame_out_cb;
505 tss->user_data = user_data;
Harald Welte78861c02020-05-14 13:28:07 +0200506 tss->pattern = &sync_patterns[pat_id];
507
Philipp Maier8eae96f2020-07-31 20:01:42 +0200508 /* An unusued E1 timeslot normally would send an idle signal that
509 * has all bits set to one. In order to prevent false-positive
510 * synchronization on startup we set all history bits to 1, to make
511 * it look like a signal from an unused timeslot. */
512 memset(tss->history, 1, sizeof(tss->history));
513
Harald Welte78861c02020-05-14 13:28:07 +0200514 return fi;
515}
516
Harald Weltece700742022-11-30 18:07:36 +0100517void osmo_trau_sync_set_pat(struct osmo_fsm_inst *fi, enum osmo_trau_sync_pat_id pat_id)
Philipp Maier8d150012020-08-07 17:13:57 +0200518{
519 struct trau_rx_sync_state *tss = fi->priv;
520
521 tss->pattern = &sync_patterns[pat_id];
522 osmo_fsm_inst_state_chg(fi, FRAME_ALIGNMENT_LOST, 0, 0);
523}
524
Harald Welte78861c02020-05-14 13:28:07 +0200525void osmo_trau_sync_rx_ubits(struct osmo_fsm_inst *fi, const ubit_t *bits, size_t n_bits)
526{
527 struct ubit_buf ubb;
528 ubb_init(&ubb, bits, n_bits);
529 osmo_fsm_inst_dispatch(fi, TRAUSYNC_E_RX_BITS, &ubb);
530}
531
532static void __attribute__((constructor)) on_dso_load_sync(void)
533{
534 int i;
535
536 for (i = 0; i < ARRAY_SIZE(sync_patterns); i++)
537 sync_pattern_register(&sync_patterns[i]);
Harald Welteab8e0662020-08-06 11:56:52 +0200538 OSMO_ASSERT(osmo_fsm_register(&trau_sync_fsm) == 0);
Harald Welte78861c02020-05-14 13:28:07 +0200539}