aboutsummaryrefslogtreecommitdiffstats
path: root/include/osmocom/isdn
diff options
context:
space:
mode:
Diffstat (limited to 'include/osmocom/isdn')
-rw-r--r--include/osmocom/isdn/Makefile.am8
-rw-r--r--include/osmocom/isdn/i460_mux.h120
-rw-r--r--include/osmocom/isdn/lapd_core.h192
-rw-r--r--include/osmocom/isdn/v110.h57
-rw-r--r--include/osmocom/isdn/v110_ta.h113
5 files changed, 490 insertions, 0 deletions
diff --git a/include/osmocom/isdn/Makefile.am b/include/osmocom/isdn/Makefile.am
new file mode 100644
index 00000000..fbc92d8b
--- /dev/null
+++ b/include/osmocom/isdn/Makefile.am
@@ -0,0 +1,8 @@
+osmoisdn_HEADERS = \
+ i460_mux.h \
+ lapd_core.h \
+ v110.h \
+ v110_ta.h \
+ $(NULL)
+
+osmoisdndir = $(includedir)/osmocom/isdn
diff --git a/include/osmocom/isdn/i460_mux.h b/include/osmocom/isdn/i460_mux.h
new file mode 100644
index 00000000..537e3257
--- /dev/null
+++ b/include/osmocom/isdn/i460_mux.h
@@ -0,0 +1,120 @@
+/*! \file i460_mux.h
+ * ITU-T I.460 sub-channel multiplexer + demultiplexer */
+/*
+ * (C) 2020 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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.
+ */
+
+#pragma once
+#include <stdint.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/msgb.h>
+
+#define OSMO_I460_NUM_SUBCHAN 8
+
+/* I.460 sub-slot rate */
+enum osmo_i460_rate {
+ OSMO_I460_RATE_NONE, /* disabled */
+ OSMO_I460_RATE_64k,
+ OSMO_I460_RATE_32k,
+ OSMO_I460_RATE_16k,
+ OSMO_I460_RATE_8k,
+};
+
+struct osmo_i460_subchan;
+
+typedef void (*out_cb_bits_t)(struct osmo_i460_subchan *schan, void *user_data,
+ const ubit_t *bits, unsigned int num_bits);
+typedef void (*out_cb_bytes_t)(struct osmo_i460_subchan *schan, void *user_data,
+ const uint8_t *bytes, unsigned int num_bytes);
+
+struct osmo_i460_subchan_demux {
+ /*! bit-buffer for output bits */
+ uint8_t *out_bitbuf;
+ /*! size of out_bitbuf in bytes */
+ unsigned int out_bitbuf_size;
+ /*! offset of next bit to be written in out_bitbuf */
+ unsigned int out_idx;
+ /*! callback to be called once we have received out_bitbuf_size bits */
+ out_cb_bits_t out_cb_bits;
+ out_cb_bytes_t out_cb_bytes;
+ void *user_data;
+};
+
+typedef void (*in_cb_queue_empty_t)(struct osmo_i460_subchan *schan, void *user_data);
+
+struct osmo_i460_subchan_mux {
+ /*! list of to-be-transmitted message buffers */
+ struct llist_head tx_queue;
+ in_cb_queue_empty_t in_cb_queue_empty;
+ void *user_data;
+};
+
+struct osmo_i460_subchan {
+ struct osmo_i460_timeslot *ts; /* back-pointer */
+ enum osmo_i460_rate rate; /* 8/16/32/64k */
+ uint8_t bit_offset; /* bit offset inside each byte of the B-channel */
+ struct osmo_i460_subchan_demux demux;
+ struct osmo_i460_subchan_mux mux;
+};
+
+struct osmo_i460_timeslot {
+ struct osmo_i460_subchan schan[OSMO_I460_NUM_SUBCHAN];
+};
+
+/*! description of a sub-channel; passed by caller */
+struct osmo_i460_schan_desc {
+ enum osmo_i460_rate rate;
+ uint8_t bit_offset;
+ struct {
+ /* size (in bits) of the internal buffer; determines granularity */
+ size_t num_bits;
+ /*! call-back function called whenever we received num_bits */
+ out_cb_bits_t out_cb_bits;
+ /*! out_cb_bytes call-back function called whenever we received num_bits.
+ * The user is usually expected to provide either out_cb_bits or out_cb_bytes. If only
+ * out_cb_bits is provided, output data will always be provided as unpacked bits; if only
+ * out_cb_bytes is provided, output data will always be provided as packet bits (bytes). If
+ * both are provided, it is up to the I.460 multiplex to decide if it calls either of the two,
+ * depending on what can be provided without extra conversion. */
+ out_cb_bytes_t out_cb_bytes;
+ /* opaque user data pointer to pass to out_cb */
+ void *user_data;
+ } demux;
+
+ struct {
+ /* call-back function whenever the muxer requires more input data from the sub-channels,
+ * but has nothing enqueued yet. A typical function would then call osmo_i460_mux_enqueue() */
+ in_cb_queue_empty_t in_cb_queue_empty;
+ /* opaque user data pointer to pass to in_cb */
+ void *user_data;
+ } mux;
+};
+
+void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len);
+
+void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg);
+int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len);
+
+void osmo_i460_ts_init(struct osmo_i460_timeslot *ts);
+
+struct osmo_i460_subchan *
+osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd);
+
+void osmo_i460_subchan_del(struct osmo_i460_subchan *schan);
+
+int osmo_i460_subchan_count(struct osmo_i460_timeslot *ts);
+
+/*! @} */
diff --git a/include/osmocom/isdn/lapd_core.h b/include/osmocom/isdn/lapd_core.h
new file mode 100644
index 00000000..1e010afe
--- /dev/null
+++ b/include/osmocom/isdn/lapd_core.h
@@ -0,0 +1,192 @@
+/*! \file lapd_core.h
+ * primitive related stuff
+ */
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/prim.h>
+
+/*! \defgroup lapd LAPD implementation common part
+ * @{
+ * \file lapd_core.h
+ */
+
+#define LOGDL(dl, level, fmt, args...) \
+ LOGP(DLLAPD, level, "(%s) " fmt, (dl)->name, ## args)
+
+/*! LAPD related primitives (L2<->L3 SAP)*/
+enum osmo_dl_prim {
+ PRIM_DL_UNIT_DATA, /*!< DL-UNIT-DATA */
+ PRIM_DL_DATA, /*!< DL-DATA */
+ PRIM_DL_EST, /*!< DL-ESTABLISH */
+ PRIM_DL_REL, /*!< DL-RLEEASE */
+ PRIM_DL_SUSP, /*!< DL-SUSPEND */
+ PRIM_DL_RES, /*!< DL-RESUME */
+ PRIM_DL_RECON, /*!< DL-RECONNECT */
+ PRIM_MDL_ERROR, /*!< MDL-ERROR */
+};
+
+/* Uses the same values as RLL, so no conversion for GSM is required. */
+#define MDL_CAUSE_T200_EXPIRED 0x01
+#define MDL_CAUSE_REEST_REQ 0x02
+#define MDL_CAUSE_UNSOL_UA_RESP 0x03
+#define MDL_CAUSE_UNSOL_DM_RESP 0x04
+#define MDL_CAUSE_UNSOL_DM_RESP_MF 0x05
+#define MDL_CAUSE_UNSOL_SPRV_RESP 0x06
+#define MDL_CAUSE_SEQ_ERR 0x07
+#define MDL_CAUSE_UFRM_INC_PARAM 0x08
+#define MDL_CAUSE_SFRM_INC_PARAM 0x09
+#define MDL_CAUSE_IFRM_INC_MBITS 0x0a
+#define MDL_CAUSE_IFRM_INC_LEN 0x0b
+#define MDL_CAUSE_FRM_UNIMPL 0x0c
+#define MDL_CAUSE_SABM_MF 0x0d
+#define MDL_CAUSE_SABM_INFO_NOTALL 0x0e
+#define MDL_CAUSE_FRMR 0x0f
+
+/*! for MDL-ERROR.ind */
+struct mdl_error_ind_param {
+ uint8_t cause; /*!< generic cause value */
+};
+
+/*! for DL-REL.req */
+struct dl_rel_req_param {
+ uint8_t mode; /*!< release mode */
+};
+
+/*! primitive header for LAPD DL-SAP primitives */
+struct osmo_dlsap_prim {
+ struct osmo_prim_hdr oph; /*!< generic primitive header */
+ union {
+ struct mdl_error_ind_param error_ind;
+ struct dl_rel_req_param rel_req;
+ } u; /*!< request-specific data */
+};
+
+/*! LAPD mode/role */
+enum lapd_mode {
+ LAPD_MODE_USER, /*!< behave like user */
+ LAPD_MODE_NETWORK, /*!< behave like network */
+};
+
+/*! LAPD state (Figure B.2/Q.921)*/
+enum lapd_state {
+ LAPD_STATE_NULL = 0,
+ LAPD_STATE_TEI_UNASS,
+ LAPD_STATE_ASS_TEI_WAIT,
+ LAPD_STATE_EST_TEI_WAIT,
+ LAPD_STATE_IDLE,
+ LAPD_STATE_SABM_SENT,
+ LAPD_STATE_DISC_SENT,
+ LAPD_STATE_MF_EST,
+ LAPD_STATE_TIMER_RECOV,
+};
+
+/*! lapd_flags */
+#define LAPD_F_RTS 0x0001
+#define LAPD_F_DROP_2ND_REJ 0x0002
+
+/*! LAPD T200 state in RTS mode */
+enum lapd_t200_rts {
+ LAPD_T200_RTS_OFF = 0,
+ LAPD_T200_RTS_PENDING,
+ LAPD_T200_RTS_RUNNING,
+};
+
+/*! LAPD message format (I / S / U) */
+enum lapd_format {
+ LAPD_FORM_UKN = 0,
+ LAPD_FORM_I,
+ LAPD_FORM_S,
+ LAPD_FORM_U,
+};
+
+/*! LAPD message context */
+struct lapd_msg_ctx {
+ struct lapd_datalink *dl;
+ int n201;
+ /* address */
+ uint8_t cr;
+ uint8_t sapi;
+ uint8_t tei;
+ uint8_t lpd;
+ /* control */
+ uint8_t format;
+ uint8_t p_f; /* poll / final bit */
+ uint8_t n_send;
+ uint8_t n_recv;
+ uint8_t s_u; /* S or repectivly U function bits */
+ /* length */
+ int length;
+ uint8_t more;
+};
+
+struct lapd_cr_ent {
+ uint8_t cmd;
+ uint8_t resp;
+};
+
+struct lapd_history {
+ struct msgb *msg; /* message to be sent / NULL, if histoy is empty */
+ int more; /* if message is fragmented */
+};
+
+/*! LAPD datalink */
+struct lapd_datalink {
+ int (*send_dlsap)(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
+ int (*send_ph_data_req)(struct lapd_msg_ctx *lctx, struct msgb *msg);
+ int (*update_pending_frames)(struct lapd_msg_ctx *lctx);
+ struct {
+ /*! filled-in once we set the lapd_mode above */
+ struct lapd_cr_ent loc2rem;
+ struct lapd_cr_ent rem2loc;
+ } cr;
+ enum lapd_mode mode; /*!< current mode of link */
+ unsigned int lapd_flags; /*!< \ref lapd_flags to change processing */
+ int use_sabme; /*!< use SABME instead of SABM */
+ int reestablish; /*!< enable reestablish support */
+ int n200, n200_est_rel; /*!< number of retranmissions */
+ struct lapd_msg_ctx lctx; /*!< LAPD context */
+ int maxf; /*!< maximum frame size (after defragmentation) */
+ uint8_t k; /*!< maximum number of unacknowledged frames */
+ uint8_t v_range; /*!< range of sequence numbers */
+ uint8_t v_send; /*!< seq nr of next I frame to be transmitted */
+ uint8_t v_ack; /*!< last frame ACKed by peer */
+ uint8_t v_recv; /*!< seq nr of next I frame expected to be received */
+ uint32_t state; /*!< LAPD state (\ref lapd_state) */
+ int seq_err_cond; /*!< condition of sequence error */
+ uint8_t own_busy; /*!< receiver busy on our side */
+ uint8_t peer_busy; /*!< receiver busy on remote side */
+ int t200_sec, t200_usec; /*!< retry timer (default 1 sec) */
+ int t203_sec, t203_usec; /*!< retry timer (default 10 secs) */
+ enum lapd_t200_rts t200_rts; /*!< state of T200 in RTS mode */
+ struct osmo_timer_list t200; /*!< T200 timer */
+ struct osmo_timer_list t203; /*!< T203 timer */
+ uint8_t retrans_ctr; /*!< re-transmission counter */
+ struct llist_head tx_queue; /*!< frames to L1 */
+ struct llist_head send_queue; /*!< frames from L3 */
+ struct msgb *send_buffer; /*!< current frame transmitting */
+ int send_out; /*!< how much was sent from send_buffer */
+ struct lapd_history *tx_hist; /*!< tx history structure array */
+ uint8_t range_hist; /*!< range of history buffer 2..2^n */
+ struct msgb *rcv_buffer; /*!< buffer to assemble the received message */
+ struct msgb *cont_res; /*!< buffer to store content resolution data on network side, to detect multiple phones on same channel */
+ char *name; /*!< user-provided name */
+};
+
+void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf)
+ OSMO_DEPRECATED("Use lapd_dl_init2() instead");
+void lapd_dl_init2(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf, const char *name);
+void lapd_dl_set_name(struct lapd_datalink *dl, const char *name);
+void lapd_dl_exit(struct lapd_datalink *dl);
+void lapd_dl_reset(struct lapd_datalink *dl);
+int lapd_dl_set_flags(struct lapd_datalink *dl, unsigned int flags);
+int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode);
+int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx);
+int lapd_ph_rts_ind(struct lapd_msg_ctx *lctx);
+int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
+int lapd_t200_timeout(struct lapd_datalink *dl);
+
+/*! @} */
diff --git a/include/osmocom/isdn/v110.h b/include/osmocom/isdn/v110.h
new file mode 100644
index 00000000..9555932e
--- /dev/null
+++ b/include/osmocom/isdn/v110.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <osmocom/core/bits.h>
+
+/* See Section 5.1.2.1 of ITU-T V.110 */
+#define MAX_D_BITS 48
+#define MAX_E_BITS 7
+#define MAX_S_BITS 9
+#define MAX_X_BITS 2
+
+/*! a 'decoded' representation of a single V.110 frame. contains unpacket D, E, S and X bits */
+struct osmo_v110_decoded_frame {
+ ubit_t d_bits[MAX_D_BITS];
+ ubit_t e_bits[MAX_E_BITS];
+ ubit_t s_bits[MAX_S_BITS];
+ ubit_t x_bits[MAX_X_BITS];
+};
+
+int osmo_v110_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits);
+int osmo_v110_encode_frame(ubit_t *ra_bits, size_t n_bits, const struct osmo_v110_decoded_frame *fr);
+
+void osmo_v110_ubit_dump(FILE *outf, const ubit_t *fr, size_t in_len);
+
+
+/*! enum for each supported V.110 synchronous RA1 function (one for each user bitrate) */
+enum osmo_v100_sync_ra1_rate {
+ OSMO_V110_SYNC_RA1_600,
+ OSMO_V110_SYNC_RA1_1200,
+ OSMO_V110_SYNC_RA1_2400,
+ OSMO_V110_SYNC_RA1_4800,
+ OSMO_V110_SYNC_RA1_7200,
+ OSMO_V110_SYNC_RA1_9600,
+ OSMO_V110_SYNC_RA1_12000,
+ OSMO_V110_SYNC_RA1_14400,
+ OSMO_V110_SYNC_RA1_19200,
+ OSMO_V110_SYNC_RA1_24000,
+ OSMO_V110_SYNC_RA1_28800,
+ OSMO_V110_SYNC_RA1_38400,
+ _NUM_OSMO_V110_SYNC_RA1
+};
+
+extern const ubit_t osmo_v110_e1e2e3[_NUM_OSMO_V110_SYNC_RA1][3];
+
+#define osmo_v110_e1e2e3_set(e_bits, rate) \
+ memcpy(e_bits, osmo_v110_e1e2e3[rate], 3)
+#define osmo_v110_e1e2e3_cmp(e_bits, rate) \
+ memcmp(e_bits, osmo_v110_e1e2e3[rate], 3)
+
+int osmo_v110_sync_ra1_get_user_data_chunk_bitlen(enum osmo_v100_sync_ra1_rate rate);
+int osmo_v110_sync_ra1_get_user_data_rate(enum osmo_v100_sync_ra1_rate rate);
+int osmo_v110_sync_ra1_get_intermediate_rate(enum osmo_v100_sync_ra1_rate rate);
+
+int osmo_v110_sync_ra1_user_to_ir(enum osmo_v100_sync_ra1_rate rate, struct osmo_v110_decoded_frame *fr,
+ const ubit_t *d_in, size_t in_len);
+
+int osmo_v110_sync_ra1_ir_to_user(enum osmo_v100_sync_ra1_rate rate, ubit_t *d_out, size_t out_len,
+ const struct osmo_v110_decoded_frame *fr);
diff --git a/include/osmocom/isdn/v110_ta.h b/include/osmocom/isdn/v110_ta.h
new file mode 100644
index 00000000..b6bf7b52
--- /dev/null
+++ b/include/osmocom/isdn/v110_ta.h
@@ -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);