forked from retronetworking/osmo-v5
Added echo cancelation option
This commit is contained in:
parent
e59752a1cb
commit
a4483db3af
|
@ -25,4 +25,7 @@ osmo_v5_le_SOURCES = logging.c \
|
|||
main.c
|
||||
|
||||
osmo_v5_le_LDADD = $(COMMON_LA) \
|
||||
$(COMMONLIBS)
|
||||
$(COMMONLIBS) \
|
||||
libecho/libecho.a \
|
||||
libg711/libg711.a \
|
||||
-lm
|
||||
|
|
75
src/layer1.c
75
src/layer1.c
|
@ -12,7 +12,10 @@
|
|||
#include "layer1.h"
|
||||
#include "v5x_l1_fsm.h"
|
||||
#include "v5x_le_management.h"
|
||||
#include "logging.h"
|
||||
#include "libg711/g711.h"
|
||||
|
||||
extern int ulaw;
|
||||
extern int test_sa7;
|
||||
extern const char *gsmtap_ip;
|
||||
|
||||
|
@ -132,6 +135,69 @@ int v5x_l1_signal_snd(struct v5x_link *v5l, enum l1_signal_prim prim)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* store TX to buffer */
|
||||
static void echo_tx(struct v5x_echo_proc *ep, uint8_t *data, int len)
|
||||
{
|
||||
int in;
|
||||
int16_t tx;
|
||||
|
||||
if (!ep->ec || !ep->enabled)
|
||||
return;
|
||||
|
||||
while (len--) {
|
||||
if (ulaw)
|
||||
tx = g711_ulaw_flipped_to_linear[*data++];
|
||||
else
|
||||
tx = g711_alaw_flipped_to_linear[*data++];
|
||||
ep->tx_buffer[ep->tx_buffer_in] = tx;
|
||||
in = (ep->tx_buffer_in + 1) % EC_BUFFER;
|
||||
/* buffer overflow condition, should not happen, if application syncs to RX */
|
||||
if (in == ep->tx_buffer_out)
|
||||
break;
|
||||
ep->tx_buffer_in = in;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove echo from RX using TX from buffer */
|
||||
static void echo_rx(struct v5x_echo_proc *ep, uint8_t *data, int len)
|
||||
{
|
||||
struct v5x_user_port *v5up = ep->port;
|
||||
int16_t tx, rx, rx_can;
|
||||
int16_t answer_buffer[len];
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
if (!ep->ec || !ep->enabled)
|
||||
return;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
/* buffer underrun condition, may happen before buffer was filled with first frame */
|
||||
if (ep->tx_buffer_out == ep->tx_buffer_in)
|
||||
tx = 0;
|
||||
else {
|
||||
tx = ep->tx_buffer[ep->tx_buffer_out];
|
||||
ep->tx_buffer_out = (ep->tx_buffer_out + 1) % EC_BUFFER;
|
||||
}
|
||||
if (ulaw) {
|
||||
rx = g711_ulaw_flipped_to_linear[*data];
|
||||
rx_can = echo_can_update(ep->ec, tx, rx);
|
||||
*data++ = g711_linear_to_ulaw_flipped[(uint16_t)rx_can];
|
||||
} else {
|
||||
rx = g711_alaw_flipped_to_linear[*data];
|
||||
rx_can = echo_can_update(ep->ec, tx, rx);
|
||||
*data++ = g711_linear_to_alaw_flipped[(uint16_t)rx_can];
|
||||
}
|
||||
answer_buffer[i] = rx + tx; /* yes, may overflow, but then it is no valid tone anyway */
|
||||
}
|
||||
|
||||
rc = answertone_process(&ep->at, answer_buffer, len);
|
||||
if (rc > 0) {
|
||||
LOGP(DV5, LOGL_NOTICE, "Detected answer tone, disable echo chanceler of %s port %d.\n",
|
||||
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
||||
ep->enabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* data L1 -> L2 from E1 interface */
|
||||
static void hdlc_rx_cb(struct e1inp_ts *ts, struct msgb *msg)
|
||||
{
|
||||
|
@ -195,10 +261,14 @@ static void raw_rx_cb(struct e1inp_ts *ts, struct msgb *msg)
|
|||
osmo_revbytebits_buf(msg->data, msg->len);
|
||||
|
||||
/* if assigned and active, send B-channel data to socket interface */
|
||||
if (v5up->ts[0] && v5up->ts[0]->nr == ts->num && v5up->ts_activated[0])
|
||||
if (v5up->ts[0] && v5up->ts[0]->nr == ts->num && v5up->ts_activated[0]) {
|
||||
echo_rx(&v5up->ep[0], msg->data, msg->len);
|
||||
ph_socket_tx_msg(&v5up->ph_socket, 1, PH_PRIM_DATA_IND, msg->data, msg->len);
|
||||
if (v5up->ts[1] && v5up->ts[1]->nr == ts->num && v5up->ts_activated[1])
|
||||
}
|
||||
if (v5up->ts[1] && v5up->ts[1]->nr == ts->num && v5up->ts_activated[1]) {
|
||||
echo_rx(&v5up->ep[1], msg->data, msg->len);
|
||||
ph_socket_tx_msg(&v5up->ph_socket, 2, PH_PRIM_DATA_IND, msg->data, msg->len);
|
||||
}
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
|
@ -345,6 +415,7 @@ void ph_socket_rx_cb(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, i
|
|||
memcpy(msgb_put(msg, length), data, length);
|
||||
lapv5ef_tx(v5up, msg);
|
||||
} else if ((channel == 1 || channel == 2) && v5up->ts[channel - 1]) {
|
||||
echo_tx(&v5up->ep[channel - 1], data, length);
|
||||
struct msgb *msg = msgb_alloc_headroom(length + 32, 32, "B MSG");
|
||||
memcpy(msgb_put(msg, length), data, length);
|
||||
ph_data_req_raw(v5up->ts[channel - 1]->link, msg, v5up->ts[channel - 1]->nr);
|
||||
|
|
10
src/main.c
10
src/main.c
|
@ -64,6 +64,7 @@
|
|||
#include "v5x_le_management.h"
|
||||
#include "v5le_vty.h"
|
||||
#include "logging.h"
|
||||
#include "libg711/g711.h"
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
@ -74,6 +75,7 @@ struct v5x_instance *v5i = NULL;
|
|||
static int daemonize = 0;
|
||||
const char *gsmtap_ip = NULL;
|
||||
int test_sa7 = 0;
|
||||
int ulaw = 0;
|
||||
|
||||
const char *v5le_copyright =
|
||||
"Copyright (C) 2022 by Harald Welte & Andreas Eversberg\r\n"
|
||||
|
@ -93,6 +95,7 @@ static void print_help()
|
|||
printf(" -V --version Print the version number\n");
|
||||
printf(" -T --gsmtap IP Send GSMTAP messages to ip. (e.g. 224.0.0.1)\n");
|
||||
printf(" -7 --rest-sa7 Continously toggle Sa7 when received. (Do loop interface.)\n");
|
||||
printf(" --ulaw Use u-Law instead of a-Law for audio processing\n");
|
||||
|
||||
printf("\nVTY reference generation:\n");
|
||||
printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
|
||||
|
@ -118,6 +121,9 @@ static void handle_long_options(const char *prog_name, const int long_option)
|
|||
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
|
||||
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
|
||||
exit(0);
|
||||
case 3:
|
||||
ulaw = 1;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
|
||||
exit(2);
|
||||
|
@ -139,6 +145,7 @@ static void handle_options(int argc, char **argv)
|
|||
{"vty-ref-xml", 0, &long_option, 2},
|
||||
{"gsmtap", 1, 0, 'T'},
|
||||
{"test-sa7", 0, 0, '7'},
|
||||
{"ulaw", 0, &long_option, 3},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
|
@ -238,6 +245,9 @@ int main(int argc, char **argv)
|
|||
v52_le_pp_init();
|
||||
v5x_le_mgmt_init();
|
||||
|
||||
/* G.711 init */
|
||||
g711_init();
|
||||
|
||||
/* create v5x instance */
|
||||
v5i = v5x_instance_alloc(tall_v5le_ctx);
|
||||
if (!v5i)
|
||||
|
|
201
src/v5le_vty.c
201
src/v5le_vty.c
|
@ -28,6 +28,10 @@ static struct v5x_interface *get_interface(struct vty *vty)
|
|||
return v5if;
|
||||
}
|
||||
|
||||
/*
|
||||
* show
|
||||
*/
|
||||
|
||||
DEFUN(show_interface, show_interface_cmd,
|
||||
"show interface",
|
||||
SHOW_STR "Show interface and states")
|
||||
|
@ -71,7 +75,7 @@ DEFUN(show_interface, show_interface_cmd,
|
|||
if (v5if->protection.li[1])
|
||||
vty_out(vty, " Protection link (secondary): %s%s",
|
||||
(v5if->protection.established[1]) ? "establised" : "down" , VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_links, show_links_cmd,
|
||||
|
@ -107,7 +111,7 @@ DEFUN(show_links, show_links_cmd,
|
|||
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr, VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_ports, show_ports_cmd,
|
||||
|
@ -155,9 +159,13 @@ DEFUN(show_ports, show_ports_cmd,
|
|||
}
|
||||
}
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* operate
|
||||
*/
|
||||
|
||||
DEFUN(pstn_restart, pstn_restart_cmd,
|
||||
"pstn-restart",
|
||||
"Perform PSTN protocol restart")
|
||||
|
@ -165,7 +173,7 @@ DEFUN(pstn_restart, pstn_restart_cmd,
|
|||
struct v5x_interface *v5if = get_interface(vty);
|
||||
|
||||
v5x_le_pstn_restart(v5if);
|
||||
return CMD_SUCCESS;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(switchover, switchover_cmd,
|
||||
|
@ -191,7 +199,7 @@ DEFUN(switchover, switchover_cmd,
|
|||
}
|
||||
|
||||
v52_le_pp_mdu_snd(v5if, MDU_Protection_switch_over_com, atoi(argv[0]), v5l->c_channel[0].ts->nr, 0);
|
||||
return CMD_SUCCESS;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(unblock_link, unblock_link_cmd,
|
||||
|
@ -213,7 +221,7 @@ DEFUN(unblock_link, unblock_link_cmd,
|
|||
}
|
||||
|
||||
v52_le_lcp_mdu_snd(v5l, MDU_LUBR);
|
||||
return CMD_SUCCESS;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(block_link, block_link_cmd,
|
||||
|
@ -235,13 +243,18 @@ DEFUN(block_link, block_link_cmd,
|
|||
}
|
||||
|
||||
v52_le_lcp_mdu_snd(v5l, MDU_LBI);
|
||||
return CMD_SUCCESS;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* configure
|
||||
*/
|
||||
|
||||
enum v5_vty_node {
|
||||
INTERFACE_NODE_V51 = _LAST_OSMOVTY_NODE + 1,
|
||||
INTERFACE_NODE_V52,
|
||||
LINK_NODE,
|
||||
PORT_NODE,
|
||||
};
|
||||
|
||||
static struct cmd_node interface_node_v51 = {
|
||||
|
@ -262,6 +275,12 @@ static struct cmd_node link_node = {
|
|||
.vtysh = 1,
|
||||
};
|
||||
|
||||
static struct cmd_node port_node = {
|
||||
.node = PORT_NODE,
|
||||
.prompt = "%s(config-port)# ",
|
||||
.vtysh = 1,
|
||||
};
|
||||
|
||||
static int v5le_vty_is_config_node(struct vty __attribute__((unused)) *vty, int node)
|
||||
{
|
||||
switch (node) {
|
||||
|
@ -276,12 +295,19 @@ static int v5le_vty_go_parent(struct vty *vty)
|
|||
{
|
||||
struct v5x_interface *v5if;
|
||||
struct v5x_link *v5l;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
switch (vty->node) {
|
||||
case PORT_NODE:
|
||||
v5up = vty->index;
|
||||
v5if = v5up->interface;
|
||||
vty->node = (v5if->dialect == V5X_DIALECT_V51) ? INTERFACE_NODE_V51 : INTERFACE_NODE_V52;
|
||||
vty->index = v5if;
|
||||
break;
|
||||
case LINK_NODE:
|
||||
v5l = vty->index;
|
||||
v5if = v5l->interface;
|
||||
vty->node = (v5if->dialect == V5X_DIALECT_V51) ? INTERFACE_NODE_V51 : INTERFACE_NODE_V52;
|
||||
vty->node = (v5if->dialect == V5X_DIALECT_V51) ? INTERFACE_NODE_V51 : INTERFACE_NODE_V52;
|
||||
vty->index = v5if;
|
||||
break;
|
||||
case INTERFACE_NODE_V51:
|
||||
|
@ -327,9 +353,9 @@ DEFUN(cfg_interface, cfg_interface_cmd,
|
|||
vty_out(vty, "%%Different interface type already created. To change, remove first.%s", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
vty->node = (v5if->dialect == V5X_DIALECT_V51) ? INTERFACE_NODE_V51 : INTERFACE_NODE_V52;
|
||||
vty->node = (v5if->dialect == V5X_DIALECT_V51) ? INTERFACE_NODE_V51 : INTERFACE_NODE_V52;
|
||||
vty->index = v5if;
|
||||
return CMD_SUCCESS;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_interface, cfg_no_interface_cmd,
|
||||
|
@ -345,7 +371,7 @@ DEFUN(cfg_no_interface, cfg_no_interface_cmd,
|
|||
v5x_interface_free(v5if);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_interface_id, cfg_interface_id_cmd,
|
||||
|
@ -408,9 +434,9 @@ DEFUN(cfg_link_v51, cfg_link_cmd_v51,
|
|||
|
||||
v5l = llist_first_entry(&v5if->links, struct v5x_link, list);
|
||||
|
||||
vty->node = LINK_NODE;
|
||||
vty->node = LINK_NODE;
|
||||
vty->index = v5l;
|
||||
return CMD_SUCCESS;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_link_v52, cfg_link_cmd_v52,
|
||||
|
@ -429,9 +455,9 @@ DEFUN(cfg_link_v52, cfg_link_cmd_v52,
|
|||
}
|
||||
}
|
||||
|
||||
vty->node = LINK_NODE;
|
||||
vty->node = LINK_NODE;
|
||||
vty->index = v5l;
|
||||
return CMD_SUCCESS;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_link_v52, cfg_no_link_cmd_v52,
|
||||
|
@ -454,7 +480,7 @@ DEFUN(cfg_no_link_v52, cfg_no_link_cmd_v52,
|
|||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_e1_line, cfg_e1_line_cmd,
|
||||
|
@ -495,14 +521,14 @@ DEFUN(cfg_no_e1_line, cfg_no_e1_line_cmd,
|
|||
|
||||
DEFUN(cfg_port_pstn_v51, cfg_port_pstn_cmd_v51,
|
||||
"port pstn <0-32767> <1-31>",
|
||||
"Create V5 user port\n" "PSTN user port\n" "L3 address\n" "Time slot")
|
||||
"Create/modify V5 user port\n" "PSTN user port\n" "L3 address\n" "Time slot")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
v5up = v5x_user_port_find(v5if, atoi(argv[0]), false);
|
||||
if (v5up) {
|
||||
vty_out(vty, "%%Given PSTN user port already exists, remove first.%s", VTY_NEWLINE);
|
||||
vty_out(vty, "%%Given PSTN user port already exists.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
v5up = v5x_user_port_create(v5if, atoi(argv[0]), V5X_USER_TYPE_PSTN, atoi(argv[1]), 0);
|
||||
|
@ -510,26 +536,34 @@ DEFUN(cfg_port_pstn_v51, cfg_port_pstn_cmd_v51,
|
|||
vty_out(vty, "%%Failed to create PSTN user port. See logging output.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vty->node = PORT_NODE;
|
||||
vty->index = v5up;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_port_pstn_v52, cfg_port_pstn_cmd_v52,
|
||||
DEFUN(cfg_port_pstn, cfg_port_pstn_cmd,
|
||||
"port pstn <0-32767>",
|
||||
"Create V5 user port\n" "PSTN user port\n" "L3 address")
|
||||
"Create/modify V5 user port\n" "PSTN user port\n" "L3 address")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
v5up = v5x_user_port_find(v5if, atoi(argv[0]), false);
|
||||
if (v5up) {
|
||||
vty_out(vty, "%%Given PSTN user port already exists, remove first.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
if (!v5up) {
|
||||
if (v5if->dialect == V5X_DIALECT_V51) {
|
||||
vty_out(vty, "%%Given PSTN user port does not exist, give time slot.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
v5up = v5x_user_port_create(v5if, atoi(argv[0]), V5X_USER_TYPE_PSTN, 0, 0);
|
||||
}
|
||||
v5up = v5x_user_port_create(v5if, atoi(argv[0]), V5X_USER_TYPE_PSTN, 0, 0);
|
||||
if (!v5up) {
|
||||
vty_out(vty, "%%Failed to create PSTN user port. See logging output.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vty->node = PORT_NODE;
|
||||
vty->index = v5up;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -551,14 +585,14 @@ DEFUN(cfg_no_port_pstn, cfg_no_port_pstn_cmd,
|
|||
|
||||
DEFUN(cfg_port_isdn_v51, cfg_port_isdn_cmd_v51,
|
||||
"port isdn <0-8175> <1-31> <1-31>",
|
||||
"Create V5 user port\n" "ISDN user port\n" "L3 address\n" "Time slot 1\n" "Time slot 2")
|
||||
"Create/modify V5 user port\n" "ISDN user port\n" "L3 address\n" "Time slot 1\n" "Time slot 2")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
v5up = v5x_user_port_find(v5if, atoi(argv[0]), true);
|
||||
if (v5up) {
|
||||
vty_out(vty, "%%Given ISDN user port already exists, remove first.%s", VTY_NEWLINE);
|
||||
vty_out(vty, "%%Given ISDN user port already exists.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
v5up = v5x_user_port_create(v5if, atoi(argv[0]), V5X_USER_TYPE_ISDN, atoi(argv[1]), atoi(argv[2]));
|
||||
|
@ -566,26 +600,34 @@ DEFUN(cfg_port_isdn_v51, cfg_port_isdn_cmd_v51,
|
|||
vty_out(vty, "%%Failed to create ISDN user port. See logging output.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vty->node = PORT_NODE;
|
||||
vty->index = v5up;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_port_isdn_v52, cfg_port_isdn_cmd_v52,
|
||||
DEFUN(cfg_port_isdn, cfg_port_isdn_cmd,
|
||||
"port isdn <0-8175>",
|
||||
"Create V5 user port\n" "ISDN user port\n" "L3 address")
|
||||
"Create/modify V5 user port\n" "ISDN user port\n" "L3 address")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
v5up = v5x_user_port_find(v5if, atoi(argv[0]), true);
|
||||
if (v5up) {
|
||||
vty_out(vty, "%%Given ISDN user port already exists, remove first.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
if (!v5up) {
|
||||
if (v5if->dialect == V5X_DIALECT_V51) {
|
||||
vty_out(vty, "%%Given ISDN user port does not exist, give time slots.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
v5up = v5x_user_port_create(v5if, atoi(argv[0]), V5X_USER_TYPE_ISDN, 0, 0);
|
||||
}
|
||||
v5up = v5x_user_port_create(v5if, atoi(argv[0]), V5X_USER_TYPE_ISDN, 0, 0);
|
||||
if (!v5up) {
|
||||
vty_out(vty, "%%Failed to create ISDN user port. See logging output.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vty->node = PORT_NODE;
|
||||
vty->index = v5up;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -605,11 +647,67 @@ DEFUN(cfg_no_port_isdn, cfg_no_port_isdn_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_echo, cfg_echo_cmd,
|
||||
"line-echo-canceler",
|
||||
"Enable line echo canceler for all calls")
|
||||
{
|
||||
struct v5x_user_port *v5up = vty->index;
|
||||
|
||||
v5up->echo_configured = 1;
|
||||
if (v5up->ts_activated[0])
|
||||
v5x_echo_reset(&v5up->ep[0], v5up->echo_configured);
|
||||
if (v5up->ts_activated[1])
|
||||
v5x_echo_reset(&v5up->ep[1], v5up->echo_configured);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_echo, cfg_no_echo_cmd,
|
||||
"no line-echo-canceler",
|
||||
NO_STR "Disable line echo canceler for all calls")
|
||||
{
|
||||
struct v5x_user_port *v5up = vty->index;
|
||||
|
||||
v5up->echo_configured = 0;
|
||||
if (v5up->ts_activated[0])
|
||||
v5x_echo_reset(&v5up->ep[0], v5up->echo_configured);
|
||||
if (v5up->ts_activated[1])
|
||||
v5x_echo_reset(&v5up->ep[1], v5up->echo_configured);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* write
|
||||
*/
|
||||
|
||||
static void config_write_user_port(struct vty *vty, struct v5x_interface *v5if)
|
||||
{
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
||||
switch (v5up->type) {
|
||||
case V5X_USER_TYPE_PSTN:
|
||||
if (v5if->dialect == V5X_DIALECT_V51)
|
||||
vty_out(vty, " port pstn %d %d%s", v5up->nr, v5up->ts[0]->nr, VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " port pstn %d%s", v5up->nr, VTY_NEWLINE);
|
||||
break;
|
||||
case V5X_USER_TYPE_ISDN:
|
||||
if (v5if->dialect == V5X_DIALECT_V51)
|
||||
vty_out(vty, " port isdn %d %d %d%s", v5up->nr, v5up->ts[0]->nr, v5up->ts[1]->nr,
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " port isdn %d%s", v5up->nr, VTY_NEWLINE);
|
||||
break;
|
||||
}
|
||||
if (v5up->echo_configured)
|
||||
vty_out(vty, " line-echo-canceler%s", VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
|
||||
static int config_write_interface_v51(struct vty *vty)
|
||||
{
|
||||
struct v5x_interface *v5if = NULL;
|
||||
struct v5x_link *v5l;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
if (!llist_empty(&v5i->interfaces))
|
||||
v5if = llist_first_entry(&v5i->interfaces, struct v5x_interface, list);
|
||||
|
@ -627,17 +725,7 @@ static int config_write_interface_v51(struct vty *vty)
|
|||
vty_out(vty, " no e1 line%s", VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " e1 line %d%s", v5l->e1_line, VTY_NEWLINE);
|
||||
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
||||
switch (v5up->type) {
|
||||
case V5X_USER_TYPE_PSTN:
|
||||
vty_out(vty, " port pstn %d %d%s", v5up->nr, v5up->ts[0]->nr, VTY_NEWLINE);
|
||||
break;
|
||||
case V5X_USER_TYPE_ISDN:
|
||||
vty_out(vty, " port isdn %d %d %d%s", v5up->nr, v5up->ts[0]->nr, v5up->ts[1]->nr,
|
||||
VTY_NEWLINE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
config_write_user_port(vty, v5if);
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
@ -646,7 +734,6 @@ static int config_write_interface_v52(struct vty *vty)
|
|||
{
|
||||
struct v5x_interface *v5if = NULL;
|
||||
struct v5x_link *v5l;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
if (!llist_empty(&v5i->interfaces))
|
||||
v5if = llist_first_entry(&v5i->interfaces, struct v5x_interface, list);
|
||||
|
@ -672,20 +759,15 @@ static int config_write_interface_v52(struct vty *vty)
|
|||
else
|
||||
vty_out(vty, " e1 line %d%s", v5l->e1_line, VTY_NEWLINE);
|
||||
}
|
||||
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
||||
switch (v5up->type) {
|
||||
case V5X_USER_TYPE_PSTN:
|
||||
vty_out(vty, " port pstn %d%s", v5up->nr, VTY_NEWLINE);
|
||||
break;
|
||||
case V5X_USER_TYPE_ISDN:
|
||||
vty_out(vty, " port isdn %d%s", v5up->nr, VTY_NEWLINE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
config_write_user_port(vty, v5if);
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* init
|
||||
*/
|
||||
|
||||
int v5le_vty_init(void)
|
||||
{
|
||||
install_element_ve(&show_interface_cmd);
|
||||
|
@ -703,8 +785,10 @@ int v5le_vty_init(void)
|
|||
install_element(INTERFACE_NODE_V51, &cfg_interface_variant_cmd);
|
||||
install_element(INTERFACE_NODE_V51, &cfg_link_cmd_v51);
|
||||
install_element(INTERFACE_NODE_V51, &cfg_port_pstn_cmd_v51);
|
||||
install_element(INTERFACE_NODE_V51, &cfg_port_pstn_cmd);
|
||||
install_element(INTERFACE_NODE_V51, &cfg_no_port_pstn_cmd);
|
||||
install_element(INTERFACE_NODE_V51, &cfg_port_isdn_cmd_v51);
|
||||
install_element(INTERFACE_NODE_V51, &cfg_port_isdn_cmd);
|
||||
install_element(INTERFACE_NODE_V51, &cfg_no_port_isdn_cmd);
|
||||
install_element(INTERFACE_NODE_V52, &cfg_interface_id_cmd);
|
||||
install_element(INTERFACE_NODE_V52, &cfg_interface_variant_cmd);
|
||||
|
@ -713,13 +797,16 @@ int v5le_vty_init(void)
|
|||
install_element(INTERFACE_NODE_V52, &cfg_interface_no_capability_cmd);
|
||||
install_element(INTERFACE_NODE_V52, &cfg_link_cmd_v52);
|
||||
install_element(INTERFACE_NODE_V52, &cfg_no_link_cmd_v52);
|
||||
install_element(INTERFACE_NODE_V52, &cfg_port_pstn_cmd_v52);
|
||||
install_element(INTERFACE_NODE_V52, &cfg_port_pstn_cmd);
|
||||
install_element(INTERFACE_NODE_V52, &cfg_no_port_pstn_cmd);
|
||||
install_element(INTERFACE_NODE_V52, &cfg_port_isdn_cmd_v52);
|
||||
install_element(INTERFACE_NODE_V52, &cfg_port_isdn_cmd);
|
||||
install_element(INTERFACE_NODE_V52, &cfg_no_port_isdn_cmd);
|
||||
install_node(&link_node, NULL);
|
||||
install_element(LINK_NODE, &cfg_e1_line_cmd);
|
||||
install_element(LINK_NODE, &cfg_no_e1_line_cmd);
|
||||
install_node(&port_node, NULL);
|
||||
install_element(PORT_NODE, &cfg_echo_cmd);
|
||||
install_element(PORT_NODE, &cfg_no_echo_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -318,6 +318,7 @@ struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t
|
|||
uint8_t ts1, uint8_t ts2)
|
||||
{
|
||||
struct v5x_user_port *v5up;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
if (v5x_user_port_find(v5if, nr, (type == V5X_USER_TYPE_ISDN))) {
|
||||
|
@ -411,6 +412,23 @@ struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t
|
|||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
v5up->ep[i].port = v5up;
|
||||
/* EC needs to adapt, NLP causes better results on quick volume changes */
|
||||
v5up->ep[i].ec = echo_can_create(EC_TAPS, ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP);
|
||||
if (!v5up->ep[i].ec) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create line echo canceler\n");
|
||||
goto error;
|
||||
}
|
||||
rc = answertone_init(&v5up->ep[i].at, 8000);
|
||||
if (rc < 0) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create answer tone detector\n");
|
||||
goto error;
|
||||
}
|
||||
if (type != V5X_USER_TYPE_ISDN)
|
||||
break;
|
||||
}
|
||||
|
||||
rc = ph_socket_init(&v5up->ph_socket, ph_socket_rx_cb, v5up, v5up->ifname, 1);
|
||||
if (rc < 0) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create PH-socket\n");
|
||||
|
@ -430,19 +448,26 @@ error:
|
|||
|
||||
void v5x_user_port_destroy(struct v5x_user_port *v5up)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOGP(DV5, LOGL_NOTICE, "Destroying V5 user port with L3 addr %d\n", v5up->nr);
|
||||
|
||||
/* close first, because it sends messages */
|
||||
ph_socket_exit(&v5up->ph_socket);
|
||||
|
||||
/* unassign ports */
|
||||
if (v5up->ts[0]) {
|
||||
v5up->ts[0]->v5up = NULL;
|
||||
v5up->ts[0] = NULL;
|
||||
}
|
||||
if (v5up->ts[1]) {
|
||||
v5up->ts[1]->v5up = NULL;
|
||||
v5up->ts[1] = NULL;
|
||||
for (i = 0; i < 2; i++) {
|
||||
/* destory line echo canceler */
|
||||
if (v5up->ep[i].ec) {
|
||||
echo_can_free(v5up->ep[i].ec);
|
||||
answertone_exit(&v5up->ep[i].at);
|
||||
v5up->ep[i].ec = NULL;
|
||||
}
|
||||
|
||||
/* unassign ports */
|
||||
if (v5up->ts[i]) {
|
||||
v5up->ts[i]->v5up = NULL;
|
||||
v5up->ts[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (v5up->ctrl)
|
||||
|
@ -459,6 +484,16 @@ void v5x_user_port_destroy(struct v5x_user_port *v5up)
|
|||
talloc_free(v5up);
|
||||
}
|
||||
|
||||
void v5x_echo_reset(struct v5x_echo_proc *ep, int enable)
|
||||
{
|
||||
ep->enabled = enable;
|
||||
answertone_reset(&ep->at);
|
||||
ep->tx_buffer_in = 0;
|
||||
ep->tx_buffer_out = 0;
|
||||
if (ep->ec)
|
||||
echo_can_flush(ep->ec);
|
||||
}
|
||||
|
||||
struct v5x_user_port *v5x_user_port_find(struct v5x_interface *v5if, uint16_t nr, bool is_isdn)
|
||||
{
|
||||
struct v5x_user_port *v5up;
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/gsm/lapd_core.h>
|
||||
|
||||
#include "libecho/echo.h"
|
||||
#include "libecho/answertone.h"
|
||||
#include "ph_socket.h"
|
||||
|
||||
/* Table 35/G.964 */
|
||||
|
@ -254,6 +256,19 @@ struct v5x_pstn_proto {
|
|||
struct osmo_timer_list timer_Tt; /* extra timer for transmit sequence ack */
|
||||
};
|
||||
|
||||
#define EC_BUFFER 1024
|
||||
#define EC_TAPS 256
|
||||
|
||||
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 */
|
||||
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 */
|
||||
|
@ -276,6 +291,8 @@ struct v5x_user_port {
|
|||
bool blocking_enabled; /* we may only send blocking updates, if true */
|
||||
bool le_unblocked; /* if port is not blocked by LE */
|
||||
bool an_unblocked; /* if port is not blocked by AN */
|
||||
int echo_configured; /* line echo canceler is configured */
|
||||
struct v5x_echo_proc ep[2]; /* echo canceler process */
|
||||
};
|
||||
|
||||
/* BCC process */
|
||||
|
@ -328,6 +345,7 @@ struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t
|
|||
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);
|
||||
|
|
|
@ -542,7 +542,7 @@ void v5x_le_pstn_fe_rcv(struct v5x_user_port *v5up, enum v5x_fe_prim prim, struc
|
|||
}
|
||||
|
||||
/*
|
||||
* BCC channel handling
|
||||
* BCC channel handling, also activation/deactivation for V5.1
|
||||
*/
|
||||
|
||||
/* assign channel */
|
||||
|
@ -554,6 +554,9 @@ void v5x_le_channel_assign(struct v5x_user_port *v5up, int channel)
|
|||
|
||||
OSMO_ASSERT(channel >= 1 && channel <= 2);
|
||||
|
||||
/* reset echo canceler state */
|
||||
v5x_echo_reset(&v5up->ep[channel - 1], v5up->echo_configured);
|
||||
|
||||
/* the channels are pre-assigned with V5.1, so only activation state is set */
|
||||
if (v5if->dialect == V5X_DIALECT_V51) {
|
||||
if (!v5up->ts[channel - 1]) {
|
||||
|
|
Loading…
Reference in New Issue