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