driver supports insn now, no command support
authorFrank Mori Hess <fmhess@speakeasy.net>
Sun, 26 Aug 2001 23:23:33 +0000 (23:23 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Sun, 26 Aug 2001 23:23:33 +0000 (23:23 +0000)
comedi/Config.in
comedi/drivers/Makefile
comedi/drivers/Makefile.in
comedi/drivers/cb_pcidas64.c

index 193452254137221564fd79929f9a78c879c430af..a0913ecd547ac31fb15f86a0b05d96155985fcbc 100644 (file)
@@ -76,6 +76,7 @@ dep_tristate 'DAS-6402 and compatibles' CONFIG_COMEDI_DAS6402 $CONFIG_COMEDI
 dep_tristate 'DAS-800 and compatibles' CONFIG_COMEDI_DAS800 $CONFIG_COMEDI
 dep_tristate 'DAS-1800 and compatibles' CONFIG_COMEDI_DAS1800 $CONFIG_COMEDI
 dep_tristate 'Computer Boards PCI-DAS series' CONFIG_COMEDI_CB_PCIDAS $CONFIG_COMEDI
+dep_tristate 'Computer Boards PCI-DAS64xx series' CONFIG_COMEDI_CB_PCIDAS64 $CONFIG_COMEDI
 dep_tristate 'Computer Boards PCI-DDA series' CONFIG_COMEDI_CB_PCIDDA $CONFIG_COMEDI
 dep_tristate 'Generic 8255 support' CONFIG_COMEDI_8255 $CONFIG_COMEDI
 dep_tristate 'Quanser Consulting MultiQ-3' CONFIG_COMEDI_MULTIQ3 $CONFIG_COMEDI
index c343953f39bff7a6679cfa967f0c9ac15b0793d5..26b0378f93c4f095b7459971fd0e713b2faba14a 100644 (file)
@@ -25,6 +25,7 @@ obj-$(CONFIG_COMEDI_ADV_PCI1710)      += adv_pci1710.o
 obj-$(CONFIG_COMEDI_APMLC_PCI230)      += amplc_pci230.o
 
 obj-$(CONFIG_COMEDI_CB_PCIDAS)         += cb_pcidas.o
+obj-$(CONFIG_COMEDI_CB_PCIDAS64)               += cb_pcidas64.o
 obj-$(CONFIG_COMEDI_CB_PCIDDA)         += cb_pcidda.o
 
 obj-$(CONFIG_COMEDI_DAQBOARD2000)      += daqboard2000.o
index fe33139b18dbf3481ba3cae68319641b180b38d1..773462dee298a96ea54bc5a6908edafe6965ca5a 100644 (file)
@@ -7,6 +7,7 @@ select(CONFIG_COMEDI_ADL_PCI9118 adl_pci9118.o)
 select(CONFIG_COMEDI_ADV_PCI1710 adv_pci1710.o)
 select(CONFIG_COMEDI_APMLC_PCI230 amplc_pci230.o)
 select(CONFIG_COMEDI_CB_PCIDAS cb_pcidas.o)
+select(CONFIG_COMEDI_CB_PCIDAS64 cb_pcidas64.o)
 select(CONFIG_COMEDI_CB_PCIDDA cb_pcidda.o)
 select(CONFIG_COMEDI_DAQBOARD2000 daqboard2000.o)
 select(CONFIG_COMEDI_DAS08 das08.o)
index 95648f0a030941b7ec09facfa73bf3dd43e37abe..4456bf0931dee68b348cbe2b6fafdc8ed2c35d32 100644 (file)
@@ -3,6 +3,8 @@
     This is a driver for the ComputerBoards/MeasurementComputing PCI-DAS
     64xxx cards.
 
+    Author:  Frank Mori Hess <fmhess@uiuc.edu>
+
     Options:
     [0] - PCI bus number
     [1] - PCI slot number
 ************************************************************************
 
 STATUS:
-       quite experimental and unfinished
+       insn supported
 
 TODO:
-       just about everything
+       command support
+       calibration subdevice
+       user counter subdevice
+       there are a number of boards this driver could support, but does not since
+               I don't know the pci device id numbers
 */
 
 #include <linux/kernel.h>
@@ -71,9 +77,45 @@ TODO:
 #define DIO_COUNTER_IOSIZE 0x29
 
 // plx pci9080 configuration registers
+// XXX could steal plx9060 header file from kernel
+
+// devpriv->main_iobase registers
+// write-only
+#define INTR_ENABLE_REG        0x0     // interrupt enable register
+#define HW_CONFIG_REG  0x2     // hardware config register
+#define    HW_CONFIG_DUMMY_BITS        0x2400  // bits that don't do anything yet but are given default values
+#define ADC_CONTROL0_REG       0x10    // adc control register 0
+#define    ADC_ENABLE_BIT      0x8000  // master adc enable
+#define ADC_CONTROL1_REG       0x12    // adc control register 1
+#define    SW_NOGATE_BIT       0x40    // disables software gate of adc
+#define ADC_CONVERT_REG        0x24    // initiates single conversion
+#define ADC_QUEUE_CLEAR_REG    0x26    // clears adc queue
+#define ADC_QUEUE_LOAD_REG     0x28    // loads adc queue
+#define    CHAN_BITS(x)        ((x) & 0x3f)
+#define    GAIN_BITS(x)        (((x) & 0x3) << 8)      // translates range index to gain bits
+#define    UNIP_BIT(x) (((x) & 0x4) << 11)     // translates range index to unipolar/bipolar bit
+#define    SE_BIT      0x1000  // single-ended/ differential bit
+#define    QUEUE_EOS_BIT       0x8000  // queue end of scan
+#define ADC_BUFFER_CLEAR_REG   0x2a
+#define ADC_QUEUE_HIGH_REG     0x2c    // high channel for internal queue, use CHAN_BITS() macro above
+#define DAC_CONTROL0_REG       0x50    // dac control register 0
+#define    DAC_ENABLE_BIT      0x8000  // dac controller enable bit
+#define DAC_CONTROL1_REG       0x52    // dac control register 0
+#define    DAC_RANGE_BITS(channel, range)      (((range) & 0x3) << (2 * ((channel) & 0x1)))
+#define    DAC_OUTPUT_ENABLE_BIT       0x80    // dac output enable bit
+#define DAC_BUFFER_CLEAR_REG 0x66      // clear dac buffer
+#define DAC_CONVERT_REG(channel)       ((0x70) + (2 * ((channel) & 0x1)))
+// read-only
+#define HW_STATUS_REG  0x0
+#define   ADC_BUSY_BIT 0x8
+#define   HW_REVISION(x)       (((x) >> 12) & 0xf)
+#define PIPE1_READ_REG 0x4
+
+// devpriv->dio_counter_iobase registers
+#define DIO_8255_OFFSET        0x0
+#define DO_REG 0x20
+#define DI_REG 0x28
 
-// bit in hexadecimal representation of range index that indicates unipolar input range
-#define IS_UNIPOLAR 0x4
 // analog input ranges for most boards
 static comedi_lrange ai_ranges =
 {
@@ -124,7 +166,109 @@ static pcidas64_board pcidas64_boards[] =
                ao_nchan: 2,
                ao_scan_speed:  10000,
        },
-// XXX supports more boards...
+       {
+               name:           "pci-das6402/12",       // XXX check
+               device_id:      0x1e,
+               ai_se_chans:    64,
+               ai_bits:        12,
+               ai_speed:       5000,
+               ao_nchan: 2,
+               ao_scan_speed:  10000,
+       },
+       {
+               name:           "pci-das64/m1/16",
+               device_id:      0x35,
+               ai_se_chans:    64,
+               ai_bits:        16,
+               ai_speed:       1000,
+               ao_nchan: 2,
+               ao_scan_speed:  10000,
+       },
+       {
+               name:           "pci-das64/m2/16",
+               device_id:      0x36,
+               ai_se_chans:    64,
+               ai_bits:        16,
+               ai_speed:       500,
+               ao_nchan: 2,
+               ao_scan_speed:  10000,
+       },
+       {
+               name:           "pci-das64/m3/16",
+               device_id:      0x37,
+               ai_se_chans:    64,
+               ai_bits:        16,
+               ai_speed:       333,
+               ao_nchan: 2,
+               ao_scan_speed:  10000,
+       },
+
+#if 0
+       {
+               name:           "pci-das6402/16/jr",
+               device_id:      0 // XXX,
+               ai_se_chans:    64,
+               ai_bits:        16,
+               ai_speed:       5000,
+               ao_nchan: 0,
+               ao_scan_speed:  10000,
+       },
+       {
+               name:           "pci-das64/m1/16/jr",
+               device_id:      0 // XXX,
+               ai_se_chans:    64,
+               ai_bits:        16,
+               ai_speed:       1000,
+               ao_nchan: 0,
+               ao_scan_speed:  10000,
+       },
+       {
+               name:           "pci-das64/m2/16/jr",
+               device_id:      0 // XXX,
+               ai_se_chans:    64,
+               ai_bits:        16,
+               ai_speed:       500,
+               ao_nchan: 0,
+               ao_scan_speed:  10000,
+       },
+       {
+               name:           "pci-das64/m3/16/jr",
+               device_id:      0 // XXX,
+               ai_se_chans:    64,
+               ai_bits:        16,
+               ai_speed:       333,
+               ao_nchan: 0,
+               ao_scan_speed:  10000,
+       },
+       {
+               name:           "pci-das64/m1/14",
+               device_id:      0,      // XXX
+               ai_se_chans:    64,
+               ai_bits:        14,
+               ai_speed:       1000,
+               ao_nchan: 2,
+               ao_scan_speed:  10000,
+       },
+       {
+               name:           "pci-das64/m2/14",
+               device_id:      0,      // XXX
+               ai_se_chans:    64,
+               ai_bits:        14,
+               ai_speed:       500,
+               ao_nchan: 2,
+               ao_scan_speed:  10000,
+       },
+       {
+               name:           "pci-das64/m3/14",
+               device_id:      0,      // XXX
+               ai_se_chans:    64,
+               ai_bits:        14,
+               ai_speed:       333,
+               ao_nchan: 2,
+               ao_scan_speed:  10000,
+       },
+#endif
+
 };
 // Number of boards in cb_pcidas_boards
 #define N_BOARDS       (sizeof(pcidas64_boards) / sizeof(pcidas64_board))
