linux/drivers/staging/comedi/drivers/pcmda12.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/pcmda12.c
   3    Driver for Winsystems PC-104 based PCM-D/A-12 8-channel AO board.
   4
   5    COMEDI - Linux Control and Measurement Device Interface
   6    Copyright (C) 2006 Calin A. Culianu <calin@ajvar.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/*
  23Driver: pcmda12
  24Description: A driver for the Winsystems PCM-D/A-12
  25Devices: [Winsystems] PCM-D/A-12 (pcmda12)
  26Author: Calin Culianu <calin@ajvar.org>
  27Updated: Fri, 13 Jan 2006 12:01:01 -0500
  28Status: works
  29
  30A driver for the relatively straightforward-to-program PCM-D/A-12.
  31This board doesn't support commands, and the only way to set its
  32analog output range is to jumper the board.  As such,
  33comedi_data_write() ignores the range value specified.
  34
  35The board uses 16 consecutive I/O addresses starting at the I/O port
  36base address.  Each address corresponds to the LSB then MSB of a
  37particular channel from 0-7.
  38
  39Note that the board is not ISA-PNP capable and thus
  40needs the I/O port comedi_config parameter.
  41
  42Note that passing a nonzero value as the second config option will
  43enable "simultaneous xfer" mode for this board, in which AO writes
  44will not take effect until a subsequent read of any AO channel.  This
  45is so that one can speed up programming by preloading all AO registers
  46with values before simultaneously setting them to take effect with one
  47read command.
  48
  49Configuration Options:
  50  [0] - I/O port base address
  51  [1] - Do Simultaneous Xfer (see description)
  52*/
  53
  54#include "../comedidev.h"
  55
  56#include <linux/pci.h>          /* for PCI devices */
  57
  58#define SDEV_NO ((int)(s - dev->subdevices))
  59#define CHANS 8
  60#define IOSIZE 16
  61#define LSB(x) ((unsigned char)((x) & 0xff))
  62#define MSB(x) ((unsigned char)((((unsigned short)(x))>>8) & 0xff))
  63#define LSB_PORT(chan) (dev->iobase + (chan)*2)
  64#define MSB_PORT(chan) (LSB_PORT(chan)+1)
  65#define BITS 12
  66
  67/*
  68 * Bords
  69 */
  70struct pcmda12_board {
  71        const char *name;
  72};
  73
  74/* note these have no effect and are merely here for reference..
  75   these are configured by jumpering the board! */
  76static const struct comedi_lrange pcmda12_ranges = {
  77        3,
  78        {
  79         UNI_RANGE(5), UNI_RANGE(10), BIP_RANGE(5)
  80         }
  81};
  82
  83static const struct pcmda12_board pcmda12_boards[] = {
  84        {
  85         .name = "pcmda12",
  86         },
  87};
  88
  89/*
  90 * Useful for shorthand access to the particular board structure
  91 */
  92#define thisboard ((const struct pcmda12_board *)dev->board_ptr)
  93
  94struct pcmda12_private {
  95
  96        unsigned int ao_readback[CHANS];
  97        int simultaneous_xfer_mode;
  98};
  99
 100#define devpriv ((struct pcmda12_private *)(dev->private))
 101
 102/*
 103 * The struct comedi_driver structure tells the Comedi core module
 104 * which functions to call to configure/deconfigure (attach/detach)
 105 * the board, and also about the kernel module that contains
 106 * the device code.
 107 */
 108static int pcmda12_attach(struct comedi_device *dev,
 109                          struct comedi_devconfig *it);
 110static int pcmda12_detach(struct comedi_device *dev);
 111
 112static void zero_chans(struct comedi_device *dev);
 113
 114static struct comedi_driver driver = {
 115        .driver_name = "pcmda12",
 116        .module = THIS_MODULE,
 117        .attach = pcmda12_attach,
 118        .detach = pcmda12_detach,
 119/* It is not necessary to implement the following members if you are
 120 * writing a driver for a ISA PnP or PCI card */
 121        /* Most drivers will support multiple types of boards by
 122         * having an array of board structures.  These were defined
 123         * in pcmda12_boards[] above.  Note that the element 'name'
 124         * was first in the structure -- Comedi uses this fact to
 125         * extract the name of the board without knowing any details
 126         * about the structure except for its length.
 127         * When a device is attached (by comedi_config), the name
 128         * of the device is given to Comedi, and Comedi tries to
 129         * match it by going through the list of board names.  If
 130         * there is a match, the address of the pointer is put
 131         * into dev->board_ptr and driver->attach() is called.
 132         *
 133         * Note that these are not necessary if you can determine
 134         * the type of board in software.  ISA PnP, PCI, and PCMCIA
 135         * devices are such boards.
 136         */
 137        .board_name = &pcmda12_boards[0].name,
 138        .offset = sizeof(struct pcmda12_board),
 139        .num_names = ARRAY_SIZE(pcmda12_boards),
 140};
 141
 142static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
 143                    struct comedi_insn *insn, unsigned int *data);
 144static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 145                    struct comedi_insn *insn, unsigned int *data);
 146
 147/*
 148 * Attach is called by the Comedi core to configure the driver
 149 * for a particular board.  If you specified a board_name array
 150 * in the driver structure, dev->board_ptr contains that
 151 * address.
 152 */
 153static int pcmda12_attach(struct comedi_device *dev,
 154                          struct comedi_devconfig *it)
 155{
 156        struct comedi_subdevice *s;
 157        unsigned long iobase;
 158
 159        iobase = it->options[0];
 160        printk(KERN_INFO
 161               "comedi%d: %s: io: %lx %s ", dev->minor, driver.driver_name,
 162               iobase, it->options[1] ? "simultaneous xfer mode enabled" : "");
 163
 164        if (!request_region(iobase, IOSIZE, driver.driver_name)) {
 165                printk("I/O port conflict\n");
 166                return -EIO;
 167        }
 168        dev->iobase = iobase;
 169
 170/*
 171 * Initialize dev->board_name.  Note that we can use the "thisboard"
 172 * macro now, since we just initialized it in the last line.
 173 */
 174        dev->board_name = thisboard->name;
 175
 176/*
 177 * Allocate the private structure area.  alloc_private() is a
 178 * convenient macro defined in comedidev.h.
 179 */
 180        if (alloc_private(dev, sizeof(struct pcmda12_private)) < 0) {
 181                printk(KERN_ERR "cannot allocate private data structure\n");
 182                return -ENOMEM;
 183        }
 184
 185        devpriv->simultaneous_xfer_mode = it->options[1];
 186
 187        /*
 188         * Allocate the subdevice structures.  alloc_subdevice() is a
 189         * convenient macro defined in comedidev.h.
 190         *
 191         * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the
 192         * 96-channel version of the board.
 193         */
 194        if (alloc_subdevices(dev, 1) < 0) {
 195                printk(KERN_ERR "cannot allocate subdevice data structures\n");
 196                return -ENOMEM;
 197        }
 198
 199        s = dev->subdevices;
 200        s->private = NULL;
 201        s->maxdata = (0x1 << BITS) - 1;
 202        s->range_table = &pcmda12_ranges;
 203        s->type = COMEDI_SUBD_AO;
 204        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 205        s->n_chan = CHANS;
 206        s->insn_write = &ao_winsn;
 207        s->insn_read = &ao_rinsn;
 208
 209        zero_chans(dev);        /* clear out all the registers, basically */
 210
 211        printk(KERN_INFO "attached\n");
 212
 213        return 1;
 214}
 215
 216/*
 217 * _detach is called to deconfigure a device.  It should deallocate
 218 * resources.
 219 * This function is also called when _attach() fails, so it should be
 220 * careful not to release resources that were not necessarily
 221 * allocated by _attach().  dev->private and dev->subdevices are
 222 * deallocated automatically by the core.
 223 */
 224static int pcmda12_detach(struct comedi_device *dev)
 225{
 226        printk(KERN_INFO
 227               "comedi%d: %s: remove\n", dev->minor, driver.driver_name);
 228        if (dev->iobase)
 229                release_region(dev->iobase, IOSIZE);
 230        return 0;
 231}
 232
 233static void zero_chans(struct comedi_device *dev)
 234{                               /* sets up an
 235                                   ASIC chip to defaults */
 236        int i;
 237        for (i = 0; i < CHANS; ++i) {
 238/*      /\* do this as one instruction?? *\/ */
 239/*      outw(0, LSB_PORT(chan)); */
 240                outb(0, LSB_PORT(i));
 241                outb(0, MSB_PORT(i));
 242        }
 243        inb(LSB_PORT(0));       /* update chans. */
 244}
 245
 246static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
 247                    struct comedi_insn *insn, unsigned int *data)
 248{
 249        int i;
 250        int chan = CR_CHAN(insn->chanspec);
 251
 252        /* Writing a list of values to an AO channel is probably not
 253         * very useful, but that's how the interface is defined. */
 254        for (i = 0; i < insn->n; ++i) {
 255
 256/*      /\* do this as one instruction?? *\/ */
 257/*      outw(data[i], LSB_PORT(chan)); */
 258
 259                /* Need to do this as two instructions due to 8-bit bus?? */
 260                /*  first, load the low byte */
 261                outb(LSB(data[i]), LSB_PORT(chan));
 262                /*  next, write the high byte */
 263                outb(MSB(data[i]), MSB_PORT(chan));
 264
 265                /* save shadow register */
 266                devpriv->ao_readback[chan] = data[i];
 267
 268                if (!devpriv->simultaneous_xfer_mode)
 269                        inb(LSB_PORT(chan));
 270        }
 271
 272        /* return the number of samples written */
 273        return i;
 274}
 275
 276/* AO subdevices should have a read insn as well as a write insn.
 277
 278   Usually this means copying a value stored in devpriv->ao_readback.
 279   However, since this driver supports simultaneous xfer then sometimes
 280   this function actually accomplishes work.
 281
 282   Simultaneaous xfer mode is accomplished by loading ALL the values
 283   you want for AO in all the channels, then READing off one of the AO
 284   registers to initiate the instantaneous simultaneous update of all
 285   DAC outputs, which makes all AO channels update simultaneously.
 286   This is useful for some control applications, I would imagine.
 287*/
 288static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 289                    struct comedi_insn *insn, unsigned int *data)
 290{
 291        int i;
 292        int chan = CR_CHAN(insn->chanspec);
 293
 294        for (i = 0; i < insn->n; i++) {
 295                if (devpriv->simultaneous_xfer_mode)
 296                        inb(LSB_PORT(chan));
 297                /* read back shadow register */
 298                data[i] = devpriv->ao_readback[chan];
 299        }
 300
 301        return i;
 302}
 303
 304/*
 305 * A convenient macro that defines init_module() and cleanup_module(),
 306 * as necessary.
 307 */
 308static int __init driver_init_module(void)
 309{
 310        return comedi_driver_register(&driver);
 311}
 312
 313static void __exit driver_cleanup_module(void)
 314{
 315        comedi_driver_unregister(&driver);
 316}
 317
 318module_init(driver_init_module);
 319module_exit(driver_cleanup_module);
 320
 321MODULE_AUTHOR("Comedi http://www.comedi.org");
 322MODULE_DESCRIPTION("Comedi low-level driver");
 323MODULE_LICENSE("GPL");
 324
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.