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@xxxxxxxxxxxxxxxxxxx> Cc: Ian Abbott <abbotti@xxxxxxxxx> Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> --- 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 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel