summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;