summaryrefslogtreecommitdiff
path: root/drivers/dahdi/dahdi-base.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dahdi/dahdi-base.c')
-rw-r--r--drivers/dahdi/dahdi-base.c250
1 files changed, 165 insertions, 85 deletions
diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c
index cb1b1c1..6ab8636 100644
--- a/drivers/dahdi/dahdi-base.c
+++ b/drivers/dahdi/dahdi-base.c
@@ -78,6 +78,10 @@
#include "hpec/hpec_user.h"
+#if defined(EMPULSE) && defined(EMFLASH)
+#error "You cannot define both EMPULSE and EMFLASH"
+#endif
+
/* Get helper arithmetic */
#include "arith.h"
#if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP)
@@ -184,13 +188,6 @@ static struct class_simple *dahdi_class = NULL;
#define class_destroy class_simple_destroy
#endif
-/*
- * See issue http://bugs.digium.com/view.php?id=13504 for more information.
- * on why reference counting on the echo canceller modules is disabled
- * currently.
- */
-#undef USE_ECHOCAN_REFCOUNT
-
static int deftaps = 64;
static int debug;
@@ -397,7 +394,6 @@ static LIST_HEAD(ecfactory_list);
struct ecfactory {
const struct dahdi_echocan_factory *ec;
- struct module *owner;
struct list_head list;
};
@@ -405,6 +401,8 @@ int dahdi_register_echocan_factory(const struct dahdi_echocan_factory *ec)
{
struct ecfactory *cur;
+ WARN_ON(!ec->owner);
+
write_lock(&ecfactory_list_lock);
/* make sure it isn't already registered */
@@ -1122,18 +1120,13 @@ retry:
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)) {
+ if (try_module_get(cur->ec->owner)) {
read_unlock(&ecfactory_list_lock);
return cur->ec;
} else {
read_unlock(&ecfactory_list_lock);
return NULL;
}
-#else
- read_unlock(&ecfactory_list_lock);
- return cur->ec;
-#endif
}
}
@@ -1159,10 +1152,8 @@ retry:
static void release_echocan(const struct dahdi_echocan_factory *ec)
{
-#ifdef USE_ECHOCAN_REFCOUNT
if (ec)
module_put(ec->owner);
-#endif
}
/**
@@ -1878,6 +1869,8 @@ static void dahdi_chan_unreg(struct dahdi_chan *chan)
might_sleep();
+ release_echocan(chan->ec_factory);
+
#ifdef CONFIG_DAHDI_NET
if (chan->flags & DAHDI_FLAG_NETDEV) {
unregister_hdlc_device(chan->hdlcnetdev->netdev);
@@ -2304,7 +2297,7 @@ static void dahdi_rbs_sethook(struct dahdi_chan *chan, int txsig, int txstate,
if (!chan->span)
return;
- if (!chan->span->flags & DAHDI_FLAG_RBS) {
+ if (!(chan->span->flags & DAHDI_FLAG_RBS)) {
module_printk(KERN_NOTICE, "dahdi_rbs: Tried to set RBS hook state on non-RBS channel %s\n", chan->name);
return;
}
@@ -3462,16 +3455,136 @@ static int dahdi_timer_ioctl(struct inode *node, struct file *file, unsigned int
return 0;
}
+static int dahdi_ioctl_getgains(struct inode *node, struct file *file,
+ unsigned int cmd, unsigned long data, int unit)
+{
+ int res = 0;
+ struct dahdi_gains *gain;
+ int i, j;
+
+ gain = kzalloc(sizeof(*gain), GFP_KERNEL);
+ if (!gain)
+ return -ENOMEM;
+
+ if (copy_from_user(gain, (struct dahdi_gains *)data, sizeof(*gain))) {
+ res = -EFAULT;
+ goto cleanup;
+ }
+ i = gain->chan; /* get channel no */
+ /* if zero, use current channel no */
+ if (!i)
+ i = unit;
+
+ /* make sure channel number makes sense */
+ if ((i < 0) || (i > DAHDI_MAX_CHANNELS) || !chans[i]) {
+ res = -EINVAL;
+ goto cleanup;
+ }
+
+ if (!(chans[i]->flags & DAHDI_FLAG_AUDIO)) {
+ res = -EINVAL;
+ goto cleanup;
+ }
+ gain->chan = i; /* put the span # in here */
+ for (j = 0; j < 256; ++j) {
+ gain->txgain[j] = chans[i]->txgain[j];
+ gain->rxgain[j] = chans[i]->rxgain[j];
+ }
+ if (copy_to_user((struct dahdi_gains *)data, gain, sizeof(*gain))) {
+ res = -EFAULT;
+ goto cleanup;
+ }
+cleanup:
+
+ kfree(gain);
+ return res;
+}
+
+static int dahdi_ioctl_setgains(struct inode *node, struct file *file,
+ unsigned int cmd, unsigned long data, int unit)
+{
+ int res = 0;
+ struct dahdi_gains *gain;
+ unsigned char *txgain, *rxgain;
+ int i, j;
+ unsigned long flags;
+ const int GAIN_TABLE_SIZE = sizeof(defgain);
+
+ gain = kzalloc(sizeof(*gain), GFP_KERNEL);
+ if (!gain)
+ return -ENOMEM;
+
+ if (copy_from_user(gain, (struct dahdi_gains *)data, sizeof(*gain))) {
+ res = -EFAULT;
+ goto cleanup;
+ }
+ i = gain->chan; /* get channel no */
+ /* if zero, use current channel no */
+ if (!i)
+ i = unit;
+ /* make sure channel number makes sense */
+ if ((i < 0) || (i > DAHDI_MAX_CHANNELS) || !chans[i]) {
+ res = -EINVAL;
+ goto cleanup;
+ }
+ if (!(chans[i]->flags & DAHDI_FLAG_AUDIO)) {
+ res = -EINVAL;
+ goto cleanup;
+ }
+
+ rxgain = kzalloc(GAIN_TABLE_SIZE*2, GFP_KERNEL);
+ if (!rxgain) {
+ res = -ENOMEM;
+ goto cleanup;
+ }
+
+ gain->chan = i; /* put the span # in here */
+ txgain = rxgain + GAIN_TABLE_SIZE;
+
+ for (j = 0; j < GAIN_TABLE_SIZE; ++j) {
+ rxgain[j] = gain->rxgain[j];
+ txgain[j] = gain->txgain[j];
+ }
+
+ if (!memcmp(rxgain, defgain, GAIN_TABLE_SIZE) &&
+ !memcmp(txgain, defgain, GAIN_TABLE_SIZE)) {
+ kfree(rxgain);
+ spin_lock_irqsave(&chans[i]->lock, flags);
+ if (chans[i]->gainalloc)
+ kfree(chans[i]->rxgain);
+ chans[i]->gainalloc = 0;
+ chans[i]->rxgain = defgain;
+ chans[i]->txgain = defgain;
+ spin_unlock_irqrestore(&chans[i]->lock, flags);
+ } else {
+ /* This is a custom gain setting */
+ spin_lock_irqsave(&chans[i]->lock, flags);
+ if (chans[i]->gainalloc)
+ kfree(chans[i]->rxgain);
+ chans[i]->gainalloc = 1;
+ chans[i]->rxgain = rxgain;
+ chans[i]->txgain = txgain;
+ spin_unlock_irqrestore(&chans[i]->lock, flags);
+ }
+
+ if (copy_to_user((struct dahdi_gains *)data, gain, sizeof(*gain))) {
+ res = -EFAULT;
+ goto cleanup;
+ }
+cleanup:
+
+ kfree(gain);
+ return res;
+}
+
static int dahdi_common_ioctl(struct inode *node, struct file *file, unsigned int cmd, unsigned long data, int unit)
{
union {
- struct dahdi_gains gain;
struct dahdi_spaninfo spaninfo;
struct dahdi_params param;
} stack;
struct dahdi_chan *chan;
unsigned long flags;
- unsigned char *txgain, *rxgain;
int i,j;
int return_master = 0;
size_t size_to_copy;
@@ -3597,68 +3710,9 @@ static int dahdi_common_ioctl(struct inode *node, struct file *file, unsigned in
break;
case DAHDI_GETGAINS_V1: /* Intentional drop through. */
case DAHDI_GETGAINS: /* get gain stuff */
- if (copy_from_user(&stack.gain,(struct dahdi_gains *) data,sizeof(stack.gain)))
- return -EFAULT;
- i = stack.gain.chan; /* get channel no */
- /* if zero, use current channel no */
- if (!i) i = unit;
- /* make sure channel number makes sense */
- if ((i < 0) || (i > DAHDI_MAX_CHANNELS) || !chans[i]) return(-EINVAL);
-
- if (!(chans[i]->flags & DAHDI_FLAG_AUDIO)) return (-EINVAL);
- stack.gain.chan = i; /* put the span # in here */
- for (j=0;j<256;j++) {
- stack.gain.txgain[j] = chans[i]->txgain[j];
- stack.gain.rxgain[j] = chans[i]->rxgain[j];
- }
- if (copy_to_user((struct dahdi_gains *) data,&stack.gain,sizeof(stack.gain)))
- return -EFAULT;
- break;
+ return dahdi_ioctl_getgains(node, file, cmd, data, unit);
case DAHDI_SETGAINS: /* set gain stuff */
- if (copy_from_user(&stack.gain,(struct dahdi_gains *) data,sizeof(stack.gain)))
- return -EFAULT;
- i = stack.gain.chan; /* get channel no */
- /* if zero, use current channel no */
- if (!i) i = unit;
- /* make sure channel number makes sense */
- if ((i < 0) || (i > DAHDI_MAX_CHANNELS) || !chans[i]) return(-EINVAL);
- if (!(chans[i]->flags & DAHDI_FLAG_AUDIO)) return (-EINVAL);
-
- if (!(rxgain = kmalloc(512, GFP_KERNEL)))
- return -ENOMEM;
-
- stack.gain.chan = i; /* put the span # in here */
- txgain = rxgain + 256;
-
- for (j=0;j<256;j++) {
- rxgain[j] = stack.gain.rxgain[j];
- txgain[j] = stack.gain.txgain[j];
- }
-
- if (!memcmp(rxgain, defgain, 256) &&
- !memcmp(txgain, defgain, 256)) {
- if (rxgain)
- kfree(rxgain);
- spin_lock_irqsave(&chans[i]->lock, flags);
- if (chans[i]->gainalloc)
- kfree(chans[i]->rxgain);
- chans[i]->gainalloc = 0;
- chans[i]->rxgain = defgain;
- chans[i]->txgain = defgain;
- spin_unlock_irqrestore(&chans[i]->lock, flags);
- } else {
- /* This is a custom gain setting */
- spin_lock_irqsave(&chans[i]->lock, flags);
- if (chans[i]->gainalloc)
- kfree(chans[i]->rxgain);
- chans[i]->gainalloc = 1;
- chans[i]->rxgain = rxgain;
- chans[i]->txgain = txgain;
- spin_unlock_irqrestore(&chans[i]->lock, flags);
- }
- if (copy_to_user((struct dahdi_gains *) data,&stack.gain,sizeof(stack.gain)))
- return -EFAULT;
- break;
+ return dahdi_ioctl_setgains(node, file, cmd, data, unit);
case DAHDI_SPANSTAT:
size_to_copy = sizeof(struct dahdi_spaninfo);
if (copy_from_user(&stack.spaninfo, (struct dahdi_spaninfo *) data, size_to_copy))
@@ -3877,6 +3931,13 @@ static int dahdi_ctl_ioctl(struct inode *inode, struct file *file, unsigned int
spin_lock_irqsave(&spans[j]->chans[x]->lock, flags);
dahdi_hangup(spans[j]->chans[x]);
spin_unlock_irqrestore(&spans[j]->chans[x]->lock, flags);
+ /*
+ * Set the rxhooksig back to
+ * DAHDI_RXSIG_INITIAL so that new events are
+ * queued on the channel with the actual
+ * recieved hook state.
+ *
+ */
spans[j]->chans[x]->rxhooksig = DAHDI_RXSIG_INITIAL;
}
}
@@ -3972,9 +4033,6 @@ static int dahdi_ctl_ioctl(struct inode *inode, struct file *file, unsigned int
if ((sigcap & ch.sigtype) != ch.sigtype)
res = -EINVAL;
- if (!res && chans[ch.chan]->span->chanconfig)
- res = chans[ch.chan]->span->chanconfig(chans[ch.chan], ch.sigtype);
-
if (chans[ch.chan]->master != chans[ch.chan]) {
struct dahdi_chan *oldmaster = chans[ch.chan]->master;
@@ -4045,6 +4103,12 @@ static int dahdi_ctl_ioctl(struct inode *inode, struct file *file, unsigned int
else
chans[ch.chan]->flags &= ~DAHDI_FLAG_MTP2;
}
+
+ if (!res && chans[ch.chan]->span->chanconfig) {
+ res = chans[ch.chan]->span->chanconfig(chans[ch.chan],
+ ch.sigtype);
+ }
+
#ifdef CONFIG_DAHDI_NET
if (!res &&
(newmaster == chans[ch.chan]) &&
@@ -4963,14 +5027,12 @@ static int ioctl_echocancel(struct dahdi_chan *chan, struct dahdi_echocanparams
ret = chan->span->echocan_create(chan, ecp, params, &ec);
if ((ret == -ENODEV) && chan->ec_factory) {
-#ifdef USE_ECHOCAN_REFCOUNT
/* try to get another reference to the module providing
this channel's echo canceler */
if (!try_module_get(chan->ec_factory->owner)) {
module_printk(KERN_ERR, "Cannot get a reference to the '%s' echo canceler\n", chan->ec_factory->name);
goto exit_with_free;
}
-#endif
/* got the reference, copy the pointer and use it for making
an echo canceler instance if possible */
@@ -7939,6 +8001,7 @@ static void coretimer_func(unsigned long param)
const unsigned long MAX_INTERVAL = 100000L;
const unsigned long FOURMS_INTERVAL = HZ/250;
const unsigned long ONESEC_INTERVAL = HZ;
+ const unsigned long MS_LIMIT = 3000;
now = current_kernel_time();
@@ -7953,6 +8016,23 @@ static void coretimer_func(unsigned long param)
mod_timer(&core_timer.timer, jiffies + FOURMS_INTERVAL);
ms_since_start = core_diff_ms(&core_timer.start_interval, &now);
+
+ /*
+ * If the system time has changed, it is possible for us to be
+ * far behind. If we are more than MS_LIMIT milliseconds
+ * behind, just reset our time base and continue so that we do
+ * not hang the system here.
+ *
+ */
+ if (unlikely((ms_since_start - atomic_read(&core_timer.count)) > MS_LIMIT)) {
+ if (printk_ratelimit())
+ module_printk(KERN_INFO, "Detected time shift.\n");
+ atomic_set(&core_timer.count, 0);
+ atomic_set(&core_timer.last_count, 0);
+ core_timer.start_interval = now;
+ return;
+ }
+
while (ms_since_start > atomic_read(&core_timer.count))
process_masterspan();