2 comedi/drivers/comedi_test.c
4 Generates fake waveform signals that can be read through
5 the command interface. It does _not_ read from any board;
6 it just generates deterministic waveforms.
7 Useful for various testing purposes.
9 Copyright (C) 2002 Joachim Wuttke <Joachim.Wuttke@icn.siemens.de>
10 Copyright (C) 2002 Frank Mori Hess <fmhess@users.sourceforge.net>
12 COMEDI - Linux Control and Measurement Device Interface
13 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation; either version 2 of the License, or
18 (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
27 Description: generates fake waveforms
28 Author: Joachim Wuttke <Joachim.Wuttke@icn.siemens.de>, Frank Mori Hess
29 <fmhess@users.sourceforge.net>, ds
32 Updated: Sat, 16 Mar 2002 17:34:48 -0800
34 This driver is mainly for testing purposes, but can also be used to
35 generate sample waveforms on systems that don't have data acquisition
38 Configuration options:
39 [0] - Amplitude in microvolts for fake waveforms (default 1 volt)
40 [1] - Period in microseconds for fake waveforms (default 0.1 sec)
42 Generates a sawtooth wave on channel 0, square wave on channel 1, additional
43 waveforms could be added to other channels (currently they return flatline
48 #include <linux/module.h>
49 #include "../comedidev.h"
51 #include <asm/div64.h>
53 #include "comedi_fc.h"
54 #include <linux/timer.h>
55 #include <linux/ktime.h>
59 enum waveform_state_bits
{
60 WAVEFORM_AI_RUNNING
= 0
63 /* Data unique to this driver */
64 struct waveform_private
{
65 struct timer_list timer
;
66 ktime_t last
; /* time last timer interrupt occurred */
67 unsigned int uvolt_amplitude
; /* waveform amplitude in microvolts */
68 unsigned long usec_period
; /* waveform period in microseconds */
69 unsigned long usec_current
; /* current time (mod waveform period) */
70 unsigned long usec_remainder
; /* usec since last scan */
71 unsigned long ai_count
; /* number of conversions remaining */
72 unsigned long state_bits
;
73 unsigned int scan_period
; /* scan period in usec */
74 unsigned int convert_period
; /* conversion period in usec */
75 unsigned int ao_loopbacks
[N_CHANS
];
78 /* 1000 nanosec in a microsec */
79 static const int nano_per_micro
= 1000;
81 /* fake analog input ranges */
82 static const struct comedi_lrange waveform_ai_ranges
= {
89 static unsigned short fake_sawtooth(struct comedi_device
*dev
,
90 unsigned int range_index
,
91 unsigned long current_time
)
93 struct waveform_private
*devpriv
= dev
->private;
94 struct comedi_subdevice
*s
= dev
->read_subdev
;
95 unsigned int offset
= s
->maxdata
/ 2;
97 const struct comedi_krange
*krange
=
98 &s
->range_table
->range
[range_index
];
101 binary_amplitude
= s
->maxdata
;
102 binary_amplitude
*= devpriv
->uvolt_amplitude
;
103 do_div(binary_amplitude
, krange
->max
- krange
->min
);
105 current_time
%= devpriv
->usec_period
;
106 value
= current_time
;
107 value
*= binary_amplitude
* 2;
108 do_div(value
, devpriv
->usec_period
);
109 value
-= binary_amplitude
; /* get rid of sawtooth's dc offset */
111 return offset
+ value
;
114 static unsigned short fake_squarewave(struct comedi_device
*dev
,
115 unsigned int range_index
,
116 unsigned long current_time
)
118 struct waveform_private
*devpriv
= dev
->private;
119 struct comedi_subdevice
*s
= dev
->read_subdev
;
120 unsigned int offset
= s
->maxdata
/ 2;
122 const struct comedi_krange
*krange
=
123 &s
->range_table
->range
[range_index
];
124 current_time
%= devpriv
->usec_period
;
127 value
*= devpriv
->uvolt_amplitude
;
128 do_div(value
, krange
->max
- krange
->min
);
130 if (current_time
< devpriv
->usec_period
/ 2)
133 return offset
+ value
;
136 static unsigned short fake_flatline(struct comedi_device
*dev
,
137 unsigned int range_index
,
138 unsigned long current_time
)
140 return dev
->read_subdev
->maxdata
/ 2;
143 /* generates a different waveform depending on what channel is read */
144 static unsigned short fake_waveform(struct comedi_device
*dev
,
145 unsigned int channel
, unsigned int range
,
146 unsigned long current_time
)
154 return fake_sawtooth(dev
, range
, current_time
);
156 return fake_squarewave(dev
, range
, current_time
);
161 return fake_flatline(dev
, range
, current_time
);
165 This is the background routine used to generate arbitrary data.
166 It should run in the background; therefore it is scheduled by
169 static void waveform_ai_interrupt(unsigned long arg
)
171 struct comedi_device
*dev
= (struct comedi_device
*)arg
;
172 struct waveform_private
*devpriv
= dev
->private;
173 struct comedi_subdevice
*s
= dev
->read_subdev
;
174 struct comedi_async
*async
= s
->async
;
175 struct comedi_cmd
*cmd
= &async
->cmd
;
177 /* all times in microsec */
178 unsigned long elapsed_time
;
179 unsigned int num_scans
;
181 bool stopping
= false;
183 /* check command is still active */
184 if (!test_bit(WAVEFORM_AI_RUNNING
, &devpriv
->state_bits
))
189 elapsed_time
= ktime_to_us(ktime_sub(now
, devpriv
->last
));
192 (devpriv
->usec_remainder
+ elapsed_time
) / devpriv
->scan_period
;
193 devpriv
->usec_remainder
=
194 (devpriv
->usec_remainder
+ elapsed_time
) % devpriv
->scan_period
;
196 if (cmd
->stop_src
== TRIG_COUNT
) {
197 unsigned int remaining
= cmd
->stop_arg
- devpriv
->ai_count
;
199 if (num_scans
>= remaining
) {
200 /* about to finish */
201 num_scans
= remaining
;
206 for (i
= 0; i
< num_scans
; i
++) {
207 for (j
= 0; j
< cmd
->chanlist_len
; j
++) {
208 unsigned short sample
;
210 sample
= fake_waveform(dev
, CR_CHAN(cmd
->chanlist
[j
]),
211 CR_RANGE(cmd
->chanlist
[j
]),
212 devpriv
->usec_current
+
213 i
* devpriv
->scan_period
+
214 j
* devpriv
->convert_period
);
215 comedi_buf_write_samples(s
, &sample
, 1);
219 devpriv
->ai_count
+= i
;
220 devpriv
->usec_current
+= elapsed_time
;
221 devpriv
->usec_current
%= devpriv
->usec_period
;
224 async
->events
|= COMEDI_CB_EOA
;
226 mod_timer(&devpriv
->timer
, jiffies
+ 1);
228 comedi_handle_events(dev
, s
);
231 static int waveform_ai_cmdtest(struct comedi_device
*dev
,
232 struct comedi_subdevice
*s
,
233 struct comedi_cmd
*cmd
)
238 /* Step 1 : check if triggers are trivially valid */
240 err
|= cfc_check_trigger_src(&cmd
->start_src
, TRIG_NOW
);
241 err
|= cfc_check_trigger_src(&cmd
->scan_begin_src
, TRIG_TIMER
);
242 err
|= cfc_check_trigger_src(&cmd
->convert_src
, TRIG_NOW
| TRIG_TIMER
);
243 err
|= cfc_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
244 err
|= cfc_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
249 /* Step 2a : make sure trigger sources are unique */
251 err
|= cfc_check_trigger_is_unique(cmd
->convert_src
);
252 err
|= cfc_check_trigger_is_unique(cmd
->stop_src
);
254 /* Step 2b : and mutually compatible */
259 /* Step 3: check if arguments are trivially valid */
261 err
|= cfc_check_trigger_arg_is(&cmd
->start_arg
, 0);
263 if (cmd
->convert_src
== TRIG_NOW
)
264 err
|= cfc_check_trigger_arg_is(&cmd
->convert_arg
, 0);
266 if (cmd
->scan_begin_src
== TRIG_TIMER
) {
267 err
|= cfc_check_trigger_arg_min(&cmd
->scan_begin_arg
,
269 if (cmd
->convert_src
== TRIG_TIMER
)
270 err
|= cfc_check_trigger_arg_min(&cmd
->scan_begin_arg
,
271 cmd
->convert_arg
* cmd
->chanlist_len
);
274 err
|= cfc_check_trigger_arg_min(&cmd
->chanlist_len
, 1);
275 err
|= cfc_check_trigger_arg_is(&cmd
->scan_end_arg
, cmd
->chanlist_len
);
277 if (cmd
->stop_src
== TRIG_COUNT
)
278 err
|= cfc_check_trigger_arg_min(&cmd
->stop_arg
, 1);
280 err
|= cfc_check_trigger_arg_is(&cmd
->stop_arg
, 0);
285 /* step 4: fix up any arguments */
287 if (cmd
->scan_begin_src
== TRIG_TIMER
) {
288 arg
= cmd
->scan_begin_arg
;
289 /* round to nearest microsec */
290 arg
= nano_per_micro
*
291 ((arg
+ (nano_per_micro
/ 2)) / nano_per_micro
);
292 err
|= cfc_check_trigger_arg_is(&cmd
->scan_begin_arg
, arg
);
294 if (cmd
->convert_src
== TRIG_TIMER
) {
295 arg
= cmd
->convert_arg
;
296 /* round to nearest microsec */
297 arg
= nano_per_micro
*
298 ((arg
+ (nano_per_micro
/ 2)) / nano_per_micro
);
299 err
|= cfc_check_trigger_arg_is(&cmd
->convert_arg
, arg
);
308 static int waveform_ai_cmd(struct comedi_device
*dev
,
309 struct comedi_subdevice
*s
)
311 struct waveform_private
*devpriv
= dev
->private;
312 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
314 if (cmd
->flags
& CMDF_PRIORITY
) {
315 dev_err(dev
->class_dev
,
316 "commands at RT priority not supported in this driver\n");
320 devpriv
->ai_count
= 0;
321 devpriv
->scan_period
= cmd
->scan_begin_arg
/ nano_per_micro
;
323 if (cmd
->convert_src
== TRIG_NOW
)
324 devpriv
->convert_period
= 0;
325 else /* TRIG_TIMER */
326 devpriv
->convert_period
= cmd
->convert_arg
/ nano_per_micro
;
328 devpriv
->last
= ktime_get();
329 devpriv
->usec_current
=
330 ((u32
)ktime_to_us(devpriv
->last
)) % devpriv
->usec_period
;
331 devpriv
->usec_remainder
= 0;
333 devpriv
->timer
.expires
= jiffies
+ 1;
334 /* mark command as active */
335 smp_mb__before_atomic();
336 set_bit(WAVEFORM_AI_RUNNING
, &devpriv
->state_bits
);
337 smp_mb__after_atomic();
338 add_timer(&devpriv
->timer
);
342 static int waveform_ai_cancel(struct comedi_device
*dev
,
343 struct comedi_subdevice
*s
)
345 struct waveform_private
*devpriv
= dev
->private;
347 /* mark command as no longer active */
348 clear_bit(WAVEFORM_AI_RUNNING
, &devpriv
->state_bits
);
349 smp_mb__after_atomic();
350 /* cannot call del_timer_sync() as may be called from timer routine */
351 del_timer(&devpriv
->timer
);
355 static int waveform_ai_insn_read(struct comedi_device
*dev
,
356 struct comedi_subdevice
*s
,
357 struct comedi_insn
*insn
, unsigned int *data
)
359 struct waveform_private
*devpriv
= dev
->private;
360 int i
, chan
= CR_CHAN(insn
->chanspec
);
362 for (i
= 0; i
< insn
->n
; i
++)
363 data
[i
] = devpriv
->ao_loopbacks
[chan
];
368 static int waveform_ao_insn_write(struct comedi_device
*dev
,
369 struct comedi_subdevice
*s
,
370 struct comedi_insn
*insn
, unsigned int *data
)
372 struct waveform_private
*devpriv
= dev
->private;
373 int i
, chan
= CR_CHAN(insn
->chanspec
);
375 for (i
= 0; i
< insn
->n
; i
++)
376 devpriv
->ao_loopbacks
[chan
] = data
[i
];
381 static int waveform_attach(struct comedi_device
*dev
,
382 struct comedi_devconfig
*it
)
384 struct waveform_private
*devpriv
;
385 struct comedi_subdevice
*s
;
386 int amplitude
= it
->options
[0];
387 int period
= it
->options
[1];
391 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
395 /* set default amplitude and period */
397 amplitude
= 1000000; /* 1 volt */
399 period
= 100000; /* 0.1 sec */
401 devpriv
->uvolt_amplitude
= amplitude
;
402 devpriv
->usec_period
= period
;
404 ret
= comedi_alloc_subdevices(dev
, 2);
408 s
= &dev
->subdevices
[0];
409 dev
->read_subdev
= s
;
410 /* analog input subdevice */
411 s
->type
= COMEDI_SUBD_AI
;
412 s
->subdev_flags
= SDF_READABLE
| SDF_GROUND
| SDF_CMD_READ
;
415 s
->range_table
= &waveform_ai_ranges
;
416 s
->len_chanlist
= s
->n_chan
* 2;
417 s
->insn_read
= waveform_ai_insn_read
;
418 s
->do_cmd
= waveform_ai_cmd
;
419 s
->do_cmdtest
= waveform_ai_cmdtest
;
420 s
->cancel
= waveform_ai_cancel
;
422 s
= &dev
->subdevices
[1];
423 dev
->write_subdev
= s
;
424 /* analog output subdevice (loopback) */
425 s
->type
= COMEDI_SUBD_AO
;
426 s
->subdev_flags
= SDF_WRITABLE
| SDF_GROUND
;
429 s
->range_table
= &waveform_ai_ranges
;
430 s
->insn_write
= waveform_ao_insn_write
;
432 /* Our default loopback value is just a 0V flatline */
433 for (i
= 0; i
< s
->n_chan
; i
++)
434 devpriv
->ao_loopbacks
[i
] = s
->maxdata
/ 2;
436 init_timer(&devpriv
->timer
);
437 devpriv
->timer
.function
= waveform_ai_interrupt
;
438 devpriv
->timer
.data
= (unsigned long)dev
;
440 dev_info(dev
->class_dev
,
441 "%s: %i microvolt, %li microsecond waveform attached\n",
443 devpriv
->uvolt_amplitude
, devpriv
->usec_period
);
448 static void waveform_detach(struct comedi_device
*dev
)
450 struct waveform_private
*devpriv
= dev
->private;
453 del_timer_sync(&devpriv
->timer
);
456 static struct comedi_driver waveform_driver
= {
457 .driver_name
= "comedi_test",
458 .module
= THIS_MODULE
,
459 .attach
= waveform_attach
,
460 .detach
= waveform_detach
,
462 module_comedi_driver(waveform_driver
);
464 MODULE_AUTHOR("Comedi http://www.comedi.org");
465 MODULE_DESCRIPTION("Comedi low-level driver");
466 MODULE_LICENSE("GPL");