diff options
Diffstat (limited to 'drivers/dahdi/voicebus/voicebus.c')
-rw-r--r-- | drivers/dahdi/voicebus/voicebus.c | 601 |
1 files changed, 466 insertions, 135 deletions
diff --git a/drivers/dahdi/voicebus/voicebus.c b/drivers/dahdi/voicebus/voicebus.c index 3fdec4c..3764b4c 100644 --- a/drivers/dahdi/voicebus/voicebus.c +++ b/drivers/dahdi/voicebus/voicebus.c @@ -63,7 +63,7 @@ #endif /*! The number of descriptors in both the tx and rx descriptor ring. */ -#define DRING_SIZE (1 << 5) /* Must be a power of 2 */ +#define DRING_SIZE (1 << 7) /* Must be a power of 2 */ #define DRING_MASK (DRING_SIZE-1) /* Interrupt status' reported in SR_CSR5 */ @@ -110,10 +110,10 @@ /* In memory structure shared by the host and the adapter. */ struct voicebus_descriptor { - u32 des0; - u32 des1; - u32 buffer1; - u32 container; /* Unused */ + volatile __le32 des0; + volatile __le32 des1; + volatile __le32 buffer1; + volatile __le32 container; /* Unused */ } __attribute__((packed)); struct voicebus_descriptor_list { @@ -127,16 +127,20 @@ struct voicebus_descriptor_list { void *pending[DRING_SIZE]; /* PCI Bus address of the descriptor list. */ dma_addr_t desc_dma; - /*! either DMA_FROM_DEVICE or DMA_TO_DEVICE */ - unsigned int direction; /*! The number of buffers currently submitted to the hardware. */ atomic_t count; /*! The number of bytes to pad each descriptor for cache alignment. */ unsigned int padding; }; - -/*! * \brief Represents a VoiceBus interface on a Digium telephony card. +/** + * struct voicebus - + * + * @tx_idle_vbb: + * @tx_idle_vbb_dma_addr: + * @max_latency: Do not allow the driver to automatically insert more than this + * much latency to the tdm stream by default. + * @count: The number of non-idle buffers that we should be expecting. */ struct voicebus { /*! The system pci device for this VoiceBus interface. */ @@ -152,6 +156,8 @@ struct voicebus { /*! Pool to allocate memory for the tx and rx descriptor rings. */ struct voicebus_descriptor_list rxd; struct voicebus_descriptor_list txd; + void *idle_vbb; + dma_addr_t idle_vbb_dma_addr; /*! Level of debugging information. 0=None, 5=Insane. */ atomic_t debuglevel; /*! Cache of buffer objects. */ @@ -186,8 +192,16 @@ struct voicebus { unsigned long flags; /*! Number of tx buffers to queue up before enabling interrupts. */ unsigned int min_tx_buffer_count; + unsigned int max_latency; + void *vbb_stash[DRING_SIZE]; + unsigned int count; }; +static inline void handle_transmit(struct voicebus *vb, void *vbb) +{ + vb->handle_transmit(vbb, vb->context); +} + /* * Use the following macros to lock the VoiceBus interface, and it won't * matter if the deferred processing is running inside the interrupt handler, @@ -275,7 +289,8 @@ stop_vb_deferred(struct voicebus *vb) #endif static inline struct voicebus_descriptor * -vb_descriptor(struct voicebus_descriptor_list *dl, int index) +vb_descriptor(const struct voicebus_descriptor_list *dl, + const unsigned int index) { struct voicebus_descriptor *d; d = (struct voicebus_descriptor *)((u8*)dl->desc + @@ -317,16 +332,55 @@ vb_initialize_descriptors(struct voicebus *vb, struct voicebus_descriptor_list * d->des1 = des1; } d->des1 |= cpu_to_le32(END_OF_RING); - dl->direction = direction; atomic_set(&dl->count, 0); return 0; } +#define OWNED(_d_) (((_d_)->des0)&OWN_BIT) +#define SET_OWNED(_d_) do { wmb(); (_d_)->des0 |= OWN_BIT; wmb(); } while (0) + static int vb_initialize_tx_descriptors(struct voicebus *vb) { - return vb_initialize_descriptors( - vb, &vb->txd, 0xe4800000 | vb->framesize, DMA_TO_DEVICE); + int i; + int des1 = 0xe4800000 | vb->framesize; + struct voicebus_descriptor *d; + struct voicebus_descriptor_list *dl = &vb->txd; + const u32 END_OF_RING = 0x02000000; + + WARN_ON(!dl); + WARN_ON((NULL == vb->idle_vbb) || (0 == vb->idle_vbb_dma_addr)); + + /* + * Add some padding to each descriptor to ensure that they are + * aligned on host system cache-line boundaries, but only for the + * cache-line sizes that we support. + * + */ + if ((0x08 == vb->cache_line_size) || (0x10 == vb->cache_line_size) || + (0x20 == vb->cache_line_size)) { + dl->padding = (vb->cache_line_size*sizeof(u32)) - sizeof(*d); + } else { + dl->padding = 0; + } + + dl->desc = pci_alloc_consistent(vb->pdev, + (sizeof(*d) + dl->padding) * + DRING_SIZE, &dl->desc_dma); + if (!dl->desc) + return -ENOMEM; + + memset(dl->desc, 0, (sizeof(*d) + dl->padding) * DRING_SIZE); + for (i = 0; i < DRING_SIZE; ++i) { + d = vb_descriptor(dl, i); + d->des1 = des1; + d->buffer1 = vb->idle_vbb_dma_addr; + dl->pending[i] = vb->idle_vbb; + SET_OWNED(d); + } + d->des1 |= cpu_to_le32(END_OF_RING); + atomic_set(&dl->count, 0); + return 0; } static int @@ -453,9 +507,37 @@ vb_is_stopped(struct voicebus *vb) } static void -vb_cleanup_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl) +vb_cleanup_tx_descriptors(struct voicebus *vb) +{ + unsigned int i; + struct voicebus_descriptor_list *dl = &vb->txd; + struct voicebus_descriptor *d; + + assert(vb_is_stopped(vb)); + + for (i = 0; i < DRING_SIZE; ++i) { + d = vb_descriptor(dl, i); + if (d->buffer1 && (d->buffer1 != vb->idle_vbb_dma_addr)) { + WARN_ON(!dl->pending[i]); + dma_unmap_single(&vb->pdev->dev, d->buffer1, + vb->framesize, DMA_TO_DEVICE); + voicebus_free(vb, dl->pending[i]); + } + d->buffer1 = vb->idle_vbb_dma_addr; + dl->pending[i] = vb->idle_vbb; + SET_OWNED(d); + } + /* Send out two idle buffers to start because sometimes the first buffer + * doesn't make it back to us. */ + dl->head = dl->tail = 2; + atomic_set(&dl->count, 0); +} + +static void +vb_cleanup_rx_descriptors(struct voicebus *vb) { unsigned int i; + struct voicebus_descriptor_list *dl = &vb->rxd; struct voicebus_descriptor *d; assert(vb_is_stopped(vb)); @@ -463,6 +545,8 @@ vb_cleanup_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl) for (i = 0; i < DRING_SIZE; ++i) { d = vb_descriptor(dl, i); if (d->buffer1) { + dma_unmap_single(&vb->pdev->dev, d->buffer1, + vb->framesize, DMA_FROM_DEVICE); d->buffer1 = 0; assert(dl->pending[i]); voicebus_free(vb, dl->pending[i]); @@ -475,6 +559,15 @@ vb_cleanup_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl) atomic_set(&dl->count, 0); } +static void vb_cleanup_descriptors(struct voicebus *vb, + struct voicebus_descriptor_list *dl) +{ + if (dl == &vb->txd) + vb_cleanup_tx_descriptors(vb); + else + vb_cleanup_rx_descriptors(vb); +} + static void vb_free_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl) { @@ -650,8 +743,8 @@ vb_initialize_interface(struct voicebus *vb) { u32 reg; - vb_cleanup_descriptors(vb, &vb->txd); - vb_cleanup_descriptors(vb, &vb->rxd); + vb_cleanup_tx_descriptors(vb); + vb_cleanup_rx_descriptors(vb); /* Pass bad packets, runt packets, disable SQE function, * store-and-forward */ @@ -684,12 +777,9 @@ vb_initialize_interface(struct voicebus *vb) return ((reg&0x7) == 0x4) ? 0 : -EIO; } -#define OWNED(_d_) (((_d_)->des0)&OWN_BIT) -#define SET_OWNED(_d_) do { wmb(); (_d_)->des0 |= OWN_BIT; wmb(); } while (0) - #ifdef DBG static void -dump_descriptor(struct voicebus *vb, volatile struct voicebus_descriptor *d) +dump_descriptor(struct voicebus *vb, struct voicebus_descriptor *d) { VB_PRINTK(vb, DEBUG, "Displaying descriptor at address %08x\n", (unsigned int)d); VB_PRINTK(vb, DEBUG, " des0: %08x\n", d->des0); @@ -715,61 +805,33 @@ show_buffer(struct voicebus *vb, void *vbb) } #endif -static inline int -vb_submit(struct voicebus *vb, struct voicebus_descriptor_list *dl, void *vbb) +/** + * voicebus_transmit - Queue a buffer on the hardware descriptor ring. + * + */ +int voicebus_transmit(struct voicebus *vb, void *vbb) { - volatile struct voicebus_descriptor *d; - unsigned int tail = dl->tail; + struct voicebus_descriptor *d; + struct voicebus_descriptor_list *dl = &vb->txd; assert_in_vb_deferred(vb); - d = vb_descriptor(dl, tail); + d = vb_descriptor(dl, dl->tail); - if (unlikely(d->buffer1)) { - /* Do not overwrite a buffer that is still in progress. */ - WARN_ON(1); + if (unlikely(d->buffer1 != vb->idle_vbb_dma_addr)) { + if (printk_ratelimit()) + dev_warn(&vb->pdev->dev, "Dropping tx buffer buffer\n"); voicebus_free(vb, vbb); - return -EBUSY; + return -EFAULT; } - dl->pending[tail] = vbb; - dl->tail = (++tail) & DRING_MASK; - d->buffer1 = dma_map_single( - &vb->pdev->dev, vbb, vb->framesize, dl->direction); + dl->pending[dl->tail] = vbb; + dl->tail = (++(dl->tail)) & DRING_MASK; + d->buffer1 = dma_map_single(&vb->pdev->dev, vbb, + vb->framesize, DMA_TO_DEVICE); SET_OWNED(d); /* That's it until the hardware is done with it. */ atomic_inc(&dl->count); return 0; } - -static inline void* -vb_retrieve(struct voicebus *vb, struct voicebus_descriptor_list *dl) -{ - volatile struct voicebus_descriptor *d; - void *vbb; - unsigned int head = dl->head; - assert_in_vb_deferred(vb); - d = vb_descriptor(dl, head); - if (d->buffer1 && !OWNED(d)) { - dma_unmap_single(&vb->pdev->dev, d->buffer1, - vb->framesize, dl->direction); - vbb = dl->pending[head]; - dl->head = (++head) & DRING_MASK; - d->buffer1 = 0; - atomic_dec(&dl->count); - return vbb; - } else { - return NULL; - } -} - -/*! - * \brief Give a frame to the hardware to transmit. - * - */ -int -voicebus_transmit(struct voicebus *vb, void *vbb) -{ - return vb_submit(vb, &vb->txd, vbb); -} EXPORT_SYMBOL(voicebus_transmit); /*! @@ -779,7 +841,27 @@ EXPORT_SYMBOL(voicebus_transmit); static inline int vb_submit_rxb(struct voicebus *vb, void *vbb) { - return vb_submit(vb, &vb->rxd, vbb); + struct voicebus_descriptor *d; + struct voicebus_descriptor_list *dl = &vb->rxd; + unsigned int tail = dl->tail; + assert_in_vb_deferred(vb); + + d = vb_descriptor(dl, tail); + + if (unlikely(d->buffer1)) { + /* Do not overwrite a buffer that is still in progress. */ + WARN_ON(1); + voicebus_free(vb, vbb); + return -EBUSY; + } + + dl->pending[tail] = vbb; + dl->tail = (++tail) & DRING_MASK; + d->buffer1 = dma_map_single(&vb->pdev->dev, vbb, + vb->framesize, DMA_FROM_DEVICE); + SET_OWNED(d); /* That's it until the hardware is done with it. */ + atomic_inc(&dl->count); + return 0; } /*! @@ -798,13 +880,48 @@ vb_submit_rxb(struct voicebus *vb, void *vbb) static inline void * vb_get_completed_txb(struct voicebus *vb) { - return vb_retrieve(vb, &vb->txd); + struct voicebus_descriptor_list *dl = &vb->txd; + struct voicebus_descriptor *d; + void *vbb; + unsigned int head = dl->head; + assert_in_vb_deferred(vb); + d = vb_descriptor(dl, head); + + if (OWNED(d) || (d->buffer1 == vb->idle_vbb_dma_addr)) + return NULL; + + dma_unmap_single(&vb->pdev->dev, d->buffer1, + vb->framesize, DMA_TO_DEVICE); + + vbb = dl->pending[head]; + dl->head = (++head) & DRING_MASK; + d->buffer1 = vb->idle_vbb_dma_addr; + SET_OWNED(d); + atomic_dec(&dl->count); + return vbb; } static inline void * vb_get_completed_rxb(struct voicebus *vb) { - return vb_retrieve(vb, &vb->rxd); + struct voicebus_descriptor *d; + struct voicebus_descriptor_list *dl = &vb->rxd; + unsigned int head = dl->head; + void *vbb; + assert_in_vb_deferred(vb); + + d = vb_descriptor(dl, head); + + if ((0 == d->buffer1) || OWNED(d)) + return NULL; + + dma_unmap_single(&vb->pdev->dev, d->buffer1, + vb->framesize, DMA_FROM_DEVICE); + vbb = dl->pending[head]; + dl->head = (++head) & DRING_MASK; + d->buffer1 = 0; + atomic_dec(&dl->count); + return vbb; } /*! @@ -942,7 +1059,7 @@ voicebus_start(struct voicebus *vb) if (unlikely(NULL == vbb)) BUG_ON(1); else - vb->handle_transmit(vbb, vb->context); + handle_transmit(vb, vbb); } stop_vb_deferred(vb); @@ -1086,6 +1203,10 @@ voicebus_release(struct voicebus *vb) /* Cleanup memory and software resources. */ vb_free_descriptors(vb, &vb->txd); vb_free_descriptors(vb, &vb->rxd); + if (vb->idle_vbb_dma_addr) { + dma_free_coherent(&vb->pdev->dev, vb->framesize, + vb->idle_vbb, vb->idle_vbb_dma_addr); + } kmem_cache_destroy(vb->buffer_cache); release_region(vb->iobase, 0xff); pci_disable_device(vb->pdev); @@ -1094,103 +1215,295 @@ voicebus_release(struct voicebus *vb) EXPORT_SYMBOL(voicebus_release); static void -__vb_increase_latency(struct voicebus *vb) +vb_increase_latency(struct voicebus *vb, unsigned int increase) { - static int __warn_once = 1; void *vbb; - int latency; + int i; assert_in_vb_deferred(vb); - latency = atomic_read(&vb->txd.count); - if (DRING_SIZE == latency) { - if (__warn_once) { - /* We must subtract two from this number since there - * are always two buffers in the TX FIFO. - */ - dev_err(&vb->pdev->dev, - "ERROR: Unable to service card within %d ms " - "and unable to further increase latency.\n", - DRING_SIZE - 2); - __warn_once = 0; - } - } else { - /* Because there are 2 buffers in the transmit FIFO on the - * hardware, setting 3 ms of latency means that the host needs - * to be able to service the cards within 1ms. This is because - * the interface will load up 2 buffers into the TX FIFO then - * attempt to read the 3rd descriptor. If the OWN bit isn't - * set, then the hardware will set the TX descriptor not - * available interrupt. - */ - dev_info(&vb->pdev->dev, "Missed interrupt. " - "Increasing latency to %d ms in order to compensate.\n", - latency + 1); - /* Set the minimum latency in case we're restarted...we don't - * want to wait for the buffer to grow to this depth again in - * that case. - */ - voicebus_set_minlatency(vb, latency+1); + if (0 == increase) + return; + + if (unlikely(increase > VOICEBUS_MAXLATENCY_BUMP)) + increase = VOICEBUS_MAXLATENCY_BUMP; + + if ((increase + vb->min_tx_buffer_count) > vb->max_latency) + increase = vb->max_latency - vb->min_tx_buffer_count; + + /* Because there are 2 buffers in the transmit FIFO on the hardware, + * setting 3 ms of latency means that the host needs to be able to + * service the cards within 1ms. This is because the interface will + * load up 2 buffers into the TX FIFO then attempt to read the 3rd + * descriptor. If the OWN bit isn't set, then the hardware will set the + * TX descriptor not available interrupt. */ + + /* Set the minimum latency in case we're restarted...we don't want to + * wait for the buffer to grow to this depth again in that case. */ + for (i = 0; i < increase; ++i) { vbb = voicebus_alloc(vb); + WARN_ON(NULL == vbb); + if (likely(NULL != vbb)) + handle_transmit(vb, vbb); + } - if (unlikely(NULL == vbb)) - BUG_ON(1); - else - vb->handle_transmit(vbb, vb->context); + /* Set the new latency (but we want to ensure that there aren't any + * printks to the console, so we don't call the function) */ + spin_lock(&vb->lock); + vb->min_tx_buffer_count += increase; + spin_unlock(&vb->lock); +} + +static void vb_set_all_owned(struct voicebus *vb, + struct voicebus_descriptor_list *dl) +{ + int i; + struct voicebus_descriptor *d; + for (i = 0; i < DRING_SIZE; ++i) { + d = vb_descriptor(dl, i); + SET_OWNED(d); } } -/*! - * \brief Actually process the completed transmit and receive buffers. +static inline void vb_set_all_tx_owned(struct voicebus *vb) +{ + vb_set_all_owned(vb, &vb->txd); +} + +/** + * __vb_get_default_behind_count() - Returns how many idle buffers are loaded in tx fifo. + * + * These buffers are going to be set, but the AN983 does not clear the owned + * bit on the descriptors until they've actually been sent around. * - * NOTE: This function may be called either from a tasklet, workqueue, or - * directly in the interrupt service routine depending on - * VOICEBUS_DEFERRED. + * If you do not check for both the current and next descriptors, you could have + * a condition where idle buffers are being sent around, but we don't detect + * them because our current descriptor always points to a non-idle buffer. */ -static inline void -vb_deferred(struct voicebus *vb) +static unsigned int __vb_get_default_behind_count(const struct voicebus *vb) { - void *vbb; -#ifdef DBG - static int count; -#endif - int underrun = test_bit(TX_UNDERRUN, &vb->flags); + const struct voicebus_descriptor_list *const dl = &vb->txd; + const struct voicebus_descriptor *current_descriptor; + const struct voicebus_descriptor *next_descriptor; + + current_descriptor = vb_descriptor(dl, dl->head); + if (current_descriptor->buffer1 == vb->idle_vbb_dma_addr) + return 2; + + next_descriptor = vb_descriptor(dl, ((dl->head + 1) & DRING_MASK)); + if (next_descriptor->buffer1 == vb->idle_vbb_dma_addr) + return 1; + + return 0; +} + +/** + * vb_check_softunderrun() - Return true if the TX FIFO has underrun valid data. + * + * Returns true if we're processing the idle buffers, which means that the host + * was not able to keep up with the hardware. This differs from a normal + * underrun in that the interface chip on the board still has descriptors to + * transmit (just in this case, they are the idle buffers). + * + */ +static inline int vb_is_softunderrun(const struct voicebus *vb) +{ + return __vb_get_default_behind_count(vb) > 0; +} +/** + * vb_recover_tx_descriptor_list() - Recovers descriptor list + * + * Returns the number of descriptors that we're behind. + * + * Called if the [head | head + 1] pointer points to one of the idle buffers. + * This means that the host computer has failed to keep far enough ahead of the + * voicebus card. This function acks the completed idle descriptors and gets + * everything setup for normal operation again. + * + */ +static unsigned int vb_recover_tx_descriptor_list(struct voicebus *vb) +{ + struct voicebus_descriptor_list *const dl = &vb->txd; + struct voicebus_descriptor *d; + struct vbb *vbb = NULL; + unsigned int behind = __vb_get_default_behind_count(vb); + WARN_ON(0 == behind); + + d = vb_descriptor(dl, dl->head); + + /* If we're only behind by one descriptor, we need to wait for all + * descriptors to go owned so we're starting with a fresh slate. This + * will also means we send an additional idle buffer. */ + if (1 == behind) { + unsigned long stop; + stop = jiffies + HZ/100; + while (OWNED(d) && time_after(stop, jiffies)) + continue; + WARN_ON(time_before(stop, jiffies)); + WARN_ON(d->buffer1 == vb->idle_vbb_dma_addr); + WARN_ON(!dl->pending[dl->head]); + dma_unmap_single(&vb->pdev->dev, d->buffer1, + vb->framesize, DMA_TO_DEVICE); + vbb = dl->pending[dl->head]; + atomic_dec(&dl->count); + --behind; + } + + /* First complete any "idle" buffers that the hardware was to actually + * complete. We've already preloaded the behind variable for the idle + * buffers that are in progress but may not be complete. */ + while (!OWNED(d)) { + d->buffer1 = vb->idle_vbb_dma_addr; + dl->pending[dl->head] = vb->idle_vbb; + SET_OWNED(d); + dl->head = ++dl->head & DRING_MASK; + d = vb_descriptor(dl, dl->head); + ++behind; + } + + /* Next get a little further ahead, because the hardware will be + * currently working on one of the idle buffers that we can't detect is + * completed yet in the previous block. Set the head and tail pointers + * to this new position so that everything can pick up normally. */ + dl->tail = dl->head = (dl->head + 10) & DRING_MASK; + + if (NULL != vbb) + handle_transmit(vb, vbb); + + return behind; +} + +/** + * vb_deferred() - Manage the transmit and receive descriptor rings. + * + */ +static void vb_deferred(struct voicebus *vb) +{ + unsigned int buffer_count; + unsigned int i; + unsigned int idle_buffers; + int softunderrun; + unsigned int starting_latency; + + int underrun = test_bit(TX_UNDERRUN, &vb->flags); start_vb_deferred(vb); - if (unlikely(underrun)) { - /* When we've underrun our FIFO, for some reason we're not - * able to keep enough transmit descriptors pending. This can - * happen if either interrupts or this deferred processing - * function is not run soon enough (within 1ms when using the - * default 3 transmit buffers to start). In this case, we'll - * insert an additional transmit buffer onto the descriptor - * list which decreases the sensitivity to latency, but also - * adds more delay to the TDM and SPI data. - */ - __vb_increase_latency(vb); + + buffer_count = 0; + + /* First, temporarily store any non-idle buffers that the hardware has + * indicated it's finished transmitting. Non idle buffers are those + * buffers that contain actual data and was filled out by the client + * driver (as of this writing, the wcte12xp or wctdm24xxp drivers) when + * passed up through the handle_transmit callback. + * + * On the other hand, idle buffers are "dummy" buffers that solely exist + * to in order to prevent the transmit descriptor ring from ever + * completely draining. */ + while ((vb->vbb_stash[buffer_count] = vb_get_completed_txb(vb))) { + ++buffer_count; + if (unlikely(VOICEBUS_DEFAULT_MAXLATENCY < buffer_count)) { + dev_warn(&vb->pdev->dev, "Critical problem detected " + "in transmit ring descriptor\n"); + if (buffer_count >= DRING_SIZE) + buffer_count = DRING_SIZE - 1; + break; + } } - /* Always handle the transmit buffers first. */ - while ((vbb = vb_get_completed_txb(vb))) - vb->handle_transmit(vbb, vb->context); + vb->count += buffer_count; + + /* Next, check to see if we're in a softunderrun condition. + * + * A soft under is when the hardware has possibly copied at least one of + * the idle buffers into it's transmit queue. Since all the buffers are + * nearly always owned by the hardware, the way we detect this is by + * checking if either of the next two buffers that we expect to complete + * are idle buffers. We need to check the next two, because the + * hardware can read two buffers into it's tx fifo where they are + * definitely going to be sent on the interface, but the hardware does + * not flip the OWN bit on the descriptors until after the buffer has + * finished being sent to the CPLD. Therefore, just looking at the own + * bit and the buffer pointed to by the dl->head is not enough. + * + * NOTE: Do not print anything to the console from the time a soft + * underrun is detected until the transmit descriptors are fixed up + * again. Otherwise the hardware could advance past where you set the + * head and tail pointers and then eventually run into the desciptor + * that it was currently working on when the softunderrun condition was + * first hit. */ + if (unlikely(vb_is_softunderrun(vb))) { + softunderrun = 1; + /* If we experienced a soft underrun, the recover function will + * a) ensure that all non-idle buffers have been completely + * tranmitted by the hardware b) Reset any idle buffers that + * have been completely transmitted c) Reset the head and tail + * pointers to somwhere in advance of where the hardware is + * currently processing and d) return how many idle_buffers were + * transmitted before our interrupt handler was called. */ + idle_buffers = vb_recover_tx_descriptor_list(vb); + vb_increase_latency(vb, idle_buffers); + } else { + softunderrun = 0; + idle_buffers = 0; + starting_latency = 0; + } + /* Now we can process the completed non-idle buffers since we know at + * this point that the transmit descriptor is in a "good" state. */ + for (i = 0; i < buffer_count; ++i) + handle_transmit(vb, vb->vbb_stash[i]); + + /* If underrun is set, it means that the hardware signalled that it + * completely ran out of transmit descriptors. This is what we are + * trying to avoid with all this racy softunderun business, but alas, + * it's still possible to happen if interrupts are locked longer than + * DRING_SIZE milliseconds for some reason. We should have already fixed + * up the descriptor ring in this case, so let's just tell the hardware + * to reread what it believes the next descriptor is. */ if (unlikely(underrun)) { + if (printk_ratelimit()) { + dev_info(&vb->pdev->dev, "Host failed to service " + "card interrupt within %d ms which is a " + "hardunderun.\n", DRING_SIZE); + } vb_rx_demand_poll(vb); vb_tx_demand_poll(vb); clear_bit(TX_UNDERRUN, &vb->flags); } - while ((vbb = vb_get_completed_rxb(vb))) { - vb->handle_receive(vbb, vb->context); - vb_submit_rxb(vb, vbb); + /* Print any messages about soft latency bumps after we fix the transmit + * descriptor ring. Otherwise it's possible to take so much time + * printing the dmesg output that we lose the lead that we got on the + * hardware, resulting in a hard underrun condition. */ + if (unlikely(softunderrun) && printk_ratelimit()) { + if (vb->max_latency != vb->min_tx_buffer_count) { + dev_info(&vb->pdev->dev, "Missed interrupt. " + "Increasing latency to %d ms in order to " + "compensate.\n", vb->min_tx_buffer_count); + } else { + dev_info(&vb->pdev->dev, "ERROR: Unable to service " + "card within %d ms and unable to further " + "increase latency.\n", vb->max_latency); + } + } + + /* And finally, pass up any receive buffers. We also use vb->count to + * make a half-hearted attempt to not pass any recieved idle buffers to + * the caller, but this needs more work.... */ + while ((vb->vbb_stash[0] = vb_get_completed_rxb(vb))) { + if (vb->count) { + vb->handle_receive(vb->vbb_stash[0], vb->context); + --vb->count; + } + vb_submit_rxb(vb, vb->vbb_stash[0]); } stop_vb_deferred(vb); } - /*! * \brief Interrupt handler for VoiceBus interface. * @@ -1358,11 +1671,12 @@ voicebus_init(struct pci_dev *pdev, u32 framesize, goto cleanup; } memset(vb, 0, sizeof(*vb)); + vb->pdev = pdev; voicebus_setdebuglevel(vb, debuglevel); + vb->max_latency = VOICEBUS_DEFAULT_MAXLATENCY; spin_lock_init(&vb->lock); init_completion(&vb->stopped_completion); - vb->pdev = pdev; set_bit(STOP, &vb->flags); clear_bit(IN_DEFERRED_PROCESSING, &vb->flags); vb->framesize = framesize; @@ -1404,9 +1718,15 @@ voicebus_init(struct pci_dev *pdev, u32 framesize, SLAB_HWCACHE_ALIGN, NULL, NULL); #endif #else +#ifdef DEBUG + vb->buffer_cache = kmem_cache_create(board_name, vb->framesize, 0, + SLAB_HWCACHE_ALIGN | SLAB_STORE_USER | + SLAB_POISON | SLAB_DEBUG_FREE, NULL); +#else vb->buffer_cache = kmem_cache_create(board_name, vb->framesize, 0, SLAB_HWCACHE_ALIGN, NULL); #endif +#endif if (NULL == vb->buffer_cache) { dev_err(&vb->pdev->dev, "Failed to allocate buffer cache.\n"); goto cleanup; @@ -1416,6 +1736,11 @@ voicebus_init(struct pci_dev *pdev, u32 framesize, /* ---------------------------------------------------------------- Configure the hardware / kernel module interfaces. ---------------------------------------------------------------- */ + if (pci_set_dma_mask(vb->pdev, DMA_BIT_MASK(32))) { + dev_err(&vb->pdev->dev, "No suitable DMA available.\n"); + goto cleanup; + } + if (pci_read_config_byte(vb->pdev, 0x0c, &vb->cache_line_size)) { dev_err(&vb->pdev->dev, "Failed read of cache line " "size from PCI configuration space.\n"); @@ -1443,6 +1768,9 @@ voicebus_init(struct pci_dev *pdev, u32 framesize, goto cleanup; } + vb->idle_vbb = dma_alloc_coherent(&vb->pdev->dev, vb->framesize, + &vb->idle_vbb_dma_addr, GFP_KERNEL); + retval = vb_initialize_tx_descriptors(vb); if (retval) goto cleanup; @@ -1493,6 +1821,9 @@ cleanup: if (vb->rxd.desc) vb_free_descriptors(vb, &vb->rxd); + dma_free_coherent(&vb->pdev->dev, vb->framesize, + vb->idle_vbb, vb->idle_vbb_dma_addr); + if (vb->buffer_cache) kmem_cache_destroy(vb->buffer_cache); |