Big hack attack results in new driver. Film at 11.
authorDavid Schleef <ds@schleef.org>
Fri, 8 Feb 2002 23:09:47 +0000 (23:09 +0000)
committerDavid Schleef <ds@schleef.org>
Fri, 8 Feb 2002 23:09:47 +0000 (23:09 +0000)
comedi/drivers/ni_at_ao.c [new file with mode: 0644]

diff --git a/comedi/drivers/ni_at_ao.c b/comedi/drivers/ni_at_ao.c
new file mode 100644 (file)
index 0000000..695ca63
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+    comedi/drivers/ni_at_ao.c
+    Driver for NI AT-AO-6/10 boards
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 2000,2002 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: ni_at_ao.o
+Description: National Instruments AT-AO-6/10
+Devices: [National Instruments] AT-AO-6 (at-ao-6), AT-AO-10 (at-ao-10)
+Author: ds
+
+20
+*/
+/*
+ * Register-level programming information can be found in NI
+ * document 320379.pdf.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/comedidev.h>
+
+
+/* board egisters */
+/* registers with _2_ are accessed when GRP2WR is set in CFG1 */
+
+#define ATAO_SIZE 0x20
+
+#define ATAO_2_DMATCCLR                0x00    /* W 16 */
+#define ATAO_DIN               0x00    /* R 16 */
+#define ATAO_DOUT              0x00    /* W 16 */
+
+#define ATAO_CFG2              0x02    /* W 16 */
+  #define CALLD1       0x8000
+  #define CALLD0       0x4000
+  #define FFRTEN       0x2000
+  #define DAC2S8       0x1000
+  #define DAC2S6       0x0800
+  #define DAC2S4       0x0400
+  #define DAC2S2       0x0200
+  #define DAC2S0       0x0100
+  #define LDAC8                0x0080
+  #define LDAC6                0x0040
+  #define LDAC4                0x0020
+  #define LDAC2                0x0010
+  #define LDAC0                0x0008
+  #define PROMEN       0x0004
+  #define SCLK         0x0002
+  #define SDATA                0x0001
+
+#define ATAO_2_INT1CLR         0x02    /* W 16 */
+
+#define ATAO_CFG3              0x04    /* W 16 */
+  #define DMAMODE      0x0040
+  #define CLKOUT       0x0020
+  #define RCLKEN       0x0010
+  #define DOUTEN2      0x0008
+  #define DOUTEN1      0x0004
+  #define EN2_5V       0x0002
+  #define SCANEN       0x0001
+
+#define ATAO_2_INT2CLR         0x04    /* W 16 */
+
+#define ATAO_82C53_BASE                0x06    /* RW 8 */
+
+#define ATAO_82C53_CNTR1       0x06    /* RW 8 */
+#define ATAO_82C53_CNTR2       0x07    /* RW 8 */
+#define ATAO_82C53_CNTR3       0x08    /* RW 8 */
+#define ATAO_82C53_CNTRCMD     0x09    /* W 8 */
+  #define CNTRSEL1     0x80
+  #define CNTRSEL0     0x40
+  #define RWSEL1       0x20
+  #define RWSEL0       0x10
+  #define MODESEL2     0x08
+  #define MODESEL1     0x04
+  #define MODESEL0     0x02
+  #define BCDSEL       0x01
+  /* read-back command */
+  #define COUNT                0x20
+  #define STATUS       0x10
+  #define CNTR3                0x08
+  #define CNTR2                0x04
+  #define CNTR1                0x02
+  /* status */
+  #define OUT          0x80
+  #define _NULL                0x40
+  #define RW1          0x20
+  #define RW0          0x10
+  #define MODE2                0x08
+  #define MODE1                0x04
+  #define MODE0                0x02
+  #define BCD          0x01
+
+#define ATAO_2_RTSISHFT                0x06    /* W 8 */
+  #define RSI          0x01
+
+#define ATAO_2_RTSISTRB                0x07    /* W 8 */
+
+#define ATAO_CFG1              0x0a    /* W 16 */
+  #define EXTINT2EN    0x8000
+  #define EXTINT1EN    0x4000
+  #define CNTINT2EN    0x2000
+  #define CNTINT1EN    0x1000
+  #define TCINTEN      0x0800
+  #define CNT1SRC      0x0400
+  #define CNT2SRC      0x0200
+  #define FIFOEN       0x0100
+  #define GRP2WR       0x0080
+  #define EXTUPDEN     0x0040
+  #define DMARQ                0x0020
+  #define DMAEN                0x0010
+  #define CH_mask      0x000f
+#define ATAO_STATUS            0x0a    /* R 16 */
+  #define FH           0x0040
+  #define FE           0x0020
+  #define FF           0x0010
+  #define INT2         0x0008
+  #define INT1         0x0004
+  #define TCINT                0x0002
+  #define PROMOUT      0x0001
+
+#define ATAO_FIFO_WRITE                0x0c    /* W 16 */
+#define ATAO_FIFO_CLEAR                0x0c    /* R 16 */
+#define ATAO_DACn(x)           (0x0c + 2*(x))  /* W */
+
+/*
+ * Board descriptions for two imaginary boards.  Describing the
+ * boards in this way is optional, and completely driver-dependent.
+ * Some drivers use arrays such as this, other do not.
+ */
+typedef struct atao_board_struct{
+       char *name;
+       int n_ao_chans;
+}atao_board;
+static atao_board atao_boards[] = {
+       {
+       name:           "ai-ao-6",
+       n_ao_chans:     6,
+       },
+       {
+       name:           "ai-ao-10",
+       n_ao_chans:     10,
+       },
+};
+#define thisboard ((atao_board *)dev->board_ptr)
+
+typedef struct{
+       unsigned short cfg1;
+       unsigned short cfg2;
+       unsigned short cfg3;
+
+       /* Used for AO readback */
+       lsampl_t ao_readback[10];
+}atao_private;
+#define devpriv ((atao_private *)dev->private)
+
+static int atao_attach(comedi_device *dev,comedi_devconfig *it);
+static int atao_detach(comedi_device *dev);
+static comedi_driver driver_atao={
+       driver_name:    "ni_at_ao",
+       module:         THIS_MODULE,
+       attach:         atao_attach,
+       detach:         atao_detach,
+       board_name:     atao_boards,
+       offset:         sizeof(atao_board),
+       num_names:      sizeof(atao_boards) / sizeof(atao_board),
+};
+COMEDI_INITCLEANUP(driver_atao);
+
+static void atao_reset(comedi_device *dev);
+
+static int atao_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
+static int atao_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
+static int atao_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,
+       comedi_insn *insn,lsampl_t *data);
+static int atao_dio_insn_config(comedi_device *dev,comedi_subdevice *s,
+       comedi_insn *insn,lsampl_t *data);
+static int atao_calib_insn_read(comedi_device *dev,comedi_subdevice *s,
+       comedi_insn *insn,lsampl_t *data);
+static int atao_calib_insn_write(comedi_device *dev,comedi_subdevice *s,
+       comedi_insn *insn,lsampl_t *data);
+
+static int atao_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       unsigned long iobase;
+
+       iobase = it->options[0];
+       if(iobase==0)iobase = 0x1c0;
+
+       printk("comedi%d: ni_at_ao: 0x%04lx",dev->minor,iobase);
+       
+       if(check_region(iobase, ATAO_SIZE) < 0){
+               printk(" I/O port conflict\n");
+               return -EIO;
+       }
+       request_region(iobase, ATAO_SIZE, "ni_at_ao");
+       dev->iobase = iobase;
+
+       //dev->board_ptr = atao_probe(dev);
+
+       dev->board_name = thisboard->name;
+
+       if(alloc_private(dev,sizeof(atao_private))<0)
+               return -ENOMEM;
+
+       dev->n_subdevices=4;
+       if(alloc_subdevices(dev)<0)
+               return -ENOMEM;
+
+       s=dev->subdevices+0;
+       /* analog output subdevice */
+       s->type=COMEDI_SUBD_AO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->n_chan=thisboard->n_ao_chans;
+       s->maxdata=(1<<12)-1;
+       s->range_table=&range_bipolar10;
+       s->insn_write = &atao_ao_winsn;
+       s->insn_read = &atao_ao_rinsn;
+
+       s=dev->subdevices+1;
+       /* digital i/o subdevice */
+       s->type=COMEDI_SUBD_DIO;
+       s->subdev_flags=SDF_READABLE|SDF_WRITEABLE;
+       s->n_chan=8;
+       s->maxdata=1;
+       s->range_table=&range_digital;
+       s->insn_bits = atao_dio_insn_bits;
+       s->insn_config = atao_dio_insn_config;
+       
+       s=dev->subdevices+2;
+       /* caldac subdevice */
+       s->type=COMEDI_SUBD_CALIB;
+       s->subdev_flags = SDF_WRITEABLE|SDF_INTERNAL;
+       s->n_chan = 21;
+       s->maxdata = 0xff;
+       s->insn_read = atao_calib_insn_read;
+       s->insn_write = atao_calib_insn_write;
+
+       s=dev->subdevices+3;
+       /* eeprom subdevice */
+       //s->type=COMEDI_SUBD_EEPROM;
+       s->type=COMEDI_SUBD_UNUSED;
+       
+       atao_reset(dev);
+
+       printk("\n");
+
+       return 0;
+}
+
+
+static int atao_detach(comedi_device *dev)
+{
+       printk("comedi%d: atao: remove\n",dev->minor);
+
+       if(dev->iobase)
+               release_region(dev->iobase, ATAO_SIZE);
+       
+       return 0;
+}
+
+
+static void atao_reset(comedi_device *dev)
+{
+       /* This is the reset sequence described in the manual */
+
+       devpriv->cfg1 = 0;
+       outw(devpriv->cfg1, dev->iobase + ATAO_CFG1);
+
+       outb(RWSEL0 | MODESEL2, dev->iobase + ATAO_82C53_CNTRCMD);
+       outb(0x03, dev->iobase + ATAO_82C53_CNTR1);
+       outb(CNTRSEL0 | RWSEL0 | MODESEL2, dev->iobase + ATAO_82C53_CNTRCMD);
+
+       devpriv->cfg2 = 0;
+       outw(devpriv->cfg2, dev->iobase + ATAO_CFG2);
+       
+       devpriv->cfg3 = 0;
+       outw(devpriv->cfg3, dev->iobase + ATAO_CFG3);
+       
+       inw(dev->iobase + ATAO_FIFO_CLEAR);
+
+       devpriv->cfg1 = GRP2WR;
+       outw(devpriv->cfg1, dev->iobase + ATAO_CFG1);
+
+       outw(0, dev->iobase + ATAO_2_INT1CLR);
+       outw(0, dev->iobase + ATAO_2_INT2CLR);
+       outw(0, dev->iobase + ATAO_2_DMATCCLR);
+
+       devpriv->cfg1 = 0;
+       outw(devpriv->cfg1, dev->iobase + ATAO_CFG1);
+}
+
+
+static int atao_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       int i;
+       int chan = CR_CHAN(insn->chanspec);
+
+       for(i=0;i<insn->n;i++){
+               outw(data[i], dev->iobase + ATAO_DACn(chan));
+               devpriv->ao_readback[chan] = data[i];
+       }
+
+       return i;
+}
+
+static int atao_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       int i;
+       int chan = CR_CHAN(insn->chanspec);
+
+       for(i=0;i<insn->n;i++)
+               data[i] = devpriv->ao_readback[chan];
+
+       return i;
+}
+
+static int atao_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,
+       comedi_insn *insn,lsampl_t *data)
+{
+       if(insn->n!=2)return -EINVAL;
+
+       if(data[0]){
+               s->state &= ~data[0];
+               s->state |= data[0]&data[1];
+               outw(s->state,dev->iobase + ATAO_DOUT);
+       }
+
+       data[1]=inw(dev->iobase + ATAO_DIN);
+
+       return 2;
+}
+
+static int atao_dio_insn_config(comedi_device *dev,comedi_subdevice *s,
+       comedi_insn *insn,lsampl_t *data)
+{
+       int chan=CR_CHAN(insn->chanspec);
+       unsigned int mask, bit;
+
+       if(insn->n!=1)return -EINVAL;
+
+       /* The input or output configuration of each digital line is
+        * configured by a special insn_config instruction.  chanspec
+        * contains the channel to be changed, and data[0] contains the
+        * value COMEDI_INPUT or COMEDI_OUTPUT. */
+
+       mask = (chan < 4) ? 0x0f : 0xf0;
+       bit = (chan < 4) ? DOUTEN1 : DOUTEN2;
+
+       if(data[0]==COMEDI_OUTPUT){
+               s->io_bits |= mask;
+               devpriv->cfg3 |= bit;
+       }else{
+               s->io_bits &= ~mask;
+               devpriv->cfg3 &= ~bit;
+       }
+
+       outw(devpriv->cfg3, dev->iobase + ATAO_CFG3);
+
+       return 1;
+}
+
+
+/*
+ * Figure 2-1 in the manual shows 3 chips labeled DAC8800, which
+ * are 8-channel 8-bit DACs.  These are most likely the calibration
+ * DACs.  It is not explicitly stated in the manual how to access
+ * the caldacs, but we can guess.
+ */
+static int atao_calib_insn_read(comedi_device *dev,comedi_subdevice *s,
+       comedi_insn *insn,lsampl_t *data)
+{
+       int i;
+       for(i=0;i<insn->n;i++){
+               data[i]=0; /* XXX */
+       }
+       return insn->n;
+}
+
+static int atao_calib_insn_write(comedi_device *dev,comedi_subdevice *s,
+       comedi_insn *insn,lsampl_t *data)
+{
+       unsigned int bitstring, bit;
+       unsigned int chan = CR_CHAN(insn->chanspec);
+
+       bitstring = ((chan&0x7)<<8)|(insn->data[insn->n-1]&0xff);
+
+       for(bit=1<<(11-1);bit;bit>>=1){
+               outw(devpriv->cfg2 | ((bit&bitstring)?SDATA:0), dev->iobase + ATAO_CFG2);
+               outw(devpriv->cfg2 | SCLK | ((bit&bitstring)?SDATA:0), dev->iobase + ATAO_CFG2);
+       }
+       /* strobe the appropriate caldac */
+       outw(devpriv->cfg2 | (((chan>>3) + 1)<<14), dev->iobase + ATAO_CFG2);
+       outw(devpriv->cfg2, dev->iobase + ATAO_CFG2);
+
+       return insn->n;
+}
+
+