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>= 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>= 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> 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 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 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> 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> 16; + } +} + +static inline short MAX16(const short *y, int len, int *pos) +{ + int k; + short max = 0; + int bestpos = 0; + for (k=0;k + * + * 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$ + * + * 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;xec, 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 + * + * 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 +#include +#define MALLOC(a) kmalloc((a), GFP_KERNEL) +#define FREE(a) kfree(a) +#else +#include +#include +#include +#include +#include +#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; kN_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 +#include +#define MALLOC(a) kmalloc((a), GFP_KERNEL) +#define FREE(a) kfree(a) +#else +#include +#include +#include +#include +#include +#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;xmax < 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;xtaps;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;xtaps;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 + * + * 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 +#include +#define MALLOC(a) kmalloc((a), GFP_KERNEL) +#define FREE(a) kfree((a)) +#else +#include +#include +#include +#include +#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;xa[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;xy[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;xTy[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 {} \; + +