isdn: initial implementation of the V.110 TA

ITU-T recommendation V.110 defines Terminal Adaptor (TA) functions
for the connection of Terminal Equipment (TE) having standard V-series
interfaces to the ISDN.  This patch brings "software" implementation
of the TA to libosmoisdn.

The primary user for this soft-TA is the mobile-side implementation
of CSD (Circuit Switched Data) in osmocom-bb.  CSD is heavily based
on V.110, which is not surprising given that GSM is a "wireless ISDN".
Nevertheless, this code will likely also be useful in the context
of retro-networking.

Similarly to the existing V.110 code in libosmoisdn, the present
implementation aims to be functional and correct, rather than
efficient in any way.  It also has several limitations, which are
not critical for the CSD use case, but eventually may be a problem
for other use cases in the context of retro-networking.

Therefore, the V.110 TA API should be considered _unstable_,
and may be subject to change in the future.

  +-------+      +------+   B-channel   +------+      +-------+
  |  TE1  |------|  TA  |~~~~~~~~~~~~~~~|  TA  |------|  TE2  |
  +-------+      +------+               +------+      +-------+

TE (also known as DTE) is basically a computer, having a V-series
(usually RS-232) connection to TA (also known as DCE).  The TA acts
like a regular analog modem, except that it is not performing any
kind of modulation or demodulation itself.

The TE-TA interface is implemented by the user supplied callback
functions, configured during the allocation of a TA instance:

* .rx_cb() - receive call-back of the application,
* .tx_cb() - transmit call-back of the application,
* .status_update_cb() - status line update call-back.

In addition to that, the application (TE) can interact with the
V.24 status lines (circuits) using the following API:

* osmo_v110_ta_{get,set}_status(),
* osmo_v110_ta_{get,set}_circuit().

The Rx and Tx between TE and TA is always driven by the TA itself,
as a result of an interaction with the lower layer implementing
the B-channel interface.  There is currently no buffering and thus
no way for TE to initiate transmission or pull data on its own.

The TA-TA (B-channel) interface is implemented by the following
functions, which are meant to be called by the lower layer
transmitting and receiving V.110 frames over certain medium:

* osmo_v110_ta_frame_in() - indicate a received V.110 frame,
* osmo_v110_ta_frame_out() - pull a V.110 frame for transmission,
* osmo_v110_ta_[de]sync_ind() - indicate a synchronization event.

The lower layer is responsible for finding the synchronization
pattern (if needed), aligning to the frame boundaries, and doing
the V.110 frame coding.

The D-channel signalling is behind the scope of this module.

Initial (Work-in-Progress) implementation by Harald Welte,
completed and co-authored by Vadim Yanitskiy.

Change-Id: I5716bd6fd0201ee7a7a29e72f775972cd374082f
Related: OS#4396
This commit is contained in:
Vadim Yanitskiy 2023-03-14 20:33:51 +01:00 committed by laforge
parent b71f461263
commit 85554db38d
10 changed files with 1758 additions and 1 deletions

View File

@ -13,5 +13,6 @@ core ADD osmo_sock_sctp_get_peer_addr_info()
core ADD gsmtap_inst_fd2() core, DEPRECATE gsmtap_inst_fd()
core behavior change osmo_tdef_fsm_inst_state_chg(): allow millisecond precision
isdn ABI change add states and flags for external T200 handling
isdn ADD initial implementation of the V.110 Terminal Adapter
gsm ABI change add T200 timer states to lapdm_datalink
gsm ABI change add UI queue to struct lapdm_datalink

View File

@ -2,6 +2,7 @@ osmoisdn_HEADERS = \
i460_mux.h \
lapd_core.h \
v110.h \
v110_ta.h \
$(NULL)
osmoisdndir = $(includedir)/osmocom/isdn

View File

@ -0,0 +1,113 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/bits.h>
#include <osmocom/isdn/v110.h>
/* Definition of this struct is [intentionally] kept private */
struct osmo_v110_ta;
/*! V.110 5.4.1 Local flow control (DTE-DCE or TE-TA) mode */
enum osmo_v110_local_flow_ctrl_mode {
OSMO_V110_LOCAL_FLOW_CTRL_NONE, /*!< No local flow control */
OSMO_V110_LOCAL_FLOW_CTRL_133_106, /*!< 5.4.1.1 133/106 operation */
OSMO_V110_LOCAL_FLOW_CTRL_105_106, /*!< 5.4.1.2 105/106 operation */
OSMO_V110_LOCAL_FLOW_CTRL_XON_XOFF, /*!< 5.4.1.3 XON/XOFF operation */
};
/*! Configuration for a V.110 TA instance */
struct osmo_v110_ta_cfg {
/*! Configuration flags (behavior switches and quirks) */
unsigned int flags;
/*! Synchronous user rate */
enum osmo_v100_sync_ra1_rate rate;
/*! Flow control configuration */
struct {
/*! Local TA-TE (DTE-DCE) flow control mode */
enum osmo_v110_local_flow_ctrl_mode local;
/*! End-to-end (TA-to-TA) flow control state */
bool end_to_end;
} flow_ctrl;
/*! Opaque application-private data; passed to call-backs. */
void *priv;
/*! Receive call-back of the application.
* \param[in] priv opaque application-private data.
* \param[in] buf output buffer for writing to be transmitted data.
* \param[in] buf_size size of the output buffer. */
void (*rx_cb)(void *priv, const ubit_t *buf, size_t buf_size);
/*! Transmit call-back of the application.
* \param[in] priv opaque application-private data.
* \param[out] buf output buffer for writing to be transmitted data.
* \param[in] buf_size size of the output buffer. */
void (*tx_cb)(void *priv, ubit_t *buf, size_t buf_size);
/*! Modem status line update call-back (optional).
* \param[in] priv opaque application-private data.
* \param[in] status updated status; bit-mask of OSMO_V110_TA_C_*. */
void (*status_update_cb)(void *priv, unsigned int status);
};
struct osmo_v110_ta *osmo_v110_ta_alloc(void *ctx, const char *name,
const struct osmo_v110_ta_cfg *cfg);
void osmo_v110_ta_free(struct osmo_v110_ta *ta);
/*! Various timers for a V.110 TA instance */
enum osmo_v110_ta_timer {
/*! 7.1.5 Loss of frame synchronization: sync recovery timer.
* T-number is not assigned in V.110, so we call it X1. */
OSMO_V110_TA_TIMER_X1 = -1,
/*! 7.1.2 Connect TA to line: sync establishment timer */
OSMO_V110_TA_TIMER_T1 = 1,
/*! 7.1.4 Disconnect mode: disconnect confirmation timer */
OSMO_V110_TA_TIMER_T2 = 2,
};
int osmo_v110_ta_set_timer_val_ms(struct osmo_v110_ta *ta,
enum osmo_v110_ta_timer timer,
unsigned long val_ms);
int osmo_v110_ta_frame_in(struct osmo_v110_ta *ta, const struct osmo_v110_decoded_frame *in);
int osmo_v110_ta_frame_out(struct osmo_v110_ta *ta, struct osmo_v110_decoded_frame *out);
int osmo_v110_ta_sync_ind(struct osmo_v110_ta *ta);
int osmo_v110_ta_desync_ind(struct osmo_v110_ta *ta);
/*! ITU-T Table 9 "Interchange circuit" (see also ITU-T V.24 Chapter 3).
* XXX: Not all circuits are present here, only those which we actually use.
* TODO: add human-friendly abbreviated circuit names. */
enum osmo_v110_ta_circuit {
OSMO_V110_TA_C_105, /*!< DTE->DCE | RTS (Request to Send) */
OSMO_V110_TA_C_106, /*!< DTE<-DCE | CTS (Clear to Send) */
OSMO_V110_TA_C_107, /*!< DTE<-DCE | DSR (Data Set Ready) */
OSMO_V110_TA_C_108, /*!< DTE->DCE | DTR (Data Terminal Ready) */
OSMO_V110_TA_C_109, /*!< DTE<-DCE | DCD (Data Carrier Detect) */
OSMO_V110_TA_C_133, /*!< DTE->DCE | Ready for receiving */
};
extern const struct value_string osmo_v110_ta_circuit_names[];
extern const struct value_string osmo_v110_ta_circuit_descs[];
/*! Get a short name of the given TA's circuit (format: NNN[/ABBR]). */
static inline const char *osmo_v110_ta_circuit_name(enum osmo_v110_ta_circuit circuit)
{
return get_value_string(osmo_v110_ta_circuit_names, circuit);
}
/*! Get a brief description of the given TA's circuit. */
static inline const char *osmo_v110_ta_circuit_desc(enum osmo_v110_ta_circuit circuit)
{
return get_value_string(osmo_v110_ta_circuit_descs, circuit);
}
unsigned int osmo_v110_ta_get_status(const struct osmo_v110_ta *ta);
bool osmo_v110_ta_get_circuit(const struct osmo_v110_ta *ta,
enum osmo_v110_ta_circuit circuit);
int osmo_v110_ta_set_circuit(struct osmo_v110_ta *ta,
enum osmo_v110_ta_circuit circuit, bool active);

View File

@ -13,7 +13,7 @@ endif
noinst_LTLIBRARIES = libisdnint.la
lib_LTLIBRARIES = libosmoisdn.la
libisdnint_la_SOURCES = i460_mux.c lapd_core.c v110.c
libisdnint_la_SOURCES = i460_mux.c lapd_core.c v110.c v110_ta.c
libisdnint_la_LDFLAGS = -no-undefined
libisdnint_la_LIBADD = $(top_builddir)/src/core/libosmocore.la

View File

