/drivers/staging/comedi/drivers/adq12b.c
C | 405 lines | 200 code | 55 blank | 150 comment | 23 complexity | fe840a98d375d35cf8c15954a4c00265 MD5 | raw file
- /*
- comedi/drivers/adq12b.c
- driver for MicroAxial ADQ12-B data acquisition and control card
- COMEDI - Linux Control and Measurement Device Interface
- Copyright (C) 2000 David A. Schleef <ds@schleef.org>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- /*
- Driver: adq12b
- Description: driver for MicroAxial ADQ12-B data acquisition and control card
- Devices: [MicroAxial] ADQ12-B (adq12b)
- Author: jeremy theler <thelerg@ib.cnea.gov.ar>
- Updated: Thu, 21 Feb 2008 02:56:27 -0300
- Status: works
- Driver for the acquisition card ADQ12-B (without any add-on).
- - Analog input is subdevice 0 (16 channels single-ended or 8 differential)
- - Digital input is subdevice 1 (5 channels)
- - Digital output is subdevice 1 (8 channels)
- - The PACER is not supported in this version
- If you do not specify any options, they will default to
- # comedi_config /dev/comedi0 adq12b 0x300,0,0
- option 1: I/O base address. The following table is provided as a help
- of the hardware jumpers.
- address jumper JADR
- 0x300 1 (factory default)
- 0x320 2
- 0x340 3
- 0x360 4
- 0x380 5
- 0x3A0 6
- option 2: unipolar/bipolar ADC selection: 0 -> bipolar, 1 -> unipolar
- selection comedi_config option JUB
- bipolar 0 2-3 (factory default)
- unipolar 1 1-2
- option 3: single-ended/differential AI selection: 0 -> SE, 1 -> differential
- selection comedi_config option JCHA JCHB
- single-ended 0 1-2 1-2 (factory default)
- differential 1 2-3 2-3
- written by jeremy theler <thelerg@ib.cnea.gov.ar>
- instituto balseiro
- comision nacional de energia atomica
- universidad nacional de cuyo
- argentina
- 21-feb-2008
- + changed supported devices string (missused the [] and ())
- 13-oct-2007
- + first try
- */
- #include "../comedidev.h"
- /* address scheme (page 2.17 of the manual) */
- #define ADQ12B_SIZE 16
- #define ADQ12B_CTREG 0x00
- #define ADQ12B_STINR 0x00
- #define ADQ12B_OUTBR 0x04
- #define ADQ12B_ADLOW 0x08
- #define ADQ12B_ADHIG 0x09
- #define ADQ12B_CONT0 0x0c
- #define ADQ12B_CONT1 0x0d
- #define ADQ12B_CONT2 0x0e
- #define ADQ12B_COWORD 0x0f
- /* mask of the bit at STINR to check end of conversion */
- #define ADQ12B_EOC 0x20
- #define TIMEOUT 20
- /* available ranges through the PGA gains */
- static const struct comedi_lrange range_adq12b_ai_bipolar = { 4, {
- BIP_RANGE(5),
- BIP_RANGE(2),
- BIP_RANGE(1),
- BIP_RANGE(0.5)
- }
- };
- static const struct comedi_lrange range_adq12b_ai_unipolar = { 4, {
- UNI_RANGE(5),
- UNI_RANGE(2),
- UNI_RANGE(1),
- UNI_RANGE
- (0.5)
- }
- };
- struct adq12b_board {
- const char *name;
- int ai_se_chans;
- int ai_diff_chans;
- int ai_bits;
- int di_chans;
- int do_chans;
- };
- static const struct adq12b_board adq12b_boards[] = {
- {
- .name = "adq12b",
- .ai_se_chans = 16,
- .ai_diff_chans = 8,
- .ai_bits = 12,
- .di_chans = 5,
- .do_chans = 8}
- /* potentially, more adq-based deviced will be added */
- /*,
- .name = "adq12b",
- .ai_chans = 16, // this is just for reference, hardcoded again later
- .ai_bits = 12,
- .di_chans = 8,
- .do_chans = 5
- }*/
- };
- #define thisboard ((const struct adq12b_board *)dev->board_ptr)
- struct adq12b_private {
- int unipolar; /* option 2 of comedi_config (1 is iobase) */
- int differential; /* option 3 of comedi_config */
- int last_channel;
- int last_range;
- unsigned int digital_state;
- };
- #define devpriv ((struct adq12b_private *)dev->private)
- /*
- * The struct comedi_driver structure tells the Comedi core module
- * which functions to call to configure/deconfigure (attach/detach)
- * the board, and also about the kernel module that contains
- * the device code.
- */
- static int adq12b_attach(struct comedi_device *dev,
- struct comedi_devconfig *it);
- static int adq12b_detach(struct comedi_device *dev);
- static struct comedi_driver driver_adq12b = {
- .driver_name = "adq12b",
- .module = THIS_MODULE,
- .attach = adq12b_attach,
- .detach = adq12b_detach,
- .board_name = &adq12b_boards[0].name,
- .offset = sizeof(struct adq12b_board),
- .num_names = ARRAY_SIZE(adq12b_boards),
- };
- static int adq12b_ai_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data);
- static int adq12b_di_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
- static int adq12b_do_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
- /*
- * Attach is called by the Comedi core to configure the driver
- * for a particular board. If you specified a board_name array
- * in the driver structure, dev->board_ptr contains that
- * address.
- */
- static int adq12b_attach(struct comedi_device *dev, struct comedi_devconfig *it)
- {
- struct comedi_subdevice *s;
- unsigned long iobase;
- int unipolar, differential;
- iobase = it->options[0];
- unipolar = it->options[1];
- differential = it->options[2];
- printk(KERN_INFO "comedi%d: adq12b called with options base=0x%03lx, "
- "%s and %s\n", dev->minor, iobase,
- (unipolar == 1) ? "unipolar" : "bipolar",
- (differential == 1) ? "differential" : "single-ended");
- /* if no address was specified, try the default 0x300 */
- if (iobase == 0) {
- printk(KERN_WARNING "comedi%d: adq12b warning: I/O base "
- "address not specified. Trying the default 0x300.\n",
- dev->minor);
- iobase = 0x300;
- }
- printk("comedi%d: adq12b: 0x%04lx ", dev->minor, iobase);
- if (!request_region(iobase, ADQ12B_SIZE, "adq12b")) {
- printk("I/O port conflict\n");
- return -EIO;
- }
- dev->iobase = iobase;
- /*
- * Initialize dev->board_name. Note that we can use the "thisboard"
- * macro now, since we just initialized it in the last line.
- */
- dev->board_name = thisboard->name;
- /*
- * Allocate the private structure area. alloc_private() is a
- * convenient macro defined in comedidev.h.
- */
- if (alloc_private(dev, sizeof(struct adq12b_private)) < 0)
- return -ENOMEM;
- /* fill in devpriv structure */
- devpriv->unipolar = unipolar;
- devpriv->differential = differential;
- devpriv->digital_state = 0;
- /* initialize channel and range to -1 so we make sure we always write
- at least once to the CTREG in the instruction */
- devpriv->last_channel = -1;
- devpriv->last_range = -1;
- /*
- * Allocate the subdevice structures. alloc_subdevice() is a
- * convenient macro defined in comedidev.h.
- */
- if (alloc_subdevices(dev, 3) < 0)
- return -ENOMEM;
- s = dev->subdevices + 0;
- /* analog input subdevice */
- s->type = COMEDI_SUBD_AI;
- if (differential) {
- s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
- s->n_chan = thisboard->ai_diff_chans;
- } else {
- s->subdev_flags = SDF_READABLE | SDF_GROUND;
- s->n_chan = thisboard->ai_se_chans;
- }
- if (unipolar)
- s->range_table = &range_adq12b_ai_unipolar;
- else
- s->range_table = &range_adq12b_ai_bipolar;
- s->maxdata = (1 << thisboard->ai_bits) - 1;
- s->len_chanlist = 4; /* This is the maximum chanlist length that
- the board can handle */
- s->insn_read = adq12b_ai_rinsn;
- s = dev->subdevices + 1;
- /* digital input subdevice */
- s->type = COMEDI_SUBD_DI;
- s->subdev_flags = SDF_READABLE;
- s->n_chan = thisboard->di_chans;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = adq12b_di_insn_bits;
- s = dev->subdevices + 2;
- /* digital output subdevice */
- s->type = COMEDI_SUBD_DO;
- s->subdev_flags = SDF_WRITABLE;
- s->n_chan = thisboard->do_chans;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = adq12b_do_insn_bits;
- printk(KERN_INFO "attached\n");
- return 0;
- }
- /*
- * _detach is called to deconfigure a device. It should deallocate
- * resources.
- * This function is also called when _attach() fails, so it should be
- * careful not to release resources that were not necessarily
- * allocated by _attach(). dev->private and dev->subdevices are
- * deallocated automatically by the core.
- */
- static int adq12b_detach(struct comedi_device *dev)
- {
- if (dev->iobase)
- release_region(dev->iobase, ADQ12B_SIZE);
- kfree(devpriv);
- printk(KERN_INFO "comedi%d: adq12b: removed\n", dev->minor);
- return 0;
- }
- /*
- * "instructions" read/write data in "one-shot" or "software-triggered"
- * mode.
- */
- static int adq12b_ai_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data)
- {
- int n, i;
- int range, channel;
- unsigned char hi, lo, status;
- /* change channel and range only if it is different from the previous */
- range = CR_RANGE(insn->chanspec);
- channel = CR_CHAN(insn->chanspec);
- if (channel != devpriv->last_channel || range != devpriv->last_range) {
- outb((range << 4) | channel, dev->iobase + ADQ12B_CTREG);
- udelay(50); /* wait for the mux to settle */
- }
- /* trigger conversion */
- status = inb(dev->iobase + ADQ12B_ADLOW);
- /* convert n samples */
- for (n = 0; n < insn->n; n++) {
- /* wait for end of convertion */
- i = 0;
- do {
- /* udelay(1); */
- status = inb(dev->iobase + ADQ12B_STINR);
- status = status & ADQ12B_EOC;
- } while (status == 0 && ++i < TIMEOUT);
- /* } while (++i < 10); */
- /* read data */
- hi = inb(dev->iobase + ADQ12B_ADHIG);
- lo = inb(dev->iobase + ADQ12B_ADLOW);
- /* printk("debug: chan=%d range=%d status=%d hi=%d lo=%d\n",
- channel, range, status, hi, lo); */
- data[n] = (hi << 8) | lo;
- }
- /* return the number of samples read/written */
- return n;
- }
- static int adq12b_di_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
- {
- /* only bits 0-4 have information about digital inputs */
- data[1] = (inb(dev->iobase + ADQ12B_STINR) & (0x1f));
- return 2;
- }
- static int adq12b_do_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
- {
- int channel;
- for (channel = 0; channel < 8; channel++)
- if (((data[0] >> channel) & 0x01) != 0)
- outb((((data[1] >> channel) & 0x01) << 3) | channel,
- dev->iobase + ADQ12B_OUTBR);
- /* store information to retrieve when asked for reading */
- if (data[0]) {
- devpriv->digital_state &= ~data[0];
- devpriv->digital_state |= (data[0] & data[1]);
- }
- data[1] = devpriv->digital_state;
- return 2;
- }
- /*
- * A convenient macro that defines init_module() and cleanup_module(),
- * as necessary.
- */
- COMEDI_INITCLEANUP(driver_adq12b);