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

Quelle  vmk80xx.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * vmk80xx.c
 * Velleman USB Board Low-Level Driver
 *
 * Copyright (C) 2009 Manuel Gebele <forensixs@gmx.de>, Germany
 *
 * COMEDI - Linux Control and Measurement Device Interface
 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
 */


/*
 * Driver: vmk80xx
 * Description: Velleman USB Board Low-Level Driver
 * Devices: [Velleman] K8055 (K8055/VM110), K8061 (K8061/VM140),
 *   VM110 (K8055/VM110), VM140 (K8061/VM140)
 * Author: Manuel Gebele <forensixs@gmx.de>
 * Updated: Sun, 10 May 2009 11:14:59 +0200
 * Status: works
 *
 * Supports:
 *  - analog input
 *  - analog output
 *  - digital input
 *  - digital output
 *  - counter
 *  - pwm
 */


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/uaccess.h>
#include <linux/comedi/comedi_usb.h>

enum {
 DEVICE_VMK8055,
 DEVICE_VMK8061
};

#define VMK8055_DI_REG  0x00
#define VMK8055_DO_REG  0x01
#define VMK8055_AO1_REG  0x02
#define VMK8055_AO2_REG  0x03
#define VMK8055_AI1_REG  0x02
#define VMK8055_AI2_REG  0x03
#define VMK8055_CNT1_REG 0x04
#define VMK8055_CNT2_REG 0x06

#define VMK8061_CH_REG  0x01
#define VMK8061_DI_REG  0x01
#define VMK8061_DO_REG  0x01
#define VMK8061_PWM_REG1 0x01
#define VMK8061_PWM_REG2 0x02
#define VMK8061_CNT_REG  0x02
#define VMK8061_AO_REG  0x02
#define VMK8061_AI_REG1  0x02
#define VMK8061_AI_REG2  0x03

#define VMK8055_CMD_RST  0x00
#define VMK8055_CMD_DEB1_TIME 0x01
#define VMK8055_CMD_DEB2_TIME 0x02
#define VMK8055_CMD_RST_CNT1 0x03
#define VMK8055_CMD_RST_CNT2 0x04
#define VMK8055_CMD_WRT_AD 0x05

#define VMK8061_CMD_RD_AI 0x00
#define VMK8061_CMR_RD_ALL_AI 0x01 /* !non-active! */
#define VMK8061_CMD_SET_AO 0x02
#define VMK8061_CMD_SET_ALL_AO 0x03 /* !non-active! */
#define VMK8061_CMD_OUT_PWM 0x04
#define VMK8061_CMD_RD_DI 0x05
#define VMK8061_CMD_DO  0x06 /* !non-active! */
#define VMK8061_CMD_CLR_DO 0x07
#define VMK8061_CMD_SET_DO 0x08
#define VMK8061_CMD_RD_CNT 0x09 /* TODO: completely pointless? */
#define VMK8061_CMD_RST_CNT 0x0a /* TODO: completely pointless? */
#define VMK8061_CMD_RD_VERSION 0x0b /* internal usage */
#define VMK8061_CMD_RD_JMP_STAT 0x0c /* TODO: not implemented yet */
#define VMK8061_CMD_RD_PWR_STAT 0x0d /* internal usage */
#define VMK8061_CMD_RD_DO 0x0e
#define VMK8061_CMD_RD_AO 0x0f
#define VMK8061_CMD_RD_PWM 0x10

#define IC3_VERSION  BIT(0)
#define IC6_VERSION  BIT(1)

#define MIN_BUF_SIZE  64
#define PACKET_TIMEOUT  10000 /* ms */

enum vmk80xx_model {
 VMK8055_MODEL,
 VMK8061_MODEL
};

static const struct comedi_lrange vmk8061_range = {
 2, {
  UNI_RANGE(5),
  UNI_RANGE(10)
 }
};

struct vmk80xx_board {
 const char *name;
 enum vmk80xx_model model;
 const struct comedi_lrange *range;
 int ai_nchans;
 unsigned int ai_maxdata;
 int ao_nchans;
 int di_nchans;
 unsigned int cnt_maxdata;
 int pwm_nchans;
 unsigned int pwm_maxdata;
};

static const struct vmk80xx_board vmk80xx_boardinfo[] = {
 [DEVICE_VMK8055] = {
  .name  = "K8055 (VM110)",
  .model  = VMK8055_MODEL,
  .range  = &range_unipolar5,
  .ai_nchans = 2,
  .ai_maxdata = 0x00ff,
  .ao_nchans = 2,
  .di_nchans = 6,
  .cnt_maxdata = 0xffff,
 },
 [DEVICE_VMK8061] = {
  .name  = "K8061 (VM140)",
  .model  = VMK8061_MODEL,
  .range  = &vmk8061_range,
  .ai_nchans = 8,
  .ai_maxdata = 0x03ff,
  .ao_nchans = 8,
  .di_nchans = 8,
  .cnt_maxdata = 0, /* unknown, device is not writeable */
  .pwm_nchans = 1,
  .pwm_maxdata = 0x03ff,
 },
};

struct vmk80xx_private {
 struct usb_endpoint_descriptor *ep_rx;
 struct usb_endpoint_descriptor *ep_tx;
 struct semaphore limit_sem;
 unsigned char *usb_rx_buf;
 unsigned char *usb_tx_buf;
 enum vmk80xx_model model;
};

static void vmk80xx_do_bulk_msg(struct comedi_device *dev)
{
 struct vmk80xx_private *devpriv = dev->private;
 struct usb_device *usb = comedi_to_usb_dev(dev);
 __u8 tx_addr;
 __u8 rx_addr;
 unsigned int tx_pipe;
 unsigned int rx_pipe;
 size_t tx_size;
 size_t rx_size;

 tx_addr = devpriv->ep_tx->bEndpointAddress;
 rx_addr = devpriv->ep_rx->bEndpointAddress;
 tx_pipe = usb_sndbulkpipe(usb, tx_addr);
 rx_pipe = usb_rcvbulkpipe(usb, rx_addr);
 tx_size = usb_endpoint_maxp(devpriv->ep_tx);
 rx_size = usb_endpoint_maxp(devpriv->ep_rx);

 usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf, tx_size, NULL,
       PACKET_TIMEOUT);

 usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, rx_size, NULL,
       PACKET_TIMEOUT);
}

static int vmk80xx_read_packet(struct comedi_device *dev)
{
 struct vmk80xx_private *devpriv = dev->private;
 struct usb_device *usb = comedi_to_usb_dev(dev);
 struct usb_endpoint_descriptor *ep;
 unsigned int pipe;

 if (devpriv->model == VMK8061_MODEL) {
  vmk80xx_do_bulk_msg(dev);
  return 0;
 }

 ep = devpriv->ep_rx;
 pipe = usb_rcvintpipe(usb, ep->bEndpointAddress);
 return usb_interrupt_msg(usb, pipe, devpriv->usb_rx_buf,
     usb_endpoint_maxp(ep), NULL,
     PACKET_TIMEOUT);
}

static int vmk80xx_write_packet(struct comedi_device *dev, int cmd)
{
 struct vmk80xx_private *devpriv = dev->private;
 struct usb_device *usb = comedi_to_usb_dev(dev);
 struct usb_endpoint_descriptor *ep;
 unsigned int pipe;

 devpriv->usb_tx_buf[0] = cmd;

 if (devpriv->model == VMK8061_MODEL) {
  vmk80xx_do_bulk_msg(dev);
  return 0;
 }

 ep = devpriv->ep_tx;
 pipe = usb_sndintpipe(usb, ep->bEndpointAddress);
 return usb_interrupt_msg(usb, pipe, devpriv->usb_tx_buf,
     usb_endpoint_maxp(ep), NULL,
     PACKET_TIMEOUT);
}

static int vmk80xx_reset_device(struct comedi_device *dev)
{
 struct vmk80xx_private *devpriv = dev->private;
 size_t size;
 int retval;

 size = usb_endpoint_maxp(devpriv->ep_tx);
 memset(devpriv->usb_tx_buf, 0, size);
 retval = vmk80xx_write_packet(dev, VMK8055_CMD_RST);
 if (retval)
  return retval;
 /* set outputs to known state as we cannot read them */
 return vmk80xx_write_packet(dev, VMK8055_CMD_WRT_AD);
}

static int vmk80xx_ai_insn_read(struct comedi_device *dev,
    struct comedi_subdevice *s,
    struct comedi_insn *insn,
    unsigned int *data)
{
 struct vmk80xx_private *devpriv = dev->private;
 int chan;
 int reg[2];
 int n;

 down(&devpriv->limit_sem);
 chan = CR_CHAN(insn->chanspec);

 switch (devpriv->model) {
 case VMK8055_MODEL:
  if (!chan)
   reg[0] = VMK8055_AI1_REG;
  else
   reg[0] = VMK8055_AI2_REG;
  break;
 case VMK8061_MODEL:
 default:
  reg[0] = VMK8061_AI_REG1;
  reg[1] = VMK8061_AI_REG2;
  devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AI;
  devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
  break;
 }

 for (n = 0; n < insn->n; n++) {
  if (vmk80xx_read_packet(dev))
   break;

  if (devpriv->model == VMK8055_MODEL) {
   data[n] = devpriv->usb_rx_buf[reg[0]];
   continue;
  }

  /* VMK8061_MODEL */
  data[n] = devpriv->usb_rx_buf[reg[0]] + 256 *
      devpriv->usb_rx_buf[reg[1]];
 }

 up(&devpriv->limit_sem);

 return n;
}

static int vmk80xx_ao_insn_write(struct comedi_device *dev,
     struct comedi_subdevice *s,
     struct comedi_insn *insn,
     unsigned int *data)
{
 struct vmk80xx_private *devpriv = dev->private;
 int chan;
 int cmd;
 int reg;
 int n;

 down(&devpriv->limit_sem);
 chan = CR_CHAN(insn->chanspec);

 switch (devpriv->model) {
 case VMK8055_MODEL:
  cmd = VMK8055_CMD_WRT_AD;
  if (!chan)
   reg = VMK8055_AO1_REG;
  else
   reg = VMK8055_AO2_REG;
  break;
 default:  /* NOTE: avoid compiler warnings */
  cmd = VMK8061_CMD_SET_AO;
  reg = VMK8061_AO_REG;
  devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
  break;
 }

 for (n = 0; n < insn->n; n++) {
  devpriv->usb_tx_buf[reg] = data[n];

  if (vmk80xx_write_packet(dev, cmd))
   break;
 }

 up(&devpriv->limit_sem);

 return n;
}

static int vmk80xx_ao_insn_read(struct comedi_device *dev,
    struct comedi_subdevice *s,
    struct comedi_insn *insn,
    unsigned int *data)
{
 struct vmk80xx_private *devpriv = dev->private;
 int chan;
 int reg;
 int n;

 down(&devpriv->limit_sem);
 chan = CR_CHAN(insn->chanspec);

 reg = VMK8061_AO_REG - 1;

 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AO;

 for (n = 0; n < insn->n; n++) {
  if (vmk80xx_read_packet(dev))
   break;

  data[n] = devpriv->usb_rx_buf[reg + chan];
 }

 up(&devpriv->limit_sem);

 return n;
}

static int vmk80xx_di_insn_bits(struct comedi_device *dev,
    struct comedi_subdevice *s,
    struct comedi_insn *insn,
    unsigned int *data)
{
 struct vmk80xx_private *devpriv = dev->private;
 unsigned char *rx_buf;
 int reg;
 int retval;

 down(&devpriv->limit_sem);

 rx_buf = devpriv->usb_rx_buf;

 if (devpriv->model == VMK8061_MODEL) {
  reg = VMK8061_DI_REG;
  devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_DI;
 } else {
  reg = VMK8055_DI_REG;
 }

 retval = vmk80xx_read_packet(dev);

 if (!retval) {
  if (devpriv->model == VMK8055_MODEL)
   data[1] = (((rx_buf[reg] >> 4) & 0x03) |
      ((rx_buf[reg] << 2) & 0x04) |
      ((rx_buf[reg] >> 3) & 0x18));
  else
   data[1] = rx_buf[reg];

  retval = 2;
 }

 up(&devpriv->limit_sem);

 return retval;
}

static int vmk80xx_do_insn_bits(struct comedi_device *dev,
    struct comedi_subdevice *s,
    struct comedi_insn *insn,
    unsigned int *data)
{
 struct vmk80xx_private *devpriv = dev->private;
 unsigned char *rx_buf = devpriv->usb_rx_buf;
 unsigned char *tx_buf = devpriv->usb_tx_buf;
 int reg, cmd;
 int ret = 0;

 if (devpriv->model == VMK8061_MODEL) {
  reg = VMK8061_DO_REG;
  cmd = VMK8061_CMD_DO;
 } else { /* VMK8055_MODEL */
  reg = VMK8055_DO_REG;
  cmd = VMK8055_CMD_WRT_AD;
 }

 down(&devpriv->limit_sem);

 if (comedi_dio_update_state(s, data)) {
  tx_buf[reg] = s->state;
  ret = vmk80xx_write_packet(dev, cmd);
  if (ret)
   goto out;
 }

 if (devpriv->model == VMK8061_MODEL) {
  tx_buf[0] = VMK8061_CMD_RD_DO;
  ret = vmk80xx_read_packet(dev);
  if (ret)
   goto out;
  data[1] = rx_buf[reg];
 } else {
  data[1] = s->state;
 }

out:
 up(&devpriv->limit_sem);

 return ret ? ret : insn->n;
}

static int vmk80xx_cnt_insn_read(struct comedi_device *dev,
     struct comedi_subdevice *s,
     struct comedi_insn *insn,
     unsigned int *data)
{
 struct vmk80xx_private *devpriv = dev->private;
 int chan;
 int reg[2];
 int n;

 down(&devpriv->limit_sem);
 chan = CR_CHAN(insn->chanspec);

 switch (devpriv->model) {
 case VMK8055_MODEL:
  if (!chan)
   reg[0] = VMK8055_CNT1_REG;
  else
   reg[0] = VMK8055_CNT2_REG;
  break;
 case VMK8061_MODEL:
 default:
  reg[0] = VMK8061_CNT_REG;
  reg[1] = VMK8061_CNT_REG;
  devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_CNT;
  break;
 }

 for (n = 0; n < insn->n; n++) {
  if (vmk80xx_read_packet(dev))
   break;

  if (devpriv->model == VMK8055_MODEL)
   data[n] = devpriv->usb_rx_buf[reg[0]];
  else /* VMK8061_MODEL */
   data[n] = devpriv->usb_rx_buf[reg[0] * (chan + 1) + 1]
       + 256 * devpriv->usb_rx_buf[reg[1] * 2 + 2];
 }

 up(&devpriv->limit_sem);

 return n;
}

static int vmk80xx_cnt_insn_config(struct comedi_device *dev,
       struct comedi_subdevice *s,
       struct comedi_insn *insn,
       unsigned int *data)
{
 struct vmk80xx_private *devpriv = dev->private;
 unsigned int chan = CR_CHAN(insn->chanspec);
 int cmd;
 int reg;
 int ret;

 down(&devpriv->limit_sem);
 switch (data[0]) {
 case INSN_CONFIG_RESET:
  if (devpriv->model == VMK8055_MODEL) {
   if (!chan) {
    cmd = VMK8055_CMD_RST_CNT1;
    reg = VMK8055_CNT1_REG;
   } else {
    cmd = VMK8055_CMD_RST_CNT2;
    reg = VMK8055_CNT2_REG;
   }
   devpriv->usb_tx_buf[reg] = 0x00;
  } else {
   cmd = VMK8061_CMD_RST_CNT;
  }
  ret = vmk80xx_write_packet(dev, cmd);
  break;
 default:
  ret = -EINVAL;
  break;
 }
 up(&devpriv->limit_sem);

 return ret ? ret : insn->n;
}

static int vmk80xx_cnt_insn_write(struct comedi_device *dev,
      struct comedi_subdevice *s,
      struct comedi_insn *insn,
      unsigned int *data)
{
 struct vmk80xx_private *devpriv = dev->private;
 unsigned long debtime;
 unsigned long val;
 int chan;
 int cmd;
 int n;

 down(&devpriv->limit_sem);
 chan = CR_CHAN(insn->chanspec);

 if (!chan)
  cmd = VMK8055_CMD_DEB1_TIME;
 else
  cmd = VMK8055_CMD_DEB2_TIME;

 for (n = 0; n < insn->n; n++) {
  debtime = data[n];
  if (debtime == 0)
   debtime = 1;

  /* TODO: Prevent overflows */
  if (debtime > 7450)
   debtime = 7450;

  val = int_sqrt(debtime * 1000 / 115);
  if (((val + 1) * val) < debtime * 1000 / 115)
   val += 1;

  devpriv->usb_tx_buf[6 + chan] = val;

  if (vmk80xx_write_packet(dev, cmd))
   break;
 }

 up(&devpriv->limit_sem);

 return n;
}

static int vmk80xx_pwm_insn_read(struct comedi_device *dev,
     struct comedi_subdevice *s,
     struct comedi_insn *insn,
     unsigned int *data)
{
 struct vmk80xx_private *devpriv = dev->private;
 unsigned char *tx_buf;
 unsigned char *rx_buf;
 int reg[2];
 int n;

 down(&devpriv->limit_sem);

 tx_buf = devpriv->usb_tx_buf;
 rx_buf = devpriv->usb_rx_buf;

 reg[0] = VMK8061_PWM_REG1;
 reg[1] = VMK8061_PWM_REG2;

 tx_buf[0] = VMK8061_CMD_RD_PWM;

 for (n = 0; n < insn->n; n++) {
  if (vmk80xx_read_packet(dev))
   break;

  data[n] = rx_buf[reg[0]] + 4 * rx_buf[reg[1]];
 }

 up(&devpriv->limit_sem);

 return n;
}

static int vmk80xx_pwm_insn_write(struct comedi_device *dev,
      struct comedi_subdevice *s,
      struct comedi_insn *insn,
      unsigned int *data)
{
 struct vmk80xx_private *devpriv = dev->private;
 unsigned char *tx_buf;
 int reg[2];
 int cmd;
 int n;

 down(&devpriv->limit_sem);

 tx_buf = devpriv->usb_tx_buf;

 reg[0] = VMK8061_PWM_REG1;
 reg[1] = VMK8061_PWM_REG2;

 cmd = VMK8061_CMD_OUT_PWM;

 /*
 * The followin piece of code was translated from the inline
 * assembler code in the DLL source code.
 *
 * asm
 *   mov eax, k  ; k is the value (data[n])
 *   and al, 03h ; al are the lower 8 bits of eax
 *   mov lo, al  ; lo is the low part (tx_buf[reg[0]])
 *   mov eax, k
 *   shr eax, 2  ; right shift eax register by 2
 *   mov hi, al  ; hi is the high part (tx_buf[reg[1]])
 * end;
 */

 for (n = 0; n < insn->n; n++) {
  tx_buf[reg[0]] = (unsigned char)(data[n] & 0x03);
  tx_buf[reg[1]] = (unsigned char)(data[n] >> 2) & 0xff;

  if (vmk80xx_write_packet(dev, cmd))
   break;
 }

 up(&devpriv->limit_sem);

 return n;
}

static int vmk80xx_find_usb_endpoints(struct comedi_device *dev)
{
 struct vmk80xx_private *devpriv = dev->private;
 struct usb_interface *intf = comedi_to_usb_interface(dev);
 struct usb_host_interface *iface_desc = intf->cur_altsetting;
 struct usb_endpoint_descriptor *ep_rx_desc, *ep_tx_desc;
 int ret;

 if (devpriv->model == VMK8061_MODEL)
  ret = usb_find_common_endpoints(iface_desc, &ep_rx_desc,
      &ep_tx_desc, NULL, NULL);
 else
  ret = usb_find_common_endpoints(iface_desc, NULL, NULL,
      &ep_rx_desc, &ep_tx_desc);

 if (ret)
  return -ENODEV;

 devpriv->ep_rx = ep_rx_desc;
 devpriv->ep_tx = ep_tx_desc;

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

 return 0;
}

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

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

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

 return 0;
}

static int vmk80xx_init_subdevices(struct comedi_device *dev)
{
 const struct vmk80xx_board *board = dev->board_ptr;
 struct vmk80xx_private *devpriv = dev->private;
 struct comedi_subdevice *s;
 int n_subd;
 int ret;

 down(&devpriv->limit_sem);

 if (devpriv->model == VMK8055_MODEL)
  n_subd = 5;
 else
  n_subd = 6;
 ret = comedi_alloc_subdevices(dev, n_subd);
 if (ret) {
  up(&devpriv->limit_sem);
  return ret;
 }

 /* Analog input subdevice */
 s = &dev->subdevices[0];
 s->type  = COMEDI_SUBD_AI;
 s->subdev_flags = SDF_READABLE | SDF_GROUND;
 s->n_chan = board->ai_nchans;
 s->maxdata = board->ai_maxdata;
 s->range_table = board->range;
 s->insn_read = vmk80xx_ai_insn_read;

 /* Analog output subdevice */
 s = &dev->subdevices[1];
 s->type  = COMEDI_SUBD_AO;
 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
 s->n_chan = board->ao_nchans;
 s->maxdata = 0x00ff;
 s->range_table = board->range;
 s->insn_write = vmk80xx_ao_insn_write;
 if (devpriv->model == VMK8061_MODEL) {
  s->subdev_flags |= SDF_READABLE;
  s->insn_read = vmk80xx_ao_insn_read;
 }

 /* Digital input subdevice */
 s = &dev->subdevices[2];
 s->type  = COMEDI_SUBD_DI;
 s->subdev_flags = SDF_READABLE;
 s->n_chan = board->di_nchans;
 s->maxdata = 1;
 s->range_table = &range_digital;
 s->insn_bits = vmk80xx_di_insn_bits;

 /* Digital output subdevice */
 s = &dev->subdevices[3];
 s->type  = COMEDI_SUBD_DO;
 s->subdev_flags = SDF_WRITABLE;
 s->n_chan = 8;
 s->maxdata = 1;
 s->range_table = &range_digital;
 s->insn_bits = vmk80xx_do_insn_bits;

 /* Counter subdevice */
 s = &dev->subdevices[4];
 s->type  = COMEDI_SUBD_COUNTER;
 s->subdev_flags = SDF_READABLE;
 s->n_chan = 2;
 s->maxdata = board->cnt_maxdata;
 s->insn_read = vmk80xx_cnt_insn_read;
 s->insn_config = vmk80xx_cnt_insn_config;
 if (devpriv->model == VMK8055_MODEL) {
  s->subdev_flags |= SDF_WRITABLE;
  s->insn_write = vmk80xx_cnt_insn_write;
 }

 /* PWM subdevice */
 if (devpriv->model == VMK8061_MODEL) {
  s = &dev->subdevices[5];
  s->type  = COMEDI_SUBD_PWM;
  s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
  s->n_chan = board->pwm_nchans;
  s->maxdata = board->pwm_maxdata;
  s->insn_read = vmk80xx_pwm_insn_read;
  s->insn_write = vmk80xx_pwm_insn_write;
 }

 up(&devpriv->limit_sem);

 return 0;
}

