staging: comedi: comedi_test: handle partial scans in timer routine
authorIan Abbott <abbotti@mev.co.uk>
Tue, 27 Oct 2015 16:59:23 +0000 (16:59 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 28 Oct 2015 23:58:36 +0000 (08:58 +0900)
For asynchronous command handling on the analog input subdevice, a
kernel timer routine is used to generate the fake waveform data.  A
"scan" consists of a number of conversions separated in time by a
conversion period.  Successive scans are separated in time by a scan
period, which is at least the conversion period multiplied by the number
of conversions per scan.  Currently, the timer routine does not generate
any data until the end of a scan period, generating whole scans of data
at a time.  Change it to generate data at the end of each conversion
period, with an extra delay after the final conversion in each scan if
necessary.  Use new member `ai_convert_time` in the private data
structure `struct waveform_private` to keep track of when the next
conversion is due.  This replaces the old member `ai_last_scan_time`
which kept track of the time of the previous scan.

Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Reviewed-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/drivers/comedi_test.c

index 468847a5da009641f0b81d0b16d1f6ef965fab53..318340c6f247fb0db3ec695cd69f49826c58e941 100644 (file)
@@ -63,7 +63,7 @@ enum waveform_state_bits {
 /* Data unique to this driver */
 struct waveform_private {
        struct timer_list ai_timer;     /* timer for AI commands */
-       u64 ai_last_scan_time;          /* time of last AI scan in usec */
+       u64 ai_convert_time;            /* time of next AI conversion in usec */
        unsigned int wf_amplitude;      /* waveform amplitude in microvolts */
        unsigned int wf_period;         /* waveform period in microseconds */
        unsigned int wf_current;        /* current time in waveform period */
@@ -183,46 +183,51 @@ static void waveform_ai_interrupt(unsigned long arg)
        struct comedi_subdevice *s = dev->read_subdev;
        struct comedi_async *async = s->async;
        struct comedi_cmd *cmd = &async->cmd;
-       unsigned int i, j;
-       unsigned long elapsed_time;
-       unsigned int num_scans;
+       u64 now;
+       unsigned int nsamples;
+       unsigned int time_increment;
 
        /* check command is still active */
        if (!test_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits))
                return;
 
-       elapsed_time = ktime_to_us(ktime_get()) - devpriv->ai_last_scan_time;
-       num_scans = elapsed_time / devpriv->ai_scan_period;
-
-       num_scans = comedi_nscans_left(s, num_scans);
-       for (i = 0; i < num_scans; i++) {
-               unsigned int scan_remain_period = devpriv->ai_scan_period;
-
-               for (j = 0; j < cmd->chanlist_len; j++) {
-                       unsigned short sample;
-
-                       if (devpriv->wf_current >= devpriv->wf_period)
-                               devpriv->wf_current %= devpriv->wf_period;
-                       sample = fake_waveform(dev, CR_CHAN(cmd->chanlist[j]),
-                                              CR_RANGE(cmd->chanlist[j]),
-                                              devpriv->wf_current);
-                       comedi_buf_write_samples(s, &sample, 1);
-                       devpriv->wf_current += devpriv->ai_convert_period;
-                       scan_remain_period -= devpriv->ai_convert_period;
+       now = ktime_to_us(ktime_get());
+       nsamples = comedi_nsamples_left(s, UINT_MAX);
+
+       while (nsamples && devpriv->ai_convert_time < now) {
+               unsigned int chanspec = cmd->chanlist[async->cur_chan];
+               unsigned short sample;
+
+               sample = fake_waveform(dev, CR_CHAN(chanspec),
+                                      CR_RANGE(chanspec), devpriv->wf_current);
+               if (comedi_buf_write_samples(s, &sample, 1) == 0)
+                       goto overrun;
+               time_increment = devpriv->ai_convert_period;
+               if (async->scan_progress == 0) {
+                       /* done last conversion in scan, so add dead time */
+                       time_increment += devpriv->ai_scan_period -
+                                         devpriv->ai_convert_period *
+                                         cmd->scan_end_arg;
                }
-               devpriv->wf_current += scan_remain_period;
-               devpriv->ai_last_scan_time += devpriv->ai_scan_period;
+               devpriv->wf_current += time_increment;
+               if (devpriv->wf_current >= devpriv->wf_period)
+                       devpriv->wf_current %= devpriv->wf_period;
+               devpriv->ai_convert_time += time_increment;
+               nsamples--;
        }
-       if (devpriv->wf_current >= devpriv->wf_period)
-               devpriv->wf_current %= devpriv->wf_period;
 
        if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) {
                async->events |= COMEDI_CB_EOA;
        } else {
+               if (devpriv->ai_convert_time > now)
+                       time_increment = devpriv->ai_convert_time - now;
+               else
+                       time_increment = 1;
                mod_timer(&devpriv->ai_timer,
-                         jiffies + usecs_to_jiffies(devpriv->ai_scan_period));
+                         jiffies + usecs_to_jiffies(time_increment));
        }
 
+overrun:
        comedi_handle_events(dev, s);
 }
 
@@ -332,6 +337,7 @@ static int waveform_ai_cmd(struct comedi_device *dev,
 {
        struct waveform_private *devpriv = dev->private;
        struct comedi_cmd *cmd = &s->async->cmd;
+       unsigned int first_convert_time;
        u64 wf_current;
 
        if (cmd->flags & CMDF_PRIORITY) {
@@ -352,13 +358,30 @@ static int waveform_ai_cmd(struct comedi_device *dev,
                devpriv->ai_scan_period = cmd->scan_begin_arg / NSEC_PER_USEC;
        }
 
-       devpriv->ai_last_scan_time = ktime_to_us(ktime_get());
-       /* Determine time within waveform period. */
-       wf_current = devpriv->ai_last_scan_time;
+       /*
+        * Simulate first conversion to occur at convert period after
+        * conversion timer starts.  If scan_begin_src is TRIG_FOLLOW, assume
+        * the conversion timer starts immediately.  If scan_begin_src is
+        * TRIG_TIMER, assume the conversion timer starts after the scan
+        * period.
+        */
+       first_convert_time = devpriv->ai_convert_period;
+       if (cmd->scan_begin_src == TRIG_TIMER)
+               first_convert_time += devpriv->ai_scan_period;
+       devpriv->ai_convert_time = ktime_to_us(ktime_get()) +
+                                  first_convert_time;
+
+       /* Determine time within waveform period at time of conversion. */
+       wf_current = devpriv->ai_convert_time;
        devpriv->wf_current = do_div(wf_current, devpriv->wf_period);
 
+       /*
+        * Schedule timer to expire just after first conversion time.
+        * Seem to need an extra jiffy here, otherwise timer expires slightly
+        * early!
+        */
        devpriv->ai_timer.expires =
-               jiffies + usecs_to_jiffies(devpriv->ai_scan_period);
+               jiffies + usecs_to_jiffies(devpriv->ai_convert_period) + 1;
 
        /* mark command as active */
        smp_mb__before_atomic();