amplc_dio200: don't access *data when insn->n == 0
[comedi.git] / comedi / drivers / amplc_dio200.c
index fe2ee77a8e0ea009f9680c2bc98ba1dde2a9a11f..fc960ef5962994af560226bcac064c74fc07d5c5 100644 (file)
@@ -29,8 +29,9 @@ Driver: amplc_dio200
 Description: Amplicon 200 Series Digital I/O
 Author: Ian Abbott <abbotti@mev.co.uk>
 Devices: [Amplicon] PC212E (pc212e), PC214E (pc214e), PC215E (pc215e),
-  PCI215 (pci215), PC218E (pc218e), PC272E (pc272e), PCI272 (pci272)
-Updated: Mon, 05 Nov 2007 14:04:04 +0000
+  PCI215 (pci215 or amplc_dio200), PC218E (pc218e), PC272E (pc272e),
+  PCI272 (pci272 or amplc_dio200)
+Updated: Wed, 22 Oct 2008 13:36:02 +0100
 Status: works
 
 Configuration options - PC212E, PC214E, PC215E, PC218E, PC272E:
@@ -217,6 +218,7 @@ order they appear in the channel list.
 /* #define PCI_VENDOR_ID_AMPLICON 0x14dc */
 #define PCI_DEVICE_ID_AMPLICON_PCI272 0x000a
 #define PCI_DEVICE_ID_AMPLICON_PCI215 0x000b
+#define PCI_DEVICE_ID_INVALID 0xffff
 
 /* 200 series registers */
 #define DIO200_IO_SIZE         0x20
@@ -264,7 +266,8 @@ enum dio200_model {
        pc214e_model,
        pc215e_model, pci215_model,
        pc218e_model,
-       pc272e_model, pci272_model
+       pc272e_model, pci272_model,
+       anypci_model
 };
 
 enum dio200_layout {
@@ -277,6 +280,7 @@ enum dio200_layout {
 
 typedef struct dio200_board_struct {
        const char *name;
+       unsigned short devid;
        enum dio200_bustype bustype;
        enum dio200_model model;
        enum dio200_layout layout;
@@ -304,6 +308,7 @@ static const dio200_board dio200_boards[] = {
 #ifdef CONFIG_COMEDI_PCI
        {
              name:     "pci215",
+             devid:    PCI_DEVICE_ID_AMPLICON_PCI215,
              bustype:  pci_bustype,
              model:    pci215_model,
              layout:   pc215_layout,
@@ -324,11 +329,20 @@ static const dio200_board dio200_boards[] = {
 #ifdef CONFIG_COMEDI_PCI
        {
              name:     "pci272",
+             devid:    PCI_DEVICE_ID_AMPLICON_PCI272,
              bustype:  pci_bustype,
              model:    pci272_model,
              layout:   pc272_layout,
                },
 #endif
+#ifdef CONFIG_COMEDI_PCI
+       {
+             name:     DIO200_DRIVER_NAME,
+             devid:    PCI_DEVICE_ID_INVALID,
+             bustype:  pci_bustype,
+             model:    anypci_model,   /* wildcard */
+               },
+#endif
 };
 
 /*
@@ -405,9 +419,9 @@ static const dio200_layout dio200_layouts[] = {
 #ifdef CONFIG_COMEDI_PCI
 static DEFINE_PCI_DEVICE_TABLE(dio200_pci_table) = {
        {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI215,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, pci215_model},
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI272,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, pci272_model},
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {0}
 };
 
@@ -440,6 +454,7 @@ typedef struct {
        int has_clk_gat_sce;
        unsigned clock_src[3];  /* Current clock sources */
        unsigned gate_src[3];   /* Current gate sources */
+       spinlock_t spinlock;
 } dio200_subdev_8254;
 
 typedef struct {
@@ -471,7 +486,11 @@ static comedi_driver driver_amplc_dio200 = {
       num_names:sizeof(dio200_boards) / sizeof(dio200_board),
 };
 
+#ifdef CONFIG_COMEDI_PCI
 COMEDI_PCI_INITCLEANUP(driver_amplc_dio200, dio200_pci_table);
+#else
+COMEDI_INITCLEANUP(driver_amplc_dio200);
+#endif
 
 /*
  * This function looks for a PCI device matching the requested board name,
@@ -483,46 +502,40 @@ dio200_find_pci(comedi_device * dev, int bus, int slot,
        struct pci_dev **pci_dev_p)
 {
        struct pci_dev *pci_dev = NULL;
-       const struct pci_device_id *pci_id;
 
        *pci_dev_p = NULL;
 
-       /* Look for PCI table entry for this model. */
-       for (pci_id = dio200_pci_table; pci_id->vendor != 0; pci_id++) {
-               if (pci_id->driver_data == thisboard->model)
-                       break;
-       }
-       if (pci_id->vendor == 0) {
-               printk(KERN_ERR
-                       "comedi%d: %s: BUG! cannot determine board type!\n",
-                       dev->minor, DIO200_DRIVER_NAME);
-               return -EINVAL;
-       }
-
        /* Look for matching PCI device. */
-       for (pci_dev = pci_get_device(pci_id->vendor, pci_id->device, NULL);
+       for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
                pci_dev != NULL;
-               pci_dev = pci_get_device(pci_id->vendor,
-                       pci_id->device, pci_dev)) {
+               pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
+                       PCI_ANY_ID, pci_dev)) {
                /* If bus/slot specified, check them. */
                if (bus || slot) {
                        if (bus != pci_dev->bus->number
                                || slot != PCI_SLOT(pci_dev->devfn))
                                continue;
                }
-#if 0
-               if (pci_id->subvendor != PCI_ANY_ID) {
-                       if (pci_dev->subsystem_vendor != pci_id->subvendor)
+               if (thisboard->model == anypci_model) {
+                       /* Match any supported model. */
+                       int i;
+
+                       for (i = 0; i < ARRAY_SIZE(dio200_boards); i++) {
+                               if (dio200_boards[i].bustype != pci_bustype)
+                                       continue;
+                               if (pci_dev->device == dio200_boards[i].devid) {
+                                       /* Change board_ptr to matched board. */
+                                       dev->board_ptr = &dio200_boards[i];
+                                       break;
+                               }
+                       }
+                       if (i == ARRAY_SIZE(dio200_boards))
                                continue;
-               }
-               if (pci_id->subdevice != PCI_ANY_ID) {
-                       if (pci_dev->subsystem_device != pci_id->subdevice)
+               } else {
+                       /* Match specific model name. */
+                       if (pci_dev->device != thisboard->devid)
                                continue;
                }
-#endif
-               if (((pci_dev->class ^ pci_id->class) & pci_id->class_mask) !=
-                       0)
-                       continue;
 
                /* Found a match. */
                *pci_dev_p = pci_dev;
@@ -1026,8 +1039,14 @@ dio200_subdev_8254_read(comedi_device * dev, comedi_subdevice * s,
 {
        dio200_subdev_8254 *subpriv = s->private;
        int chan = CR_CHAN(insn->chanspec);
+       unsigned long flags;
 
+       if (insn->n == 0)
+               return 0;
+
+       comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
        data[0] = i8254_read(subpriv->iobase, 0, chan);
+       comedi_spin_unlock_irqrestore(&subpriv->spinlock, flags);
 
        return 1;
 }
@@ -1041,8 +1060,14 @@ dio200_subdev_8254_write(comedi_device * dev, comedi_subdevice * s,
 {
        dio200_subdev_8254 *subpriv = s->private;
        int chan = CR_CHAN(insn->chanspec);
+       unsigned long flags;
+
+       if (insn->n == 0)
+               return 0;
 
+       comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
        i8254_write(subpriv->iobase, 0, chan, data[0]);
+       comedi_spin_unlock_irqrestore(&subpriv->spinlock, flags);
 
        return 1;
 }
@@ -1134,14 +1159,16 @@ dio200_subdev_8254_config(comedi_device * dev, comedi_subdevice * s,
        comedi_insn * insn, lsampl_t * data)
 {
        dio200_subdev_8254 *subpriv = s->private;
-       int ret;
+       int ret = 0;
        int chan = CR_CHAN(insn->chanspec);
+       unsigned long flags;
 
+       comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
        switch (data[0]) {
        case INSN_CONFIG_SET_COUNTER_MODE:
                ret = i8254_set_mode(subpriv->iobase, 0, chan, data[1]);
                if (ret < 0)
-                       return -EINVAL;
+                       ret = -EINVAL;
                break;
        case INSN_CONFIG_8254_READ_STATUS:
                data[1] = i8254_status(subpriv->iobase, 0, chan);
@@ -1149,30 +1176,35 @@ dio200_subdev_8254_config(comedi_device * dev, comedi_subdevice * s,
        case INSN_CONFIG_SET_GATE_SRC:
                ret = dio200_set_gate_src(subpriv, chan, data[2]);
                if (ret < 0)
-                       return -EINVAL;
+                       ret = -EINVAL;
                break;
        case INSN_CONFIG_GET_GATE_SRC:
                ret = dio200_get_gate_src(subpriv, chan);
-               if (ret < 0)
-                       return -EINVAL;
+               if (ret < 0) {
+                       ret = -EINVAL;
+                       break;
+               }
                data[2] = ret;
                break;
        case INSN_CONFIG_SET_CLOCK_SRC:
                ret = dio200_set_clock_src(subpriv, chan, data[1]);
                if (ret < 0)
-                       return -EINVAL;
+                       ret = -EINVAL;
                break;
        case INSN_CONFIG_GET_CLOCK_SRC:
                ret = dio200_get_clock_src(subpriv, chan, &data[2]);
-               if (ret < 0)
-                       return -EINVAL;
+               if (ret < 0) {
+                       ret = -EINVAL;
+                       break;
+               }
                data[1] = ret;
                break;
        default:
-               return -EINVAL;
+               ret = -EINVAL;
                break;
        }
-       return insn->n;
+       comedi_spin_unlock_irqrestore(&subpriv->spinlock, flags);
+       return ret < 0 ? ret : insn->n;
 }
 
 /*
@@ -1204,6 +1236,7 @@ dio200_subdev_8254_init(comedi_device * dev, comedi_subdevice * s,
        s->insn_write = dio200_subdev_8254_write;
        s->insn_config = dio200_subdev_8254_config;
 
+       spin_lock_init(&subpriv->spinlock);
        subpriv->iobase = offset + iobase;
        subpriv->has_clk_gat_sce = has_clk_gat_sce;
        if (has_clk_gat_sce) {