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