ARM: at91: fix board-rm9200-dt after sys_timer conversion
[GitHub/mt8127/android_kernel_alcatel_ttab.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 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.
28
29 ************************************************************************/
30 /*
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
38
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.
42
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)
46
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).
50
51 */
52
53 #include "../comedidev.h"
54
55 #include <asm/div64.h>
56
57 #include "comedi_fc.h"
58 #include <linux/timer.h>
59
60 #define N_CHANS 8
61
62 /* Data unique to this driver */
63 struct waveform_private {
64 struct timer_list timer;
65 struct timeval last; /* time at which last timer interrupt occurred */
66 unsigned int uvolt_amplitude; /* waveform amplitude in microvolts */
67 unsigned long usec_period; /* waveform period in microseconds */
68 unsigned long usec_current; /* current time (modulo waveform period) */
69 unsigned long usec_remainder; /* usec since last scan; */
70 unsigned long ai_count; /* number of conversions remaining */
71 unsigned int scan_period; /* scan period in usec */
72 unsigned int convert_period; /* conversion period in usec */
73 unsigned timer_running:1;
74 unsigned int ao_loopbacks[N_CHANS];
75 };
76
77 /* 1000 nanosec in a microsec */
78 static const int nano_per_micro = 1000;
79
80 /* fake analog input ranges */
81 static const struct comedi_lrange waveform_ai_ranges = {
82 2,
83 {
84 BIP_RANGE(10),
85 BIP_RANGE(5),
86 }
87 };
88
89 static short fake_sawtooth(struct comedi_device *dev, unsigned int range_index,
90 unsigned long current_time)
91 {
92 struct waveform_private *devpriv = dev->private;
93 struct comedi_subdevice *s = dev->read_subdev;
94 unsigned int offset = s->maxdata / 2;
95 u64 value;
96 const struct comedi_krange *krange =
97 &s->range_table->range[range_index];
98 u64 binary_amplitude;
99
100 binary_amplitude = s->maxdata;
101 binary_amplitude *= devpriv->uvolt_amplitude;
102 do_div(binary_amplitude, krange->max - krange->min);
103
104 current_time %= devpriv->usec_period;
105 value = current_time;
106 value *= binary_amplitude * 2;
107 do_div(value, devpriv->usec_period);
108 value -= binary_amplitude; /* get rid of sawtooth's dc offset */
109
110 return offset + value;
111 }
112
113 static short fake_squarewave(struct comedi_device *dev,
114 unsigned int range_index,
115 unsigned long current_time)
116 {
117 struct waveform_private *devpriv = dev->private;
118 struct comedi_subdevice *s = dev->read_subdev;
119 unsigned int offset = s->maxdata / 2;
120 u64 value;
121 const struct comedi_krange *krange =
122 &s->range_table->range[range_index];
123 current_time %= devpriv->usec_period;
124
125 value = s->maxdata;
126 value *= devpriv->uvolt_amplitude;
127 do_div(value, krange->max - krange->min);
128
129 if (current_time < devpriv->usec_period / 2)
130 value *= -1;
131
132 return offset + value;
133 }
134
135 static short fake_flatline(struct comedi_device *dev, unsigned int range_index,
136 unsigned long current_time)
137 {
138 return dev->read_subdev->maxdata / 2;
139 }
140
141 /* generates a different waveform depending on what channel is read */
142 static short fake_waveform(struct comedi_device *dev, unsigned int channel,
143 unsigned int range, unsigned long current_time)
144 {
145 enum {
146 SAWTOOTH_CHAN,
147 SQUARE_CHAN,
148 };
149 switch (channel) {
150 case SAWTOOTH_CHAN:
151 return fake_sawtooth(dev, range, current_time);
152 break;
153 case SQUARE_CHAN:
154 return fake_squarewave(dev, range, current_time);
155 break;
156 default:
157 break;
158 }
159
160 return fake_flatline(dev, range, current_time);
161 }
162
163 /*
164 This is the background routine used to generate arbitrary data.
165 It should run in the background; therefore it is scheduled by
166 a timer mechanism.
167 */
168 static void waveform_ai_interrupt(unsigned long arg)
169 {
170 struct comedi_device *dev = (struct comedi_device *)arg;
171 struct waveform_private *devpriv = dev->private;
172 struct comedi_async *async = dev->read_subdev->async;
173 struct comedi_cmd *cmd = &async->cmd;
174 unsigned int i, j;
175 /* all times in microsec */
176 unsigned long elapsed_time;
177 unsigned int num_scans;
178 struct timeval now;
179
180 do_gettimeofday(&now);
181
182 elapsed_time =
183 1000000 * (now.tv_sec - devpriv->last.tv_sec) + now.tv_usec -
184 devpriv->last.tv_usec;
185 devpriv->last = now;
186 num_scans =
187 (devpriv->usec_remainder + elapsed_time) / devpriv->scan_period;
188 devpriv->usec_remainder =
189 (devpriv->usec_remainder + elapsed_time) % devpriv->scan_period;
190 async->events = 0;
191
192 for (i = 0; i < num_scans; i++) {
193 for (j = 0; j < cmd->chanlist_len; j++) {
194 cfc_write_to_buffer(dev->read_subdev,
195 fake_waveform(dev,
196 CR_CHAN(cmd->
197 chanlist[j]),
198 CR_RANGE(cmd->
199 chanlist[j]),
200 devpriv->
201 usec_current +
202 i *
203 devpriv->scan_period +
204 j *
205 devpriv->
206 convert_period));
207 }
208 devpriv->ai_count++;
209 if (cmd->stop_src == TRIG_COUNT
210 && devpriv->ai_count >= cmd->stop_arg) {
211 async->events |= COMEDI_CB_EOA;
212 break;
213 }
214 }
215
216 devpriv->usec_current += elapsed_time;
217 devpriv->usec_current %= devpriv->usec_period;
218
219 if ((async->events & COMEDI_CB_EOA) == 0 && devpriv->timer_running)
220 mod_timer(&devpriv->timer, jiffies + 1);
221 else
222 del_timer(&devpriv->timer);
223
224 comedi_event(dev, dev->read_subdev);
225 }
226
227 static int waveform_ai_cmdtest(struct comedi_device *dev,
228 struct comedi_subdevice *s,
229 struct comedi_cmd *cmd)
230 {
231 int err = 0;
232 int tmp;
233
234 /* Step 1 : check if triggers are trivially valid */
235
236 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
237 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
238 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW | TRIG_TIMER);
239 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
240 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
241
242 if (err)
243 return 1;
244
245 /* Step 2a : make sure trigger sources are unique */
246
247 err |= cfc_check_trigger_is_unique(cmd->convert_src);
248 err |= cfc_check_trigger_is_unique(cmd->stop_src);
249
250 /* Step 2b : and mutually compatible */
251
252 if (err)
253 return 2;
254
255 /* Step 3: check if arguments are trivially valid */
256
257 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
258
259 if (cmd->convert_src == TRIG_NOW)
260 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
261
262 if (cmd->scan_begin_src == TRIG_TIMER) {
263 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
264 nano_per_micro);
265 if (cmd->convert_src == TRIG_TIMER)
266 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
267 cmd->convert_arg * cmd->chanlist_len);
268 }
269
270 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
271 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
272
273 if (cmd->stop_src == TRIG_COUNT)
274 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
275 else /* TRIG_NONE */
276 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
277
278 if (err)
279 return 3;
280
281 /* step 4: fix up any arguments */
282
283 if (cmd->scan_begin_src == TRIG_TIMER) {
284 tmp = cmd->scan_begin_arg;
285 /* round to nearest microsec */
286 cmd->scan_begin_arg =
287 nano_per_micro * ((tmp +
288 (nano_per_micro / 2)) / nano_per_micro);
289 if (tmp != cmd->scan_begin_arg)
290 err++;
291 }
292 if (cmd->convert_src == TRIG_TIMER) {
293 tmp = cmd->convert_arg;
294 /* round to nearest microsec */
295 cmd->convert_arg =
296 nano_per_micro * ((tmp +
297 (nano_per_micro / 2)) / nano_per_micro);
298 if (tmp != cmd->convert_arg)
299 err++;
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 & TRIG_RT) {
315 comedi_error(dev,
316 "commands at RT priority not supported in this driver");
317 return -1;
318 }
319
320 devpriv->timer_running = 1;
321 devpriv->ai_count = 0;
322 devpriv->scan_period = cmd->scan_begin_arg / nano_per_micro;
323
324 if (cmd->convert_src == TRIG_NOW)
325 devpriv->convert_period = 0;
326 else if (cmd->convert_src == TRIG_TIMER)
327 devpriv->convert_period = cmd->convert_arg / nano_per_micro;
328 else {
329 comedi_error(dev, "bug setting conversion period");
330 return -1;
331 }
332
333 do_gettimeofday(&devpriv->last);
334 devpriv->usec_current = devpriv->last.tv_usec % devpriv->usec_period;
335 devpriv->usec_remainder = 0;
336
337 devpriv->timer.expires = jiffies + 1;
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 devpriv->timer_running = 0;
348 del_timer(&devpriv->timer);
349 return 0;
350 }
351
352 static int waveform_ai_insn_read(struct comedi_device *dev,
353 struct comedi_subdevice *s,
354 struct comedi_insn *insn, unsigned int *data)
355 {
356 struct waveform_private *devpriv = dev->private;
357 int i, chan = CR_CHAN(insn->chanspec);
358
359 for (i = 0; i < insn->n; i++)
360 data[i] = devpriv->ao_loopbacks[chan];
361
362 return insn->n;
363 }
364
365 static int waveform_ao_insn_write(struct comedi_device *dev,
366 struct comedi_subdevice *s,
367 struct comedi_insn *insn, unsigned int *data)
368 {
369 struct waveform_private *devpriv = dev->private;
370 int i, chan = CR_CHAN(insn->chanspec);
371
372 for (i = 0; i < insn->n; i++)
373 devpriv->ao_loopbacks[chan] = data[i];
374
375 return insn->n;
376 }
377
378 static int waveform_attach(struct comedi_device *dev,
379 struct comedi_devconfig *it)
380 {
381 struct waveform_private *devpriv;
382 struct comedi_subdevice *s;
383 int amplitude = it->options[0];
384 int period = it->options[1];
385 int i;
386 int ret;
387
388 dev->board_name = dev->driver->driver_name;
389
390 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
391 if (!devpriv)
392 return -ENOMEM;
393 dev->private = devpriv;
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_WRITEABLE | SDF_GROUND;
427 s->n_chan = N_CHANS;
428 s->maxdata = 0xffff;
429 s->range_table = &waveform_ai_ranges;
430 s->len_chanlist = s->n_chan * 2;
431 s->insn_write = waveform_ao_insn_write;
432 s->do_cmd = NULL;
433 s->do_cmdtest = NULL;
434 s->cancel = NULL;
435
436 /* Our default loopback value is just a 0V flatline */
437 for (i = 0; i < s->n_chan; i++)
438 devpriv->ao_loopbacks[i] = s->maxdata / 2;
439
440 init_timer(&(devpriv->timer));
441 devpriv->timer.function = waveform_ai_interrupt;
442 devpriv->timer.data = (unsigned long)dev;
443
444 dev_info(dev->class_dev,
445 "%s: %i microvolt, %li microsecond waveform attached\n",
446 dev->board_name,
447 devpriv->uvolt_amplitude, devpriv->usec_period);
448
449 return 0;
450 }
451
452 static void waveform_detach(struct comedi_device *dev)
453 {
454 struct waveform_private *devpriv = dev->private;
455
456 if (devpriv)
457 waveform_ai_cancel(dev, dev->read_subdev);
458 }
459
460 static struct comedi_driver waveform_driver = {
461 .driver_name = "comedi_test",
462 .module = THIS_MODULE,
463 .attach = waveform_attach,
464 .detach = waveform_detach,
465 };
466 module_comedi_driver(waveform_driver);
467
468 MODULE_AUTHOR("Comedi http://www.comedi.org");
469 MODULE_DESCRIPTION("Comedi low-level driver");
470 MODULE_LICENSE("GPL");