--- /dev/null
+/*
+ comedi/drivers/cb_pcidio.c
+ A Comedi driver for PCI-DIO24H & PCI-DIO48H of ComputerBoards (currently MeasurementComputing)
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 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: cb_pcidio.o
+Description: ComputerBoards' DIO boards with PCI interface
+Devices: [ComputerBoards (currently MeasurementComputing)]
+ PCI-DIO24H, PCI-DIO48H
+Author: Yoshiya Matsuzaka
+Updated: Wed, 25 Jul 2007 15:15:41 +0900
+Status: experimental
+
+This driver has been modified from skel.c of comedi-0.7.70.
+
+Configuration Options:
+ none
+*/
+
+/*
+ * The previous block comment is used to automatically generate
+ * documentation in Comedi and Comedilib. The fields:
+ *
+ * Driver: the name of the driver
+ * Description: a short phrase describing the driver. Don't list boards.
+ * Devices: a full list of the boards that attempt to be supported by
+ * the driver. Format is "(manufacturer) board name [comedi name]",
+ * where comedi_name is the name that is used to configure the board.
+ * See the comment near board_name: in the comedi_driver structure
+ * below. If (manufacturer) or [comedi name] is missing, the previous
+ * value is used.
+ * Author: you
+ * Updated: date when the _documentation_ was last updated. Use 'date -R'
+ * to get a value for this.
+ * Status: a one-word description of the status. Valid values are:
+ * works - driver works correctly on most boards supported, and
+ * passes comedi_test.
+ * unknown - unknown. Usually put there by ds.
+ * experimental - may not work in any particular release. Author
+ * probably wants assistance testing it.
+ * bitrotten - driver has not been update in a long time, probably
+ * doesn't work, and probably is missing support for significant
+ * Comedi interface features.
+ * untested - author probably wrote it "blind", and is believed to
+ * work, but no confirmation.
+ *
+ * These headers should be followed by a blank line, and any comments
+ * you wish to say about the driver. The comment area is the place
+ * to put any known bugs, limitations, unsupported features, supported
+ * command triggers, whether or not commands are supported on particular
+ * subdevices, etc.
+ *
+ * Somewhere in the comment should be information about configuration
+ * options that are used with comedi_config.
+ */
+
+
+
+/*------------------------------ HEADER FILES ---------------------------------*/
+#include <linux/comedidev.h>
+#include <linux/pci.h> /* for PCI devices */
+
+
+
+/*-------------------------- MACROS and DATATYPES -----------------------------*/
+#define PCI_VENDOR_ID_CB 0x1307
+
+/*
+ * 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 pcidio_board_struct{
+ char *name; // anme of the board
+ int n_8255; // number of 8255 chips on board
+
+ // indices of base address regions
+ int pcicontroler_badrindex;
+ int dioregs_badrindex;
+} pcidio_board;
+
+
+
+static pcidio_board pcidio_boards[] = {
+ {
+ name: "pci-dio24h",
+ n_8255: 1,
+ pcicontroler_badrindex: 1,
+ dioregs_badrindex: 2,
+ },
+ {
+ name: "pci-dio48h",
+ n_8255: 2,
+ pcicontroler_badrindex: 0,
+ dioregs_badrindex: 1,
+ },
+};
+
+
+
+/* This is used by modprobe to translate PCI IDs to drivers. Should
+ * only be used for PCI and ISA-PnP devices */
+/* Please add your PCI vendor ID to comedidev.h, and it will be forwarded
+ * upstream. */
+static struct pci_device_id pcidio_pci_table[] __devinitdata = {
+ { PCI_VENDOR_ID_CB, 0x0014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { PCI_VENDOR_ID_CB, 0x000b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, pcidio_pci_table);
+
+
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((pcidio_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{
+ int data; // curently unused
+
+ /* would be useful for a PCI device */
+ struct pci_dev *pci_dev;
+
+ /* used for DO readback, curently unused */
+ lsampl_t do_readback[4]; /* up to 4 lsampl_t suffice to hold 96 bits for PCI-DIO96 */
+
+ unsigned long dio_reg_base; // address of port A of the first 8255 chip on board
+} pcidio_private;
+
+
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((pcidio_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 pcidio_attach(comedi_device *dev,comedi_devconfig *it);
+static int pcidio_detach(comedi_device *dev);
+static comedi_driver driver_cb_pcidio={
+ driver_name: "cb_pcidio",
+ module: THIS_MODULE,
+ attach: pcidio_attach,
+ detach: pcidio_detach,
+/* It is not necessary to implement the following members if you are
+ * writing a driver for a ISA PnP or PCI card */
+ /* Most drivers will support multiple types of boards by
+ * having an array of board structures. These were defined
+ * in pcidio_boards[] above. Note that the element 'name'
+ * was first in the structure -- Comedi uses this fact to
+ * extract the name of the board without knowing any details
+ * about the structure except for its length.
+ * When a device is attached (by comedi_config), the name
+ * of the device is given to Comedi, and Comedi tries to
+ * match it by going through the list of board names. If
+ * there is a match, the address of the pointer is put
+ * into dev->board_ptr and driver->attach() is called.
+ *
+ * Note that these are not necessary if you can determine
+ * the type of board in software. ISA PnP, PCI, and PCMCIA
+ * devices are such boards.
+ */
+// The following fields should NOT be initialized if you are dealing with PCI devices
+// board_name: pcidio_boards,
+// offset: sizeof(pcidio_board),
+// num_names: sizeof(pcidio_boards) / sizeof(pcidio_board),
+};
+
+
+
+/*------------------------------- FUNCTIONS -----------------------------------*/
+
+// The 2 functions below are currently unsed
+static int pcidio_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+static int pcidio_dio_insn_config(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+
+
+
+/*
+ * 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 pcidio_attach(comedi_device *dev, comedi_devconfig *it)
+{
+struct pci_dev* pcidev = NULL;
+int index;
+
+ printk("comedi%d: pcidio: \n", dev->minor);
+/*
+ * If you can probe the device to determine what device in a series
+ * it is, this is the place to do it. Otherwise, dev->board_ptr
+ * should already be initialized.
+ */
+/*
+ * Probe the device to determine what device in the series it is.
+ */
+
+ for(pcidev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, NULL); pcidev != NULL ;
+ pcidev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pcidev))
+ {
+ // is it not a computer boards card?
+ if(pcidev->vendor != PCI_VENDOR_ID_CB)
+ continue;
+ // loop through cards supported by this driver
+ for(index = 0; index < sizeof pcidio_boards / sizeof(pcidio_board); index++)
+ {
+ if(pcidio_pci_table[index].device != 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 = pcidio_boards + index;
+ goto found;
+ }
+ }
+
+ printk("No supported ComputerBoards/MeasurementComputing card found on "
+ "requested position\n");
+ return -EIO;
+
+found:
+
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if(alloc_private(dev, sizeof(pcidio_private)) < 0)
+ return -ENOMEM;
+
+ devpriv->pci_dev = pcidev;
+ printk("Found %s on bus %i, slot %i\n", pcidio_boards[index].name,
+ devpriv->pci_dev->bus->number, PCI_SLOT(devpriv->pci_dev->devfn));
+ devpriv->dio_reg_base
+ = pci_resource_start(devpriv->pci_dev, pcidio_boards[index].dioregs_badrindex);
+
+/*
+ * Initialize dev->board_name. Note that we can use the "thisboard"
+ * macro now, since we just initialized it in the last line.
+ */
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if(alloc_subdevices(dev, thisboard->n_8255) < 0)
+ return -ENOMEM;
+
+ int i;
+ for(i = 0; i < thisboard->n_8255; i++)
+ {
+ subdev_8255_init(dev, dev->subdevices + i,
+ NULL, devpriv->dio_reg_base + i * 4);
+ printk(" subdev %d: base = 0x%lx\n", i, devpriv->dio_reg_base + i * 4);
+ }
+
+ printk("attached\n");
+ return 1;
+}
+
+
+
+/*
+ * _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 pcidio_detach(comedi_device *dev)
+{
+ printk("comedi%d: skel: remove\n",dev->minor);
+ return 0;
+}
+
+
+
+/* DIO devices are slightly special. Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels. The
+ * comedi core can convert between insn_bits and insn_read/write */
+static int pcidio_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ if(insn->n!=2)return -EINVAL;
+
+ /* The insn data is a mask in data[0] and the new data
+ * in data[1], each channel cooresponding to a bit. */
+ if(data[0]){
+ s->state &= ~data[0];
+ s->state |= data[0]&data[1];
+ /* Write out the new digital output lines */
+ //outw(s->state,dev->iobase + SKEL_DIO);
+ }
+
+ /* on return, data[1] contains the value of the digital
+ * input and output lines. */
+ //data[1]=inw(dev->iobase + SKEL_DIO);
+ /* or we could just return the software copy of the output values if
+ * it was a purely digital output subdevice */
+ //data[1]=s->state;
+
+ return 2;
+}
+
+static int pcidio_dio_insn_config(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ int chan=CR_CHAN(insn->chanspec);
+
+ /* 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. */
+ switch(data[0])
+ {
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= 1<<chan;
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~(1<<chan);
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] = (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ //outw(s->io_bits,dev->iobase + SKEL_DIO_CONFIG);
+
+ return insn->n;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver_cb_pcidio);
+