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

Quelle  ni_usb6501.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * comedi/drivers/ni_usb6501.c
 * Comedi driver for National Instruments USB-6501
 *
 * COMEDI - Linux Control and Measurement Device Interface
 * Copyright (C) 2014 Luca Ellero <luca.ellero@brickedbrain.com>
 */


/*
 * Driver: ni_usb6501
 * Description: National Instruments USB-6501 module
 * Devices: [National Instruments] USB-6501 (ni_usb6501)
 * Author: Luca Ellero <luca.ellero@brickedbrain.com>
 * Updated: 8 Sep 2014
 * Status: works
 *
 *
 * Configuration Options:
 * none
 */


/*
 * NI-6501 - USB PROTOCOL DESCRIPTION
 *
 * Every command is composed by two USB packets:
 * - request (out)
 * - response (in)
 *
 * Every packet is at least 12 bytes long, here is the meaning of
 * every field (all values are hex):
 *
 * byte 0 is always 00
 * byte 1 is always 01
 * byte 2 is always 00
 * byte 3 is the total packet length
 *
 * byte 4 is always 00
 * byte 5 is the total packet length - 4
 * byte 6 is always 01
 * byte 7 is the command
 *
 * byte 8 is 02 (request) or 00 (response)
 * byte 9 is 00 (response) or 10 (port request) or 20 (counter request)
 * byte 10 is always 00
 * byte 11 is 00 (request) or 02 (response)
 *
 * PORT PACKETS
 *
 * CMD: 0xE READ_PORT
 * REQ: 00 01 00 10 00 0C 01 0E 02 10 00 00 00 03 <PORT> 00
 * RES: 00 01 00 10 00 0C 01 00 00 00 00 02 00 03 <BMAP> 00
 *
 * CMD: 0xF WRITE_PORT
 * REQ: 00 01 00 14 00 10 01 0F 02 10 00 00 00 03 <PORT> 00 03 <BMAP> 00 00
 * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
 *
 * CMD: 0x12 SET_PORT_DIR (0 = input, 1 = output)
 * REQ: 00 01 00 18 00 14 01 12 02 10 00 00
 *      00 05 <PORT 0> <PORT 1> <PORT 2> 00 05 00 00 00 00 00
 * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
 *
 * COUNTER PACKETS
 *
 * CMD 0x9: START_COUNTER
 * REQ: 00 01 00 0C 00 08 01 09 02 20 00 00
 * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
 *
 * CMD 0xC: STOP_COUNTER
 * REQ: 00 01 00 0C 00 08 01 0C 02 20 00 00
 * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
 *
 * CMD 0xE: READ_COUNTER
 * REQ: 00 01 00 0C 00 08 01 0E 02 20 00 00
 * RES: 00 01 00 10 00 0C 01 00 00 00 00 02 <u32 counter value, Big Endian>
 *
 * CMD 0xF: WRITE_COUNTER
 * REQ: 00 01 00 10 00 0C 01 0F 02 20 00 00 <u32 counter value, Big Endian>
 * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
 *
 *
 * Please  visit https://www.brickedbrain.com if you need
 * additional information or have any questions.
 *
 */


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/comedi/comedi_usb.h>

#define NI6501_TIMEOUT 1000

/* Port request packets */
static const u8 READ_PORT_REQUEST[] = {0x00, 0x01, 0x00, 0x10,
        0x00, 0x0C, 0x01, 0x0E,
        0x02, 0x10, 0x00, 0x00,
        0x00, 0x03, 0x00, 0x00};

static const u8 WRITE_PORT_REQUEST[] = {0x00, 0x01, 0x00, 0x14,
        0x00, 0x10, 0x01, 0x0F,
        0x02, 0x10, 0x00, 0x00,
        0x00, 0x03, 0x00, 0x00,
        0x03, 0x00, 0x00, 0x00};

static const u8 SET_PORT_DIR_REQUEST[] = {0x00, 0x01, 0x00, 0x18,
        0x00, 0x14, 0x01, 0x12,
        0x02, 0x10, 0x00, 0x00,
        0x00, 0x05, 0x00, 0x00,
        0x00, 0x00, 0x05, 0x00,
        0x00, 0x00, 0x00, 0x00};

/* Counter request packets */
static const u8 START_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x0C,
        0x00, 0x08, 0x01, 0x09,
        0x02, 0x20, 0x00, 0x00};

static const u8 STOP_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x0C,
        0x00, 0x08, 0x01, 0x0C,
        0x02, 0x20, 0x00, 0x00};

static const u8 READ_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x0C,
        0x00, 0x08, 0x01, 0x0E,
        0x02, 0x20, 0x00, 0x00};

static const u8 WRITE_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x10,
        0x00, 0x0C, 0x01, 0x0F,
        0x02, 0x20, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00};

/* Response packets */
static const u8 GENERIC_RESPONSE[] = {0x00, 0x01, 0x00, 0x0C,
        0x00, 0x08, 0x01, 0x00,
        0x00, 0x00, 0x00, 0x02};

static const u8 READ_PORT_RESPONSE[] = {0x00, 0x01, 0x00, 0x10,
        0x00, 0x0C, 0x01, 0x00,
        0x00, 0x00, 0x00, 0x02,
        0x00, 0x03, 0x00, 0x00};

static const u8 READ_COUNTER_RESPONSE[] = {0x00, 0x01, 0x00, 0x10,
        0x00, 0x0C, 0x01, 0x00,
        0x00, 0x00, 0x00, 0x02,
        0x00, 0x00, 0x00, 0x00};

/* Largest supported packets */
static const size_t TX_MAX_SIZE = sizeof(SET_PORT_DIR_REQUEST);
static const size_t RX_MAX_SIZE = sizeof(READ_PORT_RESPONSE);

enum commands {
 READ_PORT,
 WRITE_PORT,
 SET_PORT_DIR,
 START_COUNTER,
 STOP_COUNTER,
 READ_COUNTER,
 WRITE_COUNTER
};

struct ni6501_private {
 struct usb_endpoint_descriptor *ep_rx;
 struct usb_endpoint_descriptor *ep_tx;
 struct mutex mut;
 u8 *usb_rx_buf;
 u8 *usb_tx_buf;
};

static int ni6501_port_command(struct comedi_device *dev, int command,
          unsigned int val, u8 *bitmap)
{
 struct usb_device *usb = comedi_to_usb_dev(dev);
 struct ni6501_private *devpriv = dev->private;
 int request_size, response_size;
 u8 *tx = devpriv->usb_tx_buf;
 int ret;

 if (command != SET_PORT_DIR && !bitmap)
  return -EINVAL;

 mutex_lock(&devpriv->mut);

 switch (command) {
 case READ_PORT:
  request_size = sizeof(READ_PORT_REQUEST);
  response_size = sizeof(READ_PORT_RESPONSE);
  memcpy(tx, READ_PORT_REQUEST, request_size);
  tx[14] = val & 0xff;
  break;
 case WRITE_PORT:
  request_size = sizeof(WRITE_PORT_REQUEST);
  response_size = sizeof(GENERIC_RESPONSE);
  memcpy(tx, WRITE_PORT_REQUEST, request_size);
  tx[14] = val & 0xff;
  tx[17] = *bitmap;
  break;
 case SET_PORT_DIR:
  request_size = sizeof(SET_PORT_DIR_REQUEST);
  response_size = sizeof(GENERIC_RESPONSE);
  memcpy(tx, SET_PORT_DIR_REQUEST, request_size);
  tx[14] = val & 0xff;
  tx[15] = (val >> 8) & 0xff;
  tx[16] = (val >> 16) & 0xff;
  break;
 default:
  ret = -EINVAL;
  goto end;
 }

 ret = usb_bulk_msg(usb,
      usb_sndbulkpipe(usb,
        devpriv->ep_tx->bEndpointAddress),
      devpriv->usb_tx_buf,
      request_size,
      NULL,
      NI6501_TIMEOUT);
 if (ret)
  goto end;

 ret = usb_bulk_msg(usb,
      usb_rcvbulkpipe(usb,
        devpriv->ep_rx->bEndpointAddress),
      devpriv->usb_rx_buf,
      response_size,
      NULL,
      NI6501_TIMEOUT);
 if (ret)
  goto end;

 /* Check if results are valid */

 if (command == READ_PORT) {
  *bitmap = devpriv->usb_rx_buf[14];
  /* mask bitmap for comparing */
  devpriv->usb_rx_buf[14] = 0x00;

  if (memcmp(devpriv->usb_rx_buf, READ_PORT_RESPONSE,
      sizeof(READ_PORT_RESPONSE))) {
   ret = -EINVAL;
  }
 } else if (memcmp(devpriv->usb_rx_buf, GENERIC_RESPONSE,
     sizeof(GENERIC_RESPONSE))) {
  ret = -EINVAL;
 }
end:
 mutex_unlock(&devpriv->mut);

 return ret;
}

static int ni6501_counter_command(struct comedi_device *dev, int command,
      u32 *val)
{
 struct usb_device *usb = comedi_to_usb_dev(dev);
 struct ni6501_private *devpriv = dev->private;
 int request_size, response_size;
 u8 *tx = devpriv->usb_tx_buf;
 int ret;

 if ((command == READ_COUNTER || command ==  WRITE_COUNTER) && !val)
  return -EINVAL;

 mutex_lock(&devpriv->mut);

 switch (command) {
 case START_COUNTER:
  request_size = sizeof(START_COUNTER_REQUEST);
  response_size = sizeof(GENERIC_RESPONSE);
  memcpy(tx, START_COUNTER_REQUEST, request_size);
  break;
 case STOP_COUNTER:
  request_size = sizeof(STOP_COUNTER_REQUEST);
  response_size = sizeof(GENERIC_RESPONSE);
  memcpy(tx, STOP_COUNTER_REQUEST, request_size);
  break;
 case READ_COUNTER:
  request_size = sizeof(READ_COUNTER_REQUEST);
  response_size = sizeof(READ_COUNTER_RESPONSE);
  memcpy(tx, READ_COUNTER_REQUEST, request_size);
  break;
 case WRITE_COUNTER:
  request_size = sizeof(WRITE_COUNTER_REQUEST);
  response_size = sizeof(GENERIC_RESPONSE);
  memcpy(tx, WRITE_COUNTER_REQUEST, request_size);
  /* Setup tx packet: bytes 12,13,14,15 hold the */
  /* u32 counter value (Big Endian)        */
  *((__be32 *)&tx[12]) = cpu_to_be32(*val);
  break;
 default:
  ret = -EINVAL;
  goto end;
 }

 ret = usb_bulk_msg(usb,
      usb_sndbulkpipe(usb,
        devpriv->ep_tx->bEndpointAddress),
      devpriv->usb_tx_buf,
      request_size,
      NULL,
      NI6501_TIMEOUT);
 if (ret)
  goto end;

 ret = usb_bulk_msg(usb,
      usb_rcvbulkpipe(usb,
        devpriv->ep_rx->bEndpointAddress),
      devpriv->usb_rx_buf,
      response_size,
      NULL,
      NI6501_TIMEOUT);
 if (ret)
  goto end;

 /* Check if results are valid */

 if (command == READ_COUNTER) {
  int i;

  /* Read counter value: bytes 12,13,14,15 of rx packet */
  /* hold the u32 counter value (Big Endian)       */
  *val = be32_to_cpu(*((__be32 *)&devpriv->usb_rx_buf[12]));

  /* mask counter value for comparing */
  for (i = 12; i < sizeof(READ_COUNTER_RESPONSE); ++i)
   devpriv->usb_rx_buf[i] = 0x00;

  if (memcmp(devpriv->usb_rx_buf, READ_COUNTER_RESPONSE,
      sizeof(READ_COUNTER_RESPONSE))) {
   ret = -EINVAL;
  }
 } else if (memcmp(devpriv->usb_rx_buf, GENERIC_RESPONSE,
     sizeof(GENERIC_RESPONSE))) {
  ret = -EINVAL;
 }
end:
 mutex_unlock(&devpriv->mut);

 return ret;
}

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

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

 ret = ni6501_port_command(dev, SET_PORT_DIR, s->io_bits, NULL);
 if (ret)
  return ret;

 return insn->n;
}

static int ni6501_dio_insn_bits(struct comedi_device *dev,
    struct comedi_subdevice *s,
    struct comedi_insn *insn,
    unsigned int *data)
{
 unsigned int mask;
 int ret;
 u8 port;
 u8 bitmap;

 mask = comedi_dio_update_state(s, data);

 for (port = 0; port < 3; port++) {
  if (mask & (0xFF << port * 8)) {
   bitmap = (s->state >> port * 8) & 0xFF;
   ret = ni6501_port_command(dev, WRITE_PORT,
        port, &bitmap);
   if (ret)
    return ret;
  }
 }

 data[1] = 0;

 for (port = 0; port < 3; port++) {
  ret = ni6501_port_command(dev, READ_PORT, port, &bitmap);
  if (ret)
   return ret;
  data[1] |= bitmap << port * 8;
 }

 return insn->n;
}

static int ni6501_cnt_insn_config(struct comedi_device *dev,
      struct comedi_subdevice *s,
      struct comedi_insn *insn,
      unsigned int *data)
{
 int ret;
 u32 val = 0;

 switch (data[0]) {
 case INSN_CONFIG_ARM:
  ret = ni6501_counter_command(dev, START_COUNTER, NULL);
  break;
 case INSN_CONFIG_DISARM:
  ret = ni6501_counter_command(dev, STOP_COUNTER, NULL);
  break;
 case INSN_CONFIG_RESET:
  ret = ni6501_counter_command(dev, STOP_COUNTER, NULL);
  if (ret)
   break;
  ret = ni6501_counter_command(dev, WRITE_COUNTER, &val);
  break;
 default:
  return -EINVAL;
 }

 return ret ? ret : insn->n;
}

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

 for (i = 0; i < insn->n; i++) {
  ret = ni6501_counter_command(dev, READ_COUNTER, &val);
  if (ret)
   return ret;
  data[i] = val;
 }

 return insn->n;
}

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

 if (insn->n) {
  u32 val = data[insn->n - 1];

  ret = ni6501_counter_command(dev, WRITE_COUNTER, &val);
  if (ret)
   return ret;
 }

 return insn->n;
}

static int ni6501_alloc_usb_buffers(struct comedi_device *dev)
{
 struct ni6501_private *devpriv = dev->private;
 size_t size;

 size = usb_endpoint_maxp(devpriv->ep_rx);
 devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL);
 if (!devpriv->usb_rx_buf)
  return -ENOMEM;

 size = usb_endpoint_maxp(devpriv->ep_tx);
 devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
 if (!devpriv->usb_tx_buf)
  return -ENOMEM;

 return 0;
}

static int ni6501_find_endpoints(struct comedi_device *dev)
{
 struct usb_interface *intf = comedi_to_usb_interface(dev);
 struct ni6501_private *devpriv = dev->private;
 struct usb_host_interface *iface_desc = intf->cur_altsetting;
 struct usb_endpoint_descriptor *ep_desc;
 int i;

 if (iface_desc->desc.bNumEndpoints != 2) {
  dev_err(dev->class_dev, "Wrong number of endpoints\n");
  return -ENODEV;
 }

 for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
  ep_desc = &iface_desc->endpoint[i].desc;

  if (usb_endpoint_is_bulk_in(ep_desc)) {
   if (!devpriv->ep_rx)
    devpriv->ep_rx = ep_desc;
   continue;
  }

  if (usb_endpoint_is_bulk_out(ep_desc)) {
   if (!devpriv->ep_tx)
    devpriv->ep_tx = ep_desc;
   continue;
  }
 }

 if (!devpriv->ep_rx || !devpriv->ep_tx)
  return -ENODEV;

 if (usb_endpoint_maxp(devpriv->ep_rx) < RX_MAX_SIZE)
  return -ENODEV;

 if (usb_endpoint_maxp(devpriv->ep_tx) < TX_MAX_SIZE)
  return -ENODEV;

 return 0;
}

static int ni6501_auto_attach(struct comedi_device *dev,
         unsigned long context)
{
 struct usb_interface *intf = comedi_to_usb_interface(dev);
 struct ni6501_private *devpriv;
 struct comedi_subdevice *s;
 int ret;

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

 mutex_init(&devpriv->mut);
 usb_set_intfdata(intf, devpriv);

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

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

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

 /* Digital Input/Output subdevice */
 s = &dev->subdevices[0];
 s->type  = COMEDI_SUBD_DIO;
 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 s->n_chan = 24;
 s->maxdata = 1;
 s->range_table = &range_digital;
 s->insn_bits = ni6501_dio_insn_bits;
 s->insn_config = ni6501_dio_insn_config;

 /* Counter subdevice */
 s = &dev->subdevices[1];
 s->type  = COMEDI_SUBD_COUNTER;
 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
 s->n_chan = 1;
 s->maxdata = 0xffffffff;
 s->insn_read = ni6501_cnt_insn_read;
 s->insn_write = ni6501_cnt_insn_write;
 s->insn_config = ni6501_cnt_insn_config;

 return 0;
}

static void ni6501_detach(struct comedi_device *dev)
{
 struct usb_interface *intf = comedi_to_usb_interface(dev);
 struct ni6501_private *devpriv = dev->private;

 if (!devpriv)
  return;

 mutex_destroy(&devpriv->mut);

 usb_set_intfdata(intf, NULL);

 kfree(devpriv->usb_rx_buf);
 kfree(devpriv->usb_tx_buf);
}

static struct comedi_driver ni6501_driver = {
 .module  = THIS_MODULE,
 .driver_name = "ni6501",
 .auto_attach = ni6501_auto_attach,
 .detach  = ni6501_detach,
};

static int ni6501_usb_probe(struct usb_interface *intf,
       const struct usb_device_id *id)
{
 return comedi_usb_auto_config(intf, &ni6501_driver, id->driver_info);
}

static const struct usb_device_id ni6501_usb_table[] = {
 { USB_DEVICE(0x3923, 0x718a) },
 { }
};
MODULE_DEVICE_TABLE(usb, ni6501_usb_table);

static struct usb_driver ni6501_usb_driver = {
 .name  = "ni6501",
 .id_table = ni6501_usb_table,
 .probe  = ni6501_usb_probe,
 .disconnect = comedi_usb_auto_unconfig,
};
module_comedi_usb_driver(ni6501_driver, ni6501_usb_driver);

MODULE_AUTHOR("Luca Ellero");
MODULE_DESCRIPTION("Comedi driver for National Instruments USB-6501");
MODULE_LICENSE("GPL");

Messung V0.5
C=97 H=90 G=93

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