#include <linux/timer.h>
#include <linux/ktime.h>
+#include <linux/jiffies.h>
#define N_CHANS 8
/* Data unique to this driver */
struct waveform_private {
- struct timer_list timer;
- ktime_t last; /* time last timer interrupt occurred */
- unsigned int uvolt_amplitude; /* waveform amplitude in microvolts */
- unsigned long usec_period; /* waveform period in microseconds */
- unsigned long usec_current; /* current time (mod waveform period) */
- unsigned long usec_remainder; /* usec since last scan */
+ struct timer_list ai_timer; /* timer for AI commands */
+ u64 ai_last_scan_time; /* time of last AI scan 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 */
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];
};
static unsigned short fake_sawtooth(struct comedi_device *dev,
unsigned int range_index,
- unsigned long current_time)
+ unsigned int current_time)
{
struct waveform_private *devpriv = dev->private;
struct comedi_subdevice *s = dev->read_subdev;
u64 binary_amplitude;
binary_amplitude = s->maxdata;
- binary_amplitude *= devpriv->uvolt_amplitude;
+ binary_amplitude *= devpriv->wf_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);
+ do_div(value, devpriv->wf_period);
value += offset;
/* get rid of sawtooth's dc offset and clamp value */
if (value < binary_amplitude) {
static unsigned short fake_squarewave(struct comedi_device *dev,
unsigned int range_index,
- unsigned long current_time)
+ unsigned int current_time)
{
struct waveform_private *devpriv = dev->private;
struct comedi_subdevice *s = dev->read_subdev;
const struct comedi_krange *krange =
&s->range_table->range[range_index];
- current_time %= devpriv->usec_period;
value = s->maxdata;
- value *= devpriv->uvolt_amplitude;
+ value *= devpriv->wf_amplitude;
do_div(value, krange->max - krange->min);
/* get one of two values for square-wave and clamp */
- if (current_time < devpriv->usec_period / 2) {
+ if (current_time < devpriv->wf_period / 2) {
if (offset < value)
value = 0; /* negative saturation */
else
static unsigned short fake_flatline(struct comedi_device *dev,
unsigned int range_index,
- unsigned long current_time)
+ unsigned int current_time)
{
return dev->read_subdev->maxdata / 2;
}
/* generates a different waveform depending on what channel is read */
static unsigned short fake_waveform(struct comedi_device *dev,
unsigned int channel, unsigned int range,
- unsigned long current_time)
+ unsigned int current_time)
{
enum {
SAWTOOTH_CHAN,
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->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->usec_current +
- i * devpriv->scan_period +
- j * devpriv->convert_period);
+ devpriv->wf_current);
comedi_buf_write_samples(s, &sample, 1);
+ devpriv->wf_current += devpriv->ai_convert_period;
+ scan_remain_period -= devpriv->ai_convert_period;
}
+ devpriv->wf_current += scan_remain_period;
+ devpriv->ai_last_scan_time += devpriv->ai_scan_period;
}
+ if (devpriv->wf_current >= devpriv->wf_period)
+ devpriv->wf_current %= devpriv->wf_period;
- devpriv->usec_current += elapsed_time;
- devpriv->usec_current %= devpriv->usec_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->timer, jiffies + 1);
+ } else {
+ mod_timer(&devpriv->ai_timer,
+ jiffies + usecs_to_jiffies(devpriv->ai_scan_period));
+ }
comedi_handle_events(dev, s);
}
/* 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);
/* 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;
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,
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)
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;
{
struct waveform_private *devpriv = dev->private;
struct comedi_cmd *cmd = &s->async->cmd;
+ u64 wf_current;
if (cmd->flags & CMDF_PRIORITY) {
dev_err(dev->class_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 */
- devpriv->convert_period = cmd->convert_arg / NSEC_PER_USEC;
+ devpriv->ai_convert_period = 0;
+ else /* cmd->convert_src == TRIG_TIMER */
+ devpriv->ai_convert_period = cmd->convert_arg / NSEC_PER_USEC;
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ devpriv->ai_scan_period = devpriv->ai_convert_period *
+ cmd->scan_end_arg;
+ } else { /* cmd->scan_begin_src == TRIG_TIMER */
+ 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;
+ devpriv->wf_current = do_div(wf_current, devpriv->wf_period);
- devpriv->last = ktime_get();
- devpriv->usec_current =
- ((u32)ktime_to_us(devpriv->last)) % devpriv->usec_period;
- devpriv->usec_remainder = 0;
+ devpriv->ai_timer.expires =
+ jiffies + usecs_to_jiffies(devpriv->ai_scan_period);
- devpriv->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;
}
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;
}
if (period <= 0)
period = 100000; /* 0.1 sec */
- devpriv->uvolt_amplitude = amplitude;
- devpriv->usec_period = period;
+ devpriv->wf_amplitude = amplitude;
+ devpriv->wf_period = period;
ret = comedi_alloc_subdevices(dev, 2);
if (ret)
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,
- "%s: %i microvolt, %li microsecond waveform attached\n",
+ "%s: %u microvolt, %u microsecond waveform attached\n",
dev->board_name,
- devpriv->uvolt_amplitude, devpriv->usec_period);
+ devpriv->wf_amplitude, devpriv->wf_period);
return 0;
}
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 = {