Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/comedi/drivers/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 172 kB image not shown  

Quelle  ni_mio_common.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * Hardware driver for DAQ-STC based boards
 *
 * COMEDI - Linux Control and Measurement Device Interface
 * Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org>
 * Copyright (C) 2002-2006 Frank Mori Hess <fmhess@users.sourceforge.net>
 */


/*
 * This file is meant to be included by another file, e.g.,
 * ni_atmio.c or ni_pcimio.c.
 *
 * Interrupt support originally added by Truxton Fulton <trux@truxton.com>
 *
 * References (ftp://ftp.natinst.com/support/manuals):
 *   340747b.pdf  AT-MIO E series Register Level Programmer Manual
 *   341079b.pdf  PCI E Series RLPM
 *   340934b.pdf  DAQ-STC reference manual
 *
 * 67xx and 611x registers (ftp://ftp.ni.com/support/daq/mhddk/documentation/)
 *   release_ni611x.pdf
 *   release_ni67xx.pdf
 *
 * Other possibly relevant info:
 *   320517c.pdf  User manual (obsolete)
 *   320517f.pdf  User manual (new)
 *   320889a.pdf  delete
 *   320906c.pdf  maximum signal ratings
 *   321066a.pdf  about 16x
 *   321791a.pdf  discontinuation of at-mio-16e-10 rev. c
 *   321808a.pdf  about at-mio-16e-10 rev P
 *   321837a.pdf  discontinuation of at-mio-16de-10 rev d
 *   321838a.pdf  about at-mio-16de-10 rev N
 *
 * ISSUES:
 *   - the interrupt routine needs to be cleaned up
 *
 * 2006-02-07: S-Series PCI-6143: Support has been added but is not
 * fully tested as yet. Terry Barnaby, BEAM Ltd.
 */


#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/comedi/comedi_8255.h>
#include "mite.h"

#ifdef PCIDMA
#define IS_PCIMIO 1
#else
#define IS_PCIMIO 0
#endif

/* A timeout count */
#define NI_TIMEOUT 1000

/* Note: this table must match the ai_gain_* definitions */
static const short ni_gainlkup[][16] = {
 [ai_gain_16] = {0, 1, 2, 3, 4, 5, 6, 7,
   0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107},
 [ai_gain_8] = {1, 2, 4, 7, 0x101, 0x102, 0x104, 0x107},
 [ai_gain_14] = {1, 2, 3, 4, 5, 6, 7,
   0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107},
 [ai_gain_4] = {0, 1, 4, 7},
 [ai_gain_611x] = {0x00a, 0x00b, 0x001, 0x002,
     0x003, 0x004, 0x005, 0x006},
 [ai_gain_622x] = {0, 1, 4, 5},
 [ai_gain_628x] = {1, 2, 3, 4, 5, 6, 7},
 [ai_gain_6143] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
};

static const struct comedi_lrange range_ni_E_ai = {
 16, {
  BIP_RANGE(10),
  BIP_RANGE(5),
  BIP_RANGE(2.5),
  BIP_RANGE(1),
  BIP_RANGE(0.5),
  BIP_RANGE(0.25),
  BIP_RANGE(0.1),
  BIP_RANGE(0.05),
  UNI_RANGE(20),
  UNI_RANGE(10),
  UNI_RANGE(5),
  UNI_RANGE(2),
  UNI_RANGE(1),
  UNI_RANGE(0.5),
  UNI_RANGE(0.2),
  UNI_RANGE(0.1)
 }
};

static const struct comedi_lrange range_ni_E_ai_limited = {
 8, {
  BIP_RANGE(10),
  BIP_RANGE(5),
  BIP_RANGE(1),
  BIP_RANGE(0.1),
  UNI_RANGE(10),
  UNI_RANGE(5),
  UNI_RANGE(1),
  UNI_RANGE(0.1)
 }
};

static const struct comedi_lrange range_ni_E_ai_limited14 = {
 14, {
  BIP_RANGE(10),
  BIP_RANGE(5),
  BIP_RANGE(2),
  BIP_RANGE(1),
  BIP_RANGE(0.5),
  BIP_RANGE(0.2),
  BIP_RANGE(0.1),
  UNI_RANGE(10),
  UNI_RANGE(5),
  UNI_RANGE(2),
  UNI_RANGE(1),
  UNI_RANGE(0.5),
  UNI_RANGE(0.2),
  UNI_RANGE(0.1)
 }
};

static const struct comedi_lrange range_ni_E_ai_bipolar4 = {
 4, {
  BIP_RANGE(10),
  BIP_RANGE(5),
  BIP_RANGE(0.5),
  BIP_RANGE(0.05)
 }
};

static const struct comedi_lrange range_ni_E_ai_611x = {
 8, {
  BIP_RANGE(50),
  BIP_RANGE(20),
  BIP_RANGE(10),
  BIP_RANGE(5),
  BIP_RANGE(2),
  BIP_RANGE(1),
  BIP_RANGE(0.5),
  BIP_RANGE(0.2)
 }
};

static const struct comedi_lrange range_ni_M_ai_622x = {
 4, {
  BIP_RANGE(10),
  BIP_RANGE(5),
  BIP_RANGE(1),
  BIP_RANGE(0.2)
 }
};

static const struct comedi_lrange range_ni_M_ai_628x = {
 7, {
  BIP_RANGE(10),
  BIP_RANGE(5),
  BIP_RANGE(2),
  BIP_RANGE(1),
  BIP_RANGE(0.5),
  BIP_RANGE(0.2),
  BIP_RANGE(0.1)
 }
};

static const struct comedi_lrange *const ni_range_lkup[] = {
 [ai_gain_16] = &range_ni_E_ai,
 [ai_gain_8] = &range_ni_E_ai_limited,
 [ai_gain_14] = &range_ni_E_ai_limited14,
 [ai_gain_4] = &range_ni_E_ai_bipolar4,
 [ai_gain_611x] = &range_ni_E_ai_611x,
 [ai_gain_622x] = &range_ni_M_ai_622x,
 [ai_gain_628x] = &range_ni_M_ai_628x,
 [ai_gain_6143] = &range_bipolar5
};

enum aimodes {
 AIMODE_NONE = 0,
 AIMODE_HALF_FULL = 1,
 AIMODE_SCAN = 2,
 AIMODE_SAMPLE = 3,
};

enum ni_common_subdevices {
 NI_AI_SUBDEV,
 NI_AO_SUBDEV,
 NI_DIO_SUBDEV,
 NI_8255_DIO_SUBDEV,
 NI_UNUSED_SUBDEV,
 NI_CALIBRATION_SUBDEV,
 NI_EEPROM_SUBDEV,
 NI_PFI_DIO_SUBDEV,
 NI_CS5529_CALIBRATION_SUBDEV,
 NI_SERIAL_SUBDEV,
 NI_RTSI_SUBDEV,
 NI_GPCT0_SUBDEV,
 NI_GPCT1_SUBDEV,
 NI_FREQ_OUT_SUBDEV,
 NI_NUM_SUBDEVICES
};

#define NI_GPCT_SUBDEV(x) (NI_GPCT0_SUBDEV + (x))

enum timebase_nanoseconds {
 TIMEBASE_1_NS = 50,
 TIMEBASE_2_NS = 10000
};

#define SERIAL_DISABLED  0
#define SERIAL_600NS  600
#define SERIAL_1_2US  1200
#define SERIAL_10US   10000

static const int num_adc_stages_611x = 3;

#ifdef PCIDMA

static void ni_writel(struct comedi_device *dev, unsigned int data, int reg)
{
 writel(data, dev->mmio + reg);
}

static void ni_writew(struct comedi_device *dev, unsigned int data, int reg)
{
 writew(data, dev->mmio + reg);
}

static void ni_writeb(struct comedi_device *dev, unsigned int data, int reg)
{
 writeb(data, dev->mmio + reg);
}

static unsigned int ni_readl(struct comedi_device *dev, int reg)
{
 return readl(dev->mmio + reg);
}

static unsigned int ni_readw(struct comedi_device *dev, int reg)
{
 return readw(dev->mmio + reg);
}

static unsigned int ni_readb(struct comedi_device *dev, int reg)
{
 return readb(dev->mmio + reg);
}

#else /* PCIDMA */

static void ni_writel(struct comedi_device *dev, unsigned int data, int reg)
{
 outl(data, dev->iobase + reg);
}

static void ni_writew(struct comedi_device *dev, unsigned int data, int reg)
{
 outw(data, dev->iobase + reg);
}

static void ni_writeb(struct comedi_device *dev, unsigned int data, int reg)
{
 outb(data, dev->iobase + reg);
}

static unsigned int ni_readl(struct comedi_device *dev, int reg)
{
 return inl(dev->iobase + reg);
}

static unsigned int ni_readw(struct comedi_device *dev, int reg)
{
 return inw(dev->iobase + reg);
}

static unsigned int ni_readb(struct comedi_device *dev, int reg)
{
 return inb(dev->iobase + reg);
}

#endif /* PCIDMA */

/*
 * We automatically take advantage of STC registers that can be
 * read/written directly in the I/O space of the board.
 *
 * The AT-MIO and DAQCard devices map the low 8 STC registers to
 * iobase+reg*2.
 *
 * Most PCIMIO devices also map the low 8 STC registers but the
 * 611x devices map the read registers to iobase+(addr-1)*2.
 * For now non-windowed STC access is disabled if a PCIMIO device
 * is detected (devpriv->mite has been initialized).
 *
 * The M series devices do not used windowed registers for the
 * STC registers. The functions below handle the mapping of the
 * windowed STC registers to the m series register offsets.
 */


struct mio_regmap {
 unsigned int mio_reg;
 int size;
};

static const struct mio_regmap m_series_stc_write_regmap[] = {
 [NISTC_INTA_ACK_REG]  = { 0x104, 2 },
 [NISTC_INTB_ACK_REG]  = { 0x106, 2 },
 [NISTC_AI_CMD2_REG]  = { 0x108, 2 },
 [NISTC_AO_CMD2_REG]  = { 0x10a, 2 },
 [NISTC_G0_CMD_REG]  = { 0x10c, 2 },
 [NISTC_G1_CMD_REG]  = { 0x10e, 2 },
 [NISTC_AI_CMD1_REG]  = { 0x110, 2 },
 [NISTC_AO_CMD1_REG]  = { 0x112, 2 },
 /*
 * NISTC_DIO_OUT_REG maps to:
 * { NI_M_DIO_REG, 4 } and { NI_M_SCXI_SER_DO_REG, 1 }
 */

 [NISTC_DIO_OUT_REG]  = { 0, 0 }, /* DOES NOT MAP CLEANLY */
 [NISTC_DIO_CTRL_REG]  = { 0, 0 }, /* DOES NOT MAP CLEANLY */
 [NISTC_AI_MODE1_REG]  = { 0x118, 2 },
 [NISTC_AI_MODE2_REG]  = { 0x11a, 2 },
 [NISTC_AI_SI_LOADA_REG]  = { 0x11c, 4 },
 [NISTC_AI_SI_LOADB_REG]  = { 0x120, 4 },
 [NISTC_AI_SC_LOADA_REG]  = { 0x124, 4 },
 [NISTC_AI_SC_LOADB_REG]  = { 0x128, 4 },
 [NISTC_AI_SI2_LOADA_REG] = { 0x12c, 4 },
 [NISTC_AI_SI2_LOADB_REG] = { 0x130, 4 },
 [NISTC_G0_MODE_REG]  = { 0x134, 2 },
 [NISTC_G1_MODE_REG]  = { 0x136, 2 },
 [NISTC_G0_LOADA_REG]  = { 0x138, 4 },
 [NISTC_G0_LOADB_REG]  = { 0x13c, 4 },
 [NISTC_G1_LOADA_REG]  = { 0x140, 4 },
 [NISTC_G1_LOADB_REG]  = { 0x144, 4 },
 [NISTC_G0_INPUT_SEL_REG] = { 0x148, 2 },
 [NISTC_G1_INPUT_SEL_REG] = { 0x14a, 2 },
 [NISTC_AO_MODE1_REG]  = { 0x14c, 2 },
 [NISTC_AO_MODE2_REG]  = { 0x14e, 2 },
 [NISTC_AO_UI_LOADA_REG]  = { 0x150, 4 },
 [NISTC_AO_UI_LOADB_REG]  = { 0x154, 4 },
 [NISTC_AO_BC_LOADA_REG]  = { 0x158, 4 },
 [NISTC_AO_BC_LOADB_REG]  = { 0x15c, 4 },
 [NISTC_AO_UC_LOADA_REG]  = { 0x160, 4 },
 [NISTC_AO_UC_LOADB_REG]  = { 0x164, 4 },
 [NISTC_CLK_FOUT_REG]  = { 0x170, 2 },
 [NISTC_IO_BIDIR_PIN_REG] = { 0x172, 2 },
 [NISTC_RTSI_TRIG_DIR_REG] = { 0x174, 2 },
 [NISTC_INT_CTRL_REG]  = { 0x176, 2 },
 [NISTC_AI_OUT_CTRL_REG]  = { 0x178, 2 },
 [NISTC_ATRIG_ETC_REG]  = { 0x17a, 2 },
 [NISTC_AI_START_STOP_REG] = { 0x17c, 2 },
 [NISTC_AI_TRIG_SEL_REG]  = { 0x17e, 2 },
 [NISTC_AI_DIV_LOADA_REG] = { 0x180, 4 },
 [NISTC_AO_START_SEL_REG] = { 0x184, 2 },
 [NISTC_AO_TRIG_SEL_REG]  = { 0x186, 2 },
 [NISTC_G0_AUTOINC_REG]  = { 0x188, 2 },
 [NISTC_G1_AUTOINC_REG]  = { 0x18a, 2 },
 [NISTC_AO_MODE3_REG]  = { 0x18c, 2 },
 [NISTC_RESET_REG]  = { 0x190, 2 },
 [NISTC_INTA_ENA_REG]  = { 0x192, 2 },
 [NISTC_INTA2_ENA_REG]  = { 0, 0 }, /* E-Series only */
 [NISTC_INTB_ENA_REG]  = { 0x196, 2 },
 [NISTC_INTB2_ENA_REG]  = { 0, 0 }, /* E-Series only */
 [NISTC_AI_PERSONAL_REG]  = { 0x19a, 2 },
 [NISTC_AO_PERSONAL_REG]  = { 0x19c, 2 },
 [NISTC_RTSI_TRIGA_OUT_REG] = { 0x19e, 2 },
 [NISTC_RTSI_TRIGB_OUT_REG] = { 0x1a0, 2 },
 /* doc for following line: mhddk/nimseries/ChipObjects/tMSeries.h */
 [NISTC_RTSI_BOARD_REG]  = { 0x1a2, 2 },
 [NISTC_CFG_MEM_CLR_REG]  = { 0x1a4, 2 },
 [NISTC_ADC_FIFO_CLR_REG] = { 0x1a6, 2 },
 [NISTC_DAC_FIFO_CLR_REG] = { 0x1a8, 2 },
 [NISTC_AO_OUT_CTRL_REG]  = { 0x1ac, 2 },
 [NISTC_AI_MODE3_REG]  = { 0x1ae, 2 },
};

static void m_series_stc_write(struct comedi_device *dev,
          unsigned int data, unsigned int reg)
{
 const struct mio_regmap *regmap;

 if (reg < ARRAY_SIZE(m_series_stc_write_regmap)) {
  regmap = &m_series_stc_write_regmap[reg];
 } else {
  dev_warn(dev->class_dev, "%s: unhandled register=0x%x\n",
    __func__, reg);
  return;
 }

 switch (regmap->size) {
 case 4:
  ni_writel(dev, data, regmap->mio_reg);
  break;
 case 2:
  ni_writew(dev, data, regmap->mio_reg);
  break;
 default:
  dev_warn(dev->class_dev, "%s: unmapped register=0x%x\n",
    __func__, reg);
  break;
 }
}

static const struct mio_regmap m_series_stc_read_regmap[] = {
 [NISTC_AI_STATUS1_REG]  = { 0x104, 2 },
 [NISTC_AO_STATUS1_REG]  = { 0x106, 2 },
 [NISTC_G01_STATUS_REG]  = { 0x108, 2 },
 [NISTC_AI_STATUS2_REG]  = { 0, 0 }, /* Unknown */
 [NISTC_AO_STATUS2_REG]  = { 0x10c, 2 },
 [NISTC_DIO_IN_REG]  = { 0, 0 }, /* Unknown */
 [NISTC_G0_HW_SAVE_REG]  = { 0x110, 4 },
 [NISTC_G1_HW_SAVE_REG]  = { 0x114, 4 },
 [NISTC_G0_SAVE_REG]  = { 0x118, 4 },
 [NISTC_G1_SAVE_REG]  = { 0x11c, 4 },
 [NISTC_AO_UI_SAVE_REG]  = { 0x120, 4 },
 [NISTC_AO_BC_SAVE_REG]  = { 0x124, 4 },
 [NISTC_AO_UC_SAVE_REG]  = { 0x128, 4 },
 [NISTC_STATUS1_REG]  = { 0x136, 2 },
 [NISTC_DIO_SERIAL_IN_REG] = { 0x009, 1 },
 [NISTC_STATUS2_REG]  = { 0x13a, 2 },
 [NISTC_AI_SI_SAVE_REG]  = { 0x180, 4 },
 [NISTC_AI_SC_SAVE_REG]  = { 0x184, 4 },
};

static unsigned int m_series_stc_read(struct comedi_device *dev,
          unsigned int reg)
{
 const struct mio_regmap *regmap;

 if (reg < ARRAY_SIZE(m_series_stc_read_regmap)) {
  regmap = &m_series_stc_read_regmap[reg];
 } else {
  dev_warn(dev->class_dev, "%s: unhandled register=0x%x\n",
    __func__, reg);
  return 0;
 }

 switch (regmap->size) {
 case 4:
  return ni_readl(dev, regmap->mio_reg);
 case 2:
  return ni_readw(dev, regmap->mio_reg);
 case 1:
  return ni_readb(dev, regmap->mio_reg);
 default:
  dev_warn(dev->class_dev, "%s: unmapped register=0x%x\n",
    __func__, reg);
  return 0;
 }
}

static void ni_stc_writew(struct comedi_device *dev,
     unsigned int data, int reg)
{
 struct ni_private *devpriv = dev->private;
 unsigned long flags;

 if (devpriv->is_m_series) {
  m_series_stc_write(dev, data, reg);
 } else {
  spin_lock_irqsave(&devpriv->window_lock, flags);
  if (!devpriv->mite && reg < 8) {
   ni_writew(dev, data, reg * 2);
  } else {
   ni_writew(dev, reg, NI_E_STC_WINDOW_ADDR_REG);
   ni_writew(dev, data, NI_E_STC_WINDOW_DATA_REG);
  }
  spin_unlock_irqrestore(&devpriv->window_lock, flags);
 }
}

static void ni_stc_writel(struct comedi_device *dev,
     unsigned int data, int reg)
{
 struct ni_private *devpriv = dev->private;

 if (devpriv->is_m_series) {
  m_series_stc_write(dev, data, reg);
 } else {
  ni_stc_writew(dev, data >> 16, reg);
  ni_stc_writew(dev, data & 0xffff, reg + 1);
 }
}

static unsigned int ni_stc_readw(struct comedi_device *dev, int reg)
{
 struct ni_private *devpriv = dev->private;
 unsigned long flags;
 unsigned int val;

 if (devpriv->is_m_series) {
  val = m_series_stc_read(dev, reg);
 } else {
  spin_lock_irqsave(&devpriv->window_lock, flags);
  if (!devpriv->mite && reg < 8) {
   val = ni_readw(dev, reg * 2);
  } else {
   ni_writew(dev, reg, NI_E_STC_WINDOW_ADDR_REG);
   val = ni_readw(dev, NI_E_STC_WINDOW_DATA_REG);
  }
  spin_unlock_irqrestore(&devpriv->window_lock, flags);
 }
 return val;
}

static unsigned int ni_stc_readl(struct comedi_device *dev, int reg)
{
 struct ni_private *devpriv = dev->private;
 unsigned int val;

 if (devpriv->is_m_series) {
  val = m_series_stc_read(dev, reg);
 } else {
  val = ni_stc_readw(dev, reg) << 16;
  val |= ni_stc_readw(dev, reg + 1);
 }
 return val;
}

static inline void ni_set_bitfield(struct comedi_device *dev, int reg,
       unsigned int bit_mask,
       unsigned int bit_values)
{
 struct ni_private *devpriv = dev->private;
 unsigned long flags;

 spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
 switch (reg) {
 case NISTC_INTA_ENA_REG:
  devpriv->int_a_enable_reg &= ~bit_mask;
  devpriv->int_a_enable_reg |= bit_values & bit_mask;
  ni_stc_writew(dev, devpriv->int_a_enable_reg, reg);
  break;
 case NISTC_INTB_ENA_REG:
  devpriv->int_b_enable_reg &= ~bit_mask;
  devpriv->int_b_enable_reg |= bit_values & bit_mask;
  ni_stc_writew(dev, devpriv->int_b_enable_reg, reg);
  break;
 case NISTC_IO_BIDIR_PIN_REG:
  devpriv->io_bidirection_pin_reg &= ~bit_mask;
  devpriv->io_bidirection_pin_reg |= bit_values & bit_mask;
  ni_stc_writew(dev, devpriv->io_bidirection_pin_reg, reg);
  break;
 case NI_E_DMA_AI_AO_SEL_REG:
  devpriv->ai_ao_select_reg &= ~bit_mask;
  devpriv->ai_ao_select_reg |= bit_values & bit_mask;
  ni_writeb(dev, devpriv->ai_ao_select_reg, reg);
  break;
 case NI_E_DMA_G0_G1_SEL_REG:
  devpriv->g0_g1_select_reg &= ~bit_mask;
  devpriv->g0_g1_select_reg |= bit_values & bit_mask;
  ni_writeb(dev, devpriv->g0_g1_select_reg, reg);
  break;
 case NI_M_CDIO_DMA_SEL_REG:
  devpriv->cdio_dma_select_reg &= ~bit_mask;
  devpriv->cdio_dma_select_reg |= bit_values & bit_mask;
  ni_writeb(dev, devpriv->cdio_dma_select_reg, reg);
  break;
 default:
  dev_err(dev->class_dev, "called with invalid register %d\n",
   reg);
  break;
 }
 spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
}

