static int pci9118_ai_cmd()

in drivers/adl_pci9118.c [916:1139]


static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
	struct pci9118_private *devpriv = dev->private;
	struct comedi_8254 *pacer = dev->pacer;
	struct comedi_cmd *cmd = &s->async->cmd;
	unsigned int addchans = 0;
	unsigned int scanlen;

	devpriv->ai12_startstop = 0;
	devpriv->ai_flags = cmd->flags;
	devpriv->ai_add_front = 0;
	devpriv->ai_add_back = 0;

	/* prepare for start/stop conditions */
	if (cmd->start_src == TRIG_EXT)
		devpriv->ai12_startstop |= START_AI_EXT;
	if (cmd->stop_src == TRIG_EXT) {
		devpriv->ai_neverending = 1;
		devpriv->ai12_startstop |= STOP_AI_EXT;
	}
	if (cmd->stop_src == TRIG_NONE)
		devpriv->ai_neverending = 1;
	if (cmd->stop_src == TRIG_COUNT)
		devpriv->ai_neverending = 0;

	/*
	 * use additional sample at end of every scan
	 * to satisty DMA 32 bit transfer?
	 */
	devpriv->ai_add_front = 0;
	devpriv->ai_add_back = 0;
	if (devpriv->master) {
		devpriv->usedma = 1;
		if ((cmd->flags & CMDF_WAKE_EOS) &&
		    (cmd->scan_end_arg == 1)) {
			if (cmd->convert_src == TRIG_NOW)
				devpriv->ai_add_back = 1;
			if (cmd->convert_src == TRIG_TIMER) {
				devpriv->usedma = 0;
					/*
					 * use INT transfer if scanlist
					 * have only one channel
					 */
			}
		}
		if ((cmd->flags & CMDF_WAKE_EOS) &&
		    (cmd->scan_end_arg & 1) &&
		    (cmd->scan_end_arg > 1)) {
			if (cmd->scan_begin_src == TRIG_FOLLOW) {
				devpriv->usedma = 0;
				/*
				 * XXX maybe can be corrected to use 16 bit DMA
				 */
			} else {	/*
					 * well, we must insert one sample
					 * to end of EOS to meet 32 bit transfer
					 */
				devpriv->ai_add_back = 1;
			}
		}
	} else {	/* interrupt transfer don't need any correction */
		devpriv->usedma = 0;
	}

	/*
	 * we need software S&H signal?
	 * It adds two samples before every scan as minimum
	 */
	if (cmd->convert_src == TRIG_NOW && devpriv->softsshdelay) {
		devpriv->ai_add_front = 2;
		if ((devpriv->usedma == 1) && (devpriv->ai_add_back == 1)) {
							/* move it to front */
			devpriv->ai_add_front++;
			devpriv->ai_add_back = 0;
		}
		if (cmd->convert_arg < devpriv->ai_ns_min)
			cmd->convert_arg = devpriv->ai_ns_min;
		addchans = devpriv->softsshdelay / cmd->convert_arg;
		if (devpriv->softsshdelay % cmd->convert_arg)
			addchans++;
		if (addchans > (devpriv->ai_add_front - 1)) {
							/* uff, still short */
			devpriv->ai_add_front = addchans + 1;
			if (devpriv->usedma == 1)
				if ((devpriv->ai_add_front +
				     cmd->chanlist_len +
				     devpriv->ai_add_back) & 1)
					devpriv->ai_add_front++;
							/* round up to 32 bit */
		}
	}
	/* well, we now know what must be all added */
	scanlen = devpriv->ai_add_front + cmd->chanlist_len +
		  devpriv->ai_add_back;
	/*
	 * what we must take from card in real to have cmd->scan_end_arg
	 * on output?
	 */
	devpriv->ai_n_realscanlen = scanlen *
				    (cmd->scan_end_arg / cmd->chanlist_len);

	if (scanlen > s->len_chanlist) {
		dev_err(dev->class_dev,
			"range/channel list is too long for actual configuration!\n");
		return -EINVAL;
	}

	/*
	 * Configure analog input and load the chanlist.
	 * The acquisition control bits are enabled later.
	 */
	pci9118_set_chanlist(dev, s, cmd->chanlist_len, cmd->chanlist,
			     devpriv->ai_add_front, devpriv->ai_add_back);

	/* Determine acquisition mode and calculate timing */
	devpriv->ai_do = 0;
	if (cmd->scan_begin_src != TRIG_TIMER &&
	    cmd->convert_src == TRIG_TIMER) {
		/* cascaded timers 1 and 2 are used for convert timing */
		if (cmd->scan_begin_src == TRIG_EXT)
			devpriv->ai_do = 4;
		else
			devpriv->ai_do = 1;

		comedi_8254_cascade_ns_to_timer(pacer, &cmd->convert_arg,
						devpriv->ai_flags &
						CMDF_ROUND_NEAREST);
		comedi_8254_update_divisors(pacer);

		devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR;

		if (!devpriv->usedma) {
			devpriv->ai_ctrl |= PCI9118_AI_CTRL_INT;
			devpriv->int_ctrl |= PCI9118_INT_CTRL_TIMER;
		}

		if (cmd->scan_begin_src == TRIG_EXT) {
			struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[0];

			devpriv->ai_cfg |= PCI9118_AI_CFG_AM;
			outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
			comedi_8254_load(pacer, 0, dmabuf->hw >> 1,
					 I8254_MODE0 | I8254_BINARY);
			devpriv->ai_cfg |= PCI9118_AI_CFG_START;
		}
	}

	if (cmd->scan_begin_src == TRIG_TIMER &&
	    cmd->convert_src != TRIG_EXT) {
		if (!devpriv->usedma) {
			dev_err(dev->class_dev,
				"cmd->scan_begin_src=TRIG_TIMER works only with bus mastering!\n");
			return -EIO;
		}

		/* double timed action */
		devpriv->ai_do = 2;

		pci9118_calc_divisors(dev, s,
				      &cmd->scan_begin_arg, &cmd->convert_arg,
				      devpriv->ai_flags,
				      devpriv->ai_n_realscanlen,
				      &pacer->divisor1,
				      &pacer->divisor2,
				      devpriv->ai_add_front);

		devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR;
		devpriv->ai_cfg |= PCI9118_AI_CFG_BM | PCI9118_AI_CFG_BS;
		if (cmd->convert_src == TRIG_NOW && !devpriv->softsshdelay)
			devpriv->ai_cfg |= PCI9118_AI_CFG_BSSH;
		outl(devpriv->ai_n_realscanlen,
		     dev->iobase + PCI9118_AI_BURST_NUM_REG);
	}

	if (cmd->scan_begin_src == TRIG_FOLLOW &&
	    cmd->convert_src == TRIG_EXT) {
		/* external trigger conversion */
		devpriv->ai_do = 3;

		devpriv->ai_ctrl |= PCI9118_AI_CTRL_EXTM;
	}

	if (devpriv->ai_do == 0) {
		dev_err(dev->class_dev,
			"Unable to determine acquisition mode! BUG in (*do_cmdtest)?\n");
		return -EINVAL;
	}

	if (devpriv->usedma)
		devpriv->ai_ctrl |= PCI9118_AI_CTRL_DMA;

	/* set default config (disable burst and triggers) */
	devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG;
	outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
	udelay(1);
	pci9118_ai_reset_fifo(dev);

	/* clear A/D and INT status registers */
	inl(dev->iobase + PCI9118_AI_STATUS_REG);
	inl(dev->iobase + PCI9118_INT_CTRL_REG);

	devpriv->ai_act_dmapos = 0;

	if (devpriv->usedma) {
		pci9118_ai_setup_dma(dev, s);

		outl(0x02000000 | AINT_WRITE_COMPL,
		     devpriv->iobase_a + AMCC_OP_REG_INTCSR);
	} else {
		pci9118_amcc_int_ena(dev, true);
	}

	/* start async command now or wait for internal trigger */
	if (cmd->start_src == TRIG_NOW)
		pci9118_ai_cmd_start(dev);
	else if (cmd->start_src == TRIG_INT)
		s->async->inttrig = pci9118_ai_inttrig;

	/* enable external trigger for command start/stop */
	if (cmd->start_src == TRIG_EXT || cmd->stop_src == TRIG_EXT)
		pci9118_exttrg_enable(dev, true);

	return 0;
}