linux/drivers/staging/comedi/drivers/pcmad.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/pcmad.c
   3    Hardware driver for Winsystems PCM-A/D12 and PCM-A/D16
   4
   5    COMEDI - Linux Control and Measurement Device Interface
   6    Copyright (C) 2000,2001 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: pcmad
  25Description: Winsystems PCM-A/D12, PCM-A/D16
  26Author: ds
  27Devices: [Winsystems] PCM-A/D12 (pcmad12), PCM-A/D16 (pcmad16)
  28Status: untested
  29
  30This driver was written on a bet that I couldn't write a driver
  31in less than 2 hours.  I won the bet, but never got paid.  =(
  32
  33Configuration options:
  34  [0] - I/O port base
  35  [1] - unused
  36  [2] - Analog input reference
  37        0 = single ended
  38        1 = differential
  39  [3] - Analog input encoding (must match jumpers)
  40        0 = straight binary
  41        1 = two's complement
  42*/
  43
  44#include <linux/interrupt.h>
  45#include "../comedidev.h"
  46
  47#include <linux/ioport.h>
  48
  49#define PCMAD_SIZE              4
  50
  51#define PCMAD_STATUS            0
  52#define PCMAD_LSB               1
  53#define PCMAD_MSB               2
  54#define PCMAD_CONVERT           1
  55
  56struct pcmad_board_struct {
  57        const char *name;
  58        int n_ai_bits;
  59};
  60static const struct pcmad_board_struct pcmad_boards[] = {
  61        {
  62         .name = "pcmad12",
  63         .n_ai_bits = 12,
  64         },
  65        {
  66         .name = "pcmad16",
  67         .n_ai_bits = 16,
  68         },
  69};
  70
  71#define this_board ((const struct pcmad_board_struct *)(dev->board_ptr))
  72#define n_pcmad_boards ARRAY_SIZE(pcmad_boards)
  73
  74struct pcmad_priv_struct {
  75        int differential;
  76        int twos_comp;
  77};
  78#define devpriv ((struct pcmad_priv_struct *)dev->private)
  79
  80static int pcmad_attach(struct comedi_device *dev, struct comedi_devconfig *it);
  81static int pcmad_detach(struct comedi_device *dev);
  82static struct comedi_driver driver_pcmad = {
  83        .driver_name = "pcmad",
  84        .module = THIS_MODULE,
  85        .attach = pcmad_attach,
  86        .detach = pcmad_detach,
  87        .board_name = &pcmad_boards[0].name,
  88        .num_names = n_pcmad_boards,
  89        .offset = sizeof(pcmad_boards[0]),
  90};
  91
  92static int __init driver_pcmad_init_module(void)
  93{
  94        return comedi_driver_register(&driver_pcmad);
  95}
  96
  97static void __exit driver_pcmad_cleanup_module(void)
  98{
  99        comedi_driver_unregister(&driver_pcmad);
 100}
 101
 102module_init(driver_pcmad_init_module);
 103module_exit(driver_pcmad_cleanup_module);
 104
 105#define TIMEOUT 100
 106
 107static int pcmad_ai_insn_read(struct comedi_device *dev,
 108                              struct comedi_subdevice *s,
 109                              struct comedi_insn *insn, unsigned int *data)
 110{
 111        int i;
 112        int chan;
 113        int n;
 114
 115        chan = CR_CHAN(insn->chanspec);
 116
 117        for (n = 0; n < insn->n; n++) {
 118                outb(chan, dev->iobase + PCMAD_CONVERT);
 119
 120                for (i = 0; i < TIMEOUT; i++) {
 121                        if ((inb(dev->iobase + PCMAD_STATUS) & 0x3) == 0x3)
 122                                break;
 123                }
 124                data[n] = inb(dev->iobase + PCMAD_LSB);
 125                data[n] |= (inb(dev->iobase + PCMAD_MSB) << 8);
 126
 127                if (devpriv->twos_comp)
 128                        data[n] ^= (1 << (this_board->n_ai_bits - 1));
 129        }
 130
 131        return n;
 132}
 133
 134/*
 135 * options:
 136 * 0    i/o base
 137 * 1    unused
 138 * 2    0=single ended 1=differential
 139 * 3    0=straight binary 1=two's comp
 140 */
 141static int pcmad_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 142{
 143        int ret;
 144        struct comedi_subdevice *s;
 145        unsigned long iobase;
 146
 147        iobase = it->options[0];
 148        printk(KERN_INFO "comedi%d: pcmad: 0x%04lx ", dev->minor, iobase);
 149        if (!request_region(iobase, PCMAD_SIZE, "pcmad")) {
 150                printk(KERN_CONT "I/O port conflict\n");
 151                return -EIO;
 152        }
 153        printk(KERN_CONT "\n");
 154        dev->iobase = iobase;
 155
 156        ret = alloc_subdevices(dev, 1);
 157        if (ret < 0)
 158                return ret;
 159
 160        ret = alloc_private(dev, sizeof(struct pcmad_priv_struct));
 161        if (ret < 0)
 162                return ret;
 163
 164        dev->board_name = this_board->name;
 165
 166        s = dev->subdevices + 0;
 167        s->type = COMEDI_SUBD_AI;
 168        s->subdev_flags = SDF_READABLE | AREF_GROUND;
 169        s->n_chan = 16;         /* XXX */
 170        s->len_chanlist = 1;
 171        s->insn_read = pcmad_ai_insn_read;
 172        s->maxdata = (1 << this_board->n_ai_bits) - 1;
 173        s->range_table = &range_unknown;
 174
 175        return 0;
 176}
 177
 178static int pcmad_detach(struct comedi_device *dev)
 179{
 180        printk(KERN_INFO "comedi%d: pcmad: remove\n", dev->minor);
 181
 182        if (dev->irq)
 183                free_irq(dev->irq, dev);
 184
 185        if (dev->iobase)
 186                release_region(dev->iobase, PCMAD_SIZE);
 187
 188        return 0;
 189}
 190
 191MODULE_AUTHOR("Comedi http://www.comedi.org");
 192MODULE_DESCRIPTION("Comedi low-level driver");
 193MODULE_LICENSE("GPL");
 194