#ifdef PCIDMA

/* selects the MITE channel to use for DMA */
#define NI_STC_DMA_CHAN_SEL(x) (((x) < 4) ? BIT(x) : \
     ((x) == 4) ? 0x3 : \
     ((x) == 5) ? 0x5 : 0x0)

/* DMA channel setup */
static int ni_request_ai_mite_channel(struct comedi_device *dev)
{
 struct ni_private *devpriv = dev->private;
 struct mite_channel *mite_chan;
 unsigned long flags;
 unsigned int bits;

 spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
 mite_chan = mite_request_channel(devpriv->mite, devpriv->ai_mite_ring);
 if (!mite_chan) {
  spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
  dev_err(dev->class_dev,
   "failed to reserve mite dma channel for analog input\n");
  return -EBUSY;
 }
 mite_chan->dir = COMEDI_INPUT;
 devpriv->ai_mite_chan = mite_chan;

 bits = NI_STC_DMA_CHAN_SEL(mite_chan->channel);
 ni_set_bitfield(dev, NI_E_DMA_AI_AO_SEL_REG,
   NI_E_DMA_AI_SEL_MASK, NI_E_DMA_AI_SEL(bits));

 spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
 return 0;
}

static int ni_request_ao_mite_channel(struct comedi_device *dev)
{
 struct ni_private *devpriv = dev->private;
 struct mite_channel *mite_chan;
 unsigned long flags;
 unsigned int bits;

 spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
 mite_chan = mite_request_channel(devpriv->mite, devpriv->ao_mite_ring);
 if (!mite_chan) {
  spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
  dev_err(dev->class_dev,
   "failed to reserve mite dma channel for analog output\n");
  return -EBUSY;
 }
 mite_chan->dir = COMEDI_OUTPUT;
 devpriv->ao_mite_chan = mite_chan;

 bits = NI_STC_DMA_CHAN_SEL(mite_chan->channel);
 ni_set_bitfield(dev, NI_E_DMA_AI_AO_SEL_REG,
   NI_E_DMA_AO_SEL_MASK, NI_E_DMA_AO_SEL(bits));

 spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
 return 0;
}

static int ni_request_gpct_mite_channel(struct comedi_device *dev,
     unsigned int gpct_index,
     enum comedi_io_direction direction)
{
 struct ni_private *devpriv = dev->private;
 struct ni_gpct *counter = &devpriv->counter_dev->counters[gpct_index];
 struct mite_channel *mite_chan;
 unsigned long flags;
 unsigned int bits;

 spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
 mite_chan = mite_request_channel(devpriv->mite,
      devpriv->gpct_mite_ring[gpct_index]);
 if (!mite_chan) {
  spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
  dev_err(dev->class_dev,
   "failed to reserve mite dma channel for counter\n");
  return -EBUSY;
 }
 mite_chan->dir = direction;
 ni_tio_set_mite_channel(counter, mite_chan);

 bits = NI_STC_DMA_CHAN_SEL(mite_chan->channel);
 ni_set_bitfield(dev, NI_E_DMA_G0_G1_SEL_REG,
   NI_E_DMA_G0_G1_SEL_MASK(gpct_index),
   NI_E_DMA_G0_G1_SEL(gpct_index, bits));

 spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
 return 0;
}

static int ni_request_cdo_mite_channel(struct comedi_device *dev)
{
 struct ni_private *devpriv = dev->private;
 struct mite_channel *mite_chan;
 unsigned long flags;
 unsigned int bits;

 spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
 mite_chan = mite_request_channel(devpriv->mite, devpriv->cdo_mite_ring);
 if (!mite_chan) {
  spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
  dev_err(dev->class_dev,
   "failed to reserve mite dma channel for correlated digital output\n");
  return -EBUSY;
 }
 mite_chan->dir = COMEDI_OUTPUT;
 devpriv->cdo_mite_chan = mite_chan;

 /*
 * XXX just guessing NI_STC_DMA_CHAN_SEL()
 * returns the right bits, under the assumption the cdio dma
 * selection works just like ai/ao/gpct.
 * Definitely works for dma channels 0 and 1.
 */

 bits = NI_STC_DMA_CHAN_SEL(mite_chan->channel);
 ni_set_bitfield(dev, NI_M_CDIO_DMA_SEL_REG,
   NI_M_CDIO_DMA_SEL_CDO_MASK,
   NI_M_CDIO_DMA_SEL_CDO(bits));

 spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
 return 0;
}
#endif /*  PCIDMA */

static void ni_release_ai_mite_channel(struct comedi_device *dev)
{
#ifdef PCIDMA
 struct ni_private *devpriv = dev->private;
 unsigned long flags;

 spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
 if (devpriv->ai_mite_chan) {
  ni_set_bitfield(dev, NI_E_DMA_AI_AO_SEL_REG,
    NI_E_DMA_AI_SEL_MASK, 0);
  mite_release_channel(devpriv->ai_mite_chan);
  devpriv->ai_mite_chan = NULL;
 }
 spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
#endif /*  PCIDMA */
}

static void ni_release_ao_mite_channel(struct comedi_device *dev)
{
#ifdef PCIDMA
 struct ni_private *devpriv = dev->private;
 unsigned long flags;

 spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
 if (devpriv->ao_mite_chan) {
  ni_set_bitfield(dev, NI_E_DMA_AI_AO_SEL_REG,
    NI_E_DMA_AO_SEL_MASK, 0);
  mite_release_channel(devpriv->ao_mite_chan);
  devpriv->ao_mite_chan = NULL;
 }
 spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
#endif /*  PCIDMA */
}

#ifdef PCIDMA
static void ni_release_gpct_mite_channel(struct comedi_device *dev,
      unsigned int gpct_index)
{
 struct ni_private *devpriv = dev->private;
 unsigned long flags;

 spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
 if (devpriv->counter_dev->counters[gpct_index].mite_chan) {
  struct mite_channel *mite_chan =
      devpriv->counter_dev->counters[gpct_index].mite_chan;

  ni_set_bitfield(dev, NI_E_DMA_G0_G1_SEL_REG,
    NI_E_DMA_G0_G1_SEL_MASK(gpct_index), 0);
  ni_tio_set_mite_channel(&devpriv->counter_dev->counters[gpct_index],
     NULL);
  mite_release_channel(mite_chan);
 }
 spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
}

static void ni_release_cdo_mite_channel(struct comedi_device *dev)
{
 struct ni_private *devpriv = dev->private;
 unsigned long flags;

 spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
 if (devpriv->cdo_mite_chan) {
  ni_set_bitfield(dev, NI_M_CDIO_DMA_SEL_REG,
    NI_M_CDIO_DMA_SEL_CDO_MASK, 0);
  mite_release_channel(devpriv->cdo_mite_chan);
  devpriv->cdo_mite_chan = NULL;
 }
 spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
}

static void ni_e_series_enable_second_irq(struct comedi_device *dev,
       unsigned int gpct_index, short enable)
{
 struct ni_private *devpriv = dev->private;
 unsigned int val = 0;
 int reg;

 if (devpriv->is_m_series || gpct_index > 1)
  return;

 /*
 * e-series boards use the second irq signals to generate
 * dma requests for their counters
 */

 if (gpct_index == 0) {
  reg = NISTC_INTA2_ENA_REG;
  if (enable)
   val = NISTC_INTA_ENA_G0_GATE;
 } else {
  reg = NISTC_INTB2_ENA_REG;
  if (enable)
   val = NISTC_INTB_ENA_G1_GATE;
 }
 ni_stc_writew(dev, val, reg);
}
#endif /*  PCIDMA */

static void ni_clear_ai_fifo(struct comedi_device *dev)
{
 struct ni_private *devpriv = dev->private;
 static const int timeout = 10000;
 int i;

 if (devpriv->is_6143) {
  /*  Flush the 6143 data FIFO */
  ni_writel(dev, 0x10, NI6143_AI_FIFO_CTRL_REG);
  ni_writel(dev, 0x00, NI6143_AI_FIFO_CTRL_REG);
  /*  Wait for complete */
  for (i = 0; i < timeout; i++) {
   if (!(ni_readl(dev, NI6143_AI_FIFO_STATUS_REG) & 0x10))
    break;
   udelay(1);
  }
  if (i == timeout)
   dev_err(dev->class_dev, "FIFO flush timeout\n");
 } else {
  ni_stc_writew(dev, 1, NISTC_ADC_FIFO_CLR_REG);
  if (devpriv->is_625x) {
   ni_writeb(dev, 0, NI_M_STATIC_AI_CTRL_REG(0));
   ni_writeb(dev, 1, NI_M_STATIC_AI_CTRL_REG(0));
#if 0
   /*
 * The NI example code does 3 convert pulses for 625x
 * boards, But that appears to be wrong in practice.
 */

   ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE,
          NISTC_AI_CMD1_REG);
   ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE,
          NISTC_AI_CMD1_REG);
   ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE,
          NISTC_AI_CMD1_REG);
#endif
  }
 }
}

static inline void ni_ao_win_outw(struct comedi_device *dev,
      unsigned int data, int addr)
{
 struct ni_private *devpriv = dev->private;
 unsigned long flags;

 spin_lock_irqsave(&devpriv->window_lock, flags);
 ni_writew(dev, addr, NI611X_AO_WINDOW_ADDR_REG);
 ni_writew(dev, data, NI611X_AO_WINDOW_DATA_REG);
 spin_unlock_irqrestore(&devpriv->window_lock, flags);
}

static inline void ni_ao_win_outl(struct comedi_device *dev,
      unsigned int data, int addr)
{
 struct ni_private *devpriv = dev->private;
 unsigned long flags;

 spin_lock_irqsave(&devpriv->window_lock, flags);
 ni_writew(dev, addr, NI611X_AO_WINDOW_ADDR_REG);
 ni_writel(dev, data, NI611X_AO_WINDOW_DATA_REG);
 spin_unlock_irqrestore(&devpriv->window_lock, flags);
}

static inline unsigned short ni_ao_win_inw(struct comedi_device *dev, int addr)
{
 struct ni_private *devpriv = dev->private;
 unsigned long flags;
 unsigned short data;

 spin_lock_irqsave(&devpriv->window_lock, flags);
 ni_writew(dev, addr, NI611X_AO_WINDOW_ADDR_REG);
 data = ni_readw(dev, NI611X_AO_WINDOW_DATA_REG);
 spin_unlock_irqrestore(&devpriv->window_lock, flags);
 return data;
}

/*
 * ni_set_bits( ) allows different parts of the ni_mio_common driver to
 * share registers (such as Interrupt_A_Register) without interfering with
 * each other.
 *
 * NOTE: the switch/case statements are optimized out for a constant argument
 * so this is actually quite fast---  If you must wrap another function around
 * this make it inline to avoid a large speed penalty.
 *
 * value should only be 1 or 0.
 */

