linux/drivers/staging/comedi/drivers/skel.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/skel.c
   3    Skeleton code for a Comedi driver
   4
   5    COMEDI - Linux Control and Measurement Device Interface
   6    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   7
   8    This program is free software; you can redistribute it and/or modify
   9    it under the terms of the GNU General Public License as published by
  10    the Free Software Foundation; either version 2 of the License, or
  11    (at your option) any later version.
  12
  13    This program is distributed in the hope that it will be useful,
  14    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16    GNU General Public License for more details.
  17
  18    You should have received a copy of the GNU General Public License
  19    along with this program; if not, write to the Free Software
  20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21
  22*/
  23/*
  24Driver: skel
  25Description: Skeleton driver, an example for driver writers
  26Devices:
  27Author: ds
  28Updated: Mon, 18 Mar 2002 15:34:01 -0800
  29Status: works
  30
  31This driver is a documented example on how Comedi drivers are
  32written.
  33
  34Configuration Options:
  35  none
  36*/
  37
  38/*
  39 * The previous block comment is used to automatically generate
  40 * documentation in Comedi and Comedilib.  The fields:
  41 *
  42 * Driver: the name of the driver
  43 * Description: a short phrase describing the driver.  Don't list boards.
  44 * Devices: a full list of the boards that attempt to be supported by
  45 *   the driver.  Format is "(manufacturer) board name [comedi name]",
  46 *   where comedi_name is the name that is used to configure the board.
  47 *   See the comment near board_name: in the struct comedi_driver structure
  48 *   below.  If (manufacturer) or [comedi name] is missing, the previous
  49 *   value is used.
  50 * Author: you
  51 * Updated: date when the _documentation_ was last updated.  Use 'date -R'
  52 *   to get a value for this.
  53 * Status: a one-word description of the status.  Valid values are:
  54 *   works - driver works correctly on most boards supported, and
  55 *     passes comedi_test.
  56 *   unknown - unknown.  Usually put there by ds.
  57 *   experimental - may not work in any particular release.  Author
  58 *     probably wants assistance testing it.
  59 *   bitrotten - driver has not been update in a long time, probably
  60 *     doesn't work, and probably is missing support for significant
  61 *     Comedi interface features.
  62 *   untested - author probably wrote it "blind", and is believed to
  63 *     work, but no confirmation.
  64 *
  65 * These headers should be followed by a blank line, and any comments
  66 * you wish to say about the driver.  The comment area is the place
  67 * to put any known bugs, limitations, unsupported features, supported
  68 * command triggers, whether or not commands are supported on particular
  69 * subdevices, etc.
  70 *
  71 * Somewhere in the comment should be information about configuration
  72 * options that are used with comedi_config.
  73 */
  74
  75#include "../comedidev.h"
  76
  77#include <linux/pci.h>          /* for PCI devices */
  78
  79/* Imaginary registers for the imaginary board */
  80
  81#define SKEL_SIZE 0
  82
  83#define SKEL_START_AI_CONV      0
  84#define SKEL_AI_READ            0
  85
  86/*
  87 * Board descriptions for two imaginary boards.  Describing the
  88 * boards in this way is optional, and completely driver-dependent.
  89 * Some drivers use arrays such as this, other do not.
  90 */
  91struct skel_board {
  92        const char *name;
  93        int ai_chans;
  94        int ai_bits;
  95        int have_dio;
  96};
  97
  98static const struct skel_board skel_boards[] = {
  99        {
 100         .name = "skel-100",
 101         .ai_chans = 16,
 102         .ai_bits = 12,
 103         .have_dio = 1,
 104         },
 105        {
 106         .name = "skel-200",
 107         .ai_chans = 8,
 108         .ai_bits = 16,
 109         .have_dio = 0,
 110         },
 111};
 112
 113/* This is used by modprobe to translate PCI IDs to drivers.  Should
 114 * only be used for PCI and ISA-PnP devices */
 115/* Please add your PCI vendor ID to comedidev.h, and it will be forwarded
 116 * upstream. */
 117#define PCI_VENDOR_ID_SKEL 0xdafe
 118static DEFINE_PCI_DEVICE_TABLE(skel_pci_table) = {
 119        {
 120        PCI_VENDOR_ID_SKEL, 0x0100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
 121        PCI_VENDOR_ID_SKEL, 0x0200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
 122        0}
 123};
 124
 125MODULE_DEVICE_TABLE(pci, skel_pci_table);
 126
 127/*
 128 * Useful for shorthand access to the particular board structure
 129 */
 130#define thisboard ((const struct skel_board *)dev->board_ptr)
 131
 132/* this structure is for data unique to this hardware driver.  If
 133   several hardware drivers keep similar information in this structure,
 134   feel free to suggest moving the variable to the struct comedi_device struct.  */
 135struct skel_private {
 136
 137        int data;
 138
 139        /* would be useful for a PCI device */
 140        struct pci_dev *pci_dev;
 141
 142        /* Used for AO readback */
 143        unsigned int ao_readback[2];
 144};
 145
 146/*
 147 * most drivers define the following macro to make it easy to
 148 * access the private structure.
 149 */
 150#define devpriv ((struct skel_private *)dev->private)
 151
 152/*
 153 * The struct comedi_driver structure tells the Comedi core module
 154 * which functions to call to configure/deconfigure (attach/detach)
 155 * the board, and also about the kernel module that contains
 156 * the device code.
 157 */
 158static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it);
 159static int skel_detach(struct comedi_device *dev);
 160static struct comedi_driver driver_skel = {
 161        .driver_name = "dummy",
 162        .module = THIS_MODULE,
 163        .attach = skel_attach,
 164        .detach = skel_detach,
 165/* It is not necessary to implement the following members if you are
 166 * writing a driver for a ISA PnP or PCI card */
 167        /* Most drivers will support multiple types of boards by
 168         * having an array of board structures.  These were defined
 169         * in skel_boards[] above.  Note that the element 'name'
 170         * was first in the structure -- Comedi uses this fact to
 171         * extract the name of the board without knowing any details
 172         * about the structure except for its length.
 173         * When a device is attached (by comedi_config), the name
 174         * of the device is given to Comedi, and Comedi tries to
 175         * match it by going through the list of board names.  If
 176         * there is a match, the address of the pointer is put
 177         * into dev->board_ptr and driver->attach() is called.
 178         *
 179         * Note that these are not necessary if you can determine
 180         * the type of board in software.  ISA PnP, PCI, and PCMCIA
 181         * devices are such boards.
 182         */
 183        .board_name = &skel_boards[0].name,
 184        .offset = sizeof(struct skel_board),
 185        .num_names = ARRAY_SIZE(skel_boards),
 186};
 187
 188static int skel_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 189                         struct comedi_insn *insn, unsigned int *data);
 190static int skel_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
 191                         struct comedi_insn *insn, unsigned int *data);
 192static int skel_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 193                         struct comedi_insn *insn, unsigned int *data);
 194static int skel_dio_insn_bits(struct comedi_device *dev,
 195                              struct comedi_subdevice *s,
 196                              struct comedi_insn *insn, unsigned int *data);
 197static int skel_dio_insn_config(struct comedi_device *dev,
 198                                struct comedi_subdevice *s,
 199                                struct comedi_insn *insn, unsigned int *data);
 200static int skel_ai_cmdtest(struct comedi_device *dev,
 201                           struct comedi_subdevice *s, struct comedi_cmd *cmd);
 202static int skel_ns_to_timer(unsigned int *ns, int round);
 203
 204/*
 205 * Attach is called by the Comedi core to configure the driver
 206 * for a particular board.  If you specified a board_name array
 207 * in the driver structure, dev->board_ptr contains that
 208 * address.
 209 */
 210static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 211{
 212        struct comedi_subdevice *s;
 213
 214        printk("comedi%d: skel: ", dev->minor);
 215
 216/*
 217 * If you can probe the device to determine what device in a series
 218 * it is, this is the place to do it.  Otherwise, dev->board_ptr
 219 * should already be initialized.
 220 */
 221        /* dev->board_ptr = skel_probe(dev, it); */
 222
 223/*
 224 * Initialize dev->board_name.  Note that we can use the "thisboard"
 225 * macro now, since we just initialized it in the last line.
 226 */
 227        dev->board_name = thisboard->name;
 228
 229/*
 230 * Allocate the private structure area.  alloc_private() is a
 231 * convenient macro defined in comedidev.h.
 232 */
 233        if (alloc_private(dev, sizeof(struct skel_private)) < 0)
 234                return -ENOMEM;
 235
 236/*
 237 * Allocate the subdevice structures.  alloc_subdevice() is a
 238 * convenient macro defined in comedidev.h.
 239 */
 240        if (alloc_subdevices(dev, 3) < 0)
 241                return -ENOMEM;
 242
 243        s = dev->subdevices + 0;
 244        /* dev->read_subdev=s; */
 245        /* analog input subdevice */
 246        s->type = COMEDI_SUBD_AI;
 247        /* we support single-ended (ground) and differential */
 248        s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
 249        s->n_chan = thisboard->ai_chans;
 250        s->maxdata = (1 << thisboard->ai_bits) - 1;
 251        s->range_table = &range_bipolar10;
 252        s->len_chanlist = 16;   /* This is the maximum chanlist length that
 253                                   the board can handle */
 254        s->insn_read = skel_ai_rinsn;
 255/*
 256*       s->subdev_flags |= SDF_CMD_READ;
 257*       s->do_cmd = skel_ai_cmd;
 258*/
 259        s->do_cmdtest = skel_ai_cmdtest;
 260
 261        s = dev->subdevices + 1;
 262        /* analog output subdevice */
 263        s->type = COMEDI_SUBD_AO;
 264        s->subdev_flags = SDF_WRITABLE;
 265        s->n_chan = 1;
 266        s->maxdata = 0xffff;
 267        s->range_table = &range_bipolar5;
 268        s->insn_write = skel_ao_winsn;
 269        s->insn_read = skel_ao_rinsn;
 270
 271        s = dev->subdevices + 2;
 272        /* digital i/o subdevice */
 273        if (thisboard->have_dio) {
 274                s->type = COMEDI_SUBD_DIO;
 275                s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 276                s->n_chan = 16;
 277                s->maxdata = 1;
 278                s->range_table = &range_digital;
 279                s->insn_bits = skel_dio_insn_bits;
 280                s->insn_config = skel_dio_insn_config;
 281        } else {
 282                s->type = COMEDI_SUBD_UNUSED;
 283        }
 284
 285        printk("attached\n");
 286
 287        return 0;
 288}
 289
 290/*
 291 * _detach is called to deconfigure a device.  It should deallocate
 292 * resources.
 293 * This function is also called when _attach() fails, so it should be
 294 * careful not to release resources that were not necessarily
 295 * allocated by _attach().  dev->private and dev->subdevices are
 296 * deallocated automatically by the core.
 297 */
 298static int skel_detach(struct comedi_device *dev)
 299{
 300        printk("comedi%d: skel: remove\n", dev->minor);
 301
 302        return 0;
 303}
 304
 305/*
 306 * "instructions" read/write data in "one-shot" or "software-triggered"
 307 * mode.
 308 */
 309static int skel_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 310                         struct comedi_insn *insn, unsigned int *data)
 311{
 312        int n, i;
 313        unsigned int d;
 314        unsigned int status;
 315
 316        /* a typical programming sequence */
 317
 318        /* write channel to multiplexer */
 319        /* outw(chan,dev->iobase + SKEL_MUX); */
 320
 321        /* don't wait for mux to settle */
 322
 323        /* convert n samples */
 324        for (n = 0; n < insn->n; n++) {
 325                /* trigger conversion */
 326                /* outw(0,dev->iobase + SKEL_CONVERT); */
 327
 328#define TIMEOUT 100
 329                /* wait for conversion to end */
 330                for (i = 0; i < TIMEOUT; i++) {
 331                        status = 1;
 332                        /* status = inb(dev->iobase + SKEL_STATUS); */
 333                        if (status)
 334                                break;
 335                }
 336                if (i == TIMEOUT) {
 337                        /* printk() should be used instead of printk()
 338                         * whenever the code can be called from real-time. */
 339                        printk("timeout\n");
 340                        return -ETIMEDOUT;
 341                }
 342
 343                /* read data */
 344                /* d = inw(dev->iobase + SKEL_AI_DATA); */
 345                d = 0;
 346
 347                /* mangle the data as necessary */
 348                d ^= 1 << (thisboard->ai_bits - 1);
 349
 350                data[n] = d;
 351        }
 352
 353        /* return the number of samples read/written */
 354        return n;
 355}
 356
 357static int skel_ai_cmdtest(struct comedi_device *dev,
 358                           struct comedi_subdevice *s, struct comedi_cmd *cmd)
 359{
 360        int err = 0;
 361        int tmp;
 362
 363        /* cmdtest tests a particular command to see if it is valid.
 364         * Using the cmdtest ioctl, a user can create a valid cmd
 365         * and then have it executes by the cmd ioctl.
 366         *
 367         * cmdtest returns 1,2,3,4 or 0, depending on which tests
 368         * the command passes. */
 369
 370        /* step 1: make sure trigger sources are trivially valid */
 371
 372        tmp = cmd->start_src;
 373        cmd->start_src &= TRIG_NOW;
 374        if (!cmd->start_src || tmp != cmd->start_src)
 375                err++;
 376
 377        tmp = cmd->scan_begin_src;
 378        cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
 379        if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
 380                err++;
 381
 382        tmp = cmd->convert_src;
 383        cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
 384        if (!cmd->convert_src || tmp != cmd->convert_src)
 385                err++;
 386
 387        tmp = cmd->scan_end_src;
 388        cmd->scan_end_src &= TRIG_COUNT;
 389        if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
 390                err++;
 391
 392        tmp = cmd->stop_src;
 393        cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
 394        if (!cmd->stop_src || tmp != cmd->stop_src)
 395                err++;
 396
 397        if (err)
 398                return 1;
 399
 400        /* step 2: make sure trigger sources are unique and mutually compatible */
 401
 402        /* note that mutual compatiblity is not an issue here */
 403        if (cmd->scan_begin_src != TRIG_TIMER &&
 404            cmd->scan_begin_src != TRIG_EXT)
 405                err++;
 406        if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
 407                err++;
 408        if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
 409                err++;
 410
 411        if (err)
 412                return 2;
 413
 414        /* step 3: make sure arguments are trivially compatible */
 415
 416        if (cmd->start_arg != 0) {
 417                cmd->start_arg = 0;
 418                err++;
 419        }
 420#define MAX_SPEED       10000   /* in nanoseconds */
 421#define MIN_SPEED       1000000000      /* in nanoseconds */
 422
 423        if (cmd->scan_begin_src == TRIG_TIMER) {
 424                if (cmd->scan_begin_arg < MAX_SPEED) {
 425                        cmd->scan_begin_arg = MAX_SPEED;
 426                        err++;
 427                }
 428                if (cmd->scan_begin_arg > MIN_SPEED) {
 429                        cmd->scan_begin_arg = MIN_SPEED;
 430                        err++;
 431                }
 432        } else {
 433                /* external trigger */
 434                /* should be level/edge, hi/lo specification here */
 435                /* should specify multiple external triggers */
 436                if (cmd->scan_begin_arg > 9) {
 437                        cmd->scan_begin_arg = 9;
 438                        err++;
 439                }
 440        }
 441        if (cmd->convert_src == TRIG_TIMER) {
 442                if (cmd->convert_arg < MAX_SPEED) {
 443                        cmd->convert_arg = MAX_SPEED;
 444                        err++;
 445                }
 446                if (cmd->convert_arg > MIN_SPEED) {
 447                        cmd->convert_arg = MIN_SPEED;
 448                        err++;
 449                }
 450        } else {
 451                /* external trigger */
 452                /* see above */
 453                if (cmd->convert_arg > 9) {
 454                        cmd->convert_arg = 9;
 455                        err++;
 456                }
 457        }
 458
 459        if (cmd->scan_end_arg != cmd->chanlist_len) {
 460                cmd->scan_end_arg = cmd->chanlist_len;
 461                err++;
 462        }
 463        if (cmd->stop_src == TRIG_COUNT) {
 464                if (cmd->stop_arg > 0x00ffffff) {
 465                        cmd->stop_arg = 0x00ffffff;
 466                        err++;
 467                }
 468        } else {
 469                /* TRIG_NONE */
 470                if (cmd->stop_arg != 0) {
 471                        cmd->stop_arg = 0;
 472                        err++;
 473                }
 474        }
 475
 476        if (err)
 477                return 3;
 478
 479        /* step 4: fix up any arguments */
 480
 481        if (cmd->scan_begin_src == TRIG_TIMER) {
 482                tmp = cmd->scan_begin_arg;
 483                skel_ns_to_timer(&cmd->scan_begin_arg,
 484                                 cmd->flags & TRIG_ROUND_MASK);
 485                if (tmp != cmd->scan_begin_arg)
 486                        err++;
 487        }
 488        if (cmd->convert_src == TRIG_TIMER) {
 489                tmp = cmd->convert_arg;
 490                skel_ns_to_timer(&cmd->convert_arg,
 491                                 cmd->flags & TRIG_ROUND_MASK);
 492                if (tmp != cmd->convert_arg)
 493                        err++;
 494                if (cmd->scan_begin_src == TRIG_TIMER &&
 495                    cmd->scan_begin_arg <
 496                    cmd->convert_arg * cmd->scan_end_arg) {
 497                        cmd->scan_begin_arg =
 498                            cmd->convert_arg * cmd->scan_end_arg;
 499                        err++;
 500                }
 501        }
 502
 503        if (err)
 504                return 4;
 505
 506        return 0;
 507}
 508
 509/* This function doesn't require a particular form, this is just
 510 * what happens to be used in some of the drivers.  It should
 511 * convert ns nanoseconds to a counter value suitable for programming
 512 * the device.  Also, it should adjust ns so that it cooresponds to
 513 * the actual time that the device will use. */
 514static int skel_ns_to_timer(unsigned int *ns, int round)
 515{
 516        /* trivial timer */
 517        /* if your timing is done through two cascaded timers, the
 518         * i8253_cascade_ns_to_timer() function in 8253.h can be
 519         * very helpful.  There are also i8254_load() and i8254_mm_load()
 520         * which can be used to load values into the ubiquitous 8254 counters
 521         */
 522
 523        return *ns;
 524}
 525
 526static int skel_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
 527                         struct comedi_insn *insn, unsigned int *data)
 528{
 529        int i;
 530        int chan = CR_CHAN(insn->chanspec);
 531
 532        printk("skel_ao_winsn\n");
 533        /* Writing a list of values to an AO channel is probably not
 534         * very useful, but that's how the interface is defined. */
 535        for (i = 0; i < insn->n; i++) {
 536                /* a typical programming sequence */
 537                /* outw(data[i],dev->iobase + SKEL_DA0 + chan); */
 538                devpriv->ao_readback[chan] = data[i];
 539        }
 540
 541        /* return the number of samples read/written */
 542        return i;
 543}
 544
 545/* AO subdevices should have a read insn as well as a write insn.
 546 * Usually this means copying a value stored in devpriv. */
 547static int skel_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 548                         struct comedi_insn *insn, unsigned int *data)
 549{
 550        int i;
 551        int chan = CR_CHAN(insn->chanspec);
 552
 553        for (i = 0; i < insn->n; i++)
 554                data[i] = devpriv->ao_readback[chan];
 555
 556        return i;
 557}
 558
 559/* DIO devices are slightly special.  Although it is possible to
 560 * implement the insn_read/insn_write interface, it is much more
 561 * useful to applications if you implement the insn_bits interface.
 562 * This allows packed reading/writing of the DIO channels.  The
 563 * comedi core can convert between insn_bits and insn_read/write */
 564static int skel_dio_insn_bits(struct comedi_device *dev,
 565                              struct comedi_subdevice *s,
 566                              struct comedi_insn *insn, unsigned int *data)
 567{
 568        if (insn->n != 2)
 569                return -EINVAL;
 570
 571        /* The insn data is a mask in data[0] and the new data
 572         * in data[1], each channel cooresponding to a bit. */
 573        if (data[0]) {
 574                s->state &= ~data[0];
 575                s->state |= data[0] & data[1];
 576                /* Write out the new digital output lines */
 577                /* outw(s->state,dev->iobase + SKEL_DIO); */
 578        }
 579
 580        /* on return, data[1] contains the value of the digital
 581         * input and output lines. */
 582        /* data[1]=inw(dev->iobase + SKEL_DIO); */
 583        /* or we could just return the software copy of the output values if
 584         * it was a purely digital output subdevice */
 585        /* data[1]=s->state; */
 586
 587        return 2;
 588}
 589
 590static int skel_dio_insn_config(struct comedi_device *dev,
 591                                struct comedi_subdevice *s,
 592                                struct comedi_insn *insn, unsigned int *data)
 593{
 594        int chan = CR_CHAN(insn->chanspec);
 595
 596        /* The input or output configuration of each digital line is
 597         * configured by a special insn_config instruction.  chanspec
 598         * contains the channel to be changed, and data[0] contains the
 599         * value COMEDI_INPUT or COMEDI_OUTPUT. */
 600        switch (data[0]) {
 601        case INSN_CONFIG_DIO_OUTPUT:
 602                s->io_bits |= 1 << chan;
 603                break;
 604        case INSN_CONFIG_DIO_INPUT:
 605                s->io_bits &= ~(1 << chan);
 606                break;
 607        case INSN_CONFIG_DIO_QUERY:
 608                data[1] =
 609                    (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
 610                return insn->n;
 611                break;
 612        default:
 613                return -EINVAL;
 614                break;
 615        }
 616        /* outw(s->io_bits,dev->iobase + SKEL_DIO_CONFIG); */
 617
 618        return insn->n;
 619}
 620
 621/*
 622 * A convenient macro that defines init_module() and cleanup_module(),
 623 * as necessary.
 624 */
 625COMEDI_INITCLEANUP(driver_skel);
 626/* If you are writing a PCI driver you should use COMEDI_PCI_INITCLEANUP instead.
 627*/
 628/* COMEDI_PCI_INITCLEANUP(driver_skel, skel_pci_table) */
 629