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 35 kB image not shown  

Quelle  ni_labpc_common.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * comedi/drivers/ni_labpc_common.c
 *
 * Common support code for "ni_labpc", "ni_labpc_pci" and "ni_labpc_cs".
 *
 * Copyright (C) 2001-2003 Frank Mori Hess <fmhess@users.sourceforge.net>
 */


#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/comedi/comedidev.h>
#include <linux/comedi/comedi_8255.h>
#include <linux/comedi/comedi_8254.h>

#include "ni_labpc.h"
#include "ni_labpc_regs.h"
#include "ni_labpc_isadma.h"

enum scan_mode {
 MODE_SINGLE_CHAN,
 MODE_SINGLE_CHAN_INTERVAL,
 MODE_MULT_CHAN_UP,
 MODE_MULT_CHAN_DOWN,
};

static const struct comedi_lrange range_labpc_plus_ai = {
 16, {
  BIP_RANGE(5),
  BIP_RANGE(4),
  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(10),
  UNI_RANGE(8),
  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_labpc_1200_ai = {
 14, {
  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(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_labpc_ao = {
 2, {
  BIP_RANGE(5),
  UNI_RANGE(10)
 }
};

/*
 * functions that do inb/outb and readb/writeb so we can use
 * function pointers to decide which to use
 */


#ifdef CONFIG_HAS_IOPORT

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

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

#endif /* CONFIG_HAS_IOPORT */

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

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

static int labpc_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
 struct labpc_private *devpriv = dev->private;
 unsigned long flags;

 spin_lock_irqsave(&dev->spinlock, flags);
 devpriv->cmd2 &= ~(CMD2_SWTRIG | CMD2_HWTRIG | CMD2_PRETRIG);
 devpriv->write_byte(dev, devpriv->cmd2, CMD2_REG);
 spin_unlock_irqrestore(&dev->spinlock, flags);

 devpriv->cmd3 = 0;
 devpriv->write_byte(dev, devpriv->cmd3, CMD3_REG);

 return 0;
}

static void labpc_ai_set_chan_and_gain(struct comedi_device *dev,
           enum scan_mode mode,
           unsigned int chan,
           unsigned int range,
           unsigned int aref)
{
 const struct labpc_boardinfo *board = dev->board_ptr;
 struct labpc_private *devpriv = dev->private;

 if (board->is_labpc1200) {
  /*
 * The LabPC-1200 boards do not have a gain
 * of '0x10'. Skip the range values that would
 * result in this gain.
 */

  range += (range > 0) + (range > 7);
 }

 /* munge channel bits for differential/scan disabled mode */
 if ((mode == MODE_SINGLE_CHAN || mode == MODE_SINGLE_CHAN_INTERVAL) &&
     aref == AREF_DIFF)
  chan *= 2;
 devpriv->cmd1 = CMD1_MA(chan);
 devpriv->cmd1 |= CMD1_GAIN(range);

 devpriv->write_byte(dev, devpriv->cmd1, CMD1_REG);
}

static void labpc_setup_cmd6_reg(struct comedi_device *dev,
     struct comedi_subdevice *s,
     enum scan_mode mode,
     enum transfer_type xfer,
     unsigned int range,
     unsigned int aref,
     bool ena_intr)
{
 const struct labpc_boardinfo *board = dev->board_ptr;
 struct labpc_private *devpriv = dev->private;

 if (!board->is_labpc1200)
  return;

 /* reference inputs to ground or common? */
 if (aref != AREF_GROUND)
  devpriv->cmd6 |= CMD6_NRSE;
 else
  devpriv->cmd6 &= ~CMD6_NRSE;

 /* bipolar or unipolar range? */
 if (comedi_range_is_unipolar(s, range))
  devpriv->cmd6 |= CMD6_ADCUNI;
 else
  devpriv->cmd6 &= ~CMD6_ADCUNI;

 /*  interrupt on fifo half full? */
 if (xfer == fifo_half_full_transfer)
  devpriv->cmd6 |= CMD6_HFINTEN;
 else
  devpriv->cmd6 &= ~CMD6_HFINTEN;

 /* enable interrupt on counter a1 terminal count? */
 if (ena_intr)
  devpriv->cmd6 |= CMD6_DQINTEN;
 else
  devpriv->cmd6 &= ~CMD6_DQINTEN;

 /* are we scanning up or down through channels? */
 if (mode == MODE_MULT_CHAN_UP)
  devpriv->cmd6 |= CMD6_SCANUP;
 else
  devpriv->cmd6 &= ~CMD6_SCANUP;

 devpriv->write_byte(dev, devpriv->cmd6, CMD6_REG);
}

static unsigned int labpc_read_adc_fifo(struct comedi_device *dev)
{
 struct labpc_private *devpriv = dev->private;
 unsigned int lsb = devpriv->read_byte(dev, ADC_FIFO_REG);
 unsigned int msb = devpriv->read_byte(dev, ADC_FIFO_REG);

 return (msb << 8) | lsb;
}

static void labpc_clear_adc_fifo(struct comedi_device *dev)
{
 struct labpc_private *devpriv = dev->private;

 devpriv->write_byte(dev, 0x1, ADC_FIFO_CLEAR_REG);
 labpc_read_adc_fifo(dev);
}

static int labpc_ai_eoc(struct comedi_device *dev,
   struct comedi_subdevice *s,
   struct comedi_insn *insn,
   unsigned long context)
{
 struct labpc_private *devpriv = dev->private;

 devpriv->stat1 = devpriv->read_byte(dev, STAT1_REG);
 if (devpriv->stat1 & STAT1_DAVAIL)
  return 0;
 return -EBUSY;
}

static int labpc_ai_insn_read(struct comedi_device *dev,
         struct comedi_subdevice *s,
         struct comedi_insn *insn,
         unsigned int *data)
{
 struct labpc_private *devpriv = dev->private;
 unsigned int chan = CR_CHAN(insn->chanspec);
 unsigned int range = CR_RANGE(insn->chanspec);
 unsigned int aref = CR_AREF(insn->chanspec);
 int ret;
 int i;

 /* disable timed conversions, interrupt generation and dma */
 labpc_cancel(dev, s);

 labpc_ai_set_chan_and_gain(dev, MODE_SINGLE_CHAN, chan, range, aref);

 labpc_setup_cmd6_reg(dev, s, MODE_SINGLE_CHAN, fifo_not_empty_transfer,
        range, aref, false);

 /* setup cmd4 register */
 devpriv->cmd4 = 0;
 devpriv->cmd4 |= CMD4_ECLKRCV;
 /* single-ended/differential */
 if (aref == AREF_DIFF)
  devpriv->cmd4 |= CMD4_SEDIFF;
 devpriv->write_byte(dev, devpriv->cmd4, CMD4_REG);

 /* initialize pacer counter to prevent any problems */
 comedi_8254_set_mode(devpriv->counter, 0, I8254_MODE2 | I8254_BINARY);

 labpc_clear_adc_fifo(dev);

 for (i = 0; i < insn->n; i++) {
  /* trigger conversion */
  devpriv->write_byte(dev, 0x1, ADC_START_CONVERT_REG);

  ret = comedi_timeout(dev, s, insn, labpc_ai_eoc, 0);
  if (ret)
   return ret;

  data[i] = labpc_read_adc_fifo(dev);
 }

 return insn->n;
}

static bool labpc_use_continuous_mode(const struct comedi_cmd *cmd,
          enum scan_mode mode)
{
 if (mode == MODE_SINGLE_CHAN || cmd->scan_begin_src == TRIG_FOLLOW)
  return true;

 return false;
}

static unsigned int labpc_ai_convert_period(const struct comedi_cmd *cmd,
         enum scan_mode mode)
{
 if (cmd->convert_src != TRIG_TIMER)
  return 0;

 if (mode == MODE_SINGLE_CHAN && cmd->scan_begin_src == TRIG_TIMER)
  return cmd->scan_begin_arg;

 return cmd->convert_arg;
}

static void labpc_set_ai_convert_period(struct comedi_cmd *cmd,
     enum scan_mode mode, unsigned int ns)
{
 if (cmd->convert_src != TRIG_TIMER)
  return;

 if (mode == MODE_SINGLE_CHAN &&
     cmd->scan_begin_src == TRIG_TIMER) {
  cmd->scan_begin_arg = ns;
  if (cmd->convert_arg > cmd->scan_begin_arg)
   cmd->convert_arg = cmd->scan_begin_arg;
 } else {
  cmd->convert_arg = ns;
 }
}

static unsigned int labpc_ai_scan_period(const struct comedi_cmd *cmd,
      enum scan_mode mode)
{
 if (cmd->scan_begin_src != TRIG_TIMER)
  return 0;

 if (mode == MODE_SINGLE_CHAN && cmd->convert_src == TRIG_TIMER)
  return 0;

 return cmd->scan_begin_arg;
}

static void labpc_set_ai_scan_period(struct comedi_cmd *cmd,
         enum scan_mode mode, unsigned int ns)
{
 if (cmd->scan_begin_src != TRIG_TIMER)
  return;

 if (mode == MODE_SINGLE_CHAN && cmd->convert_src == TRIG_TIMER)
  return;

 cmd->scan_begin_arg = ns;
}

/* figures out what counter values to use based on command */
static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd,
        enum scan_mode mode)
{
 struct comedi_8254 *pacer = dev->pacer;
 unsigned int convert_period = labpc_ai_convert_period(cmd, mode);
 unsigned int scan_period = labpc_ai_scan_period(cmd, mode);
 unsigned int base_period;

 /*
 * If both convert and scan triggers are TRIG_TIMER, then they
 * both rely on counter b0. If only one TRIG_TIMER is used, we
 * can use the generic cascaded timing functions.
 */

 if (convert_period && scan_period) {
  /*
 * pick the lowest divisor value we can (for maximum input
 * clock speed on convert and scan counters)
 */

  pacer->next_div1 = (scan_period - 1) /
       (pacer->osc_base * I8254_MAX_COUNT) + 1;

  comedi_check_trigger_arg_min(&pacer->next_div1, 2);
  comedi_check_trigger_arg_max(&pacer->next_div1,
          I8254_MAX_COUNT);

  base_period = pacer->osc_base * pacer->next_div1;

  /*  set a0 for conversion frequency and b1 for scan frequency */
  switch (cmd->flags & CMDF_ROUND_MASK) {
  default:
  case CMDF_ROUND_NEAREST:
   pacer->next_div = DIV_ROUND_CLOSEST(convert_period,
           base_period);
   pacer->next_div2 = DIV_ROUND_CLOSEST(scan_period,
            base_period);
   break;
  case CMDF_ROUND_UP:
   pacer->next_div = DIV_ROUND_UP(convert_period,
             base_period);
   pacer->next_div2 = DIV_ROUND_UP(scan_period,
       base_period);
   break;
  case CMDF_ROUND_DOWN:
   pacer->next_div = convert_period / base_period;
   pacer->next_div2 = scan_period / base_period;
   break;
  }
  /*  make sure a0 and b1 values are acceptable */
  comedi_check_trigger_arg_min(&pacer->next_div, 2);
  comedi_check_trigger_arg_max(&pacer->next_div, I8254_MAX_COUNT);
  comedi_check_trigger_arg_min(&pacer->next_div2, 2);
  comedi_check_trigger_arg_max(&pacer->next_div2,
          I8254_MAX_COUNT);

  /*  write corrected timings to command */
  labpc_set_ai_convert_period(cmd, mode,
         base_period * pacer->next_div);
  labpc_set_ai_scan_period(cmd, mode,
      base_period * pacer->next_div2);
 } else if (scan_period) {
  /*
 * calculate cascaded counter values
 * that give desired scan timing
 * (pacer->next_div2 / pacer->next_div1)
 */

  comedi_8254_cascade_ns_to_timer(pacer, &scan_period,
      cmd->flags);
  labpc_set_ai_scan_period(cmd, mode, scan_period);
 } else if (convert_period) {
  /*
 * calculate cascaded counter values
 * that give desired conversion timing
 * (pacer->next_div / pacer->next_div1)
 */

  comedi_8254_cascade_ns_to_timer(pacer, &convert_period,
      cmd->flags);
  /* transfer div2 value so correct timer gets updated */
  pacer->next_div = pacer->next_div2;
  labpc_set_ai_convert_period(cmd, mode, convert_period);
 }
}

static enum scan_mode labpc_ai_scan_mode(const struct comedi_cmd *cmd)
{
 unsigned int chan0;
 unsigned int chan1;

 if (cmd->chanlist_len == 1)
  return MODE_SINGLE_CHAN;

 /* chanlist may be NULL during cmdtest */
 if (!cmd->chanlist)
  return MODE_MULT_CHAN_UP;

 chan0 = CR_CHAN(cmd->chanlist[0]);
 chan1 = CR_CHAN(cmd->chanlist[1]);

 if (chan0 < chan1)
  return MODE_MULT_CHAN_UP;

 if (chan0 > chan1)
  return MODE_MULT_CHAN_DOWN;

 return MODE_SINGLE_CHAN_INTERVAL;
}

static int labpc_ai_check_chanlist(struct comedi_device *dev,
       struct comedi_subdevice *s,
       struct comedi_cmd *cmd)
{
 enum scan_mode mode = labpc_ai_scan_mode(cmd);
 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
 unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
 int i;

 for (i = 0; i < cmd->chanlist_len; i++) {
  unsigned int chan = CR_CHAN(cmd->chanlist[i]);
  unsigned int range = CR_RANGE(cmd->chanlist[i]);
  unsigned int aref = CR_AREF(cmd->chanlist[i]);

  switch (mode) {
  case MODE_SINGLE_CHAN:
   break;
  case MODE_SINGLE_CHAN_INTERVAL:
   if (chan != chan0) {
    dev_dbg(dev->class_dev,
     "channel scanning order specified in chanlist is not supported by hardware\n");
    return -EINVAL;
   }
   break;
  case MODE_MULT_CHAN_UP:
   if (chan != i) {
    dev_dbg(dev->class_dev,
     "channel scanning order specified in chanlist is not supported by hardware\n");
    return -EINVAL;
   }
   break;
  case MODE_MULT_CHAN_DOWN:
   if (chan != (cmd->chanlist_len - i - 1)) {
    dev_dbg(dev->class_dev,
     "channel scanning order specified in chanlist is not supported by hardware\n");
    return -EINVAL;
   }
   break;
  }

  if (range != range0) {
   dev_dbg(dev->class_dev,
    "entries in chanlist must all have the same range\n");
   return -EINVAL;
  }

  if (aref != aref0) {
   dev_dbg(dev->class_dev,
    "entries in chanlist must all have the same reference\n");
   return -EINVAL;
  }
 }

 return 0;
}

static int labpc_ai_cmdtest(struct comedi_device *dev,
       struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
 const struct labpc_boardinfo *board = dev->board_ptr;
 int err = 0;
 int tmp, tmp2;
 unsigned int stop_mask;
 enum scan_mode mode;

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

 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
 err |= comedi_check_trigger_src(&cmd->scan_begin_src,
     TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
 err |= comedi_check_trigger_src(&cmd->convert_src,
     TRIG_TIMER | TRIG_EXT);
 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);

 stop_mask = TRIG_COUNT | TRIG_NONE;
 if (board->is_labpc1200)
  stop_mask |= TRIG_EXT;
 err |= comedi_check_trigger_src(&cmd->stop_src, stop_mask);

 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 */

 /* can't have external stop and start triggers at once */
 if (cmd->start_src == TRIG_EXT && cmd->stop_src == TRIG_EXT)
  err++;

 if (err)
  return 2;

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

 switch (cmd->start_src) {
 case TRIG_NOW:
  err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
  break;
 case TRIG_EXT:
  /* start_arg value is ignored */
  break;
 }

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

 if (cmd->convert_src == TRIG_TIMER) {
  err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
          board->ai_speed);
 }

 /* make sure scan timing is not too fast */
 if (cmd->scan_begin_src == TRIG_TIMER) {
  if (cmd->convert_src == TRIG_TIMER) {
   err |= comedi_check_trigger_arg_min(
     &cmd->scan_begin_arg,
     cmd->convert_arg * cmd->chanlist_len);
  }
  err |= comedi_check_trigger_arg_min(
     &cmd->scan_begin_arg,
     board->ai_speed * cmd->chanlist_len);
 }

 switch (cmd->stop_src) {
 case TRIG_COUNT:
  err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
  break;
 case TRIG_NONE:
  err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
  break;
  /*
 * TRIG_EXT doesn't care since it doesn't
 * trigger off a numbered channel
 */

 default:
  break;
 }

 if (err)
  return 3;

 /* step 4: fix up any arguments */

 tmp = cmd->convert_arg;
 tmp2 = cmd->scan_begin_arg;
 mode = labpc_ai_scan_mode(cmd);
 labpc_adc_timing(dev, cmd, mode);
 if (tmp != cmd->convert_arg || tmp2 != cmd->scan_begin_arg)
  err++;

 if (err)
  return 4;

 /* Step 5: check channel list if it exists */
 if (cmd->chanlist && cmd->chanlist_len > 0)
  err |= labpc_ai_check_chanlist(dev, s, cmd);

 if (err)
  return 5;

 return 0;
}

static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
 const struct labpc_boardinfo *board = dev->board_ptr;
 struct labpc_private *devpriv = dev->private;
 struct comedi_async *async = s->async;
 struct comedi_cmd *cmd = &async->cmd;
 enum scan_mode mode = labpc_ai_scan_mode(cmd);
 unsigned int chanspec = (mode == MODE_MULT_CHAN_UP) ?
    cmd->chanlist[cmd->chanlist_len - 1] :
    cmd->chanlist[0];
 unsigned int chan = CR_CHAN(chanspec);
 unsigned int range = CR_RANGE(chanspec);
 unsigned int aref = CR_AREF(chanspec);
 enum transfer_type xfer;
 unsigned long flags;

 /* make sure board is disabled before setting up acquisition */
 labpc_cancel(dev, s);

 /*  initialize software conversion count */
 if (cmd->stop_src == TRIG_COUNT)
  devpriv->count = cmd->stop_arg * cmd->chanlist_len;

 /*  setup hardware conversion counter */
 if (cmd->stop_src == TRIG_EXT) {
  /*
 * load counter a1 with count of 3
 * (pc+ manual says this is minimum allowed) using mode 0
 */

  comedi_8254_load(devpriv->counter, 1,
     3, I8254_MODE0 | I8254_BINARY);
 } else {
  /* just put counter a1 in mode 0 to set its output low */
  comedi_8254_set_mode(devpriv->counter, 1,
         I8254_MODE0 | I8254_BINARY);
 }

 /* figure out what method we will use to transfer data */
 if (devpriv->dma &&
     (cmd->flags & (CMDF_WAKE_EOS | CMDF_PRIORITY)) == 0) {
  /*
 * dma unsafe at RT priority,
 * and too much setup time for CMDF_WAKE_EOS
 */

  xfer = isa_dma_transfer;
 } else if (board->is_labpc1200 &&
     (cmd->flags & CMDF_WAKE_EOS) == 0 &&
     (cmd->stop_src != TRIG_COUNT || devpriv->count > 256)) {
  /*
 * pc-plus has no fifo-half full interrupt
 * wake-end-of-scan should interrupt on fifo not empty
 * make sure we are taking more than just a few points
 */

  xfer = fifo_half_full_transfer;
 } else {
  xfer = fifo_not_empty_transfer;
 }
 devpriv->current_transfer = xfer;

 labpc_ai_set_chan_and_gain(dev, mode, chan, range, aref);

 labpc_setup_cmd6_reg(dev, s, mode, xfer, range, aref,
        (cmd->stop_src == TRIG_EXT));

 /* manual says to set scan enable bit on second pass */
 if (mode == MODE_MULT_CHAN_UP || mode == MODE_MULT_CHAN_DOWN) {
  devpriv->cmd1 |= CMD1_SCANEN;
  /*
 * Need a brief delay before enabling scan, or scan
 * list will get screwed when you switch between
 * scan up to scan down mode - dunno why.
 */

  udelay(1);
  devpriv->write_byte(dev, devpriv->cmd1, CMD1_REG);
 }

 devpriv->write_byte(dev, cmd->chanlist_len, INTERVAL_COUNT_REG);
 /*  load count */
 devpriv->write_byte(dev, 0x1, INTERVAL_STROBE_REG);

 if (cmd->convert_src == TRIG_TIMER ||
     cmd->scan_begin_src == TRIG_TIMER) {
  struct comedi_8254 *pacer = dev->pacer;
  struct comedi_8254 *counter = devpriv->counter;

  comedi_8254_update_divisors(pacer);

  /* set up pacing */
  comedi_8254_load(pacer, 0, pacer->divisor1,
     I8254_MODE3 | I8254_BINARY);

  /* set up conversion pacing */
  comedi_8254_set_mode(counter, 0, I8254_MODE2 | I8254_BINARY);
  if (labpc_ai_convert_period(cmd, mode))
   comedi_8254_write(counter, 0, pacer->divisor);

  /* set up scan pacing */
  if (labpc_ai_scan_period(cmd, mode))
   comedi_8254_load(pacer, 1, pacer->divisor2,
      I8254_MODE2 | I8254_BINARY);
 }

 labpc_clear_adc_fifo(dev);

 if (xfer == isa_dma_transfer)
  labpc_setup_dma(dev, s);

 /*  enable error interrupts */
 devpriv->cmd3 |= CMD3_ERRINTEN;
 /*  enable fifo not empty interrupt? */
 if (xfer == fifo_not_empty_transfer)
  devpriv->cmd3 |= CMD3_FIFOINTEN;
 devpriv->write_byte(dev, devpriv->cmd3, CMD3_REG);

 /*  setup any external triggering/pacing (cmd4 register) */
 devpriv->cmd4 = 0;
 if (cmd->convert_src != TRIG_EXT)
  devpriv->cmd4 |= CMD4_ECLKRCV;
 /*
 * XXX should discard first scan when using interval scanning
 * since manual says it is not synced with scan clock.
 */

 if (!labpc_use_continuous_mode(cmd, mode)) {
  devpriv->cmd4 |= CMD4_INTSCAN;
  if (cmd->scan_begin_src == TRIG_EXT)
   devpriv->cmd4 |= CMD4_EOIRCV;
 }
 /*  single-ended/differential */
 if (aref == AREF_DIFF)
  devpriv->cmd4 |= CMD4_SEDIFF;
 devpriv->write_byte(dev, devpriv->cmd4, CMD4_REG);

 /*  startup acquisition */

 spin_lock_irqsave(&dev->spinlock, flags);

 /* use 2 cascaded counters for pacing */
 devpriv->cmd2 |= CMD2_TBSEL;

 devpriv->cmd2 &= ~(CMD2_SWTRIG | CMD2_HWTRIG | CMD2_PRETRIG);
 if (cmd->start_src == TRIG_EXT)
  devpriv->cmd2 |= CMD2_HWTRIG;
 else
  devpriv->cmd2 |= CMD2_SWTRIG;
 if (cmd->stop_src == TRIG_EXT)
  devpriv->cmd2 |= (CMD2_HWTRIG | CMD2_PRETRIG);

 devpriv->write_byte(dev, devpriv->cmd2, CMD2_REG);

 spin_unlock_irqrestore(&dev->spinlock, flags);

 return 0;
}

/* read all available samples from ai fifo */
static int labpc_drain_fifo(struct comedi_device *dev)
{
 struct labpc_private *devpriv = dev->private;
 struct comedi_async *async = dev->read_subdev->async;
 struct comedi_cmd *cmd = &async->cmd;
 unsigned short data;
 const int timeout = 10000;
 unsigned int i;

 devpriv->stat1 = devpriv->read_byte(dev, STAT1_REG);

 for (i = 0; (devpriv->stat1 & STAT1_DAVAIL) && i < timeout;
      i++) {
  /*  quit if we have all the data we want */
  if (cmd->stop_src == TRIG_COUNT) {
   if (devpriv->count == 0)
    break;
   devpriv->count--;
  }
  data = labpc_read_adc_fifo(dev);
  comedi_buf_write_samples(dev->read_subdev, &data, 1);
  devpriv->stat1 = devpriv->read_byte(dev, STAT1_REG);
 }
 if (i == timeout) {
  dev_err(dev->class_dev, "ai timeout, fifo never empties\n");
  async->events |= COMEDI_CB_ERROR;
  return -1;
 }

 return 0;
}

/*
 * Makes sure all data acquired by board is transferred to comedi (used
 * when acquisition is terminated by stop_src == TRIG_EXT).
 */

static void labpc_drain_dregs(struct comedi_device *dev)
{
 struct labpc_private *devpriv = dev->private;

 if (devpriv->current_transfer == isa_dma_transfer)
  labpc_drain_dma(dev);

 labpc_drain_fifo(dev);
}

/* interrupt service routine */
static irqreturn_t labpc_interrupt(int irq, void *d)
{
 struct comedi_device *dev = d;
 const struct labpc_boardinfo *board = dev->board_ptr;
 struct labpc_private *devpriv = dev->private;
 struct comedi_subdevice *s = dev->read_subdev;
 struct comedi_async *async;
 struct comedi_cmd *cmd;

 if (!dev->attached) {
  dev_err(dev->class_dev, "premature interrupt\n");
  return IRQ_HANDLED;
 }

 async = s->async;
 cmd = &async->cmd;

 /* read board status */
 devpriv->stat1 = devpriv->read_byte(dev, STAT1_REG);
 if (board->is_labpc1200)
  devpriv->stat2 = devpriv->read_byte(dev, STAT2_REG);

 if ((devpriv->stat1 & (STAT1_GATA0 | STAT1_CNTINT | STAT1_OVERFLOW |
          STAT1_OVERRUN | STAT1_DAVAIL)) == 0 &&
     (devpriv->stat2 & STAT2_OUTA1) == 0 &&
     (devpriv->stat2 & STAT2_FIFONHF)) {
  return IRQ_NONE;
 }

 if (devpriv->stat1 & STAT1_OVERRUN) {
  /* clear error interrupt */
  devpriv->write_byte(dev, 0x1, ADC_FIFO_CLEAR_REG);
  async->events |= COMEDI_CB_ERROR;
  comedi_handle_events(dev, s);
  dev_err(dev->class_dev, "overrun\n");
  return IRQ_HANDLED;
 }

 if (devpriv->current_transfer == isa_dma_transfer)
  labpc_handle_dma_status(dev);
 else
  labpc_drain_fifo(dev);

 if (devpriv->stat1 & STAT1_CNTINT) {
  dev_err(dev->class_dev, "handled timer interrupt?\n");
  /*  clear it */
  devpriv->write_byte(dev, 0x1, TIMER_CLEAR_REG);
 }

 if (devpriv->stat1 & STAT1_OVERFLOW) {
  /*  clear error interrupt */
  devpriv->write_byte(dev, 0x1, ADC_FIFO_CLEAR_REG);
  async->events |= COMEDI_CB_ERROR;
  comedi_handle_events(dev, s);
  dev_err(dev->class_dev, "overflow\n");
  return IRQ_HANDLED;
 }
 /*  handle external stop trigger */
 if (cmd->stop_src == TRIG_EXT) {
  if (devpriv->stat2 & STAT2_OUTA1) {
   labpc_drain_dregs(dev);
   async->events |= COMEDI_CB_EOA;
  }
 }

 /* TRIG_COUNT end of acquisition */
 if (cmd->stop_src == TRIG_COUNT) {
  if (devpriv->count == 0)
   async->events |= COMEDI_CB_EOA;
 }

 comedi_handle_events(dev, s);
 return IRQ_HANDLED;
}

static void labpc_ao_write(struct comedi_device *dev,
      struct comedi_subdevice *s,
      unsigned int chan, unsigned int val)
{
 struct labpc_private *devpriv = dev->private;

 devpriv->write_byte(dev, val & 0xff, DAC_LSB_REG(chan));
 devpriv->write_byte(dev, (val >> 8) & 0xff, DAC_MSB_REG(chan));

 s->readback[chan] = val;
}

static int labpc_ao_insn_write(struct comedi_device *dev,
          struct comedi_subdevice *s,
          struct comedi_insn *insn,
          unsigned int *data)
{
 const struct labpc_boardinfo *board = dev->board_ptr;
 struct labpc_private *devpriv = dev->private;
 unsigned int channel;
 unsigned int range;
 unsigned int i;
 unsigned long flags;

 channel = CR_CHAN(insn->chanspec);

 /*
 * Turn off pacing of analog output channel.
 * NOTE: hardware bug in daqcard-1200 means pacing cannot
 * be independently enabled/disabled for its the two channels.
 */

 spin_lock_irqsave(&dev->spinlock, flags);
 devpriv->cmd2 &= ~CMD2_LDAC(channel);
 devpriv->write_byte(dev, devpriv->cmd2, CMD2_REG);
 spin_unlock_irqrestore(&dev->spinlock, flags);

 /* set range */
 if (board->is_labpc1200) {
  range = CR_RANGE(insn->chanspec);
  if (comedi_range_is_unipolar(s, range))
   devpriv->cmd6 |= CMD6_DACUNI(channel);
  else
   devpriv->cmd6 &= ~CMD6_DACUNI(channel);
  /*  write to register */
  devpriv->write_byte(dev, devpriv->cmd6, CMD6_REG);
 }
 /* send data */
 for (i = 0; i < insn->n; i++)
  labpc_ao_write(dev, s, channel, data[i]);

 return insn->n;
}

/* lowlevel write to eeprom/dac */
static void labpc_serial_out(struct comedi_device *dev, unsigned int value,
        unsigned int value_width)
{
 struct labpc_private *devpriv = dev->private;
 int i;

 for (i = 1; i <= value_width; i++) {
  /*  clear serial clock */
  devpriv->cmd5 &= ~CMD5_SCLK;
  /*  send bits most significant bit first */
  if (value & (1 << (value_width - i)))
   devpriv->cmd5 |= CMD5_SDATA;
  else
   devpriv->cmd5 &= ~CMD5_SDATA;
  udelay(1);
  devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
  /*  set clock to load bit */
  devpriv->cmd5 |= CMD5_SCLK;
  udelay(1);
  devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
 }
}

/* lowlevel read from eeprom */
static unsigned int labpc_serial_in(struct comedi_device *dev)
{
 struct labpc_private *devpriv = dev->private;
 unsigned int value = 0;
 int i;
 const int value_width = 8; /*  number of bits wide values are */

 for (i = 1; i <= value_width; i++) {
  /*  set serial clock */
  devpriv->cmd5 |= CMD5_SCLK;
  udelay(1);
  devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
  /*  clear clock bit */
  devpriv->cmd5 &= ~CMD5_SCLK;
  udelay(1);
  devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
  /*  read bits most significant bit first */
  udelay(1);
  devpriv->stat2 = devpriv->read_byte(dev, STAT2_REG);
  if (devpriv->stat2 & STAT2_PROMOUT)
   value |= 1 << (value_width - i);
 }

 return value;
}

static unsigned int labpc_eeprom_read(struct comedi_device *dev,
          unsigned int address)
{
 struct labpc_private *devpriv = dev->private;
 unsigned int value;
 /*  bits to tell eeprom to expect a read */
 const int read_instruction = 0x3;
 /*  8 bit write lengths to eeprom */
 const int write_length = 8;

 /*  enable read/write to eeprom */
 devpriv->cmd5 &= ~CMD5_EEPROMCS;
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
 devpriv->cmd5 |= (CMD5_EEPROMCS | CMD5_WRTPRT);
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);

 /*  send read instruction */
 labpc_serial_out(dev, read_instruction, write_length);
 /*  send 8 bit address to read from */
 labpc_serial_out(dev, address, write_length);
 /*  read result */
 value = labpc_serial_in(dev);

 /*  disable read/write to eeprom */
 devpriv->cmd5 &= ~(CMD5_EEPROMCS | CMD5_WRTPRT);
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);

 return value;
}

static unsigned int labpc_eeprom_read_status(struct comedi_device *dev)
{
 struct labpc_private *devpriv = dev->private;
 unsigned int value;
 const int read_status_instruction = 0x5;
 const int write_length = 8; /*  8 bit write lengths to eeprom */

 /*  enable read/write to eeprom */
 devpriv->cmd5 &= ~CMD5_EEPROMCS;
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
 devpriv->cmd5 |= (CMD5_EEPROMCS | CMD5_WRTPRT);
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);

 /*  send read status instruction */
 labpc_serial_out(dev, read_status_instruction, write_length);
 /*  read result */
 value = labpc_serial_in(dev);

 /*  disable read/write to eeprom */
 devpriv->cmd5 &= ~(CMD5_EEPROMCS | CMD5_WRTPRT);
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);

 return value;
}

static void labpc_eeprom_write(struct comedi_device *dev,
          unsigned int address, unsigned int value)
{
 struct labpc_private *devpriv = dev->private;
 const int write_enable_instruction = 0x6;
 const int write_instruction = 0x2;
 const int write_length = 8; /*  8 bit write lengths to eeprom */

 /*  enable read/write to eeprom */
 devpriv->cmd5 &= ~CMD5_EEPROMCS;
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
 devpriv->cmd5 |= (CMD5_EEPROMCS | CMD5_WRTPRT);
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);

 /*  send write_enable instruction */
 labpc_serial_out(dev, write_enable_instruction, write_length);
 devpriv->cmd5 &= ~CMD5_EEPROMCS;
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);

 /*  send write instruction */
 devpriv->cmd5 |= CMD5_EEPROMCS;
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
 labpc_serial_out(dev, write_instruction, write_length);
 /*  send 8 bit address to write to */
 labpc_serial_out(dev, address, write_length);
 /*  write value */
 labpc_serial_out(dev, value, write_length);
 devpriv->cmd5 &= ~CMD5_EEPROMCS;
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);

 /*  disable read/write to eeprom */
 devpriv->cmd5 &= ~(CMD5_EEPROMCS | CMD5_WRTPRT);
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
}

/* writes to 8 bit calibration dacs */
static void write_caldac(struct comedi_device *dev, unsigned int channel,
    unsigned int value)
{
 struct labpc_private *devpriv = dev->private;

 /*  clear caldac load bit and make sure we don't write to eeprom */
 devpriv->cmd5 &= ~(CMD5_CALDACLD | CMD5_EEPROMCS | CMD5_WRTPRT);
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);

 /*  write 4 bit channel */
 labpc_serial_out(dev, channel, 4);
 /*  write 8 bit caldac value */
 labpc_serial_out(dev, value, 8);

 /*  set and clear caldac bit to load caldac value */
 devpriv->cmd5 |= CMD5_CALDACLD;
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
 devpriv->cmd5 &= ~CMD5_CALDACLD;
 udelay(1);
 devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
}

static int labpc_calib_insn_write(struct comedi_device *dev,
      struct comedi_subdevice *s,
      struct comedi_insn *insn,
      unsigned int *data)
{
 unsigned int chan = CR_CHAN(insn->chanspec);

 /*
 * Only write the last data value to the caldac. Preceding
 * data would be overwritten anyway.
 */

 if (insn->n > 0) {
  unsigned int val = data[insn->n - 1];

  if (s->readback[chan] != val) {
   write_caldac(dev, chan, val);
   s->readback[chan] = val;
  }
 }

 return insn->n;
}

static int labpc_eeprom_ready(struct comedi_device *dev,
         struct comedi_subdevice *s,
         struct comedi_insn *insn,
         unsigned long context)
{
 unsigned int status;

 /* make sure there isn't already a write in progress */
 status = labpc_eeprom_read_status(dev);
 if ((status & 0x1) == 0)
  return 0;
 return -EBUSY;
}

static int labpc_eeprom_insn_write(struct comedi_device *dev,
       struct comedi_subdevice *s,
       struct comedi_insn *insn,
       unsigned int *data)
{
 unsigned int chan = CR_CHAN(insn->chanspec);
 int ret;

 /* only allow writes to user area of eeprom */
 if (chan < 16 || chan > 127)
  return -EINVAL;

 /*
 * Only write the last data value to the eeprom. Preceding
 * data would be overwritten anyway.
 */

 if (insn->n > 0) {
  unsigned int val = data[insn->n - 1];

  ret = comedi_timeout(dev, s, insn, labpc_eeprom_ready, 0);
  if (ret)
   return ret;

  labpc_eeprom_write(dev, chan, val);
  s->readback[chan] = val;
 }

 return insn->n;
}

int labpc_common_attach(struct comedi_device *dev,
   unsigned int irq, unsigned long isr_flags)
{
 const struct labpc_boardinfo *board = dev->board_ptr;
 struct labpc_private *devpriv;
 struct comedi_subdevice *s;
 int ret;
 int i;

 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 if (!devpriv)
  return -ENOMEM;

 if (dev->mmio) {
  devpriv->read_byte = labpc_readb;
  devpriv->write_byte = labpc_writeb;
 } else {
#ifdef CONFIG_HAS_IOPORT
  devpriv->read_byte = labpc_inb;
  devpriv->write_byte = labpc_outb;
#else
  return -ENXIO;
#endif
 }

 /* initialize board's command registers */
 devpriv->write_byte(dev, devpriv->cmd1, CMD1_REG);
 devpriv->write_byte(dev, devpriv->cmd2, CMD2_REG);
 devpriv->write_byte(dev, devpriv->cmd3, CMD3_REG);
 devpriv->write_byte(dev, devpriv->cmd4, CMD4_REG);
 if (board->is_labpc1200) {
  devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
  devpriv->write_byte(dev, devpriv->cmd6, CMD6_REG);
 }

 if (irq) {
  ret = request_irq(irq, labpc_interrupt, isr_flags,
      dev->board_name, dev);
  if (ret == 0)
   dev->irq = irq;
 }

 if (dev->mmio) {
  dev->pacer =
      comedi_8254_mm_alloc(dev->mmio + COUNTER_B_BASE_REG,
      I8254_OSC_BASE_2MHZ, I8254_IO8, 0);
  devpriv->counter =
      comedi_8254_mm_alloc(dev->mmio + COUNTER_A_BASE_REG,
      I8254_OSC_BASE_2MHZ, I8254_IO8, 0);
 } else {
  dev->pacer =
      comedi_8254_io_alloc(dev->iobase + COUNTER_B_BASE_REG,
      I8254_OSC_BASE_2MHZ, I8254_IO8, 0);
  devpriv->counter =
      comedi_8254_io_alloc(dev->iobase + COUNTER_A_BASE_REG,
      I8254_OSC_BASE_2MHZ, I8254_IO8, 0);
 }
 if (IS_ERR(dev->pacer))
  return PTR_ERR(dev->pacer);
 if (IS_ERR(devpriv->counter))
  return PTR_ERR(devpriv->counter);

 ret = comedi_alloc_subdevices(dev, 5);
 if (ret)
  return ret;

 /* analog input subdevice */
 s = &dev->subdevices[0];
 s->type  = COMEDI_SUBD_AI;
 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF;
 s->n_chan = 8;
 s->len_chanlist = 8;
 s->maxdata = 0x0fff;
 s->range_table = board->is_labpc1200 ?
     &range_labpc_1200_ai : &range_labpc_plus_ai;
 s->insn_read = labpc_ai_insn_read;
 if (dev->irq) {
  dev->read_subdev = s;
  s->subdev_flags |= SDF_CMD_READ;
  s->do_cmd = labpc_ai_cmd;
  s->do_cmdtest = labpc_ai_cmdtest;
  s->cancel = labpc_cancel;
 }

 /* analog output */
 s = &dev->subdevices[1];
 if (board->has_ao) {
  s->type  = COMEDI_SUBD_AO;
  s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
  s->n_chan = 2;
  s->maxdata = 0x0fff;
  s->range_table = &range_labpc_ao;
  s->insn_write = labpc_ao_insn_write;

  ret = comedi_alloc_subdev_readback(s);
  if (ret)
   return ret;

  /* initialize analog outputs to a known value */
  for (i = 0; i < s->n_chan; i++)
   labpc_ao_write(dev, s, i, s->maxdata / 2);
 } else {
  s->type  = COMEDI_SUBD_UNUSED;
 }

 /* 8255 dio */
 s = &dev->subdevices[2];
 if (dev->mmio)
  ret = subdev_8255_mm_init(dev, s, DIO_BASE_REG);
 else
  ret = subdev_8255_io_init(dev, s, DIO_BASE_REG);
 if (ret)
  return ret;

 /*  calibration subdevices for boards that have one */
 s = &dev->subdevices[3];
 if (board->is_labpc1200) {
  s->type  = COMEDI_SUBD_CALIB;
  s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
  s->n_chan = 16;
  s->maxdata = 0xff;
  s->insn_write = labpc_calib_insn_write;

  ret = comedi_alloc_subdev_readback(s);
  if (ret)
   return ret;

  for (i = 0; i < s->n_chan; i++) {
   write_caldac(dev, i, s->maxdata / 2);
   s->readback[i] = s->maxdata / 2;
  }
 } else {
  s->type  = COMEDI_SUBD_UNUSED;
 }

 /* EEPROM (256 bytes) */
 s = &dev->subdevices[4];
 if (board->is_labpc1200) {
  s->type  = COMEDI_SUBD_MEMORY;
  s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
  s->n_chan = 256;
  s->maxdata = 0xff;
  s->insn_write = labpc_eeprom_insn_write;

  ret = comedi_alloc_subdev_readback(s);
  if (ret)
   return ret;

  for (i = 0; i < s->n_chan; i++)
   s->readback[i] = labpc_eeprom_read(dev, i);
 } else {
  s->type  = COMEDI_SUBD_UNUSED;
 }

 return 0;
}
EXPORT_SYMBOL_GPL(labpc_common_attach);

void labpc_common_detach(struct comedi_device *dev)
{
 struct labpc_private *devpriv = dev->private;

 if (devpriv) {
  if (!IS_ERR(devpriv->counter))
   kfree(devpriv->counter);
 }
}
EXPORT_SYMBOL_GPL(labpc_common_detach);

static int __init labpc_common_init(void)
{
 return 0;
}
module_init(labpc_common_init);

static void __exit labpc_common_exit(void)
{
}
module_exit(labpc_common_exit);

MODULE_AUTHOR("Comedi https://www.comedi.org");
MODULE_DESCRIPTION("Comedi helper for ni_labpc, ni_labpc_pci, ni_labpc_cs");
MODULE_LICENSE("GPL");

Messung V0.5
C=91 H=98 G=94

¤ Dauer der Verarbeitung: 0.9 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.