staging: comedi: addi_apci_1032: conform to new INSN_CONFIG_DIGITAL_TRIG
authorIan Abbott <abbotti@mev.co.uk>
Wed, 14 Nov 2012 11:22:56 +0000 (11:22 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 15 Nov 2012 00:25:08 +0000 (16:25 -0800)
Conform to the new definition of the `INSN_CONFIG_DIGITAL_TRIG`
configuration instruction.

Return an error if the 'trigger number' in `data[1]` is non-zero or if
the configuration operation in `data[2]` is not supported.  Deal with
the 'left-shift' amount in `data[3]`.

The trigger's input channels can only be configured as a set of rising
and falling edges ('OR' mode) or as a set of high and low levels ('AND'
mode).  Preserve the old input channels to the right of the 'left-shift'
value except when switching modes.

(The 'left-shift' support is a bit of an overkill for this driver since
the trigger only has 16 input channels.)

Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/drivers/addi_apci_1032.c

index eb31375e72e69391ce1ca5dbe5bec720d5968162..0f47113ee0b600d1f4029001560a1fa6009699a2 100644 (file)
@@ -86,10 +86,14 @@ static int apci1032_reset(struct comedi_device *dev)
  * The COS interrupt must be configured before it can be enabled.
  *
  *     data[0] : INSN_CONFIG_DIGITAL_TRIG
- *     data[1] : 0 = OR (edge) interrupts
- *               1 = AND (level) interrupts
- *     data[2] : rising-edge/high level channels
- *     data[3] : falling-edge/low level channels
+ *     data[1] : trigger number (= 0)
+ *     data[2] : configuration operation:
+ *               COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
+ *               COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
+ *               COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
+ *     data[3] : left-shift for data[4] and data[5]
+ *     data[4] : rising-edge/high level channels
+ *     data[5] : falling-edge/low level channels
  */
 static int apci1032_cos_insn_config(struct comedi_device *dev,
                                    struct comedi_subdevice *s,
@@ -97,21 +101,59 @@ static int apci1032_cos_insn_config(struct comedi_device *dev,
                                    unsigned int *data)
 {
        struct apci1032_private *devpriv = dev->private;
+       unsigned int shift, oldmask;
 
        switch (data[0]) {
        case INSN_CONFIG_DIGITAL_TRIG:
-               devpriv->mode1 = data[2];
-               devpriv->mode2 = data[3];
-
-               if (devpriv->mode1 || devpriv->mode2) {
-                       devpriv->ctrl = APCI1032_CTRL_INT_ENA;
-                       if (data[1] == 1)
-                               devpriv->ctrl = APCI1032_CTRL_INT_AND;
-                       else
-                               devpriv->ctrl = APCI1032_CTRL_INT_OR;
-               } else {
+               if (data[1] != 0)
+                       return -EINVAL;
+               shift = data[3];
+               oldmask = (1U << shift) - 1;
+               switch (data[2]) {
+               case COMEDI_DIGITAL_TRIG_DISABLE:
                        devpriv->ctrl = 0;
+                       devpriv->mode1 = 0;
+                       devpriv->mode2 = 0;
                        apci1032_reset(dev);
+                       break;
+               case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
+                       if (devpriv->ctrl != (APCI1032_CTRL_INT_ENA |
+                                             APCI1032_CTRL_INT_OR)) {
+                               /* switching to 'OR' mode */
+                               devpriv->ctrl = APCI1032_CTRL_INT_ENA |
+                                               APCI1032_CTRL_INT_OR;
+                               /* wipe old channels */
+                               devpriv->mode1 = 0;
+                               devpriv->mode2 = 0;
+                       } else {
+                               /* preserve unspecified channels */
+                               devpriv->mode1 &= oldmask;
+                               devpriv->mode2 &= oldmask;
+                       }
+                       /* configure specified channels */
+                       devpriv->mode1 |= data[4] << shift;
+                       devpriv->mode2 |= data[5] << shift;
+                       break;
+               case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
+                       if (devpriv->ctrl != (APCI1032_CTRL_INT_ENA |
+                                             APCI1032_CTRL_INT_AND)) {
+                               /* switching to 'AND' mode */
+                               devpriv->ctrl = APCI1032_CTRL_INT_ENA |
+                                               APCI1032_CTRL_INT_AND;
+                               /* wipe old channels */
+                               devpriv->mode1 = 0;
+                               devpriv->mode2 = 0;
+                       } else {
+                               /* preserve unspecified channels */
+                               devpriv->mode1 &= oldmask;
+                               devpriv->mode2 &= oldmask;
+                       }
+                       /* configure specified channels */
+                       devpriv->mode1 |= data[4] << shift;
+                       devpriv->mode2 |= data[5] << shift;
+                       break;
+               default:
+                       return -EINVAL;
                }
                break;
        default: