#pragma once /* (C) 2021 by Harald Welte * (C) 2022 by Andreas Eversberg * * 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. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include "libecho/echo_cancel.h" #include "libecho/echo_suppress.h" #include "libecho/answertone.h" #include "ph_socket.h" #define LOGV5IF(v5i, ss, level, fmt, args ...) \ LOGP(ss, level, "(%s): " fmt, v5x_interface_name(v5i), ## args) #define LOGV5L(v5l, ss, level, fmt, args ...) \ LOGP(ss, level, "(%s-L%u): " fmt, v5x_interface_name((v5l)->interface), (v5l)->id, ## args) #define LOGV5UP(v5up, ss, level, fmt, args ...) \ LOGP(ss, level, "(%s-P%u): " fmt, v5x_interface_name((v5up)->interface), (v5up)->nr, ## args) /* Table 35/G.964 */ enum v5x_mph_prim { MPH_UBR, /* Unblock (req) */ MPH_UBI, /* Unblock (ind) */ MPH_BR, /* Block (req) */ MPH_BI, /* Block (ind) */ MPH_AR, /* Activate (req) */ MPH_AI, /* Activate (ind) */ MPH_AWI, /* Access activation by user (ind) */ MPH_DSAI, /* DS Activated (ind) */ MPH_DR, /* Deactivate (req) */ MPH_DI, /* Deactivate (ind) */ MPH_GI, /* Grading Information */ MPH_DB, /* Block D-Channel from user port */ MPH_DU, /* Unblock D-Channel from user port */ MPH_ID, /* Identity Sa7=0 signal request (TX) */ MPH_NOR, /* Normal Sa7=1 signal request (TX) */ MPH_IDR, /* Request Sa7 signal (RX) */ MPH_IDI, /* Indicate Sa7=0 signal (RX) */ MPH_EIg, /* Indicate Sa7=1 singal (RX) */ MPH_EIa, /* Indicate LOS */ MPH_EIb, /* RAI */ MPH_EIc, /* AIS */ MPH_EId, /* Internal failure */ MPH_EIe, /* CRC received */ MPH_EIf, /* E-bit received */ MPH_stop, /* See L1 FSM */ MPH_proceed, /* See L1 FSM */ MPH_EIbr, /* See L1 FSM */ MPH_EIdr, /* See L1 FSM */ }; /* Table 3/G.964 */ enum v5x_fe_prim { FE_establish_req = 0x00, FE_establish_ack_ind = 0x03, FE_establish_ind = 0x01, FE_establish_ack_req = 0x02, FE_line_signal_req = 0x10, FE_line_signal_ind = 0x11, FE_protocol_param_req = 0x20, FE_disconnect_req = 0x30, FE_disconnect_compl_req = 0x32, FE_disconnect_compl_ind = 0x33, }; enum v5x_mgmt_prim { /* Table 3/G.964 */ MDU_CTRL_port_blocked, MDU_CTRL_port_unblocked, MDU_CTRL_port_restart_req, MDU_CTRL_port_restart_ack, MDU_CTRL_port_restart_compl, MDU_error_ind, /* Table 14/G.965 */ MDU_AI, MDU_DI, MDU_LAI, MDU_IDReq, MDU_IDAck, MDU_IDRel, MDU_IDRej, MDU_EIg, MDU_LUBR, MDU_LUBI, MDU_LBI, MDU_LBR, MDU_LBRN, /* Table 26/G.965 */ MDU_BCC_allocation_req, MDU_BCC_allocation_conf, MDU_BCC_allocation_reject_ind, MDU_BCC_allocation_error_ind, MDU_BCC_deallocation_req, MDU_BCC_deallocation_conf, MDU_BCC_deallocation_reject_ind, MDU_BCC_deallocation_error_ind, MDU_BCC_audit_req, MDU_BCC_audit_conf, MDU_BCC_audit_error_ind, MDU_BCC_AN_fault_ind, MDU_BCC_protocol_error_ind, /* Table 50/G.965 */ MDU_Protection_switch_over_com, MDU_Protection_OS_switch_over_com, MDU_Protection_switch_over_ack, MDU_Protection_switch_over_rej, MDU_Protection_switch_over_req, MDU_Protection_switch_over_reject_ind, MDU_Protection_switch_over_error_ind, MDU_Protection_reset_SN_ind, MDU_Protection_reset_SN_com, MDU_Protection_reset_SN_req, MDU_Protection_reset_SN_ack, MDU_Protection_reset_SN_error_ind, MDU_Protection_protocol_error_ind, /* Figure 11/G.964 */ MDL_ESTABLISH_req, MDL_ESTABLISH_cnf, MDL_ESTABLISH_ind, MDL_RELEASE_ind, MDL_ERROR_ind, MDL_LAYER_1_FAILURE_ind, }; struct osmo_fsm_inst; enum v5x_dialect { V5X_DIALECT_V51 = 1, V5X_DIALECT_V52 = 2, }; enum v5x_user_type { V5X_USER_TYPE_ISDN = 1, V5X_USER_TYPE_PSTN = 2, }; enum v5x_ctrl_type { V5X_CTRL_TYPE_COMMON = 1, V5X_CTRL_TYPE_PORT = 2, V5X_CTRL_TYPE_LINK = 3, }; /* forward-declarations */ struct v5x_interface; struct v5x_instance; struct v5x_user_port; struct v5x_link; struct v5x_l1_proto; struct v52_pp_proto; /* A C-channel is a 64k timeslot used for signalling */ struct v5x_c_channel { struct v5x_link *link; /* back-pointer */ struct v5x_timeslot *ts; /* E1 link timeslot. NULL = C-channel doesn't exist */ bool active; /* false == standby */ }; /* one physical E1 timeslot used on an E1 interface part of a V5.2 interface */ struct v5x_timeslot { uint8_t nr; struct v5x_link *link; /* back-pointer */ struct v5x_user_port *v5up; /* user port that this TS is assigned to */ bool b_channel; /* channel can be used as b-channel */ bool b_activated; /* activated (b-channel) at libosmo-abis */ }; /* one physical E1 interface used within a V5.2 interface */ struct v5x_link { struct llist_head list; /* interface.links */ uint8_t id; struct v5x_interface *interface; /* back-pointer */ struct v5x_timeslot ts[32]; /* 32 E1 slots; 0 not available */ struct v5x_c_channel c_channel[3]; /* 64k signaling possible on TS16, TS15 and TS31 */ struct v5x_l1_proto *l1; /* Layer 1 Link FSM protocol */ struct v5x_ctrl_proto *ctrl; /* Link control protocol instance */ struct osmo_fsm_inst *fi; /* Link Control FSM instance */ struct e1inp_line *e1_line; /* E1 line to use or NULL if not */ struct e1inp_line_ops e1_line_ops; /* use e1pinp_line->ops to point back to v5x_link */ }; /* one V5.x interface between AN (Access Network) and LE (Local Exchange) */ struct v5x_interface { struct llist_head list; /* instance.interfaces */ struct v5x_instance *instance; /* back-pointer */ char *name; /* user-configurable name */ enum v5x_dialect dialect; uint32_t id, id_remote; /* interface id */ uint8_t variant, variant_remote; /* provisioning variant */ bool id_remote_valid; bool variant_remote_valid; bool use_capability; /* use bearer transfer capability */ uint8_t capability; /* bearer transfer capability */ struct v5x_mgmt_proto *mgmt; /* management prococol states */ struct { struct v5x_ctrl_proto *ctrl; /* common control protocol instance */ struct lapv5_instance *li; /* Control data link */ bool established; /* track if link is up or down */ } control; struct { struct lapv5_instance *li; /* Link control data link */ bool established; /* track if link is up or down */ } lcp; struct { struct lapv5_instance *li; /* PSTN data link */ bool established; /* track if link is up or down */ } pstn; struct { struct lapv5_instance *li; /* BCC data link */ bool established; /* track if link is up or down */ } bcc; struct { uint16_t cc_id; /* CC-ID of protected C-channel */ struct v52_pp_proto *pp; /* protection protocol instance */ struct lapv5_instance *li[2]; /* Protection data link 1 + 2 */ bool established[2]; /* track if link is up or down */ } protection; struct llist_head links; /* list of v5x_link */ struct v5x_link *primary_link; /* primary link */ struct v5x_link *secondary_link; /* standby link */ struct v5x_link *cc_link; /* currently used link for signaling (primary on startup) */ struct llist_head user_ports; /* list of v5x_user_port */ struct llist_head bcc_procs; /* list of v52_bcc_procs */ }; struct v5x_l1_proto { struct v5x_link *v5l; /* back pointer */ struct osmo_fsm_inst *fi; /* L1 FSM */ int los, rai, ais, sa7; /* RX signal states */ }; struct v5x_ctrl_proto { enum v5x_ctrl_type type; /* type of control protocol: common/port/link */ void *priv; /* back pointer to the user */ struct osmo_fsm_inst *fi; /* control FSM */ struct llist_head tx_queue; /* list of message to be transmitted */ struct msgb *tx_msg; /* copy of unacked message, for second try */ }; struct v5x_pstn_proto { struct v5x_user_port *v5up; /* back pointer, if port control is used */ struct osmo_fsm_inst *fi; /* control FSM */ uint8_t S_s, S_a, S_r; /* sequence numbers */ struct msgb *tx_msg; /* copy of unacked message, for repitition */ int timeout_event; /* event when timer times out */ int timeout_count; /* how many times the timer was started/timed out */ struct osmo_timer_list timer_Tr; /* extra timer for receive sequence ack */ struct osmo_timer_list timer_Tt; /* extra timer for transmit sequence ack */ }; /* indexes of blocking/unblocking instances */ #define UNBLK_ALL_PSTN_ISDN 0 #define UNBLK_ALL_PSTN 1 #define UNBLK_ALL_ISDN 2 #define BLK_ALL_PSTN 3 #define BLK_ALL_ISDN 4 struct v5x_mgmt_proto { struct v5x_interface *interface; /* back-pointer to instance we're part of */ bool auto_restart; /* restart automatically after TC8/TC9 timeout */ bool pstn_enable_early; /* enable datalink before startup */ bool pstn_rs_pending; /* pending restart flag */ bool do_est; /* actively establish datalinks */ bool do_align; /* actively perform alignment */ bool acc_align; /* use accellerated alignment */ struct osmo_fsm_inst *system_fi; /* system startup FSM */ struct osmo_fsm_inst *pstn_dl_fi; /* PSTN DL startup FSM */ struct osmo_fsm_inst *pstn_rs_fi; /* PSTN restart FSM */ struct osmo_fsm_inst *unblk_all_fi[5]; /* unblock/block all FSM (5 instances) */ struct osmo_timer_list timer_system; /* delayed start, so sockets can connect first */ struct osmo_timer_list timer_tr1; /* see Table C.1/G.965 */ struct osmo_timer_list timer_tr2; struct osmo_timer_list timer_tc1; struct osmo_timer_list timer_tc2; struct osmo_timer_list timer_tc3; struct osmo_timer_list timer_tc7; struct osmo_timer_list timer_tc8; struct osmo_timer_list timer_tc9; struct osmo_timer_list timer_tv1; }; #define EC_BUFFER 1024 #define EC_TAPS 256 #define USE_ECHO_CANCELER 1 #define USE_ECHO_SUPPRESSOR 2 struct v5x_echo_proc { struct v5x_user_port *port; /* back pointer to user port */ int enabled; /* line echo canceler enabled (configured and not disabled by ANS) */ echo_can_state_t *ec; /* echo canceler state */ echo_sup_state_t *es; /* echo suppressor state */ int16_t tx_buffer[EC_BUFFER]; /* buffer to store TX audio */ int tx_buffer_in; /* next pos to write to buffer */ int tx_buffer_out; /* next pos to read from buffer */ struct answer_tone at; /* answer tone detector process */ }; /* one user-facing port (subscriber line) */ struct v5x_user_port { struct llist_head list; /* part of v5x_instance.ports */ struct v5x_interface *interface; /* back-pointer to instance we're part of */ uint16_t nr; /* port-number in decoded form (0..32767) */ char ifname[64]; /* name of interface, also used for PH-socket */ enum v5x_user_type type; /* type of port (ISDN/PSTN) */ struct v5x_ctrl_proto *ctrl; /* port control protocol instance */ struct osmo_fsm_inst *port_fi; /* port FSM */ struct v5x_timeslot *ts[2]; /* two time slots */ struct { } isdn; struct { struct v5x_pstn_proto *proto; } pstn; ph_socket_t ph_socket; /* unix socket to connect port to */ bool le_unblocked; /* if port is not blocked by LE */ bool an_unblocked; /* if port is not blocked by AN */ int use_line_echo; /* line echo canceler/suppressor is configured */ struct v5x_echo_proc ep[2]; /* echo canceler process */ }; /* BCC process */ struct v52_bcc_proc { struct llist_head list; /* part of v5x_instance.ports */ struct v5x_interface *interface; /* back-pointer to instance we're part of */ struct osmo_fsm_inst *fi; /* BCC FSM */ uint16_t ref; /* reference of this process */ uint8_t source_id; /* reference source */ int timer; /* timer used */ int expired; /* number of timeouts */ uint16_t user_port_id; /* port ID */ bool is_isdn; /* user port type */ uint8_t link_id; /* link ID */ uint8_t ts; /* TS nr */ uint8_t override; /* override assigned channel */ uint8_t isdn_slot; /* channel on ISDN interface */ uint8_t *isdn_multislot; /* multiple slot assignment */ bool use_capability; /* use bearer transfer capability */ uint8_t capability; /* bearer transfer capability */ }; struct v52_pp_mgmt_info { uint8_t link_id; uint8_t ts; uint8_t cause; }; /* PP process */ struct v52_pp_proto { struct v5x_interface *interface; /* back pointer, if port control is used */ struct osmo_fsm_inst *fi; /* PP FSM */ uint8_t vp_s, vp_r; /* sequence numbers */ bool vp_r_set; /* if we ever received a sequence number */ struct v52_pp_mgmt_info info; /* keep management info for resend after timeout */ int timeout_event; /* event when timer times out */ int timeout_count; /* how many times the timer was started/timed out */ }; struct v5x_instance { struct llist_head list; /* part of global list of instances */ struct llist_head interfaces; /* v5x_interface.list */ }; struct v5x_instance *v5x_instance_alloc(void *ctx); void v5x_instance_free(struct v5x_instance *v5i); struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, uint32_t id, enum v5x_dialect dialect); struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t nr, enum v5x_user_type, uint8_t ts1, uint8_t ts2); void v5x_interface_free(struct v5x_interface *v5if); struct v5x_user_port *v5x_user_port_find(struct v5x_interface *v5if, uint16_t nr, bool is_isdn); void v5x_user_port_destroy(struct v5x_user_port *v5up); void v5x_echo_reset(struct v5x_echo_proc *ep, int enable); struct v5x_link *v5x_link_create(struct v5x_interface *v5if, uint8_t id); int v5x_link_destroy(struct v5x_link *v5l); int v5x_link_count(struct v5x_interface *v5if); struct v5x_link *v5x_link_find_id(struct v5x_interface *v5if, uint8_t id); const char *v5x_interface_name(const struct v5x_interface *v5if);