]> rtime.felk.cvut.cz Git - mf6xx.git/commitdiff
Simplified Poulains Comedi driver. Not tested. Not compiled.
authorRostislav Lisovy <lisovy@gmail.com>
Sun, 3 Apr 2011 14:24:25 +0000 (16:24 +0200)
committerRostislav Lisovy <lisovy@gmail.com>
Sun, 3 Apr 2011 14:24:25 +0000 (16:24 +0200)
src/comedi/simple_driver/kernel/mf624.c [new file with mode: 0755]

diff --git a/src/comedi/simple_driver/kernel/mf624.c b/src/comedi/simple_driver/kernel/mf624.c
new file mode 100755 (executable)
index 0000000..34d886b
--- /dev/null
@@ -0,0 +1,571 @@
+/*
+ * comedi/drivers/mf624.c
+ * Code for a Humusoft MF624 driver
+ *
+ * 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: mf624.o
+Description: A comedi driver for Humusoft MF624 PCI Card
+Devices: Humusoft MF624 Multifunction I/O Card
+Author: Jean-Matthieu Bourgeot, Francois Poulain, (Minor changes -- Rostislav Lisovy)
+Updated: Wed, 19 Sep 2007 14:29:43 +0200
+Status: experimental
+
+This driver is a driver for the Humusoft MF624 PCI Card.
+
+It has :
+ * 8 channel 14 bits ADC,
+ * 8 channel 14 bits DAC,
+ * 8 digital inputs,
+ * 8 digital outputs,
+ * 4 quadrature encoder input,
+ * 5 timers/counter.
+
+Status:
+A/D Converter                  : Supported on subdevice 0, 
+D/A Converter                  : Supported on subdevice 1,
+Digital Inputs                 : Supported on subdevice 2,
+Digital Outputs                        : Supported on subdevice 3,
+Counters/Timers                        : Not yet supported.
+Quadrature Encoder Inputs      : Not yet supported
+IRQ                            : Not yet supported.
+
+Remarks:
+Simultaneous D/A update not yet supported.
+
+Configuration Options:
+none
+*/
+#include <linux/comedidev.h>
+#include <linux/pci.h>
+
+#define EXTDEBUG 
+#define MF624_EXIT_FAILURE     -1
+
+#define PCI_VENDOR_ID_MF624 0x186c
+#define PCI_DEVICE_ID_MF624 0x0624
+
+/* Subdevice numbers */
+#define MF624_SUBDEV_COUNT     4 /* Nr of subdevices */
+#define MF624_DO_SUBDEV                3
+#define MF624_DI_SUBDEV                2
+#define MF624_AO_SUBDEV                1
+#define MF624_AI_SUBDEV                0
+
+
+/* BADR0 Memory Map */
+#define MF624_GPIOC            0x54
+#define MF624_INTCSR           0x4C
+
+/* BADR1 Memory Map */
+#define MF624_ADCTRL           0x00
+#define MF624_ADDATA           0x00
+#define MF624_DIN              0x10
+#define MF624_DOUT             0x10
+#define MF624_ADSTART          0x20
+#define MF624_DA0              0x20
+
+/* BADR2 Memory Map */
+#define MF624_CTRXMODE         0x00
+#define MF624_CTRXSTATUS       0x00
+#define MF624_CTRXCTRL         0x60
+#define MF624_CTRXA            0x04
+#define MF624_CTRXDATA         0x04
+#define MF624_CTRXB            0x08
+#define MF624_IRCCTRL          0x6C
+#define MF624_IRCSTATUS                0x6C
+#define MF624_ENC_READ         0x70
+
+
+/* MASK */
+#define MF624_GPIOC_EOLC       (1 << 17) /* End of last A/D conversion */
+#define MF624_GPIOC_LDAC       (1 << 23) /* Enable Load D/A converter */
+#define MF624_GPIOC_DACEN      (1 << 26) /* Enable D/A outputs */
+
+
+typedef struct mf624_board_struct{
+       char *name;
+       unsigned short device_id;       
+       unsigned int ai_chans;
+       unsigned int ai_bits;
+       unsigned int ao_chans;
+       unsigned int ao_bits;
+       unsigned int di_chans;
+       unsigned int do_chans;
+} mf624_board;
+
+
+static const mf624_board mf624_boards[] = {
+       {
+               name:           "mf624",        /* device name          */
+               device_id:      PCI_DEVICE_ID_MF624,    /* PCI dev ID   */
+               ai_chans:       8,              /* Num of ADC channels  */
+               ai_bits:        14,             /* Num of ADC bits      */
+               ao_chans:       8,              /* Num of DAC channels  */
+               ao_bits:        14,             /* Num of DAC bits      */
+               di_chans:       8,              /* Num of digital in    */
+               do_chans:       8,              /* Num of digital out   */
+       },
+};
+
+/* Number of boards */
+#define N_BOARDS       (sizeof(mf624_boards) / sizeof(mf624_board))
+
+
+static struct pci_device_id mf624_pci_table[] __devinitdata = {
+       { PCI_VENDOR_ID_MF624, PCI_DEVICE_ID_MF624, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { 0 }
+};
+MODULE_DEVICE_TABLE(pci, mf624_pci_table);
+
+
+/* Private data structure */
+typedef struct{
+       int data;
+
+       /* would be useful for a PCI device */
+       struct pci_dev *pci_dev;
+
+       /* base addresses */
+       //resource_size_t iobase;
+
+       unsigned long BADR0;    
+       unsigned long BADR1;
+       unsigned long BADR2;    
+
+       void* IO_BADR0; 
+       void* IO_BADR1;
+       void* IO_BADR2; 
+
+       /* Used for AO readback */
+       lsampl_t ao_readback[8];
+} mf624_private;
+
+#define devpriv        ((mf624_private *) dev->private)
+
+
+/* Attach/detach functions declaration */
+/*static int mf624_attach(struct comedi_device *dev, struct comedi_devconfig *it);
+static int mf624_detach(struct comedi_device *dev);
+*/
+
+/* Analog functions operation to be attached */
+/*static int mf624_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 
+       struct comedi_insn *insn, lsampl_t *data);
+static int mf624_ai_cfg(struct comedi_device *dev, struct comedi_subdevice *s, 
+       struct comedi_insn *insn, lsampl_t *data);
+static int mf624_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 
+       struct comedi_insn *insn, lsampl_t *data);
+static int mf624_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 
+       struct comedi_insn *insn, lsampl_t *data);
+static int mf624_ao_cfg(struct comedi_device *dev, struct comedi_subdevice *s, 
+       struct comedi_insn *insn, lsampl_t *data);
+*/
+
+/* Digital functions operation to be attached */
+/*static int mf624_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, 
+       struct comedi_insn *insn, lsampl_t *data);
+static int mf624_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, 
+       struct comedi_insn *insn, lsampl_t *data);
+*/
+/*static irqreturn_t mf624_interrupt(int irq, void *d, struct pt_regs *regs);*/
+
+
+/*
+ * 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 comedi_driver driver_mf624 = {
+       driver_name:    "mf624",
+       module:         THIS_MODULE,
+       attach:         mf624_attach,
+       detach:         mf624_detach,
+};
+
+
+#define thisboard      ((mf624_board *)dev->board_ptr)
+/*
+ * 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 mf624_attach(comedi_device *dev, comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       struct pci_dev* pcidev;
+       unsigned int index, channel, status;
+
+       int regA, regB;
+
+       rt_printk("comedi%d: mf624: driver: Bourgeot - Poulain 2006-2007\n", dev->minor);
+       rt_printk("This is an experimental version, you can report \
+               some remarks or problems to fpoulain@gmail.com\n");
+
+       
+       /* Allocate the private structure area.  alloc_private() is a
+          convenient macro defined in comedidev.h. */
+       if(alloc_private(dev, sizeof(mf624_private)) < 0) {
+               return -ENOMEM;
+       }
+
+       /* Probe the device to determine what device in the series it is */
+       for(pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); 
+               pcidev != NULL; 
+               pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+
+               if(pcidev->vendor != PCI_VENDOR_ID_MF624) {
+                       continue;
+               }
+
+               /* loop through cards supported by this driver */
+               for(index = 0; index < N_BOARDS ; index++)
+               {
+                       if(mf624_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;
+                               }
+                       }
+
+                       devpriv->pci_dev = pcidev;
+                       dev->board_ptr = mf624_boards + index;
+                       goto found;
+               }
+       }
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: No supported Humusoft card \
+                       found on requested position\n", dev->minor);
+#endif
+       comedi_error(dev, "No supported Humusoft card found on requested position\n");
+       return -EIO;
+
+found:
+       rt_printk("comedi%d: mf624: Found %s on bus %i, slot %i\n", dev->minor, 
+               mf624_boards[index].name, pcidev->bus->number, PCI_SLOT(pcidev->devfn));
+
+       /* Enable PCI device and reserve I/O ports. */
+       if(pci_enable_device(pcidev)) {
+#ifdef EXTDEBUG
+               rt_printk("comedi%d: mf624: Failed to enable PCI device\n", dev->minor);
+#endif
+               comedi_error(dev, "Failed to enable PCI device\n");
+               return -EIO;
+       }
+       if(pci_request_regions(pcidev, "mf624")) {
+#ifdef EXTDEBUG
+               rt_printk("comedi%d: mf624: I/O port conflict\n", dev->minor);
+#endif
+               comedi_error(dev, "I/O port conflict\n");
+               return -EIO;
+       }
+
+       devpriv->BADR0 = pci_resource_start(devpriv->pci_dev, 0);
+       devpriv->BADR1 = pci_resource_start(devpriv->pci_dev, 2);
+       devpriv->BADR2 = pci_resource_start(devpriv->pci_dev, 4);
+
+       devpriv->IO_BADR0 = pci_ioremap_bar(devpriv->pci_dev, 0);
+       devpriv->IO_BADR1 = pci_ioremap_bar(devpriv->pci_dev, 2);
+       devpriv->IO_BADR2 = pci_ioremap_bar(devpriv->pci_dev, 4);
+
+#ifdef EXTDEBUG
+       rt_printk ("comedi%d: mf624: PCI Resource 0    addr %lx \n", dev->minor, devpriv->BADR0);
+       rt_printk ("comedi%d: mf624: PCI Resource 1    addr %lx \n", dev->minor, devpriv->BADR1);
+       rt_printk ("comedi%d: mf624: PCI Resource 2    addr %lx \n", dev->minor, devpriv->BADR2);
+
+       rt_printk ("comedi%d: mf624: IO_BADR0    addr %p \n", dev->minor, devpriv->IO_BADR0);
+       rt_printk ("comedi%d: mf624: IO_BADR1(2) addr %p \n", dev->minor, devpriv->IO_BADR1);
+       rt_printk ("comedi%d: mf624: IO_BADR2(4) addr %p \n", dev->minor, devpriv->IO_BADR2);   
+#endif
+
+       dev->board_name = thisboard->name;
+
+       /*
+        * Allocate the subdevice structures.  alloc_subdevice() is a
+        * convenient macro defined in comedidev.h.
+        */
+       if(alloc_subdevices(dev, MF624_SUBDEV_COUNT) < 0) {
+               return -ENOMEM;
+       }
+
+       /* Analog single ended (ground) input subdevice */
+       s = dev->subdevices + 0;
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE|SDF_GROUND;
+       s->n_chan = thisboard->ai_chans;
+       s->maxdata = (1<<thisboard->ai_bits)-1;
+       s->range_table = &range_bipolar10;
+       s->len_chanlist = 8;
+       s->insn_read = mf624_ai_rinsn;
+       s->insn_config = mf624_ai_cfg;
+
+       /* Analog output subdevice */
+       s = dev->subdevices + 1;
+       s->type = COMEDI_SUBD_AO;
+       s->subdev_flags = SDF_WRITABLE;
+       s->n_chan = thisboard->ao_chans;
+       s->maxdata = (1<<thisboard->ao_bits)-1;
+       s->range_table = &range_bipolar10;
+       s->insn_write = mf624_ao_winsn;
+       s->insn_read = mf624_ao_rinsn;
+       s->insn_config = mf624_ao_cfg;
+
+       /* Digital input subdevice */   
+       s = dev->subdevices + 2;
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = thisboard->di_chans;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits = mf624_di_insn_bits;
+
+       /* Digital output subdevice */  
+       s = dev->subdevices + 3;
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITABLE;
+       s->n_chan = thisboard->do_chans;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits  = mf624_do_insn_bits;
+
+
+       rt_printk("comedi%d: mf624: Driver attached\n", dev->minor);
+
+       /* Enable DAC */
+       status = ioread32(devpriv->IO_BADR0 + MF624_GPIOC) | MF624_GPIOC_DACEN;  
+       iowrite32(status, devpriv->IO_BADR0 + MF624_GPIOC);
+
+       /* Initialise Interrupt Control Status */
+       iowrite32(0x00000000, devpriv->IO_BADR0 + MF624_INTCSR);
+
+       /* Initialise analog outputs to zero */
+       for(channel = 0; channel < thisboard->ao_chans; channel ++) {
+               iowrite16(0x2000, devpriv->IO_BADR1 + MF624_DA0 + 2*channel);
+               /* Readback save */
+               devpriv->ao_readback[channel] = 0x2000;
+       }
+
+       //rt_printk("comedi%d: mf624: Board initialized\n", dev->minor);
+       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 mf624_detach(comedi_device *dev)
+{
+       //rt_printk("comedi%d: mf624: remove\n", dev->minor);
+
+       if(dev->irq) {
+               comedi_free_irq(dev->irq, dev);
+       }
+
+       if(devpriv && devpriv->pci_dev)
+       {
+               if(devpriv->BADR0)
+               {
+                       pci_release_regions(devpriv->pci_dev);
+                       pci_disable_device(devpriv->pci_dev);
+                       //rt_printk("comedi%d: mf624: remove2\n", dev->minor);
+               }
+               pci_dev_put(devpriv->pci_dev);
+               //rt_printk("comedi%d: mf624: remove3\n", dev->minor);
+       }
+
+       return 0;
+}
+
+/* read n samples on Analog Input channel */
+static int mf624_ai_rinsn(comedi_device *dev, 
+       comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
+{
+       unsigned int dat, n, i, status, chan = CR_CHAN(insn->chanspec);
+
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: mf624_ai_rinsn called \n", dev->minor);
+#endif
+
+       /* write channel to multiplexer */
+       iowrite16(1 << chan, devpriv->IO_BADR1 + MF624_ADCTRL);
+
+       /* convert n samples */
+       for(n = 0; n < insn->n; n ++){
+               /* trigger conversion */
+               dat = ioread16(devpriv->IO_BADR1 + MF624_ADSTART);
+
+#define TIMEOUT 100 
+               /* wait for conversion to end */
+               for(i = 0; i < TIMEOUT; i++){
+                       status = 1;
+                       status = ioread32(devpriv->IO_BADR0 + MF624_GPIOC);
+                       if(!(status & MF624_GPIOC_EOLC)) {
+                               break;
+                       }
+               }
+               if(i == TIMEOUT) {
+                       rt_printk("comedi%d: mf624: _ai_rinsn: conversion timeout !\n", dev->minor);
+                       comedi_error(dev, "Conversion timeout !\n");
+                       return -ETIMEDOUT;
+               }
+
+               /* read data */
+               dat = ioread16(devpriv->IO_BADR1 + MF624_ADDATA);
+
+               /* mangle the data as necessary */
+               dat ^= 1 << (thisboard->ai_bits-1);
+
+               data[n] = dat;
+       }
+
+       /* return the number of samples read/written */
+       return n;
+}
+
+
+/* Analog input configuration */
+static int mf624_ai_cfg(comedi_device *dev,
+       comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
+{
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _insn_ai_cfg called\n", dev->minor);
+#endif
+       return insn->n;
+}
+
+
+/* write n samples on Analog Output channel */
+static int mf624_ao_winsn(comedi_device *dev,
+       comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
+{
+       unsigned int i, chan = CR_CHAN(insn->chanspec), status;
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _ao_winsn called\n", dev->minor);
+#endif
+       status = ioread32(devpriv->IO_BADR0 + MF624_GPIOC) | MF624_GPIOC_DACEN;
+       iowrite32(status, devpriv->IO_BADR0 + MF624_GPIOC);
+
+       /* Writing a list of values to an AO channel is probably not
+        * very useful, but that's how the interface is defined. */
+       for(i = 0; i < insn->n; i++){
+               iowrite16(data[i], devpriv->IO_BADR1 + MF624_DA0 + 2*chan);
+               devpriv->ao_readback[chan] = data[i];
+#ifdef EXTDEBUG
+               rt_printk("comedi%d: mf624: _ao_winsn: wrote at address \
+                       %i data %i  ", dev->minor, MF624_DA0 + 2*chan , data[i] );
+#endif
+       }
+
+       /* return the number of samples read/written */
+       return i;
+}
+
+
+/* Analog output configuration */
+static int mf624_ao_cfg(comedi_device *dev,
+       comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _ao_cfg called\n, dev->minor");
+#endif
+       return insn->n;
+}
+
+
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int mf624_ao_rinsn(comedi_device *dev,
+       comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
+{
+       unsigned int i, chan = CR_CHAN(insn->chanspec);
+
+       for(i = 0; i < insn->n; i++) {
+               data[i] = devpriv->ao_readback[chan];
+       }
+
+       return i;
+}
+
+/* Write digital data */
+static int mf624_do_insn_bits(comedi_device *dev,
+       comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
+{
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _do_insn_bits called \
+               with data: %d %d\n", dev->minor, data[0], data[1]);
+#endif
+       if(insn->n != 2) {
+               return -EINVAL;
+       }
+
+       if(data[0]) {
+               s->state &= ~data[0];
+               s->state |= data[0] & data[1];
+#ifdef EXTDEBUG
+               rt_printk ("comedi%d: mf624: _do_insn_bits: out: %d \
+                       on %lx\n", dev->minor, s->state, devpriv->IO_BADR1 + MF624_DOUT );
+#endif
+               iowrite16(s->state, devpriv->IO_BADR1 + MF624_DOUT);                                    
+       }
+       return 2;
+}
+
+/* Read digital data */
+static int mf624_di_insn_bits(comedi_device *dev,
+       comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
+{
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _di_insn_bits called with \
+               data: %d %d\n", dev->minor, data[0], data[1]);
+#endif
+       if(insn->n != 2) {
+               return -EINVAL;
+       }
+
+       data[1] = ioread16(devpriv->IO_BADR1 + MF624_DIN);
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _di_insn_bits read \
+               data: %d\n", dev->minor, data[1]);
+#endif
+       return 2;
+}
+
+
+MODULE_AUTHOR("Francois Poulain <fpoulain@gmail.com>;\
+                (Rostislav Lisovy <lisovy@gmail.com>)");
+MODULE_DESCRIPTION("Humusoft MF624 Multifunction I/O Card");
+MODULE_LICENSE("GPL");
+
+
+/* A convenient macro that defines init_module() and cleanup_module(), as necessary. */
+COMEDI_INITCLEANUP(driver_mf624);