summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2010-02-26 16:40:44 +0000
committerShaun Ruffell <sruffell@digium.com>2010-02-26 16:40:44 +0000
commit4244c7073ad8a83d58d0aa3c4a87f4f36153ff3a (patch)
tree93f77629a4d68039a21652ed2bee2ef8f1de7113
parentda08f0a6db75cf34f1d88c856fc2f9e8e0377f1d (diff)
wctdm24xxp: Add support for Hx8 series cards.
The Hx8 series cards support BRI modules in addition to analog modules. git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@8123 a0bf4364-ded3-4de4-8d8a-66a801d63aff
-rw-r--r--drivers/dahdi/voicebus/voicebus.c238
-rw-r--r--drivers/dahdi/voicebus/voicebus.h7
-rw-r--r--drivers/dahdi/vpmadt032_loader/dahdi_vpmadt032_loader.c11
-rw-r--r--drivers/dahdi/wctdm24xxp/Kbuild2
-rw-r--r--drivers/dahdi/wctdm24xxp/base.c1817
-rw-r--r--drivers/dahdi/wctdm24xxp/wctdm24xxp.h135
-rw-r--r--drivers/dahdi/wctdm24xxp/xhfc.c2756
-rw-r--r--drivers/dahdi/wctdm24xxp/xhfc.h49
-rw-r--r--drivers/dahdi/wcte12xp/base.c8
9 files changed, 4389 insertions, 634 deletions
diff --git a/drivers/dahdi/voicebus/voicebus.c b/drivers/dahdi/voicebus/voicebus.c
index eacc0f7..fe23856 100644
--- a/drivers/dahdi/voicebus/voicebus.c
+++ b/drivers/dahdi/voicebus/voicebus.c
@@ -95,10 +95,11 @@
#define OWN_BIT (1 << 31)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static kmem_cache_t *buffer_cache;
+kmem_cache_t *voicebus_vbb_cache;
#else
-static struct kmem_cache *buffer_cache;
+struct kmem_cache *voicebus_vbb_cache;
#endif
+EXPORT_SYMBOL(voicebus_vbb_cache);
/* In memory structure shared by the host and the adapter. */
struct voicebus_descriptor {
@@ -365,8 +366,6 @@ vb_cleanup_tx_descriptors(struct voicebus *vb)
struct voicebus_descriptor *d;
unsigned long flags;
- WARN_ON(!vb_is_stopped(vb));
-
spin_lock_irqsave(&vb->lock, flags);
for (i = 0; i < DRING_SIZE; ++i) {
d = vb_descriptor(dl, i);
@@ -374,7 +373,7 @@ vb_cleanup_tx_descriptors(struct voicebus *vb)
WARN_ON(!dl->pending[i]);
dma_unmap_single(&vb->pdev->dev, d->buffer1,
VOICEBUS_SFRAME_SIZE, DMA_TO_DEVICE);
- voicebus_free(vb, dl->pending[i]);
+ kmem_cache_free(voicebus_vbb_cache, dl->pending[i]);
}
if (!test_bit(VOICEBUS_NORMAL_MODE, &vb->flags)) {
d->buffer1 = 0;
@@ -388,8 +387,6 @@ vb_cleanup_tx_descriptors(struct voicebus *vb)
}
}
- /* Send out two idle buffers to start because sometimes the first buffer
- * doesn't make it back to us. */
dl->head = dl->tail = 0;
spin_unlock_irqrestore(&vb->lock, flags);
atomic_set(&dl->count, 0);
@@ -403,8 +400,6 @@ vb_cleanup_rx_descriptors(struct voicebus *vb)
struct voicebus_descriptor *d;
unsigned long flags;
- BUG_ON(!vb_is_stopped(vb));
-
spin_lock_irqsave(&vb->lock, flags);
for (i = 0; i < DRING_SIZE; ++i) {
d = vb_descriptor(dl, i);
@@ -413,7 +408,7 @@ vb_cleanup_rx_descriptors(struct voicebus *vb)
VOICEBUS_SFRAME_SIZE, DMA_FROM_DEVICE);
d->buffer1 = 0;
BUG_ON(!dl->pending[i]);
- kmem_cache_free(buffer_cache, dl->pending[i]);
+ kmem_cache_free(voicebus_vbb_cache, dl->pending[i]);
dl->pending[i] = NULL;
}
d->des0 &= ~OWN_BIT;
@@ -533,7 +528,7 @@ vb_reset_interface(struct voicebus *vb)
unsigned long timeout;
u32 reg;
u32 pci_access;
- const u32 DEFAULT_PCI_ACCESS = 0xfff80002;
+ const u32 DEFAULT_PCI_ACCESS = 0xfffc0002;
u8 cache_line_size;
BUG_ON(in_interrupt());
@@ -601,7 +596,7 @@ vb_submit_rxb(struct voicebus *vb, struct vbb *vbb)
if (unlikely(d->buffer1)) {
/* Do not overwrite a buffer that is still in progress. */
WARN_ON(1);
- voicebus_free(vb, vbb);
+ kmem_cache_free(voicebus_vbb_cache, vbb);
return -EBUSY;
}
@@ -614,6 +609,49 @@ vb_submit_rxb(struct voicebus *vb, struct vbb *vbb)
return 0;
}
+/**
+ * voicebus_transmit - Queue a buffer on the hardware descriptor ring.
+ *
+ */
+int voicebus_transmit(struct voicebus *vb, struct vbb *vbb)
+{
+ struct voicebus_descriptor *d;
+ struct voicebus_descriptor_list *dl = &vb->txd;
+
+ d = vb_descriptor(dl, dl->tail);
+
+ if (unlikely((d->buffer1 != vb->idle_vbb_dma_addr) && d->buffer1)) {
+ if (printk_ratelimit())
+ dev_warn(&vb->pdev->dev, "Dropping tx buffer buffer\n");
+ kmem_cache_free(voicebus_vbb_cache, vbb);
+ /* Schedule the underrun handler to run here, since we'll need
+ * to cleanup as best we can. */
+ schedule_work(&vb->underrun_work);
+ return -EFAULT;
+ }
+
+ dl->pending[dl->tail] = vbb;
+ d->buffer1 = dma_map_single(&vb->pdev->dev, vbb->data,
+ sizeof(vbb->data), DMA_TO_DEVICE);
+ dl->tail = (++(dl->tail)) & DRING_MASK;
+ SET_OWNED(d); /* That's it until the hardware is done with it. */
+ atomic_inc(&dl->count);
+ return 0;
+}
+EXPORT_SYMBOL(voicebus_transmit);
+
+
+/*!
+ * \brief Instruct the hardware to check for a new tx descriptor.
+ */
+static inline void
+__vb_tx_demand_poll(struct voicebus *vb)
+{
+ u32 status = __vb_getctl(vb, 0x0028);
+ if ((status & 0x00700000) == 0x00600000)
+ __vb_setctl(vb, 0x0008, 0x00000000);
+}
+
static void setup_descriptors(struct voicebus *vb)
{
int i;
@@ -631,7 +669,7 @@ static void setup_descriptors(struct voicebus *vb)
vb_setctl(vb, 0x0018, (u32)vb->rxd.desc_dma);
for (i = 0; i < DRING_SIZE; ++i) {
- vbb = kmem_cache_alloc(buffer_cache, GFP_KERNEL);
+ vbb = kmem_cache_alloc(voicebus_vbb_cache, GFP_KERNEL);
if (unlikely(NULL == vbb))
BUG_ON(1);
list_add_tail(&vbb->entry, &buffers);
@@ -646,13 +684,13 @@ static void setup_descriptors(struct voicebus *vb)
if (test_bit(VOICEBUS_NORMAL_MODE, &vb->flags)) {
for (i = 0; i < vb->min_tx_buffer_count; ++i) {
- vbb = kmem_cache_alloc(buffer_cache, GFP_KERNEL);
+ vbb = kmem_cache_alloc(voicebus_vbb_cache, GFP_KERNEL);
if (unlikely(NULL == vbb))
BUG_ON(1);
else
list_add_tail(&vbb->entry, &buffers);
}
-
+
tasklet_disable(&vb->tasklet);
handle_transmit(vb, &buffers);
tasklet_enable(&vb->tasklet);
@@ -740,55 +778,6 @@ show_buffer(struct voicebus *vb, struct vbb *vbb)
}
#endif
-/**
- * voicebus_transmit - Queue a buffer on the hardware descriptor ring.
- *
- */
-static int __voicebus_transmit(struct voicebus *vb, struct vbb *vbb)
-{
- struct voicebus_descriptor *d;
- struct voicebus_descriptor_list *dl = &vb->txd;
-
- d = vb_descriptor(dl, dl->tail);
-
- if (unlikely((d->buffer1 != vb->idle_vbb_dma_addr) && d->buffer1)) {
- if (printk_ratelimit())
- dev_warn(&vb->pdev->dev, "Dropping tx buffer buffer\n");
- voicebus_free(vb, vbb);
- /* Schedule the underrun handler to run here, since we'll need
- * to cleanup as best we can. */
- schedule_work(&vb->underrun_work);
- return -EFAULT;
- }
-
- dl->pending[dl->tail] = vbb;
- d->buffer1 = dma_map_single(&vb->pdev->dev, vbb->data,
- sizeof(vbb->data), DMA_TO_DEVICE);
- dl->tail = (++(dl->tail)) & DRING_MASK;
- SET_OWNED(d); /* That's it until the hardware is done with it. */
- atomic_inc(&dl->count);
- return 0;
-}
-
-/*!
- * \brief Instruct the hardware to check for a new tx descriptor.
- */
-static inline void
-__vb_tx_demand_poll(struct voicebus *vb)
-{
- u32 status = __vb_getctl(vb, 0x0028);
- if ((status & 0x00700000) == 0x00600000)
- __vb_setctl(vb, 0x0008, 0x00000000);
-}
-
-int voicebus_transmit(struct voicebus *vb, struct vbb *vbb)
-{
- __voicebus_transmit(vb, vbb);
- __vb_tx_demand_poll(vb);
- return 0;
-}
-EXPORT_SYMBOL(voicebus_transmit);
-
/*!
* \brief Remove the next completed transmit buffer (txb) from the tx
* descriptor ring.
@@ -860,18 +849,6 @@ vb_get_completed_rxb(struct voicebus *vb, u32 *des0)
}
/*!
- * \brief Free a buffer for reuse.
- *
- */
-void
-voicebus_free(struct voicebus *vb, struct vbb *vbb)
-{
- kmem_cache_free(buffer_cache, vbb);
-}
-EXPORT_SYMBOL(voicebus_free);
-
-
-/*!
* \brief Command the hardware to check if it owns the next receive
* descriptor.
*/
@@ -925,12 +902,16 @@ static void start_packet_processing(struct voicebus *vb)
/* Start the transmit and receive processors. */
reg = __vb_getctl(vb, 0x0030);
__vb_setctl(vb, 0x0030, reg|0x00002002);
- /* Tell the interface to poll the tx and rx descriptors. */
+ __vb_getctl(vb, 0x0030);
__vb_rx_demand_poll(vb);
__vb_tx_demand_poll(vb);
+ __vb_getctl(vb, 0x0030);
VBUNLOCK(vb);
}
+static void vb_tasklet_relaxed(unsigned long data);
+static void vb_tasklet(unsigned long data);
+
/*!
* \brief Starts the VoiceBus interface.
*
@@ -951,6 +932,11 @@ voicebus_start(struct voicebus *vb)
if (!vb_is_stopped(vb))
return -EBUSY;
+ if (test_bit(VOICEBUS_NORMAL_MODE, &vb->flags))
+ tasklet_init(&vb->tasklet, vb_tasklet, (unsigned long)vb);
+ else
+ tasklet_init(&vb->tasklet, vb_tasklet_relaxed, (unsigned long)vb);
+
ret = vb_reset_interface(vb);
if (ret)
return ret;
@@ -1108,7 +1094,7 @@ vb_increase_latency(struct voicebus *vb, unsigned int increase,
/* 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 = kmem_cache_alloc(buffer_cache, GFP_ATOMIC);
+ vbb = kmem_cache_alloc(voicebus_vbb_cache, GFP_ATOMIC);
WARN_ON(NULL == vbb);
if (likely(NULL != vbb))
list_add_tail(&vbb->entry, &local);
@@ -1127,29 +1113,64 @@ vb_increase_latency(struct voicebus *vb, unsigned int increase,
vb->min_tx_buffer_count += increase;
}
-static void vb_set_all_owned(struct voicebus *vb,
- struct voicebus_descriptor_list *dl)
+/**
+ * vb_tasklet_relaxed() - Service the rings without strict timing requierments.
+ *
+ */
+static void vb_tasklet_relaxed(unsigned long data)
{
- int i;
- struct voicebus_descriptor *d;
+ struct voicebus *vb = (struct voicebus *)data;
+ LIST_HEAD(buffers);
+ struct vbb *vbb;
+ const int DEFAULT_COUNT = 5;
+ int count = DEFAULT_COUNT;
+ u32 des0 = 0;
- for (i = 0; i < DRING_SIZE; ++i) {
- d = vb_descriptor(dl, i);
- SET_OWNED(d);
+ /* 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 ((vbb = vb_get_completed_txb(vb)))
+ list_add_tail(&vbb->entry, &vb->tx_complete);
+
+ while (--count && !list_empty(&vb->tx_complete))
+ list_move_tail(vb->tx_complete.next, &buffers);
+
+ /* Prep all the new buffers for transmit before actually sending any
+ * of them. */
+ handle_transmit(vb, &buffers);
+
+ list_for_each_entry(vbb, &buffers, entry)
+ voicebus_transmit(vb, vbb);
+ INIT_LIST_HEAD(&buffers);
+
+ /* If there may still be buffers in the descriptor rings, reschedule
+ * ourself to run again. We essentially yield here to allow any other
+ * cards a chance to run. */
+ if (unlikely(!count && !test_bit(VOICEBUS_STOP, &vb->flags)))
+ tasklet_hi_schedule(&vb->tasklet);
+
+ /* And finally, pass up any receive buffers. */
+ count = DEFAULT_COUNT;
+ while (--count && (vbb = vb_get_completed_rxb(vb, &des0))) {
+ if (((des0 >> 16) & 0x7fff) == VOICEBUS_SFRAME_SIZE)
+ list_add_tail(&vbb->entry, &buffers);
}
-}
-static inline void vb_set_all_tx_owned(struct voicebus *vb)
-{
- vb_set_all_owned(vb, &vb->txd);
+ handle_receive(vb, &buffers);
+ list_for_each_entry(vbb, &buffers, entry)
+ vb_submit_rxb(vb, vbb);
+ return;
}
-/**
- * vb_deferred() - Manage the transmit and receive descriptor rings.
- *
- */
-static void vb_deferred(struct voicebus *vb)
+static void vb_tasklet(unsigned long data)
{
+ struct voicebus *vb = (struct voicebus *)data;
int softunderrun;
LIST_HEAD(buffers);
struct vbb *vbb;
@@ -1277,13 +1298,8 @@ static void vb_deferred(struct voicebus *vb)
/* And finally, pass up any receive buffers. */
count = DEFAULT_COUNT;
while (--count && (vbb = vb_get_completed_rxb(vb, &des0))) {
- if (((des0 >> 16) & 0x7fff) != VOICEBUS_SFRAME_SIZE) {
- dev_dbg(&vb->pdev->dev,
- "ERROR: Dropping packet of wrong size. (%d)\n",
- (des0>>16)&0x7fff);
- } else {
+ if (((des0 >> 16) & 0x7fff) == VOICEBUS_SFRAME_SIZE)
list_add_tail(&vbb->entry, &buffers);
- }
}
handle_receive(vb, &buffers);
@@ -1297,7 +1313,7 @@ tx_error_exit:
while (!list_empty(&buffers)) {
vbb = list_entry(buffers.next, struct vbb, entry);
list_del(&vbb->entry);
- kmem_cache_free(buffer_cache, vbb);
+ kmem_cache_free(voicebus_vbb_cache, vbb);
}
return;
}
@@ -1365,7 +1381,8 @@ vb_isr(int irq, void *dev_id)
if (unlikely((int_status &
(TX_UNAVAILABLE_INTERRUPT|RX_UNAVAILABLE_INTERRUPT)) &&
- !test_bit(VOICEBUS_STOP, &vb->flags))) {
+ !test_bit(VOICEBUS_STOP, &vb->flags) &&
+ test_bit(VOICEBUS_NORMAL_MODE, &vb->flags))) {
schedule_work(&vb->underrun_work);
__vb_setctl(vb, SR_CSR5, int_status);
} else if (likely(int_status &
@@ -1422,12 +1439,6 @@ vb_timer(unsigned long data)
}
#endif
-static void vb_tasklet(unsigned long data)
-{
- struct voicebus *vb = (struct voicebus *)data;
- vb_deferred(vb);
-}
-
/*!
* \brief Initalize the voicebus interface.
*
@@ -1462,7 +1473,6 @@ __voicebus_init(struct voicebus *vb, const char *board_name, int normal_mode)
vb->min_tx_buffer_count = VOICEBUS_DEFAULT_LATENCY;
INIT_LIST_HEAD(&vb->tx_complete);
- tasklet_init(&vb->tasklet, vb_tasklet, (unsigned long)vb);
#if defined(CONFIG_VOICEBUS_TIMER)
init_timer(&vb->timer);
@@ -1672,7 +1682,7 @@ static int __init voicebus_module_init(void)
int res;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
- buffer_cache = kmem_cache_create(THIS_MODULE->name,
+ voicebus_vbb_cache = kmem_cache_create(THIS_MODULE->name,
sizeof(struct vbb), 0,
#if defined(CONFIG_SLUB) && (LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 22))
SLAB_HWCACHE_ALIGN |
@@ -1684,18 +1694,18 @@ static int __init voicebus_module_init(void)
#endif
#else
#if (defined(DEBUG) && defined(CONFIG_SLAB_DEBUG))
- buffer_cache = kmem_cache_create(THIS_MODULE->name,
+ voicebus_vbb_cache = kmem_cache_create(THIS_MODULE->name,
sizeof(struct vbb), 0,
SLAB_HWCACHE_ALIGN | SLAB_STORE_USER |
SLAB_POISON | SLAB_DEBUG_FREE, NULL);
#else
- buffer_cache = kmem_cache_create(THIS_MODULE->name,
+ voicebus_vbb_cache = kmem_cache_create(THIS_MODULE->name,
sizeof(struct vbb), 0,
SLAB_HWCACHE_ALIGN, NULL);
#endif
#endif
- if (NULL == buffer_cache) {
+ if (NULL == voicebus_vbb_cache) {
printk(KERN_ERR "%s: Failed to allocate buffer cache.\n",
THIS_MODULE->name);
return -ENOMEM;
@@ -1715,7 +1725,7 @@ static int __init voicebus_module_init(void)
static void __exit voicebus_module_cleanup(void)
{
- kmem_cache_destroy(buffer_cache);
+ kmem_cache_destroy(voicebus_vbb_cache);
WARN_ON(!list_empty(&binary_loader_list));
}
diff --git a/drivers/dahdi/voicebus/voicebus.h b/drivers/dahdi/voicebus/voicebus.h
index 149b4d0..35f0e29 100644
--- a/drivers/dahdi/voicebus/voicebus.h
+++ b/drivers/dahdi/voicebus/voicebus.h
@@ -132,12 +132,17 @@ struct voicebus {
#endif
};
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+extern kmem_cache_t *voicebus_vbb_cache;
+#else
+extern struct kmem_cache *voicebus_vbb_cache;
+#endif
+
int __voicebus_init(struct voicebus *vb, const char *board_name,
int normal_mode);
void voicebus_release(struct voicebus *vb);
int voicebus_start(struct voicebus *vb);
int voicebus_stop(struct voicebus *vb);
-void voicebus_free(struct voicebus *vb, struct vbb *vbb);
int voicebus_transmit(struct voicebus *vb, struct vbb *vbb);
int voicebus_set_minlatency(struct voicebus *vb, unsigned int milliseconds);
int voicebus_current_latency(struct voicebus *vb);
diff --git a/drivers/dahdi/vpmadt032_loader/dahdi_vpmadt032_loader.c b/drivers/dahdi/vpmadt032_loader/dahdi_vpmadt032_loader.c
index be43780..cdba790 100644
--- a/drivers/dahdi/vpmadt032_loader/dahdi_vpmadt032_loader.c
+++ b/drivers/dahdi/vpmadt032_loader/dahdi_vpmadt032_loader.c
@@ -105,15 +105,20 @@ static int vpmadt032_load_firmware(struct voicebus *vb)
struct private_context *ctx;
const struct voicebus_operations *old;
void *old_drvdata;
+ int id;
might_sleep();
ctx = kzalloc(sizeof(struct private_context), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
init_private_context(ctx);
ctx->vb = vb;
- ret = __vpmadt032_start_load(
- 0, vb->pdev->vendor << 16 | vb->pdev->device,
- &ctx->pvt);
+
+ if (0x8007 == vb->pdev->device || 0x8008 == vb->pdev->device)
+ id = vb->pdev->vendor << 16 | 0x2400;
+ else
+ id = vb->pdev->vendor << 16 | vb->pdev->device;
+
+ ret = __vpmadt032_start_load(0, id, &ctx->pvt);
if (ret)
goto error_exit;
old_drvdata = pci_get_drvdata(vb->pdev);
diff --git a/drivers/dahdi/wctdm24xxp/Kbuild b/drivers/dahdi/wctdm24xxp/Kbuild
index 6b1d0e7..22cc71a 100644
--- a/drivers/dahdi/wctdm24xxp/Kbuild
+++ b/drivers/dahdi/wctdm24xxp/Kbuild
@@ -2,4 +2,4 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM24XXP) += wctdm24xxp.o
EXTRA_CFLAGS += -I$(src)/.. -Wno-undef
-wctdm24xxp-objs := base.o
+wctdm24xxp-objs := base.o xhfc.o
diff --git a/drivers/dahdi/wctdm24xxp/base.c b/drivers/dahdi/wctdm24xxp/base.c
index ce6799e..5cf9e85 100644
--- a/drivers/dahdi/wctdm24xxp/base.c
+++ b/drivers/dahdi/wctdm24xxp/base.c
@@ -4,6 +4,9 @@
* Written by Mark Spencer <markster@digium.com>
* Support for TDM800P and VPM150M by Matthew Fredrickson <creslin@digium.com>
*
+ * Support for Hx8 by Andrew Kohlsmith <akohlsmith@mixdown.ca> and Matthew
+ * Fredrickson <creslin@digium.com>
+ *
* Copyright (C) 2005 - 2010 Digium, Inc.
* All rights reserved.
*
@@ -34,6 +37,8 @@ Tx Gain - No Pre-Emphasis: -35.99 to 12.00 db
Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db
*/
+#define DEBUG
+
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -46,11 +51,13 @@ Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
+#include <linux/firmware.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
#include <linux/semaphore.h>
#else
#include <asm/semaphore.h>
#endif
+#include <linux/crc32.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
/* Define this if you would like to load the modules in parallel. While this
@@ -68,6 +75,7 @@ Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db
#include "proslic.h"
#include "wctdm24xxp.h"
+#include "xhfc.h"
#include "adt_lec.h"
@@ -187,10 +195,24 @@ 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 const struct wctdm_desc wcha80000 = { "HA8-0000", 0, 8 };
+static const struct wctdm_desc wchb80000 = { "HB8-0000", FLAG_EXPRESS, 8 };
+/**
+ * Returns true if the card is one of the Hybrid Digital Analog Cards.
+ */
+static inline bool is_hx8(const struct wctdm *wc)
+{
+ return (&wcha80000 == wc->desc) || (&wchb80000 == wc->desc);
+}
+
+static inline bool is_digital_span(const struct wctdm_span *wspan)
+{
+ return (wspan->span.linecompat > 0);
+}
struct wctdm *ifaces[WC_MAX_IFACES];
-spinlock_t ifacelock = SPIN_LOCK_UNLOCKED;
+DECLARE_MUTEX(ifacelock);
static void wctdm_release(struct wctdm *wc);
@@ -215,6 +237,7 @@ static int nativebridge = 0;
static int ringdebounce = DEFAULT_RING_DEBOUNCE;
static int fwringdetect = 0;
static int latency = VOICEBUS_DEFAULT_LATENCY;
+static int forceload;
#define MS_PER_HOOKCHECK (1)
#define NEONMWI_ON_DEBOUNCE (100/MS_PER_HOOKCHECK)
@@ -291,7 +314,7 @@ int schluffen(wait_queue_head_t *q)
static inline int empty_slot(struct wctdm *wc, int card)
{
int x;
- for (x=0;x<USER_COMMANDS;x++) {
+ for (x = 0; x < USER_COMMANDS; x++) {
if (!wc->cmdq[card].cmds[x])
return x;
}
@@ -438,8 +461,7 @@ static int config_vpmadt032(struct vpmadt032 *vpm, struct wctdm *wc)
vpm->curecstate[i].nlp_type = vpm->options.vpmnlptype;
vpm->curecstate[i].nlp_threshold = vpm->options.vpmnlpthresh;
vpm->curecstate[i].nlp_max_suppress = vpm->options.vpmnlpmaxsupp;
- vpm->curecstate[i].companding = (wc->span.deflaw == DAHDI_LAW_MULAW) ? ADT_COMP_ULAW : ADT_COMP_ALAW;
-
+ vpm->curecstate[i].companding = (wc->chans[i]->chan.span->deflaw == DAHDI_LAW_ALAW) ? ADT_COMP_ALAW : ADT_COMP_ULAW;
/* set_vpmadt032_chanconfig_from_state(&vpm->curecstate[i], &vpm->options, i, &chanconfig); !!! */
vpm->setchanconfig_from_state(vpm, i, &chanconfig);
if ((res = gpakConfigureChannel(vpm->dspid, i, tdmToTdm, &chanconfig, &cstatus))) {
@@ -485,6 +507,7 @@ static inline bool is_good_frame(const u8 *sframe)
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;
@@ -496,6 +519,7 @@ static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, u8 *writechunk, int w
if (test_bit(VPM150M_SPIRESET, &vpmadt032->control) || test_bit(VPM150M_HPIRESET, &vpmadt032->control)) {
if (debug & DEBUG_ECHOCAN)
dev_info(&wc->vb.pdev->dev, "HW Resetting VPMADT032...\n");
+ spin_lock_irqsave(&wc->reglock, flags);
for (x = 24; x < 28; x++) {
if (x == 24) {
if (test_and_clear_bit(VPM150M_SPIRESET, &vpmadt032->control))
@@ -507,6 +531,7 @@ static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, u8 *writechunk, int w
writechunk[CMD_BYTE(x, 1, 0)] = 0;
writechunk[CMD_BYTE(x, 2, 0)] = 0x00;
}
+ spin_unlock_irqrestore(&wc->reglock, flags);
return;
}
@@ -603,39 +628,37 @@ static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int
ecval = ecval % EC_SIZE;
#endif
- /* if a QRV card, map it to its first channel */
- if ((wc->modtype[card] == MOD_TYPE_QRV) && (card & 3))
- {
- return;
- }
- if (wc->altcs[card])
- subaddr = 0;
-
+ /* QRV and BRI modules only use commands relating to the first channel */
+ if ((card & 0x03) && (wc->modtype[card] == MOD_TYPE_QRV)) {
+ return;
+ }
+
+ if (wc->altcs[card])
+ subaddr = 0;
-
/* Skip audio */
writechunk += 24;
spin_lock_irqsave(&wc->reglock, flags);
/* Search for something waiting to transmit */
if (pos) {
- for (x=0;x<MAX_COMMANDS;x++) {
+ for (x = 0; x < MAX_COMMANDS; x++) {
if ((wc->cmdq[card].cmds[x] & (__CMD_RD | __CMD_WR)) &&
!(wc->cmdq[card].cmds[x] & (__CMD_TX | __CMD_FIN))) {
curcmd = wc->cmdq[card].cmds[x];
-#if 0
- dev_info(&wc->vb.pdev->dev, "Transmitting command '%08x' in slot %d\n", wc->cmdq[card].cmds[x], wc->txident);
-#endif
wc->cmdq[card].cmds[x] |= (wc->txident << 24) | __CMD_TX;
break;
}
}
}
+
if (!curcmd) {
/* If nothing else, use filler */
if (wc->modtype[card] == MOD_TYPE_FXS)
curcmd = CMD_RD(LINE_STATE);
else if (wc->modtype[card] == MOD_TYPE_FXO)
curcmd = CMD_RD(12);
+ else if (wc->modtype[card] == MOD_TYPE_BRI)
+ curcmd = 0x101010;
else if (wc->modtype[card] == MOD_TYPE_QRV)
curcmd = CMD_RD(3);
else if (wc->modtype[card] == MOD_TYPE_VPM) {
@@ -649,13 +672,15 @@ static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int
curcmd = CMD_RD(0x1a0);
}
}
+
if (wc->modtype[card] == MOD_TYPE_FXS) {
- writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = (1 << (subaddr));
+ writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = (1 << (subaddr));
if (curcmd & __CMD_WR)
- writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0x7f;
+ writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0x7f;
else
- writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x80 | ((curcmd >> 8) & 0x7f);
- writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff;
+ writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x80 | ((curcmd >> 8) & 0x7f);
+ writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff;
+
} else if (wc->modtype[card] == MOD_TYPE_FXO) {
static const int FXO_ADDRS[4] = { 0x00, 0x08, 0x04, 0x0c };
int idx = CMD_BYTE(card, 0, wc->altcs[card]);
@@ -663,48 +688,53 @@ static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int
writechunk[idx] = 0x20 | FXO_ADDRS[subaddr];
else
writechunk[idx] = 0x60 | FXO_ADDRS[subaddr];
- writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff;
- writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff;
+ writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff;
+ writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff;
+
} else if (wc->modtype[card] == MOD_TYPE_FXSINIT) {
/* Special case, we initialize the FXS's into the three-byte command mode then
switch to the regular mode. To send it into thee byte mode, treat the path as
6 two-byte commands and in the last one we initialize register 0 to 0x80. All modules
read this as the command to switch to daisy chain mode and we're done. */
- writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00;
- writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00;
+ writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00;
+ writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00;
if ((card & 0x1) == 0x1)
- writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x80;
+ writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x80;
else
- writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00;
+ writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00;
+
+ } else if (wc->modtype[card] == MOD_TYPE_BRI) {
+
+ if (unlikely((curcmd != 0x101010) && (curcmd & 0x1010) == 0x1010)) /* b400m CPLD */
+ writechunk[CMD_BYTE(card, 0, 0)] = 0x55;
+ else /* xhfc */
+ writechunk[CMD_BYTE(card, 0, 0)] = 0x10;
+ writechunk[CMD_BYTE(card, 1, 0)] = (curcmd >> 8) & 0xff;
+ writechunk[CMD_BYTE(card, 2, 0)] = curcmd & 0xff;
} else if (wc->modtype[card] == MOD_TYPE_VPM) {
if (curcmd & __CMD_WR)
- writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xc | ((curcmd >> 16) & 0x1);
+ writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xc | ((curcmd >> 16) & 0x1);
else
- writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xa | ((curcmd >> 16) & 0x1);
- writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff;
- writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff;
- } else if (wc->modtype[card] == MOD_TYPE_VPM150M) {
- ;
- } else if (wc->modtype[card] == MOD_TYPE_QRV) {
+ writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xa | ((curcmd >> 16) & 0x1);
+ writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff;
+ writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff;
+ } else if (wc->modtype[card] == MOD_TYPE_QRV) {
- writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00;
- if (!curcmd)
- {
- writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00;
- writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00;
- }
- else
- {
- if (curcmd & __CMD_WR)
- writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x40 | ((curcmd >> 8) & 0x3f);
- else
- writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0xc0 | ((curcmd >> 8) & 0x3f);
- writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff;
- }
+ writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00;
+ if (!curcmd) {
+ writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00;
+ writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00;
+ } else {
+ if (curcmd & __CMD_WR)
+ writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x40 | ((curcmd >> 8) & 0x3f);
+ else
+ writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0xc0 | ((curcmd >> 8) & 0x3f);
+ writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff;
+ }
} else if (wc->modtype[card] == MOD_TYPE_NONE) {
- writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00;
- writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00;
- writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00;
+ writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x10;
+ writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x10;
+ writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x10;
}
#if 0
/* XXX */
@@ -715,10 +745,10 @@ static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int
#if 0
/* XXX */
cmddesc++;
-#endif
+#endif
}
-static inline void cmd_decipher_vpmadt032(struct wctdm *wc, unsigned char *readchunk)
+static inline void cmd_decipher_vpmadt032(struct wctdm *wc, const u8 *readchunk)
{
unsigned long flags;
struct vpmadt032 *vpm = wc->vpmadt032;
@@ -747,6 +777,7 @@ static inline void cmd_decipher_vpmadt032(struct wctdm *wc, unsigned char *readc
/* Skip audio */
readchunk += 24;
+
/* Store result */
cmd->data = (0xff & readchunk[CMD_BYTE(25, 1, 0)]) << 8;
cmd->data |= readchunk[CMD_BYTE(25, 2, 0)];
@@ -757,26 +788,23 @@ static inline void cmd_decipher_vpmadt032(struct wctdm *wc, unsigned char *readc
cmd->desc |= __VPM150M_FIN;
complete(&cmd->complete);
}
-#if 0
- // if (printk_ratelimit())
- dev_info(&wc->vb.pdev->dev, "Received txident = %d, desc = 0x%x, addr = 0x%x, data = 0x%x\n", cmd->txident, cmd->desc, cmd->address, cmd->data);
-#endif
}
-static inline void cmd_decipher(struct wctdm *wc, u8 *readchunk, int card)
+static inline void cmd_decipher(struct wctdm *wc, const u8 *readchunk, int card)
{
unsigned long flags;
unsigned char ident;
int x;
- /* if a QRV card, map it to its first channel */
- if ((wc->modtype[card] == MOD_TYPE_QRV) && (card & 3))
- {
+ /* QRV and BRI modules only use commands relating to the first channel */
+ if ((card & 0x03) && (wc->modtype[card] == MOD_TYPE_QRV)) { /* || (wc->modtype[card] == MOD_TYPE_BRI))) { */
return;
}
+
/* Skip audio */
readchunk += 24;
spin_lock_irqsave(&wc->reglock, flags);
+
/* Search for any pending results */
for (x=0;x<MAX_COMMANDS;x++) {
if ((wc->cmdq[card].cmds[x] & (__CMD_RD | __CMD_WR)) &&
@@ -787,6 +815,11 @@ static inline void cmd_decipher(struct wctdm *wc, u8 *readchunk, int card)
/* Store result */
wc->cmdq[card].cmds[x] |= readchunk[CMD_BYTE(card, 2, wc->altcs[card])];
wc->cmdq[card].cmds[x] |= __CMD_FIN;
+/*
+ if (card == 0 && wc->cmdq[card].cmds[x] & __CMD_RD) {
+ dev_info(&wc->vb.pdev->dev, "decifer: got response %02x\n", wc->cmdq[card].cmds[x] & 0xff);
+ }
+*/
if (wc->cmdq[card].cmds[x] & __CMD_WR) {
/* Go ahead and clear out writes since they need no acknowledgement */
wc->cmdq[card].cmds[x] = 0x00000000;
@@ -819,6 +852,8 @@ static inline void cmd_checkisr(struct wctdm *wc, int card)
wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(5); /* Hook/Ring state */
} else if (wc->modtype[card] == MOD_TYPE_QRV) {
wc->cmdq[card & 0xfc].cmds[USER_COMMANDS + 0] = CMD_RD(3); /* COR/CTCSS state */
+ } else if (wc->modtype[card] == MOD_TYPE_BRI) {
+ wc->cmdq[card].cmds[USER_COMMANDS + 0] = wctdm_bri_checkisr(wc, card, 0);
#ifdef VPM_SUPPORT
} else if (wc->modtype[card] == MOD_TYPE_VPM) {
wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(0xb9); /* DTMF interrupt */
@@ -836,6 +871,8 @@ static inline void cmd_checkisr(struct wctdm *wc, int card)
wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(29); /* Battery */
} else if (wc->modtype[card] == MOD_TYPE_QRV) {
wc->cmdq[card & 0xfc].cmds[USER_COMMANDS + 1] = CMD_RD(3); /* Battery */
+ } else if (wc->modtype[card] == MOD_TYPE_BRI) {
+ wc->cmdq[card].cmds[USER_COMMANDS + 1] = wctdm_bri_checkisr(wc, card, 1);
#ifdef VPM_SUPPORT
} else if (wc->modtype[card] == MOD_TYPE_VPM) {
wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(0xbd); /* DTMF interrupt */
@@ -853,8 +890,8 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe)
int i;
register u8 *chanchunk;
- for (i = 0; i < wc->cards; i += 4) {
- chanchunk = &wc->chans[0 + i]->writechunk[0];
+ for (i = 0; i < wc->avchannels; i += 4) {
+ chanchunk = &wc->chans[0 + i]->chan.writechunk[0];
sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*0] = chanchunk[0];
sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*1] = chanchunk[1];
sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*2] = chanchunk[2];
@@ -864,7 +901,7 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe)
sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*6] = chanchunk[6];
sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*7] = chanchunk[7];
- chanchunk = &wc->chans[1 + i]->writechunk[0];
+ chanchunk = &wc->chans[1 + i]->chan.writechunk[0];
sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*0] = chanchunk[0];
sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*1] = chanchunk[1];
sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*2] = chanchunk[2];
@@ -874,7 +911,7 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe)
sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*6] = chanchunk[6];
sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*7] = chanchunk[7];
- chanchunk = &wc->chans[2 + i]->writechunk[0];
+ chanchunk = &wc->chans[2 + i]->chan.writechunk[0];
sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*0] = chanchunk[0];
sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*1] = chanchunk[1];
sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*2] = chanchunk[2];
@@ -884,7 +921,7 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe)
sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*6] = chanchunk[6];
sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*7] = chanchunk[7];
- chanchunk = &wc->chans[3 + i]->writechunk[0];
+ chanchunk = &wc->chans[3 + i]->chan.writechunk[0];
sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*0] = chanchunk[0];
sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*1] = chanchunk[1];
sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*2] = chanchunk[2];
@@ -899,30 +936,40 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe)
static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char *writechunk)
{
int x,y;
+ struct dahdi_span *s;
/* Calculate Transmission */
if (likely(wc->initialized)) {
- dahdi_transmit(&wc->span);
+ for (x = 0; x < MAX_SPANS; x++) {
+ if (wc->spans[x]) {
+ s = &wc->spans[x]->span;
+ dahdi_transmit(s);
+ }
+ }
insert_tdm_data(wc, writechunk);
}
- for (x=0;x<DAHDI_CHUNKSIZE;x++) {
+ for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
/* Send a sample, as a 32-bit word */
- for (y=0;y < wc->cards;y++) {
- if (!x) {
+
+ /* TODO: ABK: hmm, this was originally mods_per_board, but we
+ * need to worry about all the active "voice" timeslots, since
+ * BRI modules have a different number of TDM channels than
+ * installed modules. */
+ for (y = 0; y < wc->avchannels; y++) {
+ if (!x && y < wc->mods_per_board) {
cmd_checkisr(wc, y);
}
- if (x < 3)
+ if ((x < 3) && (y < wc->mods_per_board))
cmd_dequeue(wc, writechunk, y, x);
}
if (!x)
wc->blinktimer++;
if (wc->vpm100) {
- for (y=24;y<28;y++) {
- if (!x) {
+ for (y = NUM_MODULES; y < NUM_MODULES + NUM_EC; y++) {
+ if (!x)
cmd_checkisr(wc, y);
- }
cmd_dequeue(wc, writechunk, y, x);
}
#ifdef FANCY_ECHOCAN
@@ -939,7 +986,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->desc->ports == 4) && ((wc->ctlreg & 0x10) || (wc->modtype[NUM_CARDS] == MOD_TYPE_NONE))) {
+ if ((wc->desc->ports == 4) && ((wc->ctlreg & 0x10) || (wc->modtype[NUM_MODULES] == MOD_TYPE_NONE))) {
writechunk[EFRAME_SIZE + 2] = 0;
for (y = 0; y < 4; y++) {
if (wc->modtype[y] == MOD_TYPE_NONE)
@@ -958,11 +1005,11 @@ static inline int wctdm_setreg_full(struct wctdm *wc, int card, int addr, int va
int hit=0;
int ret;
- /* if a QRV card, use only its first channel */
- if (wc->modtype[card] == MOD_TYPE_QRV)
- {
- if (card & 3) return(0);
- }
+ /* QRV and BRI cards are only addressed at their first "port" */
+ if ((card & 0x03) && ((wc->modtype[card] == MOD_TYPE_QRV) ||
+ (wc->modtype[card] == MOD_TYPE_BRI)))
+ return 0;
+
do {
spin_lock_irqsave(&wc->reglock, flags);
hit = empty_slot(wc, card);
@@ -984,12 +1031,12 @@ static inline int wctdm_setreg_intr(struct wctdm *wc, int card, int addr, int va
{
return wctdm_setreg_full(wc, card, addr, val, 1);
}
-static inline int wctdm_setreg(struct wctdm *wc, int card, int addr, int val)
+inline int wctdm_setreg(struct wctdm *wc, int card, int addr, int val)
{
return wctdm_setreg_full(wc, card, addr, val, 0);
}
-static inline int wctdm_getreg(struct wctdm *wc, int card, int addr)
+inline int wctdm_getreg(struct wctdm *wc, int card, int addr)
{
unsigned long flags;
int hit;
@@ -1028,14 +1075,15 @@ static inline int wctdm_getreg(struct wctdm *wc, int card, int addr)
return ret;
}
+
static inline unsigned char wctdm_vpm_in(struct wctdm *wc, int unit, const unsigned int addr)
{
- return wctdm_getreg(wc, unit + NUM_CARDS, addr);
+ return wctdm_getreg(wc, unit + NUM_MODULES, addr);
}
static inline void wctdm_vpm_out(struct wctdm *wc, int unit, const unsigned int addr, const unsigned char val)
{
- wctdm_setreg(wc, unit + NUM_CARDS, addr, val);
+ wctdm_setreg(wc, unit + NUM_MODULES, addr, val);
}
/* TODO: this should go in the dahdi_voicebus module... */
@@ -1055,6 +1103,7 @@ static inline void cmd_vpmadt032_retransmit(struct wctdm *wc)
list_move_tail(&cmd->node, &vpmadt032->pending_cmds);
}
spin_unlock_irqrestore(&vpmadt032->list_lock, flags);
+
}
static inline void cmd_retransmit(struct wctdm *wc)
@@ -1064,9 +1113,11 @@ static inline void cmd_retransmit(struct wctdm *wc)
/* Force retransmissions */
spin_lock_irqsave(&wc->reglock, flags);
for (x=0;x<MAX_COMMANDS;x++) {
- for (y=0;y<wc->cards;y++) {
- if (!(wc->cmdq[y].cmds[x] & __CMD_FIN))
- wc->cmdq[y].cmds[x] &= ~(__CMD_TX | (0xff << 24));
+ for (y = 0; y < wc->mods_per_board; y++) {
+ if (wc->modtype[y] != MOD_TYPE_BRI) {
+ if (!(wc->cmdq[y].cmds[x] & __CMD_FIN))
+ wc->cmdq[y].cmds[x] &= ~(__CMD_TX | (0xff << 24));
+ }
}
}
spin_unlock_irqrestore(&wc->reglock, flags);
@@ -1085,8 +1136,8 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe)
int i;
register u8 *chanchunk;
- for (i = 0; i < wc->cards; i += 4) {
- chanchunk = &wc->chans[0 + i]->readchunk[0];
+ for (i = 0; i < wc->avchannels; i += 4) {
+ chanchunk = &wc->chans[0 + i]->chan.readchunk[0];
chanchunk[0] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*0];
chanchunk[1] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*1];
chanchunk[2] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*2];
@@ -1096,7 +1147,7 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe)
chanchunk[6] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*6];
chanchunk[7] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*7];
- chanchunk = &wc->chans[1 + i]->readchunk[0];
+ chanchunk = &wc->chans[1 + i]->chan.readchunk[0];
chanchunk[0] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*0];
chanchunk[1] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*1];
chanchunk[2] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*2];
@@ -1106,7 +1157,7 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe)
chanchunk[6] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*6];
chanchunk[7] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*7];
- chanchunk = &wc->chans[2 + i]->readchunk[0];
+ chanchunk = &wc->chans[2 + i]->chan.readchunk[0];
chanchunk[0] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*0];
chanchunk[1] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*1];
chanchunk[2] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*2];
@@ -1116,7 +1167,7 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe)
chanchunk[6] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*6];
chanchunk[7] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*7];
- chanchunk = &wc->chans[3 + i]->readchunk[0];
+ chanchunk = &wc->chans[3 + i]->chan.readchunk[0];
chanchunk[0] = sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*0];
chanchunk[1] = sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*1];
chanchunk[2] = sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*2];
@@ -1128,9 +1179,10 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe)
}
}
-static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char *readchunk)
+static inline void wctdm_receiveprep(struct wctdm *wc, const u8 *readchunk)
{
int x,y;
+ bool irqmiss = 0;
unsigned char expected;
if (unlikely(!is_good_frame(readchunk)))
@@ -1139,38 +1191,56 @@ static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char *readchunk)
if (likely(wc->initialized))
extract_tdm_data(wc, readchunk);
- for (x=0;x<DAHDI_CHUNKSIZE;x++) {
+ for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
if (x < DAHDI_CHUNKSIZE - 1) {
expected = wc->rxident+1;
wc->rxident = readchunk[EFRAME_SIZE + 1];
if (wc->rxident != expected) {
- wc->span.irqmisses++;
+ irqmiss = 1;
cmd_retransmit(wc);
}
}
- for (y=0;y < wc->cards;y++) {
+ for (y = 0; y < wc->avchannels; y++) {
if (x < 3)
cmd_decipher(wc, readchunk, y);
}
if (wc->vpm100) {
- for (y=NUM_CARDS;y < NUM_CARDS + NUM_EC; y++)
+ for (y = NUM_MODULES; y < NUM_MODULES + NUM_EC; y++)
cmd_decipher(wc, readchunk, y);
- } else if (wc->vpmadt032) {
+ } else if (wc->vpmadt032)
cmd_decipher_vpmadt032(wc, readchunk);
- }
readchunk += (EFRAME_SIZE + EFRAME_GAP);
}
+
/* XXX We're wasting 8 taps. We should get closer :( */
if (likely(wc->initialized)) {
- 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);
+ for (x = 0; x < wc->avchannels; x++) {
+ struct dahdi_chan *c = &wc->chans[x]->chan;
+ dahdi_ec_chunk(c, c->readchunk, c->writechunk);
+ }
+
+ for (x = 0; x < MAX_SPANS; x++) {
+ if (wc->spans[x]) {
+ struct dahdi_span *s = &wc->spans[x]->span;
+#if 1
+ /* Check for digital spans */
+ if (s->chanconfig == b400m_chanconfig) {
+ BUG_ON(!is_hx8(wc));
+ if (s->flags & DAHDI_FLAG_RUNNING)
+ b400m_dchan(s);
+
+ }
+#endif
+ dahdi_receive(s);
+ if (unlikely(irqmiss))
+ ++s->irqmisses;
+ }
}
- dahdi_receive(&wc->span);
}
+
/* Wake up anyone sleeping to read/write a new register */
- wake_up_interruptible(&wc->regq);
+ wake_up_interruptible_all(&wc->regq);
}
static int wait_access(struct wctdm *wc, int card)
@@ -1193,7 +1263,8 @@ static int wait_access(struct wctdm *wc, int card)
}
- if(count > (MAX-1)) dev_notice(&wc->vb.pdev->dev, " ##### Loop error (%02x) #####\n", data);
+ if (count > (MAX-1))
+ dev_notice(&wc->vb.pdev->dev, " ##### Loop error (%02x) #####\n", data);
return 0;
}
@@ -1219,7 +1290,7 @@ static int wctdm_proslic_setreg_indirect(struct wctdm *wc, int card, unsigned ch
if (address == 255)
return 0;
}
- if(!wait_access(wc, card)) {
+ if (!wait_access(wc, card)) {
wctdm_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF));
wctdm_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8));
wctdm_setreg(wc, card, IAA,address);
@@ -1258,9 +1329,10 @@ static int wctdm_proslic_init_indirect_regs(struct wctdm *wc, int card)
{
unsigned char 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))
+ 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;
}
@@ -1275,7 +1347,8 @@ static int wctdm_proslic_verify_indirect_regs(struct wctdm *wc, int card)
for (i = 0; i < ARRAY_SIZE(indirect_regs); i++)
{
- if((j = wctdm_proslic_getreg_indirect(wc, card, (unsigned char) indirect_regs[i].address)) < 0) {
+ j = wctdm_proslic_getreg_indirect(wc, card, (unsigned char) indirect_regs[i].address);
+ if (j < 0) {
dev_notice(&wc->vb.pdev->dev, "Failed to read indirect register %d\n", i);
return -1;
}
@@ -1389,7 +1462,7 @@ static inline void wctdm_qrvdri_check_hook(struct wctdm *wc, int card)
{
b1 = wc->qrvhook[qrvcard + 2];
if (debug) dev_info(&wc->vb.pdev->dev, "QRV channel %d rx state changed to %d\n",qrvcard,wc->qrvhook[qrvcard + 2]);
- dahdi_hooksig(wc->chans[qrvcard],
+ dahdi_hooksig(wc->aspan->span.chans[qrvcard],
(b1) ? DAHDI_RXSIG_OFFHOOK : DAHDI_RXSIG_ONHOOK);
wc->qrvdebtime[card] = 0;
}
@@ -1403,7 +1476,7 @@ if (debug) dev_info(&wc->vb.pdev->dev, "QRV channel %d rx state changed to %d\n"
{
b1 = wc->qrvhook[qrvcard + 3];
if (debug) dev_info(&wc->vb.pdev->dev, "QRV channel %d rx state changed to %d\n",qrvcard + 1,wc->qrvhook[qrvcard + 3]);
- dahdi_hooksig(wc->chans[qrvcard + 1],
+ dahdi_hooksig(wc->aspan->span.chans[qrvcard + 1],
(b1) ? DAHDI_RXSIG_OFFHOOK : DAHDI_RXSIG_ONHOOK);
wc->qrvdebtime[card] = 0;
}
@@ -1430,7 +1503,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
wctdm_setreg_intr(wc, card, 5, 0x8);
}
if (!fxo->offhook) {
- if(fwringdetect || neonmwi_monitor) {
+ if (fwringdetect || neonmwi_monitor) {
/* Look for ring status bits (Ring Detect Signal Negative and
* Ring Detect Signal Positive) to transition back and forth
* some number of times to indicate that a ring is occurring.
@@ -1453,11 +1526,11 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
/* If ring detect signal has transversed */
if (res && res != fxo->lastrdtx) {
/* if there are at least 3 ring polarity transversals */
- if(++fxo->lastrdtx_count >= 2) {
+ if (++fxo->lastrdtx_count >= 2) {
fxo->wasringing = 1;
if (debug)
- dev_info(&wc->vb.pdev->dev, "FW RING on %d/%d!\n", wc->span.spanno, card + 1);
- dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING);
+ dev_info(&wc->vb.pdev->dev, "FW RING on %d/%d!\n", wc->aspan->span.spanno, card + 1);
+ dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_RING);
fxo->ringdebounce = ringdebounce / 16;
} else {
fxo->lastrdtx = res;
@@ -1473,8 +1546,8 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
} else if (--fxo->ringdebounce == 0) {
fxo->wasringing = 0;
if (debug)
- dev_info(&wc->vb.pdev->dev, "FW NO RING on %d/%d!\n", wc->span.spanno, card + 1);
- dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK);
+ dev_info(&wc->vb.pdev->dev, "FW NO RING on %d/%d!\n", wc->aspan->span.spanno, card + 1);
+ dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_OFFHOOK);
}
}
} else {
@@ -1484,9 +1557,9 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
if (fxo->ringdebounce >= DAHDI_CHUNKSIZE * ringdebounce) {
if (!fxo->wasringing) {
fxo->wasringing = 1;
- dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING);
+ dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_RING);
if (debug)
- dev_info(&wc->vb.pdev->dev, "RING on %d/%d!\n", wc->span.spanno, card + 1);
+ dev_info(&wc->vb.pdev->dev, "RING on %d/%d!\n", wc->aspan->span.spanno, card + 1);
}
fxo->ringdebounce = DAHDI_CHUNKSIZE * ringdebounce;
}
@@ -1495,9 +1568,9 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
if (fxo->ringdebounce <= 0) {
if (fxo->wasringing) {
fxo->wasringing = 0;
- dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK);
+ dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_OFFHOOK);
if (debug)
- dev_info(&wc->vb.pdev->dev, "NO RING on %d/%d!\n", wc->span.spanno, card + 1);
+ dev_info(&wc->vb.pdev->dev, "NO RING on %d/%d!\n", wc->aspan->span.spanno, card + 1);
}
fxo->ringdebounce = 0;
}
@@ -1511,12 +1584,11 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
if (fxovoltage) {
if (!(wc->intcount % 100)) {
- dev_info(&wc->vb.pdev->dev, "Port %d: Voltage: %d Debounce %d\n", card + 1,
- b, fxo->battdebounce);
+ dev_info(&wc->vb.pdev->dev, "Port %d: Voltage: %d Debounce %d\n", card + 1, b, fxo->battdebounce);
}
}
- if (unlikely(DAHDI_RXSIG_INITIAL == wc->chans[card]->rxhooksig)) {
+ if (unlikely(DAHDI_RXSIG_INITIAL == wc->aspan->span.chans[card]->rxhooksig)) {
/*
* dahdi-base will set DAHDI_RXSIG_INITIAL after a
* DAHDI_STARTUP or DAHDI_CHANCONFIG ioctl so that new events
@@ -1551,10 +1623,10 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
if (--fxo->battdebounce == 0) {
fxo->battery = BATTERY_LOST;
if (debug)
- dev_info(&wc->vb.pdev->dev, "NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1);
+ dev_info(&wc->vb.pdev->dev, "NO BATTERY on %d/%d!\n", wc->aspan->span.spanno, card + 1);
#ifdef JAPAN
if (!wc->ohdebounce && wc->offhook) {
- dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK);
+ dahdi_hooksig(wc->aspan->chans[card], DAHDI_RXSIG_ONHOOK);
if (debug)
dev_info(&wc->vb.pdev->dev, "Signalled On Hook\n");
#ifdef ZERO_BATT_RING
@@ -1562,7 +1634,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
#endif
}
#else
- dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK);
+ dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_ONHOOK);
/* set the alarm timer, taking into account that part of its time
period has already passed while debouncing occurred */
fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK;
@@ -1592,18 +1664,22 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
/* going to BATTERY_PRESENT, see if we are there yet */
if (--fxo->battdebounce == 0) {
fxo->battery = BATTERY_PRESENT;
- if (debug)
- dev_info(&wc->vb.pdev->dev, "BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1,
- (b < 0) ? "-" : "+");
+ if (debug) {
+ dev_info(&wc->vb.pdev->dev,
+ "BATTERY on %d/%d (%s)!\n",
+ wc->aspan->span.spanno,
+ card + 1,
+ (b < 0) ? "-" : "+");
+ }
#ifdef ZERO_BATT_RING
if (wc->onhook) {
wc->onhook = 0;
- dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK);
+ dahdi_hooksig(wc->aspan->chans[card], DAHDI_RXSIG_OFFHOOK);
if (debug)
dev_info(&wc->vb.pdev->dev, "Signalled Off Hook\n");
}
#else
- dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK);
+ dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_OFFHOOK);
#endif
/* set the alarm timer, taking into account that part of its time
period has already passed while debouncing occurred */
@@ -1633,7 +1709,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
if (--fxo->battalarm == 0) {
/* the alarm timer has expired, so update the battery alarm state
for this channel */
- dahdi_alarm_channel(wc->chans[card], fxo->battery == BATTERY_LOST ? DAHDI_ALARM_RED : DAHDI_ALARM_NONE);
+ dahdi_alarm_channel(wc->aspan->span.chans[card], fxo->battery == BATTERY_LOST ? DAHDI_ALARM_RED : DAHDI_ALARM_NONE);
}
}
@@ -1646,7 +1722,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
fxo->polarity,
fxo->lastpol);
if (fxo->polarity)
- dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY);
+ dahdi_qevent_lock(wc->aspan->span.chans[card], DAHDI_EVENT_POLARITY);
fxo->polarity = fxo->lastpol;
}
}
@@ -1665,8 +1741,8 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
fxo->neonmwi_last_voltage = b;
if (NEONMWI_ON_DEBOUNCE == fxo->neonmwi_debounce) {
fxo->neonmwi_offcounter = neonmwi_offlimit_cycles;
- if(0 == fxo->neonmwi_state) {
- dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_NEONMWI_ACTIVE);
+ if (0 == fxo->neonmwi_state) {
+ dahdi_qevent_lock(wc->aspan->span.chans[card], DAHDI_EVENT_NEONMWI_ACTIVE);
fxo->neonmwi_state = 1;
if (debug)
dev_info(&wc->vb.pdev->dev, "NEON MWI active for card %d\n", card+1);
@@ -1685,7 +1761,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
if (fxo->neonmwi_state && 0 < fxo->neonmwi_offcounter ) {
fxo->neonmwi_offcounter--;
if (0 == fxo->neonmwi_offcounter) {
- dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_NEONMWI_INACTIVE);
+ dahdi_qevent_lock(wc->aspan->span.chans[card], DAHDI_EVENT_NEONMWI_INACTIVE);
fxo->neonmwi_state = 0;
if (debug)
dev_info(&wc->vb.pdev->dev, "NEON MWI cleared for card %d\n", card+1);
@@ -1711,7 +1787,7 @@ static void wctdm_fxs_off_hook(struct wctdm *wc, const int card)
SLIC_LF_ACTIVE_FWD;
break;
}
- dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK);
+ dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_OFFHOOK);
if (robust)
wctdm_init_proslic(wc, card, 1, 0, 1);
fxs->oldrxhook = 1;
@@ -1722,7 +1798,7 @@ static void wctdm_fxs_on_hook(struct wctdm *wc, const int card)
struct fxs *const fxs = &wc->mods[card].fxs;
if (debug & DEBUG_CARD)
dev_info(&wc->vb.pdev->dev, "wctdm: Card %d Going on hook\n", card);
- dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK);
+ dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_ONHOOK);
fxs->oldrxhook = 0;
}
@@ -1772,14 +1848,14 @@ static inline void wctdm_vpm_check(struct wctdm *wc, int x)
{
if (wc->cmdq[x].isrshadow[0]) {
if (debug & DEBUG_ECHOCAN)
- dev_info(&wc->vb.pdev->dev, "VPM: Detected dtmf ON channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[0], x - NUM_CARDS);
+ dev_info(&wc->vb.pdev->dev, "VPM: Detected dtmf ON channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[0], x - NUM_MODULES);
wc->sethook[x] = CMD_WR(0xb9, wc->cmdq[x].isrshadow[0]);
wc->cmdq[x].isrshadow[0] = 0;
/* Cancel most recent lookup, if there is one */
wc->cmdq[x].cmds[USER_COMMANDS+0] = 0x00000000;
} else if (wc->cmdq[x].isrshadow[1]) {
if (debug & DEBUG_ECHOCAN)
- dev_info(&wc->vb.pdev->dev, "VPM: Detected dtmf OFF channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[1], x - NUM_CARDS);
+ dev_info(&wc->vb.pdev->dev, "VPM: Detected dtmf OFF channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[1], x - NUM_MODULES);
wc->sethook[x] = CMD_WR(0xbd, wc->cmdq[x].isrshadow[1]);
wc->cmdq[x].isrshadow[1] = 0;
/* Cancel most recent lookup, if there is one */
@@ -1791,6 +1867,7 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e
struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
struct wctdm *wc = chan->pvt;
+ struct wctdm_chan *wchan = container_of(chan, struct wctdm_chan, chan);
const struct dahdi_echocan_ops *ops;
const struct dahdi_echocan_features *features;
@@ -1810,7 +1887,7 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e
return -EINVAL;
}
- *ec = wc->ec[chan->chanpos - 1];
+ *ec = &wchan->ec;
(*ec)->ops = ops;
(*ec)->features = *features;
@@ -1818,8 +1895,8 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e
int channel;
int unit;
- channel = (chan->chanpos - 1);
- unit = (chan->chanpos - 1) & 0x3;
+ channel = wchan->timeslot;
+ unit = wchan->timeslot & 0x3;
if (wc->vpm100 < 2)
channel >>= 2;
@@ -1835,7 +1912,7 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e
ADT_COMP_ALAW : ADT_COMP_ULAW;
return vpmadt032_echocan_create(wc->vpmadt032,
- chan->chanpos-1, comp, ecp, p);
+ wchan->timeslot, comp, ecp, p);
} else {
return -ENODEV;
}
@@ -1844,14 +1921,15 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e
static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
{
struct wctdm *wc = chan->pvt;
+ struct wctdm_chan *wchan = container_of(chan, struct wctdm_chan, chan);
memset(ec, 0, sizeof(*ec));
if (wc->vpm100) {
int channel;
int unit;
- channel = (chan->chanpos - 1);
- unit = (chan->chanpos - 1) & 0x3;
+ channel = wchan->timeslot;
+ unit = wchan->timeslot & 0x3;
if (wc->vpm100 < 2)
channel >>= 2;
@@ -1860,7 +1938,7 @@ 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->chanpos - 1, ec);
+ vpmadt032_echocan_free(wc->vpmadt032, wchan->timeslot, ec);
}
}
@@ -1915,8 +1993,8 @@ static inline void wctdm_isr_misc(struct wctdm *wc)
return;
}
- for (x=0;x<wc->cards;x++) {
- if (wc->cardflag & (1 << x)) {
+ for (x = 0; x < wc->mods_per_board; x++) {
+ if (wc->modmap & (1 << x)) {
if (wc->modtype[x] == MOD_TYPE_FXS) {
wctdm_isr_misc_fxs(wc, x);
} else if (wc->modtype[x] == MOD_TYPE_FXO) {
@@ -1927,9 +2005,8 @@ static inline void wctdm_isr_misc(struct wctdm *wc)
}
}
if (wc->vpm100 > 0) {
- for (x=NUM_CARDS;x<NUM_CARDS+NUM_EC;x++) {
+ for (x = NUM_MODULES; x < NUM_MODULES+NUM_EC; x++)
wctdm_vpm_check(wc, x);
- }
}
}
@@ -1954,6 +2031,55 @@ static void handle_transmit(struct voicebus *vb, struct list_head *buffers)
}
}
+struct sframe_packet {
+ struct list_head node;
+ u8 sframe[SFRAME_SIZE];
+};
+
+/**
+ * handle_hx8_bootmode_receive() - queue up the receive packet for later...
+ *
+ * This function is called from interrupt context and isn't optimal, but it's
+ * not the main code path.
+ */
+static void handle_hx8_bootmode_receive(struct wctdm *wc, const void *vbb)
+{
+ struct sframe_packet *frame;
+
+ frame = kzalloc(sizeof(*frame), GFP_ATOMIC);
+ if (unlikely(!frame)) {
+ WARN_ON(1);
+ return;
+ }
+
+ memcpy(frame->sframe, vbb, sizeof(frame->sframe));
+ spin_lock(&wc->frame_list_lock);
+ list_add_tail(&frame->node, &wc->frame_list);
+ spin_unlock(&wc->frame_list_lock);
+
+ /* Wake up anyone waiting for a new packet. */
+ wake_up(&wc->regq);
+ return;
+}
+
+static void handle_hx8_receive(struct voicebus *vb, struct list_head *buffers)
+{
+ struct wctdm *wc = container_of(vb, struct wctdm, vb);
+ struct vbb *vbb;
+ list_for_each_entry(vbb, buffers, entry)
+ handle_hx8_bootmode_receive(wc, vbb->data);
+}
+
+static void handle_hx8_transmit(struct voicebus *vb, struct list_head *buffers)
+{
+ struct vbb *vbb, *n;
+
+ list_for_each_entry_safe(vbb, n, buffers, entry) {
+ list_del(&vbb->entry);
+ kmem_cache_free(voicebus_vbb_cache, vbb);
+ }
+}
+
static int wctdm_voicedaa_insane(struct wctdm *wc, int card)
{
int blah;
@@ -1972,7 +2098,7 @@ static int wctdm_proslic_insane(struct wctdm *wc, int card)
insane_report=0;
blah = wctdm_getreg(wc, card, 0);
- if (debug & DEBUG_CARD)
+ if (blah != 0xff && (debug & DEBUG_CARD))
dev_info(&wc->vb.pdev->dev, "ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf));
#if 0
@@ -2045,7 +2171,7 @@ static int wctdm_proslic_powerleak_test(struct wctdm *wc, int card)
/* Wait for one second */
origjiffies = jiffies;
- while((vbat = wctdm_getreg(wc, card, 82)) > 0x6) {
+ while ((vbat = wctdm_getreg(wc, card, 82)) > 0x6) {
if ((jiffies - origjiffies) >= (HZ/2))
break;;
}
@@ -2079,7 +2205,7 @@ static int wctdm_powerup_proslic(struct wctdm *wc, int card, int fast)
if (fast)
return 0;
- while((vbat = wctdm_getreg(wc, card, 82)) < 0xc0) {
+ while ((vbat = wctdm_getreg(wc, card, 82)) < 0xc0) {
/* Wait no more than 500ms */
if ((jiffies - origjiffies) > HZ/2) {
break;
@@ -2110,25 +2236,6 @@ static int wctdm_powerup_proslic(struct wctdm *wc, int card, int fast)
/* Engage DC-DC converter */
wctdm_setreg(wc, card, 93, 0x19 /* was 0x19 */);
-#if 0
- origjiffies = jiffies;
- while(0x80 & wctdm_getreg(wc, card, 93)) {
- if ((jiffies - origjiffies) > 2 * HZ) {
- dev_info(&wc->vb.pdev->dev, "Timeout waiting for DC-DC calibration on module %d\n", card);
- return -1;
- }
- }
-
-#if 0
- /* Wait a full two seconds */
- while((jiffies - origjiffies) < 2 * HZ);
-
- /* Just check to be sure */
- vbat = wctdm_getreg(wc, card, 82);
- dev_info(&wc->vb.pdev->dev, "ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
- card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ)));
-#endif
-#endif
return 0;
}
@@ -2147,8 +2254,8 @@ static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card)
wctdm_setreg(wc, card, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM
origjiffies=jiffies;
- while( wctdm_getreg(wc,card,96)!=0 ){
- if((jiffies-origjiffies)>80)
+ while (wctdm_getreg(wc, card, 96) != 0) {
+ if ((jiffies-origjiffies) > 80)
return -1;
}
//Initialized DR 98 and 99 to get consistant results.
@@ -2158,7 +2265,7 @@ static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card)
/*******************************This is also available as a function *******************************************/
// Delay 10ms
origjiffies=jiffies;
- while((jiffies-origjiffies)<1);
+ while ((jiffies-origjiffies) < 1);
wctdm_proslic_setreg_indirect(wc, card, 88,0);
wctdm_proslic_setreg_indirect(wc,card,89,0);
wctdm_proslic_setreg_indirect(wc,card,90,0);
@@ -2173,8 +2280,8 @@ static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card)
{
wctdm_setreg(wc, card, 98,i);
origjiffies=jiffies;
- while((jiffies-origjiffies)<4);
- if((wctdm_getreg(wc,card,88)) == 0)
+ while ((jiffies-origjiffies) < 4);
+ if ((wctdm_getreg(wc, card, 88)) == 0)
break;
} // for
@@ -2182,15 +2289,16 @@ static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card)
{
wctdm_setreg(wc, card, 99,i);
origjiffies=jiffies;
- while((jiffies-origjiffies)<4);
- if((wctdm_getreg(wc,card,89)) == 0)
+ while ((jiffies-origjiffies) < 4);
+ if ((wctdm_getreg(wc, card, 89)) == 0)
break;
}//for
/*******************************The preceding is the manual gain mismatch calibration****************************/
/**********************************The following is the longitudinal Balance Cal***********************************/
wctdm_setreg(wc,card,64,1);
- while((jiffies-origjiffies)<10); // Sleep 100?
+ while ((jiffies-origjiffies) < 10) /* Sleep 100? */
+ ;
wctdm_setreg(wc, card, 64, 0);
wctdm_setreg(wc, card, 23, 0x4); // enable interrupt for the balance Cal
@@ -2220,7 +2328,7 @@ static int wctdm_proslic_calibrate(struct wctdm *wc, int card)
/* Wait for it to finish */
origjiffies = jiffies;
- while(wctdm_getreg(wc, card, 96)) {
+ while (wctdm_getreg(wc, card, 96)) {
if ((jiffies - origjiffies) > 2 * HZ) {
dev_notice(&wc->vb.pdev->dev, "Timeout waiting for calibration of module %d\n", card);
return -1;
@@ -2237,11 +2345,12 @@ static int wctdm_proslic_calibrate(struct wctdm *wc, int card)
return 0;
}
-static void wait_just_a_bit(int foo)
+void wait_just_a_bit(int foo)
{
long newjiffies;
newjiffies = jiffies + foo;
- while(jiffies < newjiffies);
+ while (jiffies < newjiffies)
+ ;
}
/*********************************************************************
@@ -2355,18 +2464,36 @@ static int set_vmwi(struct wctdm *wc, int chan_idx)
return 0;
}
+static void wctdm_voicedaa_set_ts(struct wctdm *wc, int card, int ts)
+{
+ wctdm_setreg(wc, card, 34, (ts * 8) & 0xff);
+ wctdm_setreg(wc, card, 35, (ts * 8) >> 8);
+ wctdm_setreg(wc, card, 36, (ts * 8) & 0xff);
+ wctdm_setreg(wc, card, 37, (ts * 8) >> 8);
+
+ if (debug)
+ dev_info(&wc->vb.pdev->dev, "voicedaa: card %d new timeslot: %d\n", card + 1, ts);
+}
+
static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, int sane)
{
unsigned char reg16=0, reg26=0, reg30=0, reg31=0;
+ unsigned long flags;
long newjiffies;
- if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) return -2;
+ if ((wc->modtype[card & 0xfc] == MOD_TYPE_QRV) ||
+ (wc->modtype[card & 0xfc] == MOD_TYPE_BRI))
+ return -2;
+ spin_lock_irqsave(&wc->reglock, flags);
wc->modtype[card] = MOD_TYPE_NONE;
+ spin_unlock_irqrestore(&wc->reglock, flags);
/* Wait just a bit */
wait_just_a_bit(HZ/10);
+ spin_lock_irqsave(&wc->reglock, flags);
wc->modtype[card] = MOD_TYPE_FXO;
+ spin_unlock_irqrestore(&wc->reglock, flags);
wait_just_a_bit(HZ/10);
if (!sane && wctdm_voicedaa_insane(wc, card))
@@ -2391,7 +2518,7 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual,
reg16 |= (fxo_modes[_opermode].rt);
wctdm_setreg(wc, card, 16, reg16);
- if(fwringdetect || neonmwi_monitor) {
+ if (fwringdetect || neonmwi_monitor) {
/* Enable ring detector full-wave rectifier mode */
wctdm_setreg(wc, card, 18, 2);
wctdm_setreg(wc, card, 24, 0);
@@ -2421,11 +2548,7 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual,
reg31 |= (fxo_modes[_opermode].ohs2 << 3);
wctdm_setreg(wc, card, 31, reg31);
- /* Set Transmit/Receive timeslot */
- wctdm_setreg(wc, card, 34, (card * 8) & 0xff);
- wctdm_setreg(wc, card, 35, (card * 8) >> 8);
- wctdm_setreg(wc, card, 36, (card * 8) & 0xff);
- wctdm_setreg(wc, card, 37, (card * 8) >> 8);
+ wctdm_voicedaa_set_ts(wc, card, card);
/* Enable ISO-Cap */
wctdm_setreg(wc, card, 6, 0x00);
@@ -2433,7 +2556,7 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual,
/* Wait 1000ms for ISO-cap to come up */
newjiffies = jiffies;
newjiffies += 2 * HZ;
- while((jiffies < newjiffies) && !(wctdm_getreg(wc, card, 11) & 0xf0))
+ while ((jiffies < newjiffies) && !(wctdm_getreg(wc, card, 11) & 0xf0))
wait_just_a_bit(HZ/10);
if (!(wctdm_getreg(wc, card, 11) & 0xf0)) {
@@ -2451,23 +2574,41 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual,
wctdm_set_hwgain(wc, card, fxotxgain, 1);
wctdm_set_hwgain(wc, card, fxorxgain, 0);
- if(debug)
+ if (debug)
dev_info(&wc->vb.pdev->dev, "DEBUG fxotxgain:%i.%i fxorxgain:%i.%i\n", (wctdm_getreg(wc, card, 38)/16) ? -(wctdm_getreg(wc, card, 38) - 16) : wctdm_getreg(wc, card, 38), (wctdm_getreg(wc, card, 40)/16) ? -(wctdm_getreg(wc, card, 40) - 16) : wctdm_getreg(wc, card, 40), (wctdm_getreg(wc, card, 39)/16) ? -(wctdm_getreg(wc, card, 39) - 16): wctdm_getreg(wc, card, 39), (wctdm_getreg(wc, card, 41)/16)?-(wctdm_getreg(wc, card, 41) - 16) : wctdm_getreg(wc, card, 41));
return 0;
}
+static void wctdm_proslic_set_ts(struct wctdm *wc, int card, int ts)
+{
+ wctdm_setreg(wc, card, 2, (ts * 8) & 0xff); /* Tx Start count low byte 0 */
+ wctdm_setreg(wc, card, 3, (ts * 8) >> 8); /* Tx Start count high byte 0 */
+ wctdm_setreg(wc, card, 4, (ts * 8) & 0xff); /* Rx Start count low byte 0 */
+ wctdm_setreg(wc, card, 5, (ts * 8) >> 8); /* Rx Start count high byte 0 */
+
+ if (debug)
+ dev_info(&wc->vb.pdev->dev, "proslic: card %d new timeslot: %d\n", card + 1, ts);
+}
+
static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, int sane)
{
unsigned short tmp[5];
+ unsigned long flags;
unsigned char r19,r9;
int x;
int fxsmode=0;
if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) return -2;
+ spin_lock_irqsave(&wc->reglock, flags);
+ wc->modtype[card] = MOD_TYPE_FXS;
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+ wait_just_a_bit(HZ/10);
+
/* Sanity check the ProSLIC */
if (!sane && wctdm_proslic_insane(wc, card))
return -2;
@@ -2537,7 +2678,7 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual,
}
#ifndef NO_CALIBRATION
/* Perform calibration */
- if(manual) {
+ if (manual) {
if (wctdm_proslic_manual_calibrate(wc, card)) {
//dev_notice(&wc->vb.pdev->dev, "Proslic failed on Manual Calibration\n");
if (wctdm_proslic_manual_calibrate(wc, card)) {
@@ -2548,7 +2689,7 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual,
}
}
else {
- if(wctdm_proslic_calibrate(wc, card)) {
+ if (wctdm_proslic_calibrate(wc, card)) {
//dev_notice(&wc->vb.pdev->dev, "ProSlic died on Auto Calibration.\n");
if (wctdm_proslic_calibrate(wc, card)) {
dev_notice(&wc->vb.pdev->dev, "Proslic Failed on Second Attempt to Auto Calibrate\n");
@@ -2603,11 +2744,10 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual,
wctdm_setreg(wc, card, 1, 0x20);
else
wctdm_setreg(wc, card, 1, 0x28);
- // U-Law 8-bit interface
- wctdm_setreg(wc, card, 2, (card * 8) & 0xff); // Tx Start count low byte 0
- wctdm_setreg(wc, card, 3, (card * 8) >> 8); // Tx Start count high byte 0
- wctdm_setreg(wc, card, 4, (card * 8) & 0xff); // Rx Start count low byte 0
- wctdm_setreg(wc, card, 5, (card * 8) >> 8); // Rx Start count high byte 0
+
+ /* U-Law 8-bit interface */
+ wctdm_proslic_set_ts(wc, card, card);
+
wctdm_setreg(wc, card, 18, 0xff); // clear all interrupt
wctdm_setreg(wc, card, 19, 0xff);
wctdm_setreg(wc, card, 20, 0xff);
@@ -2707,10 +2847,23 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual,
return 0;
}
+static void wctdm_qrvdri_set_ts(struct wctdm *wc, int card, int ts)
+{
+ wctdm_setreg(wc, card, 0x13, ts + 0x80); /* codec 2 tx, ts0 */
+ wctdm_setreg(wc, card, 0x17, ts + 0x80); /* codec 0 rx, ts0 */
+ wctdm_setreg(wc, card, 0x14, ts + 0x81); /* codec 1 tx, ts1 */
+ wctdm_setreg(wc, card, 0x18, ts + 0x81); /* codec 1 rx, ts1 */
+
+ if (debug)
+ dev_info(&wc->vb.pdev->dev, "qrvdri: card %d new timeslot: %d\n", card + 1, ts);
+}
+
static int wctdm_init_qrvdri(struct wctdm *wc, int card)
{
unsigned char x,y;
- unsigned long endjif;
+
+ if (MOD_TYPE_BRI == wc->modtype[card & 0xfc])
+ return -2;
/* have to set this, at least for now */
wc->modtype[card] = MOD_TYPE_QRV;
@@ -2743,18 +2896,16 @@ static int wctdm_init_qrvdri(struct wctdm *wc, int card)
wc->modtype[card] = MOD_TYPE_NONE;
return 1;
}
- for(x = 0; x < 0x30; x++)
+ for (x = 0; x < 0x30; x++)
{
if ((x >= 0x1c) && (x <= 0x1e)) wctdm_setreg(wc,card,x,0xff);
else wctdm_setreg(wc,card,x,0);
}
wctdm_setreg(wc,card,0,0x80);
- endjif = jiffies + (HZ/10);
- while(endjif > jiffies);
+ msleep(100);
wctdm_setreg(wc,card,0,0x10);
wctdm_setreg(wc,card,0,0x10);
- endjif = jiffies + (HZ/10);
- while(endjif > jiffies);
+ msleep(100);
/* set up modes */
wctdm_setreg(wc,card,0,0x1c);
/* set up I/O directions */
@@ -2765,11 +2916,9 @@ static int wctdm_init_qrvdri(struct wctdm *wc, int card)
wctdm_setreg(wc,card,3,0x11); /* D0-7 */
wctdm_setreg(wc,card,4,0xa); /* D8-11 */
wctdm_setreg(wc,card,7,0); /* CS outputs */
- /* set up timeslots */
- wctdm_setreg(wc,card,0x13,card + 0x80); /* codec 2 tx, ts0 */
- wctdm_setreg(wc,card,0x17,card + 0x80); /* codec 0 rx, ts0 */
- wctdm_setreg(wc,card,0x14,card + 0x81); /* codec 1 tx, ts1 */
- wctdm_setreg(wc,card,0x18,card + 0x81); /* codec 1 rx, ts1 */
+
+ wctdm_qrvdri_set_ts(wc, card, card);
+
/* set up for max gains */
wctdm_setreg(wc,card,0x26,0x24);
wctdm_setreg(wc,card,0x27,0x24);
@@ -2782,9 +2931,9 @@ static int wctdm_init_qrvdri(struct wctdm *wc, int card)
static void qrv_dosetup(struct dahdi_chan *chan,struct wctdm *wc)
{
-int qrvcard;
-unsigned char r;
-long l;
+ int qrvcard;
+ unsigned char r;
+ long l;
/* actually do something with the values */
qrvcard = (chan->chanpos - 1) & 0xfc;
@@ -2839,6 +2988,7 @@ long l;
return;
}
+
static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data)
{
struct wctdm_stats stats;
@@ -3159,19 +3309,29 @@ static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long
static int wctdm_open(struct dahdi_chan *chan)
{
- struct wctdm *wc = chan->pvt;
- int channo = chan->chanpos - 1;
+ struct wctdm *wc;
+ int channo;
unsigned long flags;
- if (!(wc->cardflag & (1 << (chan->chanpos - 1))))
+ wc = chan->pvt;
+ channo = chan->chanpos - 1;
+
+#if 0
+ if (!(wc->modmap & (1 << (chan->chanpos - 1))))
+ return -ENODEV;
+ if (wc->dead)
return -ENODEV;
+#endif
+ wc->usecount++;
- /* Reset the mwi indicators */
- spin_lock_irqsave(&wc->reglock, flags);
- wc->mods[channo].fxo.neonmwi_debounce = 0;
- wc->mods[channo].fxo.neonmwi_offcounter = 0;
- wc->mods[channo].fxo.neonmwi_state = 0;
- spin_unlock_irqrestore(&wc->reglock, flags);
+ if (wc->modtype[channo] == MOD_TYPE_FXO) {
+ /* Reset the mwi indicators */
+ spin_lock_irqsave(&wc->reglock, flags);
+ wc->mods[channo].fxo.neonmwi_debounce = 0;
+ wc->mods[channo].fxo.neonmwi_offcounter = 0;
+ wc->mods[channo].fxo.neonmwi_state = 0;
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ }
return 0;
}
@@ -3185,17 +3345,17 @@ static int wctdm_watchdog(struct dahdi_span *span, int event)
static int wctdm_close(struct dahdi_chan *chan)
{
- struct wctdm *wc = chan->pvt;
+ struct wctdm *wc;
int x;
signed char reg;
- for (x=0;x<wc->cards;x++) {
- if (wc->modtype[x] == MOD_TYPE_FXS) {
+
+ wc = chan->pvt;
+ for (x = 0; x < wc->mods_per_board; x++) {
+ if (MOD_TYPE_FXS == wc->modtype[x]) {
wc->mods[x].fxs.idletxhookstate =
POLARITY_XOR(x) ? SLIC_LF_ACTIVE_REV :
SLIC_LF_ACTIVE_FWD;
- }
- if (wc->modtype[x] == MOD_TYPE_QRV)
- {
+ } else if (MOD_TYPE_QRV == wc->modtype[x]) {
int qrvcard = x & 0xfc;
wc->qrvhook[x] = 0;
@@ -3206,8 +3366,10 @@ static int wctdm_close(struct dahdi_chan *chan)
wc->txgain[x] = 3599;
wc->rxgain[x] = 1199;
reg = 0;
- if (!wc->qrvhook[qrvcard]) reg |= 1;
- if (!wc->qrvhook[qrvcard + 1]) reg |= 0x10;
+ if (!wc->qrvhook[qrvcard])
+ reg |= 1;
+ if (!wc->qrvhook[qrvcard + 1])
+ reg |= 0x10;
wc->sethook[qrvcard] = CMD_WR(3, reg);
qrv_dosetup(chan,wc);
}
@@ -3219,8 +3381,8 @@ 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;
- int reg=0,qrvcard;
if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_QRV) {
qrvcard = (chan->chanpos - 1) & 0xfc;
switch(txsig) {
@@ -3255,7 +3417,7 @@ static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig)
default:
dev_notice(&wc->vb.pdev->dev, "wctdm24xxp: Can't set tx state to %d\n", txsig);
}
- } else { /* Else this is an fxs port */
+ } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) {
unsigned long flags;
struct fxs *const fxs = &wc->mods[chan->chanpos - 1].fxs;
spin_lock_irqsave(&fxs->lasttxhooklock, flags);
@@ -3309,6 +3471,7 @@ static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig)
spin_unlock_irqrestore(&fxs->lasttxhooklock, flags);
if (debug & DEBUG_CARD)
dev_info(&wc->vb.pdev->dev, "Setting FXS hook state to %d (%02x)\n", txsig, reg);
+ } else {
}
return 0;
}
@@ -3337,7 +3500,7 @@ static void wctdm_dacs_connect(struct wctdm *wc, int srccard, int dstcard)
/* proslic */
wctdm_setreg(wc, srccard, PCM_XMIT_START_COUNT_LSB, ((srccard+24) * 8) & 0xff);
wctdm_setreg(wc, srccard, PCM_XMIT_START_COUNT_MSB, ((srccard+24) * 8) >> 8);
- } else if(wc->modtype[srccard] == MOD_TYPE_FXO) {
+ } else if (wc->modtype[srccard] == MOD_TYPE_FXO) {
/* daa */
wctdm_setreg(wc, srccard, 34, ((srccard+24) * 8) & 0xff); /* TX */
wctdm_setreg(wc, srccard, 35, ((srccard+24) * 8) >> 8); /* TX */
@@ -3348,7 +3511,7 @@ static void wctdm_dacs_connect(struct wctdm *wc, int srccard, int dstcard)
/* proslic */
wctdm_setreg(wc, dstcard, PCM_RCV_START_COUNT_LSB, ((srccard+24) * 8) & 0xff);
wctdm_setreg(wc, dstcard, PCM_RCV_START_COUNT_MSB, ((srccard+24) * 8) >> 8);
- } else if(wc->modtype[dstcard] == MOD_TYPE_FXO) {
+ } else if (wc->modtype[dstcard] == MOD_TYPE_FXO) {
/* daa */
wctdm_setreg(wc, dstcard, 36, ((srccard+24) * 8) & 0xff); /* RX */
wctdm_setreg(wc, dstcard, 37, ((srccard+24) * 8) >> 8); /* RX */
@@ -3363,10 +3526,10 @@ static void wctdm_dacs_disconnect(struct wctdm *wc, int card)
dev_info(&wc->vb.pdev->dev, "wctdm_dacs_disconnect: restoring TX for %d and RX for %d\n",wc->dacssrc[card], card);
/* restore TX (source card) */
- if(wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXS){
+ if (wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXS) {
wctdm_setreg(wc, wc->dacssrc[card], PCM_XMIT_START_COUNT_LSB, (wc->dacssrc[card] * 8) & 0xff);
wctdm_setreg(wc, wc->dacssrc[card], PCM_XMIT_START_COUNT_MSB, (wc->dacssrc[card] * 8) >> 8);
- } else if(wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXO){
+ } else if (wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXO) {
wctdm_setreg(wc, card, 34, (card * 8) & 0xff);
wctdm_setreg(wc, card, 35, (card * 8) >> 8);
} else {
@@ -3374,10 +3537,10 @@ static void wctdm_dacs_disconnect(struct wctdm *wc, int card)
}
/* restore RX (this card) */
- if(wc->modtype[card] == MOD_TYPE_FXS){
- wctdm_setreg(wc, card, PCM_RCV_START_COUNT_LSB, (card * 8) & 0xff);
- wctdm_setreg(wc, card, PCM_RCV_START_COUNT_MSB, (card * 8) >> 8);
- } else if(wc->modtype[card] == MOD_TYPE_FXO){
+ if (MOD_TYPE_FXS == wc->modtype[card]) {
+ wctdm_setreg(wc, card, PCM_RCV_START_COUNT_LSB, (card * 8) & 0xff);
+ wctdm_setreg(wc, card, PCM_RCV_START_COUNT_MSB, (card * 8) >> 8);
+ } else if (MOD_TYPE_FXO == wc->modtype[card]) {
wctdm_setreg(wc, card, 36, (card * 8) & 0xff);
wctdm_setreg(wc, card, 37, (card * 8) >> 8);
} else {
@@ -3392,12 +3555,12 @@ static int wctdm_dacs(struct dahdi_chan *dst, struct dahdi_chan *src)
{
struct wctdm *wc;
- if(!nativebridge)
+ if (!nativebridge)
return 0; /* should this return -1 since unsuccessful? */
wc = dst->pvt;
- if(src) {
+ if (src) {
wctdm_dacs_connect(wc, src->chanpos - 1, dst->chanpos - 1);
if (debug)
dev_info(&wc->vb.pdev->dev, "dacs connecct: %d -> %d!\n\n", src->chanpos, dst->chanpos);
@@ -3409,77 +3572,177 @@ static int wctdm_dacs(struct dahdi_chan *dst, struct dahdi_chan *src)
return 0;
}
-static int wctdm_initialize(struct wctdm *wc)
+static struct wctdm_chan *wctdm_init_chan(struct wctdm *wc, struct wctdm_span *s, int chanoffset, int channo)
+{
+ struct wctdm_chan *c;
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c)
+ return NULL;
+
+ /* Do not change the procfs representation for non-hx8 cards. */
+ if (is_digital_span(s)) {
+ sprintf(c->chan.name, "WCBRI/%d/%d/%d", wc->pos, s->spanno,
+ channo);
+ } else {
+ sprintf(c->chan.name, "WCTDM/%d/%d", wc->pos, channo);
+ }
+
+ c->chan.chanpos = channo+1;
+ c->chan.span = &s->span;
+ c->chan.pvt = wc;
+ c->timeslot = chanoffset + channo;
+ return c;
+}
+
+#if 0
+/**
+ * wctdm_span_count() - Return the number of spans exported by this board.
+ *
+ * This is only called during initialization so let's just count the spans each
+ * time we need this information as opposed to storing another variable in the
+ * wctdm structure.
+ */
+static int wctdm_span_count(const struct wctdm *wc)
+{
+ int i;
+ int count = 0;
+ for (i = 0; i < MAX_SPANS; ++i) {
+ if (wc->spans[i])
+ ++count;
+ }
+ return count;
+}
+#endif
+
+static struct wctdm_span *wctdm_init_span(struct wctdm *wc, int spanno, int chanoffset, int chancount, int digital_span)
{
int x;
+ static int first = 1;
struct pci_dev *pdev = wc->vb.pdev;
+ struct wctdm_chan *c;
+ struct wctdm_span *s;
+ static int spancount;
+
+ s = kzalloc(sizeof(*s), GFP_KERNEL);
+ if (!s)
+ return NULL;
/* DAHDI stuff */
- sprintf(wc->span.name, "WCTDM/%d", wc->pos);
- 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,
+ s->span.owner = THIS_MODULE;
+ s->span.offset = spanno;
+
+ s->spanno = spancount++;
+
+ /* Do not change the procfs representation for non-hx8 cards. */
+ if (digital_span)
+ sprintf(s->span.name, "WCBRI/%d/%d", wc->pos, s->spanno);
+ else
+ sprintf(s->span.name, "WCTDM/%d", wc->pos);
+
+ snprintf(s->span.desc, sizeof(s->span.desc) - 1, "%s Board %d", wc->desc->name, wc->pos + 1);
+ snprintf(s->span.location, sizeof(s->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->desc->name,
- sizeof(wc->span.devicetype) - 1);
+ s->span.manufacturer = "Digium";
+ strncpy(s->span.devicetype, wc->desc->name, sizeof(s->span.devicetype) - 1);
+
if (alawoverride) {
- dev_info(&wc->vb.pdev->dev, "ALAW override parameter detected. Device will be operating in ALAW\n");
- wc->span.deflaw = DAHDI_LAW_ALAW;
+ s->span.deflaw = DAHDI_LAW_ALAW;
+ if (first) {
+ dev_info(&wc->vb.pdev->dev, "ALAW override parameter detected. Device will be operating in ALAW\n");
+ first = 0;
+ }
+ } else if (digital_span) {
+ /* BRIs are in A-law */
+ s->span.deflaw = DAHDI_LAW_ALAW;
+ } else {
+ /* Analog mods are ulaw unless alawoverride is used */
+ s->span.deflaw = DAHDI_LAW_MULAW;
+ }
+
+ if (digital_span) {
+ s->span.hdlc_hard_xmit = wctdm_hdlc_hard_xmit;
+ s->span.spanconfig = b400m_spanconfig;
+ s->span.chanconfig = b400m_chanconfig;
+ s->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4;
+ s->span.linecompat |= DAHDI_CONFIG_ESF | DAHDI_CONFIG_HDB3 | DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4;
+ s->span.linecompat |= DAHDI_CONFIG_NTTE | DAHDI_CONFIG_TERM;
} else {
- wc->span.deflaw = DAHDI_LAW_MULAW;
- }
- for (x=0;x<wc->cards;x++) {
- sprintf(wc->chans[x]->name, "WCTDM/%d/%d", wc->pos, x);
- wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR;
- wc->chans[x]->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR;
- wc->chans[x]->chanpos = x+1;
- wc->chans[x]->pvt = wc;
- }
- wc->span.owner = THIS_MODULE;
- wc->span.chans = wc->chans;
- wc->span.channels = wc->desc->ports;
- wc->span.irq = pdev->irq;
- wc->span.hooksig = wctdm_hooksig;
- wc->span.open = wctdm_open;
- wc->span.close = wctdm_close;
- wc->span.flags = DAHDI_FLAG_RBS;
- wc->span.ioctl = wctdm_ioctl;
- wc->span.watchdog = wctdm_watchdog;
- wc->span.dacs= wctdm_dacs;
+ s->span.hooksig = wctdm_hooksig;
+ s->span.flags = DAHDI_FLAG_RBS;
+ /* analog sigcap handled in fixup_analog_span() */
+ }
+
+ s->span.chans = kmalloc(sizeof(struct dahdi_chan *) * chancount, GFP_KERNEL);
+ if (!s->span.chans)
+ return NULL;
+
+ /* allocate channels for the span */
+ for (x = 0; x < chancount; x++) {
+ c = wctdm_init_chan(wc, s, chanoffset, x);
+ if (!c)
+ return NULL;
+ wc->chans[chanoffset + x] = c;
+ s->span.chans[x] = &c->chan;
+ }
+
+ s->span.channels = chancount;
+ s->span.irq = pdev->irq;
+ s->span.open = wctdm_open;
+ s->span.close = wctdm_close;
+ s->span.ioctl = wctdm_ioctl;
+ s->span.watchdog = wctdm_watchdog;
+ s->span.dacs = wctdm_dacs;
+
+ if (digital_span) {
+ wc->chans[chanoffset + 0]->chan.sigcap = DAHDI_SIG_CLEAR;
+ wc->chans[chanoffset + 1]->chan.sigcap = DAHDI_SIG_CLEAR;
+ wc->chans[chanoffset + 2]->chan.sigcap = DAHDI_SIG_HARDHDLC;
+ }
#ifdef VPM_SUPPORT
if (vpmsupport)
- wc->span.echocan_create = echocan_create;
+ s->span.echocan_create = echocan_create;
#endif
- init_waitqueue_head(&wc->span.maintq);
- wc->span.pvt = wc;
- return 0;
+ init_waitqueue_head(&s->span.maintq);
+ wc->spans[spanno] = s;
+ return s;
}
-static void wctdm_post_initialize(struct wctdm *wc)
+static void wctdm_fixup_analog_span(struct wctdm *wc, int spanno)
{
- int x;
+ struct dahdi_span *s;
+ int x, y;
/* Finalize signalling */
- for (x = 0; x <wc->cards; x++) {
- if (wc->cardflag & (1 << x)) {
- if (wc->modtype[x] == MOD_TYPE_FXO)
- wc->chans[x]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR;
- else if (wc->modtype[x] == MOD_TYPE_FXS)
- wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR;
- else if (wc->modtype[x] == MOD_TYPE_QRV)
- wc->chans[x]->sigcap = DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR;
- } else if (!(wc->chans[x]->sigcap & DAHDI_SIG_BROKEN)) {
- wc->chans[x]->sigcap = 0;
+ y = 0;
+ s = &wc->spans[spanno]->span;
+
+ for (x = 0; x < wc->desc->ports; x++) {
+ if (debug) {
+ dev_info(&wc->vb.pdev->dev,
+ "fixup_analog: x=%d, y=%d modtype=%d, "
+ "s->chans[%d]=%p\n", x, y, wc->modtype[x],
+ y, s->chans[y]);
}
+ if (wc->modtype[x] == MOD_TYPE_FXO)
+ s->chans[y++]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR;
+ else if (wc->modtype[x] == MOD_TYPE_FXS)
+ s->chans[y++]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR;
+ else if (wc->modtype[x] == MOD_TYPE_QRV)
+ s->chans[y++]->sigcap = DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR;
+ else
+ s->chans[y++]->sigcap = 0;
}
- if (wc->vpm100) {
- strncat(wc->span.devicetype, " (VPM100M)", sizeof(wc->span.devicetype) - 1);
- } else if (wc->vpmadt032) {
- strncat(wc->span.devicetype, " (VPMADT032)", sizeof(wc->span.devicetype) - 1);
+ for (x = 0; x < MAX_SPANS; x++) {
+ if (!wc->spans[x])
+ continue;
+ if (wc->vpm100)
+ strncat(wc->spans[x]->span.devicetype, " (VPM100M)", sizeof(wc->spans[x]->span.devicetype) - 1);
+ else if (wc->vpmadt032)
+ strncat(wc->spans[x]->span.devicetype, " (VPMADT032)", sizeof(wc->spans[x]->span.devicetype) - 1);
}
}
@@ -3684,173 +3947,659 @@ static void get_default_portconfig(GpakPortConfig_t *portconfig)
portconfig->EightSlotMask3 = 0x0000;
}
-static int wctdm_locate_modules(struct wctdm *wc)
+static int wctdm_initialize_vpmadt032(struct wctdm *wc)
+{
+ int x;
+ int res;
+ unsigned long flags;
+ struct vpmadt032_options options;
+
+ GpakPortConfig_t portconfig;
+
+ spin_lock_irqsave(&wc->reglock, flags);
+ for (x = NUM_MODULES; x < NUM_MODULES + NUM_EC; x++)
+ wc->modtype[x] = MOD_TYPE_NONE;
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+ options.debug = debug;
+ options.vpmnlptype = vpmnlptype;
+ options.vpmnlpthresh = vpmnlpthresh;
+ options.vpmnlpmaxsupp = vpmnlpmaxsupp;
+ options.channels = wc->avchannels;
+
+ wc->vpmadt032 = vpmadt032_alloc(&options, wc->board_name);
+ if (!wc->vpmadt032)
+ return -ENOMEM;
+
+ wc->vpmadt032->setchanconfig_from_state = setchanconfig_from_state;
+ /* wc->vpmadt032->context = wc; */
+ /* Pull the configuration information from the span holding
+ * the analog channels. */
+ get_default_portconfig(&portconfig);
+ res = vpmadt032_init(wc->vpmadt032, &wc->vb);
+ if (res) {
+ vpmadt032_free(wc->vpmadt032);
+ wc->vpmadt032 = NULL;
+ return res;
+ }
+
+ /* Now we need to configure the VPMADT032 module for this
+ * particular board. */
+ res = config_vpmadt032(wc->vpmadt032, wc);
+ if (res) {
+ vpmadt032_free(wc->vpmadt032);
+ wc->vpmadt032 = NULL;
+ return res;
+ }
+
+ return 0;
+}
+
+static int wctdm_initialize_vpm(struct wctdm *wc)
+{
+ int res = 0;
+
+ if (!vpmsupport) {
+ dev_notice(&wc->vb.pdev->dev, "VPM: Support Disabled\n");
+ } else if (!wctdm_vpm_init(wc)) {
+ dev_info(&wc->vb.pdev->dev, "VPM: Present and operational (Rev %c)\n",
+ 'A' + wc->vpm100 - 1);
+ wc->ctlreg |= 0x10;
+ } else {
+ res = wctdm_initialize_vpmadt032(wc);
+ if (!res)
+ wc->ctlreg |= 0x10;
+ }
+ return res;
+}
+
+static int wctdm_identify_modules(struct wctdm *wc)
{
int x;
unsigned long flags;
wc->ctlreg = 0x00;
-
+
/* Make sure all units go into daisy chain mode */
spin_lock_irqsave(&wc->reglock, flags);
- wc->span.irqmisses = 0;
- for (x=0;x<wc->cards;x++)
+
+/*
+ * This looks a little weird.
+ *
+ * There are only 8 physical ports on the TDM/AEX800, but the code immediately
+ * below sets 24 modules up. This has to do with the altcs magic that allows us
+ * to have single-port and quad-port modules on these products.
+ * The variable "mods_per_board" is set to the appropriate value just below the
+ * next code block.
+ *
+ * Now why this is important:
+ * The FXS modules come out of reset in a two-byte, non-chainable SPI mode.
+ * This is currently incompatible with how we do things, so we need to set
+ * them to a chained, 3-byte command mode. This is done by setting the module
+ * type to MOD_TYPE_FXSINIT for a little while so that cmd_dequeue will
+ * initialize the SLIC into the appropriate mode.
+ *
+ * This "go to 3-byte chained mode" command, however, wreaks havoc with HybridBRI.
+ *
+ * The solution:
+ * Since HybridBRI is only designed to work in an 8-port card, and since the single-port
+ * modules "show up" in SPI slots >= 8 in these cards, we only set SPI slots 8-23 to
+ * MOD_TYPE_FXSINIT. The HybridBRI will never see the command that causes it to freak
+ * out and the single-port FXS cards get what they need so that when we probe with altcs
+ * we see them.
+ */
+
+ for (x = 0; x < wc->mods_per_board; x++)
wc->modtype[x] = MOD_TYPE_FXSINIT;
+
wc->vpm100 = -1;
- for (x = wc->cards; x < wc->cards+NUM_EC; x++)
+ for (x = wc->mods_per_board; x < wc->mods_per_board+NUM_EC; x++)
wc->modtype[x] = MOD_TYPE_VPM;
+
spin_unlock_irqrestore(&wc->reglock, flags);
- /* Wait just a bit */
- for (x=0;x<10;x++)
+
+/* Wait just a bit; this makes sure that cmd_dequeue is emitting SPI commands in the appropriate mode(s). */
+ for (x = 0; x < 10; x++)
schluffen(&wc->regq);
- spin_lock_irqsave(&wc->reglock, flags);
- for (x=0;x<wc->cards;x++)
- wc->modtype[x] = MOD_TYPE_FXS;
- spin_unlock_irqrestore(&wc->reglock, flags);
-#if 0
- /* XXX */
- cmddesc = 0;
-#endif
- /* Now that all the cards have been reset, we can stop checking them all if there aren't as many */
+/* 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->desc->ports;
+ wc->mods_per_board = 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;
+ for (x = 0; x < wc->mods_per_board; x++) {
+ int sane = 0, ret = 0, readi = 0;
if (fatal_signal_pending(current))
break;
retry:
- /* Init with Auto Calibration */
if (!(ret = wctdm_init_proslic(wc, x, 0, 0, sane))) {
- wc->cardflag |= (1 << x);
+ wc->modmap |= (1 << x);
if (debug & DEBUG_CARD) {
readi = wctdm_getreg(wc,x,LOOP_I_LIMIT);
- dev_info(&wc->vb.pdev->dev, "Proslic module %d loop current is %dmA\n",x,
- ((readi*3)+20));
+ dev_info(&wc->vb.pdev->dev, "Proslic module %d loop current is %dmA\n", x, ((readi*3)+20));
}
dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- AUTO FXS/DPO\n", x + 1);
} else {
- if(ret!=-2) {
- sane=1;
+ if (ret != -2) {
+ sane = 1;
/* Init with Manual Calibration */
if (!wctdm_init_proslic(wc, x, 0, 1, sane)) {
- wc->cardflag |= (1 << x);
- if (debug & DEBUG_CARD) {
- readi = wctdm_getreg(wc,x,LOOP_I_LIMIT);
- dev_info(&wc->vb.pdev->dev, "Proslic module %d loop current is %dmA\n",x,
- ((readi*3)+20));
- }
+ wc->modmap |= (1 << x);
+
+ if (debug & DEBUG_CARD) {
+ readi = wctdm_getreg(wc, x, LOOP_I_LIMIT);
+ dev_info(&wc->vb.pdev->dev, "Proslic module %d loop current is %dmA\n", x, ((readi*3)+20));
+ }
+
dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- MANUAL FXS\n",x + 1);
} else {
dev_notice(&wc->vb.pdev->dev, "Port %d: FAILED FXS (%s)\n", x + 1, fxshonormode ? fxo_modes[_opermode].name : "FCC");
- wc->chans[x]->sigcap = DAHDI_SIG_BROKEN | __DAHDI_SIG_FXO;
- }
+ }
+
} else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) {
- wc->cardflag |= (1 << x);
- dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- AUTO FXO (%s mode)\n",x + 1, fxo_modes[_opermode].name);
- } else if (!wctdm_init_qrvdri(wc,x)) {
- wc->cardflag |= 1 << x;
- dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- QRV DRI card\n",x + 1);
+ wc->modmap |= (1 << x);
+ dev_info(&wc->vb.pdev->dev,
+ "Port %d: Installed -- AUTO FXO "
+ "(%s mode)\n", x + 1,
+ fxo_modes[_opermode].name);
+ } else if (!wctdm_init_qrvdri(wc, x)) {
+ wc->modmap |= 1 << x;
+ dev_info(&wc->vb.pdev->dev,
+ "Port %d: Installed -- QRV DRI card\n", x + 1);
+ } else if (is_hx8(wc) && !wctdm_init_b400m(wc, x)) {
+ wc->modmap |= (1 << x);
+ dev_info(&wc->vb.pdev->dev,
+ "Port %d: Installed -- XHFC "
+ "quad-span module\n", x + 1);
} else {
- if ((wc->desc->ports != 24) &&
- ((x & 0x3) == 1) && !wc->altcs[x]) {
+ if ((wc->desc->ports != 24) && ((x & 0x3) == 1) && !wc->altcs[x]) {
spin_lock_irqsave(&wc->reglock, flags);
wc->altcs[x] = 2;
+
if (wc->desc->ports == 4) {
wc->altcs[x+1] = 3;
wc->altcs[x+2] = 3;
}
- wc->modtype[x] = MOD_TYPE_FXSINIT;
- spin_unlock_irqrestore(&wc->reglock, flags);
-
- schluffen(&wc->regq);
- schluffen(&wc->regq);
- spin_lock_irqsave(&wc->reglock, flags);
- wc->modtype[x] = MOD_TYPE_FXS;
- spin_unlock_irqrestore(&wc->reglock, flags);
- if (debug & DEBUG_CARD)
- dev_info(&wc->vb.pdev->dev, "Trying port %d with alternate chip select\n", x + 1);
- goto retry;
+
+ wc->modtype[x] = MOD_TYPE_FXSINIT;
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+ schluffen(&wc->regq);
+ schluffen(&wc->regq);
+
+ spin_lock_irqsave(&wc->reglock, flags);
+ wc->modtype[x] = MOD_TYPE_FXS;
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+ if (debug & DEBUG_CARD)
+ dev_info(&wc->vb.pdev->dev, "Trying port %d with alternate chip select\n", x + 1);
+ goto retry;
+
} else {
- dev_notice(&wc->vb.pdev->dev, "Port %d: Not installed\n", x + 1);
- wc->modtype[x] = MOD_TYPE_NONE;
- wc->cardflag |= (1 << x);
- }
+ dev_notice(&wc->vb.pdev->dev, "Port %d: Not installed\n", x + 1);
+ wc->modtype[x] = MOD_TYPE_NONE;
+ }
}
}
+ } /* for (x...) */
+
+ return 0;
+}
+
+static struct pci_driver wctdm_driver;
+
+static void wctdm_back_out_gracefully(struct wctdm *wc)
+{
+ int i;
+ unsigned long flags;
+ struct sframe_packet *frame;
+ LIST_HEAD(local_list);
+
+ for (i = 0; i < ARRAY_SIZE(wc->spans); ++i) {
+ if (wc->spans[i] && wc->spans[i]->span.chans)
+ kfree(wc->spans[i]->span.chans);
+
+ kfree(wc->spans[i]);
+ wc->spans[i] = NULL;
}
- if (fatal_signal_pending(current))
- return -EINTR;
+ for (i = 0; i < ARRAY_SIZE(wc->chans); ++i) {
+ kfree(wc->chans[i]);
+ wc->chans[i] = NULL;
+ }
- if (!vpmsupport) {
- dev_notice(&wc->vb.pdev->dev, "VPM: Support Disabled\n");
- } else if (!wctdm_vpm_init(wc)) {
- dev_info(&wc->vb.pdev->dev, "VPM: Present and operational (Rev %c)\n", 'A' + wc->vpm100 - 1);
- wc->ctlreg |= 0x10;
- } else {
- int res;
- struct vpmadt032_options options;
- GpakPortConfig_t portconfig;
+ voicebus_release(&wc->vb);
+
+ spin_lock_irqsave(&wc->frame_list_lock, flags);
+ list_splice(&wc->frame_list, &local_list);
+ spin_unlock_irqrestore(&wc->frame_list_lock, flags);
+
+ while (!list_empty(&local_list)) {
+ frame = list_entry(local_list.next,
+ struct sframe_packet, node);
+ list_del(&frame->node);
+ kfree(frame);
+ }
+
+ kfree(wc);
+}
+
+static const struct voicebus_operations voicebus_operations = {
+ .handle_receive = handle_receive,
+ .handle_transmit = handle_transmit,
+};
+
+static const struct voicebus_operations hx8_voicebus_operations = {
+ .handle_receive = handle_hx8_receive,
+ .handle_transmit = handle_hx8_transmit,
+};
+
+struct cmd_results {
+ u8 results[8];
+};
+
+static int hx8_send_command(struct wctdm *wc, const u8 *command,
+ size_t count, int checksum,
+ int application, int bootloader,
+ struct cmd_results *results)
+{
+ int ret = 0;
+ struct vbb *vbb;
+ struct sframe_packet *frame;
+ const int MAX_COMMAND_LENGTH = 264 + 4;
+ unsigned long flags;
+
+ might_sleep();
+
+ /* can't boot both into the application and the bootloader at once. */
+ WARN_ON((application > 0) && (bootloader > 0));
+ if ((application > 0) && (bootloader > 0))
+ return -EINVAL;
+
+ WARN_ON(count > MAX_COMMAND_LENGTH);
+ if (count > MAX_COMMAND_LENGTH)
+ return -EINVAL;
+
+ vbb = kmem_cache_alloc(voicebus_vbb_cache, GFP_KERNEL);
+ WARN_ON(!vbb);
+ if (!vbb)
+ return -ENOMEM;
- spin_lock_irqsave(&wc->reglock, flags);
- for (x = NUM_CARDS; x < NUM_CARDS + NUM_EC; x++)
- wc->modtype[x] = MOD_TYPE_NONE;
- spin_unlock_irqrestore(&wc->reglock, flags);
+ memset(vbb->data, 0, SFRAME_SIZE);
+ memcpy(&vbb->data[EFRAME_SIZE + EFRAME_GAP], command, count);
- options.debug = debug;
- options.vpmnlptype = vpmnlptype;
- options.vpmnlpthresh = vpmnlpthresh;
- options.vpmnlpmaxsupp = vpmnlpmaxsupp;
+ vbb->data[EFRAME_SIZE] = 0x80 | ((application) ? 0 : 0x40) |
+ ((checksum) ? 0x20 : 0) | ((count & 0x100) >> 4);
+ vbb->data[EFRAME_SIZE + 1] = count & 0xff;
- wc->vpmadt032 = vpmadt032_alloc(&options, wc->board_name);
- if (!wc->vpmadt032)
- return -ENOMEM;
+ if (bootloader)
+ vbb->data[EFRAME_SIZE + 3] = 0xAA;
- wc->vpmadt032->setchanconfig_from_state = setchanconfig_from_state;
- wc->vpmadt032->options.channels = wc->span.channels;
- get_default_portconfig(&portconfig);
- res = vpmadt032_init(wc->vpmadt032, &wc->vb);
- if (res) {
- vpmadt032_free(wc->vpmadt032);
- wc->vpmadt032 = NULL;
- return res;
- }
+ spin_lock_bh(&wc->vb.lock);
+ voicebus_transmit(&wc->vb, vbb);
+ spin_unlock_bh(&wc->vb.lock);
- /* Now we need to configure the VPMADT032 module for this
- * particular board. */
- res = config_vpmadt032(wc->vpmadt032, wc);
- if (res) {
- vpmadt032_free(wc->vpmadt032);
- wc->vpmadt032 = NULL;
- return res;
+ /* Do not wait for the response if the caller doesn't care about the
+ * results. */
+ if (NULL == results)
+ return 0;
+
+ if (!wait_event_timeout(wc->regq, !list_empty(&wc->frame_list), 2*HZ)) {
+ dev_err(&wc->vb.pdev->dev, "Timeout waiting "
+ "for receive frame.\n");
+ ret = -EIO;
+ }
+
+ /* We only want the last packet received. Throw away anything else on
+ * the list */
+ frame = NULL;
+ spin_lock_irqsave(&wc->frame_list_lock, flags);
+ while (!list_empty(&wc->frame_list)) {
+ frame = list_entry(wc->frame_list.next,
+ struct sframe_packet, node);
+ list_del(&frame->node);
+ if (!list_empty(&wc->frame_list)) {
+ kfree(frame);
+ frame = NULL;
}
+ }
+ spin_unlock_irqrestore(&wc->frame_list_lock, flags);
- dev_info(&wc->vb.pdev->dev, "VPMADT032: Present and operational (Firmware version %x)\n", wc->vpmadt032->version);
- /* TODO what is 0x10 in this context? */
- wc->ctlreg |= 0x10;
+ if (frame) {
+ memcpy(results->results, &frame->sframe[EFRAME_SIZE],
+ sizeof(results->results));
+ } else {
+ ret = -EIO;
}
+ return ret;
+
+}
+
+static int hx8_get_fpga_version(struct wctdm *wc, u8 *major, u8 *minor)
+{
+ int ret;
+ struct cmd_results results;
+ u8 command[] = {0xD7, 0x00};
+
+ ret = hx8_send_command(wc, command, ARRAY_SIZE(command),
+ 0, 0, 0, &results);
+ if (ret)
+ return ret;
+
+ *major = results.results[0];
+ *minor = results.results[2];
return 0;
}
-static struct pci_driver wctdm_driver;
+static void hx8_cleanup_frame_list(struct wctdm *wc)
+{
+ unsigned long flags;
+ LIST_HEAD(local_list);
+ struct sframe_packet *frame;
+
+ spin_lock_irqsave(&wc->frame_list_lock, flags);
+ list_splice_init(&wc->frame_list, &local_list);
+ spin_unlock_irqrestore(&wc->frame_list_lock, flags);
-static void free_wc(struct wctdm *wc)
+ while (!list_empty(&local_list)) {
+ frame = list_entry(local_list.next, struct sframe_packet, node);
+ list_del(&frame->node);
+ kfree(frame);
+ }
+}
+
+static int hx8_switch_to_application(struct wctdm *wc)
+{
+ int ret;
+ u8 command[] = {0xD7, 0x00};
+
+ ret = hx8_send_command(wc, command, ARRAY_SIZE(command),
+ 0, 1, 0, NULL);
+ if (ret)
+ return ret;
+
+ msleep(1000);
+ hx8_cleanup_frame_list(wc);
+
+ return 0;
+}
+
+/**
+ * hx8_switch_to_bootloader() - Send packet to switch hx8 into bootloader
+ *
+ */
+static int hx8_switch_to_bootloader(struct wctdm *wc)
+{
+ int ret;
+ u8 command[] = {0xD7, 0x00};
+
+ ret = hx8_send_command(wc, command, ARRAY_SIZE(command),
+ 0, 0, 1, NULL);
+ if (ret)
+ return ret;
+
+ /* It takes some time for the FPGA to reload and switch it's
+ * configuration. */
+ msleep(300);
+ hx8_cleanup_frame_list(wc);
+
+ return 0;
+}
+
+struct ha80000_firmware {
+ u8 header[6];
+ u8 major_ver;
+ u8 minor_ver;
+ u8 data[54648];
+ u32 chksum;
+} __attribute__((packed));
+
+static void hx8_send_dummy(struct wctdm *wc)
{
- unsigned int x;
+ u8 command[] = {0xD7, 0x00};
+
+ hx8_send_command(wc, command, ARRAY_SIZE(command),
+ 0, 0, 0, NULL);
+}
+
+static int hx8_read_status_register(struct wctdm *wc, u8 *status)
+{
+ int ret;
+ struct cmd_results results;
+ u8 command[] = {0xD7, 0x00};
+
+ ret = hx8_send_command(wc, command, ARRAY_SIZE(command),
+ 0, 0, 0, &results);
+ if (ret)
+ return ret;
+
+ *status = results.results[3];
+ return 0;
+}
+
+static const unsigned int HYBRID_PAGE_SIZE = 264;
- for (x = 0; x < ARRAY_SIZE(wc->chans); x++) {
- if (wc->chans[x]) {
- kfree(wc->chans[x]);
+static int hx8_write_buffer(struct wctdm *wc, const u8 *buffer, size_t size)
+{
+ int ret = 0;
+ struct cmd_results results;
+ int padding_bytes = 0;
+ u8 *local_data;
+ u8 command[] = {0x84, 0, 0, 0};
+
+ if (size > HYBRID_PAGE_SIZE)
+ return -EINVAL;
+
+ if (size < HYBRID_PAGE_SIZE)
+ padding_bytes = HYBRID_PAGE_SIZE - size;
+
+ local_data = kmalloc(sizeof(command) + size + padding_bytes, GFP_KERNEL);
+ if (!local_data)
+ return -ENOMEM;
+
+ memcpy(local_data, command, sizeof(command));
+ memcpy(&local_data[sizeof(command)], buffer, size);
+ memset(&local_data[sizeof(command) + size], 0xff, padding_bytes);
+
+ ret = hx8_send_command(wc, local_data,
+ sizeof(command) + size + padding_bytes,
+ 1, 0, 0, &results);
+ if (ret)
+ goto cleanup;
+
+cleanup:
+ kfree(local_data);
+ return ret;
+}
+
+static int hx8_buffer_to_page(struct wctdm *wc, const unsigned int page)
+{
+ int ret;
+ struct cmd_results results;
+ u8 command[] = {0x83, (page & 0x180) >> 7, (page & 0x7f) << 1, 0x00};
+
+ ret = hx8_send_command(wc, command, sizeof(command),
+ 1, 0, 0, &results);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int hx8_wait_for_ready(struct wctdm *wc, const int timeout)
+{
+ int ret;
+ u8 status;
+ unsigned long local_timeout = jiffies + timeout;
+
+ do {
+ ret = hx8_read_status_register(wc, &status);
+ if (ret)
+ return ret;
+ if ((status & 0x80) > 0)
+ break;
+ } while (time_after(local_timeout, jiffies));
+
+ if (time_after(jiffies, local_timeout))
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * hx8_reload_application - reload the application firmware
+ *
+ * NOTE: The caller should ensure that the board is in bootloader mode before
+ * calling this function.
+ */
+static int hx8_reload_application(struct wctdm *wc, const struct ha80000_firmware *ha8_fw)
+{
+ unsigned int cur_page;
+ const u8 *data;
+ u8 status;
+ int ret = 0;
+ const int HYBRID_PAGE_COUNT = (sizeof(ha8_fw->data)) / HYBRID_PAGE_SIZE;
+
+ BUG_ON(!ha8_fw);
+ might_sleep();
+
+ data = &ha8_fw->data[0];
+ ret = hx8_read_status_register(wc, &status);
+ if (ret)
+ return ret;
+
+ for (cur_page = 0; cur_page < HYBRID_PAGE_COUNT; ++cur_page) {
+ /* dev_dbg(&wc->vb.pdev->dev, "PAGE: %d\n", cur_page); */
+ ret = hx8_write_buffer(wc, data, HYBRID_PAGE_SIZE);
+ if (ret)
+ return ret;
+ /* The application starts out at page 0x100 */
+ ret = hx8_buffer_to_page(wc, 0x100 + cur_page);
+ if (ret)
+ return ret;
+
+ /* wait no more than a second for the write to the page to
+ * finish */
+ ret = hx8_wait_for_ready(wc, HZ);
+ if (ret)
+ return ret;
+
+ data += HYBRID_PAGE_SIZE;
+ }
+
+ return ret;
+}
+
+static void print_hx8_recovery_message(struct device *dev)
+{
+ dev_warn(dev, "The firmware may be corrupted. Please completely "
+ "power off your system, power on, and then reload the driver "
+ "with the 'forceload' module parameter set to 1 to attempt "
+ "recovery.\n");
+}
+
+/**
+ * hx8_check_firmware - Check the firmware version and load a new one possibly.
+ *
+ */
+static int hx8_check_firmware(struct wctdm *wc)
+{
+ int ret;
+ u8 major;
+ u8 minor;
+ const struct firmware *fw;
+ const struct ha80000_firmware *ha8_fw;
+ struct device *dev = &wc->vb.pdev->dev;
+ int retries = 10;
+
+ BUG_ON(!is_hx8(wc));
+
+ might_sleep();
+
+ do {
+ hx8_send_dummy(wc);
+ ret = hx8_get_fpga_version(wc, &major, &minor);
+ if (!ret)
+ break;
+ if (fatal_signal_pending(current))
+ return -EINTR;
+ } while (--retries);
+
+ if (ret) {
+ print_hx8_recovery_message(dev);
+ return ret;
+ }
+
+ /* If we're in the bootloader, try to jump into the application. */
+ if ((1 == major) && (0x80 == minor) && !forceload) {
+ dev_dbg(dev, "Switching to application.\n");
+ hx8_switch_to_application(wc);
+ ret = hx8_get_fpga_version(wc, &major, &minor);
+ if (ret) {
+ print_hx8_recovery_message(dev);
+ return ret;
}
- if (wc->ec[x])
- kfree(wc->ec[x]);
}
- kfree(wc);
+
+ dev_dbg(dev, "FPGA VERSION: %02x.%02x\n", major, minor);
+
+ ret = request_firmware(&fw, "dahdi-fw-ha80000.bin", dev);
+ if (ret) {
+ dev_warn(dev, "Failed to load firmware from userspace, skipping "
+ "check. (%d)\n", ret);
+ return 0;
+ }
+ ha8_fw = (const struct ha80000_firmware *)fw->data;
+
+ if ((fw->size != sizeof(*ha8_fw)) ||
+ (0 != memcmp("DIGIUM", ha8_fw->header, sizeof(ha8_fw->header))) ||
+ ((crc32(~0, (void *)ha8_fw, sizeof(*ha8_fw) - sizeof(u32)) ^ ~0) !=
+ ha8_fw->chksum)) {
+ dev_warn(dev, "Firmware file is invalid. Skipping load.\n");
+ ret = 0;
+ goto cleanup;
+ }
+
+ dev_dbg(dev, "FIRMWARE: %02x.%02x\n", ha8_fw->major_ver, ha8_fw->minor_ver);
+
+ if (ha8_fw->major_ver == major &&
+ ha8_fw->minor_ver == minor) {
+ dev_dbg(dev, "Firmware versions match, skipping load.\n");
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (2 == major) {
+ hx8_switch_to_bootloader(wc);
+ ret = hx8_get_fpga_version(wc, &major, &minor);
+ if (ret)
+ goto cleanup;
+ }
+
+ /* so now we're in boot loader mode, ready to load the new firmware. */
+ ret = hx8_reload_application(wc, ha8_fw);
+ if (ret)
+ goto cleanup;
+
+ dev_dbg(dev, "Firmware reloaded. Booting into application.\n");
+
+ hx8_switch_to_application(wc);
+ ret = hx8_get_fpga_version(wc, &major, &minor);
+ if (ret)
+ goto cleanup;
+
+ dev_dbg(dev, "FPGA VERSION AFTER LOAD: %02x.%02x\n", major, minor);
+
+ if (forceload) {
+ dev_warn(dev, "Please unset forceload if your card is able to "
+ "detect the installed modules.\n");
+ }
+
+cleanup:
+ release_firmware(fw);
+ return ret;
}
#ifdef CONFIG_VOICEBUS_SYSFS
@@ -3872,11 +4621,6 @@ static DEVICE_ATTR(voicebus_current_latency, 0400,
#endif
-static const struct voicebus_operations voicebus_operations = {
- .handle_receive = handle_receive,
- .handle_transmit = handle_transmit,
-};
-
#ifdef USE_ASYNC_INIT
struct async_data {
struct pci_dev *pdev;
@@ -3891,19 +4635,17 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
#endif
{
struct wctdm *wc;
- int i;
- int y;
- int ret;
+ int i, ret;
+
+ int anamods, digimods, curchan, curspan;
- neonmwi_offlimit_cycles = neonmwi_offlimit /MS_PER_HOOKCHECK;
+ neonmwi_offlimit_cycles = neonmwi_offlimit / MS_PER_HOOKCHECK;
- if (!(wc = kmalloc(sizeof(*wc), GFP_KERNEL))) {
+ wc = kzalloc(sizeof(*wc), GFP_KERNEL);
+ if (!wc)
return -ENOMEM;
- }
- memset(wc, 0, sizeof(*wc));
- wc->desc = (struct wctdm_desc *)ent->driver_data;
- spin_lock(&ifacelock);
+ down(&ifacelock);
/* \todo this is a candidate for removal... */
for (i = 0; i < WC_MAX_IFACES; ++i) {
if (!ifaces[i]) {
@@ -3911,23 +4653,38 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
break;
}
}
- spin_unlock(&ifacelock);
+ up(&ifacelock);
+
+ wc->desc = (struct wctdm_desc *)ent->driver_data;
- snprintf(wc->board_name, sizeof(wc->board_name)-1, "%s%d",
- wctdm_driver.name, i);
+ /* This is to insure that the analog span is given lowest priority */
+ wc->oldsync = -1;
+ init_MUTEX(&wc->syncsem);
+ INIT_LIST_HEAD(&wc->frame_list);
+ spin_lock_init(&wc->frame_list_lock);
+
+ snprintf(wc->board_name, sizeof(wc->board_name)-1, "%s%d", wctdm_driver.name, i);
pci_set_drvdata(pdev, wc);
wc->vb.ops = &voicebus_operations;
wc->vb.pdev = pdev;
wc->vb.debug = &debug;
- ret = voicebus_init(&wc->vb, wc->board_name);
+
+ if (is_hx8(wc)) {
+ wc->vb.ops = &hx8_voicebus_operations;
+ ret = voicebus_no_idle_init(&wc->vb, wc->board_name);
+ } else {
+ wc->vb.ops = &voicebus_operations;
+ ret = voicebus_init(&wc->vb, wc->board_name);
+ voicebus_set_minlatency(&wc->vb, latency);
+ }
+
if (ret) {
kfree(wc);
return ret;
}
#ifdef CONFIG_VOICEBUS_SYSFS
- dev_dbg(&wc->vb.pdev->dev, "Creating sysfs attributes.\n");
ret = device_create_file(&wc->vb.pdev->dev,
&dev_attr_voicebus_current_latency);
if (ret) {
@@ -3935,70 +4692,160 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
"Failed to create device attributes.\n");
}
#endif
- if (VOICEBUS_DEFAULT_LATENCY != latency) {
- voicebus_set_minlatency(&wc->vb, latency);
- }
+ voicebus_lock_latency(&wc->vb);
+
+ init_waitqueue_head(&wc->regq);
spin_lock_init(&wc->reglock);
- wc->cards = NUM_CARDS;
+ wc->mods_per_board = NUM_MODULES;
wc->pos = i;
wc->txident = 1;
- for (y=0;y<NUM_CARDS;y++) {
- wc->flags[y] = wc->desc->flags;
- wc->dacssrc[y] = -1;
- }
- init_waitqueue_head(&wc->regq);
+ for (i = 0; i < NUM_MODULES; i++) {
+ wc->flags[i] = wc->desc->flags;
+ wc->dacssrc[i] = -1;
+ }
- for (i = 0; i < wc->cards; i++) {
- if (!(wc->chans[i] = kmalloc(sizeof(*wc->chans[i]), GFP_KERNEL))) {
- free_wc(wc);
- return -ENOMEM;
- }
- memset(wc->chans[i], 0, sizeof(*wc->chans[i]));
- if (!(wc->ec[i] = kmalloc(sizeof(*wc->ec[i]), GFP_KERNEL))) {
- free_wc(wc);
- return -ENOMEM;
- }
- memset(wc->ec[i], 0, sizeof(*wc->ec[i]));
+ /* Start the hardware processing. */
+ if (voicebus_start(&wc->vb)) {
+ BUG_ON(1);
}
+ if (is_hx8(wc)) {
+ ret = hx8_check_firmware(wc);
+ if (ret) {
+ voicebus_release(&wc->vb);
+ kfree(wc);
+ return -EIO;
+ }
- if (wctdm_initialize(wc)) {
- voicebus_release(&wc->vb);
- kfree(wc);
- return -EIO;
+ /* Switch back to the normal mode of operation */
+ voicebus_stop(&wc->vb);
+ wc->vb.ops = &voicebus_operations;
+ voicebus_set_minlatency(&wc->vb, latency);
+ voicebus_set_normal_mode(&wc->vb);
+ if (voicebus_start(&wc->vb))
+ BUG_ON(1);
}
- voicebus_lock_latency(&wc->vb);
+/* first we have to make sure that we process all module data, we'll fine-tune it later in this routine. */
+ wc->avchannels = NUM_MODULES;
- if (voicebus_start(&wc->vb))
- BUG_ON(1);
-
/* Now track down what modules are installed */
- wctdm_locate_modules(wc);
-
- /* Final initialization */
- wctdm_post_initialize(wc);
+ wctdm_identify_modules(wc);
+
+ if (fatal_signal_pending(current)) {
+ wctdm_back_out_gracefully(wc);
+ return -EINTR;
+ }
+/*
+ * Walk the module list and create a 3-channel span for every BRI module found.
+ * Empty and analog modules get a common span which is allocated outside of this loop.
+ */
+ anamods = digimods = 0;
+ curchan = curspan = 0;
+ for (i = 0; i < wc->mods_per_board; i++) {
+ struct b400m *b4;
+
+ if (wc->modtype[i] == MOD_TYPE_NONE) {
+ ++curspan;
+ continue;
+ } else if (wc->modtype[i] == MOD_TYPE_BRI) {
+ wc->spans[curspan] = wctdm_init_span(wc, curspan, curchan, 3, 1);
+ if (!wc->spans[curspan]) {
+ wctdm_back_out_gracefully(wc);
+ return -EIO;
+ }
+ b4 = wc->mods[i].bri;
+ b400m_set_dahdi_span(b4, i & 0x03, &wc->spans[curspan]->span);
+
+ ++curspan;
+ curchan += 3;
+ if (!(i & 0x03)) {
+ b400m_post_init(b4);
+ ++digimods;
+ }
+ } else {
+/*
+ * FIXME: ABK:
+ * create a wctdm_chan for every analog module and link them into a span of their own down below.
+ * then evaluate all of the callbacks and hard-code whether they are receiving a dahdi_chan or wctdm_chan *.
+ * Finally, move the union from the wctdm structure to the dahdi_chan structure, and we should have something
+ * resembling a clean dynamic # of channels/dynamic # of spans driver.
+ */
+ ++curspan;
+ ++anamods;
+ }
+
+ if (digimods > 2) {
+ dev_info(&wc->vb.pdev->dev, "More than two digital modules detected. This is unsupported.\n");
+ wctdm_back_out_gracefully(wc);
+ return -EIO;
+ }
+ }
+
+/* create an analog span if there are analog modules, or if there are no digital ones. */
+ if (anamods || !digimods) {
+ if (!digimods) {
+ curspan = 0;
+ }
+ wctdm_init_span(wc, curspan, curchan, wc->desc->ports, 0);
+ wctdm_fixup_analog_span(wc, curspan);
+ wc->aspan = wc->spans[curspan];
+ wc->aspan->span.pvt = wc;
+ curchan += wc->desc->ports;
+ ++curspan;
+ }
+
+ /* Now fix up the timeslots for the analog modules, since the digital
+ * modules are always first */
+ for (i = 0; i < wc->mods_per_board; i++) {
+ if (wc->modtype[i] == MOD_TYPE_FXS) {
+ wctdm_proslic_set_ts(wc, i, (digimods * 12) + i);
+ } else if (wc->modtype[i] == MOD_TYPE_FXO) {
+ wctdm_voicedaa_set_ts(wc, i, (digimods * 12) + i);
+ } else if (wc->modtype[i] == MOD_TYPE_QRV) {
+ wctdm_qrvdri_set_ts(wc, i, (digimods * 12) + i);
+ }
+ }
+
+ wc->digi_mods = digimods;
+
+ /* This shouldn't ever occur, but if we don't try to trap it, the driver
+ * will be scribbling into memory it doesn't own. */
+ BUG_ON(curchan > 24);
+
+ wc->avchannels = curchan;
+
+ wctdm_initialize_vpm(wc);
+
#ifdef USE_ASYNC_INIT
- async_synchronize_cookie(cookie);
+ async_synchronize_cookie(cookie);
#endif
-
/* We should be ready for DAHDI to come in now. */
- if (dahdi_register(&wc->span, 0)) {
- dev_info(&wc->vb.pdev->dev,
- "Unable to register span with DAHDI\n");
- voicebus_release(&wc->vb);
- kfree(wc);
- return -1;
+ for (i = 0; i < MAX_SPANS; ++i) {
+ if (!wc->spans[i])
+ continue;
+
+ if (dahdi_register(&wc->spans[i]->span, 0)) {
+ dev_notice(&wc->vb.pdev->dev, "Unable to register span %d with DAHDI\n", i);
+ while (i)
+ dahdi_unregister(&wc->spans[i--]->span);
+ wctdm_back_out_gracefully(wc);
+ return -1;
+ }
}
wc->initialized = 1;
- dev_info(&wc->vb.pdev->dev, "Found a Wildcard TDM: %s (%d modules)\n",
- wc->desc->name, wc->desc->ports);
-
+ dev_info(&wc->vb.pdev->dev,
+ "Found a Wildcard TDM: %s (%d digital %s, %d analog %s)\n",
+ wc->desc->name, digimods,
+ (digimods == 1) ? "module" : "modules", anamods,
+ (anamods == 1) ? "module" : "modules");
+ ret = 0;
+
voicebus_unlock_latency(&wc->vb);
return 0;
}
@@ -4043,25 +4890,27 @@ static void wctdm_release(struct wctdm *wc)
int i;
if (wc->initialized) {
- dahdi_unregister(&wc->span);
+ for (i = 0; i < MAX_SPANS; i++) {
+ if (wc->spans[i])
+ dahdi_unregister(&wc->spans[i]->span);
+ }
}
- voicebus_release(&wc->vb);
-
- spin_lock(&ifacelock);
+ down(&ifacelock);
for (i = 0; i < WC_MAX_IFACES; i++)
if (ifaces[i] == wc)
break;
ifaces[i] = NULL;
- spin_unlock(&ifacelock);
+ up(&ifacelock);
- free_wc(wc);
+ wctdm_back_out_gracefully(wc);
}
static void __devexit wctdm_remove_one(struct pci_dev *pdev)
{
struct wctdm *wc = pci_get_drvdata(pdev);
struct vpmadt032 *vpm = wc->vpmadt032;
+ int i;
#ifdef CONFIG_VOICEBUS_SYSFS
device_remove_file(&wc->vb.pdev->dev,
@@ -4075,6 +4924,12 @@ static void __devexit wctdm_remove_one(struct pci_dev *pdev)
flush_scheduled_work();
}
+ /* shut down any BRI modules */
+ for (i = 0; i < wc->mods_per_board; i += 4) {
+ if (wc->modtype[i] == MOD_TYPE_BRI)
+ wctdm_unload_b400m(wc, i);
+ }
+
voicebus_stop(&wc->vb);
if (vpm) {
@@ -4095,6 +4950,8 @@ static struct pci_device_id wctdm_pci_tbl[] = {
{ 0xd161, 0x8003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcaex2400 },
{ 0xd161, 0x8005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wctdm410 },
{ 0xd161, 0x8006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcaex410 },
+ { 0xd161, 0x8007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcha80000 },
+ { 0xd161, 0x8008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wchb80000 },
{ 0 }
};
@@ -4146,6 +5003,8 @@ static int __init wctdm_init(void)
battthresh = fxo_modes[_opermode].battthresh;
}
+ b400m_module_init();
+
res = dahdi_pci_module(&wctdm_driver);
if (res)
return -ENODEV;
@@ -4195,6 +5054,28 @@ module_param(vpmnlpthresh, int, 0600);
module_param(vpmnlpmaxsupp, int, 0600);
#endif
+/* Module parameters backed by code in xhfc.c */
+module_param(bri_debug, int, 0600);
+MODULE_PARM_DESC(bri_debug, "bitmap: 1=general 2=dtmf 4=regops 8=fops 16=ec 32=st state 64=hdlc 128=alarm");
+
+module_param(bri_spanfilter, int, 0600);
+MODULE_PARM_DESC(bri_spanfilter, "debug filter for spans. bitmap: 1=port 1, 2=port 2, 4=port 3, 8=port 4");
+
+module_param(bri_alarmdebounce, int, 0600);
+MODULE_PARM_DESC(bri_alarmdebounce, "msec to wait before set/clear alarm condition");
+
+module_param(bri_teignorered, int, 0600);
+MODULE_PARM_DESC(bri_teignorered, "1=ignore (do not inform DAHDI) if a red alarm exists in TE mode");
+
+module_param(bri_persistentlayer1, int, 0600);
+MODULE_PARM_DESC(bri_persistentlayer1, "Set to 0 for disabling automatic layer 1 reactivation (when other end deactivates it)");
+
+module_param(timingcable, int, 0600);
+MODULE_PARM_DESC(timingcable, "Set to 1 for enabling timing cable. This means that *all* cards in the system are linked together with a single timing cable");
+
+module_param(forceload, int, 0600);
+MODULE_PARM_DESC(forceload, "Set to 1 in order to force an FPGA reload after power on (currently only for HA8/HB8 cards).");
+
MODULE_DESCRIPTION("Wildcard VoiceBus Analog Card Driver");
MODULE_AUTHOR("Digium Incorporated <support@digium.com>");
MODULE_ALIAS("wctdm8xxp");
diff --git a/drivers/dahdi/wctdm24xxp/wctdm24xxp.h b/drivers/dahdi/wctdm24xxp/wctdm24xxp.h
index fe96966..58b1907 100644
--- a/drivers/dahdi/wctdm24xxp/wctdm24xxp.h
+++ b/drivers/dahdi/wctdm24xxp/wctdm24xxp.h
@@ -62,10 +62,11 @@
#define MOD_TYPE_NONE 0
#define MOD_TYPE_FXS 1
#define MOD_TYPE_FXO 2
-#define MOD_TYPE_FXSINIT 3
+#define MOD_TYPE_FXSINIT 3
#define MOD_TYPE_VPM 4
#define MOD_TYPE_QRV 5
#define MOD_TYPE_VPM150M 6
+#define MOD_TYPE_BRI 7
#define MINPEGTIME 10 * 8 /* 30 ms peak to peak gets us no more than 100 Hz */
#define PEGTIME 50 * 8 /* 50ms peak to peak gets us rings of 10 Hz or more */
@@ -88,24 +89,23 @@
#define CMD_BYTE(card,bit,altcs) (((((card) & 0x3) * 3 + (bit)) * 7) \
+ ((card) >> 2) + (altcs) + ((altcs) ? -21 : 0))
#endif
-#define NUM_CARDS 24
-#define NUM_EC 4
-#define NUM_SLOTS 6
-#define MAX_TDM_CHAN 31
+#define NUM_MODULES 24
+#define NUM_EC 4
+#define NUM_SLOTS 6
+#define MAX_TDM_CHAN 31
+#define MAX_SPANS 9
-#define NUM_CAL_REGS 12
+#define NUM_CAL_REGS 12
-#define USER_COMMANDS 8
-#define ISR_COMMANDS 2
-#define QRV_DEBOUNCETIME 20
+#define USER_COMMANDS 8
+#define ISR_COMMANDS 2
+#define QRV_DEBOUNCETIME 20
-#define MAX_COMMANDS (USER_COMMANDS + ISR_COMMANDS)
-
-
-#define VPM150M_HPI_CONTROL 0x00
-#define VPM150M_HPI_ADDRESS 0x02
-#define VPM150M_HPI_DATA 0x03
+#define MAX_COMMANDS (USER_COMMANDS + ISR_COMMANDS)
+#define VPM150M_HPI_CONTROL 0x00
+#define VPM150M_HPI_ADDRESS 0x02
+#define VPM150M_HPI_DATA 0x03
#define VPM_SUPPORT
@@ -139,22 +139,57 @@ enum battery_state {
BATTERY_LOST,
};
+/**
+ * struct wctdm_span -
+ * @span: dahdi_span to register.
+ * @timing_priority: What the priority of this span is relative to the other
+ * spans.
+ * @spanno: Which span on the card this is.
+ *
+ * NOTE: spanno would normally be taken care of by dahdi_span.offset, but
+ * appears to have meaning in xhfc.c, and that needs to be audited before
+ * changing. !!!TODO!!!
+ *
+ */
+struct wctdm_span {
+ struct dahdi_span span;
+ int timing_priority;
+ int spanno;
+};
+
+struct wctdm_chan {
+ struct dahdi_chan chan;
+ struct dahdi_echocan_state ec;
+ int timeslot;
+};
+
struct wctdm {
const struct wctdm_desc *desc;
char board_name[80];
- struct dahdi_span span;
+ int usecount;
+ int pos; /* card number in system */
+
+ spinlock_t frame_list_lock;
+ struct list_head frame_list;
+
unsigned int intcount;
unsigned char txident;
unsigned char rxident;
- int pos;
- int flags[NUM_CARDS];
- unsigned char ctlreg;
- int cards;
- int cardflag; /* Bit-map of present cards */
- int altcs[NUM_CARDS + NUM_EC];
- char qrvhook[NUM_CARDS];
- unsigned short qrvdebtime[NUM_CARDS];
- int radmode[NUM_CARDS];
+
+ int flags[NUM_MODULES]; /* bitmap of board-specific + module-specific flags */
+ unsigned char ctlreg; /* FIXME: something to do with VPM? */
+
+ int mods_per_board; /* maximum number of modules for this board */
+ int digi_mods; /* number of digital modules present */
+ int avchannels; /* active "voice" (voice, B and D) channels */
+ int modmap; /* Bit-map of present cards (1=present) */
+
+ int altcs[NUM_MODULES + NUM_EC];
+
+/* FIXME: why are all of these QRV-only members part of the main card structure? */
+ char qrvhook[NUM_MODULES];
+ unsigned short qrvdebtime[NUM_MODULES];
+ int radmode[NUM_MODULES];
#define RADMODE_INVERTCOR 1
#define RADMODE_IGNORECOR 2
#define RADMODE_EXTTONE 4
@@ -162,12 +197,13 @@ struct wctdm {
#define RADMODE_IGNORECT 16
#define RADMODE_PREEMP 32
#define RADMODE_DEEMP 64
- unsigned short debouncetime[NUM_CARDS];
- signed short rxgain[NUM_CARDS];
- signed short txgain[NUM_CARDS];
- spinlock_t reglock;
- wait_queue_head_t regq;
- /* FXO Stuff */
+ unsigned short debouncetime[NUM_MODULES];
+ signed short rxgain[NUM_MODULES];
+ signed short txgain[NUM_MODULES];
+
+ spinlock_t reglock; /* held when accessing anything affecting the module array */
+ wait_queue_head_t regq; /* for schluffen() */
+
union {
struct fxo {
int wasringing;
@@ -211,13 +247,13 @@ struct wctdm {
int reversepolarity; /* polarity reversal */
struct calregs calregs;
} fxs;
- } mods[NUM_CARDS];
- struct cmdq cmdq[NUM_CARDS + NUM_EC];
- /* Receive hook state and debouncing */
- int modtype[NUM_CARDS + NUM_EC];
- /* Set hook */
- int sethook[NUM_CARDS + NUM_EC];
- int dacssrc[NUM_CARDS];
+ struct b400m *bri;
+ } mods[NUM_MODULES];
+
+ struct cmdq cmdq[NUM_MODULES + NUM_EC];
+ int modtype[NUM_MODULES + NUM_EC]; /* type of module (FXO/FXS/QRV/VPM/etc.) in this position */
+ int sethook[NUM_MODULES + NUM_EC]; /* pending hook state command for each module */
+ int dacssrc[NUM_MODULES];
int vpm100;
@@ -225,17 +261,30 @@ struct wctdm {
#ifdef FANCY_ECHOCAN
int echocanpos;
int blinktimer;
-#endif
+#endif
struct voicebus vb;
- struct dahdi_chan *chans[NUM_CARDS];
- struct dahdi_echocan_state *ec[NUM_CARDS];
- int initialized;
+ struct wctdm_span *aspan; /* pointer to the spans[] holding the analog span */
+ struct wctdm_span *spans[MAX_SPANS];
+ struct wctdm_chan *chans[NUM_MODULES];
+
+ /* Only care about digital spans here */
+ /* int span_timing_prio[MAX_SPANS - 1]; */
+ struct semaphore syncsem;
+ int oldsync;
+
+ int initialized; /* =1 when the entire card is ready to go */
+ unsigned long checkflag; /* Internal state flags and task bits */
};
+/* Atomic flag bits for checkflag field */
+#define WCTDM_CHECK_TIMING 0
int schluffen(wait_queue_head_t *q);
+void wait_just_a_bit(int foo);
+int wctdm_getreg(struct wctdm *wc, int card, int addr);
+int wctdm_setreg(struct wctdm *wc, int card, int addr, int val);
-extern spinlock_t ifacelock;
+extern struct semaphore ifacelock;
extern struct wctdm *ifaces[WC_MAX_IFACES];
#endif
diff --git a/drivers/dahdi/wctdm24xxp/xhfc.c b/drivers/dahdi/wctdm24xxp/xhfc.c
new file mode 100644
index 0000000..500cdf7
--- /dev/null
+++ b/drivers/dahdi/wctdm24xxp/xhfc.c
@@ -0,0 +1,2756 @@
+/*
+ * B400M Quad-BRI module Driver
+ * Written by Andrew Kohlsmith <akohlsmith@mixdown.ca>
+ *
+ * Copyright (C) 2010 Digium, Inc.
+ * All rights reserved.
+ *
+ */
+
+/*
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2 as published by the
+ * Free Software Foundation. See the LICENSE file included with
+ * this program for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/ppp_defs.h>
+#include <linux/delay.h>
+
+#define FAST_HDLC_NEED_TABLES
+#include <dahdi/kernel.h>
+#include <dahdi/fasthdlc.h>
+
+#include "wctdm24xxp.h"
+#include "xhfc.h"
+
+#define HFC_NR_FIFOS 16
+#define HFC_ZMIN 0x00 /* from datasheet */
+#define HFC_ZMAX 0x7f
+#define HFC_FMIN 0x00
+#define HFC_FMAX 0x07
+
+/*
+ * yuck. Any reg which is not mandated read/write or read-only is write-only.
+ * Also, there are dozens of registers with the same address. Additionally,
+ * there are array registers (A_) which have an index register These A_
+ * registers require an index register to be written to indicate WHICH in the
+ * array you want.
+ */
+
+#define R_CIRM 0x00 /* WO */
+#define R_CTRL 0x01 /* WO */
+#define R_CLK_CFG 0x02 /* WO */
+#define A_Z1 0x04 /* RO */
+#define A_Z2 0x06 /* RO */
+#define R_RAM_ADDR 0x08 /* WO */
+#define R_RAM_CTRL 0x09 /* WO */
+#define R_FIRST_FIFO 0x0b /* WO */
+#define R_FIFO_THRES 0x0c /* WO */
+#define A_F1 0x0c /* RO */
+#define R_FIFO_MD 0x0d /* WO */
+#define A_F2 0x0d /* RO */
+#define A_INC_RES_FIFO 0x0e /* WO */
+#define A_FIFO_STA 0x0e /* RO */
+#define R_FSM_IDX 0x0f /* WO */
+#define R_FIFO 0x0f /* WO */
+
+#define R_SLOT 0x10 /* WO */
+#define R_IRQ_OVIEW 0x10 /* RO */
+#define R_MISC_IRQMSK 0x11 /* WO */
+#define R_MISC_IRQ 0x11 /* RO */
+#define R_SU_IRQMSK 0x12 /* WO */
+#define R_SU_IRQ 0x12 /* RO */
+#define R_IRQ_CTRL 0x13 /* WO */
+#define R_AF0_OVIEW 0x13 /* RO */
+#define R_PCM_MD0 0x14 /* WO */
+#define A_USAGE 0x14 /* RO */
+#define R_MSS0 0x15 /* WO */
+#define R_MSS1 0x15 /* WO */
+#define R_PCM_MD1 0x15 /* WO */
+#define R_PCM_MD2 0x15 /* WO */
+#define R_SH0H 0x15 /* WO */
+#define R_SH1H 0x15 /* WO */
+#define R_SH0L 0x15 /* WO */
+#define R_SH1L 0x15 /* WO */
+#define R_SL_SEL0 0x15 /* WO */
+#define R_SL_SEL1 0x15 /* WO */
+#define R_SL_SEL7 0x15 /* WO */
+#define R_RAM_USE 0x15 /* RO */
+#define R_SU_SEL 0x16 /* WO */
+#define R_CHIP_ID 0x16 /* RO */
+#define R_SU_SYNC 0x17 /* WO */
+#define R_BERT_STA 0x17 /* RO */
+#define R_F0_CNTL 0x18 /* RO */
+#define R_F0_CNTH 0x19 /* RO */
+#define R_TI_WD 0x1a /* WO */
+#define R_BERT_ECL 0x1a /* RO */
+#define R_BERT_WD_MD 0x1b /* WO */
+#define R_BERT_ECH 0x1b /* RO */
+#define R_STATUS 0x1c /* RO */
+#define R_SL_MAX 0x1d /* RO */
+#define R_PWM_CFG 0x1e /* WO */
+#define R_CHIP_RV 0x1f /* RO */
+
+#define R_FIFO_BL0_IRQ 0x20 /* RO */
+#define R_FIFO_BL1_IRQ 0x21 /* RO */
+#define R_FIFO_BL2_IRQ 0x22 /* RO */
+#define R_FIFO_BL3_IRQ 0x23 /* RO */
+#define R_FILL_BL0 0x24 /* RO */
+#define R_FILL_BL1 0x25 /* RO */
+#define R_FILL_BL2 0x26 /* RO */
+#define R_FILL_BL3 0x27 /* RO */
+#define R_CI_TX 0x28 /* WO */
+#define R_CI_RX 0x28 /* RO */
+#define R_CGI_CFG0 0x29 /* WO */
+#define R_CGI_STA 0x29 /* RO */
+#define R_CGI_CFG1 0x2a /* WO */
+#define R_MON_RX 0x2a /* RO */
+#define R_MON_TX 0x2b /* WO */
+
+#define A_SU_WR_STA 0x30 /* WO */
+#define A_SU_RD_STA 0x30 /* RO */
+#define A_SU_CTRL0 0x31 /* WO */
+#define A_SU_DLYL 0x31 /* RO */
+#define A_SU_CTRL1 0x32 /* WO */
+#define A_SU_DLYH 0x32 /* RO */
+#define A_SU_CTRL2 0x33 /* WO */
+#define A_MS_TX 0x34 /* WO */
+#define A_MS_RX 0x34 /* RO */
+#define A_ST_CTRL3 0x35 /* WO */
+#define A_UP_CTRL3 0x35 /* WO */
+#define A_SU_STA 0x35 /* RO */
+#define A_MS_DF 0x36 /* WO */
+#define A_SU_CLK_DLY 0x37 /* WO */
+#define R_PWM0 0x38 /* WO */
+#define R_PWM1 0x39 /* WO */
+#define A_B1_TX 0x3c /* WO */
+#define A_B1_RX 0x3c /* RO */
+#define A_B2_TX 0x3d /* WO */
+#define A_B2_RX 0x3d /* RO */
+#define A_D_TX 0x3e /* WO */
+#define A_D_RX 0x3e /* RO */
+#define A_BAC_S_TX 0x3f /* WO */
+#define A_E_RX 0x3f /* RO */
+
+#define R_GPIO_OUT1 0x40 /* WO */
+#define R_GPIO_IN1 0x40 /* RO */
+#define R_GPIO_OUT3 0x41 /* WO */
+#define R_GPIO_IN3 0x41 /* RO */
+#define R_GPIO_EN1 0x42 /* WO */
+#define R_GPIO_EN3 0x43 /* WO */
+#define R_GPIO_SEL_BL 0x44 /* WO */
+#define R_GPIO_OUT2 0x45 /* WO */
+#define R_GPIO_IN2 0x45 /* RO */
+#define R_PWM_MD 0x46 /* WO */
+#define R_GPIO_EN2 0x47 /* WO */
+#define R_GPIO_OUT0 0x48 /* WO */
+#define R_GPIO_IN0 0x48 /* RO */
+#define R_GPIO_EN0 0x4a /* WO */
+#define R_GPIO_SEL 0x4c /* WO */
+
+#define R_PLL_CTRL 0x50 /* WO */
+#define R_PLL_STA 0x50 /* RO */
+#define R_PLL_P 0x51 /* RW */
+#define R_PLL_N 0x52 /* RW */
+#define R_PLL_S 0x53 /* RW */
+
+#define A_FIFO_DATA 0x80 /* RW */
+#define A_FIFO_DATA_NOINC 0x84 /* RW */
+
+#define R_INT_DATA 0x88 /* RO */
+
+#define R_RAM_DATA 0xc0 /* RW */
+
+#define A_SL_CFG 0xd0 /* RW */
+#define A_CH_MSK 0xf4 /* RW */
+#define A_CON_HDLC 0xfa /* RW */
+#define A_SUBCH_CFG 0xfb /* RW */
+#define A_CHANNEL 0xfc /* RW */
+#define A_FIFO_SEQ 0xfd /* RW */
+#define A_FIFO_CTRL 0xff /* RW */
+
+/* R_CIRM bits */
+#define V_CLK_OFF (1 << 0) /* 1=internal clocks disabled */
+#define V_WAIT_PROC (1 << 1) /* 1=additional /WAIT after write access */
+#define V_WAIT_REG (1 << 2) /* 1=additional /WAIT for internal BUSY phase */
+#define V_SRES (1 << 3) /* soft reset (group 0) */
+#define V_HFC_RES (1 << 4) /* HFC reset (group 1) */
+#define V_PCM_RES (1 << 5) /* PCM reset (group 2) */
+#define V_SU_RES (1 << 6) /* S/T reset (group 3) */
+#define XHFC_FULL_RESET (V_SRES | V_HFC_RES | V_PCM_RES | V_SU_RES)
+
+/* R_STATUS bits */
+#define V_BUSY (1 << 0) /* 1=HFC busy, limited register access */
+#define V_PROC (1 << 1) /* 1=HFC in processing phase */
+#define V_LOST_STA (1 << 3) /* 1=frames have been lost */
+#define V_PCM_INIT (1 << 4) /* 1=PCM init in progress */
+#define V_WAK_STA (1 << 5) /* state of WAKEUP pin wien V_WAK_EN=1 */
+#define V_MISC_IRQSTA (1 << 6) /* 1=misc interrupt has occurred */
+#define V_FR_IRQSTA (1 << 7) /* 1=fifo interrupt has occured */
+#define XHFC_INTS (V_MISC_IRQSTA | V_FR_IRQSTA)
+
+/* R_FIFO_BLx_IRQ bits */
+#define V_FIFOx_TX_IRQ (1 << 0) /* FIFO TX interrupt occurred */
+#define V_FIFOx_RX_IRQ (1 << 1) /* FIFO RX interrupt occurred */
+#define FIFOx_TXRX_IRQ (V_FIFOx_TX_IRQ | V_FIFOx_RX_IRQ)
+
+/* R_FILL_BLx bits */
+#define V_FILL_FIFOx_TX (1 << 0) /* TX FIFO reached V_THRES_TX level */
+#define V_FILL_FIFOx_RX (1 << 1) /* RX FIFO reached V_THRES_RX level */
+#define FILL_FIFOx_TXRX (V_FILL_FIFOx_TX | V_FILL_FIFOx_RX)
+
+/* R_MISC_IRQ / R_MISC_IRQMSK bits */
+#define V_SLP_IRQ (1 << 0) /* frame sync pulse flips */
+#define V_TI_IRQ (1 << 1) /* timer elapsed */
+#define V_PROC_IRQ (1 << 2) /* processing/non-processing transition */
+#define V_CI_IRQ (1 << 4) /* indication bits changed */
+#define V_WAK_IRQ (1 << 5) /* WAKEUP pin */
+#define V_MON_TX_IRQ (1 << 6) /* monitor byte can be written */
+#define V_MON_RX_IRQ (1 << 7) /* monitor byte received */
+
+/* R_SU_IRQ/R_SU_IRQMSK bits */
+#define V_SU0_IRQ (1 << 0) /* interrupt/mask port 1 */
+#define V_SU1_IRQ (1 << 1) /* interrupt/mask port 2 */
+#define V_SU2_IRQ (1 << 2) /* interrupt/mask port 3 */
+#define V_SU3_IRQ (1 << 3) /* interrupt/mask port 4 */
+
+/* R_IRQ_CTRL bits */
+#define V_FIFO_IRQ_EN (1 << 0) /* enable any unmasked FIFO IRQs */
+#define V_GLOB_IRQ_EN (1 << 3) /* enable any unmasked IRQs */
+#define V_IRQ_POL (1 << 4) /* 1=IRQ active high */
+
+/* R_BERT_WD_MD bits */
+#define V_BERT_ERR (1 << 3) /* 1=generate an error bit in BERT stream */
+#define V_AUTO_WD_RES (1 << 5) /* 1=automatically kick the watchdog */
+#define V_WD_RES (1 << 7) /* 1=kick the watchdog (bit auto clears) */
+
+/* R_TI_WD bits */
+#define V_EV_TS_SHIFT (0)
+#define V_EV_TS_MASK (0x0f)
+#define V_WD_TS_SHIFT (4)
+#define V_WD_TS_MASK (0xf0)
+
+/* A_FIFO_CTRL bits */
+#define V_FIFO_IRQMSK (1 << 0) /* 1=FIFO can generate interrupts */
+#define V_BERT_EN (1 << 1) /* 1=BERT data replaces FIFO data */
+#define V_MIX_IRQ (1 << 2) /* IRQ when 0=end of frame only, 1=also when Z1==Z2 */
+#define V_FR_ABO (1 << 3) /* 1=generate frame abort/frame abort detected */
+#define V_NO_CRC (1 << 4) /* 1=do not send CRC at end of frame */
+#define V_NO_REP (1 << 5) /* 1=frame deleted after d-chan contention */
+
+/* R_CLK_CFG bits */
+#define V_CLK_PLL (1 << 0) /* Sysclk select 0=OSC_IN, 1=PLL output */
+#define V_CLKO_HI (1 << 1) /* CLKOUT selection 0=PLL/8, 1=PLL */
+#define V_CLKO_PLL (1 << 2) /* CLKOUT source 0=divider or PLL input, 1=PLL output */
+#define V_PCM_CLK (1 << 5) /* 1=PCM clk = OSC, 0 = PCM clk is 2x OSC */
+#define V_CLKO_OFF (1 << 6) /* CLKOUT enable 0=enabled */
+#define V_CLK_F1 (1 << 7) /* PLL input pin 0=OSC_IN, 1=F1_1 */
+
+/* R_PCM_MD0 bits */
+#define V_PCM_MD (1 << 0) /* 1=PCM master */
+#define V_C4_POL (1 << 1) /* 1=F0IO sampled on rising edge of C4IO */
+#define V_F0_NEG (1 << 2) /* 1=negative polarity of F0IO */
+#define V_F0_LEN (1 << 3) /* 1=F0IO active for 2 C4IO clocks */
+#define V_PCM_IDX_SEL0 (0x0 << 4) /* reg15 = R_SL_SEL0 */
+#define V_PCM_IDX_SEL1 (0x1 << 4) /* reg15 = R_SL_SEL1 */
+#define V_PCM_IDX_SEL7 (0x7 << 4) /* reg15 = R_SL_SEL7 */
+#define V_PCM_IDX_MSS0 (0x8 << 4) /* reg15 = R_MSS0 */
+#define V_PCM_IDX_MD1 (0x9 << 4) /* reg15 = R_PCM_MD1 */
+#define V_PCM_IDX_MD2 (0xa << 4) /* reg15 = R_PCM_MD2 */
+#define V_PCM_IDX_MSS1 (0xb << 4) /* reg15 = R_MSS1 */
+#define V_PCM_IDX_SH0L (0xc << 4) /* reg15 = R_SH0L */
+#define V_PCM_IDX_SH0H (0xd << 4) /* reg15 = R_SH0H */
+#define V_PCM_IDX_SH1L (0xe << 4) /* reg15 = R_SH1L */
+#define V_PCM_IDX_SH1H (0xf << 4) /* reg15 = R_SH1H */
+#define V_PCM_IDX_MASK (0xf0)
+
+/* R_PCM_MD1 bits */
+#define V_PLL_ADJ_00 (0x0 << 2) /* adj 4 times by 0.5 system clk cycles */
+#define V_PLL_ADJ_01 (0x1 << 2) /* adj 3 times by 0.5 system clk cycles */
+#define V_PLL_ADJ_10 (0x2 << 2) /* adj 2 times by 0.5 system clk cycles */
+#define V_PLL_ADJ_11 (0x3 << 2) /* adj 1 time by 0.5 system clk cycles */
+#define V_PCM_DR_2048 (0x0 << 4) /* 2.048Mbps, 32 timeslots */
+#define V_PCM_DR_4096 (0x1 << 4) /* 4.096Mbps, 64 timeslots */
+#define V_PCM_DR_8192 (0x2 << 4) /* 8.192Mbps, 128 timeslots */
+#define V_PCM_DR_075 (0x3 << 4) /* 0.75Mbps, 12 timeslots */
+#define V_PCM_LOOP (1 << 6) /* 1=internal loopback */
+#define V_PCM_SMPL (1 << 7) /* 0=sample at middle of bit cell, 1=sample at 3/4 point */
+#define V_PLL_ADJ_MASK (0x3 << 2)
+#define V_PCM_DR_MASK (0x3 << 4)
+
+/* R_PCM_MD2 bits */
+#define V_SYNC_OUT1 (1 << 1) /* SYNC_O source 0=SYNC_I or FSX_RX, 1=512kHz from PLL or multiframe */
+#define V_SYNC_SRC (1 << 2) /* 0=line interface, 1=SYNC_I */
+#define V_SYNC_OUT2 (1 << 3) /* SYNC_O source 0=rx sync or FSC_RX 1=SYNC_I or received superframe */
+#define V_C2O_EN (1 << 4) /* C2IO output enable (when V_C2I_EN=0) */
+#define V_C2I_EN (1 << 5) /* PCM controller clock source 0=C4IO, 1=C2IO */
+#define V_PLL_ICR (1 << 6) /* 0=reduce PCM frame time, 1=increase */
+#define V_PLL_MAN (1 << 7) /* 0=auto, 1=manual */
+
+/* A_SL_CFG bits */
+#define V_CH_SDIR (1 << 0) /* 1=HFC channel receives data from PCM TS */
+#define V_ROUT_TX_DIS (0x0 << 6) /* disabled, output disabled */
+#define V_ROUT_TX_LOOP (0x1 << 6) /* internally looped, output disabled */
+#define V_ROUT_TX_STIO1 (0x2 << 6) /* output data to STIO1 */
+#define V_ROUT_TX_STIO2 (0x3 << 6) /* output data to STIO2 */
+#define V_ROUT_RX_DIS (0x0 << 6) /* disabled, input data ignored */
+#define V_ROUT_RX_LOOP (0x1 << 6) /* internally looped, input data ignored */
+#define V_ROUT_RX_STIO2 (0x2 << 6) /* channel data comes from STIO1 */
+#define V_ROUT_RX_STIO1 (0x3 << 6) /* channel data comes from STIO2 */
+#define V_CH_SNUM_SHIFT (1)
+#define V_CH_SNUM_MASK (31 << 1)
+
+/* A_CON_HDLC bits */
+#define V_IFF (1 << 0) /* Inter-Frame Fill: 0=0x7e, 1=0xff */
+#define V_HDLC_TRP (1 << 1) /* 0=HDLC mode, 1=transparent */
+#define V_TRP_DISABLED (0x0 << 2) /* FIFO disabled, no interrupt */
+#define V_TRP_IRQ_64 (0x1 << 2) /* FIFO enabled, int @ 8 bytes */
+#define V_TRP_IRQ_128 (0x2 << 2) /* FIFO enabled, int @ 16 bytes */
+#define V_TRP_IRQ_256 (0x3 << 2) /* FIFO enabled, int @ 32 bytes */
+#define V_TRP_IRQ_512 (0x4 << 2) /* FIFO enabled, int @ 64 bytes */
+#define V_TRP_IRQ_1024 (0x5 << 2) /* FIFO enabled, int @ 128 bytes */
+#define V_TRP_NO_IRQ (0x7 << 2) /* FIFO enabled, no interrupt */
+#define V_HDLC_IRQ (0x3 << 2) /* HDLC: FIFO enabled, interrupt at end of frame or when FIFO > 16 byte boundary (Mixed IRQ) */
+#define V_DATA_FLOW_000 (0x0 << 5) /* see A_CON_HDLC reg description in datasheet */
+#define V_DATA_FLOW_001 (0x1 << 5) /* see A_CON_HDLC reg description in datasheet */
+#define V_DATA_FLOW_010 (0x2 << 5) /* see A_CON_HDLC reg description in datasheet */
+#define V_DATA_FLOW_011 (0x3 << 5) /* see A_CON_HDLC reg description in datasheet */
+#define V_DATA_FLOW_100 (0x4 << 5) /* see A_CON_HDLC reg description in datasheet */
+#define V_DATA_FLOW_101 (0x5 << 5) /* see A_CON_HDLC reg description in datasheet */
+#define V_DATA_FLOW_110 (0x6 << 5) /* see A_CON_HDLC reg description in datasheet */
+#define V_DATA_FLOW_111 (0x7 << 5) /* see A_CON_HDLC reg description in datasheet */
+
+/* R_FIFO bits */
+#define V_FIFO_DIR (1 << 0) /* 1=RX FIFO data */
+#define V_REV (1 << 7) /* 1=MSB first */
+#define V_FIFO_NUM_SHIFT (1)
+#define V_FIFO_NUM_MASK (0x3e)
+
+/* A_CHANNEL bits */
+#define V_CH_FDIR (1 << 0) /* 1=HFC chan for RX data */
+#define V_CH_FNUM_SHIFT (1)
+#define V_CH_FNUM_MASK (0x3e)
+
+/* R_SLOT bits */
+#define V_SL_DIR (1 << 0) /* 1=timeslot will RX PCM data from bus */
+#define V_SL_NUM_SHIFT (1)
+#define V_SL_NUM_MASK (0xfe)
+
+/* A_INC_RES_FIFO bits */
+#define V_INC_F (1 << 0) /* 1=increment FIFO F-counter (bit auto-clears) */
+#define V_RES_FIFO (1 << 1) /* 1=reset FIFO (bit auto-clears) */
+#define V_RES_LOST (1 << 2) /* 1=reset LOST error (bit auto-clears) */
+#define V_RES_FIFO_ERR (1 << 3) /* 1=reset FIFO error (bit auto-clears), check V_ABO_DONE before setting */
+
+/* R_FIFO_MD bits */
+#define V_FIFO_MD_00 (0x0 << 0) /* 16 FIFOs, 64 bytes TX/RX, 128 TX or RX if V_UNIDIR_RX */
+#define V_FIFO_MD_01 (0x1 << 0) /* 8 FIFOs, 128 bytes TX/RX, 256 TX or RX if V_UNIDIR_RX */
+#define V_FIFO_MD_10 (0x2 << 0) /* 4 FIFOs, 256 bytes TX/RX, invalid mode with V_UNIDIR_RX */
+#define V_DF_MD_SM (0x0 << 2) /* simple data flow mode */
+#define V_DF_MD_CSM (0x1 << 2) /* channel select mode */
+#define V_DF_MD_FSM (0x3 << 2) /* FIFO sequence mode */
+#define V_UNIDIR_MD (1 << 4) /* 1=unidirectional FIFO mode */
+#define V_UNIDIR_RX (1 << 5) /* 1=unidirection FIFO is RX */
+
+/* A_SUBCH_CFG bits */
+#define V_BIT_CNT_8BIT (0) /* process 8 bits */
+#define V_BIT_CNT_1BIT (1) /* process 1 bit */
+#define V_BIT_CNT_2BIT (2) /* process 2 bits */
+#define V_BIT_CNT_3BIT (3) /* process 3 bits */
+#define V_BIT_CNT_4BIT (4) /* process 4 bits */
+#define V_BIT_CNT_5BIT (5) /* process 5 bits */
+#define V_BIT_CNT_6BIT (6) /* process 6 bits */
+#define V_BIT_CNT_7BIT (7) /* process 7 bits */
+#define V_LOOP_FIFO (1 << 6) /* loop FIFO data */
+#define V_INV_DATA (1 << 7) /* invert FIFO data */
+#define V_START_BIT_SHIFT (3)
+#define V_START_BIT_MASK (0x38)
+
+/* R_SU_SYNC bits */
+#define V_SYNC_SEL_PORT0 (0x0 << 0) /* sync to TE port 0 */
+#define V_SYNC_SEL_PORT1 (0x1 << 0) /* sync to TE port 1 */
+#define V_SYNC_SEL_PORT2 (0x2 << 0) /* sync to TE port 2 */
+#define V_SYNC_SEL_PORT3 (0x3 << 0) /* sync to TE port 3 */
+#define V_SYNC_SEL_SYNCI (0x4 << 0) /* sync to SYNC_I */
+#define V_MAN_SYNC (1 << 3) /* 1=manual sync mode */
+#define V_AUTO_SYNCI (1 << 4) /* 1=SYNC_I used if FSC_RX not found */
+#define V_D_MERGE_TX (1 << 5) /* 1=all 4 dchan taken from one byte in TX */
+#define V_E_MERGE_RX (1 << 6) /* 1=all 4 echan combined in RX direction */
+#define V_D_MERGE_RX (1 << 7) /* 1=all 4 dchan combined in RX direction */
+#define V_SYNC_SEL_MASK (0x03)
+
+/* A_SU_WR_STA bits */
+#define V_SU_SET_STA_MASK (0x0f)
+#define V_SU_LD_STA (1 << 4) /* 1=force SU_SET_STA mode, must be manually cleared 6us later */
+#define V_SU_ACT_NOP (0x0 << 5) /* NOP */
+#define V_SU_ACT_DEACTIVATE (0x2 << 5) /* start deactivation. auto-clears */
+#define V_SU_ACT_ACTIVATE (0x3 << 5) /* start activation. auto-clears. */
+#define V_SET_G2_G3 (1 << 7) /* 1=auto G2->G3 in NT mode. auto-clears after transition. */
+
+/* A_SU_RD_STA */
+#define V_SU_STA_MASK (0x0f)
+#define V_SU_FR_SYNC (1 << 4) /* 1=synchronized */
+#define V_SU_T2_EXP (1 << 5) /* 1=T2 expired (NT only) */
+#define V_SU_INFO0 (1 << 6) /* 1=INFO0 */
+#define V_G2_G3 (1 << 7) /* 1=allows G2->G3 (NT only, auto-clears) */
+
+/* A_SU_CLK_DLY bits */
+#define V_SU_DLY_MASK (0x0f)
+#define V_SU_SMPL_MASK (0xf0)
+#define V_SU_SMPL_SHIFT (4)
+
+/* A_SU_CTRL0 bits */
+#define V_B1_TX_EN (1 << 0) /* 1=B1-channel transmit */
+#define V_B2_TX_EN (1 << 1) /* 1=B2-channel transmit */
+#define V_SU_MD (1 << 2) /* 0=TE, 1=NT */
+#define V_ST_D_LPRIO (1 << 3) /* D-Chan priority 0=high, 1=low */
+#define V_ST_SQ_EN (1 << 4) /* S/Q bits transmit (1=enabled) */
+#define V_SU_TST_SIG (1 << 5) /* 1=transmit test signal */
+#define V_ST_PU_CTRL (1 << 6) /* 1=enable end of pulse control */
+#define V_SU_STOP (1 << 7) /* 1=power down */
+
+/* A_SU_CTRL1 bits */
+#define V_G2_G3_EN (1 << 0) /* 1=G2->G3 allowed without V_SET_G2_G3 */
+#define V_D_RES (1 << 2) /* 1=D-chan reset */
+#define V_ST_E_IGNO (1 << 3) /* TE:1=ignore Echan, NT:should always be 1. */
+#define V_ST_E_LO (1 << 4) /* NT only: 1=force Echan low */
+#define V_BAC_D (1 << 6) /* 1=BAC bit controls Dchan TX */
+#define V_B12_SWAP (1 << 7) /* 1=swap B1/B2 */
+
+/* A_SU_CTRL2 bits */
+#define V_B1_RX_EN (1 << 0) /* 1=enable B1 RX */
+#define V_B2_RX_EN (1 << 1) /* 1=enable B2 RX */
+#define V_MS_SSYNC2 (1 << 2) /* 0 normally, see datasheet */
+#define V_BAC_S_SEL (1 << 3) /* see datasheet */
+#define V_SU_SYNC_NT (1 << 4) /* 0=sync pulses generated only in TE, 1=in TE and NT */
+#define V_SU_2KHZ (1 << 5) /* 0=96kHz test tone, 1=2kHz */
+#define V_SU_TRI (1 << 6) /* 1=tristate output buffer */
+#define V_SU_EXCHG (1 << 7) /* 1=invert output drivers */
+
+/* R_IRQ_OVIEW bits */
+#define V_FIFO_BL0_IRQ (1 << 0) /* FIFO 0-3 IRQ */
+#define V_FIFO_BL1_IRQ (1 << 1) /* FIFO 4-7 IRQ */
+#define V_FIFO_BL2_IRQ (1 << 2) /* FIFO 8-11 IRQ */
+#define V_FIFO_BL3_IRQ (1 << 3) /* FIFO 12-15 IRQ */
+#define V_MISC_IRQ (1 << 4) /* R_MISC_IRQ changed */
+#define V_STUP_IRQ (1 << 5) /* R_SU_IRQ changed */
+#define V_FIFO_BLx_IRQ (V_FIFO_BL0_IRQ | V_FIFO_BL1_IRQ | V_FIFO_BL2_IRQ | V_FIFO_BL3_IRQ)
+
+/* R_FIRST_FIFO bits */
+#define V_FIRST_FIFO_NUM_SHIFT (1)
+
+/* A_FIFO_SEQ bits */
+#define V_NEXT_FIFO_NUM_SHIFT (1)
+#define V_SEQ_END (1 << 6)
+
+#if (DAHDI_CHUNKSIZE != 8)
+#error Sorry, the b400m does not support chunksize != 8
+#endif
+
+/* general debug messages */
+#define DEBUG_GENERAL (1 << 0)
+/* emit DTMF detector messages */
+#define DEBUG_DTMF (1 << 1)
+/* emit register read/write, but only if the kernel's DEBUG is defined */
+#define DEBUG_REGS (1 << 2)
+/* emit file operation messages */
+#define DEBUG_FOPS (1 << 3)
+#define DEBUG_ECHOCAN (1 << 4)
+/* S/T state machine */
+#define DEBUG_ST_STATE (1 << 5)
+/* HDLC controller */
+#define DEBUG_HDLC (1 << 6)
+/* alarm changes */
+#define DEBUG_ALARM (1 << 7)
+/* Timing related changes */
+#define DEBUG_TIMING (1 << 8)
+
+#define DBG (bri_debug & DEBUG_GENERAL)
+#define DBG_DTMF (bri_debug & DEBUG_DTMF)
+#define DBG_REGS (bri_debug & DEBUG_REGS)
+#define DBG_FOPS (bri_debug & DEBUG_FOPS)
+#define DBG_EC (bri_debug & DEBUG_ECHOCAN)
+#define DBG_ST (bri_debug & DEBUG_ST_STATE)
+#define DBG_HDLC (bri_debug & DEBUG_HDLC)
+#define DBG_ALARM (bri_debug & DEBUG_ALARM)
+#define DBG_TIMING (bri_debug & DEBUG_TIMING)
+
+#define DBG_SPANFILTER ((1 << bspan->port) & bri_spanfilter)
+
+/* #define HARDHDLC_RX */
+
+/* Any static variables not initialized by default should be set
+ * to 0 automatically */
+int bri_debug;
+int bri_spanfilter = 9;
+int bri_teignorered = 1;
+int bri_alarmdebounce;
+int bri_persistentlayer1;
+int timingcable;
+
+static int synccard = -1;
+static int syncspan = -1;
+
+static const int TIMER_3_MS = 30000;
+
+#define b4_info(b4, format, arg...) \
+ dev_info(&(b4)->wc->vb.pdev->dev , format , ## arg)
+
+/* if defined, swaps ports 2 and 3 on the B400M module */
+#define SWAP_PORTS
+
+#define XHFC_T1 0
+#define XHFC_T2 1
+#define XHFC_T3 2
+/* T4 - Special timer, used for debug purposes for monitoring of L1 state during activation attempt. */
+#define XHFC_T4 3
+
+#define B400M_CHANNELS_PER_SPAN 3 /* 2 B-channels and 1 D-Channel for each BRI span */
+#define B400M_HDLC_BUF_LEN 128 /* arbitrary, just the max # of byts we will send to DAHDI per call */
+
+#define get_F(f1, f2, flen) { \
+ f1 = hfc_readcounter8(b4, A_F1); \
+ f2 = hfc_readcounter8(b4, A_F2); \
+ flen = f1 - f2; \
+ \
+ if (flen < 0) \
+ flen += (HFC_FMAX - HFC_FMIN) + 1; \
+ }
+
+#define get_Z(z1, z2, zlen) { \
+ z1 = hfc_readcounter8(b4, A_Z1); \
+ z2 = hfc_readcounter8(b4, A_Z2); \
+ zlen = z1 - z2; \
+ \
+ if (zlen < 0) \
+ zlen += (HFC_ZMAX - HFC_ZMIN) + 1; \
+ }
+
+struct b400m_span {
+ struct b400m *parent;
+ unsigned int port; /* which S/T port this span belongs to */
+
+ int oldstate; /* old state machine state */
+ int newalarm; /* alarm to send to DAHDI once alarm timer expires */
+ unsigned long alarmtimer;
+
+ unsigned int te_mode:1; /* 1=TE, 0=NT */
+ unsigned int term_on:1; /* 1= 390 ohm termination enable, 0 = disabled */
+ unsigned long hfc_timers[B400M_CHANNELS_PER_SPAN+1]; /* T1, T2, T3 */
+ int hfc_timer_on[B400M_CHANNELS_PER_SPAN+1]; /* 1=timer active */
+ int fifos[B400M_CHANNELS_PER_SPAN]; /* B1, B2, D <--> host fifo numbers */
+
+ /* HDLC controller fields */
+ struct dahdi_span *span; /* pointer to the actual dahdi_span */
+ struct dahdi_chan *sigchan; /* pointer to the signalling channel for this span */
+ int sigactive; /* nonzero means we're in the middle of sending an HDLC frame */
+ atomic_t hdlc_pending; /* hdlc_hard_xmit() increments, hdlc_tx_frame() decrements */
+ unsigned int frames_out;
+ unsigned int frames_in;
+
+ struct fasthdlc_state rxhdlc;
+ int infcs;
+ int f_sz;
+};
+
+/* This structure exists one per module */
+struct b400m {
+ char name[10];
+ int position; /* module position in carrier board */
+ int b400m_no; /* 0-based B400M number in system */
+
+ struct wctdm *wc; /* parent structure */
+
+ spinlock_t reglock; /* lock for all register accesses */
+
+ unsigned long ticks;
+
+ unsigned long fifo_en_rxint; /* each bit is the RX int enable for that FIFO */
+ unsigned long fifo_en_txint; /* each bit is the TX int enable for that FIFO */
+ unsigned char fifo_irqstatus; /* top-half ORs in new interrupts, bottom-half ANDs them out */
+
+ int setsyncspan; /* Span reported from HFC for sync on this card */
+ int reportedsyncspan; /* Span reported from HFC for sync on this card */
+
+ unsigned int running:1; /* interrupts are enabled */
+ unsigned int shutdown:1; /* 1=bottom half doesn't process anything, just returns */
+ unsigned int inited:1; /* FIXME: temporary */
+ unsigned int misc_irq_mask:1; /* 1= interrupt is valid */
+
+ struct b400m_span spans[4]; /* Individual spans */
+
+ struct workqueue_struct *xhfc_ws;
+ struct work_struct xhfc_wq;
+
+ unsigned char irq_oview; /* copy of r_irq_oview */
+ unsigned char fifo_fill; /* copy of R_FILL_BL0 */
+
+ struct semaphore regsem; /* lock for low-level register accesses */
+ struct semaphore fifosem; /* lock for fifo accesses */
+
+ unsigned char lastreg; /* last XHFC register accessed (used to speed up multiple address "hits" */
+};
+
+static void hfc_start_st(struct b400m_span *s);
+static void hfc_stop_st(struct b400m_span *s);
+
+void b400m_set_dahdi_span(struct b400m *b4, int spanno,
+ struct dahdi_span *span)
+{
+ span->pvt = &b4->spans[spanno];
+ b4->spans[spanno].span = span;
+}
+
+static inline void flush_hw(void)
+{
+}
+
+static int xhfc_getreg(struct wctdm *wc, int card,
+ int addr, u8 *lastreg)
+{
+ int x;
+
+ if (*lastreg != (unsigned char)addr) {
+ wctdm_setreg(wc, card, 0x60, addr);
+ *lastreg = (unsigned char)addr;
+ }
+ x = wctdm_getreg(wc, card, 0x80);
+ return x;
+}
+
+static int xhfc_setreg(struct wctdm *wc, int card, int addr,
+ int val, u8 *lastreg)
+{
+ if (*lastreg != (unsigned char)addr) {
+ wctdm_setreg(wc, card, 0x60, addr);
+ *lastreg = (unsigned char)addr;
+ }
+ return wctdm_setreg(wc, card, 0x00, val);
+}
+
+static int b400m_getreg(struct b400m *b4, int addr)
+{
+ int x;
+
+ if (down_trylock(&b4->regsem)) {
+ if (down_interruptible(&b4->regsem)) {
+ b4_info(b4, "b400m_getreg(0x%02x) interrupted\n",
+ addr);
+ return -1;
+ }
+ }
+
+ x = xhfc_getreg(b4->wc, b4->position, addr, &b4->lastreg);
+ up(&b4->regsem);
+
+ return x;
+}
+
+static int b400m_setreg(struct b400m *b4, const int addr, const int val)
+{
+ int x;
+
+ if (down_trylock(&b4->regsem)) {
+ if (down_interruptible(&b4->regsem)) {
+ b4_info(b4, "b400m_setreg(0x%02x -> 0x%02x) "
+ "interrupted\n", val, addr);
+ return -1;
+ }
+ }
+
+ x = xhfc_setreg(b4->wc, b4->position, addr, val, &b4->lastreg);
+ up(&b4->regsem);
+
+ return x;
+}
+
+
+/*
+ * A lot of the registers in the XHFC are indexed.
+ * this function sets the index, and then writes to the indexed register.
+ */
+static void b400m_setreg_ra(struct b400m *b4, u8 r, u8 rd, u8 a, u8 ad)
+{
+ if (down_trylock(&b4->regsem)) {
+ if (down_interruptible(&b4->regsem)) {
+ b4_info(b4, "b400m_setreg_ra(0x%02x -> 0x%02x) "
+ "interrupted\n", a, ad);
+ return;
+ }
+ }
+
+ xhfc_setreg(b4->wc, b4->position, r, rd, &b4->lastreg);
+ xhfc_setreg(b4->wc, b4->position, a, ad, &b4->lastreg);
+ up(&b4->regsem);
+}
+
+static u8 b400m_getreg_ra(struct b400m *b4, u8 r, u8 rd, u8 a)
+{
+ unsigned char res;
+ if (down_trylock(&b4->regsem)) {
+ if (down_interruptible(&b4->regsem)) {
+ b4_info(b4, "b400m_getreg_ra(0x%02x) interrupted\n",
+ a);
+ return -1;
+ }
+ }
+
+ xhfc_setreg(b4->wc, b4->position, r, rd, &b4->lastreg);
+ res = xhfc_getreg(b4->wc, b4->position, a, &b4->lastreg);
+ up(&b4->regsem);
+ return res;
+}
+
+
+/*
+ * XHFC-4S GPIO routines
+ *
+ * the xhfc doesn't use its gpio for anything. :-)
+ */
+
+/*
+ * initialize XHFC GPIO.
+ * GPIO 0-7 are output, low (unconnected, or used for their primary function).
+ */
+static void hfc_gpio_init(struct b400m *b4)
+{
+ /* GPIO0..3,7 are GPIO, 4,5,6 primary function */
+ b400m_setreg(b4, R_GPIO_SEL, 0x8f);
+ /* GPIO0..7 drivers set low */
+ b400m_setreg(b4, R_GPIO_OUT0, 0x00);
+ /* GPIO0..7 drivers enabled */
+ b400m_setreg(b4, R_GPIO_EN0, 0xff);
+ /* all other GPIO set to primary function */
+ b400m_setreg(b4, R_GPIO_SEL_BL, 0x00);
+
+}
+
+
+/* performs a register write and then waits for the HFC "busy" bit to clear
+ * NOTE: doesn't actually read status, since busy bit is 1us typically, and
+ * we're much, much slower than that. */
+static void hfc_setreg_waitbusy(struct b400m *b4, const unsigned int reg,
+ const unsigned int val)
+{
+ b400m_setreg(b4, reg, val);
+}
+
+/*
+ * reads an 8-bit register over over and over until the same value is read
+ * twice, then returns that value.
+ */
+static unsigned char hfc_readcounter8(struct b400m *b4, const unsigned int reg)
+{
+ unsigned char r1, r2;
+ unsigned long maxwait = 1048576;
+
+ do {
+ r1 = b400m_getreg(b4, reg);
+ r2 = b400m_getreg(b4, reg);
+ } while ((r1 != r2) && maxwait--);
+
+ if (!maxwait) {
+ if (printk_ratelimit()) {
+ dev_warn(&b4->wc->vb.pdev->dev,
+ "hfc_readcounter8(reg 0x%02x) timed out " \
+ "waiting for data to settle!\n", reg);
+ }
+ }
+
+ return r1;
+}
+
+/* performs a soft-reset of the HFC-4S. */
+static void hfc_reset(struct b400m *b4)
+{
+ unsigned long start;
+ int TIMEOUT = HZ; /* 1s */
+
+ /* Set the FIFOs to 8 128 bytes FIFOs, bidirectional, and set up the
+ * flow controller for channel select mode. */
+ /* Note, this reg has to be set *before* the SW reset */
+ b400m_setreg(b4, R_FIFO_MD, V_FIFO_MD_01 | V_DF_MD_FSM);
+
+ msleep(1); /* wait a bit for clock to settle */
+/* reset everything, wait 100ms, then allow the XHFC to come out of reset */
+ b400m_setreg(b4, R_CIRM, V_SRES);
+ flush_hw();
+
+ wait_just_a_bit(HZ/10);
+
+ b400m_setreg(b4, R_CIRM, 0x00);
+ flush_hw();
+
+ /* wait for XHFC to come out of reset. */
+ start = jiffies;
+ while (b400m_getreg(b4, R_STATUS) & (V_BUSY | V_PCM_INIT)) {
+ if (time_after(jiffies, start + TIMEOUT)) {
+ b4_info(b4, "hfc_reset() Module won't come out of "
+ "reset... continuing.\n");
+ break;
+ }
+ };
+
+ /* Disable the output clock pin, and also the PLL (it's not needed) */
+ b400m_setreg(b4, R_CTRL, 0x00);
+}
+
+static void hfc_enable_fifo_irqs(struct b400m *b4)
+{
+ b400m_setreg(b4, R_IRQ_CTRL, V_FIFO_IRQ_EN | V_GLOB_IRQ_EN);
+ flush_hw();
+}
+
+static void hfc_enable_interrupts(struct b400m *b4)
+{
+ b4->running = 1;
+
+ /* mask all misc interrupts */
+ b4->misc_irq_mask = 0x01;
+ b400m_setreg(b4, R_MISC_IRQMSK, b4->misc_irq_mask);
+
+/* clear any pending interrupts */
+ b400m_getreg(b4, R_STATUS);
+ b400m_getreg(b4, R_MISC_IRQ);
+ b400m_getreg(b4, R_FIFO_BL0_IRQ);
+ b400m_getreg(b4, R_FIFO_BL1_IRQ);
+ b400m_getreg(b4, R_FIFO_BL2_IRQ);
+ b400m_getreg(b4, R_FIFO_BL3_IRQ);
+
+ hfc_enable_fifo_irqs(b4);
+}
+
+static inline void hfc_reset_fifo(struct b400m *b4)
+{
+ hfc_setreg_waitbusy(b4, A_INC_RES_FIFO,
+ V_RES_FIFO | V_RES_LOST | V_RES_FIFO_ERR);
+}
+
+static void hfc_setup_fifo(struct b400m *b4, int fifo)
+{
+ if (fifo < 4) {
+ /* TX */
+ hfc_setreg_waitbusy(b4, R_FIFO, (fifo << V_FIFO_NUM_SHIFT));
+ b400m_setreg(b4, A_CON_HDLC,
+ V_HDLC_IRQ | V_DATA_FLOW_000 | V_IFF);
+ hfc_reset_fifo(b4);
+
+ /* RX */
+ hfc_setreg_waitbusy(b4, R_FIFO,
+ (fifo << V_FIFO_NUM_SHIFT) | V_FIFO_DIR);
+ b400m_setreg(b4, A_CON_HDLC,
+ V_HDLC_IRQ | V_DATA_FLOW_000 | V_IFF);
+ hfc_reset_fifo(b4);
+ } else {
+ /* TX */
+ hfc_setreg_waitbusy(b4, R_FIFO, (fifo << V_FIFO_NUM_SHIFT));
+ b400m_setreg(b4, A_CON_HDLC,
+ V_HDLC_TRP | V_TRP_NO_IRQ | V_DATA_FLOW_110);
+ hfc_reset_fifo(b4);
+
+ /* RX */
+ hfc_setreg_waitbusy(b4, R_FIFO,
+ (fifo << V_FIFO_NUM_SHIFT) | V_FIFO_DIR);
+ b400m_setreg(b4, A_CON_HDLC,
+ V_HDLC_TRP | V_TRP_NO_IRQ | V_DATA_FLOW_110);
+ hfc_reset_fifo(b4);
+ }
+}
+
+static void hfc_setup_pcm(struct b400m *b4, int port)
+{
+ int physport;
+ int offset;
+ int hfc_chan;
+ int ts;
+#ifdef HARDHDLC_RX
+ const int MAX_OFFSET = 2;
+#else
+ const int MAX_OFFSET = 3;
+#endif
+
+#ifdef SWAP_PORTS
+ /* swap the middle ports */
+ physport = (1 == port) ? 2 : (2 == port) ? 1 : port;
+#else
+ physport = port;
+#endif
+
+ for (offset = 0; offset < MAX_OFFSET; offset++) {
+ hfc_chan = (port * 4) + offset;
+ ts = (physport * 3) + offset;
+ ts += (b4->b400m_no * 12);
+ b400m_setreg(b4, R_SLOT, (ts << V_SL_NUM_SHIFT));
+ b400m_setreg(b4, A_SL_CFG,
+ (hfc_chan << V_CH_SNUM_SHIFT) |
+ V_ROUT_TX_STIO2);
+
+ if (offset < 2) {
+ b400m_setreg(b4, R_SLOT,
+ (ts << V_SL_NUM_SHIFT) |
+ V_SL_DIR);
+ b400m_setreg(b4, A_SL_CFG,
+ (hfc_chan << V_CH_SNUM_SHIFT) |
+ V_ROUT_RX_STIO1 | V_CH_SDIR);
+ }
+ }
+}
+
+#ifdef SWAP_PORTS
+#ifdef HARDHDLC_RX
+static const int fifos[24] = {0, 0, 2, 2, 1, 1, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6,
+ 5, 5, 5, 5, 7, 7, 7, 7 };
+#else
+static const int fifos[24] = {0, 4, 2, 6, 1, 5, 3, 7, 4, 4, 4, 4, 6, 6, 6, 6,
+ 5, 5, 5, 5, 7, 7, 7, 7 };
+#endif
+static const int hfc_chans[12] = {2, 10, 6, 14, 0, 1, 8, 9, 4, 5, 12, 13 };
+#else
+#ifdef HARDHDLC_RX
+static const int fifos[24] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5,
+ 6, 6, 6, 6, 7, 7, 7, 7 };
+#else
+static const int fifos[24] = {0, 4, 1, 5, 2, 6, 3, 7, 4, 4, 4, 4, 5, 5, 5, 5,
+ 6, 6, 6, 6, 7, 7, 7, 7 };
+#endif
+static const int hfc_chans[12] = { 2, 6, 10, 14, 0, 1, 4, 5, 8, 9, 12, 13 };
+#endif
+
+static void hfc_setup_fifo_arrays(struct b400m *b4, int fifo)
+{
+ int val;
+ if (!fifo) {
+ val = (fifos[fifo] << V_FIRST_FIFO_NUM_SHIFT) | (fifo & 1);
+ b400m_setreg(b4, R_FIRST_FIFO, val);
+ } else {
+#ifdef HARDHDLC_RX
+ val = (fifos[fifo] << V_NEXT_FIFO_NUM_SHIFT) | (fifo & 1);
+#else
+ val = (fifo < 8) ? (fifos[fifo] << V_NEXT_FIFO_NUM_SHIFT) :
+ (fifos[fifo] << V_NEXT_FIFO_NUM_SHIFT) |
+ (fifo&1);
+#endif
+ b400m_setreg(b4, A_FIFO_SEQ, val);
+ }
+
+ b400m_setreg(b4, R_FSM_IDX, fifo);
+ val = (fifo < 8) ? (hfc_chans[fifo>>1] << V_CH_FNUM_SHIFT) :
+ (hfc_chans[fifo>>1] << V_CH_FNUM_SHIFT) |
+ (fifo & 1);
+
+ b400m_setreg(b4, A_CHANNEL, val);
+ b400m_setreg(b4, A_SUBCH_CFG, 0x02);
+}
+
+static void hfc_setup_fsm(struct b400m *b4)
+{
+ int chan, fifo, port, offset;
+#ifdef SWAP_PORTS
+ const int chan_to_fifo[12] = { 4, 4, 0, 6, 6, 2, 5, 5, 1, 7, 7, 3 };
+#else
+ const int chan_to_fifo[12] = { 4, 4, 0, 5, 5, 1, 6, 6, 2, 7, 7, 3 };
+#endif
+
+ for (port = 0; port < 4; port++) {
+ for (offset = 0; offset < 3; offset++) {
+ b4->spans[port].fifos[offset] =
+ chan_to_fifo[(port * 3) + offset];
+ }
+ }
+
+ for (chan = 0; chan < ARRAY_SIZE(fifos); chan++)
+ hfc_setup_fifo_arrays(b4, chan);
+
+ b400m_setreg(b4, A_FIFO_SEQ, V_SEQ_END);
+
+ for (fifo = 0; fifo < 8; fifo++)
+ hfc_setup_fifo(b4, fifo);
+
+ for (port = 0; port < 4; port++)
+ hfc_setup_pcm(b4, port);
+}
+
+/* takes a read/write fifo pair and optionally resets it, optionally enabling
+ * the rx/tx interrupt */
+static void hfc_reset_fifo_pair(struct b400m *b4, int fifo,
+ int reset, int force_no_irq)
+{
+ unsigned char b;
+
+ if (down_interruptible(&b4->fifosem)) {
+ b4_info(b4, "Unable to retrieve fifo sem\n");
+ return;
+ }
+ b = (!force_no_irq && b4->fifo_en_txint & (1 << fifo)) ?
+ V_FIFO_IRQMSK : 0;
+ hfc_setreg_waitbusy(b4, R_FIFO, (fifo << V_FIFO_NUM_SHIFT));
+
+ if (fifo < 4)
+ b |= V_MIX_IRQ;
+
+ b400m_setreg(b4, A_FIFO_CTRL, b);
+
+ if (reset)
+ hfc_reset_fifo(b4);
+
+ b = (!force_no_irq && b4->fifo_en_rxint & (1 << fifo)) ?
+ V_FIFO_IRQMSK : 0;
+ hfc_setreg_waitbusy(b4, R_FIFO,
+ (fifo << V_FIFO_NUM_SHIFT) | V_FIFO_DIR);
+
+ if (fifo < 4)
+ b |= V_MIX_IRQ;
+
+ b400m_setreg(b4, A_FIFO_CTRL, b);
+
+ if (reset)
+ hfc_reset_fifo(b4);
+
+ up(&b4->fifosem);
+}
+
+static void xhfc_set_sync_src(struct b400m *b4, int port)
+{
+ int b;
+
+ /* -2 means we need to go back and try again later */
+ if (port == -2)
+ return;
+
+ if (port == b4->setsyncspan)
+ return;
+ else
+ b4->setsyncspan = port;
+
+ b4_info(b4, "xhfc_set_sync_src - modpos %d: setting sync to "
+ "be port %d\n", b4->position, port);
+
+ if (port == -1) /* automatic */
+ b = 0;
+ else {
+#ifdef SWAP_PORTS
+ port = (1 == port) ? 2 : (2 == port) ? 1 : port;
+#endif
+ b = (port & V_SYNC_SEL_MASK) | V_MAN_SYNC;
+ }
+
+ b400m_setreg(b4, R_SU_SYNC, b);
+}
+
+static void wctdm_change_card_sync_src(struct wctdm *wc, int newsrc, int master)
+{
+ int newctlreg;
+
+ newctlreg = wc->ctlreg;
+
+ if (master)
+ newctlreg |= (1 << 5);
+ else
+ newctlreg &= ~(1 << 5);
+
+ newctlreg &= 0xfc;
+
+ newctlreg |= newsrc;
+
+ if (DBG_TIMING) {
+ dev_info(&wc->vb.pdev->dev,
+ "Final ctlreg before swap: %02x\n", newctlreg);
+ }
+
+ wc->ctlreg = newctlreg;
+ wc->oldsync = newsrc;
+
+ msleep(10);
+}
+
+static void wctdm_change_system_sync_src(int oldsync, int oldspan,
+ int newsync, int newspan)
+{
+ struct wctdm *wc;
+ struct wctdm *oldsyncwc = NULL, *newsyncwc = NULL;
+ int newspot;
+ int i;
+ int max_latency = 0;
+
+ if (oldsync > -1)
+ oldsyncwc = ifaces[oldsync];
+
+ if (newsync > -1)
+ newsyncwc = ifaces[newsync];
+
+ if (newsync == -1) {
+ BUG_ON(!ifaces[0]);
+
+ newsyncwc = ifaces[0];
+ newsync = 0;
+ }
+
+ newspot = (-1 == newspan) ? 0 : 2 | (newspan >> 2);
+
+ if ((oldsync == newsync) && (oldspan == newspan)) {
+ dev_info(&newsyncwc->vb.pdev->dev,
+ "No need for timing change. All is same\n");
+ return;
+ }
+
+ /* First we set all sources to local timing */
+ for (i = 0; i < WC_MAX_IFACES; i++) {
+ wc = ifaces[i];
+ if ((wc != oldsyncwc) && wc) {
+ wctdm_change_card_sync_src(wc, 0, 0);
+ if (voicebus_current_latency(&wc->vb) > max_latency)
+ max_latency = voicebus_current_latency(&wc->vb);
+ }
+ }
+ msleep(max_latency << 1);
+
+ /* Set the old sync source to local timing, not driving timing */
+ if (oldsyncwc) {
+ wctdm_change_card_sync_src(oldsyncwc, 0, 0);
+ msleep(voicebus_current_latency(&oldsyncwc->vb) << 1);
+ }
+
+ dev_info(&newsyncwc->vb.pdev->dev,
+ "Setting new card %d now to be timing master\n", newsync);
+ /* Finally, set the new sync source to broadcast master timing */
+ wctdm_change_card_sync_src(newsyncwc, newspot, 1);
+ msleep(voicebus_current_latency(&newsyncwc->vb) << 1);
+
+ /* Last we double verify and set all the remaining cards to be timing
+ * slaves */
+ for (i = 0; (i < WC_MAX_IFACES) && ifaces[i]; i++) {
+ wc = ifaces[i];
+ if (i == newsync)
+ continue;
+
+ dev_info(&wc->vb.pdev->dev,
+ "Setting card %d to be timing slave\n", i);
+ wctdm_change_card_sync_src(wc, 1, 0);
+ }
+ msleep(max_latency << 1);
+ synccard = newsync;
+ syncspan = newspan;
+}
+
+static int xhfc_find_sync_with_timingcable(struct b400m *b4)
+{
+ struct wctdm *wc = b4->wc;
+ int i, j, osrc, src = -1;
+ int lowestprio = 10000;
+ int lowestcard = -1;
+
+ if (down_trylock(&ifacelock)) {
+ set_bit(WCTDM_CHECK_TIMING, &wc->checkflag);
+ return -2;
+ }
+
+ for (j = 0; j < WC_MAX_IFACES && ifaces[j]; j++) {
+ if (!ifaces[j]->initialized) {
+ set_bit(WCTDM_CHECK_TIMING, &wc->checkflag);
+ osrc = -2;
+ goto out;
+ } else {
+ for (i = 0; i < (MAX_SPANS - 1); i++) {
+ struct wctdm_span *wspan = ifaces[j]->spans[i];
+ if (wspan &&
+ wspan->timing_priority &&
+ !wspan->span.alarms &&
+ (wspan->timing_priority <
+ lowestprio)) {
+ src = i;
+ lowestprio = wspan->timing_priority;
+ lowestcard = j;
+ }
+ }
+ }
+ }
+
+ if (lowestcard != synccard) {
+ b4_info(b4, "Found new timing master, card "
+ "%d. Old is card %d\n", lowestcard, synccard);
+ } else if (src != syncspan) {
+ b4_info(b4, "Timing change, but only from %d to %d on "
+ "card %d\n", syncspan, src, lowestcard);
+ }
+
+ wctdm_change_system_sync_src(synccard, syncspan,
+ lowestcard, src);
+
+ osrc = -1;
+
+ if (wc == ifaces[lowestcard]) {
+ if (src < (b4->position + 4) && (src >= b4->position))
+ osrc = src - b4->position;
+ }
+
+out:
+ up(&ifacelock);
+
+ return osrc;
+}
+
+static int xhfc_find_sync_without_timingcable(struct b400m *b4)
+{
+ struct wctdm *wc = b4->wc;
+ int i, osrc, src = -1;
+ int lowestprio = 10000;
+ int newctlregmux;
+
+ if (down_trylock(&wc->syncsem)) {
+ set_bit(WCTDM_CHECK_TIMING, &wc->checkflag);
+ return -2;
+ }
+
+ /* Find lowest slave timing priority on digital spans */
+ for (i = 0; i < (MAX_SPANS - 1); i++) {
+ struct wctdm_span *const wspan = wc->spans[i];
+ if (wspan && wspan->timing_priority &&
+ !wspan->span.alarms &&
+ (wspan->timing_priority < lowestprio)) {
+ src = i;
+ lowestprio = wspan->timing_priority;
+ }
+ }
+
+ if (src < 0) {
+ if (DBG_TIMING)
+ b4_info(b4, "Picked analog span\n");
+ osrc = src;
+ goto check_card_timing;
+ } else {
+ if (DBG_TIMING) {
+ b4_info(b4, "Picked span offset %d to be timing "
+ "source\n", src);
+ }
+ }
+
+ osrc = ((src < (b4->position + 4)) && (src >= b4->position)) ?
+ src - b4->position : -1;
+
+ if (DBG_TIMING) {
+ b4_info(b4, "For b4->position %d timing is %d\n",
+ b4->position, osrc);
+ }
+
+check_card_timing:
+
+ if (src != -1)
+ newctlregmux = 2 | (src >> 2);
+ else
+ newctlregmux = 0;
+
+ if ((newctlregmux & 3) != (wc->ctlreg & 3)) {
+ if (DBG_TIMING) {
+ b4_info(b4, "!!!Need to change timing "
+ "on baseboard to spot %d!!!\n",
+ src >> 2);
+ }
+
+ wctdm_change_card_sync_src(wc, newctlregmux, 0);
+ } else {
+ if (DBG_TIMING) {
+ dev_info(&b4->wc->vb.pdev->dev, "!!!No need to change timing " \
+ "on baseboard to spot %d, already there!!!\n",
+ src >> 2);
+
+ }
+ }
+
+
+ up(&wc->syncsem);
+
+ return osrc;
+}
+
+/*
+ * Finds the highest-priority sync span that is not in alarm and returns it.
+ * Note: the span #s in b4->spans[].sync are 1-based, and this returns a
+ * 0-based span, or -1 if no spans are found.
+ */
+static inline int xhfc_find_sync(struct b400m *b4)
+{
+ if (timingcable)
+ return xhfc_find_sync_with_timingcable(b4);
+ else
+ return xhfc_find_sync_without_timingcable(b4);
+}
+
+/*
+ * allocates memory and pretty-prints a given S/T state engine state to it.
+ * calling routine is responsible for freeing the pointer returned! Performs
+ * no hardware access whatsoever, but does use GFP_KERNEL so do not call from
+ * IRQ context. if full == 1, prints a "full" dump; otherwise just prints
+ * current state.
+ */
+static char *hfc_decode_st_state(struct b400m *b4, struct b400m_span *span,
+ unsigned char state, int full)
+{
+ int nt, sta;
+ char s[128], *str;
+ const char *ststr[2][16] = { /* TE, NT */
+ { "RESET", "?", "SENSING", "DEACT.",
+ "AWAIT.SIG", "IDENT.INPUT", "SYNCD", "ACTIVATED",
+ "LOSTFRAMING", "?", "?", "?",
+ "?", "?", "?", "?" },
+ { "RESET", "DEACT.", "PEND.ACT", "ACTIVE",
+ "PEND.DEACT", "?", "?", "?",
+ "?", "?", "?", "?",
+ "?", "?", "?", "?" }
+ };
+
+ str = kmalloc(256, GFP_KERNEL);
+ if (!str) {
+ dev_warn(&b4->wc->vb.pdev->dev,
+ "could not allocate mem for ST state decode " \
+ "string!\n");
+ return NULL;
+ }
+
+ nt = (span->te_mode == 0);
+ sta = (state & V_SU_STA_MASK);
+
+ sprintf(str, "P%d: %s state %c%d (%s)", span->port + 1,
+ (nt ? "NT" : "TE"), (nt ? 'G' : 'F'), sta,
+ ststr[nt][sta]);
+
+ if (full) {
+ sprintf(s, " SYNC: %s, RX INFO0: %s",
+ ((state & V_SU_FR_SYNC) ? "yes" : "no"),
+ ((state & V_SU_INFO0) ? "yes" : "no"));
+ strcat(str, s);
+
+ if (nt) {
+ sprintf(s, ", T2 %s, auto G2->G3: %s",
+ ((state & V_SU_T2_EXP) ? "expired" : "OK"),
+ ((state & V_G2_G3) ? "yes" : "no"));
+ strcat(str, s);
+ }
+ }
+
+ return str;
+}
+
+/*
+ * sets an S/T port state machine to a given state. if 'auto' is nonzero,
+ * will put the state machine back in auto mode after setting the state.
+ */
+static void hfc_handle_state(struct b400m_span *s);
+static void hfc_force_st_state(struct b400m *b4, struct b400m_span *s,
+ int state, int resume_auto)
+{
+ b400m_setreg_ra(b4, R_SU_SEL, s->port, A_SU_WR_STA,
+ state | V_SU_LD_STA);
+
+ if (resume_auto)
+ b400m_setreg_ra(b4, R_SU_SEL, s->port, A_SU_WR_STA, state);
+
+ if (DBG_ST && ((1 << s->port) & bri_spanfilter)) {
+ char *x;
+
+ x = hfc_decode_st_state(b4, s, state, 1);
+ b4_info(b4, "forced port %d to state %d (auto: %d), "
+ "new decode: %s\n", s->port + 1, state,
+ resume_auto, x);
+ kfree(x);
+ }
+
+ /* make sure that we activate any timers/etc needed by this state
+ * change */
+ hfc_handle_state(s);
+}
+
+/* figures out what to do when an S/T port's timer expires. */
+static void hfc_timer_expire(struct b400m_span *s, int t_no)
+{
+ struct b400m *b4 = s->parent;
+
+ if (DBG_ST && ((1 << s->port) & bri_spanfilter)) {
+ b4_info(b4, "%lu: hfc_timer_expire, Port %d T%d "
+ "expired (value=%lu ena=%d)\n", b4->ticks,
+ s->port + 1, t_no + 1, s->hfc_timers[t_no],
+ s->hfc_timer_on[t_no]);
+ }
+ /*
+ * there are three timers associated with every HFC S/T port.
+ *
+ * T1 is used by the NT state machine, and is the maximum time the NT
+ * side should wait for G3 (active) state.
+ *
+ * T2 is not actually used in the driver, it is handled by the HFC-4S
+ * internally.
+ *
+ * T3 is used by the TE state machine; it is the maximum time the TE
+ * side should wait for the INFO4 (activated) signal.
+ */
+
+ /* First, disable the expired timer; hfc_force_st_state() may activate
+ * it again. */
+ s->hfc_timer_on[t_no] = 0;
+
+ switch (t_no) {
+ case XHFC_T1: /* switch to G4 (pending deact.), resume auto mode */
+ hfc_force_st_state(b4, s, 4, 1);
+ break;
+ case XHFC_T2: /* switch to G1 (deactivated), resume auto mode */
+ hfc_force_st_state(b4, s, 1, 1);
+ break;
+ case XHFC_T3: /* switch to F3 (deactivated), resume auto mode */
+ hfc_stop_st(s);
+ if (bri_persistentlayer1)
+ hfc_start_st(s);
+ break;
+ case XHFC_T4: /* switch to F3 (deactivated), resume auto mode */
+ hfc_handle_state(s);
+ s->hfc_timers[XHFC_T4] = b4->ticks + 1000;
+ s->hfc_timer_on[XHFC_T4] = 1;
+ break;
+ default:
+ if (printk_ratelimit()) {
+ dev_warn(&b4->wc->vb.pdev->dev,
+ "hfc_timer_expire found an unknown expired "
+ "timer (%d)??\n", t_no);
+ }
+ }
+}
+
+/*
+ * Run through the active timers on a card and deal with any expiries.
+ * Also see if the alarm debounce time has expired and if it has, tell DAHDI.
+ */
+static void hfc_update_st_timers(struct b400m *b4)
+{
+ int i, j;
+ struct b400m_span *s;
+
+ for (i = 0; i < 4; i++) {
+ s = &b4->spans[i];
+
+ for (j = XHFC_T1; j <= XHFC_T4; j++) {
+
+ /* we don't really do timer2, it is expired by the
+ * state change handler */
+ if (j == XHFC_T2)
+ continue;
+
+ if (s->hfc_timer_on[j] &&
+ time_after_eq(b4->ticks, s->hfc_timers[j]))
+ hfc_timer_expire(s, j);
+ }
+
+ if (s->span && s->newalarm != s->span->alarms &&
+ time_after_eq(b4->ticks, s->alarmtimer)) {
+ s->span->alarms = s->newalarm;
+ if ((!s->newalarm && bri_teignorered) || (!bri_teignorered))
+ dahdi_alarm_notify(s->span);
+
+ if (DBG_ALARM) {
+ dev_info(&b4->wc->vb.pdev->dev, "span %d: alarm " \
+ "%d debounced\n", i + 1,
+ s->newalarm);
+ }
+ set_bit(WCTDM_CHECK_TIMING, &b4->wc->checkflag);
+ }
+ }
+
+ if (test_and_clear_bit(WCTDM_CHECK_TIMING, &b4->wc->checkflag))
+ xhfc_set_sync_src(b4, xhfc_find_sync(b4));
+}
+
+/* this is the driver-level state machine for an S/T port */
+static void hfc_handle_state(struct b400m_span *s)
+{
+ struct b400m *b4;
+ unsigned char state, sta;
+ int nt, newsync, oldalarm;
+ unsigned long oldtimer;
+
+ b4 = s->parent;
+ nt = !s->te_mode;
+
+ state = b400m_getreg_ra(b4, R_SU_SEL, s->port, A_SU_RD_STA);
+ sta = (state & V_SU_STA_MASK);
+
+ if (DBG_ST && ((1 << s->port) & bri_spanfilter)) {
+ char *x;
+
+ x = hfc_decode_st_state(b4, s, state, 1);
+ b4_info(b4, "port %d A_SU_RD_STA old=0x%02x "
+ "now=0x%02x, decoded: %s\n", s->port + 1,
+ s->oldstate, state, x);
+ kfree(x);
+ }
+
+ oldalarm = s->newalarm;
+ oldtimer = s->alarmtimer;
+
+ if (nt) {
+ switch (sta) {
+ default: /* Invalid NT state */
+ case 0x0: /* NT state G0: Reset */
+ case 0x1: /* NT state G1: Deactivated */
+ case 0x4: /* NT state G4: Pending Deactivation */
+ s->newalarm = DAHDI_ALARM_RED;
+ break;
+ case 0x2: /* NT state G2: Pending Activation */
+ s->newalarm = DAHDI_ALARM_YELLOW;
+ break;
+ case 0x3: /* NT state G3: Active */
+ s->hfc_timer_on[XHFC_T1] = 0;
+ s->newalarm = 0;
+ break;
+ }
+ } else {
+ switch (sta) {
+ default: /* Invalid TE state */
+ case 0x0: /* TE state F0: Reset */
+ case 0x2: /* TE state F2: Sensing */
+ case 0x3: /* TE state F3: Deactivated */
+ case 0x4: /* TE state F4: Awaiting Signal */
+ case 0x8: /* TE state F8: Lost Framing */
+ s->newalarm = DAHDI_ALARM_RED;
+ break;
+ case 0x5: /* TE state F5: Identifying Input */
+ case 0x6: /* TE state F6: Synchronized */
+ s->newalarm = DAHDI_ALARM_YELLOW;
+ break;
+ case 0x7: /* TE state F7: Activated */
+ s->hfc_timer_on[XHFC_T3] = 0;
+ s->hfc_timer_on[XHFC_T4] = 0;
+ s->newalarm = 0;
+ break;
+ }
+ }
+
+ s->alarmtimer = b4->ticks + bri_alarmdebounce;
+ s->oldstate = state;
+
+ if (DBG_ALARM) {
+ b4_info(b4, "span %d: old alarm %d expires %ld, "
+ "new alarm %d expires %ld\n", s->port + 1, oldalarm,
+ oldtimer, s->newalarm, s->alarmtimer);
+ }
+
+ /* we only care about T2 expiry in G4. */
+ if (nt && (sta == 4) && (state & V_SU_T2_EXP)) {
+ if (s->hfc_timer_on[XHFC_T2])
+ hfc_timer_expire(s, XHFC_T2); /* handle T2 expiry */
+ }
+
+ /* If we're in F3 and receiving INFO0, start T3 and jump to F4 */
+ if (!nt && (sta == 3) && (state & V_SU_INFO0)) {
+ if (bri_persistentlayer1) {
+ s->hfc_timers[XHFC_T3] = b4->ticks + TIMER_3_MS;
+ s->hfc_timer_on[XHFC_T3] = 1;
+ if (DBG_ST) {
+ b4_info(b4, "port %d: receiving "
+ "INFO0 in state 3, setting T3 and "
+ "jumping to F4\n", s->port + 1);
+ }
+ hfc_start_st(s);
+ }
+ }
+
+ /* read in R_BERT_STA to determine where our current sync source is */
+ newsync = b400m_getreg(b4, R_BERT_STA) & 0x07;
+ if (newsync != b4->reportedsyncspan) {
+ if (DBG_TIMING) {
+ if (newsync == 5) {
+ b4_info(b4, "new card sync source: SYNC_I\n");
+ } else {
+ b4_info(b4, "Card position %d: new "
+ "sync source: port %d\n",
+ b4->position, newsync);
+ }
+ }
+
+ b4->reportedsyncspan = newsync;
+ }
+}
+
+static void hfc_stop_all_timers(struct b400m_span *s)
+{
+ s->hfc_timer_on[XHFC_T4] = 0;
+ s->hfc_timer_on[XHFC_T3] = 0;
+ s->hfc_timer_on[XHFC_T2] = 0;
+ s->hfc_timer_on[XHFC_T1] = 0;
+}
+
+static void hfc_stop_st(struct b400m_span *s)
+{
+ struct b400m *b4 = s->parent;
+
+ hfc_stop_all_timers(s);
+
+ b400m_setreg_ra(b4, R_SU_SEL, s->port, A_SU_WR_STA, V_SU_ACT_DEACTIVATE);
+}
+
+/*
+ * resets an S/T interface to a given NT/TE mode
+ */
+static void hfc_reset_st(struct b400m_span *s)
+{
+ int b;
+ struct b400m *b4;
+
+ b4 = s->parent;
+
+ hfc_stop_st(s);
+
+ /* force state G0/F0 (reset), then force state 1/2
+ * (deactivated/sensing) */
+ b400m_setreg_ra(b4, R_SU_SEL, s->port, A_SU_WR_STA, V_SU_LD_STA);
+ flush_hw(); /* make sure write hit hardware */
+
+ s->span->alarms = DAHDI_ALARM_RED;
+ s->newalarm = DAHDI_ALARM_RED;
+ dahdi_alarm_notify(s->span);
+
+ /* set up the clock control register. Must be done before we activate
+ * the interface. */
+ if (s->te_mode)
+ b = 0x0e;
+ else
+ b = 0x0c | (6 << V_SU_SMPL_SHIFT);
+
+ b400m_setreg(b4, A_SU_CLK_DLY, b);
+
+ /* set TE/NT mode, enable B and D channels. */
+ b400m_setreg(b4, A_SU_CTRL0, V_B1_TX_EN | V_B2_TX_EN |
+ (s->te_mode ? 0 : V_SU_MD) | V_ST_PU_CTRL);
+ b400m_setreg(b4, A_SU_CTRL1, V_G2_G3_EN);
+ b400m_setreg(b4, A_SU_CTRL2, V_B1_RX_EN | V_B2_RX_EN);
+ b400m_setreg(b4, A_ST_CTRL3, (0x7c << 1));
+
+ /* enable the state machine. */
+ b400m_setreg(b4, A_SU_WR_STA, 0x00);
+ flush_hw();
+}
+
+static void hfc_start_st(struct b400m_span *s)
+{
+ struct b400m *b4 = s->parent;
+
+ b400m_setreg_ra(b4, R_SU_SEL, s->port, A_SU_WR_STA, V_SU_ACT_ACTIVATE);
+
+ /* start T1 if in NT mode, T3 if in TE mode */
+ if (s->te_mode) {
+ /* 500ms wait first time, TIMER_3_MS afterward. */
+ s->hfc_timers[XHFC_T3] = b4->ticks + TIMER_3_MS;
+ s->hfc_timer_on[XHFC_T3] = 1;
+ s->hfc_timer_on[XHFC_T1] = 0;
+
+ s->hfc_timers[XHFC_T4] = b4->ticks + 1000;
+ s->hfc_timer_on[XHFC_T4] = 1;
+
+ if (DBG_ST) {
+ b4_info(b4, "setting port %d t3 timer to %lu\n",
+ s->port + 1, s->hfc_timers[XHFC_T3]);
+ }
+ } else {
+ static const int TIMER_1_MS = 2000;
+ s->hfc_timers[XHFC_T1] = b4->ticks + TIMER_1_MS;
+ s->hfc_timer_on[XHFC_T1] = 1;
+ s->hfc_timer_on[XHFC_T3] = 0;
+ if (DBG_ST) {
+ b4_info(b4, "setting port %d t1 timer to %lu\n",
+ s->port + 1, s->hfc_timers[XHFC_T1]);
+ }
+ }
+}
+
+/*
+ * read in the HFC GPIO to determine each port's mode (TE or NT).
+ * Then, reset and start the port.
+ * the flow controller should be set up before this is called.
+ */
+static int hdlc_start(struct b400m *b4, int fifo);
+static void hfc_init_all_st(struct b400m *b4)
+{
+ int i, gpio;
+ struct b400m_span *s;
+
+ gpio = 0;
+
+ for (i = 0; i < 4; i++) {
+ s = &b4->spans[i];
+ s->parent = b4;
+
+#ifdef SWAP_PORTS
+ s->port = (1 == i) ? 2 : (2 == i) ? 1 : i;
+#else
+ s->port = i;
+#endif
+ s->te_mode = 1;
+
+ hdlc_start(b4, s->fifos[2]);
+ }
+
+}
+
+/* NOTE: assumes fifo lock is held */
+#define debug_fz(b4, fifo, prefix, buf) \
+do { \
+ sprintf(buf, "%s: (fifo %d): f1/f2/flen=%d/%d/%d, " \
+ "z1/z2/zlen=%d/%d/%d\n", prefix, fifo, f1, f2, flen, z1, \
+ z2, zlen); \
+} while (0)
+
+/* enable FIFO RX int and reset the FIFO */
+static int hdlc_start(struct b400m *b4, int fifo)
+{
+ b4->fifo_en_txint |= (1 << fifo);
+ b4->fifo_en_rxint |= (1 << fifo);
+
+ hfc_reset_fifo_pair(b4, fifo, 1, 0);
+ return 0;
+}
+
+#ifdef HARDHDLC_RX
+
+/**
+ * hdlc_signal_complete() - Signal dahdi that we have a complete frame.
+ *
+ * @bpan: The span which received the frame.
+ * @stat: The frame status from the XHFC controller.
+ *
+ */
+static void hdlc_signal_complete(struct b400m_span *bspan, u8 stat)
+{
+ struct b400m *b4 = bspan->parent;
+
+ /* if STAT != 0, indicates bad frame */
+ if (stat != 0x00) {
+ if (DBG_HDLC && DBG_SPANFILTER) {
+ b4_info(b4, "(span %d) STAT=0x%02x indicates " \
+ "frame problem: %s\n", bspan->port + 1, stat,
+ (0xff == stat) ? "HDLC Abort" : "Bad FCS");
+ }
+
+ dahdi_hdlc_abort(bspan->sigchan, (0xff == stat) ?
+ DAHDI_EVENT_ABORT : DAHDI_EVENT_BADFCS);
+ /* STAT == 0, means frame was OK */
+ } else {
+ if (DBG_HDLC && DBG_SPANFILTER) {
+ b4_info(b4, "(span %d) Frame %d is good!\n",
+ bspan->port + 1, bspan->frames_in);
+ }
+ dahdi_hdlc_finish(bspan->sigchan);
+ }
+}
+
+/*
+ * Inner loop for D-channel receive function. Retrieves HDLC data from the
+ * hardware. If the hardware indicates that the frame is complete, we check
+ * the HDLC engine's STAT byte and update DAHDI as needed.
+ *
+ * Returns the number of HDLC frames left in the FIFO, or -1 if we couldn't
+ * get the lock.
+ */
+static int hdlc_rx_frame(struct b400m_span *bspan)
+{
+ int fifo, i, j, x, zleft;
+ int z1, z2, zlen, f1, f2, flen, new_flen;
+ unsigned char buf[B400M_HDLC_BUF_LEN];
+ char debugbuf[256];
+ struct b400m *b4 = bspan->parent;
+
+ fifo = bspan->fifos[2];
+
+ if (DBG_HDLC && DBG_SPANFILTER)
+ b4_info(b4, "hdlc_rx_frame fifo %d: start\n", fifo);
+
+ if (down_trylock(&b4->fifosem) && DBG_HDLC && DBG_SPANFILTER) {
+ b4_info(b4, "rx_frame: fifo %d 1: couldn't get lock\n",
+ fifo);
+ return -1;
+ }
+
+ hfc_setreg_waitbusy(b4, R_FIFO,
+ (fifo << V_FIFO_NUM_SHIFT) | V_FIFO_DIR);
+ get_F(f1, f2, flen);
+ get_Z(z1, z2, zlen);
+ debug_fz(b4, fifo, "hdlc_rx_frame", debugbuf);
+ up(&b4->fifosem);
+
+ if (DBG_HDLC && DBG_SPANFILTER)
+ pr_info("%s", debugbuf);
+
+ /* if we have at least one complete frame, increment zleft to include
+ * status byte */
+ zleft = zlen;
+ if (flen)
+ zleft++;
+
+ do {
+ if (zleft > B400M_HDLC_BUF_LEN)
+ j = B400M_HDLC_BUF_LEN;
+ else
+ j = zleft;
+
+ if (down_trylock(&b4->fifosem) && DBG_HDLC && DBG_SPANFILTER) {
+ b4_info(b4,
+ "rx_frame fifo %d 2: couldn't get lock\n",
+ fifo);
+ return -1;
+ }
+ hfc_setreg_waitbusy(b4, R_FIFO,
+ (fifo << V_FIFO_NUM_SHIFT) | V_FIFO_DIR);
+ for (i = 0; i < j; i++)
+ buf[i] = b400m_getreg(b4, A_FIFO_DATA);
+ up(&b4->fifosem);
+
+ /* don't send STAT byte to DAHDI */
+ x = j;
+ if (bspan->sigchan) {
+ if ((j != B400M_HDLC_BUF_LEN) && flen)
+ x--;
+ if (x)
+ dahdi_hdlc_putbuf(bspan->sigchan, buf, x);
+ }
+
+ zleft -= j;
+
+ if (DBG_HDLC && DBG_SPANFILTER) {
+ b4_info(b4, "transmitted %d bytes to dahdi, " \
+ "zleft=%d\n", x, zleft);
+ }
+
+ if (DBG_HDLC && DBG_SPANFILTER) {
+ /* !!! */
+ b4_info(b4, "hdlc_rx_frame(span %d): " \
+ "z1/z2/zlen=%d/%d/%d, zleft=%d\n",
+ bspan->port + 1, z1, z2, zlen, zleft);
+ for (i = 0; i < j; i++) {
+ b4_info(b4, "%02x%c", buf[i],
+ (i < (j - 1)) ? ' ' : '\n');
+ }
+ }
+ } while (zleft > 0);
+
+ /* Frame received, increment F2 and get an updated count of frames
+ * left */
+ if (down_trylock(&b4->fifosem) && DBG_HDLC && DBG_SPANFILTER) {
+ b4_info(b4, "rx_frame fifo %d 3: couldn't get lock\n",
+ fifo);
+ return 0;
+ }
+
+ /* go get the F count again, just in case another frame snuck in while
+ * we weren't looking. */
+ if (flen) {
+ hfc_setreg_waitbusy(b4, A_INC_RES_FIFO, V_INC_F);
+ ++bspan->frames_in;
+ get_F(f1, f2, new_flen);
+ } else
+ new_flen = flen;
+
+ up(&b4->fifosem);
+
+ /* If this channel is not configured with a signalling span we don't
+ * need to notify the rest of dahdi about this frame. */
+ if (!bspan->sigchan) {
+ if (DBG_HDLC && DBG_SPANFILTER) {
+ b4_info(b4, "hdlc_rx_frame fifo %d: " \
+ "new_flen %d, early end.\n", fifo, new_flen);
+ }
+ return new_flen;
+ }
+
+ if (flen) {
+ /* disable < 3 check for now */
+ if (0 && zlen < 3) {
+ if (DBG_HDLC && DBG_SPANFILTER)
+ b4_info(b4, "odd, zlen less then 3?\n");
+ dahdi_hdlc_abort(bspan->sigchan, DAHDI_EVENT_ABORT);
+ } else {
+ hdlc_signal_complete(bspan, buf[i - 1]);
+ }
+ }
+
+ if (DBG_HDLC && DBG_SPANFILTER) {
+ b4_info(b4, "hdlc_rx_frame fifo %d: new_flen=%d end.\n",
+ fifo, new_flen);
+ }
+
+ return new_flen;
+}
+
+#endif /* HARDHDLC_RX */
+
+
+/*
+ * Takes one blob of data from DAHDI and shoots it out to the hardware. The
+ * blob may or may not be a complete HDLC frame. If it isn't, the D-channel
+ * FIFO interrupt handler will take care of pulling the rest. Returns nonzero
+ * if there is still data to send in the current HDLC frame.
+ */
+static int hdlc_tx_frame(struct b400m_span *bspan)
+{
+ struct b400m *b4 = bspan->parent;
+ int res, i, fifo;
+ int z1, z2, zlen;
+ int f1 = -1, f2 = -1, flen = -1;
+ unsigned char buf[B400M_HDLC_BUF_LEN];
+ unsigned int size = ARRAY_SIZE(buf);
+ char debugbuf[256];
+
+ /* if we're ignoring TE red alarms and we are in alarm, restart the
+ * S/T state machine */
+ if (bspan->te_mode && (bspan->newalarm != 0)) {
+ hfc_start_st(bspan);
+ }
+
+ fifo = bspan->fifos[2];
+ res = dahdi_hdlc_getbuf(bspan->sigchan, buf, &size);
+
+ if (down_interruptible(&b4->fifosem)) {
+ static int arg;
+ b4_info(b4, "b400m: arg (%d), grabbed data from DAHDI " \
+ "but couldn't grab the lock!\n", ++arg);
+ /* TODO: Inform DAHDI that we have grabbed data and can't use
+ * it */
+ dahdi_hdlc_abort(bspan->sigchan, DAHDI_EVENT_OVERRUN);
+ return 1; /* return 1 so we keep trying */
+ }
+ hfc_setreg_waitbusy(b4, R_FIFO, (fifo << V_FIFO_NUM_SHIFT));
+
+ get_Z(z1, z2, zlen);
+ debug_fz(b4, fifo, __func__, debugbuf);
+
+ /* TODO: check zlen, etc. */
+ if ((HFC_ZMAX-zlen) < size) {
+ static int arg;
+ b4_info(b4, "b400m: arg (%d), zlen (%d) < what we " \
+ "grabbed from DAHDI (%d)!\n", ++arg, zlen, size);
+ size = zlen;
+ dahdi_hdlc_abort(bspan->sigchan, DAHDI_EVENT_OVERRUN);
+ }
+
+ if (size > 0) {
+ bspan->sigactive = 1;
+
+ for (i = 0; i < size; i++)
+ b400m_setreg(b4, A_FIFO_DATA, buf[i]);
+ /*
+ * If we got a full frame from DAHDI, increment F and
+ * decrement our HDLC pending counter. Otherwise, select the
+ * FIFO again (to start transmission) and make sure the TX IRQ
+ * is enabled so we will get called again to finish off the
+ * data
+ */
+ if (res != 0) {
+ ++bspan->frames_out;
+ bspan->sigactive = 0;
+ hfc_setreg_waitbusy(b4, A_INC_RES_FIFO, V_INC_F);
+ atomic_dec(&bspan->hdlc_pending);
+ } else {
+ hfc_setreg_waitbusy(b4, R_FIFO,
+ (fifo << V_FIFO_NUM_SHIFT));
+ }
+ }
+
+ up(&b4->fifosem);
+
+ if (0 && DBG_HDLC && DBG_SPANFILTER) {
+ b4_info(b4, "%s", debugbuf);
+
+ b4_info(b4, "hdlc_tx_frame(span %d): DAHDI gave %d " \
+ "bytes for FIFO %d (res = %d)\n",
+ bspan->port + 1, size, fifo, res);
+
+ for (i = 0; i < size; i++)
+ b4_info(b4,
+ "%02x%c\n", buf[i],
+ (i < (size - 1)) ? ' ' : '\n');
+
+ if (size && res != 0) {
+ pr_info("Transmitted frame %d on span %d\n",
+ bspan->frames_out - 1, bspan->port);
+ }
+ }
+
+ return (res == 0);
+}
+
+/*
+ * b400m lowlevel functions These are functions which impact more than just
+ * the HFC controller. (those are named hfc_xxx())
+ */
+
+/*
+ * Performs a total reset of the card, reinitializes GPIO. The card is
+ * initialized enough to have LEDs running, and that's about it. Anything to
+ * do with audio and enabling any kind of processing is done in stage2.
+ */
+static void xhfc_init_stage1(struct b400m *b4)
+{
+ int i;
+
+ hfc_reset(b4);
+ hfc_gpio_init(b4);
+
+ /* make sure interrupts are disabled */
+ b400m_setreg(b4, R_IRQ_CTRL, 0x00);
+
+ /* make sure write hits hardware */
+ flush_hw();
+
+ /* disable all FIFO interrupts */
+ for (i = 0; i < HFC_NR_FIFOS; i++) {
+ hfc_setreg_waitbusy(b4, R_FIFO, (i << V_FIFO_NUM_SHIFT));
+ /* disable the interrupt */
+ b400m_setreg(b4, A_FIFO_CTRL, 0x00);
+ hfc_setreg_waitbusy(b4, R_FIFO,
+ (i << V_FIFO_NUM_SHIFT) | V_FIFO_DIR);
+ /* disable the interrupt */
+ b400m_setreg(b4, A_FIFO_CTRL, 0x00);
+ flush_hw();
+ }
+
+ /* set fill threshhold to 16 bytes */
+ b400m_setreg(b4, R_FIFO_THRES, 0x11);
+
+ /* clear any pending FIFO interrupts */
+ b400m_getreg(b4, R_FIFO_BL2_IRQ);
+ b400m_getreg(b4, R_FIFO_BL3_IRQ);
+
+ b4->misc_irq_mask = 0x00;
+ b400m_setreg(b4, R_MISC_IRQMSK, b4->misc_irq_mask);
+ b400m_setreg(b4, R_IRQ_CTRL, 0);
+}
+
+/*
+ * Stage 2 hardware init. Sets up the flow controller, PCM and FIFOs.
+ * Initializes the echo cancellers. S/T interfaces are not initialized here,
+ * that is done later, in hfc_init_all_st(). Interrupts are enabled and once
+ * the s/t interfaces are configured, chip should be pretty much operational.
+ */
+static void xhfc_init_stage2(struct b400m *b4)
+{
+ /*
+ * set up PCM bus. XHFC is PCM slave C2IO is the clock, auto sync,
+ * SYNC_O follows SYNC_I. 128 timeslots, long frame sync positive
+ * polarity, sample on falling clock edge. STIO2 is transmit-only,
+ * STIO1 is receive-only.
+ */
+ b400m_setreg(b4, R_PCM_MD0, V_PCM_IDX_MD1);
+ b400m_setreg(b4, R_PCM_MD1, V_PCM_DR_8192 | (0x3 << 2));
+ b400m_setreg(b4, R_PCM_MD0, V_PCM_IDX_MD2);
+ b400m_setreg(b4, R_PCM_MD2, V_C2I_EN | V_SYNC_OUT1);
+ b400m_setreg(b4, R_SU_SYNC, V_SYNC_SEL_PORT0);
+
+ /* Now set up the flow controller. */
+ hfc_setup_fsm(b4);
+
+ /*
+ * At this point, everything's set up and ready to go. Don't actually
+ * enable the global interrupt pin. DAHDI still needs to start up the
+ * spans, and we don't know exactly when.
+ */
+}
+
+static int xhfc_startup(struct dahdi_span *span)
+{
+ struct b400m_span *bspan = span->pvt;
+ struct b400m *b4 = bspan->parent;
+ if (!b4->running)
+ hfc_enable_interrupts(bspan->parent);
+
+ return 0;
+}
+
+/* resets all the FIFOs for a given span. Disables IRQs for the span FIFOs */
+static void xhfc_reset_span(struct b400m_span *bspan)
+{
+ int i;
+ struct b400m *b4 = bspan->parent;
+
+ /* b4_info(b4, "xhfc_reset_span()\n"); */
+ for (i = 0; i < 3; i++)
+ hfc_reset_fifo_pair(b4, bspan->fifos[i], (i == 2) ? 1 : 0, 1);
+}
+
+static void b400m_enable_workqueues(struct wctdm *wc)
+{
+ struct b400m *b4s[2];
+ int i, numb4s = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wc->reglock, flags);
+ for (i = 0; i < wc->mods_per_board; i += 4) {
+ if (wc->modtype[i] == MOD_TYPE_BRI)
+ b4s[numb4s++] = wc->mods[i].bri;
+ }
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+ for (i = 0; i < numb4s; i++) {
+ if (b4s[i])
+ b4s[i]->shutdown = 0;
+ }
+
+}
+
+static void b400m_disable_workqueues(struct wctdm *wc)
+{
+ struct b400m *b4s[2];
+ int i, numb4s = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wc->reglock, flags);
+ for (i = 0; i < wc->mods_per_board; i += 4) {
+ if (wc->modtype[i] == MOD_TYPE_BRI)
+ b4s[numb4s++] = wc->mods[i].bri;
+ }
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+ for (i = 0; i < numb4s; i++) {
+ if (b4s[i]) {
+ down(&wc->syncsem);
+ b4s[i]->shutdown = 1;
+ up(&wc->syncsem);
+ flush_workqueue(b4s[i]->xhfc_ws);
+ }
+ }
+}
+/*
+ * Software selectable NT and TE mode settings on the B400M.
+ *
+ * mode - bitwise selection of NT vs TE mode
+ * 1 = NT; 0 = TE;
+ * bit 0 is port 0
+ * bit 1 is port 1
+ * ...
+ * term - termination resistance
+ * 0 = no termination resistance
+ * 1 = 390 ohm termination resistance switched on
+ */
+static int b400m_set_ntte(struct b400m_span *bspan, int te_mode, int term_on)
+{
+ struct b400m *b4 = bspan->parent;
+ unsigned char data;
+ unsigned char addr;
+ int all_modes = 0, all_terms = 0;
+ int i;
+
+ bspan->te_mode = te_mode;
+ bspan->term_on = term_on;
+
+ for (i = 0; i < 4; i++) {
+ if (!b4->spans[i].te_mode)
+ all_modes |= (1 << i);
+ if (b4->spans[i].term_on)
+ all_terms |= (1 << i);
+ }
+
+ data = 0x10 | ((all_terms << 4) & 0xc0) | ((all_terms << 2) & 0x0c);
+ addr = 0x10 | all_modes;
+
+ msleep(voicebus_current_latency(&b4->wc->vb) + 2);
+ wctdm_setreg(b4->wc, b4->position, addr, data);
+
+ b4->lastreg = 0xff;
+ msleep(voicebus_current_latency(&b4->wc->vb) + 2);
+
+ hfc_reset_st(bspan);
+
+ if (bri_persistentlayer1)
+ hfc_start_st(bspan);
+
+ return 0;
+}
+
+/* spanconfig for us means ...? */
+int b400m_spanconfig(struct dahdi_span *span, struct dahdi_lineconfig *lc)
+{
+ struct b400m_span *bspan;
+ struct b400m *b4;
+ struct wctdm *wc;
+ int te_mode, term;
+ int pos;
+
+ bspan = span->pvt;
+ b4 = bspan->parent;
+ wc = b4->wc;
+
+ b400m_disable_workqueues(b4->wc);
+
+ te_mode = (lc->lineconfig & DAHDI_CONFIG_NTTE) ? 0 : 1;
+
+ term = (lc->lineconfig & DAHDI_CONFIG_TERM) ? 1 : 0;
+
+ b4_info(b4, "xhfc: Configuring port %d span %d in %s " \
+ "mode with termination resistance %s\n", bspan->port,
+ span->spanno, (te_mode) ? "TE" : "NT",
+ (term) ? "ENABLED" : "DISABLED");
+
+ b400m_set_ntte(bspan, te_mode, term);
+ if (lc->sync < 0) {
+ b4_info(b4, "Span %d has invalid sync priority (%d), " \
+ "removing from sync source list\n", span->spanno,
+ lc->sync);
+ lc->sync = 0;
+ }
+
+ if (span->offset >= 4) {
+ pos = span->offset;
+ } else {
+ /* This is tricky. Have to figure out if we're slot 1 or slot
+ * 2 */
+ pos = span->offset + b4->position;
+ }
+
+ if (!te_mode && lc->sync) {
+ b4_info(b4, "NT Spans cannot be timing sources. " \
+ "Span %d requested to be timing source of " \
+ "priority %d. Changing priority to 0\n", pos,
+ lc->sync);
+ lc->sync = 0;
+ }
+
+ wc->spans[pos]->timing_priority = lc->sync;
+
+ bspan->span = span;
+ xhfc_reset_span(bspan);
+
+ /* call startup() manually here, because DAHDI won't call the startup
+ * function unless it receives an IOCTL to do so, and dahdi_cfg
+ * doesn't. */
+ xhfc_startup(span);
+
+ span->flags |= DAHDI_FLAG_RUNNING;
+
+ set_bit(WCTDM_CHECK_TIMING, &wc->checkflag);
+
+ b400m_enable_workqueues(b4->wc);
+
+ return 0;
+}
+
+/* chanconfig for us means to configure the HDLC controller, if appropriate
+ *
+ * NOTE: apparently the DAHDI ioctl function calls us with a interrupts
+ * disabled. This means we cannot actually touch the hardware, because all
+ * register accesses are wrapped up in a mutex that can sleep.
+ *
+ * The solution to that is to simply increment the span's "restart" flag, and
+ * the driver's workqueue will do the dirty work on our behalf.
+ */
+int b400m_chanconfig(struct dahdi_chan *chan, int sigtype)
+{
+ int alreadyrunning;
+ struct b400m_span *bspan = chan->span->pvt;
+ struct b400m *b4 = bspan->parent;
+ int res;
+
+ alreadyrunning = bspan->span->flags & DAHDI_FLAG_RUNNING;
+
+ if (DBG_FOPS) {
+ b4_info(b4, "%s channel %d (%s) sigtype %08x\n",
+ alreadyrunning ? "Reconfigured" : "Configured",
+ chan->channo, chan->name, sigtype);
+ }
+
+ switch (sigtype) {
+ case DAHDI_SIG_HARDHDLC:
+ if (DBG_FOPS) {
+ b4_info(b4, "%sonfiguring hardware HDLC on %s\n",
+ ((sigtype == DAHDI_SIG_HARDHDLC) ? "C" :
+ "Unc"), chan->name);
+ }
+ bspan->sigchan = chan;
+ bspan->sigactive = 0;
+ atomic_set(&bspan->hdlc_pending, 0);
+ res = 0;
+ break;
+ case DAHDI_SIG_HDLCFCS:
+ case DAHDI_SIG_HDLCNET:
+ case DAHDI_SIG_HDLCRAW:
+ /* Only HARDHDLC is supported for the signalling channel on BRI
+ * spans. */
+ res = -EINVAL;
+ break;
+ default:
+ res = 0;
+ break;
+ };
+
+ return res;
+}
+
+int b400m_dchan(struct dahdi_span *span)
+{
+ struct b400m_span *bspan;
+ struct b400m *b4;
+ unsigned char *rxb;
+ int res;
+ int i;
+
+ bspan = span->pvt;
+ b4 = bspan->parent;
+#ifdef HARDHDLC_RX
+ return 0;
+#else
+#endif
+
+ if (!bspan->sigchan)
+ return 0;
+
+ rxb = bspan->sigchan->readchunk;
+
+ if (!rxb) {
+ b4_info(b4, "No RXB!\n");
+ return 0;
+ }
+
+ for (i = 0; i < DAHDI_CHUNKSIZE; i++) {
+ fasthdlc_rx_load_nocheck(&bspan->rxhdlc, *(rxb++));
+ res = fasthdlc_rx_run(&bspan->rxhdlc);
+ /* If there is nothing there, continue */
+ if (res & RETURN_EMPTY_FLAG)
+ continue;
+ else if (res & RETURN_COMPLETE_FLAG) {
+
+ if (!bspan->f_sz)
+ continue;
+
+ /* Only count this if it's a non-empty frame */
+ if (bspan->infcs != PPP_GOODFCS) {
+ dahdi_hdlc_abort(bspan->sigchan,
+ DAHDI_EVENT_BADFCS);
+ } else {
+ dahdi_hdlc_finish(bspan->sigchan);
+ }
+ bspan->infcs = PPP_INITFCS;
+ bspan->f_sz = 0;
+ continue;
+ } else if (res & RETURN_DISCARD_FLAG) {
+
+ if (!bspan->f_sz)
+ continue;
+
+ dahdi_hdlc_abort(bspan->sigchan, DAHDI_EVENT_ABORT);
+ bspan->infcs = PPP_INITFCS;
+ bspan->f_sz = 0;
+ break;
+ } else {
+ unsigned char rxc = res;
+ bspan->infcs = PPP_FCS(bspan->infcs, rxc);
+ bspan->f_sz++;
+ dahdi_hdlc_putbuf(bspan->sigchan, &rxc, 1);
+ }
+ }
+
+ return 0;
+}
+
+/* internal functions, not specific to the hardware or DAHDI */
+
+/*
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void xhfc_work(void *data)
+{
+ struct b400m *b4 = data;
+#else
+static void xhfc_work(struct work_struct *work)
+{
+ struct b400m *b4 = container_of(work, struct b400m, xhfc_wq);
+#endif
+ int i, j, k, fifo;
+ unsigned char b, b2;
+
+ if (b4->shutdown || !b4->wc->initialized)
+ return;
+
+ b4->irq_oview = b400m_getreg(b4, R_IRQ_OVIEW);
+ b4->fifo_fill = b400m_getreg(b4, R_FILL_BL0);
+
+ if (b4->irq_oview & V_FIFO_BL0_IRQ) {
+ b4->fifo_irqstatus |= b400m_getreg(b4, R_FIFO_BL0_IRQ);
+ b4->irq_oview &= ~V_FIFO_BL0_IRQ;
+ }
+
+ /* only look at BL0, we put all D channel FIFOs in the first block. */
+ b = b2 = b4->fifo_irqstatus;
+
+ for (j = 0; j < 4; j++) {
+#ifdef SWAP_PORTS
+ fifo = (1 == j) ? 2 : (2 == j) ? 1 : j;
+#else
+ fifo = j;
+#endif
+
+#ifdef HARDHDLC_RX
+ if (b & V_FIFOx_RX_IRQ) {
+ if (fifo < 4) { /* d-channel FIFO */
+
+ /*
+ * 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 still HDLC
+ * frames waiting to be received. i.e. I get
+ * an int when F1 changes, not when F1 != F2.
+ *
+ */
+ do {
+ k = hdlc_rx_frame(&b4->spans[fifo]);
+ } while (k);
+ }
+ }
+
+#endif
+ b >>= 2;
+ }
+
+ /* zero the bits we just processed */
+ b4->fifo_irqstatus &= ~b2;
+ b4->fifo_fill &= ~b2;
+
+
+#if 1
+ /* All four D channel FIFOs are in BL0. */
+ b = b2 = b4->fifo_fill;
+
+ for (j = 0; j < 4; j++) {
+#ifdef SWAP_PORTS
+ fifo = (1 == j) ? 2 : (2 == j) ? 1 : j;
+#else
+ fifo = j;
+#endif
+ if (b4->spans[fifo].sigactive && (b & V_FIFOx_TX_IRQ))
+ hdlc_tx_frame(&b4->spans[fifo]);
+
+#ifdef HARDHDLC_RX
+ if (b & V_FIFOx_RX_IRQ)
+ hdlc_rx_frame(&b4->spans[fifo]);
+#endif
+
+ b >>= 2;
+ }
+#endif
+
+ /* Check for outgoing HDLC frame requests The HFC does not generate TX
+ * interrupts when there is room to send, so I use an atomic counter
+ * that is incremented every time DAHDI wants to send a frame, and
+ * decremented every time I send a frame. It'd be better if I could
+ * just use the interrupt handler, but the HFC seems to trigger a FIFO
+ * TX IRQ only when it has finished sending a frame, not when one can
+ * be sent.
+ */
+ for (i = 0; i < ARRAY_SIZE(b4->spans); i++) {
+ struct b400m_span *bspan = &b4->spans[i];
+
+ if (atomic_read(&bspan->hdlc_pending)) {
+ do {
+ k = hdlc_tx_frame(bspan);
+ } while (k);
+ }
+ }
+
+ b = b400m_getreg(b4, R_SU_IRQ);
+
+ if (b) {
+ for (i = 0; i < ARRAY_SIZE(b4->spans); i++) {
+ int physport;
+
+#ifdef SWAP_PORTS
+ if (i == 1)
+ physport = 2;
+ else if (i == 2)
+ physport = 1;
+ else
+ physport = i;
+#else
+ physport = i;
+#endif
+ if (b & (1 << i))
+ hfc_handle_state(&b4->spans[physport]);
+ }
+ }
+
+ hfc_update_st_timers(b4);
+}
+
+int wctdm_bri_checkisr(struct wctdm *wc, int modpos, int offset)
+{
+ struct b400m *b4;
+ int ret = 0;
+
+ /* don't do anything for non-base card slots */
+ if (modpos & 0x03)
+ return 0;
+
+ /* DEFINITELY don't do anything if our structures aren't ready! */
+ if (!wc || !wc->initialized || !(wc->mods[modpos].bri) ||
+ !((struct b400m *)wc->mods[modpos].bri)->inited) {
+ return 0;
+ }
+
+ b4 = (struct b400m *)wc->mods[modpos].bri;
+ if (offset == 0) {
+ if (!b4->shutdown) {
+ /* if (!(wc->intcount % 50)) */
+ queue_work(b4->xhfc_ws, &b4->xhfc_wq);
+ }
+ b4->ticks++;
+ }
+
+ return ret;
+}
+
+/* DAHDI calls this when it has data it wants to send to the HDLC controller */
+void wctdm_hdlc_hard_xmit(struct dahdi_chan *chan)
+{
+ struct wctdm *wc;
+ struct b400m *b4;
+ struct b400m_span *bspan;
+ struct dahdi_span *dspan;
+ int span;
+
+ dspan = chan->span;
+ bspan = dspan->pvt;
+ b4 = bspan->parent;
+ wc = b4->wc;
+ span = bspan->port;
+
+ if ((DBG_FOPS || DBG_HDLC) && DBG_SPANFILTER) {
+ b4_info(b4, "hdlc_hard_xmit on chan %s (%i/%i), " \
+ "span=%i (sigchan=%p, chan=%p)\n", chan->name,
+ chan->channo, chan->chanpos, span + 1,
+ bspan->sigchan, chan);
+ }
+
+ /* Increment the hdlc_pending counter and trigger the bottom-half so
+ * it will be picked up and sent. */
+ if (bspan->sigchan == chan)
+ atomic_inc(&bspan->hdlc_pending);
+}
+
+static int b400m_probe(struct wctdm *wc, int modpos)
+{
+ unsigned char id, x;
+ struct b400m *b4;
+ unsigned long flags;
+ int chiprev;
+
+ wctdm_setreg(wc, modpos, 0x10, 0x10);
+ id = xhfc_getreg(wc, modpos, R_CHIP_ID, &x);
+
+ /* chip ID high 7 bits must be 0x62, see datasheet */
+ if ((id & 0xfe) != 0x62)
+ return -2;
+
+ b4 = kzalloc(sizeof(struct b400m), GFP_KERNEL);
+ if (!b4) {
+ dev_err(&wc->vb.pdev->dev,
+ "Couldn't allocate memory for b400m structure!\n");
+ return -ENOMEM;
+ }
+
+ /* card found, enabled and main struct allocated. Fill it out. */
+ b4->wc = wc;
+ b4->position = modpos;
+
+ /* which B400M in the system is this one? count all of them found so
+ * far */
+ for (x = 0; x < modpos; x += 4) {
+ if (wc->modtype[x] == MOD_TYPE_BRI)
+ ++b4->b400m_no;
+ }
+
+ spin_lock_init(&b4->reglock);
+ init_MUTEX(&b4->regsem);
+ init_MUTEX(&b4->fifosem);
+
+ for (x = 0; x < 4; x++) {
+ fasthdlc_init(&b4->spans[x].rxhdlc, FASTHDLC_MODE_16);
+ b4->spans[x].infcs = PPP_INITFCS;
+ }
+
+ b4->lastreg = 0xff; /* a register we won't hit right off the bat */
+
+ chiprev = b400m_getreg(b4, R_CHIP_RV);
+
+ b4->setsyncspan = -1; /* sync span is unknown */
+ b4->reportedsyncspan = -1; /* sync span is unknown */
+
+ if (DBG) {
+ b4_info(b4, "Identified controller rev %d in module %d.\n",
+ chiprev, b4->position);
+ }
+
+ xhfc_init_stage1(b4);
+
+ xhfc_init_stage2(b4);
+ hfc_init_all_st(b4);
+
+ hfc_enable_interrupts(b4);
+
+ spin_lock_irqsave(&wc->reglock, flags);
+ wc->mods[modpos].bri = (void *)b4;
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+ return 0;
+}
+
+void b400m_post_init(struct b400m *b4)
+{
+ snprintf(b4->name, sizeof(b4->name) - 1, "b400m-%d",
+ b4->b400m_no);
+ b4->xhfc_ws = create_singlethread_workqueue(b4->name);
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+ INIT_WORK(&b4->xhfc_wq, xhfc_work, b4);
+# else
+ INIT_WORK(&b4->xhfc_wq, xhfc_work);
+# endif
+ b4->inited = 1;
+}
+
+/* functions called from the wctdm code */
+int wctdm_init_b400m(struct wctdm *wc, int card)
+{
+ int i, ret = 0;
+ unsigned long flags;
+
+ if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV)
+ return -2;
+
+ if (!(card & 0x03)) { /* only init if at lowest port in module */
+ spin_lock_irqsave(&wc->reglock, flags);
+ wc->modtype[card + 0] = MOD_TYPE_BRI;
+ wc->modtype[card + 1] = MOD_TYPE_BRI;
+ wc->modtype[card + 2] = MOD_TYPE_BRI;
+ wc->modtype[card + 3] = MOD_TYPE_BRI;
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+ for (i = 0; i < 10; i++)
+ schluffen(&wc->regq);
+
+ if (b400m_probe(wc, card) != 0) {
+ spin_lock_irqsave(&wc->reglock, flags);
+ wc->modtype[card + 0] = MOD_TYPE_NONE;
+ wc->modtype[card + 1] = MOD_TYPE_NONE;
+ wc->modtype[card + 2] = MOD_TYPE_NONE;
+ wc->modtype[card + 3] = MOD_TYPE_NONE;
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ ret = -2;
+ }
+ } else { /* for the "sub-cards" */
+ if (wc->modtype[card & 0xfc] == MOD_TYPE_BRI) {
+ spin_lock_irqsave(&wc->reglock, flags);
+ wc->modtype[card] = MOD_TYPE_BRI;
+ wc->mods[card].bri = wc->mods[card & 0xfc].bri;
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ } else {
+ ret = -2;
+ }
+ }
+
+ return ret;
+}
+
+void wctdm_unload_b400m(struct wctdm *wc, int card)
+{
+ struct b400m *b4 = wc->mods[card].bri;
+ int i;
+
+ /* TODO: shutdown once won't work if just a single card is hotswapped
+ * out. But since most of the time this is called because the entire
+ * driver is in the process of unloading, I'll leave it here. */
+ static int shutdown_once;
+
+
+ /* only really unload with the 'base' card number. base+1/2/3 aren't
+ * real. */
+ if (card & 0x03)
+ return;
+
+ if (timingcable && !shutdown_once) {
+ b4_info(b4, "Disabling all workqueues for B400Ms\n");
+ /* Gotta shut down timing change potential during this */
+ for (i = 0; i < WC_MAX_IFACES; i++) {
+ if (ifaces[i])
+ b400m_disable_workqueues(ifaces[i]);
+ }
+ b4_info(b4, "Forcing sync to card 0\n");
+ /* Put the timing configuration in a known state: card 0 is
+ * master */
+ wctdm_change_system_sync_src(synccard, syncspan, -1, -1);
+ /* Change all other cards in the system to self time before
+ * card 0 is removed */
+ b4_info(b4, "Setting all cards to return to self sync\n");
+ for (i = 1; i < WC_MAX_IFACES; i++) {
+ if (ifaces[i])
+ wctdm_change_card_sync_src(ifaces[i], 0, 0);
+ }
+
+ b4_info(b4,
+ "Finished preparing timing linked cards for "
+ "shutdown\n");
+
+ shutdown_once = 1;
+ }
+
+ if (b4) {
+ b4->inited = 0;
+
+ wait_just_a_bit(HZ/10);
+
+ /* TODO: wait for tdm24xx driver to unregister the spans */
+ /* do { ... } while(not_unregistered); */
+
+ /* Change sync source back to base board so we don't freeze up
+ * when we reset the XHFC */
+ b400m_disable_workqueues(wc);
+
+ for (i = 0; i < (MAX_SPANS - 1); i++) {
+ if (wc->spans[i])
+ wc->spans[i]->timing_priority = 0;
+ }
+
+ for (i = 0; i < 4; i++)
+ b4->spans[i].span->flags &= ~DAHDI_FLAG_RUNNING;
+
+ wctdm_change_card_sync_src(b4->wc, 0, 0);
+
+ xhfc_init_stage1(b4);
+
+ destroy_workqueue(b4->xhfc_ws);
+
+ /* Set these to MOD_TYPE_NONE to ensure that our checkisr
+ * routines are not entered */
+ wc->modtype[card] = MOD_TYPE_NONE;
+ wc->modtype[card + 1] = MOD_TYPE_NONE;
+ wc->modtype[card + 2] = MOD_TYPE_NONE;
+ wc->modtype[card + 3] = MOD_TYPE_NONE;
+
+ wc->mods[card].bri = NULL;
+ wc->mods[card + 1].bri = NULL;
+ wc->mods[card + 2].bri = NULL;
+ wc->mods[card + 3].bri = NULL;
+
+ msleep(voicebus_current_latency(&wc->vb) << 1);
+ b4_info(b4, "Driver unloaded.\n");
+ kfree(b4);
+ }
+
+}
+
+void b400m_module_init(void)
+{
+ fasthdlc_precalc();
+}
+
+void b400m_module_cleanup(void)
+{
+}
diff --git a/drivers/dahdi/wctdm24xxp/xhfc.h b/drivers/dahdi/wctdm24xxp/xhfc.h
new file mode 100644
index 0000000..2699175
--- /dev/null
+++ b/drivers/dahdi/wctdm24xxp/xhfc.h
@@ -0,0 +1,49 @@
+/*
+ * B400M Quad-BRI module Driver
+ * Written by Andrew Kohlsmith <akohlsmith@mixdown.ca>
+ *
+ * Copyright (C) 2010 Digium, Inc.
+ * All rights reserved.
+ *
+ */
+
+/*
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2 as published by the
+ * Free Software Foundation. See the LICENSE file included with
+ * this program for more details.
+ */
+
+
+#ifndef _B4XXM_H_
+#define _B4XXM_H_
+
+extern int bri_debug;
+extern int bri_spanfilter;
+extern int bri_teignorered;
+extern int bri_alarmdebounce;
+extern int bri_persistentlayer1;
+extern int timingcable;
+
+struct b400m;
+
+/* probes the given card to see if it's a B400M */
+int wctdm_init_b400m(struct wctdm *wc, int card);
+int wctdm_bri_checkisr(struct wctdm *wc, int card, int offset);
+void wctdm_unload_b400m(struct wctdm *wc, int card);
+void wctdm_hdlc_hard_xmit(struct dahdi_chan *chan);
+int b400m_spanconfig(struct dahdi_span *span, struct dahdi_lineconfig *lc);
+int b400m_dchan(struct dahdi_span *span);
+int b400m_chanconfig(struct dahdi_chan *chan, int sigtype);
+void b400m_post_init(struct b400m *b4);
+void b400m_set_dahdi_span(struct b400m *b4, int spanno, struct dahdi_span *span);
+void b400m_module_init(void);
+void b400m_module_cleanup(void);
+
+#endif /* _B4XX_H_ */
diff --git a/drivers/dahdi/wcte12xp/base.c b/drivers/dahdi/wcte12xp/base.c
index 38efdfa..f88b042 100644
--- a/drivers/dahdi/wcte12xp/base.c
+++ b/drivers/dahdi/wcte12xp/base.c
@@ -193,7 +193,7 @@ static void cmd_dequeue(struct t1 *wc, unsigned char *writechunk, int eframe, in
}
-static inline void cmd_decipher(struct t1 *wc, unsigned char *readchunk)
+static inline void cmd_decipher(struct t1 *wc, const u8 *readchunk)
{
struct command *cmd = NULL;
unsigned long flags;
@@ -221,7 +221,7 @@ static inline void cmd_decipher(struct t1 *wc, unsigned char *readchunk)
spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
}
-inline void cmd_decipher_vpmadt032(struct t1 *wc, unsigned char *readchunk)
+inline void cmd_decipher_vpmadt032(struct t1 *wc, const u8 *readchunk)
{
unsigned long flags;
struct vpmadt032 *vpm = wc->vpmadt032;
@@ -1660,7 +1660,7 @@ static void t1_do_counters(struct t1 *wc)
}
}
-static inline void t1_transmitprep(struct t1 *wc, unsigned char* writechunk)
+static inline void t1_transmitprep(struct t1 *wc, u8 *writechunk)
{
int x;
int y;
@@ -1697,7 +1697,7 @@ static inline void t1_transmitprep(struct t1 *wc, unsigned char* writechunk)
}
}
-static inline void t1_receiveprep(struct t1 *wc, unsigned char* readchunk)
+static inline void t1_receiveprep(struct t1 *wc, const u8* readchunk)
{
int x,chan;
unsigned char expected;