--- /dev/null
+#include "../comedidev.h"
+#include <linux/pci.h>
+
+#define PCI_VENDOR_ID_MF614 0x186c
+#define PCI_DEVICE_ID_MF614 0x0614
+
+
+static struct pci_device_id mf614_pci_table[] __devinitdata = {
+ { PCI_VENDOR_ID_MF614, PCI_DEVICE_ID_MF614, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, mf614_pci_table);
+
+
+typedef struct mf614_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;
+} mf614_board;
+
+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 */
+ di_chans: 8, /* Num of digital in */
+ do_chans: 8, /* Num of digital out */
+ }
+};
+
+static struct comedi_driver driver_mf614 = {
+ driver_name: "mf614",
+ module: THIS_MODULE,
+ attach: mf614_attach,
+ detach: mf614_detach,
+};
+
+/* Private data structure */
+typedef struct {
+ struct pci_dev *pci_dev;
+
+ void __iomem *BAR0_io;
+ void __iomem *BAR2_io;
+
+ u8 in_use;
+ /* Used for AO readback */
+ //lsampl_t 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);
+
+/*
+==============================================================================
+*/
+#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
+ return -ENOMEM;
+ }
+
+ devpriv->in_use = -1;
+
+ /* 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_MF614) {
+ continue;
+ }
+
+ if(mf614_boards.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 = mf614_boards + 0; // Zero offset; Only One card is supported
+ goto found;
+ }
+
+ comedi_error(dev, "Can't find MF614 card on requested position\n");
+ return -EIO;
+
+found:
+ printk("Found %s on bus %i, slot %i\n",
+ mf614_boards.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)) {
+ comedi_error(dev, "Failed to enable PCI device\n");
+ goto out_exit;
+ }
+ if(pci_request_regions(devpriv->pci_dev, "mf614")) {
+ comedi_error(dev, "Card's IO ports allready in use\n");
+ goto out_disable;
+ }
+
+ /* pci_ioremap_bar(devpriv->pci_dev, 0) is for memory regions; This are IO ports */
+ devpriv->BAR0_io = pci_iomap(devpriv->pci_dev, 0, 0);
+ if (!devpriv->BAR0_io) {
+ goto out_release;
+ }
+ devpriv->BAR2_io = pci_iomap(devpriv->pci_dev, 2, 0);
+ if (!devpriv->BAR2_io) {
+ goto out_unmap;
+ }
+
+ devpriv->in_use = 1;
+ dev->board_name = thisboard->name;
+
+ /* Allocate subdevices */
+ if(alloc_subdevices(dev, 2) < 0) {
+ return -ENOMEM;
+ }
+
+ /* DIN */
+ s = dev->subdevices + 0;
+ 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 = mf614_di_insn_bits;
+
+ /* DOUT */
+ s = dev->subdevices + 1;
+ 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 = mf614_do_insn_bits;
+
+ printk("comedi%d: mf614: Driver attached\n", dev->minor);
+
+ return 0;
+
+out_unmap:
+ pci_iounmap(devpriv->pci_dev, devpriv->BAR0_io);
+out_release:
+ pci_release_regions(devpriv->pci_dev);
+out_disable:
+ pci_disable_device(devpriv->pcidev);
+out_exit:
+ return -ENODEV;
+}
+
+static int mf614_detach(struct comedi_device *dev)
+{
+ if(devpriv && devpriv->pci_dev)
+ {
+ if(devpriv->in_use == 1)
+ {
+ pci_release_regions(devpriv->pci_dev);
+ pci_disable_device(devpriv->pci_dev);
+ pci_iounmap(devpriv->pci_dev, devpriv->BAR0_io);
+ pci_iounmap(devpriv->pci_dev, devpriv->BAR2_io);
+ }
+
+ pci_dev_put(devpriv->pci_dev);
+ }
+
+ return 0;
+}
+
+
+/*
+==============================================================================
+*/
+static int __devinit mf614_probe(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+ return comedi_pci_auto_config(dev, driver_mf614.driver_name);
+}
+
+static void __devexit mf614_remove(struct pci_dev *dev)
+{
+ comedi_pci_auto_unconfig(dev);
+}
+
+static struct pci_driver driver_mf614_pci = {
+ .id_table = mf614_pci_table,
+ .probe = &mf614_probe,
+ .remove = __devexit_p(&mf614_remove)
+};
+
+static int __init mf614_init_module(void)
+{
+ int retval;
+
+ retval = comedi_driver_register(&driver_mf614);
+ if (retval < 0)
+ return retval;
+
+ /* Copy "name" from Comedi driver struct into pci_driver_struct*/
+ driver_mf614_pci.name = (char *)driver_mf614.driver_name;
+ return pci_register_driver(&driver_mf614_pci);
+}
+
+static void __exit mf614_cleanup_module(void)
+{
+ pci_unregister_driver(&driver_mf614_pci);
+ comedi_driver_unregister(&driver_mf614);
+}
+
+module_init(mf614_init_module);
+module_exit(mf614_cleanup_module);
+
+MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");
+MODULE_DESCRIPTION("Humusoft MF614 DAQ Card");
+MODULE_LICENSE("GPL v2");