diff options
Diffstat (limited to 'include/osmocom/isdn')
-rw-r--r-- | include/osmocom/isdn/Makefile.am | 8 | ||||
-rw-r--r-- | include/osmocom/isdn/i460_mux.h | 120 | ||||
-rw-r--r-- | include/osmocom/isdn/lapd_core.h | 192 | ||||
-rw-r--r-- | include/osmocom/isdn/v110.h | 57 | ||||
-rw-r--r-- | include/osmocom/isdn/v110_ta.h | 113 |
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); |