Merge commit 'v3.7-rc1' into stable/for-linus-3.7
[firefly-linux-kernel-4.4.55.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: make sure arguments are trivially compatible */
256
257         if (cmd->start_arg != 0) {
258                 cmd->start_arg = 0;
259                 err++;
260         }
261         if (cmd->convert_src == TRIG_NOW) {
262                 if (cmd->convert_arg != 0) {
263                         cmd->convert_arg = 0;
264                         err++;
265                 }
266         }
267         if (cmd->scan_begin_src == TRIG_TIMER) {
268                 if (cmd->scan_begin_arg < nano_per_micro) {
269                         cmd->scan_begin_arg = nano_per_micro;
270                         err++;
271                 }
272                 if (cmd->convert_src == TRIG_TIMER &&
273                     cmd->scan_begin_arg <
274                     cmd->convert_arg * cmd->chanlist_len) {
275                         cmd->scan_begin_arg =
276                             cmd->convert_arg * cmd->chanlist_len;
277                         err++;
278                 }
279         }
280         /*
281          * XXX these checks are generic and should go in core if not there
282          * already
283          */
284         if (!cmd->chanlist_len) {
285                 cmd->chanlist_len = 1;
286                 err++;
287         }
288         if (cmd->scan_end_arg != cmd->chanlist_len) {
289                 cmd->scan_end_arg = cmd->chanlist_len;
290                 err++;
291         }
292
293         if (cmd->stop_src == TRIG_COUNT) {
294                 if (!cmd->stop_arg) {
295                         cmd->stop_arg = 1;
296                         err++;
297                 }
298         } else {                /* TRIG_NONE */
299                 if (cmd->stop_arg != 0) {
300                         cmd->stop_arg = 0;
301                         err++;
302                 }
303         }
304
305         if (err)
306                 return 3;
307
308         /* step 4: fix up any arguments */
309
310         if (cmd->scan_begin_src == TRIG_TIMER) {
311                 tmp = cmd->scan_begin_arg;
312                 /* round to nearest microsec */
313                 cmd->scan_begin_arg =
314                     nano_per_micro * ((tmp +
315                                        (nano_per_micro / 2)) / nano_per_micro);
316                 if (tmp != cmd->scan_begin_arg)
317                         err++;
318         }
319         if (cmd->convert_src == TRIG_TIMER) {
320                 tmp = cmd->convert_arg;
321                 /* round to nearest microsec */
322                 cmd->convert_arg =
323                     nano_per_micro * ((tmp +
324                                        (nano_per_micro / 2)) / nano_per_micro);
325                 if (tmp != cmd->convert_arg)
326                         err++;
327         }
328
329         if (err)
330                 return 4;
331
332         return 0;
333 }
334
335 static int waveform_ai_cmd(struct comedi_device *dev,
336                            struct comedi_subdevice *s)
337 {
338         struct waveform_private *devpriv = dev->private;
339         struct comedi_cmd *cmd = &s->async->cmd;
340
341         if (cmd->flags & TRIG_RT) {
342                 comedi_error(dev,
343                              "commands at RT priority not supported in this driver");
344                 return -1;
345         }
346
347         devpriv->timer_running = 1;
348         devpriv->ai_count = 0;
349         devpriv->scan_period = cmd->scan_begin_arg / nano_per_micro;
350
351         if (cmd->convert_src == TRIG_NOW)
352                 devpriv->convert_period = 0;
353         else if (cmd->convert_src == TRIG_TIMER)
354                 devpriv->convert_period = cmd->convert_arg / nano_per_micro;
355         else {
356                 comedi_error(dev, "bug setting conversion period");
357                 return -1;
358         }
359
360         do_gettimeofday(&devpriv->last);
361         devpriv->usec_current = devpriv->last.tv_usec % devpriv->usec_period;
362         devpriv->usec_remainder = 0;
363
364         devpriv->timer.expires = jiffies + 1;
365         add_timer(&devpriv->timer);
366         return 0;
367 }
368
369 static int waveform_ai_cancel(struct comedi_device *dev,
370                               struct comedi_subdevice *s)
371 {
372         struct waveform_private *devpriv = dev->private;
373
374         devpriv->timer_running = 0;
375         del_timer(&devpriv->timer);
376         return 0;
377 }
378
379 static int waveform_ai_insn_read(struct comedi_device *dev,
380                                  struct comedi_subdevice *s,
381                                  struct comedi_insn *insn, unsigned int *data)
382 {
383         struct waveform_private *devpriv = dev->private;
384         int i, chan = CR_CHAN(insn->chanspec);
385
386         for (i = 0; i < insn->n; i++)
387                 data[i] = devpriv->ao_loopbacks[chan];
388
389         return insn->n;
390 }
391
392 static int waveform_ao_insn_write(struct comedi_device *dev,
393                                   struct comedi_subdevice *s,
394                                   struct comedi_insn *insn, unsigned int *data)
395 {
396         struct waveform_private *devpriv = dev->private;
397         int i, chan = CR_CHAN(insn->chanspec);
398
399         for (i = 0; i < insn->n; i++)
400                 devpriv->ao_loopbacks[chan] = data[i];
401
402         return insn->n;
403 }
404
405 static int waveform_attach(struct comedi_device *dev,
406                            struct comedi_devconfig *it)
407 {
408         struct waveform_private *devpriv;
409         struct comedi_subdevice *s;
410         int amplitude = it->options[0];
411         int period = it->options[1];
412         int i;
413         int ret;
414
415         dev->board_name = dev->driver->driver_name;
416
417         ret = alloc_private(dev, sizeof(*devpriv));
418         if (ret < 0)
419                 return ret;
420         devpriv = dev->private;
421
422         /* set default amplitude and period */
423         if (amplitude <= 0)
424                 amplitude = 1000000;    /* 1 volt */
425         if (period <= 0)
426                 period = 100000;        /* 0.1 sec */
427
428         devpriv->uvolt_amplitude = amplitude;
429         devpriv->usec_period = period;
430
431         ret = comedi_alloc_subdevices(dev, 2);
432         if (ret)
433                 return ret;
434
435         s = &dev->subdevices[0];
436         dev->read_subdev = s;
437         /* analog input subdevice */
438         s->type = COMEDI_SUBD_AI;
439         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
440         s->n_chan = N_CHANS;
441         s->maxdata = 0xffff;
442         s->range_table = &waveform_ai_ranges;
443         s->len_chanlist = s->n_chan * 2;
444         s->insn_read = waveform_ai_insn_read;
445         s->do_cmd = waveform_ai_cmd;
446         s->do_cmdtest = waveform_ai_cmdtest;
447         s->cancel = waveform_ai_cancel;
448
449         s = &dev->subdevices[1];
450         dev->write_subdev = s;
451         /* analog output subdevice (loopback) */
452         s->type = COMEDI_SUBD_AO;
453         s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
454         s->n_chan = N_CHANS;
455         s->maxdata = 0xffff;
456         s->range_table = &waveform_ai_ranges;
457         s->len_chanlist = s->n_chan * 2;
458         s->insn_write = waveform_ao_insn_write;
459         s->do_cmd = NULL;
460         s->do_cmdtest = NULL;
461         s->cancel = NULL;
462
463         /* Our default loopback value is just a 0V flatline */
464         for (i = 0; i < s->n_chan; i++)
465                 devpriv->ao_loopbacks[i] = s->maxdata / 2;
466
467         init_timer(&(devpriv->timer));
468         devpriv->timer.function = waveform_ai_interrupt;
469         devpriv->timer.data = (unsigned long)dev;
470
471         dev_info(dev->class_dev,
472                 "%s: %i microvolt, %li microsecond waveform attached\n",
473                 dev->board_name,
474                 devpriv->uvolt_amplitude, devpriv->usec_period);
475
476         return 0;
477 }
478
479 static void waveform_detach(struct comedi_device *dev)
480 {
481         struct waveform_private *devpriv = dev->private;
482
483         if (devpriv)
484                 waveform_ai_cancel(dev, dev->read_subdev);
485 }
486
487 static struct comedi_driver waveform_driver = {
488         .driver_name    = "comedi_test",
489         .module         = THIS_MODULE,
490         .attach         = waveform_attach,
491         .detach         = waveform_detach,
492 };
493 module_comedi_driver(waveform_driver);
494
495 MODULE_AUTHOR("Comedi http://www.comedi.org");
496 MODULE_DESCRIPTION("Comedi low-level driver");
497 MODULE_LICENSE("GPL");