[PATCH v2 03/17] staging: comedi: quatech_daqp_cs: fix ai cmd timing

H Hartley Sweeten hsweeten at visionengravers.com
Fri Oct 2 00:23:48 UTC 2015


According to the users manual, the conversion timing (scanrate) is fixed
to 100, 50, or 25 kHz. The pacer clock is then used to trigger each scan.

Currently this driver tries to fake other conversion speeds by always
sampling the inputs at 100 kHz and using the pacer clock to trigger each
conversion. It does this by setting the SCANLIST_START bit for each
entry in the scan list. According to the users manual, this bit has to be
set for the first (and ONLY the first) entry of the scan list.

Fix the ai (*do_cmdtest) to properly validate the command timing and allow
the conversion time to be set.

Cleanup the ai (*do_cmd) to set the timing correctly.

Signed-off-by: H Hartley Sweeten <hsweeten at visionengravers.com>
Cc: Ian Abbott <abbotti at mev.co.uk>
Cc: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
---
 drivers/staging/comedi/drivers/quatech_daqp_cs.c | 115 ++++++++---------------
 1 file changed, 40 insertions(+), 75 deletions(-)

diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
index b2effb8..7c8f081 100644
--- a/drivers/staging/comedi/drivers/quatech_daqp_cs.c
+++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
@@ -54,6 +54,8 @@ Devices: [Quatech] DAQP-208 (daqp), DAQP-308
 #include "../comedi_pcmcia.h"
 
 struct daqp_private {
+	unsigned int pacer_div;
+	unsigned char scanrate;
 	int stop;
 
 	enum { semaphore, buffer } interrupt_mode;
@@ -114,9 +116,10 @@ struct daqp_private {
 #define DAQP_COMMAND_RSTQ		0x20
 #define DAQP_COMMAND_STOP		0x10
 #define DAQP_COMMAND_LATCH		0x08
-#define DAQP_COMMAND_100kHz		0x00
-#define DAQP_COMMAND_50kHz		0x02
-#define DAQP_COMMAND_25kHz		0x04
+#define DAQP_COMMAND_SCANRATE(x)	(((x) & 0x3) << 1)
+#define DAQP_COMMAND_SCANRATE_100KHZ	DAQP_COMMAND_SCANRATE(0)
+#define DAQP_COMMAND_SCANRATE_50KHZ	DAQP_COMMAND_SCANRATE(1)
+#define DAQP_COMMAND_SCANRATE_25KHZ	DAQP_COMMAND_SCANRATE(2)
 #define DAQP_COMMAND_FIFO_DATA		0x01
 #define DAQP_COMMAND_FIFO_PROGRAM	0x00
 
@@ -348,25 +351,18 @@ static int daqp_ns_to_timer(unsigned int *ns, unsigned int flags)
 	return timer;
 }
 
-/* cmdtest tests a particular command to see if it is valid.
- * Using the cmdtest ioctl, a user can create a valid cmd
- * and then have it executed by the cmd ioctl.
- *
- * cmdtest returns 1,2,3,4 or 0, depending on which tests
- * the command passes.
- */
-
 static int daqp_ai_cmdtest(struct comedi_device *dev,
-			   struct comedi_subdevice *s, struct comedi_cmd *cmd)
+			   struct comedi_subdevice *s,
+			   struct comedi_cmd *cmd)
 {
+	struct daqp_private *devpriv = dev->private;
 	int err = 0;
 	unsigned int arg;
 
 	/* Step 1 : check if triggers are trivially valid */
 
 	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
-	err |= comedi_check_trigger_src(&cmd->scan_begin_src,
-					TRIG_TIMER | TRIG_FOLLOW);
+	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
 	err |= comedi_check_trigger_src(&cmd->convert_src,
 					TRIG_TIMER | TRIG_NOW);
 	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
@@ -377,7 +373,6 @@ static int daqp_ai_cmdtest(struct comedi_device *dev,
 
 	/* Step 2a : make sure trigger sources are unique */
 
-	err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
 	err |= comedi_check_trigger_is_unique(cmd->convert_src);
 	err |= comedi_check_trigger_is_unique(cmd->stop_src);
 
@@ -390,28 +385,30 @@ static int daqp_ai_cmdtest(struct comedi_device *dev,
 
 	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 
-#define MAX_SPEED	10000	/* 100 kHz - in nanoseconds */
-
-	if (cmd->scan_begin_src == TRIG_TIMER) {
-		err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
-						    MAX_SPEED);
-	}
-
-	/* If both scan_begin and convert are both timer values, the only
-	 * way that can make sense is if the scan time is the number of
-	 * conversions times the convert time
-	 */
-
-	if (cmd->scan_begin_src == TRIG_TIMER && cmd->convert_src == TRIG_TIMER
-	    && cmd->scan_begin_arg != cmd->convert_arg * cmd->scan_end_arg) {
-		err |= -EINVAL;
-	}
-
+	/* 100, 50, and 25 kHz scanrates (conversion times) are supported */
 	if (cmd->convert_src == TRIG_TIMER) {
-		err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
-						    MAX_SPEED);
+		if (cmd->convert_arg <= 10000) {
+			devpriv->scanrate = DAQP_COMMAND_SCANRATE_100KHZ;
+			arg = 10000;	/* 100 kHz (in ns) */
+		} else if (cmd->convert_arg <= 20000) {
+			devpriv->scanrate = DAQP_COMMAND_SCANRATE_50KHZ;
+			arg = 20000;	/* 50 kHz (in ns) */
+		} else {
+			devpriv->scanrate = DAQP_COMMAND_SCANRATE_25KHZ;
+			arg = 40000;	/* 25 kHz (in ns) */
+		}
+		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+	} else {	/* TRIG_NOW */
+		/* use fastest converstion time */
+		devpriv->scanrate = DAQP_COMMAND_SCANRATE_100KHZ;
+		arg = 10000;	/* 100 kHz (in ns) */
 	}
 
+	/* scan begin must be large enough to scan all channels */
+	err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
+	arg *= cmd->chanlist_len;
+	err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, arg);
+
 	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 					   cmd->chanlist_len);
 
