summaryrefslogtreecommitdiff
path: root/drivers/dahdi/dahdi-base.c
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2009-04-29 18:24:04 +0000
committerShaun Ruffell <sruffell@digium.com>2009-04-29 18:24:04 +0000
commit5f94a3b91de2c3835d6852d59cab9f6876177156 (patch)
tree4c25956b6ecdcd5b902ac80b40237bc3e938be44 /drivers/dahdi/dahdi-base.c
parent4a192a3e8f16ed6143377b5726e1fb53b446f5e9 (diff)
echocan: Improve interface for echo cancelers.
Echo cancelers are now able to report if they are able to automatically disable their NLP portions in the presence of tones in the audio stream. Also, the interface is changed to allow user space to just disable the NLP portion of the echo canceler. These changes improve fax and modem handling in DAHDI. This commit merges in the changes on http://svn.digium.com/svn/dahdi/linux/team/kpfleming/echocan_work Patch by: kpfleming Also contains improvements to CED tone detection. (closes issue #13286) Reported by: viniciusfontes git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@6529 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'drivers/dahdi/dahdi-base.c')
-rw-r--r--drivers/dahdi/dahdi-base.c534
1 files changed, 292 insertions, 242 deletions
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)
{