blob: 4835f85d2e00c7a10ed13c5e92271b01c6432a79 [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
Philipp Maier4934ff52022-11-03 16:07:19 +010034#define MAX_TRAU_BYTES 160
Harald Welte78861c02020-05-14 13:28:07 +020035
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 },
Philipp Maierf2649502022-12-20 12:38:26 +0100179 [OSMO_TRAU_SYNCP_64_ER_CCU] = {
180 .name = "Ericsson CCU 64 kbps",
181 .byte_pattern = {
182 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
184 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
185 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
186 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
187 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
188 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
189 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
190 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
191 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
192 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
193 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
194 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
196 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
197 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
198 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
199 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
201 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
202 },
203 .byte_mask = {
204 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
205 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
206 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
207 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
208 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
209 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
210 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
211 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
212 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
214 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
215 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
216 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
219 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
220 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
221 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 },
225 .byte_len = 160,
226 },
Harald Welte78861c02020-05-14 13:28:07 +0200227};
228
Harald Welte78861c02020-05-14 13:28:07 +0200229static void expand_sync_pattern(struct sync_pattern *pat)
230{
231 osmo_pbit2ubit(pat->ubit_pattern, pat->byte_pattern, pat->byte_len*8);
232 osmo_pbit2ubit(pat->ubit_mask, pat->byte_mask, pat->byte_len*8);
233}
234
235static unsigned int count_one_bits(const ubit_t *in, unsigned int in_bits)
236{
237 unsigned int i, count = 0;
238
239 for (i = 0; i < in_bits; i++) {
240 if (in[i])
241 count++;
242 }
243 return count;
244}
245
246static void sync_pattern_register(struct sync_pattern *p)
247{
248 expand_sync_pattern(p);
249 p->bitcount = count_one_bits(p->ubit_mask, p->byte_len*8);
250}
251
252#if 0
253/*! correlate pattern with unpacked bits from buffer.
254 * \param[in] pattern sync_pattern against which we shall compare
255 * \param[in] bits unpacked bits to compare against pattern
256 * \param[in] num_bits number of unpacked bits
257 * \returns number of bits not matching pattern; -1 if insufficient bits available. */
258static int correlate_pattern_ubits(const struct sync_pattern *pattern,
259 const ubit_t *bits, size_t num_bits)
260{
261 int i, num_wrong = 0;
262
263 if (num_bits < pattern->byte_len*8)
264 return -1; /* insufficient data */
265
266 for (i = 0; i < pattern->byte_len *8; i++) {
267 /* if mask doesn't contain '1', we can skip this octet */
268 if (!pattern->ubit_mask)
269 continue;
270 if (bits[i] != pattern->ubit_pattern[i])
271 num_wrong++;
272 }
273
274 return num_wrong;
275}
276#endif
277
278struct trau_rx_sync_state {
279 /*! call-back to be called for every TRAU frame (called with
280 * bits=NULL in case of frame sync loss */
281 frame_out_cb_t out_cb;
282 /*! opaque user data; passed to out_cb */
283 void *user_data;
284
285 /*! history of received bits */
286 ubit_t history[MAX_TRAU_BYTES*8+1]; /* +1 not required, but helps to expose bugs */
287 /*! index of next-to-be-written ubit in history */
288 unsigned int history_idx;
289 /*! the pattern we are trying to sync to */
290 const struct sync_pattern *pattern;
291 /*! number of consecutive frames without sync */
292 unsigned int num_consecutive_errors;
293};
294
295/* correlate the history (up to the last received bit) against the pattern */
296static int correlate_history_against_pattern(struct trau_rx_sync_state *tss)
297{
298 const struct sync_pattern *pattern = tss->pattern;
299 int i, start, num_wrong = 0;
300
301 /* compute index of first bit in history array */
302 start = (ARRAY_SIZE(tss->history) + tss->history_idx - pattern->byte_len*8)
303 % ARRAY_SIZE(tss->history);
304
305 OSMO_ASSERT(ARRAY_SIZE(tss->history) >= pattern->byte_len*8);
306
307 for (i = 0; i < pattern->byte_len*8; i++) {
308 unsigned int pos = (start + i) % ARRAY_SIZE(tss->history);
309
310 /* if mask doesn't contain '1', we can skip this octet */
311 if (!pattern->ubit_mask[i])
312 continue;
313 if (tss->history[pos] != pattern->ubit_pattern[i])
314 num_wrong++;
315 }
316
317 return num_wrong;
318}
319
320/* add (append) one ubit to the history; wrap as needed */
321static void rx_history_add_bit(struct trau_rx_sync_state *tss, ubit_t bit)
322{
323 tss->history[tss->history_idx] = bit;
324 /* simply wrap around at the end */
325 tss->history_idx = (tss->history_idx + 1) % ARRAY_SIZE(tss->history);
326}
327
328/* append bits to history. We assume that this does NOT wrap */
329static void rx_history_add_bits(struct trau_rx_sync_state *tss, const ubit_t *bits, size_t n_bits)
330{
331 unsigned int frame_bits_remaining = tss->pattern->byte_len*8 - tss->history_idx;
332 OSMO_ASSERT(frame_bits_remaining >= n_bits);
333 memcpy(&tss->history[tss->history_idx], bits, n_bits);
334 tss->history_idx = tss->history_idx + n_bits;
335}
336
337/* align the history, i.e. next received bit is start of frame */
338static void rx_history_align(struct trau_rx_sync_state *tss)
339{
340 ubit_t tmp[sizeof(tss->history)];
341 size_t history_size = sizeof(tss->history);
342 size_t pattern_bits = tss->pattern->byte_len*8;
343 size_t first_bit = (history_size + tss->history_idx - pattern_bits) % history_size;
344 int i;
345
346 /* we need to shift the last received frame to the start of the history buffer;
347 * do this in two steps: First copy to a local buffer on the stack, using modulo-arithmetic
348 * as index into the history. Second, copy it back to history */
349
350 for (i = 0; i < pattern_bits; i++)
351 tmp[i] = tss->history[(first_bit + i) % history_size];
352
353 memcpy(tss->history, tmp, history_size);
354 tss->history_idx = 0;
355}
356
357enum trau_sync_state {
358 WAIT_FRAME_ALIGN,
359 FRAME_ALIGNED,
360 /* if at least 3 consecutive frames with each at least one framing error have been received */
361 FRAME_ALIGNMENT_LOST,
362};
363
364enum trau_sync_event {
365 TRAUSYNC_E_RESET,
366 /*! a buffer of bits was received (msgb with ubits) */
367 TRAUSYNC_E_RX_BITS,
368};
369
370static const struct value_string trau_sync_event_names[] = {
371 { TRAUSYNC_E_RESET, "RESET" },
372 { TRAUSYNC_E_RX_BITS, "RX_BITS" },
373 { 0, NULL }
374};
375
376
377static void trau_sync_wait_align(struct osmo_fsm_inst *fi, uint32_t event, void *data)
378{
379 struct trau_rx_sync_state *tss = (struct trau_rx_sync_state *) fi->priv;
380 struct ubit_buf *ubb;
381
382 switch (event) {
383 case TRAUSYNC_E_RX_BITS:
384 ubb = data;
385 /* append every bit individually + check if we have sync */
386 while (ubb_length(ubb) > 0) {
387 ubit_t bit = ubb_pull_ubit(ubb);
388 int rc;
389
390 rx_history_add_bit(tss, bit);
391 rc = correlate_history_against_pattern(tss);
392 if (!rc) {
393 osmo_fsm_inst_state_chg(fi, FRAME_ALIGNED, 0, 0);
394 /* treat remainder of input bits in correct state */
395 osmo_fsm_inst_dispatch(fi, TRAUSYNC_E_RX_BITS, ubb);
396 return;
397 }
398 }
399 break;
400 default:
401 OSMO_ASSERT(0);
402 }
403}
404
405static void trau_sync_aligned_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
406{
407 struct trau_rx_sync_state *tss = (struct trau_rx_sync_state *) fi->priv;
408 /* dispatch aligned frame to user */
409 rx_history_align(tss);
410 tss->out_cb(tss->user_data, tss->history, tss->pattern->byte_len*8);
411}
412
413static void trau_sync_aligned(struct osmo_fsm_inst *fi, uint32_t event, void *data)
414{
415 struct trau_rx_sync_state *tss = (struct trau_rx_sync_state *) fi->priv;
416 struct ubit_buf *ubb;
417 int rc;
418
419 switch (event) {
420 case TRAUSYNC_E_RX_BITS:
421 ubb = data;
422 while (ubb_length(ubb)) {
423 unsigned int frame_bits_remaining = tss->pattern->byte_len*8 - tss->history_idx;
424 if (ubb_length(ubb) < frame_bits_remaining) {
425 /* frame not filled by this message; just add data */
426 rx_history_add_bits(tss, ubb_data(ubb), ubb_length(ubb));
427 ubb_pull(ubb, ubb_length(ubb));
428 } else {
429 /* append as many bits as are missing in the current frame */
430 rx_history_add_bits(tss, ubb_data(ubb), frame_bits_remaining);
431 ubb_pull(ubb, frame_bits_remaining);
432
433 /* check if we still have frame sync */
434 rc = correlate_history_against_pattern(tss);
435 if (rc > 0) {
436 tss->num_consecutive_errors++;
437 if (tss->num_consecutive_errors >= 3) {
438 tss->history_idx = 0;
439 /* send NULL frame to user */
440 tss->out_cb(tss->user_data, NULL, 0);
441 osmo_fsm_inst_state_chg(fi, FRAME_ALIGNMENT_LOST, 1, T_SYNC);
442 osmo_fsm_inst_dispatch(fi, TRAUSYNC_E_RX_BITS, ubb);
443 return;
444 }
445 } else
446 tss->num_consecutive_errors = 0;
447
448 /* dispatch aligned frame to user */
449 tss->out_cb(tss->user_data, tss->history, tss->history_idx);
450 tss->history_idx = 0;
451 }
452 }
453 break;
454 default:
455 OSMO_ASSERT(0);
456 }
457}
458
459static void trau_sync_alignment_lost(struct osmo_fsm_inst *fi, uint32_t event, void *data)
460{
461 /* we try to restore sync for some amount of time before generating an error */
462
463 switch (event) {
464 case TRAUSYNC_E_RX_BITS:
465 trau_sync_wait_align(fi, event, data);
466 break;
467 default:
468 OSMO_ASSERT(0);
469 }
470}
471
472static void trau_sync_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
473{
474 switch (event) {
475 case TRAUSYNC_E_RESET:
476 osmo_fsm_inst_state_chg(fi, WAIT_FRAME_ALIGN, 0, 0);
477 break;
478 default:
479 OSMO_ASSERT(0);
480 }
481}
482
483static int trau_sync_timeout(struct osmo_fsm_inst *fi)
484{
485 switch (fi->T) {
486 case T_SYNC:
487 /* if Tsync expires before frame synchronization is
488 * again obtained the TRAU initiates sending of the
489 * urgent alarm pattern described in clause 4.10.2. */
490 osmo_fsm_inst_state_chg(fi, WAIT_FRAME_ALIGN, 0, 0);
491 break;
492 default:
493 OSMO_ASSERT(0);
494 }
495 return 0;
496}
497
498static const struct osmo_fsm_state trau_sync_states[] = {
499 [WAIT_FRAME_ALIGN] = {
500 .name = "WAIT_FRAME_ALIGN",
501 .in_event_mask = S(TRAUSYNC_E_RX_BITS),
502 .out_state_mask = S(FRAME_ALIGNED),
503 .action = trau_sync_wait_align,
504 },
505 [FRAME_ALIGNED] = {
506 .name = "FRAME_ALIGNED",
507 .in_event_mask = S(TRAUSYNC_E_RX_BITS),
508 .out_state_mask = S(FRAME_ALIGNMENT_LOST) | S(WAIT_FRAME_ALIGN),
509 .action = trau_sync_aligned,
510 .onenter = trau_sync_aligned_onenter,
511 },
512 [FRAME_ALIGNMENT_LOST] = {
513 .name = "FRAME_ALIGNMENT_LOST",
514 .in_event_mask = S(TRAUSYNC_E_RX_BITS),
515 .out_state_mask = S(WAIT_FRAME_ALIGN) | S(FRAME_ALIGNED),
516 .action = trau_sync_alignment_lost,
517 },
518};
519
520static struct osmo_fsm trau_sync_fsm = {
521 .name = "trau_sync",
522 .states = trau_sync_states,
523 .num_states = ARRAY_SIZE(trau_sync_states),
524 .allstate_event_mask = S(TRAUSYNC_E_RESET),
525 .allstate_action = trau_sync_allstate,
526 .timer_cb = trau_sync_timeout,
527 .log_subsys = DLGLOBAL,
528 .event_names = trau_sync_event_names,
529};
530
531
532struct osmo_fsm_inst *
533osmo_trau_sync_alloc(void *ctx, const char *name, frame_out_cb_t frame_out_cb,
Harald Weltece700742022-11-30 18:07:36 +0100534 enum osmo_trau_sync_pat_id pat_id, void *user_data)
Harald Welte78861c02020-05-14 13:28:07 +0200535{
536 struct trau_rx_sync_state *tss;
537 struct osmo_fsm_inst *fi;
538
539 if (pat_id >= ARRAY_SIZE(sync_patterns))
540 return NULL;
541
Keithbbff3042021-04-28 21:33:00 -0500542 fi = osmo_fsm_inst_alloc(&trau_sync_fsm, ctx, NULL, LOGL_INFO, name);
Harald Welte78861c02020-05-14 13:28:07 +0200543 if (!fi)
544 return NULL;
545 tss = talloc_zero(fi, struct trau_rx_sync_state);
546 if (!tss) {
547 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
548 return NULL;
549 }
550 fi->priv = tss;
551
552 tss->out_cb = frame_out_cb;
553 tss->user_data = user_data;
Harald Welte78861c02020-05-14 13:28:07 +0200554 tss->pattern = &sync_patterns[pat_id];
555
Philipp Maier8eae96f2020-07-31 20:01:42 +0200556 /* An unusued E1 timeslot normally would send an idle signal that
557 * has all bits set to one. In order to prevent false-positive
558 * synchronization on startup we set all history bits to 1, to make
559 * it look like a signal from an unused timeslot. */
560 memset(tss->history, 1, sizeof(tss->history));
561
Harald Welte78861c02020-05-14 13:28:07 +0200562 return fi;
563}
564
Harald Weltece700742022-11-30 18:07:36 +0100565void 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 +0200566{
567 struct trau_rx_sync_state *tss = fi->priv;
568
569 tss->pattern = &sync_patterns[pat_id];
570 osmo_fsm_inst_state_chg(fi, FRAME_ALIGNMENT_LOST, 0, 0);
571}
572
Harald Welte78861c02020-05-14 13:28:07 +0200573void osmo_trau_sync_rx_ubits(struct osmo_fsm_inst *fi, const ubit_t *bits, size_t n_bits)
574{
575 struct ubit_buf ubb;
576 ubb_init(&ubb, bits, n_bits);
577 osmo_fsm_inst_dispatch(fi, TRAUSYNC_E_RX_BITS, &ubb);
578}
579
580static void __attribute__((constructor)) on_dso_load_sync(void)
581{
582 int i;
583
584 for (i = 0; i < ARRAY_SIZE(sync_patterns); i++)
585 sync_pattern_register(&sync_patterns[i]);
Harald Welteab8e0662020-08-06 11:56:52 +0200586 OSMO_ASSERT(osmo_fsm_register(&trau_sync_fsm) == 0);
Harald Welte78861c02020-05-14 13:28:07 +0200587}