upload tizen1.0 source
[kernel/linux-2.6.36.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 /* Board descriptions */
61 struct waveform_board {
62         const char *name;
63         int ai_chans;
64         int ai_bits;
65         int have_dio;
66 };
67
68 #define N_CHANS 8
69
70 static const struct waveform_board waveform_boards[] = {
71         {
72          .name = "comedi_test",
73          .ai_chans = N_CHANS,
74          .ai_bits = 16,
75          .have_dio = 0,
76          },
77 };
78
79 #define thisboard ((const struct waveform_board *)dev->board_ptr)
80
81 /* Data unique to this driver */
82 struct waveform_private {
83         struct timer_list timer;
84         struct timeval last;    /* time at which last timer interrupt occured */
85         unsigned int uvolt_amplitude;   /* waveform amplitude in microvolts */
86         unsigned long usec_period;      /* waveform period in microseconds */
87         unsigned long usec_current;     /* current time (modulo waveform period) */
88         unsigned long usec_remainder;   /* usec since last scan; */
89         unsigned long ai_count; /* number of conversions remaining */
90         unsigned int scan_period;       /* scan period in usec */
91         unsigned int convert_period;    /* conversion period in usec */
92         unsigned timer_running:1;
93         unsigned int ao_loopbacks[N_CHANS];
94 };
95 #define devpriv ((struct waveform_private *)dev->private)
96
97 static int waveform_attach(struct comedi_device *dev,
98                            struct comedi_devconfig *it);
99 static int waveform_detach(struct comedi_device *dev);
100 static struct comedi_driver driver_waveform = {
101         .driver_name = "comedi_test",
102         .module = THIS_MODULE,
103         .attach = waveform_attach,
104         .detach = waveform_detach,
105         .board_name = &waveform_boards[0].name,
106         .offset = sizeof(struct waveform_board),
107         .num_names = ARRAY_SIZE(waveform_boards),
108 };
109
110 static int __init driver_waveform_init_module(void)
111 {
112         return comedi_driver_register(&driver_waveform);
113 }
114
115 static void __exit driver_waveform_cleanup_module(void)
116 {
117         comedi_driver_unregister(&driver_waveform);
118 }
119
120 module_init(driver_waveform_init_module);
121 module_exit(driver_waveform_cleanup_module);
122
123 static int waveform_ai_cmdtest(struct comedi_device *dev,
124                                struct comedi_subdevice *s,
125                                struct comedi_cmd *cmd);
126 static int waveform_ai_cmd(struct comedi_device *dev,
127                            struct comedi_subdevice *s);
128 static int waveform_ai_cancel(struct comedi_device *dev,
129                               struct comedi_subdevice *s);
130 static int waveform_ai_insn_read(struct comedi_device *dev,
131                                  struct comedi_subdevice *s,
132                                  struct comedi_insn *insn, unsigned int *data);
133 static int waveform_ao_insn_write(struct comedi_device *dev,
134                                   struct comedi_subdevice *s,
135                                   struct comedi_insn *insn, unsigned int *data);
136 static short fake_sawtooth(struct comedi_device *dev, unsigned int range,
137                            unsigned long current_time);
138 static short fake_squarewave(struct comedi_device *dev, unsigned int range,
139                              unsigned long current_time);
140 static short fake_flatline(struct comedi_device *dev, unsigned int range,
141                            unsigned long current_time);
142 static short fake_waveform(struct comedi_device *dev, unsigned int channel,
143                            unsigned int range, unsigned long current_time);
144
145 /* 1000 nanosec in a microsec */
146 static const int nano_per_micro = 1000;
147
148 /* fake analog input ranges */
149 static const struct comedi_lrange waveform_ai_ranges = {
150         2,
151         {
152          BIP_RANGE(10),
153          BIP_RANGE(5),
154          }
155 };
156
157 /*
158    This is the background routine used to generate arbitrary data.
159    It should run in the background; therefore it is scheduled by
160    a timer mechanism.
161 */
162 static void waveform_ai_interrupt(unsigned long arg)
163 {
164         struct comedi_device *dev = (struct comedi_device *)arg;
165         struct comedi_async *async = dev->read_subdev->async;
166         struct comedi_cmd *cmd = &async->cmd;
167         unsigned int i, j;
168         /* all times in microsec */
169         unsigned long elapsed_time;
170         unsigned int num_scans;
171         struct timeval now;
172
173         do_gettimeofday(&now);
174
175         elapsed_time =
176             1000000 * (now.tv_sec - devpriv->last.tv_sec) + now.tv_usec -
177             devpriv->last.tv_usec;
178         devpriv->last = now;
179         num_scans =
180             (devpriv->usec_remainder + elapsed_time) / devpriv->scan_period;
181         devpriv->usec_remainder =
182             (devpriv->usec_remainder + elapsed_time) % devpriv->scan_period;
183         async->events = 0;
184
185         for (i = 0; i < num_scans; i++) {
186                 for (j = 0; j < cmd->chanlist_len; j++) {
187                         cfc_write_to_buffer(dev->read_subdev,
188                                             fake_waveform(dev,
189                                                           CR_CHAN(cmd->
190                                                                   chanlist[j]),
191                                                           CR_RANGE(cmd->
192                                                                    chanlist[j]),
193                                                           devpriv->
194                                                           usec_current +
195                                                           i *
196                                                           devpriv->scan_period +
197                                                           j *
198                                                           devpriv->
199                                                           convert_period));
200                 }
201                 devpriv->ai_count++;
202                 if (cmd->stop_src == TRIG_COUNT
203                     && devpriv->ai_count >= cmd->stop_arg) {
204                         async->events |= COMEDI_CB_EOA;
205                         break;
206                 }
207         }
208
209         devpriv->usec_current += elapsed_time;
210         devpriv->usec_current %= devpriv->usec_period;
211
212         if ((async->events & COMEDI_CB_EOA) == 0 && devpriv->timer_running)
213                 mod_timer(&devpriv->timer, jiffies + 1);
214         else
215                 del_timer(&devpriv->timer);
216
217         comedi_event(dev, dev->read_subdev);
218 }
219
220 static int waveform_attach(struct comedi_device *dev,
221                            struct comedi_devconfig *it)
222 {
223         struct comedi_subdevice *s;
224         int amplitude = it->options[0];
225         int period = it->options[1];
226         int i;
227
228         dev->board_name = thisboard->name;
229
230         if (alloc_private(dev, sizeof(struct waveform_private)) < 0)
231                 return -ENOMEM;
232
233         /* set default amplitude and period */
234         if (amplitude <= 0)
235                 amplitude = 1000000;    /* 1 volt */
236         if (period <= 0)
237                 period = 100000;        /* 0.1 sec */
238
239         devpriv->uvolt_amplitude = amplitude;
240         devpriv->usec_period = period;
241
242         dev->n_subdevices = 2;
243         if (alloc_subdevices(dev, dev->n_subdevices) < 0)
244                 return -ENOMEM;
245
246         s = dev->subdevices + 0;
247         dev->read_subdev = s;
248         /* analog input subdevice */
249         s->type = COMEDI_SUBD_AI;
250         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
251         s->n_chan = thisboard->ai_chans;
252         s->maxdata = (1 << thisboard->ai_bits) - 1;
253         s->range_table = &waveform_ai_ranges;
254         s->len_chanlist = s->n_chan * 2;
255         s->insn_read = waveform_ai_insn_read;
256         s->do_cmd = waveform_ai_cmd;
257         s->do_cmdtest = waveform_ai_cmdtest;
258         s->cancel = waveform_ai_cancel;
259
260         s = dev->subdevices + 1;
261         dev->write_subdev = s;
262         /* analog output subdevice (loopback) */
263         s->type = COMEDI_SUBD_AO;
264         s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
265         s->n_chan = thisboard->ai_chans;
266         s->maxdata = (1 << thisboard->ai_bits) - 1;
267         s->range_table = &waveform_ai_ranges;
268         s->len_chanlist = s->n_chan * 2;
269         s->insn_write = waveform_ao_insn_write;
270         s->do_cmd = NULL;
271         s->do_cmdtest = NULL;
272         s->cancel = NULL;
273
274         /* Our default loopback value is just a 0V flatline */
275         for (i = 0; i < s->n_chan; i++)
276                 devpriv->ao_loopbacks[i] = s->maxdata / 2;
277
278         init_timer(&(devpriv->timer));
279         devpriv->timer.function = waveform_ai_interrupt;
280         devpriv->timer.data = (unsigned long)dev;
281
282         printk(KERN_INFO "comedi%d: comedi_test: "
283                "%i microvolt, %li microsecond waveform attached\n", dev->minor,
284                devpriv->uvolt_amplitude, devpriv->usec_period);
285         return 1;
286 }
287
288 static int waveform_detach(struct comedi_device *dev)
289 {
290         printk("comedi%d: comedi_test: remove\n", dev->minor);
291
292         if (dev->private)
293                 waveform_ai_cancel(dev, dev->read_subdev);
294
295         return 0;
296 }
297
298 static int waveform_ai_cmdtest(struct comedi_device *dev,
299                                struct comedi_subdevice *s,
300                                struct comedi_cmd *cmd)
301 {
302         int err = 0;
303         int tmp;
304
305         /* step 1: make sure trigger sources are trivially valid */
306
307         tmp = cmd->start_src;
308         cmd->start_src &= TRIG_NOW;
309         if (!cmd->start_src || tmp != cmd->start_src)
310                 err++;
311
312         tmp = cmd->scan_begin_src;
313         cmd->scan_begin_src &= TRIG_TIMER;
314         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
315                 err++;
316
317         tmp = cmd->convert_src;
318         cmd->convert_src &= TRIG_NOW | TRIG_TIMER;
319         if (!cmd->convert_src || tmp != cmd->convert_src)
320                 err++;
321
322         tmp = cmd->scan_end_src;
323         cmd->scan_end_src &= TRIG_COUNT;
324         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
325                 err++;
326
327         tmp = cmd->stop_src;
328         cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
329         if (!cmd->stop_src || tmp != cmd->stop_src)
330                 err++;
331
332         if (err)
333                 return 1;
334
335         /*
336          * step 2: make sure trigger sources are unique and mutually compatible
337          */
338
339         if (cmd->convert_src != TRIG_NOW && cmd->convert_src != TRIG_TIMER)
340                 err++;
341         if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
342                 err++;
343
344         if (err)
345                 return 2;
346
347         /* step 3: make sure arguments are trivially compatible */
348
349         if (cmd->start_arg != 0) {
350                 cmd->start_arg = 0;
351                 err++;
352         }
353         if (cmd->convert_src == TRIG_NOW) {
354                 if (cmd->convert_arg != 0) {
355                         cmd->convert_arg = 0;
356                         err++;
357                 }
358         }
359         if (cmd->scan_begin_src == TRIG_TIMER) {
360                 if (cmd->scan_begin_arg < nano_per_micro) {
361                         cmd->scan_begin_arg = nano_per_micro;
362                         err++;
363                 }
364                 if (cmd->convert_src == TRIG_TIMER &&
365                     cmd->scan_begin_arg <
366                     cmd->convert_arg * cmd->chanlist_len) {
367                         cmd->scan_begin_arg =
368                             cmd->convert_arg * cmd->chanlist_len;
369                         err++;
370                 }
371         }
372         /*
373          * XXX these checks are generic and should go in core if not there
374          * already
375          */
376         if (!cmd->chanlist_len) {
377                 cmd->chanlist_len = 1;
378                 err++;
379         }
380         if (cmd->scan_end_arg != cmd->chanlist_len) {
381                 cmd->scan_end_arg = cmd->chanlist_len;
382                 err++;
383         }
384
385         if (cmd->stop_src == TRIG_COUNT) {
386                 if (!cmd->stop_arg) {
387                         cmd->stop_arg = 1;
388                         err++;
389                 }
390         } else {                /* TRIG_NONE */
391                 if (cmd->stop_arg != 0) {
392                         cmd->stop_arg = 0;
393                         err++;
394                 }
395         }
396
397         if (err)
398                 return 3;
399
400         /* step 4: fix up any arguments */
401
402         if (cmd->scan_begin_src == TRIG_TIMER) {
403                 tmp = cmd->scan_begin_arg;
404                 /* round to nearest microsec */
405                 cmd->scan_begin_arg =
406                     nano_per_micro * ((tmp +
407                                        (nano_per_micro / 2)) / nano_per_micro);
408                 if (tmp != cmd->scan_begin_arg)
409                         err++;
410         }
411         if (cmd->convert_src == TRIG_TIMER) {
412                 tmp = cmd->convert_arg;
413                 /* round to nearest microsec */
414                 cmd->convert_arg =
415                     nano_per_micro * ((tmp +
416                                        (nano_per_micro / 2)) / nano_per_micro);
417                 if (tmp != cmd->convert_arg)
418                         err++;
419         }
420
421         if (err)
422                 return 4;
423
424         return 0;
425 }
426
427 static int waveform_ai_cmd(struct comedi_device *dev,
428                            struct comedi_subdevice *s)
429 {
430         struct comedi_cmd *cmd = &s->async->cmd;
431
432         if (cmd->flags & TRIG_RT) {
433                 comedi_error(dev,
434                              "commands at RT priority not supported in this driver");
435                 return -1;
436         }
437
438         devpriv->timer_running = 1;
439         devpriv->ai_count = 0;
440         devpriv->scan_period = cmd->scan_begin_arg / nano_per_micro;
441
442         if (cmd->convert_src == TRIG_NOW)
443                 devpriv->convert_period = 0;
444         else if (cmd->convert_src == TRIG_TIMER)
445                 devpriv->convert_period = cmd->convert_arg / nano_per_micro;
446         else {
447                 comedi_error(dev, "bug setting conversion period");
448                 return -1;
449         }
450
451         do_gettimeofday(&devpriv->last);
452         devpriv->usec_current = devpriv->last.tv_usec % devpriv->usec_period;
453         devpriv->usec_remainder = 0;
454
455         devpriv->timer.expires = jiffies + 1;
456         add_timer(&devpriv->timer);
457         return 0;
458 }
459
460 static int waveform_ai_cancel(struct comedi_device *dev,
461                               struct comedi_subdevice *s)
462 {
463         devpriv->timer_running = 0;
464         del_timer(&devpriv->timer);
465         return 0;
466 }
467
468 static short fake_sawtooth(struct comedi_device *dev, unsigned int range_index,
469                            unsigned long current_time)
470 {
471         struct comedi_subdevice *s = dev->read_subdev;
472         unsigned int offset = s->maxdata / 2;
473         u64 value;
474         const struct comedi_krange *krange =
475             &s->range_table->range[range_index];
476         u64 binary_amplitude;
477
478         binary_amplitude = s->maxdata;
479         binary_amplitude *= devpriv->uvolt_amplitude;
480         do_div(binary_amplitude, krange->max - krange->min);
481
482         current_time %= devpriv->usec_period;
483         value = current_time;
484         value *= binary_amplitude * 2;
485         do_div(value, devpriv->usec_period);
486         value -= binary_amplitude;      /* get rid of sawtooth's dc offset */
487
488         return offset + value;
489 }
490
491 static short fake_squarewave(struct comedi_device *dev,
492                              unsigned int range_index,
493                              unsigned long current_time)
494 {
495         struct comedi_subdevice *s = dev->read_subdev;
496         unsigned int offset = s->maxdata / 2;
497         u64 value;
498         const struct comedi_krange *krange =
499             &s->range_table->range[range_index];
500         current_time %= devpriv->usec_period;
501
502         value = s->maxdata;
503         value *= devpriv->uvolt_amplitude;
504         do_div(value, krange->max - krange->min);
505
506         if (current_time < devpriv->usec_period / 2)
507                 value *= -1;
508
509         return offset + value;
510 }
511
512 static short fake_flatline(struct comedi_device *dev, unsigned int range_index,
513                            unsigned long current_time)
514 {
515         return dev->read_subdev->maxdata / 2;
516 }
517
518 /* generates a different waveform depending on what channel is read */
519 static short fake_waveform(struct comedi_device *dev, unsigned int channel,
520                            unsigned int range, unsigned long current_time)
521 {
522         enum {
523                 SAWTOOTH_CHAN,
524                 SQUARE_CHAN,
525         };
526         switch (channel) {
527         case SAWTOOTH_CHAN:
528                 return fake_sawtooth(dev, range, current_time);
529                 break;
530         case SQUARE_CHAN:
531                 return fake_squarewave(dev, range, current_time);
532                 break;
533         default:
534                 break;
535         }
536
537         return fake_flatline(dev, range, current_time);
538 }
539
540 static int waveform_ai_insn_read(struct comedi_device *dev,
541                                  struct comedi_subdevice *s,
542                                  struct comedi_insn *insn, unsigned int *data)
543 {
544         int i, chan = CR_CHAN(insn->chanspec);
545
546         for (i = 0; i < insn->n; i++)
547                 data[i] = devpriv->ao_loopbacks[chan];
548
549         return insn->n;
550 }
551
552 static int waveform_ao_insn_write(struct comedi_device *dev,
553                                   struct comedi_subdevice *s,
554                                   struct comedi_insn *insn, unsigned int *data)
555 {
556         int i, chan = CR_CHAN(insn->chanspec);
557
558         for (i = 0; i < insn->n; i++)
559                 devpriv->ao_loopbacks[chan] = data[i];
560
561         return insn->n;
562 }
563
564 MODULE_AUTHOR("Comedi http://www.comedi.org");
565 MODULE_DESCRIPTION("Comedi low-level driver");
566 MODULE_LICENSE("GPL");