skeleton of a new driver, doesn't do anything yet.
authorFrank Mori Hess <fmhess@speakeasy.net>
Sat, 25 Aug 2001 23:49:23 +0000 (23:49 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Sat, 25 Aug 2001 23:49:23 +0000 (23:49 +0000)
comedi/drivers/cb_pcidas64.c [new file with mode: 0644]

diff --git a/comedi/drivers/cb_pcidas64.c b/comedi/drivers/cb_pcidas64.c
new file mode 100644 (file)
index 0000000..95648f0
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+    cb_pcidas64.c
+    This is a driver for the ComputerBoards/MeasurementComputing PCI-DAS
+    64xxx cards.
+
+    Options:
+    [0] - PCI bus number
+    [1] - PCI slot number
+
+    Copyright (C) 2001 Frank Mori Hess <fmhess@uiuc.edu>
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-8 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.
+
+************************************************************************
+
+STATUS:
+       quite experimental and unfinished
+
+TODO:
+       just about everything
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.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/pci.h>
+#include <asm/io.h>
+#include <linux/comedidev.h>
+#include "8253.h"
+#include "8255.h"
+
+#define PCIDAS64_DEBUG // enable debugging code
+//#undef PCIDAS64_DEBUG        // disable debugging code
+
+// PCI vendor number of ComputerBoards/MeasurementComputing
+#define PCI_VENDOR_CB  0x1307
+#define TIMER_BASE 25  // 40MHz master clock
+
+/* PCI-DAS64xxx base addresses */
+
+// indices of base address regions
+#define PLX9080_BADRINDEX 0
+#define MAIN_BADRINDEX 2
+#define DIO_COUNTER_BADRINDEX 3
+
+#define PLX9080_IOSIZE 0xec
+#define MAIN_IOSIZE 0x302
+#define DIO_COUNTER_IOSIZE 0x29
+
+// plx pci9080 configuration registers
+
+// 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 =
+{
+       8,
+       {
+               BIP_RANGE(10),
+               BIP_RANGE(5),
+               BIP_RANGE(2.5),
+               BIP_RANGE(1.25),
+               UNI_RANGE(10),
+               UNI_RANGE(5),
+               UNI_RANGE(2.5),
+               UNI_RANGE(1.25)
+       }
+};
+
+// analog output ranges
+static comedi_lrange ao_ranges =
+{
+       4,
+       {
+               BIP_RANGE(5),
+               BIP_RANGE(10),
+               UNI_RANGE(5),
+               UNI_RANGE(10),
+       }
+};
+
+typedef struct pcidas64_board_struct
+{
+       char *name;
+       int device_id;  // pci device id
+       int ai_se_chans;        // number of ai inputs in single-ended mode
+       int ai_bits;    // analog input resolution
+       int ai_speed;   // fastest conversion period in ns
+       int ao_nchan;   // number of analog out channels
+       int ao_scan_speed;      // analog output speed (for a scan, not conversion)
+} pcidas64_board;
+
+static pcidas64_board pcidas64_boards[] =
+{
+       {
+               name:           "pci-das6402/16",
+               device_id:      0x1d,
+               ai_se_chans:    64,
+               ai_bits:        16,
+               ai_speed:       5000,
+               ao_nchan: 2,
+               ao_scan_speed:  10000,
+       },
+// XXX supports more boards...
+};
+// Number of boards in cb_pcidas_boards
+#define N_BOARDS       (sizeof(pcidas64_boards) / sizeof(pcidas64_board))
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((pcidas64_board *)dev->board_ptr)
+
+/* this structure is for data unique to this hardware driver.  If
+   several hardware drivers keep similar information in this structure,
+   feel free to suggest moving the variable to the comedi_device struct.  */
+typedef struct
+{
+       // base addresses (physical)
+       unsigned long plx9080_phys_iobase;
+       unsigned long main_phys_iobase;
+       unsigned long dio_counter_phys_iobase;
+       // base addresses (ioremapped)
+       unsigned long plx9080_iobase;
+       unsigned long main_iobase;
+       unsigned long dio_counter_iobase;
+       // divisor of master clock for analog input pacing
+       unsigned int ai_divisor;
+       volatile unsigned int ai_count; // number of analog input samples remaining
+       // divisors of master clock for analog output pacing
+       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
+} pcidas64_private;
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((pcidas64_private *)dev->private)
+
+/*
+ * The 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 attach(comedi_device *dev,comedi_devconfig *it);
+static int detach(comedi_device *dev);
+comedi_driver driver_cb_pcidas={
+       driver_name:    "cb_pcidas64",
+       module:         THIS_MODULE,
+       attach:         attach,
+       detach:         detach,
+};
+
+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);
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver_cb_pcidas);
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board.
+ */
+static int attach(comedi_device *dev, comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       struct pci_dev* pcidev;
+       int index;
+       // base addresses
+       unsigned long plx9080_iobase;
+       unsigned long main_iobase;
+       unsigned long dio_counter_iobase;
+
+       printk("comedi%d: cb_pcidas64\n",dev->minor);
+
+/*
+ * Allocate the private structure area.
+ */
+       if(alloc_private(dev,sizeof(pcidas64_private)) < 0)
+               return -ENOMEM;
+
+/*
+ * Probe the device to determine what device in the series it is.
+ */
+
+       pci_for_each_dev(pcidev)
+       {
+               // is it not a computer boards card?
+               if(pcidev->vendor != PCI_VENDOR_CB)
+                       continue;
+               // loop through cards supported by this driver
+               for(index = 0; index < N_BOARDS; index++)
+               {
+                       if(pcidas64_boards[index].device_id != pcidev->device)
+                               continue;
+                       // was a particular bus/slot requested?
+                       if(it->options[0] || it->options[1])
+                       {
+                               // are we on the wrong bus/slot?
+                               if(pcidev->bus->number != it->options[0] ||
+                                  PCI_SLOT(pcidev->devfn) != it->options[1])
+                               {
+                                       continue;
+                               }
+                       }
+                       dev->board_ptr = pcidas64_boards + index;
+                       goto found;
+               }
+       }
+
+       printk("No supported ComputerBoards/MeasurementComputing card found on "
+               "requested position\n");
+       return -EIO;
+
+found:
+
+       printk("Found %s on bus %i, slot %i\n", pcidas64_boards[index].name,
+               pcidev->bus->number, PCI_SLOT(pcidev->devfn));
+
+       //Initialize dev->board_name
+       dev->board_name = thisboard->name;
+
+       /* Initialize devpriv->control_status and devpriv->adc_fifo to point to
+        * their base address.
+        */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+       plx9080_iobase =
+               pcidev->base_address[PLX9080_BADRINDEX] &
+               PCI_BASE_ADDRESS_MEM_MASK;
+       main_iobase =
+               pcidev->base_address[MAIN_BADRINDEX] &
+               PCI_BASE_ADDRESS_MEM_MASK;
+       dio_counter_iobase =
+               pcidev->base_address[DIO_COUNTER_BADRINDEX] &
+               PCI_BASE_ADDRESS_MEM_MASK;
+#else
+       if(pci_enable_device(pcidev))
+               return -EIO;
+       pci_set_master(pcidev);
+       plx9080_iobase =
+               pcidev->resource[PLX9080_BADRINDEX].start &
+               PCI_BASE_ADDRESS_MEM_MASK;
+       main_iobase =
+               pcidev->resource[MAIN_BADRINDEX].start &
+               PCI_BASE_ADDRESS_MEM_MASK;
+       dio_counter_iobase =
+               pcidev->resource[DIO_COUNTER_BADRINDEX].start &
+               PCI_BASE_ADDRESS_MEM_MASK;
+
+#if KERNEL_VERSION_CODE >= KERNEL_VERSION(2,3,17)
+
+       if(check_mem_region(plx9080_iobase, PLX9080_IOSIZE))
+       {
+               /* Couldn't allocate io space */
+               printk(KERN_WARNING "couldn't allocate IO space\n");
+               return -EIO;
+       }
+       if(check_mem_region(main_iobase, MAIN_IOSIZE))
+       {
+               /* Couldn't allocate io space */
+               printk(KERN_WARNING "couldn't allocate IO space\n");
+               return -EIO;
+       }
+       if(check_mem_region(dio_counter_iobase, DIO_COUNTER_IOSIZE))
+       {
+               /* Couldn't allocate io space */
+               printk(KERN_WARNING "couldn't allocate IO space\n");
+               return -EIO;
+       }
+
+       request_mem_region(plx_iobase, PLX9080_IOSIZE, "cb_pcidas64");
+       devpriv->plx9080_phys_iobase = dio_counter_iobase;
+       request_mem_region(main_iobase, MAIN_IOSIZE, "cb_pcidas64");
+       devpriv->main_phys_iobase = dio_counter_iobase;
+       request_mem_region(dio_counter_iobase, DIO_COUNTER_IOSIZE, "cb_pcidas64");
+       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
+       devpriv->plx9080_iobase = (unsigned long)ioremap(plx9080_iobase, PLX9080_IOSIZE);
+       devpriv->main_iobase = (unsigned long)ioremap(main_iobase, MAIN_IOSIZE);
+       devpriv->dio_counter_iobase = (unsigned long)ioremap(dio_counter_iobase, DIO_COUNTER_IOSIZE);
+
+       // get irq
+       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);
+
+#endif
+
+/*
+ * Allocate the subdevice structures.
+ */
+       dev->n_subdevices = 5;
+       if(alloc_subdevices(dev)<0)
+               return -ENOMEM;
+
+       s = dev->subdevices + 0;
+       /* analog input subdevice */
+       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 */
+       s->n_chan = thisboard->ai_se_chans;
+       s->len_chanlist = 8092;
+       s->maxdata = (1 << thisboard->ai_bits) - 1;
+       s->range_table = &ai_ranges;
+//     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;
+
+       // digital input
+       s = dev->subdevices + 2;
+       s->type = COMEDI_SUBD_UNUSED;
+
+       // digital output
+       s = dev->subdevices + 3;
+       s->type = COMEDI_SUBD_UNUSED;
+
+       /* 8255 */
+       s = dev->subdevices + 4;
+       s->type = COMEDI_SUBD_UNUSED;
+//     subdev_8255_init(dev, s, );
+
+       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 detach(comedi_device *dev)
+{
+       printk("comedi%d: cb_pcidas: remove\n",dev->minor);
+
+       if(devpriv)
+       {
+               if(devpriv->plx9080_iobase)
+                       iounmap((void*)devpriv->plx9080_iobase);
+               if(devpriv->main_iobase)
+                       iounmap((void*)devpriv->main_iobase);
+               if(devpriv->dio_counter_iobase)
+                       iounmap((void*)devpriv->dio_counter_iobase);
+#if KERNEL_VERSION_CODE >= KERNEL_VERSION(2,3,17)
+               if(devpriv->plx9080_phys_iobase)
+                       release_mem_region(devpriv->plx9080_iobase, PLX9080_IOSIZE);
+               if(devpriv->main_iobase)
+                       release_mem_region(devpriv->main_phys_iobase, MAIN_IOSIZE);
+               if(devpriv->dio_counter_iobase)
+                       release_mem_region(devpriv->dio_counter_phys_iobase, DIO_COUNTER_IOSIZE);
+#endif
+       }
+       if(dev->irq)
+               comedi_free_irq(dev->irq, dev);
+//     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;
+}
+
+static int ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       return -1;
+}
+
+static int ao_readback_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       return -1;
+}
+
+static int ai_cmd(comedi_device *dev,comedi_subdevice *s)
+{
+       return -1;
+}
+
+static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd)
+{
+       return -1;
+}
+
+static int ao_cmd(comedi_device *dev,comedi_subdevice *s)
+{
+       return -1;
+}
+
+static int ao_inttrig(comedi_device *dev, comedi_subdevice *subdev, unsigned int trig_num)
+{
+       return -1;
+}
+
+static int ao_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd)
+{
+       return -1;
+}
+
+static void handle_interrupt(int irq, void *d, struct pt_regs *regs)
+{
+}
+
+static int ai_cancel(comedi_device *dev, comedi_subdevice *s)
+{
+       return -1;
+}
+
+static int ao_cancel(comedi_device *dev, comedi_subdevice *s)
+{
+       return -1;
+}
+
+
+