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