@ -33,5 +33,19 @@ osmo_v110_sync_ra1_get_intermediate_rate;
osmo_v110_sync_ra1_user_to_ir;
osmo_v110_sync_ra1_ir_to_user;
osmo_v110_ta_alloc;
osmo_v110_ta_free;
osmo_v110_ta_set_timer_val_ms;
osmo_v110_ta_frame_in;
osmo_v110_ta_frame_out;
osmo_v110_ta_sync_ind;
osmo_v110_ta_desync_ind;
osmo_v110_ta_get_status;
osmo_v110_ta_get_circuit;
osmo_v110_ta_set_circuit;
osmo_v110_ta_circuit_names;
osmo_v110_ta_circuit_descs;
local: *;
};

872
src/isdn/v110_ta.c Normal file
View File

@ -0,0 +1,872 @@
/*! \file v110_ta.c
* TA (Terminal Adapter) implementation as per ITU-T V.110. */
/*
* (C) 2022 by Harald Welte <laforge@gnumonks.org>
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* Initial (Work-in-Progress) implementation by Harald Welte,
* completed and co-authored by Vadim Yanitskiy.
*
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* 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 <errno.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/tdef.h>
#include <osmocom/core/fsm.h>
#include <osmocom/isdn/v110.h>
#include <osmocom/isdn/v110_ta.h>
#define S(x) (1 << (x))
#define V24_FLAGMASK_IS_ON(flags, circuit) \
(((flags) & S(circuit)) != 0)
#define V24_FLAGMASK_IS_OFF(flags, circuit) \
(((flags) & S(circuit)) == 0)
#define V24_FLAGMASK_SET_ON(flags, circuit) \
(flags) |= S(circuit)
#define V24_FLAGMASK_SET_OFF(flags, circuit) \
(flags) &= ~S(circuit)
/* inverse logic: ON = binary 0; OFF = binary 1 */
#define V110_SX_BIT_ON 0
#define V110_SX_BIT_OFF 1
const struct value_string osmo_v110_ta_circuit_names[] = {
{ OSMO_V110_TA_C_105, "105/RTS" },
{ OSMO_V110_TA_C_106, "106/CTS" },
{ OSMO_V110_TA_C_107, "107/DSR" },
{ OSMO_V110_TA_C_108, "108/DTR" },
{ OSMO_V110_TA_C_109, "109/DCD" },
{ OSMO_V110_TA_C_133, "133" },
{ 0, NULL }
};
const struct value_string osmo_v110_ta_circuit_descs[] = {
{ OSMO_V110_TA_C_105, "Request to Send" },
{ OSMO_V110_TA_C_106, "Clear to Send" },
{ OSMO_V110_TA_C_107, "Data Set Ready" },
{ OSMO_V110_TA_C_108, "Data Terminal Ready" },
{ OSMO_V110_TA_C_109, "Data Carrier Detect" },
{ OSMO_V110_TA_C_133, "Ready for receiving" },
{ 0, NULL }
};
static const struct osmo_tdef v110_ta_tdef[] = {
{ .T = OSMO_V110_TA_TIMER_X1,
.unit = OSMO_TDEF_MS, .default_val = 3000, /* suggested in 7.1.5 e) */
.desc = "ITU-T V.110 7.1.5 Loss of frame synchronization: sync recovery timer" },
{ .T = OSMO_V110_TA_TIMER_T1,
.unit = OSMO_TDEF_MS, .default_val = 10000, /* suggested in 7.1.2.2 */
.desc = "ITU-T V.110 7.1.2 Connect TA to line: sync establishment timer" },
{ .T = OSMO_V110_TA_TIMER_T2,
.unit = OSMO_TDEF_MS, .default_val = 5000, /* suggested in 7.1.4.1 */
.desc = "ITU-T V.110 7.1.4 Disconnect mode: disconnect confirmation timer" },
{ /* end of list */ }
};
/*********************************************************************************
* V.110 TERMINAL ADAPTER FSM
*********************************************************************************/
enum v110_ta_fsm_state {
V110_TA_ST_IDLE_READY, /* 7.1.1 Idle (or ready) state */
V110_TA_ST_CON_TA_TO_LINE, /* 7.1.2 Connect TA to line state */
V110_TA_ST_DATA_TRANSFER, /* 7.1.3 Data transfer state */
V110_TA_ST_DISCONNECTING, /* 7.1.4 Disconnect mode */
V110_TA_ST_RESYNCING, /* 7.1.5 Re-synchronizing state */
};
enum v110_ta_fsm_event {
V110_TA_EV_RX_FRAME_IND, /* a V.110 frame was received by the lower layer */
V110_TA_EV_TX_FRAME_RTS, /* a V.110 frame is to be sent by the lower layer */
V110_TA_EV_V24_STATUS_CHG, /* V.24 flag-mask has been updated by TE */
V110_TA_EV_SYNC_IND, /* the lower layer has synchronized to the frame clock */
V110_TA_EV_DESYNC_IND, /* the lower layer has lost frame clock synchronization */
V110_TA_EV_TIMEOUT, /* generic event for handling a timeout condition */
};
static const struct value_string v110_ta_fsm_event_names[] = {
{ V110_TA_EV_RX_FRAME_IND, "RX_FRAME_IND" },
{ V110_TA_EV_TX_FRAME_RTS, "TX_FRAME_RTS" },
{ V110_TA_EV_V24_STATUS_CHG, "V24_STATUS_CHG" },
{ V110_TA_EV_SYNC_IND, "SYNC_IND" },
{ V110_TA_EV_DESYNC_IND, "DESYNC_IND" },
{ V110_TA_EV_TIMEOUT, "TIMEOUT" },
{ 0, NULL }
};
enum v110_ta_d_bit_mode {
V110_TA_DBIT_M_ALL_ZERO = 0, /* set all bits to binary '0' */
V110_TA_DBIT_M_ALL_ONE = 1, /* set all bits to binary '1' */
V110_TA_DBIT_M_FORWARD, /* forward D-bits to/from DTE */
};
struct v110_ta_state {
/*! V.24 status flags shared between DTE (user) and DCE (TA, us) */
unsigned int v24_flags;
struct {
/* what kind of D-bits to transmit in V.110 frames */
enum v110_ta_d_bit_mode d_bit_mode;
/* what to put in S-bits of transmitted V.110 frames */
ubit_t s_bits;
/* what to put in X-bits of transmitted V.110 frames */
ubit_t x_bits;
} tx;
struct {
enum v110_ta_d_bit_mode d_bit_mode;
} rx;
};
struct osmo_v110_ta {
const char *name;
struct osmo_tdef *Tdefs;
struct osmo_fsm_inst *fi;
struct osmo_v110_ta_cfg *cfg;
struct v110_ta_state state;
};
static inline bool v110_df_x_bits_are(const struct osmo_v110_decoded_frame *df, ubit_t cmp)
{
return (df->x_bits[0] == cmp) && (df->x_bits[1] == cmp);
}
static inline bool v110_df_s_bits_are(const struct osmo_v110_decoded_frame *df, ubit_t cmp)
{
/* ITU-T Table 2/V.110 (see also 5.1.2.3) defines the following S-bits:
* S1, S3, S4, S6, S8, S9 (6 bits total). However, fr->s_bits[] contains
* 9 (MAX_S_BITS) bits, including the undefined bits S2, S5, S7.
* Hence we must skip those undefined bits. */
static const uint8_t sbit_map[] = { 0, 2, 3, 5, 7, 8 };
for (unsigned int i = 0; i < ARRAY_SIZE(sbit_map); i++) {
uint8_t idx = sbit_map[i];
if (df->s_bits[idx] != cmp)
return false;
}
return true;
}
static inline bool v110_df_d_bits_are(const struct osmo_v110_decoded_frame *df, ubit_t cmp)
{
for (unsigned int i = 0; i < MAX_D_BITS; i++) {
if (df->d_bits[i] != cmp)
return false;
}
return true;
}
/* handle one V.110 frame and forward user bits to the application */
static void v110_ta_handle_frame(const struct osmo_v110_ta *ta,
const struct osmo_v110_decoded_frame *df)
{
const struct osmo_v110_ta_cfg *cfg = ta->cfg;
const struct v110_ta_state *ts = &ta->state;
ubit_t user_bits[MAX_D_BITS];
int num_user_bits;
int rc;
switch (ts->rx.d_bit_mode) {
case V110_TA_DBIT_M_ALL_ZERO:
case V110_TA_DBIT_M_ALL_ONE:
/* generate as many user bits as needed for the configured rate */
num_user_bits = osmo_v110_sync_ra1_get_user_data_chunk_bitlen(cfg->rate);
OSMO_ASSERT(num_user_bits > 0);
/* set them all to binary '0' or binary '1' */
memset(&user_bits[0], (int)ts->rx.d_bit_mode, num_user_bits);
cfg->rx_cb(cfg->priv, &user_bits[0], num_user_bits);
break;
case V110_TA_DBIT_M_FORWARD:
rc = osmo_v110_sync_ra1_ir_to_user(cfg->rate, &user_bits[0], sizeof(user_bits), df);
if (rc > 0)
cfg->rx_cb(cfg->priv, &user_bits[0], rc);
/* XXX else: indicate an error somehow? */
break;
}
}
/* build one V.110 frame to transmit */
static void v110_ta_build_frame(const struct osmo_v110_ta *ta,
struct osmo_v110_decoded_frame *df)
{
const struct osmo_v110_ta_cfg *cfg = ta->cfg;
const struct v110_ta_state *ts = &ta->state;
ubit_t user_bits[MAX_D_BITS];
int num_user_bits;
int rc;
/* E-bits (E1/E2/E3 may be overwritten below) */
memset(df->e_bits, 1, sizeof(df->e_bits));
/* S-bits */
memset(df->s_bits, ts->tx.s_bits, sizeof(df->s_bits));
/* X-bits */
memset(df->x_bits, ts->tx.x_bits, sizeof(df->x_bits));
/* D-bits */
switch (ts->tx.d_bit_mode) {
case V110_TA_DBIT_M_ALL_ZERO:
case V110_TA_DBIT_M_ALL_ONE:
/* set them all to binary '0' or binary '1' */
memset(df->d_bits, (int)ts->tx.d_bit_mode, sizeof(df->d_bits));
break;
case V110_TA_DBIT_M_FORWARD:
/* how many user bits to retrieve */
num_user_bits = osmo_v110_sync_ra1_get_user_data_chunk_bitlen(cfg->rate);
OSMO_ASSERT(num_user_bits > 0);
/* retrieve user bits from the application */
cfg->tx_cb(cfg->priv, &user_bits[0], num_user_bits);
/* convert user bits to intermediate rate (store to df) */
rc = osmo_v110_sync_ra1_user_to_ir(cfg->rate, df, &user_bits[0], num_user_bits);
OSMO_ASSERT(rc == 0);
break;
}
}
static void v110_ta_flags_updated(const struct osmo_v110_ta *ta)
{
const struct osmo_v110_ta_cfg *cfg = ta->cfg;
if (cfg->status_update_cb != NULL)
cfg->status_update_cb(cfg->priv, ta->state.v24_flags);
}
static const struct osmo_tdef_state_timeout v110_ta_fsm_timeouts[32] = {
[V110_TA_ST_RESYNCING] = { .T = OSMO_V110_TA_TIMER_X1 },
[V110_TA_ST_CON_TA_TO_LINE] = { .T = OSMO_V110_TA_TIMER_T1 },
[V110_TA_ST_DISCONNECTING] = { .T = OSMO_V110_TA_TIMER_T2 },
};
#define v110_ta_fsm_state_chg(state) \
osmo_tdef_fsm_inst_state_chg(fi, state, \
v110_ta_fsm_timeouts, \
((struct osmo_v110_ta *)(fi->priv))->Tdefs, \
0)
/* ITU-T V.110 Section 7.1.1 */
static void v110_ta_fsm_idle_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
struct v110_ta_state *ts = &ta->state;
/* 7.1.1.2 During the idle (or ready) state the TA will transmit continuous binary 1s into the B-channel */
ts->tx.d_bit_mode = V110_TA_DBIT_M_ALL_ONE; /* circuit 103: continuous binary '1' */
ts->tx.s_bits = V110_SX_BIT_OFF; /* OFF is binary '1' */
ts->tx.x_bits = V110_SX_BIT_OFF; /* OFF is binary '1' */
/* 7.1.1.3 During the idle (or ready) state the TA (DCE) will transmit the following toward the DTE: */
/* - circuit 104: continuous binary '1' */
ts->rx.d_bit_mode = V110_TA_DBIT_M_ALL_ONE;
/* - circuits 107, 106, 109 = OFF */
V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V110_TA_C_106);
V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V110_TA_C_107);
V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V110_TA_C_109);
v110_ta_flags_updated(ta);
}
/* ITU-T V.110 Section 7.1.1 */
static void v110_ta_fsm_idle_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
struct v110_ta_state *ts = &ta->state;
switch (event) {
case V110_TA_EV_V24_STATUS_CHG:
/* When the TA is to be switched to the data mode, circuit 108 must be ON */
if (V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_108)) {
/* 7.12.2: Start timer T1 when switching to CON_TA_LINE */
v110_ta_fsm_state_chg(V110_TA_ST_CON_TA_TO_LINE);
}
break;
case V110_TA_EV_RX_FRAME_IND:
v110_ta_handle_frame(ta, (const struct osmo_v110_decoded_frame *)data);
break;
case V110_TA_EV_TX_FRAME_RTS:
v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
break;
default:
OSMO_ASSERT(0);
}
}
/* ITU-T V.110 Section 7.1.2 */
static void v110_ta_fsm_connect_ta_to_line_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
struct v110_ta_state *ts = &ta->state;
/* 7.1.2.1 Switching to the data mode causes the TA to transmit the following towards the ISDN: */
/* a) frame synchronization pattern as described in 5.1.3.1 and 5.2.1 (done by the API user) */
/* b) circuit 103: continuous binary '1' */
ts->tx.d_bit_mode = V110_TA_DBIT_M_ALL_ONE;
/* c) status bits S = OFF and X = OFF */
ts->tx.s_bits = V110_SX_BIT_OFF; /* OFF is binary '1' */
ts->tx.x_bits = V110_SX_BIT_OFF; /* OFF is binary '1' */
/* 7.1.2.2 ... the receiver in the TA will begin to search for the frame synchronization
* pattern in the received bit stream (see 5.1.3.1 and 5.2.1) and start timer T1. */
OSMO_ASSERT(fi->T == OSMO_V110_TA_TIMER_T1);
}
/* ITU-T V.110 Section 7.1.2 */
static void v110_ta_fsm_connect_ta_to_line(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
struct v110_ta_state *ts = &ta->state;
switch (event) {
case V110_TA_EV_V24_STATUS_CHG:
/* If circuit 108 is OFF, we go back to IDLE/READY */
if (V24_FLAGMASK_IS_OFF(ts->v24_flags, OSMO_V110_TA_C_108))
v110_ta_fsm_state_chg(V110_TA_ST_IDLE_READY);
break;
case V110_TA_EV_SYNC_IND:
/* 7.1.2.3 When the receiver recognizes the frame synchronization pattern, it causes the S-
* and X-bits in the transmitted frames to be turned ON (provided that circuit 108 is ON). */
OSMO_ASSERT(V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_108));
ts->tx.s_bits = V110_SX_BIT_ON;
ts->tx.x_bits = V110_SX_BIT_ON;
break;
case V110_TA_EV_RX_FRAME_IND:
{
const struct osmo_v110_decoded_frame *df = data;
/* 7.1.2.4 When the receiver recognizes that the status of bits S and X are ON */
if (v110_df_s_bits_are(df, V110_SX_BIT_ON) &&
v110_df_x_bits_are(df, V110_SX_BIT_ON)) {
/* ... it will perform the following functions: */
/* a) Turn ON circuit 107 toward the DTE and stop timer T1 */
V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V110_TA_C_107);
osmo_timer_del(&fi->timer);
/* b) Then, circuit 103 may be connected to the data bits in the frame; however, the
* DTE must maintain a binary 1 condition on circuit 103 until circuit 106 is turned
* ON in the next portion of the sequence. */
/* c) Turn ON circuit 109 and connect the data bits to circuit 104. */
V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V110_TA_C_109);
ts->rx.d_bit_mode = V110_TA_DBIT_M_FORWARD;
/* d) After an interval of N bits (see 6.3), it will turn ON circuit 106. */
V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V110_TA_C_106);
ts->tx.d_bit_mode = V110_TA_DBIT_M_FORWARD;
v110_ta_flags_updated(ta);
/* Circuit 106 transitioning from OFF to ON will cause the transmitted data to
* transition from binary 1 to the data mode. */
v110_ta_fsm_state_chg(V110_TA_ST_DATA_TRANSFER);
}
v110_ta_handle_frame(ta, df);
break;
}
case V110_TA_EV_TX_FRAME_RTS:
v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
break;
case V110_TA_EV_TIMEOUT:
v110_ta_fsm_state_chg(V110_TA_ST_IDLE_READY);
break;
default:
OSMO_ASSERT(0);
}
}
/* ITU-T V.110 Section 7.1.3 */
static void v110_ta_fsm_data_transfer_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
struct v110_ta_state *ts = &ta->state;
/* 7.1.3.1 While in the data transfer state, the following circuit conditions exist:
* a): 105, 107, 108, and 109 are in the ON condition */
/* XXX: OSMO_ASSERT(V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_105)); */
V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V110_TA_C_107);
/* XXX: OSMO_ASSERT(V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_108)); */
V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V110_TA_C_109);
/* b) data is being transmitted on circuit 103 and received on circuit 104 */
ts->rx.d_bit_mode = V110_TA_DBIT_M_FORWARD;
ts->tx.d_bit_mode = V110_TA_DBIT_M_FORWARD;
/* c) circuits 133 (when implemented) and 106 are in the ON condition unless local out-of-band
* flow control is being used, either or both circuits may be in the ON or the OFF condition. */
if (!ta->cfg->flow_ctrl.end_to_end) {
/* XXX: OSMO_ASSERT(V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_133)); */
V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V110_TA_C_106);
}
v110_ta_flags_updated(ta);
/* 7.1.3.2 While in the data transfer state, the following status bit conditions exist: */
/* a) status bits S in both directions are in the ON condition; */
ts->tx.s_bits = V110_SX_BIT_ON;
/* b) status bits X in both directions are in the ON condition unless end-to-end flow control
* is being used, in which case status bit X in either or both directions may be in the
* ON or the OFF condition. */
if (!ta->cfg->flow_ctrl.end_to_end)
ts->tx.x_bits = V110_SX_BIT_ON;
}
/* ITU-T V.110 Section 7.1.3 */
static void v110_ta_fsm_data_transfer(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
struct v110_ta_state *ts = &ta->state;
/* 7.1.3.3 While in the data transfer state: */
/* a) the S status bits shall *not* be mapped to/from the interchange circuits */
/* b) the X status bits shall *not* be mapped according to Table 3,
* unless end-to-end flow control is implemented */
/* TODO: if (ta->cfg->flow_ctrl.end_to_end) { ... } */
switch (event) {
case V110_TA_EV_V24_STATUS_CHG:
/* 7.1.4.1 At the completion of the data transfer phase, the local DTE will indicate a
* disconnect request by turning OFF circuit 108 */
if (V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_108))
break;
v110_ta_fsm_state_chg(V110_TA_ST_DISCONNECTING);
break;
case V110_TA_EV_DESYNC_IND:
v110_ta_fsm_state_chg(V110_TA_ST_RESYNCING);
break;
case V110_TA_EV_TX_FRAME_RTS:
v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
break;
case V110_TA_EV_RX_FRAME_IND:
{
const struct osmo_v110_decoded_frame *df = data;
/* 7.1.4.2 ... this TA will recognize the transition of the status bits S from
* ON to OFF and the data bits from data to binary 0 as a disconnect request */
if (v110_df_s_bits_are(df, V110_SX_BIT_OFF) && v110_df_d_bits_are(df, 0)) {
/* ... and it will turn OFF circuits 107 and 109. */
V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V110_TA_C_107);
V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V110_TA_C_109);
v110_ta_flags_updated(ta);
/* DTE should respond by turning OFF circuit 108 */
break; /* XXX: shall we forward D-bits to DTE anyway? */
}
v110_ta_handle_frame(ta, df);
break;
}
default:
OSMO_ASSERT(0);
}
}
/* ITU-T V.110 Section 7.1.4 */
static void v110_ta_fsm_disconnect_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
struct v110_ta_state *ts = &ta->state;
/* 7.1.4.1 At the completion of the data transfer phase, the local DTE will indicate a
* disconnect request by turning OFF circuit 108. This will cause the following to occur: */
/* a) the status bits S in the frame toward ISDN will turn OFF, status bits X are kept ON */
ts->tx.s_bits = V110_SX_BIT_OFF;
/* b) circuit 106 will be turned OFF */
V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V110_TA_C_106);
v110_ta_flags_updated(ta);
/* c) the data bits in the frame will be set to binary 0. */
ts->tx.d_bit_mode = V110_TA_DBIT_M_ALL_ZERO;
/* To guard against the failure of the remote TA to respond to the disconnect request,
* the local TA may start a timer T2 (suggested value 5 s) which is stopped by the
* reception or transmission of any D-channel clearing message (DISCONNECT, RELEASE,
* RELEASE COMPLETE). */
OSMO_ASSERT(fi->T == OSMO_V110_TA_TIMER_T2);
}
/* ITU-T V.110 Section 7.1.4 */
static void v110_ta_fsm_disconnect(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
switch (event) {
case V110_TA_EV_V24_STATUS_CHG:
break; /* nothing to do */
case V110_TA_EV_TX_FRAME_RTS:
v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
break;
case V110_TA_EV_RX_FRAME_IND:
{
const struct osmo_v110_decoded_frame *df = data;
/* 7.1.4.3 The TA at the station that originated the disconnect request will
* recognize reception of S = OFF or the loss of framing signals as a disconnect
* acknowledgement and turn OFF circuits 107 and 109. */
if (v110_df_s_bits_are(df, V110_SX_BIT_OFF)) {
/* circuits 107 and 109 set to off in .onenter() */
v110_ta_fsm_state_chg(V110_TA_ST_IDLE_READY);
}
v110_ta_handle_frame(ta, df);
break;
}
case V110_TA_EV_DESYNC_IND:
case V110_TA_EV_TIMEOUT:
/* circuits 107 and 109 set to off in .onenter() */
v110_ta_fsm_state_chg(V110_TA_ST_IDLE_READY);
break;
default:
OSMO_ASSERT(0);
}
}
/* ITU-T V.110 Section 7.1.5 */
static void v110_ta_fsm_resyncing_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
struct v110_ta_state *ts = &ta->state;
/* 7.1.5 In the event of loss of frame synchronization, the (local) TA should
* attempt to resynchronize as follows: */
/* a) Place circuit 104 in binary 1 condition (passes from the data mode) */
ts->rx.d_bit_mode = V110_TA_DBIT_M_ALL_ONE;
/* b) Turn OFF status bit X in the transmitted frame */
ts->tx.x_bits = V110_SX_BIT_OFF;
/* guard timeout, see 7.1.5 e) */
OSMO_ASSERT(fi->T == OSMO_V110_TA_TIMER_X1);
}
/* ITU-T V.110 Section 7.1.5 */
static void v110_ta_fsm_resyncing(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
struct v110_ta_state *ts = &ta->state;
switch (event) {
case V110_TA_EV_V24_STATUS_CHG:
break; /* TODO: handle circuit 108 being set to OFF? */
case V110_TA_EV_TX_FRAME_RTS:
v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
break;
case V110_TA_EV_SYNC_IND:
/* f) If resynchronization is achieved, the local TA should turn ON status bit X */
ts->tx.x_bits = V110_SX_BIT_ON;
v110_ta_fsm_state_chg(V110_TA_ST_DATA_TRANSFER);
break;
case V110_TA_EV_TIMEOUT:
/* e) If after an interval of X1 seconds the local TA cannot attain synchronization,
* it should send a disconnect request by turning OFF all of the status bits for several
* (at least three) frames with data bits set to binary 0 and then disconnect by turning
* OFF circuit 107 and transferring to the disconnected mode as discussed in 7.1.4.2. */
ts->tx.s_bits = V110_SX_BIT_OFF;
ts->tx.x_bits = V110_SX_BIT_OFF;
ts->tx.d_bit_mode = V110_TA_DBIT_M_ALL_ZERO;
/* TODO: actually Tx those frames (delay state transition) */
V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V110_TA_C_107);
v110_ta_flags_updated(ta);
v110_ta_fsm_state_chg(V110_TA_ST_DISCONNECTING);
break;
default:
OSMO_ASSERT(0);
}
}
static int v110_ta_timer_cb(struct osmo_fsm_inst *fi)
{
osmo_fsm_inst_dispatch(fi, V110_TA_EV_TIMEOUT, NULL);
return 0;
}
static const struct osmo_fsm_state v110_ta_states[] = {
[V110_TA_ST_IDLE_READY] = {
.name = "IDLE_READY",
.in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
| S(V110_TA_EV_TX_FRAME_RTS)
| S(V110_TA_EV_RX_FRAME_IND),
.out_state_mask = S(V110_TA_ST_IDLE_READY)
| S(V110_TA_ST_CON_TA_TO_LINE),
.action = &v110_ta_fsm_idle_ready,
.onenter = &v110_ta_fsm_idle_ready_onenter,
},
[V110_TA_ST_CON_TA_TO_LINE] = {
.name = "CONNECT_TA_TO_LINE",
.in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
| S(V110_TA_EV_TIMEOUT)
| S(V110_TA_EV_SYNC_IND)
| S(V110_TA_EV_TX_FRAME_RTS)
| S(V110_TA_EV_RX_FRAME_IND),
.out_state_mask = S(V110_TA_ST_DATA_TRANSFER)
| S(V110_TA_ST_IDLE_READY),
.action = &v110_ta_fsm_connect_ta_to_line,
.onenter = &v110_ta_fsm_connect_ta_to_line_onenter,
},
[V110_TA_ST_DATA_TRANSFER] = {
.name = "DATA_TRANSFER",
.in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
| S(V110_TA_EV_DESYNC_IND)
| S(V110_TA_EV_TX_FRAME_RTS)
| S(V110_TA_EV_RX_FRAME_IND),
.out_state_mask = S(V110_TA_ST_RESYNCING)
| S(V110_TA_ST_DISCONNECTING),
.action = &v110_ta_fsm_data_transfer,
.onenter = &v110_ta_fsm_data_transfer_onenter,
},
[V110_TA_ST_DISCONNECTING] = {
.name = "DISCONNECTING",
.in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
| S(V110_TA_EV_TIMEOUT)
| S(V110_TA_EV_TX_FRAME_RTS)
| S(V110_TA_EV_RX_FRAME_IND)
| S(V110_TA_EV_DESYNC_IND),
.out_state_mask = S(V110_TA_ST_IDLE_READY),
.action = &v110_ta_fsm_disconnect,
.onenter = &v110_ta_fsm_disconnect_onenter,
},
[V110_TA_ST_RESYNCING] = {
.name = "RESYNCING",
.in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
| S(V110_TA_EV_TIMEOUT)
| S(V110_TA_EV_TX_FRAME_RTS)
| S(V110_TA_EV_SYNC_IND),
.out_state_mask = S(V110_TA_ST_IDLE_READY)
| S(V110_TA_ST_DATA_TRANSFER),
.action = &v110_ta_fsm_resyncing,
.onenter = &v110_ta_fsm_resyncing_onenter,
},
};
static struct osmo_fsm osmo_v110_ta_fsm = {
.name = "V110-TA",
.states = v110_ta_states,
.num_states = ARRAY_SIZE(v110_ta_states),
.timer_cb = v110_ta_timer_cb,
.log_subsys = DLGLOBAL,
.event_names = v110_ta_fsm_event_names,
};
static __attribute__((constructor)) void on_dso_load(void)
{
OSMO_ASSERT(osmo_fsm_register(&osmo_v110_ta_fsm) == 0);
}
/*! Allocate a V.110 TA (Terminal Adapter) instance.
* \param[in] ctx parent talloc context.
* \param[in] name name of the TA instance.
* \param[in] cfg initial configuration of the TA instance.
* \returns pointer to allocated TA instance; NULL on error. */
struct osmo_v110_ta *osmo_v110_ta_alloc(void *ctx, const char *name,
const struct osmo_v110_ta_cfg *cfg)
{
struct osmo_v110_ta *ta;
OSMO_ASSERT(cfg != NULL);
OSMO_ASSERT(cfg->rx_cb != NULL);
OSMO_ASSERT(cfg->tx_cb != NULL);
/* local (TE-TA) flow control is not implemented */
if (cfg->flow_ctrl.local != OSMO_V110_LOCAL_FLOW_CTRL_NONE) {
LOGP(DLGLOBAL, LOGL_ERROR, "Local (TE-TA) flow control is not implemented\n");
return NULL;
}
ta = talloc_zero(ctx, struct osmo_v110_ta);
if (ta == NULL)
return NULL;
ta->name = talloc_strdup(ta, name);
ta->cfg = talloc_memdup(ta, cfg, sizeof(*cfg));
if (ta->name == NULL || ta->cfg == NULL)
goto exit_free;
ta->Tdefs = talloc_memdup(ta, v110_ta_tdef, sizeof(v110_ta_tdef));
if (ta->Tdefs == NULL)
goto exit_free;
osmo_tdefs_reset(ta->Tdefs); /* apply default values */
ta->fi = osmo_fsm_inst_alloc(&osmo_v110_ta_fsm, ta, ta, LOGL_DEBUG, name);
if (ta->fi == NULL)
goto exit_free;
/* perform a loop transition to init the internal state */
osmo_fsm_inst_state_chg(ta->fi, V110_TA_ST_IDLE_READY, 0, 0);
return ta;
exit_free:
if (ta->fi != NULL)
osmo_fsm_inst_free(ta->fi);
talloc_free(ta);
return NULL;
}
/*! Release memory taken by the given V.110 TA instance.
* \param[in] ta TA instance to be free()d. */
void osmo_v110_ta_free(struct osmo_v110_ta *ta)
{
if (ta == NULL)
return;
if (ta->fi != NULL)
osmo_fsm_inst_free(ta->fi);
talloc_free(ta); /* also free()s name and cfg */
}
/*! Configure a timer of the given V.110 TA instance.
* \param[in] ta TA instance to be configured.
* \param[in] timer a timer to be configured.
* \param[in] val_ms the new timeout value to set (in milliseconds).
* \returns 0 in case of success; negative on error. */
int osmo_v110_ta_set_timer_val_ms(struct osmo_v110_ta *ta,
enum osmo_v110_ta_timer timer,
unsigned long val_ms)
{
return osmo_tdef_set(ta->Tdefs, (int)timer, val_ms, OSMO_TDEF_MS);
}
/*! Feed a [decoded] V.110 frame into the given TA instance.
*
* This function, like its _out counterpart, is intended to be used by the lower layers
* receiving V.110 frames over some medium. The caller of this function is responsible
* for finding the synchronization pattern (if needed), aligning to the frame boundaries,
* and decoding frames using osmo_v110_decode_frame() or osmo_csd_*_decode_frame().
*
* Bits E1/E2/E3 are expected to be set by the caller (if not being transmitted
* over the medium) in accordance with the configured synchronous user rate.
*
* Bits D1..D48 are passed to the bit rate adaption function RA1. The resulting output
* is then passed to the upper layer (application) via the configured .rx_cb(). Though,
* in certain states of the TA's FSM, bits D1..D48 are ignored and the upper layer
* gets a sequence of binary '0' or '1'.
*
* \param[in] ta TA instance to feed the given frame into.
* \param[in] in pointer to a [decoded] V.110 frame.
* \returns 0 in case of success; negative on error. */
int osmo_v110_ta_frame_in(struct osmo_v110_ta *ta, const struct osmo_v110_decoded_frame *in)
{
return osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_RX_FRAME_IND, (void *)in);
}
/*! Pull a [decoded] V.110 frame out of the given TA instance.
*
* This function, like its _in counterpart, is intended to be used by the lower layers
* transmitting V.110 frames over some medium. The caller of this function is responsible
* for encoding the output frame using osmo_v110_encode_frame() or osmo_csd_*_encode_frame().
*
* Bits E1/E2/E3 are set in accordance with the configured synchronous user rate.
* Bits E4/E5/E6/E7 are unconditionally set to binary '1'.
*
* Bits D1..D48 are set depending on the state of TA's FSM:
*
* - In data transfer mode, the user bits are obtained from the upper layer (application)
* via the configured .tx_cb(), and then passed to the bit rate adaption function RA1,
* which generates bits D1..D48.
* - In other modes, bits D1..D48 are all set to binary '0' or '1'.
*
* \param[in] ta TA instance to pull a frame from.
* \param[out] out where to store a [decoded] V.110 frame.
* \returns 0 in case of success; negative on error. */
int osmo_v110_ta_frame_out(struct osmo_v110_ta *ta, struct osmo_v110_decoded_frame *out)
{
return osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_TX_FRAME_RTS, (void *)out);
}
/*! Indicate a synchronization establishment event.
*
* This function is intended to be called when the lower layer
* achieves synchronization to the frame clock.
*
* \param[in] ta TA instance to indicate the event to.
* \returns 0 in case of success; negative on error. */
int osmo_v110_ta_sync_ind(struct osmo_v110_ta *ta)
{
return osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_SYNC_IND, NULL);
}
/*! Indicate a synchronization loss event.
*
* This function is intended to be called when the lower layer
* experiences a loss of synchronization with the frame clock.
*
* \param[in] ta TA instance to indicate the event to.
* \returns 0 in case of success; negative on error. */
int osmo_v110_ta_desync_ind(struct osmo_v110_ta *ta)
{
return osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_DESYNC_IND, NULL);
}
/*! Get the V.24 status bit-mask of the given TA instance.
* \param[in] ta TA instance to get the circuit bit-mask.
* \returns bitmask of OSMO_V110_TA_C_*. */
unsigned int osmo_v110_ta_get_status(const struct osmo_v110_ta *ta)
{
return ta->state.v24_flags;
}
/*! Set the V.24 status bit-mask of the given TA instance.
* \param[in] ta TA instance to update the circuit state.
* \param[in] status bit-mask of OSMO_V110_TA_C_*.
* \returns 0 on success; negative on error. */
static int v110_ta_set_status(struct osmo_v110_ta *ta, unsigned int status)
{
const unsigned int old_status = ta->state.v24_flags;
int rc = 0;
ta->state.v24_flags = status;
if (status != old_status)
rc = osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_V24_STATUS_CHG, NULL);
return rc;
}
/*! Get state of a V.24 circuit of the given TA instance.
* \param[in] ta TA instance to get the circuit state.
* \param[in] circuit a V.24 circuit, one of OSMO_V110_TA_C_*.
* \returns circuit state: active (true) or inactive (false). */
bool osmo_v110_ta_get_circuit(const struct osmo_v110_ta *ta,
enum osmo_v110_ta_circuit circuit)
{
return V24_FLAGMASK_IS_ON(ta->state.v24_flags, circuit);
}
/*! Activate/deactivate a V.24 circuit of the given TA instance.
* \param[in] ta TA instance to update the circuit state.
* \param[in] circuit a V.24 circuit, one of OSMO_V110_TA_C_* (DTE->DCE).
* \param[in] active activate (true) or deactivate (false) the circuit.
* \returns 0 on success; negative on error. */
int osmo_v110_ta_set_circuit(struct osmo_v110_ta *ta,
enum osmo_v110_ta_circuit circuit, bool active)
{
unsigned int status = ta->state.v24_flags;
/* permit setting only DTE->DCE circuits */
switch (circuit) {
case OSMO_V110_TA_C_105:
case OSMO_V110_TA_C_108:
case OSMO_V110_TA_C_133:
break;
default:
LOGPFSML(ta->fi, LOGL_ERROR,
"Setting circuit %s is not permitted (wrong direction?)\n",
osmo_v110_ta_circuit_name(circuit));
return -EACCES;
}
if (active)
V24_FLAGMASK_SET_ON(status, circuit);
else
V24_FLAGMASK_SET_OFF(status, circuit);
return v110_ta_set_status(ta, status);
}

