[svn-commits] fjoe: freebsd/trunk r7669 - in /freebsd/trunk: ./ drivers/dahdi/ drivers/dahd...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Dec 8 10:40:19 CST 2009


Author: fjoe
Date: Tue Dec  8 10:40:14 2009
New Revision: 7669

URL: http://svnview.digium.com/svn/dahdi?view=rev&rev=7669
Log:
Merge up to rev. 7668 from linux/trunk.

Removed:
    freebsd/trunk/drivers/dahdi/xpp/.version
Modified:
    freebsd/trunk/   (props changed)
    freebsd/trunk/drivers/dahdi/dahdi-base.c
    freebsd/trunk/drivers/dahdi/voicebus/GpakCust.c
    freebsd/trunk/drivers/dahdi/voicebus/GpakCust.h
    freebsd/trunk/drivers/dahdi/voicebus/voicebus.c
    freebsd/trunk/drivers/dahdi/voicebus/voicebus.h
    freebsd/trunk/drivers/dahdi/vpmadt032_loader/dahdi_vpmadt032_loader.c
    freebsd/trunk/drivers/dahdi/wcb4xxp/Makefile   (props changed)
    freebsd/trunk/drivers/dahdi/wcb4xxp/base.c
    freebsd/trunk/drivers/dahdi/wcb4xxp/wcb4xxp.h
    freebsd/trunk/drivers/dahdi/wct4xxp/base.c
    freebsd/trunk/drivers/dahdi/wctdm24xxp/base.c
    freebsd/trunk/drivers/dahdi/wcte12xp/base.c
    freebsd/trunk/drivers/dahdi/wcte12xp/wcte12xp.h
    freebsd/trunk/drivers/dahdi/xpp/card_bri.c
    freebsd/trunk/drivers/dahdi/xpp/card_global.c
    freebsd/trunk/drivers/dahdi/xpp/card_pri.c
    freebsd/trunk/drivers/dahdi/xpp/firmwares/PIC_TYPE_1.hex
    freebsd/trunk/drivers/dahdi/xpp/firmwares/USB_FW.hex
    freebsd/trunk/drivers/dahdi/xpp/xbus-pcm.c
    freebsd/trunk/drivers/dahdi/xpp/xbus-pcm.h
    freebsd/trunk/drivers/dahdi/xpp/xbus-sysfs.c
    freebsd/trunk/drivers/dahdi/xpp/xpp.rules
    freebsd/trunk/firmware.mk
    freebsd/trunk/include/dahdi/kernel.h

Propchange: freebsd/trunk/
------------------------------------------------------------------------------
    svn:mergeinfo = /linux/trunk:7418-7668

Modified: freebsd/trunk/drivers/dahdi/dahdi-base.c
URL: http://svnview.digium.com/svn/dahdi/freebsd/trunk/drivers/dahdi/dahdi-base.c?view=diff&rev=7669&r1=7668&r2=7669
==============================================================================
--- freebsd/trunk/drivers/dahdi/dahdi-base.c (original)
+++ freebsd/trunk/drivers/dahdi/dahdi-base.c Tue Dec  8 10:40:14 2009
@@ -8580,6 +8580,7 @@
 	const unsigned long MAX_INTERVAL = 100000L;
 	const unsigned long FOURMS_INTERVAL = HZ/250;
 	const unsigned long ONESEC_INTERVAL = HZ;
+	const unsigned long MS_LIMIT = 3000;
 
 	now = current_kernel_time();
 
@@ -8594,6 +8595,23 @@
 			mod_timer(&core_timer.timer, jiffies + FOURMS_INTERVAL);
 
 		ms_since_start = core_diff_ms(&core_timer.start_interval, &now);
+
+		/*
+		 * If the system time has changed, it is possible for us to be
+		 * far behind.  If we are more than MS_LIMIT milliseconds
+		 * behind, just reset our time base and continue so that we do
+		 * not hang the system here.
+		 *
+		 */
+		if (unlikely((ms_since_start - atomic_read(&core_timer.count)) > MS_LIMIT)) {
+			if (printk_ratelimit())
+				module_printk(KERN_INFO, "Detected time shift.\n");
+			atomic_set(&core_timer.count, 0);
+			atomic_set(&core_timer.last_count, 0);
+			core_timer.start_interval = now;
+			return;
+		}
+
 		while (ms_since_start > atomic_read(&core_timer.count))
 			process_masterspan();
 

