|
@@ -2729,66 +2729,36 @@ static int ni_ao_insn_write(struct comedi_device *dev,
|
|
|
return insn->n;
|
|
|
}
|
|
|
|
|
|
-static int ni_ao_insn_config(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- struct comedi_insn *insn, unsigned int *data)
|
|
|
-{
|
|
|
- const struct ni_board_struct *board = dev->board_ptr;
|
|
|
- struct ni_private *devpriv = dev->private;
|
|
|
- unsigned int nbytes;
|
|
|
-
|
|
|
- switch (data[0]) {
|
|
|
- case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE:
|
|
|
- switch (data[1]) {
|
|
|
- case COMEDI_OUTPUT:
|
|
|
- nbytes = comedi_samples_to_bytes(s,
|
|
|
- board->ao_fifo_depth);
|
|
|
- data[2] = 1 + nbytes;
|
|
|
- if (devpriv->mite)
|
|
|
- data[2] += devpriv->mite->fifo_size;
|
|
|
- break;
|
|
|
- case COMEDI_INPUT:
|
|
|
- data[2] = 0;
|
|
|
- break;
|
|
|
- default:
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- return 0;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- return -EINVAL;
|
|
|
-}
|
|
|
-
|
|
|
-static int ni_ao_inttrig(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- unsigned int trig_num)
|
|
|
+/*
|
|
|
+ * Arms the AO device in preparation for a trigger event.
|
|
|
+ * This function also allocates and prepares a DMA channel (or FIFO if DMA is
|
|
|
+ * not used). As a part of this preparation, this function preloads the DAC
|
|
|
+ * registers with the first values of the output stream. This ensures that the
|
|
|
+ * first clock cycle after the trigger can be used for output.
|
|
|
+ *
|
|
|
+ * Note that this function _must_ happen after a user has written data to the
|
|
|
+ * output buffers via either mmap or write(fileno,...).
|
|
|
+ */
|
|
|
+static int ni_ao_arm(struct comedi_device *dev,
|
|
|
+ struct comedi_subdevice *s)
|
|
|
{
|
|
|
struct ni_private *devpriv = dev->private;
|
|
|
- struct comedi_cmd *cmd = &s->async->cmd;
|
|
|
int ret;
|
|
|
int interrupt_b_bits;
|
|
|
int i;
|
|
|
static const int timeout = 1000;
|
|
|
|
|
|
/*
|
|
|
- * Require trig_num == cmd->start_arg when cmd->start_src == TRIG_INT.
|
|
|
- * For backwards compatibility, also allow trig_num == 0 when
|
|
|
- * cmd->start_src != TRIG_INT (i.e. when cmd->start_src == TRIG_EXT);
|
|
|
- * in that case, the internal trigger is being used as a pre-trigger
|
|
|
- * before the external trigger.
|
|
|
+ * Prevent ao from doing things like trying to allocate the ao dma
|
|
|
+ * channel multiple times.
|
|
|
*/
|
|
|
- if (!(trig_num == cmd->start_arg ||
|
|
|
- (trig_num == 0 && cmd->start_src != TRIG_INT)))
|
|
|
+ if (!devpriv->ao_needs_arming) {
|
|
|
+ dev_dbg(dev->class_dev, "%s: device does not need arming!\n",
|
|
|
+ __func__);
|
|
|
return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
- * Null trig at beginning prevent ao start trigger from executing more
|
|
|
- * than once per command (and doing things like trying to allocate the
|
|
|
- * ao dma channel multiple times).
|
|
|
- */
|
|
|
- s->async->inttrig = NULL;
|
|
|
+ devpriv->ao_needs_arming = 0;
|
|
|
|
|
|
ni_set_bits(dev, NISTC_INTB_ENA_REG,
|
|
|
NISTC_INTB_ENA_AO_FIFO | NISTC_INTB_ENA_AO_ERR, 0);
|
|
@@ -2840,6 +2810,75 @@ static int ni_ao_inttrig(struct comedi_device *dev,
|
|
|
devpriv->ao_cmd1,
|
|
|
NISTC_AO_CMD1_REG);
|
|
|
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int ni_ao_insn_config(struct comedi_device *dev,
|
|
|
+ struct comedi_subdevice *s,
|
|
|
+ struct comedi_insn *insn, unsigned int *data)
|
|
|
+{
|
|
|
+ const struct ni_board_struct *board = dev->board_ptr;
|
|
|
+ struct ni_private *devpriv = dev->private;
|
|
|
+ unsigned int nbytes;
|
|
|
+
|
|
|
+ switch (data[0]) {
|
|
|
+ case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE:
|
|
|
+ switch (data[1]) {
|
|
|
+ case COMEDI_OUTPUT:
|
|
|
+ nbytes = comedi_samples_to_bytes(s,
|
|
|
+ board->ao_fifo_depth);
|
|
|
+ data[2] = 1 + nbytes;
|
|
|
+ if (devpriv->mite)
|
|
|
+ data[2] += devpriv->mite->fifo_size;
|
|
|
+ break;
|
|
|
+ case COMEDI_INPUT:
|
|
|
+ data[2] = 0;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ case INSN_CONFIG_ARM:
|
|
|
+ return ni_ao_arm(dev, s);
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+static int ni_ao_inttrig(struct comedi_device *dev,
|
|
|
+ struct comedi_subdevice *s,
|
|
|
+ unsigned int trig_num)
|
|
|
+{
|
|
|
+ struct ni_private *devpriv = dev->private;
|
|
|
+ struct comedi_cmd *cmd = &s->async->cmd;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Require trig_num == cmd->start_arg when cmd->start_src == TRIG_INT.
|
|
|
+ * For backwards compatibility, also allow trig_num == 0 when
|
|
|
+ * cmd->start_src != TRIG_INT (i.e. when cmd->start_src == TRIG_EXT);
|
|
|
+ * in that case, the internal trigger is being used as a pre-trigger
|
|
|
+ * before the external trigger.
|
|
|
+ */
|
|
|
+ if (!(trig_num == cmd->start_arg ||
|
|
|
+ (trig_num == 0 && cmd->start_src != TRIG_INT)))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Null trig at beginning prevent ao start trigger from executing more
|
|
|
+ * than once per command.
|
|
|
+ */
|
|
|
+ s->async->inttrig = NULL;
|
|
|
+
|
|
|
+ if (devpriv->ao_needs_arming) {
|
|
|
+ /* only arm this device if it still needs arming */
|
|
|
+ ret = ni_ao_arm(dev, s);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
ni_stc_writew(dev, NISTC_AO_CMD2_START1_PULSE | devpriv->ao_cmd2,
|
|
|
NISTC_AO_CMD2_REG);
|
|
|
|
|
@@ -3227,10 +3266,17 @@ static int ni_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
|
|
ni_ao_cmd_set_interrupts(dev, s);
|
|
|
|
|
|
/*
|
|
|
- * arm(ing) and star(ting) happen in ni_ao_inttrig, which _must_ be
|
|
|
- * called for ao commands since 1) TRIG_NOW is not supported and 2) DMA
|
|
|
- * must be setup and initially written to before arm/start happen.
|
|
|
+ * arm(ing) must happen later so that DMA can be setup and DACs
|
|
|
+ * preloaded with the actual output buffer before starting.
|
|
|
+ *
|
|
|
+ * start(ing) must happen _after_ arming is completed. Starting can be
|
|
|
+ * done either via ni_ao_inttrig, or via an external trigger.
|
|
|
+ *
|
|
|
+ * **Currently, ni_ao_inttrig will automatically attempt a call to
|
|
|
+ * ni_ao_arm if the device still needs arming at that point. This
|
|
|
+ * allows backwards compatibility.
|
|
|
*/
|
|
|
+ devpriv->ao_needs_arming = 1;
|
|
|
return 0;
|
|
|
}
|
|
|
|