diff options
26 files changed, 1694 insertions, 795 deletions
@@ -128,8 +128,7 @@ install-include: for hdr in $(INST_HEADERS); do \ install -D -m 644 include/dahdi/$$hdr $(DESTDIR)/usr/include/dahdi/$$hdr; \ done - -@rm -f $(DESTDIR)/usr/include/zaptel/*.h - -@rmdir $(DESTDIR)/usr/include/zaptel + @rm -rf $(DESTDIR)/usr/include/zaptel uninstall-include: for hdr in $(INST_HEADERS); do \ @@ -528,22 +528,17 @@ cost. Known Issues ------------ -Removing echocan modules -~~~~~~~~~~~~~~~~~~~~~~~~ -Before unloading an echo-canceller module you must remove an reference to it. -Using 'etc/init.d/dahdi stop' is the preferred method. - -However if, for some reason, you want to unload just a single dahdi_echocan_* -module that you use, you must first edit /etc/dahdi/system.conf and change any -echocan lines referring to it to refer to a different echo canceller module. - -https://issues.asterisk.org/view.php?id=15327[0015327: oops after removing an echocan module that is in use] +KB1 does not function when echocancel > 128 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +KB1 was not designed to function at greater than 128 taps, and if configured +this way, will result in the destruction of audio. Ideally DAHDI would return +an error when a KB1 echocanceller is configured with greater than 128 taps. Reporting Bugs -------------- Please report bug and patches to the Asterisk bug tracker at -http://bugs.digium.com in the "DAHDI" category. +http://issues.asterisk.org in the "DAHDI" category. Links ----- 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(); diff --git a/drivers/dahdi/dahdi_dummy.c b/drivers/dahdi/dahdi_dummy.c index 590cc9b..66f6317 100644 --- a/drivers/dahdi/dahdi_dummy.c +++ b/drivers/dahdi/dahdi_dummy.c @@ -168,7 +168,7 @@ static void dahdi_dummy_timer(unsigned long param) now = current_kernel_time(); ms_since_start = timespec_diff_ms(&ztd->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 diff --git a/drivers/dahdi/dahdi_dynamic.c b/drivers/dahdi/dahdi_dynamic.c index 2e674fa..2a5c267 100644 --- a/drivers/dahdi/dahdi_dynamic.c +++ b/drivers/dahdi/dahdi_dynamic.c @@ -239,7 +239,9 @@ static void __ztdynamic_run(void) dahdi_receive(&z->span); dahdi_transmit(&z->span); /* Handle all transmissions now */ + spin_unlock_irqrestore(&dlock, flags); ztd_sendmessage(z); + spin_lock_irqsave(&dlock, flags); } z = z->next; } diff --git a/drivers/dahdi/firmware/Makefile b/drivers/dahdi/firmware/Makefile index 023279a..552435a 100644 --- a/drivers/dahdi/firmware/Makefile +++ b/drivers/dahdi/firmware/Makefile @@ -16,7 +16,7 @@ OCT6114_064_VERSION:=1.05.01 OCT6114_128_VERSION:=1.05.01 TC400M_VERSION:=MR6.12 -VPMADT032_VERSION:=1.17.0 +VPMADT032_VERSION:=1.20.0 FIRMWARE_URL:=http://downloads.digium.com/pub/telephony/firmware/releases diff --git a/drivers/dahdi/hpec/dahdi_echocan_hpec.c b/drivers/dahdi/hpec/dahdi_echocan_hpec.c index 17589b8..6a98d20 100644 --- a/drivers/dahdi/hpec/dahdi_echocan_hpec.c +++ b/drivers/dahdi/hpec/dahdi_echocan_hpec.c @@ -114,7 +114,7 @@ static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const hpec_channel_update(pvt->hpec, isig, iref); } -DECLARE_MUTEX(alloc_lock); +DECLARE_MUTEX(license_lock); static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec) @@ -133,12 +133,12 @@ static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams * pvt->dahdi.ops = &my_ops; pvt->dahdi.features = my_features; - if (down_interruptible(&alloc_lock)) + if (down_interruptible(&license_lock)) return -ENOTTY; pvt->hpec = hpec_channel_alloc(ecp->tap_length); - up(&alloc_lock); + up(&license_lock); if (!pvt->hpec) { kfree(pvt); @@ -155,8 +155,6 @@ static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val) return 1; } -DECLARE_MUTEX(license_lock); - static int hpec_license_ioctl(unsigned int cmd, unsigned long data) { struct hpec_challenge challenge; diff --git a/drivers/dahdi/proslic.h b/drivers/dahdi/proslic.h index ebd98bf..556643e 100644 --- a/drivers/dahdi/proslic.h +++ b/drivers/dahdi/proslic.h @@ -35,6 +35,23 @@ typedef struct { // Defines #define LPT 0X378 +/* Proslic Linefeed options for register 64 - Linefeed Control */ +#define SLIC_LF_OPEN 0x0 +#define SLIC_LF_ACTIVE_FWD 0x1 +#define SLIC_LF_OHTRAN_FWD 0x2 /* Forward On Hook Transfer */ +#define SLIC_LF_TIP_OPEN 0x3 +#define SLIC_LF_RINGING 0x4 +#define SLIC_LF_ACTIVE_REV 0x5 +#define SLIC_LF_OHTRAN_REV 0x6 /* Reverse On Hook Transfer */ +#define SLIC_LF_RING_OPEN 0x7 + +#define SLIC_LF_SETMASK 0x7 +#define SLIC_LF_OPPENDING 0x10 + +/* Mask used to reverse the linefeed mode between forward and + * reverse polarity. */ +#define SLIC_LF_REVMASK 0x4 + #define IDA_LO 28 #define IDA_HI 29 diff --git a/drivers/dahdi/voicebus/GpakCust.c b/drivers/dahdi/voicebus/GpakCust.c index b3228df..00fe603 100644 --- a/drivers/dahdi/voicebus/GpakCust.c +++ b/drivers/dahdi/voicebus/GpakCust.c @@ -370,10 +370,9 @@ int vpmadt032_echocan_create(struct vpmadt032 *vpm, int channo, } EXPORT_SYMBOL(vpmadt032_echocan_create); -void vpmadt032_echocan_free(struct vpmadt032 *vpm, struct dahdi_chan *chan, +void vpmadt032_echocan_free(struct vpmadt032 *vpm, int channo, struct dahdi_echocan_state *ec) { - int channo = chan->chanpos - 1; adt_lec_init_defaults(&vpm->desiredecstate[channo], 0); vpm->desiredecstate[channo].nlp_type = vpm->options.vpmnlptype; vpm->desiredecstate[channo].nlp_threshold = vpm->options.vpmnlpthresh; @@ -525,8 +524,9 @@ vpmadt032_init(struct vpmadt032 *vpm, struct voicebus *vb) return res; } vpm->curpage = -1; - set_bit(VPM150M_SWRESET, &vpm->control); + dev_info(&voicebus_get_pci_dev(vb)->dev, "Booting VPMADT032\n"); + set_bit(VPM150M_SWRESET, &vpm->control); while (test_bit(VPM150M_SWRESET, &vpm->control)) msleep(1); @@ -565,7 +565,7 @@ void vpmadt032_get_default_parameters(struct GpakEcanParms *p) p->EcanDblTalkThresh = 6; p->EcanMaxDoubleTalkThres = 40; p->EcanNlpThreshold = DEFAULT_NLPTHRESH; - p->EcanNlpConv = 0; + p->EcanNlpConv = 18; p->EcanNlpUnConv = 12; p->EcanNlpMaxSuppress = DEFAULT_NLPMAXSUPP; p->EcanCngThreshold = 43; diff --git a/drivers/dahdi/voicebus/GpakCust.h b/drivers/dahdi/voicebus/GpakCust.h index 9b4f6a8..0546bfb 100644 --- a/drivers/dahdi/voicebus/GpakCust.h +++ b/drivers/dahdi/voicebus/GpakCust.h @@ -145,7 +145,7 @@ struct vpmadt032 *vpmadt032_alloc(struct vpmadt032_options *options, void vpmadt032_free(struct vpmadt032 *vpm); 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, +void vpmadt032_echocan_free(struct vpmadt032 *vpm, int channo, struct dahdi_echocan_state *ec); struct GpakEcanParms; diff --git a/drivers/dahdi/voicebus/voicebus.c b/drivers/dahdi/voicebus/voicebus.c index d794365..70a1128 100644 --- a/drivers/dahdi/voicebus/voicebus.c +++ b/drivers/dahdi/voicebus/voicebus.c @@ -41,8 +41,6 @@ #include "vpmadtreg.h" #include "GpakCust.h" -#define assert(__x__) BUG_ON(!(__x__)) - #define INTERRUPT 0 /* Run the deferred processing in the ISR. */ #define TASKLET 1 /* Run in a tasklet. */ #define TIMER 2 /* Run in a system timer. */ @@ -56,6 +54,11 @@ #define VOICEBUS_ALLOC_FLAGS GFP_ATOMIC #endif +/* Define CONFIG_VOICEBUS_SYSFS to create some attributes under the pci device. + * This is disabled by default because it hasn't been tested on the full range + * of supported kernels. */ +#undef CONFIG_VOICEBUS_SYSFS + #if VOICEBUS_DEFERRED == TIMER #if HZ < 1000 /* \todo Put an error message here. */ @@ -63,7 +66,7 @@ #endif /*! The number of descriptors in both the tx and rx descriptor ring. */ -#define DRING_SIZE (1 << 5) /* Must be a power of 2 */ +#define DRING_SIZE (1 << 7) /* Must be a power of 2 */ #define DRING_MASK (DRING_SIZE-1) /* Interrupt status' reported in SR_CSR5 */ @@ -110,10 +113,10 @@ /* In memory structure shared by the host and the adapter. */ struct voicebus_descriptor { - u32 des0; - u32 des1; - u32 buffer1; - u32 container; /* Unused */ + volatile __le32 des0; + volatile __le32 des1; + volatile __le32 buffer1; + volatile __le32 container; /* Unused */ } __attribute__((packed)); struct voicebus_descriptor_list { @@ -127,20 +130,22 @@ struct voicebus_descriptor_list { void *pending[DRING_SIZE]; /* PCI Bus address of the descriptor list. */ dma_addr_t desc_dma; - /*! either DMA_FROM_DEVICE or DMA_TO_DEVICE */ - unsigned int direction; /*! The number of buffers currently submitted to the hardware. */ atomic_t count; /*! The number of bytes to pad each descriptor for cache alignment. */ unsigned int padding; }; - -/*! * \brief Represents a VoiceBus interface on a Digium telephony card. +/** + * struct voicebus - + * + * @tx_idle_vbb: + * @tx_idle_vbb_dma_addr: + * @max_latency: Do not allow the driver to automatically insert more than this + * much latency to the tdm stream by default. + * @count: The number of non-idle buffers that we should be expecting. */ struct voicebus { - /*! Name of this card. */ - const char *board_name; /*! The system pci device for this VoiceBus interface. */ struct pci_dev *pdev; /*! Protects access to card registers and this structure. You should @@ -154,6 +159,8 @@ struct voicebus { /*! Pool to allocate memory for the tx and rx descriptor rings. */ struct voicebus_descriptor_list rxd; struct voicebus_descriptor_list txd; + void *idle_vbb; + dma_addr_t idle_vbb_dma_addr; /*! Level of debugging information. 0=None, 5=Insane. */ atomic_t debuglevel; /*! Cache of buffer objects. */ @@ -186,12 +193,18 @@ struct voicebus { struct completion stopped_completion; /*! Flags */ unsigned long flags; - /* \todo see about removing this... */ - u32 sdi; /*! Number of tx buffers to queue up before enabling interrupts. */ unsigned int min_tx_buffer_count; + unsigned int max_latency; + void *vbb_stash[DRING_SIZE]; + unsigned int count; }; +static inline void handle_transmit(struct voicebus *vb, void *vbb) +{ + vb->handle_transmit(vbb, vb->context); +} + /* * Use the following macros to lock the VoiceBus interface, and it won't * matter if the deferred processing is running inside the interrupt handler, @@ -218,15 +231,13 @@ struct voicebus { #define VBUNLOCK_FROM_DEFERRED(_vb_) spin_lock(&((_vb_)->lock)) #endif -#define VB_PRINTK(_vb, _lvl, _fmt, _args...) \ - printk(KERN_##_lvl "%s: " _fmt, (_vb)->board_name, ## _args) - /* Bit definitions for struct voicebus.flags */ #define TX_UNDERRUN 1 #define RX_UNDERRUN 2 #define IN_DEFERRED_PROCESSING 3 #define STOP 4 #define STOPPED 5 +#define LATENCY_LOCKED 6 #if VOICEBUS_DEFERRED == WORKQUEUE #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) @@ -257,32 +268,9 @@ vb_set_workqueue_priority(struct voicebus *vb) #endif #endif -#ifdef DBG -static inline int -assert_in_vb_deferred(struct voicebus *vb) -{ - assert(test_bit(IN_DEFERRED_PROCESSING, &vb->flags)); -} - -static inline void -start_vb_deferred(struct voicebus *vb) -{ - set_bit(IN_DEFERRED_PROCESSING, &vb->flags); -} - -static inline void -stop_vb_deferred(struct voicebus *vb) -{ - clear_bit(IN_DEFERRED_PROCESSING, &vb->flags); -} -#else -#define assert_in_vb_deferred(_x_) do {; } while (0) -#define start_vb_deferred(_x_) do {; } while (0) -#define stop_vb_deferred(_x_) do {; } while (0) -#endif - static inline struct voicebus_descriptor * -vb_descriptor(struct voicebus_descriptor_list *dl, int index) +vb_descriptor(const struct voicebus_descriptor_list *dl, + const unsigned int index) { struct voicebus_descriptor *d; d = (struct voicebus_descriptor *)((u8*)dl->desc + @@ -298,7 +286,7 @@ vb_initialize_descriptors(struct voicebus *vb, struct voicebus_descriptor_list * struct voicebus_descriptor *d; const u32 END_OF_RING = 0x02000000; - assert(dl); + BUG_ON(!dl); /* * Add some padding to each descriptor to ensure that they are @@ -324,16 +312,55 @@ vb_initialize_descriptors(struct voicebus *vb, struct voicebus_descriptor_list * d->des1 = des1; } d->des1 |= cpu_to_le32(END_OF_RING); - dl->direction = direction; atomic_set(&dl->count, 0); return 0; } +#define OWNED(_d_) (((_d_)->des0)&OWN_BIT) +#define SET_OWNED(_d_) do { wmb(); (_d_)->des0 |= OWN_BIT; wmb(); } while (0) + static int vb_initialize_tx_descriptors(struct voicebus *vb) { - return vb_initialize_descriptors( - vb, &vb->txd, 0xe4800000 | vb->framesize, DMA_TO_DEVICE); + int i; + int des1 = 0xe4800000 | vb->framesize; + struct voicebus_descriptor *d; + struct voicebus_descriptor_list *dl = &vb->txd; + const u32 END_OF_RING = 0x02000000; + + WARN_ON(!dl); + WARN_ON((NULL == vb->idle_vbb) || (0 == vb->idle_vbb_dma_addr)); + + /* + * Add some padding to each descriptor to ensure that they are + * aligned on host system cache-line boundaries, but only for the + * cache-line sizes that we support. + * + */ + if ((0x08 == vb->cache_line_size) || (0x10 == vb->cache_line_size) || + (0x20 == vb->cache_line_size)) { + dl->padding = (vb->cache_line_size*sizeof(u32)) - sizeof(*d); + } else { + dl->padding = 0; + } + + dl->desc = pci_alloc_consistent(vb->pdev, + (sizeof(*d) + dl->padding) * + DRING_SIZE, &dl->desc_dma); + if (!dl->desc) + return -ENOMEM; + + memset(dl->desc, 0, (sizeof(*d) + dl->padding) * DRING_SIZE); + for (i = 0; i < DRING_SIZE; ++i) { + d = vb_descriptor(dl, i); + d->des1 = des1; + d->buffer1 = vb->idle_vbb_dma_addr; + dl->pending[i] = vb->idle_vbb; + SET_OWNED(d); + } + d->des1 |= cpu_to_le32(END_OF_RING); + atomic_set(&dl->count, 0); + return 0; } static int @@ -358,10 +385,10 @@ voicebus_set_minlatency(struct voicebus *vb, unsigned int ms) */ #define MESSAGE "%d ms is an invalid value for minumum latency. Setting to %d ms.\n" if (DRING_SIZE < ms) { - VB_PRINTK(vb, WARNING, MESSAGE, ms, DRING_SIZE); + dev_warn(&vb->pdev->dev, MESSAGE, ms, DRING_SIZE); return -EINVAL; } else if (VOICEBUS_DEFAULT_LATENCY > ms) { - VB_PRINTK(vb, WARNING, MESSAGE, ms, VOICEBUS_DEFAULT_LATENCY); + dev_warn(&vb->pdev->dev, MESSAGE, ms, VOICEBUS_DEFAULT_LATENCY); return -EINVAL; } VBLOCK(vb); @@ -419,6 +446,36 @@ voicebus_current_latency(struct voicebus *vb) } EXPORT_SYMBOL(voicebus_current_latency); +/** + * voicebus_lock_latency() - Do not increase the latency during underruns. + * + */ +void voicebus_lock_latency(struct voicebus *vb) +{ + set_bit(LATENCY_LOCKED, &vb->flags); +} +EXPORT_SYMBOL(voicebus_lock_latency); + +/** + * voicebus_unlock_latency() - Bump up the latency during underruns. + * + */ +void voicebus_unlock_latency(struct voicebus *vb) +{ + clear_bit(LATENCY_LOCKED, &vb->flags); +} +EXPORT_SYMBOL(voicebus_unlock_latency); + +/** + * voicebus_is_latency_locked() - Return 1 if latency is currently locked. + * + */ +int voicebus_is_latency_locked(const struct voicebus *vb) +{ + return test_bit(LATENCY_LOCKED, &vb->flags); +} +EXPORT_SYMBOL(voicebus_is_latency_locked); + /*! * \brief Read one of the hardware control registers without acquiring locks. */ @@ -460,18 +517,48 @@ vb_is_stopped(struct voicebus *vb) } static void -vb_cleanup_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl) +vb_cleanup_tx_descriptors(struct voicebus *vb) +{ + unsigned int i; + struct voicebus_descriptor_list *dl = &vb->txd; + struct voicebus_descriptor *d; + + BUG_ON(!vb_is_stopped(vb)); + + for (i = 0; i < DRING_SIZE; ++i) { + d = vb_descriptor(dl, i); + if (d->buffer1 && (d->buffer1 != vb->idle_vbb_dma_addr)) { + WARN_ON(!dl->pending[i]); + dma_unmap_single(&vb->pdev->dev, d->buffer1, + vb->framesize, DMA_TO_DEVICE); + voicebus_free(vb, dl->pending[i]); + } + d->buffer1 = vb->idle_vbb_dma_addr; + dl->pending[i] = vb->idle_vbb; + SET_OWNED(d); + } + /* Send out two idle buffers to start because sometimes the first buffer + * doesn't make it back to us. */ + dl->head = dl->tail = 2; + atomic_set(&dl->count, 0); +} + +static void +vb_cleanup_rx_descriptors(struct voicebus *vb) { unsigned int i; + struct voicebus_descriptor_list *dl = &vb->rxd; struct voicebus_descriptor *d; - assert(vb_is_stopped(vb)); + BUG_ON(!vb_is_stopped(vb)); for (i = 0; i < DRING_SIZE; ++i) { d = vb_descriptor(dl, i); if (d->buffer1) { + dma_unmap_single(&vb->pdev->dev, d->buffer1, + vb->framesize, DMA_FROM_DEVICE); d->buffer1 = 0; - assert(dl->pending[i]); + BUG_ON(!dl->pending[i]); voicebus_free(vb, dl->pending[i]); dl->pending[i] = NULL; } @@ -482,6 +569,15 @@ vb_cleanup_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl) atomic_set(&dl->count, 0); } +static void vb_cleanup_descriptors(struct voicebus *vb, + struct voicebus_descriptor_list *dl) +{ + if (dl == &vb->txd) + vb_cleanup_tx_descriptors(vb); + else + vb_cleanup_rx_descriptors(vb); +} + static void vb_free_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl) { @@ -519,30 +615,30 @@ vb_setctl(struct voicebus *vb, u32 addr, u32 val) } static int -__vb_sdi_clk(struct voicebus *vb) +__vb_sdi_clk(struct voicebus *vb, u32 *sdi) { unsigned int ret; - vb->sdi &= ~CSR9_MDC; - __vb_setctl(vb, 0x0048, vb->sdi); + *sdi &= ~CSR9_MDC; + __vb_setctl(vb, 0x0048, *sdi); ret = __vb_getctl(vb, 0x0048); - vb->sdi |= CSR9_MDC; - __vb_setctl(vb, 0x0048, vb->sdi); + *sdi |= CSR9_MDC; + __vb_setctl(vb, 0x0048, *sdi); return (ret & CSR9_MDI) ? 1 : 0; } static void -__vb_sdi_sendbits(struct voicebus *vb, u32 bits, int count) +__vb_sdi_sendbits(struct voicebus *vb, u32 bits, int count, u32 *sdi) { - vb->sdi &= ~CSR9_MMC; - __vb_setctl(vb, 0x0048, vb->sdi); + *sdi &= ~CSR9_MMC; + __vb_setctl(vb, 0x0048, *sdi); while (count--) { if (bits & (1 << count)) - vb->sdi |= CSR9_MDO; + *sdi |= CSR9_MDO; else - vb->sdi &= ~CSR9_MDO; + *sdi &= ~CSR9_MDO; - __vb_sdi_clk(vb); + __vb_sdi_clk(vb, sdi); } } @@ -551,13 +647,14 @@ vb_setsdi(struct voicebus *vb, int addr, u16 val) { LOCKS_VOICEBUS; u32 bits; + u32 sdi = 0; /* Send preamble */ bits = 0xffffffff; VBLOCK(vb); - __vb_sdi_sendbits(vb, bits, 32); + __vb_sdi_sendbits(vb, bits, 32, &sdi); bits = (0x5 << 12) | (1 << 7) | (addr << 2) | 0x2; - __vb_sdi_sendbits(vb, bits, 16); - __vb_sdi_sendbits(vb, val, 16); + __vb_sdi_sendbits(vb, bits, 16, &sdi); + __vb_sdi_sendbits(vb, val, 16, &sdi); VBUNLOCK(vb); } @@ -566,7 +663,7 @@ vb_enable_io_access(struct voicebus *vb) { LOCKS_VOICEBUS; u32 reg; - assert(vb->pdev); + BUG_ON(!vb->pdev); VBLOCK(vb); pci_read_config_dword(vb->pdev, 0x0004, ®); reg |= 0x00000007; @@ -620,11 +717,12 @@ vb_reset_interface(struct voicebus *vb) pci_access = DEFAULT_PCI_ACCESS | (0x3 << 14); break; default: - if (atomic_read(&vb->debuglevel)) - VB_PRINTK(vb, WARNING, "Host system set a cache size "\ - "of %d which is not supported. " \ - "Disabling memory write line and memory read line.\n", - vb->cache_line_size); + if (atomic_read(&vb->debuglevel)) { + dev_warn(&vb->pdev->dev, "Host system set a cache " + "size of %d which is not supported. " + "Disabling memory write line and memory " + "read line.\n", vb->cache_line_size); + } pci_access = 0xfe584202; break; } @@ -640,8 +738,8 @@ vb_reset_interface(struct voicebus *vb) } while ((reg & 0x00000001) && time_before(jiffies, timeout)); if (reg & 0x00000001) { - VB_PRINTK(vb, ERR, "Hardware did not come out of reset "\ - "within 100ms!"); + dev_warn(&vb->pdev->dev, "Hardware did not come out of reset " + "within 100ms!"); return -EIO; } @@ -655,8 +753,8 @@ vb_initialize_interface(struct voicebus *vb) { u32 reg; - vb_cleanup_descriptors(vb, &vb->txd); - vb_cleanup_descriptors(vb, &vb->rxd); + vb_cleanup_tx_descriptors(vb); + vb_cleanup_rx_descriptors(vb); /* Pass bad packets, runt packets, disable SQE function, * store-and-forward */ @@ -689,12 +787,9 @@ vb_initialize_interface(struct voicebus *vb) return ((reg&0x7) == 0x4) ? 0 : -EIO; } -#define OWNED(_d_) (((_d_)->des0)&OWN_BIT) -#define SET_OWNED(_d_) do { wmb(); (_d_)->des0 |= OWN_BIT; wmb(); } while (0) - #ifdef DBG static void -dump_descriptor(struct voicebus *vb, volatile struct voicebus_descriptor *d) +dump_descriptor(struct voicebus *vb, struct voicebus_descriptor *d) { VB_PRINTK(vb, DEBUG, "Displaying descriptor at address %08x\n", (unsigned int)d); VB_PRINTK(vb, DEBUG, " des0: %08x\n", d->des0); @@ -720,61 +815,32 @@ show_buffer(struct voicebus *vb, void *vbb) } #endif -static inline int -vb_submit(struct voicebus *vb, struct voicebus_descriptor_list *dl, void *vbb) +/** + * voicebus_transmit - Queue a buffer on the hardware descriptor ring. + * + */ +int voicebus_transmit(struct voicebus *vb, void *vbb) { - volatile struct voicebus_descriptor *d; - unsigned int tail = dl->tail; - assert_in_vb_deferred(vb); + struct voicebus_descriptor *d; + struct voicebus_descriptor_list *dl = &vb->txd; - d = vb_descriptor(dl, tail); + d = vb_descriptor(dl, dl->tail); - if (unlikely(d->buffer1)) { - /* Do not overwrite a buffer that is still in progress. */ - WARN_ON(1); + if (unlikely(d->buffer1 != vb->idle_vbb_dma_addr)) { + if (printk_ratelimit()) + dev_warn(&vb->pdev->dev, "Dropping tx buffer buffer\n"); voicebus_free(vb, vbb); - return -EBUSY; + return -EFAULT; } - dl->pending[tail] = vbb; - dl->tail = (++tail) & DRING_MASK; - d->buffer1 = dma_map_single( - &vb->pdev->dev, vbb, vb->framesize, dl->direction); + dl->pending[dl->tail] = vbb; + dl->tail = (++(dl->tail)) & DRING_MASK; + d->buffer1 = dma_map_single(&vb->pdev->dev, vbb, + vb->framesize, DMA_TO_DEVICE); SET_OWNED(d); /* That's it until the hardware is done with it. */ atomic_inc(&dl->count); return 0; } - -static inline void* -vb_retrieve(struct voicebus *vb, struct voicebus_descriptor_list *dl) -{ - volatile struct voicebus_descriptor *d; - void *vbb; - unsigned int head = dl->head; - assert_in_vb_deferred(vb); - d = vb_descriptor(dl, head); - if (d->buffer1 && !OWNED(d)) { - dma_unmap_single(&vb->pdev->dev, d->buffer1, - vb->framesize, dl->direction); - vbb = dl->pending[head]; - dl->head = (++head) & DRING_MASK; - d->buffer1 = 0; - atomic_dec(&dl->count); - return vbb; - } else { - return NULL; - } -} - -/*! - * \brief Give a frame to the hardware to transmit. - * - */ -int -voicebus_transmit(struct voicebus *vb, void *vbb) -{ - return vb_submit(vb, &vb->txd, vbb); -} EXPORT_SYMBOL(voicebus_transmit); /*! @@ -784,7 +850,26 @@ EXPORT_SYMBOL(voicebus_transmit); static inline int vb_submit_rxb(struct voicebus *vb, void *vbb) { - return vb_submit(vb, &vb->rxd, vbb); + struct voicebus_descriptor *d; + struct voicebus_descriptor_list *dl = &vb->rxd; + unsigned int tail = dl->tail; + + d = vb_descriptor(dl, tail); + + if (unlikely(d->buffer1)) { + /* Do not overwrite a buffer that is still in progress. */ + WARN_ON(1); + voicebus_free(vb, vbb); + return -EBUSY; + } + + dl->pending[tail] = vbb; + dl->tail = (++tail) & DRING_MASK; + d->buffer1 = dma_map_single(&vb->pdev->dev, vbb, + vb->framesize, DMA_FROM_DEVICE); + SET_OWNED(d); /* That's it until the hardware is done with it. */ + atomic_inc(&dl->count); + return 0; } /*! @@ -803,13 +888,47 @@ vb_submit_rxb(struct voicebus *vb, void *vbb) static inline void * vb_get_completed_txb(struct voicebus *vb) { - return vb_retrieve(vb, &vb->txd); + struct voicebus_descriptor_list *dl = &vb->txd; + struct voicebus_descriptor *d; + void *vbb; + unsigned int head = dl->head; + + d = vb_descriptor(dl, head); + + if (OWNED(d) || (d->buffer1 == vb->idle_vbb_dma_addr)) + return NULL; + + dma_unmap_single(&vb->pdev->dev, d->buffer1, + vb->framesize, DMA_TO_DEVICE); + + vbb = dl->pending[head]; + dl->head = (++head) & DRING_MASK; + d->buffer1 = vb->idle_vbb_dma_addr; + SET_OWNED(d); + atomic_dec(&dl->count); + return vbb; } static inline void * vb_get_completed_rxb(struct voicebus *vb) { - return vb_retrieve(vb, &vb->rxd); + struct voicebus_descriptor *d; + struct voicebus_descriptor_list *dl = &vb->rxd; + unsigned int head = dl->head; + void *vbb; + + d = vb_descriptor(dl, head); + + if ((0 == d->buffer1) || OWNED(d)) + return NULL; + + dma_unmap_single(&vb->pdev->dev, d->buffer1, + vb->framesize, DMA_FROM_DEVICE); + vbb = dl->pending[head]; + dl->head = (++head) & DRING_MASK; + d->buffer1 = 0; + atomic_dec(&dl->count); + return vbb; } /*! @@ -905,7 +1024,9 @@ voicebus_start(struct voicebus *vb) void *vbb; int ret; - assert(!in_interrupt()); + WARN_ON(pci_get_drvdata(vb->pdev) != vb); + if (pci_get_drvdata(vb->pdev) != vb) + return -EFAULT; if (!vb_is_stopped(vb)) return -EBUSY; @@ -928,7 +1049,6 @@ voicebus_start(struct voicebus *vb) * is known to not be running at this point, it is safe to call the * handle transmit as if it were. */ - start_vb_deferred(vb); /* Ensure that all the rx slots are ready for a buffer. */ for (i = 0; i < DRING_SIZE; ++i) { vbb = voicebus_alloc(vb); @@ -947,10 +1067,9 @@ voicebus_start(struct voicebus *vb) if (unlikely(NULL == vbb)) BUG_ON(1); else - vb->handle_transmit(vbb, vb->context); + handle_transmit(vb, vbb); } - stop_vb_deferred(vb); VBLOCK(vb); clear_bit(STOP, &vb->flags); @@ -971,7 +1090,7 @@ voicebus_start(struct voicebus *vb) __vb_tx_demand_poll(vb); VBUNLOCK(vb); - assert(!vb_is_stopped(vb)); + BUG_ON(vb_is_stopped(vb)); return 0; } @@ -1033,8 +1152,6 @@ vb_wait_for_completion_timeout(struct completion *x, unsigned long timeout) int voicebus_stop(struct voicebus *vb) { - assert(!in_interrupt()); - if (vb_is_stopped(vb)) return 0; @@ -1043,11 +1160,10 @@ voicebus_stop(struct voicebus *vb) vb_clear_start_transmit_bit(vb); vb_clear_start_receive_bit(vb); if (vb_wait_for_completion_timeout(&vb->stopped_completion, HZ)) { - assert(vb_is_stopped(vb)); + BUG_ON(!vb_is_stopped(vb)); } else { - VB_PRINTK(vb, WARNING, "Timeout while waiting for board to "\ - "stop.\n"); - + dev_warn(&vb->pdev->dev, "Timeout while waiting for board to " + "stop.\n"); vb_clear_start_transmit_bit(vb); vb_clear_start_receive_bit(vb); @@ -1063,6 +1179,24 @@ voicebus_stop(struct voicebus *vb) } EXPORT_SYMBOL(voicebus_stop); +#ifdef CONFIG_VOICEBUS_SYSFS +static ssize_t +voicebus_current_latency_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long flags; + struct voicebus *vb = dev_get_drvdata(dev); + unsigned int current_latency; + spin_lock_irqsave(&vb->lock, flags); + current_latency = vb->min_tx_buffer_count; + spin_unlock_irqrestore(&vb->lock, flags); + return sprintf(buf, "%d\n", current_latency); +} + +DEVICE_ATTR(voicebus_current_latency, 0444, + voicebus_current_latency_show, NULL); +#endif + /*! * \brief Prepare the interface for module unload. * @@ -1075,7 +1209,9 @@ EXPORT_SYMBOL(voicebus_stop); void voicebus_release(struct voicebus *vb) { - assert(!in_interrupt()); +#ifdef CONFIG_VOICEBUS_SYSFS + device_remove_file(&vb->pdev->dev, &dev_attr_voicebus_current_latency); +#endif /* quiesce the hardware */ voicebus_stop(vb); @@ -1092,6 +1228,10 @@ voicebus_release(struct voicebus *vb) /* Cleanup memory and software resources. */ vb_free_descriptors(vb, &vb->txd); vb_free_descriptors(vb, &vb->rxd); + if (vb->idle_vbb_dma_addr) { + dma_free_coherent(&vb->pdev->dev, vb->framesize, + vb->idle_vbb, vb->idle_vbb_dma_addr); + } kmem_cache_destroy(vb->buffer_cache); release_region(vb->iobase, 0xff); pci_disable_device(vb->pdev); @@ -1100,103 +1240,291 @@ voicebus_release(struct voicebus *vb) EXPORT_SYMBOL(voicebus_release); static void -__vb_increase_latency(struct voicebus *vb) +vb_increase_latency(struct voicebus *vb, unsigned int increase) { - static int __warn_once = 1; void *vbb; - int latency; + int i; - assert_in_vb_deferred(vb); + if (0 == increase) + return; - latency = atomic_read(&vb->txd.count); - if (DRING_SIZE == latency) { - if (__warn_once) { - /* We must subtract two from this number since there - * are always two buffers in the TX FIFO. - */ - VB_PRINTK(vb, ERR, - "ERROR: Unable to service card within %d ms "\ - "and unable to further increase latency.\n", - DRING_SIZE-2); - __warn_once = 0; - } - } else { - /* Because there are 2 buffers in the transmit FIFO on the - * hardware, setting 3 ms of latency means that the host needs - * to be able to service the cards within 1ms. This is because - * the interface will load up 2 buffers into the TX FIFO then - * attempt to read the 3rd descriptor. If the OWN bit isn't - * set, then the hardware will set the TX descriptor not - * available interrupt. - */ - VB_PRINTK(vb, INFO, "Missed interrupt. " \ - "Increasing latency to %d ms in order to compensate.\n", - latency+1); - /* Set the minimum latency in case we're restarted...we don't - * want to wait for the buffer to grow to this depth again in - * that case. - */ - voicebus_set_minlatency(vb, latency+1); + if (test_bit(LATENCY_LOCKED, &vb->flags)) + return; + + if (unlikely(increase > VOICEBUS_MAXLATENCY_BUMP)) + increase = VOICEBUS_MAXLATENCY_BUMP; + + if ((increase + vb->min_tx_buffer_count) > vb->max_latency) + increase = vb->max_latency - vb->min_tx_buffer_count; + + /* Because there are 2 buffers in the transmit FIFO on the hardware, + * setting 3 ms of latency means that the host needs to be able to + * service the cards within 1ms. This is because the interface will + * load up 2 buffers into the TX FIFO then attempt to read the 3rd + * descriptor. If the OWN bit isn't set, then the hardware will set the + * TX descriptor not available interrupt. */ + + /* Set the minimum latency in case we're restarted...we don't want to + * wait for the buffer to grow to this depth again in that case. */ + for (i = 0; i < increase; ++i) { vbb = voicebus_alloc(vb); + WARN_ON(NULL == vbb); + if (likely(NULL != vbb)) + handle_transmit(vb, vbb); + } - if (unlikely(NULL == vbb)) - BUG_ON(1); - else - vb->handle_transmit(vbb, vb->context); + /* Set the new latency (but we want to ensure that there aren't any + * printks to the console, so we don't call the function) */ + spin_lock(&vb->lock); + vb->min_tx_buffer_count += increase; + spin_unlock(&vb->lock); +} +static void vb_set_all_owned(struct voicebus *vb, + struct voicebus_descriptor_list *dl) +{ + int i; + struct voicebus_descriptor *d; + + for (i = 0; i < DRING_SIZE; ++i) { + d = vb_descriptor(dl, i); + SET_OWNED(d); } } -/*! - * \brief Actually process the completed transmit and receive buffers. +static inline void vb_set_all_tx_owned(struct voicebus *vb) +{ + vb_set_all_owned(vb, &vb->txd); +} + +/** + * __vb_get_default_behind_count() - Returns how many idle buffers are loaded in tx fifo. + * + * These buffers are going to be set, but the AN983 does not clear the owned + * bit on the descriptors until they've actually been sent around. * - * NOTE: This function may be called either from a tasklet, workqueue, or - * directly in the interrupt service routine depending on - * VOICEBUS_DEFERRED. + * If you do not check for both the current and next descriptors, you could have + * a condition where idle buffers are being sent around, but we don't detect + * them because our current descriptor always points to a non-idle buffer. */ -static inline void -vb_deferred(struct voicebus *vb) +static unsigned int __vb_get_default_behind_count(const struct voicebus *vb) { - void *vbb; -#ifdef DBG - static int count; -#endif + const struct voicebus_descriptor_list *const dl = &vb->txd; + const struct voicebus_descriptor *current_descriptor; + const struct voicebus_descriptor *next_descriptor; + + current_descriptor = vb_descriptor(dl, dl->head); + if (current_descriptor->buffer1 == vb->idle_vbb_dma_addr) + return 2; + + next_descriptor = vb_descriptor(dl, ((dl->head + 1) & DRING_MASK)); + if (next_descriptor->buffer1 == vb->idle_vbb_dma_addr) + return 1; + + return 0; +} + +/** + * vb_check_softunderrun() - Return true if the TX FIFO has underrun valid data. + * + * Returns true if we're processing the idle buffers, which means that the host + * was not able to keep up with the hardware. This differs from a normal + * underrun in that the interface chip on the board still has descriptors to + * transmit (just in this case, they are the idle buffers). + * + */ +static inline int vb_is_softunderrun(const struct voicebus *vb) +{ + return __vb_get_default_behind_count(vb) > 0; +} + +/** + * vb_recover_tx_descriptor_list() - Recovers descriptor list + * + * Returns the number of descriptors that we're behind. + * + * Called if the [head | head + 1] pointer points to one of the idle buffers. + * This means that the host computer has failed to keep far enough ahead of the + * voicebus card. This function acks the completed idle descriptors and gets + * everything setup for normal operation again. + * + */ +static unsigned int vb_recover_tx_descriptor_list(struct voicebus *vb) +{ + struct voicebus_descriptor_list *const dl = &vb->txd; + struct voicebus_descriptor *d; + struct vbb *vbb = NULL; + unsigned int behind = __vb_get_default_behind_count(vb); + WARN_ON(0 == behind); + + d = vb_descriptor(dl, dl->head); + + /* If we're only behind by one descriptor, we need to wait for all + * descriptors to go owned so we're starting with a fresh slate. This + * will also means we send an additional idle buffer. */ + if (1 == behind) { + unsigned long stop; + stop = jiffies + HZ/100; + while (OWNED(d) && time_after(stop, jiffies)) + continue; + WARN_ON(time_before(stop, jiffies)); + WARN_ON(d->buffer1 == vb->idle_vbb_dma_addr); + WARN_ON(!dl->pending[dl->head]); + dma_unmap_single(&vb->pdev->dev, d->buffer1, + vb->framesize, DMA_TO_DEVICE); + vbb = dl->pending[dl->head]; + atomic_dec(&dl->count); + --behind; + } + + /* First complete any "idle" buffers that the hardware was to actually + * complete. We've already preloaded the behind variable for the idle + * buffers that are in progress but may not be complete. */ + while (!OWNED(d)) { + d->buffer1 = vb->idle_vbb_dma_addr; + dl->pending[dl->head] = vb->idle_vbb; + SET_OWNED(d); + dl->head = ++dl->head & DRING_MASK; + d = vb_descriptor(dl, dl->head); + ++behind; + } + + /* Next get a little further ahead, because the hardware will be + * currently working on one of the idle buffers that we can't detect is + * completed yet in the previous block. Set the head and tail pointers + * to this new position so that everything can pick up normally. */ + dl->tail = dl->head = (dl->head + 10) & DRING_MASK; + + if (NULL != vbb) + handle_transmit(vb, vbb); + + return behind; +} + +/** + * vb_deferred() - Manage the transmit and receive descriptor rings. + * + */ +static void vb_deferred(struct voicebus *vb) +{ + unsigned int buffer_count; + unsigned int i; + unsigned int idle_buffers; + int softunderrun; + int underrun = test_bit(TX_UNDERRUN, &vb->flags); + buffer_count = 0; - start_vb_deferred(vb); - if (unlikely(underrun)) { - /* When we've underrun our FIFO, for some reason we're not - * able to keep enough transmit descriptors pending. This can - * happen if either interrupts or this deferred processing - * function is not run soon enough (within 1ms when using the - * default 3 transmit buffers to start). In this case, we'll - * insert an additional transmit buffer onto the descriptor - * list which decreases the sensitivity to latency, but also - * adds more delay to the TDM and SPI data. - */ - __vb_increase_latency(vb); + /* First, temporarily store any non-idle buffers that the hardware has + * indicated it's finished transmitting. Non idle buffers are those + * buffers that contain actual data and was filled out by the client + * driver (as of this writing, the wcte12xp or wctdm24xxp drivers) when + * passed up through the handle_transmit callback. + * + * On the other hand, idle buffers are "dummy" buffers that solely exist + * to in order to prevent the transmit descriptor ring from ever + * completely draining. */ + while ((vb->vbb_stash[buffer_count] = vb_get_completed_txb(vb))) { + ++buffer_count; + if (unlikely(VOICEBUS_DEFAULT_MAXLATENCY < buffer_count)) { + dev_warn(&vb->pdev->dev, "Critical problem detected " + "in transmit ring descriptor\n"); + if (buffer_count >= DRING_SIZE) + buffer_count = DRING_SIZE - 1; + break; + } } - /* Always handle the transmit buffers first. */ - while ((vbb = vb_get_completed_txb(vb))) - vb->handle_transmit(vbb, vb->context); + vb->count += buffer_count; + + /* Next, check to see if we're in a softunderrun condition. + * + * A soft under is when the hardware has possibly copied at least one of + * the idle buffers into it's transmit queue. Since all the buffers are + * nearly always owned by the hardware, the way we detect this is by + * checking if either of the next two buffers that we expect to complete + * are idle buffers. We need to check the next two, because the + * hardware can read two buffers into it's tx fifo where they are + * definitely going to be sent on the interface, but the hardware does + * not flip the OWN bit on the descriptors until after the buffer has + * finished being sent to the CPLD. Therefore, just looking at the own + * bit and the buffer pointed to by the dl->head is not enough. + * + * NOTE: Do not print anything to the console from the time a soft + * underrun is detected until the transmit descriptors are fixed up + * again. Otherwise the hardware could advance past where you set the + * head and tail pointers and then eventually run into the desciptor + * that it was currently working on when the softunderrun condition was + * first hit. */ + if (unlikely(vb_is_softunderrun(vb))) { + softunderrun = 1; + /* If we experienced a soft underrun, the recover function will + * a) ensure that all non-idle buffers have been completely + * tranmitted by the hardware b) Reset any idle buffers that + * have been completely transmitted c) Reset the head and tail + * pointers to somwhere in advance of where the hardware is + * currently processing and d) return how many idle_buffers were + * transmitted before our interrupt handler was called. */ + idle_buffers = vb_recover_tx_descriptor_list(vb); + vb_increase_latency(vb, idle_buffers); + } else { + softunderrun = 0; + idle_buffers = 0; + } + /* Now we can process the completed non-idle buffers since we know at + * this point that the transmit descriptor is in a "good" state. */ + for (i = 0; i < buffer_count; ++i) + handle_transmit(vb, vb->vbb_stash[i]); + + /* If underrun is set, it means that the hardware signalled that it + * completely ran out of transmit descriptors. This is what we are + * trying to avoid with all this racy softunderun business, but alas, + * it's still possible to happen if interrupts are locked longer than + * DRING_SIZE milliseconds for some reason. We should have already fixed + * up the descriptor ring in this case, so let's just tell the hardware + * to reread what it believes the next descriptor is. */ if (unlikely(underrun)) { + if (printk_ratelimit()) { + dev_info(&vb->pdev->dev, "Host failed to service " + "card interrupt within %d ms which is a " + "hardunderun.\n", DRING_SIZE); + } vb_rx_demand_poll(vb); vb_tx_demand_poll(vb); clear_bit(TX_UNDERRUN, &vb->flags); } - while ((vbb = vb_get_completed_rxb(vb))) { - vb->handle_receive(vbb, vb->context); - vb_submit_rxb(vb, vbb); + /* Print any messages about soft latency bumps after we fix the transmit + * descriptor ring. Otherwise it's possible to take so much time + * printing the dmesg output that we lose the lead that we got on the + * hardware, resulting in a hard underrun condition. */ + if (unlikely(softunderrun && + !test_bit(LATENCY_LOCKED, &vb->flags) && printk_ratelimit())) { + if (vb->max_latency != vb->min_tx_buffer_count) { + dev_info(&vb->pdev->dev, "Missed interrupt. " + "Increasing latency to %d ms in order to " + "compensate.\n", vb->min_tx_buffer_count); + } else { + dev_info(&vb->pdev->dev, "ERROR: Unable to service " + "card within %d ms and unable to further " + "increase latency.\n", vb->max_latency); + } } - stop_vb_deferred(vb); + /* And finally, pass up any receive buffers. We also use vb->count to + * make a half-hearted attempt to not pass any recieved idle buffers to + * the caller, but this needs more work.... */ + while ((vb->vbb_stash[0] = vb_get_completed_rxb(vb))) { + if (vb->count) { + vb->handle_receive(vb->vbb_stash[0], vb->context); + --vb->count; + } + vb_submit_rxb(vb, vb->vbb_stash[0]); + } } - /*! * \brief Interrupt handler for VoiceBus interface. * @@ -1262,15 +1590,15 @@ vb_isr(int irq, void *dev_id) } if (int_status & FATAL_BUS_ERROR_INTERRUPT) - VB_PRINTK(vb, ERR, "Fatal Bus Error detected!\n"); + dev_err(&vb->pdev->dev, "Fatal Bus Error detected!\n"); if (int_status & TX_STOPPED_INTERRUPT) { - assert(test_bit(STOP, &vb->flags)); + BUG_ON(!test_bit(STOP, &vb->flags)); __vb_disable_interrupts(vb); complete(&vb->stopped_completion); } if (int_status & RX_STOPPED_INTERRUPT) { - assert(test_bit(STOP, &vb->flags)); + BUG_ON(!test_bit(STOP, &vb->flags)); if (vb_is_stopped(vb)) { __vb_disable_interrupts(vb); complete(&vb->stopped_completion); @@ -1334,8 +1662,7 @@ vb_tasklet(unsigned long data) * \todo Complete this description. */ int -voicebus_init(struct pci_dev *pdev, u32 framesize, - const char *board_name, +voicebus_init(struct pci_dev *pdev, u32 framesize, const char *board_name, void (*handle_receive)(void *vbb, void *context), void (*handle_transmit)(void *vbb, void *context), void *context, @@ -1346,11 +1673,11 @@ voicebus_init(struct pci_dev *pdev, u32 framesize, int retval = 0; struct voicebus *vb; - assert(NULL != pdev); - assert(NULL != board_name); - assert(framesize); - assert(NULL != handle_receive); - assert(NULL != handle_transmit); + BUG_ON(NULL == pdev); + BUG_ON(NULL == board_name); + BUG_ON(0 == framesize); + BUG_ON(NULL == handle_receive); + BUG_ON(NULL == handle_transmit); /* ---------------------------------------------------------------- Initialize the pure software constructs. @@ -1358,20 +1685,19 @@ voicebus_init(struct pci_dev *pdev, u32 framesize, *vbp = NULL; vb = kmalloc(sizeof(*vb), GFP_KERNEL); if (NULL == vb) { - VB_PRINTK(vb, DEBUG, "Failed to allocate memory for voicebus "\ - "interface.\n"); + dev_dbg(&vb->pdev->dev, "Failed to allocate memory for " + "voicebus interface.\n"); retval = -ENOMEM; goto cleanup; } memset(vb, 0, sizeof(*vb)); + vb->pdev = pdev; + pci_set_drvdata(pdev, vb); voicebus_setdebuglevel(vb, debuglevel); - /* \todo make sure there is a note that the caller needs to make sure - * board_name stays in memory until voicebus_release is called. - */ - vb->board_name = board_name; + vb->max_latency = VOICEBUS_DEFAULT_MAXLATENCY; + spin_lock_init(&vb->lock); init_completion(&vb->stopped_completion); - vb->pdev = pdev; set_bit(STOP, &vb->flags); clear_bit(IN_DEFERRED_PROCESSING, &vb->flags); vb->framesize = framesize; @@ -1413,26 +1739,46 @@ voicebus_init(struct pci_dev *pdev, u32 framesize, SLAB_HWCACHE_ALIGN, NULL, NULL); #endif #else +#ifdef DEBUG + vb->buffer_cache = kmem_cache_create(board_name, vb->framesize, 0, + SLAB_HWCACHE_ALIGN | SLAB_STORE_USER | + SLAB_POISON | SLAB_DEBUG_FREE, NULL); +#else vb->buffer_cache = kmem_cache_create(board_name, vb->framesize, 0, SLAB_HWCACHE_ALIGN, NULL); #endif +#endif if (NULL == vb->buffer_cache) { - VB_PRINTK(vb, ERR, "Failed to allocate buffer cache.\n"); + dev_err(&vb->pdev->dev, "Failed to allocate buffer cache.\n"); goto cleanup; } - +#ifdef CONFIG_VOICEBUS_SYSFS + dev_dbg(&vb->pdev->dev, "Creating sysfs attributes.\n"); + retval = device_create_file(&vb->pdev->dev, + &dev_attr_voicebus_current_latency); + if (retval) { + dev_dbg(&vb->pdev->dev, + "Failed to create device attributes.\n"); + goto cleanup; + } +#endif /* ---------------------------------------------------------------- Configure the hardware / kernel module interfaces. ---------------------------------------------------------------- */ + if (pci_set_dma_mask(vb->pdev, DMA_BIT_MASK(32))) { + dev_err(&vb->pdev->dev, "No suitable DMA available.\n"); + goto cleanup; + } + if (pci_read_config_byte(vb->pdev, 0x0c, &vb->cache_line_size)) { - VB_PRINTK(vb, ERR, "Failed read of cache line " \ - "size from PCI configuration space.\n"); + dev_err(&vb->pdev->dev, "Failed read of cache line " + "size from PCI configuration space.\n"); goto cleanup; } if (pci_enable_device(pdev)) { - VB_PRINTK(vb, ERR, "Failed call to pci_enable_device.\n"); + dev_err(&vb->pdev->dev, "Failed call to pci_enable_device.\n"); retval = -EIO; goto cleanup; } @@ -1440,18 +1786,21 @@ voicebus_init(struct pci_dev *pdev, u32 framesize, /* \todo This driver should be modified to use the memory mapped I/O as opposed to IO space for portability and performance. */ if (0 == (pci_resource_flags(pdev, 0)&IORESOURCE_IO)) { - VB_PRINTK(vb, ERR, "BAR0 is not IO Memory.\n"); + dev_err(&vb->pdev->dev, "BAR0 is not IO Memory.\n"); retval = -EIO; goto cleanup; } vb->iobase = pci_resource_start(pdev, 0); if (NULL == request_region(vb->iobase, 0xff, board_name)) { - VB_PRINTK(vb, ERR, "IO Registers are in use by another " \ + dev_err(&vb->pdev->dev, "IO Registers are in use by another " "module.\n"); retval = -EIO; goto cleanup; } + vb->idle_vbb = dma_alloc_coherent(&vb->pdev->dev, vb->framesize, + &vb->idle_vbb_dma_addr, GFP_KERNEL); + retval = vb_initialize_tx_descriptors(vb); if (retval) goto cleanup; @@ -1463,18 +1812,20 @@ voicebus_init(struct pci_dev *pdev, u32 framesize, /* ---------------------------------------------------------------- Configure the hardware interface. ---------------------------------------------------------------- */ + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { + release_region(vb->iobase, 0xff); + dev_warn(&vb->pdev->dev, "No suitable DMA available.\n"); + goto cleanup; + } + pci_set_master(pdev); vb_enable_io_access(vb); #if VOICEBUS_DEFERRED != TIMER -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) -# define VB_IRQ_SHARED SA_SHIRQ -#else -# define VB_IRQ_SHARED IRQF_SHARED -#endif - if (request_irq(pdev->irq, vb_isr, VB_IRQ_SHARED, vb->board_name, - vb)) { - assert(0); + retval = request_irq(pdev->irq, vb_isr, DAHDI_IRQ_SHARED, + board_name, vb); + if (retval) { + dev_warn(&vb->pdev->dev, "Failed to request interrupt line.\n"); goto cleanup; } #endif @@ -1501,6 +1852,9 @@ cleanup: if (vb->rxd.desc) vb_free_descriptors(vb, &vb->rxd); + dma_free_coherent(&vb->pdev->dev, vb->framesize, + vb->idle_vbb, vb->idle_vbb_dma_addr); + if (vb->buffer_cache) kmem_cache_destroy(vb->buffer_cache); @@ -1511,7 +1865,7 @@ cleanup: pci_disable_device(vb->pdev); kfree(vb); - assert(0 != retval); + WARN_ON(0 == retval); return retval; } EXPORT_SYMBOL(voicebus_init); @@ -1525,6 +1879,12 @@ voicebus_get_pci_dev(struct voicebus *vb) } EXPORT_SYMBOL(voicebus_get_pci_dev); +void *voicebus_pci_dev_to_context(struct pci_dev *pdev) +{ + return ((struct voicebus *)pci_get_drvdata(pdev))->context; +} +EXPORT_SYMBOL(voicebus_pci_dev_to_context); + static spinlock_t loader_list_lock; static struct list_head binary_loader_list; @@ -1560,12 +1920,14 @@ int vpmadtreg_loadfirmware(struct voicebus *vb) module_put(loader->owner); } else { spin_unlock(&loader_list_lock); - printk(KERN_INFO "Failed to find a registered loader after loading module.\n"); + dev_info(&vb->pdev->dev, "Failed to find a " + "registered loader after loading module.\n"); ret = -ENODEV; } } else { spin_unlock(&loader_list_lock); - printk(KERN_INFO "Failed to find a registered loader after loading module.\n"); + dev_info(&vb->pdev->dev, "Failed to find a registered " + "loader after loading module.\n"); ret = -1; } return ret; diff --git a/drivers/dahdi/voicebus/voicebus.h b/drivers/dahdi/voicebus/voicebus.h index eaa51e6..16768df 100644 --- a/drivers/dahdi/voicebus/voicebus.h +++ b/drivers/dahdi/voicebus/voicebus.h @@ -32,10 +32,13 @@ struct voicebus; #define VOICEBUS_DEFAULT_LATENCY 3 +#define VOICEBUS_DEFAULT_MAXLATENCY 25 +#define VOICEBUS_MAXLATENCY_BUMP 6 void voicebus_setdebuglevel(struct voicebus *vb, u32 level); int voicebus_getdebuglevel(struct voicebus *vb); -struct pci_dev * voicebus_get_pci_dev(struct voicebus *vb); +struct pci_dev *voicebus_get_pci_dev(struct voicebus *vb); +void *voicebus_pci_dev_to_context(struct pci_dev *pdev); int voicebus_init(struct pci_dev* pdev, u32 framesize, const char *board_name, void (*handle_receive)(void *buffer, void *context), @@ -52,10 +55,13 @@ void voicebus_set_handlers(struct voicebus *vb, void voicebus_release(struct voicebus *vb); int voicebus_start(struct voicebus *vb); int voicebus_stop(struct voicebus *vb); -void * voicebus_alloc(struct voicebus* vb); +void *voicebus_alloc(struct voicebus* vb); void voicebus_free(struct voicebus *vb, void *vbb); int voicebus_transmit(struct voicebus *vb, void *vbb); int voicebus_set_minlatency(struct voicebus *vb, unsigned int milliseconds); -int voicebus_current_latency(struct voicebus *vb) ; +int voicebus_current_latency(struct voicebus *vb); +void voicebus_lock_latency(struct voicebus *vb); +void voicebus_unlock_latency(struct voicebus *vb); +int voicebus_is_latency_locked(const struct voicebus *vb); #endif /* __VOICEBUS_H__ */ diff --git a/drivers/dahdi/wcb4xxp/base.c b/drivers/dahdi/wcb4xxp/base.c index 1e05309..965eed9 100644 --- a/drivers/dahdi/wcb4xxp/base.c +++ b/drivers/dahdi/wcb4xxp/base.c @@ -47,6 +47,15 @@ #include "wcb4xxp.h" +#ifndef BIT /* added in 2.6.24 */ +#define BIT(i) (1UL << (i)) +#endif +#define BIT_SET(x, i) ((x) |= BIT(i)) +#define BIT_CLR(x, i) ((x) &= ~BIT(i)) +#define IS_SET(x, i) (((x) & BIT(i)) != 0) +#define BITMASK(i) (((u64)1 << (i)) - 1) + + #if (DAHDI_CHUNKSIZE != 8) #error Sorry, wcb4xxp does not support chunksize != 8 #endif @@ -72,10 +81,10 @@ #define DBG_HDLC (debug & DEBUG_HDLC) #define DBG_ALARM (debug & DEBUG_ALARM) -#define DBG_SPANFILTER ((1 << bspan->port) & spanfilter) +#define DBG_SPANFILTER (BIT(bspan->port) & spanfilter) static int debug = 0; -static int spanfilter = 15; +static int spanfilter = 0xFF; /* Bitmap for ports 1-8 */ #ifdef LOOPBACK_SUPPORTED static int loopback = 0; #endif @@ -114,9 +123,22 @@ static struct proc_dir_entry *myproc; struct devtype { char *desc; unsigned int flags; + int ports; /* Number of ports the card has */ + enum cards_ids card_type; /* Card type - Digium B410P, ... */ }; -static struct devtype wcb4xxp = { "Wildcard B410P", 0 }; +static struct devtype wcb4xxp = {"Wildcard B410P", .ports = 4, .card_type = B410P }; +static struct devtype hfc2s = {"HFC-2S Junghanns.NET duoBRI PCI", .ports = 2, .card_type = DUOBRI }; +static struct devtype hfc4s = {"HFC-4S Junghanns.NET quadBRI PCI", .ports = 4, .card_type = QUADBRI }; +static struct devtype hfc8s = {"HFC-4S Junghanns.NET octoBRI PCI", .ports = 8, .card_type = OCTOBRI }; +static struct devtype hfc2s_OV = {"OpenVox B200P", .ports = 2, .card_type = B200P_OV }; +static struct devtype hfc4s_OV = {"OpenVox B400P", .ports = 4, .card_type = B400P_OV }; +static struct devtype hfc8s_OV = {"OpenVox B800P", .ports = 8, .card_type = B800P_OV }; +static struct devtype hfc2s_BN = {"BeroNet BN2S0", .ports = 2, .card_type = BN2S0 }; +static struct devtype hfc4s_BN = {"BeroNet BN4S0", .ports = 4, .card_type = BN4S0 }; +static struct devtype hfc8s_BN = {"BeroNet BN8S0", .ports = 8, .card_type = BN8S0 }; + +#define CARD_HAS_EC(card) ((card)->card_type == B410P) static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec); @@ -403,7 +425,18 @@ static void hfc_gpio_init(struct b4xxp *b4) mb(); - b4xxp_setreg8(b4, R_GPIO_SEL, 0xf0); /* GPIO0..7 S/T, 8..15 GPIO */ + switch (b4->card_type) { + case OCTOBRI: /* fall through */ + case B800P_OV: /* fall through */ + case BN8S0: + /* GPIO0..15 S/T - HFC-8S uses GPIO8-15 for S/T ports 5-8 */ + b4xxp_setreg8(b4, R_GPIO_SEL, 0x00); + break; + default: + /* GPIO0..7 S/T, 8..15 GPIO */ + b4xxp_setreg8(b4, R_GPIO_SEL, 0xf0); + break; + } mb(); @@ -618,13 +651,16 @@ static void ec_init(struct b4xxp *b4) unsigned char b; unsigned int i, j, mask; + if (!CARD_HAS_EC(b4)) + return; + /* Setup GPIO */ for (i=0; i < NUM_EC; i++) { b = ec_read(b4, i, 0x1a0); dev_info(b4->dev, "VPM %d/%d init: chip ver %02x\n", i, NUM_EC - 1, b); - for (j=0; j < 4; j++) { + for (j=0; j < b4->numspans; j++) { ec_write(b4, i, 0x1a8 + j, 0x00); /* GPIO out */ ec_write(b4, i, 0x1ac + j, 0x00); /* GPIO dir */ ec_write(b4, i, 0x1b0 + j, 0x00); /* GPIO sel */ @@ -1008,7 +1044,18 @@ static void hfc_assign_dchan_fifo(struct b4xxp *b4, int port) int fifo, hfc_chan; unsigned long irq_flags; - fifo = port + 8; + switch (b4->card_type) { + case B800P_OV: /* fall through */ + case OCTOBRI: /* fall through */ + case BN8S0: + /* In HFC-8S cards we can't use ports 8-11 for dchan FIFOs */ + fifo = port + 16; + break; + default: + fifo = port + 8; + break; + } + hfc_chan = (port * 4) + 2; /* record the host's FIFO # in the span fifo array */ @@ -1210,7 +1257,7 @@ static void hfc_update_st_timers(struct b4xxp *b4) int i, j; struct b4xxp_span *s; - for (i=0; i < 4; i++) { + for (i=0; i < b4->numspans; i++) { s = &b4->spans[i]; for (j=HFC_T1; j <= HFC_T3; j++) { @@ -1413,12 +1460,21 @@ static void hfc_init_all_st(struct b4xxp *b4) gpio = b4xxp_getreg8(b4, R_GPI_IN3); - for (i=0; i < 4; i++) { + for (i=0; i < b4->numspans; i++) { s = &b4->spans[i]; s->parent = b4; s->port = i; - nt = ((gpio & (1 << (i + 4))) == 0); /* GPIO=0 = NT mode */ + /* The way the Digium B410P card reads the NT/TE mode + * jumper is the oposite of how other HFC-4S cards do: + * - In B410P: GPIO=0: NT + * - In Junghanns: GPIO=0: TE + */ + if (b4->card_type == B410P) + nt = ((gpio & (1 << (i + 4))) == 0); + else + nt = ((gpio & (1 << (i + 4))) != 0); + s->te_mode = !nt; dev_info(b4->dev, "Port %d: %s mode\n", i + 1, (nt ? "NT" : "TE")); @@ -1774,9 +1830,15 @@ static void b4xxp_init_stage1(struct b4xxp *b4) /* * set up the clock controller - * we have a 24.576MHz crystal, so the PCM clock is 2x the incoming clock. + * B410P has a 24.576MHz crystal, so the PCM clock is 2x the incoming clock. + * Other cards have a 49.152Mhz crystal, so the PCM clock equals incoming clock. */ - b4xxp_setreg8(b4, R_BRG_PCM_CFG, 0x02); + + if (b4->card_type == B410P) + b4xxp_setreg8(b4, R_BRG_PCM_CFG, 0x02); + else + b4xxp_setreg8(b4, R_BRG_PCM_CFG, V_PCM_CLK); + flush_pci(); udelay(100); /* wait a bit for clock to settle */ @@ -1807,7 +1869,7 @@ static void b4xxp_init_stage2(struct b4xxp *b4) /* * set up the flow controller. - * B channel map: + * B channel map: (4 ports cards with Hardware Echo Cancel present & active) * FIFO 0 connects Port 1 B0 using HFC channel 16 and PCM timeslots 0/1. * FIFO 1 connects Port 1 B1 using HFC channel 17 and PCM timeslots 4/5. * FIFO 2 connects Port 2 B0 using HFC channel 20 and PCM timeslots 8/9. @@ -1822,14 +1884,35 @@ static void b4xxp_init_stage2(struct b4xxp *b4) * * D channels are handled by FIFOs 8-11. * FIFO 8 connects Port 1 D using HFC channel 3 - * FIFO 9 connects Port 1 D using HFC channel 7 - * FIFO 10 connects Port 1 D using HFC channel 11 - * FIFO 11 connects Port 1 D using HFC channel 15 + * FIFO 9 connects Port 2 D using HFC channel 7 + * FIFO 10 connects Port 3 D using HFC channel 11 + * FIFO 11 connects Port 4 D using HFC channel 15 * * D channel FIFOs are operated in HDLC mode and interrupt on end of frame. + * + * B channel map: (8 ports cards without Hardware Echo Cancel) + * FIFO 0 connects Port 1 B0 using HFC channel 0 + * FIFO 1 connects Port 1 B1 using HFC channel 1 + * FIFO 2 connects Port 2 B0 using HFC channel 4 + * FIFO 3 connects Port 2 B1 using HFC channel 5 + * ......................... + * FIFO 14 connects Port 8 B0 using HFC channel 28 + * FIFO 15 connects Port 8 B1 using HFC channel 29 + * + * All B channel FIFOs have their HDLC controller in transparent mode, + * and only the FIFO for B0 on each port has its interrupt operational. + * + * D channels are handled by FIFOs 16-23. + * FIFO 16 connects Port 1 D using HFC channel 3 + * FIFO 17 connects Port 2 D using HFC channel 7 + * FIFO 18 connects Port 3 D using HFC channel 11 + * FIFO 19 connects Port 4 D using HFC channel 15 + * ................ + * FIFO 23 connects Port 8 D using HFC channel 31 + * D channel FIFOs are operated in HDLC mode and interrupt on end of frame. */ for (span=0; span < b4->numspans; span++) { - if (vpmsupport) { + if ((vpmsupport) && (CARD_HAS_EC(b4))) { hfc_assign_bchan_fifo_ec(b4, span, 0); hfc_assign_bchan_fifo_ec(b4, span, 1); } else { @@ -1854,6 +1937,79 @@ static void b4xxp_setleds(struct b4xxp *b4, unsigned char val) ec_write(b4, 0, 0x1a8 + 3, val); } +static void b4xxp_update_leds_hfc_8s(struct b4xxp *b4) +{ + unsigned long lled = 0; /* A bit set is a led OFF */ + unsigned long leddw; + int j; + struct b4xxp_span *bspan; + + b4->blinktimer++; + for (j = 7; j >= 0; j--) { + bspan = &b4->spans[7 - j]; + if (!(bspan->span.flags & DAHDI_FLAG_RUNNING) || + bspan->span.alarms) { + BIT_SET(lled, j); + continue; /* Led OFF */ + } + + if (bspan->span.mainttimer || bspan->span.maintstat) { + /* Led Blinking in maint state */ + if (b4->blinktimer >= 0x7f) + BIT_SET(lled, j); + } + /* Else: Led on */ + } + + /* Write Leds...*/ + leddw = lled << 24 | lled << 16 | lled << 8 | lled; + b4xxp_setreg8(b4, R_BRG_PCM_CFG, 0x21); + iowrite16(0x4000, b4->ioaddr + 4); + iowrite32(leddw, b4->ioaddr); + b4xxp_setreg8(b4, R_BRG_PCM_CFG, 0x20); + + if (b4->blinktimer == 0xff) + b4->blinktimer = -1; +} + +/* So far only tested for OpenVox cards. Please test it for other hardware */ +static void b4xxp_update_leds_hfc(struct b4xxp *b4) +{ + int i; + int leds = 0, green_leds = 0; /* Default: off */ + struct b4xxp_span *bspan; + + b4->blinktimer++; + for (i=0; i < b4->numspans; i++) { + bspan = &b4->spans[i]; + + if (!(bspan->span.flags & DAHDI_FLAG_RUNNING)) + continue; /* Leds are off */ + + if (bspan->span.alarms) { + /* Red blinking -> Alarm */ + if (b4->blinktimer >= 0x7f) + BIT_SET(leds, i); + } else if (bspan->span.mainttimer || bspan->span.maintstat) { + /* Green blinking -> Maint status */ + if (b4->blinktimer >= 0x7f) + BIT_SET(green_leds, i); + } else { + /* Steady grean -> No Alarm */ + BIT_SET(green_leds, i); + } + } + + /* Actually set them. for red: just set the bit in R_GPIO_EN1. + For green: in both R_GPIO_EN1 and R_GPIO_OUT1. */ + leds |= green_leds; + b4xxp_setreg8(b4, R_GPIO_EN1, leds); + b4xxp_setreg8(b4, R_GPIO_OUT1, green_leds); + + if (b4->blinktimer == 0xff) + b4->blinktimer = -1; +} + static void b4xxp_set_span_led(struct b4xxp *b4, int span, unsigned char val) { int shift, spanmask; @@ -1871,6 +2027,18 @@ static void b4xxp_update_leds(struct b4xxp *b4) int i; struct b4xxp_span *bspan; + if (b4->numspans == 8) { + /* Use the alternative function for non-Digium HFC-8S cards */ + b4xxp_update_leds_hfc_8s(b4); + return; + } + + if (b4->card_type != B410P) { + /* Use the alternative function for non-Digium HFC-4S cards */ + b4xxp_update_leds_hfc(b4); + return; + } + b4->blinktimer++; for (i=0; i < b4->numspans; i++) { bspan = &b4->spans[i]; @@ -2174,7 +2342,7 @@ static void init_spans(struct b4xxp *b4) bspan->span.close = b4xxp_close; bspan->span.ioctl = b4xxp_ioctl; bspan->span.hdlc_hard_xmit = b4xxp_hdlc_hard_xmit; - if (vpmsupport) + if (vpmsupport && CARD_HAS_EC(b4)) bspan->span.echocan_create = echocan_create; /* HDLC stuff */ @@ -2281,13 +2449,21 @@ DAHDI_IRQ_HANDLER(b4xxp_interrupt) static void b4xxp_bottom_half(unsigned long data) { struct b4xxp *b4 = (struct b4xxp *)data; - int i, j, k, gotrxfifo, fifo; + int i, j, k, gotrxfifo, fifo, fifo_low, fifo_high; unsigned char b, b2; if (b4->shutdown) return; gotrxfifo = 0; + /* HFC-4S d-chan fifos 8-11 *** HFC-8S d-chan fifos 16-23 */ + if (b4->numspans == 8) { + fifo_low = 16; + fifo_high = 23; + } else { + fifo_low = 8; + fifo_high = 11; + } for (i=0; i < 8; i++) { b = b2 = b4->fifo_irqstatus[i]; @@ -2296,7 +2472,8 @@ static void b4xxp_bottom_half(unsigned long data) fifo = i*4 + j; if (b & V_IRQ_FIFOx_TX) { - if (fifo >=8 && fifo <= 11) { /* d-chan fifo */ + if (fifo >= fifo_low && fifo <= fifo_high) { + /* d-chan fifos */ /* * WOW I don't like this. * It's bad enough that I have to send a fake frame to get an HDLC TX FIFO interrupt, @@ -2305,7 +2482,7 @@ static void b4xxp_bottom_half(unsigned long data) * Yuck. It works well, but yuck. */ do { - k = hdlc_tx_frame(&b4->spans[fifo - 8]); + k = hdlc_tx_frame(&b4->spans[fifo - fifo_low]); } while (k); } else { if (printk_ratelimit()) @@ -2314,7 +2491,7 @@ static void b4xxp_bottom_half(unsigned long data) } if (b & V_IRQ_FIFOx_RX) { - if (fifo >=8 && fifo <= 11) { + if (fifo >= fifo_low && fifo <= fifo_high) { /* dchan fifos */ /* * I have to loop here until hdlc_rx_frame says there are no more frames waiting. * for whatever reason, the HFC will not generate another interrupt if there are @@ -2322,7 +2499,7 @@ static void b4xxp_bottom_half(unsigned long data) * i.e. I get an int when F1 changes, not when F1 != F2. */ do { - k = hdlc_rx_frame(&b4->spans[fifo - 8]); + k = hdlc_rx_frame(&b4->spans[fifo - fifo_low]); } while (k); } else { if (printk_ratelimit()) @@ -2404,8 +2581,8 @@ static int b4xxp_proc_read_one(char *buf, struct b4xxp *b4) sprintf(sBuf, "Card %d, PCI identifier %s, IRQ %d\n", b4->cardno + 1, b4->dev->bus_id, b4->irq); strcat(sBuf,"Tx:\n"); - for (j=0; j<8; j++) { - for (i=0; i<12; i++) { + for (j=0; j<(b4->numspans * 2) ; j++) { /* B Channels */ + for (i=0; i<(b4->numspans * 3) ; i++) { /* All Channels */ chan = b4->spans[i/3].chans[i%3]; sprintf(str, "%02x ", chan->writechunk[j]); strcat(sBuf, str); @@ -2415,8 +2592,8 @@ static int b4xxp_proc_read_one(char *buf, struct b4xxp *b4) } strcat(sBuf, "\nRx:\n"); - for (j=0; j < 8; j++) { - for (i=0; i < 12; i++) { + for (j=0; j < (b4->numspans * 2); j++) { /* B Channels */ + for (i=0; i < (b4->numspans * 3); i++) { /* All Channels */ chan = b4->spans[i / 3].chans[i % 3]; sprintf(str, "%02x%c", chan->readchunk[j], (i == 11) ? '\n' : ' '); strcat(sBuf, str); @@ -2424,7 +2601,7 @@ static int b4xxp_proc_read_one(char *buf, struct b4xxp *b4) } strcat(sBuf, "\nPort states:\n"); - for (i=0; i < 4; i++) { + for (i=0; i < b4->numspans; i++) { int state; char *x; struct b4xxp_span *s = &b4->spans[i]; @@ -2524,7 +2701,7 @@ static int __devinit b4xx_probe(struct pci_dev *pdev, const struct pci_device_id /* card found, enabled and main struct allocated. Fill it out. */ b4->magic = WCB4XXP_MAGIC; b4->variety = dt->desc; - + b4->card_type = dt->card_type; b4->pdev = pdev; b4->dev = &pdev->dev; pci_set_drvdata(pdev, b4); @@ -2538,7 +2715,7 @@ static int __devinit b4xx_probe(struct pci_dev *pdev, const struct pci_device_id spin_lock_init(&b4->fifolock); x = b4xxp_getreg8(b4, R_CHIP_ID); - if (x != 0xc0) { /* wrong chip? */ + if ((x != 0xc0) && (x != 0x80)) { /* wrong chip? */ dev_err(&pdev->dev, "Unknown/unsupported controller detected (R_CHIP_ID = 0x%02x)\n", x); goto err_out_free_mem; } @@ -2553,7 +2730,7 @@ static int __devinit b4xx_probe(struct pci_dev *pdev, const struct pci_device_id */ /* TODO: determine whether this is a 2, 4 or 8 port card */ - b4->numspans = 4; + b4->numspans = dt->ports; b4->syncspan = -1; /* sync span is unknown */ if (b4->numspans > MAX_SPANS_PER_CARD) { dev_err(b4->dev, "Driver does not know how to handle a %d span card!\n", b4->numspans); @@ -2706,7 +2883,17 @@ static void __devexit b4xxp_remove(struct pci_dev *pdev) static struct pci_device_id b4xx_ids[] __devinitdata = { { 0xd161, 0xb410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wcb4xxp }, - { 0, } + { 0x1397, 0x16b8, 0x1397, 0xe552, 0, 0, (unsigned long)&hfc8s }, + { 0x1397, 0x08b4, 0x1397, 0xb520, 0, 0, (unsigned long)&hfc4s }, + { 0x1397, 0x08b4, 0x1397, 0xb556, 0, 0, (unsigned long)&hfc2s }, + { 0x1397, 0x08b4, 0x1397, 0xe884, 0, 0, (unsigned long)&hfc2s_OV }, + { 0x1397, 0x08b4, 0x1397, 0xe888, 0, 0, (unsigned long)&hfc4s_OV }, + { 0x1397, 0x16b8, 0x1397, 0xe998, 0, 0, (unsigned long)&hfc8s_OV }, + { 0x1397, 0x08b4, 0x1397, 0xb566, 0, 0, (unsigned long)&hfc2s_BN }, + { 0x1397, 0x08b4, 0x1397, 0xb560, 0, 0, (unsigned long)&hfc4s_BN }, + { 0x1397, 0x16b8, 0x1397, 0xb562, 0, 0, (unsigned long)&hfc8s_BN }, + {0, } + }; static struct pci_driver b4xx_driver = { @@ -2766,7 +2953,7 @@ MODULE_PARM_DESC(timer_1_ms, "NT: msec to wait for link activation, TE: unused." MODULE_PARM_DESC(timer_3_ms, "TE: msec to wait for link activation, NT: unused."); MODULE_AUTHOR("Digium Incorporated <support@digium.com>"); -MODULE_DESCRIPTION("B410P quad-port BRI module driver."); +MODULE_DESCRIPTION("B410P & Similars multi-port BRI module driver."); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, b4xx_ids); diff --git a/drivers/dahdi/wcb4xxp/wcb4xxp.h b/drivers/dahdi/wcb4xxp/wcb4xxp.h index 4542a2d..2807349 100644 --- a/drivers/dahdi/wcb4xxp/wcb4xxp.h +++ b/drivers/dahdi/wcb4xxp/wcb4xxp.h @@ -378,7 +378,7 @@ #define HFC_T3 2 #define WCB4XXP_MAGIC 0xb410c0de -#define MAX_SPANS_PER_CARD 4 +#define MAX_SPANS_PER_CARD 8 #define WCB4XXP_CHANNELS_PER_SPAN 3 /* 2 B-channels and 1 D-Channel for each BRI span */ #define WCB4XXP_HDLC_BUF_LEN 32 /* arbitrary, just the max # of byts we will send to DAHDI per call */ @@ -415,6 +415,19 @@ struct b4xxp_span { struct dahdi_chan _chans[WCB4XXP_CHANNELS_PER_SPAN]; /* Backing memory */ }; +enum cards_ids { /* Cards ==> Brand & Model */ + B410P = 0, /* Digium B410P */ + B200P_OV, /* OpenVox B200P */ + B400P_OV, /* OpenVox B400P */ + B800P_OV, /* OpenVox B800P */ + DUOBRI, /* HFC-2S Junghanns.NET duoBRI PCI */ + QUADBRI, /* HFC-4S Junghanns.NET quadBRI PCI */ + OCTOBRI, /* HFC-8S Junghanns.NET octoBRI PCI */ + BN2S0, /* BeroNet BN2S0 */ + BN4S0, /* Beronet BN4S0 */ + BN8S0 /* BeroNet BN8S0 */ + }; + /* This structure exists one per card */ struct b4xxp { unsigned magic; /* magic value to make sure we're looking at our struct */ @@ -453,6 +466,7 @@ struct b4xxp { struct b4xxp_span spans[MAX_SPANS_PER_CARD]; /* Individual spans */ int order; /* Order */ int flags; /* Device flags */ + enum cards_ids card_type; /* For LED handling mostly */ int master; /* Are we master */ int ledreg; /* copy of the LED Register */ unsigned int gpio; diff --git a/drivers/dahdi/wcfxo.c b/drivers/dahdi/wcfxo.c index 7f8a210..d5bb658 100644 --- a/drivers/dahdi/wcfxo.c +++ b/drivers/dahdi/wcfxo.c @@ -673,6 +673,16 @@ static int wcfxo_hardware_init(struct wcfxo *wc) /* Hardware stuff */ /* Reset PCI Interface chip and registers */ outb(0x0e, wc->ioaddr + WC_CNTL); + + /* Set all to outputs except AUX 4, which is an input */ + outb(0xef, wc->ioaddr + WC_AUXC); + + /* Reset the DAA (DAA uses AUX5 for reset) */ + outb(0x00, wc->ioaddr + WC_AUXD); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1 + HZ / 800); + + /* Set hook state to on hook & un-reset the DAA */ if (wc->flags & FLAG_RESET_ON_AUX5) { /* Set hook state to on hook for when we switch. Make sure reset is high */ @@ -681,8 +691,6 @@ static int wcfxo_hardware_init(struct wcfxo *wc) /* Set hook state to on hook for when we switch */ outb(0x24, wc->ioaddr + WC_AUXD); } - /* Set all to outputs except AUX 4, which is an input */ - outb(0xef, wc->ioaddr + WC_AUXC); /* Back to normal, with automatic DMA wrap around */ outb(0x01, wc->ioaddr + WC_CNTL); diff --git a/drivers/dahdi/wct4xxp/base.c b/drivers/dahdi/wct4xxp/base.c index 63a3bfc..acbdd73 100644 --- a/drivers/dahdi/wct4xxp/base.c +++ b/drivers/dahdi/wct4xxp/base.c @@ -167,7 +167,10 @@ static int t1e1override = -1; //0xFF; // -1 = jumper; 0xFF = E1 static int j1mode = 0; static int sigmode = FRMR_MODE_NO_ADDR_CMP; static int loopback = 0; -static int alarmdebounce = 0; +static int alarmdebounce = 2500; /* LOF/LFA def to 2.5s AT&T TR54016*/ +static int losalarmdebounce = 2500;/* LOS def to 2.5s AT&T TR54016*/ +static int aisalarmdebounce = 2500;/* AIS(blue) def to 2.5s AT&T TR54016*/ +static int yelalarmdebounce = 500;/* RAI(yellow) def to 0.5s AT&T devguide */ #ifdef VPM_SUPPORT static int vpmsupport = 1; /* If set to auto, vpmdtmfsupport is enabled for VPM400M and disabled for VPM450M */ @@ -250,6 +253,9 @@ struct t4_span { int redalarms; int notclear; int alarmcount; + int losalarmcount; + int aisalarmcount; + int yelalarmcount; int spanflags; int syncpos; #ifdef SUPPORT_GEN1 @@ -1913,7 +1919,7 @@ static void __t4_configure_t1(struct t4 *wc, int unit, int lineconfig, int txlev else mytxlevel = txlevel - 4; fmr1 = 0x9c; /* FMR1: Mode 1, T1 mode, CRC on for ESF, 8.192 Mhz system data rate, no XAIS */ - fmr2 = 0x22; /* FMR2: no payload loopback, auto send yellow alarm */ + fmr2 = 0x20; /* FMR2: no payload loopback, don't auto yellow */ if (loopback) fmr2 |= 0x4; fmr4 = 0x0c; /* FMR4: Lose sync on 2 out of 5 framing bits, auto resync */ @@ -2555,15 +2561,51 @@ static void t4_check_alarms(struct t4 *wc, int span) alarms |= DAHDI_ALARM_NOTOPEN; } - if (c & 0xa0) { - if (ts->alarmcount >= alarmdebounce) + if (c & 0x20) { /* LOF/LFA */ + if (ts->alarmcount >= alarmdebounce) alarms |= DAHDI_ALARM_RED; - else + else { + if (unlikely(debug && !ts->alarmcount)) { + /* starting to debounce LOF/LFA */ + printk(KERN_INFO "wct%dxxp: LOF/LFA detected " + "on span %d but debouncing for %d ms\n", + wc->numspans, span + 1, alarmdebounce); + } ts->alarmcount++; + } } else ts->alarmcount = 0; - if (c & 0x4) - alarms |= DAHDI_ALARM_BLUE; + + if (c & 0x80) { /* LOS */ + if (ts->losalarmcount >= losalarmdebounce) + alarms |= DAHDI_ALARM_RED; + else { + if (unlikely(debug && !ts->losalarmcount)) { + /* starting to debounce LOS */ + printk(KERN_INFO "wct%dxxp: LOS detected on " + "span %d but debouncing for %d ms\n", + wc->numspans, span + 1, losalarmdebounce); + } + ts->losalarmcount++; + } + } else + ts->losalarmcount = 0; + + if (c & 0x40) { /* AIS */ + if (ts->aisalarmcount >= aisalarmdebounce) + alarms |= DAHDI_ALARM_BLUE; + else { + if (unlikely(debug && !ts->aisalarmcount)) { + /* starting to debounce AIS */ + printk(KERN_INFO "wct%dxxp: AIS detected on " + "span %d but debouncing for %d ms\n", + wc->numspans, span + 1, aisalarmdebounce); + } + ts->aisalarmcount++; + } + } else + ts->aisalarmcount = 0; + if (((!ts->span.alarms) && alarms) || (ts->span.alarms && (!alarms))) @@ -2579,7 +2621,8 @@ static void t4_check_alarms(struct t4 *wc, int span) if (alarms && !(ts->spanflags & FLAG_SENDINGYELLOW)) { unsigned char fmr4; #if 1 - printk(KERN_INFO "wct%dxxp: Setting yellow alarm on span %d\n", wc->numspans, span + 1); + printk(KERN_INFO "wct%dxxp: Setting yellow alarm on span %d\n", + wc->numspans, span + 1); #endif /* We manually do yellow alarm to handle RECOVER and NOTOPEN, otherwise it's auto anyway */ fmr4 = __t4_framer_in(wc, span, 0x20); @@ -2588,7 +2631,8 @@ static void t4_check_alarms(struct t4 *wc, int span) } else if ((!alarms) && (ts->spanflags & FLAG_SENDINGYELLOW)) { unsigned char fmr4; #if 1 - printk(KERN_INFO "wct%dxxp: Clearing yellow alarm on span %d\n", wc->numspans, span + 1); + printk(KERN_INFO "wct%dxxp: Clearing yellow alarm on span %d\n", + wc->numspans, span + 1); #endif /* We manually do yellow alarm to handle RECOVER */ fmr4 = __t4_framer_in(wc, span, 0x20); @@ -2598,8 +2642,23 @@ static void t4_check_alarms(struct t4 *wc, int span) /* Re-check the timing source when we enter/leave alarm, not withstanding yellow alarm */ - if (c & 0x10) - alarms |= DAHDI_ALARM_YELLOW; + if (c & 0x10) { /* receiving yellow (RAI) */ + if (ts->yelalarmcount >= yelalarmdebounce) + alarms |= DAHDI_ALARM_YELLOW; + else { + if (unlikely(debug && !ts->yelalarmcount)) { + /* starting to debounce AIS */ + printk(KERN_INFO "wct%dxxp: yelllow (RAI) " + "detected on span %d but debouncing " + "for %d ms\n", + wc->numspans, span + 1, + yelalarmdebounce); + } + ts->yelalarmcount++; + } + } else + ts->yelalarmcount = 0; + if (ts->span.mainttimer || ts->span.maintstat) alarms |= DAHDI_ALARM_LOOPBACK; ts->span.alarms = alarms; @@ -2615,8 +2674,11 @@ static void t4_do_counters(struct t4 *wc) int docheck=0; spin_lock(&wc->reglock); - if (ts->loopupcnt || ts->loopdowncnt) + if (ts->loopupcnt || ts->loopdowncnt || ts->alarmcount + || ts->losalarmcount || ts->aisalarmcount + || ts->yelalarmcount) docheck++; + if (ts->alarmtimer) { if (!--ts->alarmtimer) { docheck++; @@ -2639,7 +2701,7 @@ static inline void __handle_leds(struct t4 *wc) for (x=0;x<wc->numspans;x++) { struct t4_span *ts = wc->tspans[x]; if (ts->span.flags & DAHDI_FLAG_RUNNING) { - if (ts->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE)) { + if ((ts->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE)) || ts->losalarmcount) { #ifdef FANCY_ALARM if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) { __t4_set_led(wc, x, WC_RED); @@ -3814,6 +3876,7 @@ static void __devexit t4_remove_one(struct pci_dev *pdev) { struct t4 *wc = pci_get_drvdata(pdev); struct dahdi_span *span; + int basesize; int i; if (!wc) { @@ -3829,6 +3892,10 @@ static void __devexit t4_remove_one(struct pci_dev *pdev) wc->vpm450m = NULL; /* Unregister spans */ + basesize = DAHDI_MAX_CHUNKSIZE * 32 * 4; + if (!(wc->tspans[0]->spanflags & FLAG_2NDGEN)) + basesize = basesize * 2; + for (i = 0; i < wc->numspans; ++i) { span = &wc->tspans[i]->span; if (test_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags)) @@ -3849,7 +3916,9 @@ static void __devexit t4_remove_one(struct pci_dev *pdev) pci_release_regions(pdev); /* Immediately free resources */ - pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 32 * 4, (void *)wc->writechunk, wc->writedma); + + pci_free_consistent(pdev, basesize * 2, + (void *)wc->writechunk, wc->writedma); order_index[wc->order]--; @@ -3931,6 +4000,9 @@ module_param(noburst, int, 0600); module_param(timingcable, int, 0600); module_param(t1e1override, int, 0600); module_param(alarmdebounce, int, 0600); +module_param(losalarmdebounce, int, 0600); +module_param(aisalarmdebounce, int, 0600); +module_param(yelalarmdebounce, int, 0600); module_param(j1mode, int, 0600); module_param(sigmode, int, 0600); #ifdef VPM_SUPPORT diff --git a/drivers/dahdi/wct4xxp/wct4xxp-diag.c b/drivers/dahdi/wct4xxp/wct4xxp-diag.c index 9e81c23..6554193 100644 --- a/drivers/dahdi/wct4xxp/wct4xxp-diag.c +++ b/drivers/dahdi/wct4xxp/wct4xxp-diag.c @@ -393,7 +393,7 @@ int main(int argc, char *argv[]) if (*(argv[1]) == '/') dahdi_copy_string(fn, argv[1], sizeof(fn)); else - snprintf(fn, sizeof(fn), "/dev/zap/%d", atoi(argv[1])); + snprintf(fn, sizeof(fn), "/dev/dahdi/%d", atoi(argv[1])); fd = open(fn, O_RDWR); if (fd <0) { fprintf(stderr, "Unable to open '%s': %s\n", fn, strerror(errno)); diff --git a/drivers/dahdi/wctc4xxp/base.c b/drivers/dahdi/wctc4xxp/base.c index b3e3f07..7cbea1d 100644 --- a/drivers/dahdi/wctc4xxp/base.c +++ b/drivers/dahdi/wctc4xxp/base.c @@ -3400,19 +3400,16 @@ wctc4xxp_add_to_device_list(struct wcdte *wc) struct wctc4xxp_desc { const char *short_name; const char *long_name; - int flags; }; static struct wctc4xxp_desc wctc400p = { .short_name = "tc400b", .long_name = "Wildcard TC400P+TC400M", - .flags = 0, }; static struct wctc4xxp_desc wctce400 = { .short_name = "tce400", .long_name = "Wildcard TCE400+TC400M", - .flags = 0, }; static int __devinit @@ -3475,7 +3472,7 @@ wctc4xxp_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) init_waitqueue_head(&wc->waitq); - if (pci_set_dma_mask(wc->pdev, DMA_32BIT_MASK)) { + if (pci_set_dma_mask(wc->pdev, DMA_BIT_MASK(32))) { release_region(wc->iobase, 0xff); DTE_PRINTK(WARNING, "No suitable DMA available.\n"); return -EIO; diff --git a/drivers/dahdi/wctdm.c b/drivers/dahdi/wctdm.c index 84f074f..eb0516a 100644 --- a/drivers/dahdi/wctdm.c +++ b/drivers/dahdi/wctdm.c @@ -441,10 +441,14 @@ static void wctdm_restart_dma(struct wctdm *wc); static inline void __write_8bits(struct wctdm *wc, unsigned char bits) { - /* Drop chip select */ +/* Out BIT_CS --\________________________________/---- */ +/* Out BIT_SCLK ---\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/------ */ +/* Out BIT_SDI ---\___/---\___/---\___/---\___/-------- */ +/* Data Bit 7 6 5 4 3 2 1 0 */ +/* Data written 0 1 0 1 0 1 0 1 */ + int x; - wc->ios |= BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Drop chip select */ wc->ios &= ~BIT_CS; outb(wc->ios, wc->ioaddr + WC_AUXD); for (x=0;x<8;x++) { @@ -455,6 +459,7 @@ static inline void __write_8bits(struct wctdm *wc, unsigned char bits) wc->ios &= ~BIT_SDI; wc->ios &= ~BIT_SCLK; outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again and repeat */ wc->ios |= BIT_SCLK; outb(wc->ios, wc->ioaddr + WC_AUXD); @@ -463,7 +468,6 @@ static inline void __write_8bits(struct wctdm *wc, unsigned char bits) /* Finally raise CS back high again */ wc->ios |= BIT_CS; outb(wc->ios, wc->ioaddr + WC_AUXD); - } static inline void __reset_spi(struct wctdm *wc) @@ -493,31 +497,36 @@ static inline void __reset_spi(struct wctdm *wc) static inline unsigned char __read_8bits(struct wctdm *wc) { +/* Out BIT_CS --\________________________________________/----*/ +/* Out BIT_SCLK ---\_/--\_/--\_/--\_/--\_/--\_/--\_/--\_/-------*/ +/* In BIT_SDO ????/1111\0000/1111\0000/1111\0000/1111\0000/???*/ +/* Data bit 7 6 5 4 3 2 1 0 */ +/* Data Read 1 0 1 0 1 0 1 0 */ + +/* Note: Clock High time is 2x Low time, due to input read */ + unsigned char res=0, c; int x; - wc->ios |= BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); /* Drop chip select */ wc->ios &= ~BIT_CS; outb(wc->ios, wc->ioaddr + WC_AUXD); for (x=0;x<8;x++) { res <<= 1; - /* Get SCLK */ + /* Drop SCLK */ wc->ios &= ~BIT_SCLK; outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Read back the value */ c = inb(wc->ioaddr + WC_AUXR); if (c & BIT_SDO) res |= 1; - /* Now raise SCLK high again */ - wc->ios |= BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); } /* Finally raise CS back high again */ wc->ios |= BIT_CS; outb(wc->ios, wc->ioaddr + WC_AUXD); - wc->ios &= ~BIT_SCLK; - outb(wc->ios, wc->ioaddr + WC_AUXD); /* And return our result */ return res; @@ -727,22 +736,23 @@ static int wctdm_proslic_verify_indirect_regs(struct wctdm *wc, int card) static inline void wctdm_proslic_recheck_sanity(struct wctdm *wc, int card) { + struct fxs *const fxs = &wc->mod[card].fxs; int res; /* Check loopback */ res = wc->reg1shadow[card]; - if (!res && (res != wc->mod[card].fxs.lasttxhook)) { + if (!res && (res != fxs->lasttxhook)) { res = wctdm_getreg(wc, card, 8); if (res) { printk(KERN_NOTICE "Ouch, part reset, quickly restoring reality (%d)\n", card); wctdm_init_proslic(wc, card, 1, 0, 1); } else { - if (wc->mod[card].fxs.palarms++ < MAX_ALARMS) { + if (fxs->palarms++ < MAX_ALARMS) { printk(KERN_NOTICE "Power alarm on module %d, resetting!\n", card + 1); - if (wc->mod[card].fxs.lasttxhook == 4) - wc->mod[card].fxs.lasttxhook = 1; - wctdm_setreg(wc, card, 64, wc->mod[card].fxs.lasttxhook); + if (fxs->lasttxhook == SLIC_LF_RINGING) + fxs->lasttxhook = SLIC_LF_ACTIVE_FWD; + wctdm_setreg(wc, card, 64, fxs->lasttxhook); } else { - if (wc->mod[card].fxs.palarms == MAX_ALARMS) + if (fxs->palarms == MAX_ALARMS) printk(KERN_NOTICE "Too many power alarms on card %d, NOT resetting!\n", card + 1); } } @@ -842,6 +852,21 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) } } + if (unlikely(DAHDI_RXSIG_INITIAL == wc->chans[card]->rxhooksig)) { + /* + * dahdi-base will set DAHDI_RXSIG_INITIAL after a + * DAHDI_STARTUP or DAHDI_CHANCONFIG ioctl so that new events + * will be queued on the channel with the current received + * hook state. Channels that use robbed-bit signalling always + * report the current received state via the dahdi_rbsbits + * call. Since we only call dahdi_hooksig when we've detected + * a change to report, let's forget our current state in order + * to force us to report it again via dahdi_hooksig. + * + */ + fxo->battery = BATTERY_UNKNOWN; + } + if (abs(b) < battthresh) { /* possible existing states: battery lost, no debounce timer @@ -966,6 +991,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card) { + struct fxs *const fxs = &wc->mod[card].fxs; char res; int hook; @@ -974,47 +1000,63 @@ static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card) res = wc->reg0shadow[card]; hook = (res & 1); - if (hook != wc->mod[card].fxs.lastrxhook) { + if (hook != fxs->lastrxhook) { /* Reset the debounce (must be multiple of 4ms) */ - wc->mod[card].fxs.debounce = 8 * (4 * 8); + fxs->debounce = 8 * (4 * 8); #if 0 - printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n", card, hook, wc->mod[card].fxs.debounce); + printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n", + card, hook, fxs->debounce); #endif } else { - if (wc->mod[card].fxs.debounce > 0) { - wc->mod[card].fxs.debounce-= 16 * DAHDI_CHUNKSIZE; + if (fxs->debounce > 0) { + fxs->debounce -= 16 * DAHDI_CHUNKSIZE; #if 0 - printk(KERN_DEBUG "Sustaining hook %d, %d\n", hook, wc->mod[card].fxs.debounce); + printk(KERN_DEBUG "Sustaining hook %d, %d\n", + hook, fxs->debounce); #endif - if (!wc->mod[card].fxs.debounce) { + if (!fxs->debounce) { #if 0 printk(KERN_DEBUG "Counted down debounce, newhook: %d...\n", hook); #endif - wc->mod[card].fxs.debouncehook = hook; + fxs->debouncehook = hook; } - if (!wc->mod[card].fxs.oldrxhook && wc->mod[card].fxs.debouncehook) { + if (!fxs->oldrxhook && fxs->debouncehook) { /* Off hook */ #if 1 if (debug) #endif printk(KERN_DEBUG "wctdm: Card %d Going off hook\n", card); + + switch (fxs->lasttxhook) { + case SLIC_LF_RINGING: + case SLIC_LF_OHTRAN_FWD: + case SLIC_LF_OHTRAN_REV: + /* just detected OffHook, during + * Ringing or OnHookTransfer */ + fxs->idletxhookstate = + POLARITY_XOR(card) ? + SLIC_LF_ACTIVE_REV : + SLIC_LF_ACTIVE_FWD; + break; + } + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); if (robust) wctdm_init_proslic(wc, card, 1, 0, 1); - wc->mod[card].fxs.oldrxhook = 1; + fxs->oldrxhook = 1; - } else if (wc->mod[card].fxs.oldrxhook && !wc->mod[card].fxs.debouncehook) { + } else if (fxs->oldrxhook && !fxs->debouncehook) { /* On hook */ #if 1 if (debug) #endif printk(KERN_DEBUG "wctdm: Card %d Going on hook\n", card); dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); - wc->mod[card].fxs.oldrxhook = 0; + fxs->oldrxhook = 0; } } } - wc->mod[card].fxs.lastrxhook = hook; + fxs->lastrxhook = hook; } DAHDI_IRQ_HANDLER(wctdm_interrupt) @@ -1046,27 +1088,40 @@ DAHDI_IRQ_HANDLER(wctdm_interrupt) for (x=0;x<4;x++) { if (wc->cardflag & (1 << x) && (wc->modtype[x] == MOD_TYPE_FXS)) { - if (wc->mod[x].fxs.lasttxhook == 0x4) { + struct fxs *const fxs = &wc->mod[x].fxs; + if (fxs->lasttxhook == SLIC_LF_RINGING) { /* RINGing, prepare for OHT */ - wc->mod[x].fxs.ohttimer = OHT_TIMER << 3; + fxs->ohttimer = OHT_TIMER << 3; /* logical XOR 3 variables module parameter 'reversepolarity', global reverse all FXS lines. ioctl channel variable fxs 'reversepolarity', Line Reversal Alert Signal if required. ioctl channel variable fxs 'vmwi_lrev', VMWI pending. */ - wc->mod[x].fxs.idletxhookstate = POLARITY_XOR(x) ? 0x6 : 0x2;/* OHT mode when idle */ - } else { - if (wc->mod[x].fxs.ohttimer) { - wc->mod[x].fxs.ohttimer-= DAHDI_CHUNKSIZE; - if (!wc->mod[x].fxs.ohttimer) { - wc->mod[x].fxs.idletxhookstate = POLARITY_XOR(x) ? 0x5 : 0x1; /* Switch to Active : Reverse Forward */ - if ((wc->mod[x].fxs.lasttxhook == 0x2) || (wc->mod[x].fxs.lasttxhook == 0x6)) { - /* Apply the change if appropriate */ - wc->mod[x].fxs.lasttxhook = POLARITY_XOR(x) ? 0x5 : 0x1; - wctdm_setreg(wc, x, 64, wc->mod[x].fxs.lasttxhook); - } - } + + /* OHT mode when idle */ + fxs->idletxhookstate = POLARITY_XOR(x) ? + SLIC_LF_OHTRAN_REV : + SLIC_LF_OHTRAN_FWD; + } else if (fxs->ohttimer) { + fxs->ohttimer -= DAHDI_CHUNKSIZE; + if (fxs->ohttimer) + continue; + + /* Switch to Active : Reverse Forward */ + fxs->idletxhookstate = POLARITY_XOR(x) ? + SLIC_LF_ACTIVE_REV : + SLIC_LF_ACTIVE_FWD; + + if ((fxs->lasttxhook == SLIC_LF_OHTRAN_FWD) || + (fxs->lasttxhook == SLIC_LF_OHTRAN_REV)) { + /* Apply the change if appropriate */ + fxs->lasttxhook = POLARITY_XOR(x) ? + SLIC_LF_ACTIVE_REV : + SLIC_LF_ACTIVE_FWD; + + wctdm_setreg(wc, x, 64, + fxs->lasttxhook); } } } @@ -1454,6 +1509,7 @@ static int wctdm_set_hwgain(struct wctdm *wc, int card, __s32 gain, __u32 tx) static int set_vmwi(struct wctdm * wc, int chan_idx) { + struct fxs *const fxs = &wc->mod[chan_idx].fxs; if (wc->mod[chan_idx].fxs.vmwi_active_messages){ wc->mod[chan_idx].fxs.vmwi_lrev = (wc->mod[chan_idx].fxs.vmwisetting.vmwi_type & DAHDI_VMWI_LREV)?1:0; wc->mod[chan_idx].fxs.vmwi_hvdc = (wc->mod[chan_idx].fxs.vmwisetting.vmwi_type & DAHDI_VMWI_HVDC)?1:0; @@ -1474,21 +1530,21 @@ static int set_vmwi(struct wctdm * wc, int chan_idx) ); } if (POLARITY_XOR(chan_idx)) { - wc->mod[chan_idx].fxs.idletxhookstate |= 0x4; + wc->mod[chan_idx].fxs.idletxhookstate |= SLIC_LF_REVMASK; /* Do not set while currently ringing or open */ - if (wc->mod[chan_idx].fxs.lasttxhook != 0x04 && - wc->mod[chan_idx ].fxs.lasttxhook != 0x00) { - wc->mod[chan_idx ].fxs.lasttxhook |= 0x4; - wctdm_setreg(wc, chan_idx, 64, wc->mod[chan_idx].fxs.lasttxhook); - } + if ((fxs->lasttxhook != SLIC_LF_RINGING) && + (fxs->lasttxhook != SLIC_LF_OPEN)) { + fxs->lasttxhook |= SLIC_LF_REVMASK; + wctdm_setreg(wc, chan_idx, 64, fxs->lasttxhook); + } } else { - wc->mod[chan_idx].fxs.idletxhookstate &= ~0x04; + wc->mod[chan_idx].fxs.idletxhookstate &= ~SLIC_LF_REVMASK; /* Do not set while currently ringing or open */ - if (wc->mod[chan_idx].fxs.lasttxhook != 0x04 && - wc->mod[chan_idx].fxs.lasttxhook != 0x00) { - wc->mod[chan_idx].fxs.lasttxhook &= ~0x04; + if ((fxs->lasttxhook != SLIC_LF_RINGING) && + (fxs->lasttxhook != SLIC_LF_OPEN)) { + wc->mod[chan_idx].fxs.lasttxhook &= ~SLIC_LF_REVMASK; wctdm_setreg(wc, chan_idx, 64, wc->mod[chan_idx].fxs.lasttxhook); - } + } } return 0; } @@ -1619,9 +1675,9 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, /* By default, don't send on hook */ if (!reversepolarity != !wc->mod[card].fxs.reversepolarity) - wc->mod[card].fxs.idletxhookstate = 5; + wc->mod[card].fxs.idletxhookstate = SLIC_LF_ACTIVE_REV; else - wc->mod[card].fxs.idletxhookstate = 1; + wc->mod[card].fxs.idletxhookstate = SLIC_LF_ACTIVE_FWD; if (sane) { /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */ @@ -1850,6 +1906,7 @@ static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long struct wctdm_echo_coefs echoregs; struct dahdi_hwgain hwgain; struct wctdm *wc = chan->pvt; + struct fxs *const fxs = &wc->mod[chan->chanpos - 1].fxs; int x; switch (cmd) { case DAHDI_ONHOOKTRANSFER: @@ -1858,10 +1915,18 @@ static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long if (get_user(x, (__user int *) data)) return -EFAULT; wc->mod[chan->chanpos - 1].fxs.ohttimer = x << 3; - wc->mod[chan->chanpos - 1].fxs.idletxhookstate = POLARITY_XOR(chan->chanpos - 1) ? 0x6 : 0x2; /* OHT mode when idle */ - if (wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x1 || wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x5) { + + /* Active mode when idle */ + fxs->idletxhookstate = POLARITY_XOR(chan->chanpos - 1) ? + SLIC_LF_ACTIVE_REV : + SLIC_LF_ACTIVE_FWD; + + if ((fxs->lasttxhook == SLIC_LF_ACTIVE_FWD) || + (fxs->lasttxhook == SLIC_LF_ACTIVE_REV)) { /* Apply the change if appropriate */ - wc->mod[chan->chanpos - 1].fxs.lasttxhook = POLARITY_XOR(chan->chanpos - 1) ? 0x6 : 0x2; + fxs->lasttxhook = POLARITY_XOR(chan->chanpos - 1) ? + SLIC_LF_OHTRAN_REV : + SLIC_LF_OHTRAN_FWD; wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); } break; @@ -1871,15 +1936,15 @@ static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) return -EINVAL; /* Can't change polarity while ringing or when open */ - if ((wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x04) || - (wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x00)) + if ((fxs->lasttxhook == SLIC_LF_RINGING) || + (fxs->lasttxhook == SLIC_LF_OPEN)) return -EINVAL; wc->mod[chan->chanpos - 1].fxs.reversepolarity = x; if ( POLARITY_XOR(chan->chanpos - 1) ) - wc->mod[chan->chanpos - 1].fxs.lasttxhook |= 0x04; + fxs->lasttxhook |= SLIC_LF_REVMASK; else - wc->mod[chan->chanpos - 1].fxs.lasttxhook &= ~0x04; + fxs->lasttxhook &= ~SLIC_LF_REVMASK; wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); break; case DAHDI_VMWI_CONFIG: @@ -2010,7 +2075,11 @@ static int wctdm_close(struct dahdi_chan *chan) wc->usecount--; module_put(THIS_MODULE); if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { - wc->mod[chan->chanpos - 1].fxs.idletxhookstate = POLARITY_XOR(chan->chanpos - 1) ? 0x5 : 0x1; + int idlehookstate; + idlehookstate = POLARITY_XOR(chan->chanpos - 1) ? + SLIC_LF_ACTIVE_REV : + SLIC_LF_ACTIVE_FWD; + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = idlehookstate; } /* If we're dead, release us now */ if (!wc->usecount && wc->dead) @@ -2038,45 +2107,51 @@ static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) printk(KERN_NOTICE "wcfxo: Can't set tx state to %d\n", txsig); } } else { + struct fxs *const fxs = &wc->mod[chan->chanpos - 1].fxs; switch(txsig) { case DAHDI_TXSIG_ONHOOK: switch(chan->sig) { case DAHDI_SIG_FXOKS: case DAHDI_SIG_FXOLS: - wc->mod[chan->chanpos-1].fxs.lasttxhook = (wc->mod[chan->chanpos-1].fxs.vmwi_hvac ? 4 : wc->mod[chan->chanpos-1].fxs.idletxhookstate); + fxs->lasttxhook = fxs->vmwi_hvac ? + SLIC_LF_RINGING : + fxs->idletxhookstate; break; case DAHDI_SIG_EM: - wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; + fxs->lasttxhook = fxs->idletxhookstate; break; case DAHDI_SIG_FXOGS: - wc->mod[chan->chanpos-1].fxs.lasttxhook = 3; + fxs->lasttxhook = SLIC_LF_TIP_OPEN; break; } break; case DAHDI_TXSIG_OFFHOOK: switch(chan->sig) { case DAHDI_SIG_EM: - wc->mod[chan->chanpos-1].fxs.lasttxhook = 5; + fxs->lasttxhook = SLIC_LF_ACTIVE_REV; break; default: - wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; + fxs->lasttxhook = fxs->idletxhookstate; break; } break; case DAHDI_TXSIG_START: - wc->mod[chan->chanpos-1].fxs.lasttxhook = 4; + fxs->lasttxhook = SLIC_LF_RINGING; break; case DAHDI_TXSIG_KEWL: - wc->mod[chan->chanpos-1].fxs.lasttxhook = 0; + fxs->lasttxhook = SLIC_LF_OPEN; break; default: printk(KERN_NOTICE "wctdm: Can't set tx state to %d\n", txsig); } - if (debug) - printk(KERN_DEBUG "Setting FXS hook state to %d (%02x)\n", txsig, wc->mod[chan->chanpos-1].fxs.lasttxhook); + if (debug) { + printk(KERN_DEBUG + "Setting FXS hook state to %d (%02x)\n", + txsig, fxs->lasttxhook); + } #if 1 - wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos-1].fxs.lasttxhook); + wctdm_setreg(wc, chan->chanpos - 1, 64, fxs->lasttxhook); #endif } return 0; diff --git a/drivers/dahdi/wctdm24xxp/base.c b/drivers/dahdi/wctdm24xxp/base.c index 7f68473..1386e5f 100644 --- a/drivers/dahdi/wctdm24xxp/base.c +++ b/drivers/dahdi/wctdm24xxp/base.c @@ -162,17 +162,17 @@ static int ectrans[4] = { 0, 1, 3, 2 }; #include "fxo_modes.h" struct wctdm_desc { - char *name; - int flags; - int ports; + const char *name; + const int flags; + const int ports; }; -static struct wctdm_desc wctdm2400 = { "Wildcard TDM2400P", 0, 24 }; -static struct wctdm_desc wctdm800 = { "Wildcard TDM800P", 0, 8 }; -static struct wctdm_desc wctdm410 = { "Wildcard TDM410P", 0, 4 }; -static struct wctdm_desc wcaex2400 = { "Wildcard AEX2400", FLAG_EXPRESS, 24 }; -static struct wctdm_desc wcaex800 = { "Wildcard AEX800", FLAG_EXPRESS, 8 }; -static struct wctdm_desc wcaex410 = { "Wildcard AEX410", FLAG_EXPRESS, 4 }; +static const struct wctdm_desc wctdm2400 = { "Wildcard TDM2400P", 0, 24 }; +static const struct wctdm_desc wctdm800 = { "Wildcard TDM800P", 0, 8 }; +static const struct wctdm_desc wctdm410 = { "Wildcard TDM410P", 0, 4 }; +static const struct wctdm_desc wcaex2400 = { "Wildcard AEX2400", FLAG_EXPRESS, 24 }; +static const struct wctdm_desc wcaex800 = { "Wildcard AEX800", FLAG_EXPRESS, 8 }; +static const struct wctdm_desc wcaex410 = { "Wildcard AEX410", FLAG_EXPRESS, 4 }; static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 }; @@ -462,9 +462,8 @@ static int config_vpmadt032(struct vpmadt032 *vpm) } -static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, unsigned char *writechunk, int whichframe) +static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, u8 *writechunk, int whichframe) { - unsigned long flags; struct vpmadt032_cmd *curcmd = NULL; struct vpmadt032 *vpmadt032 = wc->vpmadt032; int x; @@ -473,8 +472,6 @@ static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, unsigned char *writec /* Skip audio */ writechunk += 24; - spin_lock_irqsave(&wc->reglock, flags); - if (test_bit(VPM150M_SPIRESET, &vpmadt032->control) || test_bit(VPM150M_HPIRESET, &vpmadt032->control)) { if (debug & DEBUG_ECHOCAN) printk(KERN_INFO "HW Resetting VPMADT032...\n"); @@ -489,7 +486,6 @@ static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, unsigned char *writec writechunk[CMD_BYTE(x, 1, 0)] = 0; writechunk[CMD_BYTE(x, 2, 0)] = 0x00; } - spin_unlock_irqrestore(&wc->reglock, flags); return; } @@ -548,7 +544,6 @@ static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, unsigned char *writec writechunk[CMD_BYTE(27, 2, 0)] = 0; } } else if (test_and_clear_bit(VPM150M_SWRESET, &vpmadt032->control)) { - printk(KERN_INFO "Booting VPMADT032\n"); for (x = 24; x < 28; x++) { if (x == 24) writechunk[CMD_BYTE(x, 0, 0)] = (0x8 << 4); @@ -577,11 +572,9 @@ static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, unsigned char *writec if (test_bit(VPM150M_ACTIVE, &vpmadt032->control) && !whichframe && !(wc->intcount % 100)) { schedule_work(&vpmadt032->work); } - - spin_unlock_irqrestore(&wc->reglock, flags); } -static inline void cmd_dequeue(struct wctdm *wc, volatile unsigned char *writechunk, int card, int pos) +static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int card, int pos) { unsigned long flags; unsigned int curcmd=0; @@ -624,7 +617,7 @@ static inline void cmd_dequeue(struct wctdm *wc, volatile unsigned char *writech if (!curcmd) { /* If nothing else, use filler */ if (wc->modtype[card] == MOD_TYPE_FXS) - curcmd = CMD_RD(64); + curcmd = CMD_RD(LINE_STATE); else if (wc->modtype[card] == MOD_TYPE_FXO) curcmd = CMD_RD(12); else if (wc->modtype[card] == MOD_TYPE_QRV) @@ -752,7 +745,7 @@ static inline void cmd_decipher_vpmadt032(struct wctdm *wc, unsigned char *readc #endif } -static inline void cmd_decipher(struct wctdm *wc, volatile unsigned char *readchunk, int card) +static inline void cmd_decipher(struct wctdm *wc, u8 *readchunk, int card) { unsigned long flags; unsigned char ident; @@ -819,7 +812,7 @@ static inline void cmd_checkisr(struct wctdm *wc, int card) #ifdef PAQ_DEBUG wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(19); /* Transistor interrupts */ #else - wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(64); /* Battery mode */ + wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(LINE_STATE); #endif } else if (wc->modtype[card] == MOD_TYPE_FXO) { wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(29); /* Battery */ @@ -850,7 +843,7 @@ static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char *writechun } if (likely(wc->initialized)) { - if (y < wc->type) + if (y < wc->desc->ports) writechunk[y] = wc->chans[y]->writechunk[x]; } cmd_dequeue(wc, writechunk, y, x); @@ -878,7 +871,7 @@ static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char *writechun writechunk[EFRAME_SIZE] = wc->ctlreg; writechunk[EFRAME_SIZE + 1] = wc->txident++; - if ((wc->type == 4) && ((wc->ctlreg & 0x10) || (wc->modtype[NUM_CARDS] == MOD_TYPE_NONE))) { + if ((wc->desc->ports == 4) && ((wc->ctlreg & 0x10) || (wc->modtype[NUM_CARDS] == MOD_TYPE_NONE))) { writechunk[EFRAME_SIZE + 2] = 0; for (y = 0; y < 4; y++) { if (wc->modtype[y] == MOD_TYPE_NONE) @@ -1032,11 +1025,8 @@ static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char *readchunk) } } for (y=0;y < wc->cards;y++) { - if (likely(wc->initialized)) { - if (y < wc->type) { - wc->chans[y]->readchunk[x] = readchunk[y]; - } - } + if (likely(wc->initialized) && (y < wc->desc->ports)) + wc->chans[y]->readchunk[x] = readchunk[y]; cmd_decipher(wc, readchunk, y); } if (wc->vpm100) { @@ -1050,7 +1040,7 @@ static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char *readchunk) } /* XXX We're wasting 8 taps. We should get closer :( */ if (likely(wc->initialized)) { - for (x=0;x<wc->type;x++) { + for (x = 0; x < wc->desc->ports; x++) { if (wc->cardflag & (1 << x)) dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); } @@ -1088,7 +1078,7 @@ static int wait_access(struct wctdm *wc, int card) static unsigned char translate_3215(unsigned char address) { int x; - for (x=0;x<sizeof(indirect_regs)/sizeof(indirect_regs[0]);x++) { + for (x = 0; x < ARRAY_SIZE(indirect_regs); x++) { if (indirect_regs[x].address == address) { address = indirect_regs[x].altaddr; break; @@ -1145,7 +1135,7 @@ static int wctdm_proslic_init_indirect_regs(struct wctdm *wc, int card) { unsigned char i; - for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++) + for (i = 0; i < ARRAY_SIZE(indirect_regs); i++) { if(wctdm_proslic_setreg_indirect(wc, card, indirect_regs[i].address,indirect_regs[i].initial)) return -1; @@ -1160,7 +1150,7 @@ static int wctdm_proslic_verify_indirect_regs(struct wctdm *wc, int card) unsigned short i, initial; int j; - for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++) + for (i = 0; i < ARRAY_SIZE(indirect_regs); i++) { if((j = wctdm_proslic_getreg_indirect(wc, card, (unsigned char) indirect_regs[i].address)) < 0) { printk(KERN_NOTICE "Failed to read indirect register %d\n", i); @@ -1188,56 +1178,55 @@ static int wctdm_proslic_verify_indirect_regs(struct wctdm *wc, int card) static inline void wctdm_proslic_recheck_sanity(struct wctdm *wc, int card) { + struct fxs *const fxs = &wc->mods[card].fxs; int res; + unsigned long flags; #ifdef PAQ_DEBUG res = wc->cmdq[card].isrshadow[1]; res &= ~0x3; if (res) { wc->cmdq[card].isrshadow[1]=0; - wc->mods[card].fxs.palarms++; - if (wc->mods[card].fxs.palarms < MAX_ALARMS) { + fxs->palarms++; + if (fxs->palarms < MAX_ALARMS) { printk(KERN_NOTICE "Power alarm (%02x) on module %d, resetting!\n", res, card + 1); - if (wc->mods[card].fxs.lasttxhook == 4) { - wc->mods[card].fxs.lasttxhook = POLARITY_XOR(card) ? 0x15 : 0x11; - } wc->sethook[card] = CMD_WR(19, res); -#if 0 - wc->sethook[card] = CMD_WR(64, wc->mods[card].fxs.lasttxhook); -#endif - - /* wctdm_setreg_intr(wc, card, 64, wc->mods[card].fxs.lasttxhook); */ /* Update shadow register to avoid extra power alarms until next read */ wc->cmdq[card].isrshadow[1] = 0; } else { - if (wc->mods[card].fxs.palarms == MAX_ALARMS) + if (fxs->palarms == MAX_ALARMS) printk(KERN_NOTICE "Too many power alarms on card %d, NOT resetting!\n", card + 1); } } #else + spin_lock_irqsave(&fxs->lasttxhooklock, flags); res = wc->cmdq[card].isrshadow[1]; /* This makes sure the lasthook was put in reg 64 the linefeed reg */ - if (((res & 0x0f) | 0x10) == wc->mods[card].fxs.lasttxhook) - wc->mods[card].fxs.lasttxhook &= 0x0f; + if (((res & SLIC_LF_SETMASK) | SLIC_LF_OPPENDING) == fxs->lasttxhook) + fxs->lasttxhook &= SLIC_LF_SETMASK; res = !res && /* reg 64 has to be zero at last isr read */ - !(wc->mods[card].fxs.lasttxhook & 0x10 ) && /* not a transition */ - wc->mods[card].fxs.lasttxhook; /* not an intended zero */ + !(fxs->lasttxhook & SLIC_LF_OPPENDING) && /* not a transition */ + fxs->lasttxhook; /* not an intended zero */ + spin_unlock_irqrestore(&fxs->lasttxhooklock, flags); if (res) { - wc->mods[card].fxs.palarms++; - if (wc->mods[card].fxs.palarms < MAX_ALARMS) { + fxs->palarms++; + if (fxs->palarms < MAX_ALARMS) { printk(KERN_NOTICE "Power alarm on module %d, resetting!\n", card + 1); - if (wc->mods[card].fxs.lasttxhook == 4) { - wc->mods[card].fxs.lasttxhook = POLARITY_XOR(card) ? 0x15 : 0x11;; + spin_lock_irqsave(&fxs->lasttxhooklock, flags); + if (fxs->lasttxhook == SLIC_LF_RINGING) { + fxs->lasttxhook = POLARITY_XOR(card) ? + SLIC_LF_ACTIVE_REV : + SLIC_LF_ACTIVE_FWD;; } - wc->mods[card].fxs.lasttxhook |= 0x10; - wc->sethook[card] = CMD_WR(64, wc->mods[card].fxs.lasttxhook); + fxs->lasttxhook |= SLIC_LF_OPPENDING; + wc->sethook[card] = CMD_WR(LINE_STATE, fxs->lasttxhook); + spin_unlock_irqrestore(&fxs->lasttxhooklock, flags); - /* wctdm_setreg_intr(wc, card, 64, wc->mods[card].fxs.lasttxhook); */ /* Update shadow register to avoid extra power alarms until next read */ - wc->cmdq[card].isrshadow[1] = wc->mods[card].fxs.lasttxhook; + wc->cmdq[card].isrshadow[1] = fxs->lasttxhook; } else { - if (wc->mods[card].fxs.palarms == MAX_ALARMS) + if (fxs->palarms == MAX_ALARMS) printk(KERN_NOTICE "Too many power alarms on card %d, NOT resetting!\n", card + 1); } } @@ -1404,6 +1393,21 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) } } + if (unlikely(DAHDI_RXSIG_INITIAL == wc->chans[card]->rxhooksig)) { + /* + * dahdi-base will set DAHDI_RXSIG_INITIAL after a + * DAHDI_STARTUP or DAHDI_CHANCONFIG ioctl so that new events + * will be queued on the channel with the current received + * hook state. Channels that use robbed-bit signalling always + * report the current received state via the dahdi_rbsbits + * call. Since we only call dahdi_hooksig when we've detected + * a change to report, let's forget our current state in order + * to force us to report it again via dahdi_hooksig. + * + */ + fxo->battery = BATTERY_UNKNOWN; + } + if (abs_voltage < battthresh) { /* possible existing states: battery lost, no debounce timer @@ -1568,8 +1572,40 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) #undef MS_PER_CHECK_HOOK } -static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card) +static void wctdm_fxs_off_hook(struct wctdm *wc, const int card) +{ + struct fxs *const fxs = &wc->mods[card].fxs; + + if (debug & DEBUG_CARD) + printk(KERN_DEBUG "wctdm: Card %d Going off hook\n", card); + switch (fxs->lasttxhook) { + case SLIC_LF_RINGING: /* Ringing */ + case SLIC_LF_OHTRAN_FWD: /* Forward On Hook Transfer */ + case SLIC_LF_OHTRAN_REV: /* Reverse On Hook Transfer */ + /* just detected OffHook, during Ringing or OnHookTransfer */ + fxs->idletxhookstate = POLARITY_XOR(card) ? + SLIC_LF_ACTIVE_REV : + SLIC_LF_ACTIVE_FWD; + break; + } + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + if (robust) + wctdm_init_proslic(wc, card, 1, 0, 1); + fxs->oldrxhook = 1; +} + +static void wctdm_fxs_on_hook(struct wctdm *wc, const int card) +{ + struct fxs *const fxs = &wc->mods[card].fxs; + if (debug & DEBUG_CARD) + printk(KERN_DEBUG "wctdm: Card %d Going on hook\n", card); + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); + fxs->oldrxhook = 0; +} + +static inline void wctdm_proslic_check_hook(struct wctdm *wc, const int card) { + struct fxs *const fxs = &wc->mods[card].fxs; char res; int hook; @@ -1579,46 +1615,36 @@ static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card) res = wc->cmdq[card].isrshadow[0]; /* Hook state */ hook = (res & 1); - if (hook != wc->mods[card].fxs.lastrxhook) { + if (hook != fxs->lastrxhook) { /* Reset the debounce (must be multiple of 4ms) */ - wc->mods[card].fxs.debounce = 8 * (4 * 8); + fxs->debounce = 8 * (4 * 8); #if 0 - printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n", card, hook, wc->mods[card].fxs.debounce); + printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n", + card, hook, fxs->debounce); #endif } else { - if (wc->mods[card].fxs.debounce > 0) { - wc->mods[card].fxs.debounce-= 4 * DAHDI_CHUNKSIZE; + if (fxs->debounce > 0) { + fxs->debounce -= 4 * DAHDI_CHUNKSIZE; #if 0 - printk(KERN_DEBUG "Sustaining hook %d, %d\n", hook, wc->mods[card].fxs.debounce); + printk(KERN_DEBUG "Sustaining hook %d, %d\n", + hook, fxs->debounce); #endif - if (!wc->mods[card].fxs.debounce) { + if (!fxs->debounce) { #if 0 printk(KERN_DEBUG "Counted down debounce, newhook: %d...\n", hook); #endif - wc->mods[card].fxs.debouncehook = hook; - } - if (!wc->mods[card].fxs.oldrxhook && wc->mods[card].fxs.debouncehook) { - /* Off hook */ - if (debug & DEBUG_CARD) - printk(KERN_DEBUG "wctdm: Card %d Going off hook\n", card); - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); - if (robust) - wctdm_init_proslic(wc, card, 1, 0, 1); - wc->mods[card].fxs.oldrxhook = 1; - - } else if (wc->mods[card].fxs.oldrxhook && !wc->mods[card].fxs.debouncehook) { - /* On hook */ - if (debug & DEBUG_CARD) - printk(KERN_DEBUG "wctdm: Card %d Going on hook\n", card); - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); - wc->mods[card].fxs.oldrxhook = 0; + fxs->debouncehook = hook; } + + if (!fxs->oldrxhook && fxs->debouncehook) + wctdm_fxs_off_hook(wc, card); + else if (fxs->oldrxhook && !fxs->debouncehook) + wctdm_fxs_on_hook(wc, card); } } - wc->mods[card].fxs.lastrxhook = hook; + fxs->lastrxhook = hook; } - static inline void wctdm_vpm_check(struct wctdm *wc, int x) { if (wc->cmdq[x].isrshadow[0]) { @@ -1706,7 +1732,50 @@ static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec unit, channel); wctdm_vpm_out(wc, unit, channel, 0x01); } else if (wc->vpmadt032) { - vpmadt032_echocan_free(wc->vpmadt032, chan, ec); + vpmadt032_echocan_free(wc->vpmadt032, chan->chanpos - 1, ec); + } +} + +static void wctdm_isr_misc_fxs(struct wctdm *wc, int card) +{ + struct fxs *const fxs = &wc->mods[card].fxs; + unsigned long flags; + + if (!(wc->intcount % 10000)) { + /* Accept an alarm once per 10 seconds */ + if (fxs->palarms) + fxs->palarms--; + } + wctdm_proslic_check_hook(wc, card); + if (!(wc->intcount & 0xfc)) + wctdm_proslic_recheck_sanity(wc, card); + if (SLIC_LF_RINGING == fxs->lasttxhook) { + /* RINGing, prepare for OHT */ + fxs->ohttimer = OHT_TIMER << 3; + /* OHT mode when idle */ + fxs->idletxhookstate = POLARITY_XOR(card) ? SLIC_LF_OHTRAN_REV : + SLIC_LF_OHTRAN_FWD; + } else if (fxs->ohttimer) { + fxs->ohttimer -= DAHDI_CHUNKSIZE; + if (fxs->ohttimer) + return; + + /* Switch to active */ + fxs->idletxhookstate = POLARITY_XOR(card) ? SLIC_LF_ACTIVE_REV : + SLIC_LF_ACTIVE_FWD; + spin_lock_irqsave(&fxs->lasttxhooklock, flags); + if (SLIC_LF_OHTRAN_FWD == fxs->lasttxhook) { + /* Apply the change if appropriate */ + fxs->lasttxhook = SLIC_LF_OPPENDING | SLIC_LF_ACTIVE_FWD; + /* Data enqueued here */ + wc->sethook[card] = CMD_WR(LINE_STATE, fxs->lasttxhook); + } else if (SLIC_LF_OHTRAN_REV == fxs->lasttxhook) { + /* Apply the change if appropriate */ + fxs->lasttxhook = SLIC_LF_OPPENDING | SLIC_LF_ACTIVE_REV; + /* Data enqueued here */ + wc->sethook[card] = CMD_WR(LINE_STATE, fxs->lasttxhook); + } + spin_unlock_irqrestore(&fxs->lasttxhooklock, flags); } } @@ -1721,37 +1790,7 @@ static inline void wctdm_isr_misc(struct wctdm *wc) for (x=0;x<wc->cards;x++) { if (wc->cardflag & (1 << x)) { if (wc->modtype[x] == MOD_TYPE_FXS) { - if (!(wc->intcount % 10000)) { - /* Accept an alarm once per 10 seconds */ - if (wc->mods[x].fxs.palarms) - wc->mods[x].fxs.palarms--; - } - wctdm_proslic_check_hook(wc, x); - if (!(wc->intcount & 0xfc)) - wctdm_proslic_recheck_sanity(wc, x); - if (wc->mods[x].fxs.lasttxhook == 0x4) { - /* RINGing, prepare for OHT */ - wc->mods[x].fxs.ohttimer = OHT_TIMER << 3; - wc->mods[x].fxs.idletxhookstate = POLARITY_XOR(x) ? 0x6 : 0x2; /* OHT mode when idle */ - } else { - if (wc->mods[x].fxs.ohttimer) { - wc->mods[x].fxs.ohttimer-= DAHDI_CHUNKSIZE; - if (!wc->mods[x].fxs.ohttimer) { - wc->mods[x].fxs.idletxhookstate = POLARITY_XOR(x) ? 0x5 : 0x1; /* Switch to active */ - if (wc->mods[x].fxs.lasttxhook == 0x2) { - /* Apply the change if appropriate */ - wc->mods[x].fxs.lasttxhook = 0x11; - wc->sethook[x] = CMD_WR(64, wc->mods[x].fxs.lasttxhook); /* Data enqueued here */ - /* wctdm_setreg_intr(wc, x, 64, wc->mods[x].fxs.lasttxhook); */ - } else if (wc->mods[x].fxs.lasttxhook == 0x6) { - /* Apply the change if appropriate */ - wc->mods[x].fxs.lasttxhook = 0x15; - wc->sethook[x] = CMD_WR(64, wc->mods[x].fxs.lasttxhook); /* Data enqueued here */ - /* wctdm_setreg_intr(wc, x, 64, wc->mods[x].fxs.lasttxhook); */ - } - } - } - } + wctdm_isr_misc_fxs(wc, x); } else if (wc->modtype[x] == MOD_TYPE_FXO) { wctdm_voicedaa_check_hook(wc, x); } else if (wc->modtype[x] == MOD_TYPE_QRV) { @@ -1867,7 +1906,7 @@ static int wctdm_proslic_powerleak_test(struct wctdm *wc, int card) unsigned char vbat; /* Turn off linefeed */ - wctdm_setreg(wc, card, 64, 0); + wctdm_setreg(wc, card, LINE_STATE, 0); /* Power down */ wctdm_setreg(wc, card, 14, 0x10); @@ -2119,42 +2158,68 @@ static int wctdm_set_hwgain(struct wctdm *wc, int card, __s32 gain, __u32 tx) return 0; } +static int set_lasttxhook_interruptible(struct fxs *fxs, unsigned newval, int * psethook) +{ + int res = 0; + unsigned long flags; + int timeout = 0; + + do { + spin_lock_irqsave(&fxs->lasttxhooklock, flags); + if (SLIC_LF_OPPENDING & fxs->lasttxhook) { + spin_unlock_irqrestore(&fxs->lasttxhooklock, flags); + if (timeout++ > 100) + return -1; + msleep(1); + } else { + fxs->lasttxhook = (newval & SLIC_LF_SETMASK) | SLIC_LF_OPPENDING; + *psethook = CMD_WR(LINE_STATE, fxs->lasttxhook); + spin_unlock_irqrestore(&fxs->lasttxhooklock, flags); + break; + } + } while (1); + + return res; +} + +/* Must be called from within an interruptible context */ static int set_vmwi(struct wctdm *wc, int chan_idx) { int x; + struct fxs *const fxs = &wc->mods[chan_idx].fxs; + /* Presently only supports line reversal MWI */ - if (wc->mods[chan_idx].fxs.vmwi_active_messages && wc->mods[chan_idx].fxs.vmwisetting.vmwi_type & DAHDI_VMWI_LREV){ - wc->mods[chan_idx].fxs.vmwi_linereverse = 1; - } else { - wc->mods[chan_idx].fxs.vmwi_linereverse = 0; - } + if ((fxs->vmwi_active_messages) && + (fxs->vmwisetting.vmwi_type & DAHDI_VMWI_LREV)) + fxs->vmwi_linereverse = 1; + else + fxs->vmwi_linereverse = 0; + /* Set line polarity for new VMWI state */ if (POLARITY_XOR(chan_idx)) { - wc->mods[chan_idx].fxs.idletxhookstate |= 0x14; + fxs->idletxhookstate |= SLIC_LF_OPPENDING | SLIC_LF_REVMASK; /* Do not set while currently ringing or open */ - if (wc->mods[chan_idx].fxs.lasttxhook != 0x04 && - wc->mods[chan_idx].fxs.lasttxhook != 0x00) { - wc->mods[chan_idx].fxs.lasttxhook |= 0x14; - wc->sethook[chan_idx] = CMD_WR(64, wc->mods[chan_idx].fxs.lasttxhook); - } + if (((fxs->lasttxhook & SLIC_LF_SETMASK) != SLIC_LF_RINGING) && + ((fxs->lasttxhook & SLIC_LF_SETMASK) != SLIC_LF_OPEN)) { + x = fxs->lasttxhook; + x |= SLIC_LF_REVMASK; + set_lasttxhook_interruptible(fxs, x, &wc->sethook[chan_idx]); + } } else { - wc->mods[chan_idx].fxs.idletxhookstate &= ~0x04; + fxs->idletxhookstate &= ~SLIC_LF_REVMASK; /* Do not set while currently ringing or open */ - if (wc->mods[chan_idx].fxs.lasttxhook != 0x04 && - wc->mods[chan_idx].fxs.lasttxhook != 0x00) { - x = wc->mods[chan_idx].fxs.lasttxhook; - x &= ~0x04; - x |= 0x10; - wc->mods[chan_idx].fxs.lasttxhook = x; - wc->sethook[chan_idx] = CMD_WR(64, wc->mods[chan_idx].fxs.lasttxhook); + if (((fxs->lasttxhook & SLIC_LF_SETMASK) != SLIC_LF_RINGING) && + ((fxs->lasttxhook & SLIC_LF_SETMASK) != SLIC_LF_OPEN)) { + x = fxs->lasttxhook; + x &= ~SLIC_LF_REVMASK; + set_lasttxhook_interruptible(fxs, x, &wc->sethook[chan_idx]); } } if (debug) { - printk(KERN_DEBUG "Setting VMWI on channel %d, messages=%d, lrev=%d\n", - chan_idx, - wc->mods[chan_idx].fxs.vmwi_active_messages, - wc->mods[chan_idx].fxs.vmwi_linereverse - ); + printk(KERN_DEBUG + "Setting VMWI on channel %d, messages=%d, lrev=%d\n", + chan_idx, fxs->vmwi_active_messages, + fxs->vmwi_linereverse); } return 0; } @@ -2282,11 +2347,10 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, /* By default, don't send on hook */ if (!reversepolarity != !wc->mods[card].fxs.reversepolarity) { - wc->mods[card].fxs.idletxhookstate = 5; + wc->mods[card].fxs.idletxhookstate = SLIC_LF_ACTIVE_REV; } else { - wc->mods[card].fxs.idletxhookstate = 1; + wc->mods[card].fxs.idletxhookstate = SLIC_LF_ACTIVE_FWD; } - wc->mods[card].fxs.lasttxhook = 0x10; if (sane) { /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */ @@ -2329,6 +2393,7 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, } if (!fast) { + spin_lock_init(&wc->mods[card].fxs.lasttxhooklock); /* Check for power leaks */ if (wctdm_proslic_powerleak_test(wc, card)) { @@ -2504,7 +2569,7 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, printk(KERN_DEBUG "DEBUG: fxstxgain:%s fxsrxgain:%s\n",((wctdm_getreg(wc, card, 9)/8) == 1)?"3.5":(((wctdm_getreg(wc,card,9)/4) == 1)?"-3.5":"0.0"),((wctdm_getreg(wc, card, 9)/2) == 1)?"3.5":((wctdm_getreg(wc,card,9)%2)?"-3.5":"0.0")); wc->mods[card].fxs.lasttxhook = wc->mods[card].fxs.idletxhookstate; - wctdm_setreg(wc, card, 64, wc->mods[card].fxs.lasttxhook); + wctdm_setreg(wc, card, LINE_STATE, wc->mods[card].fxs.lasttxhook); return 0; } @@ -2653,6 +2718,7 @@ static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long struct dahdi_radio_stat s; struct dahdi_radio_param p; } stack; + struct fxs *const fxs = &wc->mods[chan->chanpos - 1].fxs; switch (cmd) { case DAHDI_ONHOOKTRANSFER: @@ -2660,19 +2726,27 @@ static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long return -EINVAL; if (get_user(x, (__user int *) data)) return -EFAULT; - wc->mods[chan->chanpos - 1].fxs.ohttimer = x << 3; - wc->mods[chan->chanpos - 1].fxs.idletxhookstate = 0x2; /* OHT mode when idle */ - if (wc->mods[chan->chanpos - 1].fxs.lasttxhook == 0x1 || wc->mods[chan->chanpos - 1].fxs.lasttxhook == 0x5) { - /* Apply the change if appropriate */ - wc->mods[chan->chanpos - 1].fxs.lasttxhook = POLARITY_XOR(chan->chanpos -1) ? 0x16 : 0x12; - wc->sethook[chan->chanpos - 1] = CMD_WR(64, wc->mods[chan->chanpos - 1].fxs.lasttxhook); - /* wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mods[chan->chanpos - 1].fxs.lasttxhook); */ + fxs->ohttimer = x << 3; + + /* Active mode when idle */ + fxs->idletxhookstate = POLARITY_XOR(chan->chanpos - 1) ? + SLIC_LF_ACTIVE_REV : + SLIC_LF_ACTIVE_FWD; + + if (((fxs->lasttxhook & SLIC_LF_SETMASK) == SLIC_LF_ACTIVE_FWD) || + ((fxs->lasttxhook & SLIC_LF_SETMASK) == SLIC_LF_ACTIVE_REV)) { + + set_lasttxhook_interruptible(fxs, POLARITY_XOR(chan->chanpos - 1) + ? SLIC_LF_OHTRAN_REV : SLIC_LF_OHTRAN_FWD , + &wc->sethook[chan->chanpos - 1]); } break; case DAHDI_VMWI_CONFIG: if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) return -EINVAL; - if (copy_from_user(&(wc->mods[chan->chanpos - 1].fxs.vmwisetting), (__user void *) data, sizeof(wc->mods[chan->chanpos - 1].fxs.vmwisetting))) + if (copy_from_user(&(fxs->vmwisetting), + (__user void *)data, + sizeof(fxs->vmwisetting))) return -EFAULT; set_vmwi(wc, chan->chanpos - 1); break; @@ -2683,7 +2757,7 @@ static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long return -EFAULT; if (0 > x) return -EFAULT; - wc->mods[chan->chanpos - 1].fxs.vmwi_active_messages = x; + fxs->vmwi_active_messages = x; set_vmwi(wc, chan->chanpos - 1); break; case WCTDM_GET_STATS: @@ -2728,9 +2802,10 @@ static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long wctdm_proslic_setreg_indirect(wc, chan->chanpos - 1, regop.reg, regop.val); } else { regop.val &= 0xff; - if (regop.reg == 64) - wc->mods[chan->chanpos-1].fxs.lasttxhook = (regop.val & 0x0f) | 0x10; - + if (regop.reg == LINE_STATE) { + /* Set feedback register to indicate the new state that is being set */ + fxs->lasttxhook = (regop.val & 0x0f) | SLIC_LF_OPPENDING; + } printk(KERN_INFO "Setting direct %d to %04x on %d\n", regop.reg, regop.val, chan->chanpos); wctdm_setreg(wc, chan->chanpos - 1, regop.reg, regop.val); } @@ -2783,25 +2858,23 @@ static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) return -EINVAL; /* Can't change polarity while ringing or when open */ - if ((wc->mods[chan->chanpos -1 ].fxs.lasttxhook == 0x04) || - (wc->mods[chan->chanpos -1 ].fxs.lasttxhook == 0x00)) + if (((fxs->lasttxhook & SLIC_LF_SETMASK) == SLIC_LF_RINGING) || + ((fxs->lasttxhook & SLIC_LF_SETMASK) == SLIC_LF_OPEN)) return -EINVAL; - if (x) { - wc->mods[chan->chanpos -1 ].fxs.reversepolarity = 1; - } else { - wc->mods[chan->chanpos -1 ].fxs.reversepolarity = 0; - } + + fxs->reversepolarity = (x) ? 1 : 0; + if (POLARITY_XOR(chan->chanpos -1)) { - wc->mods[chan->chanpos -1 ].fxs.idletxhookstate |= 0x14; - wc->mods[chan->chanpos -1 ].fxs.lasttxhook |= 0x14; + fxs->idletxhookstate |= SLIC_LF_REVMASK; + x = fxs->lasttxhook; + x |= SLIC_LF_REVMASK; + set_lasttxhook_interruptible(fxs, x, &wc->sethook[chan->chanpos - 1]); } else { - wc->mods[chan->chanpos -1 ].fxs.idletxhookstate &= ~0x04; - x = wc->mods[chan->chanpos -1 ].fxs.lasttxhook; - x &= ~0x04; - x |= 0x10; - wc->mods[chan->chanpos -1 ].fxs.lasttxhook = x; + fxs->idletxhookstate &= ~SLIC_LF_REVMASK; + x = fxs->lasttxhook; + x &= ~SLIC_LF_REVMASK; + set_lasttxhook_interruptible(fxs, x, &wc->sethook[chan->chanpos - 1]); } - wc->sethook[chan->chanpos - 1] = CMD_WR(64, wc->mods[chan->chanpos - 1].fxs.lasttxhook); break; case DAHDI_RADIO_GETPARAM: if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_QRV) @@ -2988,7 +3061,9 @@ static int wctdm_close(struct dahdi_chan *chan) module_put(THIS_MODULE); for (x=0;x<wc->cards;x++) { if (wc->modtype[x] == MOD_TYPE_FXS) { - wc->mods[x].fxs.idletxhookstate = POLARITY_XOR(x) ? 5 : 1; + wc->mods[x].fxs.idletxhookstate = + POLARITY_XOR(x) ? SLIC_LF_ACTIVE_REV : + SLIC_LF_ACTIVE_FWD; } if (wc->modtype[x] == MOD_TYPE_QRV) { @@ -3017,6 +3092,7 @@ static int wctdm_close(struct dahdi_chan *chan) static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) { struct wctdm *wc = chan->pvt; + int reg=0,qrvcard; if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_QRV) { qrvcard = (chan->chanpos - 1) & 0xfc; @@ -3052,21 +3128,26 @@ static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) default: printk(KERN_NOTICE "wctdm24xxp: Can't set tx state to %d\n", txsig); } - } else { + } else { /* Else this is an fxs port */ + unsigned long flags; + struct fxs *const fxs = &wc->mods[chan->chanpos - 1].fxs; + spin_lock_irqsave(&fxs->lasttxhooklock, flags); switch(txsig) { case DAHDI_TXSIG_ONHOOK: switch(chan->sig) { case DAHDI_SIG_EM: case DAHDI_SIG_FXOKS: case DAHDI_SIG_FXOLS: - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 0x10 | - wc->mods[chan->chanpos - 1].fxs.idletxhookstate; + fxs->lasttxhook = SLIC_LF_OPPENDING | + fxs->idletxhookstate; break; case DAHDI_SIG_FXOGS: if (POLARITY_XOR(chan->chanpos -1)) { - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 0x17; + fxs->lasttxhook = SLIC_LF_OPPENDING | + SLIC_LF_RING_OPEN; } else { - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 0x13; + fxs->lasttxhook = SLIC_LF_OPPENDING | + SLIC_LF_TIP_OPEN; } break; } @@ -3075,32 +3156,32 @@ static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) switch(chan->sig) { case DAHDI_SIG_EM: if (POLARITY_XOR(chan->chanpos -1)) { - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 0x11; + fxs->lasttxhook = SLIC_LF_OPPENDING | + SLIC_LF_ACTIVE_FWD; } else { - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 0x15; + fxs->lasttxhook = SLIC_LF_OPPENDING | + SLIC_LF_ACTIVE_REV; } break; default: - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 0x10 | - wc->mods[chan->chanpos - 1].fxs.idletxhookstate; + fxs->lasttxhook = SLIC_LF_OPPENDING | + fxs->idletxhookstate; break; } break; case DAHDI_TXSIG_START: - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 0x14; + fxs->lasttxhook = SLIC_LF_OPPENDING | SLIC_LF_RINGING; break; case DAHDI_TXSIG_KEWL: - wc->mods[chan->chanpos - 1].fxs.lasttxhook = 0x10; + fxs->lasttxhook = SLIC_LF_OPPENDING | SLIC_LF_OPEN; break; default: printk(KERN_NOTICE "wctdm24xxp: Can't set tx state to %d\n", txsig); } + wc->sethook[chan->chanpos - 1] = CMD_WR(LINE_STATE, fxs->lasttxhook); + spin_unlock_irqrestore(&fxs->lasttxhooklock, flags); if (debug & DEBUG_CARD) printk(KERN_DEBUG "Setting FXS hook state to %d (%02x)\n", txsig, reg); - - - wc->sethook[chan->chanpos - 1] = CMD_WR(64, wc->mods[chan->chanpos - 1].fxs.lasttxhook); - /* wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mods[chan->chanpos - 1].fxs.lasttxhook); */ } return 0; } @@ -3208,12 +3289,14 @@ static int wctdm_initialize(struct wctdm *wc) /* DAHDI stuff */ sprintf(wc->span.name, "WCTDM/%d", wc->pos); - snprintf(wc->span.desc, sizeof(wc->span.desc) - 1, "%s Board %d", wc->variety, wc->pos + 1); + snprintf(wc->span.desc, sizeof(wc->span.desc) - 1, + "%s Board %d", wc->desc->name, wc->pos + 1); snprintf(wc->span.location, sizeof(wc->span.location) - 1, "PCI%s Bus %02d Slot %02d", (wc->flags[0] & FLAG_EXPRESS) ? " Express" : "", pdev->bus->number, PCI_SLOT(pdev->devfn) + 1); wc->span.manufacturer = "Digium"; - strncpy(wc->span.devicetype, wc->variety, sizeof(wc->span.devicetype) - 1); + strncpy(wc->span.devicetype, wc->desc->name, + sizeof(wc->span.devicetype) - 1); if (alawoverride) { printk(KERN_INFO "ALAW override parameter detected. Device will be operating in ALAW\n"); wc->span.deflaw = DAHDI_LAW_ALAW; @@ -3228,7 +3311,7 @@ static int wctdm_initialize(struct wctdm *wc) wc->chans[x]->pvt = wc; } wc->span.chans = wc->chans; - wc->span.channels = wc->type; + wc->span.channels = wc->desc->ports; wc->span.irq = pdev->irq; wc->span.hooksig = wctdm_hooksig; wc->span.open = wctdm_open; @@ -3387,7 +3470,7 @@ static int wctdm_vpm_init(struct wctdm *wc) } printk(KERN_INFO "Enabling VPM100 gain adjustments on any FXO ports found\n"); - for (i = 0; i < wc->type; i++) { + for (i = 0; i < wc->desc->ports; i++) { if (wc->modtype[i] == MOD_TYPE_FXO) { /* Apply negative Tx gain of 4.5db to DAA */ wctdm_setreg(wc, i, 38, 0x14); /* 4db */ @@ -3477,7 +3560,6 @@ static int wctdm_locate_modules(struct wctdm *wc) { int x; unsigned long flags; - unsigned int startinglatency = voicebus_current_latency(wc->vb); wc->ctlreg = 0x00; /* Make sure all units go into daisy chain mode */ @@ -3503,16 +3585,13 @@ static int wctdm_locate_modules(struct wctdm *wc) #endif /* Now that all the cards have been reset, we can stop checking them all if there aren't as many */ spin_lock_irqsave(&wc->reglock, flags); - wc->cards = wc->type; + wc->cards = wc->desc->ports; spin_unlock_irqrestore(&wc->reglock, flags); /* Reset modules */ for (x=0;x<wc->cards;x++) { int sane=0,ret=0,readi=0; retry: - if (voicebus_current_latency(wc->vb) > startinglatency) { - return -EAGAIN; - } /* Init with Auto Calibration */ if (!(ret = wctdm_init_proslic(wc, x, 0, 0, sane))) { wc->cardflag |= (1 << x); @@ -3545,10 +3624,11 @@ retry: wc->cardflag |= 1 << x; printk(KERN_INFO "Port %d: Installed -- QRV DRI card\n",x + 1); } else { - if ((wc->type != 24) && ((x & 0x3) == 1) && !wc->altcs[x]) { - spin_lock_irqsave(&wc->reglock, flags); + if ((wc->desc->ports != 24) && + ((x & 0x3) == 1) && !wc->altcs[x]) { + spin_lock_irqsave(&wc->reglock, flags); wc->altcs[x] = 2; - if (wc->type == 4) { + if (wc->desc->ports == 4) { wc->altcs[x+1] = 3; wc->altcs[x+2] = 3; } @@ -3601,12 +3681,6 @@ retry: wc->vpmadt032->span = &wc->span; get_default_portconfig(&portconfig); res = vpmadt032_init(wc->vpmadt032, wc->vb); - /* In case there was an error while we were loading the VPM module. */ - if (voicebus_current_latency(wc->vb) > startinglatency) { - vpmadt032_free(wc->vpmadt032); - wc->vpmadt032 = NULL; - return -EAGAIN; - } if (res) { vpmadt032_free(wc->vpmadt032); wc->vpmadt032 = NULL; @@ -3636,7 +3710,7 @@ static void free_wc(struct wctdm *wc) { unsigned int x; - for (x = 0; x < sizeof(wc->chans)/sizeof(wc->chans[0]); x++) { + for (x = 0; x < ARRAY_SIZE(wc->chans); x++) { if (wc->chans[x]) { kfree(wc->chans[x]); } @@ -3649,19 +3723,18 @@ static void free_wc(struct wctdm *wc) static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct wctdm *wc; - struct wctdm_desc *d = (struct wctdm_desc *)ent->driver_data; int i; int y; int ret; neonmwi_offlimit_cycles = neonmwi_offlimit /MS_PER_HOOKCHECK; -retry: if (!(wc = kmalloc(sizeof(*wc), GFP_KERNEL))) { return -ENOMEM; } memset(wc, 0, sizeof(*wc)); + wc->desc = (struct wctdm_desc *)ent->driver_data; spin_lock(&ifacelock); /* \todo this is a candidate for removal... */ for (i = 0; i < WC_MAX_IFACES; ++i) { @@ -3689,12 +3762,10 @@ retry: spin_lock_init(&wc->reglock); wc->curcard = -1; wc->cards = NUM_CARDS; - wc->type = d->ports; wc->pos = i; - wc->variety = d->name; wc->txident = 1; for (y=0;y<NUM_CARDS;y++) { - wc->flags[y] = d->flags; + wc->flags[y] = wc->desc->flags; wc->dacssrc[y] = -1; } @@ -3721,29 +3792,14 @@ retry: return -EIO; } + voicebus_lock_latency(wc->vb); - /* Keep track of which device we are */ - pci_set_drvdata(pdev, wc); - - /* Start the hardware processing. */ if (voicebus_start(wc->vb)) { BUG_ON(1); } /* Now track down what modules are installed */ - ret = wctdm_locate_modules(wc); - if (-EAGAIN == ret ) { - /* The voicebus library increased the latency during - * initialization. There is a chance that the hardware is in - * an inconsistent state, so lets increase the default latency - * and start the initialization over. - */ - printk(KERN_NOTICE "%s: Restarting board initialization " \ - "after increasing latency.\n", wc->board_name); - latency = voicebus_current_latency(wc->vb); - wctdm_release(wc); - goto retry; - } + wctdm_locate_modules(wc); /* Final initialization */ wctdm_post_initialize(wc); @@ -3756,10 +3812,11 @@ retry: wc->initialized = 1; - printk(KERN_INFO "Found a Wildcard TDM: %s (%d modules)\n", wc->variety, wc->type); - ret = 0; + printk(KERN_INFO "Found a Wildcard TDM: %s (%d modules)\n", + wc->desc->name, wc->desc->ports); - return ret; + voicebus_unlock_latency(wc->vb); + return 0; } static void wctdm_release(struct wctdm *wc) @@ -3785,7 +3842,7 @@ static void wctdm_release(struct wctdm *wc) static void __devexit wctdm_remove_one(struct pci_dev *pdev) { - struct wctdm *wc = pci_get_drvdata(pdev); + struct wctdm *wc = voicebus_pci_dev_to_context(pdev); struct vpmadt032 *vpm = wc->vpmadt032; if (wc) { @@ -3837,15 +3894,15 @@ static int __init wctdm_init(void) int res; int x; - for (x = 0; x < (sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) { + for (x = 0; x < ARRAY_SIZE(fxo_modes); x++) { if (!strcmp(fxo_modes[x].name, opermode)) break; } - if (x < sizeof(fxo_modes) / sizeof(fxo_modes[0])) { + if (x < ARRAY_SIZE(fxo_modes)) { _opermode = x; } else { printk(KERN_NOTICE "Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode); - for (x = 0; x < sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++) + for (x = 0; x < ARRAY_SIZE(fxo_modes); x++) printk(KERN_NOTICE " %s\n", fxo_modes[x].name); printk(KERN_NOTICE "Note this option is CASE SENSITIVE!\n"); return -ENODEV; diff --git a/drivers/dahdi/wctdm24xxp/wctdm24xxp.h b/drivers/dahdi/wctdm24xxp/wctdm24xxp.h index 3d08bef..bf0c17b 100644 --- a/drivers/dahdi/wctdm24xxp/wctdm24xxp.h +++ b/drivers/dahdi/wctdm24xxp/wctdm24xxp.h @@ -141,7 +141,7 @@ enum battery_state { }; struct wctdm { - char *variety; + const struct wctdm_desc *desc; char board_name[80]; struct dahdi_span span; unsigned char ios; @@ -202,7 +202,17 @@ struct wctdm { int debounce; int ohttimer; int idletxhookstate; /* IDLE changing hook state */ - int lasttxhook; /* Bits 0-3 are written to proslic reg 64, Bit 4 indicates if the last write is pending */ + /* lasttxhook reflects the last value written to the proslic's reg + * 64 (LINEFEED_CONTROL) in bits 0-2. Bit 4 indicates if the last + * write is pending i.e. it is in process of being written to the + * register + * NOTE: in order for this value to actually be written to the + * proslic, the appropriate matching value must be written into the + * sethook variable so that it gets queued and handled by the + * voicebus ISR. + */ + int lasttxhook; + spinlock_t lasttxhooklock; int palarms; struct dahdi_vmwi_info vmwisetting; int vmwi_active_messages; @@ -217,17 +227,9 @@ struct wctdm { /* Set hook */ int sethook[NUM_CARDS + NUM_EC]; int dacssrc[NUM_CARDS]; - /* Type is the maximum number of FXO/FXS ports supported */ - int type; int vpm100; - unsigned long dtmfactive; - unsigned long dtmfmask; - unsigned long dtmfmutemask; - short dtmfenergy[NUM_CARDS]; - short dtmfdigit[NUM_CARDS]; - struct vpmadt032 *vpmadt032; #ifdef FANCY_ECHOCAN int echocanpos; diff --git a/drivers/dahdi/wcte12xp/base.c b/drivers/dahdi/wcte12xp/base.c index 2f16b3a..5e0854f 100644 --- a/drivers/dahdi/wcte12xp/base.c +++ b/drivers/dahdi/wcte12xp/base.c @@ -53,7 +53,10 @@ struct pci_driver te12xp_driver; int debug = 0; static int j1mode = 0; -static int alarmdebounce = 0; +static int alarmdebounce = 2500; /* LOF/LFA def to 2.5s AT&T TR54016*/ +static int losalarmdebounce = 2500; /* LOS def to 2.5s AT&T TR54016*/ +static int aisalarmdebounce = 2500; /* AIS(blue) def to 2.5s AT&T TR54016*/ +static int yelalarmdebounce = 500; /* RAI(yellow) def to 0.5s AT&T devguide */ static int loopback = 0; static int t1e1override = -1; static int unchannelized = 0; @@ -84,13 +87,12 @@ struct t1 *ifaces[WC_MAX_IFACES]; spinlock_t ifacelock = SPIN_LOCK_UNLOCKED; struct t1_desc { - char *name; - int flags; + const char *name; }; -static struct t1_desc te120p = { "Wildcard TE120P", 0 }; -static struct t1_desc te122 = { "Wildcard TE122", 0 }; -static struct t1_desc te121 = { "Wildcard TE121", 0 }; +static const struct t1_desc te120p = {"Wildcard TE120P"}; +static const struct t1_desc te122 = {"Wildcard TE122"}; +static const struct t1_desc te121 = {"Wildcard TE121"}; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) static kmem_cache_t *cmd_cache; @@ -490,7 +492,6 @@ static void cmd_dequeue_vpmadt032(struct t1 *wc, unsigned char *writechunk, int writechunk[CMD_BYTE(4, 2, 1)] = 0; } } else if (test_and_clear_bit(VPM150M_SWRESET, &vpm->control)) { - debug_printk(1, "Booting VPMADT032\n"); for (x = 0; x < 7; x++) { if (0 == x) { writechunk[CMD_BYTE(x, 0, 1)] = (0x8 << 4); @@ -729,7 +730,7 @@ static void t1_configure_t1(struct t1 *wc, int lineconfig, int txlevel) else mytxlevel = txlevel - 4; fmr1 = 0x9e; /* FMR1: Mode 0, T1 mode, CRC on for ESF, 2.048 Mhz system data rate, no XAIS */ - fmr2 = 0x22; /* FMR2: no payload loopback, auto send yellow alarm */ + fmr2 = 0x20; /* FMR2: no payload loopback, don't auto yellow alarm */ if (loopback) fmr2 |= 0x4; @@ -1188,7 +1189,7 @@ static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec if (!wc->vpmadt032) return; - vpmadt032_echocan_free(wc->vpmadt032, chan, ec); + vpmadt032_echocan_free(wc->vpmadt032, chan->chanpos - 1, ec); } static void set_span_devicetype(struct t1 *wc) @@ -1487,16 +1488,48 @@ static inline void t1_check_alarms(struct t1 *wc) alarms |= DAHDI_ALARM_NOTOPEN; } - if (c & 0xa0) { - if (wc->alarmcount >= alarmdebounce) { - if (!unchannelized) - alarms |= DAHDI_ALARM_RED; - } else + if (c & 0x20) { /* LOF/LFA */ + if (wc->alarmcount >= (alarmdebounce/100)) + alarms |= DAHDI_ALARM_RED; + else { + if (unlikely(debug && !wc->alarmcount)) { + /* starting to debounce LOF/LFA */ + t1_info(wc, "LOF/LFA detected but " + "debouncing for %d ms\n", + alarmdebounce); + } wc->alarmcount++; + } } else wc->alarmcount = 0; - if (c & 0x4) - alarms |= DAHDI_ALARM_BLUE; + + if (c & 0x80) { /* LOS */ + if (wc->losalarmcount >= (losalarmdebounce/100)) + alarms |= DAHDI_ALARM_RED; + else { + if (unlikely(debug && !wc->losalarmcount)) { + /* starting to debounce LOS */ + t1_info(wc, "LOS detected but debouncing " + "for %d ms\n", losalarmdebounce); + } + wc->losalarmcount++; + } + } else + wc->losalarmcount = 0; + + if (c & 0x40) { /* AIS */ + if (wc->aisalarmcount >= (aisalarmdebounce/100)) + alarms |= DAHDI_ALARM_BLUE; + else { + if (unlikely(debug && !wc->aisalarmcount)) { + /* starting to debounce AIS */ + t1_info(wc, "AIS detected but debouncing " + "for %d ms\n", aisalarmdebounce); + } + wc->aisalarmcount++; + } + } else + wc->aisalarmcount = 0; /* Keep track of recovering */ if ((!alarms) && wc->span.alarms) @@ -1517,9 +1550,26 @@ static inline void t1_check_alarms(struct t1 *wc) t1_setreg_full(wc, 0x20, fmr4 & ~0x20, NOT_VPM); wc->flags.sendingyellow = 0; } - + /* if ((c & 0x10) && !unchannelized) alarms |= DAHDI_ALARM_YELLOW; + */ + + if ((c & 0x10) && !unchannelized) { /* receiving yellow (RAI) */ + if (wc->yelalarmcount >= (yelalarmdebounce/100)) + alarms |= DAHDI_ALARM_YELLOW; + else { + if (unlikely(debug && !wc->yelalarmcount)) { + /* starting to debounce AIS */ + t1_info(wc, "yelllow (RAI) detected but " + "debouncing for %d ms\n", + yelalarmdebounce); + } + wc->yelalarmcount++; + } + } else + wc->yelalarmcount = 0; + if (wc->span.mainttimer || wc->span.maintstat) alarms |= DAHDI_ALARM_LOOPBACK; wc->span.alarms = alarms; @@ -1685,13 +1735,13 @@ static void timer_work_func(struct work_struct *work) { struct t1 *wc = container_of(work, struct t1, timer_work); #endif - /* Called once every 100ms */ + /* Called once every 100 ms */ if (unlikely(!test_bit(INITIALIZED, &wc->bit_flags))) return; t1_do_counters(wc); t1_check_alarms(wc); t1_check_sigbits(wc); - mod_timer(&wc->timer, jiffies + HZ/5); + mod_timer(&wc->timer, jiffies + HZ/10); } static void @@ -1708,7 +1758,6 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi struct t1_desc *d = (struct t1_desc *) ent->driver_data; unsigned int x; int res; - int startinglatency; unsigned int index = -1; for (x = 0; x < sizeof(ifaces) / sizeof(ifaces[0]); x++) { @@ -1723,7 +1772,6 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi return -EIO; } -retry: if (!(wc = kmalloc(sizeof(*wc), GFP_KERNEL))) { return -ENOMEM; } @@ -1763,13 +1811,12 @@ retry: return res; } - /* Keep track of which device we are */ - pci_set_drvdata(pdev, wc); if (VOICEBUS_DEFAULT_LATENCY != latency) { voicebus_set_minlatency(wc->vb, latency); } + + voicebus_lock_latency(wc->vb); voicebus_start(wc->vb); - startinglatency = voicebus_current_latency(wc->vb); t1_hardware_post_init(wc); for (x = 0; x < (wc->spantype == TYPE_E1 ? 31 : 24); x++) { @@ -1789,31 +1836,14 @@ retry: mod_timer(&wc->timer, jiffies + HZ/5); t1_software_init(wc); - if (voicebus_current_latency(wc->vb) > startinglatency) { - /* The voicebus library increased the latency during - * initialization because the host wasn't able to service the - * interrupts from the adapter quickly enough. In this case, - * we'll increase our latency and restart the initialization. - */ - printk(KERN_NOTICE "%s: Restarting board initialization " \ - "after increasing latency.\n", wc->name); - latency = voicebus_current_latency(wc->vb); - dahdi_unregister(&wc->span); - voicebus_release(wc->vb); - wc->vb = NULL; - free_wc(wc); - wc = NULL; - goto retry; - } - module_printk("Found a %s\n", wc->variety); - + voicebus_unlock_latency(wc->vb); return 0; } static void __devexit te12xp_remove_one(struct pci_dev *pdev) { - struct t1 *wc = pci_get_drvdata(pdev); + struct t1 *wc = voicebus_pci_dev_to_context(pdev); #ifdef VPM_SUPPORT unsigned long flags; struct vpmadt032 *vpm = wc->vpmadt032; @@ -1902,6 +1932,9 @@ module_param(loopback, int, S_IRUGO | S_IWUSR); module_param(t1e1override, int, S_IRUGO | S_IWUSR); module_param(j1mode, int, S_IRUGO | S_IWUSR); module_param(alarmdebounce, int, S_IRUGO | S_IWUSR); +module_param(losalarmdebounce, int, S_IRUGO | S_IWUSR); +module_param(aisalarmdebounce, int, S_IRUGO | S_IWUSR); +module_param(yelalarmdebounce, int, S_IRUGO | S_IWUSR); module_param(latency, int, S_IRUGO | S_IWUSR); #ifdef VPM_SUPPORT module_param(vpmsupport, int, S_IRUGO | S_IWUSR); diff --git a/drivers/dahdi/wcte12xp/wcte12xp.h b/drivers/dahdi/wcte12xp/wcte12xp.h index 2a0a0f4..ceb309a 100644 --- a/drivers/dahdi/wcte12xp/wcte12xp.h +++ b/drivers/dahdi/wcte12xp/wcte12xp.h @@ -113,7 +113,10 @@ struct t1 { } flags; unsigned char txsigs[16]; /* Copy of tx sig registers */ int alarmcount; /* How much red alarm we've seen */ - char *variety; + int losalarmcount; + int aisalarmcount; + int yelalarmcount; + const char *variety; char name[80]; unsigned long blinktimer; int loopupcnt; @@ -144,7 +147,7 @@ struct t1 { struct work_struct timer_work; }; - -int schluffen(wait_queue_head_t *q); +#define t1_info(t1, format, arg...) \ + dev_info(&voicebus_get_pci_dev(t1->vb)->dev , format , ## arg) #endif diff --git a/drivers/dahdi/xpp/xdefs.h b/drivers/dahdi/xpp/xdefs.h index ad89d4c..24b2291 100644 --- a/drivers/dahdi/xpp/xdefs.h +++ b/drivers/dahdi/xpp/xdefs.h @@ -138,11 +138,6 @@ typedef unsigned char byte; #define DRIVER_ATTR_READER(name,drv,buf) \ ssize_t name(struct device_driver *drv, char * buf) -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) -#define dev_name(dev) (dev)->bus_id -#define dev_set_name(dev, format, ...) \ - snprintf((dev)->bus_id, BUS_ID_SIZE, format, ## __VA_ARGS__); -#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) #define SET_PROC_DIRENTRY_OWNER(p) do { (p)->owner = THIS_MODULE; } while(0); #else diff --git a/include/dahdi/kernel.h b/include/dahdi/kernel.h index 0731056..6058779 100644 --- a/include/dahdi/kernel.h +++ b/include/dahdi/kernel.h @@ -85,6 +85,12 @@ #endif #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) +#define dev_name(dev) (dev)->bus_id +#define dev_set_name(dev, format, ...) \ + snprintf((dev)->bus_id, BUS_ID_SIZE, format, ## __VA_ARGS__); +#endif + /*! Default chunk size for conferences and such -- static right now, might make variable sometime. 8 samples = 1 ms = most frequent service interval possible for a USB device */ @@ -1166,4 +1172,8 @@ static inline short dahdi_txtone_nextsample(struct dahdi_chan *ss) #define kzalloc(a, b) kcalloc(1, a, b) #endif +#ifndef DMA_BIT_MASK +#define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL<<(n))-1)) +#endif + #endif /* _DAHDI_KERNEL_H */ diff --git a/include/dahdi/user.h b/include/dahdi/user.h index 5c7121a..1d0570a 100644 --- a/include/dahdi/user.h +++ b/include/dahdi/user.h @@ -1027,19 +1027,6 @@ struct dahdi_vmwi_info { #define DAHDI_ECHOCANCEL_FAX_MODE _IOW(DAHDI_CODE, 102, int) -struct torisa_debug { - unsigned int txerrors; - unsigned int irqcount; - unsigned int taskletsched; - unsigned int taskletrun; - unsigned int taskletexec; - int span1flags; - int span2flags; -}; - -/* Special torisa ioctl */ -#define TORISA_GETDEBUG _IOW(DAHDI_CODE, 60, struct torisa_debug) - /* Get current status IOCTL */ /* Defines for Radio Status (dahdi_radio_stat.radstat) bits */ |