linux/drivers/staging/comedi/drivers/comedi_test.c
<<
>>
Prefs
   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/*
  27 * Driver: comedi_test
  28 * Description: generates fake waveforms
  29 * Author: Joachim Wuttke <Joachim.Wuttke@icn.siemens.de>, Frank Mori Hess
  30 *   <fmhess@users.sourceforge.net>, ds
  31 * Devices:
  32 * Status: works
  33 * Updated: Sat, 16 Mar 2002 17:34:48 -0800
  34 *
  35 * This driver is mainly for testing purposes, but can also be used to
  36 * generate sample waveforms on systems that don't have data acquisition
  37 * hardware.
  38 *
  39 * Auto-configuration is the default mode if no parameter is supplied during
  40 * module loading. Manual configuration requires COMEDI userspace tool.
  41 * To disable auto-configuration mode, pass "noauto=1" parameter for module
  42 * loading. Refer modinfo or MODULE_PARM_DESC description below for details.
  43 *
  44 * Auto-configuration options:
  45 *   Refer modinfo or MODULE_PARM_DESC description below for details.
  46 *
  47 * Manual configuration options:
  48 *   [0] - Amplitude in microvolts for fake waveforms (default 1 volt)
  49 *   [1] - Period in microseconds for fake waveforms (default 0.1 sec)
  50 *
  51 * Generates a sawtooth wave on channel 0, square wave on channel 1, additional
  52 * waveforms could be added to other channels (currently they return flatline
  53 * zero volts).
  54 */
  55
  56#include <linux/module.h>
  57#include "../comedidev.h"
  58
  59#include <asm/div64.h>
  60
  61#include <linux/timer.h>
  62#include <linux/ktime.h>
  63#include <linux/jiffies.h>
  64#include <linux/device.h>
  65#include <linux/kdev_t.h>
  66
  67#define N_CHANS 8
  68#define DEV_NAME "comedi_testd"
  69#define CLASS_NAME "comedi_test"
  70
  71static bool config_mode;
  72static unsigned int set_amplitude;
  73static unsigned int set_period;
  74static struct class *ctcls;
  75static struct device *ctdev;
  76
  77module_param_named(noauto, config_mode, bool, 0444);
  78MODULE_PARM_DESC(noauto, "Disable auto-configuration: (1=disable [defaults to enable])");
  79
  80module_param_named(amplitude, set_amplitude, uint, 0444);
  81MODULE_PARM_DESC(amplitude, "Set auto mode wave amplitude in microvolts: (defaults to 1 volt)");
  82
  83module_param_named(period, set_period, uint, 0444);
  84MODULE_PARM_DESC(period, "Set auto mode wave period in microseconds: (defaults to 0.1 sec)");
  85
  86/* Data unique to this driver */
  87struct waveform_private {
  88        struct timer_list ai_timer;     /* timer for AI commands */
  89        u64 ai_convert_time;            /* time of next AI conversion in usec */
  90        unsigned int wf_amplitude;      /* waveform amplitude in microvolts */
  91        unsigned int wf_period;         /* waveform period in microseconds */
  92        unsigned int wf_current;        /* current time in waveform period */
  93        unsigned int ai_scan_period;    /* AI scan period in usec */
  94        unsigned int ai_convert_period; /* AI conversion period in usec */
  95        struct timer_list ao_timer;     /* timer for AO commands */
  96        u64 ao_last_scan_time;          /* time of previous AO scan in usec */
  97        unsigned int ao_scan_period;    /* AO scan period in usec */
  98        unsigned short ao_loopbacks[N_CHANS];
  99};
 100
 101/* fake analog input ranges */
 102static const struct comedi_lrange waveform_ai_ranges = {
 103        2, {
 104                BIP_RANGE(10),
 105                BIP_RANGE(5)
 106        }
 107};
 108
 109static unsigned short fake_sawtooth(struct comedi_device *dev,
 110                                    unsigned int range_index,
 111                                    unsigned int current_time)
 112{
 113        struct waveform_private *devpriv = dev->private;
 114        struct comedi_subdevice *s = dev->read_subdev;
 115        unsigned int offset = s->maxdata / 2;
 116        u64 value;
 117        const struct comedi_krange *krange =
 118            &s->range_table->range[range_index];
 119        u64 binary_amplitude;
 120
 121        binary_amplitude = s->maxdata;
 122        binary_amplitude *= devpriv->wf_amplitude;
 123        do_div(binary_amplitude, krange->max - krange->min);
 124
 125        value = current_time;
 126        value *= binary_amplitude * 2;
 127        do_div(value, devpriv->wf_period);
 128        value += offset;
 129        /* get rid of sawtooth's dc offset and clamp value */
 130        if (value < binary_amplitude) {
 131                value = 0;                      /* negative saturation */
 132        } else {
 133                value -= binary_amplitude;
 134                if (value > s->maxdata)
 135                        value = s->maxdata;     /* positive saturation */
 136        }
 137
 138        return value;
 139}
 140
 141static unsigned short fake_squarewave(struct comedi_device *dev,
 142                                      unsigned int range_index,
 143                                      unsigned int current_time)
 144{
 145        struct waveform_private *devpriv = dev->private;
 146        struct comedi_subdevice *s = dev->read_subdev;
 147        unsigned int offset = s->maxdata / 2;
 148        u64 value;
 149        const struct comedi_krange *krange =
 150            &s->range_table->range[range_index];
 151
 152        value = s->maxdata;
 153        value *= devpriv->wf_amplitude;
 154        do_div(value, krange->max - krange->min);
 155
 156        /* get one of two values for square-wave and clamp */
 157        if (current_time < devpriv->wf_period / 2) {
 158                if (offset < value)
 159                        value = 0;              /* negative saturation */
 160                else
 161                        value = offset - value;
 162        } else {
 163                value += offset;
 164                if (value > s->maxdata)
 165                        value = s->maxdata;     /* positive saturation */
 166        }
 167
 168        return value;
 169}
 170
 171static unsigned short fake_flatline(struct comedi_device *dev,
 172                                    unsigned int range_index,
 173                                    unsigned int current_time)
 174{
 175        return dev->read_subdev->maxdata / 2;
 176}
 177
 178/* generates a different waveform depending on what channel is read */
 179static unsigned short fake_waveform(struct comedi_device *dev,
 180                                    unsigned int channel, unsigned int range,
 181                                    unsigned int current_time)
 182{
 183        enum {
 184                SAWTOOTH_CHAN,
 185                SQUARE_CHAN,
 186        };
 187        switch (channel) {
 188        case SAWTOOTH_CHAN:
 189                return fake_sawtooth(dev, range, current_time);
 190        case SQUARE_CHAN:
 191                return fake_squarewave(dev, range, current_time);
 192        default:
 193                break;
 194        }
 195
 196        return fake_flatline(dev, range, current_time);
 197}
 198
 199/*
 200 * This is the background routine used to generate arbitrary data.
 201 * It should run in the background; therefore it is scheduled by
 202 * a timer mechanism.
 203 */
 204static void waveform_ai_timer(unsigned long arg)
 205{
 206        struct comedi_device *dev = (struct comedi_device *)arg;
 207        struct waveform_private *devpriv = dev->private;
 208        struct comedi_subdevice *s = dev->read_subdev;
 209        struct comedi_async *async = s->async;
 210        struct comedi_cmd *cmd = &async->cmd;
 211        u64 now;
 212        unsigned int nsamples;
 213        unsigned int time_increment;
 214
 215        now = ktime_to_us(ktime_get());
 216        nsamples = comedi_nsamples_left(s, UINT_MAX);
 217
 218        while (nsamples && devpriv->ai_convert_time < now) {
 219                unsigned int chanspec = cmd->chanlist[async->cur_chan];
 220                unsigned short sample;
 221
 222                sample = fake_waveform(dev, CR_CHAN(chanspec),
 223                                       CR_RANGE(chanspec), devpriv->wf_current);
 224                if (comedi_buf_write_samples(s, &sample, 1) == 0)
 225                        goto overrun;
 226                time_increment = devpriv->ai_convert_period;
 227                if (async->scan_progress == 0) {
 228                        /* done last conversion in scan, so add dead time */
 229                        time_increment += devpriv->ai_scan_period -
 230                                          devpriv->ai_convert_period *
 231                                          cmd->scan_end_arg;
 232                }
 233                devpriv->wf_current += time_increment;
 234                if (devpriv->wf_current >= devpriv->wf_period)
 235                        devpriv->wf_current %= devpriv->wf_period;
 236                devpriv->ai_convert_time += time_increment;
 237                nsamples--;
 238        }
 239
 240        if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) {
 241                async->events |= COMEDI_CB_EOA;
 242        } else {
 243                if (devpriv->ai_convert_time > now)
 244                        time_increment = devpriv->ai_convert_time - now;
 245                else
 246                        time_increment = 1;
 247                mod_timer(&devpriv->ai_timer,
 248                          jiffies + usecs_to_jiffies(time_increment));
 249        }
 250
 251overrun:
 252        comedi_handle_events(dev, s);
 253}
 254
 255static int waveform_ai_cmdtest(struct comedi_device *dev,
 256                               struct comedi_subdevice *s,
 257                               struct comedi_cmd *cmd)
 258{
 259        int err = 0;
 260        unsigned int arg, limit;
 261
 262        /* Step 1 : check if triggers are trivially valid */
 263
 264        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 265        err |= comedi_check_trigger_src(&cmd->scan_begin_src,
 266                                        TRIG_FOLLOW | TRIG_TIMER);
 267        err |= comedi_check_trigger_src(&cmd->convert_src,
 268                                        TRIG_NOW | TRIG_TIMER);
 269        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 270        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 271
 272        if (err)
 273                return 1;
 274
 275        /* Step 2a : make sure trigger sources are unique */
 276
 277        err |= comedi_check_trigger_is_unique(cmd->convert_src);
 278        err |= comedi_check_trigger_is_unique(cmd->stop_src);
 279
 280        /* Step 2b : and mutually compatible */
 281
 282        if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
 283                err |= -EINVAL;         /* scan period would be 0 */
 284
 285        if (err)
 286                return 2;
 287
 288        /* Step 3: check if arguments are trivially valid */
 289
 290        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 291
 292        if (cmd->convert_src == TRIG_NOW) {
 293                err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 294        } else {        /* cmd->convert_src == TRIG_TIMER */
 295                if (cmd->scan_begin_src == TRIG_FOLLOW) {
 296                        err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
 297                                                            NSEC_PER_USEC);
 298                }
 299        }
 300
 301        if (cmd->scan_begin_src == TRIG_FOLLOW) {
 302                err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 303        } else {        /* cmd->scan_begin_src == TRIG_TIMER */
 304                err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
 305                                                    NSEC_PER_USEC);
 306        }
 307
 308        err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
 309        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 310                                           cmd->chanlist_len);
 311
 312        if (cmd->stop_src == TRIG_COUNT)
 313                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 314        else    /* cmd->stop_src == TRIG_NONE */
 315                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 316
 317        if (err)
 318                return 3;
 319
 320        /* step 4: fix up any arguments */
 321
 322        if (cmd->convert_src == TRIG_TIMER) {
 323                /* round convert_arg to nearest microsecond */
 324                arg = cmd->convert_arg;
 325                arg = min(arg,
 326                          rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC));
 327                arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC);
 328                if (cmd->scan_begin_arg == TRIG_TIMER) {
 329                        /* limit convert_arg to keep scan_begin_arg in range */
 330                        limit = UINT_MAX / cmd->scan_end_arg;
 331                        limit = rounddown(limit, (unsigned int)NSEC_PER_SEC);
 332                        arg = min(arg, limit);
 333                }
 334                err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
 335        }
 336
 337        if (cmd->scan_begin_src == TRIG_TIMER) {
 338                /* round scan_begin_arg to nearest microsecond */
 339                arg = cmd->scan_begin_arg;
 340                arg = min(arg,
 341                          rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC));
 342                arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC);
 343                if (cmd->convert_src == TRIG_TIMER) {
 344                        /* but ensure scan_begin_arg is large enough */
 345                        arg = max(arg, cmd->convert_arg * cmd->scan_end_arg);
 346                }
 347                err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
 348        }
 349
 350        if (err)
 351                return 4;
 352
 353        return 0;
 354}
 355
 356static int waveform_ai_cmd(struct comedi_device *dev,
 357                           struct comedi_subdevice *s)
 358{
 359        struct waveform_private *devpriv = dev->private;
 360        struct comedi_cmd *cmd = &s->async->cmd;
 361        unsigned int first_convert_time;
 362        u64 wf_current;
 363
 364        if (cmd->flags & CMDF_PRIORITY) {
 365                dev_err(dev->class_dev,
 366                        "commands at RT priority not supported in this driver\n");
 367                return -1;
 368        }
 369
 370        if (cmd->convert_src == TRIG_NOW)
 371                devpriv->ai_convert_period = 0;
 372        else            /* cmd->convert_src == TRIG_TIMER */
 373                devpriv->ai_convert_period = cmd->convert_arg / NSEC_PER_USEC;
 374
 375        if (cmd->scan_begin_src == TRIG_FOLLOW) {
 376                devpriv->ai_scan_period = devpriv->ai_convert_period *
 377                                          cmd->scan_end_arg;
 378        } else {        /* cmd->scan_begin_src == TRIG_TIMER */
 379                devpriv->ai_scan_period = cmd->scan_begin_arg / NSEC_PER_USEC;
 380        }
 381
 382        /*
 383         * Simulate first conversion to occur at convert period after
 384         * conversion timer starts.  If scan_begin_src is TRIG_FOLLOW, assume
 385         * the conversion timer starts immediately.  If scan_begin_src is
 386         * TRIG_TIMER, assume the conversion timer starts after the scan
 387         * period.
 388         */
 389        first_convert_time = devpriv->ai_convert_period;
 390        if (cmd->scan_begin_src == TRIG_TIMER)
 391                first_convert_time += devpriv->ai_scan_period;
 392        devpriv->ai_convert_time = ktime_to_us(ktime_get()) +
 393                                   first_convert_time;
 394
 395        /* Determine time within waveform period at time of conversion. */
 396        wf_current = devpriv->ai_convert_time;
 397        devpriv->wf_current = do_div(wf_current, devpriv->wf_period);
 398
 399        /*
 400         * Schedule timer to expire just after first conversion time.
 401         * Seem to need an extra jiffy here, otherwise timer expires slightly
 402         * early!
 403         */
 404        devpriv->ai_timer.expires =
 405                jiffies + usecs_to_jiffies(devpriv->ai_convert_period) + 1;
 406        add_timer(&devpriv->ai_timer);
 407        return 0;
 408}
 409
 410static int waveform_ai_cancel(struct comedi_device *dev,
 411                              struct comedi_subdevice *s)
 412{
 413        struct waveform_private *devpriv = dev->private;
 414
 415        if (in_softirq()) {
 416                /* Assume we were called from the timer routine itself. */
 417                del_timer(&devpriv->ai_timer);
 418        } else {
 419                del_timer_sync(&devpriv->ai_timer);
 420        }
 421        return 0;
 422}
 423
 424static int waveform_ai_insn_read(struct comedi_device *dev,
 425                                 struct comedi_subdevice *s,
 426                                 struct comedi_insn *insn, unsigned int *data)
 427{
 428        struct waveform_private *devpriv = dev->private;
 429        int i, chan = CR_CHAN(insn->chanspec);
 430
 431        for (i = 0; i < insn->n; i++)
 432                data[i] = devpriv->ao_loopbacks[chan];
 433
 434        return insn->n;
 435}
 436
 437/*
 438 * This is the background routine to handle AO commands, scheduled by
 439 * a timer mechanism.
 440 */
 441static void waveform_ao_timer(unsigned long arg)
 442{
 443        struct comedi_device *dev = (struct comedi_device *)arg;
 444        struct waveform_private *devpriv = dev->private;
 445        struct comedi_subdevice *s = dev->write_subdev;
 446        struct comedi_async *async = s->async;
 447        struct comedi_cmd *cmd = &async->cmd;
 448        u64 now;
 449        u64 scans_since;
 450        unsigned int scans_avail = 0;
 451
 452        /* determine number of scan periods since last time */
 453        now = ktime_to_us(ktime_get());
 454        scans_since = now - devpriv->ao_last_scan_time;
 455        do_div(scans_since, devpriv->ao_scan_period);
 456        if (scans_since) {
 457                unsigned int i;
 458
 459                /* determine scans in buffer, limit to scans to do this time */
 460                scans_avail = comedi_nscans_left(s, 0);
 461                if (scans_avail > scans_since)
 462                        scans_avail = scans_since;
 463                if (scans_avail) {
 464                        /* skip all but the last scan to save processing time */
 465                        if (scans_avail > 1) {
 466                                unsigned int skip_bytes, nbytes;
 467
 468                                skip_bytes =
 469                                comedi_samples_to_bytes(s, cmd->scan_end_arg *
 470                                                           (scans_avail - 1));
 471                                nbytes = comedi_buf_read_alloc(s, skip_bytes);
 472                                comedi_buf_read_free(s, nbytes);
 473                                comedi_inc_scan_progress(s, nbytes);
 474                                if (nbytes < skip_bytes) {
 475                                        /* unexpected underrun! (cancelled?) */
 476                                        async->events |= COMEDI_CB_OVERFLOW;
 477                                        goto underrun;
 478                                }
 479                        }
 480                        /* output the last scan */
 481                        for (i = 0; i < cmd->scan_end_arg; i++) {
 482                                unsigned int chan = CR_CHAN(cmd->chanlist[i]);
 483                                unsigned short *pd;
 484
 485                                pd = &devpriv->ao_loopbacks[chan];
 486
 487                                if (!comedi_buf_read_samples(s, pd, 1)) {
 488                                        /* unexpected underrun! (cancelled?) */
 489                                        async->events |= COMEDI_CB_OVERFLOW;
 490                                        goto underrun;
 491                                }
 492                        }
 493                        /* advance time of last scan */
 494                        devpriv->ao_last_scan_time +=
 495                                (u64)scans_avail * devpriv->ao_scan_period;
 496                }
 497        }
 498        if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) {
 499                async->events |= COMEDI_CB_EOA;
 500        } else if (scans_avail < scans_since) {
 501                async->events |= COMEDI_CB_OVERFLOW;
 502        } else {
 503                unsigned int time_inc = devpriv->ao_last_scan_time +
 504                                        devpriv->ao_scan_period - now;
 505
 506                mod_timer(&devpriv->ao_timer,
 507                          jiffies + usecs_to_jiffies(time_inc));
 508        }
 509
 510underrun:
 511        comedi_handle_events(dev, s);
 512}
 513
 514static int waveform_ao_inttrig_start(struct comedi_device *dev,
 515                                     struct comedi_subdevice *s,
 516                                     unsigned int trig_num)
 517{
 518        struct waveform_private *devpriv = dev->private;
 519        struct comedi_async *async = s->async;
 520        struct comedi_cmd *cmd = &async->cmd;
 521
 522        if (trig_num != cmd->start_arg)
 523                return -EINVAL;
 524
 525        async->inttrig = NULL;
 526
 527        devpriv->ao_last_scan_time = ktime_to_us(ktime_get());
 528        devpriv->ao_timer.expires =
 529                jiffies + usecs_to_jiffies(devpriv->ao_scan_period);
 530        add_timer(&devpriv->ao_timer);
 531
 532        return 1;
 533}
 534
 535static int waveform_ao_cmdtest(struct comedi_device *dev,
 536                               struct comedi_subdevice *s,
 537                               struct comedi_cmd *cmd)
 538{
 539        int err = 0;
 540        unsigned int arg;
 541
 542        /* Step 1 : check if triggers are trivially valid */
 543
 544        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT);
 545        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
 546        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
 547        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 548        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 549
 550        if (err)
 551                return 1;
 552
 553        /* Step 2a : make sure trigger sources are unique */
 554
 555        err |= comedi_check_trigger_is_unique(cmd->stop_src);
 556
 557        /* Step 2b : and mutually compatible */
 558
 559        if (err)
 560                return 2;
 561
 562        /* Step 3: check if arguments are trivially valid */
 563
 564        err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
 565                                            NSEC_PER_USEC);
 566        err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 567        err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
 568        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 569                                           cmd->chanlist_len);
 570        if (cmd->stop_src == TRIG_COUNT)
 571                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 572        else    /* cmd->stop_src == TRIG_NONE */
 573                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 574
 575        if (err)
 576                return 3;
 577
 578        /* step 4: fix up any arguments */
 579
 580        /* round scan_begin_arg to nearest microsecond */
 581        arg = cmd->scan_begin_arg;
 582        arg = min(arg, rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC));
 583        arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC);
 584        err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
 585
 586        if (err)
 587                return 4;
 588
 589        return 0;
 590}
 591
 592static int waveform_ao_cmd(struct comedi_device *dev,
 593                           struct comedi_subdevice *s)
 594{
 595        struct waveform_private *devpriv = dev->private;
 596        struct comedi_cmd *cmd = &s->async->cmd;
 597
 598        if (cmd->flags & CMDF_PRIORITY) {
 599                dev_err(dev->class_dev,
 600                        "commands at RT priority not supported in this driver\n");
 601                return -1;
 602        }
 603
 604        devpriv->ao_scan_period = cmd->scan_begin_arg / NSEC_PER_USEC;
 605        s->async->inttrig = waveform_ao_inttrig_start;
 606        return 0;
 607}
 608
 609static int waveform_ao_cancel(struct comedi_device *dev,
 610                              struct comedi_subdevice *s)
 611{
 612        struct waveform_private *devpriv = dev->private;
 613
 614        s->async->inttrig = NULL;
 615        if (in_softirq()) {
 616                /* Assume we were called from the timer routine itself. */
 617                del_timer(&devpriv->ao_timer);
 618        } else {
 619                del_timer_sync(&devpriv->ao_timer);
 620        }
 621        return 0;
 622}
 623
 624static int waveform_ao_insn_write(struct comedi_device *dev,
 625                                  struct comedi_subdevice *s,
 626                                  struct comedi_insn *insn, unsigned int *data)
 627{
 628        struct waveform_private *devpriv = dev->private;
 629        int i, chan = CR_CHAN(insn->chanspec);
 630
 631        for (i = 0; i < insn->n; i++)
 632                devpriv->ao_loopbacks[chan] = data[i];
 633
 634        return insn->n;
 635}
 636
 637static int waveform_common_attach(struct comedi_device *dev,
 638                                  int amplitude, int period)
 639{
 640        struct waveform_private *devpriv;
 641        struct comedi_subdevice *s;
 642        int i;
 643        int ret;
 644
 645        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 646        if (!devpriv)
 647                return -ENOMEM;
 648
 649        devpriv->wf_amplitude = amplitude;
 650        devpriv->wf_period = period;
 651
 652        ret = comedi_alloc_subdevices(dev, 2);
 653        if (ret)
 654                return ret;
 655
 656        s = &dev->subdevices[0];
 657        dev->read_subdev = s;
 658        /* analog input subdevice */
 659        s->type = COMEDI_SUBD_AI;
 660        s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
 661        s->n_chan = N_CHANS;
 662        s->maxdata = 0xffff;
 663        s->range_table = &waveform_ai_ranges;
 664        s->len_chanlist = s->n_chan * 2;
 665        s->insn_read = waveform_ai_insn_read;
 666        s->do_cmd = waveform_ai_cmd;
 667        s->do_cmdtest = waveform_ai_cmdtest;
 668        s->cancel = waveform_ai_cancel;
 669
 670        s = &dev->subdevices[1];
 671        dev->write_subdev = s;
 672        /* analog output subdevice (loopback) */
 673        s->type = COMEDI_SUBD_AO;
 674        s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE;
 675        s->n_chan = N_CHANS;
 676        s->maxdata = 0xffff;
 677        s->range_table = &waveform_ai_ranges;
 678        s->len_chanlist = s->n_chan;
 679        s->insn_write = waveform_ao_insn_write;
 680        s->insn_read = waveform_ai_insn_read;   /* do same as AI insn_read */
 681        s->do_cmd = waveform_ao_cmd;
 682        s->do_cmdtest = waveform_ao_cmdtest;
 683        s->cancel = waveform_ao_cancel;
 684
 685        /* Our default loopback value is just a 0V flatline */
 686        for (i = 0; i < s->n_chan; i++)
 687                devpriv->ao_loopbacks[i] = s->maxdata / 2;
 688
 689        setup_timer(&devpriv->ai_timer, waveform_ai_timer, (unsigned long)dev);
 690        setup_timer(&devpriv->ao_timer, waveform_ao_timer, (unsigned long)dev);
 691
 692        dev_info(dev->class_dev,
 693                 "%s: %u microvolt, %u microsecond waveform attached\n",
 694                 dev->board_name,
 695                 devpriv->wf_amplitude, devpriv->wf_period);
 696
 697        return 0;
 698}
 699
 700static int waveform_attach(struct comedi_device *dev,
 701                           struct comedi_devconfig *it)
 702{
 703        int amplitude = it->options[0];
 704        int period = it->options[1];
 705
 706        /* set default amplitude and period */
 707        if (amplitude <= 0)
 708                amplitude = 1000000;    /* 1 volt */
 709        if (period <= 0)
 710                period = 100000;        /* 0.1 sec */
 711
 712        return waveform_common_attach(dev, amplitude, period);
 713}
 714
 715static int waveform_auto_attach(struct comedi_device *dev,
 716                                unsigned long context_unused)
 717{
 718        int amplitude = set_amplitude;
 719        int period = set_period;
 720
 721        /* set default amplitude and period */
 722        if (!amplitude)
 723                amplitude = 1000000;    /* 1 volt */
 724        if (!period)
 725                period = 100000;        /* 0.1 sec */
 726
 727        return waveform_common_attach(dev, amplitude, period);
 728}
 729
 730static void waveform_detach(struct comedi_device *dev)
 731{
 732        struct waveform_private *devpriv = dev->private;
 733
 734        if (devpriv) {
 735                del_timer_sync(&devpriv->ai_timer);
 736                del_timer_sync(&devpriv->ao_timer);
 737        }
 738}
 739
 740static struct comedi_driver waveform_driver = {
 741        .driver_name    = "comedi_test",
 742        .module         = THIS_MODULE,
 743        .attach         = waveform_attach,
 744        .auto_attach    = waveform_auto_attach,
 745        .detach         = waveform_detach,
 746};
 747
 748/*
 749 * For auto-configuration, a device is created to stand in for a
 750 * real hardware device.
 751 */
 752static int __init comedi_test_init(void)
 753{
 754        int ret;
 755
 756        ret = comedi_driver_register(&waveform_driver);
 757        if (ret) {
 758                pr_err("comedi_test: unable to register driver\n");
 759                return ret;
 760        }
 761
 762        if (!config_mode) {
 763                ctcls = class_create(THIS_MODULE, CLASS_NAME);
 764                if (IS_ERR(ctcls)) {
 765                        pr_warn("comedi_test: unable to create class\n");
 766                        goto clean3;
 767                }
 768
 769                ctdev = device_create(ctcls, NULL, MKDEV(0, 0), NULL, DEV_NAME);
 770                if (IS_ERR(ctdev)) {
 771                        pr_warn("comedi_test: unable to create device\n");
 772                        goto clean2;
 773                }
 774
 775                ret = comedi_auto_config(ctdev, &waveform_driver, 0);
 776                if (ret) {
 777                        pr_warn("comedi_test: unable to auto-configure device\n");
 778                        goto clean;
 779                }
 780        }
 781
 782        return 0;
 783
 784clean:
 785        device_destroy(ctcls, MKDEV(0, 0));
 786clean2:
 787        class_destroy(ctcls);
 788        ctdev = NULL;
 789clean3:
 790        ctcls = NULL;
 791
 792        return 0;
 793}
 794module_init(comedi_test_init);
 795
 796static void __exit comedi_test_exit(void)
 797{
 798        if (ctdev)
 799                comedi_auto_unconfig(ctdev);
 800
 801        if (ctcls) {
 802                device_destroy(ctcls, MKDEV(0, 0));
 803                class_destroy(ctcls);
 804        }
 805
 806        comedi_driver_unregister(&waveform_driver);
 807}
 808module_exit(comedi_test_exit);
 809
 810MODULE_AUTHOR("Comedi http://www.comedi.org");
 811MODULE_DESCRIPTION("Comedi low-level driver");
 812MODULE_LICENSE("GPL");
 813