diff options
author | Ian Abbott <abbotti@mev.co.uk> | 2015-10-27 12:59:23 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-10-28 19:58:36 -0400 |
commit | 1eb85ae8574eac40463ca3bfcef4608f7a374bd0 (patch) | |
tree | c1ebf965bafc270fd2c3198527a5cc73e1e92770 | |
parent | e0c6fe1294f4931af263f0386fb45943451e8b4f (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.c | 85 |
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 */ |
64 | struct waveform_private { | 64 | struct 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 | ||
230 | overrun: | ||
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(); |