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

Quelle  addi_apci_3xxx.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * addi_apci_3xxx.c
 * Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
 * Project manager: S. Weber
 *
 * ADDI-DATA GmbH
 * Dieselstrasse 3
 * D-77833 Ottersweier
 * Tel: +19(0)7223/9493-0
 * Fax: +49(0)7223/9493-92
 * http://www.addi-data.com
 * info@addi-data.com
 */


#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/comedi/comedi_pci.h>

#define CONV_UNIT_NS  BIT(0)
#define CONV_UNIT_US  BIT(1)
#define CONV_UNIT_MS  BIT(2)

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

static const struct comedi_lrange apci3xxx_ao_range = {
 2, {
  BIP_RANGE(10),
  UNI_RANGE(10)
 }
};

enum apci3xxx_boardid {
 BOARD_APCI3000_16,
 BOARD_APCI3000_8,
 BOARD_APCI3000_4,
 BOARD_APCI3006_16,
 BOARD_APCI3006_8,
 BOARD_APCI3006_4,
 BOARD_APCI3010_16,
 BOARD_APCI3010_8,
 BOARD_APCI3010_4,
 BOARD_APCI3016_16,
 BOARD_APCI3016_8,
 BOARD_APCI3016_4,
 BOARD_APCI3100_16_4,
 BOARD_APCI3100_8_4,
 BOARD_APCI3106_16_4,
 BOARD_APCI3106_8_4,
 BOARD_APCI3110_16_4,
 BOARD_APCI3110_8_4,
 BOARD_APCI3116_16_4,
 BOARD_APCI3116_8_4,
 BOARD_APCI3003,
 BOARD_APCI3002_16,
 BOARD_APCI3002_8,
 BOARD_APCI3002_4,
 BOARD_APCI3500,
};

struct apci3xxx_boardinfo {
 const char *name;
 int ai_subdev_flags;
 int ai_n_chan;
 unsigned int ai_maxdata;
 unsigned char ai_conv_units;
 unsigned int ai_min_acq_ns;
 unsigned int has_ao:1;
 unsigned int has_dig_in:1;
 unsigned int has_dig_out:1;
 unsigned int has_ttl_io:1;
};

static const struct apci3xxx_boardinfo apci3xxx_boardtypes[] = {
 [BOARD_APCI3000_16] = {
  .name   = "apci3000-16",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 16,
  .ai_maxdata  = 0x0fff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 10000,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3000_8] = {
  .name   = "apci3000-8",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 8,
  .ai_maxdata  = 0x0fff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 10000,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3000_4] = {
  .name   = "apci3000-4",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 4,
  .ai_maxdata  = 0x0fff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 10000,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3006_16] = {
  .name   = "apci3006-16",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 16,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 10000,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3006_8] = {
  .name   = "apci3006-8",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 8,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 10000,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3006_4] = {
  .name   = "apci3006-4",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 4,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 10000,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3010_16] = {
  .name   = "apci3010-16",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 16,
  .ai_maxdata  = 0x0fff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3010_8] = {
  .name   = "apci3010-8",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 8,
  .ai_maxdata  = 0x0fff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3010_4] = {
  .name   = "apci3010-4",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 4,
  .ai_maxdata  = 0x0fff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3016_16] = {
  .name   = "apci3016-16",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 16,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3016_8] = {
  .name   = "apci3016-8",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 8,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3016_4] = {
  .name   = "apci3016-4",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 4,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3100_16_4] = {
  .name   = "apci3100-16-4",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 16,
  .ai_maxdata  = 0x0fff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 10000,
  .has_ao   = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3100_8_4] = {
  .name   = "apci3100-8-4",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 8,
  .ai_maxdata  = 0x0fff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 10000,
  .has_ao   = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3106_16_4] = {
  .name   = "apci3106-16-4",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 16,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 10000,
  .has_ao   = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3106_8_4] = {
  .name   = "apci3106-8-4",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 8,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 10000,
  .has_ao   = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3110_16_4] = {
  .name   = "apci3110-16-4",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 16,
  .ai_maxdata  = 0x0fff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_ao   = 1,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3110_8_4] = {
  .name   = "apci3110-8-4",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 8,
  .ai_maxdata  = 0x0fff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_ao   = 1,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3116_16_4] = {
  .name   = "apci3116-16-4",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 16,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_ao   = 1,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3116_8_4] = {
  .name   = "apci3116-8-4",
  .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  .ai_n_chan  = 8,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_ao   = 1,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
  .has_ttl_io  = 1,
 },
 [BOARD_APCI3003] = {
  .name   = "apci3003",
  .ai_subdev_flags = SDF_DIFF,
  .ai_n_chan  = 4,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US |
       CONV_UNIT_NS,
  .ai_min_acq_ns  = 2500,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
 },
 [BOARD_APCI3002_16] = {
  .name   = "apci3002-16",
  .ai_subdev_flags = SDF_DIFF,
  .ai_n_chan  = 16,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
 },
 [BOARD_APCI3002_8] = {
  .name   = "apci3002-8",
  .ai_subdev_flags = SDF_DIFF,
  .ai_n_chan  = 8,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
 },
 [BOARD_APCI3002_4] = {
  .name   = "apci3002-4",
  .ai_subdev_flags = SDF_DIFF,
  .ai_n_chan  = 4,
  .ai_maxdata  = 0xffff,
  .ai_conv_units  = CONV_UNIT_MS | CONV_UNIT_US,
  .ai_min_acq_ns  = 5000,
  .has_dig_in  = 1,
  .has_dig_out  = 1,
 },
 [BOARD_APCI3500] = {
  .name   = "apci3500",
  .has_ao   = 1,
  .has_ttl_io  = 1,
 },
};

struct apci3xxx_private {
 unsigned int ai_timer;
 unsigned char ai_time_base;
};

static irqreturn_t apci3xxx_irq_handler(int irq, void *d)
{
 struct comedi_device *dev = d;
 struct comedi_subdevice *s = dev->read_subdev;
 unsigned int status;
 unsigned int val;

 /* Test if interrupt occur */
 status = readl(dev->mmio + 16);
 if ((status & 0x2) == 0x2) {
  /* Reset the interrupt */
  writel(status, dev->mmio + 16);

  val = readl(dev->mmio + 28);
  comedi_buf_write_samples(s, &val, 1);

  s->async->events |= COMEDI_CB_EOA;
  comedi_handle_events(dev, s);

  return IRQ_HANDLED;
 }
 return IRQ_NONE;
}

static int apci3xxx_ai_started(struct comedi_device *dev)
{
 if ((readl(dev->mmio + 8) & 0x80000) == 0x80000)
  return 1;

 return 0;
}

static int apci3xxx_ai_setup(struct comedi_device *dev, unsigned int chanspec)
{
 unsigned int chan = CR_CHAN(chanspec);
 unsigned int range = CR_RANGE(chanspec);
 unsigned int aref = CR_AREF(chanspec);
 unsigned int delay_mode;
 unsigned int val;

 if (apci3xxx_ai_started(dev))
  return -EBUSY;

 /* Clear the FIFO */
 writel(0x10000, dev->mmio + 12);

 /* Get and save the delay mode */
 delay_mode = readl(dev->mmio + 4);
 delay_mode &= 0xfffffef0;

 /* Channel configuration selection */
 writel(delay_mode, dev->mmio + 4);

 /* Make the configuration */
 val = (range & 3) | ((range >> 2) << 6) |
       ((aref == AREF_DIFF) << 7);
 writel(val, dev->mmio + 0);

 /* Channel selection */
 writel(delay_mode | 0x100, dev->mmio + 4);
 writel(chan, dev->mmio + 0);

 /* Restore delay mode */
 writel(delay_mode, dev->mmio + 4);

 /* Set the number of sequence to 1 */
 writel(1, dev->mmio + 48);

 return 0;
}

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

 status = readl(dev->mmio + 20);
 if (status & 0x1)
  return 0;
 return -EBUSY;
}

static int apci3xxx_ai_insn_read(struct comedi_device *dev,
     struct comedi_subdevice *s,
     struct comedi_insn *insn,
     unsigned int *data)
{
 int ret;
 int i;

 ret = apci3xxx_ai_setup(dev, insn->chanspec);
 if (ret)
  return ret;

 for (i = 0; i < insn->n; i++) {
  /* Start the conversion */
  writel(0x80000, dev->mmio + 8);

  /* Wait the EOS */
  ret = comedi_timeout(dev, s, insn, apci3xxx_ai_eoc, 0);
  if (ret)
   return ret;

  /* Read the analog value */
  data[i] = readl(dev->mmio + 28);
 }

 return insn->n;
}

static int apci3xxx_ai_ns_to_timer(struct comedi_device *dev,
       unsigned int *ns, unsigned int flags)
{
 const struct apci3xxx_boardinfo *board = dev->board_ptr;
 struct apci3xxx_private *devpriv = dev->private;
 unsigned int base;
 unsigned int timer;
 int time_base;

 /* time_base: 0 = ns, 1 = us, 2 = ms */
 for (time_base = 0; time_base < 3; time_base++) {
  /* skip unsupported time bases */
  if (!(board->ai_conv_units & (1 << time_base)))
   continue;

  switch (time_base) {
  case 0:
   base = 1;
   break;
  case 1:
   base = 1000;
   break;
  case 2:
   base = 1000000;
   break;
  }

  switch (flags & CMDF_ROUND_MASK) {
  case CMDF_ROUND_NEAREST:
  default:
   timer = DIV_ROUND_CLOSEST(*ns, base);
   break;
  case CMDF_ROUND_DOWN:
   timer = *ns / base;
   break;
  case CMDF_ROUND_UP:
   timer = DIV_ROUND_UP(*ns, base);
   break;
  }

  if (timer < 0x10000) {
   devpriv->ai_time_base = time_base;
   devpriv->ai_timer = timer;
   *ns = timer * time_base;
   return 0;
  }
 }
 return -EINVAL;
}

static int apci3xxx_ai_cmdtest(struct comedi_device *dev,
          struct comedi_subdevice *s,
          struct comedi_cmd *cmd)
{
 const struct apci3xxx_boardinfo *board = dev->board_ptr;
 int err = 0;
 unsigned int arg;

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

 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
 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->stop_src);

 /* Step 2b : and mutually compatible */

 if (err)
  return 2;

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

 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
         board->ai_min_acq_ns);
 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
        cmd->chanlist_len);

 if (cmd->stop_src == TRIG_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 */

 arg = cmd->convert_arg;
 err |= apci3xxx_ai_ns_to_timer(dev, &arg, cmd->flags);
 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);

 if (err)
  return 4;

 return 0;
}

static int apci3xxx_ai_cmd(struct comedi_device *dev,
      struct comedi_subdevice *s)
{
 struct apci3xxx_private *devpriv = dev->private;
 struct comedi_cmd *cmd = &s->async->cmd;
 int ret;

 ret = apci3xxx_ai_setup(dev, cmd->chanlist[0]);
 if (ret)
  return ret;

 /* Set the convert timing unit */
 writel(devpriv->ai_time_base, dev->mmio + 36);

 /* Set the convert timing */
 writel(devpriv->ai_timer, dev->mmio + 32);

 /* Start the conversion */
 writel(0x180000, dev->mmio + 8);

 return 0;
}

static int apci3xxx_ai_cancel(struct comedi_device *dev,
         struct comedi_subdevice *s)
{
 return 0;
}

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

 status = readl(dev->mmio + 96);
 if (status & 0x100)
  return 0;
 return -EBUSY;
}

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

 for (i = 0; i < insn->n; i++) {
  unsigned int val = data[i];

  /* Set the range selection */
  writel(range, dev->mmio + 96);

  /* Write the analog value to the selected channel */
  writel((val << 8) | chan, dev->mmio + 100);

  /* Wait the end of transfer */
  ret = comedi_timeout(dev, s, insn, apci3xxx_ao_eoc, 0);
  if (ret)
   return ret;

  s->readback[chan] = val;
 }

 return insn->n;
}

static int apci3xxx_di_insn_bits(struct comedi_device *dev,
     struct comedi_subdevice *s,
     struct comedi_insn *insn,
     unsigned int *data)
{
 data[1] = inl(dev->iobase + 32) & 0xf;

 return insn->n;
}

static int apci3xxx_do_insn_bits(struct comedi_device *dev,
     struct comedi_subdevice *s,
     struct comedi_insn *insn,
     unsigned int *data)
{
 s->state = inl(dev->iobase + 48) & 0xf;

 if (comedi_dio_update_state(s, data))
  outl(s->state, dev->iobase + 48);

 data[1] = s->state;

 return insn->n;
}

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

 /*
 * Port 0 (channels 0-7) are always inputs
 * Port 1 (channels 8-15) are always outputs
 * Port 2 (channels 16-23) are programmable i/o
 */

 if (data[0] != INSN_CONFIG_DIO_QUERY) {
  /* ignore all other instructions for ports 0 and 1 */
  if (chan < 16)
   return -EINVAL;

  /* changing any channel in port 2 changes the entire port */
  mask = 0xff0000;
 }

 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
 if (ret)
  return ret;

 /* update port 2 configuration */
 outl((s->io_bits >> 24) & 0xff, dev->iobase + 224);

 return insn->n;
}

static int apci3xxx_dio_insn_bits(struct comedi_device *dev,
      struct comedi_subdevice *s,
      struct comedi_insn *insn,
      unsigned int *data)
{
 unsigned int mask;
 unsigned int val;

 mask = comedi_dio_update_state(s, data);
 if (mask) {
  if (mask & 0xff)
   outl(s->state & 0xff, dev->iobase + 80);
  if (mask & 0xff0000)
   outl((s->state >> 16) & 0xff, dev->iobase + 112);
 }

 val = inl(dev->iobase + 80);
 val |= (inl(dev->iobase + 64) << 8);
 if (s->io_bits & 0xff0000)
  val |= (inl(dev->iobase + 112) << 16);
 else
  val |= (inl(dev->iobase + 96) << 16);

 data[1] = val;

 return insn->n;
}

static int apci3xxx_reset(struct comedi_device *dev)
{
 unsigned int val;
 int i;

 /* Disable the interrupt */
 disable_irq(dev->irq);

 /* Clear the start command */
 writel(0, dev->mmio + 8);

 /* Reset the interrupt flags */
 val = readl(dev->mmio + 16);
 writel(val, dev->mmio + 16);

 /* clear the EOS */
 readl(dev->mmio + 20);

 /* Clear the FIFO */
 for (i = 0; i < 16; i++)
  val = readl(dev->mmio + 28);

 /* Enable the interrupt */
 enable_irq(dev->irq);

 return 0;
}

static int apci3xxx_auto_attach(struct comedi_device *dev,
    unsigned long context)
{
 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 const struct apci3xxx_boardinfo *board = NULL;
 struct apci3xxx_private *devpriv;
 struct comedi_subdevice *s;
 int n_subdevices;
 int subdev;
 int ret;

 if (context < ARRAY_SIZE(apci3xxx_boardtypes))
  board = &apci3xxx_boardtypes[context];
 if (!board)
  return -ENODEV;
 dev->board_ptr = board;
 dev->board_name = board->name;

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

 ret = comedi_pci_enable(dev);
 if (ret)
  return ret;

 dev->iobase = pci_resource_start(pcidev, 2);
 dev->mmio = pci_ioremap_bar(pcidev, 3);
 if (!dev->mmio)
  return -ENOMEM;

 if (pcidev->irq > 0) {
  ret = request_irq(pcidev->irq, apci3xxx_irq_handler,
      IRQF_SHARED, dev->board_name, dev);
  if (ret == 0)
   dev->irq = pcidev->irq;
 }

 n_subdevices = (board->ai_n_chan ? 0 : 1) + board->has_ao +
         board->has_dig_in + board->has_dig_out +
         board->has_ttl_io;
 ret = comedi_alloc_subdevices(dev, n_subdevices);
 if (ret)
  return ret;

 subdev = 0;

 /* Analog Input subdevice */
 if (board->ai_n_chan) {
  s = &dev->subdevices[subdev];
  s->type  = COMEDI_SUBD_AI;
  s->subdev_flags = SDF_READABLE | board->ai_subdev_flags;
  s->n_chan = board->ai_n_chan;
  s->maxdata = board->ai_maxdata;
  s->range_table = &apci3xxx_ai_range;
  s->insn_read = apci3xxx_ai_insn_read;
  if (dev->irq) {
   /*
 * FIXME: The hardware supports multiple scan modes
 * but the original addi-data driver only supported
 * reading a single channel with interrupts. Need a
 * proper datasheet to fix this.
 *
 * The following scan modes are supported by the
 * hardware:
 *   1) Single software scan
 *   2) Single hardware triggered scan
 *   3) Continuous software scan
 *   4) Continuous software scan with timer delay
 *   5) Continuous hardware triggered scan
 *   6) Continuous hardware triggered scan with timer
 *      delay
 *
 * For now, limit the chanlist to a single channel.
 */

   dev->read_subdev = s;
   s->subdev_flags |= SDF_CMD_READ;
   s->len_chanlist = 1;
   s->do_cmdtest = apci3xxx_ai_cmdtest;
   s->do_cmd = apci3xxx_ai_cmd;
   s->cancel = apci3xxx_ai_cancel;
  }

  subdev++;
 }

 /* Analog Output subdevice */
 if (board->has_ao) {
  s = &dev->subdevices[subdev];
  s->type  = COMEDI_SUBD_AO;
  s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
  s->n_chan = 4;
  s->maxdata = 0x0fff;
  s->range_table = &apci3xxx_ao_range;
  s->insn_write = apci3xxx_ao_insn_write;

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

  subdev++;
 }

 /* Digital Input subdevice */
 if (board->has_dig_in) {
  s = &dev->subdevices[subdev];
  s->type  = COMEDI_SUBD_DI;
  s->subdev_flags = SDF_READABLE;
  s->n_chan = 4;
  s->maxdata = 1;
  s->range_table = &range_digital;
  s->insn_bits = apci3xxx_di_insn_bits;

  subdev++;
 }

 /* Digital Output subdevice */
 if (board->has_dig_out) {
  s = &dev->subdevices[subdev];
  s->type  = COMEDI_SUBD_DO;
  s->subdev_flags = SDF_WRITABLE;
  s->n_chan = 4;
  s->maxdata = 1;
  s->range_table = &range_digital;
  s->insn_bits = apci3xxx_do_insn_bits;

  subdev++;
 }

 /* TTL Digital I/O subdevice */
 if (board->has_ttl_io) {
  s = &dev->subdevices[subdev];
  s->type  = COMEDI_SUBD_DIO;
  s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
  s->n_chan = 24;
  s->maxdata = 1;
  s->io_bits = 0xff; /* channels 0-7 are always outputs */
  s->range_table = &range_digital;
  s->insn_config = apci3xxx_dio_insn_config;
  s->insn_bits = apci3xxx_dio_insn_bits;

  subdev++;
 }

 apci3xxx_reset(dev);
 return 0;
}

static void apci3xxx_detach(struct comedi_device *dev)
{
 if (dev->iobase)
  apci3xxx_reset(dev);
 comedi_pci_detach(dev);
}

static struct comedi_driver apci3xxx_driver = {
 .driver_name = "addi_apci_3xxx",
 .module  = THIS_MODULE,
 .auto_attach = apci3xxx_auto_attach,
 .detach  = apci3xxx_detach,
};

static int apci3xxx_pci_probe(struct pci_dev *dev,
         const struct pci_device_id *id)
{
 return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data);
}

static const struct pci_device_id apci3xxx_pci_table[] = {
 { PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 },
 { PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 },
 { PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 },
 { PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 },
 { PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 },
 { PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 },
 { PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 },
 { PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 },
 { PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 },
 { PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 },
 { PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 },
 { PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 },
 { PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 },
 { PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 },
 { PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 },
 { PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 },
 { PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 },
 { PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 },
 { PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 },
 { PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 },
 { PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 },
 { PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 },
 { PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 },
 { PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 },
 { PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 },
 { 0 }
};
MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table);

static struct pci_driver apci3xxx_pci_driver = {
 .name  = "addi_apci_3xxx",
 .id_table = apci3xxx_pci_table,
 .probe  = apci3xxx_pci_probe,
 .remove  = comedi_pci_auto_unconfig,
};
module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver);

MODULE_AUTHOR("Comedi https://www.comedi.org");
MODULE_DESCRIPTION("Comedi low-level driver");
MODULE_LICENSE("GPL");

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

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