+/*
+ * 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 = {
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 */
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;
}
continue;
}
- if(mf614_boards.device_id != pcidev->device) {
+ if(mf614_boards[0].device_id != pcidev->device) {
continue;
}
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)) {
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;
}
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;
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)
return 0;
}
-
/* Write digital data */
static int mf614_do_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
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)
{
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;
+}
+
+
/*
==============================================================================
*/