|
@@ -175,19 +175,6 @@ struct dmm32at_board {
|
|
|
int have_dio;
|
|
|
int dio_chans;
|
|
|
};
|
|
|
-static const struct dmm32at_board dmm32at_boards[] = {
|
|
|
- {
|
|
|
- .name = "dmm32at",
|
|
|
- .ai_chans = 32,
|
|
|
- .ai_bits = 16,
|
|
|
- .ai_ranges = &dmm32at_airanges,
|
|
|
- .ao_chans = 4,
|
|
|
- .ao_bits = 12,
|
|
|
- .ao_ranges = &dmm32at_aoranges,
|
|
|
- .have_dio = 1,
|
|
|
- .dio_chans = 24,
|
|
|
- },
|
|
|
-};
|
|
|
|
|
|
/* this structure is for data unique to this hardware driver. If
|
|
|
* several hardware drivers keep similar information in this structure,
|
|
@@ -212,197 +199,9 @@ struct dmm32at_private {
|
|
|
#define devpriv ((struct dmm32at_private *)dev->private)
|
|
|
|
|
|
/* prototypes for driver functions below */
|
|
|
-static int dmm32at_ai_rinsn(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- struct comedi_insn *insn, unsigned int *data);
|
|
|
-static int dmm32at_ao_winsn(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- struct comedi_insn *insn, unsigned int *data);
|
|
|
-static int dmm32at_ao_rinsn(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- struct comedi_insn *insn, unsigned int *data);
|
|
|
-static int dmm32at_dio_insn_bits(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- struct comedi_insn *insn, unsigned int *data);
|
|
|
-static int dmm32at_dio_insn_config(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- struct comedi_insn *insn,
|
|
|
- unsigned int *data);
|
|
|
-static int dmm32at_ai_cmdtest(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- struct comedi_cmd *cmd);
|
|
|
-static int dmm32at_ai_cmd(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s);
|
|
|
-static int dmm32at_ai_cancel(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s);
|
|
|
static int dmm32at_ns_to_timer(unsigned int *ns, int round);
|
|
|
-static irqreturn_t dmm32at_isr(int irq, void *d);
|
|
|
void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec);
|
|
|
|
|
|
-/*
|
|
|
- * 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 dmm32at_attach(struct comedi_device *dev,
|
|
|
- struct comedi_devconfig *it)
|
|
|
-{
|
|
|
- const struct dmm32at_board *board = comedi_board(dev);
|
|
|
- int ret;
|
|
|
- struct comedi_subdevice *s;
|
|
|
- unsigned char aihi, ailo, fifostat, aistat, intstat, airback;
|
|
|
- unsigned long iobase;
|
|
|
- unsigned int irq;
|
|
|
-
|
|
|
- iobase = it->options[0];
|
|
|
- irq = it->options[1];
|
|
|
-
|
|
|
- printk(KERN_INFO "comedi%d: dmm32at: attaching\n", dev->minor);
|
|
|
- printk(KERN_DEBUG "dmm32at: probing at address 0x%04lx, irq %u\n",
|
|
|
- iobase, irq);
|
|
|
-
|
|
|
- /* register address space */
|
|
|
- if (!request_region(iobase, DMM32AT_MEMSIZE, board->name)) {
|
|
|
- printk(KERN_ERR "comedi%d: dmm32at: I/O port conflict\n",
|
|
|
- dev->minor);
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
- dev->iobase = iobase;
|
|
|
-
|
|
|
- /* the following just makes sure the board is there and gets
|
|
|
- it to a known state */
|
|
|
-
|
|
|
- /* reset the board */
|
|
|
- dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_RESET);
|
|
|
-
|
|
|
- /* allow a millisecond to reset */
|
|
|
- udelay(1000);
|
|
|
-
|
|
|
- /* zero scan and fifo control */
|
|
|
- dmm_outb(dev, DMM32AT_FIFOCNTRL, 0x0);
|
|
|
-
|
|
|
- /* zero interrupt and clock control */
|
|
|
- dmm_outb(dev, DMM32AT_INTCLOCK, 0x0);
|
|
|
-
|
|
|
- /* write a test channel range, the high 3 bits should drop */
|
|
|
- dmm_outb(dev, DMM32AT_AILOW, 0x80);
|
|
|
- dmm_outb(dev, DMM32AT_AIHIGH, 0xff);
|
|
|
-
|
|
|
- /* set the range at 10v unipolar */
|
|
|
- dmm_outb(dev, DMM32AT_AICONF, DMM32AT_RANGE_U10);
|
|
|
-
|
|
|
- /* should take 10 us to settle, here's a hundred */
|
|
|
- udelay(100);
|
|
|
-
|
|
|
- /* read back the values */
|
|
|
- ailo = dmm_inb(dev, DMM32AT_AILOW);
|
|
|
- aihi = dmm_inb(dev, DMM32AT_AIHIGH);
|
|
|
- fifostat = dmm_inb(dev, DMM32AT_FIFOSTAT);
|
|
|
- aistat = dmm_inb(dev, DMM32AT_AISTAT);
|
|
|
- intstat = dmm_inb(dev, DMM32AT_INTCLOCK);
|
|
|
- airback = dmm_inb(dev, DMM32AT_AIRBACK);
|
|
|
-
|
|
|
- printk(KERN_DEBUG "dmm32at: lo=0x%02x hi=0x%02x fifostat=0x%02x\n",
|
|
|
- ailo, aihi, fifostat);
|
|
|
- printk(KERN_DEBUG
|
|
|
- "dmm32at: aistat=0x%02x intstat=0x%02x airback=0x%02x\n",
|
|
|
- aistat, intstat, airback);
|
|
|
-
|
|
|
- if ((ailo != 0x00) || (aihi != 0x1f) || (fifostat != 0x80) ||
|
|
|
- (aistat != 0x60 || (intstat != 0x00) || airback != 0x0c)) {
|
|
|
- printk(KERN_ERR "dmmat32: board detection failed\n");
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
-
|
|
|
- /* board is there, register interrupt */
|
|
|
- if (irq) {
|
|
|
- ret = request_irq(irq, dmm32at_isr, 0, board->name, dev);
|
|
|
- if (ret < 0) {
|
|
|
- printk(KERN_ERR "dmm32at: irq conflict\n");
|
|
|
- return ret;
|
|
|
- }
|
|
|
- dev->irq = irq;
|
|
|
- }
|
|
|
-
|
|
|
- dev->board_name = board->name;
|
|
|
-
|
|
|
-/*
|
|
|
- * Allocate the private structure area. alloc_private() is a
|
|
|
- * convenient macro defined in comedidev.h.
|
|
|
- */
|
|
|
- if (alloc_private(dev, sizeof(struct dmm32at_private)) < 0)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- ret = comedi_alloc_subdevices(dev, 3);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
-
|
|
|
- s = dev->subdevices + 0;
|
|
|
- dev->read_subdev = s;
|
|
|
- /* analog input subdevice */
|
|
|
- s->type = COMEDI_SUBD_AI;
|
|
|
- /* we support single-ended (ground) and differential */
|
|
|
- s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
|
|
|
- s->n_chan = board->ai_chans;
|
|
|
- s->maxdata = (1 << board->ai_bits) - 1;
|
|
|
- s->range_table = board->ai_ranges;
|
|
|
- s->len_chanlist = 32; /* This is the maximum chanlist length that
|
|
|
- the board can handle */
|
|
|
- s->insn_read = dmm32at_ai_rinsn;
|
|
|
- s->do_cmd = dmm32at_ai_cmd;
|
|
|
- s->do_cmdtest = dmm32at_ai_cmdtest;
|
|
|
- s->cancel = dmm32at_ai_cancel;
|
|
|
-
|
|
|
- s = dev->subdevices + 1;
|
|
|
- /* analog output subdevice */
|
|
|
- s->type = COMEDI_SUBD_AO;
|
|
|
- s->subdev_flags = SDF_WRITABLE;
|
|
|
- s->n_chan = board->ao_chans;
|
|
|
- s->maxdata = (1 << board->ao_bits) - 1;
|
|
|
- s->range_table = board->ao_ranges;
|
|
|
- s->insn_write = dmm32at_ao_winsn;
|
|
|
- s->insn_read = dmm32at_ao_rinsn;
|
|
|
-
|
|
|
- s = dev->subdevices + 2;
|
|
|
- /* digital i/o subdevice */
|
|
|
- if (board->have_dio) {
|
|
|
-
|
|
|
- /* get access to the DIO regs */
|
|
|
- dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_DIOACC);
|
|
|
- /* set the DIO's to the defualt input setting */
|
|
|
- devpriv->dio_config = DMM32AT_DIRA | DMM32AT_DIRB |
|
|
|
- DMM32AT_DIRCL | DMM32AT_DIRCH | DMM32AT_DIENABLE;
|
|
|
- dmm_outb(dev, DMM32AT_DIOCONF, devpriv->dio_config);
|
|
|
-
|
|
|
- /* set up the subdevice */
|
|
|
- s->type = COMEDI_SUBD_DIO;
|
|
|
- s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
|
|
|
- s->n_chan = board->dio_chans;
|
|
|
- s->maxdata = 1;
|
|
|
- s->state = 0;
|
|
|
- s->range_table = &range_digital;
|
|
|
- s->insn_bits = dmm32at_dio_insn_bits;
|
|
|
- s->insn_config = dmm32at_dio_insn_config;
|
|
|
- } else {
|
|
|
- s->type = COMEDI_SUBD_UNUSED;
|
|
|
- }
|
|
|
-
|
|
|
- /* success */
|
|
|
- printk(KERN_INFO "comedi%d: dmm32at: attached\n", dev->minor);
|
|
|
-
|
|
|
- return 1;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-static void dmm32at_detach(struct comedi_device *dev)
|
|
|
-{
|
|
|
- if (dev->irq)
|
|
|
- free_irq(dev->irq, dev);
|
|
|
- if (dev->iobase)
|
|
|
- release_region(dev->iobase, DMM32AT_MEMSIZE);
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* "instructions" read/write data in "one-shot" or "software-triggered"
|
|
|
* mode.
|
|
@@ -990,6 +789,178 @@ void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
|
|
|
|
|
|
}
|
|
|
|
|
|
+static int dmm32at_attach(struct comedi_device *dev,
|
|
|
+ struct comedi_devconfig *it)
|
|
|
+{
|
|
|
+ const struct dmm32at_board *board = comedi_board(dev);
|
|
|
+ int ret;
|
|
|
+ struct comedi_subdevice *s;
|
|
|
+ unsigned char aihi, ailo, fifostat, aistat, intstat, airback;
|
|
|
+ unsigned long iobase;
|
|
|
+ unsigned int irq;
|
|
|
+
|
|
|
+ iobase = it->options[0];
|
|
|
+ irq = it->options[1];
|
|
|
+
|
|
|
+ printk(KERN_INFO "comedi%d: dmm32at: attaching\n", dev->minor);
|
|
|
+ printk(KERN_DEBUG "dmm32at: probing at address 0x%04lx, irq %u\n",
|
|
|
+ iobase, irq);
|
|
|
+
|
|
|
+ /* register address space */
|
|
|
+ if (!request_region(iobase, DMM32AT_MEMSIZE, board->name)) {
|
|
|
+ printk(KERN_ERR "comedi%d: dmm32at: I/O port conflict\n",
|
|
|
+ dev->minor);
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+ dev->iobase = iobase;
|
|
|
+
|
|
|
+ /* the following just makes sure the board is there and gets
|
|
|
+ it to a known state */
|
|
|
+
|
|
|
+ /* reset the board */
|
|
|
+ dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_RESET);
|
|
|
+
|
|
|
+ /* allow a millisecond to reset */
|
|
|
+ udelay(1000);
|
|
|
+
|
|
|
+ /* zero scan and fifo control */
|
|
|
+ dmm_outb(dev, DMM32AT_FIFOCNTRL, 0x0);
|
|
|
+
|
|
|
+ /* zero interrupt and clock control */
|
|
|
+ dmm_outb(dev, DMM32AT_INTCLOCK, 0x0);
|
|
|
+
|
|
|
+ /* write a test channel range, the high 3 bits should drop */
|
|
|
+ dmm_outb(dev, DMM32AT_AILOW, 0x80);
|
|
|
+ dmm_outb(dev, DMM32AT_AIHIGH, 0xff);
|
|
|
+
|
|
|
+ /* set the range at 10v unipolar */
|
|
|
+ dmm_outb(dev, DMM32AT_AICONF, DMM32AT_RANGE_U10);
|
|
|
+
|
|
|
+ /* should take 10 us to settle, here's a hundred */
|
|
|
+ udelay(100);
|
|
|
+
|
|
|
+ /* read back the values */
|
|
|
+ ailo = dmm_inb(dev, DMM32AT_AILOW);
|
|
|
+ aihi = dmm_inb(dev, DMM32AT_AIHIGH);
|
|
|
+ fifostat = dmm_inb(dev, DMM32AT_FIFOSTAT);
|
|
|
+ aistat = dmm_inb(dev, DMM32AT_AISTAT);
|
|
|
+ intstat = dmm_inb(dev, DMM32AT_INTCLOCK);
|
|
|
+ airback = dmm_inb(dev, DMM32AT_AIRBACK);
|
|
|
+
|
|
|
+ printk(KERN_DEBUG "dmm32at: lo=0x%02x hi=0x%02x fifostat=0x%02x\n",
|
|
|
+ ailo, aihi, fifostat);
|
|
|
+ printk(KERN_DEBUG
|
|
|
+ "dmm32at: aistat=0x%02x intstat=0x%02x airback=0x%02x\n",
|
|
|
+ aistat, intstat, airback);
|
|
|
+
|
|
|
+ if ((ailo != 0x00) || (aihi != 0x1f) || (fifostat != 0x80) ||
|
|
|
+ (aistat != 0x60 || (intstat != 0x00) || airback != 0x0c)) {
|
|
|
+ printk(KERN_ERR "dmmat32: board detection failed\n");
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* board is there, register interrupt */
|
|
|
+ if (irq) {
|
|
|
+ ret = request_irq(irq, dmm32at_isr, 0, board->name, dev);
|
|
|
+ if (ret < 0) {
|
|
|
+ printk(KERN_ERR "dmm32at: irq conflict\n");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ dev->irq = irq;
|
|
|
+ }
|
|
|
+
|
|
|
+ dev->board_name = board->name;
|
|
|
+
|
|
|
+/*
|
|
|
+ * Allocate the private structure area. alloc_private() is a
|
|
|
+ * convenient macro defined in comedidev.h.
|
|
|
+ */
|
|
|
+ if (alloc_private(dev, sizeof(struct dmm32at_private)) < 0)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ ret = comedi_alloc_subdevices(dev, 3);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ s = dev->subdevices + 0;
|
|
|
+ dev->read_subdev = s;
|
|
|
+ /* analog input subdevice */
|
|
|
+ s->type = COMEDI_SUBD_AI;
|
|
|
+ /* we support single-ended (ground) and differential */
|
|
|
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
|
|
|
+ s->n_chan = board->ai_chans;
|
|
|
+ s->maxdata = (1 << board->ai_bits) - 1;
|
|
|
+ s->range_table = board->ai_ranges;
|
|
|
+ s->len_chanlist = 32; /* This is the maximum chanlist length that
|
|
|
+ the board can handle */
|
|
|
+ s->insn_read = dmm32at_ai_rinsn;
|
|
|
+ s->do_cmd = dmm32at_ai_cmd;
|
|
|
+ s->do_cmdtest = dmm32at_ai_cmdtest;
|
|
|
+ s->cancel = dmm32at_ai_cancel;
|
|
|
+
|
|
|
+ s = dev->subdevices + 1;
|
|
|
+ /* analog output subdevice */
|
|
|
+ s->type = COMEDI_SUBD_AO;
|
|
|
+ s->subdev_flags = SDF_WRITABLE;
|
|
|
+ s->n_chan = board->ao_chans;
|
|
|
+ s->maxdata = (1 << board->ao_bits) - 1;
|
|
|
+ s->range_table = board->ao_ranges;
|
|
|
+ s->insn_write = dmm32at_ao_winsn;
|
|
|
+ s->insn_read = dmm32at_ao_rinsn;
|
|
|
+
|
|
|
+ s = dev->subdevices + 2;
|
|
|
+ /* digital i/o subdevice */
|
|
|
+ if (board->have_dio) {
|
|
|
+
|
|
|
+ /* get access to the DIO regs */
|
|
|
+ dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_DIOACC);
|
|
|
+ /* set the DIO's to the defualt input setting */
|
|
|
+ devpriv->dio_config = DMM32AT_DIRA | DMM32AT_DIRB |
|
|
|
+ DMM32AT_DIRCL | DMM32AT_DIRCH | DMM32AT_DIENABLE;
|
|
|
+ dmm_outb(dev, DMM32AT_DIOCONF, devpriv->dio_config);
|
|
|
+
|
|
|
+ /* set up the subdevice */
|
|
|
+ s->type = COMEDI_SUBD_DIO;
|
|
|
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
|
|
|
+ s->n_chan = board->dio_chans;
|
|
|
+ s->maxdata = 1;
|
|
|
+ s->state = 0;
|
|
|
+ s->range_table = &range_digital;
|
|
|
+ s->insn_bits = dmm32at_dio_insn_bits;
|
|
|
+ s->insn_config = dmm32at_dio_insn_config;
|
|
|
+ } else {
|
|
|
+ s->type = COMEDI_SUBD_UNUSED;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* success */
|
|
|
+ printk(KERN_INFO "comedi%d: dmm32at: attached\n", dev->minor);
|
|
|
+
|
|
|
+ return 1;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+static void dmm32at_detach(struct comedi_device *dev)
|
|
|
+{
|
|
|
+ if (dev->irq)
|
|
|
+ free_irq(dev->irq, dev);
|
|
|
+ if (dev->iobase)
|
|
|
+ release_region(dev->iobase, DMM32AT_MEMSIZE);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct dmm32at_board dmm32at_boards[] = {
|
|
|
+ {
|
|
|
+ .name = "dmm32at",
|
|
|
+ .ai_chans = 32,
|
|
|
+ .ai_bits = 16,
|
|
|
+ .ai_ranges = &dmm32at_airanges,
|
|
|
+ .ao_chans = 4,
|
|
|
+ .ao_bits = 12,
|
|
|
+ .ao_ranges = &dmm32at_aoranges,
|
|
|
+ .have_dio = 1,
|
|
|
+ .dio_chans = 24,
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
static struct comedi_driver dmm32at_driver = {
|
|
|
.driver_name = "dmm32at",
|
|
|
.module = THIS_MODULE,
|