xref: /linux/drivers/comedi/drivers/usbduxfast.c (revision 8ffdff6a8cfbdc174a3a390b6f825a277b5bb895)
1*8ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
2*8ffdff6aSGreg Kroah-Hartman /*
3*8ffdff6aSGreg Kroah-Hartman  *  Copyright (C) 2004-2019 Bernd Porr, mail@berndporr.me.uk
4*8ffdff6aSGreg Kroah-Hartman  */
5*8ffdff6aSGreg Kroah-Hartman 
6*8ffdff6aSGreg Kroah-Hartman /*
7*8ffdff6aSGreg Kroah-Hartman  * Driver: usbduxfast
8*8ffdff6aSGreg Kroah-Hartman  * Description: University of Stirling USB DAQ & INCITE Technology Limited
9*8ffdff6aSGreg Kroah-Hartman  * Devices: [ITL] USB-DUX-FAST (usbduxfast)
10*8ffdff6aSGreg Kroah-Hartman  * Author: Bernd Porr <mail@berndporr.me.uk>
11*8ffdff6aSGreg Kroah-Hartman  * Updated: 16 Nov 2019
12*8ffdff6aSGreg Kroah-Hartman  * Status: stable
13*8ffdff6aSGreg Kroah-Hartman  */
14*8ffdff6aSGreg Kroah-Hartman 
15*8ffdff6aSGreg Kroah-Hartman /*
16*8ffdff6aSGreg Kroah-Hartman  * I must give credit here to Chris Baugher who
17*8ffdff6aSGreg Kroah-Hartman  * wrote the driver for AT-MIO-16d. I used some parts of this
18*8ffdff6aSGreg Kroah-Hartman  * driver. I also must give credits to David Brownell
19*8ffdff6aSGreg Kroah-Hartman  * who supported me with the USB development.
20*8ffdff6aSGreg Kroah-Hartman  *
21*8ffdff6aSGreg Kroah-Hartman  * Bernd Porr
22*8ffdff6aSGreg Kroah-Hartman  *
23*8ffdff6aSGreg Kroah-Hartman  *
24*8ffdff6aSGreg Kroah-Hartman  * Revision history:
25*8ffdff6aSGreg Kroah-Hartman  * 1.0: Fixed a rounding error in usbduxfast_ai_cmdtest
26*8ffdff6aSGreg Kroah-Hartman  * 0.9: Dropping the first data packet which seems to be from the last transfer.
27*8ffdff6aSGreg Kroah-Hartman  *      Buffer overflows in the FX2 are handed over to comedi.
28*8ffdff6aSGreg Kroah-Hartman  * 0.92: Dropping now 4 packets. The quad buffer has to be emptied.
29*8ffdff6aSGreg Kroah-Hartman  *       Added insn command basically for testing. Sample rate is
30*8ffdff6aSGreg Kroah-Hartman  *       1MHz/16ch=62.5kHz
31*8ffdff6aSGreg Kroah-Hartman  * 0.99: Ian Abbott pointed out a bug which has been corrected. Thanks!
32*8ffdff6aSGreg Kroah-Hartman  * 0.99a: added external trigger.
33*8ffdff6aSGreg Kroah-Hartman  * 1.00: added firmware kernel request to the driver which fixed
34*8ffdff6aSGreg Kroah-Hartman  *       udev coldplug problem
35*8ffdff6aSGreg Kroah-Hartman  */
36*8ffdff6aSGreg Kroah-Hartman 
37*8ffdff6aSGreg Kroah-Hartman #include <linux/kernel.h>
38*8ffdff6aSGreg Kroah-Hartman #include <linux/module.h>
39*8ffdff6aSGreg Kroah-Hartman #include <linux/slab.h>
40*8ffdff6aSGreg Kroah-Hartman #include <linux/input.h>
41*8ffdff6aSGreg Kroah-Hartman #include <linux/fcntl.h>
42*8ffdff6aSGreg Kroah-Hartman #include <linux/compiler.h>
43*8ffdff6aSGreg Kroah-Hartman #include "../comedi_usb.h"
44*8ffdff6aSGreg Kroah-Hartman 
45*8ffdff6aSGreg Kroah-Hartman /*
46*8ffdff6aSGreg Kroah-Hartman  * timeout for the USB-transfer
47*8ffdff6aSGreg Kroah-Hartman  */
48*8ffdff6aSGreg Kroah-Hartman #define EZTIMEOUT	30
49*8ffdff6aSGreg Kroah-Hartman 
50*8ffdff6aSGreg Kroah-Hartman /*
51*8ffdff6aSGreg Kroah-Hartman  * constants for "firmware" upload and download
52*8ffdff6aSGreg Kroah-Hartman  */
53*8ffdff6aSGreg Kroah-Hartman #define FIRMWARE		"usbduxfast_firmware.bin"
54*8ffdff6aSGreg Kroah-Hartman #define FIRMWARE_MAX_LEN	0x2000
55*8ffdff6aSGreg Kroah-Hartman #define USBDUXFASTSUB_FIRMWARE	0xA0
56*8ffdff6aSGreg Kroah-Hartman #define VENDOR_DIR_IN		0xC0
57*8ffdff6aSGreg Kroah-Hartman #define VENDOR_DIR_OUT		0x40
58*8ffdff6aSGreg Kroah-Hartman 
59*8ffdff6aSGreg Kroah-Hartman /*
60*8ffdff6aSGreg Kroah-Hartman  * internal addresses of the 8051 processor
61*8ffdff6aSGreg Kroah-Hartman  */
62*8ffdff6aSGreg Kroah-Hartman #define USBDUXFASTSUB_CPUCS	0xE600
63*8ffdff6aSGreg Kroah-Hartman 
64*8ffdff6aSGreg Kroah-Hartman /*
65*8ffdff6aSGreg Kroah-Hartman  * max length of the transfer-buffer for software upload
66*8ffdff6aSGreg Kroah-Hartman  */
67*8ffdff6aSGreg Kroah-Hartman #define TB_LEN	0x2000
68*8ffdff6aSGreg Kroah-Hartman 
69*8ffdff6aSGreg Kroah-Hartman /*
70*8ffdff6aSGreg Kroah-Hartman  * input endpoint number
71*8ffdff6aSGreg Kroah-Hartman  */
72*8ffdff6aSGreg Kroah-Hartman #define BULKINEP	6
73*8ffdff6aSGreg Kroah-Hartman 
74*8ffdff6aSGreg Kroah-Hartman /*
75*8ffdff6aSGreg Kroah-Hartman  * endpoint for the A/D channellist: bulk OUT
76*8ffdff6aSGreg Kroah-Hartman  */
77*8ffdff6aSGreg Kroah-Hartman #define CHANNELLISTEP	4
78*8ffdff6aSGreg Kroah-Hartman 
79*8ffdff6aSGreg Kroah-Hartman /*
80*8ffdff6aSGreg Kroah-Hartman  * number of channels
81*8ffdff6aSGreg Kroah-Hartman  */
82*8ffdff6aSGreg Kroah-Hartman #define NUMCHANNELS	32
83*8ffdff6aSGreg Kroah-Hartman 
84*8ffdff6aSGreg Kroah-Hartman /*
85*8ffdff6aSGreg Kroah-Hartman  * size of the waveform descriptor
86*8ffdff6aSGreg Kroah-Hartman  */
87*8ffdff6aSGreg Kroah-Hartman #define WAVESIZE	0x20
88*8ffdff6aSGreg Kroah-Hartman 
89*8ffdff6aSGreg Kroah-Hartman /*
90*8ffdff6aSGreg Kroah-Hartman  * size of one A/D value
91*8ffdff6aSGreg Kroah-Hartman  */
92*8ffdff6aSGreg Kroah-Hartman #define SIZEADIN	(sizeof(s16))
93*8ffdff6aSGreg Kroah-Hartman 
94*8ffdff6aSGreg Kroah-Hartman /*
95*8ffdff6aSGreg Kroah-Hartman  * size of the input-buffer IN BYTES
96*8ffdff6aSGreg Kroah-Hartman  */
97*8ffdff6aSGreg Kroah-Hartman #define SIZEINBUF	512
98*8ffdff6aSGreg Kroah-Hartman 
99*8ffdff6aSGreg Kroah-Hartman /*
100*8ffdff6aSGreg Kroah-Hartman  * 16 bytes
101*8ffdff6aSGreg Kroah-Hartman  */
102*8ffdff6aSGreg Kroah-Hartman #define SIZEINSNBUF	512
103*8ffdff6aSGreg Kroah-Hartman 
104*8ffdff6aSGreg Kroah-Hartman /*
105*8ffdff6aSGreg Kroah-Hartman  * size of the buffer for the dux commands in bytes
106*8ffdff6aSGreg Kroah-Hartman  */
107*8ffdff6aSGreg Kroah-Hartman #define SIZEOFDUXBUF	256
108*8ffdff6aSGreg Kroah-Hartman 
109*8ffdff6aSGreg Kroah-Hartman /*
110*8ffdff6aSGreg Kroah-Hartman  * number of in-URBs which receive the data: min=5
111*8ffdff6aSGreg Kroah-Hartman  */
112*8ffdff6aSGreg Kroah-Hartman #define NUMOFINBUFFERSHIGH	10
113*8ffdff6aSGreg Kroah-Hartman 
114*8ffdff6aSGreg Kroah-Hartman /*
115*8ffdff6aSGreg Kroah-Hartman  * min delay steps for more than one channel
116*8ffdff6aSGreg Kroah-Hartman  * basically when the mux gives up ;-)
117*8ffdff6aSGreg Kroah-Hartman  *
118*8ffdff6aSGreg Kroah-Hartman  * steps at 30MHz in the FX2
119*8ffdff6aSGreg Kroah-Hartman  */
120*8ffdff6aSGreg Kroah-Hartman #define MIN_SAMPLING_PERIOD	9
121*8ffdff6aSGreg Kroah-Hartman 
122*8ffdff6aSGreg Kroah-Hartman /*
123*8ffdff6aSGreg Kroah-Hartman  * max number of 1/30MHz delay steps
124*8ffdff6aSGreg Kroah-Hartman  */
125*8ffdff6aSGreg Kroah-Hartman #define MAX_SAMPLING_PERIOD	500
126*8ffdff6aSGreg Kroah-Hartman 
127*8ffdff6aSGreg Kroah-Hartman /*
128*8ffdff6aSGreg Kroah-Hartman  * number of received packets to ignore before we start handing data
129*8ffdff6aSGreg Kroah-Hartman  * over to comedi, it's quad buffering and we have to ignore 4 packets
130*8ffdff6aSGreg Kroah-Hartman  */
131*8ffdff6aSGreg Kroah-Hartman #define PACKETS_TO_IGNORE	4
132*8ffdff6aSGreg Kroah-Hartman 
133*8ffdff6aSGreg Kroah-Hartman /*
134*8ffdff6aSGreg Kroah-Hartman  * comedi constants
135*8ffdff6aSGreg Kroah-Hartman  */
136*8ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange range_usbduxfast_ai_range = {
137*8ffdff6aSGreg Kroah-Hartman 	2, {
138*8ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(0.75),
139*8ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(0.5)
140*8ffdff6aSGreg Kroah-Hartman 	}
141*8ffdff6aSGreg Kroah-Hartman };
142*8ffdff6aSGreg Kroah-Hartman 
143*8ffdff6aSGreg Kroah-Hartman /*
144*8ffdff6aSGreg Kroah-Hartman  * private structure of one subdevice
145*8ffdff6aSGreg Kroah-Hartman  *
146*8ffdff6aSGreg Kroah-Hartman  * this is the structure which holds all the data of this driver
147*8ffdff6aSGreg Kroah-Hartman  * one sub device just now: A/D
148*8ffdff6aSGreg Kroah-Hartman  */
149*8ffdff6aSGreg Kroah-Hartman struct usbduxfast_private {
150*8ffdff6aSGreg Kroah-Hartman 	struct urb *urb;	/* BULK-transfer handling: urb */
151*8ffdff6aSGreg Kroah-Hartman 	u8 *duxbuf;
152*8ffdff6aSGreg Kroah-Hartman 	s8 *inbuf;
153*8ffdff6aSGreg Kroah-Hartman 	short int ai_cmd_running;	/* asynchronous command is running */
154*8ffdff6aSGreg Kroah-Hartman 	int ignore;		/* counter which ignores the first buffers */
155*8ffdff6aSGreg Kroah-Hartman 	struct mutex mut;
156*8ffdff6aSGreg Kroah-Hartman };
157*8ffdff6aSGreg Kroah-Hartman 
158*8ffdff6aSGreg Kroah-Hartman /*
159*8ffdff6aSGreg Kroah-Hartman  * bulk transfers to usbduxfast
160*8ffdff6aSGreg Kroah-Hartman  */
161*8ffdff6aSGreg Kroah-Hartman #define SENDADCOMMANDS            0
162*8ffdff6aSGreg Kroah-Hartman #define SENDINITEP6               1
163*8ffdff6aSGreg Kroah-Hartman 
164*8ffdff6aSGreg Kroah-Hartman static int usbduxfast_send_cmd(struct comedi_device *dev, int cmd_type)
165*8ffdff6aSGreg Kroah-Hartman {
166*8ffdff6aSGreg Kroah-Hartman 	struct usb_device *usb = comedi_to_usb_dev(dev);
167*8ffdff6aSGreg Kroah-Hartman 	struct usbduxfast_private *devpriv = dev->private;
168*8ffdff6aSGreg Kroah-Hartman 	int nsent;
169*8ffdff6aSGreg Kroah-Hartman 	int ret;
170*8ffdff6aSGreg Kroah-Hartman 
171*8ffdff6aSGreg Kroah-Hartman 	devpriv->duxbuf[0] = cmd_type;
172*8ffdff6aSGreg Kroah-Hartman 
173*8ffdff6aSGreg Kroah-Hartman 	ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, CHANNELLISTEP),
174*8ffdff6aSGreg Kroah-Hartman 			   devpriv->duxbuf, SIZEOFDUXBUF,
175*8ffdff6aSGreg Kroah-Hartman 			   &nsent, 10000);
176*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0)
177*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev,
178*8ffdff6aSGreg Kroah-Hartman 			"could not transmit command to the usb-device, err=%d\n",
179*8ffdff6aSGreg Kroah-Hartman 			ret);
180*8ffdff6aSGreg Kroah-Hartman 	return ret;
181*8ffdff6aSGreg Kroah-Hartman }
182*8ffdff6aSGreg Kroah-Hartman 
183*8ffdff6aSGreg Kroah-Hartman static void usbduxfast_cmd_data(struct comedi_device *dev, int index,
184*8ffdff6aSGreg Kroah-Hartman 				u8 len, u8 op, u8 out, u8 log)
185*8ffdff6aSGreg Kroah-Hartman {
186*8ffdff6aSGreg Kroah-Hartman 	struct usbduxfast_private *devpriv = dev->private;
187*8ffdff6aSGreg Kroah-Hartman 
188*8ffdff6aSGreg Kroah-Hartman 	/* Set the GPIF bytes, the first byte is the command byte */
189*8ffdff6aSGreg Kroah-Hartman 	devpriv->duxbuf[1 + 0x00 + index] = len;
190*8ffdff6aSGreg Kroah-Hartman 	devpriv->duxbuf[1 + 0x08 + index] = op;
191*8ffdff6aSGreg Kroah-Hartman 	devpriv->duxbuf[1 + 0x10 + index] = out;
192*8ffdff6aSGreg Kroah-Hartman 	devpriv->duxbuf[1 + 0x18 + index] = log;
193*8ffdff6aSGreg Kroah-Hartman }
194*8ffdff6aSGreg Kroah-Hartman 
195*8ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_stop(struct comedi_device *dev, int do_unlink)
196*8ffdff6aSGreg Kroah-Hartman {
197*8ffdff6aSGreg Kroah-Hartman 	struct usbduxfast_private *devpriv = dev->private;
198*8ffdff6aSGreg Kroah-Hartman 
199*8ffdff6aSGreg Kroah-Hartman 	/* stop aquistion */
200*8ffdff6aSGreg Kroah-Hartman 	devpriv->ai_cmd_running = 0;
201*8ffdff6aSGreg Kroah-Hartman 
202*8ffdff6aSGreg Kroah-Hartman 	if (do_unlink && devpriv->urb) {
203*8ffdff6aSGreg Kroah-Hartman 		/* kill the running transfer */
204*8ffdff6aSGreg Kroah-Hartman 		usb_kill_urb(devpriv->urb);
205*8ffdff6aSGreg Kroah-Hartman 	}
206*8ffdff6aSGreg Kroah-Hartman 
207*8ffdff6aSGreg Kroah-Hartman 	return 0;
208*8ffdff6aSGreg Kroah-Hartman }
209*8ffdff6aSGreg Kroah-Hartman 
210*8ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_cancel(struct comedi_device *dev,
211*8ffdff6aSGreg Kroah-Hartman 				struct comedi_subdevice *s)
212*8ffdff6aSGreg Kroah-Hartman {
213*8ffdff6aSGreg Kroah-Hartman 	struct usbduxfast_private *devpriv = dev->private;
214*8ffdff6aSGreg Kroah-Hartman 	int ret;
215*8ffdff6aSGreg Kroah-Hartman 
216*8ffdff6aSGreg Kroah-Hartman 	mutex_lock(&devpriv->mut);
217*8ffdff6aSGreg Kroah-Hartman 	ret = usbduxfast_ai_stop(dev, 1);
218*8ffdff6aSGreg Kroah-Hartman 	mutex_unlock(&devpriv->mut);
219*8ffdff6aSGreg Kroah-Hartman 
220*8ffdff6aSGreg Kroah-Hartman 	return ret;
221*8ffdff6aSGreg Kroah-Hartman }
222*8ffdff6aSGreg Kroah-Hartman 
223*8ffdff6aSGreg Kroah-Hartman static void usbduxfast_ai_handle_urb(struct comedi_device *dev,
224*8ffdff6aSGreg Kroah-Hartman 				     struct comedi_subdevice *s,
225*8ffdff6aSGreg Kroah-Hartman 				     struct urb *urb)
226*8ffdff6aSGreg Kroah-Hartman {
227*8ffdff6aSGreg Kroah-Hartman 	struct usbduxfast_private *devpriv = dev->private;
228*8ffdff6aSGreg Kroah-Hartman 	struct comedi_async *async = s->async;
229*8ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &async->cmd;
230*8ffdff6aSGreg Kroah-Hartman 	int ret;
231*8ffdff6aSGreg Kroah-Hartman 
232*8ffdff6aSGreg Kroah-Hartman 	if (devpriv->ignore) {
233*8ffdff6aSGreg Kroah-Hartman 		devpriv->ignore--;
234*8ffdff6aSGreg Kroah-Hartman 	} else {
235*8ffdff6aSGreg Kroah-Hartman 		unsigned int nsamples;
236*8ffdff6aSGreg Kroah-Hartman 
237*8ffdff6aSGreg Kroah-Hartman 		nsamples = comedi_bytes_to_samples(s, urb->actual_length);
238*8ffdff6aSGreg Kroah-Hartman 		nsamples = comedi_nsamples_left(s, nsamples);
239*8ffdff6aSGreg Kroah-Hartman 		comedi_buf_write_samples(s, urb->transfer_buffer, nsamples);
240*8ffdff6aSGreg Kroah-Hartman 
241*8ffdff6aSGreg Kroah-Hartman 		if (cmd->stop_src == TRIG_COUNT &&
242*8ffdff6aSGreg Kroah-Hartman 		    async->scans_done >= cmd->stop_arg)
243*8ffdff6aSGreg Kroah-Hartman 			async->events |= COMEDI_CB_EOA;
244*8ffdff6aSGreg Kroah-Hartman 	}
245*8ffdff6aSGreg Kroah-Hartman 
246*8ffdff6aSGreg Kroah-Hartman 	/* if command is still running, resubmit urb for BULK transfer */
247*8ffdff6aSGreg Kroah-Hartman 	if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
248*8ffdff6aSGreg Kroah-Hartman 		urb->dev = comedi_to_usb_dev(dev);
249*8ffdff6aSGreg Kroah-Hartman 		urb->status = 0;
250*8ffdff6aSGreg Kroah-Hartman 		ret = usb_submit_urb(urb, GFP_ATOMIC);
251*8ffdff6aSGreg Kroah-Hartman 		if (ret < 0) {
252*8ffdff6aSGreg Kroah-Hartman 			dev_err(dev->class_dev, "urb resubm failed: %d", ret);
253*8ffdff6aSGreg Kroah-Hartman 			async->events |= COMEDI_CB_ERROR;
254*8ffdff6aSGreg Kroah-Hartman 		}
255*8ffdff6aSGreg Kroah-Hartman 	}
256*8ffdff6aSGreg Kroah-Hartman }
257*8ffdff6aSGreg Kroah-Hartman 
258*8ffdff6aSGreg Kroah-Hartman static void usbduxfast_ai_interrupt(struct urb *urb)
259*8ffdff6aSGreg Kroah-Hartman {
260*8ffdff6aSGreg Kroah-Hartman 	struct comedi_device *dev = urb->context;
261*8ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s = dev->read_subdev;
262*8ffdff6aSGreg Kroah-Hartman 	struct comedi_async *async = s->async;
263*8ffdff6aSGreg Kroah-Hartman 	struct usbduxfast_private *devpriv = dev->private;
264*8ffdff6aSGreg Kroah-Hartman 
265*8ffdff6aSGreg Kroah-Hartman 	/* exit if not running a command, do not resubmit urb */
266*8ffdff6aSGreg Kroah-Hartman 	if (!devpriv->ai_cmd_running)
267*8ffdff6aSGreg Kroah-Hartman 		return;
268*8ffdff6aSGreg Kroah-Hartman 
269*8ffdff6aSGreg Kroah-Hartman 	switch (urb->status) {
270*8ffdff6aSGreg Kroah-Hartman 	case 0:
271*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_ai_handle_urb(dev, s, urb);
272*8ffdff6aSGreg Kroah-Hartman 		break;
273*8ffdff6aSGreg Kroah-Hartman 
274*8ffdff6aSGreg Kroah-Hartman 	case -ECONNRESET:
275*8ffdff6aSGreg Kroah-Hartman 	case -ENOENT:
276*8ffdff6aSGreg Kroah-Hartman 	case -ESHUTDOWN:
277*8ffdff6aSGreg Kroah-Hartman 	case -ECONNABORTED:
278*8ffdff6aSGreg Kroah-Hartman 		/* after an unlink command, unplug, ... etc */
279*8ffdff6aSGreg Kroah-Hartman 		async->events |= COMEDI_CB_ERROR;
280*8ffdff6aSGreg Kroah-Hartman 		break;
281*8ffdff6aSGreg Kroah-Hartman 
282*8ffdff6aSGreg Kroah-Hartman 	default:
283*8ffdff6aSGreg Kroah-Hartman 		/* a real error */
284*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev,
285*8ffdff6aSGreg Kroah-Hartman 			"non-zero urb status received in ai intr context: %d\n",
286*8ffdff6aSGreg Kroah-Hartman 			urb->status);
287*8ffdff6aSGreg Kroah-Hartman 		async->events |= COMEDI_CB_ERROR;
288*8ffdff6aSGreg Kroah-Hartman 		break;
289*8ffdff6aSGreg Kroah-Hartman 	}
290*8ffdff6aSGreg Kroah-Hartman 
291*8ffdff6aSGreg Kroah-Hartman 	/*
292*8ffdff6aSGreg Kroah-Hartman 	 * comedi_handle_events() cannot be used in this driver. The (*cancel)
293*8ffdff6aSGreg Kroah-Hartman 	 * operation would unlink the urb.
294*8ffdff6aSGreg Kroah-Hartman 	 */
295*8ffdff6aSGreg Kroah-Hartman 	if (async->events & COMEDI_CB_CANCEL_MASK)
296*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_ai_stop(dev, 0);
297*8ffdff6aSGreg Kroah-Hartman 
298*8ffdff6aSGreg Kroah-Hartman 	comedi_event(dev, s);
299*8ffdff6aSGreg Kroah-Hartman }
300*8ffdff6aSGreg Kroah-Hartman 
301*8ffdff6aSGreg Kroah-Hartman static int usbduxfast_submit_urb(struct comedi_device *dev)
302*8ffdff6aSGreg Kroah-Hartman {
303*8ffdff6aSGreg Kroah-Hartman 	struct usb_device *usb = comedi_to_usb_dev(dev);
304*8ffdff6aSGreg Kroah-Hartman 	struct usbduxfast_private *devpriv = dev->private;
305*8ffdff6aSGreg Kroah-Hartman 	int ret;
306*8ffdff6aSGreg Kroah-Hartman 
307*8ffdff6aSGreg Kroah-Hartman 	usb_fill_bulk_urb(devpriv->urb, usb, usb_rcvbulkpipe(usb, BULKINEP),
308*8ffdff6aSGreg Kroah-Hartman 			  devpriv->inbuf, SIZEINBUF,
309*8ffdff6aSGreg Kroah-Hartman 			  usbduxfast_ai_interrupt, dev);
310*8ffdff6aSGreg Kroah-Hartman 
311*8ffdff6aSGreg Kroah-Hartman 	ret = usb_submit_urb(devpriv->urb, GFP_ATOMIC);
312*8ffdff6aSGreg Kroah-Hartman 	if (ret) {
313*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev, "usb_submit_urb error %d\n", ret);
314*8ffdff6aSGreg Kroah-Hartman 		return ret;
315*8ffdff6aSGreg Kroah-Hartman 	}
316*8ffdff6aSGreg Kroah-Hartman 	return 0;
317*8ffdff6aSGreg Kroah-Hartman }
318*8ffdff6aSGreg Kroah-Hartman 
319*8ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_check_chanlist(struct comedi_device *dev,
320*8ffdff6aSGreg Kroah-Hartman 					struct comedi_subdevice *s,
321*8ffdff6aSGreg Kroah-Hartman 					struct comedi_cmd *cmd)
322*8ffdff6aSGreg Kroah-Hartman {
323*8ffdff6aSGreg Kroah-Hartman 	unsigned int gain0 = CR_RANGE(cmd->chanlist[0]);
324*8ffdff6aSGreg Kroah-Hartman 	int i;
325*8ffdff6aSGreg Kroah-Hartman 
326*8ffdff6aSGreg Kroah-Hartman 	if (cmd->chanlist_len > 3 && cmd->chanlist_len != 16) {
327*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev, "unsupported combination of channels\n");
328*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
329*8ffdff6aSGreg Kroah-Hartman 	}
330*8ffdff6aSGreg Kroah-Hartman 
331*8ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < cmd->chanlist_len; ++i) {
332*8ffdff6aSGreg Kroah-Hartman 		unsigned int chan = CR_CHAN(cmd->chanlist[i]);
333*8ffdff6aSGreg Kroah-Hartman 		unsigned int gain = CR_RANGE(cmd->chanlist[i]);
334*8ffdff6aSGreg Kroah-Hartman 
335*8ffdff6aSGreg Kroah-Hartman 		if (chan != i) {
336*8ffdff6aSGreg Kroah-Hartman 			dev_err(dev->class_dev,
337*8ffdff6aSGreg Kroah-Hartman 				"channels are not consecutive\n");
338*8ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
339*8ffdff6aSGreg Kroah-Hartman 		}
340*8ffdff6aSGreg Kroah-Hartman 		if (gain != gain0 && cmd->chanlist_len > 3) {
341*8ffdff6aSGreg Kroah-Hartman 			dev_err(dev->class_dev,
342*8ffdff6aSGreg Kroah-Hartman 				"gain must be the same for all channels\n");
343*8ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
344*8ffdff6aSGreg Kroah-Hartman 		}
345*8ffdff6aSGreg Kroah-Hartman 	}
346*8ffdff6aSGreg Kroah-Hartman 	return 0;
347*8ffdff6aSGreg Kroah-Hartman }
348*8ffdff6aSGreg Kroah-Hartman 
349*8ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_cmdtest(struct comedi_device *dev,
350*8ffdff6aSGreg Kroah-Hartman 				 struct comedi_subdevice *s,
351*8ffdff6aSGreg Kroah-Hartman 				 struct comedi_cmd *cmd)
352*8ffdff6aSGreg Kroah-Hartman {
353*8ffdff6aSGreg Kroah-Hartman 	int err = 0;
354*8ffdff6aSGreg Kroah-Hartman 	int err2 = 0;
355*8ffdff6aSGreg Kroah-Hartman 	unsigned int steps;
356*8ffdff6aSGreg Kroah-Hartman 	unsigned int arg;
357*8ffdff6aSGreg Kroah-Hartman 
358*8ffdff6aSGreg Kroah-Hartman 	/* Step 1 : check if triggers are trivially valid */
359*8ffdff6aSGreg Kroah-Hartman 
360*8ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->start_src,
361*8ffdff6aSGreg Kroah-Hartman 					TRIG_NOW | TRIG_EXT | TRIG_INT);
362*8ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
363*8ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
364*8ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
365*8ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
366*8ffdff6aSGreg Kroah-Hartman 
367*8ffdff6aSGreg Kroah-Hartman 	if (err)
368*8ffdff6aSGreg Kroah-Hartman 		return 1;
369*8ffdff6aSGreg Kroah-Hartman 
370*8ffdff6aSGreg Kroah-Hartman 	/* Step 2a : make sure trigger sources are unique */
371*8ffdff6aSGreg Kroah-Hartman 
372*8ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_is_unique(cmd->start_src);
373*8ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_is_unique(cmd->stop_src);
374*8ffdff6aSGreg Kroah-Hartman 
375*8ffdff6aSGreg Kroah-Hartman 	/* Step 2b : and mutually compatible */
376*8ffdff6aSGreg Kroah-Hartman 
377*8ffdff6aSGreg Kroah-Hartman 	if (err)
378*8ffdff6aSGreg Kroah-Hartman 		return 2;
379*8ffdff6aSGreg Kroah-Hartman 
380*8ffdff6aSGreg Kroah-Hartman 	/* Step 3: check if arguments are trivially valid */
381*8ffdff6aSGreg Kroah-Hartman 
382*8ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
383*8ffdff6aSGreg Kroah-Hartman 
384*8ffdff6aSGreg Kroah-Hartman 	if (!cmd->chanlist_len)
385*8ffdff6aSGreg Kroah-Hartman 		err |= -EINVAL;
386*8ffdff6aSGreg Kroah-Hartman 
387*8ffdff6aSGreg Kroah-Hartman 	/* external start trigger is only valid for 1 or 16 channels */
388*8ffdff6aSGreg Kroah-Hartman 	if (cmd->start_src == TRIG_EXT &&
389*8ffdff6aSGreg Kroah-Hartman 	    cmd->chanlist_len != 1 && cmd->chanlist_len != 16)
390*8ffdff6aSGreg Kroah-Hartman 		err |= -EINVAL;
391*8ffdff6aSGreg Kroah-Hartman 
392*8ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
393*8ffdff6aSGreg Kroah-Hartman 					   cmd->chanlist_len);
394*8ffdff6aSGreg Kroah-Hartman 
395*8ffdff6aSGreg Kroah-Hartman 	/*
396*8ffdff6aSGreg Kroah-Hartman 	 * Validate the conversion timing:
397*8ffdff6aSGreg Kroah-Hartman 	 * for 1 channel the timing in 30MHz "steps" is:
398*8ffdff6aSGreg Kroah-Hartman 	 *	steps <= MAX_SAMPLING_PERIOD
399*8ffdff6aSGreg Kroah-Hartman 	 * for all other chanlist_len it is:
400*8ffdff6aSGreg Kroah-Hartman 	 *	MIN_SAMPLING_PERIOD <= steps <= MAX_SAMPLING_PERIOD
401*8ffdff6aSGreg Kroah-Hartman 	 */
402*8ffdff6aSGreg Kroah-Hartman 	steps = (cmd->convert_arg * 30) / 1000;
403*8ffdff6aSGreg Kroah-Hartman 	if (cmd->chanlist_len !=  1)
404*8ffdff6aSGreg Kroah-Hartman 		err2 |= comedi_check_trigger_arg_min(&steps,
405*8ffdff6aSGreg Kroah-Hartman 						     MIN_SAMPLING_PERIOD);
406*8ffdff6aSGreg Kroah-Hartman 	else
407*8ffdff6aSGreg Kroah-Hartman 		err2 |= comedi_check_trigger_arg_min(&steps, 1);
408*8ffdff6aSGreg Kroah-Hartman 	err2 |= comedi_check_trigger_arg_max(&steps, MAX_SAMPLING_PERIOD);
409*8ffdff6aSGreg Kroah-Hartman 	if (err2) {
410*8ffdff6aSGreg Kroah-Hartman 		err |= err2;
411*8ffdff6aSGreg Kroah-Hartman 		arg = (steps * 1000) / 30;
412*8ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
413*8ffdff6aSGreg Kroah-Hartman 	}
414*8ffdff6aSGreg Kroah-Hartman 
415*8ffdff6aSGreg Kroah-Hartman 	if (cmd->stop_src == TRIG_COUNT)
416*8ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
417*8ffdff6aSGreg Kroah-Hartman 	else	/* TRIG_NONE */
418*8ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
419*8ffdff6aSGreg Kroah-Hartman 
420*8ffdff6aSGreg Kroah-Hartman 	if (err)
421*8ffdff6aSGreg Kroah-Hartman 		return 3;
422*8ffdff6aSGreg Kroah-Hartman 
423*8ffdff6aSGreg Kroah-Hartman 	/* Step 4: fix up any arguments */
424*8ffdff6aSGreg Kroah-Hartman 
425*8ffdff6aSGreg Kroah-Hartman 	/* Step 5: check channel list if it exists */
426*8ffdff6aSGreg Kroah-Hartman 	if (cmd->chanlist && cmd->chanlist_len > 0)
427*8ffdff6aSGreg Kroah-Hartman 		err |= usbduxfast_ai_check_chanlist(dev, s, cmd);
428*8ffdff6aSGreg Kroah-Hartman 	if (err)
429*8ffdff6aSGreg Kroah-Hartman 		return 5;
430*8ffdff6aSGreg Kroah-Hartman 
431*8ffdff6aSGreg Kroah-Hartman 	return 0;
432*8ffdff6aSGreg Kroah-Hartman }
433*8ffdff6aSGreg Kroah-Hartman 
434*8ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_inttrig(struct comedi_device *dev,
435*8ffdff6aSGreg Kroah-Hartman 				 struct comedi_subdevice *s,
436*8ffdff6aSGreg Kroah-Hartman 				 unsigned int trig_num)
437*8ffdff6aSGreg Kroah-Hartman {
438*8ffdff6aSGreg Kroah-Hartman 	struct usbduxfast_private *devpriv = dev->private;
439*8ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &s->async->cmd;
440*8ffdff6aSGreg Kroah-Hartman 	int ret;
441*8ffdff6aSGreg Kroah-Hartman 
442*8ffdff6aSGreg Kroah-Hartman 	if (trig_num != cmd->start_arg)
443*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
444*8ffdff6aSGreg Kroah-Hartman 
445*8ffdff6aSGreg Kroah-Hartman 	mutex_lock(&devpriv->mut);
446*8ffdff6aSGreg Kroah-Hartman 
447*8ffdff6aSGreg Kroah-Hartman 	if (!devpriv->ai_cmd_running) {
448*8ffdff6aSGreg Kroah-Hartman 		devpriv->ai_cmd_running = 1;
449*8ffdff6aSGreg Kroah-Hartman 		ret = usbduxfast_submit_urb(dev);
450*8ffdff6aSGreg Kroah-Hartman 		if (ret < 0) {
451*8ffdff6aSGreg Kroah-Hartman 			dev_err(dev->class_dev, "urbSubmit: err=%d\n", ret);
452*8ffdff6aSGreg Kroah-Hartman 			devpriv->ai_cmd_running = 0;
453*8ffdff6aSGreg Kroah-Hartman 			mutex_unlock(&devpriv->mut);
454*8ffdff6aSGreg Kroah-Hartman 			return ret;
455*8ffdff6aSGreg Kroah-Hartman 		}
456*8ffdff6aSGreg Kroah-Hartman 		s->async->inttrig = NULL;
457*8ffdff6aSGreg Kroah-Hartman 	} else {
458*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev, "ai is already running\n");
459*8ffdff6aSGreg Kroah-Hartman 	}
460*8ffdff6aSGreg Kroah-Hartman 	mutex_unlock(&devpriv->mut);
461*8ffdff6aSGreg Kroah-Hartman 	return 1;
462*8ffdff6aSGreg Kroah-Hartman }
463*8ffdff6aSGreg Kroah-Hartman 
464*8ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_cmd(struct comedi_device *dev,
465*8ffdff6aSGreg Kroah-Hartman 			     struct comedi_subdevice *s)
466*8ffdff6aSGreg Kroah-Hartman {
467*8ffdff6aSGreg Kroah-Hartman 	struct usbduxfast_private *devpriv = dev->private;
468*8ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &s->async->cmd;
469*8ffdff6aSGreg Kroah-Hartman 	unsigned int rngmask = 0xff;
470*8ffdff6aSGreg Kroah-Hartman 	int j, ret;
471*8ffdff6aSGreg Kroah-Hartman 	long steps, steps_tmp;
472*8ffdff6aSGreg Kroah-Hartman 
473*8ffdff6aSGreg Kroah-Hartman 	mutex_lock(&devpriv->mut);
474*8ffdff6aSGreg Kroah-Hartman 	if (devpriv->ai_cmd_running) {
475*8ffdff6aSGreg Kroah-Hartman 		ret = -EBUSY;
476*8ffdff6aSGreg Kroah-Hartman 		goto cmd_exit;
477*8ffdff6aSGreg Kroah-Hartman 	}
478*8ffdff6aSGreg Kroah-Hartman 
479*8ffdff6aSGreg Kroah-Hartman 	/*
480*8ffdff6aSGreg Kroah-Hartman 	 * ignore the first buffers from the device if there
481*8ffdff6aSGreg Kroah-Hartman 	 * is an error condition
482*8ffdff6aSGreg Kroah-Hartman 	 */
483*8ffdff6aSGreg Kroah-Hartman 	devpriv->ignore = PACKETS_TO_IGNORE;
484*8ffdff6aSGreg Kroah-Hartman 
485*8ffdff6aSGreg Kroah-Hartman 	steps = (cmd->convert_arg * 30) / 1000;
486*8ffdff6aSGreg Kroah-Hartman 
487*8ffdff6aSGreg Kroah-Hartman 	switch (cmd->chanlist_len) {
488*8ffdff6aSGreg Kroah-Hartman 	case 1:
489*8ffdff6aSGreg Kroah-Hartman 		/*
490*8ffdff6aSGreg Kroah-Hartman 		 * one channel
491*8ffdff6aSGreg Kroah-Hartman 		 */
492*8ffdff6aSGreg Kroah-Hartman 
493*8ffdff6aSGreg Kroah-Hartman 		if (CR_RANGE(cmd->chanlist[0]) > 0)
494*8ffdff6aSGreg Kroah-Hartman 			rngmask = 0xff - 0x04;
495*8ffdff6aSGreg Kroah-Hartman 		else
496*8ffdff6aSGreg Kroah-Hartman 			rngmask = 0xff;
497*8ffdff6aSGreg Kroah-Hartman 
498*8ffdff6aSGreg Kroah-Hartman 		/*
499*8ffdff6aSGreg Kroah-Hartman 		 * for external trigger: looping in this state until
500*8ffdff6aSGreg Kroah-Hartman 		 * the RDY0 pin becomes zero
501*8ffdff6aSGreg Kroah-Hartman 		 */
502*8ffdff6aSGreg Kroah-Hartman 
503*8ffdff6aSGreg Kroah-Hartman 		/* we loop here until ready has been set */
504*8ffdff6aSGreg Kroah-Hartman 		if (cmd->start_src == TRIG_EXT) {
505*8ffdff6aSGreg Kroah-Hartman 			/* branch back to state 0 */
506*8ffdff6aSGreg Kroah-Hartman 			/* deceision state w/o data */
507*8ffdff6aSGreg Kroah-Hartman 			/* RDY0 = 0 */
508*8ffdff6aSGreg Kroah-Hartman 			usbduxfast_cmd_data(dev, 0, 0x01, 0x01, rngmask, 0x00);
509*8ffdff6aSGreg Kroah-Hartman 		} else {	/* we just proceed to state 1 */
510*8ffdff6aSGreg Kroah-Hartman 			usbduxfast_cmd_data(dev, 0, 0x01, 0x00, rngmask, 0x00);
511*8ffdff6aSGreg Kroah-Hartman 		}
512*8ffdff6aSGreg Kroah-Hartman 
513*8ffdff6aSGreg Kroah-Hartman 		if (steps < MIN_SAMPLING_PERIOD) {
514*8ffdff6aSGreg Kroah-Hartman 			/* for fast single channel aqu without mux */
515*8ffdff6aSGreg Kroah-Hartman 			if (steps <= 1) {
516*8ffdff6aSGreg Kroah-Hartman 				/*
517*8ffdff6aSGreg Kroah-Hartman 				 * we just stay here at state 1 and rexecute
518*8ffdff6aSGreg Kroah-Hartman 				 * the same state this gives us 30MHz sampling
519*8ffdff6aSGreg Kroah-Hartman 				 * rate
520*8ffdff6aSGreg Kroah-Hartman 				 */
521*8ffdff6aSGreg Kroah-Hartman 
522*8ffdff6aSGreg Kroah-Hartman 				/* branch back to state 1 */
523*8ffdff6aSGreg Kroah-Hartman 				/* deceision state with data */
524*8ffdff6aSGreg Kroah-Hartman 				/* doesn't matter */
525*8ffdff6aSGreg Kroah-Hartman 				usbduxfast_cmd_data(dev, 1,
526*8ffdff6aSGreg Kroah-Hartman 						    0x89, 0x03, rngmask, 0xff);
527*8ffdff6aSGreg Kroah-Hartman 			} else {
528*8ffdff6aSGreg Kroah-Hartman 				/*
529*8ffdff6aSGreg Kroah-Hartman 				 * we loop through two states: data and delay
530*8ffdff6aSGreg Kroah-Hartman 				 * max rate is 15MHz
531*8ffdff6aSGreg Kroah-Hartman 				 */
532*8ffdff6aSGreg Kroah-Hartman 				/* data */
533*8ffdff6aSGreg Kroah-Hartman 				/* doesn't matter */
534*8ffdff6aSGreg Kroah-Hartman 				usbduxfast_cmd_data(dev, 1, steps - 1,
535*8ffdff6aSGreg Kroah-Hartman 						    0x02, rngmask, 0x00);
536*8ffdff6aSGreg Kroah-Hartman 
537*8ffdff6aSGreg Kroah-Hartman 				/* branch back to state 1 */
538*8ffdff6aSGreg Kroah-Hartman 				/* deceision state w/o data */
539*8ffdff6aSGreg Kroah-Hartman 				/* doesn't matter */
540*8ffdff6aSGreg Kroah-Hartman 				usbduxfast_cmd_data(dev, 2,
541*8ffdff6aSGreg Kroah-Hartman 						    0x09, 0x01, rngmask, 0xff);
542*8ffdff6aSGreg Kroah-Hartman 			}
543*8ffdff6aSGreg Kroah-Hartman 		} else {
544*8ffdff6aSGreg Kroah-Hartman 			/*
545*8ffdff6aSGreg Kroah-Hartman 			 * we loop through 3 states: 2x delay and 1x data
546*8ffdff6aSGreg Kroah-Hartman 			 * this gives a min sampling rate of 60kHz
547*8ffdff6aSGreg Kroah-Hartman 			 */
548*8ffdff6aSGreg Kroah-Hartman 
549*8ffdff6aSGreg Kroah-Hartman 			/* we have 1 state with duration 1 */
550*8ffdff6aSGreg Kroah-Hartman 			steps = steps - 1;
551*8ffdff6aSGreg Kroah-Hartman 
552*8ffdff6aSGreg Kroah-Hartman 			/* do the first part of the delay */
553*8ffdff6aSGreg Kroah-Hartman 			usbduxfast_cmd_data(dev, 1,
554*8ffdff6aSGreg Kroah-Hartman 					    steps / 2, 0x00, rngmask, 0x00);
555*8ffdff6aSGreg Kroah-Hartman 
556*8ffdff6aSGreg Kroah-Hartman 			/* and the second part */
557*8ffdff6aSGreg Kroah-Hartman 			usbduxfast_cmd_data(dev, 2, steps - steps / 2,
558*8ffdff6aSGreg Kroah-Hartman 					    0x00, rngmask, 0x00);
559*8ffdff6aSGreg Kroah-Hartman 
560*8ffdff6aSGreg Kroah-Hartman 			/* get the data and branch back */
561*8ffdff6aSGreg Kroah-Hartman 
562*8ffdff6aSGreg Kroah-Hartman 			/* branch back to state 1 */
563*8ffdff6aSGreg Kroah-Hartman 			/* deceision state w data */
564*8ffdff6aSGreg Kroah-Hartman 			/* doesn't matter */
565*8ffdff6aSGreg Kroah-Hartman 			usbduxfast_cmd_data(dev, 3,
566*8ffdff6aSGreg Kroah-Hartman 					    0x09, 0x03, rngmask, 0xff);
567*8ffdff6aSGreg Kroah-Hartman 		}
568*8ffdff6aSGreg Kroah-Hartman 		break;
569*8ffdff6aSGreg Kroah-Hartman 
570*8ffdff6aSGreg Kroah-Hartman 	case 2:
571*8ffdff6aSGreg Kroah-Hartman 		/*
572*8ffdff6aSGreg Kroah-Hartman 		 * two channels
573*8ffdff6aSGreg Kroah-Hartman 		 * commit data to the FIFO
574*8ffdff6aSGreg Kroah-Hartman 		 */
575*8ffdff6aSGreg Kroah-Hartman 
576*8ffdff6aSGreg Kroah-Hartman 		if (CR_RANGE(cmd->chanlist[0]) > 0)
577*8ffdff6aSGreg Kroah-Hartman 			rngmask = 0xff - 0x04;
578*8ffdff6aSGreg Kroah-Hartman 		else
579*8ffdff6aSGreg Kroah-Hartman 			rngmask = 0xff;
580*8ffdff6aSGreg Kroah-Hartman 
581*8ffdff6aSGreg Kroah-Hartman 		/* data */
582*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
583*8ffdff6aSGreg Kroah-Hartman 
584*8ffdff6aSGreg Kroah-Hartman 		/* we have 1 state with duration 1: state 0 */
585*8ffdff6aSGreg Kroah-Hartman 		steps_tmp = steps - 1;
586*8ffdff6aSGreg Kroah-Hartman 
587*8ffdff6aSGreg Kroah-Hartman 		if (CR_RANGE(cmd->chanlist[1]) > 0)
588*8ffdff6aSGreg Kroah-Hartman 			rngmask = 0xff - 0x04;
589*8ffdff6aSGreg Kroah-Hartman 		else
590*8ffdff6aSGreg Kroah-Hartman 			rngmask = 0xff;
591*8ffdff6aSGreg Kroah-Hartman 
592*8ffdff6aSGreg Kroah-Hartman 		/* do the first part of the delay */
593*8ffdff6aSGreg Kroah-Hartman 		/* count */
594*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 1, steps_tmp / 2,
595*8ffdff6aSGreg Kroah-Hartman 				    0x00, 0xfe & rngmask, 0x00);
596*8ffdff6aSGreg Kroah-Hartman 
597*8ffdff6aSGreg Kroah-Hartman 		/* and the second part */
598*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 2, steps_tmp  - steps_tmp / 2,
599*8ffdff6aSGreg Kroah-Hartman 				    0x00, rngmask, 0x00);
600*8ffdff6aSGreg Kroah-Hartman 
601*8ffdff6aSGreg Kroah-Hartman 		/* data */
602*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 3, 0x01, 0x02, rngmask, 0x00);
603*8ffdff6aSGreg Kroah-Hartman 
604*8ffdff6aSGreg Kroah-Hartman 		/*
605*8ffdff6aSGreg Kroah-Hartman 		 * we have 2 states with duration 1: step 6 and
606*8ffdff6aSGreg Kroah-Hartman 		 * the IDLE state
607*8ffdff6aSGreg Kroah-Hartman 		 */
608*8ffdff6aSGreg Kroah-Hartman 		steps_tmp = steps - 2;
609*8ffdff6aSGreg Kroah-Hartman 
610*8ffdff6aSGreg Kroah-Hartman 		if (CR_RANGE(cmd->chanlist[0]) > 0)
611*8ffdff6aSGreg Kroah-Hartman 			rngmask = 0xff - 0x04;
612*8ffdff6aSGreg Kroah-Hartman 		else
613*8ffdff6aSGreg Kroah-Hartman 			rngmask = 0xff;
614*8ffdff6aSGreg Kroah-Hartman 
615*8ffdff6aSGreg Kroah-Hartman 		/* do the first part of the delay */
616*8ffdff6aSGreg Kroah-Hartman 		/* reset */
617*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 4, steps_tmp / 2,
618*8ffdff6aSGreg Kroah-Hartman 				    0x00, (0xff - 0x02) & rngmask, 0x00);
619*8ffdff6aSGreg Kroah-Hartman 
620*8ffdff6aSGreg Kroah-Hartman 		/* and the second part */
621*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
622*8ffdff6aSGreg Kroah-Hartman 				    0x00, rngmask, 0x00);
623*8ffdff6aSGreg Kroah-Hartman 
624*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
625*8ffdff6aSGreg Kroah-Hartman 		break;
626*8ffdff6aSGreg Kroah-Hartman 
627*8ffdff6aSGreg Kroah-Hartman 	case 3:
628*8ffdff6aSGreg Kroah-Hartman 		/*
629*8ffdff6aSGreg Kroah-Hartman 		 * three channels
630*8ffdff6aSGreg Kroah-Hartman 		 */
631*8ffdff6aSGreg Kroah-Hartman 		for (j = 0; j < 1; j++) {
632*8ffdff6aSGreg Kroah-Hartman 			int index = j * 2;
633*8ffdff6aSGreg Kroah-Hartman 
634*8ffdff6aSGreg Kroah-Hartman 			if (CR_RANGE(cmd->chanlist[j]) > 0)
635*8ffdff6aSGreg Kroah-Hartman 				rngmask = 0xff - 0x04;
636*8ffdff6aSGreg Kroah-Hartman 			else
637*8ffdff6aSGreg Kroah-Hartman 				rngmask = 0xff;
638*8ffdff6aSGreg Kroah-Hartman 			/*
639*8ffdff6aSGreg Kroah-Hartman 			 * commit data to the FIFO and do the first part
640*8ffdff6aSGreg Kroah-Hartman 			 * of the delay
641*8ffdff6aSGreg Kroah-Hartman 			 */
642*8ffdff6aSGreg Kroah-Hartman 			/* data */
643*8ffdff6aSGreg Kroah-Hartman 			/* no change */
644*8ffdff6aSGreg Kroah-Hartman 			usbduxfast_cmd_data(dev, index, steps / 2,
645*8ffdff6aSGreg Kroah-Hartman 					    0x02, rngmask, 0x00);
646*8ffdff6aSGreg Kroah-Hartman 
647*8ffdff6aSGreg Kroah-Hartman 			if (CR_RANGE(cmd->chanlist[j + 1]) > 0)
648*8ffdff6aSGreg Kroah-Hartman 				rngmask = 0xff - 0x04;
649*8ffdff6aSGreg Kroah-Hartman 			else
650*8ffdff6aSGreg Kroah-Hartman 				rngmask = 0xff;
651*8ffdff6aSGreg Kroah-Hartman 
652*8ffdff6aSGreg Kroah-Hartman 			/* do the second part of the delay */
653*8ffdff6aSGreg Kroah-Hartman 			/* no data */
654*8ffdff6aSGreg Kroah-Hartman 			/* count */
655*8ffdff6aSGreg Kroah-Hartman 			usbduxfast_cmd_data(dev, index + 1, steps - steps / 2,
656*8ffdff6aSGreg Kroah-Hartman 					    0x00, 0xfe & rngmask, 0x00);
657*8ffdff6aSGreg Kroah-Hartman 		}
658*8ffdff6aSGreg Kroah-Hartman 
659*8ffdff6aSGreg Kroah-Hartman 		/* 2 steps with duration 1: the idele step and step 6: */
660*8ffdff6aSGreg Kroah-Hartman 		steps_tmp = steps - 2;
661*8ffdff6aSGreg Kroah-Hartman 
662*8ffdff6aSGreg Kroah-Hartman 		/* commit data to the FIFO and do the first part of the delay */
663*8ffdff6aSGreg Kroah-Hartman 		/* data */
664*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 4, steps_tmp / 2,
665*8ffdff6aSGreg Kroah-Hartman 				    0x02, rngmask, 0x00);
666*8ffdff6aSGreg Kroah-Hartman 
667*8ffdff6aSGreg Kroah-Hartman 		if (CR_RANGE(cmd->chanlist[0]) > 0)
668*8ffdff6aSGreg Kroah-Hartman 			rngmask = 0xff - 0x04;
669*8ffdff6aSGreg Kroah-Hartman 		else
670*8ffdff6aSGreg Kroah-Hartman 			rngmask = 0xff;
671*8ffdff6aSGreg Kroah-Hartman 
672*8ffdff6aSGreg Kroah-Hartman 		/* do the second part of the delay */
673*8ffdff6aSGreg Kroah-Hartman 		/* no data */
674*8ffdff6aSGreg Kroah-Hartman 		/* reset */
675*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
676*8ffdff6aSGreg Kroah-Hartman 				    0x00, (0xff - 0x02) & rngmask, 0x00);
677*8ffdff6aSGreg Kroah-Hartman 
678*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
679*8ffdff6aSGreg Kroah-Hartman 		break;
680*8ffdff6aSGreg Kroah-Hartman 
681*8ffdff6aSGreg Kroah-Hartman 	case 16:
682*8ffdff6aSGreg Kroah-Hartman 		if (CR_RANGE(cmd->chanlist[0]) > 0)
683*8ffdff6aSGreg Kroah-Hartman 			rngmask = 0xff - 0x04;
684*8ffdff6aSGreg Kroah-Hartman 		else
685*8ffdff6aSGreg Kroah-Hartman 			rngmask = 0xff;
686*8ffdff6aSGreg Kroah-Hartman 
687*8ffdff6aSGreg Kroah-Hartman 		if (cmd->start_src == TRIG_EXT) {
688*8ffdff6aSGreg Kroah-Hartman 			/*
689*8ffdff6aSGreg Kroah-Hartman 			 * we loop here until ready has been set
690*8ffdff6aSGreg Kroah-Hartman 			 */
691*8ffdff6aSGreg Kroah-Hartman 
692*8ffdff6aSGreg Kroah-Hartman 			/* branch back to state 0 */
693*8ffdff6aSGreg Kroah-Hartman 			/* deceision state w/o data */
694*8ffdff6aSGreg Kroah-Hartman 			/* reset */
695*8ffdff6aSGreg Kroah-Hartman 			/* RDY0 = 0 */
696*8ffdff6aSGreg Kroah-Hartman 			usbduxfast_cmd_data(dev, 0, 0x01, 0x01,
697*8ffdff6aSGreg Kroah-Hartman 					    (0xff - 0x02) & rngmask, 0x00);
698*8ffdff6aSGreg Kroah-Hartman 		} else {
699*8ffdff6aSGreg Kroah-Hartman 			/*
700*8ffdff6aSGreg Kroah-Hartman 			 * we just proceed to state 1
701*8ffdff6aSGreg Kroah-Hartman 			 */
702*8ffdff6aSGreg Kroah-Hartman 
703*8ffdff6aSGreg Kroah-Hartman 			/* 30us reset pulse */
704*8ffdff6aSGreg Kroah-Hartman 			/* reset */
705*8ffdff6aSGreg Kroah-Hartman 			usbduxfast_cmd_data(dev, 0, 0xff, 0x00,
706*8ffdff6aSGreg Kroah-Hartman 					    (0xff - 0x02) & rngmask, 0x00);
707*8ffdff6aSGreg Kroah-Hartman 		}
708*8ffdff6aSGreg Kroah-Hartman 
709*8ffdff6aSGreg Kroah-Hartman 		/* commit data to the FIFO */
710*8ffdff6aSGreg Kroah-Hartman 		/* data */
711*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 1, 0x01, 0x02, rngmask, 0x00);
712*8ffdff6aSGreg Kroah-Hartman 
713*8ffdff6aSGreg Kroah-Hartman 		/* we have 2 states with duration 1 */
714*8ffdff6aSGreg Kroah-Hartman 		steps = steps - 2;
715*8ffdff6aSGreg Kroah-Hartman 
716*8ffdff6aSGreg Kroah-Hartman 		/* do the first part of the delay */
717*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 2, steps / 2,
718*8ffdff6aSGreg Kroah-Hartman 				    0x00, 0xfe & rngmask, 0x00);
719*8ffdff6aSGreg Kroah-Hartman 
720*8ffdff6aSGreg Kroah-Hartman 		/* and the second part */
721*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 3, steps - steps / 2,
722*8ffdff6aSGreg Kroah-Hartman 				    0x00, rngmask, 0x00);
723*8ffdff6aSGreg Kroah-Hartman 
724*8ffdff6aSGreg Kroah-Hartman 		/* branch back to state 1 */
725*8ffdff6aSGreg Kroah-Hartman 		/* deceision state w/o data */
726*8ffdff6aSGreg Kroah-Hartman 		/* doesn't matter */
727*8ffdff6aSGreg Kroah-Hartman 		usbduxfast_cmd_data(dev, 4, 0x09, 0x01, rngmask, 0xff);
728*8ffdff6aSGreg Kroah-Hartman 
729*8ffdff6aSGreg Kroah-Hartman 		break;
730*8ffdff6aSGreg Kroah-Hartman 	}
731*8ffdff6aSGreg Kroah-Hartman 
732*8ffdff6aSGreg Kroah-Hartman 	/* 0 means that the AD commands are sent */
733*8ffdff6aSGreg Kroah-Hartman 	ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS);
734*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0)
735*8ffdff6aSGreg Kroah-Hartman 		goto cmd_exit;
736*8ffdff6aSGreg Kroah-Hartman 
737*8ffdff6aSGreg Kroah-Hartman 	if ((cmd->start_src == TRIG_NOW) || (cmd->start_src == TRIG_EXT)) {
738*8ffdff6aSGreg Kroah-Hartman 		/* enable this acquisition operation */
739*8ffdff6aSGreg Kroah-Hartman 		devpriv->ai_cmd_running = 1;
740*8ffdff6aSGreg Kroah-Hartman 		ret = usbduxfast_submit_urb(dev);
741*8ffdff6aSGreg Kroah-Hartman 		if (ret < 0) {
742*8ffdff6aSGreg Kroah-Hartman 			devpriv->ai_cmd_running = 0;
743*8ffdff6aSGreg Kroah-Hartman 			/* fixme: unlink here?? */
744*8ffdff6aSGreg Kroah-Hartman 			goto cmd_exit;
745*8ffdff6aSGreg Kroah-Hartman 		}
746*8ffdff6aSGreg Kroah-Hartman 		s->async->inttrig = NULL;
747*8ffdff6aSGreg Kroah-Hartman 	} else {	/* TRIG_INT */
748*8ffdff6aSGreg Kroah-Hartman 		s->async->inttrig = usbduxfast_ai_inttrig;
749*8ffdff6aSGreg Kroah-Hartman 	}
750*8ffdff6aSGreg Kroah-Hartman 
751*8ffdff6aSGreg Kroah-Hartman cmd_exit:
752*8ffdff6aSGreg Kroah-Hartman 	mutex_unlock(&devpriv->mut);
753*8ffdff6aSGreg Kroah-Hartman 
754*8ffdff6aSGreg Kroah-Hartman 	return ret;
755*8ffdff6aSGreg Kroah-Hartman }
756*8ffdff6aSGreg Kroah-Hartman 
757*8ffdff6aSGreg Kroah-Hartman /*
758*8ffdff6aSGreg Kroah-Hartman  * Mode 0 is used to get a single conversion on demand.
759*8ffdff6aSGreg Kroah-Hartman  */
760*8ffdff6aSGreg Kroah-Hartman static int usbduxfast_ai_insn_read(struct comedi_device *dev,
761*8ffdff6aSGreg Kroah-Hartman 				   struct comedi_subdevice *s,
762*8ffdff6aSGreg Kroah-Hartman 				   struct comedi_insn *insn,
763*8ffdff6aSGreg Kroah-Hartman 				   unsigned int *data)
764*8ffdff6aSGreg Kroah-Hartman {
765*8ffdff6aSGreg Kroah-Hartman 	struct usb_device *usb = comedi_to_usb_dev(dev);
766*8ffdff6aSGreg Kroah-Hartman 	struct usbduxfast_private *devpriv = dev->private;
767*8ffdff6aSGreg Kroah-Hartman 	unsigned int chan = CR_CHAN(insn->chanspec);
768*8ffdff6aSGreg Kroah-Hartman 	unsigned int range = CR_RANGE(insn->chanspec);
769*8ffdff6aSGreg Kroah-Hartman 	u8 rngmask = range ? (0xff - 0x04) : 0xff;
770*8ffdff6aSGreg Kroah-Hartman 	int i, j, n, actual_length;
771*8ffdff6aSGreg Kroah-Hartman 	int ret;
772*8ffdff6aSGreg Kroah-Hartman 
773*8ffdff6aSGreg Kroah-Hartman 	mutex_lock(&devpriv->mut);
774*8ffdff6aSGreg Kroah-Hartman 
775*8ffdff6aSGreg Kroah-Hartman 	if (devpriv->ai_cmd_running) {
776*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev,
777*8ffdff6aSGreg Kroah-Hartman 			"ai_insn_read not possible, async cmd is running\n");
778*8ffdff6aSGreg Kroah-Hartman 		mutex_unlock(&devpriv->mut);
779*8ffdff6aSGreg Kroah-Hartman 		return -EBUSY;
780*8ffdff6aSGreg Kroah-Hartman 	}
781*8ffdff6aSGreg Kroah-Hartman 
782*8ffdff6aSGreg Kroah-Hartman 	/* set command for the first channel */
783*8ffdff6aSGreg Kroah-Hartman 
784*8ffdff6aSGreg Kroah-Hartman 	/* commit data to the FIFO */
785*8ffdff6aSGreg Kroah-Hartman 	/* data */
786*8ffdff6aSGreg Kroah-Hartman 	usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
787*8ffdff6aSGreg Kroah-Hartman 
788*8ffdff6aSGreg Kroah-Hartman 	/* do the first part of the delay */
789*8ffdff6aSGreg Kroah-Hartman 	usbduxfast_cmd_data(dev, 1, 0x0c, 0x00, 0xfe & rngmask, 0x00);
790*8ffdff6aSGreg Kroah-Hartman 	usbduxfast_cmd_data(dev, 2, 0x01, 0x00, 0xfe & rngmask, 0x00);
791*8ffdff6aSGreg Kroah-Hartman 	usbduxfast_cmd_data(dev, 3, 0x01, 0x00, 0xfe & rngmask, 0x00);
792*8ffdff6aSGreg Kroah-Hartman 	usbduxfast_cmd_data(dev, 4, 0x01, 0x00, 0xfe & rngmask, 0x00);
793*8ffdff6aSGreg Kroah-Hartman 
794*8ffdff6aSGreg Kroah-Hartman 	/* second part */
795*8ffdff6aSGreg Kroah-Hartman 	usbduxfast_cmd_data(dev, 5, 0x0c, 0x00, rngmask, 0x00);
796*8ffdff6aSGreg Kroah-Hartman 	usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
797*8ffdff6aSGreg Kroah-Hartman 
798*8ffdff6aSGreg Kroah-Hartman 	ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS);
799*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0) {
800*8ffdff6aSGreg Kroah-Hartman 		mutex_unlock(&devpriv->mut);
801*8ffdff6aSGreg Kroah-Hartman 		return ret;
802*8ffdff6aSGreg Kroah-Hartman 	}
803*8ffdff6aSGreg Kroah-Hartman 
804*8ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < PACKETS_TO_IGNORE; i++) {
805*8ffdff6aSGreg Kroah-Hartman 		ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
806*8ffdff6aSGreg Kroah-Hartman 				   devpriv->inbuf, SIZEINBUF,
807*8ffdff6aSGreg Kroah-Hartman 				   &actual_length, 10000);
808*8ffdff6aSGreg Kroah-Hartman 		if (ret < 0) {
809*8ffdff6aSGreg Kroah-Hartman 			dev_err(dev->class_dev, "insn timeout, no data\n");
810*8ffdff6aSGreg Kroah-Hartman 			mutex_unlock(&devpriv->mut);
811*8ffdff6aSGreg Kroah-Hartman 			return ret;
812*8ffdff6aSGreg Kroah-Hartman 		}
813*8ffdff6aSGreg Kroah-Hartman 	}
814*8ffdff6aSGreg Kroah-Hartman 
815*8ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < insn->n;) {
816*8ffdff6aSGreg Kroah-Hartman 		ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
817*8ffdff6aSGreg Kroah-Hartman 				   devpriv->inbuf, SIZEINBUF,
818*8ffdff6aSGreg Kroah-Hartman 				   &actual_length, 10000);
819*8ffdff6aSGreg Kroah-Hartman 		if (ret < 0) {
820*8ffdff6aSGreg Kroah-Hartman 			dev_err(dev->class_dev, "insn data error: %d\n", ret);
821*8ffdff6aSGreg Kroah-Hartman 			mutex_unlock(&devpriv->mut);
822*8ffdff6aSGreg Kroah-Hartman 			return ret;
823*8ffdff6aSGreg Kroah-Hartman 		}
824*8ffdff6aSGreg Kroah-Hartman 		n = actual_length / sizeof(u16);
825*8ffdff6aSGreg Kroah-Hartman 		if ((n % 16) != 0) {
826*8ffdff6aSGreg Kroah-Hartman 			dev_err(dev->class_dev, "insn data packet corrupted\n");
827*8ffdff6aSGreg Kroah-Hartman 			mutex_unlock(&devpriv->mut);
828*8ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
829*8ffdff6aSGreg Kroah-Hartman 		}
830*8ffdff6aSGreg Kroah-Hartman 		for (j = chan; (j < n) && (i < insn->n); j = j + 16) {
831*8ffdff6aSGreg Kroah-Hartman 			data[i] = ((u16 *)(devpriv->inbuf))[j];
832*8ffdff6aSGreg Kroah-Hartman 			i++;
833*8ffdff6aSGreg Kroah-Hartman 		}
834*8ffdff6aSGreg Kroah-Hartman 	}
835*8ffdff6aSGreg Kroah-Hartman 
836*8ffdff6aSGreg Kroah-Hartman 	mutex_unlock(&devpriv->mut);
837*8ffdff6aSGreg Kroah-Hartman 
838*8ffdff6aSGreg Kroah-Hartman 	return insn->n;
839*8ffdff6aSGreg Kroah-Hartman }
840*8ffdff6aSGreg Kroah-Hartman 
841*8ffdff6aSGreg Kroah-Hartman static int usbduxfast_upload_firmware(struct comedi_device *dev,
842*8ffdff6aSGreg Kroah-Hartman 				      const u8 *data, size_t size,
843*8ffdff6aSGreg Kroah-Hartman 				      unsigned long context)
844*8ffdff6aSGreg Kroah-Hartman {
845*8ffdff6aSGreg Kroah-Hartman 	struct usb_device *usb = comedi_to_usb_dev(dev);
846*8ffdff6aSGreg Kroah-Hartman 	u8 *buf;
847*8ffdff6aSGreg Kroah-Hartman 	unsigned char *tmp;
848*8ffdff6aSGreg Kroah-Hartman 	int ret;
849*8ffdff6aSGreg Kroah-Hartman 
850*8ffdff6aSGreg Kroah-Hartman 	if (!data)
851*8ffdff6aSGreg Kroah-Hartman 		return 0;
852*8ffdff6aSGreg Kroah-Hartman 
853*8ffdff6aSGreg Kroah-Hartman 	if (size > FIRMWARE_MAX_LEN) {
854*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev, "firmware binary too large for FX2\n");
855*8ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
856*8ffdff6aSGreg Kroah-Hartman 	}
857*8ffdff6aSGreg Kroah-Hartman 
858*8ffdff6aSGreg Kroah-Hartman 	/* we generate a local buffer for the firmware */
859*8ffdff6aSGreg Kroah-Hartman 	buf = kmemdup(data, size, GFP_KERNEL);
860*8ffdff6aSGreg Kroah-Hartman 	if (!buf)
861*8ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
862*8ffdff6aSGreg Kroah-Hartman 
863*8ffdff6aSGreg Kroah-Hartman 	/* we need a malloc'ed buffer for usb_control_msg() */
864*8ffdff6aSGreg Kroah-Hartman 	tmp = kmalloc(1, GFP_KERNEL);
865*8ffdff6aSGreg Kroah-Hartman 	if (!tmp) {
866*8ffdff6aSGreg Kroah-Hartman 		kfree(buf);
867*8ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
868*8ffdff6aSGreg Kroah-Hartman 	}
869*8ffdff6aSGreg Kroah-Hartman 
870*8ffdff6aSGreg Kroah-Hartman 	/* stop the current firmware on the device */
871*8ffdff6aSGreg Kroah-Hartman 	*tmp = 1;	/* 7f92 to one */
872*8ffdff6aSGreg Kroah-Hartman 	ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
873*8ffdff6aSGreg Kroah-Hartman 			      USBDUXFASTSUB_FIRMWARE,
874*8ffdff6aSGreg Kroah-Hartman 			      VENDOR_DIR_OUT,
875*8ffdff6aSGreg Kroah-Hartman 			      USBDUXFASTSUB_CPUCS, 0x0000,
876*8ffdff6aSGreg Kroah-Hartman 			      tmp, 1,
877*8ffdff6aSGreg Kroah-Hartman 			      EZTIMEOUT);
878*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0) {
879*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev, "can not stop firmware\n");
880*8ffdff6aSGreg Kroah-Hartman 		goto done;
881*8ffdff6aSGreg Kroah-Hartman 	}
882*8ffdff6aSGreg Kroah-Hartman 
883*8ffdff6aSGreg Kroah-Hartman 	/* upload the new firmware to the device */
884*8ffdff6aSGreg Kroah-Hartman 	ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
885*8ffdff6aSGreg Kroah-Hartman 			      USBDUXFASTSUB_FIRMWARE,
886*8ffdff6aSGreg Kroah-Hartman 			      VENDOR_DIR_OUT,
887*8ffdff6aSGreg Kroah-Hartman 			      0, 0x0000,
888*8ffdff6aSGreg Kroah-Hartman 			      buf, size,
889*8ffdff6aSGreg Kroah-Hartman 			      EZTIMEOUT);
890*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0) {
891*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev, "firmware upload failed\n");
892*8ffdff6aSGreg Kroah-Hartman 		goto done;
893*8ffdff6aSGreg Kroah-Hartman 	}
894*8ffdff6aSGreg Kroah-Hartman 
895*8ffdff6aSGreg Kroah-Hartman 	/* start the new firmware on the device */
896*8ffdff6aSGreg Kroah-Hartman 	*tmp = 0;	/* 7f92 to zero */
897*8ffdff6aSGreg Kroah-Hartman 	ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
898*8ffdff6aSGreg Kroah-Hartman 			      USBDUXFASTSUB_FIRMWARE,
899*8ffdff6aSGreg Kroah-Hartman 			      VENDOR_DIR_OUT,
900*8ffdff6aSGreg Kroah-Hartman 			      USBDUXFASTSUB_CPUCS, 0x0000,
901*8ffdff6aSGreg Kroah-Hartman 			      tmp, 1,
902*8ffdff6aSGreg Kroah-Hartman 			      EZTIMEOUT);
903*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0)
904*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev, "can not start firmware\n");
905*8ffdff6aSGreg Kroah-Hartman 
906*8ffdff6aSGreg Kroah-Hartman done:
907*8ffdff6aSGreg Kroah-Hartman 	kfree(tmp);
908*8ffdff6aSGreg Kroah-Hartman 	kfree(buf);
909*8ffdff6aSGreg Kroah-Hartman 	return ret;
910*8ffdff6aSGreg Kroah-Hartman }
911*8ffdff6aSGreg Kroah-Hartman 
912*8ffdff6aSGreg Kroah-Hartman static int usbduxfast_auto_attach(struct comedi_device *dev,
913*8ffdff6aSGreg Kroah-Hartman 				  unsigned long context_unused)
914*8ffdff6aSGreg Kroah-Hartman {
915*8ffdff6aSGreg Kroah-Hartman 	struct usb_interface *intf = comedi_to_usb_interface(dev);
916*8ffdff6aSGreg Kroah-Hartman 	struct usb_device *usb = comedi_to_usb_dev(dev);
917*8ffdff6aSGreg Kroah-Hartman 	struct usbduxfast_private *devpriv;
918*8ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s;
919*8ffdff6aSGreg Kroah-Hartman 	int ret;
920*8ffdff6aSGreg Kroah-Hartman 
921*8ffdff6aSGreg Kroah-Hartman 	if (usb->speed != USB_SPEED_HIGH) {
922*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev,
923*8ffdff6aSGreg Kroah-Hartman 			"This driver needs USB 2.0 to operate. Aborting...\n");
924*8ffdff6aSGreg Kroah-Hartman 		return -ENODEV;
925*8ffdff6aSGreg Kroah-Hartman 	}
926*8ffdff6aSGreg Kroah-Hartman 
927*8ffdff6aSGreg Kroah-Hartman 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
928*8ffdff6aSGreg Kroah-Hartman 	if (!devpriv)
929*8ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
930*8ffdff6aSGreg Kroah-Hartman 
931*8ffdff6aSGreg Kroah-Hartman 	mutex_init(&devpriv->mut);
932*8ffdff6aSGreg Kroah-Hartman 	usb_set_intfdata(intf, devpriv);
933*8ffdff6aSGreg Kroah-Hartman 
934*8ffdff6aSGreg Kroah-Hartman 	devpriv->duxbuf = kmalloc(SIZEOFDUXBUF, GFP_KERNEL);
935*8ffdff6aSGreg Kroah-Hartman 	if (!devpriv->duxbuf)
936*8ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
937*8ffdff6aSGreg Kroah-Hartman 
938*8ffdff6aSGreg Kroah-Hartman 	ret = usb_set_interface(usb,
939*8ffdff6aSGreg Kroah-Hartman 				intf->altsetting->desc.bInterfaceNumber, 1);
940*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0) {
941*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev,
942*8ffdff6aSGreg Kroah-Hartman 			"could not switch to alternate setting 1\n");
943*8ffdff6aSGreg Kroah-Hartman 		return -ENODEV;
944*8ffdff6aSGreg Kroah-Hartman 	}
945*8ffdff6aSGreg Kroah-Hartman 
946*8ffdff6aSGreg Kroah-Hartman 	devpriv->urb = usb_alloc_urb(0, GFP_KERNEL);
947*8ffdff6aSGreg Kroah-Hartman 	if (!devpriv->urb)
948*8ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
949*8ffdff6aSGreg Kroah-Hartman 
950*8ffdff6aSGreg Kroah-Hartman 	devpriv->inbuf = kmalloc(SIZEINBUF, GFP_KERNEL);
951*8ffdff6aSGreg Kroah-Hartman 	if (!devpriv->inbuf)
952*8ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
953*8ffdff6aSGreg Kroah-Hartman 
954*8ffdff6aSGreg Kroah-Hartman 	ret = comedi_load_firmware(dev, &usb->dev, FIRMWARE,
955*8ffdff6aSGreg Kroah-Hartman 				   usbduxfast_upload_firmware, 0);
956*8ffdff6aSGreg Kroah-Hartman 	if (ret)
957*8ffdff6aSGreg Kroah-Hartman 		return ret;
958*8ffdff6aSGreg Kroah-Hartman 
959*8ffdff6aSGreg Kroah-Hartman 	ret = comedi_alloc_subdevices(dev, 1);
960*8ffdff6aSGreg Kroah-Hartman 	if (ret)
961*8ffdff6aSGreg Kroah-Hartman 		return ret;
962*8ffdff6aSGreg Kroah-Hartman 
963*8ffdff6aSGreg Kroah-Hartman 	/* Analog Input subdevice */
964*8ffdff6aSGreg Kroah-Hartman 	s = &dev->subdevices[0];
965*8ffdff6aSGreg Kroah-Hartman 	dev->read_subdev = s;
966*8ffdff6aSGreg Kroah-Hartman 	s->type		= COMEDI_SUBD_AI;
967*8ffdff6aSGreg Kroah-Hartman 	s->subdev_flags	= SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
968*8ffdff6aSGreg Kroah-Hartman 	s->n_chan	= 16;
969*8ffdff6aSGreg Kroah-Hartman 	s->maxdata	= 0x1000;	/* 12-bit + 1 overflow bit */
970*8ffdff6aSGreg Kroah-Hartman 	s->range_table	= &range_usbduxfast_ai_range;
971*8ffdff6aSGreg Kroah-Hartman 	s->insn_read	= usbduxfast_ai_insn_read;
972*8ffdff6aSGreg Kroah-Hartman 	s->len_chanlist	= s->n_chan;
973*8ffdff6aSGreg Kroah-Hartman 	s->do_cmdtest	= usbduxfast_ai_cmdtest;
974*8ffdff6aSGreg Kroah-Hartman 	s->do_cmd	= usbduxfast_ai_cmd;
975*8ffdff6aSGreg Kroah-Hartman 	s->cancel	= usbduxfast_ai_cancel;
976*8ffdff6aSGreg Kroah-Hartman 
977*8ffdff6aSGreg Kroah-Hartman 	return 0;
978*8ffdff6aSGreg Kroah-Hartman }
979*8ffdff6aSGreg Kroah-Hartman 
980*8ffdff6aSGreg Kroah-Hartman static void usbduxfast_detach(struct comedi_device *dev)
981*8ffdff6aSGreg Kroah-Hartman {
982*8ffdff6aSGreg Kroah-Hartman 	struct usb_interface *intf = comedi_to_usb_interface(dev);
983*8ffdff6aSGreg Kroah-Hartman 	struct usbduxfast_private *devpriv = dev->private;
984*8ffdff6aSGreg Kroah-Hartman 
985*8ffdff6aSGreg Kroah-Hartman 	if (!devpriv)
986*8ffdff6aSGreg Kroah-Hartman 		return;
987*8ffdff6aSGreg Kroah-Hartman 
988*8ffdff6aSGreg Kroah-Hartman 	mutex_lock(&devpriv->mut);
989*8ffdff6aSGreg Kroah-Hartman 
990*8ffdff6aSGreg Kroah-Hartman 	usb_set_intfdata(intf, NULL);
991*8ffdff6aSGreg Kroah-Hartman 
992*8ffdff6aSGreg Kroah-Hartman 	if (devpriv->urb) {
993*8ffdff6aSGreg Kroah-Hartman 		/* waits until a running transfer is over */
994*8ffdff6aSGreg Kroah-Hartman 		usb_kill_urb(devpriv->urb);
995*8ffdff6aSGreg Kroah-Hartman 
996*8ffdff6aSGreg Kroah-Hartman 		kfree(devpriv->inbuf);
997*8ffdff6aSGreg Kroah-Hartman 		usb_free_urb(devpriv->urb);
998*8ffdff6aSGreg Kroah-Hartman 	}
999*8ffdff6aSGreg Kroah-Hartman 
1000*8ffdff6aSGreg Kroah-Hartman 	kfree(devpriv->duxbuf);
1001*8ffdff6aSGreg Kroah-Hartman 
1002*8ffdff6aSGreg Kroah-Hartman 	mutex_unlock(&devpriv->mut);
1003*8ffdff6aSGreg Kroah-Hartman 
1004*8ffdff6aSGreg Kroah-Hartman 	mutex_destroy(&devpriv->mut);
1005*8ffdff6aSGreg Kroah-Hartman }
1006*8ffdff6aSGreg Kroah-Hartman 
1007*8ffdff6aSGreg Kroah-Hartman static struct comedi_driver usbduxfast_driver = {
1008*8ffdff6aSGreg Kroah-Hartman 	.driver_name	= "usbduxfast",
1009*8ffdff6aSGreg Kroah-Hartman 	.module		= THIS_MODULE,
1010*8ffdff6aSGreg Kroah-Hartman 	.auto_attach	= usbduxfast_auto_attach,
1011*8ffdff6aSGreg Kroah-Hartman 	.detach		= usbduxfast_detach,
1012*8ffdff6aSGreg Kroah-Hartman };
1013*8ffdff6aSGreg Kroah-Hartman 
1014*8ffdff6aSGreg Kroah-Hartman static int usbduxfast_usb_probe(struct usb_interface *intf,
1015*8ffdff6aSGreg Kroah-Hartman 				const struct usb_device_id *id)
1016*8ffdff6aSGreg Kroah-Hartman {
1017*8ffdff6aSGreg Kroah-Hartman 	return comedi_usb_auto_config(intf, &usbduxfast_driver, 0);
1018*8ffdff6aSGreg Kroah-Hartman }
1019*8ffdff6aSGreg Kroah-Hartman 
1020*8ffdff6aSGreg Kroah-Hartman static const struct usb_device_id usbduxfast_usb_table[] = {
1021*8ffdff6aSGreg Kroah-Hartman 	/* { USB_DEVICE(0x4b4, 0x8613) }, testing */
1022*8ffdff6aSGreg Kroah-Hartman 	{ USB_DEVICE(0x13d8, 0x0010) },	/* real ID */
1023*8ffdff6aSGreg Kroah-Hartman 	{ USB_DEVICE(0x13d8, 0x0011) },	/* real ID */
1024*8ffdff6aSGreg Kroah-Hartman 	{ }
1025*8ffdff6aSGreg Kroah-Hartman };
1026*8ffdff6aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(usb, usbduxfast_usb_table);
1027*8ffdff6aSGreg Kroah-Hartman 
1028*8ffdff6aSGreg Kroah-Hartman static struct usb_driver usbduxfast_usb_driver = {
1029*8ffdff6aSGreg Kroah-Hartman 	.name		= "usbduxfast",
1030*8ffdff6aSGreg Kroah-Hartman 	.probe		= usbduxfast_usb_probe,
1031*8ffdff6aSGreg Kroah-Hartman 	.disconnect	= comedi_usb_auto_unconfig,
1032*8ffdff6aSGreg Kroah-Hartman 	.id_table	= usbduxfast_usb_table,
1033*8ffdff6aSGreg Kroah-Hartman };
1034*8ffdff6aSGreg Kroah-Hartman module_comedi_usb_driver(usbduxfast_driver, usbduxfast_usb_driver);
1035*8ffdff6aSGreg Kroah-Hartman 
1036*8ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
1037*8ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("USB-DUXfast, BerndPorr@f2s.com");
1038*8ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL");
1039*8ffdff6aSGreg Kroah-Hartman MODULE_FIRMWARE(FIRMWARE);
1040