/* * Driver: usbduxfast * Description: University of Stirling USB DAQ & INCITE Technology Limited * Devices: [ITL] USB-DUX-FAST (usbduxfast) * Author: Bernd Porr <mail@berndporr.me.uk> * Updated: 16 Nov 2019 * Status: stable
*/
/* * I must give credit here to Chris Baugher who * wrote the driver for AT-MIO-16d. I used some parts of this * driver. I also must give credits to David Brownell * who supported me with the USB development. * * Bernd Porr * * * Revision history: * 1.0: Fixed a rounding error in usbduxfast_ai_cmdtest * 0.9: Dropping the first data packet which seems to be from the last transfer. * Buffer overflows in the FX2 are handed over to comedi. * 0.92: Dropping now 4 packets. The quad buffer has to be emptied. * Added insn command basically for testing. Sample rate is * 1MHz/16ch=62.5kHz * 0.99: Ian Abbott pointed out a bug which has been corrected. Thanks! * 0.99a: added external trigger. * 1.00: added firmware kernel request to the driver which fixed * udev coldplug problem
*/
/* * internal addresses of the 8051 processor
*/ #define USBDUXFASTSUB_CPUCS 0xE600
/* * max length of the transfer-buffer for software upload
*/ #define TB_LEN 0x2000
/* * input endpoint number
*/ #define BULKINEP 6
/* * endpoint for the A/D channellist: bulk OUT
*/ #define CHANNELLISTEP 4
/* * number of channels
*/ #define NUMCHANNELS 32
/* * size of the waveform descriptor
*/ #define WAVESIZE 0x20
/* * size of one A/D value
*/ #define SIZEADIN (sizeof(s16))
/* * size of the input-buffer IN BYTES
*/ #define SIZEINBUF 512
/* * 16 bytes
*/ #define SIZEINSNBUF 512
/* * size of the buffer for the dux commands in bytes
*/ #define SIZEOFDUXBUF 256
/* * number of in-URBs which receive the data: min=5
*/ #define NUMOFINBUFFERSHIGH 10
/* * min delay steps for more than one channel * basically when the mux gives up ;-) * * steps at 30MHz in the FX2
*/ #define MIN_SAMPLING_PERIOD 9
/* * max number of 1/30MHz delay steps
*/ #define MAX_SAMPLING_PERIOD 500
/* * number of received packets to ignore before we start handing data * over to comedi, it's quad buffering and we have to ignore 4 packets
*/ #define PACKETS_TO_IGNORE 4
/* * private structure of one subdevice * * this is the structure which holds all the data of this driver * one sub device just now: A/D
*/ struct usbduxfast_private { struct urb *urb; /* BULK-transfer handling: urb */
u8 *duxbuf;
s8 *inbuf; shortint ai_cmd_running; /* asynchronous command is running */ int ignore; /* counter which ignores the first buffers */ struct mutex mut;
};
/* exit if not running a command, do not resubmit urb */ if (!devpriv->ai_cmd_running) return;
switch (urb->status) { case 0:
usbduxfast_ai_handle_urb(dev, s, urb); break;
case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: case -ECONNABORTED: /* after an unlink command, unplug, ... etc */
async->events |= COMEDI_CB_ERROR; break;
default: /* a real error */
dev_err(dev->class_dev, "non-zero urb status received in ai intr context: %d\n",
urb->status);
async->events |= COMEDI_CB_ERROR; break;
}
/* * comedi_handle_events() cannot be used in this driver. The (*cancel) * operation would unlink the urb.
*/ if (async->events & COMEDI_CB_CANCEL_MASK)
usbduxfast_ai_stop(dev, 0);
if (cmd->chanlist_len > 3 && cmd->chanlist_len != 16) {
dev_err(dev->class_dev, "unsupported combination of channels\n"); return -EINVAL;
}
for (i = 0; i < cmd->chanlist_len; ++i) { unsignedint chan = CR_CHAN(cmd->chanlist[i]); unsignedint gain = CR_RANGE(cmd->chanlist[i]);
if (chan != i) {
dev_err(dev->class_dev, "channels are not consecutive\n"); return -EINVAL;
} if (gain != gain0 && cmd->chanlist_len > 3) {
dev_err(dev->class_dev, "gain must be the same for all channels\n"); return -EINVAL;
}
} return 0;
}
/* Step 5: check channel list if it exists */ if (cmd->chanlist && cmd->chanlist_len > 0)
err |= usbduxfast_ai_check_chanlist(dev, s, cmd); if (err) return 5;
/* * for external trigger: looping in this state until * the RDY0 pin becomes zero
*/
/* we loop here until ready has been set */ if (cmd->start_src == TRIG_EXT) { /* branch back to state 0 */ /* deceision state w/o data */ /* RDY0 = 0 */
usbduxfast_cmd_data(dev, 0, 0x01, 0x01, rngmask, 0x00);
} else { /* we just proceed to state 1 */
usbduxfast_cmd_data(dev, 0, 0x01, 0x00, rngmask, 0x00);
}
if (steps < MIN_SAMPLING_PERIOD) { /* for fast single channel aqu without mux */ if (steps <= 1) { /* * we just stay here at state 1 and rexecute * the same state this gives us 30MHz sampling * rate
*/
/* branch back to state 1 */ /* deceision state with data */ /* doesn't matter */
usbduxfast_cmd_data(dev, 1,
0x89, 0x03, rngmask, 0xff);
} else { /* * we loop through two states: data and delay * max rate is 15MHz
*/ /* data */ /* doesn't matter */
usbduxfast_cmd_data(dev, 1, steps - 1,
0x02, rngmask, 0x00);
/* branch back to state 1 */ /* deceision state w/o data */ /* doesn't matter */
usbduxfast_cmd_data(dev, 2,
0x09, 0x01, rngmask, 0xff);
}
} else { /* * we loop through 3 states: 2x delay and 1x data * this gives a min sampling rate of 60kHz
*/
/* we have 1 state with duration 1 */
steps = steps - 1;
/* do the first part of the delay */
usbduxfast_cmd_data(dev, 1,
steps / 2, 0x00, rngmask, 0x00);
/* and the second part */
usbduxfast_cmd_data(dev, 2, steps - steps / 2,
0x00, rngmask, 0x00);
/* get the data and branch back */
/* branch back to state 1 */ /* deceision state w data */ /* doesn't matter */
usbduxfast_cmd_data(dev, 3,
0x09, 0x03, rngmask, 0xff);
} break;
case 2: /* * two channels * commit data to the FIFO
*/
case 3: /* * three channels
*/ for (j = 0; j < 1; j++) { int index = j * 2;
if (CR_RANGE(cmd->chanlist[j]) > 0)
rngmask = 0xff - 0x04; else
rngmask = 0xff; /* * commit data to the FIFO and do the first part * of the delay
*/ /* data */ /* no change */
usbduxfast_cmd_data(dev, index, steps / 2,
0x02, rngmask, 0x00);
/* do the second part of the delay */ /* no data */ /* reset */
usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
0x00, (0xff - 0x02) & rngmask, 0x00);
case 16: if (CR_RANGE(cmd->chanlist[0]) > 0)
rngmask = 0xff - 0x04; else
rngmask = 0xff;
if (cmd->start_src == TRIG_EXT) { /* * we loop here until ready has been set
*/
/* branch back to state 0 */ /* deceision state w/o data */ /* reset */ /* RDY0 = 0 */
usbduxfast_cmd_data(dev, 0, 0x01, 0x01,
(0xff - 0x02) & rngmask, 0x00);
} else { /* * we just proceed to state 1
*/
/* * Mode 0 is used to get a single conversion on demand.
*/ staticint usbduxfast_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsignedint *data)
{ struct usb_device *usb = comedi_to_usb_dev(dev); struct usbduxfast_private *devpriv = dev->private; unsignedint chan = CR_CHAN(insn->chanspec); unsignedint range = CR_RANGE(insn->chanspec);
u8 rngmask = range ? (0xff - 0x04) : 0xff; int i, j, n, actual_length; int ret;
mutex_lock(&devpriv->mut);
if (devpriv->ai_cmd_running) {
dev_err(dev->class_dev, "ai_insn_read not possible, async cmd is running\n");
mutex_unlock(&devpriv->mut); return -EBUSY;
}
/* set command for the first channel */
/* commit data to the FIFO */ /* data */
usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
/* do the first part of the delay */
usbduxfast_cmd_data(dev, 1, 0x0c, 0x00, 0xfe & rngmask, 0x00);
usbduxfast_cmd_data(dev, 2, 0x01, 0x00, 0xfe & rngmask, 0x00);
usbduxfast_cmd_data(dev, 3, 0x01, 0x00, 0xfe & rngmask, 0x00);
usbduxfast_cmd_data(dev, 4, 0x01, 0x00, 0xfe & rngmask, 0x00);
/* second part */
usbduxfast_cmd_data(dev, 5, 0x0c, 0x00, rngmask, 0x00);
usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS); if (ret < 0) {
mutex_unlock(&devpriv->mut); return ret;
}
for (i = 0; i < PACKETS_TO_IGNORE; i++) {
ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
devpriv->inbuf, SIZEINBUF,
&actual_length, 10000); if (ret < 0) {
dev_err(dev->class_dev, "insn timeout, no data\n");
mutex_unlock(&devpriv->mut); return ret;
}
}
for (i = 0; i < insn->n;) {
ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
devpriv->inbuf, SIZEINBUF,
&actual_length, 10000); if (ret < 0) {
dev_err(dev->class_dev, "insn data error: %d\n", ret);
mutex_unlock(&devpriv->mut); return ret;
}
n = actual_length / sizeof(u16); if ((n % 16) != 0) {
dev_err(dev->class_dev, "insn data packet corrupted\n");
mutex_unlock(&devpriv->mut); return -EINVAL;
} for (j = chan; (j < n) && (i < insn->n); j = j + 16) {
data[i] = ((u16 *)(devpriv->inbuf))[j];
i++;
}
}
if (size > FIRMWARE_MAX_LEN) {
dev_err(dev->class_dev, "firmware binary too large for FX2\n"); return -ENOMEM;
}
/* we generate a local buffer for the firmware */
buf = kmemdup(data, size, GFP_KERNEL); if (!buf) return -ENOMEM;
/* we need a malloc'ed buffer for usb_control_msg() */
tmp = kmalloc(1, GFP_KERNEL); if (!tmp) {
kfree(buf); return -ENOMEM;
}
/* stop the current firmware on the device */
*tmp = 1; /* 7f92 to one */
ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
USBDUXFASTSUB_FIRMWARE,
VENDOR_DIR_OUT,
USBDUXFASTSUB_CPUCS, 0x0000,
tmp, 1,
EZTIMEOUT); if (ret < 0) {
dev_err(dev->class_dev, "can not stop firmware\n"); goto done;
}
/* upload the new firmware to the device */
ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
USBDUXFASTSUB_FIRMWARE,
VENDOR_DIR_OUT,
0, 0x0000,
buf, size,
EZTIMEOUT); if (ret < 0) {
dev_err(dev->class_dev, "firmware upload failed\n"); goto done;
}
/* start the new firmware on the device */
*tmp = 0; /* 7f92 to zero */
ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
USBDUXFASTSUB_FIRMWARE,
VENDOR_DIR_OUT,
USBDUXFASTSUB_CPUCS, 0x0000,
tmp, 1,
EZTIMEOUT); if (ret < 0)
dev_err(dev->class_dev, "can not start firmware\n");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.