added das16m1 driver, compiles with warnings but completely untested. insns
authorFrank Mori Hess <fmhess@speakeasy.net>
Tue, 19 Jun 2001 23:28:00 +0000 (23:28 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Tue, 19 Jun 2001 23:28:00 +0000 (23:28 +0000)
not yet implemented.

comedi/Config.in
comedi/drivers/Makefile
comedi/drivers/das16m1.c [new file with mode: 0644]

index 8493bb66050260c6eeb939f734a84a4ffedba7a7..c187d0815797acd71df34af3e1415020775a9c64 100644 (file)
@@ -65,6 +65,7 @@ fi
 
 dep_tristate 'DAS08 compatible driver' CONFIG_COMEDI_DAS08 $CONFIG_COMEDI
 dep_tristate 'DAS16 compatible driver' CONFIG_COMEDI_DAS16 $CONFIG_COMEDI
+dep_tristate 'CIO-DAS16/M1 driver' CONFIG_COMEDI_DAS16M1 $CONFIG_COMEDI
 dep_tristate 'old DAS-08 series driver' CONFIG_COMEDI_DAS08_OLD $CONFIG_COMEDI
 dep_tristate 'old DAS-08jr driver' CONFIG_COMEDI_DAS08JR_OLD $CONFIG_COMEDI
 dep_tristate 'old DAS-1600 and compatibles' CONFIG_COMEDI_DAS1600_OLD $CONFIG_COMEDI
index ba63795dfed9af054d63995f23eebd50bd3e6849..bdcdfada66b50a87e91cd25748337bd13ed7a54c 100644 (file)
@@ -29,6 +29,7 @@ obj-$(CONFIG_COMEDI_DAQBOARD2000)     += daqboard2000.o
 
 obj-$(CONFIG_COMEDI_DAS08)             += das08.o
 obj-$(CONFIG_COMEDI_DAS16)             += das16.o
+obj-$(CONFIG_COMEDI_DAS16M1)           += das16m1.o
 obj-$(CONFIG_COMEDI_DAS08_OLD)         += das08-old.o
 obj-$(CONFIG_COMEDI_DAS08JR_OLD)       += das08jr-old.o
 obj-$(CONFIG_COMEDI_DAS16_OLD)         += das16-old.o
diff --git a/comedi/drivers/das16m1.c b/comedi/drivers/das16m1.c
new file mode 100644 (file)
index 0000000..800671a
--- /dev/null
@@ -0,0 +1,584 @@
+/*
+    comedi/drivers/das16m1.c
+    CIO-DAS16/M1 driver
+    Author: Frank Mori Hess, based on code from the das16
+      driver by Chris Baugher.
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 2000 David A. Schleef <ds@stm.lbl.gov>
+
+    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.
+
+************************************************************************
+
+TODO: add cio-das16/m1/16 support
+
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/comedidev.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <8255.h>
+#include <8253.h>
+
+//#define DEBUG
+
+#define DAS16M1_SIZE 16
+#define DAS16M1_SIZE2 8
+
+#define DAS16M1_XTAL 100       //10 MHz master clock
+
+#define HALF_FIFO 512  // 1024 sample fifo
+
+/*
+    CIO-DAS16_M1.pdf
+
+    "cio-das16/m1"
+
+  0    a/d bits 0-3, mux               start 12 bit
+  1    a/d bits 4-11           unused
+  2    status          control
+  3    di 4 bit                do 4 bit
+  4    unused                  clear interrupt
+  5    interrupt, pacer
+  6    channel/gain queue address
+  7    channel/gain queue data
+  89ab 8254
+  cdef 8254
+  400  8255
+  404-407      8254
+
+*/
+
+#define DAS16M1_TRIG           0
+#define DAS16M1_AI             0       // 16-bit wide register
+#define   AI_CHAN(x)             ((x) & 0xf)
+#define   AI_DATA(x)             (((x) >> 4) & 0xfff)
+#define DAS16M1_CS             2
+#define   EXT_TRIG               0x1
+#define   CLK_SRC                0x2
+#define   OVRUN                  0x20
+#define   IRQDATA                0x80
+#define DAS16M1_DIO            3
+#define DAS16M1_CLEAR_INTR     4
+#define DAS16M1_INTR_CONTROL   5
+#define   INT_PACER              0x3
+#define   PACER_MASK             0x3
+#define   IRQ(x)                 (((x) & 0x7) << 4)
+#define   INTE                   0x80
+#define DAS16M1_QUEUE_ADDR     6
+#define DAS16M1_QUEUE_DATA     7
+#define   Q_CHAN(x)              ((x) & 0x7)
+#define   Q_RANGE(x)             (((x) & 0xf) << 4)
+#define   UNIPOLAR               0x40
+#define DAS16M1_8254_FIRST             8
+#define DAS16M1_8254_SECOND            0xc
+#define DAS16M1_82C55                  0x400
+#define DAS16M1_8254_THIRD             0x404
+
+static comedi_lrange range_das16m1 =
+{ 9,
+       {
+               BIP_RANGE( 5 ),
+               BIP_RANGE( 2.5 ),
+               BIP_RANGE( 1.25 ),
+               BIP_RANGE( 0.625 ),
+               UNI_RANGE(10),
+               UNI_RANGE(5),
+               UNI_RANGE(2.5),
+               UNI_RANGE(1.25),
+               BIP_RANGE(10),
+       }
+};
+
+static int das16m1_do_wbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
+static int das16m1_di_rbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
+static int das16m1_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
+
+static int das16m1_cmd_test(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd);
+static int das16m1_cmd_exec(comedi_device *dev,comedi_subdevice *s);
+static int das16m1_cancel(comedi_device *dev, comedi_subdevice *s);
+
+static void das16m1_reset(comedi_device *dev);
+static void das16m1_interrupt(int irq, void *d, struct pt_regs *regs);
+
+static unsigned int das16m1_set_pacer(comedi_device *dev, unsigned int ns, int round_flag);
+#ifdef DEBUG
+static void reg_dump(comedi_device *dev);
+#endif
+
+typedef struct das16m1_board_struct{
+       char *name;
+       unsigned int ai_speed;
+}das16m1_board;
+
+static das16m1_board das16m1_boards[]={
+       {
+       name:           "das16m1",      // CIO-DAS16_M1.pdf
+       ai_speed:       1000,           // 1MHz max speed
+       },
+};
+
+#define das16m1_num_boards ((sizeof(das16m1_boards)) / (sizeof(das16m1_boards[0])))
+
+static int das16m1_attach(comedi_device *dev, comedi_devconfig *it);
+static int das16m1_detach(comedi_device *dev);
+comedi_driver driver_das16m1={
+       driver_name:    "das16m1",
+       module:         THIS_MODULE,
+       attach:         das16m1_attach,
+       detach:         das16m1_detach,
+       board_name:     das16m1_boards,
+       num_names:      das16m1_num_boards,
+       offset:         sizeof(das16m1_boards[0]),
+};
+
+struct das16m1_private_struct {
+       unsigned int    control_state;
+       volatile unsigned int   adc_count;
+       unsigned int divisor1;  // divides master clock to obtain conversion speed
+       unsigned int divisor2;  // divides master clock to obtain conversion speed
+};
+#define devpriv ((struct das16m1_private_struct *)(dev->private))
+#define thisboard ((struct das16m1_board_struct *)(dev->board_ptr))
+
+COMEDI_INITCLEANUP(driver_das16m1);
+
+static int das16m1_cmd_test(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd)
+{
+       unsigned int err=0, tmp;
+
+       /* make sure triggers are valid */
+       tmp=cmd->start_src;
+       cmd->start_src &= TRIG_NOW;
+       if(!cmd->start_src || tmp!=cmd->start_src) err++;
+
+       tmp=cmd->scan_begin_src;
+       cmd->scan_begin_src &= TRIG_FOLLOW;
+       if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src) err++;
+
+       tmp=cmd->convert_src;
+       cmd->convert_src &= TRIG_TIMER;
+       if(!cmd->convert_src || tmp!=cmd->convert_src) err++;
+
+       tmp=cmd->scan_end_src;
+       cmd->scan_end_src &= TRIG_COUNT;
+       if(!cmd->scan_end_src || tmp!=cmd->scan_end_src) err++;
+
+       tmp=cmd->stop_src;
+       cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+       if(!cmd->stop_src || tmp!=cmd->stop_src) err++;
+
+       if(err)return 1;
+
+       /* step 2: make sure trigger sources are unique and mutually compatible */
+       /* note that mutual compatiblity is not an issue here */
+       if(cmd->stop_src != TRIG_COUNT &&
+          cmd->stop_src != TRIG_NONE) err++;
+
+       if(err)return 2;
+
+       /* step 3: make sure arguments are trivially compatible */
+       if(cmd->start_arg != 0){
+               cmd->start_arg = 0;
+               err++;
+       }
+
+       if(cmd->scan_begin_src == TRIG_FOLLOW){
+               /* internal trigger */
+               if(cmd->scan_begin_arg != 0){
+                       cmd->scan_begin_arg = 0;
+                       err++;
+               }
+       }
+
+       if(cmd->convert_src == TRIG_TIMER)
+       {
+               if(cmd->convert_arg < thisboard->ai_speed)
+               {
+                       cmd->convert_arg = thisboard->ai_speed;
+                       err++;
+               }
+       }
+
+       if(cmd->scan_end_arg != cmd->chanlist_len){
+               cmd->scan_end_arg = cmd->chanlist_len;
+               err++;
+       }
+
+       if(cmd->stop_src==TRIG_COUNT){
+               /* any count is allowed */
+       }else{
+               /* TRIG_NONE */
+               if(cmd->stop_arg!=0){
+                       cmd->stop_arg=0;
+                       err++;
+               }
+       }
+
+       if(err) return 3;
+
+       /* step 4: fix up arguments */
+
+       if(cmd->convert_src == TRIG_TIMER)
+       {
+               tmp = cmd->convert_arg;
+               /* calculate counter values that give desired timing */
+               i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1),
+                       &(devpriv->divisor2), &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK);
+               if(tmp != cmd->convert_arg) err++;
+       }
+       // XXX we need to check constraints on chanlist here
+
+       if(err) return 4;
+
+       return 0;
+}
+
+static int das16m1_cmd_exec(comedi_device *dev,comedi_subdevice *s)
+{
+       comedi_cmd *cmd = &s->async->cmd;
+       unsigned int byte, i;
+
+       devpriv->adc_count = cmd->stop_arg * cmd->chanlist_len;
+
+       /* setup channel/gain queue */
+       for(i = 0; i < cmd->chanlist_len; i++)
+       {
+               outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
+               byte = Q_CHAN(CR_CHAN(cmd->chanlist[i])) | Q_RANGE(CR_RANGE(cmd->chanlist[i]));
+               outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
+       }
+
+       /* enable pacer clocked conversions */
+       devpriv->control_state |= INT_PACER;
+
+       /* set counter mode and counts */
+       das16m1_set_pacer(dev, cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
+
+       /* clear interrupt bit */
+       outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
+       /* enable interrupts and internal pacer */
+       devpriv->control_state |= INTE | INT_PACER;
+       outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+
+       return 0;
+}
+
+static int das16m1_cancel(comedi_device *dev, comedi_subdevice *s)
+{
+       devpriv->adc_count = 0;
+
+       return 0;
+}
+
+#if 0  // insns haven't been converted from das16 driver yet
+static int das16m1_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       int i,n;
+       int range;
+       int chan;
+       int msb,lsb;
+
+       /* clear crap */
+       inb(dev->iobase+DAS16M1_AI_LSB);
+       inb(dev->iobase+DAS16M1_AI_MSB);
+
+       /* set multiplexer */
+       chan = CR_CHAN(insn->chanspec);
+       outb_p(chan,dev->iobase+DAS16M1_MUX);
+
+       /* set gain */
+       if(thisboard->ai_pg != das16m1_pg_none){
+               range = CR_RANGE(insn->chanspec);
+               outb((das16m1_gainlists[thisboard->ai_pg])[range],
+                       dev->iobase+DAS16M1_GAIN);
+       }
+
+       /* How long should we wait for MUX to settle? */
+       //udelay(5);
+
+       for(n=0;n<insn->n;n++){
+               /* trigger conversion */
+               outb_p(0,dev->iobase+DAS16M1_TRIG);
+
+               for(i=0;i<DAS16M1_TIMEOUT;i++){
+                       if(!(inb(DAS16M1_STATUS)&DAS16M1_EOC))
+                               break;
+               }
+               if(i==DAS16M1_TIMEOUT){
+                       rt_printk("das16m1: timeout\n");
+                       return -ETIME;
+               }
+               msb = inb(dev->iobase + DAS16M1_AI_MSB);
+               lsb = inb(dev->iobase + DAS16M1_AI_LSB);
+               if(thisboard->ai_nbits==12){
+                       data[n] = (lsb>>4) | (msb << 4);
+               }else{
+                       data[n] = lsb | (msb << 8);
+               }
+       }
+
+       return n;
+}
+
+static int das16m1_di_rbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       data[0]=inb(dev->iobase+DAS16M1_DIO)&0xf;
+
+       return 1;
+}
+
+static int das16m1_do_wbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       outb(data[0],dev->iobase+DAS16M1_DIO);
+
+       return 1;
+}
+
+static int das16m1_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       int i;
+       int lsb,msb;
+       int chan;
+
+       chan=CR_CHAN(insn->chanspec);
+
+       for(i=0;i<insn->n;i++){
+               if(thisboard->ao_nbits==12){
+                       lsb=(data[i]<<4)&0xff;
+                       msb=(data[i]>>4)&0xff;
+               }else{
+                       lsb=data[i]&0xff;
+                       msb=(data[i]>>8)&0xff;
+               }
+
+#if 0
+               outb(lsb,dev->iobase+devpriv->ao_offset_lsb[chan]);
+               outb(msb,dev->iobase+devpriv->ao_offset_msb[chan]);
+#else
+               outb(lsb,dev->iobase+DAS16M1_AO_LSB(chan));
+               outb(msb,dev->iobase+DAS16M1_AO_MSB(chan));
+#endif
+       }
+
+       return i;
+}
+
+#endif // insns haven't been converted from das16 driver yet
+
+static void das16m1_interrupt(int irq, void *d, struct pt_regs *regs)
+{
+       int i, status;
+       sampl_t data_point;
+
+       comedi_device *dev = d;
+       comedi_subdevice *s = dev->subdevices;
+       comedi_async *async = s->async;
+
+       status = inb(dev->iobase + DAS16M1_CS);
+
+       if((status & IRQDATA) == 0 || dev->attached == 0)
+       {
+               comedi_error(dev, "spurious interrupt");
+               return;
+       }
+
+       for(i = 0; i < HALF_FIFO; i++)
+       {
+               data_point = inw(dev->iobase + DAS16M1_AI);
+               data_point = AI_DATA(data_point);
+               comedi_buf_put(async, data_point);
+
+               if(--devpriv->adc_count <= 0) {         /* end of acquisition */
+                               devpriv->control_state &= ~INTE;
+                               devpriv->control_state &= ~PACER_MASK;
+                               outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+                               async->events |= COMEDI_CB_EOA;
+                               break;
+                       }
+       }
+
+       comedi_event(dev, s, async->events);
+       async->events = 0;
+
+       /* clear interrupt */
+       outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
+}
+
+/* This function takes a time in nanoseconds and sets the     *
+ * 2 pacer clocks to the closest frequency possible. It also  *
+ * returns the actual sampling period.                        */
+static unsigned int das16m1_set_pacer(comedi_device *dev, unsigned int ns, int rounding_flags)
+{
+       i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1),
+               &(devpriv->divisor2), &ns, rounding_flags & TRIG_ROUND_MASK);
+
+       /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
+       i8254_load(dev->iobase + DAS16M1_8254_SECOND, 1, devpriv->divisor1, 2);
+       i8254_load(dev->iobase + DAS16M1_8254_SECOND, 2, devpriv->divisor2, 2);
+
+       return ns;
+}
+
+#ifdef DEBUG
+static void reg_dump(comedi_device *dev)
+{
+       printk("********DAS16M100 REGISTER DUMP********\n");
+       printk("DAS16M1_MUX: %x\n", inb(dev->iobase+DAS16M1_MUX) );
+       printk("DAS16M1_DIO: %x\n", inb(dev->iobase+DAS16M1_DIO) );
+       printk("DAS16M1_STATUS: %x\n", inb(dev->iobase+DAS16M1_STATUS) );
+       printk("DAS16M1_CONTROL: %x\n", inb(dev->iobase+DAS16M1_CONTROL) );
+       printk("DAS16M1_PACER: %x\n", inb(dev->iobase+DAS16M1_PACER) );
+       printk("DAS16M1_GAIN: %x\n", inb(dev->iobase+DAS16M1_GAIN) );
+       printk("DAS16M1_CNTR_CONTROL: %x\n", inb(dev->iobase+DAS16M1_CNTR_CONTROL) );
+       printk("DAS16M100_CONV: %x\n", inb(dev->iobase+DAS16M100_CONV) );
+       printk("DAS16M100_BURST: %x\n", inb(dev->iobase+DAS16M100_BURST) );
+       printk("DAS16M100_ENABLE: %x\n", inb(dev->iobase+DAS16M100_ENABLE) );
+       printk("DAS16M100_STATUS_B: %x\n", inb(dev->iobase+DAS16M100_STATUS_B) );
+}
+#endif
+
+/*
+ * Options list:
+ *   0  I/O base
+ *   1  IRQ
+ */
+
+static int das16m1_attach(comedi_device *dev, comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       int ret, irq;
+       int iobase;
+
+       iobase = it->options[0];
+
+       printk("comedi%d: das16m1:", dev->minor);
+
+       if((ret = alloc_private(dev, sizeof(struct das16m1_private_struct))) < 0)
+               return ret;
+
+       dev->board_name = thisboard->name;
+
+       printk(" io= 0x%04x-0x%04x 0x%04x-0x%04x",
+                  iobase, iobase + DAS16M1_SIZE,
+                  iobase + DAS16M1_82C55, iobase + DAS16M1_82C55 + DAS16M1_SIZE2);
+       if(check_region(iobase, DAS16M1_SIZE) < 0) {
+               printk(" I/O port conflict\n");
+               return -EIO;
+       }
+       if(check_region(iobase + DAS16M1_82C55, DAS16M1_SIZE2) < 0){
+               printk(" I/O port conflict\n");
+               return -EIO;
+       }
+       request_region(iobase, DAS16M1_SIZE, driver_das16m1.driver_name);
+       request_region(iobase + DAS16M1_82C55, DAS16M1_SIZE2, driver_das16m1.driver_name);
+       dev->iobase = iobase;
+
+       /* now for the irq */
+       irq = it->options[1];
+       // make sure it is valid
+       if(irq == 2 || irq == 3 || irq == 5 || irq == 7 ||
+               irq == 10 || irq == 11 || irq == 12 || irq == 15)
+       {
+               if((ret = comedi_request_irq(irq, das16m1_interrupt, 0,
+                       driver_das16m1.driver_name,dev)) < 0)
+                       return ret;
+               dev->irq = irq;
+               printk(" irq = %d\n", irq);
+       }else if(irq == 0){
+               printk(" irq = none\n");
+       }else {
+               printk(" invalid irq\n");
+               return -EINVAL;
+       }
+
+       dev->n_subdevices = 4;
+       if((ret = alloc_subdevices(dev)) < 0)
+               return ret;
+
+       s = dev->subdevices + 0;
+       dev->read_subdev = s;
+       /* ai */
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = 8;
+       s->subdev_flags |= SDF_DIFF;
+       s->len_chanlist = 256;
+       s->maxdata = (1 << 12) - 1;
+       s->range_table = &range_das16m1;
+//     s->insn_read = das16m1_ai_rinsn;
+       s->do_cmdtest = das16m1_cmd_test;
+       s->do_cmd = das16m1_cmd_exec;
+       s->cancel = das16m1_cancel;
+
+       s = dev->subdevices + 1;
+       /* di */
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = 4;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+//     s->insn_bits = das16m1_di_rbits;
+
+       s = dev->subdevices + 2;
+       /* do */
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITEABLE;
+       s->n_chan = 4;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+//     s->insn_write = das16m1_do_wbits;
+
+       s = dev->subdevices + 3;
+       /* 8255 */
+       subdev_8255_init(dev, s, NULL, (void*)(dev->iobase + DAS16M1_82C55));
+
+       // XXX should init digital output levels here also
+       /* set the interrupt level */
+       devpriv->control_state = IRQ(dev->irq);
+       outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+
+       return 0;
+}
+
+
+static int das16m1_detach(comedi_device *dev)
+{
+       printk("comedi%d: das16m1: remove\n", dev->minor);
+
+//     das16m1_reset(dev);
+
+       if(dev->subdevices)
+               subdev_8255_cleanup(dev,dev->subdevices+4);
+
+       if(dev->irq)
+               free_irq(dev->irq, dev);
+
+       if(dev->iobase){
+               release_region(dev->iobase , DAS16M1_SIZE);
+               release_region(dev->iobase + DAS16M1_82C55, DAS16M1_SIZE2);
+       }
+
+       return 0;
+}
+
+