xref: /linux/drivers/comedi/drivers/usbduxfast.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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