Commit 1eb85ae8 authored by Ian Abbott's avatar Ian Abbott Committed by Greg Kroah-Hartman

staging: comedi: comedi_test: handle partial scans in timer routine

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: default avatarIan Abbott <abbotti@mev.co.uk>
Reviewed-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e0c6fe12
...@@ -63,7 +63,7 @@ enum waveform_state_bits { ...@@ -63,7 +63,7 @@ enum waveform_state_bits {
/* Data unique to this driver */ /* Data unique to this driver */
struct waveform_private { struct waveform_private {
struct timer_list ai_timer; /* timer for AI commands */ 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_amplitude; /* waveform amplitude in microvolts */
unsigned int wf_period; /* waveform period in microseconds */ unsigned int wf_period; /* waveform period in microseconds */
unsigned int wf_current; /* current time in waveform period */ unsigned int wf_current; /* current time in waveform period */
...@@ -183,46 +183,51 @@ static void waveform_ai_interrupt(unsigned long arg) ...@@ -183,46 +183,51 @@ static void waveform_ai_interrupt(unsigned long arg)
struct comedi_subdevice *s = dev->read_subdev; struct comedi_subdevice *s = dev->read_subdev;
struct comedi_async *async = s->async; struct comedi_async *async = s->async;
struct comedi_cmd *cmd = &async->cmd; struct comedi_cmd *cmd = &async->cmd;
unsigned int i, j; u64 now;
unsigned long elapsed_time; unsigned int nsamples;
unsigned int num_scans; unsigned int time_increment;
/* check command is still active */ /* check command is still active */
if (!test_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits)) if (!test_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits))
return; return;
elapsed_time = ktime_to_us(ktime_get()) - devpriv->ai_last_scan_time; now = ktime_to_us(ktime_get());
num_scans = elapsed_time / devpriv->ai_scan_period; nsamples = comedi_nsamples_left(s, UINT_MAX);
num_scans = comedi_nscans_left(s, num_scans); while (nsamples && devpriv->ai_convert_time < now) {
for (i = 0; i < num_scans; i++) { unsigned int chanspec = cmd->chanlist[async->cur_chan];
unsigned int scan_remain_period = devpriv->ai_scan_period; unsigned short sample;
for (j = 0; j < cmd->chanlist_len; j++) { sample = fake_waveform(dev, CR_CHAN(chanspec),
unsigned short sample; CR_RANGE(chanspec), devpriv->wf_current);
if (comedi_buf_write_samples(s, &sample, 1) == 0)
if (devpriv->wf_current >= devpriv->wf_period) goto overrun;
devpriv->wf_current %= devpriv->wf_period; time_increment = devpriv->ai_convert_period;
sample = fake_waveform(dev, CR_CHAN(cmd->chanlist[j]), if (async->scan_progress == 0) {
CR_RANGE(cmd->chanlist[j]), /* done last conversion in scan, so add dead time */
devpriv->wf_current); time_increment += devpriv->ai_scan_period -
comedi_buf_write_samples(s, &sample, 1); devpriv->ai_convert_period *
devpriv->wf_current += devpriv->ai_convert_period; cmd->scan_end_arg;
scan_remain_period -= devpriv->ai_convert_period;
} }
devpriv->wf_current += scan_remain_period; devpriv->wf_current += time_increment;
devpriv->ai_last_scan_time += devpriv->ai_scan_period; 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) { if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) {
async->events |= COMEDI_CB_EOA; async->events |= COMEDI_CB_EOA;
} else { } else {
if (devpriv->ai_convert_time > now)
time_increment = devpriv->ai_convert_time - now;
else
time_increment = 1;
mod_timer(&devpriv->ai_timer, 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); comedi_handle_events(dev, s);
} }
...@@ -332,6 +337,7 @@ static int waveform_ai_cmd(struct comedi_device *dev, ...@@ -332,6 +337,7 @@ static int waveform_ai_cmd(struct comedi_device *dev,
{ {
struct waveform_private *devpriv = dev->private; struct waveform_private *devpriv = dev->private;
struct comedi_cmd *cmd = &s->async->cmd; struct comedi_cmd *cmd = &s->async->cmd;
unsigned int first_convert_time;
u64 wf_current; u64 wf_current;
if (cmd->flags & CMDF_PRIORITY) { if (cmd->flags & CMDF_PRIORITY) {
...@@ -352,13 +358,30 @@ static int waveform_ai_cmd(struct comedi_device *dev, ...@@ -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_scan_period = cmd->scan_begin_arg / NSEC_PER_USEC;
} }
devpriv->ai_last_scan_time = ktime_to_us(ktime_get()); /*
/* Determine time within waveform period. */ * Simulate first conversion to occur at convert period after
wf_current = devpriv->ai_last_scan_time; * 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); 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 = 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 */ /* mark command as active */
smp_mb__before_atomic(); smp_mb__before_atomic();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment