summaryrefslogtreecommitdiff
path: root/drivers/dahdi/voicebus/voicebus.c
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2009-11-09 19:32:45 +0000
committerShaun Ruffell <sruffell@digium.com>2009-11-09 19:32:45 +0000
commitdcf556223a1443d56f633a83325e2a5950a3e724 (patch)
treea92357cac4314ed91b17ab60341e447d98c835df /drivers/dahdi/voicebus/voicebus.c
parent0ee3bc8670599c0dfce468f33a4082818f9343db (diff)
Merged revisions 6699,6706,6714,6768,6771,6785,6812-6818,6821,6838,6928-6929,6941,6945-6946,6952,6981-6982,7003-7004,7008,7023,7027,7094-7097,7110,7117-7118,7125,7140,7147,7155,7194,7284,7293,7319,7437-7438,7445,7480,7486-7487,7512-7524,7527-7528,7534-7536 via svnmerge from
https://origsvn.digium.com/svn/dahdi/linux/trunk ........ r6699 | kpfleming | 2009-06-23 15:32:01 -0500 (Tue, 23 Jun 2009) | 3 lines Use the same mutex lock for channel allocation and license checking in dahdi_echocan_hpec, so that channel allocation won't happen while the license is being checked (or rechecked) ........ r6706 | sruffell | 2009-06-23 18:21:25 -0500 (Tue, 23 Jun 2009) | 1 line README: Minor corrections to the README. ........ r6714 | twilson | 2009-06-24 15:23:07 -0500 (Wed, 24 Jun 2009) | 1 line Remove unused torisa code from header ........ r6768 | sruffell | 2009-06-26 12:07:39 -0500 (Fri, 26 Jun 2009) | 1 line wct4xxp: Unmap the same size DMA window that was mapped. ........ r6771 | sruffell | 2009-06-26 12:22:45 -0500 (Fri, 26 Jun 2009) | 6 lines dahdi_dynamic: Release the dlock before calling accross modules. Resolves a hard lock due to a recursive spinlock grab at startup. Reported by: mapacheco (closes issue #15210) ........ r6785 | sruffell | 2009-06-28 23:47:26 -0500 (Sun, 28 Jun 2009) | 5 lines echocan: Properly keep the reference counts for the echocan modules. (closes issue #13504) (closes issue #15327) Reported by: sruffell, tzafrir ........ r6812 | tzafrir | 2009-07-02 14:25:54 -0500 (Thu, 02 Jul 2009) | 5 lines wcb4xxp: Don't assume we have 4 spans (ports). First part of extra hfcmulti drivers: the number of ports is still hardwired to 4, but just in a single place. ........ r6813 | tzafrir | 2009-07-02 14:34:05 -0500 (Thu, 02 Jul 2009) | 4 lines wcb4xxp: Extra bit macros that a needed for kernels < 2.6.24 Macros borrowed from xpp/ . ........ r6814 | tzafrir | 2009-07-02 14:42:25 -0500 (Thu, 02 Jul 2009) | 2 lines wcb4xxp: Adjust debug filter code to number of ports. ........ r6815 | tzafrir | 2009-07-02 14:52:14 -0500 (Thu, 02 Jul 2009) | 6 lines wcb4xxp: support for other HFC-xS cards (info, not code) This commit includes skeleton for the support of other HFC-[248]S-based cards. It still does not include all the different cases for different cards. ........ r6816 | tzafrir | 2009-07-02 14:59:44 -0500 (Thu, 02 Jul 2009) | 2 lines The B410P differs from other HFC-xS cards with respect to the EC unit ........ r6817 | tzafrir | 2009-07-02 15:07:36 -0500 (Thu, 02 Jul 2009) | 7 lines More B410P differences: Clock and NT/TE. * The B410P reads the NT/TE switches the other way around from other cards. * Its clock is also 1/2 of that of other cards, which causes wierd PCM on an unmodified driver. ........ r6818 | tzafrir | 2009-07-02 15:14:39 -0500 (Thu, 02 Jul 2009) | 4 lines wcb4xxp: Fix PCM handling for various cards. HFC-8S cards behave quite differently than HFC-4S cards here. ........ r6821 | tzafrir | 2009-07-02 15:18:59 -0500 (Thu, 02 Jul 2009) | 6 lines wcb4xxp: Fix LED handling in OpenVox cards (maybe also others) This commit adds extra functions to handle LEDs in the non-B410P cards. Only tested on OpenVox cards. OpenVox cards are known to have slightly different LEDs so this is likely to be slightly broken for others. ........ r6838 | sruffell | 2009-07-13 09:33:39 -0500 (Mon, 13 Jul 2009) | 1 line wctc4xxp: Remove deprecated DMA_xxBIT_MASK usage. ........ r6928 | sruffell | 2009-08-04 11:22:23 -0500 (Tue, 04 Aug 2009) | 1 line wcte12xp: Remove unused 'schluffen' declaration in wcte12xp.h ........ r6929 | sruffell | 2009-08-04 11:22:26 -0500 (Tue, 04 Aug 2009) | 4 lines wctdm24xxp: Eliminate 'variety' and 'type' members from 'struct wctdm'. struct wctdm can hold a pointer to struct wctdm_desc directly, and eliminate the need to copy members of wctdm_desc into wctdm. ........ r6941 | dbailey | 2009-08-05 09:40:45 -0500 (Wed, 05 Aug 2009) | 14 lines Change proslic linefeed register setting Insure that proslic linefeed register is not transitioned from Active to On-Hook Transmission while the channel is off-hook. Replaced magic numbers assigned to linefeed associated variables with more descriptive constants. (issue #15352) Reported by: alecdavis Patches: wctdm_prevent_ohttimer_click.diff3.txt uploaded by dbailey (license 819) Tested by: alecdavis, dbailey, vmikhelson ........ r6945 | sruffell | 2009-08-05 14:39:10 -0500 (Wed, 05 Aug 2009) | 6 lines wctdm24xxp, wctdm: Formatting changes. Update the lines affected by revision 6941. I'm taking every opportunity to move DAHDI closer to the kernel coding conventions short of just reformatting for the sake of reformatting. The majority of these changes are to bring the line lengths under 80 chars. ........ r6946 | sruffell | 2009-08-05 14:39:13 -0500 (Wed, 05 Aug 2009) | 1 line dahdi_dummy: Remove some trailing whitespace. ........ r6952 | sruffell | 2009-08-11 13:47:21 -0500 (Tue, 11 Aug 2009) | 3 lines wctdm24xxp: Remove unused members related to hardware DTMF detection. Not used anymore, so they are gone. ........ r6981 | sruffell | 2009-08-13 09:42:05 -0500 (Thu, 13 Aug 2009) | 1 line wctc4xxp: Remove flag member that is not used. ........ r6982 | sruffell | 2009-08-13 09:42:08 -0500 (Thu, 13 Aug 2009) | 1 line wctdm24xxp: Use the ARRAY_SIZE macro where appropriate. ........ r7003 | sruffell | 2009-08-13 19:46:26 -0500 (Thu, 13 Aug 2009) | 9 lines wctdm24xxp, wctdm: Detect if our hookstate has been set back to the initial state. Check if our hookstate has been set back to the initial state, typically the result of a chanconfig, and if so, if we're an FXO port, forget our current battery state. This allows the driver to determine and report again what the hook state of the port is. (related to issue #14577) (closes issue #15429) ........ r7004 | sruffell | 2009-08-13 19:46:28 -0500 (Thu, 13 Aug 2009) | 1 line dahdi-base: Add comment to explain why rxhooksig is reset on span start. ........ r7008 | sruffell | 2009-08-14 10:47:39 -0500 (Fri, 14 Aug 2009) | 11 lines wcfxo: Reset the DAA on module initialization. The X100p and clones will sometimes work and sometimes not depending on wether the DAA powers up in running state- this seems to be related to the power supply. This problem is caused by the driver not reseting the DAA and may be the source of a great many intermittent problems with this card. (closes issue #14232) Reported by: tallen8840 Patch by: tallen8840 Tested by: explidous, Flavio ........ r7023 | sruffell | 2009-08-17 09:07:06 -0500 (Mon, 17 Aug 2009) | 4 lines README: Update known issues section. Remove note about echocanceler reference counts, since that is fixed, and add a note about issue with KB1 when configured with more than 128 taps. ........ r7027 | seanbright | 2009-08-17 14:31:54 -0500 (Mon, 17 Aug 2009) | 7 lines Silence spurious warnings when trying to remove Zaptel directories during install. (closes issue #15479) Reported by: pprindeville Patches: dahdi-linux-rm.patch uploaded by pprindeville (license 347) ........ r7094 | sruffell | 2009-09-07 16:40:19 -0500 (Mon, 07 Sep 2009) | 4 lines wctdm24xxp: Remove a few more unneeded 'volatile' keywords. The writechunk and readchunk parameters are never accessed by hardware at the same time that the software is accessing them anymore. ........ r7095 | sruffell | 2009-09-07 16:40:22 -0500 (Mon, 07 Sep 2009) | 5 lines dahdi-base: Reduce the stack usage of dahdi_common_ioctl. Split the DAHDI_GETGAINS and DAHDI_SETGAINS ioctls into their own functions and dynamically allocate the 'struct dahdi_gains' structure to reduce the pressure on the stack. ........ r7096 | sruffell | 2009-09-07 16:40:25 -0500 (Mon, 07 Sep 2009) | 1 line dahdi-base: Prevent compilation if both EMPULSE and EMFLASH are defined. ........ r7097 | sruffell | 2009-09-07 16:40:29 -0500 (Mon, 07 Sep 2009) | 4 lines dahdi-base: Fix flag check in dahdi_rbs_sethook. As long as any of the flags were set, this check would have always passed. ........ r7110 | rmeyerriecks | 2009-09-14 15:30:24 -0500 (Mon, 14 Sep 2009) | 1 line Fixed issue where the clear channel flags were not being set at the appropriate time causing a channel re-configure to mis-set the last channel in each span ........ r7117 | dbailey | 2009-09-14 15:51:56 -0500 (Mon, 14 Sep 2009) | 17 lines Race condition in handling writes to proslic LINEFEED register (64) The wctdm24xxp driver has a problem where a VMWI IOCTL call followed immediately by a ONHOOKTRANSFER IOCTL call will cause the ONHOOK transfer request to be dropped. This occurs if the write to the proslic's LINEFEED register for the VMWI ICTL call is not completed when the ONHOOK transfer request IOCTL is processed. I also cleaned out some magic numbers used in setting the linefeed register. (closes issue #15875) Reported by: dbailey Patches: 15875-wctdm24xxp.diff uploaded by dbailey (license 819) Tested by: dbailey ........ r7118 | seanbright | 2009-09-14 16:10:38 -0500 (Mon, 14 Sep 2009) | 1 line Change zap -> dahdi. ........ r7125 | rmeyerriecks | 2009-09-15 09:59:06 -0500 (Tue, 15 Sep 2009) | 2 lines dahdi-base: Minor syntax change to meet style guidelines ........ r7140 | dbailey | 2009-09-15 15:50:45 -0500 (Tue, 15 Sep 2009) | 12 lines Change WCTDM SPI clock off state polarity and read timing Change the off state of the SPI clock to high and provide more time for data to settle out on SPI reads. (closes issue #15261) Reported by: alecdavis Patches: wctdm_spi_clocking.diff2.txt uploaded by alecdavis (license 585) Tested by: alecdavis, dbailey ........ r7147 | sruffell | 2009-09-16 13:19:00 -0500 (Wed, 16 Sep 2009) | 4 lines wct4xxp: Check the alarm state if we're debouncing a red alarm. This fixes a problem where if you set the alarmdebounce module parameter on gen2+ cards, you never detect when you go into red alarm. ........ r7155 | sruffell | 2009-09-21 10:24:36 -0500 (Mon, 21 Sep 2009) | 8 lines dahdi-base: dahdi_ioctl_[get|set]gains should return the res value. In function dahdi_ioctl_getgains() and dahdi_ioctl_setgains() return value assigned to res variable, but these function always return 0 which is an error. (closes issue #15916.) Patch by: ys ........ r7194 | dbailey | 2009-09-22 09:03:53 -0500 (Tue, 22 Sep 2009) | 12 lines wctdm: Add missing break A break was missing that caused DAHDI_ONHOOKTRANSFER ioctl call to fall into DAHDI_SETPOLARITY ioctl call. (issue #14261) Reported by: alecdavis Patches: wctdm_fix_ONHOOKTRANSFER.diff.txt uploaded by alecdavis (license 585) Tested by: alecdavis ........ r7284 | mattf | 2009-09-30 11:34:11 -0500 (Wed, 30 Sep 2009) | 1 line Update echocan API so it only uses channel offset in free routine ........ r7293 | tzafrir | 2009-09-30 13:09:42 -0500 (Wed, 30 Sep 2009) | 2 lines move the dev->bus_id fix from xpp to kernel.h: needed elsewhere ........ r7319 | sruffell | 2009-10-02 16:09:01 -0500 (Fri, 02 Oct 2009) | 1 line wcte12xp: The timer is called every 200ms, not every 100ms. Fix comment. ........ r7437 | sruffell | 2009-10-29 13:26:16 -0500 (Thu, 29 Oct 2009) | 10 lines dahdi-base: Do not allow jumps in system time to lock up the system w/core_timer Since dahdi coretimer uses the number of milliseconds that has actually passed to determine how many times to call dahdi_receive, it is possible that if the system time shifts after dahdi is started, that the system can appear to lock up while the core timer attempts to catch up. This change prevents soft lock ups under these conditions. This is brings the dahdi_dummy changes in r6933 into dahdi-base. (related to issue #15647) ........ r7438 | sruffell | 2009-10-29 13:26:17 -0500 (Thu, 29 Oct 2009) | 1 line wcte12xp, wctdm24xxp: VPMADT032 firmware update to 1.20. ........ r7445 | mspiceland | 2009-10-29 16:37:45 -0500 (Thu, 29 Oct 2009) | 3 lines Debounce alarms by default for wct4xxp per AT&T 54016. Also, the various alarm conditions can be debounced separately. ........ r7480 | sruffell | 2009-11-04 14:43:05 -0600 (Wed, 04 Nov 2009) | 4 lines voicebus: Increase the NLP converged threshold to 18. Brings in the change from r7065 that was on the team/sruffell/dahdi-linux-vpm119 branch. ........ r7486 | mspiceland | 2009-11-04 17:25:32 -0600 (Wed, 04 Nov 2009) | 9 lines Adding alarm debounce to single span driver (wcte12xp). Debounce yellow alarm also. In wcte12xp, change check alarm frequency to 100ms for better debounce granularity. Fix lines over 80 cols from last alarm debounce commit. ........ r7487 | mspiceland | 2009-11-04 17:28:21 -0600 (Wed, 04 Nov 2009) | 2 lines Remove commented out code block that was unintentionally left in. ........ r7512 | sruffell | 2009-11-06 18:35:38 -0600 (Fri, 06 Nov 2009) | 1 line wcte12xp: Remove unused flag member and make const the t1_descs. ........ r7513 | sruffell | 2009-11-06 18:35:38 -0600 (Fri, 06 Nov 2009) | 4 lines voicebus: Remove the VB_PRINTK macro. Unnecessarily duplicates the dev_xxx macros. Also removes the need for the board_name member from struct voicebus. ........ r7514 | sruffell | 2009-11-06 18:35:39 -0600 (Fri, 06 Nov 2009) | 4 lines voicebus: Remove sdi member from 'struct voicebus' This is only used during startup so we don't need to carry it around in the structure at all times. ........ r7515 | sruffell | 2009-11-06 18:35:40 -0600 (Fri, 06 Nov 2009) | 1 line voicebus: Set the DMA_BIT_MASK ........ r7516 | sruffell | 2009-11-06 18:35:40 -0600 (Fri, 06 Nov 2009) | 1 line voicebus: Use DAHDI_IRQ_SHARED instead of defining our own. ........ r7517 | sruffell | 2009-11-06 18:35:40 -0600 (Fri, 06 Nov 2009) | 24 lines voicebus: Send 'idle' buffers when the transmit descriptor underruns. Previously, when the host system fails to service the interrupt in a timely fashion, the transmit descriptor ring for the voicebus card would "go empty" since the interface wouldn't have another descriptor to read in. The driver only knows that it went empty, not how far behind it actually was. Therefore, the driver could just increase the latency by a millisecond and keep going waiting for another bump. Additionally, when the transmit descriptor actually goes empty, there are some cases where an in process SPI transaction to one of the modules is interrupted, which may result in corrupted module register writes on rare occassions. This now makes it possible for the voicebus drivers to coexist with some devices that periodically lock interrupts for longer than 25ms. Before this patch, the latency would constantly increase until either the modules received a corrupted frame. This patch preconfigures all the receive descriptors to send an "idle" packet that will be transmitted to the onboard modules when the host doesn't service the interrupt within (latency - 2)ms. There are now two kinds of underruns, softunderuns where the driver can detect that these idlebuffers have made it to the TX FIFO, and the normal hard underrun where the part signals a transmit descriptor unavailable interrupt. DAHDI-278. ........ r7518 | sruffell | 2009-11-06 18:35:41 -0600 (Fri, 06 Nov 2009) | 7 lines voicebus: Add function to lock the latency. Now that increases in the latency produce less undefined behavior on the SPI busses, provide an interface for client drivers to inform the voicebus library to not increase the latency if underruns are detected. This can speed up loads of the driver since latency bumps do not trigger a restart of the driver initialization. DAHDI-278. ........ r7519 | sruffell | 2009-11-06 18:35:42 -0600 (Fri, 06 Nov 2009) | 4 lines wcte12xp: Lock latency when loading No longer need to restart board initialization if the latency would have increased during initialization. DAHDI-278. ........ r7520 | sruffell | 2009-11-06 18:35:42 -0600 (Fri, 06 Nov 2009) | 4 lines wctdm24xxp: Lock latency when loading We no longer need to retry board initialization if the latency would have increased during the initialization. DAHDI-278 ........ r7521 | sruffell | 2009-11-06 18:35:43 -0600 (Fri, 06 Nov 2009) | 4 lines voicebus, wctdm24xxp, wcte12xp: Move a print out of the interrupt handler. This can be handled just as well in process context and printing to a serial console from the interrupt handler has the potential to cause long latencies. ........ r7522 | sruffell | 2009-11-06 18:35:44 -0600 (Fri, 06 Nov 2009) | 5 lines voicebus: Add optional sysfs entry for reading a boards current latency. This is off by default since it hasn't been tested on a full range of kernels, but can be useful for quickly seeing differences for latencies on different cards installed in the system. ........ r7523 | sruffell | 2009-11-06 18:35:44 -0600 (Fri, 06 Nov 2009) | 1 line voicebus: Remove 'assert' macros and use BUG_ON/WARN_ON directly. ........ r7524 | sruffell | 2009-11-06 18:35:45 -0600 (Fri, 06 Nov 2009) | 1 line voicebus: Be just a little more graceful if we cannot grab our interrupt line. ........ r7527 | sruffell | 2009-11-06 18:58:03 -0600 (Fri, 06 Nov 2009) | 1 line wcte12xp: Fix up some continued strings. ........ r7528 | sruffell | 2009-11-06 18:58:03 -0600 (Fri, 06 Nov 2009) | 1 line wct4xxp: Fix up some continued strings. ........ r7534 | sruffell | 2009-11-09 12:02:40 -0600 (Mon, 09 Nov 2009) | 1 line wct4xxp: Only print the new debounce messages when debug is set. ........ r7535 | sruffell | 2009-11-09 12:02:41 -0600 (Mon, 09 Nov 2009) | 1 line wcte12xp: Only print the new debounce messages when debug is set. ........ r7536 | sruffell | 2009-11-09 12:11:06 -0600 (Mon, 09 Nov 2009) | 4 lines wcte12xp: use the dev_xxx macro for the debounce messages. We want to know which device is reporting the debounce when there are more than one card in the system. ........ git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/branches/2.2@7539 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'drivers/dahdi/voicebus/voicebus.c')
-rw-r--r--drivers/dahdi/voicebus/voicebus.c848
1 files changed, 605 insertions, 243 deletions
diff --git a/drivers/dahdi/voicebus/voicebus.c b/drivers/dahdi/voicebus/voicebus.c
index d794365..70a1128 100644
--- a/drivers/dahdi/voicebus/voicebus.c
+++ b/drivers/dahdi/voicebus/voicebus.c
@@ -41,8 +41,6 @@
#include "vpmadtreg.h"
#include "GpakCust.h"
-#define assert(__x__) BUG_ON(!(__x__))
-
#define INTERRUPT 0 /* Run the deferred processing in the ISR. */
#define TASKLET 1 /* Run in a tasklet. */
#define TIMER 2 /* Run in a system timer. */
@@ -56,6 +54,11 @@
#define VOICEBUS_ALLOC_FLAGS GFP_ATOMIC
#endif
+/* Define CONFIG_VOICEBUS_SYSFS to create some attributes under the pci device.
+ * This is disabled by default because it hasn't been tested on the full range
+ * of supported kernels. */
+#undef CONFIG_VOICEBUS_SYSFS
+
#if VOICEBUS_DEFERRED == TIMER
#if HZ < 1000
/* \todo Put an error message here. */
@@ -63,7 +66,7 @@
#endif
/*! The number of descriptors in both the tx and rx descriptor ring. */
-#define DRING_SIZE (1 << 5) /* Must be a power of 2 */
+#define DRING_SIZE (1 << 7) /* Must be a power of 2 */
#define DRING_MASK (DRING_SIZE-1)
/* Interrupt status' reported in SR_CSR5 */
@@ -110,10 +113,10 @@
/* In memory structure shared by the host and the adapter. */
struct voicebus_descriptor {
- u32 des0;
- u32 des1;
- u32 buffer1;
- u32 container; /* Unused */
+ volatile __le32 des0;
+ volatile __le32 des1;
+ volatile __le32 buffer1;
+ volatile __le32 container; /* Unused */
} __attribute__((packed));
struct voicebus_descriptor_list {
@@ -127,20 +130,22 @@ struct voicebus_descriptor_list {
void *pending[DRING_SIZE];
/* PCI Bus address of the descriptor list. */
dma_addr_t desc_dma;
- /*! either DMA_FROM_DEVICE or DMA_TO_DEVICE */
- unsigned int direction;
/*! The number of buffers currently submitted to the hardware. */
atomic_t count;
/*! The number of bytes to pad each descriptor for cache alignment. */
unsigned int padding;
};
-
-/*! * \brief Represents a VoiceBus interface on a Digium telephony card.
+/**
+ * struct voicebus -
+ *
+ * @tx_idle_vbb:
+ * @tx_idle_vbb_dma_addr:
+ * @max_latency: Do not allow the driver to automatically insert more than this
+ * much latency to the tdm stream by default.
+ * @count: The number of non-idle buffers that we should be expecting.
*/
struct voicebus {
- /*! Name of this card. */
- const char *board_name;
/*! The system pci device for this VoiceBus interface. */
struct pci_dev *pdev;
/*! Protects access to card registers and this structure. You should
@@ -154,6 +159,8 @@ struct voicebus {
/*! Pool to allocate memory for the tx and rx descriptor rings. */
struct voicebus_descriptor_list rxd;
struct voicebus_descriptor_list txd;
+ void *idle_vbb;
+ dma_addr_t idle_vbb_dma_addr;
/*! Level of debugging information. 0=None, 5=Insane. */
atomic_t debuglevel;
/*! Cache of buffer objects. */
@@ -186,12 +193,18 @@ struct voicebus {
struct completion stopped_completion;
/*! Flags */
unsigned long flags;
- /* \todo see about removing this... */
- u32 sdi;
/*! Number of tx buffers to queue up before enabling interrupts. */
unsigned int min_tx_buffer_count;
+ unsigned int max_latency;
+ void *vbb_stash[DRING_SIZE];
+ unsigned int count;
};
+static inline void handle_transmit(struct voicebus *vb, void *vbb)
+{
+ vb->handle_transmit(vbb, vb->context);
+}
+
/*
* Use the following macros to lock the VoiceBus interface, and it won't
* matter if the deferred processing is running inside the interrupt handler,
@@ -218,15 +231,13 @@ struct voicebus {
#define VBUNLOCK_FROM_DEFERRED(_vb_) spin_lock(&((_vb_)->lock))
#endif
-#define VB_PRINTK(_vb, _lvl, _fmt, _args...) \
- printk(KERN_##_lvl "%s: " _fmt, (_vb)->board_name, ## _args)
-
/* Bit definitions for struct voicebus.flags */
#define TX_UNDERRUN 1
#define RX_UNDERRUN 2
#define IN_DEFERRED_PROCESSING 3
#define STOP 4
#define STOPPED 5
+#define LATENCY_LOCKED 6
#if VOICEBUS_DEFERRED == WORKQUEUE
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
@@ -257,32 +268,9 @@ vb_set_workqueue_priority(struct voicebus *vb)
#endif
#endif
-#ifdef DBG
-static inline int
-assert_in_vb_deferred(struct voicebus *vb)
-{
- assert(test_bit(IN_DEFERRED_PROCESSING, &vb->flags));
-}
-
-static inline void
-start_vb_deferred(struct voicebus *vb)
-{
- set_bit(IN_DEFERRED_PROCESSING, &vb->flags);
-}
-
-static inline void
-stop_vb_deferred(struct voicebus *vb)
-{
- clear_bit(IN_DEFERRED_PROCESSING, &vb->flags);
-}
-#else
-#define assert_in_vb_deferred(_x_) do {; } while (0)
-#define start_vb_deferred(_x_) do {; } while (0)
-#define stop_vb_deferred(_x_) do {; } while (0)
-#endif
-
static inline struct voicebus_descriptor *
-vb_descriptor(struct voicebus_descriptor_list *dl, int index)
+vb_descriptor(const struct voicebus_descriptor_list *dl,
+ const unsigned int index)
{
struct voicebus_descriptor *d;
d = (struct voicebus_descriptor *)((u8*)dl->desc +
@@ -298,7 +286,7 @@ vb_initialize_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *
struct voicebus_descriptor *d;
const u32 END_OF_RING = 0x02000000;
- assert(dl);
+ BUG_ON(!dl);
/*
* Add some padding to each descriptor to ensure that they are
@@ -324,16 +312,55 @@ vb_initialize_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *
d->des1 = des1;
}
d->des1 |= cpu_to_le32(END_OF_RING);
- dl->direction = direction;
atomic_set(&dl->count, 0);
return 0;
}
+#define OWNED(_d_) (((_d_)->des0)&OWN_BIT)
+#define SET_OWNED(_d_) do { wmb(); (_d_)->des0 |= OWN_BIT; wmb(); } while (0)
+
static int
vb_initialize_tx_descriptors(struct voicebus *vb)
{
- return vb_initialize_descriptors(
- vb, &vb->txd, 0xe4800000 | vb->framesize, DMA_TO_DEVICE);
+ int i;
+ int des1 = 0xe4800000 | vb->framesize;
+ struct voicebus_descriptor *d;
+ struct voicebus_descriptor_list *dl = &vb->txd;
+ const u32 END_OF_RING = 0x02000000;
+
+ WARN_ON(!dl);
+ WARN_ON((NULL == vb->idle_vbb) || (0 == vb->idle_vbb_dma_addr));
+
+ /*
+ * Add some padding to each descriptor to ensure that they are
+ * aligned on host system cache-line boundaries, but only for the
+ * cache-line sizes that we support.
+ *
+ */
+ if ((0x08 == vb->cache_line_size) || (0x10 == vb->cache_line_size) ||
+ (0x20 == vb->cache_line_size)) {
+ dl->padding = (vb->cache_line_size*sizeof(u32)) - sizeof(*d);
+ } else {
+ dl->padding = 0;
+ }
+
+ dl->desc = pci_alloc_consistent(vb->pdev,
+ (sizeof(*d) + dl->padding) *
+ DRING_SIZE, &dl->desc_dma);
+ if (!dl->desc)
+ return -ENOMEM;
+
+ memset(dl->desc, 0, (sizeof(*d) + dl->padding) * DRING_SIZE);
+ for (i = 0; i < DRING_SIZE; ++i) {
+ d = vb_descriptor(dl, i);
+ d->des1 = des1;
+ d->buffer1 = vb->idle_vbb_dma_addr;
+ dl->pending[i] = vb->idle_vbb;
+ SET_OWNED(d);
+ }
+ d->des1 |= cpu_to_le32(END_OF_RING);
+ atomic_set(&dl->count, 0);
+ return 0;
}
static int
@@ -358,10 +385,10 @@ voicebus_set_minlatency(struct voicebus *vb, unsigned int ms)
*/
#define MESSAGE "%d ms is an invalid value for minumum latency. Setting to %d ms.\n"
if (DRING_SIZE < ms) {
- VB_PRINTK(vb, WARNING, MESSAGE, ms, DRING_SIZE);
+ dev_warn(&vb->pdev->dev, MESSAGE, ms, DRING_SIZE);
return -EINVAL;
} else if (VOICEBUS_DEFAULT_LATENCY > ms) {
- VB_PRINTK(vb, WARNING, MESSAGE, ms, VOICEBUS_DEFAULT_LATENCY);
+ dev_warn(&vb->pdev->dev, MESSAGE, ms, VOICEBUS_DEFAULT_LATENCY);
return -EINVAL;
}
VBLOCK(vb);
@@ -419,6 +446,36 @@ voicebus_current_latency(struct voicebus *vb)
}
EXPORT_SYMBOL(voicebus_current_latency);
+/**
+ * voicebus_lock_latency() - Do not increase the latency during underruns.
+ *
+ */
+void voicebus_lock_latency(struct voicebus *vb)
+{
+ set_bit(LATENCY_LOCKED, &vb->flags);
+}
+EXPORT_SYMBOL(voicebus_lock_latency);
+
+/**
+ * voicebus_unlock_latency() - Bump up the latency during underruns.
+ *
+ */
+void voicebus_unlock_latency(struct voicebus *vb)
+{
+ clear_bit(LATENCY_LOCKED, &vb->flags);
+}
+EXPORT_SYMBOL(voicebus_unlock_latency);
+
+/**
+ * voicebus_is_latency_locked() - Return 1 if latency is currently locked.
+ *
+ */
+int voicebus_is_latency_locked(const struct voicebus *vb)
+{
+ return test_bit(LATENCY_LOCKED, &vb->flags);
+}
+EXPORT_SYMBOL(voicebus_is_latency_locked);
+
/*!
* \brief Read one of the hardware control registers without acquiring locks.
*/
@@ -460,18 +517,48 @@ vb_is_stopped(struct voicebus *vb)
}
static void
-vb_cleanup_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl)
+vb_cleanup_tx_descriptors(struct voicebus *vb)
+{
+ unsigned int i;
+ struct voicebus_descriptor_list *dl = &vb->txd;
+ struct voicebus_descriptor *d;
+
+ BUG_ON(!vb_is_stopped(vb));
+
+ for (i = 0; i < DRING_SIZE; ++i) {
+ d = vb_descriptor(dl, i);
+ if (d->buffer1 && (d->buffer1 != vb->idle_vbb_dma_addr)) {
+ WARN_ON(!dl->pending[i]);
+ dma_unmap_single(&vb->pdev->dev, d->buffer1,
+ vb->framesize, DMA_TO_DEVICE);
+ voicebus_free(vb, dl->pending[i]);
+ }
+ d->buffer1 = vb->idle_vbb_dma_addr;
+ dl->pending[i] = vb->idle_vbb;
+ SET_OWNED(d);
+ }
+ /* Send out two idle buffers to start because sometimes the first buffer
+ * doesn't make it back to us. */
+ dl->head = dl->tail = 2;
+ atomic_set(&dl->count, 0);
+}
+
+static void
+vb_cleanup_rx_descriptors(struct voicebus *vb)
{
unsigned int i;
+ struct voicebus_descriptor_list *dl = &vb->rxd;
struct voicebus_descriptor *d;
- assert(vb_is_stopped(vb));
+ BUG_ON(!vb_is_stopped(vb));
for (i = 0; i < DRING_SIZE; ++i) {
d = vb_descriptor(dl, i);
if (d->buffer1) {
+ dma_unmap_single(&vb->pdev->dev, d->buffer1,
+ vb->framesize, DMA_FROM_DEVICE);
d->buffer1 = 0;
- assert(dl->pending[i]);
+ BUG_ON(!dl->pending[i]);
voicebus_free(vb, dl->pending[i]);
dl->pending[i] = NULL;
}
@@ -482,6 +569,15 @@ vb_cleanup_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl)
atomic_set(&dl->count, 0);
}
+static void vb_cleanup_descriptors(struct voicebus *vb,
+ struct voicebus_descriptor_list *dl)
+{
+ if (dl == &vb->txd)
+ vb_cleanup_tx_descriptors(vb);
+ else
+ vb_cleanup_rx_descriptors(vb);
+}
+
static void
vb_free_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl)
{
@@ -519,30 +615,30 @@ vb_setctl(struct voicebus *vb, u32 addr, u32 val)
}
static int
-__vb_sdi_clk(struct voicebus *vb)
+__vb_sdi_clk(struct voicebus *vb, u32 *sdi)
{
unsigned int ret;
- vb->sdi &= ~CSR9_MDC;
- __vb_setctl(vb, 0x0048, vb->sdi);
+ *sdi &= ~CSR9_MDC;
+ __vb_setctl(vb, 0x0048, *sdi);
ret = __vb_getctl(vb, 0x0048);
- vb->sdi |= CSR9_MDC;
- __vb_setctl(vb, 0x0048, vb->sdi);
+ *sdi |= CSR9_MDC;
+ __vb_setctl(vb, 0x0048, *sdi);
return (ret & CSR9_MDI) ? 1 : 0;
}
static void
-__vb_sdi_sendbits(struct voicebus *vb, u32 bits, int count)
+__vb_sdi_sendbits(struct voicebus *vb, u32 bits, int count, u32 *sdi)
{
- vb->sdi &= ~CSR9_MMC;
- __vb_setctl(vb, 0x0048, vb->sdi);
+ *sdi &= ~CSR9_MMC;
+ __vb_setctl(vb, 0x0048, *sdi);
while (count--) {
if (bits & (1 << count))
- vb->sdi |= CSR9_MDO;
+ *sdi |= CSR9_MDO;
else
- vb->sdi &= ~CSR9_MDO;
+ *sdi &= ~CSR9_MDO;
- __vb_sdi_clk(vb);
+ __vb_sdi_clk(vb, sdi);
}
}
@@ -551,13 +647,14 @@ vb_setsdi(struct voicebus *vb, int addr, u16 val)
{
LOCKS_VOICEBUS;
u32 bits;
+ u32 sdi = 0;
/* Send preamble */
bits = 0xffffffff;
VBLOCK(vb);
- __vb_sdi_sendbits(vb, bits, 32);
+ __vb_sdi_sendbits(vb, bits, 32, &sdi);
bits = (0x5 << 12) | (1 << 7) | (addr << 2) | 0x2;
- __vb_sdi_sendbits(vb, bits, 16);
- __vb_sdi_sendbits(vb, val, 16);
+ __vb_sdi_sendbits(vb, bits, 16, &sdi);
+ __vb_sdi_sendbits(vb, val, 16, &sdi);
VBUNLOCK(vb);
}
@@ -566,7 +663,7 @@ vb_enable_io_access(struct voicebus *vb)
{
LOCKS_VOICEBUS;
u32 reg;
- assert(vb->pdev);
+ BUG_ON(!vb->pdev);
VBLOCK(vb);
pci_read_config_dword(vb->pdev, 0x0004, &reg);
reg |= 0x00000007;
@@ -620,11 +717,12 @@ vb_reset_interface(struct voicebus *vb)
pci_access = DEFAULT_PCI_ACCESS | (0x3 << 14);
break;
default:
- if (atomic_read(&vb->debuglevel))
- VB_PRINTK(vb, WARNING, "Host system set a cache size "\
- "of %d which is not supported. " \
- "Disabling memory write line and memory read line.\n",
- vb->cache_line_size);
+ if (atomic_read(&vb->debuglevel)) {
+ dev_warn(&vb->pdev->dev, "Host system set a cache "
+ "size of %d which is not supported. "
+ "Disabling memory write line and memory "
+ "read line.\n", vb->cache_line_size);
+ }
pci_access = 0xfe584202;
break;
}
@@ -640,8 +738,8 @@ vb_reset_interface(struct voicebus *vb)
} while ((reg & 0x00000001) && time_before(jiffies, timeout));
if (reg & 0x00000001) {
- VB_PRINTK(vb, ERR, "Hardware did not come out of reset "\
- "within 100ms!");
+ dev_warn(&vb->pdev->dev, "Hardware did not come out of reset "
+ "within 100ms!");
return -EIO;
}
@@ -655,8 +753,8 @@ vb_initialize_interface(struct voicebus *vb)
{
u32 reg;
- vb_cleanup_descriptors(vb, &vb->txd);
- vb_cleanup_descriptors(vb, &vb->rxd);
+ vb_cleanup_tx_descriptors(vb);
+ vb_cleanup_rx_descriptors(vb);
/* Pass bad packets, runt packets, disable SQE function,
* store-and-forward */
@@ -689,12 +787,9 @@ vb_initialize_interface(struct voicebus *vb)
return ((reg&0x7) == 0x4) ? 0 : -EIO;
}
-#define OWNED(_d_) (((_d_)->des0)&OWN_BIT)
-#define SET_OWNED(_d_) do { wmb(); (_d_)->des0 |= OWN_BIT; wmb(); } while (0)
-
#ifdef DBG
static void
-dump_descriptor(struct voicebus *vb, volatile struct voicebus_descriptor *d)
+dump_descriptor(struct voicebus *vb, struct voicebus_descriptor *d)
{
VB_PRINTK(vb, DEBUG, "Displaying descriptor at address %08x\n", (unsigned int)d);
VB_PRINTK(vb, DEBUG, " des0: %08x\n", d->des0);
@@ -720,61 +815,32 @@ show_buffer(struct voicebus *vb, void *vbb)
}
#endif
-static inline int
-vb_submit(struct voicebus *vb, struct voicebus_descriptor_list *dl, void *vbb)
+/**
+ * voicebus_transmit - Queue a buffer on the hardware descriptor ring.
+ *
+ */
+int voicebus_transmit(struct voicebus *vb, void *vbb)
{
- volatile struct voicebus_descriptor *d;
- unsigned int tail = dl->tail;
- assert_in_vb_deferred(vb);
+ struct voicebus_descriptor *d;
+ struct voicebus_descriptor_list *dl = &vb->txd;
- d = vb_descriptor(dl, tail);
+ d = vb_descriptor(dl, dl->tail);
- if (unlikely(d->buffer1)) {
- /* Do not overwrite a buffer that is still in progress. */
- WARN_ON(1);
+ if (unlikely(d->buffer1 != vb->idle_vbb_dma_addr)) {
+ if (printk_ratelimit())
+ dev_warn(&vb->pdev->dev, "Dropping tx buffer buffer\n");
voicebus_free(vb, vbb);
- return -EBUSY;
+ return -EFAULT;
}
- dl->pending[tail] = vbb;
- dl->tail = (++tail) & DRING_MASK;
- d->buffer1 = dma_map_single(
- &vb->pdev->dev, vbb, vb->framesize, dl->direction);
+ dl->pending[dl->tail] = vbb;
+ dl->tail = (++(dl->tail)) & DRING_MASK;
+ d->buffer1 = dma_map_single(&vb->pdev->dev, vbb,
+ vb->framesize, DMA_TO_DEVICE);
SET_OWNED(d); /* That's it until the hardware is done with it. */
atomic_inc(&dl->count);
return 0;
}
-
-static inline void*
-vb_retrieve(struct voicebus *vb, struct voicebus_descriptor_list *dl)
-{
- volatile struct voicebus_descriptor *d;
- void *vbb;
- unsigned int head = dl->head;
- assert_in_vb_deferred(vb);
- d = vb_descriptor(dl, head);
- if (d->buffer1 && !OWNED(d)) {
- dma_unmap_single(&vb->pdev->dev, d->buffer1,
- vb->framesize, dl->direction);
- vbb = dl->pending[head];
- dl->head = (++head) & DRING_MASK;
- d->buffer1 = 0;
- atomic_dec(&dl->count);
- return vbb;
- } else {
- return NULL;
- }
-}
-
-/*!
- * \brief Give a frame to the hardware to transmit.
- *
- */
-int
-voicebus_transmit(struct voicebus *vb, void *vbb)
-{
- return vb_submit(vb, &vb->txd, vbb);
-}
EXPORT_SYMBOL(voicebus_transmit);
/*!
@@ -784,7 +850,26 @@ EXPORT_SYMBOL(voicebus_transmit);
static inline int
vb_submit_rxb(struct voicebus *vb, void *vbb)
{
- return vb_submit(vb, &vb->rxd, vbb);
+ struct voicebus_descriptor *d;
+ struct voicebus_descriptor_list *dl = &vb->rxd;
+ unsigned int tail = dl->tail;
+
+ d = vb_descriptor(dl, tail);
+
+ if (unlikely(d->buffer1)) {
+ /* Do not overwrite a buffer that is still in progress. */
+ WARN_ON(1);
+ voicebus_free(vb, vbb);
+ return -EBUSY;
+ }
+
+ dl->pending[tail] = vbb;
+ dl->tail = (++tail) & DRING_MASK;
+ d->buffer1 = dma_map_single(&vb->pdev->dev, vbb,
+ vb->framesize, DMA_FROM_DEVICE);
+ SET_OWNED(d); /* That's it until the hardware is done with it. */
+ atomic_inc(&dl->count);
+ return 0;
}
/*!
@@ -803,13 +888,47 @@ vb_submit_rxb(struct voicebus *vb, void *vbb)
static inline void *
vb_get_completed_txb(struct voicebus *vb)
{
- return vb_retrieve(vb, &vb->txd);
+ struct voicebus_descriptor_list *dl = &vb->txd;
+ struct voicebus_descriptor *d;
+ void *vbb;
+ unsigned int head = dl->head;
+
+ d = vb_descriptor(dl, head);
+
+ if (OWNED(d) || (d->buffer1 == vb->idle_vbb_dma_addr))
+ return NULL;
+
+ dma_unmap_single(&vb->pdev->dev, d->buffer1,
+ vb->framesize, DMA_TO_DEVICE);
+
+ vbb = dl->pending[head];
+ dl->head = (++head) & DRING_MASK;
+ d->buffer1 = vb->idle_vbb_dma_addr;
+ SET_OWNED(d);
+ atomic_dec(&dl->count);
+ return vbb;
}
static inline void *
vb_get_completed_rxb(struct voicebus *vb)
{
- return vb_retrieve(vb, &vb->rxd);
+ struct voicebus_descriptor *d;
+ struct voicebus_descriptor_list *dl = &vb->rxd;
+ unsigned int head = dl->head;
+ void *vbb;
+
+ d = vb_descriptor(dl, head);
+
+ if ((0 == d->buffer1) || OWNED(d))
+ return NULL;
+
+ dma_unmap_single(&vb->pdev->dev, d->buffer1,
+ vb->framesize, DMA_FROM_DEVICE);
+ vbb = dl->pending[head];
+ dl->head = (++head) & DRING_MASK;
+ d->buffer1 = 0;
+ atomic_dec(&dl->count);
+ return vbb;
}
/*!
@@ -905,7 +1024,9 @@ voicebus_start(struct voicebus *vb)
void *vbb;
int ret;
- assert(!in_interrupt());
+ WARN_ON(pci_get_drvdata(vb->pdev) != vb);
+ if (pci_get_drvdata(vb->pdev) != vb)
+ return -EFAULT;
if (!vb_is_stopped(vb))
return -EBUSY;
@@ -928,7 +1049,6 @@ voicebus_start(struct voicebus *vb)
* is known to not be running at this point, it is safe to call the
* handle transmit as if it were.
*/
- start_vb_deferred(vb);
/* Ensure that all the rx slots are ready for a buffer. */
for (i = 0; i < DRING_SIZE; ++i) {
vbb = voicebus_alloc(vb);
@@ -947,10 +1067,9 @@ voicebus_start(struct voicebus *vb)
if (unlikely(NULL == vbb))
BUG_ON(1);
else
- vb->handle_transmit(vbb, vb->context);
+ handle_transmit(vb, vbb);
}
- stop_vb_deferred(vb);
VBLOCK(vb);
clear_bit(STOP, &vb->flags);
@@ -971,7 +1090,7 @@ voicebus_start(struct voicebus *vb)
__vb_tx_demand_poll(vb);
VBUNLOCK(vb);
- assert(!vb_is_stopped(vb));
+ BUG_ON(vb_is_stopped(vb));
return 0;
}
@@ -1033,8 +1152,6 @@ vb_wait_for_completion_timeout(struct completion *x, unsigned long timeout)
int
voicebus_stop(struct voicebus *vb)
{
- assert(!in_interrupt());
-
if (vb_is_stopped(vb))
return 0;
@@ -1043,11 +1160,10 @@ voicebus_stop(struct voicebus *vb)
vb_clear_start_transmit_bit(vb);
vb_clear_start_receive_bit(vb);
if (vb_wait_for_completion_timeout(&vb->stopped_completion, HZ)) {
- assert(vb_is_stopped(vb));
+ BUG_ON(!vb_is_stopped(vb));
} else {
- VB_PRINTK(vb, WARNING, "Timeout while waiting for board to "\
- "stop.\n");
-
+ dev_warn(&vb->pdev->dev, "Timeout while waiting for board to "
+ "stop.\n");
vb_clear_start_transmit_bit(vb);
vb_clear_start_receive_bit(vb);
@@ -1063,6 +1179,24 @@ voicebus_stop(struct voicebus *vb)
}
EXPORT_SYMBOL(voicebus_stop);
+#ifdef CONFIG_VOICEBUS_SYSFS
+static ssize_t
+voicebus_current_latency_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long flags;
+ struct voicebus *vb = dev_get_drvdata(dev);
+ unsigned int current_latency;
+ spin_lock_irqsave(&vb->lock, flags);
+ current_latency = vb->min_tx_buffer_count;
+ spin_unlock_irqrestore(&vb->lock, flags);
+ return sprintf(buf, "%d\n", current_latency);
+}
+
+DEVICE_ATTR(voicebus_current_latency, 0444,
+ voicebus_current_latency_show, NULL);
+#endif
+
/*!
* \brief Prepare the interface for module unload.
*
@@ -1075,7 +1209,9 @@ EXPORT_SYMBOL(voicebus_stop);
void
voicebus_release(struct voicebus *vb)
{
- assert(!in_interrupt());
+#ifdef CONFIG_VOICEBUS_SYSFS
+ device_remove_file(&vb->pdev->dev, &dev_attr_voicebus_current_latency);
+#endif
/* quiesce the hardware */
voicebus_stop(vb);
@@ -1092,6 +1228,10 @@ voicebus_release(struct voicebus *vb)
/* Cleanup memory and software resources. */
vb_free_descriptors(vb, &vb->txd);
vb_free_descriptors(vb, &vb->rxd);
+ if (vb->idle_vbb_dma_addr) {
+ dma_free_coherent(&vb->pdev->dev, vb->framesize,
+ vb->idle_vbb, vb->idle_vbb_dma_addr);
+ }
kmem_cache_destroy(vb->buffer_cache);
release_region(vb->iobase, 0xff);
pci_disable_device(vb->pdev);
@@ -1100,103 +1240,291 @@ voicebus_release(struct voicebus *vb)
EXPORT_SYMBOL(voicebus_release);
static void
-__vb_increase_latency(struct voicebus *vb)
+vb_increase_latency(struct voicebus *vb, unsigned int increase)
{
- static int __warn_once = 1;
void *vbb;
- int latency;
+ int i;
- assert_in_vb_deferred(vb);
+ if (0 == increase)
+ return;
- latency = atomic_read(&vb->txd.count);
- if (DRING_SIZE == latency) {
- if (__warn_once) {
- /* We must subtract two from this number since there
- * are always two buffers in the TX FIFO.
- */
- VB_PRINTK(vb, ERR,
- "ERROR: Unable to service card within %d ms "\
- "and unable to further increase latency.\n",
- DRING_SIZE-2);
- __warn_once = 0;
- }
- } else {
- /* Because there are 2 buffers in the transmit FIFO on the
- * hardware, setting 3 ms of latency means that the host needs
- * to be able to service the cards within 1ms. This is because
- * the interface will load up 2 buffers into the TX FIFO then
- * attempt to read the 3rd descriptor. If the OWN bit isn't
- * set, then the hardware will set the TX descriptor not
- * available interrupt.
- */
- VB_PRINTK(vb, INFO, "Missed interrupt. " \
- "Increasing latency to %d ms in order to compensate.\n",
- latency+1);
- /* Set the minimum latency in case we're restarted...we don't
- * want to wait for the buffer to grow to this depth again in
- * that case.
- */
- voicebus_set_minlatency(vb, latency+1);
+ if (test_bit(LATENCY_LOCKED, &vb->flags))
+ return;
+
+ if (unlikely(increase > VOICEBUS_MAXLATENCY_BUMP))
+ increase = VOICEBUS_MAXLATENCY_BUMP;
+
+ if ((increase + vb->min_tx_buffer_count) > vb->max_latency)
+ increase = vb->max_latency - vb->min_tx_buffer_count;
+
+ /* Because there are 2 buffers in the transmit FIFO on the hardware,
+ * setting 3 ms of latency means that the host needs to be able to
+ * service the cards within 1ms. This is because the interface will
+ * load up 2 buffers into the TX FIFO then attempt to read the 3rd
+ * descriptor. If the OWN bit isn't set, then the hardware will set the
+ * TX descriptor not available interrupt. */
+
+ /* Set the minimum latency in case we're restarted...we don't want to
+ * wait for the buffer to grow to this depth again in that case. */
+ for (i = 0; i < increase; ++i) {
vbb = voicebus_alloc(vb);
+ WARN_ON(NULL == vbb);
+ if (likely(NULL != vbb))
+ handle_transmit(vb, vbb);
+ }
- if (unlikely(NULL == vbb))
- BUG_ON(1);
- else
- vb->handle_transmit(vbb, vb->context);
+ /* Set the new latency (but we want to ensure that there aren't any
+ * printks to the console, so we don't call the function) */
+ spin_lock(&vb->lock);
+ vb->min_tx_buffer_count += increase;
+ spin_unlock(&vb->lock);
+}
+static void vb_set_all_owned(struct voicebus *vb,
+ struct voicebus_descriptor_list *dl)
+{
+ int i;
+ struct voicebus_descriptor *d;
+
+ for (i = 0; i < DRING_SIZE; ++i) {
+ d = vb_descriptor(dl, i);
+ SET_OWNED(d);
}
}
-/*!
- * \brief Actually process the completed transmit and receive buffers.
+static inline void vb_set_all_tx_owned(struct voicebus *vb)
+{
+ vb_set_all_owned(vb, &vb->txd);
+}
+
+/**
+ * __vb_get_default_behind_count() - Returns how many idle buffers are loaded in tx fifo.
+ *
+ * These buffers are going to be set, but the AN983 does not clear the owned
+ * bit on the descriptors until they've actually been sent around.
*
- * NOTE: This function may be called either from a tasklet, workqueue, or
- * directly in the interrupt service routine depending on
- * VOICEBUS_DEFERRED.
+ * If you do not check for both the current and next descriptors, you could have
+ * a condition where idle buffers are being sent around, but we don't detect
+ * them because our current descriptor always points to a non-idle buffer.
*/
-static inline void
-vb_deferred(struct voicebus *vb)
+static unsigned int __vb_get_default_behind_count(const struct voicebus *vb)
{
- void *vbb;
-#ifdef DBG
- static int count;
-#endif
+ const struct voicebus_descriptor_list *const dl = &vb->txd;
+ const struct voicebus_descriptor *current_descriptor;
+ const struct voicebus_descriptor *next_descriptor;
+
+ current_descriptor = vb_descriptor(dl, dl->head);
+ if (current_descriptor->buffer1 == vb->idle_vbb_dma_addr)
+ return 2;
+
+ next_descriptor = vb_descriptor(dl, ((dl->head + 1) & DRING_MASK));
+ if (next_descriptor->buffer1 == vb->idle_vbb_dma_addr)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * vb_check_softunderrun() - Return true if the TX FIFO has underrun valid data.
+ *
+ * Returns true if we're processing the idle buffers, which means that the host
+ * was not able to keep up with the hardware. This differs from a normal
+ * underrun in that the interface chip on the board still has descriptors to
+ * transmit (just in this case, they are the idle buffers).
+ *
+ */
+static inline int vb_is_softunderrun(const struct voicebus *vb)
+{
+ return __vb_get_default_behind_count(vb) > 0;
+}
+
+/**
+ * vb_recover_tx_descriptor_list() - Recovers descriptor list
+ *
+ * Returns the number of descriptors that we're behind.
+ *
+ * Called if the [head | head + 1] pointer points to one of the idle buffers.
+ * This means that the host computer has failed to keep far enough ahead of the
+ * voicebus card. This function acks the completed idle descriptors and gets
+ * everything setup for normal operation again.
+ *
+ */
+static unsigned int vb_recover_tx_descriptor_list(struct voicebus *vb)
+{
+ struct voicebus_descriptor_list *const dl = &vb->txd;
+ struct voicebus_descriptor *d;
+ struct vbb *vbb = NULL;
+ unsigned int behind = __vb_get_default_behind_count(vb);
+ WARN_ON(0 == behind);
+
+ d = vb_descriptor(dl, dl->head);
+
+ /* If we're only behind by one descriptor, we need to wait for all
+ * descriptors to go owned so we're starting with a fresh slate. This
+ * will also means we send an additional idle buffer. */
+ if (1 == behind) {
+ unsigned long stop;
+ stop = jiffies + HZ/100;
+ while (OWNED(d) && time_after(stop, jiffies))
+ continue;
+ WARN_ON(time_before(stop, jiffies));
+ WARN_ON(d->buffer1 == vb->idle_vbb_dma_addr);
+ WARN_ON(!dl->pending[dl->head]);
+ dma_unmap_single(&vb->pdev->dev, d->buffer1,
+ vb->framesize, DMA_TO_DEVICE);
+ vbb = dl->pending[dl->head];
+ atomic_dec(&dl->count);
+ --behind;
+ }
+
+ /* First complete any "idle" buffers that the hardware was to actually
+ * complete. We've already preloaded the behind variable for the idle
+ * buffers that are in progress but may not be complete. */
+ while (!OWNED(d)) {
+ d->buffer1 = vb->idle_vbb_dma_addr;
+ dl->pending[dl->head] = vb->idle_vbb;
+ SET_OWNED(d);
+ dl->head = ++dl->head & DRING_MASK;
+ d = vb_descriptor(dl, dl->head);
+ ++behind;
+ }
+
+ /* Next get a little further ahead, because the hardware will be
+ * currently working on one of the idle buffers that we can't detect is
+ * completed yet in the previous block. Set the head and tail pointers
+ * to this new position so that everything can pick up normally. */
+ dl->tail = dl->head = (dl->head + 10) & DRING_MASK;
+
+ if (NULL != vbb)
+ handle_transmit(vb, vbb);
+
+ return behind;
+}
+
+/**
+ * vb_deferred() - Manage the transmit and receive descriptor rings.
+ *
+ */
+static void vb_deferred(struct voicebus *vb)
+{
+ unsigned int buffer_count;
+ unsigned int i;
+ unsigned int idle_buffers;
+ int softunderrun;
+
int underrun = test_bit(TX_UNDERRUN, &vb->flags);
+ buffer_count = 0;
- start_vb_deferred(vb);
- if (unlikely(underrun)) {
- /* When we've underrun our FIFO, for some reason we're not
- * able to keep enough transmit descriptors pending. This can
- * happen if either interrupts or this deferred processing
- * function is not run soon enough (within 1ms when using the
- * default 3 transmit buffers to start). In this case, we'll
- * insert an additional transmit buffer onto the descriptor
- * list which decreases the sensitivity to latency, but also
- * adds more delay to the TDM and SPI data.
- */
- __vb_increase_latency(vb);
+ /* First, temporarily store any non-idle buffers that the hardware has
+ * indicated it's finished transmitting. Non idle buffers are those
+ * buffers that contain actual data and was filled out by the client
+ * driver (as of this writing, the wcte12xp or wctdm24xxp drivers) when
+ * passed up through the handle_transmit callback.
+ *
+ * On the other hand, idle buffers are "dummy" buffers that solely exist
+ * to in order to prevent the transmit descriptor ring from ever
+ * completely draining. */
+ while ((vb->vbb_stash[buffer_count] = vb_get_completed_txb(vb))) {
+ ++buffer_count;
+ if (unlikely(VOICEBUS_DEFAULT_MAXLATENCY < buffer_count)) {
+ dev_warn(&vb->pdev->dev, "Critical problem detected "
+ "in transmit ring descriptor\n");
+ if (buffer_count >= DRING_SIZE)
+ buffer_count = DRING_SIZE - 1;
+ break;
+ }
}
- /* Always handle the transmit buffers first. */
- while ((vbb = vb_get_completed_txb(vb)))
- vb->handle_transmit(vbb, vb->context);
+ vb->count += buffer_count;
+
+ /* Next, check to see if we're in a softunderrun condition.
+ *
+ * A soft under is when the hardware has possibly copied at least one of
+ * the idle buffers into it's transmit queue. Since all the buffers are
+ * nearly always owned by the hardware, the way we detect this is by
+ * checking if either of the next two buffers that we expect to complete
+ * are idle buffers. We need to check the next two, because the
+ * hardware can read two buffers into it's tx fifo where they are
+ * definitely going to be sent on the interface, but the hardware does
+ * not flip the OWN bit on the descriptors until after the buffer has
+ * finished being sent to the CPLD. Therefore, just looking at the own
+ * bit and the buffer pointed to by the dl->head is not enough.
+ *
+ * NOTE: Do not print anything to the console from the time a soft
+ * underrun is detected until the transmit descriptors are fixed up
+ * again. Otherwise the hardware could advance past where you set the
+ * head and tail pointers and then eventually run into the desciptor
+ * that it was currently working on when the softunderrun condition was
+ * first hit. */
+ if (unlikely(vb_is_softunderrun(vb))) {
+ softunderrun = 1;
+ /* If we experienced a soft underrun, the recover function will
+ * a) ensure that all non-idle buffers have been completely
+ * tranmitted by the hardware b) Reset any idle buffers that
+ * have been completely transmitted c) Reset the head and tail
+ * pointers to somwhere in advance of where the hardware is
+ * currently processing and d) return how many idle_buffers were
+ * transmitted before our interrupt handler was called. */
+ idle_buffers = vb_recover_tx_descriptor_list(vb);
+ vb_increase_latency(vb, idle_buffers);
+ } else {
+ softunderrun = 0;
+ idle_buffers = 0;
+ }
+ /* Now we can process the completed non-idle buffers since we know at
+ * this point that the transmit descriptor is in a "good" state. */
+ for (i = 0; i < buffer_count; ++i)
+ handle_transmit(vb, vb->vbb_stash[i]);
+
+ /* If underrun is set, it means that the hardware signalled that it
+ * completely ran out of transmit descriptors. This is what we are
+ * trying to avoid with all this racy softunderun business, but alas,
+ * it's still possible to happen if interrupts are locked longer than
+ * DRING_SIZE milliseconds for some reason. We should have already fixed
+ * up the descriptor ring in this case, so let's just tell the hardware
+ * to reread what it believes the next descriptor is. */
if (unlikely(underrun)) {
+ if (printk_ratelimit()) {
+ dev_info(&vb->pdev->dev, "Host failed to service "
+ "card interrupt within %d ms which is a "
+ "hardunderun.\n", DRING_SIZE);
+ }
vb_rx_demand_poll(vb);
vb_tx_demand_poll(vb);
clear_bit(TX_UNDERRUN, &vb->flags);
}
- while ((vbb = vb_get_completed_rxb(vb))) {
- vb->handle_receive(vbb, vb->context);
- vb_submit_rxb(vb, vbb);
+ /* Print any messages about soft latency bumps after we fix the transmit
+ * descriptor ring. Otherwise it's possible to take so much time
+ * printing the dmesg output that we lose the lead that we got on the
+ * hardware, resulting in a hard underrun condition. */
+ if (unlikely(softunderrun &&
+ !test_bit(LATENCY_LOCKED, &vb->flags) && printk_ratelimit())) {
+ if (vb->max_latency != vb->min_tx_buffer_count) {
+ dev_info(&vb->pdev->dev, "Missed interrupt. "
+ "Increasing latency to %d ms in order to "
+ "compensate.\n", vb->min_tx_buffer_count);
+ } else {
+ dev_info(&vb->pdev->dev, "ERROR: Unable to service "
+ "card within %d ms and unable to further "
+ "increase latency.\n", vb->max_latency);
+ }
}
- stop_vb_deferred(vb);
+ /* And finally, pass up any receive buffers. We also use vb->count to
+ * make a half-hearted attempt to not pass any recieved idle buffers to
+ * the caller, but this needs more work.... */
+ while ((vb->vbb_stash[0] = vb_get_completed_rxb(vb))) {
+ if (vb->count) {
+ vb->handle_receive(vb->vbb_stash[0], vb->context);
+ --vb->count;
+ }
+ vb_submit_rxb(vb, vb->vbb_stash[0]);
+ }
}
-
/*!
* \brief Interrupt handler for VoiceBus interface.
*
@@ -1262,15 +1590,15 @@ vb_isr(int irq, void *dev_id)
}
if (int_status & FATAL_BUS_ERROR_INTERRUPT)
- VB_PRINTK(vb, ERR, "Fatal Bus Error detected!\n");
+ dev_err(&vb->pdev->dev, "Fatal Bus Error detected!\n");
if (int_status & TX_STOPPED_INTERRUPT) {
- assert(test_bit(STOP, &vb->flags));
+ BUG_ON(!test_bit(STOP, &vb->flags));
__vb_disable_interrupts(vb);
complete(&vb->stopped_completion);
}
if (int_status & RX_STOPPED_INTERRUPT) {
- assert(test_bit(STOP, &vb->flags));
+ BUG_ON(!test_bit(STOP, &vb->flags));
if (vb_is_stopped(vb)) {
__vb_disable_interrupts(vb);
complete(&vb->stopped_completion);
@@ -1334,8 +1662,7 @@ vb_tasklet(unsigned long data)
* \todo Complete this description.
*/
int
-voicebus_init(struct pci_dev *pdev, u32 framesize,
- const char *board_name,
+voicebus_init(struct pci_dev *pdev, u32 framesize, const char *board_name,
void (*handle_receive)(void *vbb, void *context),
void (*handle_transmit)(void *vbb, void *context),
void *context,
@@ -1346,11 +1673,11 @@ voicebus_init(struct pci_dev *pdev, u32 framesize,
int retval = 0;
struct voicebus *vb;
- assert(NULL != pdev);
- assert(NULL != board_name);
- assert(framesize);
- assert(NULL != handle_receive);
- assert(NULL != handle_transmit);
+ BUG_ON(NULL == pdev);
+ BUG_ON(NULL == board_name);
+ BUG_ON(0 == framesize);
+ BUG_ON(NULL == handle_receive);
+ BUG_ON(NULL == handle_transmit);
/* ----------------------------------------------------------------
Initialize the pure software constructs.
@@ -1358,20 +1685,19 @@ voicebus_init(struct pci_dev *pdev, u32 framesize,
*vbp = NULL;
vb = kmalloc(sizeof(*vb), GFP_KERNEL);
if (NULL == vb) {
- VB_PRINTK(vb, DEBUG, "Failed to allocate memory for voicebus "\
- "interface.\n");
+ dev_dbg(&vb->pdev->dev, "Failed to allocate memory for "
+ "voicebus interface.\n");
retval = -ENOMEM;
goto cleanup;
}
memset(vb, 0, sizeof(*vb));
+ vb->pdev = pdev;
+ pci_set_drvdata(pdev, vb);
voicebus_setdebuglevel(vb, debuglevel);
- /* \todo make sure there is a note that the caller needs to make sure
- * board_name stays in memory until voicebus_release is called.
- */
- vb->board_name = board_name;
+ vb->max_latency = VOICEBUS_DEFAULT_MAXLATENCY;
+
spin_lock_init(&vb->lock);
init_completion(&vb->stopped_completion);
- vb->pdev = pdev;
set_bit(STOP, &vb->flags);
clear_bit(IN_DEFERRED_PROCESSING, &vb->flags);
vb->framesize = framesize;
@@ -1413,26 +1739,46 @@ voicebus_init(struct pci_dev *pdev, u32 framesize,
SLAB_HWCACHE_ALIGN, NULL, NULL);
#endif
#else
+#ifdef DEBUG
+ vb->buffer_cache = kmem_cache_create(board_name, vb->framesize, 0,
+ SLAB_HWCACHE_ALIGN | SLAB_STORE_USER |
+ SLAB_POISON | SLAB_DEBUG_FREE, NULL);
+#else
vb->buffer_cache = kmem_cache_create(board_name, vb->framesize, 0,
SLAB_HWCACHE_ALIGN, NULL);
#endif
+#endif
if (NULL == vb->buffer_cache) {
- VB_PRINTK(vb, ERR, "Failed to allocate buffer cache.\n");
+ dev_err(&vb->pdev->dev, "Failed to allocate buffer cache.\n");
goto cleanup;
}
-
+#ifdef CONFIG_VOICEBUS_SYSFS
+ dev_dbg(&vb->pdev->dev, "Creating sysfs attributes.\n");
+ retval = device_create_file(&vb->pdev->dev,
+ &dev_attr_voicebus_current_latency);
+ if (retval) {
+ dev_dbg(&vb->pdev->dev,
+ "Failed to create device attributes.\n");
+ goto cleanup;
+ }
+#endif
/* ----------------------------------------------------------------
Configure the hardware / kernel module interfaces.
---------------------------------------------------------------- */
+ if (pci_set_dma_mask(vb->pdev, DMA_BIT_MASK(32))) {
+ dev_err(&vb->pdev->dev, "No suitable DMA available.\n");
+ goto cleanup;
+ }
+
if (pci_read_config_byte(vb->pdev, 0x0c, &vb->cache_line_size)) {
- VB_PRINTK(vb, ERR, "Failed read of cache line " \
- "size from PCI configuration space.\n");
+ dev_err(&vb->pdev->dev, "Failed read of cache line "
+ "size from PCI configuration space.\n");
goto cleanup;
}
if (pci_enable_device(pdev)) {
- VB_PRINTK(vb, ERR, "Failed call to pci_enable_device.\n");
+ dev_err(&vb->pdev->dev, "Failed call to pci_enable_device.\n");
retval = -EIO;
goto cleanup;
}
@@ -1440,18 +1786,21 @@ voicebus_init(struct pci_dev *pdev, u32 framesize,
/* \todo This driver should be modified to use the memory mapped I/O
as opposed to IO space for portability and performance. */
if (0 == (pci_resource_flags(pdev, 0)&IORESOURCE_IO)) {
- VB_PRINTK(vb, ERR, "BAR0 is not IO Memory.\n");
+ dev_err(&vb->pdev->dev, "BAR0 is not IO Memory.\n");
retval = -EIO;
goto cleanup;
}
vb->iobase = pci_resource_start(pdev, 0);
if (NULL == request_region(vb->iobase, 0xff, board_name)) {
- VB_PRINTK(vb, ERR, "IO Registers are in use by another " \
+ dev_err(&vb->pdev->dev, "IO Registers are in use by another "
"module.\n");
retval = -EIO;
goto cleanup;
}
+ vb->idle_vbb = dma_alloc_coherent(&vb->pdev->dev, vb->framesize,
+ &vb->idle_vbb_dma_addr, GFP_KERNEL);
+
retval = vb_initialize_tx_descriptors(vb);
if (retval)
goto cleanup;
@@ -1463,18 +1812,20 @@ voicebus_init(struct pci_dev *pdev, u32 framesize,
/* ----------------------------------------------------------------
Configure the hardware interface.
---------------------------------------------------------------- */
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ release_region(vb->iobase, 0xff);
+ dev_warn(&vb->pdev->dev, "No suitable DMA available.\n");
+ goto cleanup;
+ }
+
pci_set_master(pdev);
vb_enable_io_access(vb);
#if VOICEBUS_DEFERRED != TIMER
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
-# define VB_IRQ_SHARED SA_SHIRQ
-#else
-# define VB_IRQ_SHARED IRQF_SHARED
-#endif
- if (request_irq(pdev->irq, vb_isr, VB_IRQ_SHARED, vb->board_name,
- vb)) {
- assert(0);
+ retval = request_irq(pdev->irq, vb_isr, DAHDI_IRQ_SHARED,
+ board_name, vb);
+ if (retval) {
+ dev_warn(&vb->pdev->dev, "Failed to request interrupt line.\n");
goto cleanup;
}
#endif
@@ -1501,6 +1852,9 @@ cleanup:
if (vb->rxd.desc)
vb_free_descriptors(vb, &vb->rxd);
+ dma_free_coherent(&vb->pdev->dev, vb->framesize,
+ vb->idle_vbb, vb->idle_vbb_dma_addr);
+
if (vb->buffer_cache)
kmem_cache_destroy(vb->buffer_cache);
@@ -1511,7 +1865,7 @@ cleanup:
pci_disable_device(vb->pdev);
kfree(vb);
- assert(0 != retval);
+ WARN_ON(0 == retval);
return retval;
}
EXPORT_SYMBOL(voicebus_init);
@@ -1525,6 +1879,12 @@ voicebus_get_pci_dev(struct voicebus *vb)
}
EXPORT_SYMBOL(voicebus_get_pci_dev);
+void *voicebus_pci_dev_to_context(struct pci_dev *pdev)
+{
+ return ((struct voicebus *)pci_get_drvdata(pdev))->context;
+}
+EXPORT_SYMBOL(voicebus_pci_dev_to_context);
+
static spinlock_t loader_list_lock;
static struct list_head binary_loader_list;
@@ -1560,12 +1920,14 @@ int vpmadtreg_loadfirmware(struct voicebus *vb)
module_put(loader->owner);
} else {
spin_unlock(&loader_list_lock);
- printk(KERN_INFO "Failed to find a registered loader after loading module.\n");
+ dev_info(&vb->pdev->dev, "Failed to find a "
+ "registered loader after loading module.\n");
ret = -ENODEV;
}
} else {
spin_unlock(&loader_list_lock);
- printk(KERN_INFO "Failed to find a registered loader after loading module.\n");
+ dev_info(&vb->pdev->dev, "Failed to find a registered "
+ "loader after loading module.\n");
ret = -1;
}
return ret;