fixeria has uploaded this change for review. (
https://gerrit.osmocom.org/c/libosmocore/+/35444?usp=email )
Change subject: isdn: initial implementation of the V.110 TA
......................................................................
isdn: initial implementation of the V.110 TA
Initial (Work-in-Progress) implementation by Harald Welte,
completed and co-authored by Vadim Yanitskiy.
Change-Id: I5716bd6fd0201ee7a7a29e72f775972cd374082f
Related: OS#4396
---
M include/osmocom/isdn/Makefile.am
A include/osmocom/isdn/v110_ta.h
M src/isdn/Makefile.am
M src/isdn/libosmoisdn.map
A src/isdn/v110_ta.c
5 files changed, 633 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/44/35444/1
diff --git a/include/osmocom/isdn/Makefile.am b/include/osmocom/isdn/Makefile.am
index ad2d8dd..fbc92d8 100644
--- a/include/osmocom/isdn/Makefile.am
+++ b/include/osmocom/isdn/Makefile.am
@@ -2,6 +2,7 @@
i460_mux.h \
lapd_core.h \
v110.h \
+ v110_ta.h \
$(NULL)
osmoisdndir = $(includedir)/osmocom/isdn
diff --git a/include/osmocom/isdn/v110_ta.h b/include/osmocom/isdn/v110_ta.h
new file mode 100644
index 0000000..9991c2d
--- /dev/null
+++ b/include/osmocom/isdn/v110_ta.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/isdn/v110.h>
+
+/* Definition of this struct is kept private. */
+struct osmo_v110_ta;
+
+/*! Configuration for a V.110 TA instance. */
+struct osmo_v110_ta_cfg {
+ /*! Is end-to-end flow-control enabled or not? */
+ bool end_to_end_flowctrl;
+ /*! Synchronous user rate. */
+ enum osmo_v100_sync_ra1_rate rate;
+
+ /*! 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 XXX. */
+ 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);
+
+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);
+
+/*! ITU-T V.24 Chapter 3
+ * TODO: move into its own header? */
+enum osmo_v24_circuit {
+ OSMO_V24_C_105, /* DTE->DCE | RTS (Request to Send) */
+ OSMO_V24_C_106, /* DTE<-DCE | CTS (Clear to Send) */
+ OSMO_V24_C_107, /* DTE<-DCE | DSR (Data Set Ready) */
+ OSMO_V24_C_108_1, /* XXX--XXX | Connect data set to line */
+ OSMO_V24_C_108_2, /* XXX--XXX | DTR (Data Terminal Ready) */
+ OSMO_V24_C_109, /* DTE<-DCE | DCD (Data Carrier Detect) */
+ OSMO_V24_C_125, /* DTE<-DCE | RI (Ring Indicator) */
+ OSMO_V24_C_133, /* DTE->DCE | Ready for receiving */
+};
+
+unsigned int osmo_v110_ta_get_status(const struct osmo_v110_ta *ta);
+int osmo_v110_ta_set_status(struct osmo_v110_ta *ta, unsigned int status);
+
+bool osmo_v110_ta_get_circuit(const struct osmo_v110_ta *ta,
+ enum osmo_v24_circuit circuit);
+void osmo_v110_ta_set_circuit(struct osmo_v110_ta *ta,
+ enum osmo_v24_circuit circuit, bool active);
diff --git a/src/isdn/Makefile.am b/src/isdn/Makefile.am
index f8314bd..3e7f86e 100644
--- a/src/isdn/Makefile.am
+++ b/src/isdn/Makefile.am
@@ -13,7 +13,7 @@
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
diff --git a/src/isdn/libosmoisdn.map b/src/isdn/libosmoisdn.map
index c29240b..df20ad5 100644
--- a/src/isdn/libosmoisdn.map
+++ b/src/isdn/libosmoisdn.map
@@ -33,5 +33,14 @@
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_frame_in;
+osmo_v110_ta_frame_out;
+osmo_v110_ta_get_status;
+osmo_v110_ta_set_status;
+osmo_v110_ta_get_circuit;
+osmo_v110_ta_set_circuit;
+
local: *;
};
diff --git a/src/isdn/v110_ta.c b/src/isdn/v110_ta.c
new file mode 100644
index 0000000..c8463bd
--- /dev/null
+++ b/src/isdn/v110_ta.c
@@ -0,0 +1,543 @@
+/*! \file v110_ta.c
+ * TA (Terminal Adapter) implementation as per ITU-T V.110. */
+/*
+ * (C) 2022 by Harald Welte <laforge(a)gnumonks.org>
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info(a)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/utils.h>
+#include <osmocom/core/bits.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) \
+ do { \
+ (flags) |= S(circuit); \
+ } while (0)
+
+#define V24_FLAGMASK_SET_OFF(flags, circuit) \
+ do { \
+ (flags) &= ~S(circuit); \
+ } while (0)
+
+/* inverse logic: ON = binary 0; OFF = binary 1 */
+#define V110_SX_BIT_ON 0
+#define V110_SX_BIT_OFF 1
+
+/*********************************************************************************
+ * V.110 TERMINAL ADAPTER FSMs
+ *********************************************************************************/
+
+enum v110_ta_fsm_state {
+ V110_TA_ST_IDLE_READY, /* 7.1.1 Idle (or ready) state */
+ V110_TA_ST_CON_TA_LINE, /* 7.1.2 Connect TA to line state */
+ V110_TA_ST_DATA_TRANSFER, /* 7.1.3 Data transfer state */
+ V110_TA_ST_DISCONNECT, /* 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, /* Received V.110 frame indication */
+ V110_TA_EV_TX_FRAME_RTS, /* V.110 frame Ready-to-send indication */
+#if 1
+ V110_TA_EV_V24_STATUS_CHG,
+ V110_TA_EV_SWITCH_TO_DATA_MODE,
+ V110_TA_EV_RX_SYNC_IND,
+#endif
+};
+
+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" },
+ { 0, NULL }
+};
+
+enum v110_ta_tx_d_bit_mode {
+ V110_TA_TX_FRAME_ALL_ONE,
+ V110_TA_TX_FRAME_ALL_ZERO,
+ V110_TA_TX_FRAME_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_tx_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;
+ /* what to put in E-bits of transmitted V.110 frames */
+ ubit_t e_bits[MAX_E_BITS];
+ } tx;
+ struct {
+ enum v110_ta_tx_d_bit_mode bit_mode;
+ } rx;
+};
+
+struct osmo_v110_ta {
+ const char *name;
+ struct osmo_fsm_inst *fi;
+ struct osmo_v110_ta_cfg *cfg;
+ struct v110_ta_state state;
+};
+
+/* decode one V.110 frame and forward user bits to the application */
+static void v110_ta_decode_frame(const struct osmo_v110_ta *ta,
+ const struct osmo_v110_decoded_frame *fr)
+{
+ const struct osmo_v110_ta_cfg *cfg = ta->cfg;
+ ubit_t user_bits[MAX_D_BITS];
+ int rc;
+
+ rc = osmo_v110_sync_ra1_ir_to_user(cfg->rate, &user_bits[0], sizeof(user_bits),
fr);
+ if (rc > 0)
+ cfg->rx_cb(cfg->priv, &user_bits[0], rc);
+}
+
+/* build one V.110 frame to transmit */
+static void v110_ta_build_frame(const struct osmo_v110_ta *ta,
+ struct osmo_v110_decoded_frame *fr)
+{
+ 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;
+
+ /* D-bits */
+ switch (ts->tx.d_bit_mode) {
+ case V110_TA_TX_FRAME_ALL_ONE:
+ memset(fr->d_bits, 1, sizeof(fr->d_bits));
+ break;
+ case V110_TA_TX_FRAME_ALL_ZERO:
+ memset(fr->d_bits, 0, sizeof(fr->d_bits));
+ break;
+ case V110_TA_TX_FRAME_FROM_DTE:
+ /* 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 fr) */
+ rc = osmo_v110_sync_ra1_user_to_ir(cfg->rate, fr, &user_bits[0],
num_user_bits);
+ OSMO_ASSERT(rc == 0);
+ break;
+ }
+
+ /* E-bits */
+ memcpy(fr->e_bits, ts->tx.e_bits, sizeof(fr->e_bits));
+ /* S-bits */
+ memset(fr->s_bits, ts->tx.s_bits, sizeof(fr->s_bits));
+ /* X-bits */
+ memset(fr->x_bits, ts->tx.x_bits, sizeof(fr->x_bits));
+}
+
+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);
+}
+
+/* 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;
+
+ /* E4 .. E7 bits (lower 3 bits are generated by v110 frame encoder) */
+ memset(&ts->tx.e_bits[3], 1, MAX_E_BITS - 3);
+
+ /* 7.1.1.2 During the idle (or ready) state the TA will transmit continuous binary 1s
into the B-channel */
+ /* 7.1.1.3 During the idle (or ready) state the TA (DCE) will transmit the following
toward the DTE: * */
+ /* - 104: continuous binary 1 */
+ ts->rx.bit_mode = V110_TA_TX_FRAME_ALL_ONE;
+ /* - 107, 106, 109 = OFF */
+ V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V24_C_106);
+ V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V24_C_107);
+ V24_FLAGMASK_SET_OFF(ts->v24_flags, OSMO_V24_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_RX_FRAME_IND:
+ /* XXX: should we really handle V.110 frames in this state? */
+ v110_ta_decode_frame(ta, (const struct osmo_v110_decoded_frame *)data);
+ break;
+ case V110_TA_EV_TX_FRAME_RTS:
+ /* XXX: transmit continuous binary 1 to B channels */
+ break;
+ case V110_TA_EV_SWITCH_TO_DATA_MODE:
+ /* 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_V24_C_108_2)) {
+ /* 7.12.2: Start timer T1 when switching to CON_TA_LINE */
+ osmo_fsm_inst_state_chg(fi, V110_TA_ST_CON_TA_LINE, 10, 1);
+ }
+ 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;
+
+ /* FIXME: frame sync pattern as per 5.1.3.1 / 5.2.1 */
+ /* data bits: binary 1 */
+ ts->tx.d_bit_mode = V110_TA_TX_FRAME_ALL_ONE;
+ /* S = OFF, X = OFF (ON = binary 0; OFF = binary 1) */
+ ts->tx.s_bits = V110_SX_BIT_OFF;
+ ts->tx.x_bits = V110_SX_BIT_OFF;
+ /* onenter: T1 has been started */
+ OSMO_ASSERT(fi->T == 1);
+}
+
+static bool all_bits_are(const ubit_t *in, ubit_t cmp, size_t in_len)
+{
+ for (unsigned int i = 0; i < in_len; i++) {
+ if (in[i] != cmp)
+ return false;
+ }
+ return true;
+}
+#define ARRAY_ALL_BITS_ONE(arr) all_bits_are((arr), 1, sizeof(arr))
+#define ARRAY_ALL_BITS_ZERO(arr) all_bits_are((arr), 0, sizeof(arr))
+
+/* 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_RX_FRAME_IND:
+ {
+ const struct osmo_v110_decoded_frame *fr = data;
+
+ /* 7.1.2.4 When the receiver recognizes that the status of bits S and X are in the ON
+ * condition, it will perform the following functions: */
+ if (!ARRAY_ALL_BITS_ZERO(fr->s_bits))
+ break;
+ if (!ARRAY_ALL_BITS_ZERO(fr->x_bits))
+ break;
+ /* a) Turn ON circuit 107 toward the DTE and stop timer T1. */
+ V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_107);
+ /* 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_V24_C_109);
+ /* d) After an interval of N bits (see 6.3), it will turn ON circuit 106. */
+ V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_106);
+ 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. */
+ osmo_fsm_inst_state_chg(fi, V110_TA_ST_DATA_TRANSFER, 0, 0);
+
+ v110_ta_decode_frame(ta, fr);
+ 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_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). */
+ if (V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V24_C_108_2)) {
+ ts->tx.s_bits = V110_SX_BIT_ON;
+ ts->tx.x_bits = V110_SX_BIT_ON;
+ }
+ 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;
+
+ ts->tx.d_bit_mode = V110_TA_TX_FRAME_FROM_DTE;
+
+ /* 7.1.3.1 a): 105, 107, 108/1, 108/2 and 109 are in the ON condition */
+ V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_105);
+ V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_107);
+ V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_108_1);
+ V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_108_2);
+ V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_109);
+ /* 7.1.3.1 c): 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->end_to_end_flowctrl) {
+ V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_C_133);
+ V24_FLAGMASK_SET_ON(ts->v24_flags, OSMO_V24_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->end_to_end_flowctrl)
+ 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;
+
+ 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_OFF(ts->v24_flags, OSMO_V24_C_108_2))
+ break;
+ /* 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_V24_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_TX_FRAME_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_fsm_inst_state_chg(fi, V110_TA_ST_DISCONNECT, 5, 2);
+ 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:
+ v110_ta_decode_frame(ta, (const struct osmo_v110_decoded_frame *)data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static int v110_ta_timer_cb(struct osmo_fsm_inst *fi)
+{
+ switch (fi->T) {
+ case 1: /* T1: wait for sync pattern */
+ break;
+ case 2: /* T2: wait for response to disconnect */
+ break;
+ }
+
+ 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_TX_FRAME_RTS),
+ .out_state_mask = S(V110_TA_ST_CON_TA_LINE),
+ .action = &v110_ta_fsm_idle_ready,
+ .onenter = &v110_ta_fsm_idle_ready_onenter,
+ },
+ [V110_TA_ST_CON_TA_LINE] = {
+ .name = "CONNECT_TA_TA_LINE",
+ .in_event_mask = 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_TX_FRAME_RTS)
+ | S(V110_TA_EV_RX_FRAME_IND),
+ .out_state_mask = S(V110_TA_ST_RESYNCING)
+ | S(V110_TA_ST_DISCONNECT),
+ .action = &v110_ta_fsm_data_transfer,
+ .onenter = &v110_ta_fsm_data_transfer_onenter,
+ },
+#if 0
+ [V110_TA_ST_DISCONNECT] = {
+ .name = "DISCONNECT",
+ .out_state_mask = S(V110_TA_ST_IDLE_READY),
+ },
+ [V110_TA_ST_RESYNCING] = {
+ .name = "RESYNCING",
+ .out_state_mask = S(V110_TA_ST_DATA_TRANSFER)
+ | S(V110_TA_ST_DISCONNECT),
+ },
+#endif
+};
+
+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);
+
+ 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->fi = osmo_fsm_inst_alloc(&osmo_v110_ta_fsm, ta, ta, LOGL_INFO, name);
+ if (ta->fi == NULL)
+ goto exit_free;
+
+ 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 */
+}
+
+/* XXX
+ * \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);
+}
+
+/* XXX
+ * \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);
+}
+
+/*! Get the V.24 status bitmask of the given TA instance.
+ * \param[in] ta TA instance to get the circuit bitmask.
+ * \returns bitmask of OSMO_V24_C_*. */
+unsigned int osmo_v110_ta_get_status(const struct osmo_v110_ta *ta)
+{
+ return ta->state.v24_flags;
+}
+
+/*! Set the V.24 status bitmask of the given TA instance.
+ * \param[in] ta TA instance to update the circuit state.
+ * \param[in] status bitmask of OSMO_V24_C_*.
+ * \returns 0 on success; negative on error. */
+int osmo_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_V24_C_*.
+ * \returns circuit state: active (true) or inactive (false). */
+bool osmo_v110_ta_get_circuit(const struct osmo_v110_ta *ta,
+ enum osmo_v24_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_V24_C_*.
+ * \param[in] active activate (true) or deactivate (false) the circuit. */
+void osmo_v110_ta_set_circuit(struct osmo_v110_ta *ta,
+ enum osmo_v24_circuit circuit, bool active)
+{
+ unsigned int status = ta->state.v24_flags;
+
+ if (active)
+ V24_FLAGMASK_SET_ON(status, circuit);
+ else
+ V24_FLAGMASK_SET_OFF(status, circuit);
+
+ osmo_v110_ta_set_status(ta, status);
+}
--
To view, visit
https://gerrit.osmocom.org/c/libosmocore/+/35444?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: I5716bd6fd0201ee7a7a29e72f775972cd374082f
Gerrit-Change-Number: 35444
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-MessageType: newchange