staging: comedi: amplc_pci224: use comedi_async 'scans_done' to detect EOA
[deliverable/linux.git] / drivers / staging / comedi / drivers / comedi_test.c
1 /*
2 comedi/drivers/comedi_test.c
3
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.
8
9 Copyright (C) 2002 Joachim Wuttke <Joachim.Wuttke@icn.siemens.de>
10 Copyright (C) 2002 Frank Mori Hess <fmhess@users.sourceforge.net>
11
12 COMEDI - Linux Control and Measurement Device Interface
13 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
14
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.
19
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.
24 */
25 /*
26 Driver: comedi_test
27 Description: generates fake waveforms
28 Author: Joachim Wuttke <Joachim.Wuttke@icn.siemens.de>, Frank Mori Hess
29 <fmhess@users.sourceforge.net>, ds
30 Devices:
31 Status: works
32 Updated: Sat, 16 Mar 2002 17:34:48 -0800
33
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
36 hardware.
37
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)
41
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
44 zero volts).
45
46 */
47
48 #include <linux/module.h>
49 #include "../comedidev.h"
50
51 #include <asm/div64.h>
52
53 #include "comedi_fc.h"
54 #include <linux/timer.h>
55 #include <linux/ktime.h>
56
57 #define N_CHANS 8
58
59 enum waveform_state_bits {
60 WAVEFORM_AI_RUNNING = 0
61 };
62
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];
76 };
77
78 /* 1000 nanosec in a microsec */
79 static const int nano_per_micro = 1000;
80
81 /* fake analog input ranges */
82 static const struct comedi_lrange waveform_ai_ranges = {
83 2, {
84 BIP_RANGE(10),
85 BIP_RANGE(5)
86 }
87 };
88
89 static unsigned short fake_sawtooth(struct comedi_device *dev,
90 unsigned int range_index,
91 unsigned long current_time)
92 {
93 struct waveform_private *devpriv = dev->private;
94 struct comedi_subdevice *s = dev->read_subdev;
95 unsigned int offset = s->maxdata / 2;
96 u64 value;
97 const struct comedi_krange *krange =
98 &s->range_table->range[range_index];
99 u64 binary_amplitude;
100
101 binary_amplitude = s->maxdata;
102 binary_amplitude *= devpriv->uvolt_amplitude;
103 do_div(binary_amplitude, krange->max - krange->min);
104
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 */
110
111 return offset + value;
112 }
113
114 static unsigned short fake_squarewave(struct comedi_device *dev,
115 unsigned int range_index,
116 unsigned long current_time)
117 {
118 struct waveform_private *devpriv = dev->private;
119 struct comedi_subdevice *s = dev->read_subdev;
120 unsigned int offset = s->maxdata / 2;
121 u64 value;
122 const struct comedi_krange *krange =
123 &s->range_table->range[range_index];
124 current_time %= devpriv->usec_period;
125
126 value = s->maxdata;
127 value *= devpriv->uvolt_amplitude;
128 do_div(value, krange->max - krange->min);
129
130 if (current_time < devpriv->usec_period / 2)
131 value *= -1;
132
133 return offset + value;
134 }
135
136 static unsigned short fake_flatline(struct comedi_device *dev,
137 unsigned int range_index,
138 unsigned long current_time)
139 {
140 return dev->read_subdev->maxdata / 2;
141 }
142
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)
147 {
148 enum {
149 SAWTOOTH_CHAN,
150 SQUARE_CHAN,
151 };
152 switch (channel) {
153 case SAWTOOTH_CHAN:
154 return fake_sawtooth(dev, range, current_time);
155 case SQUARE_CHAN:
156 return fake_squarewave(dev, range, current_time);
157 default:
158 break;
159 }
160
161 return fake_flatline(dev, range, current_time);
162 }
163
164 /*
165 This is the background routine used to generate arbitrary data.
166 It should run in the background; therefore it is scheduled by
167 a timer mechanism.
168 */
169 static void waveform_ai_interrupt(unsigned long arg)
170 {
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;
176 unsigned int i, j;
177 /* all times in microsec */
178 unsigned long elapsed_time;
179 unsigned int num_scans;
180 ktime_t now;
181 bool stopping = false;
182
183 /* check command is still active */
184 if (!test_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits))
185 return;
186
187 now = ktime_get();
188
189 elapsed_time = ktime_to_us(ktime_sub(now, devpriv->last));
190 devpriv->last = now;
191 num_scans =
192 (devpriv->usec_remainder + elapsed_time) / devpriv->scan_period;
193 devpriv->usec_remainder =
194 (devpriv->usec_remainder + elapsed_time) % devpriv->scan_period;
195
196 if (cmd->stop_src == TRIG_COUNT) {
197 unsigned int remaining = cmd->stop_arg - devpriv->ai_count;
198
199 if (num_scans >= remaining) {
200 /* about to finish */
201 num_scans = remaining;
202 stopping = true;
203 }
204 }
205
206 for (i = 0; i < num_scans; i++) {
207 for (j = 0; j < cmd->chanlist_len; j++) {
208 unsigned short sample;
209
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);
216 }
217 }
218
219 devpriv->ai_count += i;
220 devpriv->usec_current += elapsed_time;
221 devpriv->usec_current %= devpriv->usec_period;
222
223 if (stopping)
224 async->events |= COMEDI_CB_EOA;
225 else
226 mod_timer(&devpriv->timer, jiffies + 1);
227
228 comedi_handle_events(dev, s);
229 }
230
231 static int waveform_ai_cmdtest(struct comedi_device *dev,
232 struct comedi_subdevice *s,
233 struct comedi_cmd *cmd)
234 {
235 int err = 0;
236 unsigned int arg;
237
238 /* Step 1 : check if triggers are trivially valid */
239
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);
245
246 if (err)
247 return 1;
248
249 /* Step 2a : make sure trigger sources are unique */
250
251 err |= cfc_check_trigger_is_unique(cmd->convert_src);
252 err |= cfc_check_trigger_is_unique(cmd->stop_src);
253
254 /* Step 2b : and mutually compatible */
255
256 if (err)
257 return 2;
258
259 /* Step 3: check if arguments are trivially valid */
260
261 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
262
263 if (cmd->convert_src == TRIG_NOW)
264 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
265
266 if (cmd->scan_begin_src == TRIG_TIMER) {
267 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
268 nano_per_micro);
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);
272 }
273
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);
276
277 if (cmd->stop_src == TRIG_COUNT)
278 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
279 else /* TRIG_NONE */
280 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
281
282 if (err)
283 return 3;
284
285 /* step 4: fix up any arguments */
286
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);
293 }
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);
300 }
301
302 if (err)
303 return 4;
304
305 return 0;
306 }
307
308 static int waveform_ai_cmd(struct comedi_device *dev,
309 struct comedi_subdevice *s)
310 {
311 struct waveform_private *devpriv = dev->private;
312 struct comedi_cmd *cmd = &s->async->cmd;
313
314 if (cmd->flags & CMDF_PRIORITY) {
315 dev_err(dev->class_dev,
316 "commands at RT priority not supported in this driver\n");
317 return -1;
318 }
319
320 devpriv->ai_count = 0;
321 devpriv->scan_period = cmd->scan_begin_arg / nano_per_micro;
322
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;
327
328 devpriv->last = ktime_get();
329 devpriv->usec_current =
330 ((u32)ktime_to_us(devpriv->last)) % devpriv->usec_period;
331 devpriv->usec_remainder = 0;
332
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);
339 return 0;
340 }
341
342 static int waveform_ai_cancel(struct comedi_device *dev,
343 struct comedi_subdevice *s)
344 {
345 struct waveform_private *devpriv = dev->private;
346
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);
352 return 0;
353 }
354
355 static int waveform_ai_insn_read(struct comedi_device *dev,
356 struct comedi_subdevice *s,
357 struct comedi_insn *insn, unsigned int *data)
358 {
359 struct waveform_private *devpriv = dev->private;
360 int i, chan = CR_CHAN(insn->chanspec);
361
362 for (i = 0; i < insn->n; i++)
363 data[i] = devpriv->ao_loopbacks[chan];
364
365 return insn->n;
366 }
367
368 static int waveform_ao_insn_write(struct comedi_device *dev,
369 struct comedi_subdevice *s,
370 struct comedi_insn *insn, unsigned int *data)
371 {
372 struct waveform_private *devpriv = dev->private;
373 int i, chan = CR_CHAN(insn->chanspec);
374
375 for (i = 0; i < insn->n; i++)
376 devpriv->ao_loopbacks[chan] = data[i];
377
378 return insn->n;
379 }
380
381 static int waveform_attach(struct comedi_device *dev,
382 struct comedi_devconfig *it)
383 {
384 struct waveform_private *devpriv;
385 struct comedi_subdevice *s;
386 int amplitude = it->options[0];
387 int period = it->options[1];
388 int i;
389 int ret;
390
391 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
392 if (!devpriv)
393 return -ENOMEM;
394
395 /* set default amplitude and period */
396 if (amplitude <= 0)
397 amplitude = 1000000; /* 1 volt */
398 if (period <= 0)
399 period = 100000; /* 0.1 sec */
400
401 devpriv->uvolt_amplitude = amplitude;
402 devpriv->usec_period = period;
403
404 ret = comedi_alloc_subdevices(dev, 2);
405 if (ret)
406 return ret;
407
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;
413 s->n_chan = N_CHANS;
414 s->maxdata = 0xffff;
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;
421
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;
427 s->n_chan = N_CHANS;
428 s->maxdata = 0xffff;
429 s->range_table = &waveform_ai_ranges;
430 s->insn_write = waveform_ao_insn_write;
431
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;
435
436 init_timer(&devpriv->timer);
437 devpriv->timer.function = waveform_ai_interrupt;
438 devpriv->timer.data = (unsigned long)dev;
439
440 dev_info(dev->class_dev,
441 "%s: %i microvolt, %li microsecond waveform attached\n",
442 dev->board_name,
443 devpriv->uvolt_amplitude, devpriv->usec_period);
444
445 return 0;
446 }
447
448 static void waveform_detach(struct comedi_device *dev)
449 {
450 struct waveform_private *devpriv = dev->private;
451
452 if (devpriv)
453 del_timer_sync(&devpriv->timer);
454 }
455
456 static struct comedi_driver waveform_driver = {
457 .driver_name = "comedi_test",
458 .module = THIS_MODULE,
459 .attach = waveform_attach,
460 .detach = waveform_detach,
461 };
462 module_comedi_driver(waveform_driver);
463
464 MODULE_AUTHOR("Comedi http://www.comedi.org");
465 MODULE_DESCRIPTION("Comedi low-level driver");
466 MODULE_LICENSE("GPL");
This page took 0.042984 seconds and 6 git commands to generate.