@@ -154,6 +298,8 @@ typedef struct
        unsigned int ao_divisor;
        volatile unsigned int ao_count; // number of analog output samples remaining
        unsigned int ao_value[2];       // remember what the analog outputs are set to, to allow readback
+       unsigned int hw_revision;       // stc chip hardware revision number
+       unsigned int do_bits;   // remember digital ouput levels
 } pcidas64_private;
 
 /*
@@ -180,16 +326,17 @@ comedi_driver driver_cb_pcidas={
 static int ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
 static int ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
 static int ao_readback_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
-static int ai_cmd(comedi_device *dev,comedi_subdevice *s);
-static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
-       comedi_cmd *cmd);
-static int ao_cmd(comedi_device *dev,comedi_subdevice *s);
-static int ao_inttrig(comedi_device *dev, comedi_subdevice *subdev, unsigned int trig_num);
-static int ao_cmdtest(comedi_device *dev,comedi_subdevice *s,
-       comedi_cmd *cmd);
-static void handle_interrupt(int irq, void *d, struct pt_regs *regs);
-static int ai_cancel(comedi_device *dev, comedi_subdevice *s);
-static int ao_cancel(comedi_device *dev, comedi_subdevice *s);
+//static int ai_cmd(comedi_device *dev,comedi_subdevice *s);
+//static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd);
+//static int ao_cmd(comedi_device *dev,comedi_subdevice *s);
+//static int ao_inttrig(comedi_device *dev, comedi_subdevice *subdev, unsigned int trig_num);
+//static int ao_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd);
+//static void handle_interrupt(int irq, void *d, struct pt_regs *regs);
+//static int ai_cancel(comedi_device *dev, comedi_subdevice *s);
+//static int ao_cancel(comedi_device *dev, comedi_subdevice *s);
+static int dio_callback(int dir, int port, int data, void *arg);
+static int di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+static int do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
 
 /*
  * A convenient macro that defines init_module() and cleanup_module(),
@@ -228,6 +375,10 @@ static int attach(comedi_device *dev, comedi_devconfig *it)
                // is it not a computer boards card?
                if(pcidev->vendor != PCI_VENDOR_CB)
                        continue;
+#ifdef PCIDAS64_DEBUG
+               printk(" found computer boards device id 0x%x on bus %i slot %i\n",
+                       pcidev->device, pcidev->bus->number, PCI_SLOT(pcidev->devfn));
+#endif
                // loop through cards supported by this driver
                for(index = 0; index < N_BOARDS; index++)
                {
@@ -248,8 +399,7 @@ static int attach(comedi_device *dev, comedi_devconfig *it)
                }
        }
 
-       printk("No supported ComputerBoards/MeasurementComputing card found on "
-               "requested position\n");
+       printk("No supported ComputerBoards/MeasurementComputing card found\n");
        return -EIO;
 
 found:
@@ -316,15 +466,6 @@ found:
        devpriv->dio_counter_phys_iobase = dio_counter_iobase;
 
 #endif
-#endif
-
-#ifdef PCIDAS64_DEBUG
-
-printk("plx9080 phys io addr 0x%lx\n", devpriv->plx9080_iobase);
-printk("main phys io addr 0x%lx\n", devpriv->main_iobase);
-printk("diocounter phys io addr 0x%lx\n", devpriv->dio_counter_iobase);
-printk("irq %i\n", dev->irq);
-
 #endif
 
        // remap, won't work with 2.0 kernels but who cares
@@ -332,33 +473,43 @@ printk("irq %i\n", dev->irq);
        devpriv->main_iobase = (unsigned long)ioremap(main_iobase, MAIN_IOSIZE);
        devpriv->dio_counter_iobase = (unsigned long)ioremap(dio_counter_iobase, DIO_COUNTER_IOSIZE);
 
+       devpriv->hw_revision = HW_REVISION(readw(devpriv->main_iobase + HW_STATUS_REG));
+
        // get irq
-       if(comedi_request_irq(pcidev->irq, handle_interrupt, SA_SHIRQ, "cb_pcidas64", dev ))
+/*     if(comedi_request_irq(pcidev->irq, handle_interrupt, SA_SHIRQ, "cb_pcidas64", dev ))
        {
                printk(" unable to allocate irq %d\n", pcidev->irq);
                return -EINVAL;
        }
        dev->irq = pcidev->irq;
-
+*/
 #ifdef PCIDAS64_DEBUG
 
-printk("plx9080 virt io addr 0x%lx\n", devpriv->plx9080_iobase);
-printk("main virt io addr 0x%lx\n", devpriv->main_iobase);
-printk("diocounter virt io addr 0x%lx\n", devpriv->dio_counter_iobase);
-printk("irq %i\n", dev->irq);
+printk(" plx9080 phys io addr 0x%lx\n", devpriv->plx9080_phys_iobase);
+printk(" main phys io addr 0x%lx\n", devpriv->main_phys_iobase);
+printk(" diocounter phys io addr 0x%lx\n", devpriv->dio_counter_phys_iobase);
+printk(" irq %i\n", dev->irq);
+
+printk(" plx9080 virt io addr 0x%lx\n", devpriv->plx9080_iobase);
+printk(" main virt io addr 0x%lx\n", devpriv->main_iobase);
+printk(" diocounter virt io addr 0x%lx\n", devpriv->dio_counter_iobase);
+printk(" irq %i\n", dev->irq);
+
+printk(" stc hardware revision %i\n", devpriv->hw_revision);
 
 #endif
 
+
 /*
  * Allocate the subdevice structures.
  */
-       dev->n_subdevices = 5;
+       dev->n_subdevices = 7;
        if(alloc_subdevices(dev)<0)
                return -ENOMEM;
 
        s = dev->subdevices + 0;
        /* analog input subdevice */
-       dev->read_subdev = s;
+//     dev->read_subdev = s;
        s->type = COMEDI_SUBD_AI;
        s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF;
        /* WARNING: Number of inputs in differential mode is ignored */
@@ -366,39 +517,63 @@ printk("irq %i\n", dev->irq);
        s->len_chanlist = 8092;
        s->maxdata = (1 << thisboard->ai_bits) - 1;
        s->range_table = &ai_ranges;
-//     s->insn_read = ai_rinsn;
+       s->insn_read = ai_rinsn;
 //     s->do_cmd = ai_cmd;
 //     s->do_cmdtest = ai_cmdtest;
 //     s->cancel = ai_cancel;
 
        /* analog output subdevice */
        s = dev->subdevices + 1;
-       dev->write_subdev = s;
-       s->type = COMEDI_SUBD_AO;
-       s->subdev_flags = SDF_READABLE | SDF_WRITEABLE | SDF_GROUND;
-       s->n_chan = thisboard->ao_nchan;
-       // analog out resolution is the same as analog input resolution, so use ai_bits
-       s->maxdata = (1 << thisboard->ai_bits) - 1;
-       s->range_table = &ao_ranges;
-//     s->insn_read = ao_readback_insn;
-//     s->insn_write = ao_winsn;
-//     s->do_cmdtest = ao_cmdtest;
-//     s->do_cmd = ao_cmd;
-       s->len_chanlist = thisboard->ao_nchan;
-//     s->cancel = ao_cancel;
+       if(thisboard->ao_nchan)
+       {
+       //      dev->write_subdev = s;
+               s->type = COMEDI_SUBD_AO;
+               s->subdev_flags = SDF_READABLE | SDF_WRITEABLE | SDF_GROUND;
+               s->n_chan = thisboard->ao_nchan;
+               // analog out resolution is the same as analog input resolution, so use ai_bits
+               s->maxdata = (1 << thisboard->ai_bits) - 1;
+               s->range_table = &ao_ranges;
+               s->insn_read = ao_readback_insn;
+               s->insn_write = ao_winsn;
+       //      s->do_cmdtest = ao_cmdtest;
+       //      s->do_cmd = ao_cmd;
+       //      s->len_chanlist = thisboard->ao_nchan;
+       //      s->cancel = ao_cancel;
+       } else
+       {
+               s->type = COMEDI_SUBD_UNUSED;
+       }
 
        // digital input
        s = dev->subdevices + 2;
