blob: 2b83622b838b0db4737b0c780e0251da708714e8 [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;
Harald Welte8a95fd52018-05-11 21:04:03 +020094
95 osmo_isdnhdlc_rcv_init(&e1t->rx.hdlc, OSMO_HDLC_F_BITREVERSE);
96 //osmo_isdnhdlc_rcv_init(&e1t->rx.hdlc, 0);
97 osmo_isdnhdlc_out_init(&e1t->tx.hdlc, 0);
Harald Welte0c756eb2018-05-06 16:01:29 +020098}
99
100/*! stop E1 instance; stops all timeslots and releases any pending rx/tx buffers
101 * \param[in] e1t E1 instance which we are to stop */
102void osmo_e1_instance_reset(struct osmo_e1_instance *e1i)
103{
104 int i;
105
106 align_fsm_reset(e1i);
107
108 e1i->tx.remote_alarm = false;
109 e1i->tx.crc4_error = false;
110 e1i->tx.frame_nr = 0;
111 e1i->tx.crc4_last_smf = 0;
Harald Weltecdbcff92018-05-12 09:44:03 +0200112 e1i->tx.crc4 = crc4itu_init();
Harald Welte0c756eb2018-05-06 16:01:29 +0200113
114 e1i->rx.frame_nr = 0;
115 memset(&e1i->rx.ts0_history, 0, sizeof(e1i->rx.ts0_history));
116 e1i->rx.ts0_hist_len = 0;
117 e1i->rx.remote_alarm = false;
118 e1i->rx.remote_crc4_error = false;
119 e1i->rx.num_ts0_in_mframe_search = 0;
120
121 for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) {
122 struct osmo_e1_instance_ts *e1t = &e1i->ts[i];
123 osmo_e1_ts_reset(e1t);
124 }
125}
126
127/*! obtain pointer to TS given by instance + timeslot number
128 * \param[in] e1i E1 intance on which we work
129 * \param[in] ts_nr E1 timeslot number (1..31)
130 * \returns pointer to timeslot; NULL on error */
131struct osmo_e1_instance_ts *osmo_e1_instance_ts(struct osmo_e1_instance *e1i, uint8_t ts_nr)
132{
133 if (ts_nr == 0 || ts_nr >= ARRAY_SIZE(e1i->ts))
134 return NULL;
135
136 return &e1i->ts[ts_nr];
137}
138
139/*! configure an E1 timeslot
140 * \param[in] e1t Timeslot which we are to configure
141 * \param[in] granularity granularity (buffer size) to use on Rx
142 * \param[in] enable enable (true) or disalble (false) receiving on this TS
Harald Welte8a95fd52018-05-11 21:04:03 +0200143 * \param[in] mode the mode for this timeslot (raw or hdlc)
Harald Welte0c756eb2018-05-06 16:01:29 +0200144 * \return 0 on success; negative on error */
145int osmo_e1_ts_config(struct osmo_e1_instance_ts *e1t, e1_data_cb cb, unsigned int granularity,
Harald Welte8a95fd52018-05-11 21:04:03 +0200146 bool enable, enum osmo_e1_ts_mode mode)
Harald Welte0c756eb2018-05-06 16:01:29 +0200147{
148 e1t->rx.data_cb = cb;
149 e1t->rx.enabled = enable;
150 e1t->rx.granularity = granularity;
Harald Welte8a95fd52018-05-11 21:04:03 +0200151 e1t->mode = mode;
Harald Welte0c756eb2018-05-06 16:01:29 +0200152
153 return 0;
154}
155
156const struct value_string osmo_e1_notifv_evt_names[] = {
157 { E1_NTFY_EVT_ALIGN_FRAME, "Aligned to Frame" },
158 { E1_NTFY_EVT_ALIGN_CRC_MFRAME, "Aligned to CRC4-Multiframe" },
159 { E1_NTFY_EVT_CRC_ERROR, "CRC Error detected (local)" },
160 { E1_NTFY_EVT_REMOTE_CRC_ERROR, "CRC Error reported (remote)" },
161 { E1_NTFY_EVT_REMOTE_ALARM, "Remote Alarm condition repoorted" },
162 { 0, NULL }
163};
164
165/***********************************************************************
166 * Transmit Side
167 ***********************************************************************/
168
169/*! Enqueue a message buffer of to-be-transmitted data for a timeslot
170 * \param[in] e1i E1 instance for which to enqueue
171 * \param[in] ts_nr Timeslot number on which data is to be transmitted
172 * \param[in] msg Message buffer storing the to-be-transmitted data
173 * \returns 0 on success; negative in case of error.
174 *
175 * Ownership of \a msg is transferred from caller into this function, but only
176 * in case of successful execution (return 0)!
177 */
178void osmo_e1_ts_enqueue(struct osmo_e1_instance_ts *e1t, struct msgb *msg)
179{
180 msgb_enqueue(&e1t->tx.queue, msg);
181}
182
183/* obtain a CRC4 bit for the current frame number */
184static uint8_t e1_pull_crc4_bit(struct osmo_e1_instance *e1i)
185{
186 /* If CRC-4 is disabled, all CRC bits shall be '1' */
187 if (e1i->crc4_enabled == 0) {
188 return 0x01;
189 } else {
190 /* CRC is transmitted MSB first */
191 switch (e1i->tx.frame_nr % 8) {
192 case 0:
193 return (e1i->tx.crc4_last_smf >> 3) & 1;
194 case 2:
195 return (e1i->tx.crc4_last_smf >> 2) & 1;
196 case 4:
197 return (e1i->tx.crc4_last_smf >> 1) & 1;
198 case 6:
199 return (e1i->tx.crc4_last_smf >> 0) & 1;
200 default:
201 OSMO_ASSERT(0);
202 }
203 }
204}
205
206/* pull a single to-be-transmitted byte for TS0 */
207static uint8_t e1_pull_ts0(struct osmo_e1_instance *e1i)
208{
209 uint8_t ret;
210
211 /* according to Table 5B/G.704 - CRC-4 multiframe structure */
212 if ((e1i->tx.frame_nr % 2) == 0) {
213 /* FAS */
214 ret = G704_E1_FAS | (e1_pull_crc4_bit(e1i) << 7);
215 } else {
216 switch (e1i->tx.frame_nr) {
217 case 1:
218 case 3:
219 case 7:
220 ret = 0x40;
221 break;
222 case 5:
223 case 9:
224 case 11:
225 ret = 0xC0;
226 break;
227 case 13:
228 case 15:
229 ret = 0x40;
230 if (e1i->tx.crc4_error)
231 ret |= 0x80;
232 break;
233 }
234 ret |= e1i->tx.sa4_sa8;
235 if (e1i->tx.remote_alarm)
236 ret |= 0x20;
237 }
238
239 /* re-set CRC4 at start of sub-multiframe */
240 if (e1i->tx.frame_nr == 0 || e1i->tx.frame_nr == 8) {
241 e1i->tx.crc4_last_smf = e1i->tx.crc4;
242 e1i->tx.crc4 = 0;
243 }
244
245 /* increment frame number modulo 16 */
246 e1i->tx.frame_nr = (e1i->tx.frame_nr + 1) % 16;
247
248 return ret;
249}
250
251/* pull a single to-be-transmitted byte for TS1..31 */
252static uint8_t e1_pull_tsN(struct osmo_e1_instance_ts *e1t)
253{
254 struct msgb *msg = llist_first_entry_or_null(&e1t->tx.queue, struct msgb, list);
255 uint8_t *cur;
256
257retry:
258 /* if there's no message to transmit */
259 if (!msg) {
260 e1t->tx.underruns++;
261 return 0xFF;
262 }
263 if (msgb_length(msg) <= 0) {
264 llist_del(&msg->list);
265 msgb_free(msg);
266 msg = llist_first_entry_or_null(&e1t->tx.queue, struct msgb, list);
267 goto retry;
268 }
269 cur = msgb_pull(msg, 1);
270 return *cur;
271}
272
273/* update the current in-progress CRC4 value with data from \a out_frame */
274static void e1_tx_update_crc4(struct osmo_e1_instance *e1i, const uint8_t *out_frame)
275{
276 uint8_t ts0;
277
278 ts0 = out_frame[0];
279 /* mask off the C bits */
280 if (is_correct_fas(ts0))
281 ts0 &= 0x7F;
Harald Weltecdbcff92018-05-12 09:44:03 +0200282 e1i->tx.crc4 = crc4itu_update(e1i->tx.crc4, &ts0, 1);
Harald Welte0c756eb2018-05-06 16:01:29 +0200283 /* add the remaining bytes/bits */
Harald Weltecdbcff92018-05-12 09:44:03 +0200284 e1i->tx.crc4 = crc4itu_update(e1i->tx.crc4, out_frame+1, ARRAY_SIZE(e1i->ts)-1);
Harald Welte0c756eb2018-05-06 16:01:29 +0200285}
286
287/*! Pull one to-be-transmitted E1 frame (256bits) from the E1 instance
288 * \param e1i E1 instance for which the frame shall be generated
289 * \param[out] out_frame callee-allocated buffer to which function stores 32 bytes
290 * \returns 0 on success, negative on error */
291int osmo_e1_pull_tx_frame(struct osmo_e1_instance *e1i, uint8_t *out_frame)
292{
293 int i;
294
295 /* generate TS0 */
296 out_frame[0] = e1_pull_ts0(e1i);
297
298 /* generate TS1..31 */
299 for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) {
300 struct osmo_e1_instance_ts *e1t = &e1i->ts[i];
301 /* get next to-be-transmitted byte from the TS */
302 out_frame[i] = e1_pull_tsN(e1t);
303 }
304 /* update our CRC4 computation */
305 e1_tx_update_crc4(e1i, out_frame);
306
307 return 0;
308}
309
310/***********************************************************************
311 * Receiver Side
312 ***********************************************************************/
313
314/* According to Figure 2 / ITU-T G.706 */
315enum e1_align_state {
316 /* Frame Alignment Search */
317 E1_AS_SEARCH_FRAME,
318 /* CRC multiframe alignment search */
319 E1_AS_SEARCH_CRC_MFRAME,
320 /* monitoring for incorrect frame alignment and error performance using CRC */
321 E1_AS_ALIGNED_CRC_MFRAME,
322 /* no CRC: just frame alignment loss check */
323 E1_AS_ALIGNED_BASIC,
324};
325
326enum e1_align_event {
327 /* received a TS0 octet */
328 E1_AE_RX_TS0,
329 E1_AE_RESET
330};
331
332static const struct value_string e1_align_evt_names[] = {
333 { E1_AE_RX_TS0, "E1_AE_RX_TS0" },
334 { E1_AE_RESET, "E1_AE_RESET" },
335 { 0, NULL }
336};
337
338/* get a TS0 byte from the history. delta 0 == current, delte 1 == previous, ... */
339static uint8_t get_ts0_hist(struct osmo_e1_instance *e1i, uint8_t delta)
340{
341 return e1i->rx.ts0_history[((e1i->rx.frame_nr + 16)-delta) % 16];
342}
343
344/* ITU-T G.706 Section 4.1.1 */
345static bool frame_alignment_lost(struct osmo_e1_instance *e1i)
346{
347 if (e1i->rx.frame_nr % 2)
348 return false;
349
350 /* Frame alignment will be assumed to have been lost when three consecutive incorrect
351 * frame alignment signals have been received. */
352 if (!is_correct_fas(get_ts0_hist(e1i, 0)) &&
353 !is_correct_fas(get_ts0_hist(e1i, 2)) &&
354 !is_correct_fas(get_ts0_hist(e1i, 4)))
355 return true;
356 else
357 return false;
358}
359
360/* ITU-T G.706 Section 4.1.2 */
361static bool frame_alignment_recovered(struct osmo_e1_instance *e1i)
362{
363 /* two consecutive FAS with one non-FAS interspersed */
364 if (is_correct_fas(get_ts0_hist(e1i, 0)) &&
365 !is_correct_fas(get_ts0_hist(e1i, 1)) &&
366 is_correct_fas(get_ts0_hist(e1i, 2)))
367 return true;
368 else
369 return false;
370}
371
372/* ITU-T G.706 Section 4.2 */
373static bool crc_mframe_alignment_achieved(struct osmo_e1_instance *e1i)
374{
375 /* if current TS0 byte is FAS, we cannot detect alignment */
376 if (is_correct_fas(get_ts0_hist(e1i, 0)))
377 return false;
378 if ((get_ts0_hist(e1i, 0) >> 7) == 1 &&
379 (get_ts0_hist(e1i, 2) >> 7) == 1 &&
380 (get_ts0_hist(e1i, 4) >> 7) == 0 &&
381 (get_ts0_hist(e1i, 6) >> 7) == 1 &&
382 (get_ts0_hist(e1i, 8) >> 7) == 0 &&
383 (get_ts0_hist(e1i, 10) >> 7) == 0)
384 return true;
385 else
386 return false;
387}
388
389/* Get the CRC4 that was received from our Rx TS0 history */
390static uint8_t crc4_from_ts0_hist(struct osmo_e1_instance *e1i, bool smf2)
391{
392 uint8_t crc = 0;
393 uint8_t offset = 0;
394
395 if (smf2)
396 offset = 8;
397
Harald Weltecdbcff92018-05-12 09:44:03 +0200398 crc |= (e1i->rx.ts0_history[0+offset] >> 7) << 3;
399 crc |= (e1i->rx.ts0_history[2+offset] >> 7) << 2;
400 crc |= (e1i->rx.ts0_history[4+offset] >> 7) << 1;
401 crc |= (e1i->rx.ts0_history[6+offset] >> 7) << 0;
Harald Welte0c756eb2018-05-06 16:01:29 +0200402
403 return crc;
404}
405
406/* update the current in-progress CRC4 value with data from \a rx_frame */
407static void e1_rx_update_crc4(struct osmo_e1_instance *e1i, const uint8_t *rx_frame)
408{
409 uint8_t ts0;
410
411 ts0 = rx_frame[0];
412 /* mask off the C bits */
413 if (is_correct_fas(ts0))
414 ts0 &= 0x7F;
Harald Weltecdbcff92018-05-12 09:44:03 +0200415 e1i->rx.crc4 = crc4itu_update(e1i->rx.crc4, &ts0, 1);
Harald Welte0c756eb2018-05-06 16:01:29 +0200416 /* add the remaining bytes/bits */
Harald Weltecdbcff92018-05-12 09:44:03 +0200417 e1i->rx.crc4 = crc4itu_update(e1i->rx.crc4, rx_frame+1, ARRAY_SIZE(e1i->ts)-1);
Harald Welte0c756eb2018-05-06 16:01:29 +0200418}
419
420/* FSM State handler */
421static void e1_align_search_frame(struct osmo_fsm_inst *fi, uint32_t event, void *data)
422{
423 struct osmo_e1_instance *e1i = (struct osmo_e1_instance *) fi->priv;
424
425 if (frame_alignment_recovered(e1i)) {
426 /* if we detected the 2nd FAS, we must be in FN 2 (or at least FN%2=0 */
427 e1i->rx.frame_nr = 2;
428 notify_user(e1i, E1_NTFY_EVT_ALIGN_FRAME, true, NULL);
429 osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_CRC_MFRAME, 0, 0);
430 }
431}
432
433/* FSM State handler */
434static void e1_align_search_crc_mframe(struct osmo_fsm_inst *fi, uint32_t event, void *data)
435{
436 struct osmo_e1_instance *e1i = (struct osmo_e1_instance *) fi->priv;
437
438 if (crc_mframe_alignment_achieved(e1i)) {
439 /* if we detected the 6-bit CRC multiframe signal, we must be in FN 11 */
440 e1i->rx.frame_nr = 11;
441 /* FIXME: "at least two valid CRC multiframe alignment signals can be located within
442 * 8 ms, the time separating two CRC multiframe alignment signals being 2 ms or a
443 * multiple of 2 ms" */
444 notify_user(e1i, E1_NTFY_EVT_ALIGN_CRC_MFRAME, true, NULL);
445 osmo_fsm_inst_state_chg(fi, E1_AS_ALIGNED_CRC_MFRAME, 0, 0);
446 } else {
447 /* if no mframe alignment is established within 8ms (64 frames), fall back */
448 if (e1i->rx.num_ts0_in_mframe_search >= 64) {
449 e1i->rx.num_ts0_in_mframe_search = 0;
450 osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0);
451 }
452 e1i->rx.num_ts0_in_mframe_search++;
453 }
454}
455
456static void e1_aligned_common(struct osmo_e1_instance *e1i)
457{
458 uint8_t inb = get_ts0_hist(e1i, 0);
459
460 /* All non-FAS frames contain "A" bit in TS0 */
461 if (!is_correct_fas(inb & 0x7F)) {
462 bool old_alarm = e1i->rx.remote_alarm;
463 /* frame not containing the frame alignment signal */
464 if (inb & 0x20)
465 e1i->rx.remote_alarm = true;
466 else
467 e1i->rx.remote_alarm = false;
468 if (old_alarm != e1i->rx.remote_alarm)
469 notify_user(e1i, E1_NTFY_EVT_REMOTE_ALARM, e1i->rx.remote_alarm, NULL);
470 }
471}
472
473/* FSM State handler */
474static void e1_aligned_crc_mframe(struct osmo_fsm_inst *fi, uint32_t event, void *data)
475{
476 struct osmo_e1_instance *e1i = (struct osmo_e1_instance *) fi->priv;
477
478 if (frame_alignment_lost(e1i)) {
479 osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0);
480 return;
481 }
482
483 if (e1i->crc4_enabled) {
484 uint8_t crc_rx;
485 bool crc4_error;
486
487 /* check if we just received a complete CRC4 */
488 switch (e1i->rx.frame_nr) {
489 case 7:
490 case 15:
491 crc_rx = crc4_from_ts0_hist(e1i, e1i->rx.frame_nr == 15 ? true : false);
492 if (crc_rx != e1i->rx.crc4_last_smf)
493 crc4_error = true;
494 else
495 crc4_error = false;
496 if (crc4_error != e1i->tx.crc4_error) {
497 notify_user(e1i, E1_NTFY_EVT_CRC_ERROR, crc4_error, NULL);
498 e1i->tx.crc4_error = crc4_error;
499 }
500 /* rotate computed CRC4 one further */
501 e1i->rx.crc4_last_smf = e1i->rx.crc4;
Harald Weltecdbcff92018-05-12 09:44:03 +0200502 e1i->rx.crc4 = crc4itu_init();
Harald Welte0c756eb2018-05-06 16:01:29 +0200503 break;
504 default:
505 break;
506 }
507
508 /* check if the remote side reports any CRC errors */
509 switch (e1i->rx.frame_nr) {
510 case 13:
511 case 15:
512 crc4_error = false;
513 if ((get_ts0_hist(e1i, 0) >> 7) == 0)
514 crc4_error = true;
515 if (crc4_error != e1i->rx.remote_crc4_error) {
516 notify_user(e1i, E1_NTFY_EVT_REMOTE_CRC_ERROR, crc4_error, NULL);
517 e1i->rx.remote_crc4_error = crc4_error;
518 }
519 break;
520 }
521 }
522
523 e1_aligned_common(e1i);
524}
525
526/* FSM State handler */
527static void e1_aligned_basic(struct osmo_fsm_inst *fi, uint32_t event, void *data)
528{
529 struct osmo_e1_instance *e1i = (struct osmo_e1_instance *) fi->priv;
530
531 if (frame_alignment_lost(e1i)) {
532 osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0);
533 return;
534 }
535
536 e1_aligned_common(e1i);
537}
538
539static const struct osmo_fsm_state e1_align_states[] = {
540 [E1_AS_SEARCH_FRAME] = {
541 .name = "SEARCH_FRAME",
542 .in_event_mask = S(E1_AE_RX_TS0),
543 .out_state_mask = S(E1_AS_SEARCH_FRAME) |
544 S(E1_AS_SEARCH_CRC_MFRAME) |
545 S(E1_AS_ALIGNED_BASIC),
546 .action = e1_align_search_frame,
547 },
548 [E1_AS_SEARCH_CRC_MFRAME] = {
549 .name = "SEARCH_CRC_MFRAME",
550 .in_event_mask = S(E1_AE_RX_TS0),
551 .out_state_mask = S(E1_AS_SEARCH_FRAME) |
552 S(E1_AS_SEARCH_CRC_MFRAME) |
553 S(E1_AS_ALIGNED_CRC_MFRAME),
554 .action = e1_align_search_crc_mframe,
555 },
556 [E1_AS_ALIGNED_CRC_MFRAME] = {
557 .name = "ALIGNED_CRC_MFRAME",
558 .in_event_mask = S(E1_AE_RX_TS0),
559 .out_state_mask = S(E1_AS_SEARCH_FRAME) |
560 S(E1_AS_SEARCH_CRC_MFRAME) |
561 S(E1_AS_ALIGNED_CRC_MFRAME),
562 .action = e1_aligned_crc_mframe,
563 },
564 [E1_AS_ALIGNED_BASIC] = {
565 .name = "ALIGNED_BASIC",
566 .in_event_mask = S(E1_AE_RX_TS0),
567 .out_state_mask = S(E1_AS_SEARCH_FRAME),
568 .action = e1_aligned_basic,
569 },
570};
571
572static void e1_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
573{
574 struct osmo_e1_instance *e1i = (struct osmo_e1_instance *) fi->priv;
575
576 switch (event) {
577 case E1_AE_RESET:
578 e1i->rx.num_ts0_in_mframe_search = 0;
579 osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0);
580 break;
581 }
582}
583
584static struct osmo_fsm e1_align_fsm = {
585 .name = "e1-align",
586 .states = e1_align_states,
587 .num_states = ARRAY_SIZE(e1_align_states),
588 .allstate_event_mask = S(E1_AE_RESET),
589 .allstate_action = e1_allstate,
590 .log_subsys = DLGLOBAL,
591 .event_names = e1_align_evt_names,
592};
593
594static void align_fsm_reset(struct osmo_e1_instance *e1i)
595{
596 osmo_fsm_inst_dispatch(e1i->rx.fi, E1_AE_RESET, NULL);
597}
598
599static void e1_rx_hist_add(struct osmo_e1_instance *e1i, uint8_t inb)
600{
601 e1i->rx.ts0_history[e1i->rx.frame_nr] = inb;
602 if (e1i->rx.ts0_hist_len < 16)
603 e1i->rx.ts0_hist_len++;
604}
605
606static void e1_rx_ts0(struct osmo_e1_instance *e1i, uint8_t inb)
607{
608 /* append just-received byte to the TS0 receive history buffer */
609 e1_rx_hist_add(e1i, inb);
610
611 /* notify the FSM that a new TS0 byte was received */
612 osmo_fsm_inst_dispatch(e1i->rx.fi, E1_AE_RX_TS0, NULL);
613
614 e1i->rx.frame_nr = (e1i->rx.frame_nr + 1) % 16;
615}
616
617static void e1_rx_tsN(struct osmo_e1_instance_ts *e1t, uint8_t inb)
618{
619 struct msgb *msg;
Harald Welte8a95fd52018-05-11 21:04:03 +0200620 int count, rc;
Harald Welte0c756eb2018-05-06 16:01:29 +0200621
622 if (!e1t->rx.enabled)
623 return;
624
625 if (!e1t->rx.msg)
626 e1t->rx.msg = msgb_alloc(e1t->rx.granularity, "E1 Rx");
627 msg = e1t->rx.msg;
628 OSMO_ASSERT(msg);
629
Harald Welte8a95fd52018-05-11 21:04:03 +0200630 switch (e1t->mode) {
631 case OSMO_E1_TS_RAW:
632 /* append byte at end of msgb */
633 msgb_put_u8(msg, inb);
634 /* flush msgb, if full */
635 if (msgb_tailroom(msg) <= 0) {
636 goto flush;
637 }
638 break;
639 case OSMO_E1_TS_HDLC_CRC:
640 rc = osmo_isdnhdlc_decode(&e1t->rx.hdlc, &inb, 1, &count,
641 msgb_data(msg), msgb_tailroom(msg));
642 switch (rc) {
643 case -OSMO_HDLC_FRAMING_ERROR:
644 fprintf(stdout, "Framing Error\n");
645 break;
646 case -OSMO_HDLC_CRC_ERROR:
647 fprintf(stdout, "CRC Error\n");
648 break;
649 case -OSMO_HDLC_LENGTH_ERROR:
650 fprintf(stdout, "Length Error\n");
651 break;
652 case 0:
653 /* no output yet */
654 break;
655 default:
656 msgb_put(msg, rc);
657 goto flush;
658 }
659 break;
Harald Welte0c756eb2018-05-06 16:01:29 +0200660 }
Harald Welte8a95fd52018-05-11 21:04:03 +0200661
662 return;
663flush:
664
665 if (!e1t->rx.data_cb)
666 msgb_free(msg);
667 else
668 e1t->rx.data_cb(e1t, msg);
669 e1t->rx.msg = NULL;
Harald Welte0c756eb2018-05-06 16:01:29 +0200670}
671
672/*! Receive a single E1 frame of 32x8 (=256) bits
673 * \param e1i E1 instance for which the frame was received
674 * \param[in] in_frame caller-provided buffer of 32 octets
675 *
676 * The idea is that whoever calls us will already have done the bit-alignment,
677 * i.e. the first bit of TS0 of the frame will be octet-aligned and hence the
678 * entire 256bit buffer is provided as octet-aligned 32bytes in \a in_frame.
679 */
680int osmo_e1_rx_frame(struct osmo_e1_instance *e1i, const uint8_t *in_frame)
681{
682 int i;
683
684 e1_rx_update_crc4(e1i, in_frame);
685
686 e1_rx_ts0(e1i, in_frame[0]);
687
688 for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) {
689 struct osmo_e1_instance_ts *e1t = &e1i->ts[i];
Harald Welted3941c62018-05-11 16:47:59 +0200690 e1_rx_tsN(e1t, in_frame[i]);
Harald Welte0c756eb2018-05-06 16:01:29 +0200691 }
692
693 return 0;
694}
695
696int osmo_e1_init(void)
697{
698 return osmo_fsm_register(&e1_align_fsm);
699}