static inline void ni_set_bits(struct comedi_device *dev, int reg,
          unsigned int bits, unsigned int value)
{
 unsigned int bit_values;

 if (value)
  bit_values = bits;
 else
  bit_values = 0;
 ni_set_bitfield(dev, reg, bits, bit_values);
}

#ifdef PCIDMA
static void ni_sync_ai_dma(struct comedi_device *dev)
{
 struct ni_private *devpriv = dev->private;
 struct comedi_subdevice *s = dev->read_subdev;
 unsigned long flags;

 spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
 if (devpriv->ai_mite_chan)
  mite_sync_dma(devpriv->ai_mite_chan, s);
 spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
}

static int ni_ai_drain_dma(struct comedi_device *dev)
{
 struct ni_private *devpriv = dev->private;
 int i;
 static const int timeout = 10000;
 unsigned long flags;
 int retval = 0;

 spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
 if (devpriv->ai_mite_chan) {
  for (i = 0; i < timeout; i++) {
   if ((ni_stc_readw(dev, NISTC_AI_STATUS1_REG) &
        NISTC_AI_STATUS1_FIFO_E) &&
       mite_bytes_in_transit(devpriv->ai_mite_chan) == 0)
    break;
   udelay(5);
  }
  if (i == timeout) {
   dev_err(dev->class_dev, "timed out\n");
   dev_err(dev->class_dev,
    "mite_bytes_in_transit=%i, AI_Status1_Register=0x%x\n",
    mite_bytes_in_transit(devpriv->ai_mite_chan),
    ni_stc_readw(dev, NISTC_AI_STATUS1_REG));
   retval = -1;
  }
 }
 spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);

 ni_sync_ai_dma(dev);

 return retval;
}

static int ni_ao_wait_for_dma_load(struct comedi_device *dev)
{
 static const int timeout = 10000;
 int i;

 for (i = 0; i < timeout; i++) {
  unsigned short b_status;

  b_status = ni_stc_readw(dev, NISTC_AO_STATUS1_REG);
  if (b_status & NISTC_AO_STATUS1_FIFO_HF)
   break;
  /*
 * If we poll too often, the pci bus activity seems
 * to slow the dma transfer down.
 */

  usleep_range(10, 100);
 }
 if (i == timeout) {
  dev_err(dev->class_dev, "timed out waiting for dma load\n");
  return -EPIPE;
 }
 return 0;
}
#endif /* PCIDMA */

#ifndef PCIDMA

static void ni_ao_fifo_load(struct comedi_device *dev,
       struct comedi_subdevice *s, int n)
{
 struct ni_private *devpriv = dev->private;
 int i;
 unsigned short d;
 unsigned int packed_data;

 for (i = 0; i < n; i++) {
  comedi_buf_read_samples(s, &d, 1);

  if (devpriv->is_6xxx) {
   packed_data = d & 0xffff;
   /* 6711 only has 16 bit wide ao fifo */
   if (!devpriv->is_6711) {
    comedi_buf_read_samples(s, &d, 1);
    i++;
    packed_data |= (d << 16) & 0xffff0000;
   }
   ni_writel(dev, packed_data, NI611X_AO_FIFO_DATA_REG);
  } else {
   ni_writew(dev, d, NI_E_AO_FIFO_DATA_REG);
  }
 }
}

/*
 *  There's a small problem if the FIFO gets really low and we
 *  don't have the data to fill it.  Basically, if after we fill
 *  the FIFO with all the data available, the FIFO is _still_
 *  less than half full, we never clear the interrupt.  If the
 *  IRQ is in edge mode, we never get another interrupt, because
 *  this one wasn't cleared.  If in level mode, we get flooded
 *  with interrupts that we can't fulfill, because nothing ever
 *  gets put into the buffer.
 *
 *  This kind of situation is recoverable, but it is easier to
 *  just pretend we had a FIFO underrun, since there is a good
 *  chance it will happen anyway.  This is _not_ the case for
 *  RT code, as RT code might purposely be running close to the
 *  metal.  Needs to be fixed eventually.
 */

static int ni_ao_fifo_half_empty(struct comedi_device *dev,
     struct comedi_subdevice *s)
{
 const struct ni_board_struct *board = dev->board_ptr;
 unsigned int nbytes;
 unsigned int nsamples;

 nbytes = comedi_buf_read_n_available(s);
 if (nbytes == 0) {
  s->async->events |= COMEDI_CB_OVERFLOW;
  return 0;
 }

 nsamples = comedi_bytes_to_samples(s, nbytes);
 if (nsamples > board->ao_fifo_depth / 2)
  nsamples = board->ao_fifo_depth / 2;

 ni_ao_fifo_load(dev, s, nsamples);

 return 1;
}

static int ni_ao_prep_fifo(struct comedi_device *dev,
      struct comedi_subdevice *s)
{
 const struct ni_board_struct *board = dev->board_ptr;
 struct ni_private *devpriv = dev->private;
 unsigned int nbytes;
 unsigned int nsamples;

 /* reset fifo */
 ni_stc_writew(dev, 1, NISTC_DAC_FIFO_CLR_REG);
 if (devpriv->is_6xxx)
  ni_ao_win_outl(dev, 0x6, NI611X_AO_FIFO_OFFSET_LOAD_REG);

 /* load some data */
 nbytes = comedi_buf_read_n_available(s);
 if (nbytes == 0)
  return 0;

 nsamples = comedi_bytes_to_samples(s, nbytes);
 if (nsamples > board->ao_fifo_depth)
  nsamples = board->ao_fifo_depth;

 ni_ao_fifo_load(dev, s, nsamples);

 return nsamples;
}

static void ni_ai_fifo_read(struct comedi_device *dev,
       struct comedi_subdevice *s, int n)
{
 struct ni_private *devpriv = dev->private;
 struct comedi_async *async = s->async;
 unsigned int dl;
 unsigned short data;
 int i;

 if (devpriv->is_611x) {
  for (i = 0; i < n / 2; i++) {
   dl = ni_readl(dev, NI611X_AI_FIFO_DATA_REG);
   /* This may get the hi/lo data in the wrong order */
   data = (dl >> 16) & 0xffff;
   comedi_buf_write_samples(s, &data, 1);
   data = dl & 0xffff;
   comedi_buf_write_samples(s, &data, 1);
  }
  /* Check if there's a single sample stuck in the FIFO */
  if (n % 2) {
   dl = ni_readl(dev, NI611X_AI_FIFO_DATA_REG);
   data = dl & 0xffff;
   comedi_buf_write_samples(s, &data, 1);
  }
 } else if (devpriv->is_6143) {
  /*
 * This just reads the FIFO assuming the data is present,
 * no checks on the FIFO status are performed.
 */

  for (i = 0; i < n / 2; i++) {
   dl = ni_readl(dev, NI6143_AI_FIFO_DATA_REG);

   data = (dl >> 16) & 0xffff;
   comedi_buf_write_samples(s, &data, 1);
   data = dl & 0xffff;
   comedi_buf_write_samples(s, &data, 1);
  }
  if (n % 2) {
   /* Assume there is a single sample stuck in the FIFO */
   /* Get stranded sample into FIFO */
   ni_writel(dev, 0x01, NI6143_AI_FIFO_CTRL_REG);
   dl = ni_readl(dev, NI6143_AI_FIFO_DATA_REG);
   data = (dl >> 16) & 0xffff;
   comedi_buf_write_samples(s, &data, 1);
  }
 } else {
  if (n > ARRAY_SIZE(devpriv->ai_fifo_buffer)) {
   dev_err(dev->class_dev,
    "bug! ai_fifo_buffer too small\n");
   async->events |= COMEDI_CB_ERROR;
   return;
  }
  for (i = 0; i < n; i++) {
   devpriv->ai_fifo_buffer[i] =
       ni_readw(dev, NI_E_AI_FIFO_DATA_REG);
  }
  comedi_buf_write_samples(s, devpriv->ai_fifo_buffer, n);
 }
}

static void ni_handle_fifo_half_full(struct comedi_device *dev)
{
 const struct ni_board_struct *board = dev->board_ptr;
 struct comedi_subdevice *s = dev->read_subdev;
 int n;

 n = board->ai_fifo_depth / 2;

 ni_ai_fifo_read(dev, s, n);
}
#endif

/* Empties the AI fifo */
static void ni_handle_fifo_dregs(struct comedi_device *dev)
{
 struct ni_private *devpriv = dev->private;
 struct comedi_subdevice *s = dev->read_subdev;
 unsigned int dl;
 unsigned short data;
 int i;

 if (devpriv->is_611x) {
  while ((ni_stc_readw(dev, NISTC_AI_STATUS1_REG) &
   NISTC_AI_STATUS1_FIFO_E) == 0) {
   dl = ni_readl(dev, NI611X_AI_FIFO_DATA_REG);

   /* This may get the hi/lo data in the wrong order */
   data = dl >> 16;
   comedi_buf_write_samples(s, &data, 1);
   data = dl & 0xffff;
   comedi_buf_write_samples(s, &data, 1);
  }
 } else if (devpriv->is_6143) {
  i = 0;
  while (ni_readl(dev, NI6143_AI_FIFO_STATUS_REG) & 0x04) {
   dl = ni_readl(dev, NI6143_AI_FIFO_DATA_REG);

   /* This may get the hi/lo data in the wrong order */
   data = dl >> 16;
   comedi_buf_write_samples(s, &data, 1);
   data = dl & 0xffff;
   comedi_buf_write_samples(s, &data, 1);
   i += 2;
  }
  /*  Check if stranded sample is present */
  if (ni_readl(dev, NI6143_AI_FIFO_STATUS_REG) & 0x01) {
   /* Get stranded sample into FIFO */
   ni_writel(dev, 0x01, NI6143_AI_FIFO_CTRL_REG);
   dl = ni_readl(dev, NI6143_AI_FIFO_DATA_REG);
   data = (dl >> 16) & 0xffff;
   comedi_buf_write_samples(s, &data, 1);
  }

 } else {
  unsigned short fe; /* fifo empty */

  fe = ni_stc_readw(dev, NISTC_AI_STATUS1_REG) &
       NISTC_AI_STATUS1_FIFO_E;
  while (fe == 0) {
   for (i = 0;
        i < ARRAY_SIZE(devpriv->ai_fifo_buffer); i++) {
    fe = ni_stc_readw(dev, NISTC_AI_STATUS1_REG) &
         NISTC_AI_STATUS1_FIFO_E;
    if (fe)
     break;
    devpriv->ai_fifo_buffer[i] =
        ni_readw(dev, NI_E_AI_FIFO_DATA_REG);
   }
   comedi_buf_write_samples(s, devpriv->ai_fifo_buffer, i);
  }
 }
}

static void get_last_sample_611x(struct comedi_device *dev)
{
 struct ni_private *devpriv = dev->private;
 struct comedi_subdevice *s = dev->read_subdev;
 unsigned short data;
 unsigned int dl;

 if (!devpriv->is_611x)
  return;

 /* Check if there's a single sample stuck in the FIFO */
 if (ni_readb(dev, NI_E_STATUS_REG) & 0x80) {
  dl = ni_readl(dev, NI611X_AI_FIFO_DATA_REG);
  data = dl & 0xffff;
  comedi_buf_write_samples(s, &data, 1);
 }
}

static void get_last_sample_6143(struct comedi_device *dev)
{
 struct ni_private *devpriv = dev->private;
 struct comedi_subdevice *s = dev->read_subdev;
 unsigned short data;
 unsigned int dl;

 if (!devpriv->is_6143)
  return;

 /* Check if there's a single sample stuck in the FIFO */
 if (ni_readl(dev, NI6143_AI_FIFO_STATUS_REG) & 0x01) {
  /* Get stranded sample into FIFO */
  ni_writel(dev, 0x01, NI6143_AI_FIFO_CTRL_REG);
  dl = ni_readl(dev, NI6143_AI_FIFO_DATA_REG);

  /* This may get the hi/lo data in the wrong order */
  data = (dl >> 16) & 0xffff;
  comedi_buf_write_samples(s, &data, 1);
 }
}

static void shutdown_ai_command(struct comedi_device *dev)
{
 struct comedi_subdevice *s = dev->read_subdev;

#ifdef PCIDMA
 ni_ai_drain_dma(dev);
#endif
 ni_handle_fifo_dregs(dev);
 get_last_sample_611x(dev);
 get_last_sample_6143(dev);

 s->async->events |= COMEDI_CB_EOA;
}

static void ni_handle_eos(struct comedi_device *dev, struct comedi_subdevice *s)
{
 struct ni_private *devpriv = dev->private;

 if (devpriv->aimode == AIMODE_SCAN) {
#ifdef PCIDMA
  static const int timeout = 10;
  int i;

  for (i = 0; i < timeout; i++) {
   ni_sync_ai_dma(dev);
   if ((s->async->events & COMEDI_CB_EOS))
    break;
   udelay(1);
  }
#else
  ni_handle_fifo_dregs(dev);
  s->async->events |= COMEDI_CB_EOS;
#endif
 }
 /* handle special case of single scan */
 if (devpriv->ai_cmd2 & NISTC_AI_CMD2_END_ON_EOS)
  shutdown_ai_command(dev);
}

static void handle_gpct_interrupt(struct comedi_device *dev,
      unsigned short counter_index)
{
#ifdef PCIDMA
 struct ni_private *devpriv = dev->private;
 struct comedi_subdevice *s;

 s = &dev->subdevices[NI_GPCT_SUBDEV(counter_index)];

 ni_tio_handle_interrupt(&devpriv->counter_dev->counters[counter_index],
    s);
 comedi_handle_events(dev, s);
#endif
}

static void ack_a_interrupt(struct comedi_device *dev, unsigned short a_status)
{
 unsigned short ack = 0;

 if (a_status & NISTC_AI_STATUS1_SC_TC)
  ack |= NISTC_INTA_ACK_AI_SC_TC;
 if (a_status & NISTC_AI_STATUS1_START1)
  ack |= NISTC_INTA_ACK_AI_START1;
 if (a_status & NISTC_AI_STATUS1_START)
  ack |= NISTC_INTA_ACK_AI_START;
 if (a_status & NISTC_AI_STATUS1_STOP)
  ack |= NISTC_INTA_ACK_AI_STOP;
 if (a_status & NISTC_AI_STATUS1_OVER)
  ack |= NISTC_INTA_ACK_AI_ERR;
 if (ack)
  ni_stc_writew(dev, ack, NISTC_INTA_ACK_REG);
}

static void handle_a_interrupt(struct comedi_device *dev,
          struct comedi_subdevice *s,
          unsigned short status)
{
 struct comedi_cmd *cmd = &s->async->cmd;

 /* test for all uncommon interrupt events at the same time */
 if (status & (NISTC_AI_STATUS1_ERR |
        NISTC_AI_STATUS1_SC_TC | NISTC_AI_STATUS1_START1)) {
  if (status == 0xffff) {
   dev_err(dev->class_dev, "Card removed?\n");
   /*
 * We probably aren't even running a command now,
 * so it's a good idea to be careful.
 */

   if (comedi_is_subdevice_running(s))
    s->async->events |= COMEDI_CB_ERROR;
   return;
  }
  if (status & NISTC_AI_STATUS1_ERR) {
   dev_err(dev->class_dev, "ai error a_status=%04x\n",
    status);

   shutdown_ai_command(dev);

   s->async->events |= COMEDI_CB_ERROR;
   if (status & NISTC_AI_STATUS1_OVER)
    s->async->events |= COMEDI_CB_OVERFLOW;
   return;
  }
  if (status & NISTC_AI_STATUS1_SC_TC) {
   if (cmd->stop_src == TRIG_COUNT)
    shutdown_ai_command(dev);
  }
 }
#ifndef PCIDMA
 if (status & NISTC_AI_STATUS1_FIFO_HF) {
  int i;
  static const int timeout = 10;
  /*
 * PCMCIA cards (at least 6036) seem to stop producing
 * interrupts if we fail to get the fifo less than half
 * full, so loop to be sure.
 */

  for (i = 0; i < timeout; ++i) {
   ni_handle_fifo_half_full(dev);
   if ((ni_stc_readw(dev, NISTC_AI_STATUS1_REG) &
        NISTC_AI_STATUS1_FIFO_HF) == 0)
    break;
  }
 }
#endif /*  !PCIDMA */

 if (status & NISTC_AI_STATUS1_STOP)
  ni_handle_eos(dev, s);
}

static void ack_b_interrupt(struct comedi_device *dev, unsigned short b_status)
{
 unsigned short ack = 0;

 if (b_status & NISTC_AO_STATUS1_BC_TC)
  ack |= NISTC_INTB_ACK_AO_BC_TC;
 if (b_status & NISTC_AO_STATUS1_OVERRUN)
  ack |= NISTC_INTB_ACK_AO_ERR;
 if (b_status & NISTC_AO_STATUS1_START)
  ack |= NISTC_INTB_ACK_AO_START;
 if (b_status & NISTC_AO_STATUS1_START1)
  ack |= NISTC_INTB_ACK_AO_START1;
 if (b_status & NISTC_AO_STATUS1_UC_TC)
  ack |= NISTC_INTB_ACK_AO_UC_TC;
 if (b_status & NISTC_AO_STATUS1_UI2_TC)
  ack |= NISTC_INTB_ACK_AO_UI2_TC;
 if (b_status & NISTC_AO_STATUS1_UPDATE)
  ack |= NISTC_INTB_ACK_AO_UPDATE;
 if (ack)
  ni_stc_writew(dev, ack, NISTC_INTB_ACK_REG);
}

static void handle_b_interrupt(struct comedi_device *dev,
          struct comedi_subdevice *s,
          unsigned short b_status)
{
 if (b_status == 0xffff)
  return;
 if (b_status & NISTC_AO_STATUS1_OVERRUN) {
  dev_err(dev->class_dev,
   "AO FIFO underrun status=0x%04x status2=0x%04x\n",
   b_status, ni_stc_readw(dev, NISTC_AO_STATUS2_REG));
  s->async->events |= COMEDI_CB_OVERFLOW;
 }

 if (s->async->cmd.stop_src != TRIG_NONE &&
     b_status & NISTC_AO_STATUS1_BC_TC)
  s->async->events |= COMEDI_CB_EOA;

#ifndef PCIDMA
 if (b_status & NISTC_AO_STATUS1_FIFO_REQ) {
  int ret;

  ret = ni_ao_fifo_half_empty(dev, s);
  if (!ret) {
   dev_err(dev->class_dev, "AO buffer underrun\n");
   ni_set_bits(dev, NISTC_INTB_ENA_REG,
        NISTC_INTB_ENA_AO_FIFO |
        NISTC_INTB_ENA_AO_ERR, 0);
   s->async->events |= COMEDI_CB_OVERFLOW;
  }
 }
#endif
}

static void ni_ai_munge(struct comedi_device *dev, struct comedi_subdevice *s,
   void *data, unsigned int num_bytes,
   unsigned int chan_index)
{
 struct ni_private *devpriv = dev->private;
 struct comedi_async *async = s->async;
 struct comedi_cmd *cmd = &async->cmd;
 unsigned int nsamples = comedi_bytes_to_samples(s, num_bytes);
 unsigned short *array = data;
 unsigned int *larray = data;
 unsigned int i;
#ifdef PCIDMA
 __le16 *barray = data;
 __le32 *blarray = data;
#endif

 for (i = 0; i < nsamples; i++) {
#ifdef PCIDMA
  if (s->subdev_flags & SDF_LSAMPL)
   larray[i] = le32_to_cpu(blarray[i]);
  else
   array[i] = le16_to_cpu(barray[i]);
#endif
  if (s->subdev_flags & SDF_LSAMPL)
   larray[i] += devpriv->ai_offset[chan_index];
  else
   array[i] += devpriv->ai_offset[chan_index];
  chan_index++;
  chan_index %= cmd->chanlist_len;
 }
}

#ifdef PCIDMA

static int ni_ai_setup_MITE_dma(struct comedi_device *dev)
{
 struct ni_private *devpriv = dev->private;
 struct comedi_subdevice *s = dev->read_subdev;
 int retval;
 unsigned long flags;

 retval = ni_request_ai_mite_channel(dev);
 if (retval)
  return retval;

 /* write alloc the entire buffer */
 comedi_buf_write_alloc(s, s->async->prealloc_bufsz);

 spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
 if (!devpriv->ai_mite_chan) {
  spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
  return -EIO;
 }

 if (devpriv->is_611x || devpriv->is_6143)
  mite_prep_dma(devpriv->ai_mite_chan, 32, 16);
 else if (devpriv->is_628x)
  mite_prep_dma(devpriv->ai_mite_chan, 32, 32);
 else
  mite_prep_dma(devpriv->ai_mite_chan, 16, 16);

 /*start the MITE */
 mite_dma_arm(devpriv->ai_mite_chan);
 spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);

 return 0;
}

static int ni_ao_setup_MITE_dma(struct comedi_device *dev)
{
 struct ni_private *devpriv = dev->private;
 struct comedi_subdevice *s = dev->write_subdev;
 int retval;
 unsigned long flags;

 retval = ni_request_ao_mite_channel(dev);
 if (retval)
  return retval;

 /* read alloc the entire buffer */
 comedi_buf_read_alloc(s, s->async->prealloc_bufsz);

 spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
 if (devpriv->ao_mite_chan) {
  if (devpriv->is_611x || devpriv->is_6713) {
   mite_prep_dma(devpriv->ao_mite_chan, 32, 32);
  } else {
   /*
 * Doing 32 instead of 16 bit wide transfers from
 * memory makes the mite do 32 bit pci transfers,
 * doubling pci bandwidth.
 */

   mite_prep_dma(devpriv->ao_mite_chan, 16, 32);
  }
  mite_dma_arm(devpriv->ao_mite_chan);
 } else {
  retval = -EIO;
 }
 spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);

 return retval;
}

#endif /*  PCIDMA */

/*
 * used for both cancel ioctl and board initialization
 *
 * this is pretty harsh for a cancel, but it works...
 */

static int ni_ai_reset(struct comedi_device *dev, struct comedi_subdevice *s)
{
 struct ni_private *devpriv = dev->private;
 unsigned int ai_personal;
 unsigned int ai_out_ctrl;

 ni_release_ai_mite_channel(dev);
 /* ai configuration */
 ni_stc_writew(dev, NISTC_RESET_AI_CFG_START | NISTC_RESET_AI,
        NISTC_RESET_REG);

 ni_set_bits(dev, NISTC_INTA_ENA_REG, NISTC_INTA_ENA_AI_MASK, 0);

 ni_clear_ai_fifo(dev);

 if (!devpriv->is_6143)
  ni_writeb(dev, NI_E_MISC_CMD_EXT_ATRIG, NI_E_MISC_CMD_REG);

 ni_stc_writew(dev, NISTC_AI_CMD1_DISARM, NISTC_AI_CMD1_REG);
 ni_stc_writew(dev, NISTC_AI_MODE1_START_STOP |
      NISTC_AI_MODE1_RSVD
       /*| NISTC_AI_MODE1_TRIGGER_ONCE */,
        NISTC_AI_MODE1_REG);
 ni_stc_writew(dev, 0, NISTC_AI_MODE2_REG);
 /* generate FIFO interrupts on non-empty */
 ni_stc_writew(dev, NISTC_AI_MODE3_FIFO_MODE_NE,
        NISTC_AI_MODE3_REG);

 ai_personal = NISTC_AI_PERSONAL_SHIFTIN_PW |
        NISTC_AI_PERSONAL_SOC_POLARITY |
        NISTC_AI_PERSONAL_LOCALMUX_CLK_PW;
 ai_out_ctrl = NISTC_AI_OUT_CTRL_SCAN_IN_PROG_SEL(3) |
        NISTC_AI_OUT_CTRL_EXTMUX_CLK_SEL(0) |
        NISTC_AI_OUT_CTRL_LOCALMUX_CLK_SEL(2) |
        NISTC_AI_OUT_CTRL_SC_TC_SEL(3);
 if (devpriv->is_611x) {
  ai_out_ctrl |= NISTC_AI_OUT_CTRL_CONVERT_HIGH;
 } else if (devpriv->is_6143) {
  ai_out_ctrl |= NISTC_AI_OUT_CTRL_CONVERT_LOW;
 } else {
  ai_personal |= NISTC_AI_PERSONAL_CONVERT_PW;
  if (devpriv->is_622x)
   ai_out_ctrl |= NISTC_AI_OUT_CTRL_CONVERT_HIGH;
  else
   ai_out_ctrl |= NISTC_AI_OUT_CTRL_CONVERT_LOW;
 }
 ni_stc_writew(dev, ai_personal, NISTC_AI_PERSONAL_REG);
 ni_stc_writew(dev, ai_out_ctrl, NISTC_AI_OUT_CTRL_REG);

 /* the following registers should not be changed, because there
 * are no backup registers in devpriv.  If you want to change
 * any of these, add a backup register and other appropriate code:
 *      NISTC_AI_MODE1_REG
 *      NISTC_AI_MODE3_REG
 *      NISTC_AI_PERSONAL_REG
 *      NISTC_AI_OUT_CTRL_REG
 */


 /* clear interrupts */
 ni_stc_writew(dev, NISTC_INTA_ACK_AI_ALL, NISTC_INTA_ACK_REG);

 ni_stc_writew(dev, NISTC_RESET_AI_CFG_END, NISTC_RESET_REG);

 return 0;
}

static int ni_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
{
 unsigned long flags;
 int count;

 /*  lock to avoid race with interrupt handler */
 spin_lock_irqsave(&dev->spinlock, flags);
#ifndef PCIDMA
 ni_handle_fifo_dregs(dev);
#else
 ni_sync_ai_dma(dev);
#endif
 count = comedi_buf_n_bytes_ready(s);
 spin_unlock_irqrestore(&dev->spinlock, flags);

 return count;
}

static void ni_prime_channelgain_list(struct comedi_device *dev)
{
 int i;

 ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE, NISTC_AI_CMD1_REG);
 for (i = 0; i < NI_TIMEOUT; ++i) {
  if (!(ni_stc_readw(dev, NISTC_AI_STATUS1_REG) &
        NISTC_AI_STATUS1_FIFO_E)) {
   ni_stc_writew(dev, 1, NISTC_ADC_FIFO_CLR_REG);
   return;
  }
  udelay(1);
 }
 dev_err(dev->class_dev, "timeout loading channel/gain list\n");
}

static void ni_m_series_load_channelgain_list(struct comedi_device *dev,
           unsigned int n_chan,
           unsigned int *list)
{
 const struct ni_board_struct *board = dev->board_ptr;
 struct ni_private *devpriv = dev->private;
 unsigned int chan, range, aref;
 unsigned int i;
 unsigned int dither;
 unsigned int range_code;

 ni_stc_writew(dev, 1, NISTC_CFG_MEM_CLR_REG);

 if ((list[0] & CR_ALT_SOURCE)) {
  unsigned int bypass_bits;

  chan = CR_CHAN(list[0]);
  range = CR_RANGE(list[0]);
  range_code = ni_gainlkup[board->gainlkup][range];
  dither = (list[0] & CR_ALT_FILTER) != 0;
  bypass_bits = NI_M_CFG_BYPASS_FIFO |
         NI_M_CFG_BYPASS_AI_CHAN(chan) |
         NI_M_CFG_BYPASS_AI_GAIN(range_code) |
         devpriv->ai_calib_source;
  if (dither)
   bypass_bits |= NI_M_CFG_BYPASS_AI_DITHER;
  /*  don't use 2's complement encoding */
  bypass_bits |= NI_M_CFG_BYPASS_AI_POLARITY;
  ni_writel(dev, bypass_bits, NI_M_CFG_BYPASS_FIFO_REG);
 } else {
  ni_writel(dev, 0, NI_M_CFG_BYPASS_FIFO_REG);
 }
 for (i = 0; i < n_chan; i++) {
  unsigned int config_bits = 0;

  chan = CR_CHAN(list[i]);
  aref = CR_AREF(list[i]);
  range = CR_RANGE(list[i]);
  dither = (list[i] & CR_ALT_FILTER) != 0;

  range_code = ni_gainlkup[board->gainlkup][range];
  devpriv->ai_offset[i] = 0;
  switch (aref) {
  case AREF_DIFF:
   config_bits |= NI_M_AI_CFG_CHAN_TYPE_DIFF;
   break;
  case AREF_COMMON:
   config_bits |= NI_M_AI_CFG_CHAN_TYPE_COMMON;
   break;
  case AREF_GROUND:
   config_bits |= NI_M_AI_CFG_CHAN_TYPE_GROUND;
   break;
  case AREF_OTHER:
   break;
  }
  config_bits |= NI_M_AI_CFG_CHAN_SEL(chan);
  config_bits |= NI_M_AI_CFG_BANK_SEL(chan);
  config_bits |= NI_M_AI_CFG_GAIN(range_code);
  if (i == n_chan - 1)
   config_bits |= NI_M_AI_CFG_LAST_CHAN;
  if (dither)
   config_bits |= NI_M_AI_CFG_DITHER;
  /*  don't use 2's complement encoding */
  config_bits |= NI_M_AI_CFG_POLARITY;
  ni_writew(dev, config_bits, NI_M_AI_CFG_FIFO_DATA_REG);
 }
 ni_prime_channelgain_list(dev);
}

/*
 * Notes on the 6110 and 6111:
 * These boards a slightly different than the rest of the series, since
 * they have multiple A/D converters.
 * From the driver side, the configuration memory is a
 * little different.
 * Configuration Memory Low:
 *   bits 15-9: same
 *   bit 8: unipolar/bipolar (should be 0 for bipolar)
 *   bits 0-3: gain.  This is 4 bits instead of 3 for the other boards
 *       1001 gain=0.1 (+/- 50)
 *       1010 0.2
 *       1011 0.1
 *       0001 1
 *       0010 2
 *       0011 5
 *       0100 10
 *       0101 20
 *       0110 50
 * Configuration Memory High:
 *   bits 12-14: Channel Type
 *       001 for differential
 *       000 for calibration
 *   bit 11: coupling  (this is not currently handled)
 *       1 AC coupling
 *       0 DC coupling
 *   bits 0-2: channel
 *       valid channels are 0-3
 */

static void ni_load_channelgain_list(struct comedi_device *dev,
         struct comedi_subdevice *s,
         unsigned int n_chan, unsigned int *list)
{
 const struct ni_board_struct *board = dev->board_ptr;
 struct ni_private *devpriv = dev->private;
 unsigned int offset = (s->maxdata + 1) >> 1;
 unsigned int chan, range, aref;
 unsigned int i;
 unsigned int hi, lo;
 unsigned int dither;

 if (devpriv->is_m_series) {
  ni_m_series_load_channelgain_list(dev, n_chan, list);
  return;
 }
 if (n_chan == 1 && !devpriv->is_611x && !devpriv->is_6143) {
  if (devpriv->changain_state &&
      devpriv->changain_spec == list[0]) {
   /*  ready to go. */
   return;
  }
  devpriv->changain_state = 1;
  devpriv->changain_spec = list[0];
 } else {
  devpriv->changain_state = 0;
 }

 ni_stc_writew(dev, 1, NISTC_CFG_MEM_CLR_REG);

 /*  Set up Calibration mode if required */
 if (devpriv->is_6143) {
  if ((list[0] & CR_ALT_SOURCE) &&
      !devpriv->ai_calib_source_enabled) {
   /*  Strobe Relay enable bit */
   ni_writew(dev, devpriv->ai_calib_source |
           NI6143_CALIB_CHAN_RELAY_ON,
      NI6143_CALIB_CHAN_REG);
   ni_writew(dev, devpriv->ai_calib_source,
      NI6143_CALIB_CHAN_REG);
   devpriv->ai_calib_source_enabled = 1;
   /* Allow relays to change */
   msleep_interruptible(100);
  } else if (!(list[0] & CR_ALT_SOURCE) &&
      devpriv->ai_calib_source_enabled) {
   /*  Strobe Relay disable bit */
   ni_writew(dev, devpriv->ai_calib_source |
           NI6143_CALIB_CHAN_RELAY_OFF,
      NI6143_CALIB_CHAN_REG);
   ni_writew(dev, devpriv->ai_calib_source,
      NI6143_CALIB_CHAN_REG);
   devpriv->ai_calib_source_enabled = 0;
   /* Allow relays to change */
   msleep_interruptible(100);
  }
 }

 for (i = 0; i < n_chan; i++) {
  if (!devpriv->is_6143 && (list[i] & CR_ALT_SOURCE))
   chan = devpriv->ai_calib_source;
  else
   chan = CR_CHAN(list[i]);
  aref = CR_AREF(list[i]);
  range = CR_RANGE(list[i]);
  dither = (list[i] & CR_ALT_FILTER) != 0;

  /* fix the external/internal range differences */
  range = ni_gainlkup[board->gainlkup][range];
  if (devpriv->is_611x)
   devpriv->ai_offset[i] = offset;
  else
   devpriv->ai_offset[i] = (range & 0x100) ? 0 : offset;

  hi = 0;
  if ((list[i] & CR_ALT_SOURCE)) {
   if (devpriv->is_611x)
    ni_writew(dev, CR_CHAN(list[i]) & 0x0003,
       NI611X_CALIB_CHAN_SEL_REG);
  } else {
   if (devpriv->is_611x)
    aref = AREF_DIFF;
   else if (devpriv->is_6143)
    aref = AREF_OTHER;
   switch (aref) {
   case AREF_DIFF:
    hi |= NI_E_AI_CFG_HI_TYPE_DIFF;
    break;
   case AREF_COMMON:
    hi |= NI_E_AI_CFG_HI_TYPE_COMMON;
    break;
   case AREF_GROUND:
    hi |= NI_E_AI_CFG_HI_TYPE_GROUND;
    break;
   case AREF_OTHER:
    break;
   }
  }
  hi |= NI_E_AI_CFG_HI_CHAN(chan);

  ni_writew(dev, hi, NI_E_AI_CFG_HI_REG);

  if (!devpriv->is_6143) {
   lo = NI_E_AI_CFG_LO_GAIN(range);

   if (i == n_chan - 1)
    lo |= NI_E_AI_CFG_LO_LAST_CHAN;
   if (dither)
    lo |= NI_E_AI_CFG_LO_DITHER;

   ni_writew(dev, lo, NI_E_AI_CFG_LO_REG);
  }
 }

 /* prime the channel/gain list */
 if (!devpriv->is_611x && !devpriv->is_6143)
  ni_prime_channelgain_list(dev);
}

static int ni_ai_insn_read(struct comedi_device *dev,
      struct comedi_subdevice *s,
      struct comedi_insn *insn,
      unsigned int *data)
{
 struct ni_private *devpriv = dev->private;
 unsigned int mask = s->maxdata;
 int i, n;
 unsigned int signbits;
 unsigned int d;

 ni_load_channelgain_list(dev, s, 1, &insn->chanspec);

 ni_clear_ai_fifo(dev);

 signbits = devpriv->ai_offset[0];
 if (devpriv->is_611x) {
  for (n = 0; n < num_adc_stages_611x; n++) {
   ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE,
          NISTC_AI_CMD1_REG);
   udelay(1);
  }
  for (n = 0; n < insn->n; n++) {
   ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE,
          NISTC_AI_CMD1_REG);
   /* The 611x has screwy 32-bit FIFOs. */
   d = 0;
   for (i = 0; i < NI_TIMEOUT; i++) {
    if (ni_readb(dev, NI_E_STATUS_REG) & 0x80) {
     d = ni_readl(dev,
           NI611X_AI_FIFO_DATA_REG);
     d >>= 16;
     d &= 0xffff;
     break;
    }
    if (!(ni_stc_readw(dev, NISTC_AI_STATUS1_REG) &
          NISTC_AI_STATUS1_FIFO_E)) {
     d = ni_readl(dev,
           NI611X_AI_FIFO_DATA_REG);
     d &= 0xffff;
     break;
    }
   }
   if (i == NI_TIMEOUT) {
    dev_err(dev->class_dev, "timeout\n");
    return -ETIME;
   }
   d += signbits;
   data[n] = d & 0xffff;
  }
 } else if (devpriv->is_6143) {
  for (n = 0; n < insn->n; n++) {
   ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE,
          NISTC_AI_CMD1_REG);

   /*
 * The 6143 has 32-bit FIFOs. You need to strobe a
 * bit to move a single 16bit stranded sample into
 * the FIFO.
 */

   d = 0;
   for (i = 0; i < NI_TIMEOUT; i++) {
    if (ni_readl(dev, NI6143_AI_FIFO_STATUS_REG) &
        0x01) {
     /* Get stranded sample into FIFO */
     ni_writel(dev, 0x01,
        NI6143_AI_FIFO_CTRL_REG);
     d = ni_readl(dev,
           NI6143_AI_FIFO_DATA_REG);
     break;
    }
   }
   if (i == NI_TIMEOUT) {
    dev_err(dev->class_dev, "timeout\n");
    return -ETIME;
   }
   data[n] = (((d >> 16) & 0xFFFF) + signbits) & 0xFFFF;
  }
 } else {
  for (n = 0; n < insn->n; n++) {
   ni_stc_writew(dev, NISTC_AI_CMD1_CONVERT_PULSE,
          NISTC_AI_CMD1_REG);
   for (i = 0; i < NI_TIMEOUT; i++) {
    if (!(ni_stc_readw(dev, NISTC_AI_STATUS1_REG) &
          NISTC_AI_STATUS1_FIFO_E))
     break;
   }
   if (i == NI_TIMEOUT) {
    dev_err(dev->class_dev, "timeout\n");
    return -ETIME;
   }
   if (devpriv->is_m_series) {
    d = ni_readl(dev, NI_M_AI_FIFO_DATA_REG);
    d &= mask;
    data[n] = d;
   } else {
    d = ni_readw(dev, NI_E_AI_FIFO_DATA_REG);
    d += signbits;
    data[n] = d & 0xffff;
   }
  }
 }
 return insn->n;
}

static int ni_ns_to_timer(const struct comedi_device *dev,
     unsigned int nanosec, unsigned int flags)
{
 struct ni_private *devpriv = dev->private;
 int divider;

 switch (flags & CMDF_ROUND_MASK) {
 case CMDF_ROUND_NEAREST:
 default:
  divider = DIV_ROUND_CLOSEST(nanosec, devpriv->clock_ns);
  break;
 case CMDF_ROUND_DOWN:
  divider = (nanosec) / devpriv->clock_ns;
  break;
 case CMDF_ROUND_UP:
  divider = DIV_ROUND_UP(nanosec, devpriv->clock_ns);
  break;
 }
 return divider - 1;
}

static unsigned int ni_timer_to_ns(const struct comedi_device *dev, int timer)
{
 struct ni_private *devpriv = dev->private;

 return devpriv->clock_ns * (timer + 1);
}

static void ni_cmd_set_mite_transfer(struct mite_ring *ring,
         struct comedi_subdevice *sdev,
         const struct comedi_cmd *cmd,
         unsigned int max_count)
{
#ifdef PCIDMA
 unsigned int nbytes = max_count;

 if (cmd->stop_arg > 0 && cmd->stop_arg < max_count)
  nbytes = cmd->stop_arg;
 nbytes *= comedi_bytes_per_scan(sdev);

 if (nbytes > sdev->async->prealloc_bufsz) {
  if (cmd->stop_arg > 0)
   dev_err(sdev->device->class_dev,
    "%s: tried exact data transfer limits greater than buffer size\n",
    __func__);

  /*
 * we can only transfer up to the size of the buffer.  In this
 * case, the user is expected to continue to write into the
 * comedi buffer (already implemented as a ring buffer).
 */

  nbytes = sdev->async->prealloc_bufsz;
 }

 mite_init_ring_descriptors(ring, sdev, nbytes);
#else
 dev_err(sdev->device->class_dev,
  "%s: exact data transfer limits not implemented yet without DMA\n",
  __func__);
#endif
}

static unsigned int ni_min_ai_scan_period_ns(struct comedi_device *dev,
          unsigned int num_channels)
{
 const struct ni_board_struct *board = dev->board_ptr;
 struct ni_private *devpriv = dev->private;

 /* simultaneously-sampled inputs */
 if (devpriv->is_611x || devpriv->is_6143)
  return board->ai_speed;

 /* multiplexed inputs */
 return board->ai_speed * num_channels;
}

static int ni_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
    struct comedi_cmd *cmd)
{
 const struct ni_board_struct *board = dev->board_ptr;
 struct ni_private *devpriv = dev->private;
 int err = 0;
 unsigned int sources;

 /* Step 1 : check if triggers are trivially valid */

 err |= comedi_check_trigger_src(&cmd->start_src,
     TRIG_NOW | TRIG_INT | TRIG_EXT);
 err |= comedi_check_trigger_src(&cmd->scan_begin_src,
     TRIG_TIMER | TRIG_EXT);

 sources = TRIG_TIMER | TRIG_EXT;
 if (devpriv->is_611x || devpriv->is_6143)
  sources |= TRIG_NOW;
 err |= comedi_check_trigger_src(&cmd->convert_src, sources);

 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);

 if (err)
  return 1;

 /* Step 2a : make sure trigger sources are unique */

 err |= comedi_check_trigger_is_unique(cmd->start_src);
 err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
 err |= comedi_check_trigger_is_unique(cmd->convert_src);
 err |= comedi_check_trigger_is_unique(cmd->stop_src);

 /* Step 2b : and mutually compatible */

 if (err)
  return 2;

 /* Step 3: check if arguments are trivially valid */

 switch (cmd->start_src) {
 case TRIG_NOW:
 case TRIG_INT:
  err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
  break;
 case TRIG_EXT:
  err |= ni_check_trigger_arg_roffs(CR_CHAN(cmd->start_arg),
        NI_AI_StartTrigger,
        &devpriv->routing_tables, 1);
  break;
 }

 if (cmd->scan_begin_src == TRIG_TIMER) {
  err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
   ni_min_ai_scan_period_ns(dev, cmd->chanlist_len));
  err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
          devpriv->clock_ns *
          0xffffff);
 } else if (cmd->scan_begin_src == TRIG_EXT) {
  /* external trigger */
  err |= ni_check_trigger_arg_roffs(CR_CHAN(cmd->scan_begin_arg),
        NI_AI_SampleClock,
        &devpriv->routing_tables, 1);
 } else {  /* TRIG_OTHER */
  err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 }

 if (cmd->convert_src == TRIG_TIMER) {
  if (devpriv->is_611x || devpriv->is_6143) {
   err |= comedi_check_trigger_arg_is(&cmd->convert_arg,
          0);
  } else {
   err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
           board->ai_speed);
   err |= comedi_check_trigger_arg_max(&cmd->convert_arg,
           devpriv->clock_ns *
           0xffff);
  }
 } else if (cmd->convert_src == TRIG_EXT) {
  /* external trigger */
  err |= ni_check_trigger_arg_roffs(CR_CHAN(cmd->convert_arg),
        NI_AI_ConvertClock,
        &devpriv->routing_tables, 1);
 } else if (cmd->convert_src == TRIG_NOW) {
  err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 }

 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
        cmd->chanlist_len);

 if (cmd->stop_src == TRIG_COUNT) {
  unsigned int max_count = 0x01000000;

  if (devpriv->is_611x)
   max_count -= num_adc_stages_611x;
  err |= comedi_check_trigger_arg_max(&cmd->stop_arg, max_count);
  err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 } else {
  /* TRIG_NONE */
  err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 }

 if (err)
  return 3;

 /* step 4: fix up any arguments */

 if (cmd->scan_begin_src == TRIG_TIMER) {
  unsigned int tmp = cmd->scan_begin_arg;

  cmd->scan_begin_arg =
      ni_timer_to_ns(dev, ni_ns_to_timer(dev,
             cmd->scan_begin_arg,
             cmd->flags));
  if (tmp != cmd->scan_begin_arg)
   err++;
 }
 if (cmd->convert_src == TRIG_TIMER) {
  if (!devpriv->is_611x && !devpriv->is_6143) {
   unsigned int tmp = cmd->convert_arg;

   cmd->convert_arg =
       ni_timer_to_ns(dev, ni_ns_to_timer(dev,
              cmd->convert_arg,
              cmd->flags));
   if (tmp != cmd->convert_arg)
    err++;
   if (cmd->scan_begin_src == TRIG_TIMER &&
       cmd->scan_begin_arg <
       cmd->convert_arg * cmd->scan_end_arg) {
    cmd->scan_begin_arg =
        cmd->convert_arg * cmd->scan_end_arg;
    err++;
   }
  }
 }

 if (err)
  return 4;

 return 0;
}

static int ni_ai_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;

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

 ni_stc_writew(dev, NISTC_AI_CMD2_START1_PULSE | devpriv->ai_cmd2,
        NISTC_AI_CMD2_REG);
 s->async->inttrig = NULL;

 return 1;
}

static int ni_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
 struct ni_private *devpriv = dev->private;
 const struct comedi_cmd *cmd = &s->async->cmd;
 int timer;
 int mode1 = 0;  /* mode1 is needed for both stop and convert */
 int mode2 = 0;
 int start_stop_select = 0;
 unsigned int stop_count;
 int interrupt_a_enable = 0;
 unsigned int ai_trig;

 if (dev->irq == 0) {
  dev_err(dev->class_dev, "cannot run command without an irq\n");
  return -EIO;
 }
 ni_clear_ai_fifo(dev);

 ni_load_channelgain_list(dev, s, cmd->chanlist_len, cmd->chanlist);

 /* start configuration */
 ni_stc_writew(dev, NISTC_RESET_AI_CFG_START, NISTC_RESET_REG);

 /*
 * Disable analog triggering for now, since it interferes
 * with the use of pfi0.
 */

 devpriv->an_trig_etc_reg &= ~NISTC_ATRIG_ETC_ENA;
 ni_stc_writew(dev, devpriv->an_trig_etc_reg, NISTC_ATRIG_ETC_REG);

 ai_trig = NISTC_AI_TRIG_START2_SEL(0) | NISTC_AI_TRIG_START1_SYNC;
 switch (cmd->start_src) {
 case TRIG_INT:
 case TRIG_NOW:
  ai_trig |= NISTC_AI_TRIG_START1_EDGE |
      NISTC_AI_TRIG_START1_SEL(0);
  break;
 case TRIG_EXT:
  ai_trig |= NISTC_AI_TRIG_START1_SEL(
    ni_get_reg_value_roffs(
     CR_CHAN(cmd->start_arg),
     NI_AI_StartTrigger,
     &devpriv->routing_tables, 1));

  if (cmd->start_arg & CR_INVERT)
   ai_trig |= NISTC_AI_TRIG_START1_POLARITY;
  if (cmd->start_arg & CR_EDGE)
   ai_trig |= NISTC_AI_TRIG_START1_EDGE;
  break;
 }
 ni_stc_writew(dev, ai_trig, NISTC_AI_TRIG_SEL_REG);

 mode2 &= ~NISTC_AI_MODE2_PRE_TRIGGER;
 mode2 &= ~NISTC_AI_MODE2_SC_INIT_LOAD_SRC;
 mode2 &= ~NISTC_AI_MODE2_SC_RELOAD_MODE;
 ni_stc_writew(dev, mode2, NISTC_AI_MODE2_REG);

 if (cmd->chanlist_len == 1 || devpriv->is_611x || devpriv->is_6143) {
  /* logic low */
  start_stop_select |= NISTC_AI_STOP_POLARITY |
         NISTC_AI_STOP_SEL(31) |
         NISTC_AI_STOP_SYNC;
 } else {
  /*  ai configuration memory */
  start_stop_select |= NISTC_AI_STOP_SEL(19);
 }
 ni_stc_writew(dev, start_stop_select, NISTC_AI_START_STOP_REG);

 devpriv->ai_cmd2 = 0;
 switch (cmd->stop_src) {
 case TRIG_COUNT:
  stop_count = cmd->stop_arg - 1;

  if (devpriv->is_611x) {
   /*  have to take 3 stage adc pipeline into account */
   stop_count += num_adc_stages_611x;
  }
  /* stage number of scans */
  ni_stc_writel(dev, stop_count, NISTC_AI_SC_LOADA_REG);

  mode1 |= NISTC_AI_MODE1_START_STOP |
    NISTC_AI_MODE1_RSVD |
    NISTC_AI_MODE1_TRIGGER_ONCE;
  ni_stc_writew(dev, mode1, NISTC_AI_MODE1_REG);
  /* load SC (Scan Count) */
  ni_stc_writew(dev, NISTC_AI_CMD1_SC_LOAD, NISTC_AI_CMD1_REG);

  if (stop_count == 0) {
   devpriv->ai_cmd2 |= NISTC_AI_CMD2_END_ON_EOS;
   interrupt_a_enable |= NISTC_INTA_ENA_AI_STOP;
   /*
 * This is required to get the last sample for
 * chanlist_len > 1, not sure why.
 */

   if (cmd->chanlist_len > 1)
    start_stop_select |= NISTC_AI_STOP_POLARITY |
           NISTC_AI_STOP_EDGE;
  }
  break;
 case TRIG_NONE:
  /* stage number of scans */
  ni_stc_writel(dev, 0, NISTC_AI_SC_LOADA_REG);

  mode1 |= NISTC_AI_MODE1_START_STOP |
    NISTC_AI_MODE1_RSVD |
    NISTC_AI_MODE1_CONTINUOUS;
  ni_stc_writew(dev, mode1, NISTC_AI_MODE1_REG);

  /* load SC (Scan Count) */
  ni_stc_writew(dev, NISTC_AI_CMD1_SC_LOAD, NISTC_AI_CMD1_REG);
  break;
 }

 switch (cmd->scan_begin_src) {
 case TRIG_TIMER:
  /*
 * stop bits for non 611x boards
 * NISTC_AI_MODE3_SI_TRIG_DELAY=0
 * NISTC_AI_MODE2_PRE_TRIGGER=0
 * NISTC_AI_START_STOP_REG:
 * NISTC_AI_START_POLARITY=0 (?) rising edge
 * NISTC_AI_START_EDGE=1 edge triggered
 * NISTC_AI_START_SYNC=1 (?)
 * NISTC_AI_START_SEL=0 SI_TC
 * NISTC_AI_STOP_POLARITY=0 rising edge
 * NISTC_AI_STOP_EDGE=0 level
 * NISTC_AI_STOP_SYNC=1
 * NISTC_AI_STOP_SEL=19 external pin (configuration mem)
 */

  start_stop_select |= NISTC_AI_START_EDGE | NISTC_AI_START_SYNC;
  ni_stc_writew(dev, start_stop_select, NISTC_AI_START_STOP_REG);

  mode2 &= ~NISTC_AI_MODE2_SI_INIT_LOAD_SRC; /* A */
  mode2 |= NISTC_AI_MODE2_SI_RELOAD_MODE(0);
  /* mode2 |= NISTC_AI_MODE2_SC_RELOAD_MODE; */
  ni_stc_writew(dev, mode2, NISTC_AI_MODE2_REG);

  /* load SI */
  timer = ni_ns_to_timer(dev, cmd->scan_begin_arg,
           CMDF_ROUND_NEAREST);
  ni_stc_writel(dev, timer, NISTC_AI_SI_LOADA_REG);
  ni_stc_writew(dev, NISTC_AI_CMD1_SI_LOAD, NISTC_AI_CMD1_REG);
  break;
 case TRIG_EXT:
  if (cmd->scan_begin_arg & CR_EDGE)
   start_stop_select |= NISTC_AI_START_EDGE;
  if (cmd->scan_begin_arg & CR_INVERT) /* falling edge */
   start_stop_select |= NISTC_AI_START_POLARITY;
  if (cmd->scan_begin_src != cmd->convert_src ||
      (cmd->scan_begin_arg & ~CR_EDGE) !=
      (cmd->convert_arg & ~CR_EDGE))
   start_stop_select |= NISTC_AI_START_SYNC;

  start_stop_select |= NISTC_AI_START_SEL(
     ni_get_reg_value_roffs(
      CR_CHAN(cmd->scan_begin_arg),
      NI_AI_SampleClock,
      &devpriv->routing_tables, 1));
  ni_stc_writew(dev, start_stop_select, NISTC_AI_START_STOP_REG);
  break;
 }

 switch (cmd->convert_src) {
 case TRIG_TIMER:
 case TRIG_NOW:
  if (cmd->convert_arg == 0 || cmd->convert_src == TRIG_NOW)
   timer = 1;
  else
   timer = ni_ns_to_timer(dev, cmd->convert_arg,
            CMDF_ROUND_NEAREST);
  /* 0,0 does not work */
  ni_stc_writew(dev, 1, NISTC_AI_SI2_LOADA_REG);
  ni_stc_writew(dev, timer, NISTC_AI_SI2_LOADB_REG);

  mode2 &= ~NISTC_AI_MODE2_SI2_INIT_LOAD_SRC; /* A */
  mode2 |= NISTC_AI_MODE2_SI2_RELOAD_MODE; /* alternate */
  ni_stc_writew(dev, mode2, NISTC_AI_MODE2_REG);

  ni_stc_writew(dev, NISTC_AI_CMD1_SI2_LOAD, NISTC_AI_CMD1_REG);

  mode2 |= NISTC_AI_MODE2_SI2_INIT_LOAD_SRC; /* B */
  mode2 |= NISTC_AI_MODE2_SI2_RELOAD_MODE; /* alternate */
  ni_stc_writew(dev, mode2, NISTC_AI_MODE2_REG);
  break;
 case TRIG_EXT:
  mode1 |= NISTC_AI_MODE1_CONVERT_SRC(
    ni_get_reg_value_roffs(
      CR_CHAN(cmd->convert_arg),
      NI_AI_ConvertClock,
      &devpriv->routing_tables, 1));
  if ((cmd->convert_arg & CR_INVERT) == 0)
   mode1 |= NISTC_AI_MODE1_CONVERT_POLARITY;
  ni_stc_writew(dev, mode1, NISTC_AI_MODE1_REG);

  mode2 |= NISTC_AI_MODE2_SC_GATE_ENA |
    NISTC_AI_MODE2_START_STOP_GATE_ENA;
  ni_stc_writew(dev, mode2, NISTC_AI_MODE2_REG);

  break;
 }

 if (dev->irq) {
  /* interrupt on FIFO, errors, SC_TC */
  interrupt_a_enable |= NISTC_INTA_ENA_AI_ERR |
          NISTC_INTA_ENA_AI_SC_TC;

#ifndef PCIDMA
  interrupt_a_enable |= NISTC_INTA_ENA_AI_FIFO;
#endif

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=94 G=94

¤ Dauer der Verarbeitung: 0.23 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.