added 2.6.29.6 aldebaran kernel
[nao-ulib.git] / kernel / 2.6.29.6-aldebaran-rt / drivers / staging / comedi / drivers / comedi_test.c
blob4b4c37d0748246824e722b80f98fb2cb06da95e6
1 /*
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.
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 ************************************************************************/
31 Driver: comedi_test
32 Description: generates fake waveforms
33 Author: Joachim Wuttke <Joachim.Wuttke@icn.siemens.de>, Frank Mori Hess
34 <fmhess@users.sourceforge.net>, ds
35 Devices:
36 Status: works
37 Updated: Sat, 16 Mar 2002 17:34:48 -0800
39 This driver is mainly for testing purposes, but can also be used to
40 generate sample waveforms on systems that don't have data acquisition
41 hardware.
43 Configuration options:
44 [0] - Amplitude in microvolts for fake waveforms (default 1 volt)
45 [1] - Period in microseconds for fake waveforms (default 0.1 sec)
47 Generates a sawtooth wave on channel 0, square wave on channel 1, additional
48 waveforms could be added to other channels (currently they return flatline
49 zero volts).
53 #include "../comedidev.h"
55 #include <asm/div64.h>
57 #include "comedi_fc.h"
59 /* Board descriptions */
60 struct waveform_board {
61 const char *name;
62 int ai_chans;
63 int ai_bits;
64 int have_dio;
67 #define N_CHANS 8
69 static const struct waveform_board waveform_boards[] = {
71 .name = "comedi_test",
72 .ai_chans = N_CHANS,
73 .ai_bits = 16,
74 .have_dio = 0,
78 #define thisboard ((const struct waveform_board *)dev->board_ptr)
80 /* Data unique to this driver */
81 struct waveform_private {
82 struct timer_list timer;
83 struct timeval last; /* time at which last timer interrupt occured */
84 unsigned int uvolt_amplitude; /* waveform amplitude in microvolts */
85 unsigned long usec_period; /* waveform period in microseconds */
86 unsigned long usec_current; /* current time (modulo waveform period) */
87 unsigned long usec_remainder; /* usec since last scan; */
88 unsigned long ai_count; /* number of conversions remaining */
89 unsigned int scan_period; /* scan period in usec */
90 unsigned int convert_period; /* conversion period in usec */
91 unsigned timer_running:1;
92 lsampl_t ao_loopbacks[N_CHANS];
94 #define devpriv ((struct waveform_private *)dev->private)
96 static int waveform_attach(comedi_device *dev, comedi_devconfig *it);
97 static int waveform_detach(comedi_device *dev);
98 static comedi_driver driver_waveform = {
99 .driver_name = "comedi_test",
100 .module = THIS_MODULE,
101 .attach = waveform_attach,
102 .detach = waveform_detach,
103 .board_name = &waveform_boards[0].name,
104 .offset = sizeof(struct waveform_board),
105 .num_names = sizeof(waveform_boards) / sizeof(struct waveform_board),
108 COMEDI_INITCLEANUP(driver_waveform);
110 static int waveform_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
111 comedi_cmd *cmd);
112 static int waveform_ai_cmd(comedi_device *dev, comedi_subdevice *s);
113 static int waveform_ai_cancel(comedi_device *dev, comedi_subdevice *s);
114 static int waveform_ai_insn_read(comedi_device *dev, comedi_subdevice *s,
115 comedi_insn *insn, lsampl_t *data);
116 static int waveform_ao_insn_write(comedi_device *dev, comedi_subdevice *s,
117 comedi_insn *insn, lsampl_t *data);
118 static sampl_t fake_sawtooth(comedi_device *dev, unsigned int range,
119 unsigned long current_time);
120 static sampl_t fake_squarewave(comedi_device *dev, unsigned int range,
121 unsigned long current_time);
122 static sampl_t fake_flatline(comedi_device *dev, unsigned int range,
123 unsigned long current_time);
124 static sampl_t fake_waveform(comedi_device *dev, unsigned int channel,
125 unsigned int range, unsigned long current_time);
127 /* 1000 nanosec in a microsec */
128 static const int nano_per_micro = 1000;
130 /* fake analog input ranges */
131 static const comedi_lrange waveform_ai_ranges = {
134 BIP_RANGE(10),
135 BIP_RANGE(5),
140 This is the background routine used to generate arbitrary data.
141 It should run in the background; therefore it is scheduled by
142 a timer mechanism.
144 static void waveform_ai_interrupt(unsigned long arg)
146 comedi_device *dev = (comedi_device *) arg;
147 comedi_async *async = dev->read_subdev->async;
148 comedi_cmd *cmd = &async->cmd;
149 unsigned int i, j;
150 /* all times in microsec */
151 unsigned long elapsed_time;
152 unsigned int num_scans;
153 struct timeval now;
155 do_gettimeofday(&now);
157 elapsed_time =
158 1000000 * (now.tv_sec - devpriv->last.tv_sec) + now.tv_usec -
159 devpriv->last.tv_usec;
160 devpriv->last = now;
161 num_scans =
162 (devpriv->usec_remainder + elapsed_time) / devpriv->scan_period;
163 devpriv->usec_remainder =
164 (devpriv->usec_remainder + elapsed_time) % devpriv->scan_period;
165 async->events = 0;
167 for (i = 0; i < num_scans; i++) {
168 for (j = 0; j < cmd->chanlist_len; j++) {
169 cfc_write_to_buffer(dev->read_subdev,
170 fake_waveform(dev, CR_CHAN(cmd->chanlist[j]),
171 CR_RANGE(cmd->chanlist[j]),
172 devpriv->usec_current +
173 i * devpriv->scan_period +
174 j * devpriv->convert_period));
176 devpriv->ai_count++;
177 if (cmd->stop_src == TRIG_COUNT
178 && devpriv->ai_count >= cmd->stop_arg) {
179 async->events |= COMEDI_CB_EOA;
180 break;
184 devpriv->usec_current += elapsed_time;
185 devpriv->usec_current %= devpriv->usec_period;
187 if ((async->events & COMEDI_CB_EOA) == 0 && devpriv->timer_running)
188 mod_timer(&devpriv->timer, jiffies + 1);
189 else
190 del_timer(&devpriv->timer);
192 comedi_event(dev, dev->read_subdev);
195 static int waveform_attach(comedi_device *dev, comedi_devconfig *it)
197 comedi_subdevice *s;
198 int amplitude = it->options[0];
199 int period = it->options[1];
200 int i;
202 dev->board_name = thisboard->name;
204 if (alloc_private(dev, sizeof(struct waveform_private)) < 0)
205 return -ENOMEM;
207 /* set default amplitude and period */
208 if (amplitude <= 0)
209 amplitude = 1000000; /* 1 volt */
210 if (period <= 0)
211 period = 100000; /* 0.1 sec */
213 devpriv->uvolt_amplitude = amplitude;
214 devpriv->usec_period = period;
216 dev->n_subdevices = 2;
217 if (alloc_subdevices(dev, dev->n_subdevices) < 0)
218 return -ENOMEM;
220 s = dev->subdevices + 0;
221 dev->read_subdev = s;
222 /* analog input subdevice */
223 s->type = COMEDI_SUBD_AI;
224 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
225 s->n_chan = thisboard->ai_chans;
226 s->maxdata = (1 << thisboard->ai_bits) - 1;
227 s->range_table = &waveform_ai_ranges;
228 s->len_chanlist = s->n_chan * 2;
229 s->insn_read = waveform_ai_insn_read;
230 s->do_cmd = waveform_ai_cmd;
231 s->do_cmdtest = waveform_ai_cmdtest;
232 s->cancel = waveform_ai_cancel;
234 s = dev->subdevices + 1;
235 dev->write_subdev = s;
236 /* analog output subdevice (loopback) */
237 s->type = COMEDI_SUBD_AO;
238 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
239 s->n_chan = thisboard->ai_chans;
240 s->maxdata = (1 << thisboard->ai_bits) - 1;
241 s->range_table = &waveform_ai_ranges;
242 s->len_chanlist = s->n_chan * 2;
243 s->insn_write = waveform_ao_insn_write;
244 s->do_cmd = NULL;
245 s->do_cmdtest = NULL;
246 s->cancel = NULL;
248 /* Our default loopback value is just a 0V flatline */
249 for (i = 0; i < s->n_chan; i++)
250 devpriv->ao_loopbacks[i] = s->maxdata / 2;
252 init_timer(&(devpriv->timer));
253 devpriv->timer.function = waveform_ai_interrupt;
254 devpriv->timer.data = (unsigned long)dev;
256 printk(KERN_INFO "comedi%d: comedi_test: "
257 "%i microvolt, %li microsecond waveform attached\n", dev->minor,
258 devpriv->uvolt_amplitude, devpriv->usec_period);
259 return 1;
262 static int waveform_detach(comedi_device *dev)
264 printk("comedi%d: comedi_test: remove\n", dev->minor);
266 if (dev->private)
267 waveform_ai_cancel(dev, dev->read_subdev);
269 return 0;
272 static int waveform_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
273 comedi_cmd *cmd)
275 int err = 0;
276 int tmp;
278 /* step 1: make sure trigger sources are trivially valid */
280 tmp = cmd->start_src;
281 cmd->start_src &= TRIG_NOW;
282 if (!cmd->start_src || tmp != cmd->start_src)
283 err++;
285 tmp = cmd->scan_begin_src;
286 cmd->scan_begin_src &= TRIG_TIMER;
287 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
288 err++;
290 tmp = cmd->convert_src;
291 cmd->convert_src &= TRIG_NOW | TRIG_TIMER;
292 if (!cmd->convert_src || tmp != cmd->convert_src)
293 err++;
295 tmp = cmd->scan_end_src;
296 cmd->scan_end_src &= TRIG_COUNT;
297 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
298 err++;
300 tmp = cmd->stop_src;
301 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
302 if (!cmd->stop_src || tmp != cmd->stop_src)
303 err++;
305 if (err)
306 return 1;
309 * step 2: make sure trigger sources are unique and mutually compatible
312 if (cmd->convert_src != TRIG_NOW && cmd->convert_src != TRIG_TIMER)
313 err++;
314 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
315 err++;
317 if (err)
318 return 2;
320 /* step 3: make sure arguments are trivially compatible */
322 if (cmd->start_arg != 0) {
323 cmd->start_arg = 0;
324 err++;
326 if (cmd->convert_src == TRIG_NOW) {
327 if (cmd->convert_arg != 0) {
328 cmd->convert_arg = 0;
329 err++;
332 if (cmd->scan_begin_src == TRIG_TIMER) {
333 if (cmd->scan_begin_arg < nano_per_micro) {
334 cmd->scan_begin_arg = nano_per_micro;
335 err++;
337 if (cmd->convert_src == TRIG_TIMER &&
338 cmd->scan_begin_arg <
339 cmd->convert_arg * cmd->chanlist_len) {
340 cmd->scan_begin_arg =
341 cmd->convert_arg * cmd->chanlist_len;
342 err++;
346 * XXX these checks are generic and should go in core if not there
347 * already
349 if (!cmd->chanlist_len) {
350 cmd->chanlist_len = 1;
351 err++;
353 if (cmd->scan_end_arg != cmd->chanlist_len) {
354 cmd->scan_end_arg = cmd->chanlist_len;
355 err++;
358 if (cmd->stop_src == TRIG_COUNT) {
359 if (!cmd->stop_arg) {
360 cmd->stop_arg = 1;
361 err++;
363 } else { /* TRIG_NONE */
364 if (cmd->stop_arg != 0) {
365 cmd->stop_arg = 0;
366 err++;
370 if (err)
371 return 3;
373 /* step 4: fix up any arguments */
375 if (cmd->scan_begin_src == TRIG_TIMER) {
376 tmp = cmd->scan_begin_arg;
377 /* round to nearest microsec */
378 cmd->scan_begin_arg =
379 nano_per_micro * ((tmp +
380 (nano_per_micro / 2)) / nano_per_micro);
381 if (tmp != cmd->scan_begin_arg)
382 err++;
384 if (cmd->convert_src == TRIG_TIMER) {
385 tmp = cmd->convert_arg;
386 /* round to nearest microsec */
387 cmd->convert_arg =
388 nano_per_micro * ((tmp +
389 (nano_per_micro / 2)) / nano_per_micro);
390 if (tmp != cmd->convert_arg)
391 err++;
394 if (err)
395 return 4;
397 return 0;
400 static int waveform_ai_cmd(comedi_device *dev, comedi_subdevice *s)
402 comedi_cmd *cmd = &s->async->cmd;
404 if (cmd->flags & TRIG_RT) {
405 comedi_error(dev,
406 "commands at RT priority not supported in this driver");
407 return -1;
410 devpriv->timer_running = 1;
411 devpriv->ai_count = 0;
412 devpriv->scan_period = cmd->scan_begin_arg / nano_per_micro;
414 if (cmd->convert_src == TRIG_NOW)
415 devpriv->convert_period = 0;
416 else if (cmd->convert_src == TRIG_TIMER)
417 devpriv->convert_period = cmd->convert_arg / nano_per_micro;
418 else {
419 comedi_error(dev, "bug setting conversion period");
420 return -1;
423 do_gettimeofday(&devpriv->last);
424 devpriv->usec_current = devpriv->last.tv_usec % devpriv->usec_period;
425 devpriv->usec_remainder = 0;
427 devpriv->timer.expires = jiffies + 1;
428 add_timer(&devpriv->timer);
429 return 0;
432 static int waveform_ai_cancel(comedi_device *dev, comedi_subdevice *s)
434 devpriv->timer_running = 0;
435 del_timer(&devpriv->timer);
436 return 0;
439 static sampl_t fake_sawtooth(comedi_device *dev, unsigned int range_index,
440 unsigned long current_time)
442 comedi_subdevice *s = dev->read_subdev;
443 unsigned int offset = s->maxdata / 2;
444 u64 value;
445 const comedi_krange *krange = &s->range_table->range[range_index];
446 u64 binary_amplitude;
448 binary_amplitude = s->maxdata;
449 binary_amplitude *= devpriv->uvolt_amplitude;
450 do_div(binary_amplitude, krange->max - krange->min);
452 current_time %= devpriv->usec_period;
453 value = current_time;
454 value *= binary_amplitude * 2;
455 do_div(value, devpriv->usec_period);
456 value -= binary_amplitude; /* get rid of sawtooth's dc offset */
458 return offset + value;
460 static sampl_t fake_squarewave(comedi_device *dev, unsigned int range_index,
461 unsigned long current_time)
463 comedi_subdevice *s = dev->read_subdev;
464 unsigned int offset = s->maxdata / 2;
465 u64 value;
466 const comedi_krange *krange = &s->range_table->range[range_index];
467 current_time %= devpriv->usec_period;
469 value = s->maxdata;
470 value *= devpriv->uvolt_amplitude;
471 do_div(value, krange->max - krange->min);
473 if (current_time < devpriv->usec_period / 2)
474 value *= -1;
476 return offset + value;
479 static sampl_t fake_flatline(comedi_device *dev, unsigned int range_index,
480 unsigned long current_time)
482 return dev->read_subdev->maxdata / 2;
485 /* generates a different waveform depending on what channel is read */
486 static sampl_t fake_waveform(comedi_device *dev, unsigned int channel,
487 unsigned int range, unsigned long current_time)
489 enum {
490 SAWTOOTH_CHAN,
491 SQUARE_CHAN,
493 switch (channel) {
494 case SAWTOOTH_CHAN:
495 return fake_sawtooth(dev, range, current_time);
496 break;
497 case SQUARE_CHAN:
498 return fake_squarewave(dev, range, current_time);
499 break;
500 default:
501 break;
504 return fake_flatline(dev, range, current_time);
507 static int waveform_ai_insn_read(comedi_device *dev, comedi_subdevice *s,
508 comedi_insn *insn, lsampl_t *data)
510 int i, chan = CR_CHAN(insn->chanspec);
512 for (i = 0; i < insn->n; i++)
513 data[i] = devpriv->ao_loopbacks[chan];
515 return insn->n;
518 static int waveform_ao_insn_write(comedi_device *dev, comedi_subdevice *s,
519 comedi_insn *insn, lsampl_t *data)
521 int i, chan = CR_CHAN(insn->chanspec);
523 for (i = 0; i < insn->n; i++)
524 devpriv->ao_loopbacks[chan] = data[i];
526 return insn->n;