pcmad.c revision b6ac161364eccce1bea4a23a9de395883e90d7ab
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 92COMEDI_INITCLEANUP(driver_pcmad); 93 94#define TIMEOUT 100 95 96static int pcmad_ai_insn_read(struct comedi_device *dev, 97 struct comedi_subdevice *s, 98 struct comedi_insn *insn, unsigned int *data) 99{ 100 int i; 101 int chan; 102 int n; 103 104 chan = CR_CHAN(insn->chanspec); 105 106 for (n = 0; n < insn->n; n++) { 107 outb(chan, dev->iobase + PCMAD_CONVERT); 108 109 for (i = 0; i < TIMEOUT; i++) { 110 if ((inb(dev->iobase + PCMAD_STATUS) & 0x3) == 0x3) 111 break; 112 } 113 data[n] = inb(dev->iobase + PCMAD_LSB); 114 data[n] |= (inb(dev->iobase + PCMAD_MSB) << 8); 115 116 if (devpriv->twos_comp) { 117 data[n] ^= (1 << (this_board->n_ai_bits - 1)); 118 } 119 } 120 121 return n; 122} 123 124/* 125 * options: 126 * 0 i/o base 127 * 1 unused 128 * 2 0=single ended 1=differential 129 * 3 0=straight binary 1=two's comp 130 */ 131static int pcmad_attach(struct comedi_device *dev, struct comedi_devconfig *it) 132{ 133 int ret; 134 struct comedi_subdevice *s; 135 unsigned long iobase; 136 137 iobase = it->options[0]; 138 printk("comedi%d: pcmad: 0x%04lx ", dev->minor, iobase); 139 if (!request_region(iobase, PCMAD_SIZE, "pcmad")) { 140 printk("I/O port conflict\n"); 141 return -EIO; 142 } 143 dev->iobase = iobase; 144 145 ret = alloc_subdevices(dev, 1); 146 if (ret < 0) 147 return ret; 148 149 ret = alloc_private(dev, sizeof(struct pcmad_priv_struct)); 150 if (ret < 0) 151 return ret; 152 153 dev->board_name = this_board->name; 154 155 s = dev->subdevices + 0; 156 s->type = COMEDI_SUBD_AI; 157 s->subdev_flags = SDF_READABLE | AREF_GROUND; 158 s->n_chan = 16; /* XXX */ 159 s->len_chanlist = 1; 160 s->insn_read = pcmad_ai_insn_read; 161 s->maxdata = (1 << this_board->n_ai_bits) - 1; 162 s->range_table = &range_unknown; 163 164 return 0; 165} 166 167static int pcmad_detach(struct comedi_device *dev) 168{ 169 printk("comedi%d: pcmad: remove\n", dev->minor); 170 171 if (dev->irq) { 172 free_irq(dev->irq, dev); 173 } 174 if (dev->iobase) 175 release_region(dev->iobase, PCMAD_SIZE); 176 177 return 0; 178} 179