diff options
-rw-r--r-- | drivers/dahdi/voicebus/voicebus.c | 304 | ||||
-rw-r--r-- | drivers/dahdi/voicebus/voicebus.h | 2 |
2 files changed, 143 insertions, 163 deletions
diff --git a/drivers/dahdi/voicebus/voicebus.c b/drivers/dahdi/voicebus/voicebus.c index d68c1f3..8853093 100644 --- a/drivers/dahdi/voicebus/voicebus.c +++ b/drivers/dahdi/voicebus/voicebus.c @@ -141,12 +141,10 @@ static inline void handle_transmit(struct voicebus *vb, void *vbb) #endif /* Bit definitions for struct voicebus.flags */ -#define TX_UNDERRUN 1 -#define RX_UNDERRUN 2 -#define IN_DEFERRED_PROCESSING 3 -#define STOP 4 -#define STOPPED 5 -#define LATENCY_LOCKED 6 +#define IN_DEFERRED_PROCESSING 1 +#define STOP 2 +#define STOPPED 3 +#define LATENCY_LOCKED 4 #if VOICEBUS_DEFERRED == WORKQUEUE #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) @@ -629,13 +627,74 @@ vb_reset_interface(struct voicebus *vb) return 0; } +/*! + * \brief Give a frame to the hardware to use for receiving. + * + */ +static inline int +vb_submit_rxb(struct voicebus *vb, void *vbb) +{ + struct voicebus_descriptor *d; + struct voicebus_descriptor_list *dl = &vb->rxd; + unsigned int tail = dl->tail; + + d = vb_descriptor(dl, tail); + + if (unlikely(d->buffer1)) { + /* Do not overwrite a buffer that is still in progress. */ + WARN_ON(1); + voicebus_free(vb, vbb); + return -EBUSY; + } + + dl->pending[tail] = vbb; + dl->tail = (++tail) & DRING_MASK; + d->buffer1 = dma_map_single(&vb->pdev->dev, vbb, + VOICEBUS_SFRAME_SIZE, DMA_FROM_DEVICE); + SET_OWNED(d); /* That's it until the hardware is done with it. */ + atomic_inc(&dl->count); + return 0; +} + +static void setup_descriptors(struct voicebus *vb) +{ + int i; + void *vbb; + + vb_cleanup_tx_descriptors(vb); + vb_cleanup_rx_descriptors(vb); + + /* Tell the card where the descriptors are in host memory. */ + vb_setctl(vb, 0x0020, (u32)vb->txd.desc_dma); + vb_setctl(vb, 0x0018, (u32)vb->rxd.desc_dma); + + for (i = 0; i < DRING_SIZE; ++i) { + vbb = voicebus_alloc(vb); + if (unlikely(NULL == vbb)) { + BUG_ON(1); + /* \todo I need to make sure the driver can recover + * from this condition. .... */ + } else { + vb_submit_rxb(vb, vbb); + } + } + + for (i = 0; i < vb->min_tx_buffer_count; ++i) { + vbb = voicebus_alloc(vb); + + if (unlikely(NULL == vbb)) + BUG_ON(1); + else + handle_transmit(vb, vbb); + } +} + static int vb_initialize_interface(struct voicebus *vb) { u32 reg; - vb_cleanup_tx_descriptors(vb); - vb_cleanup_rx_descriptors(vb); + setup_descriptors(vb); /* Pass bad packets, runt packets, disable SQE function, * store-and-forward */ @@ -643,10 +702,6 @@ vb_initialize_interface(struct voicebus *vb) /* ...disable jabber and the receive watchdog. */ vb_setctl(vb, 0x0078, 0x00000013); - /* Tell the card where the descriptors are in host memory. */ - vb_setctl(vb, 0x0020, (u32)vb->txd.desc_dma); - vb_setctl(vb, 0x0018, (u32)vb->rxd.desc_dma); - reg = vb_getctl(vb, 0x00fc); vb_setctl(vb, 0x00fc, (reg & ~0x7) | 0x7); vb_setsdi(vb, 0x00, 0x0100); @@ -725,35 +780,6 @@ int voicebus_transmit(struct voicebus *vb, void *vbb) EXPORT_SYMBOL(voicebus_transmit); /*! - * \brief Give a frame to the hardware to use for receiving. - * - */ -static inline int -vb_submit_rxb(struct voicebus *vb, void *vbb) -{ - struct voicebus_descriptor *d; - struct voicebus_descriptor_list *dl = &vb->rxd; - unsigned int tail = dl->tail; - - d = vb_descriptor(dl, tail); - - if (unlikely(d->buffer1)) { - /* Do not overwrite a buffer that is still in progress. */ - WARN_ON(1); - voicebus_free(vb, vbb); - return -EBUSY; - } - - dl->pending[tail] = vbb; - dl->tail = (++tail) & DRING_MASK; - d->buffer1 = dma_map_single(&vb->pdev->dev, vbb, - VOICEBUS_SFRAME_SIZE, DMA_FROM_DEVICE); - SET_OWNED(d); /* That's it until the hardware is done with it. */ - atomic_inc(&dl->count); - return 0; -} - -/*! * \brief Remove the next completed transmit buffer (txb) from the tx * descriptor ring. * @@ -832,19 +858,6 @@ __vb_tx_demand_poll(struct voicebus *vb) } /*! - * \brief Command the hardware to check if it owns the next transmit - * descriptor. - */ -static void -vb_tx_demand_poll(struct voicebus *vb) -{ - LOCKS_VOICEBUS; - VBLOCK(vb); - __vb_tx_demand_poll(vb); - VBUNLOCK(vb); -} - -/*! * \brief Command the hardware to check if it owns the next receive * descriptor. */ @@ -855,15 +868,6 @@ __vb_rx_demand_poll(struct voicebus *vb) } static void -vb_rx_demand_poll(struct voicebus *vb) -{ - LOCKS_VOICEBUS; - VBLOCK(vb); - __vb_rx_demand_poll(vb); - VBUNLOCK(vb); -} - -static void __vb_enable_interrupts(struct voicebus *vb) { __vb_setctl(vb, IER_CSR7, DEFAULT_INTERRUPTS); @@ -884,6 +888,31 @@ vb_disable_interrupts(struct voicebus *vb) VBUNLOCK(vb); } +static void start_packet_processing(struct voicebus *vb) +{ + LOCKS_VOICEBUS; + u32 reg; + + VBLOCK(vb); + clear_bit(STOP, &vb->flags); + clear_bit(STOPPED, &vb->flags); +#if VOICEBUS_DEFERRED == TIMER + vb->timer.expires = jiffies + HZ/1000; + add_timer(&vb->timer); +#else + /* Clear the interrupt status register. */ + __vb_setctl(vb, SR_CSR5, 0xffffffff); + __vb_enable_interrupts(vb); +#endif + /* 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_rx_demand_poll(vb); + __vb_tx_demand_poll(vb); + VBUNLOCK(vb); +} + /*! * \brief Starts the VoiceBus interface. * @@ -899,10 +928,6 @@ vb_disable_interrupts(struct voicebus *vb) int voicebus_start(struct voicebus *vb) { - LOCKS_VOICEBUS; - u32 reg; - int i; - void *vbb; int ret; if (!vb_is_stopped(vb)) @@ -915,57 +940,7 @@ voicebus_start(struct voicebus *vb) if (ret) return ret; - /* We must set up a minimum of three buffers to start with, since two - * are immediately read into the TX FIFO, and the descriptor of the - * third is read as soon as the first buffer is done. - */ - - /* - * NOTE: handle_transmit is normally only called in the context of the - * deferred processing thread. Since the deferred processing thread - * is known to not be running at this point, it is safe to call the - * handle transmit as if it were. - */ - /* Ensure that all the rx slots are ready for a buffer. */ - for (i = 0; i < DRING_SIZE; ++i) { - vbb = voicebus_alloc(vb); - if (unlikely(NULL == vbb)) { - BUG_ON(1); - /* \todo I need to make sure the driver can recover - * from this condition. .... */ - } else { - vb_submit_rxb(vb, vbb); - } - } - - for (i = 0; i < vb->min_tx_buffer_count; ++i) { - vbb = voicebus_alloc(vb); - - if (unlikely(NULL == vbb)) - BUG_ON(1); - else - handle_transmit(vb, vbb); - - } - - VBLOCK(vb); - clear_bit(STOP, &vb->flags); - clear_bit(STOPPED, &vb->flags); -#if VOICEBUS_DEFERRED == TIMER - vb->timer.expires = jiffies + HZ/1000; - add_timer(&vb->timer); -#else - /* Clear the interrupt status register. */ - __vb_setctl(vb, SR_CSR5, 0xffffffff); - __vb_enable_interrupts(vb); -#endif - /* 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_rx_demand_poll(vb); - __vb_tx_demand_poll(vb); - VBUNLOCK(vb); + start_packet_processing(vb); BUG_ON(vb_is_stopped(vb)); @@ -1053,6 +1028,14 @@ voicebus_release(struct voicebus *vb) { /* quiesce the hardware */ voicebus_stop(vb); + + /* Make sure the underrun_work isn't running or going to run. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) + flush_scheduled_work(); +#else + cancel_work_sync(&vb->underrun_work); +#endif + #if VOICEBUS_DEFERRED == WORKQUEUE destroy_workqueue(vb->workqueue); #elif VOICEBUS_DEFERRED == TASKLET @@ -1269,8 +1252,6 @@ static void vb_deferred(struct voicebus *vb) unsigned int idle_buffers; int softunderrun; - int underrun = test_bit(TX_UNDERRUN, &vb->flags); - buffer_count = 0; /* First, temporarily store any non-idle buffers that the hardware has @@ -1335,24 +1316,6 @@ static void vb_deferred(struct voicebus *vb) for (i = 0; i < buffer_count; ++i) handle_transmit(vb, vb->vbb_stash[i]); - /* If underrun is set, it means that the hardware signalled that it - * completely ran out of transmit descriptors. This is what we are - * trying to avoid with all this racy softunderun business, but alas, - * it's still possible to happen if interrupts are locked longer than - * DRING_SIZE milliseconds for some reason. We should have already fixed - * up the descriptor ring in this case, so let's just tell the hardware - * to reread what it believes the next descriptor is. */ - if (unlikely(underrun)) { - if (printk_ratelimit()) { - dev_info(&vb->pdev->dev, "Host failed to service " - "card interrupt within %d ms which is a " - "hardunderun.\n", DRING_SIZE); - } - vb_rx_demand_poll(vb); - vb_tx_demand_poll(vb); - clear_bit(TX_UNDERRUN, &vb->flags); - } - /* Print any messages about soft latency bumps after we fix the transmit * descriptor ring. Otherwise it's possible to take so much time * printing the dmesg output that we lose the lead that we got on the @@ -1387,6 +1350,35 @@ static void vb_deferred(struct voicebus *vb) } } +/** + * handle_hardunderrun() - reset the AN983 after experiencing a hardunderrun. + * @work: The work_struct used to queue this function. + * + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void handle_hardunderrun(void *data) +{ + struct voicebus *vb = data; +#else +static void handle_hardunderrun(struct work_struct *work) +{ + struct voicebus *vb = container_of(work, struct voicebus, + underrun_work); +#endif + if (printk_ratelimit()) { + dev_info(&vb->pdev->dev, "Host failed to service " + "card interrupt within %d ms which is a " + "hardunderun.\n", DRING_SIZE); + } + voicebus_stop(vb); + + if (vb->ops->handle_error) + vb->ops->handle_error(vb); + + setup_descriptors(vb); + start_packet_processing(vb); +} + /*! * \brief Interrupt handler for VoiceBus interface. * @@ -1414,7 +1406,11 @@ vb_isr(int irq, void *dev_id) if (!int_status) return IRQ_NONE; - if (likely(int_status & TX_COMPLETE_INTERRUPT)) { + if (unlikely((int_status & TX_UNAVAILABLE_INTERRUPT) && + !test_bit(STOP, &vb->flags))) { + schedule_work(&vb->underrun_work); + __vb_setctl(vb, SR_CSR5, int_status); + } else if (likely(int_status & TX_COMPLETE_INTERRUPT)) { /* ******************************************************** */ /* NORMAL INTERRUPT CASE */ /* ******************************************************** */ @@ -1427,30 +1423,6 @@ vb_isr(int irq, void *dev_id) # endif __vb_setctl(vb, SR_CSR5, TX_COMPLETE_INTERRUPT); } else { - /* ******************************************************** */ - /* ABNORMAL / ERROR CONDITIONS */ - /* ******************************************************** */ - if ((int_status & TX_UNAVAILABLE_INTERRUPT)) { - /* This can happen if the host fails to service the - * interrupt within the required time interval (1ms - * for each buffer on the queue). Increasing the - * depth of the tx queue (up to a maximum of - * DRING_SIZE) can make the driver / system more - * tolerant of interrupt latency under periods of - * heavy system load, but also increases the general - * latency that the driver adds to the voice - * conversations. - */ - set_bit(TX_UNDERRUN, &vb->flags); -# if VOICEBUS_DEFERRED == WORKQUEUE - queue_work(vb->workqueue, &vb->workitem); -# elif VOICEBUS_DEFERRED == TASKLET - tasklet_schedule(&vb->tasklet); -# else - vb_deferred(vb); -# endif - } - if (int_status & FATAL_BUS_ERROR_INTERRUPT) dev_err(&vb->pdev->dev, "Fatal Bus Error detected!\n"); @@ -1567,6 +1539,12 @@ voicebus_init(struct voicebus *vb, const char *board_name) vb->timer.data = (unsigned long)vb; #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&vb->underrun_work, handle_hardunderrun, vb); +#else + INIT_WORK(&vb->underrun_work, handle_hardunderrun); +#endif + /* ---------------------------------------------------------------- Configure the hardware / kernel module interfaces. ---------------------------------------------------------------- */ diff --git a/drivers/dahdi/voicebus/voicebus.h b/drivers/dahdi/voicebus/voicebus.h index a038b91..be713c6 100644 --- a/drivers/dahdi/voicebus/voicebus.h +++ b/drivers/dahdi/voicebus/voicebus.h @@ -58,6 +58,7 @@ struct voicebus; struct voicebus_operations { void (*handle_receive)(struct voicebus *vb, void *vbb); void (*handle_transmit)(struct voicebus *vb, void *vbb); + void (*handle_error)(struct voicebus *vb); }; /** @@ -94,6 +95,7 @@ struct voicebus { #elif VOICEBUS_DEFERRED == TIMER struct timer_list timer; #endif + struct work_struct underrun_work; const struct voicebus_operations *ops; struct completion stopped_completion; unsigned long flags; |