View File

@ -54,6 +54,7 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \
auth/xor2g_test \
v110/frame_test \
v110/ra1_test \
v110/ta_test \
gsm44021/frame_csd_test \
osmo_io/osmo_io_test \
soft_uart/soft_uart_test \
@ -373,6 +374,9 @@ v110_frame_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la $(LDADD)
v110_ra1_test_SOURCES = v110/ra1_test.c
v110_ra1_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la $(LDADD)
v110_ta_test_SOURCES = v110/ta_test.c
v110_ta_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la $(LDADD)
gsm44021_frame_csd_test_SOURCES = gsm44021/frame_csd_test.c
gsm44021_frame_csd_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la \
$(top_builddir)/src/gsm/libosmogsm.la \
@ -493,6 +497,7 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
smscb/cbsp_test.ok \
v110/frame_test.ok \
v110/ra1_test.ok \
v110/ta_test.err \
gsm44021/frame_csd_test.ok \
osmo_io/osmo_io_test.ok osmo_io/osmo_io_test.err \
soft_uart/soft_uart_test.ok \
@ -712,6 +717,8 @@ endif
>$(srcdir)/v110/frame_test.ok
v110/ra1_test \
>$(srcdir)/v110/ra1_test.ok
v110/ta_test \
2>$(srcdir)/v110/ta_test.err
gsm44021/frame_csd_test \
>$(srcdir)/gsm44021/frame_csd_test.ok
osmo_io/osmo_io_test \