@@ -425,17 +422,9 @@ static int daqp_ai_cmdtest(struct comedi_device *dev,
 
 	/* step 4: fix up any arguments */
 
-	if (cmd->scan_begin_src == TRIG_TIMER) {
-		arg = cmd->scan_begin_arg;
-		daqp_ns_to_timer(&arg, cmd->flags);
-		err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
-	}
-
-	if (cmd->convert_src == TRIG_TIMER) {
-		arg = cmd->convert_arg;
-		daqp_ns_to_timer(&arg, cmd->flags);
-		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
-	}
+	arg = cmd->scan_begin_arg;
+	devpriv->pacer_div = daqp_ns_to_timer(&arg, cmd->flags);
+	err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
 
 	if (err)
 		return 4;
@@ -448,9 +437,7 @@ static int daqp_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 	struct daqp_private *devpriv = dev->private;
 	struct comedi_cmd *cmd = &s->async->cmd;
 	int counter;
-	int scanlist_start_on_every_entry;
 	int threshold;
-
 	int i;
 	int v;
 
@@ -465,36 +452,14 @@ static int daqp_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 	/* Reset scan list queue */
 	outb(DAQP_COMMAND_RSTQ, dev->iobase + DAQP_COMMAND);
 
-	/* Program pacer clock
-	 *
-	 * There's two modes we can operate in.  If convert_src is
-	 * TRIG_TIMER, then convert_arg specifies the time between
-	 * each conversion, so we program the pacer clock to that
-	 * frequency and set the SCANLIST_START bit on every scanlist
-	 * entry.  Otherwise, convert_src is TRIG_NOW, which means
-	 * we want the fastest possible conversions, scan_begin_src
-	 * is TRIG_TIMER, and scan_begin_arg specifies the time between
-	 * each scan, so we program the pacer clock to this frequency
-	 * and only set the SCANLIST_START bit on the first entry.
-	 */
-
-	if (cmd->convert_src == TRIG_TIMER) {
-		counter = daqp_ns_to_timer(&cmd->convert_arg, cmd->flags);
-		outb(counter & 0xff, dev->iobase + DAQP_PACER_LOW);
-		outb((counter >> 8) & 0xff, dev->iobase + DAQP_PACER_MID);
-		outb((counter >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH);
-		scanlist_start_on_every_entry = 1;
-	} else {
-		counter = daqp_ns_to_timer(&cmd->scan_begin_arg, cmd->flags);
-		outb(counter & 0xff, dev->iobase + DAQP_PACER_LOW);
-		outb((counter >> 8) & 0xff, dev->iobase + DAQP_PACER_MID);
-		outb((counter >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH);
-		scanlist_start_on_every_entry = 0;
-	}
+	/* Program pacer clock */
+	outb(devpriv->pacer_div & 0xff, dev->iobase + DAQP_PACER_LOW);
+	outb((devpriv->pacer_div >> 8) & 0xff, dev->iobase + DAQP_PACER_MID);
+	outb((devpriv->pacer_div >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH);
 
 	/* Program scan list */
 	for (i = 0; i < cmd->chanlist_len; i++) {
-		int start = (i == 0 || scanlist_start_on_every_entry);
+		int start = (i == 0);
 
 		daqp_ai_set_one_scanlist_entry(dev, cmd->chanlist[i], start);
 	}
@@ -620,7 +585,7 @@ static int daqp_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 	devpriv->interrupt_mode = buffer;
 
 	/* Start conversion */
-	outb(DAQP_COMMAND_ARM | DAQP_COMMAND_FIFO_DATA,
+	outb(DAQP_COMMAND_ARM | DAQP_COMMAND_FIFO_DATA | devpriv->scanrate,
 	     dev->iobase + DAQP_COMMAND);
 
 	return 0;
-- 
2.5.1



More information about the devel mailing list