diff options
Diffstat (limited to 'drivers/dahdi/dahdi-base.c')
-rw-r--r-- | drivers/dahdi/dahdi-base.c | 534 |
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) { |