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.patch 12613 2006-03-12 18:54:31Z oej $
+ *
+ * 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 {} \;
+
+
