summaryrefslogtreecommitdiff
path: root/drivers/dahdi/wctc4xxp/base.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dahdi/wctc4xxp/base.c')
-rw-r--r--drivers/dahdi/wctc4xxp/base.c258
1 files changed, 181 insertions, 77 deletions
diff --git a/drivers/dahdi/wctc4xxp/base.c b/drivers/dahdi/wctc4xxp/base.c
index 0dc55ad..6668971 100644
--- a/drivers/dahdi/wctc4xxp/base.c
+++ b/drivers/dahdi/wctc4xxp/base.c
@@ -86,6 +86,10 @@
#endif
#endif
+/* The total number of active channels over which the driver will start polling
+ * the card every 10 ms. */
+#define POLLING_CALL_THRESHOLD 40
+
#define INVALID 999 /* Used to mark invalid channels, commands, etc.. */
#define MAX_CHANNEL_PACKETS 5
@@ -397,6 +401,7 @@ struct wcdte {
struct semaphore chansem;
#define DTE_READY 1
#define DTE_SHUTDOWN 2
+#define DTE_POLLING 3
unsigned long flags;
/* This is a device-global list of commands that are waiting to be
@@ -407,6 +412,7 @@ struct wcdte {
spinlock_t rx_list_lock;
struct list_head rx_list;
+ spinlock_t rx_lock;
unsigned int seq_num;
int last_rx_seq_num;
@@ -443,7 +449,11 @@ struct wcdte {
struct napi_struct napi;
#endif
struct timer_list watchdog;
-
+ atomic_t open_channels;
+ struct timer_list polling;
+#if HZ > 100
+ unsigned long jiffies_at_last_poll;
+#endif
};
#ifdef HAVE_NETDEV_PRIV
@@ -845,7 +855,7 @@ struct wctc4xxp_descriptor {
__le32 container; /* Unused */
} __attribute__((packed));
-#define DRING_SIZE (1 << 5) /* Must be a power of two */
+#define DRING_SIZE (1 << 7) /* Must be a power of two */
#define DRING_MASK (DRING_SIZE-1)
#define MIN_PACKET_LEN 64
@@ -1028,9 +1038,10 @@ __wctc4xxp_getctl(struct wcdte *wc, unsigned int addr)
static inline void
wctc4xxp_setctl(struct wcdte *wc, unsigned int addr, unsigned int val)
{
- spin_lock_bh(&wc->reglock);
+ unsigned long flags;
+ spin_lock_irqsave(&wc->reglock, flags);
__wctc4xxp_setctl(wc, addr, val);
- spin_unlock_bh(&wc->reglock);
+ spin_unlock_irqrestore(&wc->reglock, flags);
}
static inline void
@@ -1549,10 +1560,7 @@ wctc4xxp_cleanup_descriptor_ring(struct wctc4xxp_descriptor_ring *dr)
{
int i;
struct wctc4xxp_descriptor *d;
- unsigned long flags;
- /* NOTE: The DTE must be in the stopped state. */
- spin_lock_irqsave(&dr->lock, flags);
for (i = 0; i < DRING_SIZE; ++i) {
d = wctc4xxp_descriptor(dr, i);
if (d->buffer1) {
@@ -1570,7 +1578,6 @@ wctc4xxp_cleanup_descriptor_ring(struct wctc4xxp_descriptor_ring *dr)
dr->head = 0;
dr->tail = 0;
dr->count = 0;
- spin_unlock_irqrestore(&dr->lock, flags);
pci_free_consistent(dr->pdev, (sizeof(*d)+dr->padding) * DRING_SIZE,
dr->desc, dr->desc_dma);
}
@@ -1578,13 +1585,14 @@ wctc4xxp_cleanup_descriptor_ring(struct wctc4xxp_descriptor_ring *dr)
static void wctc4xxp_cleanup_command_list(struct wcdte *wc)
{
struct tcb *cmd;
+ unsigned long flags;
LIST_HEAD(local_list);
- spin_lock_bh(&wc->cmd_list_lock);
+ spin_lock_irqsave(&wc->cmd_list_lock, flags);
list_splice_init(&wc->cmd_list, &local_list);
list_splice_init(&wc->waiting_for_response_list, &local_list);
list_splice_init(&wc->rx_list, &local_list);
- spin_unlock_bh(&wc->cmd_list_lock);
+ spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
while (!list_empty(&local_list)) {
cmd = list_entry(local_list.next, struct tcb, node);
@@ -1600,25 +1608,28 @@ static void wctc4xxp_cleanup_command_list(struct wcdte *wc)
static void
wctc4xxp_add_to_command_list(struct wcdte *wc, struct tcb *cmd)
{
- spin_lock_bh(&wc->cmd_list_lock);
+ unsigned long flags;
+ spin_lock_irqsave(&wc->cmd_list_lock, flags);
list_add_tail(&cmd->node, &wc->cmd_list);
- spin_unlock_bh(&wc->cmd_list_lock);
+ spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
}
static void
wctc4xxp_add_to_response_list(struct wcdte *wc, struct tcb *cmd)
{
- spin_lock_bh(&wc->cmd_list_lock);
+ unsigned long flags;
+ spin_lock_irqsave(&wc->cmd_list_lock, flags);
list_add_tail(&cmd->node, &wc->waiting_for_response_list);
- spin_unlock_bh(&wc->cmd_list_lock);
+ spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
}
static void
wctc4xxp_remove_from_response_list(struct wcdte *wc, struct tcb *cmd)
{
- spin_lock_bh(&wc->cmd_list_lock);
+ unsigned long flags;
+ spin_lock_irqsave(&wc->cmd_list_lock, flags);
list_del_init(&cmd->node);
- spin_unlock_bh(&wc->cmd_list_lock);
+ spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
}
static void
@@ -1709,9 +1720,10 @@ static unsigned int
wctc4xxp_getctl(struct wcdte *wc, unsigned int addr)
{
unsigned int val;
- spin_lock_bh(&wc->reglock);
+ unsigned long flags;
+ spin_lock_irqsave(&wc->reglock, flags);
val = __wctc4xxp_getctl(wc, addr);
- spin_unlock_bh(&wc->reglock);
+ spin_unlock_irqrestore(&wc->reglock, flags);
return val;
}
@@ -1721,12 +1733,13 @@ wctc4xxp_cleanup_channel_private(struct wcdte *wc,
{
struct tcb *cmd, *temp;
struct channel_pvt *cpvt = dtc->pvt;
+ unsigned long flags;
LIST_HEAD(local_list);
- spin_lock_bh(&cpvt->lock);
+ spin_lock_irqsave(&cpvt->lock, flags);
list_splice_init(&cpvt->rx_queue, &local_list);
dahdi_tc_clear_data_waiting(dtc);
- spin_unlock_bh(&cpvt->lock);
+ spin_unlock_irqrestore(&cpvt->lock, flags);
memset(&cpvt->stats, 0, sizeof(cpvt->stats));
list_for_each_entry_safe(cmd, temp, &local_list, node) {
@@ -1821,6 +1834,35 @@ do_channel_allocate(struct dahdi_transcoder_channel *dtc)
return res;
}
+static void
+wctc4xxp_setintmask(struct wcdte *wc, unsigned int intmask)
+{
+ wc->intmask = intmask;
+ wctc4xxp_setctl(wc, 0x0038, intmask);
+}
+
+static void
+wctc4xxp_enable_interrupts(struct wcdte *wc)
+{
+ wctc4xxp_setintmask(wc, 0x000180c0);
+}
+
+static void
+wctc4xxp_disable_interrupts(struct wcdte *wc)
+{
+ /* Disable interrupts */
+ wctc4xxp_setintmask(wc, 0x00000000);
+ wctc4xxp_setctl(wc, 0x0084, 0x00000000);
+}
+
+static void
+wctc4xxp_enable_polling(struct wcdte *wc)
+{
+ set_bit(DTE_POLLING, &wc->flags);
+ mod_timer(&wc->polling, jiffies + 1);
+ wctc4xxp_disable_interrupts(wc);
+}
+
static int
wctc4xxp_operation_allocate(struct dahdi_transcoder_channel *dtc)
{
@@ -1832,6 +1874,12 @@ wctc4xxp_operation_allocate(struct dahdi_transcoder_channel *dtc)
return -EIO;
}
+ atomic_inc(&wc->open_channels);
+ if (atomic_read(&wc->open_channels) > POLLING_CALL_THRESHOLD) {
+ if (!test_bit(DTE_POLLING, &wc->flags))
+ wctc4xxp_enable_polling(wc);
+ }
+
if (dahdi_tc_is_built(dtc)) {
DTE_DEBUG(DTE_DEBUG_CHANNEL_SETUP,
"Allocating channel %p which is already built.\n", dtc);
@@ -1840,6 +1888,13 @@ wctc4xxp_operation_allocate(struct dahdi_transcoder_channel *dtc)
return do_channel_allocate(dtc);
}
+static void
+wctc4xxp_disable_polling(struct wcdte *wc)
+{
+ clear_bit(DTE_POLLING, &wc->flags);
+ wctc4xxp_enable_interrupts(wc);
+}
+
static int
wctc4xxp_operation_release(struct dahdi_transcoder_channel *dtc)
{
@@ -1869,6 +1924,14 @@ wctc4xxp_operation_release(struct dahdi_transcoder_channel *dtc)
return -EINTR;
#endif
+ atomic_dec(&wc->open_channels);
+ if (atomic_read(&wc->open_channels) < POLLING_CALL_THRESHOLD) {
+ if (test_bit(DTE_POLLING, &wc->flags))
+ wctc4xxp_disable_polling(wc);
+ }
+
+ DTE_DEBUG(DTE_DEBUG_GENERAL, "%d open channels.\n", atomic_read(&wc->open_channels));
+
packets_received = atomic_read(&cpvt->stats.packets_received);
packets_sent = atomic_read(&cpvt->stats.packets_sent);
@@ -1924,7 +1987,8 @@ get_ready_cmd(struct dahdi_transcoder_channel *dtc)
{
struct channel_pvt *cpvt = dtc->pvt;
struct tcb *cmd;
- spin_lock_bh(&cpvt->lock);
+ unsigned long flags;
+ spin_lock_irqsave(&cpvt->lock, flags);
if (!list_empty(&cpvt->rx_queue)) {
WARN_ON(!dahdi_tc_is_data_waiting(dtc));
cmd = list_entry(cpvt->rx_queue.next, struct tcb, node);
@@ -1934,10 +1998,60 @@ get_ready_cmd(struct dahdi_transcoder_channel *dtc)
}
if (list_empty(&cpvt->rx_queue))
dahdi_tc_clear_data_waiting(dtc);
- spin_unlock_bh(&cpvt->lock);
+ spin_unlock_irqrestore(&cpvt->lock, flags);
return cmd;
}
+static int
+wctc4xxp_handle_receive_ring(struct wcdte *wc)
+{
+ struct tcb *cmd;
+ unsigned long flags;
+ unsigned int count = 0;
+
+ /* If we can't grab this lock, another thread must already be checking
+ * the receive ring...so we should just finish up, and we'll try again
+ * later. */
+ if (!spin_trylock_irqsave(&wc->rx_lock, flags))
+ return 0;
+
+ while ((cmd = wctc4xxp_retrieve(wc->rxd))) {
+ ++count;
+ spin_lock(&wc->rx_list_lock);
+ list_add_tail(&cmd->node, &wc->rx_list);
+ spin_unlock(&wc->rx_list_lock);
+ cmd = __alloc_cmd(GFP_ATOMIC, 0);
+ if (!cmd) {
+ DTE_PRINTK(ERR, "Out of memory in %s.\n", __func__);
+ } else {
+ if (wctc4xxp_submit(wc->rxd, cmd)) {
+ DTE_PRINTK(ERR, "Failed submit in %s\n",
+ __func__);
+ free_cmd(cmd);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&wc->rx_lock, flags);
+ return count;
+}
+
+static void
+__wctc4xxp_polling(struct wcdte *wc)
+{
+ if (wctc4xxp_handle_receive_ring(wc))
+ schedule_work(&wc->deferred_work);
+}
+
+static void
+wctc4xxp_polling(unsigned long data)
+{
+ struct wcdte *wc = (struct wcdte *)data;
+ __wctc4xxp_polling(wc);
+ if (test_bit(DTE_POLLING, &wc->flags))
+ mod_timer(&wc->polling, jiffies + 1);
+}
+
+
/* Called with a buffer in which to copy a transcoded frame. */
static ssize_t
wctc4xxp_read(struct file *file, char __user *frame, size_t count, loff_t *ppos)
@@ -2073,6 +2187,19 @@ wctc4xxp_write(struct file *file, const char __user *frame,
atomic_inc(&cpvt->stats.packets_sent);
wctc4xxp_transmit_cmd(wc, cmd);
+
+ if (test_bit(DTE_POLLING, &wc->flags)) {
+#if HZ == 100
+ __wctc4xxp_polling(wc);
+#else
+ if (jiffies != wc->jiffies_at_last_poll) {
+ wc->jiffies_at_last_poll = jiffies;
+ __wctc4xxp_polling(wc);
+ }
+#endif
+ }
+
+
return count;
}
@@ -2115,14 +2242,14 @@ do_rx_response_packet(struct wcdte *wc, struct tcb *cmd)
wc->last_rx_seq_num = rxhdr->seq_num;
}
- spin_lock_bh(&wc->cmd_list_lock);
+ spin_lock_irqsave(&wc->cmd_list_lock, flags);
list_for_each_entry_safe(pos, temp,
&wc->waiting_for_response_list, node) {
listhdr = pos->data;
if ((listhdr->function == rxhdr->function) &&
(listhdr->channel == rxhdr->channel)) {
- spin_lock_irqsave(&pos->lock, flags);
+ spin_lock(&pos->lock);
list_del_init(&pos->node);
pos->flags &= ~(__WAIT_FOR_RESPONSE);
pos->response = cmd;
@@ -2131,13 +2258,13 @@ do_rx_response_packet(struct wcdte *wc, struct tcb *cmd)
if (pos->flags & TX_COMPLETE) {
complete(&pos->complete);
}
- spin_unlock_irqrestore(&pos->lock, flags);
+ spin_unlock(&pos->lock);
handled = 1;
break;
}
}
- spin_unlock_bh(&wc->cmd_list_lock);
+ spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
if (!handled) {
DTE_DEBUG(DTE_DEBUG_GENERAL, "Freeing unhandled response ch:(%04x)\n",
@@ -2155,7 +2282,7 @@ do_rx_ack_packet(struct wcdte *wc, struct tcb *cmd)
rxhdr = cmd->data;
- spin_lock_bh(&wc->cmd_list_lock);
+ spin_lock_irqsave(&wc->cmd_list_lock, flags);
list_for_each_entry_safe(pos, temp,
&wc->waiting_for_response_list, node) {
listhdr = pos->data;
@@ -2168,26 +2295,26 @@ do_rx_ack_packet(struct wcdte *wc, struct tcb *cmd)
complete(&pos->complete);
} else if ((listhdr->seq_num == rxhdr->seq_num) &&
(listhdr->channel == rxhdr->channel)) {
- spin_lock_irqsave(&pos->lock, flags);
+ spin_lock(&pos->lock);
if (pos->flags & __WAIT_FOR_RESPONSE) {
pos->flags &= ~(__WAIT_FOR_ACK);
- spin_unlock_irqrestore(&pos->lock, flags);
+ spin_unlock(&pos->lock);
} else {
list_del_init(&pos->node);
if (pos->flags & DO_NOT_AUTO_FREE) {
WARN_ON(!(pos->flags & TX_COMPLETE));
complete(&pos->complete);
- spin_unlock_irqrestore(&pos->lock, flags);
+ spin_unlock(&pos->lock);
} else {
- spin_unlock_irqrestore(&pos->lock, flags);
+ spin_unlock(&pos->lock);
free_cmd(pos);
}
}
break;
}
}
- spin_unlock_bh(&wc->cmd_list_lock);
+ spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
/* There is never a reason to store up the ack packets. */
free_cmd(cmd);
@@ -2286,6 +2413,7 @@ queue_rtp_packet(struct wcdte *wc, struct tcb *cmd)
struct dahdi_transcoder_channel *dtc;
struct channel_pvt *cpvt;
struct rtp_packet *packet = cmd->data;
+ unsigned long flags;
index = (be16_to_cpu(packet->udphdr.dest) - 0x5000) / 2;
if (unlikely(index >= wc->numchannels)) {
@@ -2312,10 +2440,10 @@ queue_rtp_packet(struct wcdte *wc, struct tcb *cmd)
}
cpvt = dtc->pvt;
- spin_lock_bh(&cpvt->lock);
+ spin_lock_irqsave(&cpvt->lock, flags);
list_add_tail(&cmd->node, &cpvt->rx_queue);
dahdi_tc_set_data_waiting(dtc);
- spin_unlock_bh(&cpvt->lock);
+ spin_unlock_irqrestore(&cpvt->lock, flags);
dahdi_transcoder_alert(dtc);
return;
}
@@ -2364,14 +2492,14 @@ static inline void service_tx_ring(struct wcdte *wc)
/* We've freed up a spot in the hardware ring buffer. If
* another packet is queued up, let's submit it to the
* hardware. */
- spin_lock_bh(&wc->cmd_list_lock);
+ spin_lock_irqsave(&wc->cmd_list_lock, flags);
if (!list_empty(&wc->cmd_list)) {
cmd = list_entry(wc->cmd_list.next, struct tcb, node);
list_del_init(&cmd->node);
} else {
cmd = NULL;
}
- spin_unlock_bh(&wc->cmd_list_lock);
+ spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
if (cmd)
wctc4xxp_transmit_cmd(wc, cmd);
@@ -2422,7 +2550,6 @@ static void deferred_work_func(struct work_struct *work)
DAHDI_IRQ_HANDLER(wctc4xxp_interrupt)
{
struct wcdte *wc = dev_id;
- struct tcb *cmd;
u32 ints;
u32 reg;
#define TX_COMPLETE_INTERRUPT 0x00000001
@@ -2443,24 +2570,7 @@ DAHDI_IRQ_HANDLER(wctc4xxp_interrupt)
reg |= TX_COMPLETE_INTERRUPT;
if (ints & RX_COMPLETE_INTERRUPT) {
- while ((cmd = wctc4xxp_retrieve(wc->rxd))) {
- spin_lock(&wc->rx_list_lock);
- list_add_tail(&cmd->node, &wc->rx_list);
- spin_unlock(&wc->rx_list_lock);
-
- cmd = __alloc_cmd(GFP_ATOMIC, 0);
- if (!cmd) {
- DTE_PRINTK(ERR,
- "Out of memory in %s.\n", __func__);
- } else {
- if (wctc4xxp_submit(wc->rxd, cmd)) {
- DTE_PRINTK(ERR,
- "Failed submit in %s\n",
- __func__);
- free_cmd(cmd);
- }
- }
- }
+ wctc4xxp_handle_receive_ring(wc);
reg |= RX_COMPLETE_INTERRUPT;
}
@@ -2558,19 +2668,6 @@ wctc4xxp_hardware_init(struct wcdte *wc)
}
static void
-wctc4xxp_setintmask(struct wcdte *wc, unsigned int intmask)
-{
- wc->intmask = intmask;
- wctc4xxp_setctl(wc, 0x0038, intmask);
-}
-
-static void
-wctc4xxp_enable_interrupts(struct wcdte *wc)
-{
- wctc4xxp_setintmask(wc, 0x000180c0);
-}
-
-static void
wctc4xxp_start_dma(struct wcdte *wc)
{
int res;
@@ -2630,13 +2727,6 @@ wctc4xxp_stop_dma(struct wcdte *wc)
msleep(1);
}
-static void
-wctc4xxp_disable_interrupts(struct wcdte *wc)
-{
- /* Disable interrupts */
- wctc4xxp_setintmask(wc, 0x00000000);
- wctc4xxp_setctl(wc, 0x0084, 0x00000000);
-}
#define MDIO_SHIFT_CLK 0x10000
#define MDIO_DATA_WRITE1 0x20000
@@ -3303,6 +3393,7 @@ wctc4xxp_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
spin_lock_init(&wc->reglock);
spin_lock_init(&wc->cmd_list_lock);
spin_lock_init(&wc->rx_list_lock);
+ spin_lock_init(&wc->rx_lock);
INIT_LIST_HEAD(&wc->cmd_list);
INIT_LIST_HEAD(&wc->waiting_for_response_list);
INIT_LIST_HEAD(&wc->rx_list);
@@ -3410,6 +3501,14 @@ wctc4xxp_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
setup_timer(&wc->watchdog, wctc4xxp_watchdog, (unsigned long)wc);
# endif
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+ wc->polling.function = wctc4xxp_polling;
+ wc->polling.data = (unsigned long)wc;
+ init_timer(&wc->polling);
+# else
+ setup_timer(&wc->polling, wctc4xxp_polling, (unsigned long)wc);
+# endif
+
/* ------------------------------------------------------------------
* Load the firmware and start the DTE.
* --------------------------------------------------------------- */
@@ -3530,6 +3629,12 @@ static void __devexit wctc4xxp_remove_one(struct pci_dev *pdev)
del_timer_sync(&wc->watchdog);
}
+ /* This should already be stopped, but it doesn't hurt to make sure. */
+ clear_bit(DTE_POLLING, &wc->flags);
+ if (del_timer_sync(&wc->polling)) {
+ del_timer_sync(&wc->polling);
+ }
+
wctc4xxp_net_unregister(wc);
/* Stop any DMA */
@@ -3548,7 +3653,6 @@ static void __devexit wctc4xxp_remove_one(struct pci_dev *pdev)
/* Free Resources */
release_region(wc->iobase, 0xff);
- spin_lock_bh(&wc->cmd_list_lock);
if (wc->txd) {
if (wc->txd->desc)
wctc4xxp_cleanup_descriptor_ring(wc->txd);
@@ -3559,7 +3663,7 @@ static void __devexit wctc4xxp_remove_one(struct pci_dev *pdev)
wctc4xxp_cleanup_descriptor_ring(wc->rxd);
kfree(wc->rxd);
}
- spin_unlock_bh(&wc->cmd_list_lock);
+
wctc4xxp_cleanup_command_list(wc);
wctc4xxp_cleanup_channels(wc);