Modified: freebsd/trunk/drivers/dahdi/voicebus/GpakCust.c
URL: http://svnview.digium.com/svn/dahdi/freebsd/trunk/drivers/dahdi/voicebus/GpakCust.c?view=diff&rev=7669&r1=7668&r2=7669
==============================================================================
--- freebsd/trunk/drivers/dahdi/voicebus/GpakCust.c (original)
+++ freebsd/trunk/drivers/dahdi/voicebus/GpakCust.c Tue Dec  8 10:40:14 2009
@@ -52,6 +52,9 @@
 
 static rwlock_t ifacelock;
 static struct vpmadt032 *ifaces[MAX_DSP_CORES];
+
+#define vpm_info(vpm, format, arg...)         \
+        dev_info(&voicebus_get_pci_dev(vpm->vb)->dev , format , ## arg)
 
 static inline struct vpmadt032 *find_iface(const unsigned short dspid)
 {
@@ -212,26 +215,42 @@
 	return res;
 }
 
-static int vpmadt032_enable_ec(struct vpmadt032 *vpm, int channel)
+struct change_order {
+	struct list_head node;
+	unsigned int channel;
+	struct adt_lec_params params;
+};
+
+static struct change_order *alloc_change_order(void)
+{
+	return kzalloc(sizeof(struct change_order), GFP_KERNEL);
+}
+
+static void free_change_order(struct change_order *order)
+{
+	kfree(order);
+}
+
+static int vpmadt032_enable_ec(struct vpmadt032 *vpm, int channel,
+			       enum adt_companding companding)
 {
 	int res;
 	GPAK_AlgControlStat_t pstatus;
 	GpakAlgCtrl_t control;
 
- 	control = (ADT_COMP_ALAW == vpm->desiredecstate[channel].companding) ?
- 			EnableALawSwCompanding :
- 			EnableMuLawSwCompanding;
+	control = (ADT_COMP_ALAW == companding) ? EnableALawSwCompanding :
+						  EnableMuLawSwCompanding;
  
 	if (vpm->options.debug & DEBUG_ECHOCAN) {
 		const char *law;
 		law = (control == EnableMuLawSwCompanding) ? "MuLaw" : "ALaw";
-		printk(KERN_DEBUG "Enabling ecan on channel: %d (%s)\n",
-				channel, law);
+		vpm_info(vpm, "Enabling ecan on channel: %d (%s)\n",
+			 channel, law);
 	}
 	res = gpakAlgControl(vpm->dspid, channel, control, &pstatus);
 	if (res) {
-		printk(KERN_WARNING "Unable to set SW Companding on " \
-			"channel %d (reason %d)\n", channel, res);
+		vpm_info(vpm, "Unable to set SW Companding on "
+			 "channel %d (reason %d)\n", channel, res);
 	}
 	res = gpakAlgControl(vpm->dspid, channel, EnableEcanA, &pstatus);
 	return res;
@@ -243,16 +262,96 @@
 	GPAK_AlgControlStat_t pstatus;
 
 	if (vpm->options.debug & DEBUG_ECHOCAN)
-		printk(KERN_DEBUG "Disabling ecan on channel: %d\n", channel);
+		vpm_info(vpm, "Disabling ecan on channel: %d\n", channel);
 
 	res = gpakAlgControl(vpm->dspid, channel, BypassSwCompanding, &pstatus);
 	if (res) {
-		printk(KERN_WARNING "Unable to disable sw companding on " \
-			"echo cancellation channel %d (reason %d)\n",
-			channel, res);
+		vpm_info(vpm, "Unable to disable sw companding on "
+			 "echo cancellation channel %d (reason %d)\n",
+			 channel, res);
 	}
 	res = gpakAlgControl(vpm->dspid, channel, BypassEcanA, &pstatus);
 	return res;
+}
+
+static struct change_order *get_next_order(struct vpmadt032 *vpm)
+{
+	struct change_order *order;
+
+	spin_lock(&vpm->change_list_lock);
+	if (!list_empty(&vpm->change_list)) {
+		order = list_entry(vpm->change_list.next,
+				   struct change_order, node);
+		list_del(&order->node);
+	} else {
+		order = NULL;
+	}
+	spin_unlock(&vpm->change_list_lock);
+
+	return order;
+}
+
+static int nlp_settings_changed(const struct adt_lec_params *a,
+				const struct adt_lec_params *b)
+{
+	return ((a->nlp_type != b->nlp_type) ||
+		(a->nlp_threshold != b->nlp_threshold) ||
+		(a->nlp_max_suppress != b->nlp_max_suppress));
+}
+
+static int echocan_on(const struct adt_lec_params *new,
+		      const struct adt_lec_params *old)
+{
+	return ((new->tap_length != old->tap_length) &&
+	       (new->tap_length > 0));
+}
+
+static int echocan_off(const struct adt_lec_params *new,
+		       const struct adt_lec_params *old)
+{
+	return ((new->tap_length != old->tap_length) &&
+	       (0 == new->tap_length));
+}
+
+static void update_channel_config(struct vpmadt032 *vpm, unsigned int channel,
+				  struct adt_lec_params *desired)
+{
+	int res;
+	GPAK_AlgControlStat_t pstatus;
+	GPAK_ChannelConfigStat_t cstatus;
+	GPAK_TearDownChanStat_t tstatus;
+	GpakChannelConfig_t chanconfig;
+
+	if (vpm->options.debug & DEBUG_ECHOCAN) {
+		vpm_info(vpm, "Reconfiguring chan %d for nlp %d, "
+			 "nlp_thresh %d, and max_supp %d\n", channel + 1,
+			 desired->nlp_type, desired->nlp_threshold,
+			 desired->nlp_max_suppress);
+	}
+
+	vpm->setchanconfig_from_state(vpm, channel, &chanconfig);
+
+	res = gpakTearDownChannel(vpm->dspid, channel, &tstatus);
+	if (res)
+		return;
+
+	res = gpakConfigureChannel(vpm->dspid, channel, tdmToTdm,
+				   &chanconfig, &cstatus);
+	if (res)
+		return;
+
+	if (!desired->tap_length) {
+		res = gpakAlgControl(vpm->dspid, channel,
+				     BypassSwCompanding, &pstatus);
+		if (res) {
+			vpm_info(vpm, "Unable to disable sw companding on "
+				 "echo cancellation channel %d (reason %d)\n",
+				 channel, res);
+		}
+		gpakAlgControl(vpm->dspid, channel, BypassEcanA, &pstatus);
+	}
+
+	return;
 }
 
 /**
@@ -272,97 +371,81 @@
 {
 	struct vpmadt032 *vpm = container_of(data, struct vpmadt032, work);
 #endif
-	struct adt_lec_params *curstate, *desiredstate;
-	int channel;
-
-	/* Sweep through all the echo can channels on the VPMADT032 module,
-	 * looking for ones where the desired state does not match the current
-	 * state.
-	 */
-	for (channel = 0; channel < vpm->options.channels; channel++) {
-		GPAK_AlgControlStat_t pstatus;
-		int res = 1;
-		curstate = &vpm->curecstate[channel];
-		desiredstate = &vpm->desiredecstate[channel];
-
-		if ((desiredstate->nlp_type != curstate->nlp_type) ||
-		    (desiredstate->nlp_threshold != curstate->nlp_threshold) ||
-		    (desiredstate->nlp_max_suppress != curstate->nlp_max_suppress)) {
-
-			GPAK_ChannelConfigStat_t cstatus;
-			GPAK_TearDownChanStat_t tstatus;
-			GpakChannelConfig_t chanconfig;
-
-			if (vpm->options.debug & DEBUG_ECHOCAN)
-				printk(KERN_DEBUG "Reconfiguring chan %d for nlp %d, nlp_thresh %d, and max_supp %d\n", channel + 1, vpm->desiredecstate[channel].nlp_type,
-					desiredstate->nlp_threshold, desiredstate->nlp_max_suppress);
-
-			vpm->setchanconfig_from_state(vpm, channel, &chanconfig);
-
-			res = gpakTearDownChannel(vpm->dspid, channel, &tstatus);
-			if (res)
-				goto vpm_bh_out;
-
-			res = gpakConfigureChannel(vpm->dspid, channel, tdmToTdm, &chanconfig, &cstatus);
-			if (res)
-				goto vpm_bh_out;
-
-			if (!desiredstate->tap_length) {
-				res = gpakAlgControl(vpm->dspid, channel, BypassSwCompanding, &pstatus);
-				if (res)
-					printk("Unable to disable sw companding on echo cancellation channel %d (reason %d)\n", channel, res);
-				res = gpakAlgControl(vpm->dspid, channel, BypassEcanA, &pstatus);
-			}
-
-		} else if (desiredstate->tap_length != curstate->tap_length) {
-			if (desiredstate->tap_length)
-				res = vpmadt032_enable_ec(vpm, channel);
-			else
-				res = vpmadt032_disable_ec(vpm, channel);
-		}
-vpm_bh_out:
-		if (!res)
-			*curstate = *desiredstate;
-	}
-	return;
-}
+	struct change_order *order;
+
+	while ((order = get_next_order(vpm))) {
+
+		struct adt_lec_params *old;
+		struct adt_lec_params *new;
+		unsigned int channel;
+
+		channel = order->channel;
+		BUG_ON(channel >= ARRAY_SIZE(vpm->curecstate));
+		old = &vpm->curecstate[channel];
+		new = &order->params;
+
+		if (nlp_settings_changed(new, old))
+			update_channel_config(vpm, channel, new);
+		else if (echocan_on(new, old))
+			vpmadt032_enable_ec(vpm, channel, new->companding);
+		else if (echocan_off(new, old))
+			vpmadt032_disable_ec(vpm, channel);
+
+		*old = order->params;
+		free_change_order(order);
+	}
+}
+
 #include "adt_lec.c"