static int vmk80xx_auto_attach(struct comedi_device *dev,
          unsigned long context)
{
 struct usb_interface *intf = comedi_to_usb_interface(dev);
 const struct vmk80xx_board *board = NULL;
 struct vmk80xx_private *devpriv;
 int ret;

 if (context < ARRAY_SIZE(vmk80xx_boardinfo))
  board = &vmk80xx_boardinfo[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;

 devpriv->model = board->model;

 sema_init(&devpriv->limit_sem, 8);

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

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

 usb_set_intfdata(intf, devpriv);

 if (devpriv->model == VMK8055_MODEL)
  vmk80xx_reset_device(dev);

 return vmk80xx_init_subdevices(dev);
}

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

 if (!devpriv)
  return;

 down(&devpriv->limit_sem);

 usb_set_intfdata(intf, NULL);

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

 up(&devpriv->limit_sem);
}

static struct comedi_driver vmk80xx_driver = {
 .module  = THIS_MODULE,
 .driver_name = "vmk80xx",
 .auto_attach = vmk80xx_auto_attach,
 .detach  = vmk80xx_detach,
};

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

static const struct usb_device_id vmk80xx_usb_id_table[] = {
 { USB_DEVICE(0x10cf, 0x5500), .driver_info = DEVICE_VMK8055 },
 { USB_DEVICE(0x10cf, 0x5501), .driver_info = DEVICE_VMK8055 },
 { USB_DEVICE(0x10cf, 0x5502), .driver_info = DEVICE_VMK8055 },
 { USB_DEVICE(0x10cf, 0x5503), .driver_info = DEVICE_VMK8055 },
 { USB_DEVICE(0x10cf, 0x8061), .driver_info = DEVICE_VMK8061 },
 { USB_DEVICE(0x10cf, 0x8062), .driver_info = DEVICE_VMK8061 },
 { USB_DEVICE(0x10cf, 0x8063), .driver_info = DEVICE_VMK8061 },
 { USB_DEVICE(0x10cf, 0x8064), .driver_info = DEVICE_VMK8061 },
 { USB_DEVICE(0x10cf, 0x8065), .driver_info = DEVICE_VMK8061 },
 { USB_DEVICE(0x10cf, 0x8066), .driver_info = DEVICE_VMK8061 },
 { USB_DEVICE(0x10cf, 0x8067), .driver_info = DEVICE_VMK8061 },
 { USB_DEVICE(0x10cf, 0x8068), .driver_info = DEVICE_VMK8061 },
 { }
};
MODULE_DEVICE_TABLE(usb, vmk80xx_usb_id_table);

static struct usb_driver vmk80xx_usb_driver = {
 .name  = "vmk80xx",
 .id_table = vmk80xx_usb_id_table,
 .probe  = vmk80xx_usb_probe,
 .disconnect = comedi_usb_auto_unconfig,
};
module_comedi_usb_driver(vmk80xx_driver, vmk80xx_usb_driver);

MODULE_AUTHOR("Manuel Gebele ");
MODULE_DESCRIPTION("Velleman USB Board Low-Level Driver");
MODULE_LICENSE("GPL");

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

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ¤

*© 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.