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