diff options
-rw-r--r-- | drivers/dahdi/adt_lec.c | 2 | ||||
-rw-r--r-- | drivers/dahdi/dahdi-base.c | 534 | ||||
-rw-r--r-- | drivers/dahdi/dahdi_echocan_jpah.c | 84 | ||||
-rw-r--r-- | drivers/dahdi/dahdi_echocan_kb1.c | 380 | ||||
-rw-r--r-- | drivers/dahdi/dahdi_echocan_mg2.c | 475 | ||||
-rw-r--r-- | drivers/dahdi/dahdi_echocan_oslec.c | 86 | ||||
-rw-r--r-- | drivers/dahdi/dahdi_echocan_sec.c | 387 | ||||
-rw-r--r-- | drivers/dahdi/dahdi_echocan_sec2.c | 402 | ||||
-rw-r--r-- | drivers/dahdi/ecdis.h | 66 | ||||
-rw-r--r-- | drivers/dahdi/hpec/dahdi_echocan_hpec.c | 88 | ||||
-rw-r--r-- | drivers/dahdi/hpec/hpec.h | 10 | ||||
-rw-r--r-- | drivers/dahdi/voicebus/GpakCust.c | 47 | ||||
-rw-r--r-- | drivers/dahdi/voicebus/GpakCust.h | 6 | ||||
-rw-r--r-- | drivers/dahdi/wcb4xxp/base.c | 73 | ||||
-rw-r--r-- | drivers/dahdi/wcb4xxp/wcb4xxp.h | 1 | ||||
-rw-r--r-- | drivers/dahdi/wct4xxp/base.c | 117 | ||||
-rw-r--r-- | drivers/dahdi/wctdm24xxp/base.c | 96 | ||||
-rw-r--r-- | drivers/dahdi/wctdm24xxp/wctdm24xxp.h | 1 | ||||
-rw-r--r-- | drivers/dahdi/wcte12xp/base.c | 41 | ||||
-rw-r--r-- | drivers/dahdi/wcte12xp/wcte12xp.h | 1 | ||||
-rw-r--r-- | include/dahdi/dahdi_config.h | 7 | ||||
-rw-r--r-- | include/dahdi/kernel.h | 263 | ||||
-rw-r--r-- | include/dahdi/user.h | 23 |
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; |