staging: comedi: adl_pci6208: don't deadlock while waiting to write ao data
authorH Hartley Sweeten <hsweeten@visionengravers.com>
Wed, 18 Sep 2013 18:48:41 +0000 (11:48 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 25 Sep 2013 22:45:42 +0000 (15:45 -0700)
Remove a possible deadlock while waiting to write the analog output data.

The data transfer rate for every D/A data write in this driver is 2.2us.
Wait up to 10us for the board to be ready then and return -ETIME.

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Reviewed-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/drivers/adl_pci6208.c

index 9150582fad068ef59b645fae89df71d4a858df91..059d7ea9503d1b58136ec02bcaf82447f62bb80e 100644 (file)
@@ -39,6 +39,7 @@ References:
 */
 
 #include <linux/module.h>
+#include <linux/delay.h>
 #include <linux/pci.h>
 
 #include "../comedidev.h"
@@ -82,6 +83,21 @@ struct pci6208_private {
        unsigned int ao_readback[PCI6208_MAX_AO_CHANNELS];
 };
 
+static int pci6208_ao_wait_for_data_send(struct comedi_device *dev,
+                                        unsigned int timeout)
+{
+       unsigned int status;
+
+       while (timeout--) {
+               status = inw(dev->iobase + PCI6208_AO_STATUS);
+               if ((status & PCI6208_AO_STATUS_DATA_SEND) == 0)
+                       return 0;
+               udelay(1);
+       }
+
+       return -ETIME;
+}
+
 static int pci6208_ao_winsn(struct comedi_device *dev,
                            struct comedi_subdevice *s,
                            struct comedi_insn *insn, unsigned int *data)
@@ -90,15 +106,16 @@ static int pci6208_ao_winsn(struct comedi_device *dev,
        int chan = CR_CHAN(insn->chanspec);
        unsigned int invert = 1 << (16 - 1);
        unsigned int val = devpriv->ao_readback[chan];
-       unsigned short status;
+       int ret;
        int i;
 
        for (i = 0; i < insn->n; i++) {
                val = data[i];
 
-               do {
-                       status = inw(dev->iobase + PCI6208_AO_STATUS);
-               } while (status & PCI6208_AO_STATUS_DATA_SEND);
+               /* D/A transfer rate is 2.2us, wait up to 10us */
+               ret = pci6208_ao_wait_for_data_send(dev, 10);
+               if (ret)
+                       return ret;
 
                outw(val ^ invert, dev->iobase + PCI6208_AO_CONTROL(chan));
        }