18ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
28ffdff6aSGreg Kroah-Hartman /*
38ffdff6aSGreg Kroah-Hartman * Copyright (C) 2004-2019 Bernd Porr, mail@berndporr.me.uk
48ffdff6aSGreg Kroah-Hartman */
58ffdff6aSGreg Kroah-Hartman
68ffdff6aSGreg Kroah-Hartman /*
78ffdff6aSGreg Kroah-Hartman * Driver: usbduxfast
88ffdff6aSGreg Kroah-Hartman * Description: University of Stirling USB DAQ & INCITE Technology Limited
98ffdff6aSGreg Kroah-Hartman * Devices: [ITL] USB-DUX-FAST (usbduxfast)
108ffdff6aSGreg Kroah-Hartman * Author: Bernd Porr <mail@berndporr.me.uk>
118ffdff6aSGreg Kroah-Hartman * Updated: 16 Nov 2019
128ffdff6aSGreg Kroah-Hartman * Status: stable
138ffdff6aSGreg Kroah-Hartman */
148ffdff6aSGreg Kroah-Hartman
158ffdff6aSGreg Kroah-Hartman /*
168ffdff6aSGreg Kroah-Hartman * I must give credit here to Chris Baugher who
178ffdff6aSGreg Kroah-Hartman * wrote the driver for AT-MIO-16d. I used some parts of this
188ffdff6aSGreg Kroah-Hartman * driver. I also must give credits to David Brownell
198ffdff6aSGreg Kroah-Hartman * who supported me with the USB development.
208ffdff6aSGreg Kroah-Hartman *
218ffdff6aSGreg Kroah-Hartman * Bernd Porr
228ffdff6aSGreg Kroah-Hartman *
238ffdff6aSGreg Kroah-Hartman *
248ffdff6aSGreg Kroah-Hartman * Revision history:
258ffdff6aSGreg Kroah-Hartman * 1.0: Fixed a rounding error in usbduxfast_ai_cmdtest
268ffdff6aSGreg Kroah-Hartman * 0.9: Dropping the first data packet which seems to be from the last transfer.
278ffdff6aSGreg Kroah-Hartman * Buffer overflows in the FX2 are handed over to comedi.
288ffdff6aSGreg Kroah-Hartman * 0.92: Dropping now 4 packets. The quad buffer has to be emptied.
298ffdff6aSGreg Kroah-Hartman * Added insn command basically for testing. Sample rate is
308ffdff6aSGreg Kroah-Hartman * 1MHz/16ch=62.5kHz
318ffdff6aSGreg Kroah-Hartman * 0.99: Ian Abbott pointed out a bug which has been corrected. Thanks!
328ffdff6aSGreg Kroah-Hartman * 0.99a: added external trigger.
338ffdff6aSGreg Kroah-Hartman * 1.00: added firmware kernel request to the driver which fixed
348ffdff6aSGreg Kroah-Hartman * udev coldplug problem
358ffdff6aSGreg Kroah-Hartman */
368ffdff6aSGreg Kroah-Hartman
378ffdff6aSGreg Kroah-Hartman #include <linux/kernel.h>
388ffdff6aSGreg Kroah-Hartman #include <linux/module.h>
398ffdff6aSGreg Kroah-Hartman #include <linux/slab.h>
408ffdff6aSGreg Kroah-Hartman #include <linux/input.h>
418ffdff6aSGreg Kroah-Hartman #include <linux/fcntl.h>
428ffdff6aSGreg Kroah-Hartman #include <linux/compiler.h>
43*df0e68c1SIan Abbott #include <linux/comedi/comedi_usb.h>
448ffdff6aSGreg Kroah-Hartman
458ffdff6aSGreg Kroah-Hartman /*
468ffdff6aSGreg Kroah-Hartman * timeout for the USB-transfer
478ffdff6aSGreg Kroah-Hartman */
488ffdff6aSGreg Kroah-Hartman #define EZTIMEOUT 30
498ffdff6aSGreg Kroah-Hartman
508ffdff6aSGreg Kroah-Hartman /*
518ffdff6aSGreg Kroah-Hartman * constants for "firmware" upload and download
528ffdff6aSGreg Kroah-Hartman */
538ffdff6aSGreg Kroah-Hartman #define FIRMWARE "usbduxfast_firmware.bin"
548ffdff6aSGreg Kroah-Hartman #define FIRMWARE_MAX_LEN 0x2000
558ffdff6aSGreg Kroah-Hartman #define USBDUXFASTSUB_FIRMWARE 0xA0
568ffdff6aSGreg Kroah-Hartman #define VENDOR_DIR_IN 0xC0
578ffdff6aSGreg Kroah-Hartman #define VENDOR_DIR_OUT 0x40
588ffdff6aSGreg Kroah-Hartman
598ffdff6aSGreg Kroah-Hartman /*
608ffdff6aSGreg Kroah-Hartman * internal addresses of the 8051 processor
618ffdff6aSGreg Kroah-Hartman */
628ffdff6aSGreg Kroah-Hartman #define USBDUXFASTSUB_CPUCS 0xE600
638ffdff6aSGreg Kroah-Hartman
648ffdff6aSGreg Kroah-Hartman /*
658ffdff6aSGreg Kroah-Hartman * max length of the transfer-buffer for software upload
668ffdff6aSGreg Kroah-Hartman */
678ffdff6aSGreg Kroah-Hartman #define TB_LEN 0x2000
688ffdff6aSGreg Kroah-Hartman
698ffdff6aSGreg Kroah-Hartman /*
708ffdff6aSGreg Kroah-Hartman * input endpoint number
718ffdff6aSGreg Kroah-Hartman */
728ffdff6aSGreg Kroah-Hartman #define BULKINEP 6
738ffdff6aSGreg Kroah-Hartman
748ffdff6aSGreg Kroah-Hartman /*
758ffdff6aSGreg Kroah-Hartman * endpoint for the A/D channellist: bulk OUT
768ffdff6aSGreg Kroah-Hartman */
778ffdff6aSGreg Kroah-Hartman #define CHANNELLISTEP 4
788ffdff6aSGreg Kroah-Hartman
798ffdff6aSGreg Kroah-Hartman /*
808ffdff6aSGreg Kroah-Hartman * number of channels
818ffdff6aSGreg Kroah-Hartman */
828ffdff6aSGreg Kroah-Hartman #define NUMCHANNELS 32
838ffdff6aSGreg Kroah-Hartman
848ffdff6aSGreg Kroah-Hartman /*
858ffdff6aSGreg Kroah-Hartman * size of the waveform descriptor
868ffdff6aSGreg Kroah-Hartman */
878ffdff6aSGreg Kroah-Hartman #define WAVESIZE 0x20
888ffdff6aSGreg Kroah-Hartman
898ffdff6aSGreg Kroah-Hartman /*
908ffdff6aSGreg Kroah-Hartman * size of one A/D value
918ffdff6aSGreg Kroah-Hartman */
928ffdff6aSGreg Kroah-Hartman #define SIZEADIN (sizeof(s16))
938ffdff6aSGreg Kroah-Hartman
948ffdff6aSGreg Kroah-Hartman /*
958ffdff6aSGreg Kroah-Hartman * size of the input-buffer IN BYTES
968ffdff6aSGreg Kroah-Hartman */
978ffdff6aSGreg Kroah-Hartman #define SIZEINBUF 512
988ffdff6aSGreg Kroah-Hartman
998ffdff6aSGreg Kroah-Hartman /*
1008ffdff6aSGreg Kroah-Hartman * 16 bytes
1018ffdff6aSGreg Kroah-Hartman */
1028ffdff6aSGreg Kroah-Hartman #define SIZEINSNBUF 512
1038ffdff6aSGreg Kroah-Hartman
1048ffdff6aSGreg Kroah-Hartman /*
1058ffdff6aSGreg Kroah-Hartman * size of the buffer for the dux commands in bytes
1068ffdff6aSGreg Kroah-Hartman */
1078ffdff6aSGreg Kroah-Hartman #define SIZEOFDUXBUF 256
1088ffdff6aSGreg Kroah-Hartman
1098ffdff6aSGreg Kroah-Hartman /*
1108ffdff6aSGreg Kroah-Hartman * number of in-URBs which receive the data: min=5
1118ffdff6aSGreg Kroah-Hartman */
1128ffdff6aSGreg Kroah-Hartman #define NUMOFINBUFFERSHIGH 10
1138ffdff6aSGreg Kroah-Hartman
1148ffdff6aSGreg Kroah-Hartman /*
1158ffdff6aSGreg Kroah-Hartman * min delay steps for more than one channel
1168ffdff6aSGreg Kroah-Hartman * basically when the mux gives up ;-)
1178ffdff6aSGreg Kroah-Hartman *
1188ffdff6aSGreg Kroah-Hartman * steps at 30MHz in the FX2
1198ffdff6aSGreg Kroah-Hartman */
1208ffdff6aSGreg Kroah-Hartman #define MIN_SAMPLING_PERIOD 9
1218ffdff6aSGreg Kroah-Hartman
1228ffdff6aSGreg Kroah-Hartman /*
1238ffdff6aSGreg Kroah-Hartman * max number of 1/30MHz delay steps
1248ffdff6aSGreg Kroah-Hartman */
1258ffdff6aSGreg Kroah-Hartman #define MAX_SAMPLING_PERIOD 500
1268ffdff6aSGreg Kroah-Hartman
1278ffdff6aSGreg Kroah-Hartman /*
1288ffdff6aSGreg Kroah-Hartman * number of received packets to ignore before we start handing data
1298ffdff6aSGreg Kroah-Hartman * over to comedi, it's quad buffering and we have to ignore 4 packets
1308ffdff6aSGreg Kroah-Hartman */
1318ffdff6aSGreg Kroah-Hartman #define PACKETS_TO_IGNORE 4
1328ffdff6aSGreg Kroah-Hartman
1338ffdff6aSGreg Kroah-Hartman /*
1348ffdff6aSGreg Kroah-Hartman * comedi constants
1358ffdff6aSGreg Kroah-Hartman */
1368ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange range_usbduxfast_ai_range = {
1378ffdff6aSGreg Kroah-Hartman 2, {
1388ffdff6aSGreg Kroah-Hartman BIP_RANGE(0.75),
1398ffdff6aSGreg Kroah-Hartman BIP_RANGE(0.5)
1408ffdff6aSGreg Kroah-Hartman }
1418ffdff6aSGreg Kroah-Hartman };
1428ffdff6aSGreg Kroah-Hartman
1438ffdff6aSGreg Kroah-Hartman /*
1448ffdff6aSGreg Kroah-Hartman * private structure of one subdevice
1458ffdff6aSGreg Kroah-Hartman *
1468ffdff6aSGreg Kroah-Hartman * this is the structure which holds all the data of this driver
1478ffdff6aSGreg Kroah-Hartman * one sub device just now: A/D
1488ffdff6aSGreg Kroah-Hartman */
1498ffdff6aSGreg Kroah-Hartman struct usbduxfast_private {
1508ffdff6aSGreg Kroah-Hartman struct urb *urb; /* BULK-transfer handling: urb */
1518ffdff6aSGreg Kroah-Hartman u8 *duxbuf;
1528ffdff6aSGreg Kroah-Hartman s8 *inbuf;
1538ffdff6aSGreg Kroah-Hartman short int ai_cmd_running; /* asynchronous command is running */
1548ffdff6aSGreg Kroah-Hartman int ignore; /* counter which ignores the first buffers */
1558ffdff6aSGreg Kroah-Hartman struct mutex mut;
1568ffdff6aSGreg Kroah-Hartman };
1578ffdff6aSGreg Kroah-Hartman
1588ffdff6aSGreg Kroah-Hartman /*
1598ffdff6aSGreg Kroah-Hartman * bulk transfers to usbduxfast
1608ffdff6aSGreg Kroah-Hartman */
1618ffdff6aSGreg Kroah-Hartman #define SENDADCOMMANDS 0
1628ffdff6aSGreg Kroah-Hartman #define SENDINITEP6 1
1638ffdff6aSGreg Kroah-Hartman
usbduxfast_send_cmd(struct comedi_device * dev,int cmd_type)1648ffdff6aSGreg Kroah-Hartman static int usbduxfast_send_cmd(struct comedi_device *dev, int cmd_type)
1658ffdff6aSGreg Kroah-Hartman {
1668ffdff6aSGreg Kroah-Hartman struct usb_device *usb = comedi_to_usb_dev(dev);
1678ffdff6aSGreg Kroah-Hartman struct usbduxfast_private *devpriv = dev->private;
1688ffdff6aSGreg Kroah-Hartman int nsent;
1698ffdff6aSGreg Kroah-Hartman int ret;
1708ffdff6aSGreg Kroah-Hartman
1718ffdff6aSGreg Kroah-Hartman devpriv->duxbuf[0] = cmd_type;
1728ffdff6aSGreg Kroah-Hartman
1738ffdff6aSGreg Kroah-Hartman ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, CHANNELLISTEP),
1748ffdff6aSGreg Kroah-Hartman devpriv->duxbuf, SIZEOFDUXBUF,
1758ffdff6aSGreg Kroah-Hartman &nsent, 10000);
1768ffdff6aSGreg Kroah-Hartman if (ret < 0)
1778ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev,
1788ffdff6aSGreg Kroah-Hartman "could not transmit command to the usb-device, err=%d\n",
1798ffdff6aSGreg Kroah-Hartman ret);
1808ffdff6aSGreg Kroah-Hartman return ret;
1818ffdff6aSGreg Kroah-Hartman }
1828ffdff6aSGreg Kroah-Hartman
usbduxfast_cmd_data(struct comedi_device * dev,int index,u8 len,u8 op,u8 out,u8 log)1838ffdff6aSGreg Kroah-Hartman static void usbduxfast_cmd_data(struct comedi_device *dev, int index,
1848ffdff6aSGreg Kroah-Hartman u8 len, u8 op, u8 out, u8 log)
1858ffdff6aSGreg Kroah-Hartman {
1868ffdff6aSGreg Kroah-Hartman struct usbduxfast_private *devpriv = dev->private;
1878ffdff6aSGreg Kroah-Hartman
1888ffdff6aSGreg Kroah-Hartman /* Set the GPIF bytes, the first byte is the command byte */
1898ffdff6aSGreg Kroah-Hartman devpriv->duxbuf[1 + 0x00 + index] = len;
1908ffdff6aSGreg Kroah-Hartman devpriv->duxbuf[1 + 0x08 + index] = op;
1918ffdff6aSGreg Kroah-Hartman devpriv->duxbuf[1 + 0x10 + index] = out;
1928ffdff6aSGreg Kroah-Hartman devpriv->duxbuf[1 + 0x18 + index] = log;
1938ffdff6aSGreg Kroah-Hartman }
1948ffdff6aSGreg Kroah-Hartman
usbduxfast_ai_stop(struct comedi_device * dev,int do_unlink)1958ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_stop(struct comedi_device *dev, int do_unlink)
1968ffdff6aSGreg Kroah-Hartman {
1978ffdff6aSGreg Kroah-Hartman struct usbduxfast_private *devpriv = dev->private;
1988ffdff6aSGreg Kroah-Hartman
1998ffdff6aSGreg Kroah-Hartman /* stop aquistion */
2008ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_running = 0;
2018ffdff6aSGreg Kroah-Hartman
2028ffdff6aSGreg Kroah-Hartman if (do_unlink && devpriv->urb) {
2038ffdff6aSGreg Kroah-Hartman /* kill the running transfer */
2048ffdff6aSGreg Kroah-Hartman usb_kill_urb(devpriv->urb);
2058ffdff6aSGreg Kroah-Hartman }
2068ffdff6aSGreg Kroah-Hartman
2078ffdff6aSGreg Kroah-Hartman return 0;
2088ffdff6aSGreg Kroah-Hartman }
2098ffdff6aSGreg Kroah-Hartman
usbduxfast_ai_cancel(struct comedi_device * dev,struct comedi_subdevice * s)2108ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_cancel(struct comedi_device *dev,
2118ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s)
2128ffdff6aSGreg Kroah-Hartman {
2138ffdff6aSGreg Kroah-Hartman struct usbduxfast_private *devpriv = dev->private;
2148ffdff6aSGreg Kroah-Hartman int ret;
2158ffdff6aSGreg Kroah-Hartman
2168ffdff6aSGreg Kroah-Hartman mutex_lock(&devpriv->mut);
2178ffdff6aSGreg Kroah-Hartman ret = usbduxfast_ai_stop(dev, 1);
2188ffdff6aSGreg Kroah-Hartman mutex_unlock(&devpriv->mut);
2198ffdff6aSGreg Kroah-Hartman
2208ffdff6aSGreg Kroah-Hartman return ret;
2218ffdff6aSGreg Kroah-Hartman }
2228ffdff6aSGreg Kroah-Hartman
usbduxfast_ai_handle_urb(struct comedi_device * dev,struct comedi_subdevice * s,struct urb * urb)2238ffdff6aSGreg Kroah-Hartman static void usbduxfast_ai_handle_urb(struct comedi_device *dev,
2248ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
2258ffdff6aSGreg Kroah-Hartman struct urb *urb)
2268ffdff6aSGreg Kroah-Hartman {
2278ffdff6aSGreg Kroah-Hartman struct usbduxfast_private *devpriv = dev->private;
2288ffdff6aSGreg Kroah-Hartman struct comedi_async *async = s->async;
2298ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &async->cmd;
2308ffdff6aSGreg Kroah-Hartman int ret;
2318ffdff6aSGreg Kroah-Hartman
2328ffdff6aSGreg Kroah-Hartman if (devpriv->ignore) {
2338ffdff6aSGreg Kroah-Hartman devpriv->ignore--;
2348ffdff6aSGreg Kroah-Hartman } else {
2358ffdff6aSGreg Kroah-Hartman unsigned int nsamples;
2368ffdff6aSGreg Kroah-Hartman
2378ffdff6aSGreg Kroah-Hartman nsamples = comedi_bytes_to_samples(s, urb->actual_length);
2388ffdff6aSGreg Kroah-Hartman nsamples = comedi_nsamples_left(s, nsamples);
2398ffdff6aSGreg Kroah-Hartman comedi_buf_write_samples(s, urb->transfer_buffer, nsamples);
2408ffdff6aSGreg Kroah-Hartman
2418ffdff6aSGreg Kroah-Hartman if (cmd->stop_src == TRIG_COUNT &&
2428ffdff6aSGreg Kroah-Hartman async->scans_done >= cmd->stop_arg)
2438ffdff6aSGreg Kroah-Hartman async->events |= COMEDI_CB_EOA;
2448ffdff6aSGreg Kroah-Hartman }
2458ffdff6aSGreg Kroah-Hartman
2468ffdff6aSGreg Kroah-Hartman /* if command is still running, resubmit urb for BULK transfer */
2478ffdff6aSGreg Kroah-Hartman if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
2488ffdff6aSGreg Kroah-Hartman urb->dev = comedi_to_usb_dev(dev);
2498ffdff6aSGreg Kroah-Hartman urb->status = 0;
2508ffdff6aSGreg Kroah-Hartman ret = usb_submit_urb(urb, GFP_ATOMIC);
2518ffdff6aSGreg Kroah-Hartman if (ret < 0) {
2528ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "urb resubm failed: %d", ret);
2538ffdff6aSGreg Kroah-Hartman async->events |= COMEDI_CB_ERROR;
2548ffdff6aSGreg Kroah-Hartman }
2558ffdff6aSGreg Kroah-Hartman }
2568ffdff6aSGreg Kroah-Hartman }
2578ffdff6aSGreg Kroah-Hartman
usbduxfast_ai_interrupt(struct urb * urb)2588ffdff6aSGreg Kroah-Hartman static void usbduxfast_ai_interrupt(struct urb *urb)
2598ffdff6aSGreg Kroah-Hartman {
2608ffdff6aSGreg Kroah-Hartman struct comedi_device *dev = urb->context;
2618ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s = dev->read_subdev;
2628ffdff6aSGreg Kroah-Hartman struct comedi_async *async = s->async;
2638ffdff6aSGreg Kroah-Hartman struct usbduxfast_private *devpriv = dev->private;
2648ffdff6aSGreg Kroah-Hartman
2658ffdff6aSGreg Kroah-Hartman /* exit if not running a command, do not resubmit urb */
2668ffdff6aSGreg Kroah-Hartman if (!devpriv->ai_cmd_running)
2678ffdff6aSGreg Kroah-Hartman return;
2688ffdff6aSGreg Kroah-Hartman
2698ffdff6aSGreg Kroah-Hartman switch (urb->status) {
2708ffdff6aSGreg Kroah-Hartman case 0:
2718ffdff6aSGreg Kroah-Hartman usbduxfast_ai_handle_urb(dev, s, urb);
2728ffdff6aSGreg Kroah-Hartman break;
2738ffdff6aSGreg Kroah-Hartman
2748ffdff6aSGreg Kroah-Hartman case -ECONNRESET:
2758ffdff6aSGreg Kroah-Hartman case -ENOENT:
2768ffdff6aSGreg Kroah-Hartman case -ESHUTDOWN:
2778ffdff6aSGreg Kroah-Hartman case -ECONNABORTED:
2788ffdff6aSGreg Kroah-Hartman /* after an unlink command, unplug, ... etc */
2798ffdff6aSGreg Kroah-Hartman async->events |= COMEDI_CB_ERROR;
2808ffdff6aSGreg Kroah-Hartman break;
2818ffdff6aSGreg Kroah-Hartman
2828ffdff6aSGreg Kroah-Hartman default:
2838ffdff6aSGreg Kroah-Hartman /* a real error */
2848ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev,
2858ffdff6aSGreg Kroah-Hartman "non-zero urb status received in ai intr context: %d\n",
2868ffdff6aSGreg Kroah-Hartman urb->status);
2878ffdff6aSGreg Kroah-Hartman async->events |= COMEDI_CB_ERROR;
2888ffdff6aSGreg Kroah-Hartman break;
2898ffdff6aSGreg Kroah-Hartman }
2908ffdff6aSGreg Kroah-Hartman
2918ffdff6aSGreg Kroah-Hartman /*
2928ffdff6aSGreg Kroah-Hartman * comedi_handle_events() cannot be used in this driver. The (*cancel)
2938ffdff6aSGreg Kroah-Hartman * operation would unlink the urb.
2948ffdff6aSGreg Kroah-Hartman */
2958ffdff6aSGreg Kroah-Hartman if (async->events & COMEDI_CB_CANCEL_MASK)
2968ffdff6aSGreg Kroah-Hartman usbduxfast_ai_stop(dev, 0);
2978ffdff6aSGreg Kroah-Hartman
2988ffdff6aSGreg Kroah-Hartman comedi_event(dev, s);
2998ffdff6aSGreg Kroah-Hartman }
3008ffdff6aSGreg Kroah-Hartman
usbduxfast_submit_urb(struct comedi_device * dev)3018ffdff6aSGreg Kroah-Hartman static int usbduxfast_submit_urb(struct comedi_device *dev)
3028ffdff6aSGreg Kroah-Hartman {
3038ffdff6aSGreg Kroah-Hartman struct usb_device *usb = comedi_to_usb_dev(dev);
3048ffdff6aSGreg Kroah-Hartman struct usbduxfast_private *devpriv = dev->private;
3058ffdff6aSGreg Kroah-Hartman int ret;
3068ffdff6aSGreg Kroah-Hartman
3078ffdff6aSGreg Kroah-Hartman usb_fill_bulk_urb(devpriv->urb, usb, usb_rcvbulkpipe(usb, BULKINEP),
3088ffdff6aSGreg Kroah-Hartman devpriv->inbuf, SIZEINBUF,
3098ffdff6aSGreg Kroah-Hartman usbduxfast_ai_interrupt, dev);
3108ffdff6aSGreg Kroah-Hartman
3118ffdff6aSGreg Kroah-Hartman ret = usb_submit_urb(devpriv->urb, GFP_ATOMIC);
3128ffdff6aSGreg Kroah-Hartman if (ret) {
3138ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "usb_submit_urb error %d\n", ret);
3148ffdff6aSGreg Kroah-Hartman return ret;
3158ffdff6aSGreg Kroah-Hartman }
3168ffdff6aSGreg Kroah-Hartman return 0;
3178ffdff6aSGreg Kroah-Hartman }
3188ffdff6aSGreg Kroah-Hartman
usbduxfast_ai_check_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)3198ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_check_chanlist(struct comedi_device *dev,
3208ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
3218ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd)
3228ffdff6aSGreg Kroah-Hartman {
3238ffdff6aSGreg Kroah-Hartman unsigned int gain0 = CR_RANGE(cmd->chanlist[0]);
3248ffdff6aSGreg Kroah-Hartman int i;
3258ffdff6aSGreg Kroah-Hartman
3268ffdff6aSGreg Kroah-Hartman if (cmd->chanlist_len > 3 && cmd->chanlist_len != 16) {
3278ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "unsupported combination of channels\n");
3288ffdff6aSGreg Kroah-Hartman return -EINVAL;
3298ffdff6aSGreg Kroah-Hartman }
3308ffdff6aSGreg Kroah-Hartman
3318ffdff6aSGreg Kroah-Hartman for (i = 0; i < cmd->chanlist_len; ++i) {
3328ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(cmd->chanlist[i]);
3338ffdff6aSGreg Kroah-Hartman unsigned int gain = CR_RANGE(cmd->chanlist[i]);
3348ffdff6aSGreg Kroah-Hartman
3358ffdff6aSGreg Kroah-Hartman if (chan != i) {
3368ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev,
3378ffdff6aSGreg Kroah-Hartman "channels are not consecutive\n");
3388ffdff6aSGreg Kroah-Hartman return -EINVAL;
3398ffdff6aSGreg Kroah-Hartman }
3408ffdff6aSGreg Kroah-Hartman if (gain != gain0 && cmd->chanlist_len > 3) {
3418ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev,
3428ffdff6aSGreg Kroah-Hartman "gain must be the same for all channels\n");
3438ffdff6aSGreg Kroah-Hartman return -EINVAL;
3448ffdff6aSGreg Kroah-Hartman }
3458ffdff6aSGreg Kroah-Hartman }
3468ffdff6aSGreg Kroah-Hartman return 0;
3478ffdff6aSGreg Kroah-Hartman }
3488ffdff6aSGreg Kroah-Hartman
usbduxfast_ai_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)3498ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_cmdtest(struct comedi_device *dev,
3508ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
3518ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd)
3528ffdff6aSGreg Kroah-Hartman {
3538ffdff6aSGreg Kroah-Hartman int err = 0;
3548ffdff6aSGreg Kroah-Hartman int err2 = 0;
3558ffdff6aSGreg Kroah-Hartman unsigned int steps;
3568ffdff6aSGreg Kroah-Hartman unsigned int arg;
3578ffdff6aSGreg Kroah-Hartman
3588ffdff6aSGreg Kroah-Hartman /* Step 1 : check if triggers are trivially valid */
3598ffdff6aSGreg Kroah-Hartman
3608ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->start_src,
3618ffdff6aSGreg Kroah-Hartman TRIG_NOW | TRIG_EXT | TRIG_INT);
3628ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
3638ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
3648ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
3658ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
3668ffdff6aSGreg Kroah-Hartman
3678ffdff6aSGreg Kroah-Hartman if (err)
3688ffdff6aSGreg Kroah-Hartman return 1;
3698ffdff6aSGreg Kroah-Hartman
3708ffdff6aSGreg Kroah-Hartman /* Step 2a : make sure trigger sources are unique */
3718ffdff6aSGreg Kroah-Hartman
3728ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_is_unique(cmd->start_src);
3738ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_is_unique(cmd->stop_src);
3748ffdff6aSGreg Kroah-Hartman
3758ffdff6aSGreg Kroah-Hartman /* Step 2b : and mutually compatible */
3768ffdff6aSGreg Kroah-Hartman
3778ffdff6aSGreg Kroah-Hartman if (err)
3788ffdff6aSGreg Kroah-Hartman return 2;
3798ffdff6aSGreg Kroah-Hartman
3808ffdff6aSGreg Kroah-Hartman /* Step 3: check if arguments are trivially valid */
3818ffdff6aSGreg Kroah-Hartman
3828ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
3838ffdff6aSGreg Kroah-Hartman
3848ffdff6aSGreg Kroah-Hartman if (!cmd->chanlist_len)
3858ffdff6aSGreg Kroah-Hartman err |= -EINVAL;
3868ffdff6aSGreg Kroah-Hartman
3878ffdff6aSGreg Kroah-Hartman /* external start trigger is only valid for 1 or 16 channels */
3888ffdff6aSGreg Kroah-Hartman if (cmd->start_src == TRIG_EXT &&
3898ffdff6aSGreg Kroah-Hartman cmd->chanlist_len != 1 && cmd->chanlist_len != 16)
3908ffdff6aSGreg Kroah-Hartman err |= -EINVAL;
3918ffdff6aSGreg Kroah-Hartman
3928ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
3938ffdff6aSGreg Kroah-Hartman cmd->chanlist_len);
3948ffdff6aSGreg Kroah-Hartman
3958ffdff6aSGreg Kroah-Hartman /*
3968ffdff6aSGreg Kroah-Hartman * Validate the conversion timing:
3978ffdff6aSGreg Kroah-Hartman * for 1 channel the timing in 30MHz "steps" is:
3988ffdff6aSGreg Kroah-Hartman * steps <= MAX_SAMPLING_PERIOD
3998ffdff6aSGreg Kroah-Hartman * for all other chanlist_len it is:
4008ffdff6aSGreg Kroah-Hartman * MIN_SAMPLING_PERIOD <= steps <= MAX_SAMPLING_PERIOD
4018ffdff6aSGreg Kroah-Hartman */
4028ffdff6aSGreg Kroah-Hartman steps = (cmd->convert_arg * 30) / 1000;
4038ffdff6aSGreg Kroah-Hartman if (cmd->chanlist_len != 1)
4048ffdff6aSGreg Kroah-Hartman err2 |= comedi_check_trigger_arg_min(&steps,
4058ffdff6aSGreg Kroah-Hartman MIN_SAMPLING_PERIOD);
4068ffdff6aSGreg Kroah-Hartman else
4078ffdff6aSGreg Kroah-Hartman err2 |= comedi_check_trigger_arg_min(&steps, 1);
4088ffdff6aSGreg Kroah-Hartman err2 |= comedi_check_trigger_arg_max(&steps, MAX_SAMPLING_PERIOD);
4098ffdff6aSGreg Kroah-Hartman if (err2) {
4108ffdff6aSGreg Kroah-Hartman err |= err2;
4118ffdff6aSGreg Kroah-Hartman arg = (steps * 1000) / 30;
4128ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
4138ffdff6aSGreg Kroah-Hartman }
4148ffdff6aSGreg Kroah-Hartman
4158ffdff6aSGreg Kroah-Hartman if (cmd->stop_src == TRIG_COUNT)
4168ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
4178ffdff6aSGreg Kroah-Hartman else /* TRIG_NONE */
4188ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
4198ffdff6aSGreg Kroah-Hartman
4208ffdff6aSGreg Kroah-Hartman if (err)
4218ffdff6aSGreg Kroah-Hartman return 3;
4228ffdff6aSGreg Kroah-Hartman
4238ffdff6aSGreg Kroah-Hartman /* Step 4: fix up any arguments */
4248ffdff6aSGreg Kroah-Hartman
4258ffdff6aSGreg Kroah-Hartman /* Step 5: check channel list if it exists */
4268ffdff6aSGreg Kroah-Hartman if (cmd->chanlist && cmd->chanlist_len > 0)
4278ffdff6aSGreg Kroah-Hartman err |= usbduxfast_ai_check_chanlist(dev, s, cmd);
4288ffdff6aSGreg Kroah-Hartman if (err)
4298ffdff6aSGreg Kroah-Hartman return 5;
4308ffdff6aSGreg Kroah-Hartman
4318ffdff6aSGreg Kroah-Hartman return 0;
4328ffdff6aSGreg Kroah-Hartman }
4338ffdff6aSGreg Kroah-Hartman
usbduxfast_ai_inttrig(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int trig_num)4348ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_inttrig(struct comedi_device *dev,
4358ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
4368ffdff6aSGreg Kroah-Hartman unsigned int trig_num)
4378ffdff6aSGreg Kroah-Hartman {
4388ffdff6aSGreg Kroah-Hartman struct usbduxfast_private *devpriv = dev->private;
4398ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &s->async->cmd;
4408ffdff6aSGreg Kroah-Hartman int ret;
4418ffdff6aSGreg Kroah-Hartman
4428ffdff6aSGreg Kroah-Hartman if (trig_num != cmd->start_arg)
4438ffdff6aSGreg Kroah-Hartman return -EINVAL;
4448ffdff6aSGreg Kroah-Hartman
4458ffdff6aSGreg Kroah-Hartman mutex_lock(&devpriv->mut);
4468ffdff6aSGreg Kroah-Hartman
4478ffdff6aSGreg Kroah-Hartman if (!devpriv->ai_cmd_running) {
4488ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_running = 1;
4498ffdff6aSGreg Kroah-Hartman ret = usbduxfast_submit_urb(dev);
4508ffdff6aSGreg Kroah-Hartman if (ret < 0) {
4518ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "urbSubmit: err=%d\n", ret);
4528ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_running = 0;
4538ffdff6aSGreg Kroah-Hartman mutex_unlock(&devpriv->mut);
4548ffdff6aSGreg Kroah-Hartman return ret;
4558ffdff6aSGreg Kroah-Hartman }
4568ffdff6aSGreg Kroah-Hartman s->async->inttrig = NULL;
4578ffdff6aSGreg Kroah-Hartman } else {
4588ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "ai is already running\n");
4598ffdff6aSGreg Kroah-Hartman }
4608ffdff6aSGreg Kroah-Hartman mutex_unlock(&devpriv->mut);
4618ffdff6aSGreg Kroah-Hartman return 1;
4628ffdff6aSGreg Kroah-Hartman }
4638ffdff6aSGreg Kroah-Hartman
usbduxfast_ai_cmd(struct comedi_device * dev,struct comedi_subdevice * s)4648ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_cmd(struct comedi_device *dev,
4658ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s)
4668ffdff6aSGreg Kroah-Hartman {
4678ffdff6aSGreg Kroah-Hartman struct usbduxfast_private *devpriv = dev->private;
4688ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &s->async->cmd;
4698ffdff6aSGreg Kroah-Hartman unsigned int rngmask = 0xff;
4708ffdff6aSGreg Kroah-Hartman int j, ret;
4718ffdff6aSGreg Kroah-Hartman long steps, steps_tmp;
4728ffdff6aSGreg Kroah-Hartman
4738ffdff6aSGreg Kroah-Hartman mutex_lock(&devpriv->mut);
4748ffdff6aSGreg Kroah-Hartman if (devpriv->ai_cmd_running) {
4758ffdff6aSGreg Kroah-Hartman ret = -EBUSY;
4768ffdff6aSGreg Kroah-Hartman goto cmd_exit;
4778ffdff6aSGreg Kroah-Hartman }
4788ffdff6aSGreg Kroah-Hartman
4798ffdff6aSGreg Kroah-Hartman /*
4808ffdff6aSGreg Kroah-Hartman * ignore the first buffers from the device if there
4818ffdff6aSGreg Kroah-Hartman * is an error condition
4828ffdff6aSGreg Kroah-Hartman */
4838ffdff6aSGreg Kroah-Hartman devpriv->ignore = PACKETS_TO_IGNORE;
4848ffdff6aSGreg Kroah-Hartman
4858ffdff6aSGreg Kroah-Hartman steps = (cmd->convert_arg * 30) / 1000;
4868ffdff6aSGreg Kroah-Hartman
4878ffdff6aSGreg Kroah-Hartman switch (cmd->chanlist_len) {
4888ffdff6aSGreg Kroah-Hartman case 1:
4898ffdff6aSGreg Kroah-Hartman /*
4908ffdff6aSGreg Kroah-Hartman * one channel
4918ffdff6aSGreg Kroah-Hartman */
4928ffdff6aSGreg Kroah-Hartman
4938ffdff6aSGreg Kroah-Hartman if (CR_RANGE(cmd->chanlist[0]) > 0)
4948ffdff6aSGreg Kroah-Hartman rngmask = 0xff - 0x04;
4958ffdff6aSGreg Kroah-Hartman else
4968ffdff6aSGreg Kroah-Hartman rngmask = 0xff;
4978ffdff6aSGreg Kroah-Hartman
4988ffdff6aSGreg Kroah-Hartman /*
4998ffdff6aSGreg Kroah-Hartman * for external trigger: looping in this state until
5008ffdff6aSGreg Kroah-Hartman * the RDY0 pin becomes zero
5018ffdff6aSGreg Kroah-Hartman */
5028ffdff6aSGreg Kroah-Hartman
5038ffdff6aSGreg Kroah-Hartman /* we loop here until ready has been set */
5048ffdff6aSGreg Kroah-Hartman if (cmd->start_src == TRIG_EXT) {
5058ffdff6aSGreg Kroah-Hartman /* branch back to state 0 */
5068ffdff6aSGreg Kroah-Hartman /* deceision state w/o data */
5078ffdff6aSGreg Kroah-Hartman /* RDY0 = 0 */
5088ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 0, 0x01, 0x01, rngmask, 0x00);
5098ffdff6aSGreg Kroah-Hartman } else { /* we just proceed to state 1 */
5108ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 0, 0x01, 0x00, rngmask, 0x00);
5118ffdff6aSGreg Kroah-Hartman }
5128ffdff6aSGreg Kroah-Hartman
5138ffdff6aSGreg Kroah-Hartman if (steps < MIN_SAMPLING_PERIOD) {
5148ffdff6aSGreg Kroah-Hartman /* for fast single channel aqu without mux */
5158ffdff6aSGreg Kroah-Hartman if (steps <= 1) {
5168ffdff6aSGreg Kroah-Hartman /*
5178ffdff6aSGreg Kroah-Hartman * we just stay here at state 1 and rexecute
5188ffdff6aSGreg Kroah-Hartman * the same state this gives us 30MHz sampling
5198ffdff6aSGreg Kroah-Hartman * rate
5208ffdff6aSGreg Kroah-Hartman */
5218ffdff6aSGreg Kroah-Hartman
5228ffdff6aSGreg Kroah-Hartman /* branch back to state 1 */
5238ffdff6aSGreg Kroah-Hartman /* deceision state with data */
5248ffdff6aSGreg Kroah-Hartman /* doesn't matter */
5258ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 1,
5268ffdff6aSGreg Kroah-Hartman 0x89, 0x03, rngmask, 0xff);
5278ffdff6aSGreg Kroah-Hartman } else {
5288ffdff6aSGreg Kroah-Hartman /*
5298ffdff6aSGreg Kroah-Hartman * we loop through two states: data and delay
5308ffdff6aSGreg Kroah-Hartman * max rate is 15MHz
5318ffdff6aSGreg Kroah-Hartman */
5328ffdff6aSGreg Kroah-Hartman /* data */
5338ffdff6aSGreg Kroah-Hartman /* doesn't matter */
5348ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 1, steps - 1,
5358ffdff6aSGreg Kroah-Hartman 0x02, rngmask, 0x00);
5368ffdff6aSGreg Kroah-Hartman
5378ffdff6aSGreg Kroah-Hartman /* branch back to state 1 */
5388ffdff6aSGreg Kroah-Hartman /* deceision state w/o data */
5398ffdff6aSGreg Kroah-Hartman /* doesn't matter */
5408ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 2,
5418ffdff6aSGreg Kroah-Hartman 0x09, 0x01, rngmask, 0xff);
5428ffdff6aSGreg Kroah-Hartman }
5438ffdff6aSGreg Kroah-Hartman } else {
5448ffdff6aSGreg Kroah-Hartman /*
5458ffdff6aSGreg Kroah-Hartman * we loop through 3 states: 2x delay and 1x data
5468ffdff6aSGreg Kroah-Hartman * this gives a min sampling rate of 60kHz
5478ffdff6aSGreg Kroah-Hartman */
5488ffdff6aSGreg Kroah-Hartman
5498ffdff6aSGreg Kroah-Hartman /* we have 1 state with duration 1 */
5508ffdff6aSGreg Kroah-Hartman steps = steps - 1;
5518ffdff6aSGreg Kroah-Hartman
5528ffdff6aSGreg Kroah-Hartman /* do the first part of the delay */
5538ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 1,
5548ffdff6aSGreg Kroah-Hartman steps / 2, 0x00, rngmask, 0x00);
5558ffdff6aSGreg Kroah-Hartman
5568ffdff6aSGreg Kroah-Hartman /* and the second part */
5578ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 2, steps - steps / 2,
5588ffdff6aSGreg Kroah-Hartman 0x00, rngmask, 0x00);
5598ffdff6aSGreg Kroah-Hartman
5608ffdff6aSGreg Kroah-Hartman /* get the data and branch back */
5618ffdff6aSGreg Kroah-Hartman
5628ffdff6aSGreg Kroah-Hartman /* branch back to state 1 */
5638ffdff6aSGreg Kroah-Hartman /* deceision state w data */
5648ffdff6aSGreg Kroah-Hartman /* doesn't matter */
5658ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 3,
5668ffdff6aSGreg Kroah-Hartman 0x09, 0x03, rngmask, 0xff);
5678ffdff6aSGreg Kroah-Hartman }
5688ffdff6aSGreg Kroah-Hartman break;
5698ffdff6aSGreg Kroah-Hartman
5708ffdff6aSGreg Kroah-Hartman case 2:
5718ffdff6aSGreg Kroah-Hartman /*
5728ffdff6aSGreg Kroah-Hartman * two channels
5738ffdff6aSGreg Kroah-Hartman * commit data to the FIFO
5748ffdff6aSGreg Kroah-Hartman */
5758ffdff6aSGreg Kroah-Hartman
5768ffdff6aSGreg Kroah-Hartman if (CR_RANGE(cmd->chanlist[0]) > 0)
5778ffdff6aSGreg Kroah-Hartman rngmask = 0xff - 0x04;
5788ffdff6aSGreg Kroah-Hartman else
5798ffdff6aSGreg Kroah-Hartman rngmask = 0xff;
5808ffdff6aSGreg Kroah-Hartman
5818ffdff6aSGreg Kroah-Hartman /* data */
5828ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
5838ffdff6aSGreg Kroah-Hartman
5848ffdff6aSGreg Kroah-Hartman /* we have 1 state with duration 1: state 0 */
5858ffdff6aSGreg Kroah-Hartman steps_tmp = steps - 1;
5868ffdff6aSGreg Kroah-Hartman
5878ffdff6aSGreg Kroah-Hartman if (CR_RANGE(cmd->chanlist[1]) > 0)
5888ffdff6aSGreg Kroah-Hartman rngmask = 0xff - 0x04;
5898ffdff6aSGreg Kroah-Hartman else
5908ffdff6aSGreg Kroah-Hartman rngmask = 0xff;
5918ffdff6aSGreg Kroah-Hartman
5928ffdff6aSGreg Kroah-Hartman /* do the first part of the delay */
5938ffdff6aSGreg Kroah-Hartman /* count */
5948ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 1, steps_tmp / 2,
5958ffdff6aSGreg Kroah-Hartman 0x00, 0xfe & rngmask, 0x00);
5968ffdff6aSGreg Kroah-Hartman
5978ffdff6aSGreg Kroah-Hartman /* and the second part */
5988ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 2, steps_tmp - steps_tmp / 2,
5998ffdff6aSGreg Kroah-Hartman 0x00, rngmask, 0x00);
6008ffdff6aSGreg Kroah-Hartman
6018ffdff6aSGreg Kroah-Hartman /* data */
6028ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 3, 0x01, 0x02, rngmask, 0x00);
6038ffdff6aSGreg Kroah-Hartman
6048ffdff6aSGreg Kroah-Hartman /*
6058ffdff6aSGreg Kroah-Hartman * we have 2 states with duration 1: step 6 and
6068ffdff6aSGreg Kroah-Hartman * the IDLE state
6078ffdff6aSGreg Kroah-Hartman */
6088ffdff6aSGreg Kroah-Hartman steps_tmp = steps - 2;
6098ffdff6aSGreg Kroah-Hartman
6108ffdff6aSGreg Kroah-Hartman if (CR_RANGE(cmd->chanlist[0]) > 0)
6118ffdff6aSGreg Kroah-Hartman rngmask = 0xff - 0x04;
6128ffdff6aSGreg Kroah-Hartman else
6138ffdff6aSGreg Kroah-Hartman rngmask = 0xff;
6148ffdff6aSGreg Kroah-Hartman
6158ffdff6aSGreg Kroah-Hartman /* do the first part of the delay */
6168ffdff6aSGreg Kroah-Hartman /* reset */
6178ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 4, steps_tmp / 2,
6188ffdff6aSGreg Kroah-Hartman 0x00, (0xff - 0x02) & rngmask, 0x00);
6198ffdff6aSGreg Kroah-Hartman
6208ffdff6aSGreg Kroah-Hartman /* and the second part */
6218ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
6228ffdff6aSGreg Kroah-Hartman 0x00, rngmask, 0x00);
6238ffdff6aSGreg Kroah-Hartman
6248ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
6258ffdff6aSGreg Kroah-Hartman break;
6268ffdff6aSGreg Kroah-Hartman
6278ffdff6aSGreg Kroah-Hartman case 3:
6288ffdff6aSGreg Kroah-Hartman /*
6298ffdff6aSGreg Kroah-Hartman * three channels
6308ffdff6aSGreg Kroah-Hartman */
6318ffdff6aSGreg Kroah-Hartman for (j = 0; j < 1; j++) {
6328ffdff6aSGreg Kroah-Hartman int index = j * 2;
6338ffdff6aSGreg Kroah-Hartman
6348ffdff6aSGreg Kroah-Hartman if (CR_RANGE(cmd->chanlist[j]) > 0)
6358ffdff6aSGreg Kroah-Hartman rngmask = 0xff - 0x04;
6368ffdff6aSGreg Kroah-Hartman else
6378ffdff6aSGreg Kroah-Hartman rngmask = 0xff;
6388ffdff6aSGreg Kroah-Hartman /*
6398ffdff6aSGreg Kroah-Hartman * commit data to the FIFO and do the first part
6408ffdff6aSGreg Kroah-Hartman * of the delay
6418ffdff6aSGreg Kroah-Hartman */
6428ffdff6aSGreg Kroah-Hartman /* data */
6438ffdff6aSGreg Kroah-Hartman /* no change */
6448ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, index, steps / 2,
6458ffdff6aSGreg Kroah-Hartman 0x02, rngmask, 0x00);
6468ffdff6aSGreg Kroah-Hartman
6478ffdff6aSGreg Kroah-Hartman if (CR_RANGE(cmd->chanlist[j + 1]) > 0)
6488ffdff6aSGreg Kroah-Hartman rngmask = 0xff - 0x04;
6498ffdff6aSGreg Kroah-Hartman else
6508ffdff6aSGreg Kroah-Hartman rngmask = 0xff;
6518ffdff6aSGreg Kroah-Hartman
6528ffdff6aSGreg Kroah-Hartman /* do the second part of the delay */
6538ffdff6aSGreg Kroah-Hartman /* no data */
6548ffdff6aSGreg Kroah-Hartman /* count */
6558ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, index + 1, steps - steps / 2,
6568ffdff6aSGreg Kroah-Hartman 0x00, 0xfe & rngmask, 0x00);
6578ffdff6aSGreg Kroah-Hartman }
6588ffdff6aSGreg Kroah-Hartman
6598ffdff6aSGreg Kroah-Hartman /* 2 steps with duration 1: the idele step and step 6: */
6608ffdff6aSGreg Kroah-Hartman steps_tmp = steps - 2;
6618ffdff6aSGreg Kroah-Hartman
6628ffdff6aSGreg Kroah-Hartman /* commit data to the FIFO and do the first part of the delay */
6638ffdff6aSGreg Kroah-Hartman /* data */
6648ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 4, steps_tmp / 2,
6658ffdff6aSGreg Kroah-Hartman 0x02, rngmask, 0x00);
6668ffdff6aSGreg Kroah-Hartman
6678ffdff6aSGreg Kroah-Hartman if (CR_RANGE(cmd->chanlist[0]) > 0)
6688ffdff6aSGreg Kroah-Hartman rngmask = 0xff - 0x04;
6698ffdff6aSGreg Kroah-Hartman else
6708ffdff6aSGreg Kroah-Hartman rngmask = 0xff;
6718ffdff6aSGreg Kroah-Hartman
6728ffdff6aSGreg Kroah-Hartman /* do the second part of the delay */
6738ffdff6aSGreg Kroah-Hartman /* no data */
6748ffdff6aSGreg Kroah-Hartman /* reset */
6758ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
6768ffdff6aSGreg Kroah-Hartman 0x00, (0xff - 0x02) & rngmask, 0x00);
6778ffdff6aSGreg Kroah-Hartman
6788ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
6798ffdff6aSGreg Kroah-Hartman break;
6808ffdff6aSGreg Kroah-Hartman
6818ffdff6aSGreg Kroah-Hartman case 16:
6828ffdff6aSGreg Kroah-Hartman if (CR_RANGE(cmd->chanlist[0]) > 0)
6838ffdff6aSGreg Kroah-Hartman rngmask = 0xff - 0x04;
6848ffdff6aSGreg Kroah-Hartman else
6858ffdff6aSGreg Kroah-Hartman rngmask = 0xff;
6868ffdff6aSGreg Kroah-Hartman
6878ffdff6aSGreg Kroah-Hartman if (cmd->start_src == TRIG_EXT) {
6888ffdff6aSGreg Kroah-Hartman /*
6898ffdff6aSGreg Kroah-Hartman * we loop here until ready has been set
6908ffdff6aSGreg Kroah-Hartman */
6918ffdff6aSGreg Kroah-Hartman
6928ffdff6aSGreg Kroah-Hartman /* branch back to state 0 */
6938ffdff6aSGreg Kroah-Hartman /* deceision state w/o data */
6948ffdff6aSGreg Kroah-Hartman /* reset */
6958ffdff6aSGreg Kroah-Hartman /* RDY0 = 0 */
6968ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 0, 0x01, 0x01,
6978ffdff6aSGreg Kroah-Hartman (0xff - 0x02) & rngmask, 0x00);
6988ffdff6aSGreg Kroah-Hartman } else {
6998ffdff6aSGreg Kroah-Hartman /*
7008ffdff6aSGreg Kroah-Hartman * we just proceed to state 1
7018ffdff6aSGreg Kroah-Hartman */
7028ffdff6aSGreg Kroah-Hartman
7038ffdff6aSGreg Kroah-Hartman /* 30us reset pulse */
7048ffdff6aSGreg Kroah-Hartman /* reset */
7058ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 0, 0xff, 0x00,
7068ffdff6aSGreg Kroah-Hartman (0xff - 0x02) & rngmask, 0x00);
7078ffdff6aSGreg Kroah-Hartman }
7088ffdff6aSGreg Kroah-Hartman
7098ffdff6aSGreg Kroah-Hartman /* commit data to the FIFO */
7108ffdff6aSGreg Kroah-Hartman /* data */
7118ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 1, 0x01, 0x02, rngmask, 0x00);
7128ffdff6aSGreg Kroah-Hartman
7138ffdff6aSGreg Kroah-Hartman /* we have 2 states with duration 1 */
7148ffdff6aSGreg Kroah-Hartman steps = steps - 2;
7158ffdff6aSGreg Kroah-Hartman
7168ffdff6aSGreg Kroah-Hartman /* do the first part of the delay */
7178ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 2, steps / 2,
7188ffdff6aSGreg Kroah-Hartman 0x00, 0xfe & rngmask, 0x00);
7198ffdff6aSGreg Kroah-Hartman
7208ffdff6aSGreg Kroah-Hartman /* and the second part */
7218ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 3, steps - steps / 2,
7228ffdff6aSGreg Kroah-Hartman 0x00, rngmask, 0x00);
7238ffdff6aSGreg Kroah-Hartman
7248ffdff6aSGreg Kroah-Hartman /* branch back to state 1 */
7258ffdff6aSGreg Kroah-Hartman /* deceision state w/o data */
7268ffdff6aSGreg Kroah-Hartman /* doesn't matter */
7278ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 4, 0x09, 0x01, rngmask, 0xff);
7288ffdff6aSGreg Kroah-Hartman
7298ffdff6aSGreg Kroah-Hartman break;
7308ffdff6aSGreg Kroah-Hartman }
7318ffdff6aSGreg Kroah-Hartman
7328ffdff6aSGreg Kroah-Hartman /* 0 means that the AD commands are sent */
7338ffdff6aSGreg Kroah-Hartman ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS);
7348ffdff6aSGreg Kroah-Hartman if (ret < 0)
7358ffdff6aSGreg Kroah-Hartman goto cmd_exit;
7368ffdff6aSGreg Kroah-Hartman
7378ffdff6aSGreg Kroah-Hartman if ((cmd->start_src == TRIG_NOW) || (cmd->start_src == TRIG_EXT)) {
7388ffdff6aSGreg Kroah-Hartman /* enable this acquisition operation */
7398ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_running = 1;
7408ffdff6aSGreg Kroah-Hartman ret = usbduxfast_submit_urb(dev);
7418ffdff6aSGreg Kroah-Hartman if (ret < 0) {
7428ffdff6aSGreg Kroah-Hartman devpriv->ai_cmd_running = 0;
7438ffdff6aSGreg Kroah-Hartman /* fixme: unlink here?? */
7448ffdff6aSGreg Kroah-Hartman goto cmd_exit;
7458ffdff6aSGreg Kroah-Hartman }
7468ffdff6aSGreg Kroah-Hartman s->async->inttrig = NULL;
7478ffdff6aSGreg Kroah-Hartman } else { /* TRIG_INT */
7488ffdff6aSGreg Kroah-Hartman s->async->inttrig = usbduxfast_ai_inttrig;
7498ffdff6aSGreg Kroah-Hartman }
7508ffdff6aSGreg Kroah-Hartman
7518ffdff6aSGreg Kroah-Hartman cmd_exit:
7528ffdff6aSGreg Kroah-Hartman mutex_unlock(&devpriv->mut);
7538ffdff6aSGreg Kroah-Hartman
7548ffdff6aSGreg Kroah-Hartman return ret;
7558ffdff6aSGreg Kroah-Hartman }
7568ffdff6aSGreg Kroah-Hartman
7578ffdff6aSGreg Kroah-Hartman /*
7588ffdff6aSGreg Kroah-Hartman * Mode 0 is used to get a single conversion on demand.
7598ffdff6aSGreg Kroah-Hartman */
usbduxfast_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)7608ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_insn_read(struct comedi_device *dev,
7618ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
7628ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn,
7638ffdff6aSGreg Kroah-Hartman unsigned int *data)
7648ffdff6aSGreg Kroah-Hartman {
7658ffdff6aSGreg Kroah-Hartman struct usb_device *usb = comedi_to_usb_dev(dev);
7668ffdff6aSGreg Kroah-Hartman struct usbduxfast_private *devpriv = dev->private;
7678ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec);
7688ffdff6aSGreg Kroah-Hartman unsigned int range = CR_RANGE(insn->chanspec);
7698ffdff6aSGreg Kroah-Hartman u8 rngmask = range ? (0xff - 0x04) : 0xff;
7708ffdff6aSGreg Kroah-Hartman int i, j, n, actual_length;
7718ffdff6aSGreg Kroah-Hartman int ret;
7728ffdff6aSGreg Kroah-Hartman
7738ffdff6aSGreg Kroah-Hartman mutex_lock(&devpriv->mut);
7748ffdff6aSGreg Kroah-Hartman
7758ffdff6aSGreg Kroah-Hartman if (devpriv->ai_cmd_running) {
7768ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev,
7778ffdff6aSGreg Kroah-Hartman "ai_insn_read not possible, async cmd is running\n");
7788ffdff6aSGreg Kroah-Hartman mutex_unlock(&devpriv->mut);
7798ffdff6aSGreg Kroah-Hartman return -EBUSY;
7808ffdff6aSGreg Kroah-Hartman }
7818ffdff6aSGreg Kroah-Hartman
7828ffdff6aSGreg Kroah-Hartman /* set command for the first channel */
7838ffdff6aSGreg Kroah-Hartman
7848ffdff6aSGreg Kroah-Hartman /* commit data to the FIFO */
7858ffdff6aSGreg Kroah-Hartman /* data */
7868ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
7878ffdff6aSGreg Kroah-Hartman
7888ffdff6aSGreg Kroah-Hartman /* do the first part of the delay */
7898ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 1, 0x0c, 0x00, 0xfe & rngmask, 0x00);
7908ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 2, 0x01, 0x00, 0xfe & rngmask, 0x00);
7918ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 3, 0x01, 0x00, 0xfe & rngmask, 0x00);
7928ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 4, 0x01, 0x00, 0xfe & rngmask, 0x00);
7938ffdff6aSGreg Kroah-Hartman
7948ffdff6aSGreg Kroah-Hartman /* second part */
7958ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 5, 0x0c, 0x00, rngmask, 0x00);
7968ffdff6aSGreg Kroah-Hartman usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
7978ffdff6aSGreg Kroah-Hartman
7988ffdff6aSGreg Kroah-Hartman ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS);
7998ffdff6aSGreg Kroah-Hartman if (ret < 0) {
8008ffdff6aSGreg Kroah-Hartman mutex_unlock(&devpriv->mut);
8018ffdff6aSGreg Kroah-Hartman return ret;
8028ffdff6aSGreg Kroah-Hartman }
8038ffdff6aSGreg Kroah-Hartman
8048ffdff6aSGreg Kroah-Hartman for (i = 0; i < PACKETS_TO_IGNORE; i++) {
8058ffdff6aSGreg Kroah-Hartman ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
8068ffdff6aSGreg Kroah-Hartman devpriv->inbuf, SIZEINBUF,
8078ffdff6aSGreg Kroah-Hartman &actual_length, 10000);
8088ffdff6aSGreg Kroah-Hartman if (ret < 0) {
8098ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "insn timeout, no data\n");
8108ffdff6aSGreg Kroah-Hartman mutex_unlock(&devpriv->mut);
8118ffdff6aSGreg Kroah-Hartman return ret;
8128ffdff6aSGreg Kroah-Hartman }
8138ffdff6aSGreg Kroah-Hartman }
8148ffdff6aSGreg Kroah-Hartman
8158ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n;) {
8168ffdff6aSGreg Kroah-Hartman ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
8178ffdff6aSGreg Kroah-Hartman devpriv->inbuf, SIZEINBUF,
8188ffdff6aSGreg Kroah-Hartman &actual_length, 10000);
8198ffdff6aSGreg Kroah-Hartman if (ret < 0) {
8208ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "insn data error: %d\n", ret);
8218ffdff6aSGreg Kroah-Hartman mutex_unlock(&devpriv->mut);
8228ffdff6aSGreg Kroah-Hartman return ret;
8238ffdff6aSGreg Kroah-Hartman }
8248ffdff6aSGreg Kroah-Hartman n = actual_length / sizeof(u16);
8258ffdff6aSGreg Kroah-Hartman if ((n % 16) != 0) {
8268ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "insn data packet corrupted\n");
8278ffdff6aSGreg Kroah-Hartman mutex_unlock(&devpriv->mut);
8288ffdff6aSGreg Kroah-Hartman return -EINVAL;
8298ffdff6aSGreg Kroah-Hartman }
8308ffdff6aSGreg Kroah-Hartman for (j = chan; (j < n) && (i < insn->n); j = j + 16) {
8318ffdff6aSGreg Kroah-Hartman data[i] = ((u16 *)(devpriv->inbuf))[j];
8328ffdff6aSGreg Kroah-Hartman i++;
8338ffdff6aSGreg Kroah-Hartman }
8348ffdff6aSGreg Kroah-Hartman }
8358ffdff6aSGreg Kroah-Hartman
8368ffdff6aSGreg Kroah-Hartman mutex_unlock(&devpriv->mut);
8378ffdff6aSGreg Kroah-Hartman
8388ffdff6aSGreg Kroah-Hartman return insn->n;
8398ffdff6aSGreg Kroah-Hartman }
8408ffdff6aSGreg Kroah-Hartman
usbduxfast_upload_firmware(struct comedi_device * dev,const u8 * data,size_t size,unsigned long context)8418ffdff6aSGreg Kroah-Hartman static int usbduxfast_upload_firmware(struct comedi_device *dev,
8428ffdff6aSGreg Kroah-Hartman const u8 *data, size_t size,
8438ffdff6aSGreg Kroah-Hartman unsigned long context)
8448ffdff6aSGreg Kroah-Hartman {
8458ffdff6aSGreg Kroah-Hartman struct usb_device *usb = comedi_to_usb_dev(dev);
8468ffdff6aSGreg Kroah-Hartman u8 *buf;
8478ffdff6aSGreg Kroah-Hartman unsigned char *tmp;
8488ffdff6aSGreg Kroah-Hartman int ret;
8498ffdff6aSGreg Kroah-Hartman
8508ffdff6aSGreg Kroah-Hartman if (!data)
8518ffdff6aSGreg Kroah-Hartman return 0;
8528ffdff6aSGreg Kroah-Hartman
8538ffdff6aSGreg Kroah-Hartman if (size > FIRMWARE_MAX_LEN) {
8548ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "firmware binary too large for FX2\n");
8558ffdff6aSGreg Kroah-Hartman return -ENOMEM;
8568ffdff6aSGreg Kroah-Hartman }
8578ffdff6aSGreg Kroah-Hartman
8588ffdff6aSGreg Kroah-Hartman /* we generate a local buffer for the firmware */
8598ffdff6aSGreg Kroah-Hartman buf = kmemdup(data, size, GFP_KERNEL);
8608ffdff6aSGreg Kroah-Hartman if (!buf)
8618ffdff6aSGreg Kroah-Hartman return -ENOMEM;
8628ffdff6aSGreg Kroah-Hartman
8638ffdff6aSGreg Kroah-Hartman /* we need a malloc'ed buffer for usb_control_msg() */
8648ffdff6aSGreg Kroah-Hartman tmp = kmalloc(1, GFP_KERNEL);
8658ffdff6aSGreg Kroah-Hartman if (!tmp) {
8668ffdff6aSGreg Kroah-Hartman kfree(buf);
8678ffdff6aSGreg Kroah-Hartman return -ENOMEM;
8688ffdff6aSGreg Kroah-Hartman }
8698ffdff6aSGreg Kroah-Hartman
8708ffdff6aSGreg Kroah-Hartman /* stop the current firmware on the device */
8718ffdff6aSGreg Kroah-Hartman *tmp = 1; /* 7f92 to one */
8728ffdff6aSGreg Kroah-Hartman ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
8738ffdff6aSGreg Kroah-Hartman USBDUXFASTSUB_FIRMWARE,
8748ffdff6aSGreg Kroah-Hartman VENDOR_DIR_OUT,
8758ffdff6aSGreg Kroah-Hartman USBDUXFASTSUB_CPUCS, 0x0000,
8768ffdff6aSGreg Kroah-Hartman tmp, 1,
8778ffdff6aSGreg Kroah-Hartman EZTIMEOUT);
8788ffdff6aSGreg Kroah-Hartman if (ret < 0) {
8798ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "can not stop firmware\n");
8808ffdff6aSGreg Kroah-Hartman goto done;
8818ffdff6aSGreg Kroah-Hartman }
8828ffdff6aSGreg Kroah-Hartman
8838ffdff6aSGreg Kroah-Hartman /* upload the new firmware to the device */
8848ffdff6aSGreg Kroah-Hartman ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
8858ffdff6aSGreg Kroah-Hartman USBDUXFASTSUB_FIRMWARE,
8868ffdff6aSGreg Kroah-Hartman VENDOR_DIR_OUT,
8878ffdff6aSGreg Kroah-Hartman 0, 0x0000,
8888ffdff6aSGreg Kroah-Hartman buf, size,
8898ffdff6aSGreg Kroah-Hartman EZTIMEOUT);
8908ffdff6aSGreg Kroah-Hartman if (ret < 0) {
8918ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "firmware upload failed\n");
8928ffdff6aSGreg Kroah-Hartman goto done;
8938ffdff6aSGreg Kroah-Hartman }
8948ffdff6aSGreg Kroah-Hartman
8958ffdff6aSGreg Kroah-Hartman /* start the new firmware on the device */
8968ffdff6aSGreg Kroah-Hartman *tmp = 0; /* 7f92 to zero */
8978ffdff6aSGreg Kroah-Hartman ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
8988ffdff6aSGreg Kroah-Hartman USBDUXFASTSUB_FIRMWARE,
8998ffdff6aSGreg Kroah-Hartman VENDOR_DIR_OUT,
9008ffdff6aSGreg Kroah-Hartman USBDUXFASTSUB_CPUCS, 0x0000,
9018ffdff6aSGreg Kroah-Hartman tmp, 1,
9028ffdff6aSGreg Kroah-Hartman EZTIMEOUT);
9038ffdff6aSGreg Kroah-Hartman if (ret < 0)
9048ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "can not start firmware\n");
9058ffdff6aSGreg Kroah-Hartman
9068ffdff6aSGreg Kroah-Hartman done:
9078ffdff6aSGreg Kroah-Hartman kfree(tmp);
9088ffdff6aSGreg Kroah-Hartman kfree(buf);
9098ffdff6aSGreg Kroah-Hartman return ret;
9108ffdff6aSGreg Kroah-Hartman }
9118ffdff6aSGreg Kroah-Hartman
usbduxfast_auto_attach(struct comedi_device * dev,unsigned long context_unused)9128ffdff6aSGreg Kroah-Hartman static int usbduxfast_auto_attach(struct comedi_device *dev,
9138ffdff6aSGreg Kroah-Hartman unsigned long context_unused)
9148ffdff6aSGreg Kroah-Hartman {
9158ffdff6aSGreg Kroah-Hartman struct usb_interface *intf = comedi_to_usb_interface(dev);
9168ffdff6aSGreg Kroah-Hartman struct usb_device *usb = comedi_to_usb_dev(dev);
9178ffdff6aSGreg Kroah-Hartman struct usbduxfast_private *devpriv;
9188ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s;
9198ffdff6aSGreg Kroah-Hartman int ret;
9208ffdff6aSGreg Kroah-Hartman
9218ffdff6aSGreg Kroah-Hartman if (usb->speed != USB_SPEED_HIGH) {
9228ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev,
9238ffdff6aSGreg Kroah-Hartman "This driver needs USB 2.0 to operate. Aborting...\n");
9248ffdff6aSGreg Kroah-Hartman return -ENODEV;
9258ffdff6aSGreg Kroah-Hartman }
9268ffdff6aSGreg Kroah-Hartman
9278ffdff6aSGreg Kroah-Hartman devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
9288ffdff6aSGreg Kroah-Hartman if (!devpriv)
9298ffdff6aSGreg Kroah-Hartman return -ENOMEM;
9308ffdff6aSGreg Kroah-Hartman
9318ffdff6aSGreg Kroah-Hartman mutex_init(&devpriv->mut);
9328ffdff6aSGreg Kroah-Hartman usb_set_intfdata(intf, devpriv);
9338ffdff6aSGreg Kroah-Hartman
9348ffdff6aSGreg Kroah-Hartman devpriv->duxbuf = kmalloc(SIZEOFDUXBUF, GFP_KERNEL);
9358ffdff6aSGreg Kroah-Hartman if (!devpriv->duxbuf)
9368ffdff6aSGreg Kroah-Hartman return -ENOMEM;
9378ffdff6aSGreg Kroah-Hartman
9388ffdff6aSGreg Kroah-Hartman ret = usb_set_interface(usb,
9398ffdff6aSGreg Kroah-Hartman intf->altsetting->desc.bInterfaceNumber, 1);
9408ffdff6aSGreg Kroah-Hartman if (ret < 0) {
9418ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev,
9428ffdff6aSGreg Kroah-Hartman "could not switch to alternate setting 1\n");
9438ffdff6aSGreg Kroah-Hartman return -ENODEV;
9448ffdff6aSGreg Kroah-Hartman }
9458ffdff6aSGreg Kroah-Hartman
9468ffdff6aSGreg Kroah-Hartman devpriv->urb = usb_alloc_urb(0, GFP_KERNEL);
9478ffdff6aSGreg Kroah-Hartman if (!devpriv->urb)
9488ffdff6aSGreg Kroah-Hartman return -ENOMEM;
9498ffdff6aSGreg Kroah-Hartman
9508ffdff6aSGreg Kroah-Hartman devpriv->inbuf = kmalloc(SIZEINBUF, GFP_KERNEL);
9518ffdff6aSGreg Kroah-Hartman if (!devpriv->inbuf)
9528ffdff6aSGreg Kroah-Hartman return -ENOMEM;
9538ffdff6aSGreg Kroah-Hartman
9548ffdff6aSGreg Kroah-Hartman ret = comedi_load_firmware(dev, &usb->dev, FIRMWARE,
9558ffdff6aSGreg Kroah-Hartman usbduxfast_upload_firmware, 0);
9568ffdff6aSGreg Kroah-Hartman if (ret)
9578ffdff6aSGreg Kroah-Hartman return ret;
9588ffdff6aSGreg Kroah-Hartman
9598ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdevices(dev, 1);
9608ffdff6aSGreg Kroah-Hartman if (ret)
9618ffdff6aSGreg Kroah-Hartman return ret;
9628ffdff6aSGreg Kroah-Hartman
9638ffdff6aSGreg Kroah-Hartman /* Analog Input subdevice */
9648ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[0];
9658ffdff6aSGreg Kroah-Hartman dev->read_subdev = s;
9668ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AI;
9678ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
9688ffdff6aSGreg Kroah-Hartman s->n_chan = 16;
9698ffdff6aSGreg Kroah-Hartman s->maxdata = 0x1000; /* 12-bit + 1 overflow bit */
9708ffdff6aSGreg Kroah-Hartman s->range_table = &range_usbduxfast_ai_range;
9718ffdff6aSGreg Kroah-Hartman s->insn_read = usbduxfast_ai_insn_read;
9728ffdff6aSGreg Kroah-Hartman s->len_chanlist = s->n_chan;
9738ffdff6aSGreg Kroah-Hartman s->do_cmdtest = usbduxfast_ai_cmdtest;
9748ffdff6aSGreg Kroah-Hartman s->do_cmd = usbduxfast_ai_cmd;
9758ffdff6aSGreg Kroah-Hartman s->cancel = usbduxfast_ai_cancel;
9768ffdff6aSGreg Kroah-Hartman
9778ffdff6aSGreg Kroah-Hartman return 0;
9788ffdff6aSGreg Kroah-Hartman }
9798ffdff6aSGreg Kroah-Hartman
usbduxfast_detach(struct comedi_device * dev)9808ffdff6aSGreg Kroah-Hartman static void usbduxfast_detach(struct comedi_device *dev)
9818ffdff6aSGreg Kroah-Hartman {
9828ffdff6aSGreg Kroah-Hartman struct usb_interface *intf = comedi_to_usb_interface(dev);
9838ffdff6aSGreg Kroah-Hartman struct usbduxfast_private *devpriv = dev->private;
9848ffdff6aSGreg Kroah-Hartman
9858ffdff6aSGreg Kroah-Hartman if (!devpriv)
9868ffdff6aSGreg Kroah-Hartman return;
9878ffdff6aSGreg Kroah-Hartman
9888ffdff6aSGreg Kroah-Hartman mutex_lock(&devpriv->mut);
9898ffdff6aSGreg Kroah-Hartman
9908ffdff6aSGreg Kroah-Hartman usb_set_intfdata(intf, NULL);
9918ffdff6aSGreg Kroah-Hartman
9928ffdff6aSGreg Kroah-Hartman if (devpriv->urb) {
9938ffdff6aSGreg Kroah-Hartman /* waits until a running transfer is over */
9948ffdff6aSGreg Kroah-Hartman usb_kill_urb(devpriv->urb);
9958ffdff6aSGreg Kroah-Hartman
9968ffdff6aSGreg Kroah-Hartman kfree(devpriv->inbuf);
9978ffdff6aSGreg Kroah-Hartman usb_free_urb(devpriv->urb);
9988ffdff6aSGreg Kroah-Hartman }
9998ffdff6aSGreg Kroah-Hartman
10008ffdff6aSGreg Kroah-Hartman kfree(devpriv->duxbuf);
10018ffdff6aSGreg Kroah-Hartman
10028ffdff6aSGreg Kroah-Hartman mutex_unlock(&devpriv->mut);
10038ffdff6aSGreg Kroah-Hartman
10048ffdff6aSGreg Kroah-Hartman mutex_destroy(&devpriv->mut);
10058ffdff6aSGreg Kroah-Hartman }
10068ffdff6aSGreg Kroah-Hartman
10078ffdff6aSGreg Kroah-Hartman static struct comedi_driver usbduxfast_driver = {
10088ffdff6aSGreg Kroah-Hartman .driver_name = "usbduxfast",
10098ffdff6aSGreg Kroah-Hartman .module = THIS_MODULE,
10108ffdff6aSGreg Kroah-Hartman .auto_attach = usbduxfast_auto_attach,
10118ffdff6aSGreg Kroah-Hartman .detach = usbduxfast_detach,
10128ffdff6aSGreg Kroah-Hartman };
10138ffdff6aSGreg Kroah-Hartman
usbduxfast_usb_probe(struct usb_interface * intf,const struct usb_device_id * id)10148ffdff6aSGreg Kroah-Hartman static int usbduxfast_usb_probe(struct usb_interface *intf,
10158ffdff6aSGreg Kroah-Hartman const struct usb_device_id *id)
10168ffdff6aSGreg Kroah-Hartman {
10178ffdff6aSGreg Kroah-Hartman return comedi_usb_auto_config(intf, &usbduxfast_driver, 0);
10188ffdff6aSGreg Kroah-Hartman }
10198ffdff6aSGreg Kroah-Hartman
10208ffdff6aSGreg Kroah-Hartman static const struct usb_device_id usbduxfast_usb_table[] = {
10218ffdff6aSGreg Kroah-Hartman /* { USB_DEVICE(0x4b4, 0x8613) }, testing */
10228ffdff6aSGreg Kroah-Hartman { USB_DEVICE(0x13d8, 0x0010) }, /* real ID */
10238ffdff6aSGreg Kroah-Hartman { USB_DEVICE(0x13d8, 0x0011) }, /* real ID */
10248ffdff6aSGreg Kroah-Hartman { }
10258ffdff6aSGreg Kroah-Hartman };
10268ffdff6aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(usb, usbduxfast_usb_table);
10278ffdff6aSGreg Kroah-Hartman
10288ffdff6aSGreg Kroah-Hartman static struct usb_driver usbduxfast_usb_driver = {
10298ffdff6aSGreg Kroah-Hartman .name = "usbduxfast",
10308ffdff6aSGreg Kroah-Hartman .probe = usbduxfast_usb_probe,
10318ffdff6aSGreg Kroah-Hartman .disconnect = comedi_usb_auto_unconfig,
10328ffdff6aSGreg Kroah-Hartman .id_table = usbduxfast_usb_table,
10338ffdff6aSGreg Kroah-Hartman };
10348ffdff6aSGreg Kroah-Hartman module_comedi_usb_driver(usbduxfast_driver, usbduxfast_usb_driver);
10358ffdff6aSGreg Kroah-Hartman
10368ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
10378ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("USB-DUXfast, BerndPorr@f2s.com");
10388ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL");
10398ffdff6aSGreg Kroah-Hartman MODULE_FIRMWARE(FIRMWARE);
1040