-       s->type = COMEDI_SUBD_UNUSED;
+       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 = di_rbits;
 
        // digital output
        s = dev->subdevices + 3;
-       s->type = COMEDI_SUBD_UNUSED;
+       s->type=COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITEABLE | SDF_READABLE;
+       s->n_chan = 4;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits = do_wbits;
 
        /* 8255 */
        s = dev->subdevices + 4;
+       subdev_8255_init(dev, s, dio_callback,
+               (void*) (devpriv->dio_counter_iobase + DIO_8255_OFFSET));
+
+       // user counter subd XXX
+       s = dev->subdevices + 5;
+       s->type = COMEDI_SUBD_UNUSED;
+
+       // calibration subd XXX
+       s = dev->subdevices + 6;
        s->type = COMEDI_SUBD_UNUSED;
-//     subdev_8255_init(dev, s, );
 
        return 0;
 }
@@ -435,65 +610,144 @@ static int detach(comedi_device *dev)
        }
        if(dev->irq)
                comedi_free_irq(dev->irq, dev);
-//     if(dev->subdevices)
-//             subdev_8255_cleanup(dev,dev->subdevices + 4);
+       if(dev->subdevices)
+               subdev_8255_cleanup(dev,dev->subdevices + 4);
 
        return 0;
 }
 
 static int ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
 {
-       return -1;
+       unsigned int bits, n, i;
+       const int timeout = 1000;
+
+       // disable card's interrupt sources
+       writew(0, devpriv->main_iobase + INTR_ENABLE_REG);
+
+       // use internal queue
+       writew(HW_CONFIG_DUMMY_BITS, devpriv->main_iobase + HW_CONFIG_REG);
+
+       /* disable pacing, triggering, etc */
+       writew(ADC_ENABLE_BIT, devpriv->main_iobase + ADC_CONTROL0_REG);
+       writew(0, devpriv->main_iobase + ADC_CONTROL1_REG);
+
+       // load internal queue
+       bits = 0;
+       // set channel
+       bits |= CHAN_BITS(CR_CHAN(insn->chanspec));
+       // set gain
+       bits |= GAIN_BITS(CR_RANGE(insn->chanspec));
+       // set unipolar / bipolar
+       bits |= UNIP_BIT(CR_RANGE(insn->chanspec));
+       // set single-ended / differential
+       if(CR_AREF(insn->chanspec) != AREF_DIFF)
+               bits |= SE_BIT;
+       // set stop channel
+       writew(CHAN_BITS(CR_CHAN(insn->chanspec)), devpriv->main_iobase + ADC_QUEUE_HIGH_REG);
+       // set start channel, and rest of settings
+       writew(bits, devpriv->main_iobase + ADC_QUEUE_LOAD_REG);
+
+       // clear adc buffer
+       writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG);
+
+       for(n = 0; n < insn->n; n++)
+       {
+               /* trigger conversion */
+               writew(0, devpriv->main_iobase + ADC_CONVERT_REG);
+
+               // wait for data
+               for(i = 0; i < timeout; i++)
+               {
+                       if(!(readw(devpriv->main_iobase + HW_STATUS_REG) & ADC_BUSY_BIT))
+                               break;
+               }
+               if(i == timeout)
+               {
+                       comedi_error(dev, " analog input read insn timed out");
+                       return -ETIME;
+               }
+               data[n] = readw(devpriv->main_iobase + PIPE1_READ_REG);
+       }
+
+       return n;
 }
 
