summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2009-04-29 18:24:04 +0000
committerShaun Ruffell <sruffell@digium.com>2009-04-29 18:24:04 +0000
commit5f94a3b91de2c3835d6852d59cab9f6876177156 (patch)
tree4c25956b6ecdcd5b902ac80b40237bc3e938be44
parent4a192a3e8f16ed6143377b5726e1fb53b446f5e9 (diff)
echocan: Improve interface for echo cancelers.
Echo cancelers are now able to report if they are able to automatically disable their NLP portions in the presence of tones in the audio stream. Also, the interface is changed to allow user space to just disable the NLP portion of the echo canceler. These changes improve fax and modem handling in DAHDI. This commit merges in the changes on http://svn.digium.com/svn/dahdi/linux/team/kpfleming/echocan_work Patch by: kpfleming Also contains improvements to CED tone detection. (closes issue #13286) Reported by: viniciusfontes git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@6529 a0bf4364-ded3-4de4-8d8a-66a801d63aff
-rw-r--r--drivers/dahdi/adt_lec.c2
-rw-r--r--drivers/dahdi/dahdi-base.c534
-rw-r--r--drivers/dahdi/dahdi_echocan_jpah.c84
-rw-r--r--drivers/dahdi/dahdi_echocan_kb1.c380
-rw-r--r--drivers/dahdi/dahdi_echocan_mg2.c475
-rw-r--r--drivers/dahdi/dahdi_echocan_oslec.c86
-rw-r--r--drivers/dahdi/dahdi_echocan_sec.c387
-rw-r--r--drivers/dahdi/dahdi_echocan_sec2.c402
-rw-r--r--drivers/dahdi/ecdis.h66
-rw-r--r--drivers/dahdi/hpec/dahdi_echocan_hpec.c88
-rw-r--r--drivers/dahdi/hpec/hpec.h10
-rw-r--r--drivers/dahdi/voicebus/GpakCust.c47
-rw-r--r--drivers/dahdi/voicebus/GpakCust.h6
-rw-r--r--drivers/dahdi/wcb4xxp/base.c73
-rw-r--r--drivers/dahdi/wcb4xxp/wcb4xxp.h1
-rw-r--r--drivers/dahdi/wct4xxp/base.c117
-rw-r--r--drivers/dahdi/wctdm24xxp/base.c96
-rw-r--r--drivers/dahdi/wctdm24xxp/wctdm24xxp.h1
-rw-r--r--drivers/dahdi/wcte12xp/base.c41
-rw-r--r--drivers/dahdi/wcte12xp/wcte12xp.h1
-rw-r--r--include/dahdi/dahdi_config.h7
-rw-r--r--include/dahdi/kernel.h263
-rw-r--r--include/dahdi/user.h23
23 files changed, 1945 insertions, 1245 deletions
diff --git a/drivers/dahdi/adt_lec.c b/drivers/dahdi/adt_lec.c
index a566d5d..02c1322 100644
--- a/drivers/dahdi/adt_lec.c
+++ b/drivers/dahdi/adt_lec.c
@@ -39,6 +39,8 @@ static int adt_lec_parse_params(struct adt_lec_params *params,
unsigned int x;
char *c;
+ params->tap_length = ecp->tap_length;
+
for (x = 0; x < ecp->param_count; x++) {
for (c = p[x].name; *c; c++)
*c = tolower(*c);
diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c
index e9316a8..991816f 100644
--- a/drivers/dahdi/dahdi-base.c
+++ b/drivers/dahdi/dahdi-base.c
@@ -47,22 +47,12 @@
#include <linux/moduleparam.h>
#include <linux/list.h>
+#include <linux/ppp_defs.h>
+
#include <asm/atomic.h>
#define module_printk(level, fmt, args...) printk(level "%s: " fmt, THIS_MODULE->name, ## args)
-#ifndef CONFIG_OLD_HDLC_API
-#define NEW_HDLC_INTERFACE
-#endif
-
-#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 BUF_MUNGE */
#include <dahdi/version.h>
@@ -71,11 +61,9 @@
#include <dahdi/kernel.h>
#include "ecdis.h"
-#ifdef CONFIG_DAHDI_NET
-#include <linux/netdevice.h>
-#endif /* CONFIG_DAHDI_NET */
-
-#include <linux/ppp_defs.h>
+#ifndef CONFIG_OLD_HDLC_API
+#define NEW_HDLC_INTERFACE
+#endif
#ifdef CONFIG_DAHDI_PPP
#include <linux/netdevice.h>
@@ -83,6 +71,10 @@
#include <linux/if_ppp.h>
#endif
+#ifdef CONFIG_DAHDI_NET
+#include <linux/netdevice.h>
+#endif
+
#include "hpec/hpec_user.h"
/* Get helper arithmetic */
@@ -143,8 +135,8 @@ EXPORT_SYMBOL(dahdi_alarm_channel);
EXPORT_SYMBOL(dahdi_register_chardev);
EXPORT_SYMBOL(dahdi_unregister_chardev);
-EXPORT_SYMBOL(dahdi_register_echocan);
-EXPORT_SYMBOL(dahdi_unregister_echocan);
+EXPORT_SYMBOL(dahdi_register_echocan_factory);
+EXPORT_SYMBOL(dahdi_unregister_echocan_factory);
EXPORT_SYMBOL(dahdi_set_hpec_ioctl);
@@ -378,62 +370,62 @@ static struct dahdi_zone *tone_zones[DAHDI_TONE_ZONE_MAX];
#define NUM_SIGS 10
#ifdef DEFINE_RWLOCK
-static DEFINE_RWLOCK(echocan_list_lock);
+static DEFINE_RWLOCK(ecfactory_list_lock);
#else
-static rwlock_t echocan_list_lock = RW_LOCK_UNLOCKED;
+static rwlock_t ecfactory_list_lock = __RW_LOCK_UNLOCKED();
#endif
-static LIST_HEAD(echocan_list);
+static LIST_HEAD(ecfactory_list);
-struct echocan {
- const struct dahdi_echocan *ec;
+struct ecfactory {
+ const struct dahdi_echocan_factory *ec;
struct module *owner;
struct list_head list;
};
-int dahdi_register_echocan(const struct dahdi_echocan *ec)
+int dahdi_register_echocan_factory(const struct dahdi_echocan_factory *ec)
{
- struct echocan *cur;
+ struct ecfactory *cur;
- write_lock(&echocan_list_lock);
+ write_lock(&ecfactory_list_lock);
/* make sure it isn't already registered */
- list_for_each_entry(cur, &echocan_list, list) {
+ list_for_each_entry(cur, &ecfactory_list, list) {
if (cur->ec == ec) {
- write_unlock(&echocan_list_lock);
+ write_unlock(&ecfactory_list_lock);
return -EPERM;
}
}
if (!(cur = kzalloc(sizeof(*cur), GFP_KERNEL))) {
- write_unlock(&echocan_list_lock);
+ write_unlock(&ecfactory_list_lock);
return -ENOMEM;
}
cur->ec = ec;
INIT_LIST_HEAD(&cur->list);
- list_add_tail(&cur->list, &echocan_list);
+ list_add_tail(&cur->list, &ecfactory_list);
- write_unlock(&echocan_list_lock);
+ write_unlock(&ecfactory_list_lock);
return 0;
}
-void dahdi_unregister_echocan(const struct dahdi_echocan *ec)
+void dahdi_unregister_echocan_factory(const struct dahdi_echocan_factory *ec)
{
- struct echocan *cur, *next;
+ struct ecfactory *cur, *next;
- write_lock(&echocan_list_lock);
+ write_lock(&ecfactory_list_lock);
- list_for_each_entry_safe(cur, next, &echocan_list, list) {
+ list_for_each_entry_safe(cur, next, &ecfactory_list, list) {
if (cur->ec == ec) {
list_del(&cur->list);
break;
}
}
- write_unlock(&echocan_list_lock);
+ write_unlock(&ecfactory_list_lock);
}
static inline void rotate_sums(void)
@@ -663,9 +655,13 @@ static int dahdi_proc_read(char *page, char **start, off_t off, int count, int *
chan->chan_alarms);
if (chan->ec_factory)
- len += snprintf(page+len, count-len, " (EC: %s) ",
+ len += snprintf(page+len, count-len, "(SWEC: %s) ",
chan->ec_factory->name);
+ if (chan->ec_state)
+ len += snprintf(page+len, count-len, "(EC: %s) ",
+ chan->ec_state->ops->name);
+
len += snprintf(page+len, count-len, "\n");
/* If everything printed so far is before beginning
@@ -1080,28 +1076,9 @@ static void reset_conf(struct dahdi_chan *chan)
}
-static inline int hw_echocancel_off(struct dahdi_chan *chan)
-{
- int ret = 0;
-
- if (!chan->span)
- return -ENODEV;
-
- if (chan->span->echocan) {
- ret = chan->span->echocan(chan, 0);
- } else if (chan->span->echocan_with_params) {
- struct dahdi_echocanparams ecp = {
- .tap_length = 0,
- };
- ret = chan->span->echocan_with_params(chan, &ecp, NULL);
- }
-
- return ret;
-}
-
-static const struct dahdi_echocan *find_echocan(const char *name)
+static const struct dahdi_echocan_factory *find_echocan(const char *name)
{
- struct echocan *cur;
+ struct ecfactory *cur;
char name_upper[strlen(name) + 1];
char *c;
const char *d;
@@ -1115,26 +1092,26 @@ static const struct dahdi_echocan *find_echocan(const char *name)
*c = '\0';
retry:
- read_lock(&echocan_list_lock);
+ read_lock(&ecfactory_list_lock);
- list_for_each_entry(cur, &echocan_list, list) {
+ list_for_each_entry(cur, &ecfactory_list, list) {
if (!strcmp(name_upper, cur->ec->name)) {
#ifdef USE_ECHOCAN_REFCOUNT
if (try_module_get(cur->owner)) {
- read_unlock(&echocan_list_lock);
+ read_unlock(&ecfactory_list_lock);
return cur->ec;
} else {
- read_unlock(&echocan_list_lock);
+ read_unlock(&ecfactory_list_lock);
return NULL;
}
#else
- read_unlock(&echocan_list_lock);
+ read_unlock(&ecfactory_list_lock);
return cur->ec;
#endif
}
}
- read_unlock(&echocan_list_lock);
+ read_unlock(&ecfactory_list_lock);
if (tried_once) {
return NULL;
@@ -1154,10 +1131,11 @@ retry:
goto retry;
}
-static void release_echocan(const struct dahdi_echocan *ec)
+static void release_echocan(const struct dahdi_echocan_factory *ec)
{
#ifdef USE_ECHOCAN_REFCOUNT
- module_put(ec->owner);
+ if (ec)
+ module_put(ec->owner);
#endif
}
@@ -1173,8 +1151,8 @@ static void close_channel(struct dahdi_chan *chan)
{
unsigned long flags;
void *rxgain = NULL;
- struct echo_can_state *ec_state;
- const struct dahdi_echocan *ec_current;
+ struct dahdi_echocan_state *ec_state;
+ const struct dahdi_echocan_factory *ec_current;
int oldconf;
short *readchunkpreec;
#ifdef CONFIG_DAHDI_PPP
@@ -1252,14 +1230,12 @@ static void close_channel(struct dahdi_chan *chan)
chan->span->dacs(chan, NULL);
if (ec_state) {
- ec_current->echo_can_free(ec_state);
+ ec_state->ops->echocan_free(chan, ec_state);
release_echocan(ec_current);
}
spin_unlock_irqrestore(&chan->lock, flags);
- hw_echocancel_off(chan);
-
if (rxgain)
kfree(rxgain);
if (readchunkpreec)
@@ -2449,8 +2425,8 @@ static int initialize_channel(struct dahdi_chan *chan)
int res;
unsigned long flags;
void *rxgain=NULL;
- struct echo_can_state *ec_state;
- const struct dahdi_echocan *ec_current;
+ struct dahdi_echocan_state *ec_state;
+ const struct dahdi_echocan_factory *ec_current;
if ((res = dahdi_reallocbufs(chan, DAHDI_DEFAULT_BLOCKSIZE, DAHDI_DEFAULT_NUM_BUFS)))
return res;
@@ -2464,10 +2440,6 @@ static int initialize_channel(struct dahdi_chan *chan)
chan->ec_state = NULL;
ec_current = chan->ec_current;
chan->ec_current = NULL;
- chan->echocancel = 0;
- chan->echostate = ECHO_STATE_IDLE;
- chan->echolastupdate = 0;
- chan->echotimer = 0;
chan->txdisable = 0;
chan->rxdisable = 0;
@@ -2562,7 +2534,7 @@ static int initialize_channel(struct dahdi_chan *chan)
}
if (ec_state) {
- ec_current->echo_can_free(ec_state);
+ ec_state->ops->echocan_free(chan, ec_state);
release_echocan(ec_current);
}
@@ -2570,8 +2542,6 @@ static int initialize_channel(struct dahdi_chan *chan)
set_tone_zone(chan, -1);
- hw_echocancel_off(chan);
-
if (rxgain)
kfree(rxgain);
@@ -3437,7 +3407,6 @@ static int dahdi_common_ioctl(struct inode *node, struct file *file, unsigned in
struct dahdi_chan *chan;
unsigned long flags;
unsigned char *txgain, *rxgain;
- struct dahdi_chan *mychan;
int i,j;
int return_master = 0;
size_t size_to_copy;
@@ -3678,7 +3647,14 @@ static int dahdi_common_ioctl(struct inode *node, struct file *file, unsigned in
break;
case DAHDI_CHANDIAG_V1: /* Intentional drop through. */
case DAHDI_CHANDIAG:
- get_user(j, (int *)data); /* get channel number from user */
+ {
+ /* there really is no need to initialize this structure because when it is used it has
+ * already been completely overwritten, but apparently the compiler cannot figure that
+ * out and warns about uninitialized usage... so initialize it.
+ */
+ struct dahdi_echocan_state ec_state = { .ops = NULL, };
+
+ get_user(j, (int *) data); /* get channel number from user */
/* make sure its a valid channel number */
if ((j < 1) || (j >= maxchans))
return -EINVAL;
@@ -3686,54 +3662,52 @@ static int dahdi_common_ioctl(struct inode *node, struct file *file, unsigned in
if (!chans[j])
return -EINVAL;
- if (!(mychan = kmalloc(sizeof(*mychan), GFP_KERNEL)))
+ chan = kmalloc(sizeof(*chan), GFP_KERNEL);
+ if (!chan)
return -ENOMEM;
/* lock channel */
spin_lock_irqsave(&chans[j]->lock, flags);
/* make static copy of channel */
- memcpy(mychan, chans[j], sizeof(*mychan));
+ *chan = *chans[j];
+ if (chan->ec_state) {
+ ec_state = *chan->ec_state;
+ }
/* release it. */
spin_unlock_irqrestore(&chans[j]->lock, flags);
module_printk(KERN_INFO, "Dump of DAHDI Channel %d (%s,%d,%d):\n\n",j,
- mychan->name,mychan->channo,mychan->chanpos);
- module_printk(KERN_INFO, "flags: %x hex, writechunk: %08lx, readchunk: %08lx\n",
- (unsigned int) mychan->flags, (long) mychan->writechunk, (long) mychan->readchunk);
- module_printk(KERN_INFO, "rxgain: %08lx, txgain: %08lx, gainalloc: %d\n",
- (long) mychan->rxgain, (long)mychan->txgain, mychan->gainalloc);
- module_printk(KERN_INFO, "span: %08lx, sig: %x hex, sigcap: %x hex\n",
- (long)mychan->span, mychan->sig, mychan->sigcap);
+ chan->name, chan->channo, chan->chanpos);
+ module_printk(KERN_INFO, "flags: %x hex, writechunk: %p, readchunk: %p\n",
+ (unsigned int) chan->flags, chan->writechunk, chan->readchunk);
+ module_printk(KERN_INFO, "rxgain: %p, txgain: %p, gainalloc: %d\n",
+ chan->rxgain, chan->txgain, chan->gainalloc);
+ module_printk(KERN_INFO, "span: %p, sig: %x hex, sigcap: %x hex\n",
+ chan->span, chan->sig, chan->sigcap);
module_printk(KERN_INFO, "inreadbuf: %d, outreadbuf: %d, inwritebuf: %d, outwritebuf: %d\n",
- mychan->inreadbuf, mychan->outreadbuf, mychan->inwritebuf, mychan->outwritebuf);
+ chan->inreadbuf, chan->outreadbuf, chan->inwritebuf, chan->outwritebuf);
module_printk(KERN_INFO, "blocksize: %d, numbufs: %d, txbufpolicy: %d, txbufpolicy: %d\n",
- mychan->blocksize, mychan->numbufs, mychan->txbufpolicy, mychan->rxbufpolicy);
+ chan->blocksize, chan->numbufs, chan->txbufpolicy, chan->rxbufpolicy);
module_printk(KERN_INFO, "txdisable: %d, rxdisable: %d, iomask: %d\n",
- mychan->txdisable, mychan->rxdisable, mychan->iomask);
- module_printk(KERN_INFO, "curzone: %08lx, tonezone: %d, curtone: %08lx, tonep: %d\n",
- (long) mychan->curzone, mychan->tonezone, (long) mychan->curtone, mychan->tonep);
+ chan->txdisable, chan->rxdisable, chan->iomask);
+ module_printk(KERN_INFO, "curzone: %p, tonezone: %d, curtone: %p, tonep: %d\n",
+ chan->curzone, chan->tonezone, chan->curtone, chan->tonep);
module_printk(KERN_INFO, "digitmode: %d, txdialbuf: %s, dialing: %d, aftdialtimer: %d, cadpos. %d\n",
- mychan->digitmode, mychan->txdialbuf, mychan->dialing,
- mychan->afterdialingtimer, mychan->cadencepos);
+ chan->digitmode, chan->txdialbuf, chan->dialing,
+ chan->afterdialingtimer, chan->cadencepos);
module_printk(KERN_INFO, "confna: %d, confn: %d, confmode: %d, confmute: %d\n",
- mychan->confna, mychan->_confn, mychan->confmode, mychan->confmute);
- module_printk(KERN_INFO, "ec: %08lx, echocancel: %d, deflaw: %d, xlaw: %08lx\n",
- (long) mychan->ec_state, mychan->echocancel, mychan->deflaw, (long) mychan->xlaw);
- module_printk(KERN_INFO, "echostate: %02x, echotimer: %d, echolastupdate: %d\n",
- (int) mychan->echostate, mychan->echotimer, mychan->echolastupdate);
- module_printk(KERN_INFO, "itimer: %d, otimer: %d, ringdebtimer: %d\n\n",
- mychan->itimer, mychan->otimer, mychan->ringdebtimer);
-#if 0
- if (mychan->ec_state) {
- int x;
- /* Dump the echo canceller parameters */
- for (x=0;x<mychan->ec_state->taps;x++) {
- module_printk(KERN_INFO, "tap %d: %d\n", x, mychan->ec_state->fir_taps[x]);
- }
+ chan->confna, chan->_confn, chan->confmode, chan->confmute);
+ module_printk(KERN_INFO, "ec: %p, deflaw: %d, xlaw: %p\n",
+ chan->ec_state, chan->deflaw, chan->xlaw);
+ if (chan->ec_state) {
+ module_printk(KERN_INFO, "echostate: %02x, echotimer: %d, echolastupdate: %d\n",
+ ec_state.status.mode, ec_state.status.pretrain_timer, ec_state.status.last_train_tap);
}
-#endif
- kfree(mychan);
+ module_printk(KERN_INFO, "itimer: %d, otimer: %d, ringdebtimer: %d\n\n",
+ chan->itimer, chan->otimer, chan->ringdebtimer);
+ kfree(chan);
break;
+ }
default:
return -ENOTTY;
}
@@ -3851,7 +3825,7 @@ static int dahdi_ctl_ioctl(struct inode *inode, struct file *file, unsigned int
case DAHDI_ATTACH_ECHOCAN:
{
struct dahdi_attach_echocan ae;
- const struct dahdi_echocan *new = NULL, *old;
+ const struct dahdi_echocan_factory *new = NULL, *old;
if (copy_from_user(&ae, (struct dahdi_attach_echocan *) data, sizeof(ae))) {
return -EFAULT;
@@ -4172,19 +4146,19 @@ static int dahdi_ctl_ioctl(struct inode *inode, struct file *file, unsigned int
case DAHDI_GETVERSION:
{
struct dahdi_versioninfo vi;
- struct echocan *cur;
+ struct ecfactory *cur;
size_t space = sizeof(vi.echo_canceller) - 1;
memset(&vi, 0, sizeof(vi));
dahdi_copy_string(vi.version, DAHDI_VERSION, sizeof(vi.version));
- read_lock(&echocan_list_lock);
- list_for_each_entry(cur, &echocan_list, list) {
+ read_lock(&ecfactory_list_lock);
+ list_for_each_entry(cur, &ecfactory_list, list) {
strncat(vi.echo_canceller + strlen(vi.echo_canceller), cur->ec->name, space);
space -= strlen(cur->ec->name);
if (space < 1) {
break;
}
- if (cur->list.next && (cur->list.next != &echocan_list)) {
+ if (cur->list.next && (cur->list.next != &ecfactory_list)) {
strncat(vi.echo_canceller + strlen(vi.echo_canceller), ", ", space);
space -= 2;
if (space < 1) {
@@ -4192,7 +4166,7 @@ static int dahdi_ctl_ioctl(struct inode *inode, struct file *file, unsigned int
}
}
}
- read_unlock(&echocan_list_lock);
+ read_unlock(&ecfactory_list_lock);
if (copy_to_user((struct dahdi_versioninfo *) data, &vi, sizeof(vi)))
return -EFAULT;
break;
@@ -4854,8 +4828,8 @@ static void do_ppp_calls(unsigned long data)
static int ioctl_echocancel(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, void *data)
{
- struct echo_can_state *ec = NULL, *ec_state;
- const struct dahdi_echocan *ec_current;
+ struct dahdi_echocan_state *ec = NULL, *ec_state;
+ const struct dahdi_echocan_factory *ec_current;
struct dahdi_echocanparam *params;
int ret;
unsigned long flags;
@@ -4870,26 +4844,15 @@ static int ioctl_echocancel(struct dahdi_chan *chan, struct dahdi_echocanparams
chan->ec_state = NULL;
ec_current = chan->ec_current;
chan->ec_current = NULL;
- chan->echocancel = 0;
- chan->echostate = ECHO_STATE_IDLE;
- chan->echolastupdate = 0;
- chan->echotimer = 0;
spin_unlock_irqrestore(&chan->lock, flags);
if (ec_state) {
- ec_current->echo_can_free(ec_state);
+ ec_state->ops->echocan_free(chan, ec_state);
release_echocan(ec_current);
}
- hw_echocancel_off(chan);
return 0;
}
- /* if parameters were supplied and this channel's span provides an echocan,
- but not one that takes params, then we must punt here and return an error */
- if (ecp->param_count && chan->span && chan->span->echocan &&
- !chan->span->echocan_with_params)
- return -EINVAL;
-
params = kmalloc(sizeof(params[0]) * DAHDI_MAX_ECHOCANPARAMS, GFP_KERNEL);
if (!params)
@@ -4902,6 +4865,7 @@ static int ioctl_echocancel(struct dahdi_chan *chan, struct dahdi_echocanparams
goto exit_with_free;
}
+ /* free any echocan that may be on the channel already */
spin_lock_irqsave(&chan->lock, flags);
ec_state = chan->ec_state;
chan->ec_state = NULL;
@@ -4909,34 +4873,31 @@ static int ioctl_echocancel(struct dahdi_chan *chan, struct dahdi_echocanparams
chan->ec_current = NULL;
spin_unlock_irqrestore(&chan->lock, flags);
if (ec_state) {
- ec_current->echo_can_free(ec_state);
+ ec_state->ops->echocan_free(chan, ec_state);
release_echocan(ec_current);
}
+ switch (ecp->tap_length) {
+ case 32:
+ case 64:
+ case 128:
+ case 256:
+ case 512:
+ case 1024:
+ break;
+ default:
+ ecp->tap_length = deftaps;
+ }
+
ret = -ENODEV;
+ ec_current = NULL;
/* attempt to use the span's echo canceler; fall back to built-in
if it fails (but not if an error occurs) */
- if (chan->span) {
- if (chan->span->echocan_with_params)
- ret = chan->span->echocan_with_params(chan, ecp, params);
- else if (chan->span->echocan)
- ret = chan->span->echocan(chan, ecp->tap_length);
- }
+ if (chan->span && chan->span->echocan_create)
+ ret = chan->span->echocan_create(chan, ecp, params, &ec);
if ((ret == -ENODEV) && chan->ec_factory) {
- switch (ecp->tap_length) {
- case 32:
- case 64:
- case 128:
- case 256:
- case 512:
- case 1024:
- break;
- default:
- ecp->tap_length = deftaps;
- }
-
#ifdef USE_ECHOCAN_REFCOUNT
/* try to get another reference to the module providing
this channel's echo canceler */
@@ -4950,21 +4911,32 @@ static int ioctl_echocancel(struct dahdi_chan *chan, struct dahdi_echocanparams
an echo canceler instance if possible */
ec_current = chan->ec_factory;
- if ((ret = ec_current->echo_can_create(ecp, params, &ec))) {
+ ret = ec_current->echocan_create(chan, ecp, params, &ec);
+ if (ret) {
release_echocan(ec_current);
goto exit_with_free;
}
+ if (!ec) {
+ module_printk(KERN_ERR, "%s failed to allocate an " \
+ "dahdi_echocan_state instance.\n",
+ ec_current->name);
+ ret = -EFAULT;
+ goto exit_with_free;
+ }
+ }
+ if (ec) {
spin_lock_irqsave(&chan->lock, flags);
- chan->echocancel = ecp->tap_length;
chan->ec_current = ec_current;
chan->ec_state = ec;
- chan->echostate = ECHO_STATE_IDLE;
- chan->echolastupdate = 0;
- chan->echotimer = 0;
- echo_can_disable_detector_init(&chan->txecdis);
- echo_can_disable_detector_init(&chan->rxecdis);
+ ec->status.mode = ECHO_MODE_ACTIVE;
+ if (!ec->features.CED_tx_detect) {
+ echo_can_disable_detector_init(&chan->ec_state->txecdis);
+ }
+ if (!ec->features.CED_rx_detect) {
+ echo_can_disable_detector_init(&chan->ec_state->rxecdis);
+ }
spin_unlock_irqrestore(&chan->lock, flags);
}
@@ -4974,6 +4946,56 @@ exit_with_free:
return ret;
}
+static void set_echocan_fax_mode(struct dahdi_chan *chan, unsigned int channo, const char *reason, unsigned int enable)
+{
+ if (enable) {
+ if (!chan->ec_state)
+ module_printk(KERN_NOTICE, "Ignoring FAX mode request because of %s for channel %d with no echo canceller\n", reason, channo);
+ else if (chan->ec_state->status.mode == ECHO_MODE_FAX)
+ module_printk(KERN_NOTICE, "Ignoring FAX mode request because of %s for echo canceller already in FAX mode on channel %d\n", reason, channo);
+ else if (chan->ec_state->status.mode != ECHO_MODE_ACTIVE)
+ module_printk(KERN_NOTICE, "Ignoring FAX mode request because of %s for echo canceller not in active mode on channel %d\n", reason, channo);
+ else if (chan->ec_state->features.NLP_automatic) {
+ /* for echocans that automatically do the right thing, just
+ * mark it as being in FAX mode without making any
+ * changes, as none are necessary.
+ */
+ chan->ec_state->status.mode = ECHO_MODE_FAX;
+ } else if (chan->ec_state->features.NLP_toggle) {
+ module_printk(KERN_NOTICE, "Disabled echo canceller NLP because of %s on channel %d\n", reason, channo);
+ dahdi_qevent_nolock(chan, DAHDI_EVENT_EC_NLP_DISABLED);
+ chan->ec_state->ops->echocan_NLP_toggle(chan->ec_state, 0);
+ chan->ec_state->status.mode = ECHO_MODE_FAX;
+ } else {
+ module_printk(KERN_NOTICE, "Idled echo canceller because of %s on channel %d\n", reason, channo);
+ chan->ec_state->status.mode = ECHO_MODE_IDLE;
+ }
+ } else {
+ if (!chan->ec_state)
+ module_printk(KERN_NOTICE, "Ignoring voice mode request because of %s for channel %d with no echo canceller\n", reason, channo);
+ else if (chan->ec_state->status.mode == ECHO_MODE_ACTIVE)
+ module_printk(KERN_NOTICE, "Ignoring voice mode request because of %s for echo canceller already in voice mode on channel %d\n", reason, channo);
+ else if ((chan->ec_state->status.mode != ECHO_MODE_FAX) &&
+ (chan->ec_state->status.mode != ECHO_MODE_IDLE))
+ module_printk(KERN_NOTICE, "Ignoring voice mode request because of %s for echo canceller not in FAX or idle mode on channel %d\n", reason, channo);
+ else if (chan->ec_state->features.NLP_automatic) {
+ /* for echocans that automatically do the right thing, just
+ * mark it as being in active mode without making any
+ * changes, as none are necessary.
+ */
+ chan->ec_state->status.mode = ECHO_MODE_ACTIVE;
+ } else if (chan->ec_state->features.NLP_toggle) {
+ module_printk(KERN_NOTICE, "Enabled echo canceller NLP because of %s on channel %d\n", reason, channo);
+ dahdi_qevent_nolock(chan, DAHDI_EVENT_EC_NLP_ENABLED);
+ chan->ec_state->ops->echocan_NLP_toggle(chan->ec_state, 1);
+ chan->ec_state->status.mode = ECHO_MODE_ACTIVE;
+ } else {
+ module_printk(KERN_NOTICE, "Activated echo canceller because of %s on channel %d\n", reason, channo);
+ chan->ec_state->status.mode = ECHO_MODE_ACTIVE;
+ }
+ }
+}
+
static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit)
{
struct dahdi_chan *chan = chans[unit];
@@ -5020,8 +5042,8 @@ static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int
/* Coming out of audio mode, also clear all
conferencing and gain related info as well
as echo canceller */
- struct echo_can_state *ec_state;
- const struct dahdi_echocan *ec_current;
+ struct dahdi_echocan_state *ec_state;
+ const struct dahdi_echocan_factory *ec_current;
spin_lock_irqsave(&chan->lock, flags);
chan->flags &= ~DAHDI_FLAG_AUDIO;
@@ -5054,13 +5076,10 @@ static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int
spin_unlock_irqrestore(&chan->lock, flags);
if (ec_state) {
- ec_current->echo_can_free(ec_state);
+ ec_state->ops->echocan_free(chan, ec_state);
release_echocan(ec_current);
}
- /* Disable any native echo cancellation as well */
- hw_echocancel_off(chan);
-
if (rxgain)
kfree(rxgain);
if (oldconf) dahdi_check_conf(oldconf);
@@ -5074,8 +5093,8 @@ static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int
if (!chan->ppp) {
chan->ppp = kzalloc(sizeof(struct ppp_channel), GFP_KERNEL);
if (chan->ppp) {
- struct echo_can_state *tec;
- const struct dahdi_echocan *ec_current;
+ struct dahdi_echocan_state *tec;
+ const struct dahdi_echocan_factory *ec_current;
chan->ppp->private = chan;
chan->ppp->ops = &ztppp_ops;
@@ -5100,10 +5119,6 @@ static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int
chan->ec_state = NULL;
ec_current = chan->ec_current;
chan->ec_current = NULL;
- chan->echocancel = 0;
- chan->echostate = ECHO_STATE_IDLE;
- chan->echolastupdate = 0;
- chan->echotimer = 0;
/* Make sure there's no gain */
if (chan->gainalloc)
kfree(chan->rxgain);
@@ -5112,10 +5127,9 @@ static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int
chan->gainalloc = 0;
chan->flags &= ~DAHDI_FLAG_AUDIO;
chan->flags |= (DAHDI_FLAG_PPP | DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS);
- hw_echocancel_off(chan);
if (tec) {
- ec_current->echo_can_free(tec);
+ tec->ops->echocan_free(chan, tec);
release_echocan(ec_current);
}
} else
@@ -5201,21 +5215,31 @@ static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int
j <<= 3;
if (chan->ec_state) {
/* Start pretraining stage */
- chan->echostate = ECHO_STATE_PRETRAINING;
- chan->echotimer = j;
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->ec_state->status.mode = ECHO_MODE_PRETRAINING;
+ chan->ec_state->status.pretrain_timer = j;
+ spin_unlock_irqrestore(&chan->lock, flags);
} else
return -EINVAL;
break;
+ case DAHDI_ECHOCANCEL_FAX_MODE:
+ if (!chan->ec_state) {
+ return -EINVAL;
+ } else {
+ get_user(j, (int *) data);
+ spin_lock_irqsave(&chan->lock, flags);
+ set_echocan_fax_mode(chan, chan->channo, "ioctl", j ? 1 : 0);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ }
+ break;
case DAHDI_SETTXBITS:
if (chan->sig != DAHDI_SIG_CAS)
return -EINVAL;
get_user(j,(int *)data);
dahdi_cas_setbits(chan, j);
- rv = 0;
break;
case DAHDI_GETRXBITS:
put_user(chan->rxsig, (int *)data);
- rv = 0;
break;
case DAHDI_LOOPBACK:
get_user(j, (int *)data);
@@ -5225,7 +5249,6 @@ static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int
else
chan->flags &= ~DAHDI_FLAG_LOOPED;
spin_unlock_irqrestore(&chan->lock, flags);
- rv = 0;
break;
case DAHDI_HOOK:
get_user(j,(int *)data);
@@ -5286,7 +5309,6 @@ static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int
rv = schluffen(&chan->txstateq);
if (rv) return rv;
#endif
- rv = 0;
break;
case DAHDI_WINK:
spin_lock_irqsave(&chan->lock, flags);
@@ -5466,13 +5488,6 @@ int dahdi_register(struct dahdi_span *span, int prefmaster)
span->deflaw = DAHDI_LAW_MULAW;
}
- if (span->echocan && span->echocan_with_params) {
- module_printk(KERN_NOTICE, "Span %s implements both echocan "
- "and echocan_with_params functions, preserving only "
- "echocan_with_params, please fix driver!\n", span->name);
- span->echocan = NULL;
- }
-
for (x = 0; x < span->channels; x++) {
span->chans[x]->span = span;
dahdi_chan_reg(span->chans[x]);
@@ -5741,26 +5756,17 @@ static inline void __dahdi_process_getaudio_chunk(struct dahdi_chan *ss, unsigne
/* Okay, now we've got something to transmit */
for (x=0;x<DAHDI_CHUNKSIZE;x++)
getlin[x] = DAHDI_XLAW(txb[x], ms);
-#ifndef NO_ECHOCAN_DISABLE
- if (ms->ec_state) {
- for (x=0;x<DAHDI_CHUNKSIZE;x++) {
- /* Check for echo cancel disabling tone */
- if (echo_can_disable_detector_update(&ms->txecdis, getlin[x])) {
- module_printk(KERN_NOTICE, "Disabled echo canceller because of tone (tx) on channel %d\n", ss->channo);
- ms->echocancel = 0;
- ms->echostate = ECHO_STATE_IDLE;
- ms->echolastupdate = 0;
- ms->echotimer = 0;
- ms->ec_current->echo_can_free(ms->ec_state);
- ms->ec_state = NULL;
- release_echocan(ms->ec_current);
- ms->ec_current = NULL;
- __qevent(ss, DAHDI_EVENT_EC_DISABLED);
+
+ if (ms->ec_state && (ms->ec_state->status.mode == ECHO_MODE_ACTIVE) && !ms->ec_state->features.CED_tx_detect) {
+ for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
+ if (echo_can_disable_detector_update(&ms->ec_state->txecdis, getlin[x])) {
+ set_echocan_fax_mode(ms, ss->channo, "CED tx detected", 1);
+ dahdi_qevent_nolock(ms, DAHDI_EVENT_TX_CED_DETECTED);
break;
}
}
}
-#endif
+
if ((!ms->confmute && !ms->dialing) || (ms->flags & DAHDI_FLAG_PSEUDO)) {
/* Handle conferencing on non-clear channel and non-HDLC channels */
switch(ms->confmode & DAHDI_CONF_MODE_MASK) {
@@ -5945,13 +5951,13 @@ static inline void __dahdi_process_getaudio_chunk(struct dahdi_chan *ss, unsigne
break;
}
}
- if (ms->confmute || (ms->echostate & __ECHO_STATE_MUTE)) {
+ if (ms->confmute || (ms->ec_state && (ms->ec_state->status.mode) & __ECHO_MODE_MUTE)) {
txb[0] = DAHDI_LIN2X(0, ms);
memset(txb + 1, txb[0], DAHDI_CHUNKSIZE - 1);
- if (ms->echostate == ECHO_STATE_STARTTRAINING) {
+ if (ms->ec_state && (ms->ec_state->status.mode == ECHO_MODE_STARTTRAINING)) {
/* Transmit impulse now */
txb[0] = DAHDI_LIN2X(16384, ms);
- ms->echostate = ECHO_STATE_AWAITINGECHO;
+ ms->ec_state->status.mode = ECHO_MODE_AWAITINGECHO;
}
}
/* save value from last chunk */
@@ -6545,6 +6551,47 @@ void dahdi_rbsbits(struct dahdi_chan *chan, int cursig)
spin_unlock_irqrestore(&chan->lock, flags);
}
+static void process_echocan_events(struct dahdi_chan *chan)
+{
+ union dahdi_echocan_events events = chan->ec_state->events;
+
+ if (events.CED_tx_detected) {
+ dahdi_qevent_nolock(chan, DAHDI_EVENT_TX_CED_DETECTED);
+ if (chan->ec_state) {
+ if (chan->ec_state->status.mode == ECHO_MODE_ACTIVE)
+ set_echocan_fax_mode(chan, chan->channo, "CED tx detected", 1);
+ else
+ module_printk(KERN_NOTICE, "Detected CED tone (tx) on channel %d\n", chan->channo);
+ }
+ }
+
+ if (events.CED_rx_detected) {
+ dahdi_qevent_nolock(chan, DAHDI_EVENT_RX_CED_DETECTED);
+ if (chan->ec_state) {
+ if (chan->ec_state->status.mode == ECHO_MODE_ACTIVE)
+ set_echocan_fax_mode(chan, chan->channo, "CED rx detected", 1);
+ else
+ module_printk(KERN_NOTICE, "Detected CED tone (rx) on channel %d\n", chan->channo);
+ }
+ }
+
+ if (events.CNG_tx_detected)
+ dahdi_qevent_nolock(chan, DAHDI_EVENT_TX_CNG_DETECTED);
+
+ if (events.CNG_rx_detected)
+ dahdi_qevent_nolock(chan, DAHDI_EVENT_RX_CNG_DETECTED);
+
+ if (events.NLP_auto_disabled) {
+ dahdi_qevent_nolock(chan, DAHDI_EVENT_EC_NLP_DISABLED);
+ chan->ec_state->status.mode = ECHO_MODE_FAX;
+ }
+
+ if (events.NLP_auto_enabled) {
+ dahdi_qevent_nolock(chan, DAHDI_EVENT_EC_NLP_ENABLED);
+ chan->ec_state->status.mode = ECHO_MODE_ACTIVE;
+ }
+}
+
static inline void __dahdi_ec_chunk(struct dahdi_chan *ss, unsigned char *rxchunk, const unsigned char *txchunk)
{
short rxlin, txlin;
@@ -6565,41 +6612,52 @@ static inline void __dahdi_ec_chunk(struct dahdi_chan *ss, unsigned char *rxchun
#if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP)
dahdi_kernel_fpu_begin();
#endif
- if (ss->echostate & __ECHO_STATE_MUTE) {
+ if (ss->ec_state->status.mode & __ECHO_MODE_MUTE) {
/* Special stuff for training the echo can */
for (x=0;x<DAHDI_CHUNKSIZE;x++) {
rxlin = DAHDI_XLAW(rxchunk[x], ss);
txlin = DAHDI_XLAW(txchunk[x], ss);
- if (ss->echostate == ECHO_STATE_PRETRAINING) {
- if (--ss->echotimer <= 0) {
- ss->echotimer = 0;
- ss->echostate = ECHO_STATE_STARTTRAINING;
+ if (ss->ec_state->status.mode == ECHO_MODE_PRETRAINING) {
+ if (--ss->ec_state->status.pretrain_timer <= 0) {
+ ss->ec_state->status.pretrain_timer = 0;
+ ss->ec_state->status.mode = ECHO_MODE_STARTTRAINING;
}
}
- if ((ss->echostate == ECHO_STATE_AWAITINGECHO) && (txlin > 8000)) {
- ss->echolastupdate = 0;
- ss->echostate = ECHO_STATE_TRAINING;
+ if ((ss->ec_state->status.mode == ECHO_MODE_AWAITINGECHO) && (txlin > 8000)) {
+ ss->ec_state->status.last_train_tap = 0;
+ ss->ec_state->status.mode = ECHO_MODE_TRAINING;
}
- if (ss->echostate == ECHO_STATE_TRAINING) {
- if (ss->ec_current->echo_can_traintap(ss->ec_state, ss->echolastupdate++, rxlin)) {
+ if (ss->ec_state->status.mode == ECHO_MODE_TRAINING) {
+ if (ss->ec_state->ops->echocan_traintap(ss->ec_state, ss->ec_state->status.last_train_tap++, rxlin)) {
#if 0
- module_printk(KERN_NOTICE, "Finished training (%d taps trained)!\n", ss->echolastupdate);
+ module_printk(KERN_NOTICE, "Finished training (%d taps trained)!\n", ss->ec_state->status.last_train_tap);
#endif
- ss->echostate = ECHO_STATE_ACTIVE;
+ ss->ec_state->status.mode = ECHO_MODE_ACTIVE;
}
}
rxlin = 0;
rxchunk[x] = DAHDI_LIN2X((int)rxlin, ss);
}
- } else {
- short rxlins[DAHDI_CHUNKSIZE], txlins[DAHDI_CHUNKSIZE];
- for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
- rxlins[x] = DAHDI_XLAW(rxchunk[x], ss);
- txlins[x] = DAHDI_XLAW(txchunk[x], ss);
- }
- ss->ec_current->echo_can_array_update(ss->ec_state, rxlins, txlins);
- for (x = 0; x < DAHDI_CHUNKSIZE; x++)
- rxchunk[x] = DAHDI_LIN2X((int) rxlins[x], ss);
+ } else if (ss->ec_state->status.mode != ECHO_MODE_IDLE) {
+ ss->ec_state->events.all = 0;
+
+ if (ss->ec_state->ops->echocan_process) {
+ short rxlins[DAHDI_CHUNKSIZE], txlins[DAHDI_CHUNKSIZE];
+
+ for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
+ rxlins[x] = DAHDI_XLAW(rxchunk[x], ss);
+ txlins[x] = DAHDI_XLAW(txchunk[x], ss);
+ }
+ ss->ec_state->ops->echocan_process(ss->ec_state, rxlins, txlins, DAHDI_CHUNKSIZE);
+
+ for (x = 0; x < DAHDI_CHUNKSIZE; x++)
+ rxchunk[x] = DAHDI_LIN2X((int) rxlins[x], ss);
+ } else if (ss->ec_state->ops->echocan_events)
+ ss->ec_state->ops->echocan_events(ss->ec_state);
+
+ if (ss->ec_state->events.all)
+ process_echocan_events(ss);
+
}
#if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP)
kernel_fpu_end();
@@ -6692,24 +6750,16 @@ static inline void __dahdi_process_putaudio_chunk(struct dahdi_chan *ss, unsigne
putlin[x] = DAHDI_XLAW(rxb[x], ms);
}
-#ifndef NO_ECHOCAN_DISABLE
- if (ms->ec_state) {
- for (x=0;x<DAHDI_CHUNKSIZE;x++) {
- if (echo_can_disable_detector_update(&ms->rxecdis, putlin[x])) {
- module_printk(KERN_NOTICE, "Disabled echo canceller because of tone (rx) on channel %d\n", ss->channo);
- ms->echocancel = 0;
- ms->echostate = ECHO_STATE_IDLE;
- ms->echolastupdate = 0;
- ms->echotimer = 0;
- ms->ec_current->echo_can_free(ms->ec_state);
- ms->ec_state = NULL;
- release_echocan(ms->ec_current);
- ms->ec_current = NULL;
+ if (ms->ec_state && (ms->ec_state->status.mode == ECHO_MODE_ACTIVE) && !ms->ec_state->features.CED_rx_detect) {
+ for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
+ if (echo_can_disable_detector_update(&ms->ec_state->rxecdis, putlin[x])) {
+ set_echocan_fax_mode(ms, ss->channo, "CED rx detected", 1);
+ dahdi_qevent_nolock(ms, DAHDI_EVENT_RX_CED_DETECTED);
break;
}
}
}
-#endif
+
/* if doing rx tone decoding */
if (ms->rxp1 && ms->rxp2 && ms->rxp3)
{
diff --git a/drivers/dahdi/dahdi_echocan_jpah.c b/drivers/dahdi/dahdi_echocan_jpah.c
index 3dda26f..05bd2fd 100644
--- a/drivers/dahdi/dahdi_echocan_jpah.c
+++ b/drivers/dahdi/dahdi_echocan_jpah.c
@@ -40,84 +40,98 @@ static int debug;
#define module_printk(level, fmt, args...) printk(level "%s: " fmt, THIS_MODULE->name, ## args)
#define debug_printk(level, fmt, args...) if (debug >= level) printk("%s (%s): " fmt, THIS_MODULE->name, __FUNCTION__, ## args)
-struct echo_can_state {
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size);
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val);
+
+static const struct dahdi_echocan_factory my_factory = {
+ .name = "JPAH",
+ .owner = THIS_MODULE,
+ .echocan_create = echo_can_create,
+};
+
+static const struct dahdi_echocan_ops my_ops = {
+ .name = "JPAH",
+ .echocan_free = echo_can_free,
+ .echocan_process = echo_can_process,
+ .echocan_traintap = echo_can_traintap,
+};
+
+struct ec_pvt {
+ struct dahdi_echocan_state dahdi;
int blah;
};
-static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p,
- struct echo_can_state **ec)
+#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi)
+
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
- unsigned int x;
- char *c;
+ struct ec_pvt *pvt;
- if ((*ec = kmalloc(sizeof(**ec), GFP_KERNEL))) {
- memset(ec, 0, sizeof(**ec));
+ if (ecp->param_count > 0) {
+ printk(KERN_WARNING "JPAH does not support parameters; failing request\n");
+ return -EINVAL;
}
- for (x = 0; x < ecp->param_count; x++) {
- for (c = p[x].name; *c; c++)
- *c = tolower(*c);
- printk(KERN_WARNING "Unknown parameter supplied to JPAH echo canceler: '%s'\n", p[x].name);
- kfree(*ec);
+ pvt = kzalloc(sizeof(*pvt), GFP_KERNEL);
+ if (!pvt)
+ return -ENOMEM;
- return -EINVAL;
- }
+ pvt->dahdi.ops = &my_ops;
+ *ec = &pvt->dahdi;
return 0;
}
-static void echo_can_free(struct echo_can_state *ec)
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
{
- kfree(ec);
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ kfree(pvt);
}
-static void echo_can_update(struct echo_can_state *ec, short *isig, short *iref)
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size)
{
- unsigned int x;
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+ u32 x;
- for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
- if (ec->blah < 2) {
- ec->blah++;
+ for (x = 0; x < size; x++) {
+ if (pvt->blah < 2) {
+ pvt->blah++;
*isig++ = 0;
} else {
- ec->blah = 0;
+ pvt->blah = 0;
isig++;
}
}
}
-static int echo_can_traintap(struct echo_can_state *ec, int pos, short val)
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val)
{
return 0;
}
-static const struct dahdi_echocan me = {
- .name = "JPAH",
- .owner = THIS_MODULE,
- .echo_can_create = echo_can_create,
- .echo_can_free = echo_can_free,
- .echo_can_array_update = echo_can_update,
- .echo_can_traintap = echo_can_traintap,
-};
-
static int __init mod_init(void)
{
- if (dahdi_register_echocan(&me)) {
+ if (dahdi_register_echocan_factory(&my_factory)) {
module_printk(KERN_ERR, "could not register with DAHDI core\n");
return -EPERM;
}
- module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", me.name);
+ module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name);
return 0;
}
static void __exit mod_exit(void)
{
- dahdi_unregister_echocan(&me);
+ dahdi_unregister_echocan_factory(&my_factory);
}
module_param(debug, int, S_IRUGO | S_IWUSR);
diff --git a/drivers/dahdi/dahdi_echocan_kb1.c b/drivers/dahdi/dahdi_echocan_kb1.c
index 7fc2dc5..94f8523 100644
--- a/drivers/dahdi/dahdi_echocan_kb1.c
+++ b/drivers/dahdi/dahdi_echocan_kb1.c
@@ -142,8 +142,33 @@ typedef struct {
short *buf_d;
} echo_can_cb_s;
-/* Echo canceller definition */
-struct echo_can_state {
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size);
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val);
+static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable);
+
+static const struct dahdi_echocan_factory my_factory = {
+ .name = "KB1",
+ .owner = THIS_MODULE,
+ .echocan_create = echo_can_create,
+};
+
+static const struct dahdi_echocan_features my_features = {
+ .NLP_toggle = 1,
+};
+
+static const struct dahdi_echocan_ops my_ops = {
+ .name = "KB1",
+ .echocan_free = echo_can_free,
+ .echocan_process = echo_can_process,
+ .echocan_traintap = echo_can_traintap,
+ .echocan_NLP_toggle = echocan_NLP_toggle,
+};
+
+struct ec_pvt {
+ struct dahdi_echocan_state dahdi;
/* an arbitrary ID for this echo can - this really should be settable from the calling channel... */
int id;
@@ -208,8 +233,11 @@ struct echo_can_state {
int avg_Lu_i_ok;
#endif
unsigned int aggressive:1;
+ int use_nlp;
};
+#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi)
+
static inline void init_cb_s(echo_can_cb_s *cb, int len, void *where)
{
cb->buf_d = (short *)where;
@@ -236,77 +264,79 @@ static inline short get_cc_s(echo_can_cb_s *cb, int pos)
return cb->buf_d[cb->idx_d + pos];
}
-static inline void init_cc(struct echo_can_state *ec, int N, int maxy, int maxu)
+static inline void init_cc(struct ec_pvt *pvt, int N, int maxy, int maxu)
{
-
- void *ptr = ec;
+ void *ptr = pvt;
unsigned long tmp;
+
/* Double-word align past end of state */
- ptr += sizeof(struct echo_can_state);
+ ptr += sizeof(*pvt);
tmp = (unsigned long)ptr;
tmp += 3;
tmp &= ~3L;
ptr = (void *)tmp;
/* Reset parameters */
- ec->N_d = N;
- ec->beta2_i = DEFAULT_BETA1_I;
+ pvt->N_d = N;
+ pvt->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);
+ pvt->a_i = ptr;
+ ptr += (sizeof(int) * pvt->N_d);
+ pvt->a_s = ptr;
+ ptr += (sizeof(short) * pvt->N_d);
/* Reset Y circular buffer (short version) */
- init_cb_s(&ec->y_s, maxy, ptr);
+ init_cb_s(&pvt->y_s, maxy, ptr);
ptr += (sizeof(short) * (maxy) * 2);
/* Reset Sigma circular buffer (short version for FIR filter) */
- init_cb_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr);
+ init_cb_s(&pvt->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);
+ init_cb_s(&pvt->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);
+ init_cb_s(&pvt->y_tilde_s, pvt->N_d, ptr);
/* Reset the absolute time index */
- ec->i_d = (int)0;
+ pvt->i_d = (int)0;
/* Reset the power computations (for y and u) */
- ec->Ly_i = DEFAULT_CUTOFF_I;
- ec->Lu_i = DEFAULT_CUTOFF_I;
+ pvt->Ly_i = DEFAULT_CUTOFF_I;
+ pvt->Lu_i = DEFAULT_CUTOFF_I;
#ifdef MEC2_STATS
/* set the identity */
- ec->id = (int)&ptr;
+ pvt->id = (int)&ptr;
/* Reset performance stats */
- ec->cntr_nearend_speech_frames = (int)0;
- ec->cntr_residualcorrected_frames = (int)0;
- ec->cntr_residualcorrected_framesskipped = (int)0;
- ec->cntr_coeff_updates = (int)0;
- ec->cntr_coeff_missedupdates = (int)0;
-
- ec->avg_Lu_i_toolow = (int)0;
- ec->avg_Lu_i_ok = (int)0;
+ pvt->cntr_nearend_speech_frames = (int)0;
+ pvt->cntr_residualcorrected_frames = (int)0;
+ pvt->cntr_residualcorrected_framesskipped = (int)0;
+ pvt->cntr_coeff_updates = (int)0;
+ pvt->cntr_coeff_missedupdates = (int)0;
+
+ pvt->avg_Lu_i_toolow = (int)0;
+ pvt->avg_Lu_i_ok = (int)0;
#endif
/* Reset the near-end speech detector */
- ec->s_tilde_i = (int)0;
- ec->y_tilde_i = (int)0;
- ec->HCNTR_d = (int)0;
+ pvt->s_tilde_i = (int)0;
+ pvt->y_tilde_i = (int)0;
+ pvt->HCNTR_d = (int)0;
}
-static void echo_can_free(struct echo_can_state *ec)
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
{
- kfree(ec);
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ kfree(pvt);
}
-static inline short sample_update(struct echo_can_state *ec, short iref, short isig)
+static inline short sample_update(struct ec_pvt *pvt, short iref, short isig)
{
/* Declare local variables that are used more than once */
/* ... */
@@ -335,17 +365,17 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
/* Update the Far-end receive signal circular buffers and accumulators */
/* ------------------------------------------------------------------- */
/* Delete the oldest sample from the power estimate accumulator */
- ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I;
+ pvt->y_tilde_i -= abs(get_cc_s(&pvt->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1)) >> DEFAULT_ALPHA_YT_I;
/* Add the new sample to the power estimate accumulator */
- ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I;
+ pvt->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I;
/* Push a copy of the new sample into its circular buffer */
- add_cc_s(&ec->y_s, iref);
+ add_cc_s(&pvt->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 = CONVOLVE2(pvt->a_s,
+ pvt->y_s.buf_d + pvt->y_s.idx_d,
+ pvt->N_d);
rs >>= 15;
/* eq. (3): compute the output value (see figure 3) and the error
@@ -355,27 +385,27 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
u = isig - rs;
/* Push a copy of the output value sample into its circular buffer */
- add_cc_s(&ec->u_s, u);
+ add_cc_s(&pvt->u_s, u);
/* Update the Near-end hybrid signal circular buffers and accumulators */
/* ------------------------------------------------------------------- */
/* Delete the oldest sample from the power estimate accumulator */
- ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 ));
+ pvt->s_tilde_i -= abs(get_cc_s(&pvt->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1));
/* Add the new sample to the power estimate accumulator */
- ec->s_tilde_i += abs(isig);
+ pvt->s_tilde_i += abs(isig);
/* Push a copy of the new sample into it's circular buffer */
- add_cc_s(&ec->s_s, isig);
+ add_cc_s(&pvt->s_s, isig);
/* Push a copy of the current short-time average of the far-end receive signal into it's circular buffer */
- add_cc_s(&ec->y_tilde_s, ec->y_tilde_i);
+ add_cc_s(&pvt->y_tilde_s, pvt->y_tilde_i);
/* flow B on pg. 428 */
/* If the hangover timer isn't running then compute the new convergence factor, otherwise set Py_i to 32768 */
- if (!ec->HCNTR_d) {
- Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->Ly_i >> DEFAULT_SIGMA_LY_I);
+ if (!pvt->HCNTR_d) {
+ Py_i = (pvt->Ly_i >> DEFAULT_SIGMA_LY_I) * (pvt->Ly_i >> DEFAULT_SIGMA_LY_I);
Py_i >>= 15;
} else {
Py_i = (1 << 15);
@@ -389,107 +419,107 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
* Still needs conversion!
*/
- 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)));
+ if (pvt->start_speech_d != 0) {
+ if (pvt->i_d > (DEFAULT_T0 + pvt->start_speech_d)*(SAMPLE_FREQ)) {
+ pvt->beta2_d = max_cc_float(MIN_BETA, DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((pvt->i_d/(float)SAMPLE_FREQ) - DEFAULT_T0 - pvt->start_speech_d)));
}
} else {
- ec->beta2_d = DEFAULT_BETA1;
+ pvt->beta2_d = DEFAULT_BETA1;
}
#endif
/* Fixed point, inverted */
- ec->beta2_i = DEFAULT_BETA1_I;
+ pvt->beta2_i = DEFAULT_BETA1_I;
/* Fixed point version, inverted */
- two_beta_i = (ec->beta2_i * Py_i) >> 15;
+ two_beta_i = (pvt->beta2_i * Py_i) >> 15;
if (!two_beta_i)
two_beta_i++;
/* Update the Suppressed signal power estimate accumulator */
/* ------------------------------------------------------- */
/* Delete the oldest sample from the power estimate accumulator */
- ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ;
+ pvt->Lu_i -= abs(get_cc_s(&pvt->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1));
/* Add the new sample to the power estimate accumulator */
- ec->Lu_i += abs(u);
+ pvt->Lu_i += abs(u);
/* Update the Far-end reference signal power estimate accumulator */
/* -------------------------------------------------------------- */
/* eq. (10): update power estimate of the reference */
/* Delete the oldest sample from the power estimate accumulator */
- ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ;
+ pvt->Ly_i -= abs(get_cc_s(&pvt->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ;
/* Add the new sample to the power estimate accumulator */
- ec->Ly_i += abs(iref);
+ pvt->Ly_i += abs(iref);
- if (ec->Ly_i < DEFAULT_CUTOFF_I)
- ec->Ly_i = DEFAULT_CUTOFF_I;
+ if (pvt->Ly_i < DEFAULT_CUTOFF_I)
+ pvt->Ly_i = DEFAULT_CUTOFF_I;
/* Update the Peak far-end receive signal detected */
/* ----------------------------------------------- */
- if (ec->y_tilde_i > ec->max_y_tilde) {
+ if (pvt->y_tilde_i > pvt->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) {
+ pvt->max_y_tilde = pvt->y_tilde_i;
+ pvt->max_y_tilde_pos = pvt->N_d - 1;
+ } else if (--pvt->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);
+ pvt->max_y_tilde = MAX16(pvt->y_tilde_s.buf_d + pvt->y_tilde_s.idx_d, pvt->N_d, &pvt->max_y_tilde_pos);
}
/* Determine if near end speech was detected in this sample */
/* -------------------------------------------------------- */
- if (((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde)
- && (ec->max_y_tilde > 0)) {
+ if (((pvt->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > pvt->max_y_tilde)
+ && (pvt->max_y_tilde > 0)) {
/* Then start the Hangover counter */
- ec->HCNTR_d = DEFAULT_HANGT;
+ pvt->HCNTR_d = DEFAULT_HANGT;
#ifdef MEC2_STATS_DETAILED
- printk(KERN_INFO "Reset near end speech timer with: s_tilde_i %d, stmnt %d, max_y_tilde %d\n", ec->s_tilde_i, (ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), ec->max_y_tilde);
+ printk(KERN_INFO "Reset near end speech timer with: s_tilde_i %d, stmnt %d, max_y_tilde %d\n", pvt->s_tilde_i, (pvt->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), pvt->max_y_tilde);
#endif
#ifdef MEC2_STATS
- ++ec->cntr_nearend_speech_frames;
+ ++pvt->cntr_nearend_speech_frames;
#endif
- } else if (ec->HCNTR_d > (int)0) {
+ } else if (pvt->HCNTR_d > (int)0) {
/* otherwise, if it's still non-zero, decrement the Hangover counter by one sample */
#ifdef MEC2_STATS
- ++ec->cntr_nearend_speech_frames;
+ ++pvt->cntr_nearend_speech_frames;
#endif
- ec->HCNTR_d--;
+ pvt->HCNTR_d--;
}
/* Update coefficients if no near-end speech in this sample (ie. HCNTR_d = 0)
* and we have enough signal to bother trying to update.
* --------------------------------------------------------------------------
*/
- if (!ec->HCNTR_d && /* no near-end speech present */
- !(ec->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */
- if (ec->Lu_i > MIN_UPDATE_THRESH_I) { /* there is sufficient energy above the noise floor to contain meaningful data */
+ if (!pvt->HCNTR_d && /* no near-end speech present */
+ !(pvt->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */
+ if (pvt->Lu_i > MIN_UPDATE_THRESH_I) { /* there is sufficient energy above the noise floor to contain meaningful data */
/* so loop over all the filter coefficients */
#ifdef MEC2_STATS_DETAILED
- printk( KERN_INFO "updating coefficients with: ec->Lu_i %9d\n", ec->Lu_i);
+ printk(KERN_INFO "updating coefficients with: pvt->Lu_i %9d\n", pvt->Lu_i);
#endif
#ifdef MEC2_STATS
- ec->avg_Lu_i_ok = ec->avg_Lu_i_ok + ec->Lu_i;
- ++ec->cntr_coeff_updates;
+ pvt->avg_Lu_i_ok = pvt->avg_Lu_i_ok + pvt->Lu_i;
+ ++pvt->cntr_coeff_updates;
#endif
- 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;
- }
- } else {
+ for (k = 0; k < pvt->N_d; k++) {
+ /* eq. (7): compute an expectation over M_d samples */
+ int grad2;
+ grad2 = CONVOLVE2(pvt->u_s.buf_d + pvt->u_s.idx_d,
+ pvt->y_s.buf_d + pvt->y_s.idx_d + k,
+ DEFAULT_M);
+ /* eq. (7): update the coefficient */
+ pvt->a_i[k] += grad2 / two_beta_i;
+ pvt->a_s[k] = pvt->a_i[k] >> 16;
+ }
+ } else {
#ifdef MEC2_STATS_DETAILED
- printk( KERN_INFO "insufficient signal to update coefficients ec->Lu_i %5d < %5d\n", ec->Lu_i, MIN_UPDATE_THRESH_I);
+ printk(KERN_INFO "insufficient signal to update coefficients pvt->Lu_i %5d < %5d\n", pvt->Lu_i, MIN_UPDATE_THRESH_I);
#endif
#ifdef MEC2_STATS
- ec->avg_Lu_i_toolow = ec->avg_Lu_i_toolow + ec->Lu_i;
- ++ec->cntr_coeff_missedupdates;
+ pvt->avg_Lu_i_toolow = pvt->avg_Lu_i_toolow + pvt->Lu_i;
+ ++pvt->cntr_coeff_missedupdates;
#endif
- }
+ }
}
/* paragraph below eq. (15): if no near-end speech in the sample and
@@ -497,112 +527,116 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
* then perform residual error suppression
*/
#ifdef MEC2_STATS_DETAILED
- if (ec->HCNTR_d == 0)
- printk( KERN_INFO "possibily correcting frame with ec->Ly_i %9d ec->Lu_i %9d and expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1)));
+ if (pvt->HCNTR_d == 0)
+ printk(KERN_INFO "possibly correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d and expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1)));
#endif
#ifndef NO_ECHO_SUPPRESSOR
- if (ec->aggressive) {
- if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) {
- for (k=0; k < 2; k++) {
- u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1);
- }
+ if (pvt->use_nlp) {
+ if (pvt->aggressive) {
+ if ((pvt->HCNTR_d < AGGRESSIVE_HCNTR) && (pvt->Ly_i > (pvt->Lu_i << 1))) {
+ for (k = 0; k < 2; k++) {
+ u = u * (pvt->Lu_i >> DEFAULT_SIGMA_LU_I) / ((pvt->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1);
+ }
#ifdef MEC2_STATS_DETAILED
- printk( KERN_INFO "aggresively correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1)));
+ printk(KERN_INFO "aggresively correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1)));
#endif
#ifdef MEC2_STATS
- ++ec->cntr_residualcorrected_frames;
+ ++pvt->cntr_residualcorrected_frames;
#endif
- }
- } else {
- if (ec->HCNTR_d == 0) {
- if ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I) {
- for (k=0; k < 1; k++) {
- u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1);
- }
+ }
+ } else {
+ if (pvt->HCNTR_d == 0) {
+ if ((pvt->Ly_i/(pvt->Lu_i + 1)) > DEFAULT_SUPPR_I) {
+ for (k = 0; k < 1; k++) {
+ u = u * (pvt->Lu_i >> DEFAULT_SIGMA_LU_I) / ((pvt->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1);
+ }
#ifdef MEC2_STATS_DETAILED
- printk( KERN_INFO "correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1)));
+ printk(KERN_INFO "correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1)));
#endif
#ifdef MEC2_STATS
- ++ec->cntr_residualcorrected_frames;
+ ++pvt->cntr_residualcorrected_frames;
#endif
- }
+ }
#ifdef MEC2_STATS
- else {
- ++ec->cntr_residualcorrected_framesskipped;
- }
+ else {
+ ++pvt->cntr_residualcorrected_framesskipped;
+ }
#endif
+ }
}
}
#endif
#if 0
/* This will generate a non-linear supression factor, once converted */
- 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);
+ if ((pvt->HCNTR_d == 0) &&
+ ((pvt->Lu_d/pvt->Ly_d) < DEFAULT_SUPPR) &&
+ (pvt->Lu_d/pvt->Ly_d > EC_MIN_DB_VALUE)) {
+ suppr_factor = (10 / (float)(SUPPR_FLOOR - SUPPR_CEIL)) * log(pvt->Lu_d/pvt->Ly_d)
+ - SUPPR_CEIL / (float)(SUPPR_FLOOR - SUPPR_CEIL);
u_suppr = pow(10.0, (suppr_factor) * RES_SUPR_FACTOR / 10.0) * u_suppr;
}
#endif
#ifdef MEC2_STATS
/* Periodically dump performance stats */
- if ((ec->i_d % MEC2_STATS) == 0) {
+ if ((pvt->i_d % MEC2_STATS) == 0) {
/* make sure to avoid div0's! */
- if (ec->cntr_coeff_missedupdates > 0)
- ec->avg_Lu_i_toolow = (int)(ec->avg_Lu_i_toolow / ec->cntr_coeff_missedupdates);
+ if (pvt->cntr_coeff_missedupdates > 0)
+ pvt->avg_Lu_i_toolow = (int)(pvt->avg_Lu_i_toolow / pvt->cntr_coeff_missedupdates);
else
- ec->avg_Lu_i_toolow = -1;
+ pvt->avg_Lu_i_toolow = -1;
- if (ec->cntr_coeff_updates > 0)
- ec->avg_Lu_i_ok = (ec->avg_Lu_i_ok / ec->cntr_coeff_updates);
+ if (pvt->cntr_coeff_updates > 0)
+ pvt->avg_Lu_i_ok = (pvt->avg_Lu_i_ok / pvt->cntr_coeff_updates);
else
- ec->avg_Lu_i_ok = -1;
+ pvt->avg_Lu_i_ok = -1;
printk( KERN_INFO "%d: Near end speech: %5d Residuals corrected/skipped: %5d/%5d Coefficients updated ok/low sig: %3d/%3d Lu_i avg ok/low sig %6d/%5d\n",
- ec->id,
- ec->cntr_nearend_speech_frames,
- ec->cntr_residualcorrected_frames, ec->cntr_residualcorrected_framesskipped,
- ec->cntr_coeff_updates, ec->cntr_coeff_missedupdates,
- ec->avg_Lu_i_ok, ec->avg_Lu_i_toolow);
-
- ec->cntr_nearend_speech_frames = 0;
- ec->cntr_residualcorrected_frames = 0;
- ec->cntr_residualcorrected_framesskipped = 0;
- ec->cntr_coeff_updates = 0;
- ec->cntr_coeff_missedupdates = 0;
- ec->avg_Lu_i_ok = 0;
- ec->avg_Lu_i_toolow = 0;
+ pvt->id,
+ pvt->cntr_nearend_speech_frames,
+ pvt->cntr_residualcorrected_frames, pvt->cntr_residualcorrected_framesskipped,
+ pvt->cntr_coeff_updates, pvt->cntr_coeff_missedupdates,
+ pvt->avg_Lu_i_ok, pvt->avg_Lu_i_toolow);
+
+ pvt->cntr_nearend_speech_frames = 0;
+ pvt->cntr_residualcorrected_frames = 0;
+ pvt->cntr_residualcorrected_framesskipped = 0;
+ pvt->cntr_coeff_updates = 0;
+ pvt->cntr_coeff_missedupdates = 0;
+ pvt->avg_Lu_i_ok = 0;
+ pvt->avg_Lu_i_toolow = 0;
}
#endif
/* Increment the sample index and return the corrected sample */
- ec->i_d++;
+ pvt->i_d++;
return u;
}
-static void echo_can_update(struct echo_can_state *ec, short *isig, short *iref)
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size)
{
- unsigned int x;
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+ u32 x;
short result;
- for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
- result = sample_update(ec, *iref, *isig);
+ for (x = 0; x < size; x++) {
+ result = sample_update(pvt, *iref, *isig);
*isig++ = result;
++iref;
}
}
-static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p,
- struct echo_can_state **ec)
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
int maxy;
int maxu;
size_t size;
unsigned int x;
char *c;
+ struct ec_pvt *pvt;
maxy = ecp->tap_length + DEFAULT_M;
maxu = DEFAULT_M;
@@ -622,75 +656,81 @@ static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocan
2 * sizeof(short) * (maxu) + /* u_s */
2 * sizeof(short) * ecp->tap_length; /* y_tilde_s */
- if (!(*ec = kmalloc(size, GFP_KERNEL)))
+ pvt = kzalloc(size, GFP_KERNEL);
+ if (!pvt)
return -ENOMEM;
- memset(*ec, 0, size);
+ pvt->dahdi.ops = &my_ops;
- (*ec)->aggressive = aggressive;
+ pvt->aggressive = aggressive;
+ pvt->dahdi.features = my_features;
for (x = 0; x < ecp->param_count; x++) {
for (c = p[x].name; *c; c++)
*c = tolower(*c);
if (!strcmp(p[x].name, "aggressive")) {
- (*ec)->aggressive = p[x].value ? 1 : 0;
+ pvt->aggressive = p[x].value ? 1 : 0;
} else {
printk(KERN_WARNING "Unknown parameter supplied to KB1 echo canceler: '%s'\n", p[x].name);
- kfree(*ec);
+ kfree(pvt);
return -EINVAL;
}
}
- init_cc(*ec, ecp->tap_length, maxy, maxu);
+ init_cc(pvt, ecp->tap_length, maxy, maxu);
+ /* Non-linear processor - a fancy way to say "zap small signals, to avoid
+ accumulating noise". */
+ pvt->use_nlp = TRUE;
+ *ec = &pvt->dahdi;
return 0;
}
-static int echo_can_traintap(struct echo_can_state *ec, int pos, short val)
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val)
{
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
/* Set the hangover counter to the length of the can to
* avoid adjustments occuring immediately after initial forced training
*/
- ec->HCNTR_d = ec->N_d << 1;
+ pvt->HCNTR_d = pvt->N_d << 1;
- if (pos >= ec->N_d)
+ if (pos >= pvt->N_d)
return 1;
- ec->a_i[pos] = val << 17;
- ec->a_s[pos] = val << 1;
+ pvt->a_i[pos] = val << 17;
+ pvt->a_s[pos] = val << 1;
- if (++pos >= ec->N_d)
+ if (++pos >= pvt->N_d)
return 1;
return 0;
}
-static const struct dahdi_echocan me = {
- .name = "KB1",
- .owner = THIS_MODULE,
- .echo_can_create = echo_can_create,
- .echo_can_free = echo_can_free,
- .echo_can_array_update = echo_can_update,
- .echo_can_traintap = echo_can_traintap,
-};
+static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable)
+{
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ pvt->use_nlp = enable ? 1 : 0;
+}
static int __init mod_init(void)
{
- if (dahdi_register_echocan(&me)) {
+ if (dahdi_register_echocan_factory(&my_factory)) {
module_printk(KERN_ERR, "could not register with DAHDI core\n");
return -EPERM;
}
- module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", me.name);
+ module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name);
return 0;
}
static void __exit mod_exit(void)
{
- dahdi_unregister_echocan(&me);
+ dahdi_unregister_echocan_factory(&my_factory);
}
module_param(debug, int, S_IRUGO | S_IWUSR);
diff --git a/drivers/dahdi/dahdi_echocan_mg2.c b/drivers/dahdi/dahdi_echocan_mg2.c
index 4291edd..5dfd882 100644
--- a/drivers/dahdi/dahdi_echocan_mg2.c
+++ b/drivers/dahdi/dahdi_echocan_mg2.c
@@ -51,11 +51,11 @@ static int aggressive;
#define RESTORE_COEFFS {\
int x;\
- memcpy(ec->a_i, ec->c_i, ec->N_d*sizeof(int));\
- for (x=0;x<ec->N_d;x++) {\
- ec->a_s[x] = ec->a_i[x] >> 16;\
+ memcpy(pvt->a_i, pvt->c_i, pvt->N_d*sizeof(int));\
+ for (x = 0; x < pvt->N_d; x++) {\
+ pvt->a_s[x] = pvt->a_i[x] >> 16;\
}\
- ec->backup = BACKUP;\
+ pvt->backup = BACKUP;\
}
/* Uncomment to provide summary statistics for overall echo can performance every 4000 samples */
@@ -174,8 +174,33 @@ typedef struct {
short *buf_d;
} echo_can_cb_s;
-/* Echo canceller definition */
-struct echo_can_state {
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size);
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val);
+static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable);
+
+static const struct dahdi_echocan_factory my_factory = {
+ .name = "MG2",
+ .owner = THIS_MODULE,
+ .echocan_create = echo_can_create,
+};
+
+static const struct dahdi_echocan_features my_features = {
+ .NLP_toggle = 1,
+};
+
+static const struct dahdi_echocan_ops my_ops = {
+ .name = "MG2",
+ .echocan_free = echo_can_free,
+ .echocan_process = echo_can_process,
+ .echocan_traintap = echo_can_traintap,
+ .echocan_NLP_toggle = echocan_NLP_toggle,
+};
+
+struct ec_pvt {
+ struct dahdi_echocan_state dahdi;
/* an arbitrary ID for this echo can - this really should be settable from the calling channel... */
int id;
@@ -249,9 +274,11 @@ struct echo_can_state {
#ifdef DC_NORMALIZE
int dc_estimate;
#endif
-
+ int use_nlp;
};
+#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi)
+
static inline void init_cb_s(echo_can_cb_s *cb, int len, void *where)
{
cb->buf_d = (short *)where;
@@ -278,83 +305,85 @@ static inline short get_cc_s(echo_can_cb_s *cb, int pos)
return cb->buf_d[cb->idx_d + pos];
}
-static inline void init_cc(struct echo_can_state *ec, int N, int maxy, int maxu)
+static inline void init_cc(struct ec_pvt *pvt, int N, int maxy, int maxu)
{
-
- void *ptr = ec;
+ void *ptr = pvt;
unsigned long tmp;
+
/* Double-word align past end of state */
- ptr += sizeof(struct echo_can_state);
+ ptr += sizeof(*pvt);
tmp = (unsigned long)ptr;
tmp += 3;
tmp &= ~3L;
ptr = (void *)tmp;
/* Reset parameters */
- ec->N_d = N;
- ec->beta2_i = DEFAULT_BETA1_I;
+ pvt->N_d = N;
+ pvt->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);
+ pvt->a_i = ptr;
+ ptr += (sizeof(int) * pvt->N_d);
+ pvt->a_s = ptr;
+ ptr += (sizeof(short) * pvt->N_d);
/* Allocate backup memory */
- ec->b_i = ptr;
- ptr += (sizeof(int) * ec->N_d);
- ec->c_i = ptr;
- ptr += (sizeof(int) * ec->N_d);
+ pvt->b_i = ptr;
+ ptr += (sizeof(int) * pvt->N_d);
+ pvt->c_i = ptr;
+ ptr += (sizeof(int) * pvt->N_d);
/* Reset Y circular buffer (short version) */
- init_cb_s(&ec->y_s, maxy, ptr);
+ init_cb_s(&pvt->y_s, maxy, ptr);
ptr += (sizeof(short) * (maxy) * 2);
/* Reset Sigma circular buffer (short version for FIR filter) */
- init_cb_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr);
+ init_cb_s(&pvt->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);
+ init_cb_s(&pvt->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);
+ init_cb_s(&pvt->y_tilde_s, pvt->N_d, ptr);
/* Reset the absolute time index */
- ec->i_d = (int)0;
+ pvt->i_d = (int)0;
/* Reset the power computations (for y and u) */
- ec->Ly_i = DEFAULT_CUTOFF_I;
- ec->Lu_i = DEFAULT_CUTOFF_I;
+ pvt->Ly_i = DEFAULT_CUTOFF_I;
+ pvt->Lu_i = DEFAULT_CUTOFF_I;
#ifdef MEC2_STATS
/* set the identity */
- ec->id = (int)&ptr;
+ pvt->id = (int)&ptr;
/* Reset performance stats */
- ec->cntr_nearend_speech_frames = (int)0;
- ec->cntr_residualcorrected_frames = (int)0;
- ec->cntr_residualcorrected_framesskipped = (int)0;
- ec->cntr_coeff_updates = (int)0;
- ec->cntr_coeff_missedupdates = (int)0;
-
- ec->avg_Lu_i_toolow = (int)0;
- ec->avg_Lu_i_ok = (int)0;
+ pvt->cntr_nearend_speech_frames = (int)0;
+ pvt->cntr_residualcorrected_frames = (int)0;
+ pvt->cntr_residualcorrected_framesskipped = (int)0;
+ pvt->cntr_coeff_updates = (int)0;
+ pvt->cntr_coeff_missedupdates = (int)0;
+
+ pvt->avg_Lu_i_toolow = (int)0;
+ pvt->avg_Lu_i_ok = (int)0;
#endif
/* Reset the near-end speech detector */
- ec->s_tilde_i = (int)0;
- ec->y_tilde_i = (int)0;
- ec->HCNTR_d = (int)0;
+ pvt->s_tilde_i = (int)0;
+ pvt->y_tilde_i = (int)0;
+ pvt->HCNTR_d = (int)0;
}
-static void echo_can_free(struct echo_can_state *ec)
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
{
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
#if defined(DC_NORMALIZE) && defined(MEC2_DCBIAS_MESSAGE)
- printk(KERN_INFO "EC: DC bias calculated: %d V\n", ec->dc_estimate >> 15);
+ printk(KERN_INFO "EC: DC bias calculated: %d V\n", pvt->dc_estimate >> 15);
#endif
- kfree(ec);
+ kfree(pvt);
}
#ifdef DC_NORMALIZE
@@ -365,7 +394,7 @@ short inline dc_removal(int *dc_estimate, short samp)
}
#endif
-static inline short sample_update(struct echo_can_state *ec, short iref, short isig)
+static inline short sample_update(struct ec_pvt *pvt, short iref, short isig)
{
/* Declare local variables that are used more than once */
/* ... */
@@ -380,7 +409,7 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
int two_beta_i;
#ifdef DC_NORMALIZE
- isig = dc_removal(&ec->dc_estimate, isig);
+ isig = dc_removal(&pvt->dc_estimate, isig);
#endif
/* flow A on pg. 428 */
@@ -398,29 +427,29 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
/* Update the Far-end receive signal circular buffers and accumulators */
/* ------------------------------------------------------------------- */
/* Delete the oldest sample from the power estimate accumulator */
- ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I;
+ pvt->y_tilde_i -= abs(get_cc_s(&pvt->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1)) >> DEFAULT_ALPHA_YT_I;
/* Add the new sample to the power estimate accumulator */
- ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I;
+ pvt->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I;
/* Push a copy of the new sample into its circular buffer */
- add_cc_s(&ec->y_s, iref);
+ add_cc_s(&pvt->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 = CONVOLVE2(pvt->a_s,
+ pvt->y_s.buf_d + pvt->y_s.idx_d,
+ pvt->N_d);
rs >>= 15;
- if (ec->lastsig == isig) {
- ec->lastcount++;
+ if (pvt->lastsig == isig) {
+ pvt->lastcount++;
} else {
- ec->lastcount = 0;
- ec->lastsig = isig;
+ pvt->lastcount = 0;
+ pvt->lastsig = isig;
}
if (isig == 0) {
u = 0;
- } else if (ec->lastcount > 255) {
+ } else if (pvt->lastcount > 255) {
/* We have seen the same input-signal more than 255 times,
* we should pass it through uncancelled, as we are likely on hold */
u = isig;
@@ -429,11 +458,11 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
if (rs < -32768) {
rs = -32768;
- ec->HCNTR_d = DEFAULT_HANGT;
+ pvt->HCNTR_d = DEFAULT_HANGT;
RESTORE_COEFFS;
} else if (rs > 32767) {
rs = 32767;
- ec->HCNTR_d = DEFAULT_HANGT;
+ pvt->HCNTR_d = DEFAULT_HANGT;
RESTORE_COEFFS;
}
@@ -456,35 +485,35 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
}
/* Push a copy of the output value sample into its circular buffer */
- add_cc_s(&ec->u_s, u);
+ add_cc_s(&pvt->u_s, u);
- if (!ec->backup) {
+ if (!pvt->backup) {
/* Backup coefficients periodically */
- ec->backup = BACKUP;
- memcpy(ec->c_i,ec->b_i,ec->N_d*sizeof(int));
- memcpy(ec->b_i,ec->a_i,ec->N_d*sizeof(int));
+ pvt->backup = BACKUP;
+ memcpy(pvt->c_i, pvt->b_i, pvt->N_d*sizeof(int));
+ memcpy(pvt->b_i, pvt->a_i, pvt->N_d*sizeof(int));
} else
- ec->backup--;
+ pvt->backup--;
/* Update the Near-end hybrid signal circular buffers and accumulators */
/* ------------------------------------------------------------------- */
/* Delete the oldest sample from the power estimate accumulator */
- ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 ));
+ pvt->s_tilde_i -= abs(get_cc_s(&pvt->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1));
/* Add the new sample to the power estimate accumulator */
- ec->s_tilde_i += abs(isig);
+ pvt->s_tilde_i += abs(isig);
/* Push a copy of the new sample into it's circular buffer */
- add_cc_s(&ec->s_s, isig);
+ add_cc_s(&pvt->s_s, isig);
/* Push a copy of the current short-time average of the far-end receive signal into it's circular buffer */
- add_cc_s(&ec->y_tilde_s, ec->y_tilde_i);
+ add_cc_s(&pvt->y_tilde_s, pvt->y_tilde_i);
/* flow B on pg. 428 */
/* If the hangover timer isn't running then compute the new convergence factor, otherwise set Py_i to 32768 */
- if (!ec->HCNTR_d) {
- Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->Ly_i >> DEFAULT_SIGMA_LY_I);
+ if (!pvt->HCNTR_d) {
+ Py_i = (pvt->Ly_i >> DEFAULT_SIGMA_LY_I) * (pvt->Ly_i >> DEFAULT_SIGMA_LY_I);
Py_i >>= 15;
} else {
Py_i = (1 << 15);
@@ -498,139 +527,139 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
* Still needs conversion!
*/
- 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)));
+ if (pvt->start_speech_d != 0) {
+ if (pvt->i_d > (DEFAULT_T0 + pvt->start_speech_d)*(SAMPLE_FREQ)) {
+ pvt->beta2_d = max_cc_float(MIN_BETA, DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((pvt->i_d/(float)SAMPLE_FREQ) - DEFAULT_T0 - pvt->start_speech_d)));
}
} else {
- ec->beta2_d = DEFAULT_BETA1;
+ pvt->beta2_d = DEFAULT_BETA1;
}
#endif
/* Fixed point, inverted */
- ec->beta2_i = DEFAULT_BETA1_I;
+ pvt->beta2_i = DEFAULT_BETA1_I;
/* Fixed point version, inverted */
- two_beta_i = (ec->beta2_i * Py_i) >> 15;
+ two_beta_i = (pvt->beta2_i * Py_i) >> 15;
if (!two_beta_i)
two_beta_i++;
/* Update the Suppressed signal power estimate accumulator */
/* ------------------------------------------------------- */
/* Delete the oldest sample from the power estimate accumulator */
- ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ;
+ pvt->Lu_i -= abs(get_cc_s(&pvt->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1));
/* Add the new sample to the power estimate accumulator */
- ec->Lu_i += abs(u);
+ pvt->Lu_i += abs(u);
/* Update the Far-end reference signal power estimate accumulator */
/* -------------------------------------------------------------- */
/* eq. (10): update power estimate of the reference */
/* Delete the oldest sample from the power estimate accumulator */
- ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ;
+ pvt->Ly_i -= abs(get_cc_s(&pvt->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ;
/* Add the new sample to the power estimate accumulator */
- ec->Ly_i += abs(iref);
+ pvt->Ly_i += abs(iref);
- if (ec->Ly_i < DEFAULT_CUTOFF_I)
- ec->Ly_i = DEFAULT_CUTOFF_I;
+ if (pvt->Ly_i < DEFAULT_CUTOFF_I)
+ pvt->Ly_i = DEFAULT_CUTOFF_I;
/* Update the Peak far-end receive signal detected */
/* ----------------------------------------------- */
- if (ec->y_tilde_i > ec->max_y_tilde) {
+ if (pvt->y_tilde_i > pvt->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) {
+ pvt->max_y_tilde = pvt->y_tilde_i;
+ pvt->max_y_tilde_pos = pvt->N_d - 1;
+ } else if (--pvt->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);
+ pvt->max_y_tilde = MAX16(pvt->y_tilde_s.buf_d + pvt->y_tilde_s.idx_d, pvt->N_d, &pvt->max_y_tilde_pos);
}
/* Determine if near end speech was detected in this sample */
/* -------------------------------------------------------- */
- if (((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde)
- && (ec->max_y_tilde > 0)) {
+ if (((pvt->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > pvt->max_y_tilde)
+ && (pvt->max_y_tilde > 0)) {
/* Then start the Hangover counter */
- ec->HCNTR_d = DEFAULT_HANGT;
+ pvt->HCNTR_d = DEFAULT_HANGT;
RESTORE_COEFFS;
#ifdef MEC2_STATS_DETAILED
- printk(KERN_INFO "Reset near end speech timer with: s_tilde_i %d, stmnt %d, max_y_tilde %d\n", ec->s_tilde_i, (ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), ec->max_y_tilde);
+ printk(KERN_INFO "Reset near end speech timer with: s_tilde_i %d, stmnt %d, max_y_tilde %d\n", pvt->s_tilde_i, (pvt->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), pvt->max_y_tilde);
#endif
#ifdef MEC2_STATS
- ++ec->cntr_nearend_speech_frames;
+ ++pvt->cntr_nearend_speech_frames;
#endif
- } else if (ec->HCNTR_d > (int)0) {
+ } else if (pvt->HCNTR_d > (int)0) {
/* otherwise, if it's still non-zero, decrement the Hangover counter by one sample */
#ifdef MEC2_STATS
- ++ec->cntr_nearend_speech_frames;
+ ++pvt->cntr_nearend_speech_frames;
#endif
- ec->HCNTR_d--;
+ pvt->HCNTR_d--;
}
/* Update coefficients if no near-end speech in this sample (ie. HCNTR_d = 0)
* and we have enough signal to bother trying to update.
* --------------------------------------------------------------------------
*/
- if (!ec->HCNTR_d && /* no near-end speech present */
- !(ec->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */
- if (ec->Lu_i > MIN_UPDATE_THRESH_I) { /* there is sufficient energy above the noise floor to contain meaningful data */
+ if (!pvt->HCNTR_d && /* no near-end speech present */
+ !(pvt->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */
+ if (pvt->Lu_i > MIN_UPDATE_THRESH_I) { /* there is sufficient energy above the noise floor to contain meaningful data */
/* so loop over all the filter coefficients */
#ifdef USED_COEFFS
- int max_coeffs[USED_COEFFS];
- int *pos;
+ int max_coeffs[USED_COEFFS];
+ int *pos;
- if (ec->N_d > USED_COEFFS)
- memset(max_coeffs, 0, USED_COEFFS*sizeof(int));
+ if (pvt->N_d > USED_COEFFS)
+ memset(max_coeffs, 0, USED_COEFFS*sizeof(int));
#endif
#ifdef MEC2_STATS_DETAILED
- printk(KERN_INFO "updating coefficients with: ec->Lu_i %9d\n", ec->Lu_i);
+ printk(KERN_INFO "updating coefficients with: pvt->Lu_i %9d\n", pvt->Lu_i);
#endif
#ifdef MEC2_STATS
- ec->avg_Lu_i_ok = ec->avg_Lu_i_ok + ec->Lu_i;
- ++ec->cntr_coeff_updates;
+ pvt->avg_Lu_i_ok = pvt->avg_Lu_i_ok + pvt->Lu_i;
+ ++pvt->cntr_coeff_updates;
#endif
- 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;
+ for (k = 0; k < pvt->N_d; k++) {
+ /* eq. (7): compute an expectation over M_d samples */
+ int grad2;
+ grad2 = CONVOLVE2(pvt->u_s.buf_d + pvt->u_s.idx_d,
+ pvt->y_s.buf_d + pvt->y_s.idx_d + k,
+ DEFAULT_M);
+ /* eq. (7): update the coefficient */
+ pvt->a_i[k] += grad2 / two_beta_i;
+ pvt->a_s[k] = pvt->a_i[k] >> 16;
#ifdef USED_COEFFS
- if (ec->N_d > USED_COEFFS) {
- if (abs(ec->a_i[k]) > max_coeffs[USED_COEFFS-1]) {
- /* More or less insertion-sort... */
- pos = max_coeffs;
- while (*pos > abs(ec->a_i[k]))
- pos++;
-
- if (*pos > max_coeffs[USED_COEFFS-1])
- memmove(pos+1, pos, (USED_COEFFS-(pos-max_coeffs)-1)*sizeof(int));
-
- *pos = abs(ec->a_i[k]);
- }
+ if (pvt->N_d > USED_COEFFS) {
+ if (abs(pvt->a_i[k]) > max_coeffs[USED_COEFFS-1]) {
+ /* More or less insertion-sort... */
+ pos = max_coeffs;
+ while (*pos > abs(pvt->a_i[k]))
+ pos++;
+
+ if (*pos > max_coeffs[USED_COEFFS-1])
+ memmove(pos+1, pos, (USED_COEFFS-(pos-max_coeffs)-1)*sizeof(int));
+
+ *pos = abs(pvt->a_i[k]);
}
-#endif
}
+#endif
+ }
#ifdef USED_COEFFS
- /* Filter out irrelevant coefficients */
- if (ec->N_d > USED_COEFFS)
- for (k=0; k < ec->N_d; k++)
- if (abs(ec->a_i[k]) < max_coeffs[USED_COEFFS-1])
- ec->a_i[k] = ec->a_s[k] = 0;
+ /* Filter out irrelevant coefficients */
+ if (pvt->N_d > USED_COEFFS)
+ for (k = 0; k < pvt->N_d; k++)
+ if (abs(pvt->a_i[k]) < max_coeffs[USED_COEFFS-1])
+ pvt->a_i[k] = pvt->a_s[k] = 0;
#endif
- } else {
+ } else {
#ifdef MEC2_STATS_DETAILED
- printk(KERN_INFO "insufficient signal to update coefficients ec->Lu_i %5d < %5d\n", ec->Lu_i, MIN_UPDATE_THRESH_I);
+ printk(KERN_INFO "insufficient signal to update coefficients pvt->Lu_i %5d < %5d\n", pvt->Lu_i, MIN_UPDATE_THRESH_I);
#endif
#ifdef MEC2_STATS
- ec->avg_Lu_i_toolow = ec->avg_Lu_i_toolow + ec->Lu_i;
- ++ec->cntr_coeff_missedupdates;
+ pvt->avg_Lu_i_toolow = pvt->avg_Lu_i_toolow + pvt->Lu_i;
+ ++pvt->cntr_coeff_missedupdates;
#endif
- }
+ }
}
/* paragraph below eq. (15): if no near-end speech in the sample and
@@ -638,112 +667,116 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
* then perform residual error suppression
*/
#ifdef MEC2_STATS_DETAILED
- if (ec->HCNTR_d == 0)
- printk(KERN_INFO "possibily correcting frame with ec->Ly_i %9d ec->Lu_i %9d and expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1)));
+ if (pvt->HCNTR_d == 0)
+ printk(KERN_INFO "possibly correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d and expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1)));
#endif
#ifndef NO_ECHO_SUPPRESSOR
- if (ec->aggressive) {
- if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) {
- for (k=0; k < 2; k++) {
- u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1);
- }
+ if (pvt->use_nlp) {
+ if (pvt->aggressive) {
+ if ((pvt->HCNTR_d < AGGRESSIVE_HCNTR) && (pvt->Ly_i > (pvt->Lu_i << 1))) {
+ for (k = 0; k < 2; k++) {
+ u = u * (pvt->Lu_i >> DEFAULT_SIGMA_LU_I) / ((pvt->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1);
+ }
#ifdef MEC2_STATS_DETAILED
- printk(KERN_INFO "aggresively correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1)));
+ printk(KERN_INFO "aggresively correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1)));
#endif
#ifdef MEC2_STATS
- ++ec->cntr_residualcorrected_frames;
+ ++pvt->cntr_residualcorrected_frames;
#endif
- }
- } else {
- if (ec->HCNTR_d == 0) {
- if ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I) {
- for (k=0; k < 1; k++) {
- u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1);
- }
+ }
+ } else {
+ if (pvt->HCNTR_d == 0) {
+ if ((pvt->Ly_i/(pvt->Lu_i + 1)) > DEFAULT_SUPPR_I) {
+ for (k = 0; k < 1; k++) {
+ u = u * (pvt->Lu_i >> DEFAULT_SIGMA_LU_I) / ((pvt->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1);
+ }
#ifdef MEC2_STATS_DETAILED
- printk(KERN_INFO "correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1)));
+ printk(KERN_INFO "correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1)));
#endif
#ifdef MEC2_STATS
- ++ec->cntr_residualcorrected_frames;
+ ++pvt->cntr_residualcorrected_frames;
#endif
- }
+ }
#ifdef MEC2_STATS
- else {
- ++ec->cntr_residualcorrected_framesskipped;
- }
+ else {
+ ++pvt->cntr_residualcorrected_framesskipped;
+ }
#endif
+ }
}
}
#endif
#if 0
/* This will generate a non-linear supression factor, once converted */
- 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);
+ if ((pvt->HCNTR_d == 0) &&
+ ((pvt->Lu_d/pvt->Ly_d) < DEFAULT_SUPPR) &&
+ (pvt->Lu_d/pvt->Ly_d > EC_MIN_DB_VALUE)) {
+ suppr_factor = (10 / (float)(SUPPR_FLOOR - SUPPR_CEIL)) * log(pvt->Lu_d/pvt->Ly_d)
+ - SUPPR_CEIL / (float)(SUPPR_FLOOR - SUPPR_CEIL);
u_suppr = pow(10.0, (suppr_factor) * RES_SUPR_FACTOR / 10.0) * u_suppr;
}
#endif
#ifdef MEC2_STATS
/* Periodically dump performance stats */
- if ((ec->i_d % MEC2_STATS) == 0) {
+ if ((pvt->i_d % MEC2_STATS) == 0) {
/* make sure to avoid div0's! */
- if (ec->cntr_coeff_missedupdates > 0)
- ec->avg_Lu_i_toolow = (int)(ec->avg_Lu_i_toolow / ec->cntr_coeff_missedupdates);
+ if (pvt->cntr_coeff_missedupdates > 0)
+ pvt->avg_Lu_i_toolow = (int)(pvt->avg_Lu_i_toolow / pvt->cntr_coeff_missedupdates);
else
- ec->avg_Lu_i_toolow = -1;
+ pvt->avg_Lu_i_toolow = -1;
- if (ec->cntr_coeff_updates > 0)
- ec->avg_Lu_i_ok = (ec->avg_Lu_i_ok / ec->cntr_coeff_updates);
+ if (pvt->cntr_coeff_updates > 0)
+ pvt->avg_Lu_i_ok = (pvt->avg_Lu_i_ok / pvt->cntr_coeff_updates);
else
- ec->avg_Lu_i_ok = -1;
+ pvt->avg_Lu_i_ok = -1;
printk(KERN_INFO "%d: Near end speech: %5d Residuals corrected/skipped: %5d/%5d Coefficients updated ok/low sig: %3d/%3d Lu_i avg ok/low sig %6d/%5d\n",
- ec->id,
- ec->cntr_nearend_speech_frames,
- ec->cntr_residualcorrected_frames, ec->cntr_residualcorrected_framesskipped,
- ec->cntr_coeff_updates, ec->cntr_coeff_missedupdates,
- ec->avg_Lu_i_ok, ec->avg_Lu_i_toolow);
-
- ec->cntr_nearend_speech_frames = 0;
- ec->cntr_residualcorrected_frames = 0;
- ec->cntr_residualcorrected_framesskipped = 0;
- ec->cntr_coeff_updates = 0;
- ec->cntr_coeff_missedupdates = 0;
- ec->avg_Lu_i_ok = 0;
- ec->avg_Lu_i_toolow = 0;
+ pvt->id,
+ pvt->cntr_nearend_speech_frames,
+ pvt->cntr_residualcorrected_frames, pvt->cntr_residualcorrected_framesskipped,
+ pvt->cntr_coeff_updates, pvt->cntr_coeff_missedupdates,
+ pvt->avg_Lu_i_ok, pvt->avg_Lu_i_toolow);
+
+ pvt->cntr_nearend_speech_frames = 0;
+ pvt->cntr_residualcorrected_frames = 0;
+ pvt->cntr_residualcorrected_framesskipped = 0;
+ pvt->cntr_coeff_updates = 0;
+ pvt->cntr_coeff_missedupdates = 0;
+ pvt->avg_Lu_i_ok = 0;
+ pvt->avg_Lu_i_toolow = 0;
}
#endif
/* Increment the sample index and return the corrected sample */
- ec->i_d++;
+ pvt->i_d++;
return u;
}
-static void echo_can_update(struct echo_can_state *ec, short *isig, short *iref)
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size)
{
- unsigned int x;
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+ u32 x;
short result;
- for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
- result = sample_update(ec, *iref, *isig);
+ for (x = 0; x < size; x++) {
+ result = sample_update(pvt, *iref, *isig);
*isig++ = result;
++iref;
}
}
-static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p,
- struct echo_can_state **ec)
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
int maxy;
int maxu;
size_t size;
unsigned int x;
char *c;
+ struct ec_pvt *pvt;
maxy = ecp->tap_length + DEFAULT_M;
maxu = DEFAULT_M;
@@ -764,81 +797,87 @@ static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocan
2 * sizeof(short) * (maxu) + /* u_s */
2 * sizeof(short) * ecp->tap_length; /* y_tilde_s */
- if (!(*ec = kmalloc(size, GFP_KERNEL)))
+ pvt = kzalloc(size, GFP_KERNEL);
+ if (!pvt)
return -ENOMEM;
- memset(*ec, 0, size);
+ pvt->dahdi.ops = &my_ops;
- (*ec)->aggressive = aggressive;
+ pvt->aggressive = aggressive;
+ pvt->dahdi.features = my_features;
for (x = 0; x < ecp->param_count; x++) {
for (c = p[x].name; *c; c++)
*c = tolower(*c);
if (!strcmp(p[x].name, "aggressive")) {
- (*ec)->aggressive = p[x].value ? 1 : 0;
+ pvt->aggressive = p[x].value ? 1 : 0;
} else {
printk(KERN_WARNING "Unknown parameter supplied to MG2 echo canceler: '%s'\n", p[x].name);
- kfree(*ec);
+ kfree(pvt);
return -EINVAL;
}
}
- init_cc(*ec, ecp->tap_length, maxy, maxu);
+ init_cc(pvt, ecp->tap_length, maxy, maxu);
+ /* Non-linear processor - a fancy way to say "zap small signals, to avoid
+ accumulating noise". */
+ pvt->use_nlp = TRUE;
+ *ec = &pvt->dahdi;
return 0;
}
-static int echo_can_traintap(struct echo_can_state *ec, int pos, short val)
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val)
{
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
/* Set the hangover counter to the length of the can to
* avoid adjustments occuring immediately after initial forced training
*/
- ec->HCNTR_d = ec->N_d << 1;
+ pvt->HCNTR_d = pvt->N_d << 1;
- if (pos >= ec->N_d) {
- memcpy(ec->b_i,ec->a_i,ec->N_d*sizeof(int));
- memcpy(ec->c_i,ec->a_i,ec->N_d*sizeof(int));
+ if (pos >= pvt->N_d) {
+ memcpy(pvt->b_i, pvt->a_i, pvt->N_d*sizeof(int));
+ memcpy(pvt->c_i, pvt->a_i, pvt->N_d*sizeof(int));
return 1;
}
- ec->a_i[pos] = val << 17;
- ec->a_s[pos] = val << 1;
+ pvt->a_i[pos] = val << 17;
+ pvt->a_s[pos] = val << 1;
- if (++pos >= ec->N_d) {
- memcpy(ec->b_i,ec->a_i,ec->N_d*sizeof(int));
- memcpy(ec->c_i,ec->a_i,ec->N_d*sizeof(int));
+ if (++pos >= pvt->N_d) {
+ memcpy(pvt->b_i, pvt->a_i, pvt->N_d*sizeof(int));
+ memcpy(pvt->c_i, pvt->a_i, pvt->N_d*sizeof(int));
return 1;
}
return 0;
}
-static const struct dahdi_echocan me = {
- .name = "MG2",
- .owner = THIS_MODULE,
- .echo_can_create = echo_can_create,
- .echo_can_free = echo_can_free,
- .echo_can_array_update = echo_can_update,
- .echo_can_traintap = echo_can_traintap,
-};
+static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable)
+{
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ pvt->use_nlp = enable ? 1 : 0;
+}
static int __init mod_init(void)
{
- if (dahdi_register_echocan(&me)) {
+ if (dahdi_register_echocan_factory(&my_factory)) {
module_printk(KERN_ERR, "could not register with DAHDI core\n");
return -EPERM;
}
- module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", me.name);
+ module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name);
return 0;
}
static void __exit mod_exit(void)
{
- dahdi_unregister_echocan(&me);
+ dahdi_unregister_echocan_factory(&my_factory);
}
module_param(debug, int, S_IRUGO | S_IWUSR);
diff --git a/drivers/dahdi/dahdi_echocan_oslec.c b/drivers/dahdi/dahdi_echocan_oslec.c
index 4d1c53f..a8d38b9 100644
--- a/drivers/dahdi/dahdi_echocan_oslec.c
+++ b/drivers/dahdi/dahdi_echocan_oslec.c
@@ -32,73 +32,107 @@
/* Fix this if OSLEC is elsewhere */
#include "../staging/echo/oslec.h"
//#include <linux/oslec.h>
-/* "provide" struct echo_can_state */
-//#define oslec_state echo_can_state
#include <dahdi/kernel.h>
#define module_printk(level, fmt, args...) printk(level "%s: " fmt, THIS_MODULE->name, ## args)
-static void echo_can_free(struct echo_can_state *ec)
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size);
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val);
+
+static const struct dahdi_echocan_factory my_factory = {
+ .name = "OSLEC",
+ .owner = THIS_MODULE,
+ .echocan_create = echo_can_create,
+};
+
+static const struct dahdi_echocan_ops my_ops = {
+ .name = "OSLEC",
+ .echocan_free = echo_can_free,
+ .echocan_process = echo_can_process,
+ .echocan_traintap = echo_can_traintap,
+};
+
+struct ec_pvt {
+ struct oslec_state *oslec;
+ struct dahdi_echocan_state dahdi;
+};
+
+#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi)
+
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
{
- oslec_free((struct oslec_state *)ec);
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ oslec_free(pvt->oslec);
+ kfree(pvt);
}
-static void echo_can_update(struct echo_can_state *ec, short *isig, short *iref)
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size)
{
- unsigned int SampleNum;
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+ u32 SampleNum;
- for (SampleNum = 0; SampleNum < DAHDI_CHUNKSIZE; SampleNum++, iref++)
- {
+ for (SampleNum = 0; SampleNum < size; SampleNum++, iref++) {
short iCleanSample;
- iCleanSample = (short) oslec_update((struct oslec_state *)ec, *iref, *isig);
+
+ iCleanSample = oslec_update(pvt->oslec, *iref, *isig);
*isig++ = iCleanSample;
}
}
-static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p,
- struct echo_can_state **ec)
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
+ struct ec_pvt *pvt;
+
if (ecp->param_count > 0) {
printk(KERN_WARNING "OSLEC does not support parameters; failing request\n");
return -EINVAL;
}
- *ec = (struct echo_can_state *)oslec_create(ecp->tap_length, ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP | ECHO_CAN_USE_CLIP | ECHO_CAN_USE_TX_HPF | ECHO_CAN_USE_RX_HPF);
+ pvt = kzalloc(sizeof(*pvt), GFP_KERNEL);
+ if (!pvt)
+ return -ENOMEM;
+
+ pvt->dahdi.ops = &my_ops;
+
+ pvt->oslec = oslec_create(ecp->tap_length, ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP | ECHO_CAN_USE_CLIP | ECHO_CAN_USE_TX_HPF | ECHO_CAN_USE_RX_HPF);
- return *ec ? 0 : -ENOTTY;
+ if (!pvt->oslec) {
+ kfree(pvt);
+ *ec = NULL;
+ return -ENOTTY;
+ } else {
+ *ec = &pvt->dahdi;
+ return 0;
+ }
}
-static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val)
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val)
{
return 1;
}
-static const struct dahdi_echocan me = {
- .name = "OSLEC",
- .owner = THIS_MODULE,
- .echo_can_create = echo_can_create,
- .echo_can_free = echo_can_free,
- .echo_can_array_update = echo_can_update,
- .echo_can_traintap = echo_can_traintap,
-};
-
static int __init mod_init(void)
{
- if (dahdi_register_echocan(&me)) {
+ if (dahdi_register_echocan_factory(&my_factory)) {
module_printk(KERN_ERR, "could not register with DAHDI core\n");
return -EPERM;
}
- module_printk(KERN_INFO, "Registered echo canceler '%s'\n", me.name);
+ module_printk(KERN_INFO, "Registered echo canceler '%s'\n", my_factory.name);
return 0;
}
static void __exit mod_exit(void)
{
- dahdi_unregister_echocan(&me);
+ dahdi_unregister_echocan_factory(&my_factory);
}
MODULE_DESCRIPTION("DAHDI OSLEC wrapper");
diff --git a/drivers/dahdi/dahdi_echocan_sec.c b/drivers/dahdi/dahdi_echocan_sec.c
index 14af244..2f6876e 100644
--- a/drivers/dahdi/dahdi_echocan_sec.c
+++ b/drivers/dahdi/dahdi_echocan_sec.c
@@ -67,36 +67,6 @@ static int debug;
#define NONUPDATE_DWELL_TIME 600 /* 600 samples, or 75ms */
-struct echo_can_state
-{
- int tx_power;
- int rx_power;
- int clean_rx_power;
-
- int rx_power_threshold;
- int nonupdate_dwell;
-
- int16_t *tx_history; /* Last N tx samples */
- int32_t *fir_taps; /* Echo FIR taps */
- int16_t *fir_taps_short; /* Echo FIR taps, shorts instead of ints */
-
- int curr_pos;
-
- int taps;
- int tap_mask;
- int use_nlp;
- int use_suppressor;
-
- int32_t supp_test1;
- int32_t supp_test2;
- int32_t supp1;
- int32_t supp2;
-
- int32_t latest_correction; /* Indication of the magnitude of the latest
- adaption, or a code to indicate why adaption
- was skipped, for test purposes */
-};
-
/* Original parameters :
#define MIN_TX_POWER_FOR_ADAPTION 256
#define MIN_RX_POWER_FOR_ADAPTION 128
@@ -110,221 +80,270 @@ struct echo_can_state
#define MIN_RX_POWER_FOR_ADAPTION 64
*/
-static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p,
- struct echo_can_state **ec)
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size);
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val);
+static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable);
+
+static const struct dahdi_echocan_factory my_factory = {
+ .name = "SEC",
+ .owner = THIS_MODULE,
+ .echocan_create = echo_can_create,
+};
+
+static const struct dahdi_echocan_features my_features = {
+ .NLP_toggle = 1,
+};
+
+static const struct dahdi_echocan_ops my_ops = {
+ .name = "SEC",
+ .echocan_free = echo_can_free,
+ .echocan_process = echo_can_process,
+ .echocan_traintap = echo_can_traintap,
+ .echocan_NLP_toggle = echocan_NLP_toggle,
+};
+
+struct ec_pvt {
+ struct dahdi_echocan_state dahdi;
+ int tx_power;
+ int rx_power;
+ int clean_rx_power;
+
+ int rx_power_threshold;
+ int nonupdate_dwell;
+
+ int16_t *tx_history; /* Last N tx samples */
+ int32_t *fir_taps; /* Echo FIR taps */
+ int16_t *fir_taps_short; /* Echo FIR taps, shorts instead of ints */
+
+ int curr_pos;
+
+ int taps;
+ int tap_mask;
+ int use_nlp;
+ int use_suppressor;
+
+ int32_t supp_test1;
+ int32_t supp_test2;
+ int32_t supp1;
+ int32_t supp2;
+
+ int32_t latest_correction; /* Indication of the magnitude of the latest
+ adaption, or a code to indicate why adaption
+ was skipped, for test purposes */
+};
+
+#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi)
+
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
+ struct ec_pvt *pvt;
size_t size;
-
+
if (ecp->param_count > 0) {
- printk(KERN_WARNING "SEC echo canceler does not support parameters; failing request\n");
+ printk(KERN_WARNING "SEC does not support parameters; failing request\n");
return -EINVAL;
}
+
+ size = sizeof(*pvt) + ecp->tap_length * sizeof(int32_t) + ecp->tap_length * 3 * sizeof(int16_t);
- size = sizeof(**ec) + ecp->tap_length * sizeof(int32_t) + ecp->tap_length * 3 * sizeof(int16_t);
-
- if (!(*ec = kmalloc(size, GFP_KERNEL)))
+ pvt = kzalloc(size, GFP_KERNEL);
+ if (!pvt)
return -ENOMEM;
-
- memset(*ec, 0, size);
-
- (*ec)->taps = ecp->tap_length;
- (*ec)->tap_mask = ecp->tap_length - 1;
- (*ec)->tx_history = (int16_t *) (*ec + sizeof(**ec));
- (*ec)->fir_taps = (int32_t *) (*ec + sizeof(**ec) +
- ecp->tap_length * 2 * sizeof(int16_t));
- (*ec)->fir_taps_short = (int16_t *) (*ec + sizeof(**ec) +
- ecp->tap_length * sizeof(int32_t) +
- ecp->tap_length * 2 * sizeof(int16_t));
- (*ec)->rx_power_threshold = 10000000;
- (*ec)->use_suppressor = FALSE;
+
+ pvt->dahdi.ops = &my_ops;
+ pvt->dahdi.features = my_features;
+
+ pvt->taps = ecp->tap_length;
+ pvt->tap_mask = ecp->tap_length - 1;
+ pvt->tx_history = (int16_t *) (pvt + sizeof(*pvt));
+ pvt->fir_taps = (int32_t *) (pvt + sizeof(*pvt) +
+ ecp->tap_length * 2 * sizeof(int16_t));
+ pvt->fir_taps_short = (int16_t *) (pvt + sizeof(*pvt) +
+ ecp->tap_length * sizeof(int32_t) +
+ ecp->tap_length * 2 * sizeof(int16_t));
+ pvt->rx_power_threshold = 10000000;
+ pvt->use_suppressor = FALSE;
/* Non-linear processor - a fancy way to say "zap small signals, to avoid
accumulating noise". */
- (*ec)->use_nlp = TRUE;
+ pvt->use_nlp = TRUE;
+ *ec = &pvt->dahdi;
return 0;
}
-/*- End of function --------------------------------------------------------*/
-static void echo_can_free(struct echo_can_state *ec)
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
{
- kfree(ec);
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ kfree(pvt);
}
-/*- End of function --------------------------------------------------------*/
-static inline int16_t sample_update(struct echo_can_state *ec, int16_t tx, int16_t rx)
+static inline int16_t sample_update(struct ec_pvt *pvt, int16_t tx, int16_t rx)
{
- int32_t echo_value;
- int clean_rx;
- int nsuppr;
-
- ec->tx_history[ec->curr_pos] = tx;
- ec->tx_history[ec->curr_pos + ec->taps] = tx;
-
- /* Evaluate the echo - i.e. apply the FIR filter */
- /* Assume the gain of the FIR does not exceed unity. Exceeding unity
- would seem like a rather poor thing for an echo cancellor to do :)
- This means we can compute the result with a total disregard for
- overflows. 16bits x 16bits -> 31bits, so no overflow can occur in
- any multiply. While accumulating we may overflow and underflow the
- 32 bit scale often. However, if the gain does not exceed unity,
- everything should work itself out, and the final result will be
- OK, without any saturation logic. */
- /* Overflow is very much possible here, and we do nothing about it because
- of the compute costs */
- /* 16 bit coeffs for the LMS give lousy results (maths good, actual sound
- bad!), but 32 bit coeffs require some shifting. On balance 32 bit seems
- best */
+ int32_t echo_value;
+ int clean_rx;
+ int nsuppr;
+
+ pvt->tx_history[pvt->curr_pos] = tx;
+ pvt->tx_history[pvt->curr_pos + pvt->taps] = tx;
+
+ /* Evaluate the echo - i.e. apply the FIR filter */
+ /* Assume the gain of the FIR does not exceed unity. Exceeding unity
+ would seem like a rather poor thing for an echo cancellor to do :)
+ This means we can compute the result with a total disregard for
+ overflows. 16bits x 16bits -> 31bits, so no overflow can occur in
+ any multiply. While accumulating we may overflow and underflow the
+ 32 bit scale often. However, if the gain does not exceed unity,
+ everything should work itself out, and the final result will be
+ OK, without any saturation logic. */
+ /* Overflow is very much possible here, and we do nothing about it because
+ of the compute costs */
+ /* 16 bit coeffs for the LMS give lousy results (maths good, actual sound
+ bad!), but 32 bit coeffs require some shifting. On balance 32 bit seems
+ best */
#ifdef USE_SHORTS
- echo_value = CONVOLVE2(ec->fir_taps_short, ec->tx_history + ec->curr_pos, ec->taps);
+ echo_value = CONVOLVE2(pvt->fir_taps_short, pvt->tx_history + pvt->curr_pos, pvt->taps);
#else
- echo_value = CONVOLVE(ec->fir_taps, ec->tx_history + ec->curr_pos, ec->taps);
+ echo_value = CONVOLVE(pvt->fir_taps, pvt->tx_history + pvt->curr_pos, pvt->taps);
#endif
- echo_value >>= 16;
-
- /* And the answer is..... */
- clean_rx = rx - echo_value;
-
- /* That was the easy part. Now we need to adapt! */
- if (ec->nonupdate_dwell > 0)
- ec->nonupdate_dwell--;
-
- /* If there is very little being transmitted, any attempt to train is
- futile. We would either be training on the far end's noise or signal,
- the channel's own noise, or our noise. Either way, this is hardly good
- training, so don't do it (avoid trouble). */
- /* If the received power is very low, either we are sending very little or
- we are already well adapted. There is little point in trying to improve
- the adaption under these circumstanceson, so don't do it (reduce the
- compute load). */
- if (ec->tx_power > MIN_TX_POWER_FOR_ADAPTION
- &&
- ec->rx_power > MIN_RX_POWER_FOR_ADAPTION)
- {
- /* This is a really crude piece of decision logic, but it does OK
- for now. */
- if (ec->tx_power > ec->rx_power << 1)
- {
- /* There is no far-end speech detected */
- if (ec->nonupdate_dwell == 0)
- {
- /* ... and we are not in the dwell time from previous speech. */
- //nsuppr = saturate((clean_rx << 16)/ec->tx_power);
- nsuppr = (clean_rx << 16) / ec->tx_power;
- nsuppr >>= 4;
- if (nsuppr > 512)
- nsuppr = 512;
- if (nsuppr < -512)
- nsuppr = -512;
-
- /* Update the FIR taps */
- ec->latest_correction = 0;
+ echo_value >>= 16;
+
+ /* And the answer is..... */
+ clean_rx = rx - echo_value;
+
+ /* That was the easy part. Now we need to adapt! */
+ if (pvt->nonupdate_dwell > 0)
+ pvt->nonupdate_dwell--;
+
+ /* If there is very little being transmitted, any attempt to train is
+ futile. We would either be training on the far end's noise or signal,
+ the channel's own noise, or our noise. Either way, this is hardly good
+ training, so don't do it (avoid trouble). */
+ /* If the received power is very low, either we are sending very little or
+ we are already well adapted. There is little point in trying to improve
+ the adaption under these circumstanceson, so don't do it (reduce the
+ compute load). */
+ if (pvt->tx_power > MIN_TX_POWER_FOR_ADAPTION && pvt->rx_power > MIN_RX_POWER_FOR_ADAPTION) {
+ /* This is a really crude piece of decision logic, but it does OK
+ for now. */
+ if (pvt->tx_power > pvt->rx_power << 1) {
+ /* There is no far-end speech detected */
+ if (pvt->nonupdate_dwell == 0) {
+ /* ... and we are not in the dwell time from previous speech. */
+ /* nsuppr = saturate((clean_rx << 16)/pvt->tx_power); */
+ nsuppr = (clean_rx << 16) / pvt->tx_power;
+ nsuppr >>= 4;
+ if (nsuppr > 512)
+ nsuppr = 512;
+ if (nsuppr < -512)
+ nsuppr = -512;
+
+ /* Update the FIR taps */
+ pvt->latest_correction = 0;
#ifdef USE_SHORTS
- UPDATE2(ec->fir_taps, ec->fir_taps_short, ec->tx_history + ec->curr_pos, nsuppr, ec->taps);
+ UPDATE2(pvt->fir_taps, pvt->fir_taps_short, pvt->tx_history + pvt->curr_pos, nsuppr, pvt->taps);
#else
- UPDATE(ec->fir_taps, ec->fir_taps_short, ec->tx_history + ec->curr_pos, nsuppr, ec->taps);
+ UPDATE(pvt->fir_taps, pvt->fir_taps_short, pvt->tx_history + pvt->curr_pos, nsuppr, pvt->taps);
#endif
- } else
- {
- ec->latest_correction = -3;
- }
+ } else {
+ pvt->latest_correction = -3;
+ }
+ } else {
+ pvt->nonupdate_dwell = NONUPDATE_DWELL_TIME;
+ pvt->latest_correction = -2;
+ }
+ } else {
+ pvt->nonupdate_dwell = 0;
+ pvt->latest_correction = -1;
}
- else
- {
- ec->nonupdate_dwell = NONUPDATE_DWELL_TIME;
- ec->latest_correction = -2;
- }
- }
- else
- {
- ec->nonupdate_dwell = 0;
- ec->latest_correction = -1;
- }
- /* Calculate short term power levels using very simple single pole IIRs */
- /* TODO: Is the nasty modulus approach the fastest, or would a real
- tx*tx power calculation actually be faster? */
- ec->tx_power += ((abs(tx) - ec->tx_power) >> 5);
- ec->rx_power += ((abs(rx) - ec->rx_power) >> 5);
- ec->clean_rx_power += ((abs(clean_rx) - ec->clean_rx_power) >> 5);
+ /* Calculate short term power levels using very simple single pole IIRs */
+ /* TODO: Is the nasty modulus approach the fastest, or would a real
+ tx*tx power calculation actually be faster? */
+ pvt->tx_power += ((abs(tx) - pvt->tx_power) >> 5);
+ pvt->rx_power += ((abs(rx) - pvt->rx_power) >> 5);
+ pvt->clean_rx_power += ((abs(clean_rx) - pvt->clean_rx_power) >> 5);
#if defined(XYZZY)
- if (ec->use_suppressor)
- {
- ec->supp_test1 += (ec->tx_history[ec->curr_pos] - ec->tx_history[(ec->curr_pos - 7) & ec->tap_mask]);
- ec->supp_test2 += (ec->tx_history[(ec->curr_pos - 24) & ec->tap_mask] - ec->tx_history[(ec->curr_pos - 31) & ec->tap_mask]);
- if (ec->supp_test1 > 42 && ec->supp_test2 > 42)
- supp_change = 25;
- else
- supp_change = 50;
- supp = supp_change + k1*ec->supp1 + k2*ec->supp2;
- ec->supp2 = ec->supp1;
- ec->supp1 = supp;
- clean_rx *= (1 - supp);
- }
+ if (pvt->use_suppressor) {
+ pvt->supp_test1 += (pvt->tx_history[pvt->curr_pos] - pvt->tx_history[(pvt->curr_pos - 7) & pvt->tap_mask]);
+ pvt->supp_test2 += (pvt->tx_history[(pvt->curr_pos - 24) & pvt->tap_mask] - pvt->tx_history[(pvt->curr_pos - 31) & pvt->tap_mask]);
+ if (pvt->supp_test1 > 42 && pvt->supp_test2 > 42)
+ supp_change = 25;
+ else
+ supp_change = 50;
+ supp = supp_change + k1*pvt->supp1 + k2*pvt->supp2;
+ pvt->supp2 = pvt->supp1;
+ pvt->supp1 = supp;
+ clean_rx *= (1 - supp);
+ }
#endif
- if (ec->use_nlp && ec->rx_power < 32)
- clean_rx = 0;
+ if (pvt->use_nlp && pvt->rx_power < 32)
+ clean_rx = 0;
- /* Roll around the rolling buffer */
- ec->curr_pos = (ec->curr_pos - 1) & ec->tap_mask;
+ /* Roll around the rolling buffer */
+ pvt->curr_pos = (pvt->curr_pos - 1) & pvt->tap_mask;
- return clean_rx;
+ return clean_rx;
}
-/*- End of function --------------------------------------------------------*/
-static void echo_can_update(struct echo_can_state *ec, short *isig, short *iref)
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size)
{
- unsigned int x;
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+ u32 x;
short result;
- for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
- result = sample_update(ec, *iref, *isig);
+ for (x = 0; x < size; x++) {
+ result = sample_update(pvt, *iref, *isig);
*isig++ = result;
++iref;
}
}
-/*- End of function --------------------------------------------------------*/
-static int echo_can_traintap(struct echo_can_state *ec, int pos, short val)
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val)
{
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
/* Reset hang counter to avoid adjustments after
initial forced training */
- ec->nonupdate_dwell = ec->taps << 1;
- if (pos >= ec->taps)
+ pvt->nonupdate_dwell = pvt->taps << 1;
+ if (pos >= pvt->taps)
return 1;
- ec->fir_taps[pos] = val << 17;
- ec->fir_taps_short[pos] = val << 1;
- if (++pos >= ec->taps)
+ pvt->fir_taps[pos] = val << 17;
+ pvt->fir_taps_short[pos] = val << 1;
+ if (++pos >= pvt->taps)
return 1;
- return 0;
+ else
+ return 0;
}
-/*- End of function --------------------------------------------------------*/
-static const struct dahdi_echocan me = {
- .name = "SEC",
- .owner = THIS_MODULE,
- .echo_can_create = echo_can_create,
- .echo_can_free = echo_can_free,
- .echo_can_array_update = echo_can_update,
- .echo_can_traintap = echo_can_traintap,
-};
+static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable)
+{
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ pvt->use_nlp = enable ? 1 : 0;
+}
static int __init mod_init(void)
{
- if (dahdi_register_echocan(&me)) {
+ if (dahdi_register_echocan_factory(&my_factory)) {
module_printk(KERN_ERR, "could not register with DAHDI core\n");
return -EPERM;
}
- module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", me.name);
+ module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name);
return 0;
}
static void __exit mod_exit(void)
{
- dahdi_unregister_echocan(&me);
+ dahdi_unregister_echocan_factory(&my_factory);
}
module_param(debug, int, S_IRUGO | S_IWUSR);
diff --git a/drivers/dahdi/dahdi_echocan_sec2.c b/drivers/dahdi/dahdi_echocan_sec2.c
index 5133cbf..f578f39 100644
--- a/drivers/dahdi/dahdi_echocan_sec2.c
+++ b/drivers/dahdi/dahdi_echocan_sec2.c
@@ -64,36 +64,6 @@ static int debug;
#define NONUPDATE_DWELL_TIME 600 /* 600 samples, or 75ms */
-struct echo_can_state
-{
- int tx_power;
- int rx_power;
- int clean_rx_power;
-
- int rx_power_threshold;
- int nonupdate_dwell;
-
- fir16_state_t fir_state;
- int16_t *fir_taps16; /* 16-bit version of FIR taps */
- int32_t *fir_taps32; /* 32-bit version of FIR taps */
-
- int curr_pos;
-
- int taps;
- int tap_mask;
- int use_nlp;
- int use_suppressor;
-
- int32_t supp_test1;
- int32_t supp_test2;
- int32_t supp1;
- int32_t supp2;
-
- int32_t latest_correction; /* Indication of the magnitude of the latest
- adaption, or a code to indicate why adaption
- was skipped, for test purposes */
-};
-
/*
* According to Jim...
*/
@@ -106,231 +76,269 @@ struct echo_can_state
/* #define MIN_TX_POWER_FOR_ADAPTION 4096
#define MIN_RX_POWER_FOR_ADAPTION 64 */
-static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p,
- struct echo_can_state **ec)
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size);
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val);
+
+static const struct dahdi_echocan_factory my_factory = {
+ .name = "SEC2",
+ .owner = THIS_MODULE,
+ .echocan_create = echo_can_create,
+};
+
+static const struct dahdi_echocan_ops my_ops = {
+ .name = "SEC2",
+ .echocan_free = echo_can_free,
+ .echocan_process = echo_can_process,
+ .echocan_traintap = echo_can_traintap,
+};
+
+struct ec_pvt {
+ struct dahdi_echocan_state dahdi;
+ int tx_power;
+ int rx_power;
+ int clean_rx_power;
+
+ int rx_power_threshold;
+ int nonupdate_dwell;
+
+ fir16_state_t fir_state;
+ int16_t *fir_taps16; /* 16-bit version of FIR taps */
+ int32_t *fir_taps32; /* 32-bit version of FIR taps */
+
+ int curr_pos;
+
+ int taps;
+ int tap_mask;
+ int use_nlp;
+ int use_suppressor;
+
+ int32_t supp_test1;
+ int32_t supp_test2;
+ int32_t supp1;
+ int32_t supp2;
+
+ int32_t latest_correction; /* Indication of the magnitude of the latest
+ adaption, or a code to indicate why adaption
+ was skipped, for test purposes */
+};
+
+#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi)
+
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
+ struct ec_pvt *pvt;
size_t size;
if (ecp->param_count > 0) {
- printk(KERN_WARNING "SEC-2 echo canceler does not support parameters; failing request\n");
+ printk(KERN_WARNING "SEC2 does not support parameters; failing request\n");
return -EINVAL;
}
- size = sizeof(**ec) + ecp->tap_length * sizeof(int32_t) + ecp->tap_length * 3 * sizeof(int16_t);
+ size = sizeof(*pvt) + ecp->tap_length * sizeof(int32_t) + ecp->tap_length * 3 * sizeof(int16_t);
- if (!(*ec = kmalloc(size, GFP_KERNEL)))
+ pvt = kzalloc(size, GFP_KERNEL);
+ if (!pvt)
return -ENOMEM;
-
- memset(*ec, 0, size);
- (*ec)->taps = ecp->tap_length;
- (*ec)->curr_pos = ecp->tap_length - 1;
- (*ec)->tap_mask = ecp->tap_length - 1;
- (*ec)->fir_taps32 = (int32_t *) (*ec + sizeof(**ec));
- (*ec)->fir_taps16 = (int16_t *) (*ec + sizeof(**ec) + ecp->tap_length * sizeof(int32_t));
+ pvt->dahdi.ops = &my_ops;
+
+ if (ecp->param_count > 0) {
+ printk(KERN_WARNING "SEC-2 echo canceler does not support parameters; failing request\n");
+ return -EINVAL;
+ }
+
+ pvt->taps = ecp->tap_length;
+ pvt->curr_pos = ecp->tap_length - 1;
+ pvt->tap_mask = ecp->tap_length - 1;
+ pvt->fir_taps32 = (int32_t *) (pvt + sizeof(*pvt));
+ pvt->fir_taps16 = (int16_t *) (pvt + sizeof(*pvt) + ecp->tap_length * sizeof(int32_t));
/* Create FIR filter */
- fir16_create(&(*ec)->fir_state, (*ec)->fir_taps16, (*ec)->taps);
- (*ec)->rx_power_threshold = 10000000;
- (*ec)->use_suppressor = FALSE;
+ fir16_create(&pvt->fir_state, pvt->fir_taps16, pvt->taps);
+ pvt->rx_power_threshold = 10000000;
+ pvt->use_suppressor = FALSE;
/* Non-linear processor - a fancy way to say "zap small signals, to avoid
accumulating noise". */
- (*ec)->use_nlp = FALSE;
+ pvt->use_nlp = FALSE;
+ *ec = &pvt->dahdi;
return 0;
}
-/*- End of function --------------------------------------------------------*/
-static void echo_can_free(struct echo_can_state *ec)
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
{
- fir16_free(&ec->fir_state);
- kfree(ec);
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ fir16_free(&pvt->fir_state);
+ kfree(pvt);
}
-/*- End of function --------------------------------------------------------*/
-static inline int16_t sample_update(struct echo_can_state *ec, int16_t tx, int16_t rx)
+static inline int16_t sample_update(struct ec_pvt *pvt, int16_t tx, int16_t rx)
{
- int offset1;
- int offset2;
- int32_t echo_value;
- int clean_rx;
- int nsuppr;
- int i;
- int correction;
-
- /* Evaluate the echo - i.e. apply the FIR filter */
- /* Assume the gain of the FIR does not exceed unity. Exceeding unity
- would seem like a rather poor thing for an echo cancellor to do :)
- This means we can compute the result with a total disregard for
- overflows. 16bits x 16bits -> 31bits, so no overflow can occur in
- any multiply. While accumulating we may overflow and underflow the
- 32 bit scale often. However, if the gain does not exceed unity,
- everything should work itself out, and the final result will be
- OK, without any saturation logic. */
- /* Overflow is very much possible here, and we do nothing about it because
- of the compute costs */
- /* 16 bit coeffs for the LMS give lousy results (maths good, actual sound
- bad!), but 32 bit coeffs require some shifting. On balance 32 bit seems
- best */
- echo_value = fir16 (&ec->fir_state, tx);
-
- /* And the answer is..... */
- clean_rx = rx - echo_value;
-
- /* That was the easy part. Now we need to adapt! */
- if (ec->nonupdate_dwell > 0)
- ec->nonupdate_dwell--;
-
- /* If there is very little being transmitted, any attempt to train is
- futile. We would either be training on the far end's noise or signal,
- the channel's own noise, or our noise. Either way, this is hardly good
- training, so don't do it (avoid trouble). */
- /* If the received power is very low, either we are sending very little or
- we are already well adapted. There is little point in trying to improve
- the adaption under these circumstanceson, so don't do it (reduce the
- compute load). */
- if (ec->tx_power > MIN_TX_POWER_FOR_ADAPTION
- &&
- ec->rx_power > MIN_RX_POWER_FOR_ADAPTION)
- {
- /* This is a really crude piece of decision logic, but it does OK
- for now. */
- if (ec->tx_power > 2*ec->rx_power)
- {
- /* There is no far-end speech detected */
- if (ec->nonupdate_dwell == 0)
- {
- /* ... and we are not in the dwell time from previous speech. */
- //nsuppr = saturate((clean_rx << 16)/ec->tx_power);
- nsuppr = clean_rx >> 3;
-
- /* Update the FIR taps */
- offset2 = ec->curr_pos + 1;
- offset1 = ec->taps - offset2;
- ec->latest_correction = 0;
- for (i = ec->taps - 1; i >= offset1; i--)
- {
- correction = ec->fir_state.history[i - offset1]*nsuppr;
- /* Leak to avoid false training on signals with multiple
- strong correlations. */
- ec->fir_taps32[i] -= (ec->fir_taps32[i] >> 12);
- ec->fir_taps32[i] += correction;
- ec->fir_state.coeffs[i] = ec->fir_taps32[i] >> 15;
- ec->latest_correction += abs(correction);
- }
- for ( ; i >= 0; i--)
- {
- correction = ec->fir_state.history[i + offset2]*nsuppr;
- /* Leak to avoid false training on signals with multiple
- strong correlations. */
- ec->fir_taps32[i] -= (ec->fir_taps32[i] >> 12);
- ec->fir_taps32[i] += correction;
- ec->fir_state.coeffs[i] = ec->fir_taps32[i] >> 15;
- ec->latest_correction += abs(correction);
- }
- }
- else
- {
- ec->latest_correction = -1;
- }
+ int offset1;
+ int offset2;
+ int32_t echo_value;
+ int clean_rx;
+ int nsuppr;
+ int i;
+ int correction;
+
+ /* Evaluate the echo - i.e. apply the FIR filter */
+ /* Assume the gain of the FIR does not exceed unity. Exceeding unity
+ would seem like a rather poor thing for an echo cancellor to do :)
+ This means we can compute the result with a total disregard for
+ overflows. 16bits x 16bits -> 31bits, so no overflow can occur in
+ any multiply. While accumulating we may overflow and underflow the
+ 32 bit scale often. However, if the gain does not exceed unity,
+ everything should work itself out, and the final result will be
+ OK, without any saturation logic. */
+ /* Overflow is very much possible here, and we do nothing about it because
+ of the compute costs */
+ /* 16 bit coeffs for the LMS give lousy results (maths good, actual sound
+ bad!), but 32 bit coeffs require some shifting. On balance 32 bit seems
+ best */
+ echo_value = fir16 (&pvt->fir_state, tx);
+
+ /* And the answer is..... */
+ clean_rx = rx - echo_value;
+
+ /* That was the easy part. Now we need to adapt! */
+ if (pvt->nonupdate_dwell > 0)
+ pvt->nonupdate_dwell--;
+
+ /* If there is very little being transmitted, any attempt to train is
+ futile. We would either be training on the far end's noise or signal,
+ the channel's own noise, or our noise. Either way, this is hardly good
+ training, so don't do it (avoid trouble). */
+ /* If the received power is very low, either we are sending very little or
+ we are already well adapted. There is little point in trying to improve
+ the adaption under these circumstanceson, so don't do it (reduce the
+ compute load). */
+ if (pvt->tx_power > MIN_TX_POWER_FOR_ADAPTION && pvt->rx_power > MIN_RX_POWER_FOR_ADAPTION) {
+ /* This is a really crude piece of decision logic, but it does OK
+ for now. */
+ if (pvt->tx_power > 2*pvt->rx_power) {
+ /* There is no far-end speech detected */
+ if (pvt->nonupdate_dwell == 0) {
+ /* ... and we are not in the dwell time from previous speech. */
+ /* nsuppr = saturate((clean_rx << 16)/pvt->tx_power); */
+ nsuppr = clean_rx >> 3;
+
+ /* Update the FIR taps */
+ offset2 = pvt->curr_pos + 1;
+ offset1 = pvt->taps - offset2;
+ pvt->latest_correction = 0;
+ for (i = pvt->taps - 1; i >= offset1; i--) {
+ correction = pvt->fir_state.history[i - offset1]*nsuppr;
+ /* Leak to avoid false training on signals with multiple
+ strong correlations. */
+ pvt->fir_taps32[i] -= (pvt->fir_taps32[i] >> 12);
+ pvt->fir_taps32[i] += correction;
+ pvt->fir_state.coeffs[i] = pvt->fir_taps32[i] >> 15;
+ pvt->latest_correction += abs(correction);
+ }
+ for ( ; i >= 0; i--) {
+ correction = pvt->fir_state.history[i + offset2]*nsuppr;
+ /* Leak to avoid false training on signals with multiple
+ strong correlations. */
+ pvt->fir_taps32[i] -= (pvt->fir_taps32[i] >> 12);
+ pvt->fir_taps32[i] += correction;
+ pvt->fir_state.coeffs[i] = pvt->fir_taps32[i] >> 15;
+ pvt->latest_correction += abs(correction);
+ }
+ } else {
+ pvt->latest_correction = -1;
+ }
+ } else {
+ pvt->nonupdate_dwell = NONUPDATE_DWELL_TIME;
+ pvt->latest_correction = -2;
+ }
+ } else {
+ pvt->nonupdate_dwell = 0;
+ pvt->latest_correction = -3;
}
- else
- {
- ec->nonupdate_dwell = NONUPDATE_DWELL_TIME;
- ec->latest_correction = -2;
- }
- }
- else
- {
- ec->nonupdate_dwell = 0;
- ec->latest_correction = -3;
- }
- /* Calculate short term power levels using very simple single pole IIRs */
- /* TODO: Is the nasty modulus approach the fastest, or would a real
- tx*tx power calculation actually be faster? */
- ec->tx_power += ((abs(tx) - ec->tx_power) >> 5);
- ec->rx_power += ((abs(rx) - ec->rx_power) >> 5);
- ec->clean_rx_power += ((abs(clean_rx) - ec->clean_rx_power) >> 5);
+ /* Calculate short term power levels using very simple single pole IIRs */
+ /* TODO: Is the nasty modulus approach the fastest, or would a real
+ tx*tx power calculation actually be faster? */
+ pvt->tx_power += ((abs(tx) - pvt->tx_power) >> 5);
+ pvt->rx_power += ((abs(rx) - pvt->rx_power) >> 5);
+ pvt->clean_rx_power += ((abs(clean_rx) - pvt->clean_rx_power) >> 5);
#if defined(XYZZY)
- if (ec->use_suppressor)
- {
- ec->supp_test1 += (ec->fir_state.history[ec->curr_pos] - ec->fir_state.history[(ec->curr_pos - 7) & ec->tap_mask]);
- ec->supp_test2 += (ec->fir_state.history[(ec->curr_pos - 24) & ec->tap_mask] - ec->fir_state.history[(ec->curr_pos - 31) & ec->tap_mask]);
- if (ec->supp_test1 > 42 && ec->supp_test2 > 42)
- supp_change = 25;
- else
- supp_change = 50;
- supp = supp_change + k1*ec->supp1 + k2*ec->supp2;
- ec->supp2 = ec->supp1;
- ec->supp1 = supp;
- clean_rx *= (1 - supp);
- }
+ if (pvt->use_suppressor) {
+ pvt->supp_test1 += (pvt->fir_state.history[pvt->curr_pos] - pvt->fir_state.history[(pvt->curr_pos - 7) & pvt->tap_mask]);
+ pvt->supp_test2 += (pvt->fir_state.history[(pvt->curr_pos - 24) & pvt->tap_mask] - pvt->fir_state.history[(pvt->curr_pos - 31) & pvt->tap_mask]);
+ if (pvt->supp_test1 > 42 && pvt->supp_test2 > 42)
+ supp_change = 25;
+ else
+ supp_change = 50;
+ supp = supp_change + k1*pvt->supp1 + k2*pvt->supp2;
+ pvt->supp2 = pvt->supp1;
+ pvt->supp1 = supp;
+ clean_rx *= (1 - supp);
+ }
#endif
- if (ec->use_nlp && ec->rx_power < 32)
- clean_rx = 0;
+ if (pvt->use_nlp && pvt->rx_power < 32)
+ clean_rx = 0;
- /* Roll around the rolling buffer */
- if (ec->curr_pos <= 0)
- ec->curr_pos = ec->taps;
- ec->curr_pos--;
+ /* Roll around the rolling buffer */
+ if (pvt->curr_pos <= 0)
+ pvt->curr_pos = pvt->taps;
+ pvt->curr_pos--;
- return clean_rx;
+ return clean_rx;
}
-/*- End of function --------------------------------------------------------*/
-static void echo_can_update(struct echo_can_state *ec, short *isig, short *iref)
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size)
{
- unsigned int x;
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+ u32 x;
short result;
- for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
- result = sample_update(ec, *iref, *isig);
+ for (x = 0; x < size; x++) {
+ result = sample_update(pvt, *iref, *isig);
*isig++ = result;
++iref;
}
}
-/*- End of function --------------------------------------------------------*/
-static int echo_can_traintap(struct echo_can_state *ec, int pos, short val)
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val)
{
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
/* Reset hang counter to avoid adjustments after
initial forced training */
- ec->nonupdate_dwell = ec->taps << 1;
- if (pos >= ec->taps)
+ pvt->nonupdate_dwell = pvt->taps << 1;
+ if (pos >= pvt->taps)
return 1;
- ec->fir_taps32[pos] = val << 17;
- ec->fir_taps16[pos] = val << 1;
- if (++pos >= ec->taps)
+ pvt->fir_taps32[pos] = val << 17;
+ pvt->fir_taps16[pos] = val << 1;
+ if (++pos >= pvt->taps)
return 1;
- return 0;
+ else
+ return 0;
}
-static const struct dahdi_echocan me = {
- .name = "SEC2",
- .owner = THIS_MODULE,
- .echo_can_create = echo_can_create,
- .echo_can_free = echo_can_free,
- .echo_can_array_update = echo_can_update,
- .echo_can_traintap = echo_can_traintap,
-};
-
static int __init mod_init(void)
{
- if (dahdi_register_echocan(&me)) {
+ if (dahdi_register_echocan_factory(&my_factory)) {
module_printk(KERN_ERR, "could not register with DAHDI core\n");
return -EPERM;
}
- module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", me.name);
+ module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name);
return 0;
}
static void __exit mod_exit(void)
{
- dahdi_unregister_echocan(&me);
+ dahdi_unregister_echocan_factory(&my_factory);
}
module_param(debug, int, S_IRUGO | S_IWUSR);
diff --git a/drivers/dahdi/ecdis.h b/drivers/dahdi/ecdis.h
index 205b2e3..e8083fe 100644
--- a/drivers/dahdi/ecdis.h
+++ b/drivers/dahdi/ecdis.h
@@ -53,10 +53,10 @@ static inline void echo_can_disable_detector_init (echo_can_disable_detector_sta
/*- End of function --------------------------------------------------------*/
static inline int echo_can_disable_detector_update (echo_can_disable_detector_state_t *det,
- int16_t amp)
+ int16_t amp)
{
- int16_t notched;
-
+ 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).
@@ -66,40 +66,38 @@ static inline int echo_can_disable_detector_update (echo_can_disable_detector_st
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;
+ if (det->channel_level >= 70) {
+ /* There is adequate energy in the channel. Is it mostly at 2100Hz? */
+ if (det->notch_level*6 < det->channel_level) {
+ det->tone_cycle_duration++;
+ /* 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))) {
+ /* It's ANS/PR (CED with polarity reversals), so wait
+ for at least three cycles before returning a hit. */
+ det->good_cycles++;
+ if (det->good_cycles > 2)
+ det->hit = TRUE;
+ }
+ det->tone_cycle_duration = 0;
+ det->tone_present = TRUE;
+ } else if (det->tone_cycle_duration >= 600 * 8) {
+ /* It's ANS (CED without polarity reversals)
+ so return a hit. */
+ det->hit = TRUE;
+ }
+ } else {
+ det->tone_present = FALSE;
}
- det->tone_present = TRUE;
- }
- else
- {
+ } else {
det->tone_present = FALSE;
- }
- det->tone_cycle_duration++;
- }
- else
- {
- det->tone_present = FALSE;
- det->tone_cycle_duration = 0;
- det->good_cycles = 0;
+ det->tone_cycle_duration = 0;
+ det->good_cycles = 0;
}
- return det->hit;
+
+ return det->hit;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/
diff --git a/drivers/dahdi/hpec/dahdi_echocan_hpec.c b/drivers/dahdi/hpec/dahdi_echocan_hpec.c
index ceccf4e..17589b8 100644
--- a/drivers/dahdi/hpec/dahdi_echocan_hpec.c
+++ b/drivers/dahdi/hpec/dahdi_echocan_hpec.c
@@ -36,7 +36,39 @@ static int debug;
#include "hpec_user.h"
#include "hpec.h"
-static int __attribute__((regparm(0))) __attribute__((format (printf, 1, 2))) logger(const char *format, ...)
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, __u32 size);
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val);
+
+static const struct dahdi_echocan_factory my_factory = {
+ .name = "HPEC",
+ .owner = THIS_MODULE,
+ .echocan_create = echo_can_create,
+};
+
+static const struct dahdi_echocan_features my_features = {
+ .NLP_automatic = 1,
+ .CED_tx_detect = 1,
+ .CED_rx_detect = 1,
+};
+
+static const struct dahdi_echocan_ops my_ops = {
+ .name = "HPEC",
+ .echocan_free = echo_can_free,
+ .echocan_process = echo_can_process,
+ .echocan_traintap = echo_can_traintap,
+};
+
+struct ec_pvt {
+ struct hpec_state *hpec;
+ struct dahdi_echocan_state dahdi;
+};
+
+#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi)
+
+static int __attribute__((regparm(0), format(printf, 1, 2))) logger(const char *format, ...)
{
int res;
va_list args;
@@ -67,37 +99,58 @@ static void memfree(void *ptr)
kfree(ptr);
}
-static void echo_can_free(struct echo_can_state *ec)
+static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
{
- hpec_channel_free(ec);
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ hpec_channel_free(pvt->hpec);
+ kfree(pvt);
}
-static void echo_can_array_update(struct echo_can_state *ec, short *isig, short *iref)
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, __u32 size)
{
- hpec_channel_update(ec, isig, iref);
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+
+ hpec_channel_update(pvt->hpec, isig, iref);
}
DECLARE_MUTEX(alloc_lock);
-static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p,
- struct echo_can_state **ec)
+static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
+ struct ec_pvt *pvt;
+
if (ecp->param_count > 0) {
printk(KERN_WARNING "HPEC does not support parameters; failing request\n");
return -EINVAL;
}
+ pvt = kzalloc(sizeof(*pvt), GFP_KERNEL);
+ if (!pvt)
+ return -ENOMEM;
+
+ pvt->dahdi.ops = &my_ops;
+ pvt->dahdi.features = my_features;
+
if (down_interruptible(&alloc_lock))
return -ENOTTY;
- *ec = hpec_channel_alloc(ecp->tap_length);
+ pvt->hpec = hpec_channel_alloc(ecp->tap_length);
up(&alloc_lock);
- return *ec ? 0 : -ENOTTY;
+ if (!pvt->hpec) {
+ kfree(pvt);
+ *ec = NULL;
+ return -ENOTTY;
+ } else {
+ *ec = &pvt->dahdi;
+ return 0;
+ }
}
-static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val)
+static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val)
{
return 1;
}
@@ -135,24 +188,15 @@ static int hpec_license_ioctl(unsigned int cmd, unsigned long data)
}
}
-static const struct dahdi_echocan me = {
- .name = "HPEC",
- .owner = THIS_MODULE,
- .echo_can_create = echo_can_create,
- .echo_can_free = echo_can_free,
- .echo_can_array_update = echo_can_array_update,
- .echo_can_traintap = echo_can_traintap,
-};
-
static int __init mod_init(void)
{
- if (dahdi_register_echocan(&me)) {
+ if (dahdi_register_echocan_factory(&my_factory)) {
module_printk(KERN_ERR, "could not register with DAHDI core\n");
return -EPERM;
}
- module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", me.name);
+ module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name);
hpec_init(logger, debug, DAHDI_CHUNKSIZE, memalloc, memfree);
@@ -163,7 +207,7 @@ static int __init mod_init(void)
static void __exit mod_exit(void)
{
- dahdi_unregister_echocan(&me);
+ dahdi_unregister_echocan_factory(&my_factory);
dahdi_set_hpec_ioctl(NULL);
diff --git a/drivers/dahdi/hpec/hpec.h b/drivers/dahdi/hpec/hpec.h
index 832775e..0c205a2 100644
--- a/drivers/dahdi/hpec/hpec.h
+++ b/drivers/dahdi/hpec/hpec.h
@@ -22,9 +22,9 @@
#if !defined(_HPEC_H)
#define _HPEC_H
-struct echo_can_state;
+struct hpec_state;
-void __attribute__((regparm(0))) hpec_init(int __attribute__((regparm(0))) __attribute__((format (printf, 1, 2))) (*logger)(const char *format, ...),
+void __attribute__((regparm(0))) hpec_init(int __attribute__((regparm(0), format(printf, 1, 2))) (*logger)(const char *format, ...),
unsigned int debug,
unsigned int chunk_size,
void * (*memalloc)(size_t len),
@@ -36,11 +36,11 @@ int __attribute__((regparm(0))) hpec_license_challenge(struct hpec_challenge *ch
int __attribute__((regparm(0))) hpec_license_check(struct hpec_license *license);
-struct echo_can_state __attribute__((regparm(0))) *hpec_channel_alloc(unsigned int len);
+struct hpec_state __attribute__((regparm(0))) *hpec_channel_alloc(unsigned int len);
-void __attribute__((regparm(0))) hpec_channel_free(struct echo_can_state *channel);
+void __attribute__((regparm(0))) hpec_channel_free(struct hpec_state *channel);
-void __attribute__((regparm(0))) hpec_channel_update(struct echo_can_state *channel, short *isig, short *iref);
+void __attribute__((regparm(0))) hpec_channel_update(struct hpec_state *channel, short *isig, const short *iref);
#endif /* !defined(_HPEC_H) */
diff --git a/drivers/dahdi/voicebus/GpakCust.c b/drivers/dahdi/voicebus/GpakCust.c
index 386c916..ccd880c 100644
--- a/drivers/dahdi/voicebus/GpakCust.c
+++ b/drivers/dahdi/voicebus/GpakCust.c
@@ -293,21 +293,9 @@ vpm_bh_out:
return;
}
#include "adt_lec.c"
-int vpmadt032_echocan_with_params(struct vpmadt032 *vpm, int channo,
- struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p)
+static void vpmadt032_check_and_schedule_update(struct vpmadt032 *vpm, int channo)
{
int update;
- unsigned int ret;
-
- ret = adt_lec_parse_params(&vpm->desiredecstate[channo], ecp, p);
- if (ret)
- return ret;
-
- /* The driver cannot control the number of taps on the VPMADT032
- * module. Instead, it uses tap_length to enable or disable the echo
- * cancellation. */
- vpm->desiredecstate[channo].tap_length = (ecp->tap_length) ? 1 : 0;
-
/* Only update the parameters if the new state of the echo canceller
* is different than the current state. */
update = memcmp(&vpm->curecstate[channo],
@@ -322,10 +310,41 @@ int vpmadt032_echocan_with_params(struct vpmadt032 *vpm, int channo,
*/
schedule_work(&vpm->work);
}
+}
+int vpmadt032_echocan_create(struct vpmadt032 *vpm, int channo,
+ struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p)
+{
+ unsigned int ret;
+ ret = adt_lec_parse_params(&vpm->desiredecstate[channo], ecp, p);
+ if (ret)
+ return ret;
+
+ if (vpm->options.debug & DEBUG_ECHOCAN)
+ printk(KERN_DEBUG "echocan: Channel is %d length %d\n", channo, ecp->tap_length);
+
+ /* The driver cannot control the number of taps on the VPMADT032
+ * module. Instead, it uses tap_length to enable or disable the echo
+ * cancellation. */
+ vpm->desiredecstate[channo].tap_length = (ecp->tap_length) ? 1 : 0;
+
+ vpmadt032_check_and_schedule_update(vpm, channo);
return 0;
}
-EXPORT_SYMBOL(vpmadt032_echocan_with_params);
+EXPORT_SYMBOL(vpmadt032_echocan_create);
+
+void vpmadt032_echocan_free(struct vpmadt032 *vpm, struct dahdi_chan *chan,
+ struct dahdi_echocan_state *ec)
+{
+ int channo = chan->chanpos - 1;
+ adt_lec_init_defaults(&vpm->desiredecstate[channo], 0);
+
+ if (vpm->options.debug & DEBUG_ECHOCAN)
+ printk(KERN_DEBUG "echocan: Channel is %d length 0\n", channo);
+
+ vpmadt032_check_and_schedule_update(vpm, channo);
+}
+EXPORT_SYMBOL(vpmadt032_echocan_free);
struct vpmadt032 *vpmadt032_alloc(struct vpmadt032_options *options)
{
diff --git a/drivers/dahdi/voicebus/GpakCust.h b/drivers/dahdi/voicebus/GpakCust.h
index befdabc..db5fbdd 100644
--- a/drivers/dahdi/voicebus/GpakCust.h
+++ b/drivers/dahdi/voicebus/GpakCust.h
@@ -118,15 +118,19 @@ struct vpmadt032 {
};
struct voicebus;
+struct dahdi_chan;
struct dahdi_echocanparams;
struct dahdi_echocanparam;
+struct dahdi_echocan_state;
char vpmadt032tone_to_zaptone(GpakToneCodes_t tone);
int vpmadt032_init(struct vpmadt032 *vpm, struct voicebus *vb);
struct vpmadt032 *vpmadt032_alloc(struct vpmadt032_options *options);
void vpmadt032_free(struct vpmadt032 *vpm);
-int vpmadt032_echocan_with_params(struct vpmadt032 *vpm, int channo,
+int vpmadt032_echocan_create(struct vpmadt032 *vpm, int channo,
struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p);
+void vpmadt032_echocan_free(struct vpmadt032 *vpm, struct dahdi_chan *chan,
+ struct dahdi_echocan_state *ec);
/* If there is a command ready to go to the VPMADT032, return it, otherwise NULL */
static inline struct vpmadt032_cmd *vpmadt032_get_ready_cmd(struct vpmadt032 *vpm)
diff --git a/drivers/dahdi/wcb4xxp/base.c b/drivers/dahdi/wcb4xxp/base.c
index 0fea600..d07ce7c 100644
--- a/drivers/dahdi/wcb4xxp/base.c
+++ b/drivers/dahdi/wcb4xxp/base.c
@@ -114,6 +114,20 @@ struct devtype {
static struct devtype wcb4xxp = { "Wildcard B410P", 0 };
+static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+
+static const struct dahdi_echocan_features my_ec_features = {
+ .NLP_automatic = 1,
+ .CED_tx_detect = 1,
+ .CED_rx_detect = 1,
+};
+
+static const struct dahdi_echocan_ops my_ec_ops = {
+ .name = "HWEC",
+ .echocan_free = echocan_free,
+};
#if 0
static const char *wcb4xxp_rcsdata = "$RCSfile: base.c,v $ $Revision$";
@@ -1884,33 +1898,50 @@ static void b4xxp_update_leds(struct b4xxp *b4)
}
}
-static int b4xxp_echocan(struct dahdi_chan *chan, int eclen)
+static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
- struct b4xxp *b4 = chan->pvt;
+ struct b4xxp_span *bspan = chan->span->pvt;
int channel;
- int unit;
-
- if (chan->chanpos != 3)
- unit = chan->chanpos - 1;
- else
- return 0;
-
- channel = (chan->span->offset * 8) + ((chan->chanpos - 1) * 4) + 1;
-
- if (eclen) { /* Enable */
- if (DBG_EC)
- printk("Enabling echo cancellation on chan %d span %d\n", chan->chanpos, chan->span->offset);
- ec_write(b4, unit, channel, 0x7e);
- } else { /* Disable */
- if (DBG_EC)
- printk("Disabling echo cancellation on chan %d span %d\n", chan->chanpos, chan->span->offset);
- ec_write(b4, unit, channel, 0x01);
+
+ if (chan->chanpos == 3) {
+ printk(KERN_WARNING "Cannot enable echo canceller on D channel of span %d; failing request\n", chan->span->offset);
+ return -EINVAL;
}
+
+ if (ecp->param_count > 0) {
+ printk(KERN_WARNING "wcb4xxp echo canceller does not support parameters; failing request\n");
+ return -EINVAL;
+ }
+
+ *ec = &bspan->ec[chan->chanpos];
+ (*ec)->ops = &my_ec_ops;
+ (*ec)->features = my_ec_features;
+
+ if (DBG_EC)
+ printk("Enabling echo cancellation on chan %d span %d\n", chan->chanpos, chan->span->offset);
+
+ channel = (chan->span->offset * 8) + ((chan->chanpos - 1) * 4) + 1;
- return 0;
+ ec_write(bspan->parent, chan->chanpos - 1, channel, 0x7e);
+ return 0;
}
+static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
+{
+ struct b4xxp_span *bspan = chan->span->pvt;
+ int channel;
+
+ memset(ec, 0, sizeof(*ec));
+
+ if (DBG_EC)
+ printk("Disabling echo cancellation on chan %d span %d\n", chan->chanpos, chan->span->offset);
+
+ channel = (chan->span->offset * 8) + ((chan->chanpos - 1) * 4) + 1;
+
+ ec_write(bspan->parent, chan->chanpos - 1, channel, 0x01);
+}
/*
* Filesystem and DAHDI interfaces
@@ -2140,7 +2171,7 @@ static void init_spans(struct b4xxp *b4)
bspan->span.ioctl = b4xxp_ioctl;
bspan->span.hdlc_hard_xmit = b4xxp_hdlc_hard_xmit;
if (vpmsupport)
- bspan->span.echocan = b4xxp_echocan;
+ bspan->span.echocan_create = echocan_create;
/* HDLC stuff */
bspan->sigchan = NULL;
diff --git a/drivers/dahdi/wcb4xxp/wcb4xxp.h b/drivers/dahdi/wcb4xxp/wcb4xxp.h
index 5c930da..4542a2d 100644
--- a/drivers/dahdi/wcb4xxp/wcb4xxp.h
+++ b/drivers/dahdi/wcb4xxp/wcb4xxp.h
@@ -411,6 +411,7 @@ struct b4xxp_span {
struct dahdi_span span; /* zaptel span info for this span */
struct dahdi_chan *chans[WCB4XXP_CHANNELS_PER_SPAN]; /* Individual channels */
+ struct dahdi_echocan_state ec[WCB4XXP_CHANNELS_PER_SPAN]; /* echocan state for each channel */
struct dahdi_chan _chans[WCB4XXP_CHANNELS_PER_SPAN]; /* Backing memory */
};
diff --git a/drivers/dahdi/wct4xxp/base.c b/drivers/dahdi/wct4xxp/base.c
index 28ba8ee..3bfd9cc 100644
--- a/drivers/dahdi/wct4xxp/base.c
+++ b/drivers/dahdi/wct4xxp/base.c
@@ -283,6 +283,7 @@ struct t4_span {
struct work_struct swork;
#endif
struct dahdi_chan *chans[32]; /* Individual channels */
+ struct dahdi_echocan_state *ec[32]; /* Echocan state for each channel */
};
struct t4 {
@@ -345,12 +346,38 @@ struct t4 {
#define T4_VPM_PRESENT (1 << 28)
-
#ifdef VPM_SUPPORT
static void t4_vpm400_init(struct t4 *wc);
static void t4_vpm450_init(struct t4 *wc);
static void t4_vpm_set_dtmf_threshold(struct t4 *wc, unsigned int threshold);
+
+static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+
+static const struct dahdi_echocan_features vpm400m_ec_features = {
+ .NLP_automatic = 1,
+ .CED_tx_detect = 1,
+ .CED_rx_detect = 1,
+};
+
+static const struct dahdi_echocan_features vpm450m_ec_features = {
+ .NLP_automatic = 1,
+ .CED_tx_detect = 1,
+ .CED_rx_detect = 1,
+};
+
+static const struct dahdi_echocan_ops vpm400m_ec_ops = {
+ .name = "VPM400M",
+ .echocan_free = echocan_free,
+};
+
+static const struct dahdi_echocan_ops vpm450m_ec_ops = {
+ .name = "VPM450M",
+ .echocan_free = echocan_free,
+};
#endif
+
static void __set_clear(struct t4 *wc, int span);
static int t4_startup(struct dahdi_span *span);
static int t4_shutdown(struct dahdi_span *span);
@@ -1089,43 +1116,84 @@ static int t4_vpm_unit(int span, int channel)
return unit;
}
-static int t4_echocan(struct dahdi_chan *chan, int eclen)
+static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
struct t4 *wc = chan->pvt;
+ struct t4_span *tspan = chan->span->pvt;
int channel;
- int unit;
-
+ const struct dahdi_echocan_ops *ops;
+ const struct dahdi_echocan_features *features;
+
if (!wc->vpm)
return -ENODEV;
if (chan->span->offset >= vpmspans)
return -ENODEV;
- if (wc->t1e1)
- channel = chan->chanpos;
- else
- channel = chan->chanpos + 4;
+ if (wc->vpm450m) {
+ ops = &vpm450m_ec_ops;
+ features = &vpm450m_ec_features;
+ } else {
+ ops = &vpm400m_ec_ops;
+ features = &vpm400m_ec_features;
+ }
+
+ if (ecp->param_count > 0) {
+ printk(KERN_WARNING "%s echo canceller does not support parameters; failing request\n", ops->name);
+ return -EINVAL;
+ }
+
+ *ec = tspan->ec[chan->chanpos - 1];
+ (*ec)->ops = ops;
+ (*ec)->features = *features;
+
+ channel = wc->t1e1 ? chan->chanpos : chan->chanpos + 4;
+
if (wc->vpm450m) {
channel = channel << 2;
channel |= chan->span->offset;
- if(debug & DEBUG_ECHOCAN)
+ if (debug & DEBUG_ECHOCAN)
printk(KERN_DEBUG "echocan: Card is %d, Channel is %d, Span is %d, offset is %d length %d\n",
- wc->num, chan->chanpos, chan->span->offset, channel, eclen);
- vpm450m_setec(wc->vpm450m, channel, eclen);
-// Mark msleep(10);
-// msleep(100); // longer test
+ wc->num, chan->chanpos, chan->span->offset, channel, ecp->tap_length);
+ vpm450m_setec(wc->vpm450m, channel, ecp->tap_length);
} else {
- unit = t4_vpm_unit(chan->span->offset, channel);
- if(debug & DEBUG_ECHOCAN)
+ int unit = t4_vpm_unit(chan->span->offset, channel);
+
+ if (debug & DEBUG_ECHOCAN)
printk(KERN_DEBUG "echocan: Card is %d, Channel is %d, Span is %d, unit is %d, unit offset is %d length %d\n",
- wc->num, chan->chanpos, chan->span->offset, unit, channel, eclen);
- if (eclen)
- t4_vpm_out(wc,unit,channel,0x3e);
- else
- t4_vpm_out(wc,unit,channel,0x01);
+ wc->num, chan->chanpos, chan->span->offset, unit, channel, ecp->tap_length);
+ t4_vpm_out(wc, unit, channel, 0x3e);
}
+
return 0;
}
+
+static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
+{
+ struct t4 *wc = chan->pvt;
+ int channel;
+
+ memset(ec, 0, sizeof(*ec));
+
+ channel = wc->t1e1 ? chan->chanpos : chan->chanpos + 4;
+
+ if (wc->vpm450m) {
+ channel = channel << 2;
+ channel |= chan->span->offset;
+ if (debug & DEBUG_ECHOCAN)
+ printk(KERN_DEBUG "echocan: Card is %d, Channel is %d, Span is %d, offset is %d length 0\n",
+ wc->num, chan->chanpos, chan->span->offset, channel);
+ vpm450m_setec(wc->vpm450m, channel, 0);
+ } else {
+ int unit = t4_vpm_unit(chan->span->offset, channel);
+
+ if (debug & DEBUG_ECHOCAN)
+ printk(KERN_DEBUG "echocan: Card is %d, Channel is %d, Span is %d, unit is %d, unit offset is %d length 0\n",
+ wc->num, chan->chanpos, chan->span->offset, unit, channel);
+ t4_vpm_out(wc, unit, channel, 0x01);
+ }
+}
#endif
static int t4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data)
@@ -1593,7 +1661,7 @@ static void init_spans(struct t4 *wc)
ts->span.hdlc_hard_xmit = t4_hdlc_hard_xmit;
if (gen2) {
#ifdef VPM_SUPPORT
- ts->span.echocan = t4_echocan;
+ ts->span.echocan_create = echocan_create;
#endif
ts->span.dacs = t4_dacs;
}
@@ -3494,6 +3562,8 @@ static void free_wc(struct t4 *wc)
if (wc->tspans[x]->chans[y]) {
kfree(wc->tspans[x]->chans[y]);
}
+ if (wc->tspans[x]->ec[y])
+ kfree(wc->tspans[x]->ec[y]);
}
kfree(wc->tspans[x]);
}
@@ -3630,6 +3700,11 @@ static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_i
return -ENOMEM;
}
memset(wc->tspans[x]->chans[f], 0, sizeof(*wc->tspans[x]->chans[f]));
+ if (!(wc->tspans[x]->ec[f] = kmalloc(sizeof(*wc->tspans[x]->ec[f]), GFP_KERNEL))) {
+ free_wc(wc);
+ return -ENOMEM;
+ }
+ memset(wc->tspans[x]->ec[f], 0, sizeof(*wc->tspans[x]->ec[f]));
}
#ifdef ENABLE_WORKQUEUES
diff --git a/drivers/dahdi/wctdm24xxp/base.c b/drivers/dahdi/wctdm24xxp/base.c
index 9a6a8ce..0985a57 100644
--- a/drivers/dahdi/wctdm24xxp/base.c
+++ b/drivers/dahdi/wctdm24xxp/base.c
@@ -228,6 +228,32 @@ static int vpmnlpthresh = 24;
/* See vpmnlptype = 4 for more info */
static int vpmnlpmaxsupp = 0;
+static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+
+static const struct dahdi_echocan_features vpm100m_ec_features = {
+ .NLP_automatic = 1,
+ .CED_tx_detect = 1,
+ .CED_rx_detect = 1,
+};
+
+static const struct dahdi_echocan_features vpm150m_ec_features = {
+ .NLP_automatic = 1,
+ .CED_tx_detect = 1,
+ .CED_rx_detect = 1,
+};
+
+static const struct dahdi_echocan_ops vpm100m_ec_ops = {
+ .name = "VPM100M",
+ .echocan_free = echocan_free,
+};
+
+static const struct dahdi_echocan_ops vpm150m_ec_ops = {
+ .name = "VPM150M",
+ .echocan_free = echocan_free,
+};
+
static int wctdm_init_proslic(struct wctdm *wc, int card, int fast , int manual, int sane);
static inline int CMD_BYTE(int card, int bit, int altcs)
@@ -1628,10 +1654,32 @@ static inline void wctdm_vpm_check(struct wctdm *wc, int x)
}
}
-static int wctdm_echocan_with_params(struct dahdi_chan *chan,
- struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p)
+static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
struct wctdm *wc = chan->pvt;
+ const struct dahdi_echocan_ops *ops;
+ const struct dahdi_echocan_features *features;
+
+ if (!wc->vpm100 && !wc->vpmadt032)
+ return -ENODEV;
+
+ if (wc->vpmadt032) {
+ ops = &vpm150m_ec_ops;
+ features = &vpm150m_ec_features;
+ } else {
+ ops = &vpm100m_ec_ops;
+ features = &vpm100m_ec_features;
+ }
+
+ if (wc->vpm100 && (ecp->param_count > 0)) {
+ printk(KERN_WARNING "%s echo canceller does not support parameters; failing request\n", ops->name);
+ return -EINVAL;
+ }
+
+ *ec = wc->ec[chan->chanpos - 1];
+ (*ec)->ops = ops;
+ (*ec)->features = *features;
if (wc->vpm100) {
int channel;
@@ -1642,23 +1690,42 @@ static int wctdm_echocan_with_params(struct dahdi_chan *chan,
if (wc->vpm100 < 2)
channel >>= 2;
- if(debug & DEBUG_ECHOCAN)
- printk(KERN_DEBUG "echocan: Unit is %d, Channel is %d length %d\n",
- unit, channel, ecp->tap_length);
- if (ecp->tap_length)
- wctdm_vpm_out(wc,unit,channel,0x3e);
- else
- wctdm_vpm_out(wc,unit,channel,0x01);
+ if (debug & DEBUG_ECHOCAN)
+ printk(KERN_DEBUG "echocan: Unit is %d, Channel is %d length %d\n", unit, channel, ecp->tap_length);
+ wctdm_vpm_out(wc, unit, channel, 0x3e);
return 0;
} else if (wc->vpmadt032) {
- return vpmadt032_echocan_with_params(wc->vpmadt032,
+ return vpmadt032_echocan_create(wc->vpmadt032,
chan->chanpos-1, ecp, p);
} else {
return -ENODEV;
}
}
+static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
+{
+ struct wctdm *wc = chan->pvt;
+
+ memset(ec, 0, sizeof(*ec));
+ if (wc->vpm100) {
+ int channel;
+ int unit;
+
+ channel = (chan->chanpos - 1);
+ unit = (chan->chanpos - 1) & 0x3;
+ if (wc->vpm100 < 2)
+ channel >>= 2;
+
+ if (debug & DEBUG_ECHOCAN)
+ printk(KERN_DEBUG "echocan: Unit is %d, Channel is %d length 0\n",
+ unit, channel);
+ wctdm_vpm_out(wc, unit, channel, 0x01);
+ } else if (wc->vpmadt032) {
+ vpmadt032_echocan_free(wc->vpmadt032, chan, ec);
+ }
+}
+
static inline void wctdm_isr_misc(struct wctdm *wc)
{
int x;
@@ -3187,7 +3254,7 @@ static int wctdm_initialize(struct wctdm *wc)
wc->span.watchdog = wctdm_watchdog;
wc->span.dacs= wctdm_dacs;
#ifdef VPM_SUPPORT
- wc->span.echocan_with_params = wctdm_echocan_with_params;
+ wc->span.echocan_create = echocan_create;
#endif
init_waitqueue_head(&wc->span.maintq);
@@ -3587,6 +3654,8 @@ static void free_wc(struct wctdm *wc)
if (wc->chans[x]) {
kfree(wc->chans[x]);
}
+ if (wc->ec[x])
+ kfree(wc->ec[x]);
}
kfree(wc);
}
@@ -3651,6 +3720,11 @@ retry:
return -ENOMEM;
}
memset(wc->chans[i], 0, sizeof(*wc->chans[i]));
+ if (!(wc->ec[i] = kmalloc(sizeof(*wc->ec[i]), GFP_KERNEL))) {
+ free_wc(wc);
+ return -ENOMEM;
+ }
+ memset(wc->ec[i], 0, sizeof(*wc->ec[i]));
}
diff --git a/drivers/dahdi/wctdm24xxp/wctdm24xxp.h b/drivers/dahdi/wctdm24xxp/wctdm24xxp.h
index 4bb90b7..3d08bef 100644
--- a/drivers/dahdi/wctdm24xxp/wctdm24xxp.h
+++ b/drivers/dahdi/wctdm24xxp/wctdm24xxp.h
@@ -235,6 +235,7 @@ struct wctdm {
#endif
struct voicebus *vb;
struct dahdi_chan *chans[NUM_CARDS];
+ struct dahdi_echocan_state *ec[NUM_CARDS];
int initialized;
};
diff --git a/drivers/dahdi/wcte12xp/base.c b/drivers/dahdi/wcte12xp/base.c
index 30f89e9..03eb34c 100644
--- a/drivers/dahdi/wcte12xp/base.c
+++ b/drivers/dahdi/wcte12xp/base.c
@@ -63,6 +63,20 @@ static int vpmtsisupport = 0;
int vpmnlptype = 3;
int vpmnlpthresh = 24;
int vpmnlpmaxsupp = 0;
+static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+
+static const struct dahdi_echocan_features vpm150m_ec_features = {
+ .NLP_automatic = 1,
+ .CED_tx_detect = 1,
+ .CED_rx_detect = 1,
+};
+
+static const struct dahdi_echocan_ops vpm150m_ec_ops = {
+ .name = "VPM150M",
+ .echocan_free = echocan_free,
+};
struct t1 *ifaces[WC_MAX_IFACES];
spinlock_t ifacelock = SPIN_LOCK_UNLOCKED;
@@ -636,8 +650,10 @@ static void free_wc(struct t1 *wc)
struct command *cmd;
LIST_HEAD(list);
- for (x = 0; x < (wc->spantype == TYPE_E1 ? 31 : 24); x++)
+ for (x = 0; x < (wc->spantype == TYPE_E1 ? 31 : 24); x++) {
kfree(wc->chans[x]);
+ kfree(wc->ec[x]);
+ }
spin_lock_irqsave(&wc->cmd_list_lock, flags);
list_splice_init(&wc->active_cmds, &list);
@@ -1150,17 +1166,26 @@ static int t1xxp_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long
return 0;
}
-static int t1xxp_echocan_with_params(struct dahdi_chan *chan,
- struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p)
+static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
struct t1 *wc = chan->pvt;
if (!wc->vpmadt032) {
return -ENODEV;
}
- return vpmadt032_echocan_with_params(wc->vpmadt032, chan->chanpos - 1,
+ return vpmadt032_echocan_create(wc->vpmadt032, chan->chanpos - 1,
ecp, p);
}
+static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
+{
+ struct t1 *wc = chan->pvt;
+ if (!wc->vpmadt032)
+ return;
+
+ vpmadt032_echocan_free(wc->vpmadt032, chan, ec);
+}
+
static int t1_software_init(struct t1 *wc)
{
int x;
@@ -1207,7 +1232,7 @@ static int t1_software_init(struct t1 *wc)
wc->span.close = t1xxp_close;
wc->span.ioctl = t1xxp_ioctl;
#ifdef VPM_SUPPORT
- wc->span.echocan_with_params = t1xxp_echocan_with_params;
+ wc->span.echocan_create = echocan_create;
#endif
if (wc->spantype == TYPE_E1) {
@@ -1752,6 +1777,12 @@ retry:
return -ENOMEM;
}
memset(wc->chans[x], 0, sizeof(*wc->chans[x]));
+ if (!(wc->ec[x] = kmalloc(sizeof(*wc->ec[x]), GFP_KERNEL))) {
+ free_wc(wc);
+ ifaces[index] = NULL;
+ return -ENOMEM;
+ }
+ memset(wc->ec[x], 0, sizeof(*wc->ec[x]));
}
mod_timer(&wc->timer, jiffies + HZ/5);
diff --git a/drivers/dahdi/wcte12xp/wcte12xp.h b/drivers/dahdi/wcte12xp/wcte12xp.h
index a93df54..2a0a0f4 100644
--- a/drivers/dahdi/wcte12xp/wcte12xp.h
+++ b/drivers/dahdi/wcte12xp/wcte12xp.h
@@ -127,6 +127,7 @@ struct t1 {
unsigned char ec_chunk2[32][DAHDI_CHUNKSIZE];
struct dahdi_span span; /* Span */
struct dahdi_chan *chans[32]; /* Channels */
+ struct dahdi_echocan_state *ec[32]; /* Echocan state for channels */
unsigned long ctlreg;
struct voicebus* vb;
atomic_t txints;
diff --git a/include/dahdi/dahdi_config.h b/include/dahdi/dahdi_config.h
index 68a4517..865439d 100644
--- a/include/dahdi/dahdi_config.h
+++ b/include/dahdi/dahdi_config.h
@@ -68,13 +68,6 @@
*/
/* #define CONFIG_DAHDI_MMX */
-/*
- * Define to turn off the echo canceler disable tone detector,
- * which will cause DAHDI to ignore the 2100 Hz echo cancel disable
- * tone.
- */
-/* #define NO_ECHOCAN_DISABLE */
-
/* We now use the linux kernel config to detect which options to use */
/* You can still override them below */
#if defined(CONFIG_HDLC) || defined(CONFIG_HDLC_MODULE)
diff --git a/include/dahdi/kernel.h b/include/dahdi/kernel.h
index e2ece4c..0731056 100644
--- a/include/dahdi/kernel.h
+++ b/include/dahdi/kernel.h
@@ -148,6 +148,231 @@ struct confq {
int outbuf;
};
+struct dahdi_chan;
+struct dahdi_echocan_state;
+
+/*! Features a DAHDI echo canceler (software or hardware) can provide to the DAHDI core. */
+struct dahdi_echocan_features {
+
+ /*! Able to detect CED tone (2100 Hz with phase reversals) in the transmit direction.
+ * If the echocan can detect this tone, it may report it it as an event (see
+ * the events.CED_tx_detected field of dahdi_echocan_state), and if it will automatically
+ * disable itself or its non-linear processor, then the NLP_automatic feature flag should also
+ * be set so that the DAHDI core doesn't bother trying to do so.
+ */
+ u32 CED_tx_detect:1;
+
+ /*! Able to detect CED tone (2100 Hz with phase reversals) in the receive direction.
+ * If the echocan can detect this tone, it may report it it as an event (see
+ * the events.CED_rx_detected field of dahdi_echocan_state), and if it will automatically
+ * disable itself or its non-linear processor, then the NLP_automatic flag feature should also
+ * be set so that the DAHDI core doesn't bother trying to do so.
+ */
+ u32 CED_rx_detect:1;
+
+ /*! Able to detect CNG tone (1100 Hz) in the transmit direction. */
+ u32 CNG_tx_detect:1;
+
+ /*! Able to detect CNG tone (1100 Hz) in the receive direction. */
+ u32 CNG_rx_detect:1;
+
+ /*! If the echocan's NLP can be enabled and disabled without requiring destruction
+ * and recreation of the state structure, this feature flag should be set and the
+ * echocan_NLP_toggle field of the dahdi_echocan_ops structure should be filled with a
+ * pointer to the function to perform that operation.
+ */
+ u32 NLP_toggle:1;
+
+ /*! If the echocan will automatically disable itself (or even just its NLP) based on
+ * detection of a CED tone in either direction, this feature flag should be set (along
+ * with the tone detection feature flags).
+ */
+ u32 NLP_automatic:1;
+};
+
+/*! Operations (methods) that can be performed on a DAHDI echo canceler instance (state
+ * structure) after it has been created, by either a software or hardware echo canceller.
+ * The echo canceler must populate the owner field of the dahdi_echocan_state structure
+ * with a pointer to the relevant operations structure for that instance.
+ */
+struct dahdi_echocan_ops {
+
+ /*! The name of the echocan that created this structure. */
+ const char *name;
+
+ /*! \brief Free an echocan state structure.
+ * \param[in,out] ec Pointer to the state structure to free.
+ *
+ * \return Nothing.
+ */
+ void (*echocan_free)(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
+
+ /*! \brief Process an array of audio samples through the echocan.
+ * \param[in,out] ec Pointer to the state structure.
+ * \param[in,out] isig The receive direction data (will be modified).
+ * \param[in] iref The transmit direction data.
+ * \param[in] size The number of elements in the isig and iref arrays.
+ *
+ * Note: This function can also return events in the events field of the
+ * dahdi_echocan_state structure. If it can do so, then the echocan does
+ * not need to provide the echocan_events function.
+ *
+ * \return Nothing.
+ */
+ void (*echocan_process)(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size);
+
+ /*! \brief Retrieve events from the echocan.
+ * \param[in,out] ec Pointer to the state structure.
+ *
+ *
+ * If any events have occurred, the events field of the dahdi_echocan_state
+ * structure should be updated to include them.
+ *
+ * \return Nothing.
+ */
+ void (*echocan_events)(struct dahdi_echocan_state *ec);
+
+ /*! \brief Feed a sample (and its position) for echocan training.
+ * \param[in,out] ec Pointer to the state structure.
+ * \param[in] pos The tap position to be 'trained'.
+ * \param[in] val The receive direction sample for the specified tap position.
+ *
+ * \retval Zero if training should continue.
+ * \retval Non-zero if training is complete.
+ */
+ int (*echocan_traintap)(struct dahdi_echocan_state *ec, int pos, short val);
+
+ /*! \brief Enable or disable non-linear processing (NLP) in the echocan.
+ * \param[in,out] ec Pointer to the state structure.
+ * \param[in] enable Zero to disable, non-zero to enable.
+ *
+ * \return Nothing.
+ */
+ void (*echocan_NLP_toggle)(struct dahdi_echocan_state *ec, unsigned int enable);
+};
+
+/*! A factory for creating instances of software echo cancelers to be used on DAHDI channels. */
+struct dahdi_echocan_factory {
+
+ /*! The name of the factory. */
+ const char *name;
+
+ /*! Pointer to the module that owns this factory; the module's reference count will be
+ * incremented/decremented by the DAHDI core as needed.
+ */
+ struct module *owner;
+
+ /*! \brief Function to create an instance of the echocan.
+ * \param[in] ecp Structure defining parameters to be used for the instance creation.
+ * \param[in] p Pointer to the beginning of an (optional) array of user-defined parameters.
+ * \param[out] ec Pointer to the state structure that is created, if any.
+ *
+ * \retval Zero on success.
+ * \retval Non-zero on failure (return value will be returned to userspace so it should be a
+ * standard error number).
+ */
+ int (*echocan_create)(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
+};
+
+/*! \brief Register an echo canceler factory with the DAHDI core.
+ * \param[in] ec Pointer to the dahdi_echocan_factory structure to be registered.
+ *
+ * \retval Zero on success.
+ * \retval Non-zero on failure (return value will be a standard error number).
+ */
+int dahdi_register_echocan_factory(const struct dahdi_echocan_factory *ec);
+
+/*! \brief Unregister a previously-registered echo canceler factory from the DAHDI core.
+ * \param[in] ec Pointer to the dahdi_echocan_factory structure to be unregistered.
+ *
+ * \return Nothing.
+ */
+void dahdi_unregister_echocan_factory(const struct dahdi_echocan_factory *ec);
+
+enum dahdi_echocan_mode {
+ __ECHO_MODE_MUTE = 1 << 8,
+ ECHO_MODE_IDLE = 0,
+ ECHO_MODE_PRETRAINING = 1 | __ECHO_MODE_MUTE,
+ ECHO_MODE_STARTTRAINING = 2 | __ECHO_MODE_MUTE,
+ ECHO_MODE_AWAITINGECHO = 3 | __ECHO_MODE_MUTE,
+ ECHO_MODE_TRAINING = 4 | __ECHO_MODE_MUTE,
+ ECHO_MODE_ACTIVE = 5,
+ ECHO_MODE_FAX = 6,
+};
+
+/*! An instance of a DAHDI echo canceler (software or hardware). */
+struct dahdi_echocan_state {
+
+ /*! Pointer to a dahdi_echocan_ops structure of operations that can be
+ * performed on this instance.
+ */
+ const struct dahdi_echocan_ops *ops;
+
+ /*! State data used by the DAHDI core's CED detector for the transmit
+ * direction, if needed.
+ */
+ echo_can_disable_detector_state_t txecdis;
+
+ /*! State data used by the DAHDI core's CED detector for the receive
+ * direction, if needed.
+ */
+ echo_can_disable_detector_state_t rxecdis;
+
+ /*! Features offered by the echo canceler that provided this instance. */
+ struct dahdi_echocan_features features;
+
+ struct {
+ /*! The mode the echocan is currently in. */
+ enum dahdi_echocan_mode mode;
+
+ /*! The last tap position that was fed to the echocan's training function. */
+ u32 last_train_tap;
+
+ /*! How many samples to wait before beginning the training operation. */
+ u32 pretrain_timer;
+ } status;
+
+ /*! This structure contains event flags, allowing the echocan to report
+ * events that occurred as it processed the transmit and receive streams
+ * of samples. Each call to the echocan_process operation for this
+ * instance may report events, so the structure should be cleared before
+ * calling that operation.
+ */
+ union dahdi_echocan_events {
+ u32 all;
+ struct {
+ /*! CED tone was detected in the transmit direction. If the
+ * echocan automatically disables its NLP when this occurs,
+ * it must also signal the NLP_auto_disabled event during the *same*
+ * call to echocan_process that reports the CED detection.
+ */
+ u32 CED_tx_detected:1;
+
+ /*! CED tone was detected in the receive direction. If the
+ * echocan automatically disables its NLP when this occurs,
+ * it must also signal the NLP_auto_disabled event during the *same*
+ * call to echocan_process that reports the CED detection.
+ */
+ u32 CED_rx_detected:1;
+
+ /*! CNG tone was detected in the transmit direction. */
+ u32 CNG_tx_detected:1;
+
+ /*! CNG tone was detected in the receive direction. */
+ u32 CNG_rx_detected:1;
+
+ /*! The echocan disabled its NLP automatically.
+ */
+ u32 NLP_auto_disabled:1;
+
+ /*! The echocan enabled its NLP automatically.
+ */
+ u32 NLP_auto_enabled:1;
+ };
+ } events;
+};
+
struct dahdi_chan {
#ifdef CONFIG_DAHDI_NET
/*! \note Must be first */
@@ -295,22 +520,14 @@ struct dahdi_chan {
short conflast2[DAHDI_MAX_CHUNKSIZE]; /*!< Previous last conference sample -- pseudo part of channel */
- /*! Is echo cancellation enabled or disabled */
- int echocancel;
/*! The echo canceler module that should be used to create an
instance when this channel needs one */
- const struct dahdi_echocan *ec_factory;
+ const struct dahdi_echocan_factory *ec_factory;
/*! The echo canceler module that owns the instance currently
on this channel, if one is present */
- const struct dahdi_echocan *ec_current;
- /*! The private state data of the echo canceler instance in use */
- struct echo_can_state *ec_state;
- echo_can_disable_detector_state_t txecdis;
- echo_can_disable_detector_state_t rxecdis;
-
- int echostate; /*!< State of echo canceller */
- int echolastupdate; /*!< Last echo can update pos */
- int echotimer; /*!< Timer for echo update */
+ const struct dahdi_echocan_factory *ec_current;
+ /*! The state data of the echo canceler instance in use */
+ struct dahdi_echocan_state *ec_state;
/* RBS timings */
int prewinktime; /*!< pre-wink time (ms) */
@@ -374,21 +591,6 @@ struct dahdi_hdlc {
};
#endif
-/* Echo cancellation */
-struct echo_can_state;
-
-struct dahdi_echocan {
- const char *name;
- struct module *owner;
- int (*echo_can_create)(struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p, struct echo_can_state **ec);
- void (*echo_can_free)(struct echo_can_state *ec);
- void (*echo_can_array_update)(struct echo_can_state *ec, short *isig, short *iref);
- int (*echo_can_traintap)(struct echo_can_state *ec, int pos, short val);
-};
-
-int dahdi_register_echocan(const struct dahdi_echocan *ec);
-void dahdi_unregister_echocan(const struct dahdi_echocan *ec);
-
/*! Define the maximum block size */
#define DAHDI_MAX_BLOCKSIZE 8192
@@ -600,10 +802,9 @@ struct dahdi_span {
/*! Opt: IOCTL */
int (*ioctl)(struct dahdi_chan *chan, unsigned int cmd, unsigned long data);
- /*! Opt: Native echo cancellation (simple) */
- int (*echocan)(struct dahdi_chan *chan, int ecval);
-
- int (*echocan_with_params)(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p);
+ /*! Opt: Provide echo cancellation on a channel */
+ int (*echocan_create)(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
/* Okay, now we get to the signalling. You have several options: */
diff --git a/include/dahdi/user.h b/include/dahdi/user.h
index 7434fcf..20d7d7e 100644
--- a/include/dahdi/user.h
+++ b/include/dahdi/user.h
@@ -410,6 +410,24 @@ enum {
/* No neon MWI pulses were detected over some period of time */
#define DAHDI_EVENT_NEONMWI_INACTIVE 22
+/* A CED tone was detected on the channel in the transmit direction */
+#define DAHDI_EVENT_TX_CED_DETECTED 23
+
+/* A CED tone was detected on the channel in the receive direction */
+#define DAHDI_EVENT_RX_CED_DETECTED 24
+
+/* A CNG tone was detected on the channel in the transmit direction */
+#define DAHDI_EVENT_TX_CNG_DETECTED 25
+
+/* A CNG tone was detected on the channel in the receive direction */
+#define DAHDI_EVENT_RX_CNG_DETECTED 26
+
+/* The echo canceler's NLP (only) was disabled */
+#define DAHDI_EVENT_EC_NLP_DISABLED 27
+
+/* The echo canceler's NLP (only) was enabled */
+#define DAHDI_EVENT_EC_NLP_ENABLED 28
+
#define DAHDI_EVENT_PULSEDIGIT (1 << 16) /* This is OR'd with the digit received */
#define DAHDI_EVENT_DTMFDOWN (1 << 17) /* Ditto for DTMF key down event */
#define DAHDI_EVENT_DTMFUP (1 << 18) /* Ditto for DTMF key up event */
@@ -1002,9 +1020,12 @@ struct dahdi_vmwi_info {
#define DAHDI_STARTUP _IOW(DAHDI_CODE, 99, int)
#define DAHDI_SHUTDOWN _IOW(DAHDI_CODE, 100, int)
-
#define DAHDI_HDLC_RATE _IOW(DAHDI_CODE, 101, int)
+/* Put a channel's echo canceller into 'FAX mode' if possible */
+
+#define DAHDI_ECHOCANCEL_FAX_MODE _IOW(DAHDI_CODE, 102, int)
+
struct torisa_debug {
unsigned int txerrors;
unsigned int irqcount;