osmo-e1-hardware/software/e1-tracer/analyze/osmo_e1f.c

711 lines
19 KiB
C
Raw Normal View History

import latest e1-tracer host software This was originally in https://github.com/smunaut/ice40-playground but has meanwhile been removed, only a fork at https://github.com/laf0rge/ice40-playground remained. Let's import the code here. The history is not all that exciting so I'm saving myself the effort of git filter-branch or the like to import the actual old commits. The commit log is here for reference: commit bd36d40cc30acbae68978df79eff03f0539a9a21 Author: Harald Welte <laforge@osmocom.org> Date: Fri Sep 4 10:21:51 2020 +0200 e1-recorder main.c: Don't exist just because one ISO transfer failed I get LIBUSB_TRANSFER_ERROR every so often (once every few hours) on my laptop. Sure, it likely means there was some data lost, but the trace can continue juts fine after this error. So make it non-fatal and continue running. commit 9b02b3e87400ba3d27bff05e2632fbd5a0698e8c Author: Harald Welte <laforge@osmocom.org> Date: Mon Aug 31 10:01:20 2020 +0200 Add 'replay' to re-play 2Mbps stream with realistic speed (to stdout) commit 23103d3a8f9cc8b3f0ba602dbb4240040b2d008a Author: Harald Welte <laforge@osmocom.org> Date: Sun Aug 30 15:41:12 2020 +0200 dump: Use osmo_e1f to decode; send HLDC over GSMTAP commit 7acd063a1245a34835d4488be3caad592f3c19d5 Author: Harald Welte <laforge@osmocom.org> Date: Thu Aug 20 16:48:15 2020 +0200 dump.c: Add error plotting support commit 90b90d04fff494b70ad220bd82ac48d43f3096c9 Author: Harald Welte <laforge@osmocom.org> Date: Mon Jan 13 18:20:39 2020 +0100 dump: Add support for writing LAPD PCAP of E1 superchannel commit f3a0cc34f0901c2cf257367351dddfddc6f7301e Author: Harald Welte <laforge@osmocom.org> Date: Mon Jan 13 16:37:23 2020 +0100 dump.c: Move handling of frames to separate function commit 8deffb5907979e43e3a4236ea7bb8d54513513ac Author: Harald Welte <laforge@osmocom.org> Date: Mon Jan 13 16:34:39 2020 +0100 dump.c: remove local hexdump.[ch] and use libosmocore proper commit 80d81e78737237ba3b6bae1deb7d5107d7f613b6 Author: Harald Welte <laforge@osmocom.org> Date: Tue Sep 1 13:52:52 2020 +0200 tracer: use 0x6151 as PID See https://github.com/openmoko/openmoko-usb-oui/commit/5e68831b26dbf912d831a0c00ca3045e6285b458 commit b219af27b34bbd6deb2419aa38852d8d227b9fc0 Author: Harald Welte <laforge@osmocom.org> Date: Mon Jan 6 13:47:28 2020 +0100 e1-recorder: Don't silently ignore write failures The disk may be full, for example. We should report that to the user and terminate the program. commit ead99fdfae6f1db54c79a91ef407a86c67352d87 Author: Harald Welte <laforge@osmocom.org> Date: Mon Jan 6 12:41:23 2020 +0100 e1-recorder: Exit process on transfer + resubmit failure This is particularly importnat in case the USB device for some reason gets disconnected and re-connects. We want the process to fail fast and have systemd respawn us. commit a521e026c76056a60531c490fa2ef84e8e633c9a Author: Harald Welte <laforge@osmocom.org> Date: Mon Jan 6 12:35:20 2020 +0100 e1-recorder: use libusb_strerror() to decode error messages commit 43a8fbdfdc35f5ba7564cfe5c92f43b124abf710 Author: Harald Welte <laforge@osmocom.org> Date: Sun Jan 5 19:49:58 2020 +0100 e1-recorder: Acytually make '-r' (SCHED_RR) work commit 84e28225d9ba1de9fb09660a96bd3b0b798c88d9 Author: Harald Welte <laforge@osmocom.org> Date: Sun Jan 5 17:53:11 2020 +0100 e1-recorder: Add small 'dump' program This program will print one line for each E1 frame, where each line consists of 32 hex bytes: one for each timeslot commit 0c697b40620b5d18480e626991dc639daadf58a6 Author: Sylvain Munaut <tnt@246tNt.com> Date: Sun Dec 29 13:13:17 2019 +0100 projects/riscv_usb: Host software for the dual channel sniffer Signed-off-by: Sylvain Munaut <tnt@246tNt.com> Closes: OS#5673 Change-Id: I9319c1eb8f822830307c1a181d357c58ce43efba
2022-10-30 21:20:55 +00:00
/* Osmocom Software Defined E1
* Implements ITU-T Rec. G.704 Section 2.3
*
* (C) 2018 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/fsm.h>
#include "crc4itu.h"
#include "osmo_e1f.h"
#define S(x) (1 << (x))
/* Frame Alignment Signal (BIT1 may be overwritten with CRC-4) */
#define G704_E1_FAS 0x1B
static inline bool is_correct_fas(uint8_t bt) {
if ((bt & 0x7F) == G704_E1_FAS)
return true;
else
return false;
}
/* are we in SMF II (true) or I (false) */
static inline bool is_smf_II(const struct osmo_e1f_tx_state *tx) {
if (tx->frame_nr >= 8)
return true;
return false;
}
static struct osmo_fsm e1_align_fsm;
static void align_fsm_reset(struct osmo_e1f_instance *e1i);
static void notify_user(struct osmo_e1f_instance *e1i, enum osmo_e1f_notify_event evt,
bool present, void *priv)
{
if (!e1i->notify_cb)
return;
e1i->notify_cb(e1i, evt, present, priv);
}
/*! Initialize a (caller-allocated) Osmocom E1 Instance
* \param[inout] e1i E1 Instance to be initialized
* \returns 0 on success, negative on error */
int osmo_e1f_instance_init(struct osmo_e1f_instance *e1i, const char *name, e1_notify_cb cb,
bool crc4_enabled, void *priv)
{
int i;
e1i->crc4_enabled = crc4_enabled;
e1i->notify_cb = cb;
e1i->tx.sa4_sa8 = 0x00;
e1i->priv = priv;
for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) {
struct osmo_e1f_instance_ts *e1t = &e1i->ts[i];
e1t->ts_nr = i;
e1t->inst = e1i;
INIT_LLIST_HEAD(&e1t->tx.queue);
e1t->rx.granularity = 256;
}
e1i->rx.fi = osmo_fsm_inst_alloc(&e1_align_fsm, NULL, e1i, LOGL_DEBUG, name);
if (!e1i->rx.fi)
return -1;
osmo_e1f_instance_reset(e1i);
return 0;
}
/*! stop E1 timeslot; release any pending rx/tx buffers
* \param[in] e1t Timeslot which we are to stop, disable and release buffers */
void osmo_e1f_ts_reset(struct osmo_e1f_instance_ts *e1t)
{
e1t->tx.underruns = 0;
msgb_queue_free(&e1t->tx.queue);
e1t->rx.enabled = false;
msgb_free(e1t->rx.msg);
e1t->rx.msg = NULL;
osmo_isdnhdlc_rcv_init(&e1t->rx.hdlc, OSMO_HDLC_F_BITREVERSE);
//osmo_isdnhdlc_rcv_init(&e1t->rx.hdlc, 0);
osmo_isdnhdlc_out_init(&e1t->tx.hdlc, 0);
}
/*! stop E1 instance; stops all timeslots and releases any pending rx/tx buffers
* \param[in] e1t E1 instance which we are to stop */
void osmo_e1f_instance_reset(struct osmo_e1f_instance *e1i)
{
int i;
align_fsm_reset(e1i);
e1i->tx.remote_alarm = false;
e1i->tx.crc4_error = false;
e1i->tx.frame_nr = 0;
e1i->tx.crc4_last_smf = 0;
e1i->tx.crc4 = crc4itu_init();
e1i->rx.frame_nr = 0;
memset(&e1i->rx.ts0_history, 0, sizeof(e1i->rx.ts0_history));
e1i->rx.ts0_hist_len = 0;
e1i->rx.remote_alarm = false;
e1i->rx.remote_crc4_error = false;
e1i->rx.num_ts0_in_mframe_search = 0;
for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) {
struct osmo_e1f_instance_ts *e1t = &e1i->ts[i];
osmo_e1f_ts_reset(e1t);
}
}
/*! obtain pointer to TS given by instance + timeslot number
* \param[in] e1i E1 intance on which we work
* \param[in] ts_nr E1 timeslot number (1..31)
* \returns pointer to timeslot; NULL on error */
struct osmo_e1f_instance_ts *osmo_e1f_instance_ts(struct osmo_e1f_instance *e1i, uint8_t ts_nr)
{
if (ts_nr == 0 || ts_nr >= ARRAY_SIZE(e1i->ts))
return NULL;
return &e1i->ts[ts_nr];
}
/*! configure an E1 timeslot
* \param[in] e1t Timeslot which we are to configure
* \param[in] granularity granularity (buffer size) to use on Rx
* \param[in] enable enable (true) or disalble (false) receiving on this TS
* \param[in] mode the mode for this timeslot (raw or hdlc)
* \return 0 on success; negative on error */
int osmo_e1f_ts_config(struct osmo_e1f_instance_ts *e1t, e1_data_cb cb, unsigned int granularity,
bool enable, enum osmo_e1f_ts_mode mode)
{
e1t->rx.data_cb = cb;
e1t->rx.enabled = enable;
e1t->rx.granularity = granularity;
e1t->mode = mode;
return 0;
}
const struct value_string osmo_e1f_notifv_evt_names[] = {
{ E1_NTFY_EVT_ALIGN_FRAME, "Aligned to Frame" },
{ E1_NTFY_EVT_ALIGN_CRC_MFRAME, "Aligned to CRC4-Multiframe" },
{ E1_NTFY_EVT_CRC_ERROR, "CRC Error detected (local)" },
{ E1_NTFY_EVT_REMOTE_CRC_ERROR, "CRC Error reported (remote)" },
{ E1_NTFY_EVT_REMOTE_ALARM, "Remote Alarm condition repoorted" },
{ 0, NULL }
};
/***********************************************************************
* Transmit Side
***********************************************************************/
/*! Enqueue a message buffer of to-be-transmitted data for a timeslot
* \param[in] e1i E1 instance for which to enqueue
* \param[in] ts_nr Timeslot number on which data is to be transmitted
* \param[in] msg Message buffer storing the to-be-transmitted data
* \returns 0 on success; negative in case of error.
*
* Ownership of \a msg is transferred from caller into this function, but only
* in case of successful execution (return 0)!
*/
void osmo_e1f_ts_enqueue(struct osmo_e1f_instance_ts *e1t, struct msgb *msg)
{
msgb_enqueue(&e1t->tx.queue, msg);
}
/* obtain a CRC4 bit for the current frame number */
static uint8_t e1_pull_crc4_bit(struct osmo_e1f_instance *e1i)
{
/* If CRC-4 is disabled, all CRC bits shall be '1' */
if (e1i->crc4_enabled == 0)
return 0x01;
/* CRC is transmitted MSB first */
switch (e1i->tx.frame_nr % 8) {
case 0:
return (e1i->tx.crc4_last_smf >> 3) & 1;
case 2:
return (e1i->tx.crc4_last_smf >> 2) & 1;
case 4:
return (e1i->tx.crc4_last_smf >> 1) & 1;
case 6:
return (e1i->tx.crc4_last_smf >> 0) & 1;
default:
OSMO_ASSERT(0);
}
}
/* pull a single to-be-transmitted byte for TS0 */
static uint8_t e1_pull_ts0(struct osmo_e1f_instance *e1i)
{
uint8_t ret;
/* according to Table 5B/G.704 - CRC-4 multiframe structure */
if ((e1i->tx.frame_nr % 2) == 0) {
/* FAS */
ret = G704_E1_FAS | (e1_pull_crc4_bit(e1i) << 7);
} else {
switch (e1i->tx.frame_nr) {
case 1:
case 3:
case 7:
ret = 0x40;
break;
case 5:
case 9:
case 11:
ret = 0xC0;
break;
case 13:
case 15:
ret = 0x40;
if (e1i->tx.crc4_error)
ret |= 0x80;
break;
}
ret |= e1i->tx.sa4_sa8;
if (e1i->tx.remote_alarm)
ret |= 0x20;
}
/* re-set CRC4 at start of sub-multiframe */
if (e1i->tx.frame_nr == 0 || e1i->tx.frame_nr == 8) {
e1i->tx.crc4_last_smf = e1i->tx.crc4;
e1i->tx.crc4 = 0;
}
/* increment frame number modulo 16 */
e1i->tx.frame_nr = (e1i->tx.frame_nr + 1) % 16;
return ret;
}
/* pull a single to-be-transmitted byte for TS1..31 */
static uint8_t e1_pull_tsN(struct osmo_e1f_instance_ts *e1t)
{
struct msgb *msg = llist_first_entry_or_null(&e1t->tx.queue, struct msgb, list);
uint8_t *cur;
retry:
/* if there's no message to transmit */
if (!msg) {
e1t->tx.underruns++;
return 0xFF;
}
if (msgb_length(msg) <= 0) {
llist_del(&msg->list);
msgb_free(msg);
msg = llist_first_entry_or_null(&e1t->tx.queue, struct msgb, list);
goto retry;
}
cur = msgb_pull(msg, 1);
return *cur;
}
/* update the current in-progress CRC4 value with data from \a out_frame */
static void e1_tx_update_crc4(struct osmo_e1f_instance *e1i, const uint8_t *out_frame)
{
uint8_t ts0;
ts0 = out_frame[0];
/* mask off the C bits */
if (is_correct_fas(ts0))
ts0 &= 0x7F;
e1i->tx.crc4 = crc4itu_update(e1i->tx.crc4, &ts0, 1);
/* add the remaining bytes/bits */
e1i->tx.crc4 = crc4itu_update(e1i->tx.crc4, out_frame+1, ARRAY_SIZE(e1i->ts)-1);
}
/*! Pull one to-be-transmitted E1 frame (256bits) from the E1 instance
* \param e1i E1 instance for which the frame shall be generated
* \param[out] out_frame callee-allocated buffer to which function stores 32 bytes
* \returns 0 on success, negative on error */
int osmo_e1f_pull_tx_frame(struct osmo_e1f_instance *e1i, uint8_t *out_frame)
{
int i;
/* generate TS0 */
out_frame[0] = e1_pull_ts0(e1i);
/* generate TS1..31 */
for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) {
struct osmo_e1f_instance_ts *e1t = &e1i->ts[i];
/* get next to-be-transmitted byte from the TS */
out_frame[i] = e1_pull_tsN(e1t);
}
/* update our CRC4 computation */
e1_tx_update_crc4(e1i, out_frame);
return 0;
}
/***********************************************************************
* Receiver Side
***********************************************************************/
/* According to Figure 2 / ITU-T G.706 */
enum e1_align_state {
/* Frame Alignment Search */
E1_AS_SEARCH_FRAME,
/* CRC multiframe alignment search */
E1_AS_SEARCH_CRC_MFRAME,
/* monitoring for incorrect frame alignment and error performance using CRC */
E1_AS_ALIGNED_CRC_MFRAME,
/* no CRC: just frame alignment loss check */
E1_AS_ALIGNED_BASIC,
};
enum e1_align_event {
/* received a TS0 octet */
E1_AE_RX_TS0,
E1_AE_RESET
};
static const struct value_string e1_align_evt_names[] = {
{ E1_AE_RX_TS0, "E1_AE_RX_TS0" },
{ E1_AE_RESET, "E1_AE_RESET" },
{ 0, NULL }
};
/* get a TS0 byte from the history. delta 0 == current, delte 1 == previous, ... */
static uint8_t get_ts0_hist(struct osmo_e1f_instance *e1i, uint8_t delta)
{
return e1i->rx.ts0_history[((e1i->rx.frame_nr + 16)-delta) % 16];
}
/* ITU-T G.706 Section 4.1.1 */
static bool frame_alignment_lost(struct osmo_e1f_instance *e1i)
{
if (e1i->rx.frame_nr % 2)
return false;
/* Frame alignment will be assumed to have been lost when three consecutive incorrect
* frame alignment signals have been received. */
if (!is_correct_fas(get_ts0_hist(e1i, 0)) &&
!is_correct_fas(get_ts0_hist(e1i, 2)) &&
!is_correct_fas(get_ts0_hist(e1i, 4)))
return true;
else
return false;
}
/* ITU-T G.706 Section 4.1.2 */
static bool frame_alignment_recovered(struct osmo_e1f_instance *e1i)
{
/* two consecutive FAS with one non-FAS interspersed */
if (is_correct_fas(get_ts0_hist(e1i, 0)) &&
!is_correct_fas(get_ts0_hist(e1i, 1)) &&
is_correct_fas(get_ts0_hist(e1i, 2)))
return true;
else
return false;
}
/* ITU-T G.706 Section 4.2 */
static bool crc_mframe_alignment_achieved(struct osmo_e1f_instance *e1i)
{
/* if current TS0 byte is FAS, we cannot detect alignment */
if (is_correct_fas(get_ts0_hist(e1i, 0)))
return false;
if ((get_ts0_hist(e1i, 0) >> 7) == 1 &&
(get_ts0_hist(e1i, 2) >> 7) == 1 &&
(get_ts0_hist(e1i, 4) >> 7) == 0 &&
(get_ts0_hist(e1i, 6) >> 7) == 1 &&
(get_ts0_hist(e1i, 8) >> 7) == 0 &&
(get_ts0_hist(e1i, 10) >> 7) == 0)
return true;
else
return false;
}
/* Get the CRC4 that was received from our Rx TS0 history */
static uint8_t crc4_from_ts0_hist(struct osmo_e1f_instance *e1i, bool smf2)
{
uint8_t crc = 0;
uint8_t offset = 0;
if (smf2)
offset = 8;
crc |= (e1i->rx.ts0_history[0+offset] >> 7) << 3;
crc |= (e1i->rx.ts0_history[2+offset] >> 7) << 2;
crc |= (e1i->rx.ts0_history[4+offset] >> 7) << 1;
crc |= (e1i->rx.ts0_history[6+offset] >> 7) << 0;
return crc;
}
/* update the current in-progress CRC4 value with data from \a rx_frame */
static void e1_rx_update_crc4(struct osmo_e1f_instance *e1i, const uint8_t *rx_frame)
{
uint8_t ts0;
ts0 = rx_frame[0];
/* mask off the C bits */
if (is_correct_fas(ts0))
ts0 &= 0x7F;
e1i->rx.crc4 = crc4itu_update(e1i->rx.crc4, &ts0, 1);
/* add the remaining bytes/bits */
e1i->rx.crc4 = crc4itu_update(e1i->rx.crc4, rx_frame+1, ARRAY_SIZE(e1i->ts)-1);
}
/* FSM State handler */
static void e1_align_search_frame(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv;
if (frame_alignment_recovered(e1i)) {
/* if we detected the 2nd FAS, we must be in FN 2 (or at least FN%2=0 */
e1i->rx.frame_nr = 2;
notify_user(e1i, E1_NTFY_EVT_ALIGN_FRAME, true, NULL);
osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_CRC_MFRAME, 0, 0);
}
}
/* FSM State handler */
static void e1_align_search_crc_mframe(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv;
if (crc_mframe_alignment_achieved(e1i)) {
/* if we detected the 6-bit CRC multiframe signal, we must be in FN 11 */
e1i->rx.frame_nr = 11;
/* FIXME: "at least two valid CRC multiframe alignment signals can be located within
* 8 ms, the time separating two CRC multiframe alignment signals being 2 ms or a
* multiple of 2 ms" */
notify_user(e1i, E1_NTFY_EVT_ALIGN_CRC_MFRAME, true, NULL);
osmo_fsm_inst_state_chg(fi, E1_AS_ALIGNED_CRC_MFRAME, 0, 0);
} else {
/* if no mframe alignment is established within 8ms (64 frames), fall back */
if (e1i->rx.num_ts0_in_mframe_search >= 64) {
e1i->rx.num_ts0_in_mframe_search = 0;
osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0);
}
e1i->rx.num_ts0_in_mframe_search++;
}
}
static void e1_aligned_common(struct osmo_e1f_instance *e1i)
{
uint8_t inb = get_ts0_hist(e1i, 0);
/* All non-FAS frames contain "A" bit in TS0 */
if (!is_correct_fas(inb & 0x7F)) {
bool old_alarm = e1i->rx.remote_alarm;
/* frame not containing the frame alignment signal */
if (inb & 0x20)
e1i->rx.remote_alarm = true;
else
e1i->rx.remote_alarm = false;
if (old_alarm != e1i->rx.remote_alarm)
notify_user(e1i, E1_NTFY_EVT_REMOTE_ALARM, e1i->rx.remote_alarm, NULL);
}
}
/* FSM State handler */
static void e1_aligned_crc_mframe(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv;
if (frame_alignment_lost(e1i)) {
osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0);
return;
}
if (e1i->crc4_enabled) {
uint8_t crc_rx;
bool crc4_error;
/* check if we just received a complete CRC4 */
switch (e1i->rx.frame_nr) {
case 7:
case 15:
crc_rx = crc4_from_ts0_hist(e1i, e1i->rx.frame_nr == 15 ? true : false);
if (crc_rx != e1i->rx.crc4_last_smf)
crc4_error = true;
else
crc4_error = false;
if (crc4_error != e1i->tx.crc4_error) {
notify_user(e1i, E1_NTFY_EVT_CRC_ERROR, crc4_error, NULL);
e1i->tx.crc4_error = crc4_error;
}
/* rotate computed CRC4 one further */
e1i->rx.crc4_last_smf = e1i->rx.crc4;
e1i->rx.crc4 = crc4itu_init();
break;
default:
break;
}
/* check if the remote side reports any CRC errors */
switch (e1i->rx.frame_nr) {
case 13:
case 15:
crc4_error = false;
if ((get_ts0_hist(e1i, 0) >> 7) == 0)
crc4_error = true;
if (crc4_error != e1i->rx.remote_crc4_error) {
notify_user(e1i, E1_NTFY_EVT_REMOTE_CRC_ERROR, crc4_error, NULL);
e1i->rx.remote_crc4_error = crc4_error;
}
break;
}
}
e1_aligned_common(e1i);
}
/* FSM State handler */
static void e1_aligned_basic(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv;
if (frame_alignment_lost(e1i)) {
osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0);
return;
}
e1_aligned_common(e1i);
}
static const struct osmo_fsm_state e1_align_states[] = {
[E1_AS_SEARCH_FRAME] = {
.name = "SEARCH_FRAME",
.in_event_mask = S(E1_AE_RX_TS0),
.out_state_mask = S(E1_AS_SEARCH_FRAME) |
S(E1_AS_SEARCH_CRC_MFRAME) |
S(E1_AS_ALIGNED_BASIC),
.action = e1_align_search_frame,
},
[E1_AS_SEARCH_CRC_MFRAME] = {
.name = "SEARCH_CRC_MFRAME",
.in_event_mask = S(E1_AE_RX_TS0),
.out_state_mask = S(E1_AS_SEARCH_FRAME) |
S(E1_AS_SEARCH_CRC_MFRAME) |
S(E1_AS_ALIGNED_CRC_MFRAME),
.action = e1_align_search_crc_mframe,
},
[E1_AS_ALIGNED_CRC_MFRAME] = {
.name = "ALIGNED_CRC_MFRAME",
.in_event_mask = S(E1_AE_RX_TS0),
.out_state_mask = S(E1_AS_SEARCH_FRAME) |
S(E1_AS_SEARCH_CRC_MFRAME) |
S(E1_AS_ALIGNED_CRC_MFRAME),
.action = e1_aligned_crc_mframe,
},
[E1_AS_ALIGNED_BASIC] = {
.name = "ALIGNED_BASIC",
.in_event_mask = S(E1_AE_RX_TS0),
.out_state_mask = S(E1_AS_SEARCH_FRAME),
.action = e1_aligned_basic,
},
};
static void e1_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv;
switch (event) {
case E1_AE_RESET:
e1i->rx.num_ts0_in_mframe_search = 0;
osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0);
break;
}
}
static struct osmo_fsm e1_align_fsm = {
.name = "e1-align",
.states = e1_align_states,
.num_states = ARRAY_SIZE(e1_align_states),
.allstate_event_mask = S(E1_AE_RESET),
.allstate_action = e1_allstate,
.log_subsys = DLGLOBAL,
.event_names = e1_align_evt_names,
};
static void align_fsm_reset(struct osmo_e1f_instance *e1i)
{
osmo_fsm_inst_dispatch(e1i->rx.fi, E1_AE_RESET, NULL);
}
static void e1_rx_hist_add(struct osmo_e1f_instance *e1i, uint8_t inb)
{
e1i->rx.ts0_history[e1i->rx.frame_nr] = inb;
if (e1i->rx.ts0_hist_len < 16)
e1i->rx.ts0_hist_len++;
}
static void e1_rx_ts0(struct osmo_e1f_instance *e1i, uint8_t inb)
{
/* append just-received byte to the TS0 receive history buffer */
e1_rx_hist_add(e1i, inb);
/* notify the FSM that a new TS0 byte was received */
osmo_fsm_inst_dispatch(e1i->rx.fi, E1_AE_RX_TS0, NULL);
e1i->rx.frame_nr = (e1i->rx.frame_nr + 1) % 16;
}
static void e1_rx_tsN(struct osmo_e1f_instance_ts *e1t, uint8_t inb)
{
struct msgb *msg;
int count, rc;
if (!e1t->rx.enabled)
return;
if (!e1t->rx.msg)
e1t->rx.msg = msgb_alloc(e1t->rx.granularity, "E1 Rx");
msg = e1t->rx.msg;
OSMO_ASSERT(msg);
switch (e1t->mode) {
case OSMO_E1F_TS_RAW:
/* append byte at end of msgb */
msgb_put_u8(msg, inb);
/* flush msgb, if full */
if (msgb_tailroom(msg) <= 0) {
goto flush;
}
break;
case OSMO_E1F_TS_HDLC_CRC:
rc = osmo_isdnhdlc_decode(&e1t->rx.hdlc, &inb, 1, &count,
msgb_data(msg), msgb_tailroom(msg));
switch (rc) {
case -OSMO_HDLC_FRAMING_ERROR:
fprintf(stdout, "Framing Error\n");
break;
case -OSMO_HDLC_CRC_ERROR:
fprintf(stdout, "CRC Error\n");
break;
case -OSMO_HDLC_LENGTH_ERROR:
fprintf(stdout, "Length Error\n");
break;
case 0:
/* no output yet */
break;
default:
msgb_put(msg, rc);
goto flush;
}
break;
}
return;
flush:
if (!e1t->rx.data_cb)
msgb_free(msg);
else
e1t->rx.data_cb(e1t, msg);
e1t->rx.msg = NULL;
}
/*! Receive a single E1 frame of 32x8 (=256) bits
* \param e1i E1 instance for which the frame was received
* \param[in] in_frame caller-provided buffer of 32 octets
*
* The idea is that whoever calls us will already have done the bit-alignment,
* i.e. the first bit of TS0 of the frame will be octet-aligned and hence the
* entire 256bit buffer is provided as octet-aligned 32bytes in \a in_frame.
*/
int osmo_e1f_rx_frame(struct osmo_e1f_instance *e1i, const uint8_t *in_frame)
{
int i;
e1_rx_update_crc4(e1i, in_frame);
e1_rx_ts0(e1i, in_frame[0]);
for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) {
struct osmo_e1f_instance_ts *e1t = &e1i->ts[i];
e1_rx_tsN(e1t, in_frame[i]);
}
return 0;
}
int osmo_e1f_init(void)
{
return osmo_fsm_register(&e1_align_fsm);
}