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};
  60
  61struct pcmad_priv_struct {
  62        int differential;
  63        int twos_comp;
  64};
  65
  66#define TIMEOUT 100
  67
  68static int pcmad_ai_insn_read(struct comedi_device *dev,
  69                              struct comedi_subdevice *s,
  70                              struct comedi_insn *insn, unsigned int *data)
  71{
  72        const struct pcmad_board_struct *board = comedi_board(dev);
  73        struct pcmad_priv_struct *devpriv = dev->private;
  74        int i;
  75        int chan;
  76        int n;
  77
  78        chan = CR_CHAN(insn->chanspec);
  79
  80        for (n = 0; n < insn->n; n++) {
  81                outb(chan, dev->iobase + PCMAD_CONVERT);
  82
  83                for (i = 0; i < TIMEOUT; i++) {
  84                        if ((inb(dev->iobase + PCMAD_STATUS) & 0x3) == 0x3)
  85                                break;
  86                }
  87                data[n] = inb(dev->iobase + PCMAD_LSB);
  88                data[n] |= (inb(dev->iobase + PCMAD_MSB) << 8);
  89
  90                if (devpriv->twos_comp)
  91                        data[n] ^= (1 << (board->n_ai_bits - 1));
  92        }
  93
  94        return n;
  95}
  96
  97/*
  98 * options:
  99 * 0    i/o base
 100 * 1    unused
 101 * 2    0=single ended 1=differential
 102 * 3    0=straight binary 1=two's comp
 103 */
 104static int pcmad_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 105{
 106        const struct pcmad_board_struct *board = comedi_board(dev);
 107        struct pcmad_priv_struct *devpriv;
 108        int ret;
 109        struct comedi_subdevice *s;
 110        unsigned long iobase;
 111
 112        iobase = it->options[0];
 113        printk(KERN_INFO "comedi%d: pcmad: 0x%04lx ", dev->minor, iobase);
 114        if (!request_region(iobase, PCMAD_SIZE, "pcmad")) {
 115                printk(KERN_CONT "I/O port conflict\n");
 116                return -EIO;
 117        }
 118        printk(KERN_CONT "\n");
 119        dev->iobase = iobase;
 120
 121        ret = comedi_alloc_subdevices(dev, 1);
 122        if (ret)
 123                return ret;
 124
 125        devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
 126        if (!devpriv)
 127                return -ENOMEM;
 128        dev->private = devpriv;
 129
 130        dev->board_name = board->name;
 131
 132        s = &dev->subdevices[0];
 133        s->type = COMEDI_SUBD_AI;
 134        s->subdev_flags = SDF_READABLE | AREF_GROUND;
 135        s->n_chan = 16;         /* XXX */
 136        s->len_chanlist = 1;
 137        s->insn_read = pcmad_ai_insn_read;
 138        s->maxdata = (1 << board->n_ai_bits) - 1;
 139        s->range_table = &range_unknown;
 140
 141        return 0;
 142}
 143
 144static void pcmad_detach(struct comedi_device *dev)
 145{
 146        if (dev->irq)
 147                free_irq(dev->irq, dev);
 148        if (dev->iobase)
 149                release_region(dev->iobase, PCMAD_SIZE);
 150}
 151
 152static const struct pcmad_board_struct pcmad_boards[] = {
 153        {
 154                .name           = "pcmad12",
 155                .n_ai_bits      = 12,
 156        }, {
 157                .name           = "pcmad16",
 158                .n_ai_bits      = 16,
 159        },
 160};
 161static struct comedi_driver pcmad_driver = {
 162        .driver_name    = "pcmad",
 163        .module         = THIS_MODULE,
 164        .attach         = pcmad_attach,
 165        .detach         = pcmad_detach,
 166        .board_name     = &pcmad_boards[0].name,
 167        .num_names      = ARRAY_SIZE(pcmad_boards),
 168        .offset         = sizeof(pcmad_boards[0]),
 169};
 170module_comedi_driver(pcmad_driver);
 171
 172MODULE_AUTHOR("Comedi http://www.comedi.org");
 173MODULE_DESCRIPTION("Comedi low-level driver");
 174MODULE_LICENSE("GPL");
 175