diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/usbduxfast.c')
-rw-r--r-- | drivers/staging/comedi/drivers/usbduxfast.c | 1039 |
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); |