aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIan Abbott <abbotti@mev.co.uk>2015-10-27 12:59:23 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-10-28 19:58:36 -0400
commit1eb85ae8574eac40463ca3bfcef4608f7a374bd0 (patch)
treec1ebf965bafc270fd2c3198527a5cc73e1e92770
parente0c6fe1294f4931af263f0386fb45943451e8b4f (diff)
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: Ian Abbott <abbotti@mev.co.uk> Reviewed-by: H Hartley Sweeten <hsweeten@visionengravers.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/staging/comedi/drivers/comedi_test.c85
1 files changed, 54 insertions, 31 deletions
diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c
index 468847a5da00..318340c6f247 100644
--- a/drivers/staging/comedi/drivers/comedi_test.c
+++ b/drivers/staging/comedi/drivers/comedi_test.c
@@ -63,7 +63,7 @@ enum waveform_state_bits {
63/* Data unique to this driver */ 63/* Data unique to this driver */
64struct waveform_private { 64struct waveform_private {
65 struct timer_list ai_timer; /* timer for AI commands */ 65 struct timer_list ai_timer; /* timer for AI commands */
66 u64 ai_last_scan_time; /* time of last AI scan in usec */ 66 u64 ai_convert_time; /* time of next AI conversion in usec */
67 unsigned int wf_amplitude; /* waveform amplitude in microvolts */ 67 unsigned int wf_amplitude; /* waveform amplitude in microvolts */
68 unsigned int wf_period; /* waveform period in microseconds */ 68 unsigned int wf_period; /* waveform period in microseconds */
69 unsigned int wf_current; /* current time in waveform period */ 69 unsigned int wf_current; /* current time in waveform period */
@@ -183,46 +183,51 @@ static void waveform_ai_interrupt(unsigned long arg)
183 struct comedi_subdevice *s = dev->read_subdev; 183 struct comedi_subdevice *s = dev->read_subdev;
184 struct comedi_async *async = s->async; 184 struct comedi_async *async = s->async;
185 struct comedi_cmd *cmd = &async->cmd; 185 struct comedi_cmd *cmd = &async->cmd;
186 unsigned int i, j; 186 u64 now;
187 unsigned long elapsed_time; 187 unsigned int nsamples;
188 unsigned int num_scans; 188 unsigned int time_increment;
189 189
190 /* check command is still active */ 190 /* check command is still active */
191 if (!test_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits)) 191 if (!test_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits))
192 return; 192 return;
193 193
194 elapsed_time = ktime_to_us(ktime_get()) - devpriv->ai_last_scan_time; 194 now = ktime_to_us(ktime_get());
195 num_scans = elapsed_time / devpriv->ai_scan_period; 195 nsamples = comedi_nsamples_left(s, UINT_MAX);
196 196
197 num_scans = comedi_nscans_left(s, num_scans); 197 while (nsamples && devpriv->ai_convert_time < now) {
198 for (i = 0; i < num_scans; i++) { 198 unsigned int chanspec = cmd->chanlist[async->cur_chan];
199 unsigned int scan_remain_period = devpriv->ai_scan_period; 199 unsigned short sample;
200 200
201 for (j = 0; j < cmd->chanlist_len; j++) { 201 sample = fake_waveform(dev, CR_CHAN(chanspec),
202 unsigned short sample; 202 CR_RANGE(chanspec), devpriv->wf_current);
203 203 if (comedi_buf_write_samples(s, &sample, 1) == 0)
204 if (devpriv->wf_current >= devpriv->wf_period) 204 goto overrun;
205 devpriv->wf_current %= devpriv->wf_period; 205 time_increment = devpriv->ai_convert_period;
206 sample = fake_waveform(dev, CR_CHAN(cmd->chanlist[j]), 206 if (async->scan_progress == 0) {
207 CR_RANGE(cmd->chanlist[j]), 207 /* done last conversion in scan, so add dead time */
208 devpriv->wf_current); 208 time_increment += devpriv->ai_scan_period -
209 comedi_buf_write_samples(s, &sample, 1); 209 devpriv->ai_convert_period *
210 devpriv->wf_current += devpriv->ai_convert_period; 210 cmd->scan_end_arg;
211 scan_remain_period -= devpriv->ai_convert_period;
212 } 211 }
213 devpriv->wf_current += scan_remain_period; 212 devpriv->wf_current += time_increment;
214 devpriv->ai_last_scan_time += devpriv->ai_scan_period; 213 if (devpriv->wf_current >= devpriv->wf_period)
214 devpriv->wf_current %= devpriv->wf_period;
215 devpriv->ai_convert_time += time_increment;
216 nsamples--;
215 } 217 }
216 if (devpriv->wf_current >= devpriv->wf_period)
217 devpriv->wf_current %= devpriv->wf_period;
218 218
219 if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) { 219 if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) {
220 async->events |= COMEDI_CB_EOA; 220 async->events |= COMEDI_CB_EOA;
221 } else { 221 } else {
222 if (devpriv->ai_convert_time > now)
223 time_increment = devpriv->ai_convert_time - now;
224 else
225 time_increment = 1;
222 mod_timer(&devpriv->ai_timer, 226 mod_timer(&devpriv->ai_timer,
223 jiffies + usecs_to_jiffies(devpriv->ai_scan_period)); 227 jiffies + usecs_to_jiffies(time_increment));
224 } 228 }
225 229
230overrun:
226 comedi_handle_events(dev, s); 231 comedi_handle_events(dev, s);
227} 232}
228 233
@@ -332,6 +337,7 @@ static int waveform_ai_cmd(struct comedi_device *dev,
332{ 337{
333 struct waveform_private *devpriv = dev->private; 338 struct waveform_private *devpriv = dev->private;
334 struct comedi_cmd *cmd = &s->async->cmd; 339 struct comedi_cmd *cmd = &s->async->cmd;
340 unsigned int first_convert_time;
335 u64 wf_current; 341 u64 wf_current;
336 342
337 if (cmd->flags & CMDF_PRIORITY) { 343 if (cmd->flags & CMDF_PRIORITY) {
@@ -352,13 +358,30 @@ static int waveform_ai_cmd(struct comedi_device *dev,
352 devpriv->ai_scan_period = cmd->scan_begin_arg / NSEC_PER_USEC; 358 devpriv->ai_scan_period = cmd->scan_begin_arg / NSEC_PER_USEC;
353 } 359 }
354 360
355 devpriv->ai_last_scan_time = ktime_to_us(ktime_get()); 361 /*
356 /* Determine time within waveform period. */ 362 * Simulate first conversion to occur at convert period after
357 wf_current = devpriv->ai_last_scan_time; 363 * conversion timer starts. If scan_begin_src is TRIG_FOLLOW, assume
364 * the conversion timer starts immediately. If scan_begin_src is
365 * TRIG_TIMER, assume the conversion timer starts after the scan
366 * period.
367 */
368 first_convert_time = devpriv->ai_convert_period;
369 if (cmd->scan_begin_src == TRIG_TIMER)
370 first_convert_time += devpriv->ai_scan_period;
371 devpriv->ai_convert_time = ktime_to_us(ktime_get()) +
372 first_convert_time;
373
374 /* Determine time within waveform period at time of conversion. */
375 wf_current = devpriv->ai_convert_time;
358 devpriv->wf_current = do_div(wf_current, devpriv->wf_period); 376 devpriv->wf_current = do_div(wf_current, devpriv->wf_period);
359 377
378 /*
379 * Schedule timer to expire just after first conversion time.
380 * Seem to need an extra jiffy here, otherwise timer expires slightly
381 * early!
382 */
360 devpriv->ai_timer.expires = 383 devpriv->ai_timer.expires =
361 jiffies + usecs_to_jiffies(devpriv->ai_scan_period); 384 jiffies + usecs_to_jiffies(devpriv->ai_convert_period) + 1;
362 385
363 /* mark command as active */ 386 /* mark command as active */
364 smp_mb__before_atomic(); 387 smp_mb__before_atomic();