[PATCH 049/108] staging: comedi: addi_apci_3120: fix apci3120_ai_insn_read()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Now that the scanning and interrupt support have been removed from this
function it can be refactored to work correctly.

The comedi core expects (*insn_read) functions to read insn->n values
from the hardware and return the number of samples read. This function
currently just reads one sample but it returns insn->n.

Fix this function to work like the core expects.

Use comedi_timeout() to prevent a possible deadlock in the loop that
waits for the end-of-conversion.

Signed-off-by: H Hartley Sweeten <hsweeten@xxxxxxxxxxxxxxxxxxx>
Cc: Ian Abbott <abbotti@xxxxxxxxx>
Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
---
 .../comedi/drivers/addi-data/hwdrv_apci3120.c      | 90 ++++++++++------------
 1 file changed, 40 insertions(+), 50 deletions(-)

diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c
index 51cdecb..5fb8848 100644
--- a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c
@@ -139,6 +139,19 @@ static int apci3120_setup_chan_list(struct comedi_device *dev,
 	return 1;		/*  we can serve this with scan logic */
 }
 
+static int apci3120_ai_eoc(struct comedi_device *dev,
+			   struct comedi_subdevice *s,
+			   struct comedi_insn *insn,
+			   unsigned long context)
+{
+	unsigned int status;
+
+	status = inw(dev->iobase + APCI3120_RD_STATUS);
+	if ((status & APCI3120_EOC) == 0)
+		return 0;
+	return -EBUSY;
+}
+
 static int apci3120_ai_insn_read(struct comedi_device *dev,
 				 struct comedi_subdevice *s,
 				 struct comedi_insn *insn,
@@ -146,67 +159,44 @@ static int apci3120_ai_insn_read(struct comedi_device *dev,
 {
 	struct apci3120_private *devpriv = dev->private;
 	unsigned int divisor;
-	unsigned int ns;
-	unsigned short us_TmpValue;
-
-	/*  fix conversion time to 10 us */
-	ns = 10000;
-
-	/*  Clear software registers */
-	devpriv->timer_mode = 0;
-	devpriv->mode = 0;
-
-	if (insn->unused[0] == 222) {	/*  second insn read */
-	} else {
-		devpriv->tsk_Current = current;	/*  Save the current process task structure */
-
-		divisor = apci3120_ns_to_timer(dev, 0, ns, CMDF_ROUND_NEAREST);
-
-		us_TmpValue = (unsigned short) devpriv->b_InterruptMode;
-
-		switch (us_TmpValue) {
-
-		case APCI3120_EOC_MODE:
-			apci3120_ai_reset_fifo(dev);
-
-			/*  Initialize the sequence array */
-			if (!apci3120_setup_chan_list(dev, s, 1,
-					&insn->chanspec))
-				return -EINVAL;
-
-			/* Initialize Timer 0 mode 4 */
-			apci3120_timer_set_mode(dev, 0, APCI3120_TIMER_MODE4);
+	int ret;
+	int i;
 
-			outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
+	/* set mode for A/D conversions by software trigger with timer 0 */
+	devpriv->mode = APCI3120_MODE_TIMER2_CLK_OSC |
+			APCI3120_MODE_TIMER2_AS_TIMER;
+	outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
 
-			apci3120_timer_enable(dev, 0, true);
+	/* load chanlist for single channel scan */
+	if (!apci3120_setup_chan_list(dev, s, 1, &insn->chanspec))
+		return -EINVAL;
 
-			/* Set the conversion time */
-			apci3120_timer_write(dev, 0, divisor);
+	/*
+	 * Timer 0 is used in MODE4 (software triggered strobe) to set the
+	 * conversion time for each acquisition. Each conversion is triggered
+	 * when the divisor is written to the timer, The conversion is done
+	 * when the EOC bit in the status register is '0'.
+	 */
+	apci3120_timer_set_mode(dev, 0, APCI3120_TIMER_MODE4);
+	apci3120_timer_enable(dev, 0, true);
 
-			us_TmpValue =
-				(unsigned short) inw(dev->iobase + APCI3120_RD_STATUS);
+	/* fixed conversion time of 10 us */
+	divisor = apci3120_ns_to_timer(dev, 0, 10000, CMDF_ROUND_NEAREST);
 
-			do {
-				/*  Waiting for the end of conversion */
-				us_TmpValue = inw(dev->iobase +
-						  APCI3120_RD_STATUS);
-			} while ((us_TmpValue & APCI3120_EOC) == APCI3120_EOC);
+	apci3120_ai_reset_fifo(dev);
 
-			/* Read the result in FIFO  and put it in insn data pointer */
-			us_TmpValue = inw(dev->iobase + 0);
-			*data = us_TmpValue;
+	for (i = 0; i < insn->n; i++) {
+		/* trigger conversion */
+		apci3120_timer_write(dev, 0, divisor);
 
-			apci3120_ai_reset_fifo(dev);
-			break;
-		default:
-			dev_err(dev->class_dev, "inputs wrong\n");
+		ret = comedi_timeout(dev, s, insn, apci3120_ai_eoc, 0);
+		if (ret)
+			return ret;
 
-		}
+		data[i] = inw(dev->iobase + 0);
 	}
 
 	return insn->n;
-
 }
 
 static int apci3120_reset(struct comedi_device *dev)
-- 
2.0.3

_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel




[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux