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

Quelle  ni_6527.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * ni_6527.c
 * Comedi driver for National Instruments PCI-6527
 *
 * COMEDI - Linux Control and Measurement Device Interface
 * Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org>
 */


/*
 * Driver: ni_6527
 * Description: National Instruments 6527
 * Devices: [National Instruments] PCI-6527 (pci-6527), PXI-6527 (pxi-6527)
 * Author: David A. Schleef <ds@schleef.org>
 * Updated: Sat, 25 Jan 2003 13:24:40 -0800
 * Status: works
 *
 * Configuration Options: not applicable, uses PCI auto config
 */


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

/*
 * PCI BAR1 - Register memory map
 *
 * Manuals (available from ftp://ftp.natinst.com/support/manuals)
 * 370106b.pdf 6527 Register Level Programmer Manual
 */

#define NI6527_DI_REG(x)  (0x00 + (x))
#define NI6527_DO_REG(x)  (0x03 + (x))
#define NI6527_ID_REG   0x06
#define NI6527_CLR_REG   0x07
#define NI6527_CLR_EDGE   BIT(3)
#define NI6527_CLR_OVERFLOW  BIT(2)
#define NI6527_CLR_FILT   BIT(1)
#define NI6527_CLR_INTERVAL  BIT(0)
#define NI6527_CLR_IRQS   (NI6527_CLR_EDGE | NI6527_CLR_OVERFLOW)
#define NI6527_CLR_RESET_FILT  (NI6527_CLR_FILT | NI6527_CLR_INTERVAL)
#define NI6527_FILT_INTERVAL_REG(x) (0x08 + (x))
#define NI6527_FILT_ENA_REG(x)  (0x0c + (x))
#define NI6527_STATUS_REG  0x14
#define NI6527_STATUS_IRQ  BIT(2)
#define NI6527_STATUS_OVERFLOW  BIT(1)
#define NI6527_STATUS_EDGE  BIT(0)
#define NI6527_CTRL_REG   0x15
#define NI6527_CTRL_FALLING  BIT(4)
#define NI6527_CTRL_RISING  BIT(3)
#define NI6527_CTRL_IRQ   BIT(2)
#define NI6527_CTRL_OVERFLOW  BIT(1)
#define NI6527_CTRL_EDGE  BIT(0)
#define NI6527_CTRL_DISABLE_IRQS 0
#define NI6527_CTRL_ENABLE_IRQS  (NI6527_CTRL_FALLING | \
      NI6527_CTRL_RISING | \
      NI6527_CTRL_IRQ | NI6527_CTRL_EDGE)
#define NI6527_RISING_EDGE_REG(x) (0x18 + (x))
#define NI6527_FALLING_EDGE_REG(x) (0x20 + (x))

enum ni6527_boardid {
 BOARD_PCI6527,
 BOARD_PXI6527,
};

struct ni6527_board {
 const char *name;
};

static const struct ni6527_board ni6527_boards[] = {
 [BOARD_PCI6527] = {
  .name  = "pci-6527",
 },
 [BOARD_PXI6527] = {
  .name  = "pxi-6527",
 },
};

struct ni6527_private {
 unsigned int filter_interval;
 unsigned int filter_enable;
};

static void ni6527_set_filter_interval(struct comedi_device *dev,
           unsigned int val)
{
 struct ni6527_private *devpriv = dev->private;

 if (val != devpriv->filter_interval) {
  writeb(val & 0xff, dev->mmio + NI6527_FILT_INTERVAL_REG(0));
  writeb((val >> 8) & 0xff,
         dev->mmio + NI6527_FILT_INTERVAL_REG(1));
  writeb((val >> 16) & 0x0f,
         dev->mmio + NI6527_FILT_INTERVAL_REG(2));

  writeb(NI6527_CLR_INTERVAL, dev->mmio + NI6527_CLR_REG);

  devpriv->filter_interval = val;
 }
}

static void ni6527_set_filter_enable(struct comedi_device *dev,
         unsigned int val)
{
 writeb(val & 0xff, dev->mmio + NI6527_FILT_ENA_REG(0));
 writeb((val >> 8) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(1));
 writeb((val >> 16) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(2));
}

static int ni6527_di_insn_config(struct comedi_device *dev,
     struct comedi_subdevice *s,
     struct comedi_insn *insn,
     unsigned int *data)
{
 struct ni6527_private *devpriv = dev->private;
 unsigned int chan = CR_CHAN(insn->chanspec);
 unsigned int interval;

 switch (data[0]) {
 case INSN_CONFIG_FILTER:
  /*
 * The deglitch filter interval is specified in nanoseconds.
 * The hardware supports intervals in 200ns increments. Round
 * the user values up and return the actual interval.
 */

  interval = (data[1] + 100) / 200;
  data[1] = interval * 200;

  if (interval) {
   ni6527_set_filter_interval(dev, interval);
   devpriv->filter_enable |= 1 << chan;
  } else {
   devpriv->filter_enable &= ~(1 << chan);
  }
  ni6527_set_filter_enable(dev, devpriv->filter_enable);
  break;
 default:
  return -EINVAL;
 }

 return insn->n;
}

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

 val = readb(dev->mmio + NI6527_DI_REG(0));
 val |= (readb(dev->mmio + NI6527_DI_REG(1)) << 8);
 val |= (readb(dev->mmio + NI6527_DI_REG(2)) << 16);

 data[1] = val;

 return insn->n;
}

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

 mask = comedi_dio_update_state(s, data);
 if (mask) {
  /* Outputs are inverted */
  unsigned int val = s->state ^ 0xffffff;

  if (mask & 0x0000ff)
   writeb(val & 0xff, dev->mmio + NI6527_DO_REG(0));
  if (mask & 0x00ff00)
   writeb((val >> 8) & 0xff,
          dev->mmio + NI6527_DO_REG(1));
  if (mask & 0xff0000)
   writeb((val >> 16) & 0xff,
          dev->mmio + NI6527_DO_REG(2));
 }

 data[1] = s->state;

 return insn->n;
}

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

 status = readb(dev->mmio + NI6527_STATUS_REG);
 if (!(status & NI6527_STATUS_IRQ))
  return IRQ_NONE;

 if (status & NI6527_STATUS_EDGE) {
  unsigned short val = 0;

  comedi_buf_write_samples(s, &val, 1);
  comedi_handle_events(dev, s);
 }

 writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG);

 return IRQ_HANDLED;
}

static int ni6527_intr_cmdtest(struct comedi_device *dev,
          struct comedi_subdevice *s,
          struct comedi_cmd *cmd)
{
 int err = 0;

 /* 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_OTHER);
 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT);

 if (err)
  return 1;

 /* Step 2a : make sure trigger sources are unique */
 /* Step 2b : and mutually compatible */

 /* 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_is(&cmd->convert_arg, 0);
 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
        cmd->chanlist_len);
 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);

 if (err)
  return 3;

 /* Step 4: fix up any arguments */

 /* Step 5: check channel list if it exists */

 return 0;
}

static int ni6527_intr_cmd(struct comedi_device *dev,
      struct comedi_subdevice *s)
{
 writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG);
 writeb(NI6527_CTRL_ENABLE_IRQS, dev->mmio + NI6527_CTRL_REG);

 return 0;
}

static int ni6527_intr_cancel(struct comedi_device *dev,
         struct comedi_subdevice *s)
{
 writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG);

 return 0;
}

static int ni6527_intr_insn_bits(struct comedi_device *dev,
     struct comedi_subdevice *s,
     struct comedi_insn *insn, unsigned int *data)
{
 data[1] = 0;
 return insn->n;
}

static void ni6527_set_edge_detection(struct comedi_device *dev,
          unsigned int mask,
          unsigned int rising,
          unsigned int falling)
{
 unsigned int i;

 rising &= mask;
 falling &= mask;
 for (i = 0; i < 2; i++) {
  if (mask & 0xff) {
   if (~mask & 0xff) {
    /* preserve rising-edge detection channels */
    rising |= readb(dev->mmio +
      NI6527_RISING_EDGE_REG(i)) &
       (~mask & 0xff);
    /* preserve falling-edge detection channels */
    falling |= readb(dev->mmio +
       NI6527_FALLING_EDGE_REG(i)) &
        (~mask & 0xff);
   }
   /* update rising-edge detection channels */
   writeb(rising & 0xff,
          dev->mmio + NI6527_RISING_EDGE_REG(i));
   /* update falling-edge detection channels */
   writeb(falling & 0xff,
          dev->mmio + NI6527_FALLING_EDGE_REG(i));
  }
  rising >>= 8;
  falling >>= 8;
  mask >>= 8;
 }
}

static int ni6527_intr_insn_config(struct comedi_device *dev,
       struct comedi_subdevice *s,
       struct comedi_insn *insn,
       unsigned int *data)
{
 unsigned int mask = 0xffffffff;
 unsigned int rising, falling, shift;

 switch (data[0]) {
 case INSN_CONFIG_CHANGE_NOTIFY:
  /* check_insn_config_length() does not check this instruction */
  if (insn->n != 3)
   return -EINVAL;
  rising = data[1];
  falling = data[2];
  ni6527_set_edge_detection(dev, mask, rising, falling);
  break;
 case INSN_CONFIG_DIGITAL_TRIG:
  /* check trigger number */
  if (data[1] != 0)
   return -EINVAL;
  /* check digital trigger operation */
  switch (data[2]) {
  case COMEDI_DIGITAL_TRIG_DISABLE:
   rising = 0;
   falling = 0;
   break;
  case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
   /* check shift amount */
   shift = data[3];
   if (shift >= 32) {
    mask = 0;
    rising = 0;
    falling = 0;
   } else {
    mask <<= shift;
    rising = data[4] << shift;
    falling = data[5] << shift;
   }
   break;
  default:
   return -EINVAL;
  }
  ni6527_set_edge_detection(dev, mask, rising, falling);
  break;
 default:
  return -EINVAL;
 }

 return insn->n;
}

static void ni6527_reset(struct comedi_device *dev)
{
 /* disable deglitch filters on all channels */
 ni6527_set_filter_enable(dev, 0);

 /* disable edge detection */
 ni6527_set_edge_detection(dev, 0xffffffff, 0, 0);

 writeb(NI6527_CLR_IRQS | NI6527_CLR_RESET_FILT,
        dev->mmio + NI6527_CLR_REG);
 writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
}

static int ni6527_auto_attach(struct comedi_device *dev,
         unsigned long context)
{
 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 const struct ni6527_board *board = NULL;
 struct ni6527_private *devpriv;
 struct comedi_subdevice *s;
 int ret;

 if (context < ARRAY_SIZE(ni6527_boards))
  board = &ni6527_boards[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->mmio = pci_ioremap_bar(pcidev, 1);
 if (!dev->mmio)
  return -ENOMEM;

 /* make sure this is actually a 6527 device */
 if (readb(dev->mmio + NI6527_ID_REG) != 0x27)
  return -ENODEV;

 ni6527_reset(dev);

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

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

 /* Digital Input subdevice */
 s = &dev->subdevices[0];
 s->type  = COMEDI_SUBD_DI;
 s->subdev_flags = SDF_READABLE;
 s->n_chan = 24;
 s->maxdata = 1;
 s->range_table = &range_digital;
 s->insn_config = ni6527_di_insn_config;
 s->insn_bits = ni6527_di_insn_bits;

 /* Digital Output subdevice */
 s = &dev->subdevices[1];
 s->type  = COMEDI_SUBD_DO;
 s->subdev_flags = SDF_WRITABLE;
 s->n_chan = 24;
 s->maxdata = 1;
 s->range_table = &range_digital;
 s->insn_bits = ni6527_do_insn_bits;

 /* Edge detection interrupt subdevice */
 s = &dev->subdevices[2];
 if (dev->irq) {
  dev->read_subdev = s;
  s->type  = COMEDI_SUBD_DI;
  s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
  s->n_chan = 1;
  s->maxdata = 1;
  s->range_table = &range_digital;
  s->insn_config = ni6527_intr_insn_config;
  s->insn_bits = ni6527_intr_insn_bits;
  s->len_chanlist = 1;
  s->do_cmdtest = ni6527_intr_cmdtest;
  s->do_cmd = ni6527_intr_cmd;
  s->cancel = ni6527_intr_cancel;
 } else {
  s->type = COMEDI_SUBD_UNUSED;
 }

 return 0;
}

static void ni6527_detach(struct comedi_device *dev)
{
 if (dev->mmio)
  ni6527_reset(dev);
 comedi_pci_detach(dev);
}

static struct comedi_driver ni6527_driver = {
 .driver_name = "ni_6527",
 .module  = THIS_MODULE,
 .auto_attach = ni6527_auto_attach,
 .detach  = ni6527_detach,
};

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

static const struct pci_device_id ni6527_pci_table[] = {
 { PCI_VDEVICE(NI, 0x2b10), BOARD_PXI6527 },
 { PCI_VDEVICE(NI, 0x2b20), BOARD_PCI6527 },
 { 0 }
};
MODULE_DEVICE_TABLE(pci, ni6527_pci_table);

static struct pci_driver ni6527_pci_driver = {
 .name  = "ni_6527",
 .id_table = ni6527_pci_table,
 .probe  = ni6527_pci_probe,
 .remove  = comedi_pci_auto_unconfig,
};
module_comedi_pci_driver(ni6527_driver, ni6527_pci_driver);

MODULE_AUTHOR("Comedi https://www.comedi.org");
MODULE_DESCRIPTION("Comedi driver for National Instruments PCI-6527");
MODULE_LICENSE("GPL");

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

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