-static void vpmadt032_check_and_schedule_update(struct vpmadt032 *vpm, int channo)
-{
-	int update;
-	/* Only update the parameters if the new state of the echo canceller
-	 * is different than the current state. */
-	update = memcmp(&vpm->curecstate[channo],
-			&vpm->desiredecstate[channo],
-			sizeof(vpm->curecstate[channo]));
-	if (update && test_bit(VPM150M_ACTIVE, &vpm->control)) {
-		/* Since updating the parameters can take a bit of time while
-		 * the driver sends messages to the VPMADT032 and waits for
-		 * their responses, we'll push the work of updating the
-		 * parameters to a work queue so the caller can continue to
-		 * proceed with setting up the call.
-		 */
-		queue_work(vpm->wq, &vpm->work);
-	}
+static void vpmadt032_check_and_schedule_update(struct vpmadt032 *vpm,
+						struct change_order *order)
+{
+	struct change_order *cur;
+	struct change_order *n;
+
+	INIT_LIST_HEAD(&order->node);
+	spin_lock(&vpm->change_list_lock);
+	list_for_each_entry_safe(cur, n, &vpm->change_list, node) {
+		if (cur->channel == order->channel) {
+			list_replace(&cur->node, &order->node);
+			free_change_order(cur);
+			break;
+		}
+	}
+	if (list_empty(&order->node))
+		list_add_tail(&order->node, &vpm->change_list);
+	spin_unlock(&vpm->change_list_lock);
+
+	queue_work(vpm->wq, &vpm->work);
 }
 int vpmadt032_echocan_create(struct vpmadt032 *vpm, int channo,
 	struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p)
 {
 	unsigned int ret;
-
-	ret = adt_lec_parse_params(&vpm->desiredecstate[channo], ecp, p);
-	if (ret)
+	struct change_order *order = alloc_change_order();
+	if (!order)
+		return -ENOMEM;
+
+	memcpy(&order->params, &vpm->curecstate[channo], sizeof(order->params));
+	ret = adt_lec_parse_params(&order->params, ecp, p);
+	if (ret) {
+		free_change_order(order);
 		return ret;
-
-	if (vpm->options.debug & DEBUG_ECHOCAN)
-		printk(KERN_DEBUG "echocan: Channel is %d length %d\n", channo, ecp->tap_length);
+	}
+
+	if (vpm->options.debug & DEBUG_ECHOCAN) {
+		vpm_info(vpm, "Channel is %d length %d\n",
+			 channo, ecp->tap_length);
+	}
 
 	/* The driver cannot control the number of taps on the VPMADT032
 	 * module. Instead, it uses tap_length to enable or disable the echo
 	 * cancellation. */
-	vpm->desiredecstate[channo].tap_length = (ecp->tap_length) ? 1 : 0;
-
-	vpmadt032_check_and_schedule_update(vpm, channo);
+	order->params.tap_length = (ecp->tap_length) ? 1 : 0;
+	order->params.companding = vpm->companding;
+	order->channel = channo;
+
+	vpmadt032_check_and_schedule_update(vpm, order);
 	return 0;
 }
 EXPORT_SYMBOL(vpmadt032_echocan_create);
@@ -370,15 +453,22 @@
 void vpmadt032_echocan_free(struct vpmadt032 *vpm, int channo,
 	struct dahdi_echocan_state *ec)
 {
-	adt_lec_init_defaults(&vpm->desiredecstate[channo], 0);
-	vpm->desiredecstate[channo].nlp_type = vpm->options.vpmnlptype;
-	vpm->desiredecstate[channo].nlp_threshold = vpm->options.vpmnlpthresh;
-	vpm->desiredecstate[channo].nlp_max_suppress = vpm->options.vpmnlpmaxsupp;
+	struct change_order *order;
+	order = alloc_change_order();
+	WARN_ON(!order);
+	if (!order)
+		return;
+
+	adt_lec_init_defaults(&order->params, 0);
+	order->params.nlp_type = vpm->options.vpmnlptype;
+	order->params.nlp_threshold = vpm->options.vpmnlpthresh;
+	order->params.nlp_max_suppress = vpm->options.vpmnlpmaxsupp;
+	order->channel = channo;
 
 	if (vpm->options.debug & DEBUG_ECHOCAN)
-		printk(KERN_DEBUG "echocan: Channel is %d length 0\n", channo);
-
-	vpmadt032_check_and_schedule_update(vpm, channo);
+		vpm_info(vpm, "Channel is %d length 0\n", channo);
+
+	vpmadt032_check_and_schedule_update(vpm, order);
 }
 EXPORT_SYMBOL(vpmadt032_echocan_free);
 
@@ -410,6 +500,8 @@
 	/* Init our vpmadt032 struct */
 	memcpy(&vpm->options, options, sizeof(*options));
 	spin_lock_init(&vpm->list_lock);
+	spin_lock_init(&vpm->change_list_lock);
+	INIT_LIST_HEAD(&vpm->change_list);
 	INIT_LIST_HEAD(&vpm->free_cmds);
 	INIT_LIST_HEAD(&vpm->pending_cmds);
 	INIT_LIST_HEAD(&vpm->active_cmds);
@@ -443,8 +535,6 @@
 		kfree(vpm);
 		printk(KERN_NOTICE "Unable to initialize another vpmadt032 modules\n");
 		vpm = NULL;
-	} else if (vpm->options.debug & DEBUG_ECHOCAN) {
-		printk(KERN_DEBUG "Setting VPMADT032 DSP ID to %d\n", vpm->dspid);
 	}
 
 	return vpm;
@@ -461,6 +551,9 @@
 
 	BUG_ON(!vpm->setchanconfig_from_state);
 	BUG_ON(!vpm->wq);
+	BUG_ON(!vb);
+
+	vpm->vb = vb;
 
 	might_sleep();
 
@@ -521,8 +614,9 @@
 		return res;
 	}
 	vpm->curpage = -1;
+
+	dev_info(&voicebus_get_pci_dev(vb)->dev, "Booting VPMADT032\n");
 	set_bit(VPM150M_SWRESET, &vpm->control);
-
 	while (test_bit(VPM150M_SWRESET, &vpm->control))
 		msleep(1);
 
@@ -561,7 +655,7 @@
 	p->EcanDblTalkThresh = 6;
 	p->EcanMaxDoubleTalkThres = 40;
 	p->EcanNlpThreshold = DEFAULT_NLPTHRESH;
-	p->EcanNlpConv = 0;
+	p->EcanNlpConv = 18;
 	p->EcanNlpUnConv = 12;
 	p->EcanNlpMaxSuppress = DEFAULT_NLPMAXSUPP;
 	p->EcanCngThreshold = 43;
@@ -579,6 +673,7 @@
 {
 	unsigned long flags;
 	struct vpmadt032_cmd *cmd;
+	struct change_order *order;
 	LIST_HEAD(local_list);
 
 	BUG_ON(!vpm);
@@ -597,6 +692,16 @@
 		cmd = list_entry(local_list.next, struct vpmadt032_cmd, node);
 		list_del(&cmd->node);
 		kfree(cmd);
+	}
+
+	spin_lock(&vpm->change_list_lock);
+	list_splice(&vpm->change_list, &local_list);
+	spin_unlock(&vpm->change_list_lock);
+
+	while (!list_empty(&local_list)) {
+		order = list_entry(local_list.next, struct change_order, node);
+		list_del(&order->node);
+		kfree(order);
 	}
 
 	BUG_ON(ifaces[vpm->dspid] != vpm);

