[svn-commits] sruffell: linux/trunk r7565 - in /linux/trunk/drivers/dahdi: voicebus/ wctdm2...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Nov 12 13:22:13 CST 2009


Author: sruffell
Date: Thu Nov 12 13:22:06 2009
New Revision: 7565

URL: http://svnview.digium.com/svn/dahdi?view=rev&rev=7565
Log:
voicebus: Fix race when enabling/disabling hardware echocan.

This closes a race condition where it was possible for the driver to
believe it has enabled the VPMADT032 when in fact, it really has not.
This fixes a regression introduced in dahdi-linux 2.2.0.

(issue #15724)

Modified:
    linux/trunk/drivers/dahdi/voicebus/GpakCust.c
    linux/trunk/drivers/dahdi/voicebus/GpakCust.h
    linux/trunk/drivers/dahdi/wctdm24xxp/base.c
    linux/trunk/drivers/dahdi/wcte12xp/base.c

Modified: linux/trunk/drivers/dahdi/voicebus/GpakCust.c
URL: http://svnview.digium.com/svn/dahdi/linux/trunk/drivers/dahdi/voicebus/GpakCust.c?view=diff&rev=7565&r1=7564&r2=7565
==============================================================================
--- linux/trunk/drivers/dahdi/voicebus/GpakCust.c (original)
+++ linux/trunk/drivers/dahdi/voicebus/GpakCust.c Thu Nov 12 13:22:06 2009
@@ -212,15 +212,31 @@
 	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;
@@ -253,6 +269,86 @@
 	}
 	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) {
+		printk(KERN_DEBUG "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) {
+			printk("Unable to disable sw companding on echo "
+			       "cancellation channel %d (reason %d)\n",
+			       channel, res);
+		}
+		gpakAlgControl(vpm->dspid, channel, BypassEcanA, &pstatus);
+	}
+
+	return;
 }
 
 /**
@@ -272,87 +368,67 @@
 {
 	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 >= MAX_CHANNELS_PER_SPAN);
+		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);
@@ -360,9 +436,10 @@
 	/* 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->channel = channo;
+
+	vpmadt032_check_and_schedule_update(vpm, order);
 	return 0;
 }
 EXPORT_SYMBOL(vpmadt032_echocan_create);
@@ -370,15 +447,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);
+	vpmadt032_check_and_schedule_update(vpm, order);
 }
 EXPORT_SYMBOL(vpmadt032_echocan_free);
 
@@ -410,6 +494,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);
@@ -580,6 +666,7 @@
 {
 	unsigned long flags;
 	struct vpmadt032_cmd *cmd;
+	struct change_order *order;
 	LIST_HEAD(local_list);
 
 	BUG_ON(!vpm);
@@ -598,6 +685,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: linux/trunk/drivers/dahdi/voicebus/GpakCust.h
URL: http://svnview.digium.com/svn/dahdi/linux/trunk/drivers/dahdi/voicebus/GpakCust.h?view=diff&rev=7565&r1=7564&r2=7565
==============================================================================
--- linux/trunk/drivers/dahdi/voicebus/GpakCust.h (original)
+++ linux/trunk/drivers/dahdi/voicebus/GpakCust.h Thu Nov 12 13:22:06 2009
@@ -115,8 +115,10 @@
 	unsigned long control;
 	unsigned char curpage;
 	unsigned short version;
+	enum adt_companding companding;
 	struct adt_lec_params curecstate[MAX_CHANNELS_PER_SPAN];
-	struct adt_lec_params desiredecstate[MAX_CHANNELS_PER_SPAN];
+	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;

Modified: linux/trunk/drivers/dahdi/wctdm24xxp/base.c
URL: http://svnview.digium.com/svn/dahdi/linux/trunk/drivers/dahdi/wctdm24xxp/base.c?view=diff&rev=7565&r1=7564&r2=7565
==============================================================================
--- linux/trunk/drivers/dahdi/wctdm24xxp/base.c (original)
+++ linux/trunk/drivers/dahdi/wctdm24xxp/base.c Thu Nov 12 13:22:06 2009
@@ -312,7 +312,8 @@
 	chanconfig->MuteToneB = Disabled;
 	chanconfig->FaxCngDetB = Disabled;
 
-	chanconfig->SoftwareCompand = (vpm->desiredecstate[channel].companding == ADT_COMP_ALAW) ? cmpPCMA : cmpPCMU;
+	chanconfig->SoftwareCompand = (ADT_COMP_ALAW == vpm->companding) ?
+						cmpPCMA : cmpPCMU;
 	chanconfig->FrameRate = rate2ms;
 	p = &chanconfig->EcanParametersA;
 
@@ -422,7 +423,6 @@
 		vpm->curecstate[i].nlp_threshold = vpm->options.vpmnlpthresh;
 		vpm->curecstate[i].nlp_max_suppress = vpm->options.vpmnlpmaxsupp;
 		vpm->curecstate[i].companding = (wc->span.deflaw == DAHDI_LAW_MULAW) ? ADT_COMP_ULAW : ADT_COMP_ALAW;
-		memcpy(&vpm->desiredecstate[i], &vpm->curecstate[i], sizeof(vpm->curecstate[i]));
 
 		/* set_vpmadt032_chanconfig_from_state(&vpm->curecstate[i], &vpm->options, i, &chanconfig); !!! */
 		vpm->setchanconfig_from_state(vpm, i, &chanconfig);

Modified: linux/trunk/drivers/dahdi/wcte12xp/base.c
URL: http://svnview.digium.com/svn/dahdi/linux/trunk/drivers/dahdi/wcte12xp/base.c?view=diff&rev=7565&r1=7564&r2=7565
==============================================================================
--- linux/trunk/drivers/dahdi/wcte12xp/base.c (original)
+++ linux/trunk/drivers/dahdi/wcte12xp/base.c Thu Nov 12 13:22:06 2009
@@ -355,7 +355,6 @@
 		vpm->curecstate[channel].nlp_threshold = vpm->options.vpmnlpthresh;
 		vpm->curecstate[channel].nlp_max_suppress = vpm->options.vpmnlpmaxsupp;
 		vpm->curecstate[channel].companding = (wc->spantype == TYPE_T1) ? ADT_COMP_ULAW : ADT_COMP_ALAW;
-		memcpy(&vpm->desiredecstate[channel], &vpm->curecstate[channel], sizeof(vpm->curecstate[channel]));
 
 		vpm->setchanconfig_from_state(vpm, channel, &chanconfig);
 		if ((res = gpakConfigureChannel(vpm->dspid, channel, tdmToTdm, &chanconfig, &cstatus))) {




More information about the svn-commits mailing list