162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (C) 2004-2019 Bernd Porr, mail@berndporr.me.uk
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/*
762306a36Sopenharmony_ci * Driver: usbduxfast
862306a36Sopenharmony_ci * Description: University of Stirling USB DAQ & INCITE Technology Limited
962306a36Sopenharmony_ci * Devices: [ITL] USB-DUX-FAST (usbduxfast)
1062306a36Sopenharmony_ci * Author: Bernd Porr <mail@berndporr.me.uk>
1162306a36Sopenharmony_ci * Updated: 16 Nov 2019
1262306a36Sopenharmony_ci * Status: stable
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/*
1662306a36Sopenharmony_ci * I must give credit here to Chris Baugher who
1762306a36Sopenharmony_ci * wrote the driver for AT-MIO-16d. I used some parts of this
1862306a36Sopenharmony_ci * driver. I also must give credits to David Brownell
1962306a36Sopenharmony_ci * who supported me with the USB development.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * Bernd Porr
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * Revision history:
2562306a36Sopenharmony_ci * 1.0: Fixed a rounding error in usbduxfast_ai_cmdtest
2662306a36Sopenharmony_ci * 0.9: Dropping the first data packet which seems to be from the last transfer.
2762306a36Sopenharmony_ci *      Buffer overflows in the FX2 are handed over to comedi.
2862306a36Sopenharmony_ci * 0.92: Dropping now 4 packets. The quad buffer has to be emptied.
2962306a36Sopenharmony_ci *       Added insn command basically for testing. Sample rate is
3062306a36Sopenharmony_ci *       1MHz/16ch=62.5kHz
3162306a36Sopenharmony_ci * 0.99: Ian Abbott pointed out a bug which has been corrected. Thanks!
3262306a36Sopenharmony_ci * 0.99a: added external trigger.
3362306a36Sopenharmony_ci * 1.00: added firmware kernel request to the driver which fixed
3462306a36Sopenharmony_ci *       udev coldplug problem
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include <linux/kernel.h>
3862306a36Sopenharmony_ci#include <linux/module.h>
3962306a36Sopenharmony_ci#include <linux/slab.h>
4062306a36Sopenharmony_ci#include <linux/input.h>
4162306a36Sopenharmony_ci#include <linux/fcntl.h>
4262306a36Sopenharmony_ci#include <linux/compiler.h>
4362306a36Sopenharmony_ci#include <linux/comedi/comedi_usb.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * timeout for the USB-transfer
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci#define EZTIMEOUT	30
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * constants for "firmware" upload and download
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_ci#define FIRMWARE		"usbduxfast_firmware.bin"
5462306a36Sopenharmony_ci#define FIRMWARE_MAX_LEN	0x2000
5562306a36Sopenharmony_ci#define USBDUXFASTSUB_FIRMWARE	0xA0
5662306a36Sopenharmony_ci#define VENDOR_DIR_IN		0xC0
5762306a36Sopenharmony_ci#define VENDOR_DIR_OUT		0x40
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/*
6062306a36Sopenharmony_ci * internal addresses of the 8051 processor
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_ci#define USBDUXFASTSUB_CPUCS	0xE600
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/*
6562306a36Sopenharmony_ci * max length of the transfer-buffer for software upload
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_ci#define TB_LEN	0x2000
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * input endpoint number
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_ci#define BULKINEP	6
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * endpoint for the A/D channellist: bulk OUT
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_ci#define CHANNELLISTEP	4
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/*
8062306a36Sopenharmony_ci * number of channels
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_ci#define NUMCHANNELS	32
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/*
8562306a36Sopenharmony_ci * size of the waveform descriptor
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_ci#define WAVESIZE	0x20
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/*
9062306a36Sopenharmony_ci * size of one A/D value
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ci#define SIZEADIN	(sizeof(s16))
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/*
9562306a36Sopenharmony_ci * size of the input-buffer IN BYTES
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_ci#define SIZEINBUF	512
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * 16 bytes
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_ci#define SIZEINSNBUF	512
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/*
10562306a36Sopenharmony_ci * size of the buffer for the dux commands in bytes
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_ci#define SIZEOFDUXBUF	256
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/*
11062306a36Sopenharmony_ci * number of in-URBs which receive the data: min=5
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_ci#define NUMOFINBUFFERSHIGH	10
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/*
11562306a36Sopenharmony_ci * min delay steps for more than one channel
11662306a36Sopenharmony_ci * basically when the mux gives up ;-)
11762306a36Sopenharmony_ci *
11862306a36Sopenharmony_ci * steps at 30MHz in the FX2
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_ci#define MIN_SAMPLING_PERIOD	9
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/*
12362306a36Sopenharmony_ci * max number of 1/30MHz delay steps
12462306a36Sopenharmony_ci */
12562306a36Sopenharmony_ci#define MAX_SAMPLING_PERIOD	500
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/*
12862306a36Sopenharmony_ci * number of received packets to ignore before we start handing data
12962306a36Sopenharmony_ci * over to comedi, it's quad buffering and we have to ignore 4 packets
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_ci#define PACKETS_TO_IGNORE	4
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/*
13462306a36Sopenharmony_ci * comedi constants
13562306a36Sopenharmony_ci */
13662306a36Sopenharmony_cistatic const struct comedi_lrange range_usbduxfast_ai_range = {
13762306a36Sopenharmony_ci	2, {
13862306a36Sopenharmony_ci		BIP_RANGE(0.75),
13962306a36Sopenharmony_ci		BIP_RANGE(0.5)
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * private structure of one subdevice
14562306a36Sopenharmony_ci *
14662306a36Sopenharmony_ci * this is the structure which holds all the data of this driver
14762306a36Sopenharmony_ci * one sub device just now: A/D
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_cistruct usbduxfast_private {
15062306a36Sopenharmony_ci	struct urb *urb;	/* BULK-transfer handling: urb */
15162306a36Sopenharmony_ci	u8 *duxbuf;
15262306a36Sopenharmony_ci	s8 *inbuf;
15362306a36Sopenharmony_ci	short int ai_cmd_running;	/* asynchronous command is running */
15462306a36Sopenharmony_ci	int ignore;		/* counter which ignores the first buffers */
15562306a36Sopenharmony_ci	struct mutex mut;
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/*
15962306a36Sopenharmony_ci * bulk transfers to usbduxfast
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_ci#define SENDADCOMMANDS            0
16262306a36Sopenharmony_ci#define SENDINITEP6               1
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic int usbduxfast_send_cmd(struct comedi_device *dev, int cmd_type)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct usb_device *usb = comedi_to_usb_dev(dev);
16762306a36Sopenharmony_ci	struct usbduxfast_private *devpriv = dev->private;
16862306a36Sopenharmony_ci	int nsent;
16962306a36Sopenharmony_ci	int ret;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	devpriv->duxbuf[0] = cmd_type;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, CHANNELLISTEP),
17462306a36Sopenharmony_ci			   devpriv->duxbuf, SIZEOFDUXBUF,
17562306a36Sopenharmony_ci			   &nsent, 10000);
17662306a36Sopenharmony_ci	if (ret < 0)
17762306a36Sopenharmony_ci		dev_err(dev->class_dev,
17862306a36Sopenharmony_ci			"could not transmit command to the usb-device, err=%d\n",
17962306a36Sopenharmony_ci			ret);
18062306a36Sopenharmony_ci	return ret;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic void usbduxfast_cmd_data(struct comedi_device *dev, int index,
18462306a36Sopenharmony_ci				u8 len, u8 op, u8 out, u8 log)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct usbduxfast_private *devpriv = dev->private;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* Set the GPIF bytes, the first byte is the command byte */
18962306a36Sopenharmony_ci	devpriv->duxbuf[1 + 0x00 + index] = len;
19062306a36Sopenharmony_ci	devpriv->duxbuf[1 + 0x08 + index] = op;
19162306a36Sopenharmony_ci	devpriv->duxbuf[1 + 0x10 + index] = out;
19262306a36Sopenharmony_ci	devpriv->duxbuf[1 + 0x18 + index] = log;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int usbduxfast_ai_stop(struct comedi_device *dev, int do_unlink)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct usbduxfast_private *devpriv = dev->private;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* stop aquistion */
20062306a36Sopenharmony_ci	devpriv->ai_cmd_running = 0;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (do_unlink && devpriv->urb) {
20362306a36Sopenharmony_ci		/* kill the running transfer */
20462306a36Sopenharmony_ci		usb_kill_urb(devpriv->urb);
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return 0;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic int usbduxfast_ai_cancel(struct comedi_device *dev,
21162306a36Sopenharmony_ci				struct comedi_subdevice *s)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct usbduxfast_private *devpriv = dev->private;
21462306a36Sopenharmony_ci	int ret;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	mutex_lock(&devpriv->mut);
21762306a36Sopenharmony_ci	ret = usbduxfast_ai_stop(dev, 1);
21862306a36Sopenharmony_ci	mutex_unlock(&devpriv->mut);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return ret;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void usbduxfast_ai_handle_urb(struct comedi_device *dev,
22462306a36Sopenharmony_ci				     struct comedi_subdevice *s,
22562306a36Sopenharmony_ci				     struct urb *urb)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct usbduxfast_private *devpriv = dev->private;
22862306a36Sopenharmony_ci	struct comedi_async *async = s->async;
22962306a36Sopenharmony_ci	struct comedi_cmd *cmd = &async->cmd;
23062306a36Sopenharmony_ci	int ret;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (devpriv->ignore) {
23362306a36Sopenharmony_ci		devpriv->ignore--;
23462306a36Sopenharmony_ci	} else {
23562306a36Sopenharmony_ci		unsigned int nsamples;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		nsamples = comedi_bytes_to_samples(s, urb->actual_length);
23862306a36Sopenharmony_ci		nsamples = comedi_nsamples_left(s, nsamples);
23962306a36Sopenharmony_ci		comedi_buf_write_samples(s, urb->transfer_buffer, nsamples);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci		if (cmd->stop_src == TRIG_COUNT &&
24262306a36Sopenharmony_ci		    async->scans_done >= cmd->stop_arg)
24362306a36Sopenharmony_ci			async->events |= COMEDI_CB_EOA;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* if command is still running, resubmit urb for BULK transfer */
24762306a36Sopenharmony_ci	if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
24862306a36Sopenharmony_ci		urb->dev = comedi_to_usb_dev(dev);
24962306a36Sopenharmony_ci		urb->status = 0;
25062306a36Sopenharmony_ci		ret = usb_submit_urb(urb, GFP_ATOMIC);
25162306a36Sopenharmony_ci		if (ret < 0) {
25262306a36Sopenharmony_ci			dev_err(dev->class_dev, "urb resubm failed: %d", ret);
25362306a36Sopenharmony_ci			async->events |= COMEDI_CB_ERROR;
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic void usbduxfast_ai_interrupt(struct urb *urb)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct comedi_device *dev = urb->context;
26162306a36Sopenharmony_ci	struct comedi_subdevice *s = dev->read_subdev;
26262306a36Sopenharmony_ci	struct comedi_async *async = s->async;
26362306a36Sopenharmony_ci	struct usbduxfast_private *devpriv = dev->private;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* exit if not running a command, do not resubmit urb */
26662306a36Sopenharmony_ci	if (!devpriv->ai_cmd_running)
26762306a36Sopenharmony_ci		return;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	switch (urb->status) {
27062306a36Sopenharmony_ci	case 0:
27162306a36Sopenharmony_ci		usbduxfast_ai_handle_urb(dev, s, urb);
27262306a36Sopenharmony_ci		break;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	case -ECONNRESET:
27562306a36Sopenharmony_ci	case -ENOENT:
27662306a36Sopenharmony_ci	case -ESHUTDOWN:
27762306a36Sopenharmony_ci	case -ECONNABORTED:
27862306a36Sopenharmony_ci		/* after an unlink command, unplug, ... etc */
27962306a36Sopenharmony_ci		async->events |= COMEDI_CB_ERROR;
28062306a36Sopenharmony_ci		break;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	default:
28362306a36Sopenharmony_ci		/* a real error */
28462306a36Sopenharmony_ci		dev_err(dev->class_dev,
28562306a36Sopenharmony_ci			"non-zero urb status received in ai intr context: %d\n",
28662306a36Sopenharmony_ci			urb->status);
28762306a36Sopenharmony_ci		async->events |= COMEDI_CB_ERROR;
28862306a36Sopenharmony_ci		break;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/*
29262306a36Sopenharmony_ci	 * comedi_handle_events() cannot be used in this driver. The (*cancel)
29362306a36Sopenharmony_ci	 * operation would unlink the urb.
29462306a36Sopenharmony_ci	 */
29562306a36Sopenharmony_ci	if (async->events & COMEDI_CB_CANCEL_MASK)
29662306a36Sopenharmony_ci		usbduxfast_ai_stop(dev, 0);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	comedi_event(dev, s);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic int usbduxfast_submit_urb(struct comedi_device *dev)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct usb_device *usb = comedi_to_usb_dev(dev);
30462306a36Sopenharmony_ci	struct usbduxfast_private *devpriv = dev->private;
30562306a36Sopenharmony_ci	int ret;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	usb_fill_bulk_urb(devpriv->urb, usb, usb_rcvbulkpipe(usb, BULKINEP),
30862306a36Sopenharmony_ci			  devpriv->inbuf, SIZEINBUF,
30962306a36Sopenharmony_ci			  usbduxfast_ai_interrupt, dev);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	ret = usb_submit_urb(devpriv->urb, GFP_ATOMIC);
31262306a36Sopenharmony_ci	if (ret) {
31362306a36Sopenharmony_ci		dev_err(dev->class_dev, "usb_submit_urb error %d\n", ret);
31462306a36Sopenharmony_ci		return ret;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci	return 0;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int usbduxfast_ai_check_chanlist(struct comedi_device *dev,
32062306a36Sopenharmony_ci					struct comedi_subdevice *s,
32162306a36Sopenharmony_ci					struct comedi_cmd *cmd)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	unsigned int gain0 = CR_RANGE(cmd->chanlist[0]);
32462306a36Sopenharmony_ci	int i;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (cmd->chanlist_len > 3 && cmd->chanlist_len != 16) {
32762306a36Sopenharmony_ci		dev_err(dev->class_dev, "unsupported combination of channels\n");
32862306a36Sopenharmony_ci		return -EINVAL;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	for (i = 0; i < cmd->chanlist_len; ++i) {
33262306a36Sopenharmony_ci		unsigned int chan = CR_CHAN(cmd->chanlist[i]);
33362306a36Sopenharmony_ci		unsigned int gain = CR_RANGE(cmd->chanlist[i]);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		if (chan != i) {
33662306a36Sopenharmony_ci			dev_err(dev->class_dev,
33762306a36Sopenharmony_ci				"channels are not consecutive\n");
33862306a36Sopenharmony_ci			return -EINVAL;
33962306a36Sopenharmony_ci		}
34062306a36Sopenharmony_ci		if (gain != gain0 && cmd->chanlist_len > 3) {
34162306a36Sopenharmony_ci			dev_err(dev->class_dev,
34262306a36Sopenharmony_ci				"gain must be the same for all channels\n");
34362306a36Sopenharmony_ci			return -EINVAL;
34462306a36Sopenharmony_ci		}
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci	return 0;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic int usbduxfast_ai_cmdtest(struct comedi_device *dev,
35062306a36Sopenharmony_ci				 struct comedi_subdevice *s,
35162306a36Sopenharmony_ci				 struct comedi_cmd *cmd)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	int err = 0;
35462306a36Sopenharmony_ci	int err2 = 0;
35562306a36Sopenharmony_ci	unsigned int steps;
35662306a36Sopenharmony_ci	unsigned int arg;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* Step 1 : check if triggers are trivially valid */
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	err |= comedi_check_trigger_src(&cmd->start_src,
36162306a36Sopenharmony_ci					TRIG_NOW | TRIG_EXT | TRIG_INT);
36262306a36Sopenharmony_ci	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
36362306a36Sopenharmony_ci	err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
36462306a36Sopenharmony_ci	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
36562306a36Sopenharmony_ci	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (err)
36862306a36Sopenharmony_ci		return 1;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* Step 2a : make sure trigger sources are unique */
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	err |= comedi_check_trigger_is_unique(cmd->start_src);
37362306a36Sopenharmony_ci	err |= comedi_check_trigger_is_unique(cmd->stop_src);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/* Step 2b : and mutually compatible */
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (err)
37862306a36Sopenharmony_ci		return 2;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* Step 3: check if arguments are trivially valid */
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (!cmd->chanlist_len)
38562306a36Sopenharmony_ci		err |= -EINVAL;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* external start trigger is only valid for 1 or 16 channels */
38862306a36Sopenharmony_ci	if (cmd->start_src == TRIG_EXT &&
38962306a36Sopenharmony_ci	    cmd->chanlist_len != 1 && cmd->chanlist_len != 16)
39062306a36Sopenharmony_ci		err |= -EINVAL;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
39362306a36Sopenharmony_ci					   cmd->chanlist_len);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/*
39662306a36Sopenharmony_ci	 * Validate the conversion timing:
39762306a36Sopenharmony_ci	 * for 1 channel the timing in 30MHz "steps" is:
39862306a36Sopenharmony_ci	 *	steps <= MAX_SAMPLING_PERIOD
39962306a36Sopenharmony_ci	 * for all other chanlist_len it is:
40062306a36Sopenharmony_ci	 *	MIN_SAMPLING_PERIOD <= steps <= MAX_SAMPLING_PERIOD
40162306a36Sopenharmony_ci	 */
40262306a36Sopenharmony_ci	steps = (cmd->convert_arg * 30) / 1000;
40362306a36Sopenharmony_ci	if (cmd->chanlist_len !=  1)
40462306a36Sopenharmony_ci		err2 |= comedi_check_trigger_arg_min(&steps,
40562306a36Sopenharmony_ci						     MIN_SAMPLING_PERIOD);
40662306a36Sopenharmony_ci	else
40762306a36Sopenharmony_ci		err2 |= comedi_check_trigger_arg_min(&steps, 1);
40862306a36Sopenharmony_ci	err2 |= comedi_check_trigger_arg_max(&steps, MAX_SAMPLING_PERIOD);
40962306a36Sopenharmony_ci	if (err2) {
41062306a36Sopenharmony_ci		err |= err2;
41162306a36Sopenharmony_ci		arg = (steps * 1000) / 30;
41262306a36Sopenharmony_ci		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (cmd->stop_src == TRIG_COUNT)
41662306a36Sopenharmony_ci		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
41762306a36Sopenharmony_ci	else	/* TRIG_NONE */
41862306a36Sopenharmony_ci		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (err)
42162306a36Sopenharmony_ci		return 3;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/* Step 4: fix up any arguments */
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	/* Step 5: check channel list if it exists */
42662306a36Sopenharmony_ci	if (cmd->chanlist && cmd->chanlist_len > 0)
42762306a36Sopenharmony_ci		err |= usbduxfast_ai_check_chanlist(dev, s, cmd);
42862306a36Sopenharmony_ci	if (err)
42962306a36Sopenharmony_ci		return 5;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic int usbduxfast_ai_inttrig(struct comedi_device *dev,
43562306a36Sopenharmony_ci				 struct comedi_subdevice *s,
43662306a36Sopenharmony_ci				 unsigned int trig_num)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct usbduxfast_private *devpriv = dev->private;
43962306a36Sopenharmony_ci	struct comedi_cmd *cmd = &s->async->cmd;
44062306a36Sopenharmony_ci	int ret;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (trig_num != cmd->start_arg)
44362306a36Sopenharmony_ci		return -EINVAL;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	mutex_lock(&devpriv->mut);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (!devpriv->ai_cmd_running) {
44862306a36Sopenharmony_ci		devpriv->ai_cmd_running = 1;
44962306a36Sopenharmony_ci		ret = usbduxfast_submit_urb(dev);
45062306a36Sopenharmony_ci		if (ret < 0) {
45162306a36Sopenharmony_ci			dev_err(dev->class_dev, "urbSubmit: err=%d\n", ret);
45262306a36Sopenharmony_ci			devpriv->ai_cmd_running = 0;
45362306a36Sopenharmony_ci			mutex_unlock(&devpriv->mut);
45462306a36Sopenharmony_ci			return ret;
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci		s->async->inttrig = NULL;
45762306a36Sopenharmony_ci	} else {
45862306a36Sopenharmony_ci		dev_err(dev->class_dev, "ai is already running\n");
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci	mutex_unlock(&devpriv->mut);
46162306a36Sopenharmony_ci	return 1;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic int usbduxfast_ai_cmd(struct comedi_device *dev,
46562306a36Sopenharmony_ci			     struct comedi_subdevice *s)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct usbduxfast_private *devpriv = dev->private;
46862306a36Sopenharmony_ci	struct comedi_cmd *cmd = &s->async->cmd;
46962306a36Sopenharmony_ci	unsigned int rngmask = 0xff;
47062306a36Sopenharmony_ci	int j, ret;
47162306a36Sopenharmony_ci	long steps, steps_tmp;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	mutex_lock(&devpriv->mut);
47462306a36Sopenharmony_ci	if (devpriv->ai_cmd_running) {
47562306a36Sopenharmony_ci		ret = -EBUSY;
47662306a36Sopenharmony_ci		goto cmd_exit;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/*
48062306a36Sopenharmony_ci	 * ignore the first buffers from the device if there
48162306a36Sopenharmony_ci	 * is an error condition
48262306a36Sopenharmony_ci	 */
48362306a36Sopenharmony_ci	devpriv->ignore = PACKETS_TO_IGNORE;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	steps = (cmd->convert_arg * 30) / 1000;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	switch (cmd->chanlist_len) {
48862306a36Sopenharmony_ci	case 1:
48962306a36Sopenharmony_ci		/*
49062306a36Sopenharmony_ci		 * one channel
49162306a36Sopenharmony_ci		 */
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		if (CR_RANGE(cmd->chanlist[0]) > 0)
49462306a36Sopenharmony_ci			rngmask = 0xff - 0x04;
49562306a36Sopenharmony_ci		else
49662306a36Sopenharmony_ci			rngmask = 0xff;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		/*
49962306a36Sopenharmony_ci		 * for external trigger: looping in this state until
50062306a36Sopenharmony_ci		 * the RDY0 pin becomes zero
50162306a36Sopenharmony_ci		 */
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		/* we loop here until ready has been set */
50462306a36Sopenharmony_ci		if (cmd->start_src == TRIG_EXT) {
50562306a36Sopenharmony_ci			/* branch back to state 0 */
50662306a36Sopenharmony_ci			/* deceision state w/o data */
50762306a36Sopenharmony_ci			/* RDY0 = 0 */
50862306a36Sopenharmony_ci			usbduxfast_cmd_data(dev, 0, 0x01, 0x01, rngmask, 0x00);
50962306a36Sopenharmony_ci		} else {	/* we just proceed to state 1 */
51062306a36Sopenharmony_ci			usbduxfast_cmd_data(dev, 0, 0x01, 0x00, rngmask, 0x00);
51162306a36Sopenharmony_ci		}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		if (steps < MIN_SAMPLING_PERIOD) {
51462306a36Sopenharmony_ci			/* for fast single channel aqu without mux */
51562306a36Sopenharmony_ci			if (steps <= 1) {
51662306a36Sopenharmony_ci				/*
51762306a36Sopenharmony_ci				 * we just stay here at state 1 and rexecute
51862306a36Sopenharmony_ci				 * the same state this gives us 30MHz sampling
51962306a36Sopenharmony_ci				 * rate
52062306a36Sopenharmony_ci				 */
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci				/* branch back to state 1 */
52362306a36Sopenharmony_ci				/* deceision state with data */
52462306a36Sopenharmony_ci				/* doesn't matter */
52562306a36Sopenharmony_ci				usbduxfast_cmd_data(dev, 1,
52662306a36Sopenharmony_ci						    0x89, 0x03, rngmask, 0xff);
52762306a36Sopenharmony_ci			} else {
52862306a36Sopenharmony_ci				/*
52962306a36Sopenharmony_ci				 * we loop through two states: data and delay
53062306a36Sopenharmony_ci				 * max rate is 15MHz
53162306a36Sopenharmony_ci				 */
53262306a36Sopenharmony_ci				/* data */
53362306a36Sopenharmony_ci				/* doesn't matter */
53462306a36Sopenharmony_ci				usbduxfast_cmd_data(dev, 1, steps - 1,
53562306a36Sopenharmony_ci						    0x02, rngmask, 0x00);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci				/* branch back to state 1 */
53862306a36Sopenharmony_ci				/* deceision state w/o data */
53962306a36Sopenharmony_ci				/* doesn't matter */
54062306a36Sopenharmony_ci				usbduxfast_cmd_data(dev, 2,
54162306a36Sopenharmony_ci						    0x09, 0x01, rngmask, 0xff);
54262306a36Sopenharmony_ci			}
54362306a36Sopenharmony_ci		} else {
54462306a36Sopenharmony_ci			/*
54562306a36Sopenharmony_ci			 * we loop through 3 states: 2x delay and 1x data
54662306a36Sopenharmony_ci			 * this gives a min sampling rate of 60kHz
54762306a36Sopenharmony_ci			 */
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci			/* we have 1 state with duration 1 */
55062306a36Sopenharmony_ci			steps = steps - 1;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci			/* do the first part of the delay */
55362306a36Sopenharmony_ci			usbduxfast_cmd_data(dev, 1,
55462306a36Sopenharmony_ci					    steps / 2, 0x00, rngmask, 0x00);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci			/* and the second part */
55762306a36Sopenharmony_ci			usbduxfast_cmd_data(dev, 2, steps - steps / 2,
55862306a36Sopenharmony_ci					    0x00, rngmask, 0x00);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci			/* get the data and branch back */
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci			/* branch back to state 1 */
56362306a36Sopenharmony_ci			/* deceision state w data */
56462306a36Sopenharmony_ci			/* doesn't matter */
56562306a36Sopenharmony_ci			usbduxfast_cmd_data(dev, 3,
56662306a36Sopenharmony_ci					    0x09, 0x03, rngmask, 0xff);
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci		break;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	case 2:
57162306a36Sopenharmony_ci		/*
57262306a36Sopenharmony_ci		 * two channels
57362306a36Sopenharmony_ci		 * commit data to the FIFO
57462306a36Sopenharmony_ci		 */
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		if (CR_RANGE(cmd->chanlist[0]) > 0)
57762306a36Sopenharmony_ci			rngmask = 0xff - 0x04;
57862306a36Sopenharmony_ci		else
57962306a36Sopenharmony_ci			rngmask = 0xff;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		/* data */
58262306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		/* we have 1 state with duration 1: state 0 */
58562306a36Sopenharmony_ci		steps_tmp = steps - 1;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci		if (CR_RANGE(cmd->chanlist[1]) > 0)
58862306a36Sopenharmony_ci			rngmask = 0xff - 0x04;
58962306a36Sopenharmony_ci		else
59062306a36Sopenharmony_ci			rngmask = 0xff;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci		/* do the first part of the delay */
59362306a36Sopenharmony_ci		/* count */
59462306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 1, steps_tmp / 2,
59562306a36Sopenharmony_ci				    0x00, 0xfe & rngmask, 0x00);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		/* and the second part */
59862306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 2, steps_tmp  - steps_tmp / 2,
59962306a36Sopenharmony_ci				    0x00, rngmask, 0x00);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		/* data */
60262306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 3, 0x01, 0x02, rngmask, 0x00);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		/*
60562306a36Sopenharmony_ci		 * we have 2 states with duration 1: step 6 and
60662306a36Sopenharmony_ci		 * the IDLE state
60762306a36Sopenharmony_ci		 */
60862306a36Sopenharmony_ci		steps_tmp = steps - 2;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		if (CR_RANGE(cmd->chanlist[0]) > 0)
61162306a36Sopenharmony_ci			rngmask = 0xff - 0x04;
61262306a36Sopenharmony_ci		else
61362306a36Sopenharmony_ci			rngmask = 0xff;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		/* do the first part of the delay */
61662306a36Sopenharmony_ci		/* reset */
61762306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 4, steps_tmp / 2,
61862306a36Sopenharmony_ci				    0x00, (0xff - 0x02) & rngmask, 0x00);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		/* and the second part */
62162306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
62262306a36Sopenharmony_ci				    0x00, rngmask, 0x00);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
62562306a36Sopenharmony_ci		break;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	case 3:
62862306a36Sopenharmony_ci		/*
62962306a36Sopenharmony_ci		 * three channels
63062306a36Sopenharmony_ci		 */
63162306a36Sopenharmony_ci		for (j = 0; j < 1; j++) {
63262306a36Sopenharmony_ci			int index = j * 2;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci			if (CR_RANGE(cmd->chanlist[j]) > 0)
63562306a36Sopenharmony_ci				rngmask = 0xff - 0x04;
63662306a36Sopenharmony_ci			else
63762306a36Sopenharmony_ci				rngmask = 0xff;
63862306a36Sopenharmony_ci			/*
63962306a36Sopenharmony_ci			 * commit data to the FIFO and do the first part
64062306a36Sopenharmony_ci			 * of the delay
64162306a36Sopenharmony_ci			 */
64262306a36Sopenharmony_ci			/* data */
64362306a36Sopenharmony_ci			/* no change */
64462306a36Sopenharmony_ci			usbduxfast_cmd_data(dev, index, steps / 2,
64562306a36Sopenharmony_ci					    0x02, rngmask, 0x00);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci			if (CR_RANGE(cmd->chanlist[j + 1]) > 0)
64862306a36Sopenharmony_ci				rngmask = 0xff - 0x04;
64962306a36Sopenharmony_ci			else
65062306a36Sopenharmony_ci				rngmask = 0xff;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci			/* do the second part of the delay */
65362306a36Sopenharmony_ci			/* no data */
65462306a36Sopenharmony_ci			/* count */
65562306a36Sopenharmony_ci			usbduxfast_cmd_data(dev, index + 1, steps - steps / 2,
65662306a36Sopenharmony_ci					    0x00, 0xfe & rngmask, 0x00);
65762306a36Sopenharmony_ci		}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci		/* 2 steps with duration 1: the idele step and step 6: */
66062306a36Sopenharmony_ci		steps_tmp = steps - 2;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		/* commit data to the FIFO and do the first part of the delay */
66362306a36Sopenharmony_ci		/* data */
66462306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 4, steps_tmp / 2,
66562306a36Sopenharmony_ci				    0x02, rngmask, 0x00);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		if (CR_RANGE(cmd->chanlist[0]) > 0)
66862306a36Sopenharmony_ci			rngmask = 0xff - 0x04;
66962306a36Sopenharmony_ci		else
67062306a36Sopenharmony_ci			rngmask = 0xff;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		/* do the second part of the delay */
67362306a36Sopenharmony_ci		/* no data */
67462306a36Sopenharmony_ci		/* reset */
67562306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
67662306a36Sopenharmony_ci				    0x00, (0xff - 0x02) & rngmask, 0x00);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
67962306a36Sopenharmony_ci		break;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	case 16:
68262306a36Sopenharmony_ci		if (CR_RANGE(cmd->chanlist[0]) > 0)
68362306a36Sopenharmony_ci			rngmask = 0xff - 0x04;
68462306a36Sopenharmony_ci		else
68562306a36Sopenharmony_ci			rngmask = 0xff;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci		if (cmd->start_src == TRIG_EXT) {
68862306a36Sopenharmony_ci			/*
68962306a36Sopenharmony_ci			 * we loop here until ready has been set
69062306a36Sopenharmony_ci			 */
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci			/* branch back to state 0 */
69362306a36Sopenharmony_ci			/* deceision state w/o data */
69462306a36Sopenharmony_ci			/* reset */
69562306a36Sopenharmony_ci			/* RDY0 = 0 */
69662306a36Sopenharmony_ci			usbduxfast_cmd_data(dev, 0, 0x01, 0x01,
69762306a36Sopenharmony_ci					    (0xff - 0x02) & rngmask, 0x00);
69862306a36Sopenharmony_ci		} else {
69962306a36Sopenharmony_ci			/*
70062306a36Sopenharmony_ci			 * we just proceed to state 1
70162306a36Sopenharmony_ci			 */
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci			/* 30us reset pulse */
70462306a36Sopenharmony_ci			/* reset */
70562306a36Sopenharmony_ci			usbduxfast_cmd_data(dev, 0, 0xff, 0x00,
70662306a36Sopenharmony_ci					    (0xff - 0x02) & rngmask, 0x00);
70762306a36Sopenharmony_ci		}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		/* commit data to the FIFO */
71062306a36Sopenharmony_ci		/* data */
71162306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 1, 0x01, 0x02, rngmask, 0x00);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci		/* we have 2 states with duration 1 */
71462306a36Sopenharmony_ci		steps = steps - 2;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci		/* do the first part of the delay */
71762306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 2, steps / 2,
71862306a36Sopenharmony_ci				    0x00, 0xfe & rngmask, 0x00);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci		/* and the second part */
72162306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 3, steps - steps / 2,
72262306a36Sopenharmony_ci				    0x00, rngmask, 0x00);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci		/* branch back to state 1 */
72562306a36Sopenharmony_ci		/* deceision state w/o data */
72662306a36Sopenharmony_ci		/* doesn't matter */
72762306a36Sopenharmony_ci		usbduxfast_cmd_data(dev, 4, 0x09, 0x01, rngmask, 0xff);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci		break;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/* 0 means that the AD commands are sent */
73362306a36Sopenharmony_ci	ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS);
73462306a36Sopenharmony_ci	if (ret < 0)
73562306a36Sopenharmony_ci		goto cmd_exit;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if ((cmd->start_src == TRIG_NOW) || (cmd->start_src == TRIG_EXT)) {
73862306a36Sopenharmony_ci		/* enable this acquisition operation */
73962306a36Sopenharmony_ci		devpriv->ai_cmd_running = 1;
74062306a36Sopenharmony_ci		ret = usbduxfast_submit_urb(dev);
74162306a36Sopenharmony_ci		if (ret < 0) {
74262306a36Sopenharmony_ci			devpriv->ai_cmd_running = 0;
74362306a36Sopenharmony_ci			/* fixme: unlink here?? */
74462306a36Sopenharmony_ci			goto cmd_exit;
74562306a36Sopenharmony_ci		}
74662306a36Sopenharmony_ci		s->async->inttrig = NULL;
74762306a36Sopenharmony_ci	} else {	/* TRIG_INT */
74862306a36Sopenharmony_ci		s->async->inttrig = usbduxfast_ai_inttrig;
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_cicmd_exit:
75262306a36Sopenharmony_ci	mutex_unlock(&devpriv->mut);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	return ret;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci/*
75862306a36Sopenharmony_ci * Mode 0 is used to get a single conversion on demand.
75962306a36Sopenharmony_ci */
76062306a36Sopenharmony_cistatic int usbduxfast_ai_insn_read(struct comedi_device *dev,
76162306a36Sopenharmony_ci				   struct comedi_subdevice *s,
76262306a36Sopenharmony_ci				   struct comedi_insn *insn,
76362306a36Sopenharmony_ci				   unsigned int *data)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct usb_device *usb = comedi_to_usb_dev(dev);
76662306a36Sopenharmony_ci	struct usbduxfast_private *devpriv = dev->private;
76762306a36Sopenharmony_ci	unsigned int chan = CR_CHAN(insn->chanspec);
76862306a36Sopenharmony_ci	unsigned int range = CR_RANGE(insn->chanspec);
76962306a36Sopenharmony_ci	u8 rngmask = range ? (0xff - 0x04) : 0xff;
77062306a36Sopenharmony_ci	int i, j, n, actual_length;
77162306a36Sopenharmony_ci	int ret;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	mutex_lock(&devpriv->mut);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	if (devpriv->ai_cmd_running) {
77662306a36Sopenharmony_ci		dev_err(dev->class_dev,
77762306a36Sopenharmony_ci			"ai_insn_read not possible, async cmd is running\n");
77862306a36Sopenharmony_ci		mutex_unlock(&devpriv->mut);
77962306a36Sopenharmony_ci		return -EBUSY;
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	/* set command for the first channel */
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	/* commit data to the FIFO */
78562306a36Sopenharmony_ci	/* data */
78662306a36Sopenharmony_ci	usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	/* do the first part of the delay */
78962306a36Sopenharmony_ci	usbduxfast_cmd_data(dev, 1, 0x0c, 0x00, 0xfe & rngmask, 0x00);
79062306a36Sopenharmony_ci	usbduxfast_cmd_data(dev, 2, 0x01, 0x00, 0xfe & rngmask, 0x00);
79162306a36Sopenharmony_ci	usbduxfast_cmd_data(dev, 3, 0x01, 0x00, 0xfe & rngmask, 0x00);
79262306a36Sopenharmony_ci	usbduxfast_cmd_data(dev, 4, 0x01, 0x00, 0xfe & rngmask, 0x00);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* second part */
79562306a36Sopenharmony_ci	usbduxfast_cmd_data(dev, 5, 0x0c, 0x00, rngmask, 0x00);
79662306a36Sopenharmony_ci	usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS);
79962306a36Sopenharmony_ci	if (ret < 0) {
80062306a36Sopenharmony_ci		mutex_unlock(&devpriv->mut);
80162306a36Sopenharmony_ci		return ret;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	for (i = 0; i < PACKETS_TO_IGNORE; i++) {
80562306a36Sopenharmony_ci		ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
80662306a36Sopenharmony_ci				   devpriv->inbuf, SIZEINBUF,
80762306a36Sopenharmony_ci				   &actual_length, 10000);
80862306a36Sopenharmony_ci		if (ret < 0) {
80962306a36Sopenharmony_ci			dev_err(dev->class_dev, "insn timeout, no data\n");
81062306a36Sopenharmony_ci			mutex_unlock(&devpriv->mut);
81162306a36Sopenharmony_ci			return ret;
81262306a36Sopenharmony_ci		}
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	for (i = 0; i < insn->n;) {
81662306a36Sopenharmony_ci		ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
81762306a36Sopenharmony_ci				   devpriv->inbuf, SIZEINBUF,
81862306a36Sopenharmony_ci				   &actual_length, 10000);
81962306a36Sopenharmony_ci		if (ret < 0) {
82062306a36Sopenharmony_ci			dev_err(dev->class_dev, "insn data error: %d\n", ret);
82162306a36Sopenharmony_ci			mutex_unlock(&devpriv->mut);
82262306a36Sopenharmony_ci			return ret;
82362306a36Sopenharmony_ci		}
82462306a36Sopenharmony_ci		n = actual_length / sizeof(u16);
82562306a36Sopenharmony_ci		if ((n % 16) != 0) {
82662306a36Sopenharmony_ci			dev_err(dev->class_dev, "insn data packet corrupted\n");
82762306a36Sopenharmony_ci			mutex_unlock(&devpriv->mut);
82862306a36Sopenharmony_ci			return -EINVAL;
82962306a36Sopenharmony_ci		}
83062306a36Sopenharmony_ci		for (j = chan; (j < n) && (i < insn->n); j = j + 16) {
83162306a36Sopenharmony_ci			data[i] = ((u16 *)(devpriv->inbuf))[j];
83262306a36Sopenharmony_ci			i++;
83362306a36Sopenharmony_ci		}
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	mutex_unlock(&devpriv->mut);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	return insn->n;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic int usbduxfast_upload_firmware(struct comedi_device *dev,
84262306a36Sopenharmony_ci				      const u8 *data, size_t size,
84362306a36Sopenharmony_ci				      unsigned long context)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	struct usb_device *usb = comedi_to_usb_dev(dev);
84662306a36Sopenharmony_ci	u8 *buf;
84762306a36Sopenharmony_ci	unsigned char *tmp;
84862306a36Sopenharmony_ci	int ret;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	if (!data)
85162306a36Sopenharmony_ci		return 0;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (size > FIRMWARE_MAX_LEN) {
85462306a36Sopenharmony_ci		dev_err(dev->class_dev, "firmware binary too large for FX2\n");
85562306a36Sopenharmony_ci		return -ENOMEM;
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	/* we generate a local buffer for the firmware */
85962306a36Sopenharmony_ci	buf = kmemdup(data, size, GFP_KERNEL);
86062306a36Sopenharmony_ci	if (!buf)
86162306a36Sopenharmony_ci		return -ENOMEM;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	/* we need a malloc'ed buffer for usb_control_msg() */
86462306a36Sopenharmony_ci	tmp = kmalloc(1, GFP_KERNEL);
86562306a36Sopenharmony_ci	if (!tmp) {
86662306a36Sopenharmony_ci		kfree(buf);
86762306a36Sopenharmony_ci		return -ENOMEM;
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	/* stop the current firmware on the device */
87162306a36Sopenharmony_ci	*tmp = 1;	/* 7f92 to one */
87262306a36Sopenharmony_ci	ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
87362306a36Sopenharmony_ci			      USBDUXFASTSUB_FIRMWARE,
87462306a36Sopenharmony_ci			      VENDOR_DIR_OUT,
87562306a36Sopenharmony_ci			      USBDUXFASTSUB_CPUCS, 0x0000,
87662306a36Sopenharmony_ci			      tmp, 1,
87762306a36Sopenharmony_ci			      EZTIMEOUT);
87862306a36Sopenharmony_ci	if (ret < 0) {
87962306a36Sopenharmony_ci		dev_err(dev->class_dev, "can not stop firmware\n");
88062306a36Sopenharmony_ci		goto done;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	/* upload the new firmware to the device */
88462306a36Sopenharmony_ci	ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
88562306a36Sopenharmony_ci			      USBDUXFASTSUB_FIRMWARE,
88662306a36Sopenharmony_ci			      VENDOR_DIR_OUT,
88762306a36Sopenharmony_ci			      0, 0x0000,
88862306a36Sopenharmony_ci			      buf, size,
88962306a36Sopenharmony_ci			      EZTIMEOUT);
89062306a36Sopenharmony_ci	if (ret < 0) {
89162306a36Sopenharmony_ci		dev_err(dev->class_dev, "firmware upload failed\n");
89262306a36Sopenharmony_ci		goto done;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	/* start the new firmware on the device */
89662306a36Sopenharmony_ci	*tmp = 0;	/* 7f92 to zero */
89762306a36Sopenharmony_ci	ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
89862306a36Sopenharmony_ci			      USBDUXFASTSUB_FIRMWARE,
89962306a36Sopenharmony_ci			      VENDOR_DIR_OUT,
90062306a36Sopenharmony_ci			      USBDUXFASTSUB_CPUCS, 0x0000,
90162306a36Sopenharmony_ci			      tmp, 1,
90262306a36Sopenharmony_ci			      EZTIMEOUT);
90362306a36Sopenharmony_ci	if (ret < 0)
90462306a36Sopenharmony_ci		dev_err(dev->class_dev, "can not start firmware\n");
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_cidone:
90762306a36Sopenharmony_ci	kfree(tmp);
90862306a36Sopenharmony_ci	kfree(buf);
90962306a36Sopenharmony_ci	return ret;
91062306a36Sopenharmony_ci}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic int usbduxfast_auto_attach(struct comedi_device *dev,
91362306a36Sopenharmony_ci				  unsigned long context_unused)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct usb_interface *intf = comedi_to_usb_interface(dev);
91662306a36Sopenharmony_ci	struct usb_device *usb = comedi_to_usb_dev(dev);
91762306a36Sopenharmony_ci	struct usbduxfast_private *devpriv;
91862306a36Sopenharmony_ci	struct comedi_subdevice *s;
91962306a36Sopenharmony_ci	int ret;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	if (usb->speed != USB_SPEED_HIGH) {
92262306a36Sopenharmony_ci		dev_err(dev->class_dev,
92362306a36Sopenharmony_ci			"This driver needs USB 2.0 to operate. Aborting...\n");
92462306a36Sopenharmony_ci		return -ENODEV;
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
92862306a36Sopenharmony_ci	if (!devpriv)
92962306a36Sopenharmony_ci		return -ENOMEM;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	mutex_init(&devpriv->mut);
93262306a36Sopenharmony_ci	usb_set_intfdata(intf, devpriv);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	devpriv->duxbuf = kmalloc(SIZEOFDUXBUF, GFP_KERNEL);
93562306a36Sopenharmony_ci	if (!devpriv->duxbuf)
93662306a36Sopenharmony_ci		return -ENOMEM;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	ret = usb_set_interface(usb,
93962306a36Sopenharmony_ci				intf->altsetting->desc.bInterfaceNumber, 1);
94062306a36Sopenharmony_ci	if (ret < 0) {
94162306a36Sopenharmony_ci		dev_err(dev->class_dev,
94262306a36Sopenharmony_ci			"could not switch to alternate setting 1\n");
94362306a36Sopenharmony_ci		return -ENODEV;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	devpriv->urb = usb_alloc_urb(0, GFP_KERNEL);
94762306a36Sopenharmony_ci	if (!devpriv->urb)
94862306a36Sopenharmony_ci		return -ENOMEM;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	devpriv->inbuf = kmalloc(SIZEINBUF, GFP_KERNEL);
95162306a36Sopenharmony_ci	if (!devpriv->inbuf)
95262306a36Sopenharmony_ci		return -ENOMEM;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	ret = comedi_load_firmware(dev, &usb->dev, FIRMWARE,
95562306a36Sopenharmony_ci				   usbduxfast_upload_firmware, 0);
95662306a36Sopenharmony_ci	if (ret)
95762306a36Sopenharmony_ci		return ret;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	ret = comedi_alloc_subdevices(dev, 1);
96062306a36Sopenharmony_ci	if (ret)
96162306a36Sopenharmony_ci		return ret;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	/* Analog Input subdevice */
96462306a36Sopenharmony_ci	s = &dev->subdevices[0];
96562306a36Sopenharmony_ci	dev->read_subdev = s;
96662306a36Sopenharmony_ci	s->type		= COMEDI_SUBD_AI;
96762306a36Sopenharmony_ci	s->subdev_flags	= SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
96862306a36Sopenharmony_ci	s->n_chan	= 16;
96962306a36Sopenharmony_ci	s->maxdata	= 0x1000;	/* 12-bit + 1 overflow bit */
97062306a36Sopenharmony_ci	s->range_table	= &range_usbduxfast_ai_range;
97162306a36Sopenharmony_ci	s->insn_read	= usbduxfast_ai_insn_read;
97262306a36Sopenharmony_ci	s->len_chanlist	= s->n_chan;
97362306a36Sopenharmony_ci	s->do_cmdtest	= usbduxfast_ai_cmdtest;
97462306a36Sopenharmony_ci	s->do_cmd	= usbduxfast_ai_cmd;
97562306a36Sopenharmony_ci	s->cancel	= usbduxfast_ai_cancel;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	return 0;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistatic void usbduxfast_detach(struct comedi_device *dev)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	struct usb_interface *intf = comedi_to_usb_interface(dev);
98362306a36Sopenharmony_ci	struct usbduxfast_private *devpriv = dev->private;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	if (!devpriv)
98662306a36Sopenharmony_ci		return;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	mutex_lock(&devpriv->mut);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	usb_set_intfdata(intf, NULL);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	if (devpriv->urb) {
99362306a36Sopenharmony_ci		/* waits until a running transfer is over */
99462306a36Sopenharmony_ci		usb_kill_urb(devpriv->urb);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci		kfree(devpriv->inbuf);
99762306a36Sopenharmony_ci		usb_free_urb(devpriv->urb);
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	kfree(devpriv->duxbuf);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	mutex_unlock(&devpriv->mut);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	mutex_destroy(&devpriv->mut);
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic struct comedi_driver usbduxfast_driver = {
100862306a36Sopenharmony_ci	.driver_name	= "usbduxfast",
100962306a36Sopenharmony_ci	.module		= THIS_MODULE,
101062306a36Sopenharmony_ci	.auto_attach	= usbduxfast_auto_attach,
101162306a36Sopenharmony_ci	.detach		= usbduxfast_detach,
101262306a36Sopenharmony_ci};
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_cistatic int usbduxfast_usb_probe(struct usb_interface *intf,
101562306a36Sopenharmony_ci				const struct usb_device_id *id)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	return comedi_usb_auto_config(intf, &usbduxfast_driver, 0);
101862306a36Sopenharmony_ci}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_cistatic const struct usb_device_id usbduxfast_usb_table[] = {
102162306a36Sopenharmony_ci	/* { USB_DEVICE(0x4b4, 0x8613) }, testing */
102262306a36Sopenharmony_ci	{ USB_DEVICE(0x13d8, 0x0010) },	/* real ID */
102362306a36Sopenharmony_ci	{ USB_DEVICE(0x13d8, 0x0011) },	/* real ID */
102462306a36Sopenharmony_ci	{ }
102562306a36Sopenharmony_ci};
102662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, usbduxfast_usb_table);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic struct usb_driver usbduxfast_usb_driver = {
102962306a36Sopenharmony_ci	.name		= "usbduxfast",
103062306a36Sopenharmony_ci	.probe		= usbduxfast_usb_probe,
103162306a36Sopenharmony_ci	.disconnect	= comedi_usb_auto_unconfig,
103262306a36Sopenharmony_ci	.id_table	= usbduxfast_usb_table,
103362306a36Sopenharmony_ci};
103462306a36Sopenharmony_cimodule_comedi_usb_driver(usbduxfast_driver, usbduxfast_usb_driver);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ciMODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
103762306a36Sopenharmony_ciMODULE_DESCRIPTION("USB-DUXfast, BerndPorr@f2s.com");
103862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
103962306a36Sopenharmony_ciMODULE_FIRMWARE(FIRMWARE);
1040