Modified: freebsd/trunk/drivers/dahdi/voicebus/GpakCust.h
URL: http://svnview.digium.com/svn/dahdi/freebsd/trunk/drivers/dahdi/voicebus/GpakCust.h?view=diff&rev=7669&r1=7668&r2=7669
==============================================================================
--- freebsd/trunk/drivers/dahdi/voicebus/GpakCust.h (original)
+++ freebsd/trunk/drivers/dahdi/voicebus/GpakCust.h Tue Dec  8 10:40:14 2009
@@ -105,9 +105,8 @@
 
 struct GpakChannelConfig;
 
-#define MAX_CHANNELS_PER_SPAN 32
 struct vpmadt032 {
-	void *context;
+	struct voicebus *vb;
 	struct work_struct work;
 	struct workqueue_struct *wq;
 	int dspid;
@@ -115,8 +114,10 @@
 	unsigned long control;
 	unsigned char curpage;
 	unsigned short version;
-	struct adt_lec_params curecstate[MAX_CHANNELS_PER_SPAN];
-	struct adt_lec_params desiredecstate[MAX_CHANNELS_PER_SPAN];
+	enum adt_companding companding;
+	struct adt_lec_params curecstate[MAX_CHANNELS];
+	spinlock_t change_list_lock;
+	struct list_head change_list;
 	spinlock_t list_lock;
 	/* Commands that are ready to be used. */
 	struct list_head free_cmds;
@@ -124,7 +125,6 @@
 	struct list_head pending_cmds;
 	/* Commands that are currently in progress by the VPM module */
 	struct list_head active_cmds;
-	unsigned char curtone[MAX_CHANNELS_PER_SPAN];
 	struct vpmadt032_options options;
 	void (*setchanconfig_from_state)(struct vpmadt032 *vpm, int channel, struct GpakChannelConfig *chanconfig);
 	/* This must be last */

Modified: freebsd/trunk/drivers/dahdi/voicebus/voicebus.c
URL: http://svnview.digium.com/svn/dahdi/freebsd/trunk/drivers/dahdi/voicebus/voicebus.c?view=diff&rev=7669&r1=7668&r2=7669
==============================================================================
--- freebsd/trunk/drivers/dahdi/voicebus/voicebus.c (original)
+++ freebsd/trunk/drivers/dahdi/voicebus/voicebus.c Tue Dec  8 10:40:14 2009
@@ -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 @@
 	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 @@
 	/*! 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,11 +193,17 @@
 	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
@@ -218,15 +231,13 @@
 #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 @@
 #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 @@
 	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 @@
 		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 @@
 	 */
 #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 @@
 }
 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 @@
 }
 
 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));
