staging: comedi: pcl726: add support for the external interrupt signal
authorH Hartley Sweeten <hsweeten@visionengravers.com>
Wed, 25 Sep 2013 22:41:40 +0000 (15:41 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 26 Sep 2013 16:32:20 +0000 (09:32 -0700)
The ACL-6126 board supports an external interrupt signal on pin 17 of
its I/O connector (CN3). Add a new subdevice to this driver to support
asynchronous commands with this input.

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/pcl726.c

index b7b14aaea4b0e2d16deb0131860e6d2ba04b3992..6df6e097b301a1da0a1776a0e6f83464aa2b32a1 100644 (file)
@@ -67,6 +67,8 @@ Interrupts are not supported.
 
 #include "../comedidev.h"
 
+#include "comedi_fc.h"
+
 #define PCL726_AO_MSB_REG(x)   (0x00 + ((x) * 2))
 #define PCL726_AO_LSB_REG(x)   (0x01 + ((x) * 2))
 #define PCL726_DO_MSB_REG      0x0c
@@ -157,10 +159,94 @@ static const struct pcl726_board boardtypes[] = {
 struct pcl726_private {
        const struct comedi_lrange *rangelist[12];
        unsigned int ao_readback[12];
+       unsigned int cmd_running:1;
 };
 
+static int pcl818_intr_insn_bits(struct comedi_device *dev,
+                                struct comedi_subdevice *s,
+                                struct comedi_insn *insn,
+                                unsigned int *data)
+{
+       data[1] = 0;
+       return insn->n;
+}
+
+static int pcl818_intr_cmdtest(struct comedi_device *dev,
+                              struct comedi_subdevice *s,
+                              struct comedi_cmd *cmd)
+{
+       int err = 0;
+
+       /* Step 1 : check if triggers are trivially valid */
+
+       err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+       err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+       err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+       err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+       err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+       if (err)
+               return 1;
+
+       /* Step 2a : make sure trigger sources are unique */
+       /* Step 2b : and mutually compatible */
+
+       if (err)
+               return 2;
+
+       /* Step 3: check if arguments are trivially valid */
+
+       err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
+       err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+       err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
+       err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, 1);
+       err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+       if (err)
+               return 3;
+
+       /* step 4: ignored */
+
+       if (err)
+               return 4;
+
+       return 0;
+}
+
+static int pcl818_intr_cmd(struct comedi_device *dev,
+                          struct comedi_subdevice *s)
+{
+       struct pcl726_private *devpriv = dev->private;
+
+       devpriv->cmd_running = 1;
+
+       return 0;
+}
+
+static int pcl818_intr_cancel(struct comedi_device *dev,
+                             struct comedi_subdevice *s)
+{
+       struct pcl726_private *devpriv = dev->private;
+
+       devpriv->cmd_running = 0;
+
+       return 0;
+}
+
 static irqreturn_t pcl818_interrupt(int irq, void *d)
 {
+       struct comedi_device *dev = d;
+       struct comedi_subdevice *s = dev->read_subdev;
+       struct pcl726_private *devpriv = dev->private;
+
+       if (devpriv->cmd_running) {
+               pcl818_intr_cancel(dev, s);
+
+               comedi_buf_put(s->async, 0);
+               s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
+               comedi_event(dev, s);
+       }
+
        return IRQ_HANDLED;
 }
 
@@ -262,6 +348,7 @@ static int pcl726_attach(struct comedi_device *dev,
        const struct pcl726_board *board = comedi_board(dev);
        struct pcl726_private *devpriv;
        struct comedi_subdevice *s;
+       int subdev;
        int ret;
        int i;
 
@@ -296,12 +383,17 @@ static int pcl726_attach(struct comedi_device *dev,
                        devpriv->rangelist[i] = &range_unknown;
        }
 
-       ret = comedi_alloc_subdevices(dev, board->have_dio ? 3 : 1);
+       subdev = board->have_dio ? 3 : 1;
+       if (dev->irq)
+               subdev++;
+       ret = comedi_alloc_subdevices(dev, subdev);
        if (ret)
                return ret;
 
+       subdev = 0;
+
        /* Analog Output subdevice */
-       s = &dev->subdevices[0];
+       s = &dev->subdevices[subdev++];
        s->type         = COMEDI_SUBD_AO;
        s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
        s->n_chan       = board->ao_nchan;
@@ -312,7 +404,7 @@ static int pcl726_attach(struct comedi_device *dev,
 
        if (board->have_dio) {
                /* Digital Input subdevice */
-               s = &dev->subdevices[1];
+               s = &dev->subdevices[subdev++];
                s->type         = COMEDI_SUBD_DI;
                s->subdev_flags = SDF_READABLE;
                s->n_chan       = 16;
@@ -321,7 +413,7 @@ static int pcl726_attach(struct comedi_device *dev,
                s->range_table  = &range_digital;
 
                /* Digital Output subdevice */
-               s = &dev->subdevices[2];
+               s = &dev->subdevices[subdev++];
                s->type         = COMEDI_SUBD_DO;
                s->subdev_flags = SDF_WRITABLE;
                s->n_chan       = 16;
@@ -330,6 +422,21 @@ static int pcl726_attach(struct comedi_device *dev,
                s->range_table  = &range_digital;
        }
 
+       if (dev->irq) {
+               /* Digial Input subdevice - Interrupt support */
+               s = &dev->subdevices[subdev++];
+               dev->read_subdev = s;
+               s->type         = COMEDI_SUBD_DI;
+               s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+               s->n_chan       = 1;
+               s->maxdata      = 1;
+               s->range_table  = &range_digital;
+               s->insn_bits    = pcl818_intr_insn_bits;
+               s->do_cmdtest   = pcl818_intr_cmdtest;
+               s->do_cmd       = pcl818_intr_cmd;
+               s->cancel       = pcl818_intr_cancel;
+       }
+
        return 0;
 }