blob: 4571c5d81ef0bc61e50beaf98eed0cb9f4302b94 [file] [log] [blame]
Harald Weltef5e72642022-10-30 22:20:55 +01001/* Osmocom Software Defined E1
2 * Implements ITU-T Rec. G.704 Section 2.3
3 *
4 * (C) 2018 by Harald Welte <laforge@gnumonks.org>
5 * All Rights Reserved
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published
11 * by the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
20#include <stdbool.h>
21#include <stdint.h>
22#include <unistd.h>
23#include <errno.h>
24#include <string.h>
25
26#include <osmocom/core/msgb.h>
27#include <osmocom/core/linuxlist.h>
28#include <osmocom/core/logging.h>
29#include <osmocom/core/fsm.h>
30
31#include "crc4itu.h"
32#include "osmo_e1f.h"
33
34#define S(x) (1 << (x))
35
36/* Frame Alignment Signal (BIT1 may be overwritten with CRC-4) */
37#define G704_E1_FAS 0x1B
38
39static inline bool is_correct_fas(uint8_t bt) {
40 if ((bt & 0x7F) == G704_E1_FAS)
41 return true;
42 else
43 return false;
44}
45
46/* are we in SMF II (true) or I (false) */
47static inline bool is_smf_II(const struct osmo_e1f_tx_state *tx) {
48 if (tx->frame_nr >= 8)
49 return true;
50 return false;
51}
52
53static struct osmo_fsm e1_align_fsm;
54static void align_fsm_reset(struct osmo_e1f_instance *e1i);
55
56static void notify_user(struct osmo_e1f_instance *e1i, enum osmo_e1f_notify_event evt,
57 bool present, void *priv)
58{
59 if (!e1i->notify_cb)
60 return;
61 e1i->notify_cb(e1i, evt, present, priv);
62}
63
64/*! Initialize a (caller-allocated) Osmocom E1 Instance
65 * \param[inout] e1i E1 Instance to be initialized
66 * \returns 0 on success, negative on error */
67int osmo_e1f_instance_init(struct osmo_e1f_instance *e1i, const char *name, e1_notify_cb cb,
68 bool crc4_enabled, void *priv)
69{
70 int i;
71
72 e1i->crc4_enabled = crc4_enabled;
73 e1i->notify_cb = cb;
74 e1i->tx.sa4_sa8 = 0x00;
75
76 e1i->priv = priv;
77
78 for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) {
79 struct osmo_e1f_instance_ts *e1t = &e1i->ts[i];
80 e1t->ts_nr = i;
81 e1t->inst = e1i;
82 INIT_LLIST_HEAD(&e1t->tx.queue);
83
84 e1t->rx.granularity = 256;
85 }
86
87 e1i->rx.fi = osmo_fsm_inst_alloc(&e1_align_fsm, NULL, e1i, LOGL_DEBUG, name);
88 if (!e1i->rx.fi)
89 return -1;
90
91 osmo_e1f_instance_reset(e1i);
92
93 return 0;
94}
95
96/*! stop E1 timeslot; release any pending rx/tx buffers
97 * \param[in] e1t Timeslot which we are to stop, disable and release buffers */
98void osmo_e1f_ts_reset(struct osmo_e1f_instance_ts *e1t)
99{
100 e1t->tx.underruns = 0;
101 msgb_queue_free(&e1t->tx.queue);
102
103 e1t->rx.enabled = false;
104 msgb_free(e1t->rx.msg);
105 e1t->rx.msg = NULL;
106
107 osmo_isdnhdlc_rcv_init(&e1t->rx.hdlc, OSMO_HDLC_F_BITREVERSE);
108 //osmo_isdnhdlc_rcv_init(&e1t->rx.hdlc, 0);
109 osmo_isdnhdlc_out_init(&e1t->tx.hdlc, 0);
110}
111
112/*! stop E1 instance; stops all timeslots and releases any pending rx/tx buffers
113 * \param[in] e1t E1 instance which we are to stop */
114void osmo_e1f_instance_reset(struct osmo_e1f_instance *e1i)
115{
116 int i;
117
118 align_fsm_reset(e1i);
119
120 e1i->tx.remote_alarm = false;
121 e1i->tx.crc4_error = false;
122 e1i->tx.frame_nr = 0;
123 e1i->tx.crc4_last_smf = 0;
124 e1i->tx.crc4 = crc4itu_init();
125
126 e1i->rx.frame_nr = 0;
127 memset(&e1i->rx.ts0_history, 0, sizeof(e1i->rx.ts0_history));
128 e1i->rx.ts0_hist_len = 0;
129 e1i->rx.remote_alarm = false;
130 e1i->rx.remote_crc4_error = false;
131 e1i->rx.num_ts0_in_mframe_search = 0;
132
133 for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) {
134 struct osmo_e1f_instance_ts *e1t = &e1i->ts[i];
135 osmo_e1f_ts_reset(e1t);
136 }
137}
138
139/*! obtain pointer to TS given by instance + timeslot number
140 * \param[in] e1i E1 intance on which we work
141 * \param[in] ts_nr E1 timeslot number (1..31)
142 * \returns pointer to timeslot; NULL on error */
143struct osmo_e1f_instance_ts *osmo_e1f_instance_ts(struct osmo_e1f_instance *e1i, uint8_t ts_nr)
144{
145 if (ts_nr == 0 || ts_nr >= ARRAY_SIZE(e1i->ts))
146 return NULL;
147
148 return &e1i->ts[ts_nr];
149}
150
151/*! configure an E1 timeslot
152 * \param[in] e1t Timeslot which we are to configure
153 * \param[in] granularity granularity (buffer size) to use on Rx
154 * \param[in] enable enable (true) or disalble (false) receiving on this TS
155 * \param[in] mode the mode for this timeslot (raw or hdlc)
156 * \return 0 on success; negative on error */
157int osmo_e1f_ts_config(struct osmo_e1f_instance_ts *e1t, e1_data_cb cb, unsigned int granularity,
158 bool enable, enum osmo_e1f_ts_mode mode)
159{
160 e1t->rx.data_cb = cb;
161 e1t->rx.enabled = enable;
162 e1t->rx.granularity = granularity;
163 e1t->mode = mode;
164
165 return 0;
166}
167
168const struct value_string osmo_e1f_notifv_evt_names[] = {
169 { E1_NTFY_EVT_ALIGN_FRAME, "Aligned to Frame" },
170 { E1_NTFY_EVT_ALIGN_CRC_MFRAME, "Aligned to CRC4-Multiframe" },
171 { E1_NTFY_EVT_CRC_ERROR, "CRC Error detected (local)" },
172 { E1_NTFY_EVT_REMOTE_CRC_ERROR, "CRC Error reported (remote)" },
173 { E1_NTFY_EVT_REMOTE_ALARM, "Remote Alarm condition repoorted" },
174 { 0, NULL }
175};
176
177/***********************************************************************
178 * Transmit Side
179 ***********************************************************************/
180
181/*! Enqueue a message buffer of to-be-transmitted data for a timeslot
182 * \param[in] e1i E1 instance for which to enqueue
183 * \param[in] ts_nr Timeslot number on which data is to be transmitted
184 * \param[in] msg Message buffer storing the to-be-transmitted data
185 * \returns 0 on success; negative in case of error.
186 *
187 * Ownership of \a msg is transferred from caller into this function, but only
188 * in case of successful execution (return 0)!
189 */
190void osmo_e1f_ts_enqueue(struct osmo_e1f_instance_ts *e1t, struct msgb *msg)
191{
192 msgb_enqueue(&e1t->tx.queue, msg);
193}
194
195/* obtain a CRC4 bit for the current frame number */
196static uint8_t e1_pull_crc4_bit(struct osmo_e1f_instance *e1i)
197{
198 /* If CRC-4 is disabled, all CRC bits shall be '1' */
199 if (e1i->crc4_enabled == 0)
200 return 0x01;
201
202 /* CRC is transmitted MSB first */
203 switch (e1i->tx.frame_nr % 8) {
204 case 0:
205 return (e1i->tx.crc4_last_smf >> 3) & 1;
206 case 2:
207 return (e1i->tx.crc4_last_smf >> 2) & 1;
208 case 4:
209 return (e1i->tx.crc4_last_smf >> 1) & 1;
210 case 6:
211 return (e1i->tx.crc4_last_smf >> 0) & 1;
212 default:
213 OSMO_ASSERT(0);
214 }
215}
216
217/* pull a single to-be-transmitted byte for TS0 */
218static uint8_t e1_pull_ts0(struct osmo_e1f_instance *e1i)
219{
220 uint8_t ret;
221
222 /* according to Table 5B/G.704 - CRC-4 multiframe structure */
223 if ((e1i->tx.frame_nr % 2) == 0) {
224 /* FAS */
225 ret = G704_E1_FAS | (e1_pull_crc4_bit(e1i) << 7);
226 } else {
227 switch (e1i->tx.frame_nr) {
228 case 1:
229 case 3:
230 case 7:
231 ret = 0x40;
232 break;
233 case 5:
234 case 9:
235 case 11:
236 ret = 0xC0;
237 break;
238 case 13:
239 case 15:
240 ret = 0x40;
241 if (e1i->tx.crc4_error)
242 ret |= 0x80;
243 break;
244 }
245 ret |= e1i->tx.sa4_sa8;
246 if (e1i->tx.remote_alarm)
247 ret |= 0x20;
248 }
249
250 /* re-set CRC4 at start of sub-multiframe */
251 if (e1i->tx.frame_nr == 0 || e1i->tx.frame_nr == 8) {
252 e1i->tx.crc4_last_smf = e1i->tx.crc4;
253 e1i->tx.crc4 = 0;
254 }
255
256 /* increment frame number modulo 16 */
257 e1i->tx.frame_nr = (e1i->tx.frame_nr + 1) % 16;
258
259 return ret;
260}
261
262/* pull a single to-be-transmitted byte for TS1..31 */
263static uint8_t e1_pull_tsN(struct osmo_e1f_instance_ts *e1t)
264{
265 struct msgb *msg = llist_first_entry_or_null(&e1t->tx.queue, struct msgb, list);
266 uint8_t *cur;
267
268retry:
269 /* if there's no message to transmit */
270 if (!msg) {
271 e1t->tx.underruns++;
272 return 0xFF;
273 }
274 if (msgb_length(msg) <= 0) {
275 llist_del(&msg->list);
276 msgb_free(msg);
277 msg = llist_first_entry_or_null(&e1t->tx.queue, struct msgb, list);
278 goto retry;
279 }
280 cur = msgb_pull(msg, 1);
281 return *cur;
282}
283
284/* update the current in-progress CRC4 value with data from \a out_frame */
285static void e1_tx_update_crc4(struct osmo_e1f_instance *e1i, const uint8_t *out_frame)
286{
287 uint8_t ts0;
288
289 ts0 = out_frame[0];
290 /* mask off the C bits */
291 if (is_correct_fas(ts0))
292 ts0 &= 0x7F;
293 e1i->tx.crc4 = crc4itu_update(e1i->tx.crc4, &ts0, 1);
294 /* add the remaining bytes/bits */
295 e1i->tx.crc4 = crc4itu_update(e1i->tx.crc4, out_frame+1, ARRAY_SIZE(e1i->ts)-1);
296}
297
298/*! Pull one to-be-transmitted E1 frame (256bits) from the E1 instance
299 * \param e1i E1 instance for which the frame shall be generated
300 * \param[out] out_frame callee-allocated buffer to which function stores 32 bytes
301 * \returns 0 on success, negative on error */
302int osmo_e1f_pull_tx_frame(struct osmo_e1f_instance *e1i, uint8_t *out_frame)
303{
304 int i;
305
306 /* generate TS0 */
307 out_frame[0] = e1_pull_ts0(e1i);
308
309 /* generate TS1..31 */
310 for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) {
311 struct osmo_e1f_instance_ts *e1t = &e1i->ts[i];
312 /* get next to-be-transmitted byte from the TS */
313 out_frame[i] = e1_pull_tsN(e1t);
314 }
315 /* update our CRC4 computation */
316 e1_tx_update_crc4(e1i, out_frame);
317
318 return 0;
319}
320
321/***********************************************************************
322 * Receiver Side
323 ***********************************************************************/
324
325/* According to Figure 2 / ITU-T G.706 */
326enum e1_align_state {
327 /* Frame Alignment Search */
328 E1_AS_SEARCH_FRAME,
329 /* CRC multiframe alignment search */
330 E1_AS_SEARCH_CRC_MFRAME,
331 /* monitoring for incorrect frame alignment and error performance using CRC */
332 E1_AS_ALIGNED_CRC_MFRAME,
333 /* no CRC: just frame alignment loss check */
334 E1_AS_ALIGNED_BASIC,
335};
336
337enum e1_align_event {
338 /* received a TS0 octet */
339 E1_AE_RX_TS0,
340 E1_AE_RESET
341};
342
343static const struct value_string e1_align_evt_names[] = {
344 { E1_AE_RX_TS0, "E1_AE_RX_TS0" },
345 { E1_AE_RESET, "E1_AE_RESET" },
346 { 0, NULL }
347};
348
349/* get a TS0 byte from the history. delta 0 == current, delte 1 == previous, ... */
350static uint8_t get_ts0_hist(struct osmo_e1f_instance *e1i, uint8_t delta)
351{
352 return e1i->rx.ts0_history[((e1i->rx.frame_nr + 16)-delta) % 16];
353}
354
355/* ITU-T G.706 Section 4.1.1 */
356static bool frame_alignment_lost(struct osmo_e1f_instance *e1i)
357{
358 if (e1i->rx.frame_nr % 2)
359 return false;
360
361 /* Frame alignment will be assumed to have been lost when three consecutive incorrect
362 * frame alignment signals have been received. */
363 if (!is_correct_fas(get_ts0_hist(e1i, 0)) &&
364 !is_correct_fas(get_ts0_hist(e1i, 2)) &&
365 !is_correct_fas(get_ts0_hist(e1i, 4)))
366 return true;
367 else
368 return false;
369}
370
371/* ITU-T G.706 Section 4.1.2 */
372static bool frame_alignment_recovered(struct osmo_e1f_instance *e1i)
373{
374 /* two consecutive FAS with one non-FAS interspersed */
375 if (is_correct_fas(get_ts0_hist(e1i, 0)) &&
376 !is_correct_fas(get_ts0_hist(e1i, 1)) &&
377 is_correct_fas(get_ts0_hist(e1i, 2)))
378 return true;
379 else
380 return false;
381}
382
383/* ITU-T G.706 Section 4.2 */
384static bool crc_mframe_alignment_achieved(struct osmo_e1f_instance *e1i)
385{
386 /* if current TS0 byte is FAS, we cannot detect alignment */
387 if (is_correct_fas(get_ts0_hist(e1i, 0)))
388 return false;
389 if ((get_ts0_hist(e1i, 0) >> 7) == 1 &&
390 (get_ts0_hist(e1i, 2) >> 7) == 1 &&
391 (get_ts0_hist(e1i, 4) >> 7) == 0 &&
392 (get_ts0_hist(e1i, 6) >> 7) == 1 &&
393 (get_ts0_hist(e1i, 8) >> 7) == 0 &&
394 (get_ts0_hist(e1i, 10) >> 7) == 0)
395 return true;
396 else
397 return false;
398}
399
400/* Get the CRC4 that was received from our Rx TS0 history */
401static uint8_t crc4_from_ts0_hist(struct osmo_e1f_instance *e1i, bool smf2)
402{
403 uint8_t crc = 0;
404 uint8_t offset = 0;
405
406 if (smf2)
407 offset = 8;
408
409 crc |= (e1i->rx.ts0_history[0+offset] >> 7) << 3;
410 crc |= (e1i->rx.ts0_history[2+offset] >> 7) << 2;
411 crc |= (e1i->rx.ts0_history[4+offset] >> 7) << 1;
412 crc |= (e1i->rx.ts0_history[6+offset] >> 7) << 0;
413
414 return crc;
415}
416
417/* update the current in-progress CRC4 value with data from \a rx_frame */
418static void e1_rx_update_crc4(struct osmo_e1f_instance *e1i, const uint8_t *rx_frame)
419{
420 uint8_t ts0;
421
422 ts0 = rx_frame[0];
423 /* mask off the C bits */
424 if (is_correct_fas(ts0))
425 ts0 &= 0x7F;
426 e1i->rx.crc4 = crc4itu_update(e1i->rx.crc4, &ts0, 1);
427 /* add the remaining bytes/bits */
428 e1i->rx.crc4 = crc4itu_update(e1i->rx.crc4, rx_frame+1, ARRAY_SIZE(e1i->ts)-1);
429}
430
431/* FSM State handler */
432static void e1_align_search_frame(struct osmo_fsm_inst *fi, uint32_t event, void *data)
433{
434 struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv;
435
436 if (frame_alignment_recovered(e1i)) {
437 /* if we detected the 2nd FAS, we must be in FN 2 (or at least FN%2=0 */
438 e1i->rx.frame_nr = 2;
439 notify_user(e1i, E1_NTFY_EVT_ALIGN_FRAME, true, NULL);
440 osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_CRC_MFRAME, 0, 0);
441 }
442}
443
444/* FSM State handler */
445static void e1_align_search_crc_mframe(struct osmo_fsm_inst *fi, uint32_t event, void *data)
446{
447 struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv;
448
449 if (crc_mframe_alignment_achieved(e1i)) {
450 /* if we detected the 6-bit CRC multiframe signal, we must be in FN 11 */
451 e1i->rx.frame_nr = 11;
452 /* FIXME: "at least two valid CRC multiframe alignment signals can be located within
453 * 8 ms, the time separating two CRC multiframe alignment signals being 2 ms or a
454 * multiple of 2 ms" */
455 notify_user(e1i, E1_NTFY_EVT_ALIGN_CRC_MFRAME, true, NULL);
456 osmo_fsm_inst_state_chg(fi, E1_AS_ALIGNED_CRC_MFRAME, 0, 0);
457 } else {
458 /* if no mframe alignment is established within 8ms (64 frames), fall back */
459 if (e1i->rx.num_ts0_in_mframe_search >= 64) {
460 e1i->rx.num_ts0_in_mframe_search = 0;
461 osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0);
462 }
463 e1i->rx.num_ts0_in_mframe_search++;
464 }
465}
466
467static void e1_aligned_common(struct osmo_e1f_instance *e1i)
468{
469 uint8_t inb = get_ts0_hist(e1i, 0);
470
471 /* All non-FAS frames contain "A" bit in TS0 */
472 if (!is_correct_fas(inb & 0x7F)) {
473 bool old_alarm = e1i->rx.remote_alarm;
474 /* frame not containing the frame alignment signal */
475 if (inb & 0x20)
476 e1i->rx.remote_alarm = true;
477 else
478 e1i->rx.remote_alarm = false;
479 if (old_alarm != e1i->rx.remote_alarm)
480 notify_user(e1i, E1_NTFY_EVT_REMOTE_ALARM, e1i->rx.remote_alarm, NULL);
481 }
482}
483
484/* FSM State handler */
485static void e1_aligned_crc_mframe(struct osmo_fsm_inst *fi, uint32_t event, void *data)
486{
487 struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv;
488
489 if (frame_alignment_lost(e1i)) {
490 osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0);
491 return;
492 }
493
494 if (e1i->crc4_enabled) {
495 uint8_t crc_rx;
496 bool crc4_error;
497
498 /* check if we just received a complete CRC4 */
499 switch (e1i->rx.frame_nr) {
500 case 7:
501 case 15:
502 crc_rx = crc4_from_ts0_hist(e1i, e1i->rx.frame_nr == 15 ? true : false);
503 if (crc_rx != e1i->rx.crc4_last_smf)
504 crc4_error = true;
505 else
506 crc4_error = false;
507 if (crc4_error != e1i->tx.crc4_error) {
508 notify_user(e1i, E1_NTFY_EVT_CRC_ERROR, crc4_error, NULL);
509 e1i->tx.crc4_error = crc4_error;
510 }
511 /* rotate computed CRC4 one further */
512 e1i->rx.crc4_last_smf = e1i->rx.crc4;
513 e1i->rx.crc4 = crc4itu_init();
514 break;
515 default:
516 break;
517 }
518
519 /* check if the remote side reports any CRC errors */
520 switch (e1i->rx.frame_nr) {
521 case 13:
522 case 15:
523 crc4_error = false;
524 if ((get_ts0_hist(e1i, 0) >> 7) == 0)
525 crc4_error = true;
526 if (crc4_error != e1i->rx.remote_crc4_error) {
527 notify_user(e1i, E1_NTFY_EVT_REMOTE_CRC_ERROR, crc4_error, NULL);
528 e1i->rx.remote_crc4_error = crc4_error;
529 }
530 break;
531 }
532 }
533
534 e1_aligned_common(e1i);
535}
536
537/* FSM State handler */
538static void e1_aligned_basic(struct osmo_fsm_inst *fi, uint32_t event, void *data)
539{
540 struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv;
541
542 if (frame_alignment_lost(e1i)) {
543 osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0);
544 return;
545 }
546
547 e1_aligned_common(e1i);
548}
549
550static const struct osmo_fsm_state e1_align_states[] = {
551 [E1_AS_SEARCH_FRAME] = {
552 .name = "SEARCH_FRAME",
553 .in_event_mask = S(E1_AE_RX_TS0),
554 .out_state_mask = S(E1_AS_SEARCH_FRAME) |
555 S(E1_AS_SEARCH_CRC_MFRAME) |
556 S(E1_AS_ALIGNED_BASIC),
557 .action = e1_align_search_frame,
558 },
559 [E1_AS_SEARCH_CRC_MFRAME] = {
560 .name = "SEARCH_CRC_MFRAME",
561 .in_event_mask = S(E1_AE_RX_TS0),
562 .out_state_mask = S(E1_AS_SEARCH_FRAME) |
563 S(E1_AS_SEARCH_CRC_MFRAME) |
564 S(E1_AS_ALIGNED_CRC_MFRAME),
565 .action = e1_align_search_crc_mframe,
566 },
567 [E1_AS_ALIGNED_CRC_MFRAME] = {
568 .name = "ALIGNED_CRC_MFRAME",
569 .in_event_mask = S(E1_AE_RX_TS0),
570 .out_state_mask = S(E1_AS_SEARCH_FRAME) |
571 S(E1_AS_SEARCH_CRC_MFRAME) |
572 S(E1_AS_ALIGNED_CRC_MFRAME),
573 .action = e1_aligned_crc_mframe,
574 },
575 [E1_AS_ALIGNED_BASIC] = {
576 .name = "ALIGNED_BASIC",
577 .in_event_mask = S(E1_AE_RX_TS0),
578 .out_state_mask = S(E1_AS_SEARCH_FRAME),
579 .action = e1_aligned_basic,
580 },
581};
582
583static void e1_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
584{
585 struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv;
586
587 switch (event) {
588 case E1_AE_RESET:
589 e1i->rx.num_ts0_in_mframe_search = 0;
590 osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0);
591 break;
592 }
593}
594
595static struct osmo_fsm e1_align_fsm = {
596 .name = "e1-align",
597 .states = e1_align_states,
598 .num_states = ARRAY_SIZE(e1_align_states),
599 .allstate_event_mask = S(E1_AE_RESET),
600 .allstate_action = e1_allstate,
601 .log_subsys = DLGLOBAL,
602 .event_names = e1_align_evt_names,
603};
604
605static void align_fsm_reset(struct osmo_e1f_instance *e1i)
606{
607 osmo_fsm_inst_dispatch(e1i->rx.fi, E1_AE_RESET, NULL);
608}
609
610static void e1_rx_hist_add(struct osmo_e1f_instance *e1i, uint8_t inb)
611{
612 e1i->rx.ts0_history[e1i->rx.frame_nr] = inb;
613 if (e1i->rx.ts0_hist_len < 16)
614 e1i->rx.ts0_hist_len++;
615}
616
617static void e1_rx_ts0(struct osmo_e1f_instance *e1i, uint8_t inb)
618{
619 /* append just-received byte to the TS0 receive history buffer */
620 e1_rx_hist_add(e1i, inb);
621
622 /* notify the FSM that a new TS0 byte was received */
623 osmo_fsm_inst_dispatch(e1i->rx.fi, E1_AE_RX_TS0, NULL);
624
625 e1i->rx.frame_nr = (e1i->rx.frame_nr + 1) % 16;
626}
627
628static void e1_rx_tsN(struct osmo_e1f_instance_ts *e1t, uint8_t inb)
629{
630 struct msgb *msg;
631 int count, rc;
632
633 if (!e1t->rx.enabled)
634 return;
635
636 if (!e1t->rx.msg)
637 e1t->rx.msg = msgb_alloc(e1t->rx.granularity, "E1 Rx");
638 msg = e1t->rx.msg;
639 OSMO_ASSERT(msg);
640
641 switch (e1t->mode) {
642 case OSMO_E1F_TS_RAW:
643 /* append byte at end of msgb */
644 msgb_put_u8(msg, inb);
645 /* flush msgb, if full */
646 if (msgb_tailroom(msg) <= 0) {
647 goto flush;
648 }
649 break;
650 case OSMO_E1F_TS_HDLC_CRC:
651 rc = osmo_isdnhdlc_decode(&e1t->rx.hdlc, &inb, 1, &count,
652 msgb_data(msg), msgb_tailroom(msg));
653 switch (rc) {
654 case -OSMO_HDLC_FRAMING_ERROR:
655 fprintf(stdout, "Framing Error\n");
656 break;
657 case -OSMO_HDLC_CRC_ERROR:
658 fprintf(stdout, "CRC Error\n");
659 break;
660 case -OSMO_HDLC_LENGTH_ERROR:
661 fprintf(stdout, "Length Error\n");
662 break;
663 case 0:
664 /* no output yet */
665 break;
666 default:
667 msgb_put(msg, rc);
668 goto flush;
669 }
670 break;
671 }
672
673 return;
674flush:
675
676 if (!e1t->rx.data_cb)
677 msgb_free(msg);
678 else
679 e1t->rx.data_cb(e1t, msg);
680 e1t->rx.msg = NULL;
681}
682
683/*! Receive a single E1 frame of 32x8 (=256) bits
684 * \param e1i E1 instance for which the frame was received
685 * \param[in] in_frame caller-provided buffer of 32 octets
686 *
687 * The idea is that whoever calls us will already have done the bit-alignment,
688 * i.e. the first bit of TS0 of the frame will be octet-aligned and hence the
689 * entire 256bit buffer is provided as octet-aligned 32bytes in \a in_frame.
690 */
691int osmo_e1f_rx_frame(struct osmo_e1f_instance *e1i, const uint8_t *in_frame)
692{
693 int i;
694
695 e1_rx_update_crc4(e1i, in_frame);
696
697 e1_rx_ts0(e1i, in_frame[0]);
698
699 for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) {
700 struct osmo_e1f_instance_ts *e1t = &e1i->ts[i];
701 e1_rx_tsN(e1t, in_frame[i]);
702 }
703
704 return 0;
705}
706
707int osmo_e1f_init(void)
708{
709 return osmo_fsm_register(&e1_align_fsm);
710}