+	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;
+
+	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;
 		}
@@ -480,6 +567,15 @@
 	dl->head = 0;
 	dl->tail = 0;
 	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
@@ -519,30 +615,30 @@
 }
 
 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 &= ~CSR9_MMC;
-	__vb_setctl(vb, 0x0048, vb->sdi);
+__vb_sdi_sendbits(struct voicebus *vb, u32 bits, int count, u32 *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;
-
-		__vb_sdi_clk(vb);
+			*sdi &= ~CSR9_MDO;
+
+		__vb_sdi_clk(vb, sdi);
 	}
 }
 
@@ -551,13 +647,14 @@
 {
 	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 @@
 {
 	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 @@
 		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 @@
 	} 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 @@
 {
 	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 @@
 	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,12 +815,44 @@
 }
 #endif
 
+/**
+ * voicebus_transmit - Queue a buffer on the hardware descriptor ring.
+ *
+ */
+int voicebus_transmit(struct voicebus *vb, void *vbb)
+{
+	struct voicebus_descriptor *d;
+	struct voicebus_descriptor_list *dl = &vb->txd;
+
+	d = vb_descriptor(dl, dl->tail);
+
+	if (unlikely(d->buffer1 != vb->idle_vbb_dma_addr)) {
+		if (printk_ratelimit())
+			dev_warn(&vb->pdev->dev, "Dropping tx buffer buffer\n");
+		voicebus_free(vb, vbb);
+		return -EFAULT;
+	}
+
+	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;
+}
+EXPORT_SYMBOL(voicebus_transmit);
+
+/*!
+ * \brief Give a frame to the hardware to use for receiving.
+ *
+ */
 static inline int
-vb_submit(struct voicebus *vb, struct voicebus_descriptor_list *dl, void *vbb)
-{
-	volatile struct voicebus_descriptor *d;
+vb_submit_rxb(struct voicebus *vb, void *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);
 
@@ -738,53 +865,11 @@
 
 	dl->pending[tail] = vbb;
 	dl->tail = (++tail) & DRING_MASK;
-	d->buffer1 = dma_map_single(
-			&vb->pdev->dev, vbb, vb->framesize, dl->direction);
+	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;
-}
-
-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);
-
-/*!
- * \brief Give a frame to the hardware to use for receiving.
- *
- */
-static inline int
-vb_submit_rxb(struct voicebus *vb, void *vbb)
-{
-	return vb_submit(vb, &vb->rxd, vbb);
 }
 
 /*!
@@ -803,13 +888,47 @@
 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 @@
 	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 @@
 	 *  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 @@
 		if (unlikely(NULL == vbb))
 			BUG_ON(1);
 		else
-			vb->handle_transmit(vbb, vb->context);
-
-	}
-	stop_vb_deferred(vb);
+			handle_transmit(vb, vbb);
+
+	}
 
 	VBLOCK(vb);
 	clear_bit(STOP, &vb->flags);
@@ -971,7 +1090,7 @@
 	__vb_tx_demand_poll(vb);
 	VBUNLOCK(vb);
 
-	assert(!vb_is_stopped(vb));
+	BUG_ON(vb_is_stopped(vb));
 
 	return 0;
 }
@@ -1033,8 +1152,6 @@
 int
 voicebus_stop(struct voicebus *vb)
 {
-	assert(!in_interrupt());
-
 	if (vb_is_stopped(vb))
 		return 0;
 
@@ -1043,11 +1160,10 @@
 	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 @@
 }
 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 @@
 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 @@
 	/* 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,102 +1240,290 @@
 EXPORT_SYMBOL(voicebus_release);
 
 static void
-__vb_increase_latency(struct voicebus *vb)
-{
-	static int __warn_once = 1;
+vb_increase_latency(struct voicebus *vb, unsigned int increase)
+{
 	void *vbb;
-	int latency;
-
-	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.
-			 */
-			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;
+	int i;
+
+	if (0 == increase)
+		return;
+
+	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);
+	}
+
+	/* 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);
+	}
+}
+
+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.
+ *
+ * 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 unsigned int __vb_get_default_behind_count(const struct voicebus *vb)
+{

[... 3586 lines stripped ...]



More information about the svn-commits mailing list