]> rtime.felk.cvut.cz Git - mf6xx.git/blobdiff - src/comedi/mf614_simple_driver/kernel/mf614.c
Added license information + small bugfixes.
[mf6xx.git] / src / comedi / mf614_simple_driver / kernel / mf614.c
index 7f571a2eb81816ab324c8061cd98668c680d67e4..26153b9d781375dd9a0109677fe6fc2e09980e31 100644 (file)
@@ -1,11 +1,54 @@
+/*
+ * Comedi driver fo Humusoft MF614 DAQ card.
+ * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com> 
+ * 
+ * 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.
+ */
+
 #include "../comedidev.h"
 #include <linux/pci.h>
 
-#define PCI_VENDOR_ID_MF614 0x186c
-#define PCI_DEVICE_ID_MF614 0x0614
+ #define PCI_VENDOR_ID_MF614   0x186c
+#define PCI_DEVICE_ID_MF614    0x0614
+
+/* BAR0 regs */
+#define ADCTRL_reg             0x0
+#define ADLO_reg               0x0
+#define ADHI_reg               0x1
+
+#define DIN_reg                        0x6
+#define DOUT_reg               0x6
+
+#define DALE_reg               0x8
+#define DA0LO_reg              0x8
+#define DA0HI_reg              0x9
+#define DA1LO_reg              0xA
+#define DA1HI_reg              0xB
+#define DA2LO_reg              0xC
+#define DA2HI_reg              0xD
+#define DA3LO_reg              0xE
+#define DA3HI_reg              0xF
+
+#define ADCTRL_DEFAULT         (1 << 6)
+#define ADCTRL_RNG             (1 << 4)
+#define ADCTRL_BIP             (1 << 3)
+
+/* BAR2 regs */
+#define STAT_reg               0x10
 
-#define DIN_reg                0x6
-#define DOUT_reg       0x6
+#define STAT_CC                (1 << 2)
 
 
 static struct pci_device_id mf614_pci_table[] __devinitdata = {
@@ -31,19 +74,21 @@ static const mf614_board mf614_boards[] = {
                name:           "mf614",        /* device name          */
                device_id:      PCI_DEVICE_ID_MF614,    /* 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      */
+               ai_bits:        12,             /* Num of ADC bits      */
+               ao_chans:       4,              /* Num of DAC channels  */
+               ao_bits:        12,             /* Num of DAC bits      */
                di_chans:       8,              /* Num of digital in    */
                do_chans:       8,              /* Num of digital out   */
        }
 };
 
+static int mf614_attach(struct comedi_device *dev, struct comedi_devconfig *it);
+static int mf614_detach(struct comedi_device *dev);
 static struct comedi_driver driver_mf614 = {
-       driver_name:    "mf614",
-       module:         THIS_MODULE,
-       attach:         mf614_attach,
-       detach:         mf614_detach,
+       driver_name:    "mf614",
+       module:         THIS_MODULE,
+       attach:         mf614_attach,
+       detach:         mf614_detach,
 };
 
 /* Private data structure */
@@ -55,27 +100,41 @@ typedef struct {
 
        u8 in_use;
        /* Used for AO readback */
-       //lsampl_t ao_readback[8];
+       unsigned int ao_readback[8];
 } mf614_private;
 
-static int mf614_attach(struct comedi_device *dev, struct comedi_devconfig *it);
-static int mf614_detach(struct comedi_device *dev);
 
 /*
 ==============================================================================
 */
+
+
+static int mf614_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, 
+                             struct comedi_insn *insn, unsigned int *data);
+static int mf614_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, 
+                             struct comedi_insn *insn, unsigned int *data);
+static int mf614_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 
+                          struct comedi_insn *insn, unsigned int *data);
+static int mf614_ai_cfg(struct comedi_device *dev, struct comedi_subdevice *s, 
+                        struct comedi_insn *insn, unsigned int *data);
+static int mf614_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, 
+                          struct comedi_insn *insn, unsigned int *data);
+static int mf614_ao_cfg(struct comedi_device *dev, struct comedi_subdevice *s, 
+                        struct comedi_insn *insn, unsigned int *data);
+static int mf614_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 
+                          struct comedi_insn *insn, unsigned int *data);
+
 #define devpriv                ((mf614_private *) dev->private)
 #define thisboard              ((mf614_board *) dev->board_ptr)
 static int mf614_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 {
        struct comedi_subdevice *s;
        struct pci_dev* pcidev;
-       //unsigned int channel; 
-       unsigned int status;
 
        printk("comedi%d: mf614: ", dev->minor);
 
-       if(alloc_private(dev, sizeof(mf614_private)) < 0) { // No need to free() later
+       //Comedi function; No need to kfree() later
+       if(alloc_private(dev, sizeof(mf614_private)) < 0) {
                return -ENOMEM;
        }
        
@@ -91,7 +150,7 @@ static int mf614_attach(struct comedi_device *dev, struct comedi_devconfig *it)
                        continue;
                }
 
-               if(mf614_boards.device_id != pcidev->device) {
+               if(mf614_boards[0].device_id != pcidev->device) {
                        continue;
                }
 
@@ -115,7 +174,7 @@ static int mf614_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 
 found:
        printk("Found %s on bus %i, slot %i\n",  
-               mf614_boards.name, pcidev->bus->number, PCI_SLOT(pcidev->devfn));
+               mf614_boards[0].name, pcidev->bus->number, PCI_SLOT(pcidev->devfn));
 
        /* comedi_pci_enable(struct pci_dev *pdev, const char *res_name) */
        if(pci_enable_device(devpriv->pci_dev)) {
@@ -137,11 +196,12 @@ found:
                goto out_unmap;
        }
        
-       devpriv->in_use = 1;
+       
        dev->board_name = thisboard->name;
        
        /* Allocate subdevices */
-       if(alloc_subdevices(dev, 2) < 0) {
+       //Comedi function; No need to kfree() later
+       if(alloc_subdevices(dev, 4) < 0) {
                return -ENOMEM;
        }
        
@@ -161,9 +221,33 @@ found:
        s->n_chan = thisboard->do_chans;
        s->maxdata = 1;
        s->range_table = &range_digital;
-       s->insn_bits  = mf614_do_insn_bits;
+       s->insn_bits = mf614_do_insn_bits;
+       
+       /* Analog input */
+       s = dev->subdevices + 2;
+       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 = mf614_ai_rinsn;
+       s->insn_config = mf614_ai_cfg;
+
+       /* Analog output */
+       s = dev->subdevices + 3;
+       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 = mf614_ao_winsn;
+       s->insn_read = mf614_ao_rinsn;
+       s->insn_config = mf614_ao_cfg;
+       
 
        printk("comedi%d: mf614: Driver attached\n", dev->minor);
+       devpriv->in_use = 1;
        
        return 0;
 
@@ -172,11 +256,19 @@ out_unmap:
 out_release:
        pci_release_regions(devpriv->pci_dev);
 out_disable:
-       pci_disable_device(devpriv->pcidev);
+       pci_disable_device(devpriv->pci_dev);
 out_exit:
        return -ENODEV;
 }
 
+/*
+ * _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 mf614_detach(struct comedi_device *dev)
 {
        if(devpriv && devpriv->pci_dev)
@@ -195,7 +287,6 @@ static int mf614_detach(struct comedi_device *dev)
        return 0;
 }
 
-
 /* Write digital data */
 static int mf614_do_insn_bits(struct comedi_device *dev, 
                              struct comedi_subdevice *s, 
@@ -205,17 +296,19 @@ static int mf614_do_insn_bits(struct comedi_device *dev,
                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];
+               s->state &= ~data[0]; // Get old value
+               s->state |= data[0] & data[1]; // Write new value to it
 
-               iowrite8(s->state, BAR0_io + DOUT_reg);
+               iowrite8(s->state, devpriv->BAR0_io + DOUT_reg);
        }
        return 2;
 }
 
 /* Read digital data */
-static int mf624_di_insn_bits(struct comedi_device *dev, 
+static int mf614_di_insn_bits(struct comedi_device *dev, 
                              struct comedi_subdevice *s, 
                              struct comedi_insn *insn, unsigned int *data)
 {
@@ -228,6 +321,125 @@ static int mf624_di_insn_bits(struct comedi_device *dev,
        return 2;
 }
 
+/* Read n samples on Analog Input channel */
+static int mf614_ai_rinsn(struct comedi_device *dev,
+                          struct comedi_subdevice *s, 
+                          struct comedi_insn *insn, unsigned int *data)
+{
+       unsigned int dat, n, i, status;
+       unsigned int chan = CR_CHAN(insn->chanspec);
+
+       /* write channel to multiplexer */
+       iowrite8((chan) | ADCTRL_DEFAULT | ADCTRL_BIP | ADCTRL_RNG, devpriv->BAR0_io + ADCTRL_reg);
+
+       /* convert n samples */
+       for(n = 0; n < insn->n; n ++) {
+               #define TIMEOUT 100 
+               /* wait for conversion to end */
+               for(i = 0; i < TIMEOUT; i++){
+                       status = 1;
+                       status = ioread8(devpriv->BAR2_io + STAT_reg);
+                       if(!(status & STAT_CC)) {
+                               break;
+                       }
+               }
+               if (i == TIMEOUT) {
+                       comedi_error(dev, "Conversion timeout!\n");
+                       return -ETIMEDOUT;
+               }
+
+               /* read data */
+               dat = ioread8(devpriv->BAR0_io + ADLO_reg);
+               dat |= (ioread8(devpriv->BAR0_io + ADHI_reg) << 8);
+
+               /* 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 mf614_ai_cfg(struct comedi_device *dev,
+                        struct comedi_subdevice *s, 
+                        struct comedi_insn *insn, unsigned int *data)
+{
+       return insn->n;
+}
+
+/* Write n samples on Analog Output channel */
+static int mf614_ao_winsn(struct comedi_device *dev,
+                          struct comedi_subdevice *s, 
+                          struct comedi_insn *insn, unsigned int *data)
+{
+       unsigned int i;
+       unsigned int chan = CR_CHAN(insn->chanspec);
+       u8 regLO, regHI;
+
+       /* 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++){
+               switch(chan) {
+                       case 0:
+                               regLO = DA0LO_reg;
+                               regHI = DA0HI_reg;
+                               break;
+                       case 1:
+                               regLO = DA1LO_reg;
+                               regHI = DA1HI_reg;
+                               break;
+                       case 2:
+                               regLO = DA2LO_reg;
+                               regHI = DA2HI_reg;
+                               break;
+                       case 3:
+                               regLO = DA3LO_reg;
+                               regHI = DA3HI_reg;
+                               break;
+                       default:
+                               comedi_error(dev, "Wrong D/A channel number!\n");
+                               return -ENODEV;
+               }
+               
+               iowrite8((u8)(data[i] & 0xFF), devpriv->BAR0_io + regLO);
+               iowrite8((u8)(data[i] >> 8), devpriv->BAR0_io + regHI);
+               ioread8(devpriv->BAR0_io + DALE_reg); // Trigger DA conversion
+
+               devpriv->ao_readback[chan] = data[i];
+       }
+
+       /* return the number of samples read/written */
+       return i;
+}
+
+/* Analog output configuration */
+static int mf614_ao_cfg(struct comedi_device *dev,
+                        struct comedi_subdevice *s, 
+                        struct comedi_insn *insn, unsigned int *data)
+{
+       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 mf614_ao_rinsn(struct comedi_device *dev,
+                          struct comedi_subdevice *s, 
+                          struct comedi_insn *insn, unsigned int *data)
+{
+       unsigned int i, chan = CR_CHAN(insn->chanspec);
+
+       for(i = 0; i < insn->n; i++) {
+               data[i] = devpriv->ao_readback[chan];
+       }
+
+       return i;
+}
+
+
 /*
 ==============================================================================
 */