Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1256972 > unrolled thread
| Started by | Ian Abbott <abbotti@mev.co.uk> |
|---|---|
| First post | 2015-10-27 18:10 +0100 |
| Last post | 2015-10-29 00:20 +0100 |
| Articles | 9 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH 00/16] staging: comedi: comedi_test: enhancements Ian Abbott <abbotti@mev.co.uk> - 2015-10-27 18:10 +0100
[PATCH 11/16] staging: comedi: comedi_test: make timer rate similar to scan rate Ian Abbott <abbotti@mev.co.uk> - 2015-10-27 18:10 +0100
[PATCH 08/16] staging: comedi: comedi_test: simplify time since last AI scan Ian Abbott <abbotti@mev.co.uk> - 2015-10-27 18:10 +0100
[PATCH 05/16] staging: comedi: comedi_test: support scan_begin_src == TRIG_FOLLOW Ian Abbott <abbotti@mev.co.uk> - 2015-10-27 18:10 +0100
[PATCH 15/16] staging: comedi: comedi_test: rename waveform_ai_interrupt() Ian Abbott <abbotti@mev.co.uk> - 2015-10-27 18:10 +0100
[PATCH 06/16] staging: comedi: comedi_test: move modulo operations for waveform Ian Abbott <abbotti@mev.co.uk> - 2015-10-27 18:10 +0100
[PATCH 14/16] staging: comedi: comedi_test: handle partial scans in timer routine Ian Abbott <abbotti@mev.co.uk> - 2015-10-27 18:10 +0100
[PATCH 09/16] staging: comedi: comedi_test: rename members for AI commands Ian Abbott <abbotti@mev.co.uk> - 2015-10-27 18:10 +0100
RE: [PATCH 00/16] staging: comedi: comedi_test: enhancements Hartley Sweeten <HartleyS@visionengravers.com> - 2015-10-29 00:20 +0100
| From | Ian Abbott <abbotti@mev.co.uk> |
|---|---|
| Date | 2015-10-27 18:10 +0100 |
| Subject | [PATCH 00/16] staging: comedi: comedi_test: enhancements |
| Message-ID | <qofUZ-7TL-7@gated-at.bofh.it> |
The "comedi_test" module is a driver for a dummy COMEDI device. It has an analog input subdevice and an analog output subdevice. The analog input subdevice supports COMEDI asynchronous acquisition commands using waveform generators to generate the input data for each channel. A kernel timer is used to driver the acquisition. This series of patches cleans up the driver, enhances the existing asynchronous command support on the analog input subdevice, and adds asynchronous command support on the analog output subdevice. 01) staging: comedi: comedi_test: reformat multi-line comments 02) staging: comedi: comedi_test: saturate fake waveform values 03) staging: comedi: comedi_test: remove nano_per_micro 04) staging: comedi: comedi_test: limit maximum convert_arg 05) staging: comedi: comedi_test: support scan_begin_src == TRIG_FOLLOW 06) staging: comedi: comedi_test: move modulo operations for waveform 07) staging: comedi: comedi_test: use unsigned int for waveform timing 08) staging: comedi: comedi_test: simplify time since last AI scan 09) staging: comedi: comedi_test: rename members for AI commands 10) staging: comedi: comedi_test: rename waveform members 11) staging: comedi: comedi_test: make timer rate similar to scan rate 12) staging: comedi: comedi_test: use unsigned short for loopback values 13) staging: comedi: comedi_test: allow read-back of AO channels 14) staging: comedi: comedi_test: handle partial scans in timer routine 15) staging: comedi: comedi_test: rename waveform_ai_interrupt() 16) staging: comedi: comedi_test: implement commands on AO subdevice drivers/staging/comedi/drivers/comedi_test.c | 565 ++++++++++++++++++++------- 1 file changed, 416 insertions(+), 149 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [next] | [standalone]
| From | Ian Abbott <abbotti@mev.co.uk> |
|---|---|
| Date | 2015-10-27 18:10 +0100 |
| Subject | [PATCH 11/16] staging: comedi: comedi_test: make timer rate similar to scan rate |
| Message-ID | <qofV1-7TL-51@gated-at.bofh.it> |
| In reply to | #1256972 |
The asynchronous command handling for the analog input subdevice uses a
kernel timer which expires approximately `HZ` times a second. However,
it only needs to do anything after each scan period. Set the timer to
expire just after the next scan period.
Although the timer expiry function `waveform_ai_interrupt()` uses
precise time values to generate the fake waveforms used to generate the
data, those time values are constructed in a precise sequence, and do
not depend on the time the timer expiry function is actually called. So
the timer expiry rate does not have to be very precise.
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
---
drivers/staging/comedi/drivers/comedi_test.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c
index 1b3ad7f..9655dc3 100644
--- a/drivers/staging/comedi/drivers/comedi_test.c
+++ b/drivers/staging/comedi/drivers/comedi_test.c
@@ -52,6 +52,7 @@
#include <linux/timer.h>
#include <linux/ktime.h>
+#include <linux/jiffies.h>
#define N_CHANS 8
@@ -215,10 +216,12 @@ static void waveform_ai_interrupt(unsigned long arg)
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;
- else
- mod_timer(&devpriv->ai_timer, jiffies + 1);
+ } else {
+ mod_timer(&devpriv->ai_timer,
+ jiffies + usecs_to_jiffies(devpriv->ai_scan_period));
+ }
comedi_handle_events(dev, s);
}
@@ -354,7 +357,9 @@ static int waveform_ai_cmd(struct comedi_device *dev,
wf_current = devpriv->ai_last_scan_time;
devpriv->wf_current = do_div(wf_current, devpriv->wf_period);
- devpriv->ai_timer.expires = jiffies + 1;
+ devpriv->ai_timer.expires =
+ jiffies + usecs_to_jiffies(devpriv->ai_scan_period);
+
/* mark command as active */
smp_mb__before_atomic();
set_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits);
--
2.6.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Ian Abbott <abbotti@mev.co.uk> |
|---|---|
| Date | 2015-10-27 18:10 +0100 |
| Subject | [PATCH 08/16] staging: comedi: comedi_test: simplify time since last AI scan |
| Message-ID | <qofV1-7TL-49@gated-at.bofh.it> |
| In reply to | #1256972 |
The private data structure `struct waveform_private` currently uses
member `last` to remember the time of the last timer interrupt, and the
member `usec_remainder` to keep track of how far into a simulated scan
the interrupt occurred. Replace these with a single new member
`ai_last_scan_time` that records the time of the last scan. This
simplifies the calculation of the number of scans to simulate in the
timer routine, `waveform_ai_interrupt()`.
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
---
drivers/staging/comedi/drivers/comedi_test.c | 27 ++++++++++-----------------
1 file changed, 10 insertions(+), 17 deletions(-)
diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c
index 158e090..78fde3a 100644
--- a/drivers/staging/comedi/drivers/comedi_test.c
+++ b/drivers/staging/comedi/drivers/comedi_test.c
@@ -62,11 +62,10 @@ enum waveform_state_bits {
/* Data unique to this driver */
struct waveform_private {
struct timer_list timer;
- ktime_t last; /* time last timer interrupt occurred */
+ u64 ai_last_scan_time; /* time of last AI scan in usec */
unsigned int uvolt_amplitude; /* waveform amplitude in microvolts */
unsigned int usec_period; /* waveform period in microseconds */
unsigned int usec_current; /* current time (mod waveform period) */
- unsigned long usec_remainder; /* usec since last scan */
unsigned long state_bits;
unsigned int scan_period; /* scan period in usec */
unsigned int convert_period; /* conversion period in usec */
@@ -184,27 +183,19 @@ static void waveform_ai_interrupt(unsigned long arg)
struct comedi_async *async = s->async;
struct comedi_cmd *cmd = &async->cmd;
unsigned int i, j;
- /* all times in microsec */
unsigned long elapsed_time;
unsigned int num_scans;
- ktime_t now;
/* check command is still active */
if (!test_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits))
return;
- now = ktime_get();
-
- elapsed_time = ktime_to_us(ktime_sub(now, devpriv->last));
- devpriv->last = now;
- num_scans =
- (devpriv->usec_remainder + elapsed_time) / devpriv->scan_period;
- devpriv->usec_remainder =
- (devpriv->usec_remainder + elapsed_time) % devpriv->scan_period;
+ elapsed_time = ktime_to_us(ktime_get()) - devpriv->ai_last_scan_time;
+ num_scans = elapsed_time / devpriv->scan_period;
num_scans = comedi_nscans_left(s, num_scans);
for (i = 0; i < num_scans; i++) {
- unsigned long scan_remain_period = devpriv->scan_period;
+ unsigned int scan_remain_period = devpriv->scan_period;
for (j = 0; j < cmd->chanlist_len; j++) {
unsigned short sample;
@@ -219,6 +210,7 @@ static void waveform_ai_interrupt(unsigned long arg)
scan_remain_period -= devpriv->convert_period;
}
devpriv->usec_current += scan_remain_period;
+ devpriv->ai_last_scan_time += devpriv->scan_period;
}
if (devpriv->usec_current >= devpriv->usec_period)
devpriv->usec_current %= devpriv->usec_period;
@@ -337,6 +329,7 @@ static int waveform_ai_cmd(struct comedi_device *dev,
{
struct waveform_private *devpriv = dev->private;
struct comedi_cmd *cmd = &s->async->cmd;
+ u64 usec_current;
if (cmd->flags & CMDF_PRIORITY) {
dev_err(dev->class_dev,
@@ -356,10 +349,10 @@ static int waveform_ai_cmd(struct comedi_device *dev,
devpriv->scan_period = cmd->scan_begin_arg / NSEC_PER_USEC;
}
- devpriv->last = ktime_get();
- devpriv->usec_current =
- ((u32)ktime_to_us(devpriv->last)) % devpriv->usec_period;
- devpriv->usec_remainder = 0;
+ devpriv->ai_last_scan_time = ktime_to_us(ktime_get());
+ /* Determine time within waveform period. */
+ usec_current = devpriv->ai_last_scan_time;
+ devpriv->usec_current = do_div(usec_current, devpriv->usec_period);
devpriv->timer.expires = jiffies + 1;
/* mark command as active */
--
2.6.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Ian Abbott <abbotti@mev.co.uk> |
|---|---|
| Date | 2015-10-27 18:10 +0100 |
| Subject | [PATCH 05/16] staging: comedi: comedi_test: support scan_begin_src == TRIG_FOLLOW |
| Message-ID | <qofV1-7TL-61@gated-at.bofh.it> |
| In reply to | #1256972 |
It is quite common for COMEDI subdevices that support commands to
support setting `scan_begin_src` to `TRIG_FOLLOW`. This means the next
scan begins once all conversions in the current scan are complete.
Support the following timing combinations for the AI subdevice:
scan_begin_src == TRIG_TIMER && convert_src == TRIG_TIMER
scan_begin_src == TRIG_TIMER && convert_src == TRIG_NOW
scan_begin_src == TRIG_FOLLOW && convert_src == TRIG_TIMER
The actual scan period in microseconds is stored in the `scan_period`
member of the private data structure `struct waveform_private`. An
`unsigned int` is still wide enough, because the conversion period is no
more than `UINT_MAX / 1000` microseconds and the number of conversions
is no more than 16 (`N_CHANS * 2`).
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
---
drivers/staging/comedi/drivers/comedi_test.c | 64 +++++++++++++++++++---------
1 file changed, 44 insertions(+), 20 deletions(-)
diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c
index f011fbd..cc35bd6 100644
--- a/drivers/staging/comedi/drivers/comedi_test.c
+++ b/drivers/staging/comedi/drivers/comedi_test.c
@@ -239,7 +239,8 @@ static int waveform_ai_cmdtest(struct comedi_device *dev,
/* 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);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_TIMER);
err |= comedi_check_trigger_src(&cmd->convert_src,
TRIG_NOW | TRIG_TIMER);
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
@@ -255,6 +256,9 @@ static int waveform_ai_cmdtest(struct comedi_device *dev,
/* Step 2b : and mutually compatible */
+ if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
+ err |= -EINVAL; /* scan period would be 0 */
+
if (err)
return 2;
@@ -262,11 +266,21 @@ static int waveform_ai_cmdtest(struct comedi_device *dev,
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
- if (cmd->convert_src == TRIG_NOW)
+ if (cmd->convert_src == TRIG_NOW) {
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ } else { /* cmd->convert_src == TRIG_TIMER */
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ NSEC_PER_USEC);
+ }
+ }
- err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
- NSEC_PER_USEC);
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ } else { /* cmd->scan_begin_src == TRIG_TIMER */
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ NSEC_PER_USEC);
+ }
err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
@@ -274,7 +288,7 @@ static int waveform_ai_cmdtest(struct comedi_device *dev,
if (cmd->stop_src == TRIG_COUNT)
err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
- else /* TRIG_NONE */
+ else /* cmd->stop_src == TRIG_NONE */
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
if (err)
@@ -288,22 +302,27 @@ static int waveform_ai_cmdtest(struct comedi_device *dev,
arg = min(arg,
rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC));
arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC);
- /* limit convert_arg to keep scan_begin_arg in range */
- limit = UINT_MAX / cmd->scan_end_arg;
- limit = rounddown(limit, (unsigned int)NSEC_PER_SEC);
- arg = min(arg, limit);
+ if (cmd->scan_begin_arg == TRIG_TIMER) {
+ /* limit convert_arg to keep scan_begin_arg in range */
+ limit = UINT_MAX / cmd->scan_end_arg;
+ limit = rounddown(limit, (unsigned int)NSEC_PER_SEC);
+ arg = min(arg, limit);
+ }
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
}
- /* round scan_begin_arg to nearest microsecond */
- arg = cmd->scan_begin_arg;
- arg = min(arg, rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC));
- arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC);
- if (cmd->convert_src == TRIG_TIMER) {
- /* but ensure scan_begin_arg is large enough */
- arg = max(arg, cmd->convert_arg * cmd->scan_end_arg);
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* round scan_begin_arg to nearest microsecond */
+ arg = cmd->scan_begin_arg;
+ arg = min(arg,
+ rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC));
+ arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC);
+ if (cmd->convert_src == TRIG_TIMER) {
+ /* but ensure scan_begin_arg is large enough */
+ arg = max(arg, cmd->convert_arg * cmd->scan_end_arg);
+ }
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
}
- err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
if (err)
return 4;
@@ -323,13 +342,18 @@ static int waveform_ai_cmd(struct comedi_device *dev,
return -1;
}
- devpriv->scan_period = cmd->scan_begin_arg / NSEC_PER_USEC;
-
if (cmd->convert_src == TRIG_NOW)
devpriv->convert_period = 0;
- else /* TRIG_TIMER */
+ else /* cmd->convert_src == TRIG_TIMER */
devpriv->convert_period = cmd->convert_arg / NSEC_PER_USEC;
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ devpriv->scan_period = devpriv->convert_period *
+ cmd->scan_end_arg;
+ } else { /* cmd->scan_begin_src == TRIG_TIMER */
+ devpriv->scan_period = cmd->scan_begin_arg / NSEC_PER_USEC;
+ }
+
devpriv->last = ktime_get();
devpriv->usec_current =
((u32)ktime_to_us(devpriv->last)) % devpriv->usec_period;
--
2.6.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Ian Abbott <abbotti@mev.co.uk> |
|---|---|
| Date | 2015-10-27 18:10 +0100 |
| Subject | [PATCH 15/16] staging: comedi: comedi_test: rename waveform_ai_interrupt() |
| Message-ID | <qofV1-7TL-63@gated-at.bofh.it> |
| In reply to | #1256972 |
`waveform_ai_interrupt()` is a timer expiry function used to generate
fake waveform data for an analog input subdevice. Rename it to
`waveform_ai_timer()`.
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
---
drivers/staging/comedi/drivers/comedi_test.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c
index 318340c..14a0b62 100644
--- a/drivers/staging/comedi/drivers/comedi_test.c
+++ b/drivers/staging/comedi/drivers/comedi_test.c
@@ -176,7 +176,7 @@ static unsigned short fake_waveform(struct comedi_device *dev,
* It should run in the background; therefore it is scheduled by
* a timer mechanism.
*/
-static void waveform_ai_interrupt(unsigned long arg)
+static void waveform_ai_timer(unsigned long arg)
{
struct comedi_device *dev = (struct comedi_device *)arg;
struct waveform_private *devpriv = dev->private;
@@ -486,8 +486,7 @@ static int waveform_attach(struct comedi_device *dev,
for (i = 0; i < s->n_chan; i++)
devpriv->ao_loopbacks[i] = s->maxdata / 2;
- setup_timer(&devpriv->ai_timer, waveform_ai_interrupt,
- (unsigned long)dev);
+ setup_timer(&devpriv->ai_timer, waveform_ai_timer, (unsigned long)dev);
dev_info(dev->class_dev,
"%s: %u microvolt, %u microsecond waveform attached\n",
--
2.6.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Ian Abbott <abbotti@mev.co.uk> |
|---|---|
| Date | 2015-10-27 18:10 +0100 |
| Subject | [PATCH 06/16] staging: comedi: comedi_test: move modulo operations for waveform |
| Message-ID | <qofV1-7TL-65@gated-at.bofh.it> |
| In reply to | #1256972 |
The fake waveform generator functions, `fake_sawtooth()` and
`fake_squarewave()`, called from `fake_waveform()`, have a
`current_time` parameter which is the time since the start of a waveform
period. The parameter value may be greater than the waveform period so
they do a modulo operation to bring it into range. Do the modulo
operations outside the functions in `waveform_ai_interrupt()` so that
the waveform generator functions always get a `current_time` parameter
less than the waveform period and do not have to do the modulo operation
themselves. Also, only do the modulo operations when the time since the
start of a waveform exceeds the waveform period. Usually, several
samples are produced in each waveform period and modulo operations are
typically more expensive than a simple comparison.
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
---
drivers/staging/comedi/drivers/comedi_test.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c
index cc35bd6..0215228 100644
--- a/drivers/staging/comedi/drivers/comedi_test.c
+++ b/drivers/staging/comedi/drivers/comedi_test.c
@@ -97,7 +97,6 @@ static unsigned short fake_sawtooth(struct comedi_device *dev,
binary_amplitude *= devpriv->uvolt_amplitude;
do_div(binary_amplitude, krange->max - krange->min);
- current_time %= devpriv->usec_period;
value = current_time;
value *= binary_amplitude * 2;
do_div(value, devpriv->usec_period);
@@ -125,7 +124,6 @@ static unsigned short fake_squarewave(struct comedi_device *dev,
const struct comedi_krange *krange =
&s->range_table->range[range_index];
- current_time %= devpriv->usec_period;
value = s->maxdata;
value *= devpriv->uvolt_amplitude;
do_div(value, krange->max - krange->min);
@@ -206,20 +204,24 @@ static void waveform_ai_interrupt(unsigned long arg)
num_scans = comedi_nscans_left(s, num_scans);
for (i = 0; i < num_scans; i++) {
+ unsigned long scan_remain_period = devpriv->scan_period;
+
for (j = 0; j < cmd->chanlist_len; j++) {
unsigned short sample;
+ if (devpriv->usec_current >= devpriv->usec_period)
+ devpriv->usec_current %= devpriv->usec_period;
sample = fake_waveform(dev, CR_CHAN(cmd->chanlist[j]),
CR_RANGE(cmd->chanlist[j]),
- devpriv->usec_current +
- i * devpriv->scan_period +
- j * devpriv->convert_period);
+ devpriv->usec_current);
comedi_buf_write_samples(s, &sample, 1);
+ devpriv->usec_current += devpriv->convert_period;
+ scan_remain_period -= devpriv->convert_period;
}
+ devpriv->usec_current += scan_remain_period;
}
-
- devpriv->usec_current += elapsed_time;
- devpriv->usec_current %= devpriv->usec_period;
+ if (devpriv->usec_current >= devpriv->usec_period)
+ devpriv->usec_current %= devpriv->usec_period;
if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
async->events |= COMEDI_CB_EOA;
--
2.6.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Ian Abbott <abbotti@mev.co.uk> |
|---|---|
| Date | 2015-10-27 18:10 +0100 |
| Subject | [PATCH 14/16] staging: comedi: comedi_test: handle partial scans in timer routine |
| Message-ID | <qofV2-7TL-73@gated-at.bofh.it> |
| In reply to | #1256972 |
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>
---
drivers/staging/comedi/drivers/comedi_test.c | 85 ++++++++++++++++++----------
1 file changed, 54 insertions(+), 31 deletions(-)
diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c
index 468847a..318340c 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 {
/* 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();
--
2.6.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Ian Abbott <abbotti@mev.co.uk> |
|---|---|
| Date | 2015-10-27 18:10 +0100 |
| Subject | [PATCH 09/16] staging: comedi: comedi_test: rename members for AI commands |
| Message-ID | <qofV2-7TL-75@gated-at.bofh.it> |
| In reply to | #1256972 |
Rename the members of `struct waveform_private` that are used to handle
AI commands, apart from those members used to control fake waveform
generation. The renames are `timer` --> `ai_timer`, `scan_period` -->
`ai_scan_period`, and `convert_period` --> `ai_convert_period`.
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
---
drivers/staging/comedi/drivers/comedi_test.c | 38 ++++++++++++++--------------
1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c
index 78fde3a..8e618ea 100644
--- a/drivers/staging/comedi/drivers/comedi_test.c
+++ b/drivers/staging/comedi/drivers/comedi_test.c
@@ -61,14 +61,14 @@ enum waveform_state_bits {
/* Data unique to this driver */
struct waveform_private {
- struct timer_list timer;
+ struct timer_list ai_timer; /* timer for AI commands */
u64 ai_last_scan_time; /* time of last AI scan in usec */
unsigned int uvolt_amplitude; /* waveform amplitude in microvolts */
unsigned int usec_period; /* waveform period in microseconds */
unsigned int usec_current; /* current time (mod waveform period) */
unsigned long state_bits;
- unsigned int scan_period; /* scan period in usec */
- unsigned int convert_period; /* conversion period in usec */
+ unsigned int ai_scan_period; /* AI scan period in usec */
+ unsigned int ai_convert_period; /* AI conversion period in usec */
unsigned int ao_loopbacks[N_CHANS];
};
@@ -191,11 +191,11 @@ static void waveform_ai_interrupt(unsigned long arg)
return;
elapsed_time = ktime_to_us(ktime_get()) - devpriv->ai_last_scan_time;
- num_scans = elapsed_time / devpriv->scan_period;
+ 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->scan_period;
+ unsigned int scan_remain_period = devpriv->ai_scan_period;
for (j = 0; j < cmd->chanlist_len; j++) {
unsigned short sample;
@@ -206,11 +206,11 @@ static void waveform_ai_interrupt(unsigned long arg)
CR_RANGE(cmd->chanlist[j]),
devpriv->usec_current);
comedi_buf_write_samples(s, &sample, 1);
- devpriv->usec_current += devpriv->convert_period;
- scan_remain_period -= devpriv->convert_period;
+ devpriv->usec_current += devpriv->ai_convert_period;
+ scan_remain_period -= devpriv->ai_convert_period;
}
devpriv->usec_current += scan_remain_period;
- devpriv->ai_last_scan_time += devpriv->scan_period;
+ devpriv->ai_last_scan_time += devpriv->ai_scan_period;
}
if (devpriv->usec_current >= devpriv->usec_period)
devpriv->usec_current %= devpriv->usec_period;
@@ -218,7 +218,7 @@ static void waveform_ai_interrupt(unsigned long arg)
if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
async->events |= COMEDI_CB_EOA;
else
- mod_timer(&devpriv->timer, jiffies + 1);
+ mod_timer(&devpriv->ai_timer, jiffies + 1);
comedi_handle_events(dev, s);
}
@@ -338,15 +338,15 @@ static int waveform_ai_cmd(struct comedi_device *dev,
}
if (cmd->convert_src == TRIG_NOW)
- devpriv->convert_period = 0;
+ devpriv->ai_convert_period = 0;
else /* cmd->convert_src == TRIG_TIMER */
- devpriv->convert_period = cmd->convert_arg / NSEC_PER_USEC;
+ devpriv->ai_convert_period = cmd->convert_arg / NSEC_PER_USEC;
if (cmd->scan_begin_src == TRIG_FOLLOW) {
- devpriv->scan_period = devpriv->convert_period *
- cmd->scan_end_arg;
+ devpriv->ai_scan_period = devpriv->ai_convert_period *
+ cmd->scan_end_arg;
} else { /* cmd->scan_begin_src == TRIG_TIMER */
- devpriv->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());
@@ -354,12 +354,12 @@ static int waveform_ai_cmd(struct comedi_device *dev,
usec_current = devpriv->ai_last_scan_time;
devpriv->usec_current = do_div(usec_current, devpriv->usec_period);
- devpriv->timer.expires = jiffies + 1;
+ devpriv->ai_timer.expires = jiffies + 1;
/* mark command as active */
smp_mb__before_atomic();
set_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits);
smp_mb__after_atomic();
- add_timer(&devpriv->timer);
+ add_timer(&devpriv->ai_timer);
return 0;
}
@@ -372,7 +372,7 @@ static int waveform_ai_cancel(struct comedi_device *dev,
clear_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits);
smp_mb__after_atomic();
/* cannot call del_timer_sync() as may be called from timer routine */
- del_timer(&devpriv->timer);
+ del_timer(&devpriv->ai_timer);
return 0;
}
@@ -457,7 +457,7 @@ static int waveform_attach(struct comedi_device *dev,
for (i = 0; i < s->n_chan; i++)
devpriv->ao_loopbacks[i] = s->maxdata / 2;
- setup_timer(&devpriv->timer, waveform_ai_interrupt,
+ setup_timer(&devpriv->ai_timer, waveform_ai_interrupt,
(unsigned long)dev);
dev_info(dev->class_dev,
@@ -473,7 +473,7 @@ static void waveform_detach(struct comedi_device *dev)
struct waveform_private *devpriv = dev->private;
if (devpriv)
- del_timer_sync(&devpriv->timer);
+ del_timer_sync(&devpriv->ai_timer);
}
static struct comedi_driver waveform_driver = {
--
2.6.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Hartley Sweeten <HartleyS@visionengravers.com> |
|---|---|
| Date | 2015-10-29 00:20 +0100 |
| Message-ID | <qoIaB-In-1@gated-at.bofh.it> |
| In reply to | #1256972 |
On Tuesday, October 27, 2015 9:59 AM, Ian Abbott wrote: > The "comedi_test" module is a driver for a dummy COMEDI device. It has > an analog input subdevice and an analog output subdevice. The analog > input subdevice supports COMEDI asynchronous acquisition commands using > waveform generators to generate the input data for each channel. A > kernel timer is used to driver the acquisition. > > This series of patches cleans up the driver, enhances the existing > asynchronous command support on the analog input subdevice, and adds > asynchronous command support on the analog output subdevice. > > 01) staging: comedi: comedi_test: reformat multi-line comments > 02) staging: comedi: comedi_test: saturate fake waveform values > 03) staging: comedi: comedi_test: remove nano_per_micro > 04) staging: comedi: comedi_test: limit maximum convert_arg > 05) staging: comedi: comedi_test: support scan_begin_src == TRIG_FOLLOW > 06) staging: comedi: comedi_test: move modulo operations for waveform > 07) staging: comedi: comedi_test: use unsigned int for waveform timing > 08) staging: comedi: comedi_test: simplify time since last AI scan > 09) staging: comedi: comedi_test: rename members for AI commands > 10) staging: comedi: comedi_test: rename waveform members > 11) staging: comedi: comedi_test: make timer rate similar to scan rate > 12) staging: comedi: comedi_test: use unsigned short for loopback values > 13) staging: comedi: comedi_test: allow read-back of AO channels > 14) staging: comedi: comedi_test: handle partial scans in timer routine > 15) staging: comedi: comedi_test: rename waveform_ai_interrupt() > 16) staging: comedi: comedi_test: implement commands on AO subdevice > > drivers/staging/comedi/drivers/comedi_test.c | 565 ++++++++++++++++++++------- > 1 file changed, 416 insertions(+), 149 deletions(-) Reviewed-by: H Hartley Sweeten <hsweeten@visionengravers.com> -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web