-static int ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+static int ao_winsn(comedi_device *dev, comedi_subdevice *s,
+       comedi_insn *insn, lsampl_t *data)
 {
-       return -1;
+       int chan = CR_CHAN(insn->chanspec);
+       int range = CR_RANGE(insn->chanspec);
+       unsigned int bits;
+
+       // do some initializing
+       writew(DAC_ENABLE_BIT, devpriv->main_iobase + DAC_CONTROL0_REG);
+
+       // set range
+       bits = DAC_OUTPUT_ENABLE_BIT;
+       bits |= DAC_RANGE_BITS(chan, range);
+       writew(bits, devpriv->main_iobase + DAC_CONTROL1_REG);
+
+       // clear buffer
+       writew(0, devpriv->main_iobase + DAC_BUFFER_CLEAR_REG);
+
+       // write to channel
+       writew(data[0], devpriv->main_iobase + DAC_CONVERT_REG(chan));
+
+       // remember output value
+       devpriv->ao_value[chan] = data[0];
+
+       return 1;
 }
 
 static int ao_readback_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
 {
-       return -1;
-}
+       data[0] = devpriv->ao_value[CR_CHAN(insn->chanspec)];
 
-static int ai_cmd(comedi_device *dev,comedi_subdevice *s)
-{
-       return -1;
+       return 1;
 }
 
-static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd)
+static int dio_callback(int dir, int port, int data, void *arg)
 {
-       return -1;
-}
+       unsigned long iobase = (int)arg;
 
-static int ao_cmd(comedi_device *dev,comedi_subdevice *s)
-{
-       return -1;
+       if(dir)
+       {
+               writeb(data, iobase + port);
+               return 0;
+       }else
+       {
+               return readb(iobase + port);
+       }
 }
 
-static int ao_inttrig(comedi_device *dev, comedi_subdevice *subdev, unsigned int trig_num)
+static int di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
 {
-       return -1;
-}
+       lsampl_t bits;
 
-static int ao_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd)
-{
-       return -1;
-}
+       bits = readb(devpriv->dio_counter_iobase + DI_REG);
+       bits &= 0xf;
+       data[1] = bits;
+       data[0] = 0;
 
-static void handle_interrupt(int irq, void *d, struct pt_regs *regs)
-{
+       return 2;
 }
 
-static int ai_cancel(comedi_device *dev, comedi_subdevice *s)
+static int do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
 {
-       return -1;
-}
+       lsampl_t wbits;
 
-static int ao_cancel(comedi_device *dev, comedi_subdevice *s)
-{
-       return -1;
-}
+       data[0] &= 0xf;
+       wbits = devpriv->do_bits;
+       // zero bits we are going to change
+       wbits &= ~data[0];
+       // set new bits
+       wbits |= data[0] & data[1];
+       devpriv->do_bits = wbits;
+
+       writeb(devpriv->do_bits, devpriv->dio_counter_iobase + DO_REG);
 
+       data[1] = wbits;
 
+       return 2;
+}