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