View File

@ -511,6 +511,12 @@ cat $abs_srcdir/v110/ra1_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/v110/ra1_test], [], [expout],[])
AT_CLEANUP
AT_SETUP([v110_ta_test])
AT_KEYWORDS([v110_ta_test])
cat $abs_srcdir/v110/ta_test.err > experr
AT_CHECK([$abs_top_builddir/tests/v110/ta_test], [], [], [experr])
AT_CLEANUP
AT_SETUP([gsm44021_frame_csd_test])
AT_KEYWORDS([gsm44021_frame_csd_test])
cat $abs_srcdir/gsm44021/frame_csd_test.ok > expout

459
tests/v110/ta_test.c Normal file
View File

@ -0,0 +1,459 @@
/*
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
*
* All Rights Reserved
*
* 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 <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/application.h>
#include <osmocom/isdn/v110.h>
#include <osmocom/isdn/v110_ta.h>
static void *test_ctx = NULL;
/* inverse logic: ON = binary 0; OFF = binary 1 */
#define V110_SX_BIT_ON 0
#define V110_SX_BIT_OFF 1
/*********************************************************************************
* V.110 TA configuration and callbacks
*********************************************************************************/
static void v110_ta_test_rx_cb(void *priv, const ubit_t *buf, size_t buf_size)
{
fprintf(stderr, "%s(buf_size=%zu): %s\n",
__func__, buf_size, osmo_ubit_dump(buf, buf_size));
}
static void v110_ta_test_tx_cb(void *priv, ubit_t *buf, size_t buf_size)
{
for (size_t i = 0; i < buf_size; i++)
buf[i] = (i & 1);
fprintf(stderr, "%s(buf_size=%zu): %s\n",
__func__, buf_size, osmo_ubit_dump(buf, buf_size));
}
static void v110_ta_test_status_update_cb(void *priv, unsigned int status)
{
fprintf(stderr, "%s(status=0x%08x)\n", __func__, status);
}
static const struct osmo_v110_ta_cfg v110_ta_test_cfg = {
.rate = OSMO_V110_SYNC_RA1_9600,
.rx_cb = &v110_ta_test_rx_cb,
.tx_cb = &v110_ta_test_tx_cb,
.status_update_cb = &v110_ta_test_status_update_cb,
};
/*********************************************************************************
* various helper functions
*********************************************************************************/
static void v110_ta_test_init_df(struct osmo_v110_decoded_frame *df)
{
/* quickly set all the bits to binary '1' */
memset(df, 1, sizeof(*df));
/* D-bits: 0101... pattern */
for (unsigned int i = 0; i < MAX_D_BITS; i += 2)
df->d_bits[i] = 0;
/* E-bits: E1/E2/E3 indicate 9600 bps */
df->e_bits[0] = 0;
}
static void v110_ta_test_dump_df(const struct osmo_v110_decoded_frame *df)
{
fprintf(stderr, " D-bits: %s\n", osmo_ubit_dump(&df->d_bits[0], MAX_D_BITS));
fprintf(stderr, " E-bits: %s\n", osmo_ubit_dump(&df->e_bits[0], MAX_E_BITS));
fprintf(stderr, " S-bits: %s\n", osmo_ubit_dump(&df->s_bits[0], MAX_S_BITS));
fprintf(stderr, " X-bits: %s\n", osmo_ubit_dump(&df->x_bits[0], MAX_X_BITS));
}
static void v110_ta_test_dump_circuit(const struct osmo_v110_ta *ta,
enum osmo_v110_ta_circuit circuit,
bool exp_state)
{
bool state = osmo_v110_ta_get_circuit(ta, circuit);
fprintf(stderr, "circuit %s (%s) is %s (expected to be %s)\n",
osmo_v110_ta_circuit_name(circuit),
osmo_v110_ta_circuit_desc(circuit),
state ? "ON" : "OFF",
exp_state ? "ON" : "OFF");
}
static void v110_ta_test_set_circuit(struct osmo_v110_ta *ta,
enum osmo_v110_ta_circuit circuit,
bool active)
{
int rc;
fprintf(stderr, "setting circuit %s (%s) %s\n",
osmo_v110_ta_circuit_name(circuit),
osmo_v110_ta_circuit_desc(circuit),
active ? "ON" : "OFF");
rc = osmo_v110_ta_set_circuit(ta, circuit, active);
fprintf(stderr, "osmo_v110_ta_set_circuit() returns %d\n", rc);
}
/*********************************************************************************
* the actual tests
*********************************************************************************/
static void test_idle_ready(void)
{
struct osmo_v110_decoded_frame df = { 0 };
struct osmo_v110_ta *ta;
int rc;
fprintf(stderr, "\n==== Running %s()\n", __func__);
ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
OSMO_ASSERT(ta != NULL);
/* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
fprintf(stderr, "Initial status: 0x%08x\n", osmo_v110_ta_get_status(ta));
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, false);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, false);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, false);
fprintf(stderr, "osmo_v110_ta_frame_in(): all bits set to binary '1'\n");
memset(&df, 1, sizeof(df));
v110_ta_test_dump_df(&df);
rc = osmo_v110_ta_frame_in(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
fprintf(stderr, "osmo_v110_ta_frame_out(): expecting all bits set to binary '1'\n");
rc = osmo_v110_ta_frame_out(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
if (rc == 0)
v110_ta_test_dump_df(&df);
v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, true);
v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, false);
v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, true);
osmo_v110_ta_free(ta);
}
static void test_conn_ta_line(void)
{
struct osmo_v110_decoded_frame df = { 0 };
struct osmo_v110_ta *ta;
int rc;
fprintf(stderr, "\n==== Running %s()\n", __func__);
ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
OSMO_ASSERT(ta != NULL);
/* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, true);
/* we expect the TA FSM to be in V110_TA_ST_CON_TA_TO_LINE */
fprintf(stderr, "osmo_v110_ta_frame_out(): S-/X-bits are expected to be 1 (OFF)\n");
fprintf(stderr, "osmo_v110_ta_frame_out(): D-/E-bits are all expected to be 1\n");
rc = osmo_v110_ta_frame_out(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
if (rc == 0)
v110_ta_test_dump_df(&df);
/* TODO: test implicit sync by sending V110_TA_EV_RX_FRAME_IND */
fprintf(stderr, "osmo_v110_ta_sync_ind(): the lower layer indicates sync event\n");
osmo_v110_ta_sync_ind(ta);
fprintf(stderr, "osmo_v110_ta_frame_out(): S-/X-bits are expected to be 0 (ON)\n");
fprintf(stderr, "osmo_v110_ta_frame_out(): D-/E-bits are all expected to be 1\n");
rc = osmo_v110_ta_frame_out(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
if (rc == 0)
v110_ta_test_dump_df(&df);
fprintf(stderr, "osmo_v110_ta_frame_in(): S-/X-bits are OFF, expect no state change\n");
v110_ta_test_init_df(&df);
v110_ta_test_dump_df(&df);
rc = osmo_v110_ta_frame_in(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
fprintf(stderr, "osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change\n");
memset(&df.s_bits[0], V110_SX_BIT_ON, sizeof(df.s_bits));
memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
v110_ta_test_dump_df(&df);
rc = osmo_v110_ta_frame_in(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
/* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
osmo_v110_ta_free(ta);
}
static void _test_data_transfer_enter(struct osmo_v110_ta *ta)
{
struct osmo_v110_decoded_frame df;
int rc;
OSMO_ASSERT(osmo_v110_ta_get_circuit(ta, OSMO_V110_TA_C_108) == false);
/* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, true);
/* we expect the TA FSM to be in V110_TA_ST_CON_TA_TO_LINE */
fprintf(stderr, "osmo_v110_ta_sync_ind(): the lower layer indicates sync event\n");
osmo_v110_ta_sync_ind(ta);
fprintf(stderr, "osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change\n");
v110_ta_test_init_df(&df);
memset(&df.s_bits[0], V110_SX_BIT_ON, sizeof(df.s_bits));
memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
v110_ta_test_dump_df(&df);
rc = osmo_v110_ta_frame_in(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
/* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
}
static void test_data_transfer(void)
{
struct osmo_v110_decoded_frame df = { 0 };
struct osmo_v110_ta *ta;
int rc;
fprintf(stderr, "\n==== Running %s()\n", __func__);
ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
OSMO_ASSERT(ta != NULL);
/* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
_test_data_transfer_enter(ta);
/* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, true);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, true);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, true);
fprintf(stderr, "osmo_v110_ta_frame_out(): S-/X-bits are expected to be 0 (ON)\n");
fprintf(stderr, "osmo_v110_ta_frame_out(): E1..E3-bits are expected to be 011 (9600)\n");
fprintf(stderr, "osmo_v110_ta_frame_out(): we also expect the .tx_cb() to be called\n");
rc = osmo_v110_ta_frame_out(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
if (rc == 0)
v110_ta_test_dump_df(&df);
fprintf(stderr, "osmo_v110_ta_frame_in(): feed that frame that we pulled out back into the TA\n");
rc = osmo_v110_ta_frame_in(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
osmo_v110_ta_free(ta);
}
static void test_data_transfer_disc_local(void)
{
struct osmo_v110_decoded_frame df = { 0 };
struct osmo_v110_ta *ta;
int rc;
fprintf(stderr, "\n==== Running %s()\n", __func__);
ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
OSMO_ASSERT(ta != NULL);
/* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
_test_data_transfer_enter(ta);
/* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
fprintf(stderr, "local TE initiates disconnection\n");
v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, false);
/* we expect the TA FSM to be in V110_TA_ST_DISCONNECTING */
fprintf(stderr, "osmo_v110_ta_frame_out(): S-bits are expected to be 1 (OFF)\n");
fprintf(stderr, "osmo_v110_ta_frame_out(): X-bits are expected to be 0 (ON)\n");
fprintf(stderr, "osmo_v110_ta_frame_out(): D-bits are all expected to be 0\n");
rc = osmo_v110_ta_frame_out(ta, &df); /* TODO: what E-bits do we expect? */
fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
if (rc == 0)
v110_ta_test_dump_df(&df);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, false);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, true);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, true);
fprintf(stderr, "osmo_v110_ta_frame_in(): S-/X-bits are ON, expect no state change\n");
v110_ta_test_init_df(&df);
memset(&df.s_bits[0], V110_SX_BIT_ON, sizeof(df.s_bits));
memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
v110_ta_test_dump_df(&df);
rc = osmo_v110_ta_frame_in(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
fprintf(stderr, "osmo_v110_ta_frame_in(): S-bits are OFF, expect state change\n");
v110_ta_test_init_df(&df);
memset(&df.s_bits[0], V110_SX_BIT_OFF, sizeof(df.s_bits));
memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
v110_ta_test_dump_df(&df);
rc = osmo_v110_ta_frame_in(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
/* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, false);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, false);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, false);
osmo_v110_ta_free(ta);
}
static void test_data_transfer_disc_remote(void)
{
struct osmo_v110_decoded_frame df = { 0 };
struct osmo_v110_ta *ta;
int rc;
fprintf(stderr, "\n==== Running %s()\n", __func__);
ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
OSMO_ASSERT(ta != NULL);
/* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
_test_data_transfer_enter(ta);
/* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
fprintf(stderr, "remote TE initiates disconnection\n");
fprintf(stderr, "osmo_v110_ta_frame_in(): S-bits are OFF, X-bits are ON\n");
fprintf(stderr, "osmo_v110_ta_frame_in(): D-bits are all set to 0\n");
v110_ta_test_init_df(&df);
memset(&df.s_bits[0], V110_SX_BIT_OFF, sizeof(df.s_bits));
memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
memset(&df.d_bits[0], 0, sizeof(df.d_bits));
v110_ta_test_dump_df(&df);
rc = osmo_v110_ta_frame_in(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, false);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, false);
fprintf(stderr, "local TE confirms disconnection\n");
v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, false);
/* we expect the TA FSM to be in V110_TA_ST_DISCONNECTING */
osmo_v110_ta_desync_ind(ta);
/* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, false);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, false);
v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, false);
osmo_v110_ta_free(ta);
}
static void test_syncing(void)
{
struct osmo_v110_decoded_frame df = { 0 };
struct osmo_v110_ta *ta;
int rc;
fprintf(stderr, "\n==== Running %s()\n", __func__);
ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
OSMO_ASSERT(ta != NULL);
/* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
_test_data_transfer_enter(ta);
/* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
fprintf(stderr, "osmo_v110_ta_sync_ind(): the lower layer indicates out-of-sync event\n");
osmo_v110_ta_desync_ind(ta);
/* we expect the TA FSM to be in V110_TA_ST_RESYNCING */
fprintf(stderr, "osmo_v110_ta_frame_out(): S-bits are expected to be 0 (ON)\n");
fprintf(stderr, "osmo_v110_ta_frame_out(): X-bits are expected to be 1 (OFF)\n");
fprintf(stderr, "osmo_v110_ta_frame_out(): D-bits are to be set by .tx_cb()\n");
rc = osmo_v110_ta_frame_out(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
if (rc == 0)
v110_ta_test_dump_df(&df);
fprintf(stderr, "osmo_v110_ta_sync_ind(): the lower layer indicates sync event\n");
osmo_v110_ta_sync_ind(ta);
/* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
fprintf(stderr, "osmo_v110_ta_frame_out(): S-bits are expected to be 0 (ON)\n");
fprintf(stderr, "osmo_v110_ta_frame_out(): X-bits are expected to be 0 (ON)\n");
fprintf(stderr, "osmo_v110_ta_frame_out(): D-bits are to be set by .tx_cb()\n");
rc = osmo_v110_ta_frame_out(ta, &df);
fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
if (rc == 0)
v110_ta_test_dump_df(&df);
osmo_v110_ta_free(ta);
}
int main(int argc, char **argv)
{
test_ctx = talloc_named_const(NULL, 0, __FILE__);
osmo_init_logging2(test_ctx, NULL);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_level(osmo_stderr_target, 1);
log_set_print_category(osmo_stderr_target, 1);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
osmo_fsm_log_addr(false);
osmo_fsm_log_timeouts(true);
log_set_category_filter(osmo_stderr_target, DLGLOBAL, 1, LOGL_DEBUG);
test_idle_ready();
test_conn_ta_line();
/* TODO: test_conn_ta_line_timeout() */
test_data_transfer();
test_data_transfer_disc_local();
test_data_transfer_disc_remote();
/* TODO: test_disc_timeout() */
test_syncing();
/* TODO: test_syncing_timeout() */
log_fini();
OSMO_ASSERT(talloc_total_blocks(test_ctx) == 1);
talloc_free(test_ctx);
return 0;
}

284
tests/v110/ta_test.err Normal file
View File

@ -0,0 +1,284 @@
==== Running test_idle_ready()
DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Allocated
DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: State change to IDLE_READY (no timeout)
v110_ta_test_status_update_cb(status=0x00000000)
Initial status: 0x00000000
circuit 106/CTS (Clear to Send) is OFF (expected to be OFF)
circuit 107/DSR (Data Set Ready) is OFF (expected to be OFF)
circuit 109/DCD (Data Carrier Detect) is OFF (expected to be OFF)
osmo_v110_ta_frame_in(): all bits set to binary '1'
D-bits: 111111111111111111111111111111111111111111111111
E-bits: 1111111
S-bits: 111111111
X-bits: 11
DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Received Event RX_FRAME_IND
v110_ta_test_rx_cb(buf_size=48): 111111111111111111111111111111111111111111111111
osmo_v110_ta_frame_in() returns 0
osmo_v110_ta_frame_out(): expecting all bits set to binary '1'
DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Received Event TX_FRAME_RTS
osmo_v110_ta_frame_out() returns 0
D-bits: 111111111111111111111111111111111111111111111111
E-bits: 1111111
S-bits: 111111111
X-bits: 11
setting circuit 108/DTR (Data Terminal Ready) ON
DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Received Event V24_STATUS_CHG
DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
osmo_v110_ta_set_circuit() returns 0
setting circuit 108/DTR (Data Terminal Ready) OFF
DLGLOBAL DEBUG V110-TA(test_idle_ready){CONNECT_TA_TO_LINE}: Received Event V24_STATUS_CHG
DLGLOBAL DEBUG V110-TA(test_idle_ready){CONNECT_TA_TO_LINE}: State change to IDLE_READY (no timeout)
v110_ta_test_status_update_cb(status=0x00000000)
osmo_v110_ta_set_circuit() returns 0
setting circuit 108/DTR (Data Terminal Ready) ON
DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Received Event V24_STATUS_CHG
DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
osmo_v110_ta_set_circuit() returns 0
DLGLOBAL DEBUG V110-TA(test_idle_ready){CONNECT_TA_TO_LINE}: Deallocated
==== Running test_conn_ta_line()
DLGLOBAL DEBUG V110-TA(test_conn_ta_line){IDLE_READY}: Allocated
DLGLOBAL DEBUG V110-TA(test_conn_ta_line){IDLE_READY}: State change to IDLE_READY (no timeout)
v110_ta_test_status_update_cb(status=0x00000000)
setting circuit 108/DTR (Data Terminal Ready) ON
DLGLOBAL DEBUG V110-TA(test_conn_ta_line){IDLE_READY}: Received Event V24_STATUS_CHG
DLGLOBAL DEBUG V110-TA(test_conn_ta_line){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
osmo_v110_ta_set_circuit() returns 0
osmo_v110_ta_frame_out(): S-/X-bits are expected to be 1 (OFF)
osmo_v110_ta_frame_out(): D-/E-bits are all expected to be 1
DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event TX_FRAME_RTS
osmo_v110_ta_frame_out() returns 0
D-bits: 111111111111111111111111111111111111111111111111
E-bits: 1111111
S-bits: 111111111
X-bits: 11
osmo_v110_ta_sync_ind(): the lower layer indicates sync event
DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
osmo_v110_ta_frame_out(): S-/X-bits are expected to be 0 (ON)
osmo_v110_ta_frame_out(): D-/E-bits are all expected to be 1
DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event TX_FRAME_RTS
osmo_v110_ta_frame_out() returns 0
D-bits: 111111111111111111111111111111111111111111111111
E-bits: 1111111
S-bits: 000000000
X-bits: 00
osmo_v110_ta_frame_in(): S-/X-bits are OFF, expect no state change
D-bits: 010101010101010101010101010101010101010101010101
E-bits: 0111111
S-bits: 111111111
X-bits: 11
DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
v110_ta_test_rx_cb(buf_size=48): 111111111111111111111111111111111111111111111111
osmo_v110_ta_frame_in() returns 0
osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
D-bits: 010101010101010101010101010101010101010101010101
E-bits: 0111111
S-bits: 000000000
X-bits: 00
DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
v110_ta_test_status_update_cb(status=0x0000001e)
DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
v110_ta_test_status_update_cb(status=0x0000001e)
v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
osmo_v110_ta_frame_in() returns 0
DLGLOBAL DEBUG V110-TA(test_conn_ta_line){DATA_TRANSFER}: Deallocated
==== Running test_data_transfer()
DLGLOBAL DEBUG V110-TA(test_data_transfer){IDLE_READY}: Allocated
DLGLOBAL DEBUG V110-TA(test_data_transfer){IDLE_READY}: State change to IDLE_READY (no timeout)
v110_ta_test_status_update_cb(status=0x00000000)
setting circuit 108/DTR (Data Terminal Ready) ON
DLGLOBAL DEBUG V110-TA(test_data_transfer){IDLE_READY}: Received Event V24_STATUS_CHG
DLGLOBAL DEBUG V110-TA(test_data_transfer){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
osmo_v110_ta_set_circuit() returns 0
osmo_v110_ta_sync_ind(): the lower layer indicates sync event
DLGLOBAL DEBUG V110-TA(test_data_transfer){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
D-bits: 010101010101010101010101010101010101010101010101
E-bits: 0111111
S-bits: 000000000
X-bits: 00
DLGLOBAL DEBUG V110-TA(test_data_transfer){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
v110_ta_test_status_update_cb(status=0x0000001e)
DLGLOBAL DEBUG V110-TA(test_data_transfer){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
v110_ta_test_status_update_cb(status=0x0000001e)
v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
osmo_v110_ta_frame_in() returns 0
circuit 106/CTS (Clear to Send) is ON (expected to be ON)
circuit 107/DSR (Data Set Ready) is ON (expected to be ON)
circuit 109/DCD (Data Carrier Detect) is ON (expected to be ON)
osmo_v110_ta_frame_out(): S-/X-bits are expected to be 0 (ON)
osmo_v110_ta_frame_out(): E1..E3-bits are expected to be 011 (9600)
osmo_v110_ta_frame_out(): we also expect the .tx_cb() to be called
DLGLOBAL DEBUG V110-TA(test_data_transfer){DATA_TRANSFER}: Received Event TX_FRAME_RTS
v110_ta_test_tx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
osmo_v110_ta_frame_out() returns 0
D-bits: 010101010101010101010101010101010101010101010101
E-bits: 0111111
S-bits: 000000000
X-bits: 00
osmo_v110_ta_frame_in(): feed that frame that we pulled out back into the TA
DLGLOBAL DEBUG V110-TA(test_data_transfer){DATA_TRANSFER}: Received Event RX_FRAME_IND
v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
osmo_v110_ta_frame_in() returns 0
DLGLOBAL DEBUG V110-TA(test_data_transfer){DATA_TRANSFER}: Deallocated
==== Running test_data_transfer_disc_local()
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: Allocated
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: State change to IDLE_READY (no timeout)
v110_ta_test_status_update_cb(status=0x00000000)
setting circuit 108/DTR (Data Terminal Ready) ON
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: Received Event V24_STATUS_CHG
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
osmo_v110_ta_set_circuit() returns 0
osmo_v110_ta_sync_ind(): the lower layer indicates sync event
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
D-bits: 010101010101010101010101010101010101010101010101
E-bits: 0111111
S-bits: 000000000
X-bits: 00
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
v110_ta_test_status_update_cb(status=0x0000001e)
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
v110_ta_test_status_update_cb(status=0x0000001e)
v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
osmo_v110_ta_frame_in() returns 0
local TE initiates disconnection
setting circuit 108/DTR (Data Terminal Ready) OFF
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DATA_TRANSFER}: Received Event V24_STATUS_CHG
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DATA_TRANSFER}: State change to DISCONNECTING (T2, 5s)
v110_ta_test_status_update_cb(status=0x00000014)
osmo_v110_ta_set_circuit() returns 0
osmo_v110_ta_frame_out(): S-bits are expected to be 1 (OFF)
osmo_v110_ta_frame_out(): X-bits are expected to be 0 (ON)
osmo_v110_ta_frame_out(): D-bits are all expected to be 0
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DISCONNECTING}: Received Event TX_FRAME_RTS
osmo_v110_ta_frame_out() returns 0
D-bits: 000000000000000000000000000000000000000000000000
E-bits: 1111111
S-bits: 111111111
X-bits: 00
circuit 106/CTS (Clear to Send) is OFF (expected to be OFF)
circuit 107/DSR (Data Set Ready) is ON (expected to be ON)
circuit 109/DCD (Data Carrier Detect) is ON (expected to be ON)
osmo_v110_ta_frame_in(): S-/X-bits are ON, expect no state change
D-bits: 010101010101010101010101010101010101010101010101
E-bits: 0111111
S-bits: 000000000
X-bits: 00
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DISCONNECTING}: Received Event RX_FRAME_IND
v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
osmo_v110_ta_frame_in() returns 0
osmo_v110_ta_frame_in(): S-bits are OFF, expect state change
D-bits: 010101010101010101010101010101010101010101010101
E-bits: 0111111
S-bits: 111111111
X-bits: 00
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DISCONNECTING}: Received Event RX_FRAME_IND
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DISCONNECTING}: State change to IDLE_READY (no timeout)
v110_ta_test_status_update_cb(status=0x00000000)
v110_ta_test_rx_cb(buf_size=48): 111111111111111111111111111111111111111111111111
osmo_v110_ta_frame_in() returns 0
circuit 106/CTS (Clear to Send) is OFF (expected to be OFF)
circuit 107/DSR (Data Set Ready) is OFF (expected to be OFF)
circuit 109/DCD (Data Carrier Detect) is OFF (expected to be OFF)
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: Deallocated
==== Running test_data_transfer_disc_remote()
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: Allocated
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: State change to IDLE_READY (no timeout)
v110_ta_test_status_update_cb(status=0x00000000)
setting circuit 108/DTR (Data Terminal Ready) ON
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: Received Event V24_STATUS_CHG
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
osmo_v110_ta_set_circuit() returns 0
osmo_v110_ta_sync_ind(): the lower layer indicates sync event
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
D-bits: 010101010101010101010101010101010101010101010101
E-bits: 0111111
S-bits: 000000000
X-bits: 00
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
v110_ta_test_status_update_cb(status=0x0000001e)
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
v110_ta_test_status_update_cb(status=0x0000001e)
v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
osmo_v110_ta_frame_in() returns 0
remote TE initiates disconnection
osmo_v110_ta_frame_in(): S-bits are OFF, X-bits are ON
osmo_v110_ta_frame_in(): D-bits are all set to 0
D-bits: 000000000000000000000000000000000000000000000000
E-bits: 0111111
S-bits: 111111111
X-bits: 00
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DATA_TRANSFER}: Received Event RX_FRAME_IND
v110_ta_test_status_update_cb(status=0x0000000a)
osmo_v110_ta_frame_in() returns 0
circuit 107/DSR (Data Set Ready) is OFF (expected to be OFF)
circuit 109/DCD (Data Carrier Detect) is OFF (expected to be OFF)
local TE confirms disconnection
setting circuit 108/DTR (Data Terminal Ready) OFF
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DATA_TRANSFER}: Received Event V24_STATUS_CHG
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DATA_TRANSFER}: State change to DISCONNECTING (T2, 5s)
v110_ta_test_status_update_cb(status=0x00000000)
osmo_v110_ta_set_circuit() returns 0
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DISCONNECTING}: Received Event DESYNC_IND
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DISCONNECTING}: State change to IDLE_READY (no timeout)
v110_ta_test_status_update_cb(status=0x00000000)
circuit 106/CTS (Clear to Send) is OFF (expected to be OFF)
circuit 107/DSR (Data Set Ready) is OFF (expected to be OFF)
circuit 109/DCD (Data Carrier Detect) is OFF (expected to be OFF)
DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: Deallocated
==== Running test_syncing()
DLGLOBAL DEBUG V110-TA(test_syncing){IDLE_READY}: Allocated
DLGLOBAL DEBUG V110-TA(test_syncing){IDLE_READY}: State change to IDLE_READY (no timeout)
v110_ta_test_status_update_cb(status=0x00000000)
setting circuit 108/DTR (Data Terminal Ready) ON
DLGLOBAL DEBUG V110-TA(test_syncing){IDLE_READY}: Received Event V24_STATUS_CHG
DLGLOBAL DEBUG V110-TA(test_syncing){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
osmo_v110_ta_set_circuit() returns 0
osmo_v110_ta_sync_ind(): the lower layer indicates sync event
DLGLOBAL DEBUG V110-TA(test_syncing){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
D-bits: 010101010101010101010101010101010101010101010101
E-bits: 0111111
S-bits: 000000000
X-bits: 00
DLGLOBAL DEBUG V110-TA(test_syncing){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
v110_ta_test_status_update_cb(status=0x0000001e)
DLGLOBAL DEBUG V110-TA(test_syncing){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
v110_ta_test_status_update_cb(status=0x0000001e)
v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
osmo_v110_ta_frame_in() returns 0
osmo_v110_ta_sync_ind(): the lower layer indicates out-of-sync event
DLGLOBAL DEBUG V110-TA(test_syncing){DATA_TRANSFER}: Received Event DESYNC_IND
DLGLOBAL DEBUG V110-TA(test_syncing){DATA_TRANSFER}: State change to RESYNCING (X1, 3s)
osmo_v110_ta_frame_out(): S-bits are expected to be 0 (ON)
osmo_v110_ta_frame_out(): X-bits are expected to be 1 (OFF)
osmo_v110_ta_frame_out(): D-bits are to be set by .tx_cb()
DLGLOBAL DEBUG V110-TA(test_syncing){RESYNCING}: Received Event TX_FRAME_RTS
v110_ta_test_tx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
osmo_v110_ta_frame_out() returns 0
D-bits: 010101010101010101010101010101010101010101010101
E-bits: 0111111
S-bits: 000000000
X-bits: 11
osmo_v110_ta_sync_ind(): the lower layer indicates sync event
DLGLOBAL DEBUG V110-TA(test_syncing){RESYNCING}: Received Event SYNC_IND
DLGLOBAL DEBUG V110-TA(test_syncing){RESYNCING}: State change to DATA_TRANSFER (no timeout)
v110_ta_test_status_update_cb(status=0x0000001e)
osmo_v110_ta_frame_out(): S-bits are expected to be 0 (ON)
osmo_v110_ta_frame_out(): X-bits are expected to be 0 (ON)
osmo_v110_ta_frame_out(): D-bits are to be set by .tx_cb()
DLGLOBAL DEBUG V110-TA(test_syncing){DATA_TRANSFER}: Received Event TX_FRAME_RTS
v110_ta_test_tx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
osmo_v110_ta_frame_out() returns 0
D-bits: 010101010101010101010101010101010101010101010101
E-bits: 0111111
S-bits: 000000000
X-bits: 00
DLGLOBAL DEBUG V110-TA(test_syncing){DATA_TRANSFER}: Deallocated