Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
[cascardo/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
56 #define N_CHANS 8
57
58 /* Data unique to this driver */
59 struct waveform_private {
60         struct timer_list timer;
61         struct timeval last;            /* time last timer interrupt occurred */
62         unsigned int uvolt_amplitude;   /* waveform amplitude in microvolts */
63         unsigned long usec_period;      /* waveform period in microseconds */
64         unsigned long usec_current;     /* current time (mod waveform period) */
65         unsigned long usec_remainder;   /* usec since last scan */
66         unsigned long ai_count;         /* number of conversions remaining */
67         unsigned int scan_period;       /* scan period in usec */
68         unsigned int convert_period;    /* conversion period in usec */
69         unsigned int ao_loopbacks[N_CHANS];
70 };
71
72 /* 1000 nanosec in a microsec */
73 static const int nano_per_micro = 1000;
74
75 /* fake analog input ranges */
76 static const struct comedi_lrange waveform_ai_ranges = {
77         2, {
78                 BIP_RANGE(10),
79                 BIP_RANGE(5)
80         }
81 };
82
83 static unsigned short fake_sawtooth(struct comedi_device *dev,
84                                     unsigned int range_index,
85                                     unsigned long current_time)
86 {
87         struct waveform_private *devpriv = dev->private;
88         struct comedi_subdevice *s = dev->read_subdev;
89         unsigned int offset = s->maxdata / 2;
90         u64 value;
91         const struct comedi_krange *krange =
92             &s->range_table->range[range_index];
93         u64 binary_amplitude;
94
95         binary_amplitude = s->maxdata;
96         binary_amplitude *= devpriv->uvolt_amplitude;
97         do_div(binary_amplitude, krange->max - krange->min);
98
99         current_time %= devpriv->usec_period;
100         value = current_time;
101         value *= binary_amplitude * 2;
102         do_div(value, devpriv->usec_period);
103         value -= binary_amplitude;      /* get rid of sawtooth's dc offset */
104
105         return offset + value;
106 }
107
108 static unsigned short fake_squarewave(struct comedi_device *dev,
109                                       unsigned int range_index,
110                                       unsigned long current_time)
111 {
112         struct waveform_private *devpriv = dev->private;
113         struct comedi_subdevice *s = dev->read_subdev;
114         unsigned int offset = s->maxdata / 2;
115         u64 value;
116         const struct comedi_krange *krange =
117             &s->range_table->range[range_index];
118         current_time %= devpriv->usec_period;
119
120         value = s->maxdata;
121         value *= devpriv->uvolt_amplitude;
122         do_div(value, krange->max - krange->min);
123
124         if (current_time < devpriv->usec_period / 2)
125                 value *= -1;
126
127         return offset + value;
128 }
129
130 static unsigned short fake_flatline(struct comedi_device *dev,
131                                     unsigned int range_index,
132                                     unsigned long current_time)
133 {
134         return dev->read_subdev->maxdata / 2;
135 }
136
137 /* generates a different waveform depending on what channel is read */
138 static unsigned short fake_waveform(struct comedi_device *dev,
139                                     unsigned int channel, unsigned int range,
140                                     unsigned long current_time)
141 {
142         enum {
143                 SAWTOOTH_CHAN,
144                 SQUARE_CHAN,
145         };
146         switch (channel) {
147         case SAWTOOTH_CHAN:
148                 return fake_sawtooth(dev, range, current_time);
149         case SQUARE_CHAN:
150                 return fake_squarewave(dev, range, current_time);
151         default:
152                 break;
153         }
154
155         return fake_flatline(dev, range, current_time);
156 }
157
158 /*
159    This is the background routine used to generate arbitrary data.
160    It should run in the background; therefore it is scheduled by
161    a timer mechanism.
162 */
163 static void waveform_ai_interrupt(unsigned long arg)
164 {
165         struct comedi_device *dev = (struct comedi_device *)arg;
166         struct waveform_private *devpriv = dev->private;
167         struct comedi_async *async = dev->read_subdev->async;
168         struct comedi_cmd *cmd = &async->cmd;
169         unsigned int i, j;
170         /* all times in microsec */
171         unsigned long elapsed_time;
172         unsigned int num_scans;
173         struct timeval now;
174         bool stopping = false;
175
176         do_gettimeofday(&now);
177
178         elapsed_time =
179             1000000 * (now.tv_sec - devpriv->last.tv_sec) + now.tv_usec -
180             devpriv->last.tv_usec;
181         devpriv->last = now;
182         num_scans =
183             (devpriv->usec_remainder + elapsed_time) / devpriv->scan_period;
184         devpriv->usec_remainder =
185             (devpriv->usec_remainder + elapsed_time) % devpriv->scan_period;
186
187         if (cmd->stop_src == TRIG_COUNT) {
188                 unsigned int remaining = cmd->stop_arg - devpriv->ai_count;
189
190                 if (num_scans >= remaining) {
191                         /* about to finish */
192                         num_scans = remaining;
193                         stopping = true;
194                 }
195         }
196
197         for (i = 0; i < num_scans; i++) {
198                 for (j = 0; j < cmd->chanlist_len; j++) {
199                         unsigned short sample;
200
201                         sample = fake_waveform(dev, CR_CHAN(cmd->chanlist[j]),
202                                                CR_RANGE(cmd->chanlist[j]),
203                                                devpriv->usec_current +
204                                                    i * devpriv->scan_period +
205                                                    j * devpriv->convert_period);
206                         cfc_write_to_buffer(dev->read_subdev, sample);
207                 }
208         }
209
210         devpriv->ai_count += i;
211         devpriv->usec_current += elapsed_time;
212         devpriv->usec_current %= devpriv->usec_period;
213
214         if (stopping)
215                 async->events |= COMEDI_CB_EOA;
216         else
217                 mod_timer(&devpriv->timer, jiffies + 1);
218
219         comedi_event(dev, dev->read_subdev);
220 }
221
222 static int waveform_ai_cmdtest(struct comedi_device *dev,
223                                struct comedi_subdevice *s,
224                                struct comedi_cmd *cmd)
225 {
226         int err = 0;
227         unsigned int arg;
228
229         /* Step 1 : check if triggers are trivially valid */
230
231         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
232         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
233         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW | TRIG_TIMER);
234         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
235         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
236
237         if (err)
238                 return 1;
239
240         /* Step 2a : make sure trigger sources are unique */
241
242         err |= cfc_check_trigger_is_unique(cmd->convert_src);
243         err |= cfc_check_trigger_is_unique(cmd->stop_src);
244
245         /* Step 2b : and mutually compatible */
246
247         if (err)
248                 return 2;
249
250         /* Step 3: check if arguments are trivially valid */
251
252         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
253
254         if (cmd->convert_src == TRIG_NOW)
255                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
256
257         if (cmd->scan_begin_src == TRIG_TIMER) {
258                 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
259                                                  nano_per_micro);
260                 if (cmd->convert_src == TRIG_TIMER)
261                         err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
262                                         cmd->convert_arg * cmd->chanlist_len);
263         }
264
265         err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
266         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
267
268         if (cmd->stop_src == TRIG_COUNT)
269                 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
270         else    /* TRIG_NONE */
271                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
272
273         if (err)
274                 return 3;
275
276         /* step 4: fix up any arguments */
277
278         if (cmd->scan_begin_src == TRIG_TIMER) {
279                 arg = cmd->scan_begin_arg;
280                 /* round to nearest microsec */
281                 arg = nano_per_micro *
282                       ((arg + (nano_per_micro / 2)) / nano_per_micro);
283                 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
284         }
285         if (cmd->convert_src == TRIG_TIMER) {
286                 arg = cmd->convert_arg;
287                 /* round to nearest microsec */
288                 arg = nano_per_micro *
289                       ((arg + (nano_per_micro / 2)) / nano_per_micro);
290                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
291         }
292
293         if (err)
294                 return 4;
295
296         return 0;
297 }
298
299 static int waveform_ai_cmd(struct comedi_device *dev,
300                            struct comedi_subdevice *s)
301 {
302         struct waveform_private *devpriv = dev->private;
303         struct comedi_cmd *cmd = &s->async->cmd;
304
305         if (cmd->flags & TRIG_RT) {
306                 dev_err(dev->class_dev,
307                         "commands at RT priority not supported in this driver\n");
308                 return -1;
309         }
310
311         devpriv->ai_count = 0;
312         devpriv->scan_period = cmd->scan_begin_arg / nano_per_micro;
313
314         if (cmd->convert_src == TRIG_NOW)
315                 devpriv->convert_period = 0;
316         else    /* TRIG_TIMER */
317                 devpriv->convert_period = cmd->convert_arg / nano_per_micro;
318
319         do_gettimeofday(&devpriv->last);
320         devpriv->usec_current = devpriv->last.tv_usec % devpriv->usec_period;
321         devpriv->usec_remainder = 0;
322
323         devpriv->timer.expires = jiffies + 1;
324         add_timer(&devpriv->timer);
325         return 0;
326 }
327
328 static int waveform_ai_cancel(struct comedi_device *dev,
329                               struct comedi_subdevice *s)
330 {
331         struct waveform_private *devpriv = dev->private;
332
333         del_timer_sync(&devpriv->timer);
334         return 0;
335 }
336
337 static int waveform_ai_insn_read(struct comedi_device *dev,
338                                  struct comedi_subdevice *s,
339                                  struct comedi_insn *insn, unsigned int *data)
340 {
341         struct waveform_private *devpriv = dev->private;
342         int i, chan = CR_CHAN(insn->chanspec);
343
344         for (i = 0; i < insn->n; i++)
345                 data[i] = devpriv->ao_loopbacks[chan];
346
347         return insn->n;
348 }
349
350 static int waveform_ao_insn_write(struct comedi_device *dev,
351                                   struct comedi_subdevice *s,
352                                   struct comedi_insn *insn, unsigned int *data)
353 {
354         struct waveform_private *devpriv = dev->private;
355         int i, chan = CR_CHAN(insn->chanspec);
356
357         for (i = 0; i < insn->n; i++)
358                 devpriv->ao_loopbacks[chan] = data[i];
359
360         return insn->n;
361 }
362
363 static int waveform_attach(struct comedi_device *dev,
364                            struct comedi_devconfig *it)
365 {
366         struct waveform_private *devpriv;
367         struct comedi_subdevice *s;
368         int amplitude = it->options[0];
369         int period = it->options[1];
370         int i;
371         int ret;
372
373         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
374         if (!devpriv)
375                 return -ENOMEM;
376
377         /* set default amplitude and period */
378         if (amplitude <= 0)
379                 amplitude = 1000000;    /* 1 volt */
380         if (period <= 0)
381                 period = 100000;        /* 0.1 sec */
382
383         devpriv->uvolt_amplitude = amplitude;
384         devpriv->usec_period = period;
385
386         ret = comedi_alloc_subdevices(dev, 2);
387         if (ret)
388                 return ret;
389
390         s = &dev->subdevices[0];
391         dev->read_subdev = s;
392         /* analog input subdevice */
393         s->type = COMEDI_SUBD_AI;
394         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
395         s->n_chan = N_CHANS;
396         s->maxdata = 0xffff;
397         s->range_table = &waveform_ai_ranges;
398         s->len_chanlist = s->n_chan * 2;
399         s->insn_read = waveform_ai_insn_read;
400         s->do_cmd = waveform_ai_cmd;
401         s->do_cmdtest = waveform_ai_cmdtest;
402         s->cancel = waveform_ai_cancel;
403
404         s = &dev->subdevices[1];
405         dev->write_subdev = s;
406         /* analog output subdevice (loopback) */
407         s->type = COMEDI_SUBD_AO;
408         s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
409         s->n_chan = N_CHANS;
410         s->maxdata = 0xffff;
411         s->range_table = &waveform_ai_ranges;
412         s->insn_write = waveform_ao_insn_write;
413
414         /* Our default loopback value is just a 0V flatline */
415         for (i = 0; i < s->n_chan; i++)
416                 devpriv->ao_loopbacks[i] = s->maxdata / 2;
417
418         init_timer(&(devpriv->timer));
419         devpriv->timer.function = waveform_ai_interrupt;
420         devpriv->timer.data = (unsigned long)dev;
421
422         dev_info(dev->class_dev,
423                 "%s: %i microvolt, %li microsecond waveform attached\n",
424                 dev->board_name,
425                 devpriv->uvolt_amplitude, devpriv->usec_period);
426
427         return 0;
428 }
429
430 static void waveform_detach(struct comedi_device *dev)
431 {
432         struct waveform_private *devpriv = dev->private;
433
434         if (devpriv)
435                 waveform_ai_cancel(dev, dev->read_subdev);
436 }
437
438 static struct comedi_driver waveform_driver = {
439         .driver_name    = "comedi_test",
440         .module         = THIS_MODULE,
441         .attach         = waveform_attach,
442         .detach         = waveform_detach,
443 };
444 module_comedi_driver(waveform_driver);
445
446 MODULE_AUTHOR("Comedi http://www.comedi.org");
447 MODULE_DESCRIPTION("Comedi low-level driver");
448 MODULE_LICENSE("GPL");