diff --git a/drivers/staging/comedi/drivers/addi_apci_2032.c b/drivers/staging/comedi/drivers/addi_apci_2032.c index 98691fdfbd48ce6cc4466bafe7109325ab8bf790..dd81ddc3d986a0188f8ea699dc39e187ac548d09 100644 --- a/drivers/staging/comedi/drivers/addi_apci_2032.c +++ b/drivers/staging/comedi/drivers/addi_apci_2032.c @@ -58,6 +58,12 @@ struct apci2032_private { unsigned int wdog_ctrl; }; +struct apci2032_int_private { + spinlock_t spinlock; + bool active; + unsigned char enabled_isns; +}; + static int apci2032_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, @@ -163,6 +169,16 @@ static int apci2032_int_insn_bits(struct comedi_device *dev, return insn->n; } +static void apci2032_int_stop(struct comedi_device *dev, + struct comedi_subdevice *s) +{ + struct apci2032_int_private *subpriv = s->private; + + subpriv->active = false; + subpriv->enabled_isns = 0; + outl(0x0, dev->iobase + APCI2032_INT_CTRL_REG); +} + static int apci2032_int_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) @@ -172,8 +188,8 @@ static int apci2032_int_cmdtest(struct comedi_device *dev, /* 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_OTHER); - err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW); + err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); + err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW); err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE); @@ -189,17 +205,9 @@ static int apci2032_int_cmdtest(struct comedi_device *dev, /* Step 3: check if arguments are trivially valid */ err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); - - /* - * 0 == no trigger - * 1 == trigger on VCC interrupt - * 2 == trigger on CC interrupt - * 3 == trigger on either VCC or CC interrupt - */ - err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg, 3); - + 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->scan_end_arg, cmd->chanlist_len); err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); if (err) @@ -217,8 +225,20 @@ static int apci2032_int_cmd(struct comedi_device *dev, struct comedi_subdevice *s) { struct comedi_cmd *cmd = &s->async->cmd; + struct apci2032_int_private *subpriv = s->private; + unsigned char enabled_isns; + unsigned int n; + unsigned long flags; + + enabled_isns = 0; + for (n = 0; n < cmd->chanlist_len; n++) + enabled_isns |= 1 << CR_CHAN(cmd->chanlist[n]); - outl(cmd->scan_begin_arg, dev->iobase + APCI2032_INT_CTRL_REG); + spin_lock_irqsave(&subpriv->spinlock, flags); + subpriv->enabled_isns = enabled_isns; + subpriv->active = true; + outl(subpriv->enabled_isns, dev->iobase + APCI2032_INT_CTRL_REG); + spin_unlock_irqrestore(&subpriv->spinlock, flags); return 0; } @@ -226,7 +246,13 @@ static int apci2032_int_cmd(struct comedi_device *dev, static int apci2032_int_cancel(struct comedi_device *dev, struct comedi_subdevice *s) { - outl(0x0, dev->iobase + APCI2032_INT_CTRL_REG); + struct apci2032_int_private *subpriv = s->private; + unsigned long flags; + + spin_lock_irqsave(&subpriv->spinlock, flags); + if (subpriv->active) + apci2032_int_stop(dev, s); + spin_unlock_irqrestore(&subpriv->spinlock, flags); return 0; } @@ -235,7 +261,9 @@ static irqreturn_t apci2032_interrupt(int irq, void *d) { struct comedi_device *dev = d; struct comedi_subdevice *s = dev->read_subdev; + struct apci2032_int_private *subpriv; unsigned int val; + bool do_event = false; if (!dev->attached) return IRQ_NONE; @@ -245,6 +273,9 @@ static irqreturn_t apci2032_interrupt(int irq, void *d) if (!val) return IRQ_NONE; + subpriv = s->private; + spin_lock(&subpriv->spinlock); + val = inl(dev->iobase + APCI2032_INT_STATUS_REG) & 3; /* Disable triggered interrupt sources. */ outl(~val & 3, dev->iobase + APCI2032_INT_CTRL_REG); @@ -254,11 +285,31 @@ static irqreturn_t apci2032_interrupt(int irq, void *d) * they'd keep triggering interrupts repeatedly. */ - if (comedi_buf_put(s->async, val)) - s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; - else - s->async->events |= COMEDI_CB_OVERFLOW; - comedi_event(dev, s); + if (subpriv->active && (val & subpriv->enabled_isns) != 0) { + unsigned short bits; + unsigned int n, len; + unsigned int *chanlist; + + /* Bits in scan data correspond to indices in channel list. */ + bits = 0; + len = s->async->cmd.chanlist_len; + chanlist = &s->async->cmd.chanlist[0]; + for (n = 0; n < len; n++) + if ((val & (1U << CR_CHAN(chanlist[n]))) != 0) + bits |= 1U << n; + + if (comedi_buf_put(s->async, bits)) { + s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; + } else { + apci2032_int_stop(dev, s); + s->async->events |= COMEDI_CB_OVERFLOW; + } + do_event = true; + } + + spin_unlock(&subpriv->spinlock); + if (do_event) + comedi_event(dev, s); return IRQ_HANDLED; } @@ -327,10 +378,18 @@ static int apci2032_auto_attach(struct comedi_device *dev, /* Initialize the interrupt subdevice */ s = &dev->subdevices[2]; if (dev->irq) { + struct apci2032_int_private *subpriv; + dev->read_subdev = s; + subpriv = kzalloc(sizeof(*subpriv), GFP_KERNEL); + if (!subpriv) + return -ENOMEM; + spin_lock_init(&subpriv->spinlock); + s->private = subpriv; s->type = COMEDI_SUBD_DI; s->subdev_flags = SDF_READABLE | SDF_CMD_READ; s->n_chan = 2; + s->len_chanlist = 2; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = apci2032_int_insn_bits; @@ -352,6 +411,8 @@ static void apci2032_detach(struct comedi_device *dev) apci2032_reset(dev); if (dev->irq) free_irq(dev->irq, dev); + if (dev->read_subdev) + kfree(dev->read_subdev->private); if (pcidev) { if (dev->iobase) comedi_pci_disable(pcidev);