mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-03 11:11:05 +00:00
2501 lines
69 KiB
Diff
2501 lines
69 KiB
Diff
![]() |
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/arith.h mISDN/drivers/isdn/hardware/mISDN/arith.h
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/arith.h 1970-01-01 01:00:00.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/arith.h 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -0,0 +1,347 @@
|
||
|
+#ifndef _ZAPTEL_ARITH_H
|
||
|
+#define _ZAPTEL_ARITH_H
|
||
|
+/*
|
||
|
+ * Handy add/subtract functions to operate on chunks of shorts.
|
||
|
+ * Feel free to add customizations for additional architectures
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+#ifdef CONFIG_ZAPTEL_MMX
|
||
|
+#ifdef ZT_CHUNKSIZE
|
||
|
+static inline void __ACSS(volatile short *dst, const short *src)
|
||
|
+{
|
||
|
+ __asm__ __volatile__ (
|
||
|
+ "movq 0(%0), %%mm0;\n"
|
||
|
+ "movq 0(%1), %%mm1;\n"
|
||
|
+ "movq 8(%0), %%mm2;\n"
|
||
|
+ "movq 8(%1), %%mm3;\n"
|
||
|
+ "paddsw %%mm1, %%mm0;\n"
|
||
|
+ "paddsw %%mm3, %%mm2;\n"
|
||
|
+ "movq %%mm0, 0(%0);\n"
|
||
|
+ "movq %%mm2, 8(%0);\n"
|
||
|
+ : "=r" (dst)
|
||
|
+ : "r" (src), "0" (dst)
|
||
|
+ : "memory"
|
||
|
+#if CLOBBERMMX
|
||
|
+ , "%mm0", "%mm1", "%mm2", "%mm3"
|
||
|
+#endif
|
||
|
+ );
|
||
|
+
|
||
|
+}
|
||
|
+static inline void __SCSS(volatile short *dst, const short *src)
|
||
|
+{
|
||
|
+ __asm__ __volatile__ (
|
||
|
+ "movq 0(%0), %%mm0;\n"
|
||
|
+ "movq 0(%1), %%mm1;\n"
|
||
|
+ "movq 8(%0), %%mm2;\n"
|
||
|
+ "movq 8(%1), %%mm3;\n"
|
||
|
+ "psubsw %%mm1, %%mm0;\n"
|
||
|
+ "psubsw %%mm3, %%mm2;\n"
|
||
|
+ "movq %%mm0, 0(%0);\n"
|
||
|
+ "movq %%mm2, 8(%0);\n"
|
||
|
+ : "=r" (dst)
|
||
|
+ : "r" (src), "0" (dst)
|
||
|
+ : "memory"
|
||
|
+#if CLOBBERMMX
|
||
|
+ , "%mm0", "%mm1", "%mm2", "%mm3"
|
||
|
+#endif
|
||
|
+ );
|
||
|
+
|
||
|
+}
|
||
|
+
|
||
|
+#if (ZT_CHUNKSIZE == 8)
|
||
|
+#define ACSS(a,b) __ACSS(a,b)
|
||
|
+#define SCSS(a,b) __SCSS(a,b)
|
||
|
+#elif (ZT_CHUNKSIZE > 8)
|
||
|
+static inline void ACSS(volatile short *dst, const short *src)
|
||
|
+{
|
||
|
+ int x;
|
||
|
+ for (x=0;x<ZT_CHUNKSIZE;x+=8)
|
||
|
+ __ACSS(dst + x, src + x);
|
||
|
+}
|
||
|
+static inline void SCSS(volatile short *dst, const short *src)
|
||
|
+{
|
||
|
+ int x;
|
||
|
+ for (x=0;x<ZT_CHUNKSIZE;x+=8)
|
||
|
+ __SCSS(dst + x, src + x);
|
||
|
+}
|
||
|
+#else
|
||
|
+#error No MMX for ZT_CHUNKSIZE < 8
|
||
|
+#endif
|
||
|
+#endif
|
||
|
+static inline int CONVOLVE(const int *coeffs, const short *hist, int len)
|
||
|
+{
|
||
|
+ int sum;
|
||
|
+ /* Divide length by 16 */
|
||
|
+ len >>= 4;
|
||
|
+
|
||
|
+ /* Clear our accumulator, mm4 */
|
||
|
+
|
||
|
+ /*
|
||
|
+
|
||
|
+ For every set of eight...
|
||
|
+
|
||
|
+ Load 16 coefficients into four registers...
|
||
|
+ Shift each word right 16 to make them shorts...
|
||
|
+ Pack the resulting shorts into two registers...
|
||
|
+ With the coefficients now in mm0 and mm2, load the
|
||
|
+ history into mm1 and mm3...
|
||
|
+ Multiply/add mm1 into mm0, and mm3 into mm2...
|
||
|
+ Add mm2 into mm0 (without saturation, alas). Now we have two half-results.
|
||
|
+ Accumulate in mm4 (again, without saturation, alas)
|
||
|
+ */
|
||
|
+ __asm__ (
|
||
|
+ "pxor %%mm4, %%mm4;\n"
|
||
|
+ "mov %1, %%edi;\n"
|
||
|
+ "mov %2, %%esi;\n"
|
||
|
+ "mov %3, %%ecx;\n"
|
||
|
+ "1:"
|
||
|
+ "movq 0(%%edi), %%mm0;\n"
|
||
|
+ "movq 8(%%edi), %%mm1;\n"
|
||
|
+ "movq 16(%%edi), %%mm2;\n"
|
||
|
+ "movq 24(%%edi), %%mm3;\n"
|
||
|
+ /* can't use 4/5 since 4 is the accumulator for us */
|
||
|
+ "movq 32(%%edi), %%mm6;\n"
|
||
|
+ "movq 40(%%edi), %%mm7;\n"
|
||
|
+ "psrad $16, %%mm0;\n"
|
||
|
+ "psrad $16, %%mm1;\n"
|
||
|
+ "psrad $16, %%mm2;\n"
|
||
|
+ "psrad $16, %%mm3;\n"
|
||
|
+ "psrad $16, %%mm6;\n"
|
||
|
+ "psrad $16, %%mm7;\n"
|
||
|
+ "packssdw %%mm1, %%mm0;\n"
|
||
|
+ "packssdw %%mm3, %%mm2;\n"
|
||
|
+ "packssdw %%mm7, %%mm6;\n"
|
||
|
+ "movq 0(%%esi), %%mm1;\n"
|
||
|
+ "movq 8(%%esi), %%mm3;\n"
|
||
|
+ "movq 16(%%esi), %%mm7;\n"
|
||
|
+ "pmaddwd %%mm1, %%mm0;\n"
|
||
|
+ "pmaddwd %%mm3, %%mm2;\n"
|
||
|
+ "pmaddwd %%mm7, %%mm6;\n"
|
||
|
+ "paddd %%mm6, %%mm4;\n"
|
||
|
+ "paddd %%mm2, %%mm4;\n"
|
||
|
+ "paddd %%mm0, %%mm4;\n"
|
||
|
+ /* Come back and do for the last few bytes */
|
||
|
+ "movq 48(%%edi), %%mm6;\n"
|
||
|
+ "movq 56(%%edi), %%mm7;\n"
|
||
|
+ "psrad $16, %%mm6;\n"
|
||
|
+ "psrad $16, %%mm7;\n"
|
||
|
+ "packssdw %%mm7, %%mm6;\n"
|
||
|
+ "movq 24(%%esi), %%mm7;\n"
|
||
|
+ "pmaddwd %%mm7, %%mm6;\n"
|
||
|
+ "paddd %%mm6, %%mm4;\n"
|
||
|
+ "add $64, %%edi;\n"
|
||
|
+ "add $32, %%esi;\n"
|
||
|
+ "dec %%ecx;\n"
|
||
|
+ "jnz 1b;\n"
|
||
|
+ "movq %%mm4, %%mm0;\n"
|
||
|
+ "psrlq $32, %%mm0;\n"
|
||
|
+ "paddd %%mm0, %%mm4;\n"
|
||
|
+ "movd %%mm4, %0;\n"
|
||
|
+ : "=r" (sum)
|
||
|
+ : "r" (coeffs), "r" (hist), "r" (len)
|
||
|
+ : "%ecx", "%edi", "%esi"
|
||
|
+ );
|
||
|
+
|
||
|
+ return sum;
|
||
|
+}
|
||
|
+
|
||
|
+static inline void UPDATE(volatile int *taps, const short *history, const int nsuppr, const int ntaps)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+ int correction;
|
||
|
+ for (i=0;i<ntaps;i++) {
|
||
|
+ correction = history[i] * nsuppr;
|
||
|
+ taps[i] += correction;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static inline void UPDATE2(volatile int *taps, volatile short *taps_short, const short *history, const int nsuppr, const int ntaps)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+ int correction;
|
||
|
+#if 0
|
||
|
+ ntaps >>= 4;
|
||
|
+ /* First, load up taps, */
|
||
|
+ __asm__ (
|
||
|
+ "pxor %%mm4, %%mm4;\n"
|
||
|
+ "mov %0, %%edi;\n"
|
||
|
+ "mov %1, %%esi;\n"
|
||
|
+ "mov %3, %%ecx;\n"
|
||
|
+ "1:"
|
||
|
+ "jnz 1b;\n"
|
||
|
+ "movq %%mm4, %%mm0;\n"
|
||
|
+ "psrlq $32, %%mm0;\n"
|
||
|
+ "paddd %%mm0, %%mm4;\n"
|
||
|
+ "movd %%mm4, %0;\n"
|
||
|
+ : "=r" (taps), "=r" (taps_short)
|
||
|
+ : "r" (history), "r" (nsuppr), "r" (ntaps), "0" (taps)
|
||
|
+ : "%ecx", "%edi", "%esi"
|
||
|
+ );
|
||
|
+#endif
|
||
|
+#if 1
|
||
|
+ for (i=0;i<ntaps;i++) {
|
||
|
+ correction = history[i] * nsuppr;
|
||
|
+ taps[i] += correction;
|
||
|
+ taps_short[i] = taps[i] >> 16;
|
||
|
+ }
|
||
|
+#endif
|
||
|
+}
|
||
|
+
|
||
|
+static inline int CONVOLVE2(const short *coeffs, const short *hist, int len)
|
||
|
+{
|
||
|
+ int sum;
|
||
|
+ /* Divide length by 16 */
|
||
|
+ len >>= 4;
|
||
|
+
|
||
|
+ /* Clear our accumulator, mm4 */
|
||
|
+
|
||
|
+ /*
|
||
|
+
|
||
|
+ For every set of eight...
|
||
|
+ Load in eight coefficients and eight historic samples, multliply add and
|
||
|
+ accumulate the result
|
||
|
+ */
|
||
|
+ __asm__ (
|
||
|
+ "pxor %%mm4, %%mm4;\n"
|
||
|
+ "mov %1, %%edi;\n"
|
||
|
+ "mov %2, %%esi;\n"
|
||
|
+ "mov %3, %%ecx;\n"
|
||
|
+ "1:"
|
||
|
+ "movq 0(%%edi), %%mm0;\n"
|
||
|
+ "movq 8(%%edi), %%mm2;\n"
|
||
|
+ "movq 0(%%esi), %%mm1;\n"
|
||
|
+ "movq 8(%%esi), %%mm3;\n"
|
||
|
+ "pmaddwd %%mm1, %%mm0;\n"
|
||
|
+ "pmaddwd %%mm3, %%mm2;\n"
|
||
|
+ "paddd %%mm2, %%mm4;\n"
|
||
|
+ "paddd %%mm0, %%mm4;\n"
|
||
|
+ "movq 16(%%edi), %%mm0;\n"
|
||
|
+ "movq 24(%%edi), %%mm2;\n"
|
||
|
+ "movq 16(%%esi), %%mm1;\n"
|
||
|
+ "movq 24(%%esi), %%mm3;\n"
|
||
|
+ "pmaddwd %%mm1, %%mm0;\n"
|
||
|
+ "pmaddwd %%mm3, %%mm2;\n"
|
||
|
+ "paddd %%mm2, %%mm4;\n"
|
||
|
+ "paddd %%mm0, %%mm4;\n"
|
||
|
+ "add $32, %%edi;\n"
|
||
|
+ "add $32, %%esi;\n"
|
||
|
+ "dec %%ecx;\n"
|
||
|
+ "jnz 1b;\n"
|
||
|
+ "movq %%mm4, %%mm0;\n"
|
||
|
+ "psrlq $32, %%mm0;\n"
|
||
|
+ "paddd %%mm0, %%mm4;\n"
|
||
|
+ "movd %%mm4, %0;\n"
|
||
|
+ : "=r" (sum)
|
||
|
+ : "r" (coeffs), "r" (hist), "r" (len)
|
||
|
+ : "%ecx", "%edi", "%esi"
|
||
|
+ );
|
||
|
+
|
||
|
+ return sum;
|
||
|
+}
|
||
|
+static inline short MAX16(const short *y, int len, int *pos)
|
||
|
+{
|
||
|
+ int k;
|
||
|
+ short max = 0;
|
||
|
+ int bestpos = 0;
|
||
|
+ for (k=0;k<len;k++) {
|
||
|
+ if (max < y[k]) {
|
||
|
+ bestpos = k;
|
||
|
+ max = y[k];
|
||
|
+ }
|
||
|
+ }
|
||
|
+ *pos = (len - 1 - bestpos);
|
||
|
+ return max;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+#else
|
||
|
+
|
||
|
+#ifdef ZT_CHUNKSIZE
|
||
|
+static inline void ACSS(short *dst, short *src)
|
||
|
+{
|
||
|
+ int x,sum;
|
||
|
+ /* Add src to dst with saturation, storing in dst */
|
||
|
+ for (x=0;x<ZT_CHUNKSIZE;x++) {
|
||
|
+ sum = dst[x]+src[x];
|
||
|
+ if (sum > 32767)
|
||
|
+ sum = 32767;
|
||
|
+ else if (sum < -32768)
|
||
|
+ sum = -32768;
|
||
|
+ dst[x] = sum;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static inline void SCSS(short *dst, short *src)
|
||
|
+{
|
||
|
+ int x,sum;
|
||
|
+ /* Add src to dst with saturation, storing in dst */
|
||
|
+ for (x=0;x<ZT_CHUNKSIZE;x++) {
|
||
|
+ sum = dst[x]-src[x];
|
||
|
+ if (sum > 32767)
|
||
|
+ sum = 32767;
|
||
|
+ else if (sum < -32768)
|
||
|
+ sum = -32768;
|
||
|
+ dst[x] = sum;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+#endif /* ZT_CHUNKSIZE */
|
||
|
+
|
||
|
+static inline int CONVOLVE(const int *coeffs, const short *hist, int len)
|
||
|
+{
|
||
|
+ int x;
|
||
|
+ int sum = 0;
|
||
|
+ for (x=0;x<len;x++)
|
||
|
+ sum += (coeffs[x] >> 16) * hist[x];
|
||
|
+ return sum;
|
||
|
+}
|
||
|
+
|
||
|
+static inline int CONVOLVE2(const short *coeffs, const short *hist, int len)
|
||
|
+{
|
||
|
+ int x;
|
||
|
+ int sum = 0;
|
||
|
+ for (x=0;x<len;x++)
|
||
|
+ sum += coeffs[x] * hist[x];
|
||
|
+ return sum;
|
||
|
+}
|
||
|
+
|
||
|
+static inline void UPDATE(int *taps, const short *history, const int nsuppr, const int ntaps)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+ int correction;
|
||
|
+ for (i=0;i<ntaps;i++) {
|
||
|
+ correction = history[i] * nsuppr;
|
||
|
+ taps[i] += correction;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static inline void UPDATE2(int *taps, short *taps_short, const short *history, const int nsuppr, const int ntaps)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+ int correction;
|
||
|
+ for (i=0;i<ntaps;i++) {
|
||
|
+ correction = history[i] * nsuppr;
|
||
|
+ taps[i] += correction;
|
||
|
+ taps_short[i] = taps[i] >> 16;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static inline short MAX16(const short *y, int len, int *pos)
|
||
|
+{
|
||
|
+ int k;
|
||
|
+ short max = 0;
|
||
|
+ int bestpos = 0;
|
||
|
+ for (k=0;k<len;k++) {
|
||
|
+ if (max < y[k]) {
|
||
|
+ bestpos = k;
|
||
|
+ max = y[k];
|
||
|
+ }
|
||
|
+ }
|
||
|
+ *pos = (len - 1 - bestpos);
|
||
|
+ return max;
|
||
|
+}
|
||
|
+
|
||
|
+#endif /* MMX */
|
||
|
+#endif /* _ZAPTEL_ARITH_H */
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/biquad.h mISDN/drivers/isdn/hardware/mISDN/biquad.h
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/biquad.h 1970-01-01 01:00:00.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/biquad.h 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -0,0 +1,73 @@
|
||
|
+/*
|
||
|
+ * SpanDSP - a series of DSP components for telephony
|
||
|
+ *
|
||
|
+ * biquad.h - General telephony bi-quad section routines (currently this just
|
||
|
+ * handles canonic/type 2 form)
|
||
|
+ *
|
||
|
+ * Written by Steve Underwood <steveu@coppice.org>
|
||
|
+ *
|
||
|
+ * Copyright (C) 2001 Steve Underwood
|
||
|
+ *
|
||
|
+ * All rights reserved.
|
||
|
+ *
|
||
|
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+typedef struct
|
||
|
+{
|
||
|
+ int32_t gain;
|
||
|
+ int32_t a1;
|
||
|
+ int32_t a2;
|
||
|
+ int32_t b1;
|
||
|
+ int32_t b2;
|
||
|
+
|
||
|
+ int32_t z1;
|
||
|
+ int32_t z2;
|
||
|
+} biquad2_state_t;
|
||
|
+
|
||
|
+static inline void biquad2_init (biquad2_state_t *bq,
|
||
|
+ int32_t gain,
|
||
|
+ int32_t a1,
|
||
|
+ int32_t a2,
|
||
|
+ int32_t b1,
|
||
|
+ int32_t b2)
|
||
|
+{
|
||
|
+ bq->gain = gain;
|
||
|
+ bq->a1 = a1;
|
||
|
+ bq->a2 = a2;
|
||
|
+ bq->b1 = b1;
|
||
|
+ bq->b2 = b2;
|
||
|
+
|
||
|
+ bq->z1 = 0;
|
||
|
+ bq->z2 = 0;
|
||
|
+}
|
||
|
+/*- End of function --------------------------------------------------------*/
|
||
|
+
|
||
|
+static inline int16_t biquad2 (biquad2_state_t *bq, int16_t sample)
|
||
|
+{
|
||
|
+ int32_t y;
|
||
|
+ int32_t z0;
|
||
|
+
|
||
|
+ z0 = sample*bq->gain + bq->z1*bq->a1 + bq->z2*bq->a2;
|
||
|
+ y = z0 + bq->z1*bq->b1 + bq->z2*bq->b2;
|
||
|
+
|
||
|
+ bq->z2 = bq->z1;
|
||
|
+ bq->z1 = z0 >> 15;
|
||
|
+ y >>= 15;
|
||
|
+ return y;
|
||
|
+}
|
||
|
+/*- End of function --------------------------------------------------------*/
|
||
|
+/*- End of file ------------------------------------------------------------*/
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/dsp_cancel.c mISDN/drivers/isdn/hardware/mISDN/dsp_cancel.c
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/dsp_cancel.c 1970-01-01 01:00:00.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/dsp_cancel.c 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -0,0 +1,390 @@
|
||
|
+/* $Id: mISDN_echo_cancel.patch,v 1.1 2005/11/07 16:01:31 nadi Exp $
|
||
|
+ *
|
||
|
+ * Simple but fast Echo cancellation for mISDN_dsp.
|
||
|
+ *
|
||
|
+ * Copyright Andreas Eversberg (jolly@jolly.de)
|
||
|
+ *
|
||
|
+ * This software may be used and distributed according to the terms
|
||
|
+ * of the GNU General Public License, incorporated herein by reference.
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+#include "layer1.h"
|
||
|
+#include "helper.h"
|
||
|
+#include "debug.h"
|
||
|
+#include "dsp.h"
|
||
|
+
|
||
|
+
|
||
|
+/*
|
||
|
+ * how this works:
|
||
|
+ *
|
||
|
+ *
|
||
|
+ *
|
||
|
+ */
|
||
|
+void bchdev_echocancel_chunk(dsp_t* dev, uint8_t *rxchunk, uint8_t *txchunk, uint16_t size);
|
||
|
+int bchdev_echocancel_activate(dsp_t* dev, int deftaps, int train);
|
||
|
+void bchdev_echocancel_deactivate(dsp_t* dev);
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+static char flip_table[256];
|
||
|
+
|
||
|
+void dsp_cancel_init_flip_bits()
|
||
|
+{
|
||
|
+ int i,k;
|
||
|
+
|
||
|
+ for (i = 0 ; i < 256 ; i++) {
|
||
|
+ unsigned char sample = 0 ;
|
||
|
+ for (k = 0; k<8; k++) {
|
||
|
+ if ( i & 1 << k ) sample |= 0x80 >> k;
|
||
|
+ }
|
||
|
+ flip_table[i] = sample;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static unsigned char * flip_buf_bits ( unsigned char * buf , int len)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+ char * start = buf;
|
||
|
+
|
||
|
+ for (i = 0 ; i < len; i++) {
|
||
|
+ buf[i] = flip_table[buf[i]];
|
||
|
+ }
|
||
|
+
|
||
|
+ return start;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+void
|
||
|
+dsp_cancel_tx(dsp_t *dsp, u8 *data, int len)
|
||
|
+{
|
||
|
+ if (!dsp ) return ;
|
||
|
+ if (!data) return;
|
||
|
+
|
||
|
+ if (dsp->txbuflen + len < ECHOCAN_BUFLEN) {
|
||
|
+ memcpy(&dsp->txbuf[dsp->txbuflen],data,len);
|
||
|
+ dsp->txbuflen+=len;
|
||
|
+ } else {
|
||
|
+ printk("ECHOCAN: TXBUF Overflow len:%d newlen:%d\n",dsp->txbuflen,len);
|
||
|
+ dsp->txbuflen=0;
|
||
|
+ }
|
||
|
+
|
||
|
+}
|
||
|
+
|
||
|
+void
|
||
|
+dsp_cancel_rx(dsp_t *dsp, u8 *data, int len)
|
||
|
+{
|
||
|
+ if (!dsp ) return ;
|
||
|
+ if (!data) return;
|
||
|
+
|
||
|
+ if (len <= dsp->txbuflen) {
|
||
|
+ char tmp[ECHOCAN_BUFLEN];
|
||
|
+
|
||
|
+ int delta=dsp->txbuflen-len;
|
||
|
+
|
||
|
+ memcpy(tmp,&dsp->txbuf[len],delta);
|
||
|
+
|
||
|
+ flip_buf_bits(data,len);
|
||
|
+ flip_buf_bits(dsp->txbuf,len);
|
||
|
+ bchdev_echocancel_chunk(dsp, data, dsp->txbuf, len);
|
||
|
+ flip_buf_bits(data,len);
|
||
|
+
|
||
|
+ memcpy(dsp->txbuf,tmp,delta);
|
||
|
+ dsp->txbuflen=delta;
|
||
|
+ //dsp->txbuflen=0;
|
||
|
+
|
||
|
+ //bchdev_echocancel_chunk(dsp, dsp->txbuf, data, len);
|
||
|
+ } else {
|
||
|
+ printk("ECHOCAN: TXBUF Underrun len:%d newlen:%d\n",dsp->txbuflen,len);
|
||
|
+ }
|
||
|
+
|
||
|
+}
|
||
|
+
|
||
|
+int
|
||
|
+dsp_cancel_init(dsp_t *dsp, int deftaps, int training, int delay)
|
||
|
+{
|
||
|
+
|
||
|
+ if (!dsp) return -1;
|
||
|
+
|
||
|
+ printk("DSP_CANCEL_INIT called\n");
|
||
|
+
|
||
|
+ if (delay < 0)
|
||
|
+ {
|
||
|
+ printk("Disabling EC\n");
|
||
|
+ dsp->cancel_enable = 0;
|
||
|
+
|
||
|
+ dsp->txbuflen=0;
|
||
|
+
|
||
|
+ bchdev_echocancel_deactivate(dsp);
|
||
|
+
|
||
|
+ return(0);
|
||
|
+ }
|
||
|
+
|
||
|
+ dsp->txbuflen=0;
|
||
|
+ dsp->rxbuflen=0;
|
||
|
+
|
||
|
+
|
||
|
+ bchdev_echocancel_activate(dsp,deftaps, training);
|
||
|
+
|
||
|
+ printk("Enabling EC\n");
|
||
|
+ dsp->cancel_enable = 1;
|
||
|
+ return(0);
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+/*****************************************************/
|
||
|
+#define __ECHO_STATE_MUTE (1 << 8)
|
||
|
+#define ECHO_STATE_IDLE (0)
|
||
|
+#define ECHO_STATE_PRETRAINING (1 | (__ECHO_STATE_MUTE))
|
||
|
+#define ECHO_STATE_STARTTRAINING (2 | (__ECHO_STATE_MUTE))
|
||
|
+#define ECHO_STATE_AWAITINGECHO (3 | (__ECHO_STATE_MUTE))
|
||
|
+#define ECHO_STATE_TRAINING (4 | (__ECHO_STATE_MUTE))
|
||
|
+#define ECHO_STATE_ACTIVE (5)
|
||
|
+
|
||
|
+#define AMI_MASK 0x55
|
||
|
+
|
||
|
+
|
||
|
+static unsigned char linear2alaw (short linear)
|
||
|
+{
|
||
|
+ int mask;
|
||
|
+ int seg;
|
||
|
+ int pcm_val;
|
||
|
+ static int seg_end[8] =
|
||
|
+ {
|
||
|
+ 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
|
||
|
+ };
|
||
|
+
|
||
|
+ pcm_val = linear;
|
||
|
+ if (pcm_val >= 0)
|
||
|
+ {
|
||
|
+ /* Sign (7th) bit = 1 */
|
||
|
+ mask = AMI_MASK | 0x80;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ /* Sign bit = 0 */
|
||
|
+ mask = AMI_MASK;
|
||
|
+ pcm_val = -pcm_val;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Convert the scaled magnitude to segment number. */
|
||
|
+ for (seg = 0; seg < 8; seg++)
|
||
|
+ {
|
||
|
+ if (pcm_val <= seg_end[seg])
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ /* Combine the sign, segment, and quantization bits. */
|
||
|
+ return ((seg << 4) | ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask;
|
||
|
+}
|
||
|
+
|
||
|
+/*- End of function --------------------------------------------------------*/
|
||
|
+
|
||
|
+static short int alaw2linear (uint8_t alaw)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+ int seg;
|
||
|
+
|
||
|
+ alaw ^= AMI_MASK;
|
||
|
+ i = ((alaw & 0x0F) << 4);
|
||
|
+ seg = (((int) alaw & 0x70) >> 4);
|
||
|
+ if (seg)
|
||
|
+ i = (i + 0x100) << (seg - 1);
|
||
|
+ return (short int) ((alaw & 0x80) ? i : -i);
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/** @return string of given echo cancellation state */
|
||
|
+char* bchdev_echocancel_statestr(uint16_t state)
|
||
|
+{
|
||
|
+ switch(state) {
|
||
|
+ case ECHO_STATE_IDLE:
|
||
|
+ return "idle";
|
||
|
+ break;
|
||
|
+ case ECHO_STATE_PRETRAINING:
|
||
|
+ return "pre-training";
|
||
|
+ break;
|
||
|
+ case ECHO_STATE_STARTTRAINING:
|
||
|
+ return "transmit impulse";
|
||
|
+ break;
|
||
|
+ case ECHO_STATE_AWAITINGECHO:
|
||
|
+ return "awaiting echo";
|
||
|
+ break;
|
||
|
+ case ECHO_STATE_TRAINING:
|
||
|
+ return "training start";
|
||
|
+ break;
|
||
|
+ case ECHO_STATE_ACTIVE:
|
||
|
+ return "training finished";
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ return "unknown";
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+/** Changes state of echo cancellation to given state */
|
||
|
+void bchdev_echocancel_setstate(dsp_t* dev, uint16_t state)
|
||
|
+{
|
||
|
+ char* statestr = bchdev_echocancel_statestr(state);
|
||
|
+
|
||
|
+ printk("bchdev: echo cancel state %d (%s)\n", state & 0xff, statestr);
|
||
|
+ if (state == ECHO_STATE_ACTIVE)
|
||
|
+ printk("bchdev: %d taps trained\n", dev->echolastupdate);
|
||
|
+ dev->echostate = state;
|
||
|
+}
|
||
|
+
|
||
|
+static int buf_size=0;
|
||
|
+static int ec_timer=2000;
|
||
|
+//static int ec_timer=1000;
|
||
|
+
|
||
|
+
|
||
|
+/** Activates echo cancellation for the given bch_dev, device must have been locked before! */
|
||
|
+int bchdev_echocancel_activate(dsp_t* dev, int deftaps, int training)
|
||
|
+{
|
||
|
+ int taps;
|
||
|
+
|
||
|
+ if (! dev) return -EINVAL;
|
||
|
+
|
||
|
+ if (dev->ec && dev->ecdis_rd && dev->ecdis_wr) {
|
||
|
+ // already active
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (deftaps>0) {
|
||
|
+ taps=deftaps;
|
||
|
+ } else {
|
||
|
+ taps=128;
|
||
|
+ }
|
||
|
+
|
||
|
+
|
||
|
+ switch (buf_size) {
|
||
|
+ case 0: taps += 0; break;
|
||
|
+ case 1: taps += 256-128; break;
|
||
|
+ case 2: taps += 512-128; break;
|
||
|
+ default: taps += 1024-128;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!dev->ec) dev->ec = echo_can_create(taps, 0);
|
||
|
+ if (!dev->ec) {
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
+ dev->echolastupdate = 0;
|
||
|
+
|
||
|
+ if (!training) {
|
||
|
+ dev->echotimer=0;
|
||
|
+ bchdev_echocancel_setstate(dev, ECHO_STATE_IDLE);
|
||
|
+ } else {
|
||
|
+ if (training<10)
|
||
|
+ training= ec_timer;
|
||
|
+
|
||
|
+ dev->echotimer = training;
|
||
|
+ bchdev_echocancel_setstate(dev, ECHO_STATE_PRETRAINING);
|
||
|
+
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!dev->ecdis_rd) dev->ecdis_rd = kmalloc(sizeof(echo_can_disable_detector_state_t), GFP_KERNEL);
|
||
|
+ if (!dev->ecdis_rd) {
|
||
|
+ kfree(dev->ec); dev->ec = NULL;
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+ echo_can_disable_detector_init(dev->ecdis_rd);
|
||
|
+
|
||
|
+ if (!dev->ecdis_wr) dev->ecdis_wr = kmalloc(sizeof(echo_can_disable_detector_state_t), GFP_KERNEL);
|
||
|
+ if (!dev->ecdis_wr) {
|
||
|
+ kfree(dev->ec); dev->ec = NULL;
|
||
|
+ kfree(dev->ecdis_rd); dev->ecdis_rd = NULL;
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+ echo_can_disable_detector_init(dev->ecdis_wr);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/** Deactivates echo cancellation for the given bch_dev, device must have been locked before! */
|
||
|
+void bchdev_echocancel_deactivate(dsp_t* dev)
|
||
|
+{
|
||
|
+ if (! dev) return;
|
||
|
+
|
||
|
+ //chan_misdn_log("bchdev: deactivating echo cancellation on port=%04x, chan=%02x\n", dev->stack->port, dev->channel);
|
||
|
+
|
||
|
+ if (dev->ec) echo_can_free(dev->ec);
|
||
|
+ dev->ec = NULL;
|
||
|
+
|
||
|
+ dev->echolastupdate = 0;
|
||
|
+ dev->echotimer = 0;
|
||
|
+ bchdev_echocancel_setstate(dev, ECHO_STATE_IDLE);
|
||
|
+
|
||
|
+ if (dev->ecdis_rd) kfree(dev->ecdis_rd);
|
||
|
+ dev->ecdis_rd = NULL;
|
||
|
+
|
||
|
+ if (dev->ecdis_wr) kfree(dev->ecdis_wr);
|
||
|
+ dev->ecdis_wr = NULL;
|
||
|
+}
|
||
|
+
|
||
|
+/** Processes one TX- and one RX-packet with echocancellation */
|
||
|
+void bchdev_echocancel_chunk(dsp_t* dev, uint8_t *rxchunk, uint8_t *txchunk, uint16_t size)
|
||
|
+{
|
||
|
+ int16_t rxlin, txlin;
|
||
|
+ uint16_t pos;
|
||
|
+
|
||
|
+ /* Perform echo cancellation on a chunk if requested */
|
||
|
+ if (dev->ec) {
|
||
|
+ if (dev->echostate & __ECHO_STATE_MUTE) {
|
||
|
+ if (dev->echostate == ECHO_STATE_STARTTRAINING) {
|
||
|
+ // Transmit impulse now
|
||
|
+ txchunk[0] = linear2alaw(16384);
|
||
|
+ memset(txchunk+1, 0, size-1);
|
||
|
+ bchdev_echocancel_setstate(dev, ECHO_STATE_TRAINING); //AWAITINGECHO);
|
||
|
+ } else {
|
||
|
+ // train the echo cancellation
|
||
|
+ for (pos = 0; pos < size; pos++) {
|
||
|
+ rxlin = alaw2linear(rxchunk[pos]);
|
||
|
+ txlin = alaw2linear(txchunk[pos]);
|
||
|
+ if (dev->echostate == ECHO_STATE_PRETRAINING) {
|
||
|
+ if (dev->echotimer <= 0) {
|
||
|
+ dev->echotimer = 0;
|
||
|
+ bchdev_echocancel_setstate(dev, ECHO_STATE_STARTTRAINING);
|
||
|
+ } else {
|
||
|
+ dev->echotimer--;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if ((dev->echostate == ECHO_STATE_AWAITINGECHO) && (txlin > 8000)) {
|
||
|
+ dev->echolastupdate = 0;
|
||
|
+ bchdev_echocancel_setstate(dev, ECHO_STATE_TRAINING);
|
||
|
+ }
|
||
|
+ if (dev->echostate == ECHO_STATE_TRAINING) {
|
||
|
+ if (echo_can_traintap(dev->ec, dev->echolastupdate++, rxlin)) {
|
||
|
+ bchdev_echocancel_setstate(dev, ECHO_STATE_ACTIVE);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ rxchunk[pos] = linear2alaw(0);
|
||
|
+ txchunk[pos] = linear2alaw(0);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ for (pos = 0; pos < size; pos++) {
|
||
|
+ rxlin = alaw2linear(rxchunk[pos]);
|
||
|
+ txlin = alaw2linear(txchunk[pos]);
|
||
|
+
|
||
|
+ if (echo_can_disable_detector_update(dev->ecdis_rd, rxlin) ||
|
||
|
+ echo_can_disable_detector_update(dev->ecdis_wr, txlin)) {
|
||
|
+ bchdev_echocancel_deactivate(dev);
|
||
|
+ printk("EC: Fax detected, EC disabled\n");
|
||
|
+ return ;
|
||
|
+ } else {
|
||
|
+ rxlin = echo_can_update(dev->ec, txlin, rxlin);
|
||
|
+ rxchunk[pos] = linear2alaw(rxlin);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+/******************************************************/
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/dsp_core.c mISDN/drivers/isdn/hardware/mISDN/dsp_core.c
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/dsp_core.c 2005-01-29 17:15:21.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/dsp_core.c 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -42,17 +42,11 @@
|
||
|
* v |
|
||
|
* +-----+-------------+-----+
|
||
|
* |(3)(4) |
|
||
|
- * | |
|
||
|
- * | |
|
||
|
* | CMX |
|
||
|
* | |
|
||
|
- * | |
|
||
|
- * | |
|
||
|
- * | |
|
||
|
* | +-------------+
|
||
|
* | | ^
|
||
|
* | | |
|
||
|
- * | | |
|
||
|
* |+---------+| +----+----+
|
||
|
* ||(1) || |(5) |
|
||
|
* || || | |
|
||
|
@@ -62,7 +56,6 @@
|
||
|
* |+----+----+| +----+----+
|
||
|
* +-----+-----+ ^
|
||
|
* | |
|
||
|
- * | |
|
||
|
* v |
|
||
|
* +----+----+ +----+----+
|
||
|
* |(5) | |(2) |
|
||
|
@@ -74,8 +67,18 @@
|
||
|
* | ^
|
||
|
* | |
|
||
|
* v |
|
||
|
+ * +----+-------------+----+
|
||
|
+ * |(7) |
|
||
|
+ * | |
|
||
|
+ * | Echo Cancellation |
|
||
|
+ * | |
|
||
|
+ * | |
|
||
|
+ * +----+-------------+----+
|
||
|
+ * | ^
|
||
|
+ * | |
|
||
|
+ * v |
|
||
|
* +----+----+ +----+----+
|
||
|
- * |(7) | |(7) |
|
||
|
+ * |(8) | |(8) |
|
||
|
* | | | |
|
||
|
* | Encrypt | | Decrypt |
|
||
|
* | | | |
|
||
|
@@ -115,6 +118,13 @@
|
||
|
* data to/form upper layer may be swithed on/off individually without loosing
|
||
|
* features of CMX, Tones and DTMF.
|
||
|
*
|
||
|
+ * Echo Cancellation: Sometimes we like to cancel echo from the interface.
|
||
|
+ * Note that a VoIP call may not have echo caused by the IP phone. The echo
|
||
|
+ * is generated by the telephone line connected to it. Because the delay
|
||
|
+ * is high, it becomes an echo. RESULT: Echo Cachelation is required if
|
||
|
+ * both echo AND delay is applied to an interface.
|
||
|
+ * Remember that software CMX always generates a more or less delay.
|
||
|
+ *
|
||
|
* If all used features can be realized in hardware, and if transmit and/or
|
||
|
* receive data ist disabled, the card may not send/receive any data at all.
|
||
|
* Not receiving is usefull if only announcements are played. Not sending is
|
||
|
@@ -215,6 +225,9 @@
|
||
|
printk(KERN_ERR "%s: failed to create tx packet\n", __FUNCTION__);
|
||
|
return;
|
||
|
}
|
||
|
+ /* if echo cancellation is enabled */
|
||
|
+ if (dsp->cancel_enable)
|
||
|
+ dsp_cancel_tx(dsp, nskb->data, nskb->len);
|
||
|
/* crypt if enabled */
|
||
|
if (dsp->bf_enable)
|
||
|
dsp_bf_encrypt(dsp, nskb->data, nskb->len);
|
||
|
@@ -380,6 +393,34 @@
|
||
|
if (dsp_debug & DEBUG_DSP_CMX)
|
||
|
dsp_cmx_debug(dsp);
|
||
|
break;
|
||
|
+ case ECHOCAN_ON: /* turn echo calcellation on */
|
||
|
+
|
||
|
+ if (len<4) {
|
||
|
+ ret = -EINVAL;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ int ec_arr[2];
|
||
|
+
|
||
|
+ memcpy(&ec_arr,data,sizeof(ec_arr));
|
||
|
+
|
||
|
+
|
||
|
+ printk("data[0]: %d data[1]: %d, len :%d\n",ec_arr[0],
|
||
|
+ ec_arr[1] ,len);
|
||
|
+
|
||
|
+ if (dsp_debug & DEBUG_DSP_CORE)
|
||
|
+ printk(KERN_DEBUG "%s: turn echo cancelation on (delay=%d attenuation-shift=%d\n", __FUNCTION__, ec_arr[0], ec_arr[1]);
|
||
|
+
|
||
|
+ ret = dsp_cancel_init(dsp, ec_arr[0], ec_arr[1] ,1);
|
||
|
+
|
||
|
+ dsp_cmx_hardware(dsp->conf, dsp);
|
||
|
+ break;
|
||
|
+ case ECHOCAN_OFF: /* turn echo calcellation off */
|
||
|
+ if (dsp_debug & DEBUG_DSP_CORE)
|
||
|
+ printk(KERN_DEBUG "%s: turn echo cancelation off\n", __FUNCTION__);
|
||
|
+
|
||
|
+ ret = dsp_cancel_init(dsp, 0,0,-1);
|
||
|
+ dsp_cmx_hardware(dsp->conf, dsp);
|
||
|
+ break;
|
||
|
case BF_ENABLE_KEY: /* turn blowfish on */
|
||
|
if (len<4 || len>56) {
|
||
|
ret = -EINVAL;
|
||
|
@@ -522,6 +563,9 @@
|
||
|
/* decrypt if enabled */
|
||
|
if (dsp->bf_enable)
|
||
|
dsp_bf_decrypt(dsp, skb->data, skb->len);
|
||
|
+ /* if echo cancellation is enabled */
|
||
|
+ if (dsp->cancel_enable)
|
||
|
+ dsp_cancel_rx(dsp, skb->data, skb->len);
|
||
|
/* check if dtmf soft decoding is turned on */
|
||
|
if (dsp->dtmf.software) {
|
||
|
digits = dsp_dtmf_goertzel_decode(dsp, skb->data, skb->len, (dsp_options&DSP_OPT_ULAW)?1:0);
|
||
|
@@ -919,6 +963,9 @@
|
||
|
dsp_audio_generate_ulaw_samples();
|
||
|
dsp_audio_generate_volume_changes();
|
||
|
|
||
|
+
|
||
|
+ dsp_cancel_init_flip_bits();
|
||
|
+
|
||
|
/* init global lock */
|
||
|
lock_HW_init(&dsp_lock);
|
||
|
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/dsp.h mISDN/drivers/isdn/hardware/mISDN/dsp.h
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/dsp.h 2005-01-29 17:15:31.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/dsp.h 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -40,6 +40,13 @@
|
||
|
#include "memdbg.h"
|
||
|
#endif
|
||
|
|
||
|
+#include "ecdis.h"
|
||
|
+#include "mec2.h"
|
||
|
+
|
||
|
+//#include "mec.h"
|
||
|
+//#include "mec3.h"
|
||
|
+
|
||
|
+
|
||
|
extern int dsp_options;
|
||
|
extern int dsp_debug;
|
||
|
|
||
|
@@ -109,6 +116,8 @@
|
||
|
|
||
|
#define DSP_DTMF_NPOINTS 102
|
||
|
|
||
|
+#define ECHOCAN_BUFLEN 4*128
|
||
|
+
|
||
|
typedef struct _dtmf_t {
|
||
|
int software; /* dtmf uses software decoding */
|
||
|
int hardware; /* dtmf uses hardware decoding */
|
||
|
@@ -120,6 +129,13 @@
|
||
|
} dtmf_t;
|
||
|
|
||
|
|
||
|
+/**************
|
||
|
+ *Cancel Stuff*
|
||
|
+ ***************/
|
||
|
+
|
||
|
+void dsp_cancel_init_flip_bits(void);
|
||
|
+
|
||
|
+
|
||
|
/***************
|
||
|
* tones stuff *
|
||
|
***************/
|
||
|
@@ -200,6 +216,25 @@
|
||
|
u8 bf_crypt_inring[16];
|
||
|
u8 bf_data_out[9];
|
||
|
int bf_sync;
|
||
|
+
|
||
|
+ /* echo cancellation stuff */
|
||
|
+ int cancel_enable;
|
||
|
+ echo_can_state_t* ec; /**< == NULL: echo cancellation disabled;
|
||
|
+ != NULL: echo cancellation enabled */
|
||
|
+
|
||
|
+ echo_can_disable_detector_state_t* ecdis_rd;
|
||
|
+ echo_can_disable_detector_state_t* ecdis_wr;
|
||
|
+
|
||
|
+ uint16_t echotimer;
|
||
|
+ uint16_t echostate;
|
||
|
+ uint16_t echolastupdate;
|
||
|
+
|
||
|
+ char txbuf[ECHOCAN_BUFLEN];
|
||
|
+ int txbuflen;
|
||
|
+
|
||
|
+ char rxbuf[ECHOCAN_BUFLEN];
|
||
|
+ int rxbuflen;
|
||
|
+
|
||
|
} dsp_t;
|
||
|
|
||
|
/* functions */
|
||
|
@@ -228,4 +263,8 @@
|
||
|
extern int dsp_bf_init(dsp_t *dsp, const u8 *key, unsigned int keylen);
|
||
|
extern void dsp_bf_cleanup(dsp_t *dsp);
|
||
|
|
||
|
+extern void dsp_cancel_tx(dsp_t *dsp, u8 *data, int len);
|
||
|
+extern void dsp_cancel_rx(dsp_t *dsp, u8 *data, int len);
|
||
|
+extern int dsp_cancel_init(dsp_t *dsp, int taps, int training, int delay);
|
||
|
+
|
||
|
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/ec.c mISDN/drivers/isdn/hardware/mISDN/ec.c
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/ec.c 1970-01-01 01:00:00.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/ec.c 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -0,0 +1,105 @@
|
||
|
+#include "mec2.h"
|
||
|
+#include "ec.h"
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+#define __ECHO_STATE_MUTE (1 << 8)
|
||
|
+#define ECHO_STATE_IDLE (0)
|
||
|
+#define ECHO_STATE_PRETRAINING (1 | (__ECHO_STATE_MUTE))
|
||
|
+#define ECHO_STATE_STARTTRAINING (2 | (__ECHO_STATE_MUTE))
|
||
|
+#define ECHO_STATE_AWAITINGECHO (3 | (__ECHO_STATE_MUTE))
|
||
|
+#define ECHO_STATE_TRAINING (4 | (__ECHO_STATE_MUTE))
|
||
|
+#define ECHO_STATE_ACTIVE (5)
|
||
|
+
|
||
|
+#define AMI_MASK 0x55
|
||
|
+
|
||
|
+static unsigned char linear2alaw (short linear)
|
||
|
+{
|
||
|
+ int mask;
|
||
|
+ int seg;
|
||
|
+ int pcm_val;
|
||
|
+ static int seg_end[8] =
|
||
|
+ {
|
||
|
+ 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
|
||
|
+ };
|
||
|
+
|
||
|
+ pcm_val = linear;
|
||
|
+ if (pcm_val >= 0)
|
||
|
+ {
|
||
|
+ /* Sign (7th) bit = 1 */
|
||
|
+ mask = AMI_MASK | 0x80;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ /* Sign bit = 0 */
|
||
|
+ mask = AMI_MASK;
|
||
|
+ pcm_val = -pcm_val;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Convert the scaled magnitude to segment number. */
|
||
|
+ for (seg = 0; seg < 8; seg++)
|
||
|
+ {
|
||
|
+ if (pcm_val <= seg_end[seg])
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ /* Combine the sign, segment, and quantization bits. */
|
||
|
+ return ((seg << 4) | ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask;
|
||
|
+}
|
||
|
+/*- End of function --------------------------------------------------------*/
|
||
|
+
|
||
|
+static short int alaw2linear (uint8_t alaw)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+ int seg;
|
||
|
+
|
||
|
+ alaw ^= AMI_MASK;
|
||
|
+ i = ((alaw & 0x0F) << 4);
|
||
|
+ seg = (((int) alaw & 0x70) >> 4);
|
||
|
+ if (seg)
|
||
|
+ i = (i + 0x100) << (seg - 1);
|
||
|
+ return (short int) ((alaw & 0x80) ? i : -i);
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+void ec_chunk(struct echo_can_s *echo_can, unsigned char *rxchunk, const unsigned char *txchunk, int chunk_size)
|
||
|
+{
|
||
|
+ short rxlin, txlin;
|
||
|
+ int x;
|
||
|
+ //unsigned long flags;
|
||
|
+ /* Perform echo cancellation on a chunk if necessary */
|
||
|
+ if (echo_can->ec) {
|
||
|
+ if (echo_can->echostate & __ECHO_STATE_MUTE) {
|
||
|
+ /* Special stuff for training the echo can */
|
||
|
+ for (x=0;x< chunk_size;x++) {
|
||
|
+ rxlin = alaw2linear(rxchunk[x]);
|
||
|
+ txlin = alaw2linear(txchunk[x]);
|
||
|
+ if (echo_can->echostate == ECHO_STATE_PRETRAINING) {
|
||
|
+ if (--echo_can->echotimer <= 0) {
|
||
|
+ echo_can->echotimer = 0;
|
||
|
+ echo_can->echostate = ECHO_STATE_STARTTRAINING;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if ((echo_can->echostate == ECHO_STATE_AWAITINGECHO) && (txlin > 8000)) {
|
||
|
+ echo_can->echolastupdate = 0;
|
||
|
+ echo_can->echostate = ECHO_STATE_TRAINING;
|
||
|
+ }
|
||
|
+ if (echo_can->echostate == ECHO_STATE_TRAINING) {
|
||
|
+ if (echo_can_traintap(echo_can->ec, echo_can->echolastupdate++, rxlin)) {
|
||
|
+#if 0
|
||
|
+ printf("Finished training (%d taps trained)!\n", echo_can->echolastupdate);
|
||
|
+#endif
|
||
|
+ echo_can->echostate = ECHO_STATE_ACTIVE;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ rxlin = 0;
|
||
|
+ rxchunk[x] = linear2alaw((int)rxlin);
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ for (x=0;x<chunk_size;x++) {
|
||
|
+ rxlin = alaw2linear(rxchunk[x]);
|
||
|
+ rxlin = echo_can_update(echo_can->ec, alaw2linear(txchunk[x]), rxlin);
|
||
|
+ rxchunk[x] = linear2alaw((int)rxlin);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/ecdis.h mISDN/drivers/isdn/hardware/mISDN/ecdis.h
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/ecdis.h 1970-01-01 01:00:00.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/ecdis.h 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -0,0 +1,118 @@
|
||
|
+/*
|
||
|
+ * SpanDSP - a series of DSP components for telephony
|
||
|
+ *
|
||
|
+ * ec_disable_detector.h - A detector which should eventually meet the
|
||
|
+ * G.164/G.165 requirements for detecting the
|
||
|
+ * 2100Hz echo cancellor disable tone.
|
||
|
+ *
|
||
|
+ * Written by Steve Underwood <steveu@coppice.org>
|
||
|
+ *
|
||
|
+ * Copyright (C) 2001 Steve Underwood
|
||
|
+ *
|
||
|
+ * All rights reserved.
|
||
|
+ *
|
||
|
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+#include "biquad.h"
|
||
|
+
|
||
|
+typedef struct
|
||
|
+{
|
||
|
+ biquad2_state_t notch;
|
||
|
+ int notch_level;
|
||
|
+ int channel_level;
|
||
|
+ int tone_present;
|
||
|
+ int tone_cycle_duration;
|
||
|
+ int good_cycles;
|
||
|
+ int hit;
|
||
|
+} echo_can_disable_detector_state_t;
|
||
|
+
|
||
|
+
|
||
|
+#define FALSE 0
|
||
|
+#define TRUE (!FALSE)
|
||
|
+
|
||
|
+static inline void echo_can_disable_detector_init (echo_can_disable_detector_state_t *det)
|
||
|
+{
|
||
|
+ /* Elliptic notch */
|
||
|
+ /* This is actually centred at 2095Hz, but gets the balance we want, due
|
||
|
+ to the asymmetric walls of the notch */
|
||
|
+ biquad2_init (&det->notch,
|
||
|
+ (int32_t) (-0.7600000*32768.0),
|
||
|
+ (int32_t) (-0.1183852*32768.0),
|
||
|
+ (int32_t) (-0.5104039*32768.0),
|
||
|
+ (int32_t) ( 0.1567596*32768.0),
|
||
|
+ (int32_t) ( 1.0000000*32768.0));
|
||
|
+
|
||
|
+ det->channel_level = 0;
|
||
|
+ det->notch_level = 0;
|
||
|
+ det->tone_present = FALSE;
|
||
|
+ det->tone_cycle_duration = 0;
|
||
|
+ det->good_cycles = 0;
|
||
|
+ det->hit = 0;
|
||
|
+}
|
||
|
+/*- End of function --------------------------------------------------------*/
|
||
|
+
|
||
|
+static inline int echo_can_disable_detector_update (echo_can_disable_detector_state_t *det,
|
||
|
+ int16_t amp)
|
||
|
+{
|
||
|
+ int16_t notched;
|
||
|
+
|
||
|
+ notched = biquad2 (&det->notch, amp);
|
||
|
+ /* Estimate the overall energy in the channel, and the energy in
|
||
|
+ the notch (i.e. overall channel energy - tone energy => noise).
|
||
|
+ Use abs instead of multiply for speed (is it really faster?).
|
||
|
+ Damp the overall energy a little more for a stable result.
|
||
|
+ Damp the notch energy a little less, so we don't damp out the
|
||
|
+ blip every time the phase reverses */
|
||
|
+ det->channel_level += ((abs(amp) - det->channel_level) >> 5);
|
||
|
+ det->notch_level += ((abs(notched) - det->notch_level) >> 4);
|
||
|
+ if (det->channel_level > 280)
|
||
|
+ {
|
||
|
+ /* There is adequate energy in the channel. Is it mostly at 2100Hz? */
|
||
|
+ if (det->notch_level*6 < det->channel_level)
|
||
|
+ {
|
||
|
+ /* The notch says yes, so we have the tone. */
|
||
|
+ if (!det->tone_present)
|
||
|
+ {
|
||
|
+ /* Do we get a kick every 450+-25ms? */
|
||
|
+ if (det->tone_cycle_duration >= 425*8
|
||
|
+ &&
|
||
|
+ det->tone_cycle_duration <= 475*8)
|
||
|
+ {
|
||
|
+ det->good_cycles++;
|
||
|
+ if (det->good_cycles > 2)
|
||
|
+ det->hit = TRUE;
|
||
|
+ }
|
||
|
+ det->tone_cycle_duration = 0;
|
||
|
+ }
|
||
|
+ det->tone_present = TRUE;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ det->tone_present = FALSE;
|
||
|
+ }
|
||
|
+ det->tone_cycle_duration++;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ det->tone_present = FALSE;
|
||
|
+ det->tone_cycle_duration = 0;
|
||
|
+ det->good_cycles = 0;
|
||
|
+ }
|
||
|
+ return det->hit;
|
||
|
+}
|
||
|
+/*- End of function --------------------------------------------------------*/
|
||
|
+/*- End of file ------------------------------------------------------------*/
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/ec.h mISDN/drivers/isdn/hardware/mISDN/ec.h
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/ec.h 1970-01-01 01:00:00.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/ec.h 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -0,0 +1,12 @@
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+struct echo_can_s {
|
||
|
+ int echostate;
|
||
|
+ int echotimer;
|
||
|
+ int echolastupdate;
|
||
|
+ echo_can_state_t *ec;
|
||
|
+};
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/hfc_multi.c mISDN/drivers/isdn/hardware/mISDN/hfc_multi.c
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/hfc_multi.c 2005-01-31 18:24:03.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/hfc_multi.c 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -136,7 +136,7 @@
|
||
|
static int nt_t1_count[] = { 480, 240, 120, 60, 30, 15, 8, 4 };
|
||
|
#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */
|
||
|
#define CLKDEL_NT 0x0c /* CLKDEL in NT mode (0x60 MUST not be included!) */
|
||
|
-static u_char silence = 0xff; /* silence by LAW */
|
||
|
+static u_char mysilence = 0xff; /* silence by LAW */
|
||
|
|
||
|
/* enable 32 bit fifo access (PC usage) */
|
||
|
#define FIFO_32BIT_ACCESS
|
||
|
@@ -903,11 +903,11 @@
|
||
|
bch->tx_idx = bch->tx_len = 0;
|
||
|
}
|
||
|
/* now we have no more data, so in case of transparent,
|
||
|
- * we set the last byte in fifo to 'silence' in case we will get
|
||
|
+ * we set the last byte in fifo to 'mysilence' in case we will get
|
||
|
* no more data at all. this prevents sending an undefined value.
|
||
|
*/
|
||
|
if (!hdlc)
|
||
|
- HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence);
|
||
|
+ HFC_outb_(hc, A_FIFO_DATA0_NOINC, mysilence);
|
||
|
}
|
||
|
|
||
|
|
||
|
@@ -1551,7 +1551,7 @@
|
||
|
HFC_outb(hc, A_IRQ_MSK, 0);
|
||
|
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
|
||
|
HFC_wait(hc);
|
||
|
- HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence); /* tx silence */
|
||
|
+ HFC_outb_(hc, A_FIFO_DATA0_NOINC, mysilence); /* tx silence */
|
||
|
/* enable RX fifo */
|
||
|
HFC_outb(hc, R_FIFO, (ch<<1)|1);
|
||
|
HFC_wait(hc);
|
||
|
@@ -1692,7 +1692,7 @@
|
||
|
|
||
|
/* if off */
|
||
|
if (len <= 0) {
|
||
|
- HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence);
|
||
|
+ HFC_outb_(hc, A_FIFO_DATA0_NOINC, mysilence);
|
||
|
if (hc->chan[ch].slot_tx>=0) {
|
||
|
if (debug & DEBUG_HFCMULTI_MODE)
|
||
|
printk(KERN_DEBUG "%s: connecting PCM due to no more TONE: channel %d slot_tx %d\n", __FUNCTION__, ch, hc->chan[ch].slot_tx);
|
||
|
@@ -2183,7 +2183,7 @@
|
||
|
ret = 0;
|
||
|
break;
|
||
|
|
||
|
- /* set silence */
|
||
|
+ /* set mysilence */
|
||
|
case HW_SPL_LOOP_OFF:
|
||
|
if (debug & DEBUG_HFCMULTI_MSG)
|
||
|
printk(KERN_DEBUG "%s: HW_SPL_LOOP_OFF\n", __FUNCTION__);
|
||
|
@@ -2799,7 +2799,13 @@
|
||
|
if (debug & DEBUG_HFCMULTI_INIT)
|
||
|
printk(KERN_DEBUG "setup_pci(): investigating card entry %d (looking for type %d)\n", i, hc->type);
|
||
|
inuse:
|
||
|
+
|
||
|
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
|
||
|
+ tmp_dev = pci_get_subsys(id_list[i].vendor_id, id_list[i].device_id, id_list[i].vendor_sub, id_list[i].device_sub, tmp_dev);
|
||
|
+#else
|
||
|
tmp_dev = pci_find_subsys(id_list[i].vendor_id, id_list[i].device_id, id_list[i].vendor_sub, id_list[i].device_sub, tmp_dev);
|
||
|
+#endif
|
||
|
+
|
||
|
if (tmp_dev) {
|
||
|
/* skip if already in use */
|
||
|
list_for_each_entry_safe(hc_tmp, next, &HFCM_obj.ilist, list) {
|
||
|
@@ -3318,9 +3324,9 @@
|
||
|
hc->type = type[HFC_cnt] & 0xff;
|
||
|
if (type[HFC_cnt] & 0x100) {
|
||
|
test_and_set_bit(HFC_CHIP_ULAW, &hc->chip);
|
||
|
- silence = 0xff; /* ulaw silence */
|
||
|
+ mysilence = 0xff; /* ulaw silence */
|
||
|
} else
|
||
|
- silence = 0x2a; /* alaw silence */
|
||
|
+ mysilence = 0x2a; /* alaw silence */
|
||
|
if (type[HFC_cnt] & 0x200)
|
||
|
test_and_set_bit(HFC_CHIP_DTMF, &hc->chip);
|
||
|
// if ((type[HFC_cnt]&0x400) && hc->type==4)
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/l3_udss1.c mISDN/drivers/isdn/hardware/mISDN/l3_udss1.c
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/l3_udss1.c 2005-03-26 11:21:39.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/l3_udss1.c 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -1202,6 +1202,14 @@
|
||
|
err = check_infoelements(pc, skb, ie_PROGRESS);
|
||
|
if (err)
|
||
|
l3dss1_std_ie_err(pc, err);
|
||
|
+ /* START: patch by steinwej - http://www.beronet.com/bugs/bug_view_page.php?bug_id=0000095 */
|
||
|
+ /* clear T310 if running */
|
||
|
+ L3DelTimer(&pc->timer);
|
||
|
+ if (pc->t303skb) {
|
||
|
+ dev_kfree_skb(pc->t303skb);
|
||
|
+ pc->t303skb = NULL;
|
||
|
+ }
|
||
|
+ /* END */
|
||
|
if (ERR_IE_COMPREHENSION != err) {
|
||
|
if (mISDN_l3up(pc, CC_PROGRESS | INDICATION, skb))
|
||
|
dev_kfree_skb(skb);
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/Makefile mISDN/drivers/isdn/hardware/mISDN/Makefile
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/Makefile 2005-06-05 14:44:10.000000000 +0200
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/Makefile 2005-12-05 19:03:11.000000000 +0100
|
||
|
@@ -30,6 +30,7 @@
|
||
|
|
||
|
ifdef CONFIG_MISDN_SPEEDFAX
|
||
|
obj-$(CONFIG_MISDN_DRV) += sedlfax.o
|
||
|
+obj-$(CONFIG_MISDN_DRV) += faxl3.o
|
||
|
endif
|
||
|
|
||
|
ifdef CONFIG_MISDN_W6692
|
||
|
@@ -70,8 +71,6 @@
|
||
|
asn1_basic_service.o asn1_address.o asn1_enc.o capi_enc.o \
|
||
|
supp_serv.o
|
||
|
mISDN_dtmf-objs := dtmf.o
|
||
|
-mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o
|
||
|
+mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_cancel.o
|
||
|
mISDN_x25dte-objs := x25_dte.o x25_l3.o
|
||
|
I4LmISDN-objs := i4l_mISDN.o
|
||
|
-
|
||
|
-include Rules.mISDN
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/Makefile.v2.6 mISDN/drivers/isdn/hardware/mISDN/Makefile.v2.6
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/Makefile.v2.6 2005-06-05 14:44:10.000000000 +0200
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/Makefile.v2.6 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -71,6 +71,6 @@
|
||
|
asn1_basic_service.o asn1_address.o asn1_enc.o capi_enc.o \
|
||
|
supp_serv.o
|
||
|
mISDN_dtmf-objs := dtmf.o
|
||
|
-mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o
|
||
|
+mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_cancel.o
|
||
|
mISDN_x25dte-objs := x25_dte.o x25_l3.o
|
||
|
I4LmISDN-objs := i4l_mISDN.o
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/mec2_const.h mISDN/drivers/isdn/hardware/mISDN/mec2_const.h
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/mec2_const.h 1970-01-01 01:00:00.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/mec2_const.h 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -0,0 +1,25 @@
|
||
|
+/*
|
||
|
+ Important constants for tuning mec2 echo can
|
||
|
+ */
|
||
|
+#ifndef _MEC2_CONST_H
|
||
|
+#define _MEC2_CONST_H
|
||
|
+
|
||
|
+
|
||
|
+/* Convergence speed -- higher means slower */
|
||
|
+#define DEFAULT_BETA1_I 2048
|
||
|
+#define DEFAULT_SIGMA_LY_I 7
|
||
|
+#define DEFAULT_SIGMA_LU_I 7
|
||
|
+#define DEFAULT_ALPHA_ST_I 5
|
||
|
+#define DEFAULT_ALPHA_YT_I 5
|
||
|
+#define DEFAULT_CUTOFF_I 128
|
||
|
+#define DEFAULT_HANGT 600
|
||
|
+#define DEFAULT_SUPPR_I 16
|
||
|
+#define MIN_UPDATE_THRESH_I 4096
|
||
|
+#define DEFAULT_M 16
|
||
|
+#define SUPPR_FLOOR -64
|
||
|
+#define SUPPR_CEIL -24
|
||
|
+#define RES_SUPR_FACTOR -20
|
||
|
+#define AGGRESSIVE_HCNTR 160 /* 20ms */
|
||
|
+
|
||
|
+#endif /* _MEC2_CONST_H */
|
||
|
+
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/mec2.h mISDN/drivers/isdn/hardware/mISDN/mec2.h
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/mec2.h 1970-01-01 01:00:00.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/mec2.h 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -0,0 +1,409 @@
|
||
|
+/*
|
||
|
+ * Mark's Second Echo Canceller
|
||
|
+ *
|
||
|
+ * Copyright (C) 2002, Digium, Inc.
|
||
|
+ *
|
||
|
+ * This program is free software and may be used and
|
||
|
+ * distributed according to the terms of the GNU
|
||
|
+ * General Public License, incorporated herein by
|
||
|
+ * reference.
|
||
|
+ *
|
||
|
+ */
|
||
|
+#ifndef _MARK2_ECHO_H
|
||
|
+#define _MARK2_ECHO_H
|
||
|
+
|
||
|
+#ifdef __KERNEL__
|
||
|
+#include <linux/kernel.h>
|
||
|
+#include <linux/slab.h>
|
||
|
+#define MALLOC(a) kmalloc((a), GFP_KERNEL)
|
||
|
+#define FREE(a) kfree(a)
|
||
|
+#else
|
||
|
+#include <stdlib.h>
|
||
|
+#include <unistd.h>
|
||
|
+#include <stdint.h>
|
||
|
+#include <string.h>
|
||
|
+#include <math.h>
|
||
|
+#define MALLOC(a) malloc(a)
|
||
|
+#define FREE(a) free(a)
|
||
|
+#endif
|
||
|
+
|
||
|
+/* Get optimized routines for math */
|
||
|
+#include "arith.h"
|
||
|
+
|
||
|
+#ifndef NULL
|
||
|
+#define NULL 0
|
||
|
+#endif
|
||
|
+#ifndef FALSE
|
||
|
+#define FALSE 0
|
||
|
+#endif
|
||
|
+#ifndef TRUE
|
||
|
+#define TRUE (!FALSE)
|
||
|
+#endif
|
||
|
+
|
||
|
+#include "mec2_const.h"
|
||
|
+
|
||
|
+/* Circular buffer definition */
|
||
|
+typedef struct {
|
||
|
+ int idx_d;
|
||
|
+ int size_d;
|
||
|
+ short *buf_d; /* Twice as large as we need */
|
||
|
+} echo_can_cb_s;
|
||
|
+
|
||
|
+// class definition
|
||
|
+//
|
||
|
+typedef struct {
|
||
|
+ /* Echo canceller definition */
|
||
|
+
|
||
|
+ /* absolute time */
|
||
|
+ int i_d;
|
||
|
+
|
||
|
+ /* pre-computed constants */
|
||
|
+
|
||
|
+ int N_d;
|
||
|
+ int beta2_i;
|
||
|
+
|
||
|
+ // declare accumulators for power computations
|
||
|
+ //
|
||
|
+ int Ly_i;
|
||
|
+ int Lu_i;
|
||
|
+
|
||
|
+ // declare an accumulator for the near-end signal detector
|
||
|
+ //
|
||
|
+ int s_tilde_i;
|
||
|
+ int HCNTR_d;
|
||
|
+
|
||
|
+ // circular buffers and coefficients
|
||
|
+ //
|
||
|
+ int *a_i;
|
||
|
+ short *a_s;
|
||
|
+ echo_can_cb_s y_s;
|
||
|
+ echo_can_cb_s s_s;
|
||
|
+ echo_can_cb_s u_s;
|
||
|
+ echo_can_cb_s y_tilde_s;
|
||
|
+ int y_tilde_i;
|
||
|
+
|
||
|
+ /* Max memory */
|
||
|
+ short max_y_tilde;
|
||
|
+ int max_y_tilde_pos;
|
||
|
+
|
||
|
+} echo_can_state_t;
|
||
|
+
|
||
|
+static inline void init_cb_s(echo_can_cb_s *cb, int len, void *where)
|
||
|
+{
|
||
|
+ cb->buf_d = (short *)where;
|
||
|
+ cb->idx_d = 0;
|
||
|
+ cb->size_d = len;
|
||
|
+}
|
||
|
+
|
||
|
+static inline void add_cc_s(echo_can_cb_s *cb, short newval)
|
||
|
+{
|
||
|
+ /* Can't use modulus because N+M isn't a power of two (generally) */
|
||
|
+ cb->idx_d--;
|
||
|
+ if (cb->idx_d < (int)0)
|
||
|
+ {cb->idx_d += cb->size_d;}
|
||
|
+ /* Load two copies into memory */
|
||
|
+ cb->buf_d[cb->idx_d] = newval;
|
||
|
+ cb->buf_d[cb->idx_d + cb->size_d] = newval;
|
||
|
+}
|
||
|
+
|
||
|
+static inline short get_cc_s(echo_can_cb_s *cb, int pos)
|
||
|
+{
|
||
|
+ /* Load two copies into memory */
|
||
|
+ return cb->buf_d[cb->idx_d + pos];
|
||
|
+}
|
||
|
+
|
||
|
+static inline void init_cc(echo_can_state_t *ec, int N, int maxy, int maxu) {
|
||
|
+
|
||
|
+ void *ptr = ec;
|
||
|
+ unsigned long tmp;
|
||
|
+ /* double-word align past end of state */
|
||
|
+ ptr += sizeof(echo_can_state_t);
|
||
|
+ tmp = (unsigned long)ptr;
|
||
|
+ tmp += 3;
|
||
|
+ tmp &= ~3L;
|
||
|
+ ptr = (void *)tmp;
|
||
|
+
|
||
|
+ // reset parameters
|
||
|
+ //
|
||
|
+ ec->N_d = N;
|
||
|
+ ec->beta2_i = DEFAULT_BETA1_I;
|
||
|
+
|
||
|
+ // allocate coefficient memory
|
||
|
+ //
|
||
|
+ ec->a_i = ptr;
|
||
|
+ ptr += (sizeof(int) * ec->N_d);
|
||
|
+ ec->a_s = ptr;
|
||
|
+ ptr += (sizeof(short) * ec->N_d);
|
||
|
+
|
||
|
+ /* Reset Y circular buffer (short version) */
|
||
|
+ init_cb_s(&ec->y_s, maxy, ptr);
|
||
|
+ ptr += (sizeof(short) * (maxy) * 2);
|
||
|
+
|
||
|
+ /* Reset Sig circular buffer (short version for FIR filter) */
|
||
|
+ init_cb_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr);
|
||
|
+ ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2);
|
||
|
+
|
||
|
+ init_cb_s(&ec->u_s, maxu, ptr);
|
||
|
+ ptr += (sizeof(short) * maxu * 2);
|
||
|
+
|
||
|
+ // allocate a buffer for the reference signal power computation
|
||
|
+ //
|
||
|
+ init_cb_s(&ec->y_tilde_s, ec->N_d, ptr);
|
||
|
+
|
||
|
+
|
||
|
+ // reset absolute time
|
||
|
+ //
|
||
|
+ ec->i_d = (int)0;
|
||
|
+
|
||
|
+ // reset the power computations (for y and u)
|
||
|
+ //
|
||
|
+ ec->Ly_i = DEFAULT_CUTOFF_I;
|
||
|
+ ec->Lu_i = DEFAULT_CUTOFF_I;
|
||
|
+
|
||
|
+ // reset the near-end speech detector
|
||
|
+ //
|
||
|
+ ec->s_tilde_i = 0;
|
||
|
+ ec->HCNTR_d = (int)0;
|
||
|
+
|
||
|
+ // exit gracefully
|
||
|
+ //
|
||
|
+}
|
||
|
+
|
||
|
+static inline void echo_can_free(echo_can_state_t *ec)
|
||
|
+{
|
||
|
+ FREE(ec);
|
||
|
+}
|
||
|
+
|
||
|
+static inline short echo_can_update(echo_can_state_t *ec, short iref, short isig) {
|
||
|
+
|
||
|
+ /* declare local variables that are used more than once
|
||
|
+ */
|
||
|
+ int k;
|
||
|
+ int rs;
|
||
|
+ short u;
|
||
|
+ int Py_i;
|
||
|
+ int two_beta_i;
|
||
|
+
|
||
|
+ /***************************************************************************
|
||
|
+ //
|
||
|
+ // flow A on pg. 428
|
||
|
+ //
|
||
|
+ ***************************************************************************/
|
||
|
+
|
||
|
+ /* eq. (16): high-pass filter the input to generate the next value;
|
||
|
+ // push the current value into the circular buffer
|
||
|
+ //
|
||
|
+ // sdc_im1_d = sdc_d;
|
||
|
+ // sdc_d = sig;
|
||
|
+ // s_i_d = sdc_d;
|
||
|
+ // s_d = s_i_d;
|
||
|
+ // s_i_d = (float)(1.0 - gamma_d) * s_i_d
|
||
|
+ + (float)(0.5 * (1.0 - gamma_d)) * (sdc_d - sdc_im1_d); */
|
||
|
+
|
||
|
+
|
||
|
+ /* Delete last sample from power estimate */
|
||
|
+ ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I;
|
||
|
+ /* push the reference data onto the circular buffer */
|
||
|
+ add_cc_s(&ec->y_s, iref);
|
||
|
+
|
||
|
+ /* eq. (2): compute r in fixed-point */
|
||
|
+ rs = CONVOLVE2(ec->a_s, ec->y_s.buf_d + ec->y_s.idx_d, ec->N_d);
|
||
|
+ rs >>= 15;
|
||
|
+
|
||
|
+ /* eq. (3): compute the output value (see figure 3) and the error
|
||
|
+ // note: the error is the same as the output signal when near-end
|
||
|
+ // speech is not present
|
||
|
+ */
|
||
|
+ u = isig - rs;
|
||
|
+
|
||
|
+ add_cc_s(&ec->u_s, u);
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+ /* Delete oldest part of received s_tilde */
|
||
|
+ ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 ));
|
||
|
+
|
||
|
+ /* push the signal on the circular buffer, too */
|
||
|
+ add_cc_s(&ec->s_s, isig);
|
||
|
+ ec->s_tilde_i += abs(isig);
|
||
|
+ ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_YT_I;
|
||
|
+
|
||
|
+ /* Add to our list of recent y_tilde's */
|
||
|
+ add_cc_s(&ec->y_tilde_s, ec->y_tilde_i);
|
||
|
+
|
||
|
+ /****************************************************************************
|
||
|
+ //
|
||
|
+ // flow B on pg. 428
|
||
|
+ //
|
||
|
+ ****************************************************************************/
|
||
|
+
|
||
|
+ /* compute the new convergence factor
|
||
|
+ */
|
||
|
+ Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->Ly_i >> DEFAULT_SIGMA_LY_I);
|
||
|
+ Py_i >>= 15;
|
||
|
+ if (ec->HCNTR_d > 0) {
|
||
|
+ Py_i = (1 << 15);
|
||
|
+ }
|
||
|
+
|
||
|
+#if 0
|
||
|
+ printf("Py: %e, Py_i: %e\n", Py, Py_i * AMPL_SCALE_1);
|
||
|
+#endif
|
||
|
+
|
||
|
+ /* Vary rate of adaptation depending on position in the file
|
||
|
+ // Do not do this for the first (DEFAULT_UPDATE_TIME) secs after speech
|
||
|
+ // has begun of the file to allow the echo cancellor to estimate the
|
||
|
+ // channel accurately
|
||
|
+ */
|
||
|
+#if 0
|
||
|
+ if (ec->start_speech_d != 0 ){
|
||
|
+ if ( ec->i_d > (DEFAULT_T0 + ec->start_speech_d)*(SAMPLE_FREQ) ){
|
||
|
+ ec->beta2_d = max_cc_float(MIN_BETA,
|
||
|
+ DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((ec->i_d/(float)SAMPLE_FREQ) -
|
||
|
+ DEFAULT_T0 -
|
||
|
+ ec->start_speech_d)));
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else {ec->beta2_d = DEFAULT_BETA1;}
|
||
|
+#endif
|
||
|
+
|
||
|
+ ec->beta2_i = DEFAULT_BETA1_I; /* Fixed point, inverted */
|
||
|
+
|
||
|
+ two_beta_i = (ec->beta2_i * Py_i) >> 15; /* Fixed point version, inverted */
|
||
|
+ if (!two_beta_i)
|
||
|
+ two_beta_i++;
|
||
|
+
|
||
|
+ /* Update Lu_i (Suppressed power estimate) */
|
||
|
+ ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ;
|
||
|
+ ec->Lu_i += abs(u);
|
||
|
+
|
||
|
+ /* eq. (10): update power estimate of the reference
|
||
|
+ */
|
||
|
+ ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ;
|
||
|
+ ec->Ly_i += abs(iref);
|
||
|
+
|
||
|
+ if (ec->Ly_i < DEFAULT_CUTOFF_I)
|
||
|
+ ec->Ly_i = DEFAULT_CUTOFF_I;
|
||
|
+
|
||
|
+#if 0
|
||
|
+ printf("Float: %e, Int: %e\n", ec->Ly_d, (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * AMPL_SCALE_1);
|
||
|
+#endif
|
||
|
+
|
||
|
+ if (ec->y_tilde_i > ec->max_y_tilde) {
|
||
|
+ /* New highest y_tilde with full life */
|
||
|
+ ec->max_y_tilde = ec->y_tilde_i;
|
||
|
+ ec->max_y_tilde_pos = ec->N_d - 1;
|
||
|
+ } else if (--ec->max_y_tilde_pos < 0) {
|
||
|
+ /* Time to find new max y tilde... */
|
||
|
+ ec->max_y_tilde = MAX16(ec->y_tilde_s.buf_d + ec->y_tilde_s.idx_d, ec->N_d, &ec->max_y_tilde_pos);
|
||
|
+ }
|
||
|
+
|
||
|
+ if ((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde)
|
||
|
+ {
|
||
|
+ ec->HCNTR_d = DEFAULT_HANGT;
|
||
|
+ }
|
||
|
+ else if (ec->HCNTR_d > (int)0)
|
||
|
+ {
|
||
|
+ ec->HCNTR_d--;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* update coefficients if no near-end speech and we have enough signal
|
||
|
+ * to bother trying to update.
|
||
|
+ */
|
||
|
+ if (!ec->HCNTR_d && !(ec->i_d % DEFAULT_M) &&
|
||
|
+ (ec->Lu_i > MIN_UPDATE_THRESH_I)) {
|
||
|
+ // loop over all filter coefficients
|
||
|
+ //
|
||
|
+ for (k=0; k<ec->N_d; k++) {
|
||
|
+
|
||
|
+ // eq. (7): compute an expectation over M_d samples
|
||
|
+ //
|
||
|
+ int grad2;
|
||
|
+ grad2 = CONVOLVE2(ec->u_s.buf_d + ec->u_s.idx_d,
|
||
|
+ ec->y_s.buf_d + ec->y_s.idx_d + k, DEFAULT_M);
|
||
|
+ // eq. (7): update the coefficient
|
||
|
+ //
|
||
|
+ ec->a_i[k] += grad2 / two_beta_i;
|
||
|
+ ec->a_s[k] = ec->a_i[k] >> 16;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ /* paragraph below eq. (15): if no near-end speech,
|
||
|
+ // check for residual error suppression
|
||
|
+ */
|
||
|
+#ifndef NO_ECHO_SUPPRESSOR
|
||
|
+#ifdef AGGRESSIVE_SUPPRESSOR
|
||
|
+ if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) {
|
||
|
+ u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1);
|
||
|
+ u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1);
|
||
|
+ }
|
||
|
+#else
|
||
|
+ if ((ec->HCNTR_d == 0) && ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I)) {
|
||
|
+ u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1);
|
||
|
+ }
|
||
|
+#endif
|
||
|
+#endif
|
||
|
+
|
||
|
+#if 0
|
||
|
+ if ((ec->HCNTR_d == 0) && ((ec->Lu_d/ec->Ly_d) < DEFAULT_SUPPR) &&
|
||
|
+ (ec->Lu_d/ec->Ly_d > EC_MIN_DB_VALUE)) {
|
||
|
+ suppr_factor = (10/(float)(SUPPR_FLOOR-SUPPR_CEIL))*log(ec->Lu_d/ec->Ly_d)
|
||
|
+ - SUPPR_CEIL/(float)(SUPPR_FLOOR - SUPPR_CEIL);
|
||
|
+
|
||
|
+ u_suppr = pow(10.0,(suppr_factor)*RES_SUPR_FACTOR/10.0)*u_suppr;
|
||
|
+
|
||
|
+ }
|
||
|
+#endif
|
||
|
+ ec->i_d++;
|
||
|
+ return u;
|
||
|
+}
|
||
|
+
|
||
|
+static inline echo_can_state_t *echo_can_create(int len, int adaption_mode)
|
||
|
+{
|
||
|
+ echo_can_state_t *ec;
|
||
|
+ int maxy;
|
||
|
+ int maxu;
|
||
|
+ maxy = len + DEFAULT_M;
|
||
|
+ maxu = DEFAULT_M;
|
||
|
+ if (maxy < (1 << DEFAULT_ALPHA_YT_I))
|
||
|
+ maxy = (1 << DEFAULT_ALPHA_YT_I);
|
||
|
+ if (maxy < (1 << DEFAULT_SIGMA_LY_I))
|
||
|
+ maxy = (1 << DEFAULT_SIGMA_LY_I);
|
||
|
+ if (maxu < (1 << DEFAULT_SIGMA_LU_I))
|
||
|
+ maxu = (1 << DEFAULT_SIGMA_LU_I);
|
||
|
+ ec = (echo_can_state_t *)MALLOC(sizeof(echo_can_state_t) +
|
||
|
+ 4 + /* align */
|
||
|
+ sizeof(int) * len + /* a_i */
|
||
|
+ sizeof(short) * len + /* a_s */
|
||
|
+ 2 * sizeof(short) * (maxy) + /* y_s */
|
||
|
+ 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */
|
||
|
+ 2 * sizeof(short) * (maxu) + /* u_s */
|
||
|
+ 2 * sizeof(short) * len); /* y_tilde_s */
|
||
|
+ if (ec) {
|
||
|
+ memset(ec, 0, sizeof(echo_can_state_t) +
|
||
|
+ 4 + /* align */
|
||
|
+ sizeof(int) * len + /* a_i */
|
||
|
+ sizeof(short) * len + /* a_s */
|
||
|
+ 2 * sizeof(short) * (maxy) + /* y_s */
|
||
|
+ 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */
|
||
|
+ 2 * sizeof(short) * (maxu) + /* u_s */
|
||
|
+ 2 * sizeof(short) * len); /* y_tilde_s */
|
||
|
+ init_cc(ec, len, maxy, maxu);
|
||
|
+ }
|
||
|
+ return ec;
|
||
|
+}
|
||
|
+
|
||
|
+static inline int echo_can_traintap(echo_can_state_t *ec, int pos, short val)
|
||
|
+{
|
||
|
+ /* Reset hang counter to avoid adjustments after
|
||
|
+ initial forced training */
|
||
|
+ ec->HCNTR_d = ec->N_d << 1;
|
||
|
+ if (pos >= ec->N_d)
|
||
|
+ return 1;
|
||
|
+ ec->a_i[pos] = val << 17;
|
||
|
+ ec->a_s[pos] = val << 1;
|
||
|
+ if (++pos >= ec->N_d)
|
||
|
+ return 1;
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+#endif
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/mec3.h mISDN/drivers/isdn/hardware/mISDN/mec3.h
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/mec3.h 1970-01-01 01:00:00.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/mec3.h 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -0,0 +1,243 @@
|
||
|
+/*
|
||
|
+ * Mark's Third Echo Canceller
|
||
|
+ *
|
||
|
+ * Copyright (C) 2003, Digium, Inc.
|
||
|
+ *
|
||
|
+ * This program is free software and may be used
|
||
|
+ * and distributed under the terms of the GNU General Public
|
||
|
+ * License, incorporated herein by reference.
|
||
|
+ *
|
||
|
+ * Dedicated to the crew of the Columbia, STS-107 for their
|
||
|
+ * bravery and courageous sacrifice for science.
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+#ifndef _MARK3_ECHO_H
|
||
|
+#define _MARK3_ECHO_H
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+#ifdef __KERNEL__
|
||
|
+#include <linux/kernel.h>
|
||
|
+#include <linux/slab.h>
|
||
|
+#define MALLOC(a) kmalloc((a), GFP_KERNEL)
|
||
|
+#define FREE(a) kfree(a)
|
||
|
+#else
|
||
|
+#include <stdlib.h>
|
||
|
+#include <unistd.h>
|
||
|
+#include <stdint.h>
|
||
|
+#include <string.h>
|
||
|
+#include <math.h>
|
||
|
+#define MALLOC(a) malloc(a)
|
||
|
+#define FREE(a) free(a)
|
||
|
+#endif
|
||
|
+
|
||
|
+/* Features */
|
||
|
+
|
||
|
+/*
|
||
|
+ * DO_BACKUP -- Backup coefficients, and revert in the presense of double talk to try to prevent
|
||
|
+ * them from diverging during the ramp-up before the DTD kicks in
|
||
|
+ */
|
||
|
+/* #define DO_BACKUP */
|
||
|
+
|
||
|
+#define STEP_SHIFT 2 /* Convergence rate higher = slower / better (as a shift) */
|
||
|
+
|
||
|
+#define SIGMA_REF_PWR 655 /* Keep denominator from being 0 */
|
||
|
+
|
||
|
+#define MIN_TX_ENERGY 256 /* Must have at least this much reference */
|
||
|
+#define MIN_RX_ENERGY 32 /* Must have at least this much receive energy */
|
||
|
+
|
||
|
+#define MAX_ATTENUATION_SHIFT 6 /* Maximum amount of loss we care about */
|
||
|
+#define MAX_BETA 1024
|
||
|
+
|
||
|
+#define SUPPR_SHIFT 4 /* Amount of loss at which we suppress audio */
|
||
|
+
|
||
|
+#define HANG_TIME 600 /* Hangover time */
|
||
|
+
|
||
|
+#define NTAPS 2048 /* Maximum number of echo can taps */
|
||
|
+
|
||
|
+#define BACKUP 256 /* Backup every this number of samples */
|
||
|
+
|
||
|
+#define POWER_OFFSET 5 /* Shift power by this amount to be sure we don't overflow the
|
||
|
+ reference power. Higher = less likely to overflow, lower = more accurage */
|
||
|
+
|
||
|
+#include "arith.h"
|
||
|
+
|
||
|
+typedef struct {
|
||
|
+ short buf[NTAPS * 2];
|
||
|
+ short max;
|
||
|
+ int maxexp;
|
||
|
+} cbuf_s;
|
||
|
+
|
||
|
+typedef struct {
|
||
|
+ short a_s[NTAPS]; /* Coefficients in shorts */
|
||
|
+ int a_i[NTAPS]; /* Coefficients in ints*/
|
||
|
+#ifdef DO_BACKUP
|
||
|
+ int b_i[NTAPS]; /* Coefficients (backup1) */
|
||
|
+ int c_i[NTAPS]; /* Coefficients (backup2) */
|
||
|
+#endif
|
||
|
+ cbuf_s ref; /* Reference excitation */
|
||
|
+ cbuf_s sig; /* Signal (echo + near end + noise) */
|
||
|
+ cbuf_s e; /* Error */
|
||
|
+ int refpwr; /* Reference power */
|
||
|
+ int taps; /* Number of taps */
|
||
|
+ int tappwr; /* Power of taps */
|
||
|
+ int hcntr; /* Hangtime counter */
|
||
|
+ int pos; /* Position in curcular buffers */
|
||
|
+ int backup; /* Backup timer */
|
||
|
+} echo_can_state_t;
|
||
|
+
|
||
|
+static inline void echo_can_free(echo_can_state_t *ec)
|
||
|
+{
|
||
|
+ FREE(ec);
|
||
|
+}
|
||
|
+
|
||
|
+static inline void buf_add(cbuf_s *b, short sample, int pos, int taps)
|
||
|
+{
|
||
|
+ /* Store and keep track of maxima */
|
||
|
+ int x;
|
||
|
+ b->buf[pos] = sample;
|
||
|
+ b->buf[pos + taps] = sample;
|
||
|
+ if (sample > b->max) {
|
||
|
+ b->max = sample;
|
||
|
+ b->maxexp = taps;
|
||
|
+ } else {
|
||
|
+ b->maxexp--;
|
||
|
+ if (!b->maxexp) {
|
||
|
+ b->max = 0;
|
||
|
+ for (x=0;x<taps;x++)
|
||
|
+ if (b->max < abs(b->buf[pos + x])) {
|
||
|
+ b->max = abs(b->buf[pos + x]);
|
||
|
+ b->maxexp = x + 1;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static inline short echo_can_update(echo_can_state_t *ec, short ref, short sig)
|
||
|
+{
|
||
|
+ int x;
|
||
|
+ short u;
|
||
|
+ int refpwr;
|
||
|
+ int beta; /* Factor */
|
||
|
+ int se; /* Simulated echo */
|
||
|
+#ifdef DO_BACKUP
|
||
|
+ if (!ec->backup) {
|
||
|
+ /* Backup coefficients periodically */
|
||
|
+ ec->backup = BACKUP;
|
||
|
+ memcpy(ec->c_i,ec->b_i,sizeof(ec->c_i));
|
||
|
+ memcpy(ec->b_i,ec->a_i,sizeof(ec->b_i));
|
||
|
+ } else
|
||
|
+ ec->backup--;
|
||
|
+#endif
|
||
|
+ /* Remove old samples from reference power calculation */
|
||
|
+ ec->refpwr -= ((ec->ref.buf[ec->pos] * ec->ref.buf[ec->pos]) >> POWER_OFFSET);
|
||
|
+
|
||
|
+ /* Store signal and reference */
|
||
|
+ buf_add(&ec->ref, ref, ec->pos, ec->taps);
|
||
|
+ buf_add(&ec->sig, sig, ec->pos, ec->taps);
|
||
|
+
|
||
|
+ /* Add new reference power */
|
||
|
+ ec->refpwr += ((ec->ref.buf[ec->pos] * ec->ref.buf[ec->pos]) >> POWER_OFFSET);
|
||
|
+
|
||
|
+
|
||
|
+ /* Calculate simulated echo */
|
||
|
+ se = CONVOLVE2(ec->a_s, ec->ref.buf + ec->pos, ec->taps);
|
||
|
+ se >>= 15;
|
||
|
+
|
||
|
+ u = sig - se;
|
||
|
+ if (ec->hcntr)
|
||
|
+ ec->hcntr--;
|
||
|
+
|
||
|
+ /* Store error */
|
||
|
+ buf_add(&ec->e, sig, ec->pos, ec->taps);
|
||
|
+ if ((ec->ref.max > MIN_TX_ENERGY) &&
|
||
|
+ (ec->sig.max > MIN_RX_ENERGY) &&
|
||
|
+ (ec->e.max > (ec->ref.max >> MAX_ATTENUATION_SHIFT))) {
|
||
|
+ /* We have sufficient energy */
|
||
|
+ if (ec->sig.max < (ec->ref.max >> 1)) {
|
||
|
+ /* No double talk */
|
||
|
+ if (!ec->hcntr) {
|
||
|
+ refpwr = ec->refpwr >> (16 - POWER_OFFSET);
|
||
|
+ if (refpwr < SIGMA_REF_PWR)
|
||
|
+ refpwr = SIGMA_REF_PWR;
|
||
|
+ beta = (u << 16) / refpwr;
|
||
|
+ beta >>= STEP_SHIFT;
|
||
|
+ if (beta > MAX_BETA)
|
||
|
+ beta = 0;
|
||
|
+ if (beta < -MAX_BETA)
|
||
|
+ beta = 0;
|
||
|
+ /* Update coefficients */
|
||
|
+ for (x=0;x<ec->taps;x++) {
|
||
|
+ ec->a_i[x] += beta * ec->ref.buf[ec->pos + x];
|
||
|
+ ec->a_s[x] = ec->a_i[x] >> 16;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+#ifdef DO_BACKUP
|
||
|
+ if (!ec->hcntr) {
|
||
|
+ /* Our double talk detector is turning on for the first time. Revert
|
||
|
+ our coefficients, since we're probably well into the double talk by now */
|
||
|
+ memcpy(ec->a_i, ec->c_i, sizeof(ec->a_i));
|
||
|
+ for (x=0;x<ec->taps;x++) {
|
||
|
+ ec->a_s[x] = ec->a_i[x] >> 16;
|
||
|
+ }
|
||
|
+ }
|
||
|
+#endif
|
||
|
+ /* Reset hang-time counter, and prevent backups */
|
||
|
+ ec->hcntr = HANG_TIME;
|
||
|
+#ifdef DO_BACKUP
|
||
|
+ ec->backup = BACKUP;
|
||
|
+#endif
|
||
|
+ }
|
||
|
+ }
|
||
|
+#ifndef NO_ECHO__SUPPRESSOR
|
||
|
+ if (ec->e.max < (ec->ref.max >> SUPPR_SHIFT)) {
|
||
|
+ /* Suppress residual echo */
|
||
|
+ u *= u;
|
||
|
+ u >>= 16;
|
||
|
+ }
|
||
|
+#endif
|
||
|
+ ec->pos--;
|
||
|
+ if (ec->pos < 0)
|
||
|
+ ec->pos = ec->taps-1;
|
||
|
+ return u;
|
||
|
+}
|
||
|
+
|
||
|
+static inline echo_can_state_t *echo_can_create(int taps, int adaption_mode)
|
||
|
+{
|
||
|
+ echo_can_state_t *ec;
|
||
|
+ int x;
|
||
|
+
|
||
|
+ //taps = NTAPS;
|
||
|
+ ec = MALLOC(sizeof(echo_can_state_t));
|
||
|
+ if (ec) {
|
||
|
+ memset(ec, 0, sizeof(echo_can_state_t));
|
||
|
+ ec->taps = taps;
|
||
|
+ ec->pos = ec->taps-1;
|
||
|
+ for (x=0;x<31;x++) {
|
||
|
+ if ((1 << x) >= ec->taps) {
|
||
|
+ ec->tappwr = x;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return ec;
|
||
|
+}
|
||
|
+
|
||
|
+static inline int echo_can_traintap(echo_can_state_t *ec, int pos, short val)
|
||
|
+{
|
||
|
+ /* Reset hang counter to avoid adjustments after
|
||
|
+ initial forced training */
|
||
|
+ ec->hcntr = ec->taps << 1;
|
||
|
+ if (pos >= ec->taps)
|
||
|
+ return 1;
|
||
|
+ ec->a_i[pos] = val << 17;
|
||
|
+ ec->a_s[pos] = val << 1;
|
||
|
+ if (++pos >= ec->taps)
|
||
|
+ return 1;
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+#endif
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/mec.h mISDN/drivers/isdn/hardware/mISDN/mec.h
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/mec.h 1970-01-01 01:00:00.000000000 +0100
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/mec.h 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -0,0 +1,308 @@
|
||
|
+/*
|
||
|
+ * Mark's Echo Canceller
|
||
|
+ *
|
||
|
+ * Mark Spencer <markster@linux-support.net>
|
||
|
+ *
|
||
|
+ * Simple, LMS Echo Canceller with double talk detection.
|
||
|
+ * Partly based on the TI App note:
|
||
|
+ * "Digital Voice Echo Canceller with a TMS 32020"
|
||
|
+ *
|
||
|
+ * Special additional thanks to:
|
||
|
+ * Jim Dixon (Lambda Telecommunications)
|
||
|
+ * Iman Ghobrial (Adtran, Inc.)
|
||
|
+ *
|
||
|
+ * Copyright (C) 2001, Linux Support Services, Inc.
|
||
|
+ *
|
||
|
+ * This program is free software and may be used and
|
||
|
+ * distributed according to the terms of the GNU
|
||
|
+ * General Public License, incorporated herein by
|
||
|
+ * reference.
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+#ifndef _MEC_H
|
||
|
+#define _MEC_H
|
||
|
+
|
||
|
+/* You have to express the size of the echo canceller in taps as
|
||
|
+ a power of 2 (6 = 64 taps, 7 = 128 taps, 8 = 256 taps) */
|
||
|
+#define NUM_TAPS_POW2 6 /* Size of echo canceller in power of 2 (taps) */
|
||
|
+#define NUM_TAPS (1 << NUM_TAPS_POW2) /* Actual number of taps */
|
||
|
+#define TAP_MASK (NUM_TAPS-1)
|
||
|
+
|
||
|
+
|
||
|
+#define SIGMA_LU_POW NUM_TAPS_POW2
|
||
|
+#define SIGMA_LY_POW NUM_TAPS_POW2
|
||
|
+#define SIGMA_YT_POW (NUM_TAPS_POW2 - 1)
|
||
|
+#define SIGMA_ST_POW (NUM_TAPS_POW2 - 1)
|
||
|
+
|
||
|
+#define BETA_POW 8
|
||
|
+
|
||
|
+#define CUTOFF_S 4
|
||
|
+
|
||
|
+/* The higher you make this, the better the quality, but the more CPU time required */
|
||
|
+#define MIN_QUALITY 100
|
||
|
+
|
||
|
+/* This optimization saves a lot of processor but may degrade quality */
|
||
|
+#define OPTIMIZEDIV
|
||
|
+
|
||
|
+#if 0
|
||
|
+/* This converges much more slowly but saves processor */
|
||
|
+#define MIN_UPDATE 256
|
||
|
+#define MIN_SKIP 8
|
||
|
+#endif
|
||
|
+
|
||
|
+#define HANG_T 600 /* 600 samples, or 75ms */
|
||
|
+
|
||
|
+typedef struct mark_ec {
|
||
|
+ /* Circular position */
|
||
|
+ int cpos;
|
||
|
+ short y[NUM_TAPS]; /* Last N samples (relative to cpos) transmitted */
|
||
|
+ short y_abs[NUM_TAPS]; /* Last N samples (relative to cpos) transmitted (abs value) */
|
||
|
+ short s[NUM_TAPS]; /* Last N samples (relative to cpos) received */
|
||
|
+ short s_abs[NUM_TAPS]; /* Last N samples (relative to cpos) received (abs value) */
|
||
|
+ short u[NUM_TAPS]; /* Last N samples (relative to cpos) with echo removed */
|
||
|
+ short u_abs[NUM_TAPS]; /* Last N samples (relative to cpos) with echo removed */
|
||
|
+
|
||
|
+ int Ly; /* tx power */
|
||
|
+ int Lu; /* Power of echo-cancelled output */
|
||
|
+
|
||
|
+ int Ty[NUM_TAPS]; /* Short term power estimate of transmit */
|
||
|
+ int Ts; /* Short term power estimate of received signal */
|
||
|
+
|
||
|
+ int a[NUM_TAPS]; /* Tap weight coefficients (not relative) */
|
||
|
+
|
||
|
+ short sdc[NUM_TAPS]; /* Near end signal before High Pass Filter */
|
||
|
+
|
||
|
+ int samples; /* Sample count */
|
||
|
+ int pass; /* Number of passes we've made */
|
||
|
+
|
||
|
+ int hangt;
|
||
|
+
|
||
|
+ int lastmax; /* Optimize maximum search */
|
||
|
+ int maxTy; /* Maximum Ty */
|
||
|
+} echo_can_state_t;
|
||
|
+
|
||
|
+#define INLINE inline
|
||
|
+
|
||
|
+#ifdef __KERNEL__
|
||
|
+#include <linux/kernel.h>
|
||
|
+#include <linux/slab.h>
|
||
|
+#define MALLOC(a) kmalloc((a), GFP_KERNEL)
|
||
|
+#define FREE(a) kfree((a))
|
||
|
+#else
|
||
|
+#include <stdlib.h>
|
||
|
+#include <unistd.h>
|
||
|
+#include <stdint.h>
|
||
|
+#include <string.h>
|
||
|
+#define MALLOC(a) malloc(a)
|
||
|
+#define FREE(a) free(a)
|
||
|
+#endif
|
||
|
+
|
||
|
+static INLINE echo_can_state_t *echo_can_create(int len, int adaption_mode)
|
||
|
+{
|
||
|
+ echo_can_state_t *ec;
|
||
|
+ /* Uhm, we're only one length, sorry. */
|
||
|
+ ec = MALLOC(sizeof(echo_can_state_t));
|
||
|
+ if (ec)
|
||
|
+ memset(ec, 0, sizeof(*ec));
|
||
|
+ return ec;
|
||
|
+}
|
||
|
+
|
||
|
+#define PASSPOS 32000
|
||
|
+#undef PASSPOS
|
||
|
+
|
||
|
+static INLINE void echo_can_free(echo_can_state_t *ec)
|
||
|
+{
|
||
|
+ FREE(ec);
|
||
|
+}
|
||
|
+
|
||
|
+static INLINE int16_t echo_can_update(echo_can_state_t *ec, int16_t tx, int16_t rx)
|
||
|
+{
|
||
|
+ /* Process a sample, where tx is the near end and rx is the far end + echo */
|
||
|
+
|
||
|
+ int suppr;
|
||
|
+ int nsuppr;
|
||
|
+ short rxabs, txabs;
|
||
|
+ register int Lu;
|
||
|
+ register int x;
|
||
|
+ register int pos;
|
||
|
+ register int r_hat; /* Estimated echo */
|
||
|
+ int oldrxabs;
|
||
|
+ int oldtxabs;
|
||
|
+ int oldsupprabs;
|
||
|
+ int supprabs;
|
||
|
+#ifdef MIN_UPDATE
|
||
|
+ int totalupd;
|
||
|
+#endif
|
||
|
+
|
||
|
+ txabs = abs(tx);
|
||
|
+ rxabs = abs(rx);
|
||
|
+
|
||
|
+ ec->pass++;
|
||
|
+
|
||
|
+ r_hat = 0;
|
||
|
+
|
||
|
+ /* Load next value */
|
||
|
+ ec->y[ec->cpos] = tx;
|
||
|
+
|
||
|
+ /* Load next abs value */
|
||
|
+ oldtxabs = ec->y_abs[ec->cpos];
|
||
|
+ ec->y_abs[ec->cpos] = txabs;
|
||
|
+
|
||
|
+ /* Bring in receive value (near-end signal) */
|
||
|
+ ec->sdc[ec->cpos] = rx;
|
||
|
+
|
||
|
+ /* Bring in receive value absolute value */
|
||
|
+ oldrxabs = ec->s_abs[ec->cpos];
|
||
|
+ ec->s_abs[ec->cpos] = rxabs;
|
||
|
+
|
||
|
+ Lu = ec->Lu | 1;
|
||
|
+
|
||
|
+#if 0
|
||
|
+ /* Apply first order high pass filter (3 dB @ 160 Hz) */
|
||
|
+ tx = ec->s[ec->cpos] = (1.0-DEFGAMMA) * ec->s[(ec->cpos - 1) & TAP_MASK] +
|
||
|
+ 0.5 * (1.0-DEFGAMMA) * ( ec->sdc[(ec->cpos - 1) & TAP_MASK] - ec->sdc[(ec->cpos - 2) & TAP_MASK]);
|
||
|
+#endif
|
||
|
+
|
||
|
+ /* Estimate echo */
|
||
|
+ pos = ec->cpos;
|
||
|
+ for (x=0;x<NUM_TAPS;x++) {
|
||
|
+ r_hat += ec->a[x] * ec->y[pos];
|
||
|
+ /* Go backwards in time and loop around circular buffer */
|
||
|
+ pos = (pos - 1) & TAP_MASK;
|
||
|
+ }
|
||
|
+
|
||
|
+ r_hat >>= 16;
|
||
|
+
|
||
|
+ if (ec->hangt > 0)
|
||
|
+ ec->hangt--;
|
||
|
+
|
||
|
+ /* printf("rx: %F, rhat: %F\n", rx, r_hat); */
|
||
|
+ /* Calculate suppressed amount */
|
||
|
+ suppr = rx - r_hat;
|
||
|
+
|
||
|
+ if (ec->pass > NUM_TAPS) {
|
||
|
+ /* Have to have enough taps to start with */
|
||
|
+ if (ec->maxTy > ec->Ts) {
|
||
|
+ /* There is no near-end speech detected */
|
||
|
+ if (!ec->hangt) {
|
||
|
+ /* We're not in the hang-time from the end of near-end speech */
|
||
|
+ if ((ec->Ly > 1024) && ((ec->Ly / Lu) < MIN_QUALITY)) {
|
||
|
+#ifdef OPTIMIZEDIV
|
||
|
+ /* We both have enough signal on the transmit */
|
||
|
+ nsuppr = (suppr << 18) / ec->Ly;
|
||
|
+
|
||
|
+ if (nsuppr > 32767)
|
||
|
+ nsuppr = 32767;
|
||
|
+ if (nsuppr < -32768)
|
||
|
+ nsuppr = -32768;
|
||
|
+
|
||
|
+ nsuppr /= ec->Ly;
|
||
|
+#else
|
||
|
+ /* We both have enough signal on the transmit */
|
||
|
+ nsuppr = (suppr << 16) / ec->Ly;
|
||
|
+
|
||
|
+ if (nsuppr > 32767)
|
||
|
+ nsuppr = 32767;
|
||
|
+ if (nsuppr < -32768)
|
||
|
+ nsuppr = -32768;
|
||
|
+
|
||
|
+#endif
|
||
|
+
|
||
|
+ /* Update coefficients */
|
||
|
+ pos = ec->cpos;
|
||
|
+#ifdef MIN_UPDATE
|
||
|
+ totalupd =0;
|
||
|
+#endif
|
||
|
+ for (x=0;x<NUM_TAPS;x++) {
|
||
|
+ register int adj;
|
||
|
+ adj = ec->y[pos] * nsuppr;
|
||
|
+#ifndef OPTIMIZEDIV
|
||
|
+ adj /= ec->Ly;
|
||
|
+ adj >>= BETA_POW;
|
||
|
+#else
|
||
|
+ adj >>= BETA_POW + 2;
|
||
|
+#endif
|
||
|
+#ifdef PASSPOS
|
||
|
+ if (ec->pass > PASSPOS)
|
||
|
+ printf("tx: %d, old %d: %d, adj %d, nsuppr: %d, power: %d\n", tx, x, ec->a[x], adj, nsuppr, ec->Ly);
|
||
|
+#endif
|
||
|
+ ec->a[x] += adj;
|
||
|
+#ifdef MIN_UPDATE
|
||
|
+ totalupd += abs(adj);
|
||
|
+#endif
|
||
|
+ /* Go backwards in time and loop around circular buffer */
|
||
|
+ pos = (pos - 1) & TAP_MASK;
|
||
|
+ }
|
||
|
+#ifdef MIN_UPDATE
|
||
|
+ /* If we didn't update at least this much, delay for many more taps */
|
||
|
+ if (totalupd < MIN_UPDATE) {
|
||
|
+ ec->hangt += MIN_SKIP;
|
||
|
+ }
|
||
|
+#endif
|
||
|
+ }
|
||
|
+
|
||
|
+ }
|
||
|
+ } else
|
||
|
+ /* Near end speech detected */
|
||
|
+ ec->hangt = HANG_T;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Save supression and absolute values */
|
||
|
+ supprabs = abs(suppr);
|
||
|
+ oldsupprabs = ec->u_abs[ec->cpos];
|
||
|
+ ec->u[ec->cpos] = suppr;
|
||
|
+ ec->u_abs[ec->cpos] = supprabs;
|
||
|
+
|
||
|
+ /* Update tx power */
|
||
|
+ ec->Ly += (txabs >> SIGMA_LY_POW) - (oldtxabs >> SIGMA_LY_POW);
|
||
|
+
|
||
|
+ /* Update rx power */
|
||
|
+ ec->Lu += (supprabs >> SIGMA_LU_POW) - (oldsupprabs >> SIGMA_LU_POW);
|
||
|
+
|
||
|
+ /* Short term power of tx */
|
||
|
+ ec->Ty[ec->cpos] = ec->Ty[(ec->cpos - 1) & TAP_MASK] +
|
||
|
+ ((txabs >> SIGMA_YT_POW ) - (oldtxabs >> SIGMA_YT_POW));
|
||
|
+
|
||
|
+ /* Keep track of highest */
|
||
|
+ if (ec->lastmax == ec->cpos) {
|
||
|
+ register int maxTy = 0;
|
||
|
+ /* Have to loop through and find the new highest since our old highest expired */
|
||
|
+ /* Estimate echo */
|
||
|
+ pos = ec->cpos;
|
||
|
+ for (x=0;x<NUM_TAPS;x++) {
|
||
|
+ if (ec->Ty[pos] > maxTy)
|
||
|
+ maxTy = ec->Ty[pos];
|
||
|
+ /* Go backwards in time and loop around circular buffer */
|
||
|
+ pos = (pos - 1) & TAP_MASK;
|
||
|
+ }
|
||
|
+ ec->maxTy = maxTy;
|
||
|
+ } else {
|
||
|
+ /* Just keep the highest */
|
||
|
+ if (ec->Ty[ec->cpos] > ec->maxTy) {
|
||
|
+ ec->maxTy = ec->Ty[ec->cpos];
|
||
|
+ ec->lastmax = ec->cpos;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ ec->Ts += (rxabs >> SIGMA_ST_POW) - (oldrxabs >> SIGMA_ST_POW) ;
|
||
|
+
|
||
|
+ /* Increment position memory */
|
||
|
+ ec->cpos = (ec->cpos + 1 ) & TAP_MASK;
|
||
|
+
|
||
|
+ return suppr;
|
||
|
+}
|
||
|
+
|
||
|
+static inline int echo_can_traintap(echo_can_state_t *ec, int pos, short val)
|
||
|
+{
|
||
|
+ /* Reset hang counter to avoid adjustments after
|
||
|
+ initial forced training */
|
||
|
+ ec->hangt = NUM_TAPS << 1;
|
||
|
+ if (pos >= NUM_TAPS)
|
||
|
+ return 1;
|
||
|
+ ec->a[pos] = val << 17;
|
||
|
+ if (++pos >= NUM_TAPS)
|
||
|
+ return 1;
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+#endif
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/sedl_fax.c mISDN/drivers/isdn/hardware/mISDN/sedl_fax.c
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/sedl_fax.c 2004-08-27 21:27:40.000000000 +0200
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/sedl_fax.c 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -811,8 +811,8 @@
|
||
|
return(err);
|
||
|
}
|
||
|
|
||
|
- printk(KERN_INFO "mISDN: sedlpci found adapter %s at %s\n",
|
||
|
- (char *) ent->driver_data, pdev->slot_name);
|
||
|
+/* printk(KERN_INFO "mISDN: sedlpci found adapter %s at %s\n",
|
||
|
+ (char *) ent->driver_data, pdev->slot_name); */
|
||
|
|
||
|
card->cfg = pci_resource_start(pdev, 0);
|
||
|
card->irq = pdev->irq;
|
||
|
diff -u -r -P /tmp/mISDN/drivers/isdn/hardware/mISDN/w6692.c mISDN/drivers/isdn/hardware/mISDN/w6692.c
|
||
|
--- /tmp/mISDN/drivers/isdn/hardware/mISDN/w6692.c 2004-08-27 21:27:40.000000000 +0200
|
||
|
+++ mISDN/drivers/isdn/hardware/mISDN/w6692.c 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -1502,8 +1502,8 @@
|
||
|
return(err);
|
||
|
}
|
||
|
|
||
|
- printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n",
|
||
|
- (char *) ent->driver_data, pdev->slot_name);
|
||
|
+/* printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n",
|
||
|
+ (char *) ent->driver_data, pdev->slot_name); */
|
||
|
|
||
|
card->addr = pci_resource_start(pdev, 1);
|
||
|
card->irq = pdev->irq;
|
||
|
diff -u -r -P /tmp/mISDN/include/linux/mISDNif.h mISDN/include/linux/mISDNif.h
|
||
|
--- /tmp/mISDN/include/linux/mISDNif.h 2005-02-05 11:18:17.000000000 +0100
|
||
|
+++ mISDN/include/linux/mISDNif.h 2005-12-02 09:57:08.000000000 +0100
|
||
|
@@ -173,6 +173,8 @@
|
||
|
#define BF_DISABLE 0x2315
|
||
|
#define BF_ACCEPT 0x2316
|
||
|
#define BF_REJECT 0x2317
|
||
|
+#define ECHOCAN_ON 0x2318
|
||
|
+#define ECHOCAN_OFF 0x2319
|
||
|
#define HW_POTS_ON 0x1001
|
||
|
#define HW_POTS_OFF 0x1002
|
||
|
#define HW_POTS_SETMICVOL 0x1100
|
||
|
diff -u -r -P /tmp/mISDN/Makefile mISDN/Makefile
|
||
|
--- /tmp/mISDN/Makefile 1970-01-01 01:00:00.000000000 +0100
|
||
|
+++ mISDN/Makefile 2005-12-05 19:08:57.000000000 +0100
|
||
|
@@ -0,0 +1,54 @@
|
||
|
+BASEDIR=$(shell pwd)
|
||
|
+
|
||
|
+
|
||
|
+INSTALL_PREFIX := /
|
||
|
+export INSTALL_PREFIX
|
||
|
+
|
||
|
+#PATH to linux source/headers
|
||
|
+#LINUX=/usr/src/linux
|
||
|
+LINUX=/lib/modules/$(shell uname -r)/build
|
||
|
+
|
||
|
+MISDNDIR=$(BASEDIR)
|
||
|
+MISDN_SRC=$(MISDNDIR)/drivers/isdn/hardware/mISDN
|
||
|
+
|
||
|
+########################################
|
||
|
+# USER CONFIGS END
|
||
|
+########################################
|
||
|
+
|
||
|
+CONFIGS+=CONFIG_MISDN_DRV=m CONFIG_MISDN_DSP=m
|
||
|
+CONFIGS+=CONFIG_MISDN_HFCMULTI=m
|
||
|
+CONFIGS+=CONFIG_MISDN_HFCPCI=m
|
||
|
+CONFIGS+=CONFIG_MISDN_HFCUSB=m
|
||
|
+#CONFIGS+=CONFIG_MISDN_AVM_FRITZ=m
|
||
|
+
|
||
|
+
|
||
|
+MINCLUDES+=-I$(MISDNDIR)/include
|
||
|
+
|
||
|
+all:
|
||
|
+ @echo
|
||
|
+ @echo "Makeing mISDN"
|
||
|
+ @echo "============="
|
||
|
+ @echo
|
||
|
+ cp $(MISDNDIR)/drivers/isdn/hardware/mISDN/Makefile.v2.6 $(MISDNDIR)/drivers/isdn/hardware/mISDN/Makefile
|
||
|
+
|
||
|
+ cd $(LINUX) ; make SUBDIRS=$(MISDN_SRC) modules $(CONFIGS) LINUXINCLUDE="$(MINCLUDES) -I$(LINUX)/include"
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+install: all
|
||
|
+ cd $(LINUX) ; make SUBDIRS=$(MISDN_SRC) modules_install
|
||
|
+ cp $(MISDNDIR)/include/linux/*.h $(INSTALL_PREFIX)/usr/include/linux/
|
||
|
+ depmod
|
||
|
+
|
||
|
+.PHONY: install all clean
|
||
|
+
|
||
|
+clean:
|
||
|
+ rm -rf drivers/isdn/hardware/mISDN/*.o
|
||
|
+ rm -rf drivers/isdn/hardware/mISDN/*.ko
|
||
|
+ rm -rf *~
|
||
|
+ find . -iname ".*.cmd" -exec rm -rf {} \;
|
||
|
+ find . -iname ".*.d" -exec rm -rf {} \;
|
||
|
+ find . -iname "*.mod.c" -exec rm -rf {} \;
|
||
|
+ find . -iname "*.mod" -exec rm -rf {} \;
|
||
|
+
|
||
|
+
|