aboutsummaryrefslogtreecommitdiff
path: root/drivers/misc/echo
diff options
context:
space:
mode:
authorLibravatar Linus Torvalds <torvalds@linux-foundation.org>2023-02-21 18:24:12 -0800
committerLibravatar Linus Torvalds <torvalds@linux-foundation.org>2023-02-21 18:24:12 -0800
commit5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch)
treecc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/misc/echo
downloadlinux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz
linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski: "Core: - Add dedicated kmem_cache for typical/small skb->head, avoid having to access struct page at kfree time, and improve memory use. - Introduce sysctl to set default RPS configuration for new netdevs. - Define Netlink protocol specification format which can be used to describe messages used by each family and auto-generate parsers. Add tools for generating kernel data structures and uAPI headers. - Expose all net/core sysctls inside netns. - Remove 4s sleep in netpoll if carrier is instantly detected on boot. - Add configurable limit of MDB entries per port, and port-vlan. - Continue populating drop reasons throughout the stack. - Retire a handful of legacy Qdiscs and classifiers. Protocols: - Support IPv4 big TCP (TSO frames larger than 64kB). - Add IP_LOCAL_PORT_RANGE socket option, to control local port range on socket by socket basis. - Track and report in procfs number of MPTCP sockets used. - Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path manager. - IPv6: don't check net.ipv6.route.max_size and rely on garbage collection to free memory (similarly to IPv4). - Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986). - ICMP: add per-rate limit counters. - Add support for user scanning requests in ieee802154. - Remove static WEP support. - Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate reporting. - WiFi 7 EHT channel puncturing support (client & AP). BPF: - Add a rbtree data structure following the "next-gen data structure" precedent set by recently added linked list, that is, by using kfunc + kptr instead of adding a new BPF map type. - Expose XDP hints via kfuncs with initial support for RX hash and timestamp metadata. - Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to better support decap on GRE tunnel devices not operating in collect metadata. - Improve x86 JIT's codegen for PROBE_MEM runtime error checks. - Remove the need for trace_printk_lock for bpf_trace_printk and bpf_trace_vprintk helpers. - Extend libbpf's bpf_tracing.h support for tracing arguments of kprobes/uprobes and syscall as a special case. - Significantly reduce the search time for module symbols by livepatch and BPF. - Enable cpumasks to be used as kptrs, which is useful for tracing programs tracking which tasks end up running on which CPUs in different time intervals. - Add support for BPF trampoline on s390x and riscv64. - Add capability to export the XDP features supported by the NIC. - Add __bpf_kfunc tag for marking kernel functions as kfuncs. - Add cgroup.memory=nobpf kernel parameter option to disable BPF memory accounting for container environments. Netfilter: - Remove the CLUSTERIP target. It has been marked as obsolete for years, and we still have WARN splats wrt races of the out-of-band /proc interface installed by this target. - Add 'destroy' commands to nf_tables. They are identical to the existing 'delete' commands, but do not return an error if the referenced object (set, chain, rule...) did not exist. Driver API: - Improve cpumask_local_spread() locality to help NICs set the right IRQ affinity on AMD platforms. - Separate C22 and C45 MDIO bus transactions more clearly. - Introduce new DCB table to control DSCP rewrite on egress. - Support configuration of Physical Layer Collision Avoidance (PLCA) Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of shared medium Ethernet. - Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing preemption of low priority frames by high priority frames. - Add support for controlling MACSec offload using netlink SET. - Rework devlink instance refcounts to allow registration and de-registration under the instance lock. Split the code into multiple files, drop some of the unnecessarily granular locks and factor out common parts of netlink operation handling. - Add TX frame aggregation parameters (for USB drivers). - Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning messages with notifications for debug. - Allow offloading of UDP NEW connections via act_ct. - Add support for per action HW stats in TC. - Support hardware miss to TC action (continue processing in SW from a specific point in the action chain). - Warn if old Wireless Extension user space interface is used with modern cfg80211/mac80211 drivers. Do not support Wireless Extensions for Wi-Fi 7 devices at all. Everyone should switch to using nl80211 interface instead. - Improve the CAN bit timing configuration. Use extack to return error messages directly to user space, update the SJW handling, including the definition of a new default value that will benefit CAN-FD controllers, by increasing their oscillator tolerance. New hardware / drivers: - Ethernet: - nVidia BlueField-3 support (control traffic driver) - Ethernet support for imx93 SoCs - Motorcomm yt8531 gigabit Ethernet PHY - onsemi NCN26000 10BASE-T1S PHY (with support for PLCA) - Microchip LAN8841 PHY (incl. cable diagnostics and PTP) - Amlogic gxl MDIO mux - WiFi: - RealTek RTL8188EU (rtl8xxxu) - Qualcomm Wi-Fi 7 devices (ath12k) - CAN: - Renesas R-Car V4H Drivers: - Bluetooth: - Set Per Platform Antenna Gain (PPAG) for Intel controllers. - Ethernet NICs: - Intel (1G, igc): - support TSN / Qbv / packet scheduling features of i226 model - Intel (100G, ice): - use GNSS subsystem instead of TTY - multi-buffer XDP support - extend support for GPIO pins to E823 devices - nVidia/Mellanox: - update the shared buffer configuration on PFC commands - implement PTP adjphase function for HW offset control - TC support for Geneve and GRE with VF tunnel offload - more efficient crypto key management method - multi-port eswitch support - Netronome/Corigine: - add DCB IEEE support - support IPsec offloading for NFP3800 - Freescale/NXP (enetc): - support XDP_REDIRECT for XDP non-linear buffers - improve reconfig, avoid link flap and waiting for idle - support MAC Merge layer - Other NICs: - sfc/ef100: add basic devlink support for ef100 - ionic: rx_push mode operation (writing descriptors via MMIO) - bnxt: use the auxiliary bus abstraction for RDMA - r8169: disable ASPM and reset bus in case of tx timeout - cpsw: support QSGMII mode for J721e CPSW9G - cpts: support pulse-per-second output - ngbe: add an mdio bus driver - usbnet: optimize usbnet_bh() by avoiding unnecessary queuing - r8152: handle devices with FW with NCM support - amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation - virtio-net: support multi buffer XDP - virtio/vsock: replace virtio_vsock_pkt with sk_buff - tsnep: XDP support - Ethernet high-speed switches: - nVidia/Mellanox (mlxsw): - add support for latency TLV (in FW control messages) - Microchip (sparx5): - separate explicit and implicit traffic forwarding rules, make the implicit rules always active - add support for egress DSCP rewrite - IS0 VCAP support (Ingress Classification) - IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS etc.) - ES2 VCAP support (Egress Access Control) - support for Per-Stream Filtering and Policing (802.1Q, 8.6.5.1) - Ethernet embedded switches: - Marvell (mv88e6xxx): - add MAB (port auth) offload support - enable PTP receive for mv88e6390 - NXP (ocelot): - support MAC Merge layer - support for the the vsc7512 internal copper phys - Microchip: - lan9303: convert to PHYLINK - lan966x: support TC flower filter statistics - lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x - lan937x: support Credit Based Shaper configuration - ksz9477: support Energy Efficient Ethernet - other: - qca8k: convert to regmap read/write API, use bulk operations - rswitch: Improve TX timestamp accuracy - Intel WiFi (iwlwifi): - EHT (Wi-Fi 7) rate reporting - STEP equalizer support: transfer some STEP (connection to radio on platforms with integrated wifi) related parameters from the BIOS to the firmware. - Qualcomm 802.11ax WiFi (ath11k): - IPQ5018 support - Fine Timing Measurement (FTM) responder role support - channel 177 support - MediaTek WiFi (mt76): - per-PHY LED support - mt7996: EHT (Wi-Fi 7) support - Wireless Ethernet Dispatch (WED) reset support - switch to using page pool allocator - RealTek WiFi (rtw89): - support new version of Bluetooth co-existance - Mobile: - rmnet: support TX aggregation" * tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits) page_pool: add a comment explaining the fragment counter usage net: ethtool: fix __ethtool_dev_mm_supported() implementation ethtool: pse-pd: Fix double word in comments xsk: add linux/vmalloc.h to xsk.c sefltests: netdevsim: wait for devlink instance after netns removal selftest: fib_tests: Always cleanup before exit net/mlx5e: Align IPsec ASO result memory to be as required by hardware net/mlx5e: TC, Set CT miss to the specific ct action instance net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG net/mlx5: Refactor tc miss handling to a single function net/mlx5: Kconfig: Make tc offload depend on tc skb extension net/sched: flower: Support hardware miss to tc action net/sched: flower: Move filter handle initialization earlier net/sched: cls_api: Support hardware miss to tc action net/sched: Rename user cookie and act cookie sfc: fix builds without CONFIG_RTC_LIB sfc: clean up some inconsistent indentings net/mlx4_en: Introduce flexible array to silence overflow warning net: lan966x: Fix possible deadlock inside PTP net/ulp: Remove redundant ->clone() test in inet_clone_ulp(). ...
Diffstat (limited to 'drivers/misc/echo')
-rw-r--r--drivers/misc/echo/Kconfig9
-rw-r--r--drivers/misc/echo/Makefile2
-rw-r--r--drivers/misc/echo/echo.c589
-rw-r--r--drivers/misc/echo/echo.h175
-rw-r--r--drivers/misc/echo/fir.h154
-rw-r--r--drivers/misc/echo/oslec.h81
6 files changed, 1010 insertions, 0 deletions
diff --git a/drivers/misc/echo/Kconfig b/drivers/misc/echo/Kconfig
new file mode 100644
index 000000000..ce0a37a47
--- /dev/null
+++ b/drivers/misc/echo/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config ECHO
+ tristate "Line Echo Canceller support"
+ help
+ This driver provides line echo cancelling support for mISDN and
+ Zaptel drivers.
+
+ To compile this driver as a module, choose M here. The module
+ will be called echo.
diff --git a/drivers/misc/echo/Makefile b/drivers/misc/echo/Makefile
new file mode 100644
index 000000000..5b97467ff
--- /dev/null
+++ b/drivers/misc/echo/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ECHO) += echo.o
diff --git a/drivers/misc/echo/echo.c b/drivers/misc/echo/echo.c
new file mode 100644
index 000000000..3c4eaba86
--- /dev/null
+++ b/drivers/misc/echo/echo.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * echo.c - A line echo canceller. This code is being developed
+ * against and partially complies with G168.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ * and David Rowe <david_at_rowetel_dot_com>
+ *
+ * Copyright (C) 2001, 2003 Steve Underwood, 2007 David Rowe
+ *
+ * Based on a bit from here, a bit from there, eye of toad, ear of
+ * bat, 15 years of failed attempts by David and a few fried brain
+ * cells.
+ *
+ * All rights reserved.
+ */
+
+/*! \file */
+
+/* Implementation Notes
+ David Rowe
+ April 2007
+
+ This code started life as Steve's NLMS algorithm with a tap
+ rotation algorithm to handle divergence during double talk. I
+ added a Geigel Double Talk Detector (DTD) [2] and performed some
+ G168 tests. However I had trouble meeting the G168 requirements,
+ especially for double talk - there were always cases where my DTD
+ failed, for example where near end speech was under the 6dB
+ threshold required for declaring double talk.
+
+ So I tried a two path algorithm [1], which has so far given better
+ results. The original tap rotation/Geigel algorithm is available
+ in SVN http://svn.rowetel.com/software/oslec/tags/before_16bit.
+ It's probably possible to make it work if some one wants to put some
+ serious work into it.
+
+ At present no special treatment is provided for tones, which
+ generally cause NLMS algorithms to diverge. Initial runs of a
+ subset of the G168 tests for tones (e.g ./echo_test 6) show the
+ current algorithm is passing OK, which is kind of surprising. The
+ full set of tests needs to be performed to confirm this result.
+
+ One other interesting change is that I have managed to get the NLMS
+ code to work with 16 bit coefficients, rather than the original 32
+ bit coefficents. This reduces the MIPs and storage required.
+ I evaulated the 16 bit port using g168_tests.sh and listening tests
+ on 4 real-world samples.
+
+ I also attempted the implementation of a block based NLMS update
+ [2] but although this passes g168_tests.sh it didn't converge well
+ on the real-world samples. I have no idea why, perhaps a scaling
+ problem. The block based code is also available in SVN
+ http://svn.rowetel.com/software/oslec/tags/before_16bit. If this
+ code can be debugged, it will lead to further reduction in MIPS, as
+ the block update code maps nicely onto DSP instruction sets (it's a
+ dot product) compared to the current sample-by-sample update.
+
+ Steve also has some nice notes on echo cancellers in echo.h
+
+ References:
+
+ [1] Ochiai, Areseki, and Ogihara, "Echo Canceller with Two Echo
+ Path Models", IEEE Transactions on communications, COM-25,
+ No. 6, June
+ 1977.
+ https://www.rowetel.com/images/echo/dual_path_paper.pdf
+
+ [2] The classic, very useful paper that tells you how to
+ actually build a real world echo canceller:
+ Messerschmitt, Hedberg, Cole, Haoui, Winship, "Digital Voice
+ Echo Canceller with a TMS320020,
+ https://www.rowetel.com/images/echo/spra129.pdf
+
+ [3] I have written a series of blog posts on this work, here is
+ Part 1: http://www.rowetel.com/blog/?p=18
+
+ [4] The source code http://svn.rowetel.com/software/oslec/
+
+ [5] A nice reference on LMS filters:
+ https://en.wikipedia.org/wiki/Least_mean_squares_filter
+
+ Credits:
+
+ Thanks to Steve Underwood, Jean-Marc Valin, and Ramakrishnan
+ Muthukrishnan for their suggestions and email discussions. Thanks
+ also to those people who collected echo samples for me such as
+ Mark, Pawel, and Pavel.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "echo.h"
+
+#define MIN_TX_POWER_FOR_ADAPTION 64
+#define MIN_RX_POWER_FOR_ADAPTION 64
+#define DTD_HANGOVER 600 /* 600 samples, or 75ms */
+#define DC_LOG2BETA 3 /* log2() of DC filter Beta */
+
+/* adapting coeffs using the traditional stochastic descent (N)LMS algorithm */
+
+static inline void lms_adapt_bg(struct oslec_state *ec, int clean, int shift)
+{
+ int i;
+
+ int offset1;
+ int offset2;
+ int factor;
+ int exp;
+
+ if (shift > 0)
+ factor = clean << shift;
+ else
+ factor = clean >> -shift;
+
+ /* Update the FIR taps */
+
+ offset2 = ec->curr_pos;
+ offset1 = ec->taps - offset2;
+
+ for (i = ec->taps - 1; i >= offset1; i--) {
+ exp = (ec->fir_state_bg.history[i - offset1] * factor);
+ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15);
+ }
+ for (; i >= 0; i--) {
+ exp = (ec->fir_state_bg.history[i + offset2] * factor);
+ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15);
+ }
+}
+
+static inline int top_bit(unsigned int bits)
+{
+ if (bits == 0)
+ return -1;
+ else
+ return (int)fls((int32_t) bits) - 1;
+}
+
+struct oslec_state *oslec_create(int len, int adaption_mode)
+{
+ struct oslec_state *ec;
+ int i;
+ const int16_t *history;
+
+ ec = kzalloc(sizeof(*ec), GFP_KERNEL);
+ if (!ec)
+ return NULL;
+
+ ec->taps = len;
+ ec->log2taps = top_bit(len);
+ ec->curr_pos = ec->taps - 1;
+
+ ec->fir_taps16[0] =
+ kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL);
+ if (!ec->fir_taps16[0])
+ goto error_oom_0;
+
+ ec->fir_taps16[1] =
+ kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL);
+ if (!ec->fir_taps16[1])
+ goto error_oom_1;
+
+ history = fir16_create(&ec->fir_state, ec->fir_taps16[0], ec->taps);
+ if (!history)
+ goto error_state;
+ history = fir16_create(&ec->fir_state_bg, ec->fir_taps16[1], ec->taps);
+ if (!history)
+ goto error_state_bg;
+
+ for (i = 0; i < 5; i++)
+ ec->xvtx[i] = ec->yvtx[i] = ec->xvrx[i] = ec->yvrx[i] = 0;
+
+ ec->cng_level = 1000;
+ oslec_adaption_mode(ec, adaption_mode);
+
+ ec->snapshot = kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL);
+ if (!ec->snapshot)
+ goto error_snap;
+
+ ec->cond_met = 0;
+ ec->pstates = 0;
+ ec->ltxacc = ec->lrxacc = ec->lcleanacc = ec->lclean_bgacc = 0;
+ ec->ltx = ec->lrx = ec->lclean = ec->lclean_bg = 0;
+ ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0;
+ ec->lbgn = ec->lbgn_acc = 0;
+ ec->lbgn_upper = 200;
+ ec->lbgn_upper_acc = ec->lbgn_upper << 13;
+
+ return ec;
+
+error_snap:
+ fir16_free(&ec->fir_state_bg);
+error_state_bg:
+ fir16_free(&ec->fir_state);
+error_state:
+ kfree(ec->fir_taps16[1]);
+error_oom_1:
+ kfree(ec->fir_taps16[0]);
+error_oom_0:
+ kfree(ec);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(oslec_create);
+
+void oslec_free(struct oslec_state *ec)
+{
+ int i;
+
+ fir16_free(&ec->fir_state);
+ fir16_free(&ec->fir_state_bg);
+ for (i = 0; i < 2; i++)
+ kfree(ec->fir_taps16[i]);
+ kfree(ec->snapshot);
+ kfree(ec);
+}
+EXPORT_SYMBOL_GPL(oslec_free);
+
+void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode)
+{
+ ec->adaption_mode = adaption_mode;
+}
+EXPORT_SYMBOL_GPL(oslec_adaption_mode);
+
+void oslec_flush(struct oslec_state *ec)
+{
+ int i;
+
+ ec->ltxacc = ec->lrxacc = ec->lcleanacc = ec->lclean_bgacc = 0;
+ ec->ltx = ec->lrx = ec->lclean = ec->lclean_bg = 0;
+ ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0;
+
+ ec->lbgn = ec->lbgn_acc = 0;
+ ec->lbgn_upper = 200;
+ ec->lbgn_upper_acc = ec->lbgn_upper << 13;
+
+ ec->nonupdate_dwell = 0;
+
+ fir16_flush(&ec->fir_state);
+ fir16_flush(&ec->fir_state_bg);
+ ec->fir_state.curr_pos = ec->taps - 1;
+ ec->fir_state_bg.curr_pos = ec->taps - 1;
+ for (i = 0; i < 2; i++)
+ memset(ec->fir_taps16[i], 0, ec->taps * sizeof(int16_t));
+
+ ec->curr_pos = ec->taps - 1;
+ ec->pstates = 0;
+}
+EXPORT_SYMBOL_GPL(oslec_flush);
+
+void oslec_snapshot(struct oslec_state *ec)
+{
+ memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps * sizeof(int16_t));
+}
+EXPORT_SYMBOL_GPL(oslec_snapshot);
+
+/* Dual Path Echo Canceller */
+
+int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx)
+{
+ int32_t echo_value;
+ int clean_bg;
+ int tmp;
+ int tmp1;
+
+ /*
+ * Input scaling was found be required to prevent problems when tx
+ * starts clipping. Another possible way to handle this would be the
+ * filter coefficent scaling.
+ */
+
+ ec->tx = tx;
+ ec->rx = rx;
+ tx >>= 1;
+ rx >>= 1;
+
+ /*
+ * Filter DC, 3dB point is 160Hz (I think), note 32 bit precision
+ * required otherwise values do not track down to 0. Zero at DC, Pole
+ * at (1-Beta) on real axis. Some chip sets (like Si labs) don't
+ * need this, but something like a $10 X100P card does. Any DC really
+ * slows down convergence.
+ *
+ * Note: removes some low frequency from the signal, this reduces the
+ * speech quality when listening to samples through headphones but may
+ * not be obvious through a telephone handset.
+ *
+ * Note that the 3dB frequency in radians is approx Beta, e.g. for Beta
+ * = 2^(-3) = 0.125, 3dB freq is 0.125 rads = 159Hz.
+ */
+
+ if (ec->adaption_mode & ECHO_CAN_USE_RX_HPF) {
+ tmp = rx << 15;
+
+ /*
+ * Make sure the gain of the HPF is 1.0. This can still
+ * saturate a little under impulse conditions, and it might
+ * roll to 32768 and need clipping on sustained peak level
+ * signals. However, the scale of such clipping is small, and
+ * the error due to any saturation should not markedly affect
+ * the downstream processing.
+ */
+ tmp -= (tmp >> 4);
+
+ ec->rx_1 += -(ec->rx_1 >> DC_LOG2BETA) + tmp - ec->rx_2;
+
+ /*
+ * hard limit filter to prevent clipping. Note that at this
+ * stage rx should be limited to +/- 16383 due to right shift
+ * above
+ */
+ tmp1 = ec->rx_1 >> 15;
+ if (tmp1 > 16383)
+ tmp1 = 16383;
+ if (tmp1 < -16383)
+ tmp1 = -16383;
+ rx = tmp1;
+ ec->rx_2 = tmp;
+ }
+
+ /* Block average of power in the filter states. Used for
+ adaption power calculation. */
+
+ {
+ int new, old;
+
+ /* efficient "out with the old and in with the new" algorithm so
+ we don't have to recalculate over the whole block of
+ samples. */
+ new = (int)tx * (int)tx;
+ old = (int)ec->fir_state.history[ec->fir_state.curr_pos] *
+ (int)ec->fir_state.history[ec->fir_state.curr_pos];
+ ec->pstates +=
+ ((new - old) + (1 << (ec->log2taps - 1))) >> ec->log2taps;
+ if (ec->pstates < 0)
+ ec->pstates = 0;
+ }
+
+ /* Calculate short term average levels using simple single pole IIRs */
+
+ ec->ltxacc += abs(tx) - ec->ltx;
+ ec->ltx = (ec->ltxacc + (1 << 4)) >> 5;
+ ec->lrxacc += abs(rx) - ec->lrx;
+ ec->lrx = (ec->lrxacc + (1 << 4)) >> 5;
+
+ /* Foreground filter */
+
+ ec->fir_state.coeffs = ec->fir_taps16[0];
+ echo_value = fir16(&ec->fir_state, tx);
+ ec->clean = rx - echo_value;
+ ec->lcleanacc += abs(ec->clean) - ec->lclean;
+ ec->lclean = (ec->lcleanacc + (1 << 4)) >> 5;
+
+ /* Background filter */
+
+ echo_value = fir16(&ec->fir_state_bg, tx);
+ clean_bg = rx - echo_value;
+ ec->lclean_bgacc += abs(clean_bg) - ec->lclean_bg;
+ ec->lclean_bg = (ec->lclean_bgacc + (1 << 4)) >> 5;
+
+ /* Background Filter adaption */
+
+ /* Almost always adap bg filter, just simple DT and energy
+ detection to minimise adaption in cases of strong double talk.
+ However this is not critical for the dual path algorithm.
+ */
+ ec->factor = 0;
+ ec->shift = 0;
+ if (!ec->nonupdate_dwell) {
+ int p, logp, shift;
+
+ /* Determine:
+
+ f = Beta * clean_bg_rx/P ------ (1)
+
+ where P is the total power in the filter states.
+
+ The Boffins have shown that if we obey (1) we converge
+ quickly and avoid instability.
+
+ The correct factor f must be in Q30, as this is the fixed
+ point format required by the lms_adapt_bg() function,
+ therefore the scaled version of (1) is:
+
+ (2^30) * f = (2^30) * Beta * clean_bg_rx/P
+ factor = (2^30) * Beta * clean_bg_rx/P ----- (2)
+
+ We have chosen Beta = 0.25 by experiment, so:
+
+ factor = (2^30) * (2^-2) * clean_bg_rx/P
+
+ (30 - 2 - log2(P))
+ factor = clean_bg_rx 2 ----- (3)
+
+ To avoid a divide we approximate log2(P) as top_bit(P),
+ which returns the position of the highest non-zero bit in
+ P. This approximation introduces an error as large as a
+ factor of 2, but the algorithm seems to handle it OK.
+
+ Come to think of it a divide may not be a big deal on a
+ modern DSP, so its probably worth checking out the cycles
+ for a divide versus a top_bit() implementation.
+ */
+
+ p = MIN_TX_POWER_FOR_ADAPTION + ec->pstates;
+ logp = top_bit(p) + ec->log2taps;
+ shift = 30 - 2 - logp;
+ ec->shift = shift;
+
+ lms_adapt_bg(ec, clean_bg, shift);
+ }
+
+ /* very simple DTD to make sure we dont try and adapt with strong
+ near end speech */
+
+ ec->adapt = 0;
+ if ((ec->lrx > MIN_RX_POWER_FOR_ADAPTION) && (ec->lrx > ec->ltx))
+ ec->nonupdate_dwell = DTD_HANGOVER;
+ if (ec->nonupdate_dwell)
+ ec->nonupdate_dwell--;
+
+ /* Transfer logic */
+
+ /* These conditions are from the dual path paper [1], I messed with
+ them a bit to improve performance. */
+
+ if ((ec->adaption_mode & ECHO_CAN_USE_ADAPTION) &&
+ (ec->nonupdate_dwell == 0) &&
+ /* (ec->Lclean_bg < 0.875*ec->Lclean) */
+ (8 * ec->lclean_bg < 7 * ec->lclean) &&
+ /* (ec->Lclean_bg < 0.125*ec->Ltx) */
+ (8 * ec->lclean_bg < ec->ltx)) {
+ if (ec->cond_met == 6) {
+ /*
+ * BG filter has had better results for 6 consecutive
+ * samples
+ */
+ ec->adapt = 1;
+ memcpy(ec->fir_taps16[0], ec->fir_taps16[1],
+ ec->taps * sizeof(int16_t));
+ } else
+ ec->cond_met++;
+ } else
+ ec->cond_met = 0;
+
+ /* Non-Linear Processing */
+
+ ec->clean_nlp = ec->clean;
+ if (ec->adaption_mode & ECHO_CAN_USE_NLP) {
+ /*
+ * Non-linear processor - a fancy way to say "zap small
+ * signals, to avoid residual echo due to (uLaw/ALaw)
+ * non-linearity in the channel.".
+ */
+
+ if ((16 * ec->lclean < ec->ltx)) {
+ /*
+ * Our e/c has improved echo by at least 24 dB (each
+ * factor of 2 is 6dB, so 2*2*2*2=16 is the same as
+ * 6+6+6+6=24dB)
+ */
+ if (ec->adaption_mode & ECHO_CAN_USE_CNG) {
+ ec->cng_level = ec->lbgn;
+
+ /*
+ * Very elementary comfort noise generation.
+ * Just random numbers rolled off very vaguely
+ * Hoth-like. DR: This noise doesn't sound
+ * quite right to me - I suspect there are some
+ * overflow issues in the filtering as it's too
+ * "crackly".
+ * TODO: debug this, maybe just play noise at
+ * high level or look at spectrum.
+ */
+
+ ec->cng_rndnum =
+ 1664525U * ec->cng_rndnum + 1013904223U;
+ ec->cng_filter =
+ ((ec->cng_rndnum & 0xFFFF) - 32768 +
+ 5 * ec->cng_filter) >> 3;
+ ec->clean_nlp =
+ (ec->cng_filter * ec->cng_level * 8) >> 14;
+
+ } else if (ec->adaption_mode & ECHO_CAN_USE_CLIP) {
+ /* This sounds much better than CNG */
+ if (ec->clean_nlp > ec->lbgn)
+ ec->clean_nlp = ec->lbgn;
+ if (ec->clean_nlp < -ec->lbgn)
+ ec->clean_nlp = -ec->lbgn;
+ } else {
+ /*
+ * just mute the residual, doesn't sound very
+ * good, used mainly in G168 tests
+ */
+ ec->clean_nlp = 0;
+ }
+ } else {
+ /*
+ * Background noise estimator. I tried a few
+ * algorithms here without much luck. This very simple
+ * one seems to work best, we just average the level
+ * using a slow (1 sec time const) filter if the
+ * current level is less than a (experimentally
+ * derived) constant. This means we dont include high
+ * level signals like near end speech. When combined
+ * with CNG or especially CLIP seems to work OK.
+ */
+ if (ec->lclean < 40) {
+ ec->lbgn_acc += abs(ec->clean) - ec->lbgn;
+ ec->lbgn = (ec->lbgn_acc + (1 << 11)) >> 12;
+ }
+ }
+ }
+
+ /* Roll around the taps buffer */
+ if (ec->curr_pos <= 0)
+ ec->curr_pos = ec->taps;
+ ec->curr_pos--;
+
+ if (ec->adaption_mode & ECHO_CAN_DISABLE)
+ ec->clean_nlp = rx;
+
+ /* Output scaled back up again to match input scaling */
+
+ return (int16_t) ec->clean_nlp << 1;
+}
+EXPORT_SYMBOL_GPL(oslec_update);
+
+/* This function is separated from the echo canceller is it is usually called
+ as part of the tx process. See rx HP (DC blocking) filter above, it's
+ the same design.
+
+ Some soft phones send speech signals with a lot of low frequency
+ energy, e.g. down to 20Hz. This can make the hybrid non-linear
+ which causes the echo canceller to fall over. This filter can help
+ by removing any low frequency before it gets to the tx port of the
+ hybrid.
+
+ It can also help by removing and DC in the tx signal. DC is bad
+ for LMS algorithms.
+
+ This is one of the classic DC removal filters, adjusted to provide
+ sufficient bass rolloff to meet the above requirement to protect hybrids
+ from things that upset them. The difference between successive samples
+ produces a lousy HPF, and then a suitably placed pole flattens things out.
+ The final result is a nicely rolled off bass end. The filtering is
+ implemented with extended fractional precision, which noise shapes things,
+ giving very clean DC removal.
+*/
+
+int16_t oslec_hpf_tx(struct oslec_state *ec, int16_t tx)
+{
+ int tmp;
+ int tmp1;
+
+ if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF) {
+ tmp = tx << 15;
+
+ /*
+ * Make sure the gain of the HPF is 1.0. The first can still
+ * saturate a little under impulse conditions, and it might
+ * roll to 32768 and need clipping on sustained peak level
+ * signals. However, the scale of such clipping is small, and
+ * the error due to any saturation should not markedly affect
+ * the downstream processing.
+ */
+ tmp -= (tmp >> 4);
+
+ ec->tx_1 += -(ec->tx_1 >> DC_LOG2BETA) + tmp - ec->tx_2;
+ tmp1 = ec->tx_1 >> 15;
+ if (tmp1 > 32767)
+ tmp1 = 32767;
+ if (tmp1 < -32767)
+ tmp1 = -32767;
+ tx = tmp1;
+ ec->tx_2 = tmp;
+ }
+
+ return tx;
+}
+EXPORT_SYMBOL_GPL(oslec_hpf_tx);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Rowe");
+MODULE_DESCRIPTION("Open Source Line Echo Canceller");
+MODULE_VERSION("0.3.0");
diff --git a/drivers/misc/echo/echo.h b/drivers/misc/echo/echo.h
new file mode 100644
index 000000000..56b4b95fd
--- /dev/null
+++ b/drivers/misc/echo/echo.h
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * echo.c - A line echo canceller. This code is being developed
+ * against and partially complies with G168.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ * and David Rowe <david_at_rowetel_dot_com>
+ *
+ * Copyright (C) 2001 Steve Underwood and 2007 David Rowe
+ *
+ * All rights reserved.
+ */
+
+#ifndef __ECHO_H
+#define __ECHO_H
+
+/*
+Line echo cancellation for voice
+
+What does it do?
+
+This module aims to provide G.168-2002 compliant echo cancellation, to remove
+electrical echoes (e.g. from 2-4 wire hybrids) from voice calls.
+
+How does it work?
+
+The heart of the echo cancellor is FIR filter. This is adapted to match the
+echo impulse response of the telephone line. It must be long enough to
+adequately cover the duration of that impulse response. The signal transmitted
+to the telephone line is passed through the FIR filter. Once the FIR is
+properly adapted, the resulting output is an estimate of the echo signal
+received from the line. This is subtracted from the received signal. The result
+is an estimate of the signal which originated at the far end of the line, free
+from echos of our own transmitted signal.
+
+The least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and
+was introduced in 1960. It is the commonest form of filter adaption used in
+things like modem line equalisers and line echo cancellers. There it works very
+well. However, it only works well for signals of constant amplitude. It works
+very poorly for things like speech echo cancellation, where the signal level
+varies widely. This is quite easy to fix. If the signal level is normalised -
+similar to applying AGC - LMS can work as well for a signal of varying
+amplitude as it does for a modem signal. This normalised least mean squares
+(NLMS) algorithm is the commonest one used for speech echo cancellation. Many
+other algorithms exist - e.g. RLS (essentially the same as Kalman filtering),
+FAP, etc. Some perform significantly better than NLMS. However, factors such
+as computational complexity and patents favour the use of NLMS.
+
+A simple refinement to NLMS can improve its performance with speech. NLMS tends
+to adapt best to the strongest parts of a signal. If the signal is white noise,
+the NLMS algorithm works very well. However, speech has more low frequency than
+high frequency content. Pre-whitening (i.e. filtering the signal to flatten its
+spectrum) the echo signal improves the adapt rate for speech, and ensures the
+final residual signal is not heavily biased towards high frequencies. A very
+low complexity filter is adequate for this, so pre-whitening adds little to the
+compute requirements of the echo canceller.
+
+An FIR filter adapted using pre-whitened NLMS performs well, provided certain
+conditions are met:
+
+ - The transmitted signal has poor self-correlation.
+ - There is no signal being generated within the environment being
+ cancelled.
+
+The difficulty is that neither of these can be guaranteed.
+
+If the adaption is performed while transmitting noise (or something fairly
+noise like, such as voice) the adaption works very well. If the adaption is
+performed while transmitting something highly correlative (typically narrow
+band energy such as signalling tones or DTMF), the adaption can go seriously
+wrong. The reason is there is only one solution for the adaption on a near
+random signal - the impulse response of the line. For a repetitive signal,
+there are any number of solutions which converge the adaption, and nothing
+guides the adaption to choose the generalised one. Allowing an untrained
+canceller to converge on this kind of narrowband energy probably a good thing,
+since at least it cancels the tones. Allowing a well converged canceller to
+continue converging on such energy is just a way to ruin its generalised
+adaption. A narrowband detector is needed, so adapation can be suspended at
+appropriate times.
+
+The adaption process is based on trying to eliminate the received signal. When
+there is any signal from within the environment being cancelled it may upset
+the adaption process. Similarly, if the signal we are transmitting is small,
+noise may dominate and disturb the adaption process. If we can ensure that the
+adaption is only performed when we are transmitting a significant signal level,
+and the environment is not, things will be OK. Clearly, it is easy to tell when
+we are sending a significant signal. Telling, if the environment is generating
+a significant signal, and doing it with sufficient speed that the adaption will
+not have diverged too much more we stop it, is a little harder.
+
+The key problem in detecting when the environment is sourcing significant
+energy is that we must do this very quickly. Given a reasonably long sample of
+the received signal, there are a number of strategies which may be used to
+assess whether that signal contains a strong far end component. However, by the
+time that assessment is complete the far end signal will have already caused
+major mis-convergence in the adaption process. An assessment algorithm is
+needed which produces a fairly accurate result from a very short burst of far
+end energy.
+
+How do I use it?
+
+The echo cancellor processes both the transmit and receive streams sample by
+sample. The processing function is not declared inline. Unfortunately,
+cancellation requires many operations per sample, so the call overhead is only
+a minor burden.
+*/
+
+#include "fir.h"
+#include "oslec.h"
+
+/*
+ G.168 echo canceller descriptor. This defines the working state for a line
+ echo canceller.
+*/
+struct oslec_state {
+ int16_t tx;
+ int16_t rx;
+ int16_t clean;
+ int16_t clean_nlp;
+
+ int nonupdate_dwell;
+ int curr_pos;
+ int taps;
+ int log2taps;
+ int adaption_mode;
+
+ int cond_met;
+ int32_t pstates;
+ int16_t adapt;
+ int32_t factor;
+ int16_t shift;
+
+ /* Average levels and averaging filter states */
+ int ltxacc;
+ int lrxacc;
+ int lcleanacc;
+ int lclean_bgacc;
+ int ltx;
+ int lrx;
+ int lclean;
+ int lclean_bg;
+ int lbgn;
+ int lbgn_acc;
+ int lbgn_upper;
+ int lbgn_upper_acc;
+
+ /* foreground and background filter states */
+ struct fir16_state_t fir_state;
+ struct fir16_state_t fir_state_bg;
+ int16_t *fir_taps16[2];
+
+ /* DC blocking filter states */
+ int tx_1;
+ int tx_2;
+ int rx_1;
+ int rx_2;
+
+ /* optional High Pass Filter states */
+ int32_t xvtx[5];
+ int32_t yvtx[5];
+ int32_t xvrx[5];
+ int32_t yvrx[5];
+
+ /* Parameters for the optional Hoth noise generator */
+ int cng_level;
+ int cng_rndnum;
+ int cng_filter;
+
+ /* snapshot sample of coeffs used for development */
+ int16_t *snapshot;
+};
+
+#endif /* __ECHO_H */
diff --git a/drivers/misc/echo/fir.h b/drivers/misc/echo/fir.h
new file mode 100644
index 000000000..4d0821025
--- /dev/null
+++ b/drivers/misc/echo/fir.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * fir.h - General telephony FIR routines
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2002 Steve Underwood
+ *
+ * All rights reserved.
+ */
+
+#if !defined(_FIR_H_)
+#define _FIR_H_
+
+/*
+ Ideas for improvement:
+
+ 1/ Rewrite filter for dual MAC inner loop. The issue here is handling
+ history sample offsets that are 16 bit aligned - the dual MAC needs
+ 32 bit aligmnent. There are some good examples in libbfdsp.
+
+ 2/ Use the hardware circular buffer facility tohalve memory usage.
+
+ 3/ Consider using internal memory.
+
+ Using less memory might also improve speed as cache misses will be
+ reduced. A drop in MIPs and memory approaching 50% should be
+ possible.
+
+ The foreground and background filters currenlty use a total of
+ about 10 MIPs/ch as measured with speedtest.c on a 256 TAP echo
+ can.
+*/
+
+/*
+ * 16 bit integer FIR descriptor. This defines the working state for a single
+ * instance of an FIR filter using 16 bit integer coefficients.
+ */
+struct fir16_state_t {
+ int taps;
+ int curr_pos;
+ const int16_t *coeffs;
+ int16_t *history;
+};
+
+/*
+ * 32 bit integer FIR descriptor. This defines the working state for a single
+ * instance of an FIR filter using 32 bit integer coefficients, and filtering
+ * 16 bit integer data.
+ */
+struct fir32_state_t {
+ int taps;
+ int curr_pos;
+ const int32_t *coeffs;
+ int16_t *history;
+};
+
+/*
+ * Floating point FIR descriptor. This defines the working state for a single
+ * instance of an FIR filter using floating point coefficients and data.
+ */
+struct fir_float_state_t {
+ int taps;
+ int curr_pos;
+ const float *coeffs;
+ float *history;
+};
+
+static inline const int16_t *fir16_create(struct fir16_state_t *fir,
+ const int16_t *coeffs, int taps)
+{
+ fir->taps = taps;
+ fir->curr_pos = taps - 1;
+ fir->coeffs = coeffs;
+ fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL);
+ return fir->history;
+}
+
+static inline void fir16_flush(struct fir16_state_t *fir)
+{
+ memset(fir->history, 0, fir->taps * sizeof(int16_t));
+}
+
+static inline void fir16_free(struct fir16_state_t *fir)
+{
+ kfree(fir->history);
+}
+
+static inline int16_t fir16(struct fir16_state_t *fir, int16_t sample)
+{
+ int32_t y;
+ int i;
+ int offset1;
+ int offset2;
+
+ fir->history[fir->curr_pos] = sample;
+
+ offset2 = fir->curr_pos;
+ offset1 = fir->taps - offset2;
+ y = 0;
+ for (i = fir->taps - 1; i >= offset1; i--)
+ y += fir->coeffs[i] * fir->history[i - offset1];
+ for (; i >= 0; i--)
+ y += fir->coeffs[i] * fir->history[i + offset2];
+ if (fir->curr_pos <= 0)
+ fir->curr_pos = fir->taps;
+ fir->curr_pos--;
+ return (int16_t) (y >> 15);
+}
+
+static inline const int16_t *fir32_create(struct fir32_state_t *fir,
+ const int32_t *coeffs, int taps)
+{
+ fir->taps = taps;
+ fir->curr_pos = taps - 1;
+ fir->coeffs = coeffs;
+ fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL);
+ return fir->history;
+}
+
+static inline void fir32_flush(struct fir32_state_t *fir)
+{
+ memset(fir->history, 0, fir->taps * sizeof(int16_t));
+}
+
+static inline void fir32_free(struct fir32_state_t *fir)
+{
+ kfree(fir->history);
+}
+
+static inline int16_t fir32(struct fir32_state_t *fir, int16_t sample)
+{
+ int i;
+ int32_t y;
+ int offset1;
+ int offset2;
+
+ fir->history[fir->curr_pos] = sample;
+ offset2 = fir->curr_pos;
+ offset1 = fir->taps - offset2;
+ y = 0;
+ for (i = fir->taps - 1; i >= offset1; i--)
+ y += fir->coeffs[i] * fir->history[i - offset1];
+ for (; i >= 0; i--)
+ y += fir->coeffs[i] * fir->history[i + offset2];
+ if (fir->curr_pos <= 0)
+ fir->curr_pos = fir->taps;
+ fir->curr_pos--;
+ return (int16_t) (y >> 15);
+}
+
+#endif
diff --git a/drivers/misc/echo/oslec.h b/drivers/misc/echo/oslec.h
new file mode 100644
index 000000000..f1adac143
--- /dev/null
+++ b/drivers/misc/echo/oslec.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * OSLEC - A line echo canceller. This code is being developed
+ * against and partially complies with G168. Using code from SpanDSP
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ * and David Rowe <david_at_rowetel_dot_com>
+ *
+ * Copyright (C) 2001 Steve Underwood and 2007-2008 David Rowe
+ *
+ * All rights reserved.
+ */
+
+#ifndef __OSLEC_H
+#define __OSLEC_H
+
+/* Mask bits for the adaption mode */
+#define ECHO_CAN_USE_ADAPTION 0x01
+#define ECHO_CAN_USE_NLP 0x02
+#define ECHO_CAN_USE_CNG 0x04
+#define ECHO_CAN_USE_CLIP 0x08
+#define ECHO_CAN_USE_TX_HPF 0x10
+#define ECHO_CAN_USE_RX_HPF 0x20
+#define ECHO_CAN_DISABLE 0x40
+
+/**
+ * oslec_state: G.168 echo canceller descriptor.
+ *
+ * This defines the working state for a line echo canceller.
+ */
+struct oslec_state;
+
+/**
+ * oslec_create - Create a voice echo canceller context.
+ * @len: The length of the canceller, in samples.
+ * @return: The new canceller context, or NULL if the canceller could not be
+ * created.
+ */
+struct oslec_state *oslec_create(int len, int adaption_mode);
+
+/**
+ * oslec_free - Free a voice echo canceller context.
+ * @ec: The echo canceller context.
+ */
+void oslec_free(struct oslec_state *ec);
+
+/**
+ * oslec_flush - Flush (reinitialise) a voice echo canceller context.
+ * @ec: The echo canceller context.
+ */
+void oslec_flush(struct oslec_state *ec);
+
+/**
+ * oslec_adaption_mode - set the adaption mode of a voice echo canceller context.
+ * @ec The echo canceller context.
+ * @adaption_mode: The mode.
+ */
+void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode);
+
+void oslec_snapshot(struct oslec_state *ec);
+
+/**
+ * oslec_update: Process a sample through a voice echo canceller.
+ * @ec: The echo canceller context.
+ * @tx: The transmitted audio sample.
+ * @rx: The received audio sample.
+ *
+ * The return value is the clean (echo cancelled) received sample.
+ */
+int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx);
+
+/**
+ * oslec_hpf_tx: Process to high pass filter the tx signal.
+ * @ec: The echo canceller context.
+ * @tx: The transmitted auio sample.
+ *
+ * The return value is the HP filtered transmit sample, send this to your D/A.
+ */
+int16_t oslec_hpf_tx(struct oslec_state *ec, int16_t tx);
+
+#endif /* __OSLEC_H */