Beispiel #1
0
static irqreturn_t apci1032_interrupt(int irq, void *d)
{
	struct comedi_device *dev = d;
	struct apci1032_private *devpriv = dev->private;
	struct comedi_subdevice *s = dev->read_subdev;
	unsigned int ctrl;

	/* check interrupt is from this device */
	if ((inl(devpriv->amcc_iobase + AMCC_OP_REG_INTCSR) &
	     INTCSR_INTR_ASSERTED) == 0)
		return IRQ_NONE;

	/* check interrupt is enabled */
	ctrl = inl(dev->iobase + APCI1032_CTRL_REG);
	if ((ctrl & APCI1032_CTRL_INT_ENA) == 0)
		return IRQ_HANDLED;

	/* disable the interrupt */
	outl(ctrl & ~APCI1032_CTRL_INT_ENA, dev->iobase + APCI1032_CTRL_REG);

	s->state = inl(dev->iobase + APCI1032_STATUS_REG) & 0xffff;
	comedi_buf_put(s, s->state);
	s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
	comedi_event(dev, s);

	/* enable the interrupt */
	outl(ctrl, dev->iobase + APCI1032_CTRL_REG);

	return IRQ_HANDLED;
}
Beispiel #2
0
static irqreturn_t pcl711_interrupt(int irq, void *d)
{
	int lo, hi;
	int data;
	struct comedi_device *dev = d;
	struct comedi_subdevice *s = dev->subdevices + 0;

	if (!dev->attached) {
		comedi_error(dev, "spurious interrupt");
		return IRQ_HANDLED;
	}

	hi = inb(dev->iobase + PCL711_AD_HI);
	lo = inb(dev->iobase + PCL711_AD_LO);
	outb(0, dev->iobase + PCL711_CLRINTR);

	data = (hi << 8) | lo;

	/* FIXME! Nothing else sets ntrig! */
	if (!(--devpriv->ntrig)) {
		if (this_board->is_8112)
			outb(1, dev->iobase + PCL711_MODE);
		else
			outb(0, dev->iobase + PCL711_MODE);

		s->async->events |= COMEDI_CB_EOA;
	}
	comedi_event(dev, s);
	return IRQ_HANDLED;
}
/*
 * 'do_cmd' function for an 'INTERRUPT' subdevice.
 */
static int dio200_subdev_intr_cmd(struct comedi_device *dev,
				  struct comedi_subdevice *s)
{
	struct comedi_cmd *cmd = &s->async->cmd;
	struct dio200_subdev_intr *subpriv = s->private;
	unsigned long flags;
	int event = 0;

	spin_lock_irqsave(&subpriv->spinlock, flags);
	subpriv->active = true;

	/* Set up end of acquisition. */
	if (cmd->stop_src == TRIG_COUNT)
		subpriv->stopcount = cmd->stop_arg;
	else	/* TRIG_NONE */
		subpriv->stopcount = 0;

	if (cmd->start_src == TRIG_INT)
		s->async->inttrig = dio200_inttrig_start_intr;
	else	/* TRIG_NOW */
		event = dio200_start_intr(dev, s);

	spin_unlock_irqrestore(&subpriv->spinlock, flags);

	if (event)
		comedi_event(dev, s);

	return 0;
}
Beispiel #4
0
void ni_pcidio_event(struct comedi_device *dev, struct comedi_subdevice *s)
{
	if (s->
	    async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
			     COMEDI_CB_OVERFLOW)) {
		ni_pcidio_cancel(dev, s);
	}
	comedi_event(dev, s);
}
Beispiel #5
0
static irqreturn_t atmio16d_interrupt(int irq, void *d)
{
	struct comedi_device *dev = d;
	struct comedi_subdevice *s = &dev->subdevices[0];

	comedi_buf_put(s->async, inw(dev->iobase + AD_FIFO_REG));

	comedi_event(dev, s);
	return IRQ_HANDLED;
}
Beispiel #6
0
static void usbduxsigma_ai_urb_complete(struct urb *urb)
{
	struct comedi_device *dev = urb->context;
	struct usbduxsigma_private *devpriv = dev->private;
	struct comedi_subdevice *s = dev->read_subdev;
	struct comedi_async *async = s->async;

	/* exit if not running a command, do not resubmit urb */
	if (!devpriv->ai_cmd_running)
		return;

	switch (urb->status) {
	case 0:
		/* copy the result in the transfer buffer */
		memcpy(devpriv->in_buf, urb->transfer_buffer, SIZEINBUF);
		usbduxsigma_ai_handle_urb(dev, s, urb);
		break;

	case -EILSEQ:
		/*
		 * error in the ISOchronous data
		 * we don't copy the data into the transfer buffer
		 * and recycle the last data byte
		 */
		dev_dbg(dev->class_dev, "CRC error in ISO IN stream\n");
		usbduxsigma_ai_handle_urb(dev, s, urb);
		break;

	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
	case -ECONNABORTED:
		/* happens after an unlink command */
		async->events |= COMEDI_CB_ERROR;
		break;

	default:
		/* a real error */
		dev_err(dev->class_dev, "%s: non-zero urb status (%d)\n",
			__func__, urb->status);
		async->events |= COMEDI_CB_ERROR;
		break;
	}

	/*
	 * comedi_handle_events() cannot be used in this driver. The (*cancel)
	 * operation would unlink the urb.
	 */
	if (async->events & COMEDI_CB_CANCEL_MASK)
		usbduxsigma_ai_stop(dev, 0);

	comedi_event(dev, s);
}
unsigned int cfc_handle_events(comedi_device *dev, comedi_subdevice *subd)
{
	unsigned int events = subd->async->events;

	if (events == 0)
		return events;

	if (events & (COMEDI_CB_EOA | COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW))
		subd->cancel(dev, subd);

	comedi_event(dev, subd);

	return events;
}
Beispiel #8
0
static void usbduxsigma_ao_urb_complete(struct urb *urb)
{
	struct comedi_device *dev = urb->context;
	struct usbduxsigma_private *devpriv = dev->private;
	struct comedi_subdevice *s = dev->write_subdev;
	struct comedi_async *async = s->async;

	/* exit if not running a command, do not resubmit urb */
	if (!devpriv->ao_cmd_running)
		return;

	switch (urb->status) {
	case 0:
		usbduxsigma_ao_handle_urb(dev, s, urb);
		break;

	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
	case -ECONNABORTED:
		/* happens after an unlink command */
		async->events |= COMEDI_CB_ERROR;
		break;

	default:
		/* a real error */
		dev_err(dev->class_dev, "%s: non-zero urb status (%d)\n",
			__func__, urb->status);
		async->events |= COMEDI_CB_ERROR;
		break;
	}

	/*
	 * comedi_handle_events() cannot be used in this driver. The (*cancel)
	 * operation would unlink the urb.
	 */
	if (async->events & COMEDI_CB_CANCEL_MASK)
		usbduxsigma_ao_stop(dev, 0);

	comedi_event(dev, s);
}
Beispiel #9
0
static irqreturn_t dt2811_interrupt(int irq, void *d)
{
	int lo, hi;
	int data;
	struct comedi_device *dev = d;

	if (!dev->attached) {
		comedi_error(dev, "spurious interrupt");
		return IRQ_HANDLED;
	}

	lo = inb(dev->iobase + DT2811_ADDATLO);
	hi = inb(dev->iobase + DT2811_ADDATHI);

	data = lo + (hi << 8);

	if (!(--devpriv->ntrig)) {
		/* how to turn off acquisition */
		s->async->events |= COMEDI_SB_EOA;
	}
	comedi_event(dev, s);
	return IRQ_HANDLED;
}
static int dio200_inttrig_start_intr(struct comedi_device *dev,
				     struct comedi_subdevice *s,
				     unsigned int trig_num)
{
	struct dio200_subdev_intr *subpriv = s->private;
	struct comedi_cmd *cmd = &s->async->cmd;
	unsigned long flags;
	int event = 0;

	if (trig_num != cmd->start_arg)
		return -EINVAL;

	spin_lock_irqsave(&subpriv->spinlock, flags);
	s->async->inttrig = NULL;
	if (subpriv->active)
		event = dio200_start_intr(dev, s);

	spin_unlock_irqrestore(&subpriv->spinlock, flags);

	if (event)
		comedi_event(dev, s);

	return 1;
}
Beispiel #11
0
static void usbduxsub_ao_isoc_irq(struct urb *urb)
{
	struct comedi_device *dev = urb->context;
	struct comedi_subdevice *s = dev->write_subdev;
	struct usbdux_private *devpriv = dev->private;
	int8_t *datap;
	int len;
	int ret;
	int i;

	switch (urb->status) {
	case 0:
		/* success */
		break;

	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
	case -ECONNABORTED:
		/* after an unlink command, unplug, ... etc */
		/* no unlink needed here. Already shutting down. */
		if (devpriv->ao_cmd_running) {
			s->async->events |= COMEDI_CB_EOA;
			comedi_event(dev, s);
			usbdux_ao_stop(dev, 0);
		}
		return;

	default:
		/* a real error */
		if (devpriv->ao_cmd_running) {
			dev_err(dev->class_dev,
				"Non-zero urb status received in ao intr context: %d\n",
				urb->status);
			s->async->events |= COMEDI_CB_ERROR;
			s->async->events |= COMEDI_CB_EOA;
			comedi_event(dev, s);
			/* we do an unlink if we are in the high speed mode */
			usbdux_ao_stop(dev, 0);
		}
		return;
	}

	/* are we actually running? */
	if (!devpriv->ao_cmd_running)
		return;

	/* normal operation: executing a command in this subdevice */
	devpriv->ao_counter--;
	if ((int)devpriv->ao_counter <= 0) {
		/* timer zero */
		devpriv->ao_counter = devpriv->ao_timer;

		/* handle non continous acquisition */
		if (!devpriv->ao_continous) {
			/* fixed number of samples */
			devpriv->ao_sample_count--;
			if (devpriv->ao_sample_count < 0) {
				/* all samples transmitted */
				usbdux_ao_stop(dev, 0);
				s->async->events |= COMEDI_CB_EOA;
				comedi_event(dev, s);
				/* no resubmit of the urb */
				return;
			}
		}

		/* transmit data to the USB bus */
		datap = urb->transfer_buffer;
		len = s->async->cmd.chanlist_len;
		*datap++ = len;
		for (i = 0; i < s->async->cmd.chanlist_len; i++) {
			unsigned int chan = devpriv->ao_chanlist[i];
			short val;

			ret = comedi_buf_get(s->async, &val);
			if (ret < 0) {
				dev_err(dev->class_dev, "buffer underflow\n");
				s->async->events |= (COMEDI_CB_EOA |
						     COMEDI_CB_OVERFLOW);
			}
			/* pointer to the DA */
			*datap++ = val & 0xff;
			*datap++ = (val >> 8) & 0xff;
			*datap++ = chan << 6;
			devpriv->ao_readback[chan] = val;

			s->async->events |= COMEDI_CB_BLOCK;
			comedi_event(dev, s);
		}
	}
Beispiel #12
0
/* analogue IN - interrupt service routine */
static void usbduxsub_ai_isoc_irq(struct urb *urb)
{
	struct comedi_device *dev = urb->context;
	struct comedi_subdevice *s = dev->read_subdev;
	struct usbdux_private *devpriv = dev->private;
	int i, err, n;

	/* first we test if something unusual has just happened */
	switch (urb->status) {
	case 0:
		/* copy the result in the transfer buffer */
		memcpy(devpriv->in_buf, urb->transfer_buffer, SIZEINBUF);
		break;
	case -EILSEQ:
		/* error in the ISOchronous data */
		/* we don't copy the data into the transfer buffer */
		/* and recycle the last data byte */
		dev_dbg(dev->class_dev, "CRC error in ISO IN stream\n");
		break;

	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
	case -ECONNABORTED:
		/* happens after an unlink command */
		if (devpriv->ai_cmd_running) {
			s->async->events |= COMEDI_CB_EOA;
			s->async->events |= COMEDI_CB_ERROR;
			comedi_event(dev, s);
			/* stop the transfer w/o unlink */
			usbdux_ai_stop(dev, 0);
		}
		return;

	default:
		/* a real error on the bus */
		/* pass error to comedi if we are really running a command */
		if (devpriv->ai_cmd_running) {
			dev_err(dev->class_dev,
				"Non-zero urb status received in ai intr context: %d\n",
				urb->status);
			s->async->events |= COMEDI_CB_EOA;
			s->async->events |= COMEDI_CB_ERROR;
			comedi_event(dev, s);
			/* don't do an unlink here */
			usbdux_ai_stop(dev, 0);
		}
		return;
	}

	/*
	 * at this point we are reasonably sure that nothing dodgy has happened
	 * are we running a command?
	 */
	if (unlikely(!devpriv->ai_cmd_running)) {
		/*
		 * not running a command, do not continue execution if no
		 * asynchronous command is running in particular not resubmit
		 */
		return;
	}

	urb->dev = comedi_to_usb_dev(dev);

	/* resubmit the urb */
	err = usb_submit_urb(urb, GFP_ATOMIC);
	if (unlikely(err < 0)) {
		dev_err(dev->class_dev,
			"urb resubmit failed in int-context! err=%d\n", err);
		if (err == -EL2NSYNC)
			dev_err(dev->class_dev,
				"buggy USB host controller or bug in IRQ handler!\n");
		s->async->events |= COMEDI_CB_EOA;
		s->async->events |= COMEDI_CB_ERROR;
		comedi_event(dev, s);
		/* don't do an unlink here */
		usbdux_ai_stop(dev, 0);
		return;
	}

	devpriv->ai_counter--;
	if (likely(devpriv->ai_counter > 0))
		return;

	/* timer zero, transfer measurements to comedi */
	devpriv->ai_counter = devpriv->ai_timer;

	/* test, if we transmit only a fixed number of samples */
	if (!devpriv->ai_continous) {
		/* not continuous, fixed number of samples */
		devpriv->ai_sample_count--;
		/* all samples received? */
		if (devpriv->ai_sample_count < 0) {
			/* prevent a resubmit next time */
			usbdux_ai_stop(dev, 0);
			/* say comedi that the acquistion is over */
			s->async->events |= COMEDI_CB_EOA;
			comedi_event(dev, s);
			return;
		}
	}
	/* get the data from the USB bus and hand it over to comedi */
	n = s->async->cmd.chanlist_len;
	for (i = 0; i < n; i++) {
		unsigned int range = CR_RANGE(s->async->cmd.chanlist[i]);
		int16_t val = le16_to_cpu(devpriv->in_buf[i]);

		/* bipolar data is two's-complement */
		if (comedi_range_is_bipolar(s, range))
			val ^= ((s->maxdata + 1) >> 1);

		/* transfer data */
		err = comedi_buf_put(s->async, val);
		if (unlikely(err == 0)) {
			/* buffer overflow */
			usbdux_ai_stop(dev, 0);
			return;
		}
	}
	/* tell comedi that data is there */
	s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
	comedi_event(dev, s);
}
Beispiel #13
0
static void usbduxsigma_ao_urb_complete(struct urb *urb)
{
	struct comedi_device *dev = urb->context;
	struct usbduxsigma_private *devpriv = dev->private;
	struct comedi_subdevice *s = dev->write_subdev;
	struct comedi_cmd *cmd = &s->async->cmd;
	uint8_t *datap;
	int ret;
	int i;

	switch (urb->status) {
	case 0:
		/* success */
		break;

	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
	case -ECONNABORTED:
		/* happens after an unlink command */
		if (devpriv->ao_cmd_running) {
			usbduxsigma_ao_stop(dev, 0);	/* w/o unlink */
			s->async->events |= COMEDI_CB_EOA;
			comedi_event(dev, s);
		}
		return;

	default:
		/* a real error */
		if (devpriv->ao_cmd_running) {
			dev_err(dev->class_dev,
				"%s: non-zero urb status (%d)\n",
				__func__, urb->status);
			usbduxsigma_ao_stop(dev, 0);	/* w/o unlink */
			s->async->events |= (COMEDI_CB_ERROR | COMEDI_CB_EOA);
			comedi_event(dev, s);
		}
		return;
	}

	if (!devpriv->ao_cmd_running)
		return;

	devpriv->ao_counter--;
	if ((int)devpriv->ao_counter <= 0) {
		/* timer zero, transfer from comedi */
		devpriv->ao_counter = devpriv->ao_timer;

		if (cmd->stop_src == TRIG_COUNT) {
			/* not continuous, fixed number of samples */
			devpriv->ao_sample_count--;
			if (devpriv->ao_sample_count < 0) {
				usbduxsigma_ao_stop(dev, 0);	/* w/o unlink */
				/* acquistion is over, tell comedi */
				s->async->events |= COMEDI_CB_EOA;
				comedi_event(dev, s);
				return;
			}
		}

		/* transmit data to the USB bus */
		datap = urb->transfer_buffer;
		*datap++ = cmd->chanlist_len;
		for (i = 0; i < cmd->chanlist_len; i++) {
			unsigned int chan = CR_CHAN(cmd->chanlist[i]);
			unsigned short val;

			ret = comedi_buf_get(s, &val);
			if (ret < 0) {
				dev_err(dev->class_dev, "buffer underflow\n");
				s->async->events |= (COMEDI_CB_EOA |
						     COMEDI_CB_OVERFLOW);
			}
			*datap++ = val;
			*datap++ = chan;
			s->readback[chan] = val;

			s->async->events |= COMEDI_CB_BLOCK;
			comedi_event(dev, s);
		}
	}

	urb->transfer_buffer_length = SIZEOUTBUF;
	urb->dev = comedi_to_usb_dev(dev);
	urb->status = 0;
	if (devpriv->high_speed)
		urb->interval = 8;	/* uframes */
	else
		urb->interval = 1;	/* frames */
	urb->number_of_packets = 1;
	urb->iso_frame_desc[0].offset = 0;
	urb->iso_frame_desc[0].length = SIZEOUTBUF;
	urb->iso_frame_desc[0].status = 0;
	ret = usb_submit_urb(urb, GFP_ATOMIC);
	if (ret < 0) {
		dev_err(dev->class_dev,
			"%s: urb resubmit failed (%d)\n",
			__func__, ret);
		if (ret == -EL2NSYNC)
			dev_err(dev->class_dev,
				"buggy USB host controller or bug in IRQ handler\n");
		usbduxsigma_ao_stop(dev, 0);	/* w/o unlink */
		s->async->events |= (COMEDI_CB_EOA | COMEDI_CB_ERROR);
		comedi_event(dev, s);
	}
}
Beispiel #14
0
static void usbduxsigma_ai_urb_complete(struct urb *urb)
{
	struct comedi_device *dev = urb->context;
	struct usbduxsigma_private *devpriv = dev->private;
	struct comedi_subdevice *s = dev->read_subdev;
	struct comedi_cmd *cmd = &s->async->cmd;
	unsigned int dio_state;
	uint32_t val;
	int ret;
	int i;

	/* first we test if something unusual has just happened */
	switch (urb->status) {
	case 0:
		/* copy the result in the transfer buffer */
		memcpy(devpriv->in_buf, urb->transfer_buffer, SIZEINBUF);
		break;
	case -EILSEQ:
		/*
		 * error in the ISOchronous data
		 * we don't copy the data into the transfer buffer
		 * and recycle the last data byte
		 */
		dev_dbg(dev->class_dev, "CRC error in ISO IN stream\n");

		break;

	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
	case -ECONNABORTED:
		/* happens after an unlink command */
		if (devpriv->ai_cmd_running) {
			usbduxsigma_ai_stop(dev, 0);	/* w/o unlink */
			/* we are still running a command, tell comedi */
			s->async->events |= (COMEDI_CB_EOA | COMEDI_CB_ERROR);
			comedi_event(dev, s);
		}
		return;

	default:
		/*
		 * a real error on the bus
		 * pass error to comedi if we are really running a command
		 */
		if (devpriv->ai_cmd_running) {
			dev_err(dev->class_dev,
				"%s: non-zero urb status (%d)\n",
				__func__, urb->status);
			usbduxsigma_ai_stop(dev, 0);	/* w/o unlink */
			s->async->events |= (COMEDI_CB_EOA | COMEDI_CB_ERROR);
			comedi_event(dev, s);
		}
		return;
	}

	if (unlikely(!devpriv->ai_cmd_running))
		return;

	urb->dev = comedi_to_usb_dev(dev);

	ret = usb_submit_urb(urb, GFP_ATOMIC);
	if (unlikely(ret < 0)) {
		dev_err(dev->class_dev, "%s: urb resubmit failed (%d)\n",
			__func__, ret);
		if (ret == -EL2NSYNC)
			dev_err(dev->class_dev,
				"buggy USB host controller or bug in IRQ handler\n");
		usbduxsigma_ai_stop(dev, 0);	/* w/o unlink */
		s->async->events |= (COMEDI_CB_EOA | COMEDI_CB_ERROR);
		comedi_event(dev, s);
		return;
	}

	/* get the state of the dio pins to allow external trigger */
	dio_state = be32_to_cpu(devpriv->in_buf[0]);

	devpriv->ai_counter--;
	if (likely(devpriv->ai_counter > 0))
		return;

	/* timer zero, transfer measurements to comedi */
	devpriv->ai_counter = devpriv->ai_timer;

	if (cmd->stop_src == TRIG_COUNT) {
		/* not continuous, fixed number of samples */
		devpriv->ai_sample_count--;
		if (devpriv->ai_sample_count < 0) {
			usbduxsigma_ai_stop(dev, 0);	/* w/o unlink */
			/* acquistion is over, tell comedi */
			s->async->events |= COMEDI_CB_EOA;
			comedi_event(dev, s);
			return;
		}
	}

	/* get the data from the USB bus and hand it over to comedi */
	for (i = 0; i < cmd->chanlist_len; i++) {
		/* transfer data, note first byte is the DIO state */
		val = be32_to_cpu(devpriv->in_buf[i+1]);
		val &= 0x00ffffff;	/* strip status byte */
		val ^= 0x00800000;	/* convert to unsigned */

		ret = cfc_write_array_to_buffer(s, &val, sizeof(uint32_t));
		if (unlikely(ret == 0)) {
			/* buffer overflow */
			usbduxsigma_ai_stop(dev, 0);	/* w/o unlink */
			return;
		}
	}
	/* tell comedi that data is there */
	s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
	comedi_event(dev, s);
}
/*
 * This is called from the interrupt service routine to handle a read
 * scan on an 'INTERRUPT' subdevice.
 */
static int dio200_handle_read_intr(struct comedi_device *dev,
				   struct comedi_subdevice *s)
{
	const struct dio200_layout *layout = dio200_dev_layout(dev);
	struct dio200_subdev_intr *subpriv = s->private;
	unsigned triggered;
	unsigned intstat;
	unsigned cur_enabled;
	unsigned int oldevents;
	unsigned long flags;

	triggered = 0;

	spin_lock_irqsave(&subpriv->spinlock, flags);
	oldevents = s->async->events;
	if (layout->has_int_sce) {
		/*
		 * Collect interrupt sources that have triggered and disable
		 * them temporarily.  Loop around until no extra interrupt
		 * sources have triggered, at which point, the valid part of
		 * the interrupt status register will read zero, clearing the
		 * cause of the interrupt.
		 *
		 * Mask off interrupt sources already seen to avoid infinite
		 * loop in case of misconfiguration.
		 */
		cur_enabled = subpriv->enabled_isns;
		while ((intstat = (dio200_read8(dev, subpriv->ofs) &
				   subpriv->valid_isns & ~triggered)) != 0) {
			triggered |= intstat;
			cur_enabled &= ~triggered;
			dio200_write8(dev, subpriv->ofs, cur_enabled);
		}
	} else {
		/*
		 * No interrupt status register.  Assume the single interrupt
		 * source has triggered.
		 */
		triggered = subpriv->enabled_isns;
	}

	if (triggered) {
		/*
		 * Some interrupt sources have triggered and have been
		 * temporarily disabled to clear the cause of the interrupt.
		 *
		 * Reenable them NOW to minimize the time they are disabled.
		 */
		cur_enabled = subpriv->enabled_isns;
		if (layout->has_int_sce)
			dio200_write8(dev, subpriv->ofs, cur_enabled);

		if (subpriv->active) {
			/*
			 * The command is still active.
			 *
			 * Ignore interrupt sources that the command isn't
			 * interested in (just in case there's a race
			 * condition).
			 */
			if (triggered & subpriv->enabled_isns)
				/* Collect scan data. */
				dio200_read_scan_intr(dev, s, triggered);
		}
	}
	spin_unlock_irqrestore(&subpriv->spinlock, flags);

	if (oldevents != s->async->events)
		comedi_event(dev, s);

	return (triggered != 0);
}