summaryrefslogtreecommitdiff
path: root/drivers/staging/comedi/drivers/usbduxfast.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/drivers/usbduxfast.c')
-rw-r--r--drivers/staging/comedi/drivers/usbduxfast.c1039
1 files changed, 0 insertions, 1039 deletions
diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c
deleted file mode 100644
index 4af012968cb6..000000000000
--- a/drivers/staging/comedi/drivers/usbduxfast.c
+++ /dev/null
@@ -1,1039 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2004-2019 Bernd Porr, mail@berndporr.me.uk
- */
-
-/*
- * Driver: usbduxfast
- * Description: University of Stirling USB DAQ & INCITE Technology Limited
- * Devices: [ITL] USB-DUX-FAST (usbduxfast)
- * Author: Bernd Porr <mail@berndporr.me.uk>
- * Updated: 16 Nov 2019
- * Status: stable
- */
-
-/*
- * I must give credit here to Chris Baugher who
- * wrote the driver for AT-MIO-16d. I used some parts of this
- * driver. I also must give credits to David Brownell
- * who supported me with the USB development.
- *
- * Bernd Porr
- *
- *
- * Revision history:
- * 1.0: Fixed a rounding error in usbduxfast_ai_cmdtest
- * 0.9: Dropping the first data packet which seems to be from the last transfer.
- * Buffer overflows in the FX2 are handed over to comedi.
- * 0.92: Dropping now 4 packets. The quad buffer has to be emptied.
- * Added insn command basically for testing. Sample rate is
- * 1MHz/16ch=62.5kHz
- * 0.99: Ian Abbott pointed out a bug which has been corrected. Thanks!
- * 0.99a: added external trigger.
- * 1.00: added firmware kernel request to the driver which fixed
- * udev coldplug problem
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/input.h>
-#include <linux/fcntl.h>
-#include <linux/compiler.h>
-#include "../comedi_usb.h"
-
-/*
- * timeout for the USB-transfer
- */
-#define EZTIMEOUT 30
-
-/*
- * constants for "firmware" upload and download
- */
-#define FIRMWARE "usbduxfast_firmware.bin"
-#define FIRMWARE_MAX_LEN 0x2000
-#define USBDUXFASTSUB_FIRMWARE 0xA0
-#define VENDOR_DIR_IN 0xC0
-#define VENDOR_DIR_OUT 0x40
-
-/*
- * internal addresses of the 8051 processor
- */
-#define USBDUXFASTSUB_CPUCS 0xE600
-
-/*
- * max length of the transfer-buffer for software upload
- */
-#define TB_LEN 0x2000
-
-/*
- * input endpoint number
- */
-#define BULKINEP 6
-
-/*
- * endpoint for the A/D channellist: bulk OUT
- */
-#define CHANNELLISTEP 4
-
-/*
- * number of channels
- */
-#define NUMCHANNELS 32
-
-/*
- * size of the waveform descriptor
- */
-#define WAVESIZE 0x20
-
-/*
- * size of one A/D value
- */
-#define SIZEADIN (sizeof(s16))
-
-/*
- * size of the input-buffer IN BYTES
- */
-#define SIZEINBUF 512
-
-/*
- * 16 bytes
- */
-#define SIZEINSNBUF 512
-
-/*
- * size of the buffer for the dux commands in bytes
- */
-#define SIZEOFDUXBUF 256
-
-/*
- * number of in-URBs which receive the data: min=5
- */
-#define NUMOFINBUFFERSHIGH 10
-
-/*
- * min delay steps for more than one channel
- * basically when the mux gives up ;-)
- *
- * steps at 30MHz in the FX2
- */
-#define MIN_SAMPLING_PERIOD 9
-
-/*
- * max number of 1/30MHz delay steps
- */
-#define MAX_SAMPLING_PERIOD 500
-
-/*
- * number of received packets to ignore before we start handing data
- * over to comedi, it's quad buffering and we have to ignore 4 packets
- */
-#define PACKETS_TO_IGNORE 4
-
-/*
- * comedi constants
- */
-static const struct comedi_lrange range_usbduxfast_ai_range = {
- 2, {
- BIP_RANGE(0.75),
- BIP_RANGE(0.5)
- }
-};
-
-/*
- * private structure of one subdevice
- *
- * this is the structure which holds all the data of this driver
- * one sub device just now: A/D
- */
-struct usbduxfast_private {
- struct urb *urb; /* BULK-transfer handling: urb */
- u8 *duxbuf;
- s8 *inbuf;
- short int ai_cmd_running; /* asynchronous command is running */
- int ignore; /* counter which ignores the first buffers */
- struct mutex mut;
-};
-
-/*
- * bulk transfers to usbduxfast
- */
-#define SENDADCOMMANDS 0
-#define SENDINITEP6 1
-
-static int usbduxfast_send_cmd(struct comedi_device *dev, int cmd_type)
-{
- struct usb_device *usb = comedi_to_usb_dev(dev);
- struct usbduxfast_private *devpriv = dev->private;
- int nsent;
- int ret;
-
- devpriv->duxbuf[0] = cmd_type;
-
- ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, CHANNELLISTEP),
- devpriv->duxbuf, SIZEOFDUXBUF,
- &nsent, 10000);
- if (ret < 0)
- dev_err(dev->class_dev,
- "could not transmit command to the usb-device, err=%d\n",
- ret);
- return ret;
-}
-
-static void usbduxfast_cmd_data(struct comedi_device *dev, int index,
- u8 len, u8 op, u8 out, u8 log)
-{
- struct usbduxfast_private *devpriv = dev->private;
-
- /* Set the GPIF bytes, the first byte is the command byte */
- devpriv->duxbuf[1 + 0x00 + index] = len;
- devpriv->duxbuf[1 + 0x08 + index] = op;
- devpriv->duxbuf[1 + 0x10 + index] = out;
- devpriv->duxbuf[1 + 0x18 + index] = log;
-}
-
-static int usbduxfast_ai_stop(struct comedi_device *dev, int do_unlink)
-{
- struct usbduxfast_private *devpriv = dev->private;
-
- /* stop aquistion */
- devpriv->ai_cmd_running = 0;
-
- if (do_unlink && devpriv->urb) {
- /* kill the running transfer */
- usb_kill_urb(devpriv->urb);
- }
-
- return 0;
-}
-
-static int usbduxfast_ai_cancel(struct comedi_device *dev,
- struct comedi_subdevice *s)
-{
- struct usbduxfast_private *devpriv = dev->private;
- int ret;
-
- mutex_lock(&devpriv->mut);
- ret = usbduxfast_ai_stop(dev, 1);
- mutex_unlock(&devpriv->mut);
-
- return ret;
-}
-
-static void usbduxfast_ai_handle_urb(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct urb *urb)
-{
- struct usbduxfast_private *devpriv = dev->private;
- struct comedi_async *async = s->async;
- struct comedi_cmd *cmd = &async->cmd;
- int ret;
-
- if (devpriv->ignore) {
- devpriv->ignore--;
- } else {
- unsigned int nsamples;
-
- nsamples = comedi_bytes_to_samples(s, urb->actual_length);
- nsamples = comedi_nsamples_left(s, nsamples);
- comedi_buf_write_samples(s, urb->transfer_buffer, nsamples);
-
- if (cmd->stop_src == TRIG_COUNT &&
- async->scans_done >= cmd->stop_arg)
- async->events |= COMEDI_CB_EOA;
- }
-
- /* if command is still running, resubmit urb for BULK transfer */
- if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
- urb->dev = comedi_to_usb_dev(dev);
- urb->status = 0;
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret < 0) {
- dev_err(dev->class_dev, "urb resubm failed: %d", ret);
- async->events |= COMEDI_CB_ERROR;
- }
- }
-}
-
-static void usbduxfast_ai_interrupt(struct urb *urb)
-{
- struct comedi_device *dev = urb->context;
- struct comedi_subdevice *s = dev->read_subdev;
- struct comedi_async *async = s->async;
- struct usbduxfast_private *devpriv = dev->private;
-
- /* exit if not running a command, do not resubmit urb */
- if (!devpriv->ai_cmd_running)
- return;
-
- switch (urb->status) {
- case 0:
- usbduxfast_ai_handle_urb(dev, s, urb);
- break;
-
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- case -ECONNABORTED:
- /* after an unlink command, unplug, ... etc */
- async->events |= COMEDI_CB_ERROR;
- break;
-
- default:
- /* a real error */
- dev_err(dev->class_dev,
- "non-zero urb status received in ai intr context: %d\n",
- urb->status);
- async->events |= COMEDI_CB_ERROR;
- break;
- }
-
- /*
- * comedi_handle_events() cannot be used in this driver. The (*cancel)
- * operation would unlink the urb.
- */
- if (async->events & COMEDI_CB_CANCEL_MASK)
- usbduxfast_ai_stop(dev, 0);
-
- comedi_event(dev, s);
-}
-
-static int usbduxfast_submit_urb(struct comedi_device *dev)
-{
- struct usb_device *usb = comedi_to_usb_dev(dev);
- struct usbduxfast_private *devpriv = dev->private;
- int ret;
-
- usb_fill_bulk_urb(devpriv->urb, usb, usb_rcvbulkpipe(usb, BULKINEP),
- devpriv->inbuf, SIZEINBUF,
- usbduxfast_ai_interrupt, dev);
-
- ret = usb_submit_urb(devpriv->urb, GFP_ATOMIC);
- if (ret) {
- dev_err(dev->class_dev, "usb_submit_urb error %d\n", ret);
- return ret;
- }
- return 0;
-}
-
-static int usbduxfast_ai_check_chanlist(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_cmd *cmd)
-{
- unsigned int gain0 = CR_RANGE(cmd->chanlist[0]);
- int i;
-
- if (cmd->chanlist_len > 3 && cmd->chanlist_len != 16) {
- dev_err(dev->class_dev, "unsupported combination of channels\n");
- return -EINVAL;
- }
-
- for (i = 0; i < cmd->chanlist_len; ++i) {
- unsigned int chan = CR_CHAN(cmd->chanlist[i]);
- unsigned int gain = CR_RANGE(cmd->chanlist[i]);
-
- if (chan != i) {
- dev_err(dev->class_dev,
- "channels are not consecutive\n");
- return -EINVAL;
- }
- if (gain != gain0 && cmd->chanlist_len > 3) {
- dev_err(dev->class_dev,
- "gain must be the same for all channels\n");
- return -EINVAL;
- }
- }
- return 0;
-}
-
-static int usbduxfast_ai_cmdtest(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_cmd *cmd)
-{
- int err = 0;
- int err2 = 0;
- unsigned int steps;
- unsigned int arg;
-
- /* Step 1 : check if triggers are trivially valid */
-
- err |= comedi_check_trigger_src(&cmd->start_src,
- TRIG_NOW | TRIG_EXT | TRIG_INT);
- 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->start_src);
- 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);
-
- if (!cmd->chanlist_len)
- err |= -EINVAL;
-
- /* external start trigger is only valid for 1 or 16 channels */
- if (cmd->start_src == TRIG_EXT &&
- cmd->chanlist_len != 1 && cmd->chanlist_len != 16)
- err |= -EINVAL;
-
- err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
- cmd->chanlist_len);
-
- /*
- * Validate the conversion timing:
- * for 1 channel the timing in 30MHz "steps" is:
- * steps <= MAX_SAMPLING_PERIOD
- * for all other chanlist_len it is:
- * MIN_SAMPLING_PERIOD <= steps <= MAX_SAMPLING_PERIOD
- */
- steps = (cmd->convert_arg * 30) / 1000;
- if (cmd->chanlist_len != 1)
- err2 |= comedi_check_trigger_arg_min(&steps,
- MIN_SAMPLING_PERIOD);
- else
- err2 |= comedi_check_trigger_arg_min(&steps, 1);
- err2 |= comedi_check_trigger_arg_max(&steps, MAX_SAMPLING_PERIOD);
- if (err2) {
- err |= err2;
- arg = (steps * 1000) / 30;
- err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
- }
-
- 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 */
-
- /* Step 5: check channel list if it exists */
- if (cmd->chanlist && cmd->chanlist_len > 0)
- err |= usbduxfast_ai_check_chanlist(dev, s, cmd);
- if (err)
- return 5;
-
- return 0;
-}
-
-static int usbduxfast_ai_inttrig(struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned int trig_num)
-{
- struct usbduxfast_private *devpriv = dev->private;
- struct comedi_cmd *cmd = &s->async->cmd;
- int ret;
-
- if (trig_num != cmd->start_arg)
- return -EINVAL;
-
- mutex_lock(&devpriv->mut);
-
- if (!devpriv->ai_cmd_running) {
- devpriv->ai_cmd_running = 1;
- ret = usbduxfast_submit_urb(dev);
- if (ret < 0) {
- dev_err(dev->class_dev, "urbSubmit: err=%d\n", ret);
- devpriv->ai_cmd_running = 0;
- mutex_unlock(&devpriv->mut);
- return ret;
- }
- s->async->inttrig = NULL;
- } else {
- dev_err(dev->class_dev, "ai is already running\n");
- }
- mutex_unlock(&devpriv->mut);
- return 1;
-}
-
-static int usbduxfast_ai_cmd(struct comedi_device *dev,
- struct comedi_subdevice *s)
-{
- struct usbduxfast_private *devpriv = dev->private;
- struct comedi_cmd *cmd = &s->async->cmd;
- unsigned int rngmask = 0xff;
- int j, ret;
- long steps, steps_tmp;
-
- mutex_lock(&devpriv->mut);
- if (devpriv->ai_cmd_running) {
- ret = -EBUSY;
- goto cmd_exit;
- }
-
- /*
- * ignore the first buffers from the device if there
- * is an error condition
- */
- devpriv->ignore = PACKETS_TO_IGNORE;
-
- steps = (cmd->convert_arg * 30) / 1000;
-
- switch (cmd->chanlist_len) {
- case 1:
- /*
- * one channel
- */
-
- if (CR_RANGE(cmd->chanlist[0]) > 0)
- rngmask = 0xff - 0x04;
- else
- rngmask = 0xff;
-
- /*
- * for external trigger: looping in this state until
- * the RDY0 pin becomes zero
- */
-
- /* we loop here until ready has been set */
- if (cmd->start_src == TRIG_EXT) {
- /* branch back to state 0 */
- /* deceision state w/o data */
- /* RDY0 = 0 */
- usbduxfast_cmd_data(dev, 0, 0x01, 0x01, rngmask, 0x00);
- } else { /* we just proceed to state 1 */
- usbduxfast_cmd_data(dev, 0, 0x01, 0x00, rngmask, 0x00);
- }
-
- if (steps < MIN_SAMPLING_PERIOD) {
- /* for fast single channel aqu without mux */
- if (steps <= 1) {
- /*
- * we just stay here at state 1 and rexecute
- * the same state this gives us 30MHz sampling
- * rate
- */
-
- /* branch back to state 1 */
- /* deceision state with data */
- /* doesn't matter */
- usbduxfast_cmd_data(dev, 1,
- 0x89, 0x03, rngmask, 0xff);
- } else {
- /*
- * we loop through two states: data and delay
- * max rate is 15MHz
- */
- /* data */
- /* doesn't matter */
- usbduxfast_cmd_data(dev, 1, steps - 1,
- 0x02, rngmask, 0x00);
-
- /* branch back to state 1 */
- /* deceision state w/o data */
- /* doesn't matter */
- usbduxfast_cmd_data(dev, 2,
- 0x09, 0x01, rngmask, 0xff);
- }
- } else {
- /*
- * we loop through 3 states: 2x delay and 1x data
- * this gives a min sampling rate of 60kHz
- */
-
- /* we have 1 state with duration 1 */
- steps = steps - 1;
-
- /* do the first part of the delay */
- usbduxfast_cmd_data(dev, 1,
- steps / 2, 0x00, rngmask, 0x00);
-
- /* and the second part */
- usbduxfast_cmd_data(dev, 2, steps - steps / 2,
- 0x00, rngmask, 0x00);
-
- /* get the data and branch back */
-
- /* branch back to state 1 */
- /* deceision state w data */
- /* doesn't matter */
- usbduxfast_cmd_data(dev, 3,
- 0x09, 0x03, rngmask, 0xff);
- }
- break;
-
- case 2:
- /*
- * two channels
- * commit data to the FIFO
- */
-
- if (CR_RANGE(cmd->chanlist[0]) > 0)
- rngmask = 0xff - 0x04;
- else
- rngmask = 0xff;
-
- /* data */
- usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
-
- /* we have 1 state with duration 1: state 0 */
- steps_tmp = steps - 1;
-
- if (CR_RANGE(cmd->chanlist[1]) > 0)
- rngmask = 0xff - 0x04;
- else
- rngmask = 0xff;
-
- /* do the first part of the delay */
- /* count */
- usbduxfast_cmd_data(dev, 1, steps_tmp / 2,
- 0x00, 0xfe & rngmask, 0x00);
-
- /* and the second part */
- usbduxfast_cmd_data(dev, 2, steps_tmp - steps_tmp / 2,
- 0x00, rngmask, 0x00);
-
- /* data */
- usbduxfast_cmd_data(dev, 3, 0x01, 0x02, rngmask, 0x00);
-
- /*
- * we have 2 states with duration 1: step 6 and
- * the IDLE state
- */
- steps_tmp = steps - 2;
-
- if (CR_RANGE(cmd->chanlist[0]) > 0)
- rngmask = 0xff - 0x04;
- else
- rngmask = 0xff;
-
- /* do the first part of the delay */
- /* reset */
- usbduxfast_cmd_data(dev, 4, steps_tmp / 2,
- 0x00, (0xff - 0x02) & rngmask, 0x00);
-
- /* and the second part */
- usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
- 0x00, rngmask, 0x00);
-
- usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
- break;
-
- case 3:
- /*
- * three channels
- */
- for (j = 0; j < 1; j++) {
- int index = j * 2;
-
- if (CR_RANGE(cmd->chanlist[j]) > 0)
- rngmask = 0xff - 0x04;
- else
- rngmask = 0xff;
- /*
- * commit data to the FIFO and do the first part
- * of the delay
- */
- /* data */
- /* no change */
- usbduxfast_cmd_data(dev, index, steps / 2,
- 0x02, rngmask, 0x00);
-
- if (CR_RANGE(cmd->chanlist[j + 1]) > 0)
- rngmask = 0xff - 0x04;
- else
- rngmask = 0xff;
-
- /* do the second part of the delay */
- /* no data */
- /* count */
- usbduxfast_cmd_data(dev, index + 1, steps - steps / 2,
- 0x00, 0xfe & rngmask, 0x00);
- }
-
- /* 2 steps with duration 1: the idele step and step 6: */
- steps_tmp = steps - 2;
-
- /* commit data to the FIFO and do the first part of the delay */
- /* data */
- usbduxfast_cmd_data(dev, 4, steps_tmp / 2,
- 0x02, rngmask, 0x00);
-
- if (CR_RANGE(cmd->chanlist[0]) > 0)
- rngmask = 0xff - 0x04;
- else
- rngmask = 0xff;
-
- /* do the second part of the delay */
- /* no data */
- /* reset */
- usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
- 0x00, (0xff - 0x02) & rngmask, 0x00);
-
- usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
- break;
-
- case 16:
- if (CR_RANGE(cmd->chanlist[0]) > 0)
- rngmask = 0xff - 0x04;
- else
- rngmask = 0xff;
-
- if (cmd->start_src == TRIG_EXT) {
- /*
- * we loop here until ready has been set
- */
-
- /* branch back to state 0 */
- /* deceision state w/o data */
- /* reset */
- /* RDY0 = 0 */
- usbduxfast_cmd_data(dev, 0, 0x01, 0x01,
- (0xff - 0x02) & rngmask, 0x00);
- } else {
- /*
- * we just proceed to state 1
- */
-
- /* 30us reset pulse */
- /* reset */
- usbduxfast_cmd_data(dev, 0, 0xff, 0x00,
- (0xff - 0x02) & rngmask, 0x00);
- }
-
- /* commit data to the FIFO */
- /* data */
- usbduxfast_cmd_data(dev, 1, 0x01, 0x02, rngmask, 0x00);
-
- /* we have 2 states with duration 1 */
- steps = steps - 2;
-
- /* do the first part of the delay */
- usbduxfast_cmd_data(dev, 2, steps / 2,
- 0x00, 0xfe & rngmask, 0x00);
-
- /* and the second part */
- usbduxfast_cmd_data(dev, 3, steps - steps / 2,
- 0x00, rngmask, 0x00);
-
- /* branch back to state 1 */
- /* deceision state w/o data */
- /* doesn't matter */
- usbduxfast_cmd_data(dev, 4, 0x09, 0x01, rngmask, 0xff);
-
- break;
- }
-
- /* 0 means that the AD commands are sent */
- ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS);
- if (ret < 0)
- goto cmd_exit;
-
- if ((cmd->start_src == TRIG_NOW) || (cmd->start_src == TRIG_EXT)) {
- /* enable this acquisition operation */
- devpriv->ai_cmd_running = 1;
- ret = usbduxfast_submit_urb(dev);
- if (ret < 0) {
- devpriv->ai_cmd_running = 0;
- /* fixme: unlink here?? */
- goto cmd_exit;
- }
- s->async->inttrig = NULL;
- } else { /* TRIG_INT */
- s->async->inttrig = usbduxfast_ai_inttrig;
- }
-
-cmd_exit:
- mutex_unlock(&devpriv->mut);
-
- return ret;
-}
-
-/*
- * Mode 0 is used to get a single conversion on demand.
- */
-static int usbduxfast_ai_insn_read(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- struct usb_device *usb = comedi_to_usb_dev(dev);
- struct usbduxfast_private *devpriv = dev->private;
- unsigned int chan = CR_CHAN(insn->chanspec);
- unsigned int range = CR_RANGE(insn->chanspec);
- u8 rngmask = range ? (0xff - 0x04) : 0xff;
- int i, j, n, actual_length;
- int ret;
-
- mutex_lock(&devpriv->mut);
-
- if (devpriv->ai_cmd_running) {
- dev_err(dev->class_dev,
- "ai_insn_read not possible, async cmd is running\n");
- mutex_unlock(&devpriv->mut);
- return -EBUSY;
- }
-
- /* set command for the first channel */
-
- /* commit data to the FIFO */
- /* data */
- usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
-
- /* do the first part of the delay */
- usbduxfast_cmd_data(dev, 1, 0x0c, 0x00, 0xfe & rngmask, 0x00);
- usbduxfast_cmd_data(dev, 2, 0x01, 0x00, 0xfe & rngmask, 0x00);
- usbduxfast_cmd_data(dev, 3, 0x01, 0x00, 0xfe & rngmask, 0x00);
- usbduxfast_cmd_data(dev, 4, 0x01, 0x00, 0xfe & rngmask, 0x00);
-
- /* second part */
- usbduxfast_cmd_data(dev, 5, 0x0c, 0x00, rngmask, 0x00);
- usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
-
- ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS);
- if (ret < 0) {
- mutex_unlock(&devpriv->mut);
- return ret;
- }
-
- for (i = 0; i < PACKETS_TO_IGNORE; i++) {
- ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
- devpriv->inbuf, SIZEINBUF,
- &actual_length, 10000);
- if (ret < 0) {
- dev_err(dev->class_dev, "insn timeout, no data\n");
- mutex_unlock(&devpriv->mut);
- return ret;
- }
- }
-
- for (i = 0; i < insn->n;) {
- ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
- devpriv->inbuf, SIZEINBUF,
- &actual_length, 10000);
- if (ret < 0) {
- dev_err(dev->class_dev, "insn data error: %d\n", ret);
- mutex_unlock(&devpriv->mut);
- return ret;
- }
- n = actual_length / sizeof(u16);
- if ((n % 16) != 0) {
- dev_err(dev->class_dev, "insn data packet corrupted\n");
- mutex_unlock(&devpriv->mut);
- return -EINVAL;
- }
- for (j = chan; (j < n) && (i < insn->n); j = j + 16) {
- data[i] = ((u16 *)(devpriv->inbuf))[j];
- i++;
- }
- }
-
- mutex_unlock(&devpriv->mut);
-
- return insn->n;
-}
-
-static int usbduxfast_upload_firmware(struct comedi_device *dev,
- const u8 *data, size_t size,
- unsigned long context)
-{
- struct usb_device *usb = comedi_to_usb_dev(dev);
- u8 *buf;
- unsigned char *tmp;
- int ret;
-
- if (!data)
- return 0;
-
- if (size > FIRMWARE_MAX_LEN) {
- dev_err(dev->class_dev, "firmware binary too large for FX2\n");
- return -ENOMEM;
- }
-
- /* we generate a local buffer for the firmware */
- buf = kmemdup(data, size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- /* we need a malloc'ed buffer for usb_control_msg() */
- tmp = kmalloc(1, GFP_KERNEL);
- if (!tmp) {
- kfree(buf);
- return -ENOMEM;
- }
-
- /* stop the current firmware on the device */
- *tmp = 1; /* 7f92 to one */
- ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
- USBDUXFASTSUB_FIRMWARE,
- VENDOR_DIR_OUT,
- USBDUXFASTSUB_CPUCS, 0x0000,
- tmp, 1,
- EZTIMEOUT);
- if (ret < 0) {
- dev_err(dev->class_dev, "can not stop firmware\n");
- goto done;
- }
-
- /* upload the new firmware to the device */
- ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
- USBDUXFASTSUB_FIRMWARE,
- VENDOR_DIR_OUT,
- 0, 0x0000,
- buf, size,
- EZTIMEOUT);
- if (ret < 0) {
- dev_err(dev->class_dev, "firmware upload failed\n");
- goto done;
- }
-
- /* start the new firmware on the device */
- *tmp = 0; /* 7f92 to zero */
- ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
- USBDUXFASTSUB_FIRMWARE,
- VENDOR_DIR_OUT,
- USBDUXFASTSUB_CPUCS, 0x0000,
- tmp, 1,
- EZTIMEOUT);
- if (ret < 0)
- dev_err(dev->class_dev, "can not start firmware\n");
-
-done:
- kfree(tmp);
- kfree(buf);
- return ret;
-}
-
-static int usbduxfast_auto_attach(struct comedi_device *dev,
- unsigned long context_unused)
-{
- struct usb_interface *intf = comedi_to_usb_interface(dev);
- struct usb_device *usb = comedi_to_usb_dev(dev);
- struct usbduxfast_private *devpriv;
- struct comedi_subdevice *s;
- int ret;
-
- if (usb->speed != USB_SPEED_HIGH) {
- dev_err(dev->class_dev,
- "This driver needs USB 2.0 to operate. Aborting...\n");
- return -ENODEV;
- }
-
- devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
- if (!devpriv)
- return -ENOMEM;
-
- mutex_init(&devpriv->mut);
- usb_set_intfdata(intf, devpriv);
-
- devpriv->duxbuf = kmalloc(SIZEOFDUXBUF, GFP_KERNEL);
- if (!devpriv->duxbuf)
- return -ENOMEM;
-
- ret = usb_set_interface(usb,
- intf->altsetting->desc.bInterfaceNumber, 1);
- if (ret < 0) {
- dev_err(dev->class_dev,
- "could not switch to alternate setting 1\n");
- return -ENODEV;
- }
-
- devpriv->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!devpriv->urb)
- return -ENOMEM;
-
- devpriv->inbuf = kmalloc(SIZEINBUF, GFP_KERNEL);
- if (!devpriv->inbuf)
- return -ENOMEM;
-
- ret = comedi_load_firmware(dev, &usb->dev, FIRMWARE,
- usbduxfast_upload_firmware, 0);
- if (ret)
- return ret;
-
- ret = comedi_alloc_subdevices(dev, 1);
- if (ret)
- return ret;
-
- /* Analog Input subdevice */
- s = &dev->subdevices[0];
- dev->read_subdev = s;
- s->type = COMEDI_SUBD_AI;
- s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
- s->n_chan = 16;
- s->maxdata = 0x1000; /* 12-bit + 1 overflow bit */
- s->range_table = &range_usbduxfast_ai_range;
- s->insn_read = usbduxfast_ai_insn_read;
- s->len_chanlist = s->n_chan;
- s->do_cmdtest = usbduxfast_ai_cmdtest;
- s->do_cmd = usbduxfast_ai_cmd;
- s->cancel = usbduxfast_ai_cancel;
-
- return 0;
-}
-
-static void usbduxfast_detach(struct comedi_device *dev)
-{
- struct usb_interface *intf = comedi_to_usb_interface(dev);
- struct usbduxfast_private *devpriv = dev->private;
-
- if (!devpriv)
- return;
-
- mutex_lock(&devpriv->mut);
-
- usb_set_intfdata(intf, NULL);
-
- if (devpriv->urb) {
- /* waits until a running transfer is over */
- usb_kill_urb(devpriv->urb);
-
- kfree(devpriv->inbuf);
- usb_free_urb(devpriv->urb);
- }
-
- kfree(devpriv->duxbuf);
-
- mutex_unlock(&devpriv->mut);
-
- mutex_destroy(&devpriv->mut);
-}
-
-static struct comedi_driver usbduxfast_driver = {
- .driver_name = "usbduxfast",
- .module = THIS_MODULE,
- .auto_attach = usbduxfast_auto_attach,
- .detach = usbduxfast_detach,
-};
-
-static int usbduxfast_usb_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- return comedi_usb_auto_config(intf, &usbduxfast_driver, 0);
-}
-
-static const struct usb_device_id usbduxfast_usb_table[] = {
- /* { USB_DEVICE(0x4b4, 0x8613) }, testing */
- { USB_DEVICE(0x13d8, 0x0010) }, /* real ID */
- { USB_DEVICE(0x13d8, 0x0011) }, /* real ID */
- { }
-};
-MODULE_DEVICE_TABLE(usb, usbduxfast_usb_table);
-
-static struct usb_driver usbduxfast_usb_driver = {
- .name = "usbduxfast",
- .probe = usbduxfast_usb_probe,
- .disconnect = comedi_usb_auto_unconfig,
- .id_table = usbduxfast_usb_table,
-};
-module_comedi_usb_driver(usbduxfast_driver, usbduxfast_usb_driver);
-
-MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
-MODULE_DESCRIPTION("USB-DUXfast, BerndPorr@f2s.com");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(FIRMWARE);