diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/usbdux.c')
-rw-r--r-- | drivers/staging/comedi/drivers/usbdux.c | 2485 |
1 files changed, 876 insertions, 1609 deletions
diff --git a/drivers/staging/comedi/drivers/usbdux.c b/drivers/staging/comedi/drivers/usbdux.c index 279e5bd493fa..701ad1a69394 100644 --- a/drivers/staging/comedi/drivers/usbdux.c +++ b/drivers/staging/comedi/drivers/usbdux.c @@ -78,9 +78,6 @@ sampling rate. If you sample two channels you get 4kHz and so on. * */ -/* generates loads of debug info */ -/* #define NOISY_DUX_DEBUGBUG */ - #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -94,42 +91,29 @@ sampling rate. If you sample two channels you get 4kHz and so on. #include "comedi_fc.h" -/* timeout for the USB-transfer in ms*/ -#define BULK_TIMEOUT 1000 - -/* constants for "firmware" upload and download */ -#define FIRMWARE "usbdux_firmware.bin" -#define USBDUXSUB_FIRMWARE 0xA0 -#define VENDOR_DIR_IN 0xC0 -#define VENDOR_DIR_OUT 0x40 - -/* internal addresses of the 8051 processor */ -#define USBDUXSUB_CPUCS 0xE600 - -/* - * the minor device number, major is 180 only for debugging purposes and to - * upload special firmware (programming the eeprom etc) which is not compatible - * with the comedi framwork - */ -#define USBDUXSUB_MINOR 32 - -/* max lenghth of the transfer-buffer for software upload */ -#define TB_LEN 0x2000 - -/* Input endpoint number: ISO/IRQ */ -#define ISOINEP 6 - -/* Output endpoint number: ISO/IRQ */ -#define ISOOUTEP 2 - -/* This EP sends DUX commands to USBDUX */ -#define COMMAND_OUT_EP 1 - -/* This EP receives the DUX commands from USBDUX */ -#define COMMAND_IN_EP 8 - -/* Output endpoint for PWM */ -#define PWM_EP 4 +/* constants for firmware upload and download */ +#define USBDUX_FIRMWARE "usbdux_firmware.bin" +#define USBDUX_FIRMWARE_MAX_LEN 0x2000 +#define USBDUX_FIRMWARE_CMD 0xa0 +#define VENDOR_DIR_IN 0xc0 +#define VENDOR_DIR_OUT 0x40 +#define USBDUX_CPU_CS 0xe600 + +/* usbdux bulk transfer commands */ +#define USBDUX_CMD_MULT_AI 0 +#define USBDUX_CMD_AO 1 +#define USBDUX_CMD_DIO_CFG 2 +#define USBDUX_CMD_DIO_BITS 3 +#define USBDUX_CMD_SINGLE_AI 4 +#define USBDUX_CMD_TIMER_RD 5 +#define USBDUX_CMD_TIMER_WR 6 +#define USBDUX_CMD_PWM_ON 7 +#define USBDUX_CMD_PWM_OFF 8 + +#define USBDUX_NUM_AO_CHAN 4 + +/* timeout for the USB-transfer in ms */ +#define BULK_TIMEOUT 1000 /* 300Hz max frequ under PWM */ #define MIN_PWM_PERIOD ((long)(1E9/300)) @@ -137,9 +121,6 @@ sampling rate. If you sample two channels you get 4kHz and so on. /* Default PWM frequency */ #define PWM_DEFAULT_PERIOD ((long)(1E9/100)) -/* Number of channels */ -#define NUMCHANNELS 8 - /* Size of one A/D value */ #define SIZEADIN ((sizeof(int16_t))) @@ -152,9 +133,6 @@ sampling rate. If you sample two channels you get 4kHz and so on. /* 16 bytes. */ #define SIZEINSNBUF 16 -/* Number of DA channels */ -#define NUMOUTCHANNELS 8 - /* size of one value for the D/A converter: channel and value */ #define SIZEDAOUT ((sizeof(int8_t)+sizeof(int16_t))) @@ -185,101 +163,56 @@ sampling rate. If you sample two channels you get 4kHz and so on. /* must have more buffers due to buggy USB ctr */ #define NUMOFOUTBUFFERSHIGH 10 -/* Total number of usbdux devices */ -#define NUMUSBDUX 16 - -/* Analogue in subdevice */ -#define SUBDEV_AD 0 - -/* Analogue out subdevice */ -#define SUBDEV_DA 1 - -/* Digital I/O */ -#define SUBDEV_DIO 2 - -/* counter */ -#define SUBDEV_COUNTER 3 - -/* timer aka pwm output */ -#define SUBDEV_PWM 4 - /* number of retries to get the right dux command */ #define RETRIES 10 -/**************************************************/ -/* comedi constants */ -static const struct comedi_lrange range_usbdux_ai_range = { 4, { - BIP_RANGE - (4.096), - BIP_RANGE(4.096 - / 2), - UNI_RANGE - (4.096), - UNI_RANGE(4.096 - / 2) - } +static const struct comedi_lrange range_usbdux_ai_range = { + 4, { + BIP_RANGE(4.096), + BIP_RANGE(4.096 / 2), + UNI_RANGE(4.096), + UNI_RANGE(4.096 / 2) + } }; -static const struct comedi_lrange range_usbdux_ao_range = { 2, { - BIP_RANGE - (4.096), - UNI_RANGE - (4.096), - } +static const struct comedi_lrange range_usbdux_ao_range = { + 2, { + BIP_RANGE(4.096), + UNI_RANGE(4.096) + } }; -/* - * 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 usbduxsub { - /* attached? */ - int attached; - /* is it associated with a subdevice? */ - int probed; - /* pointer to the usb-device */ - struct usb_device *usbdev; +struct usbdux_private { /* actual number of in-buffers */ - int num_in_buffers; + int n_ai_urbs; /* actual number of out-buffers */ - int num_out_buffers; + int n_ao_urbs; /* ISO-transfer handling: buffers */ - struct urb **urb_in; - struct urb **urb_out; + struct urb **ai_urbs; + struct urb **ao_urbs; /* pwm-transfer handling */ - struct urb *urb_pwm; + struct urb *pwm_urb; /* PWM period */ unsigned int pwm_period; /* PWM internal delay for the GPIF in the FX2 */ - int8_t pwn_delay; + int8_t pwm_delay; /* size of the PWM buffer which holds the bit pattern */ - int size_pwm_buf; + int pwm_buf_sz; /* input buffer for the ISO-transfer */ - int16_t *in_buffer; + int16_t *in_buf; /* input buffer for single insn */ - int16_t *insn_buffer; - /* output buffer for single DA outputs */ - int16_t *out_buffer; - /* interface number */ - int ifnum; - /* interface structure in 2.6 */ - struct usb_interface *interface; - /* comedi device for the interrupt context */ - struct comedi_device *comedidev; - /* is it USB_SPEED_HIGH or not? */ - short int high_speed; - /* asynchronous command is running */ - short int ai_cmd_running; - short int ao_cmd_running; - /* pwm is running */ - short int pwm_cmd_running; - /* continous acquisition */ - short int ai_continous; - short int ao_continous; + int16_t *insn_buf; + + int8_t ao_chanlist[USBDUX_NUM_AO_CHAN]; + unsigned int ao_readback[USBDUX_NUM_AO_CHAN]; + + unsigned int high_speed:1; + unsigned int ai_cmd_running:1; + unsigned int ai_continous:1; + unsigned int ao_cmd_running:1; + unsigned int ao_continous:1; + unsigned int pwm_cmd_running:1; + /* number of samples to acquire */ int ai_sample_count; int ao_sample_count; @@ -291,132 +224,62 @@ struct usbduxsub { unsigned int ao_counter; /* interval in frames/uframes */ unsigned int ai_interval; - /* D/A commands */ - int8_t *dac_commands; /* commands */ int8_t *dux_commands; struct semaphore sem; }; -/* - * The pointer to the private usb-data of the driver is also the private data - * for the comedi-device. This has to be global as the usb subsystem needs - * global variables. The other reason is that this structure must be there - * _before_ any comedi command is issued. The usb subsystem must be initialised - * before comedi can access it. - */ -static struct usbduxsub usbduxsub[NUMUSBDUX]; - -static DEFINE_SEMAPHORE(start_stop_sem); - -/* - * Stops the data acquision - * It should be safe to call this function from any context - */ -static int usbduxsub_unlink_inurbs(struct usbduxsub *usbduxsub_tmp) +static void usbdux_unlink_urbs(struct urb **urbs, int num_urbs) { - int i = 0; - int err = 0; + int i; - if (usbduxsub_tmp && usbduxsub_tmp->urb_in) { - for (i = 0; i < usbduxsub_tmp->num_in_buffers; i++) { - if (usbduxsub_tmp->urb_in[i]) { - /* We wait here until all transfers have been - * cancelled. */ - usb_kill_urb(usbduxsub_tmp->urb_in[i]); - } - dev_dbg(&usbduxsub_tmp->interface->dev, - "comedi: usbdux: unlinked InURB %d, err=%d\n", - i, err); - } - } - return err; + for (i = 0; i < num_urbs; i++) + usb_kill_urb(urbs[i]); } -/* - * This will stop a running acquisition operation - * Is called from within this driver from both the - * interrupt context and from comedi - */ -static int usbdux_ai_stop(struct usbduxsub *this_usbduxsub, int do_unlink) +static void usbdux_ai_stop(struct comedi_device *dev, int do_unlink) { - int ret = 0; - - if (!this_usbduxsub) { - pr_err("comedi?: usbdux_ai_stop: this_usbduxsub=NULL!\n"); - return -EFAULT; - } - dev_dbg(&this_usbduxsub->interface->dev, "comedi: usbdux_ai_stop\n"); + struct usbdux_private *devpriv = dev->private; - if (do_unlink) { - /* stop aquistion */ - ret = usbduxsub_unlink_inurbs(this_usbduxsub); - } + if (do_unlink && devpriv->ai_urbs) + usbdux_unlink_urbs(devpriv->ai_urbs, devpriv->n_ai_urbs); - this_usbduxsub->ai_cmd_running = 0; - - return ret; + devpriv->ai_cmd_running = 0; } -/* - * This will cancel a running acquisition operation. - * This is called by comedi but never from inside the driver. - */ static int usbdux_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s) { - struct usbduxsub *this_usbduxsub; - int res = 0; - - /* force unlink of all urbs */ - this_usbduxsub = dev->private; - if (!this_usbduxsub) - return -EFAULT; - - dev_dbg(&this_usbduxsub->interface->dev, "comedi: usbdux_ai_cancel\n"); + struct usbdux_private *devpriv = dev->private; /* prevent other CPUs from submitting new commands just now */ - down(&this_usbduxsub->sem); - if (!(this_usbduxsub->probed)) { - up(&this_usbduxsub->sem); - return -ENODEV; - } + down(&devpriv->sem); /* unlink only if the urb really has been submitted */ - res = usbdux_ai_stop(this_usbduxsub, this_usbduxsub->ai_cmd_running); - up(&this_usbduxsub->sem); - return res; + usbdux_ai_stop(dev, devpriv->ai_cmd_running); + up(&devpriv->sem); + + return 0; } /* analogue IN - interrupt service routine */ static void usbduxsub_ai_isoc_irq(struct urb *urb) { + struct comedi_device *dev = urb->context; + struct comedi_subdevice *s = dev->read_subdev; + struct usbdux_private *devpriv = dev->private; int i, err, n; - struct usbduxsub *this_usbduxsub; - struct comedi_device *this_comedidev; - struct comedi_subdevice *s; - - /* the context variable points to the subdevice */ - this_comedidev = urb->context; - /* the private structure of the subdevice is struct usbduxsub */ - this_usbduxsub = this_comedidev->private; - /* subdevice which is the AD converter */ - s = &this_comedidev->subdevices[SUBDEV_AD]; /* first we test if something unusual has just happened */ switch (urb->status) { case 0: /* copy the result in the transfer buffer */ - memcpy(this_usbduxsub->in_buffer, - urb->transfer_buffer, SIZEINBUF); + memcpy(devpriv->in_buf, urb->transfer_buffer, SIZEINBUF); break; case -EILSEQ: /* error in the ISOchronous data */ /* we don't copy the data into the transfer buffer */ /* and recycle the last data byte */ - dev_dbg(&urb->dev->dev, - "comedi%d: usbdux: CRC error in ISO IN stream.\n", - this_usbduxsub->comedidev->minor); - + dev_dbg(dev->class_dev, "CRC error in ISO IN stream\n"); break; case -ECONNRESET: @@ -424,29 +287,27 @@ static void usbduxsub_ai_isoc_irq(struct urb *urb) case -ESHUTDOWN: case -ECONNABORTED: /* happens after an unlink command */ - if (this_usbduxsub->ai_cmd_running) { - /* we are still running a command */ - /* tell this comedi */ + if (devpriv->ai_cmd_running) { s->async->events |= COMEDI_CB_EOA; s->async->events |= COMEDI_CB_ERROR; - comedi_event(this_usbduxsub->comedidev, s); + comedi_event(dev, s); /* stop the transfer w/o unlink */ - usbdux_ai_stop(this_usbduxsub, 0); + usbdux_ai_stop(dev, 0); } return; default: /* a real error on the bus */ /* pass error to comedi if we are really running a command */ - if (this_usbduxsub->ai_cmd_running) { - dev_err(&urb->dev->dev, - "Non-zero urb status received in ai intr " - "context: %d\n", urb->status); + if (devpriv->ai_cmd_running) { + dev_err(dev->class_dev, + "Non-zero urb status received in ai intr context: %d\n", + urb->status); s->async->events |= COMEDI_CB_EOA; s->async->events |= COMEDI_CB_ERROR; - comedi_event(this_usbduxsub->comedidev, s); + comedi_event(dev, s); /* don't do an unlink here */ - usbdux_ai_stop(this_usbduxsub, 0); + usbdux_ai_stop(dev, 0); } return; } @@ -455,7 +316,7 @@ static void usbduxsub_ai_isoc_irq(struct urb *urb) * at this point we are reasonably sure that nothing dodgy has happened * are we running a command? */ - if (unlikely((!(this_usbduxsub->ai_cmd_running)))) { + if (unlikely(!devpriv->ai_cmd_running)) { /* * not running a command, do not continue execution if no * asynchronous command is running in particular not resubmit @@ -463,144 +324,101 @@ static void usbduxsub_ai_isoc_irq(struct urb *urb) return; } - urb->dev = this_usbduxsub->usbdev; + urb->dev = comedi_to_usb_dev(dev); /* resubmit the urb */ err = usb_submit_urb(urb, GFP_ATOMIC); if (unlikely(err < 0)) { - dev_err(&urb->dev->dev, - "comedi_: urb resubmit failed in int-context! err=%d\n", - err); + dev_err(dev->class_dev, + "urb resubmit failed in int-context! err=%d\n", err); if (err == -EL2NSYNC) - dev_err(&urb->dev->dev, - "buggy USB host controller or bug in IRQ " - "handler!\n"); + dev_err(dev->class_dev, + "buggy USB host controller or bug in IRQ handler!\n"); s->async->events |= COMEDI_CB_EOA; s->async->events |= COMEDI_CB_ERROR; - comedi_event(this_usbduxsub->comedidev, s); + comedi_event(dev, s); /* don't do an unlink here */ - usbdux_ai_stop(this_usbduxsub, 0); + usbdux_ai_stop(dev, 0); return; } - this_usbduxsub->ai_counter--; - if (likely(this_usbduxsub->ai_counter > 0)) + devpriv->ai_counter--; + if (likely(devpriv->ai_counter > 0)) return; /* timer zero, transfer measurements to comedi */ - this_usbduxsub->ai_counter = this_usbduxsub->ai_timer; + devpriv->ai_counter = devpriv->ai_timer; /* test, if we transmit only a fixed number of samples */ - if (!(this_usbduxsub->ai_continous)) { + if (!devpriv->ai_continous) { /* not continuous, fixed number of samples */ - this_usbduxsub->ai_sample_count--; + devpriv->ai_sample_count--; /* all samples received? */ - if (this_usbduxsub->ai_sample_count < 0) { + if (devpriv->ai_sample_count < 0) { /* prevent a resubmit next time */ - usbdux_ai_stop(this_usbduxsub, 0); + usbdux_ai_stop(dev, 0); /* say comedi that the acquistion is over */ s->async->events |= COMEDI_CB_EOA; - comedi_event(this_usbduxsub->comedidev, s); + comedi_event(dev, s); return; } } /* get the data from the USB bus and hand it over to comedi */ n = s->async->cmd.chanlist_len; for (i = 0; i < n; i++) { + unsigned int range = CR_RANGE(s->async->cmd.chanlist[i]); + int16_t val = le16_to_cpu(devpriv->in_buf[i]); + + /* bipolar data is two's-complement */ + if (comedi_range_is_bipolar(s, range)) + val ^= ((s->maxdata + 1) >> 1); + /* transfer data */ - if (CR_RANGE(s->async->cmd.chanlist[i]) <= 1) { - err = comedi_buf_put - (s->async, - le16_to_cpu(this_usbduxsub->in_buffer[i]) ^ 0x800); - } else { - err = comedi_buf_put - (s->async, - le16_to_cpu(this_usbduxsub->in_buffer[i])); - } + err = comedi_buf_put(s->async, val); if (unlikely(err == 0)) { /* buffer overflow */ - usbdux_ai_stop(this_usbduxsub, 0); + usbdux_ai_stop(dev, 0); return; } } /* tell comedi that data is there */ s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; - comedi_event(this_usbduxsub->comedidev, s); -} - -static int usbduxsub_unlink_outurbs(struct usbduxsub *usbduxsub_tmp) -{ - int i = 0; - int err = 0; - - if (usbduxsub_tmp && usbduxsub_tmp->urb_out) { - for (i = 0; i < usbduxsub_tmp->num_out_buffers; i++) { - if (usbduxsub_tmp->urb_out[i]) - usb_kill_urb(usbduxsub_tmp->urb_out[i]); - - dev_dbg(&usbduxsub_tmp->interface->dev, - "comedi: usbdux: unlinked OutURB %d: res=%d\n", - i, err); - } - } - return err; + comedi_event(dev, s); } -/* This will cancel a running acquisition operation - * in any context. - */ -static int usbdux_ao_stop(struct usbduxsub *this_usbduxsub, int do_unlink) +static void usbdux_ao_stop(struct comedi_device *dev, int do_unlink) { - int ret = 0; - - if (!this_usbduxsub) - return -EFAULT; - dev_dbg(&this_usbduxsub->interface->dev, "comedi: usbdux_ao_cancel\n"); - - if (do_unlink) - ret = usbduxsub_unlink_outurbs(this_usbduxsub); + struct usbdux_private *devpriv = dev->private; - this_usbduxsub->ao_cmd_running = 0; + if (do_unlink && devpriv->ao_urbs) + usbdux_unlink_urbs(devpriv->ao_urbs, devpriv->n_ao_urbs); - return ret; + devpriv->ao_cmd_running = 0; } -/* force unlink, is called by comedi */ static int usbdux_ao_cancel(struct comedi_device *dev, struct comedi_subdevice *s) { - struct usbduxsub *this_usbduxsub = dev->private; - int res = 0; - - if (!this_usbduxsub) - return -EFAULT; + struct usbdux_private *devpriv = dev->private; /* prevent other CPUs from submitting a command just now */ - down(&this_usbduxsub->sem); - if (!(this_usbduxsub->probed)) { - up(&this_usbduxsub->sem); - return -ENODEV; - } + down(&devpriv->sem); /* unlink only if it is really running */ - res = usbdux_ao_stop(this_usbduxsub, this_usbduxsub->ao_cmd_running); - up(&this_usbduxsub->sem); - return res; + usbdux_ao_stop(dev, devpriv->ao_cmd_running); + up(&devpriv->sem); + + return 0; } static void usbduxsub_ao_isoc_irq(struct urb *urb) { - int i, ret; + struct comedi_device *dev = urb->context; + struct comedi_subdevice *s = dev->write_subdev; + struct usbdux_private *devpriv = dev->private; int8_t *datap; - struct usbduxsub *this_usbduxsub; - struct comedi_device *this_comedidev; - struct comedi_subdevice *s; - - /* the context variable points to the subdevice */ - this_comedidev = urb->context; - /* the private structure of the subdevice is struct usbduxsub */ - this_usbduxsub = this_comedidev->private; - - s = &this_comedidev->subdevices[SUBDEV_DA]; + int len; + int ret; + int i; switch (urb->status) { case 0: @@ -613,246 +431,131 @@ static void usbduxsub_ao_isoc_irq(struct urb *urb) case -ECONNABORTED: /* after an unlink command, unplug, ... etc */ /* no unlink needed here. Already shutting down. */ - if (this_usbduxsub->ao_cmd_running) { + if (devpriv->ao_cmd_running) { s->async->events |= COMEDI_CB_EOA; - comedi_event(this_usbduxsub->comedidev, s); - usbdux_ao_stop(this_usbduxsub, 0); + comedi_event(dev, s); + usbdux_ao_stop(dev, 0); } return; default: /* a real error */ - if (this_usbduxsub->ao_cmd_running) { - dev_err(&urb->dev->dev, - "comedi_: Non-zero urb status received in ao " - "intr context: %d\n", urb->status); + if (devpriv->ao_cmd_running) { + dev_err(dev->class_dev, + "Non-zero urb status received in ao intr context: %d\n", + urb->status); s->async->events |= COMEDI_CB_ERROR; s->async->events |= COMEDI_CB_EOA; - comedi_event(this_usbduxsub->comedidev, s); + comedi_event(dev, s); /* we do an unlink if we are in the high speed mode */ - usbdux_ao_stop(this_usbduxsub, 0); + usbdux_ao_stop(dev, 0); } return; } /* are we actually running? */ - if (!(this_usbduxsub->ao_cmd_running)) + if (!devpriv->ao_cmd_running) return; /* normal operation: executing a command in this subdevice */ - this_usbduxsub->ao_counter--; - if ((int)this_usbduxsub->ao_counter <= 0) { + devpriv->ao_counter--; + if ((int)devpriv->ao_counter <= 0) { /* timer zero */ - this_usbduxsub->ao_counter = this_usbduxsub->ao_timer; + devpriv->ao_counter = devpriv->ao_timer; /* handle non continous acquisition */ - if (!(this_usbduxsub->ao_continous)) { + if (!devpriv->ao_continous) { /* fixed number of samples */ - this_usbduxsub->ao_sample_count--; - if (this_usbduxsub->ao_sample_count < 0) { + devpriv->ao_sample_count--; + if (devpriv->ao_sample_count < 0) { /* all samples transmitted */ - usbdux_ao_stop(this_usbduxsub, 0); + usbdux_ao_stop(dev, 0); s->async->events |= COMEDI_CB_EOA; - comedi_event(this_usbduxsub->comedidev, s); + comedi_event(dev, s); /* no resubmit of the urb */ return; } } + /* transmit data to the USB bus */ - ((uint8_t *) (urb->transfer_buffer))[0] = - s->async->cmd.chanlist_len; + datap = urb->transfer_buffer; + len = s->async->cmd.chanlist_len; + *datap++ = len; for (i = 0; i < s->async->cmd.chanlist_len; i++) { - short temp; - if (i >= NUMOUTCHANNELS) - break; + unsigned int chan = devpriv->ao_chanlist[i]; + short val; - /* pointer to the DA */ - datap = - (&(((int8_t *) urb->transfer_buffer)[i * 3 + 1])); - /* get the data from comedi */ - ret = comedi_buf_get(s->async, &temp); - datap[0] = temp; - datap[1] = temp >> 8; - datap[2] = this_usbduxsub->dac_commands[i]; - /* printk("data[0]=%x, data[1]=%x, data[2]=%x\n", */ - /* datap[0],datap[1],datap[2]); */ + ret = comedi_buf_get(s->async, &val); if (ret < 0) { - dev_err(&urb->dev->dev, - "comedi: buffer underflow\n"); - s->async->events |= COMEDI_CB_EOA; - s->async->events |= COMEDI_CB_OVERFLOW; + dev_err(dev->class_dev, "buffer underflow\n"); + s->async->events |= (COMEDI_CB_EOA | + COMEDI_CB_OVERFLOW); } - /* transmit data to comedi */ + /* pointer to the DA */ + *datap++ = val & 0xff; + *datap++ = (val >> 8) & 0xff; + *datap++ = chan; + devpriv->ao_readback[chan] = val; + s->async->events |= COMEDI_CB_BLOCK; - comedi_event(this_usbduxsub->comedidev, s); + comedi_event(dev, s); } } urb->transfer_buffer_length = SIZEOUTBUF; - urb->dev = this_usbduxsub->usbdev; + urb->dev = comedi_to_usb_dev(dev); urb->status = 0; - if (this_usbduxsub->ao_cmd_running) { - if (this_usbduxsub->high_speed) { - /* uframes */ - urb->interval = 8; - } else { - /* frames */ - urb->interval = 1; - } + if (devpriv->ao_cmd_running) { + if (devpriv->high_speed) + urb->interval = 8; /* uframes */ + else + urb->interval = 1; /* frames */ urb->number_of_packets = 1; urb->iso_frame_desc[0].offset = 0; urb->iso_frame_desc[0].length = SIZEOUTBUF; urb->iso_frame_desc[0].status = 0; ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret < 0) { - dev_err(&urb->dev->dev, - "comedi_: ao urb resubm failed in int-cont. " - "ret=%d", ret); + dev_err(dev->class_dev, + "ao urb resubm failed in int-cont. ret=%d", + ret); if (ret == EL2NSYNC) - dev_err(&urb->dev->dev, - "buggy USB host controller or bug in " - "IRQ handling!\n"); + dev_err(dev->class_dev, + "buggy USB host controller or bug in IRQ handling!\n"); s->async->events |= COMEDI_CB_EOA; s->async->events |= COMEDI_CB_ERROR; - comedi_event(this_usbduxsub->comedidev, s); + comedi_event(dev, s); /* don't do an unlink here */ - usbdux_ao_stop(this_usbduxsub, 0); + usbdux_ao_stop(dev, 0); } } } -#define FIRMWARE_MAX_LEN 0x2000 - -static int usbdux_firmware_upload(struct comedi_device *dev, - const u8 *data, size_t size, - unsigned long context) +static int usbdux_submit_urbs(struct comedi_device *dev, + struct urb **urbs, int num_urbs, + int input_urb) { - struct usbduxsub *usbduxsub = dev->private; - struct usb_device *usb = usbduxsub->usbdev; - uint8_t *buf; - uint8_t *tmp; + struct usb_device *usb = comedi_to_usb_dev(dev); + struct usbdux_private *devpriv = dev->private; + struct urb *urb; int ret; - - if (!data) - return 0; - - if (size > FIRMWARE_MAX_LEN) { - dev_err(&usbduxsub->interface->dev, - "usbdux firmware binary it 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), - USBDUXSUB_FIRMWARE, - VENDOR_DIR_OUT, - USBDUXSUB_CPUCS, 0x0000, - tmp, 1, - BULK_TIMEOUT); - if (ret < 0) { - dev_err(&usbduxsub->interface->dev, - "comedi_: can not stop firmware\n"); - goto done; - } - - /* upload the new firmware to the device */ - ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0), - USBDUXSUB_FIRMWARE, - VENDOR_DIR_OUT, - 0, 0x0000, - buf, size, - BULK_TIMEOUT); - if (ret < 0) { - dev_err(&usbduxsub->interface->dev, - "comedi_: 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), - USBDUXSUB_FIRMWARE, - VENDOR_DIR_OUT, - USBDUXSUB_CPUCS, 0x0000, - tmp, 1, - BULK_TIMEOUT); - if (ret < 0) - dev_err(&usbduxsub->interface->dev, - "comedi_: can not start firmware\n"); - -done: - kfree(tmp); - kfree(buf); - return ret; -} - -static int usbduxsub_submit_inurbs(struct usbduxsub *usbduxsub) -{ - int i, err_flag; - - if (!usbduxsub) - return -EFAULT; + int i; /* Submit all URBs and start the transfer on the bus */ - for (i = 0; i < usbduxsub->num_in_buffers; i++) { - /* in case of a resubmission after an unlink... */ - usbduxsub->urb_in[i]->interval = usbduxsub->ai_interval; - usbduxsub->urb_in[i]->context = usbduxsub->comedidev; - usbduxsub->urb_in[i]->dev = usbduxsub->usbdev; - usbduxsub->urb_in[i]->status = 0; - usbduxsub->urb_in[i]->transfer_flags = URB_ISO_ASAP; - dev_dbg(&usbduxsub->interface->dev, - "comedi%d: submitting in-urb[%d]: %p,%p intv=%d\n", - usbduxsub->comedidev->minor, i, - (usbduxsub->urb_in[i]->context), - (usbduxsub->urb_in[i]->dev), - (usbduxsub->urb_in[i]->interval)); - err_flag = usb_submit_urb(usbduxsub->urb_in[i], GFP_ATOMIC); - if (err_flag) { - dev_err(&usbduxsub->interface->dev, - "comedi_: ai: usb_submit_urb(%d) error %d\n", - i, err_flag); - return err_flag; - } - } - return 0; -} + for (i = 0; i < num_urbs; i++) { + urb = urbs[i]; -static int usbduxsub_submit_outurbs(struct usbduxsub *usbduxsub) -{ - int i, err_flag; - - if (!usbduxsub) - return -EFAULT; - - for (i = 0; i < usbduxsub->num_out_buffers; i++) { - dev_dbg(&usbduxsub->interface->dev, - "comedi_: submitting out-urb[%d]\n", i); /* in case of a resubmission after an unlink... */ - usbduxsub->urb_out[i]->context = usbduxsub->comedidev; - usbduxsub->urb_out[i]->dev = usbduxsub->usbdev; - usbduxsub->urb_out[i]->status = 0; - usbduxsub->urb_out[i]->transfer_flags = URB_ISO_ASAP; - err_flag = usb_submit_urb(usbduxsub->urb_out[i], GFP_ATOMIC); - if (err_flag) { - dev_err(&usbduxsub->interface->dev, - "comedi_: ao: usb_submit_urb(%d) error %d\n", - i, err_flag); - return err_flag; - } + if (input_urb) + urb->interval = devpriv->ai_interval; + urb->context = dev; + urb->dev = usb; + urb->status = 0; + urb->transfer_flags = URB_ISO_ASAP; + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + return ret; } return 0; } @@ -860,13 +563,10 @@ static int usbduxsub_submit_outurbs(struct usbduxsub *usbduxsub) static int usbdux_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) { - struct usbduxsub *this_usbduxsub = dev->private; + struct usbdux_private *this_usbduxsub = dev->private; int err = 0, i; unsigned int tmp_timer; - if (!(this_usbduxsub->probed)) - return -ENODEV; - /* Step 1 : check if triggers are trivially valid */ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT); @@ -956,221 +656,143 @@ static int8_t create_adc_command(unsigned int chan, int range) return (chan << 4) | ((p == 1) << 2) | ((r == 1) << 3); } -/* bulk transfers to usbdux */ +static int send_dux_commands(struct comedi_device *dev, int cmd_type) +{ + struct usb_device *usb = comedi_to_usb_dev(dev); + struct usbdux_private *devpriv = dev->private; + int nsent; -#define SENDADCOMMANDS 0 -#define SENDDACOMMANDS 1 -#define SENDDIOCONFIGCOMMAND 2 -#define SENDDIOBITSCOMMAND 3 -#define SENDSINGLEAD 4 -#define READCOUNTERCOMMAND 5 -#define WRITECOUNTERCOMMAND 6 -#define SENDPWMON 7 -#define SENDPWMOFF 8 + devpriv->dux_commands[0] = cmd_type; -static int send_dux_commands(struct usbduxsub *this_usbduxsub, int cmd_type) -{ - int result, nsent; - - this_usbduxsub->dux_commands[0] = cmd_type; -#ifdef NOISY_DUX_DEBUGBUG - printk(KERN_DEBUG "comedi%d: usbdux: dux_commands: ", - this_usbduxsub->comedidev->minor); - for (result = 0; result < SIZEOFDUXBUFFER; result++) - printk(" %02x", this_usbduxsub->dux_commands[result]); - printk("\n"); -#endif - result = usb_bulk_msg(this_usbduxsub->usbdev, - usb_sndbulkpipe(this_usbduxsub->usbdev, - COMMAND_OUT_EP), - this_usbduxsub->dux_commands, SIZEOFDUXBUFFER, - &nsent, BULK_TIMEOUT); - if (result < 0) - dev_err(&this_usbduxsub->interface->dev, "comedi%d: " - "could not transmit dux_command to the usb-device, " - "err=%d\n", this_usbduxsub->comedidev->minor, result); - - return result; + return usb_bulk_msg(usb, usb_sndbulkpipe(usb, 1), + devpriv->dux_commands, SIZEOFDUXBUFFER, + &nsent, BULK_TIMEOUT); } -static int receive_dux_commands(struct usbduxsub *this_usbduxsub, int command) +static int receive_dux_commands(struct comedi_device *dev, int command) { - int result = (-EFAULT); + struct usb_device *usb = comedi_to_usb_dev(dev); + struct usbdux_private *devpriv = dev->private; + int ret; int nrec; int i; for (i = 0; i < RETRIES; i++) { - result = usb_bulk_msg(this_usbduxsub->usbdev, - usb_rcvbulkpipe(this_usbduxsub->usbdev, - COMMAND_IN_EP), - this_usbduxsub->insn_buffer, SIZEINSNBUF, + ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, 8), + devpriv->insn_buf, SIZEINSNBUF, &nrec, BULK_TIMEOUT); - if (result < 0) { - dev_err(&this_usbduxsub->interface->dev, "comedi%d: " - "insn: USB error %d while receiving DUX command" - "\n", this_usbduxsub->comedidev->minor, result); - return result; - } - if (le16_to_cpu(this_usbduxsub->insn_buffer[0]) == command) - return result; + if (ret < 0) + return ret; + if (le16_to_cpu(devpriv->insn_buf[0]) == command) + return ret; } - /* this is only reached if the data has been requested a couple of - * times */ - dev_err(&this_usbduxsub->interface->dev, "comedi%d: insn: " - "wrong data returned from firmware: want cmd %d, got cmd %d.\n", - this_usbduxsub->comedidev->minor, command, - le16_to_cpu(this_usbduxsub->insn_buffer[0])); + /* command not received */ return -EFAULT; } static int usbdux_ai_inttrig(struct comedi_device *dev, - struct comedi_subdevice *s, unsigned int trignum) + struct comedi_subdevice *s, + unsigned int trignum) { - int ret; - struct usbduxsub *this_usbduxsub = dev->private; - if (!this_usbduxsub) - return -EFAULT; + struct usbdux_private *devpriv = dev->private; + int ret = -EINVAL; - down(&this_usbduxsub->sem); - if (!(this_usbduxsub->probed)) { - up(&this_usbduxsub->sem); - return -ENODEV; - } - dev_dbg(&this_usbduxsub->interface->dev, - "comedi%d: usbdux_ai_inttrig\n", dev->minor); - - if (trignum != 0) { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: usbdux_ai_inttrig: invalid trignum\n", - dev->minor); - up(&this_usbduxsub->sem); - return -EINVAL; - } - if (!(this_usbduxsub->ai_cmd_running)) { - this_usbduxsub->ai_cmd_running = 1; - ret = usbduxsub_submit_inurbs(this_usbduxsub); + down(&devpriv->sem); + + if (trignum != 0) + goto ai_trig_exit; + + if (!devpriv->ai_cmd_running) { + devpriv->ai_cmd_running = 1; + ret = usbdux_submit_urbs(dev, devpriv->ai_urbs, + devpriv->n_ai_urbs, 1); if (ret < 0) { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: usbdux_ai_inttrig: " - "urbSubmit: err=%d\n", dev->minor, ret); - this_usbduxsub->ai_cmd_running = 0; - up(&this_usbduxsub->sem); - return ret; + devpriv->ai_cmd_running = 0; + goto ai_trig_exit; } s->async->inttrig = NULL; } else { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: ai_inttrig but acqu is already running\n", - dev->minor); + ret = -EBUSY; } - up(&this_usbduxsub->sem); - return 1; + +ai_trig_exit: + up(&devpriv->sem); + return ret; } static int usbdux_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) { + struct usbdux_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; - unsigned int chan, range; - int i, ret; - struct usbduxsub *this_usbduxsub = dev->private; - int result; - - if (!this_usbduxsub) - return -EFAULT; - - dev_dbg(&this_usbduxsub->interface->dev, - "comedi%d: usbdux_ai_cmd\n", dev->minor); + int len = cmd->chanlist_len; + int ret = -EBUSY; + int i; /* block other CPUs from starting an ai_cmd */ - down(&this_usbduxsub->sem); + down(&devpriv->sem); + + if (devpriv->ai_cmd_running) + goto ai_cmd_exit; - if (!(this_usbduxsub->probed)) { - up(&this_usbduxsub->sem); - return -ENODEV; - } - if (this_usbduxsub->ai_cmd_running) { - dev_err(&this_usbduxsub->interface->dev, "comedi%d: " - "ai_cmd not possible. Another ai_cmd is running.\n", - dev->minor); - up(&this_usbduxsub->sem); - return -EBUSY; - } /* set current channel of the running acquisition to zero */ s->async->cur_chan = 0; - this_usbduxsub->dux_commands[1] = cmd->chanlist_len; - for (i = 0; i < cmd->chanlist_len; ++i) { - chan = CR_CHAN(cmd->chanlist[i]); - range = CR_RANGE(cmd->chanlist[i]); - if (i >= NUMCHANNELS) { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: channel list too long\n", - dev->minor); - break; - } - this_usbduxsub->dux_commands[i + 2] = - create_adc_command(chan, range); - } - - dev_dbg(&this_usbduxsub->interface->dev, - "comedi %d: sending commands to the usb device: size=%u\n", - dev->minor, NUMCHANNELS); + devpriv->dux_commands[1] = len; + for (i = 0; i < len; ++i) { + unsigned int chan = CR_CHAN(cmd->chanlist[i]); + unsigned int range = CR_RANGE(cmd->chanlist[i]); - result = send_dux_commands(this_usbduxsub, SENDADCOMMANDS); - if (result < 0) { - up(&this_usbduxsub->sem); - return result; + devpriv->dux_commands[i + 2] = create_adc_command(chan, range); } - if (this_usbduxsub->high_speed) { + ret = send_dux_commands(dev, USBDUX_CMD_MULT_AI); + if (ret < 0) + goto ai_cmd_exit; + + if (devpriv->high_speed) { /* * every channel gets a time window of 125us. Thus, if we * sample all 8 channels we need 1ms. If we sample only one * channel we need only 125us */ - this_usbduxsub->ai_interval = 1; + devpriv->ai_interval = 1; /* find a power of 2 for the interval */ - while ((this_usbduxsub->ai_interval) < (cmd->chanlist_len)) { - this_usbduxsub->ai_interval = - (this_usbduxsub->ai_interval) * 2; - } - this_usbduxsub->ai_timer = cmd->scan_begin_arg / (125000 * - (this_usbduxsub-> - ai_interval)); + while (devpriv->ai_interval < len) + devpriv->ai_interval *= 2; + + devpriv->ai_timer = cmd->scan_begin_arg / + (125000 * devpriv->ai_interval); } else { /* interval always 1ms */ - this_usbduxsub->ai_interval = 1; - this_usbduxsub->ai_timer = cmd->scan_begin_arg / 1000000; + devpriv->ai_interval = 1; + devpriv->ai_timer = cmd->scan_begin_arg / 1000000; } - if (this_usbduxsub->ai_timer < 1) { - dev_err(&this_usbduxsub->interface->dev, "comedi%d: ai_cmd: " - "timer=%d, scan_begin_arg=%d. " - "Not properly tested by cmdtest?\n", dev->minor, - this_usbduxsub->ai_timer, cmd->scan_begin_arg); - up(&this_usbduxsub->sem); - return -EINVAL; + if (devpriv->ai_timer < 1) { + ret = -EINVAL; + goto ai_cmd_exit; } - this_usbduxsub->ai_counter = this_usbduxsub->ai_timer; + + devpriv->ai_counter = devpriv->ai_timer; if (cmd->stop_src == TRIG_COUNT) { /* data arrives as one packet */ - this_usbduxsub->ai_sample_count = cmd->stop_arg; - this_usbduxsub->ai_continous = 0; + devpriv->ai_sample_count = cmd->stop_arg; + devpriv->ai_continous = 0; } else { /* continous acquisition */ - this_usbduxsub->ai_continous = 1; - this_usbduxsub->ai_sample_count = 0; + devpriv->ai_continous = 1; + devpriv->ai_sample_count = 0; } if (cmd->start_src == TRIG_NOW) { /* enable this acquisition operation */ - this_usbduxsub->ai_cmd_running = 1; - ret = usbduxsub_submit_inurbs(this_usbduxsub); + devpriv->ai_cmd_running = 1; + ret = usbdux_submit_urbs(dev, devpriv->ai_urbs, + devpriv->n_ai_urbs, 1); if (ret < 0) { - this_usbduxsub->ai_cmd_running = 0; + devpriv->ai_cmd_running = 0; /* fixme: unlink here?? */ - up(&this_usbduxsub->sem); - return ret; + goto ai_cmd_exit; } s->async->inttrig = NULL; } else { @@ -1179,202 +801,156 @@ static int usbdux_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) /* wait for an internal signal */ s->async->inttrig = usbdux_ai_inttrig; } - up(&this_usbduxsub->sem); - return 0; + +ai_cmd_exit: + up(&devpriv->sem); + + return ret; } /* Mode 0 is used to get a single conversion on demand */ static int usbdux_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { + struct usbdux_private *devpriv = dev->private; + unsigned int chan = CR_CHAN(insn->chanspec); + unsigned int range = CR_RANGE(insn->chanspec); + unsigned int val; + int ret = -EBUSY; int i; - unsigned int one = 0; - int chan, range; - int err; - struct usbduxsub *this_usbduxsub = dev->private; - - if (!this_usbduxsub) - return 0; - dev_dbg(&this_usbduxsub->interface->dev, - "comedi%d: ai_insn_read, insn->n=%d, insn->subdev=%d\n", - dev->minor, insn->n, insn->subdev); + down(&devpriv->sem); - down(&this_usbduxsub->sem); - if (!(this_usbduxsub->probed)) { - up(&this_usbduxsub->sem); - return -ENODEV; - } - if (this_usbduxsub->ai_cmd_running) { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: ai_insn_read not possible. " - "Async Command is running.\n", dev->minor); - up(&this_usbduxsub->sem); - return 0; - } + if (devpriv->ai_cmd_running) + goto ai_read_exit; - /* sample one channel */ - chan = CR_CHAN(insn->chanspec); - range = CR_RANGE(insn->chanspec); /* set command for the first channel */ - this_usbduxsub->dux_commands[1] = create_adc_command(chan, range); + devpriv->dux_commands[1] = create_adc_command(chan, range); /* adc commands */ - err = send_dux_commands(this_usbduxsub, SENDSINGLEAD); - if (err < 0) { - up(&this_usbduxsub->sem); - return err; - } + ret = send_dux_commands(dev, USBDUX_CMD_SINGLE_AI); + if (ret < 0) + goto ai_read_exit; for (i = 0; i < insn->n; i++) { - err = receive_dux_commands(this_usbduxsub, SENDSINGLEAD); - if (err < 0) { - up(&this_usbduxsub->sem); - return 0; - } - one = le16_to_cpu(this_usbduxsub->insn_buffer[1]); - if (CR_RANGE(insn->chanspec) <= 1) - one = one ^ 0x800; + ret = receive_dux_commands(dev, USBDUX_CMD_SINGLE_AI); + if (ret < 0) + goto ai_read_exit; + + val = le16_to_cpu(devpriv->insn_buf[1]); - data[i] = one; + /* bipolar data is two's-complement */ + if (comedi_range_is_bipolar(s, range)) + val ^= ((s->maxdata + 1) >> 1); + + data[i] = val; } - up(&this_usbduxsub->sem); - return i; -} -/************************************/ -/* analog out */ +ai_read_exit: + up(&devpriv->sem); + + return ret ? ret : insn->n; +} static int usbdux_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { + struct usbdux_private *devpriv = dev->private; + unsigned int chan = CR_CHAN(insn->chanspec); int i; - int chan = CR_CHAN(insn->chanspec); - struct usbduxsub *this_usbduxsub = dev->private; - - if (!this_usbduxsub) - return -EFAULT; - down(&this_usbduxsub->sem); - if (!(this_usbduxsub->probed)) { - up(&this_usbduxsub->sem); - return -ENODEV; - } + down(&devpriv->sem); for (i = 0; i < insn->n; i++) - data[i] = this_usbduxsub->out_buffer[chan]; + data[i] = devpriv->ao_readback[chan]; + up(&devpriv->sem); - up(&this_usbduxsub->sem); - return i; + return insn->n; } static int usbdux_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { - int i, err; - int chan = CR_CHAN(insn->chanspec); - struct usbduxsub *this_usbduxsub = dev->private; + struct usbdux_private *devpriv = dev->private; + unsigned int chan = CR_CHAN(insn->chanspec); + unsigned int val = devpriv->ao_readback[chan]; + int16_t *p = (int16_t *)&devpriv->dux_commands[2]; + int ret = -EBUSY; + int i; - if (!this_usbduxsub) - return -EFAULT; + down(&devpriv->sem); - dev_dbg(&this_usbduxsub->interface->dev, - "comedi%d: ao_insn_write\n", dev->minor); + if (devpriv->ao_cmd_running) + goto ao_write_exit; - down(&this_usbduxsub->sem); - if (!(this_usbduxsub->probed)) { - up(&this_usbduxsub->sem); - return -ENODEV; - } - if (this_usbduxsub->ao_cmd_running) { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: ao_insn_write: " - "ERROR: asynchronous ao_cmd is running\n", dev->minor); - up(&this_usbduxsub->sem); - return 0; - } + /* number of channels: 1 */ + devpriv->dux_commands[1] = 1; + /* channel number */ + devpriv->dux_commands[4] = chan << 6; for (i = 0; i < insn->n; i++) { - dev_dbg(&this_usbduxsub->interface->dev, - "comedi%d: ao_insn_write: data[chan=%d,i=%d]=%d\n", - dev->minor, chan, i, data[i]); + val = data[i]; - /* number of channels: 1 */ - this_usbduxsub->dux_commands[1] = 1; /* one 16 bit value */ - *((int16_t *) (this_usbduxsub->dux_commands + 2)) = - cpu_to_le16(data[i]); - this_usbduxsub->out_buffer[chan] = data[i]; - /* channel number */ - this_usbduxsub->dux_commands[4] = (chan << 6); - err = send_dux_commands(this_usbduxsub, SENDDACOMMANDS); - if (err < 0) { - up(&this_usbduxsub->sem); - return err; - } + *p = cpu_to_le16(val); + + ret = send_dux_commands(dev, USBDUX_CMD_AO); + if (ret < 0) + goto ao_write_exit; } - up(&this_usbduxsub->sem); + devpriv->ao_readback[chan] = val; + +ao_write_exit: + up(&devpriv->sem); - return i; + return ret ? ret : insn->n; } static int usbdux_ao_inttrig(struct comedi_device *dev, - struct comedi_subdevice *s, unsigned int trignum) + struct comedi_subdevice *s, + unsigned int trignum) { - int ret; - struct usbduxsub *this_usbduxsub = dev->private; + struct usbdux_private *devpriv = dev->private; + int ret = -EINVAL; - if (!this_usbduxsub) - return -EFAULT; + down(&devpriv->sem); - down(&this_usbduxsub->sem); - if (!(this_usbduxsub->probed)) { - up(&this_usbduxsub->sem); - return -ENODEV; - } - if (trignum != 0) { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: usbdux_ao_inttrig: invalid trignum\n", - dev->minor); - up(&this_usbduxsub->sem); - return -EINVAL; - } - if (!(this_usbduxsub->ao_cmd_running)) { - this_usbduxsub->ao_cmd_running = 1; - ret = usbduxsub_submit_outurbs(this_usbduxsub); + if (trignum != 0) + goto ao_trig_exit; + + if (!devpriv->ao_cmd_running) { + devpriv->ao_cmd_running = 1; + ret = usbdux_submit_urbs(dev, devpriv->ao_urbs, + devpriv->n_ao_urbs, 0); if (ret < 0) { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: usbdux_ao_inttrig: submitURB: " - "err=%d\n", dev->minor, ret); - this_usbduxsub->ao_cmd_running = 0; - up(&this_usbduxsub->sem); - return ret; + devpriv->ao_cmd_running = 0; + goto ao_trig_exit; } s->async->inttrig = NULL; } else { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: ao_inttrig but acqu is already running.\n", - dev->minor); + ret = -EBUSY; } - up(&this_usbduxsub->sem); - return 1; + +ao_trig_exit: + up(&devpriv->sem); + return ret; } static int usbdux_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) { - struct usbduxsub *this_usbduxsub = dev->private; + struct usbdux_private *this_usbduxsub = dev->private; int err = 0; unsigned int flags; if (!this_usbduxsub) return -EFAULT; - if (!(this_usbduxsub->probed)) - return -ENODEV; - /* Step 1 : check if triggers are trivially valid */ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT); @@ -1451,99 +1027,72 @@ static int usbdux_ao_cmdtest(struct comedi_device *dev, static int usbdux_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s) { + struct usbdux_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; - unsigned int chan, gain; - int i, ret; - struct usbduxsub *this_usbduxsub = dev->private; + int ret = -EBUSY; + int i; - if (!this_usbduxsub) - return -EFAULT; + down(&devpriv->sem); - down(&this_usbduxsub->sem); - if (!(this_usbduxsub->probed)) { - up(&this_usbduxsub->sem); - return -ENODEV; - } - dev_dbg(&this_usbduxsub->interface->dev, - "comedi%d: %s\n", dev->minor, __func__); + if (devpriv->ao_cmd_running) + goto ao_cmd_exit; /* set current channel of the running acquisition to zero */ s->async->cur_chan = 0; + for (i = 0; i < cmd->chanlist_len; ++i) { - chan = CR_CHAN(cmd->chanlist[i]); - gain = CR_RANGE(cmd->chanlist[i]); - if (i >= NUMOUTCHANNELS) { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: %s: channel list too long\n", - dev->minor, __func__); - break; - } - this_usbduxsub->dac_commands[i] = (chan << 6); - dev_dbg(&this_usbduxsub->interface->dev, - "comedi%d: dac command for ch %d is %x\n", - dev->minor, i, this_usbduxsub->dac_commands[i]); + unsigned int chan = CR_CHAN(cmd->chanlist[i]); + + devpriv->ao_chanlist[i] = chan << 6; } /* we count in steps of 1ms (125us) */ /* 125us mode not used yet */ - if (0) { /* (this_usbduxsub->high_speed) */ + if (0) { /* (devpriv->high_speed) */ /* 125us */ /* timing of the conversion itself: every 125 us */ - this_usbduxsub->ao_timer = cmd->convert_arg / 125000; + devpriv->ao_timer = cmd->convert_arg / 125000; } else { /* 1ms */ /* timing of the scan: we get all channels at once */ - this_usbduxsub->ao_timer = cmd->scan_begin_arg / 1000000; - dev_dbg(&this_usbduxsub->interface->dev, - "comedi%d: scan_begin_src=%d, scan_begin_arg=%d, " - "convert_src=%d, convert_arg=%d\n", dev->minor, - cmd->scan_begin_src, cmd->scan_begin_arg, - cmd->convert_src, cmd->convert_arg); - dev_dbg(&this_usbduxsub->interface->dev, - "comedi%d: ao_timer=%d (ms)\n", - dev->minor, this_usbduxsub->ao_timer); - if (this_usbduxsub->ao_timer < 1) { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: usbdux: ao_timer=%d, " - "scan_begin_arg=%d. " - "Not properly tested by cmdtest?\n", - dev->minor, this_usbduxsub->ao_timer, - cmd->scan_begin_arg); - up(&this_usbduxsub->sem); - return -EINVAL; + devpriv->ao_timer = cmd->scan_begin_arg / 1000000; + if (devpriv->ao_timer < 1) { + ret = -EINVAL; + goto ao_cmd_exit; } } - this_usbduxsub->ao_counter = this_usbduxsub->ao_timer; + + devpriv->ao_counter = devpriv->ao_timer; if (cmd->stop_src == TRIG_COUNT) { /* not continuous */ /* counter */ /* high speed also scans everything at once */ - if (0) { /* (this_usbduxsub->high_speed) */ - this_usbduxsub->ao_sample_count = - (cmd->stop_arg) * (cmd->scan_end_arg); + if (0) { /* (devpriv->high_speed) */ + devpriv->ao_sample_count = cmd->stop_arg * + cmd->scan_end_arg; } else { /* there's no scan as the scan has been */ /* perf inside the FX2 */ /* data arrives as one packet */ - this_usbduxsub->ao_sample_count = cmd->stop_arg; + devpriv->ao_sample_count = cmd->stop_arg; } - this_usbduxsub->ao_continous = 0; + devpriv->ao_continous = 0; } else { /* continous acquisition */ - this_usbduxsub->ao_continous = 1; - this_usbduxsub->ao_sample_count = 0; + devpriv->ao_continous = 1; + devpriv->ao_sample_count = 0; } if (cmd->start_src == TRIG_NOW) { /* enable this acquisition operation */ - this_usbduxsub->ao_cmd_running = 1; - ret = usbduxsub_submit_outurbs(this_usbduxsub); + devpriv->ao_cmd_running = 1; + ret = usbdux_submit_urbs(dev, devpriv->ao_urbs, + devpriv->n_ao_urbs, 0); if (ret < 0) { - this_usbduxsub->ao_cmd_running = 0; + devpriv->ao_cmd_running = 0; /* fixme: unlink here?? */ - up(&this_usbduxsub->sem); - return ret; + goto ao_cmd_exit; } s->async->inttrig = NULL; } else { @@ -1553,149 +1102,123 @@ static int usbdux_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s) s->async->inttrig = usbdux_ao_inttrig; } - up(&this_usbduxsub->sem); - return 0; +ao_cmd_exit: + up(&devpriv->sem); + + return ret; } static int usbdux_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { - int chan = CR_CHAN(insn->chanspec); + int ret; - /* The input or output configuration of each digital line is - * configured by a special insn_config instruction. chanspec - * contains the channel to be changed, and data[0] contains the - * value COMEDI_INPUT or COMEDI_OUTPUT. */ + ret = comedi_dio_insn_config(dev, s, insn, data, 0); + if (ret) + return ret; - switch (data[0]) { - case INSN_CONFIG_DIO_OUTPUT: - s->io_bits |= 1 << chan; /* 1 means Out */ - break; - case INSN_CONFIG_DIO_INPUT: - s->io_bits &= ~(1 << chan); - break; - case INSN_CONFIG_DIO_QUERY: - data[1] = - (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; - break; - default: - return -EINVAL; - break; - } - /* we don't tell the firmware here as it would take 8 frames */ - /* to submit the information. We do it in the insn_bits. */ + /* + * We don't tell the firmware here as it would take 8 frames + * to submit the information. We do it in the insn_bits. + */ return insn->n; } static int usbdux_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { - struct usbduxsub *this_usbduxsub = dev->private; - int err; + struct usbdux_private *devpriv = dev->private; + unsigned int mask = data[0]; + unsigned int bits = data[1]; + int ret; - if (!this_usbduxsub) - return -EFAULT; + down(&devpriv->sem); - down(&this_usbduxsub->sem); + s->state &= ~mask; + s->state |= (bits & mask); - if (!(this_usbduxsub->probed)) { - up(&this_usbduxsub->sem); - return -ENODEV; - } + devpriv->dux_commands[1] = s->io_bits; + devpriv->dux_commands[2] = s->state; - /* The insn data is a mask in data[0] and the new data - * in data[1], each channel cooresponding to a bit. */ - s->state &= ~data[0]; - s->state |= data[0] & data[1]; - this_usbduxsub->dux_commands[1] = s->io_bits; - this_usbduxsub->dux_commands[2] = s->state; - - /* This command also tells the firmware to return */ - /* the digital input lines */ - err = send_dux_commands(this_usbduxsub, SENDDIOBITSCOMMAND); - if (err < 0) { - up(&this_usbduxsub->sem); - return err; - } - err = receive_dux_commands(this_usbduxsub, SENDDIOBITSCOMMAND); - if (err < 0) { - up(&this_usbduxsub->sem); - return err; - } + /* + * This command also tells the firmware to return + * the digital input lines. + */ + ret = send_dux_commands(dev, USBDUX_CMD_DIO_BITS); + if (ret < 0) + goto dio_exit; + ret = receive_dux_commands(dev, USBDUX_CMD_DIO_BITS); + if (ret < 0) + goto dio_exit; - data[1] = le16_to_cpu(this_usbduxsub->insn_buffer[1]); - up(&this_usbduxsub->sem); - return insn->n; + data[1] = le16_to_cpu(devpriv->insn_buf[1]); + +dio_exit: + up(&devpriv->sem); + + return ret ? ret : insn->n; } -/* reads the 4 counters, only two are used just now */ static int usbdux_counter_read(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { - struct usbduxsub *this_usbduxsub = dev->private; - int chan = insn->chanspec; - int err; - - if (!this_usbduxsub) - return -EFAULT; + struct usbdux_private *devpriv = dev->private; + unsigned int chan = CR_CHAN(insn->chanspec); + int ret = 0; + int i; - down(&this_usbduxsub->sem); + down(&devpriv->sem); - if (!(this_usbduxsub->probed)) { - up(&this_usbduxsub->sem); - return -ENODEV; - } + for (i = 0; i < insn->n; i++) { + ret = send_dux_commands(dev, USBDUX_CMD_TIMER_RD); + if (ret < 0) + goto counter_read_exit; + ret = receive_dux_commands(dev, USBDUX_CMD_TIMER_RD); + if (ret < 0) + goto counter_read_exit; - err = send_dux_commands(this_usbduxsub, READCOUNTERCOMMAND); - if (err < 0) { - up(&this_usbduxsub->sem); - return err; + data[i] = le16_to_cpu(devpriv->insn_buf[chan + 1]); } - err = receive_dux_commands(this_usbduxsub, READCOUNTERCOMMAND); - if (err < 0) { - up(&this_usbduxsub->sem); - return err; - } +counter_read_exit: + up(&devpriv->sem); - data[0] = le16_to_cpu(this_usbduxsub->insn_buffer[chan + 1]); - up(&this_usbduxsub->sem); - return 1; + return ret ? ret : insn->n; } static int usbdux_counter_write(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { - struct usbduxsub *this_usbduxsub = dev->private; - int err; - - if (!this_usbduxsub) - return -EFAULT; + struct usbdux_private *devpriv = dev->private; + unsigned int chan = CR_CHAN(insn->chanspec); + int16_t *p = (int16_t *)&devpriv->dux_commands[2]; + int ret = 0; + int i; - down(&this_usbduxsub->sem); + down(&devpriv->sem); - if (!(this_usbduxsub->probed)) { - up(&this_usbduxsub->sem); - return -ENODEV; - } + devpriv->dux_commands[1] = chan; - this_usbduxsub->dux_commands[1] = insn->chanspec; - *((int16_t *) (this_usbduxsub->dux_commands + 2)) = cpu_to_le16(*data); + for (i = 0; i < insn->n; i++) { + *p = cpu_to_le16(data[i]); - err = send_dux_commands(this_usbduxsub, WRITECOUNTERCOMMAND); - if (err < 0) { - up(&this_usbduxsub->sem); - return err; + ret = send_dux_commands(dev, USBDUX_CMD_TIMER_WR); + if (ret < 0) + break; } - up(&this_usbduxsub->sem); + up(&devpriv->sem); - return 1; + return ret ? ret : insn->n; } static int usbdux_counter_config(struct comedi_device *dev, @@ -1706,73 +1229,43 @@ static int usbdux_counter_config(struct comedi_device *dev, return 2; } -/***********************************/ -/* PWM */ - -static int usbduxsub_unlink_pwm_urbs(struct usbduxsub *usbduxsub_tmp) +static void usbduxsub_unlink_pwm_urbs(struct comedi_device *dev) { - int err = 0; + struct usbdux_private *devpriv = dev->private; - if (usbduxsub_tmp && usbduxsub_tmp->urb_pwm) { - if (usbduxsub_tmp->urb_pwm) - usb_kill_urb(usbduxsub_tmp->urb_pwm); - dev_dbg(&usbduxsub_tmp->interface->dev, - "comedi: unlinked PwmURB: res=%d\n", err); - } - return err; + usb_kill_urb(devpriv->pwm_urb); } -/* This cancels a running acquisition operation - * in any context. - */ -static int usbdux_pwm_stop(struct usbduxsub *this_usbduxsub, int do_unlink) +static void usbdux_pwm_stop(struct comedi_device *dev, int do_unlink) { - int ret = 0; - - if (!this_usbduxsub) - return -EFAULT; + struct usbdux_private *devpriv = dev->private; - dev_dbg(&this_usbduxsub->interface->dev, "comedi: %s\n", __func__); if (do_unlink) - ret = usbduxsub_unlink_pwm_urbs(this_usbduxsub); - - this_usbduxsub->pwm_cmd_running = 0; + usbduxsub_unlink_pwm_urbs(dev); - return ret; + devpriv->pwm_cmd_running = 0; } -/* force unlink - is called by comedi */ static int usbdux_pwm_cancel(struct comedi_device *dev, struct comedi_subdevice *s) { - struct usbduxsub *this_usbduxsub = dev->private; - int res = 0; + struct usbdux_private *devpriv = dev->private; + int ret; + down(&devpriv->sem); /* unlink only if it is really running */ - res = usbdux_pwm_stop(this_usbduxsub, this_usbduxsub->pwm_cmd_running); + usbdux_pwm_stop(dev, devpriv->pwm_cmd_running); + ret = send_dux_commands(dev, USBDUX_CMD_PWM_OFF); + up(&devpriv->sem); - dev_dbg(&this_usbduxsub->interface->dev, - "comedi %d: sending pwm off command to the usb device.\n", - dev->minor); - - return send_dux_commands(this_usbduxsub, SENDPWMOFF); + return ret; } static void usbduxsub_pwm_irq(struct urb *urb) { + struct comedi_device *dev = urb->context; + struct usbdux_private *devpriv = dev->private; int ret; - struct usbduxsub *this_usbduxsub; - struct comedi_device *this_comedidev; - struct comedi_subdevice *s; - - /* printk(KERN_DEBUG "PWM: IRQ\n"); */ - - /* the context variable points to the subdevice */ - this_comedidev = urb->context; - /* the private structure of the subdevice is struct usbduxsub */ - this_usbduxsub = this_comedidev->private; - - s = &this_comedidev->subdevices[SUBDEV_DA]; switch (urb->status) { case 0: @@ -1787,220 +1280,171 @@ static void usbduxsub_pwm_irq(struct urb *urb) * after an unlink command, unplug, ... etc * no unlink needed here. Already shutting down. */ - if (this_usbduxsub->pwm_cmd_running) - usbdux_pwm_stop(this_usbduxsub, 0); + if (devpriv->pwm_cmd_running) + usbdux_pwm_stop(dev, 0); return; default: /* a real error */ - if (this_usbduxsub->pwm_cmd_running) { - dev_err(&this_usbduxsub->interface->dev, - "comedi_: Non-zero urb status received in " - "pwm intr context: %d\n", urb->status); - usbdux_pwm_stop(this_usbduxsub, 0); + if (devpriv->pwm_cmd_running) { + dev_err(dev->class_dev, + "Non-zero urb status received in pwm intr context: %d\n", + urb->status); + usbdux_pwm_stop(dev, 0); } return; } /* are we actually running? */ - if (!(this_usbduxsub->pwm_cmd_running)) + if (!devpriv->pwm_cmd_running) return; - urb->transfer_buffer_length = this_usbduxsub->size_pwm_buf; - urb->dev = this_usbduxsub->usbdev; + urb->transfer_buffer_length = devpriv->pwm_buf_sz; + urb->dev = comedi_to_usb_dev(dev); urb->status = 0; - if (this_usbduxsub->pwm_cmd_running) { + if (devpriv->pwm_cmd_running) { ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret < 0) { - dev_err(&this_usbduxsub->interface->dev, - "comedi_: pwm urb resubm failed in int-cont. " - "ret=%d", ret); + dev_err(dev->class_dev, + "pwm urb resubm failed in int-cont. ret=%d", + ret); if (ret == EL2NSYNC) - dev_err(&this_usbduxsub->interface->dev, - "buggy USB host controller or bug in " - "IRQ handling!\n"); + dev_err(dev->class_dev, + "buggy USB host controller or bug in IRQ handling!\n"); /* don't do an unlink here */ - usbdux_pwm_stop(this_usbduxsub, 0); + usbdux_pwm_stop(dev, 0); } } } -static int usbduxsub_submit_pwm_urbs(struct usbduxsub *usbduxsub) +static int usbduxsub_submit_pwm_urbs(struct comedi_device *dev) { - int err_flag; - - if (!usbduxsub) - return -EFAULT; - - dev_dbg(&usbduxsub->interface->dev, "comedi_: submitting pwm-urb\n"); + struct usb_device *usb = comedi_to_usb_dev(dev); + struct usbdux_private *devpriv = dev->private; + struct urb *urb = devpriv->pwm_urb; /* in case of a resubmission after an unlink... */ - usb_fill_bulk_urb(usbduxsub->urb_pwm, - usbduxsub->usbdev, - usb_sndbulkpipe(usbduxsub->usbdev, PWM_EP), - usbduxsub->urb_pwm->transfer_buffer, - usbduxsub->size_pwm_buf, usbduxsub_pwm_irq, - usbduxsub->comedidev); - - err_flag = usb_submit_urb(usbduxsub->urb_pwm, GFP_ATOMIC); - if (err_flag) { - dev_err(&usbduxsub->interface->dev, - "comedi_: usbdux: pwm: usb_submit_urb error %d\n", - err_flag); - return err_flag; - } - return 0; + usb_fill_bulk_urb(urb, usb, usb_sndbulkpipe(usb, 4), + urb->transfer_buffer, + devpriv->pwm_buf_sz, + usbduxsub_pwm_irq, + dev); + + return usb_submit_urb(urb, GFP_ATOMIC); } static int usbdux_pwm_period(struct comedi_device *dev, - struct comedi_subdevice *s, unsigned int period) + struct comedi_subdevice *s, + unsigned int period) { - struct usbduxsub *this_usbduxsub = dev->private; + struct usbdux_private *devpriv = dev->private; int fx2delay = 255; if (period < MIN_PWM_PERIOD) { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: illegal period setting for pwm.\n", - dev->minor); return -EAGAIN; } else { - fx2delay = period / ((int)(6 * 512 * (1.0 / 0.033))) - 6; - if (fx2delay > 255) { - dev_err(&this_usbduxsub->interface->dev, - "comedi%d: period %d for pwm is too low.\n", - dev->minor, period); + fx2delay = (period / (6 * 512 * 1000 / 33)) - 6; + if (fx2delay > 255) return -EAGAIN; - } } - this_usbduxsub->pwn_delay = fx2delay; - this_usbduxsub->pwm_period = period; - dev_dbg(&this_usbduxsub->interface->dev, "%s: frequ=%d, period=%d\n", - __func__, period, fx2delay); + devpriv->pwm_delay = fx2delay; + devpriv->pwm_period = period; + return 0; } -/* is called from insn so there's no need to do all the sanity checks */ static int usbdux_pwm_start(struct comedi_device *dev, struct comedi_subdevice *s) { - int ret, i; - struct usbduxsub *this_usbduxsub = dev->private; + struct usbdux_private *devpriv = dev->private; + int ret = 0; - dev_dbg(&this_usbduxsub->interface->dev, "comedi%d: %s\n", - dev->minor, __func__); + down(&devpriv->sem); - if (this_usbduxsub->pwm_cmd_running) { - /* already running */ - return 0; - } + if (devpriv->pwm_cmd_running) + goto pwm_start_exit; - this_usbduxsub->dux_commands[1] = ((int8_t) this_usbduxsub->pwn_delay); - ret = send_dux_commands(this_usbduxsub, SENDPWMON); + devpriv->dux_commands[1] = devpriv->pwm_delay; + ret = send_dux_commands(dev, USBDUX_CMD_PWM_ON); if (ret < 0) - return ret; + goto pwm_start_exit; /* initialise the buffer */ - for (i = 0; i < this_usbduxsub->size_pwm_buf; i++) - ((char *)(this_usbduxsub->urb_pwm->transfer_buffer))[i] = 0; + memset(devpriv->pwm_urb->transfer_buffer, 0, devpriv->pwm_buf_sz); - this_usbduxsub->pwm_cmd_running = 1; - ret = usbduxsub_submit_pwm_urbs(this_usbduxsub); - if (ret < 0) { - this_usbduxsub->pwm_cmd_running = 0; - return ret; - } - return 0; + devpriv->pwm_cmd_running = 1; + ret = usbduxsub_submit_pwm_urbs(dev); + if (ret < 0) + devpriv->pwm_cmd_running = 0; + +pwm_start_exit: + up(&devpriv->sem); + + return ret; } -/* generates the bit pattern for PWM with the optional sign bit */ -static int usbdux_pwm_pattern(struct comedi_device *dev, - struct comedi_subdevice *s, int channel, - unsigned int value, unsigned int sign) +static void usbdux_pwm_pattern(struct comedi_device *dev, + struct comedi_subdevice *s, + unsigned int chan, + unsigned int value, + unsigned int sign) { - struct usbduxsub *this_usbduxsub = dev->private; - int i, szbuf; - char *p_buf; - char pwm_mask; - char sgn_mask; - char c; - - if (!this_usbduxsub) - return -EFAULT; + struct usbdux_private *devpriv = dev->private; + char pwm_mask = (1 << chan); /* DIO bit for the PWM data */ + char sgn_mask = (16 << chan); /* DIO bit for the sign */ + char *buf = (char *)(devpriv->pwm_urb->transfer_buffer); + int szbuf = devpriv->pwm_buf_sz; + int i; - /* this is the DIO bit which carries the PWM data */ - pwm_mask = (1 << channel); - /* this is the DIO bit which carries the optional direction bit */ - sgn_mask = (16 << channel); - /* this is the buffer which will be filled with the with bit */ - /* pattern for one period */ - szbuf = this_usbduxsub->size_pwm_buf; - p_buf = (char *)(this_usbduxsub->urb_pwm->transfer_buffer); for (i = 0; i < szbuf; i++) { - c = *p_buf; - /* reset bits */ - c = c & (~pwm_mask); - /* set the bit as long as the index is lower than the value */ + char c = *buf; + + c &= ~pwm_mask; if (i < value) - c = c | pwm_mask; - /* set the optional sign bit for a relay */ - if (!sign) { - /* positive value */ - c = c & (~sgn_mask); - } else { - /* negative value */ - c = c | sgn_mask; - } - *(p_buf++) = c; + c |= pwm_mask; + if (!sign) + c &= ~sgn_mask; + else + c |= sgn_mask; + *buf++ = c; } - return 1; } static int usbdux_pwm_write(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { - struct usbduxsub *this_usbduxsub = dev->private; + unsigned int chan = CR_CHAN(insn->chanspec); - if (!this_usbduxsub) - return -EFAULT; - - if ((insn->n) != 1) { - /* - * doesn't make sense to have more than one value here because - * it would just overwrite the PWM buffer a couple of times - */ + /* + * It doesn't make sense to support more than one value here + * because it would just overwrite the PWM buffer. + */ + if (insn->n != 1) return -EINVAL; - } /* - * the sign is set via a special INSN only, this gives us 8 bits for - * normal operation - * relay sign 0 by default + * The sign is set via a special INSN only, this gives us 8 bits + * for normal operation, sign is 0 by default. */ - return usbdux_pwm_pattern(dev, s, CR_CHAN(insn->chanspec), data[0], 0); -} + usbdux_pwm_pattern(dev, s, chan, data[0], 0); -static int usbdux_pwm_read(struct comedi_device *x1, - struct comedi_subdevice *x2, struct comedi_insn *x3, - unsigned int *x4) -{ - /* not needed */ - return -EINVAL; -}; + return insn->n; +} -/* switches on/off PWM */ static int usbdux_pwm_config(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) + struct comedi_insn *insn, + unsigned int *data) { - struct usbduxsub *this_usbduxsub = dev->private; + struct usbdux_private *devpriv = dev->private; + unsigned int chan = CR_CHAN(insn->chanspec); + switch (data[0]) { case INSN_CONFIG_ARM: - /* switch it on */ - dev_dbg(&this_usbduxsub->interface->dev, - "comedi%d: %s: pwm on\n", dev->minor, __func__); /* * if not zero the PWM is limited to a certain time which is * not supported here @@ -2009,33 +1453,22 @@ static int usbdux_pwm_config(struct comedi_device *dev, return -EINVAL; return usbdux_pwm_start(dev, s); case INSN_CONFIG_DISARM: - dev_dbg(&this_usbduxsub->interface->dev, - "comedi%d: %s: pwm off\n", dev->minor, __func__); return usbdux_pwm_cancel(dev, s); case INSN_CONFIG_GET_PWM_STATUS: - /* - * to check if the USB transmission has failed or in case PWM - * was limited to n cycles to check if it has terminated - */ - data[1] = this_usbduxsub->pwm_cmd_running; + data[1] = devpriv->pwm_cmd_running; return 0; case INSN_CONFIG_PWM_SET_PERIOD: - dev_dbg(&this_usbduxsub->interface->dev, - "comedi%d: %s: setting period\n", dev->minor, __func__); return usbdux_pwm_period(dev, s, data[1]); case INSN_CONFIG_PWM_GET_PERIOD: - data[1] = this_usbduxsub->pwm_period; + data[1] = devpriv->pwm_period; return 0; case INSN_CONFIG_PWM_SET_H_BRIDGE: - /* value in the first byte and the sign in the second for a - relay */ - return usbdux_pwm_pattern(dev, s, - /* the channel number */ - CR_CHAN(insn->chanspec), - /* actual PWM data */ - data[1], - /* just a sign */ - (data[2] != 0)); + /* + * data[1] = value + * data[2] = sign (for a relay) + */ + usbdux_pwm_pattern(dev, s, chan, data[1], (data[2] != 0)); + return 0; case INSN_CONFIG_PWM_GET_H_BRIDGE: /* values are not kept in this driver, nothing to return here */ return -EINVAL; @@ -2043,253 +1476,331 @@ static int usbdux_pwm_config(struct comedi_device *dev, return -EINVAL; } -/* end of PWM */ -/*****************************************************************/ - -static void tidy_up(struct usbduxsub *usbduxsub_tmp) +static int usbdux_firmware_upload(struct comedi_device *dev, + const u8 *data, size_t size, + unsigned long context) { - int i; + struct usb_device *usb = comedi_to_usb_dev(dev); + uint8_t *buf; + uint8_t *tmp; + int ret; - if (!usbduxsub_tmp) - return; - dev_dbg(&usbduxsub_tmp->interface->dev, "comedi_: tiding up\n"); + if (!data) + return 0; - /* shows the usb subsystem that the driver is down */ - if (usbduxsub_tmp->interface) - usb_set_intfdata(usbduxsub_tmp->interface, NULL); + if (size > USBDUX_FIRMWARE_MAX_LEN) { + dev_err(dev->class_dev, + "usbdux firmware binary it too large for FX2.\n"); + return -ENOMEM; + } - usbduxsub_tmp->probed = 0; + /* we generate a local buffer for the firmware */ + buf = kmemdup(data, size, GFP_KERNEL); + if (!buf) + return -ENOMEM; - if (usbduxsub_tmp->urb_in) { - if (usbduxsub_tmp->ai_cmd_running) { - usbduxsub_tmp->ai_cmd_running = 0; - usbduxsub_unlink_inurbs(usbduxsub_tmp); - } - for (i = 0; i < usbduxsub_tmp->num_in_buffers; i++) { - kfree(usbduxsub_tmp->urb_in[i]->transfer_buffer); - usbduxsub_tmp->urb_in[i]->transfer_buffer = NULL; - usb_kill_urb(usbduxsub_tmp->urb_in[i]); - usb_free_urb(usbduxsub_tmp->urb_in[i]); - usbduxsub_tmp->urb_in[i] = NULL; - } - kfree(usbduxsub_tmp->urb_in); - usbduxsub_tmp->urb_in = NULL; + /* we need a malloc'ed buffer for usb_control_msg() */ + tmp = kmalloc(1, GFP_KERNEL); + if (!tmp) { + kfree(buf); + return -ENOMEM; } - if (usbduxsub_tmp->urb_out) { - if (usbduxsub_tmp->ao_cmd_running) { - usbduxsub_tmp->ao_cmd_running = 0; - usbduxsub_unlink_outurbs(usbduxsub_tmp); - } - for (i = 0; i < usbduxsub_tmp->num_out_buffers; i++) { - kfree(usbduxsub_tmp->urb_out[i]->transfer_buffer); - usbduxsub_tmp->urb_out[i]->transfer_buffer = NULL; - if (usbduxsub_tmp->urb_out[i]) { - usb_kill_urb(usbduxsub_tmp->urb_out[i]); - usb_free_urb(usbduxsub_tmp->urb_out[i]); - usbduxsub_tmp->urb_out[i] = NULL; - } - } - kfree(usbduxsub_tmp->urb_out); - usbduxsub_tmp->urb_out = NULL; + + /* stop the current firmware on the device */ + *tmp = 1; /* 7f92 to one */ + ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0), + USBDUX_FIRMWARE_CMD, + VENDOR_DIR_OUT, + USBDUX_CPU_CS, 0x0000, + tmp, 1, + BULK_TIMEOUT); + if (ret < 0) { + dev_err(dev->class_dev, "can not stop firmware\n"); + goto done; } - if (usbduxsub_tmp->urb_pwm) { - if (usbduxsub_tmp->pwm_cmd_running) { - usbduxsub_tmp->pwm_cmd_running = 0; - usbduxsub_unlink_pwm_urbs(usbduxsub_tmp); - } - kfree(usbduxsub_tmp->urb_pwm->transfer_buffer); - usbduxsub_tmp->urb_pwm->transfer_buffer = NULL; - usb_kill_urb(usbduxsub_tmp->urb_pwm); - usb_free_urb(usbduxsub_tmp->urb_pwm); - usbduxsub_tmp->urb_pwm = NULL; + + /* upload the new firmware to the device */ + ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0), + USBDUX_FIRMWARE_CMD, + VENDOR_DIR_OUT, + 0, 0x0000, + buf, size, + BULK_TIMEOUT); + if (ret < 0) { + dev_err(dev->class_dev, "firmware upload failed\n"); + goto done; } - kfree(usbduxsub_tmp->in_buffer); - usbduxsub_tmp->in_buffer = NULL; - kfree(usbduxsub_tmp->insn_buffer); - usbduxsub_tmp->insn_buffer = NULL; - kfree(usbduxsub_tmp->out_buffer); - usbduxsub_tmp->out_buffer = NULL; - kfree(usbduxsub_tmp->dac_commands); - usbduxsub_tmp->dac_commands = NULL; - kfree(usbduxsub_tmp->dux_commands); - usbduxsub_tmp->dux_commands = NULL; - usbduxsub_tmp->ai_cmd_running = 0; - usbduxsub_tmp->ao_cmd_running = 0; - usbduxsub_tmp->pwm_cmd_running = 0; + + /* start the new firmware on the device */ + *tmp = 0; /* 7f92 to zero */ + ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0), + USBDUX_FIRMWARE_CMD, + VENDOR_DIR_OUT, + USBDUX_CPU_CS, 0x0000, + tmp, 1, + BULK_TIMEOUT); + if (ret < 0) + dev_err(dev->class_dev, "can not start firmware\n"); + +done: + kfree(tmp); + kfree(buf); + return ret; } -static int usbdux_attach_common(struct comedi_device *dev, - struct usbduxsub *udev) +static int usbdux_alloc_usb_buffers(struct comedi_device *dev) { - int ret; - struct comedi_subdevice *s = NULL; - int n_subdevs; + struct usb_device *usb = comedi_to_usb_dev(dev); + struct usbdux_private *devpriv = dev->private; + struct urb *urb; + int i; - down(&udev->sem); - /* pointer back to the corresponding comedi device */ - udev->comedidev = dev; + devpriv->dux_commands = kzalloc(SIZEOFDUXBUFFER, GFP_KERNEL); + devpriv->in_buf = kzalloc(SIZEINBUF, GFP_KERNEL); + devpriv->insn_buf = kzalloc(SIZEINSNBUF, GFP_KERNEL); + devpriv->ai_urbs = kcalloc(devpriv->n_ai_urbs, sizeof(void *), + GFP_KERNEL); + devpriv->ao_urbs = kcalloc(devpriv->n_ao_urbs, sizeof(void *), + GFP_KERNEL); + if (!devpriv->dux_commands || !devpriv->in_buf || !devpriv->insn_buf || + !devpriv->ai_urbs || !devpriv->ao_urbs) + return -ENOMEM; - /* set number of subdevices */ - if (udev->high_speed) { - /* with pwm */ - n_subdevs = 5; - } else { - /* without pwm */ - n_subdevs = 4; - } + for (i = 0; i < devpriv->n_ai_urbs; i++) { + /* one frame: 1ms */ + urb = usb_alloc_urb(1, GFP_KERNEL); + if (!urb) + return -ENOMEM; + devpriv->ai_urbs[i] = urb; + + urb->dev = usb; + urb->context = dev; + urb->pipe = usb_rcvisocpipe(usb, 6); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = kzalloc(SIZEINBUF, GFP_KERNEL); + if (!urb->transfer_buffer) + return -ENOMEM; - ret = comedi_alloc_subdevices(dev, n_subdevs); - if (ret) { - up(&udev->sem); - return ret; + urb->complete = usbduxsub_ai_isoc_irq; + urb->number_of_packets = 1; + urb->transfer_buffer_length = SIZEINBUF; + urb->iso_frame_desc[0].offset = 0; + urb->iso_frame_desc[0].length = SIZEINBUF; } - /* private structure is also simply the usb-structure */ - dev->private = udev; + for (i = 0; i < devpriv->n_ao_urbs; i++) { + /* one frame: 1ms */ + urb = usb_alloc_urb(1, GFP_KERNEL); + if (!urb) + return -ENOMEM; + devpriv->ao_urbs[i] = urb; + + urb->dev = usb; + urb->context = dev; + urb->pipe = usb_sndisocpipe(usb, 2); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = kzalloc(SIZEOUTBUF, GFP_KERNEL); + if (!urb->transfer_buffer) + return -ENOMEM; - /* the first subdevice is the A/D converter */ - s = &dev->subdevices[SUBDEV_AD]; - /* the URBs get the comedi subdevice */ - /* which is responsible for reading */ - /* this is the subdevice which reads data */ - dev->read_subdev = s; - /* the subdevice receives as private structure the */ - /* usb-structure */ - s->private = NULL; - /* analog input */ - s->type = COMEDI_SUBD_AI; - /* readable and ref is to ground */ - s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ; - /* 8 channels */ - s->n_chan = 8; - /* length of the channellist */ - s->len_chanlist = 8; - /* callback functions */ - s->insn_read = usbdux_ai_insn_read; - s->do_cmdtest = usbdux_ai_cmdtest; - s->do_cmd = usbdux_ai_cmd; - s->cancel = usbdux_ai_cancel; - /* max value from the A/D converter (12bit) */ - s->maxdata = 0xfff; - /* range table to convert to physical units */ - s->range_table = (&range_usbdux_ai_range); - - /* analog out */ - s = &dev->subdevices[SUBDEV_DA]; - /* analog out */ - s->type = COMEDI_SUBD_AO; - /* backward pointer */ - dev->write_subdev = s; - /* the subdevice receives as private structure the */ - /* usb-structure */ - s->private = NULL; - /* are writable */ - s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE; - /* 4 channels */ - s->n_chan = 4; - /* length of the channellist */ - s->len_chanlist = 4; - /* 12 bit resolution */ - s->maxdata = 0x0fff; - /* bipolar range */ - s->range_table = (&range_usbdux_ao_range); - /* callback */ - s->do_cmdtest = usbdux_ao_cmdtest; - s->do_cmd = usbdux_ao_cmd; - s->cancel = usbdux_ao_cancel; - s->insn_read = usbdux_ao_insn_read; - s->insn_write = usbdux_ao_insn_write; - - /* digital I/O */ - s = &dev->subdevices[SUBDEV_DIO]; - s->type = COMEDI_SUBD_DIO; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE; - s->n_chan = 8; - s->maxdata = 1; - s->range_table = (&range_digital); - s->insn_bits = usbdux_dio_insn_bits; - s->insn_config = usbdux_dio_insn_config; - /* we don't use it */ - s->private = NULL; - - /* counter */ - s = &dev->subdevices[SUBDEV_COUNTER]; - s->type = COMEDI_SUBD_COUNTER; - s->subdev_flags = SDF_WRITABLE | SDF_READABLE; - s->n_chan = 4; - s->maxdata = 0xFFFF; - s->insn_read = usbdux_counter_read; - s->insn_write = usbdux_counter_write; - s->insn_config = usbdux_counter_config; - - if (udev->high_speed) { - /* timer / pwm */ - s = &dev->subdevices[SUBDEV_PWM]; - s->type = COMEDI_SUBD_PWM; - s->subdev_flags = SDF_WRITABLE | SDF_PWM_HBRIDGE; - s->n_chan = 8; - /* this defines the max duty cycle resolution */ - s->maxdata = udev->size_pwm_buf; - s->insn_write = usbdux_pwm_write; - s->insn_read = usbdux_pwm_read; - s->insn_config = usbdux_pwm_config; - usbdux_pwm_period(dev, s, PWM_DEFAULT_PERIOD); + urb->complete = usbduxsub_ao_isoc_irq; + urb->number_of_packets = 1; + urb->transfer_buffer_length = SIZEOUTBUF; + urb->iso_frame_desc[0].offset = 0; + urb->iso_frame_desc[0].length = SIZEOUTBUF; + if (devpriv->high_speed) + urb->interval = 8; /* uframes */ + else + urb->interval = 1; /* frames */ } - /* finally decide that it's attached */ - udev->attached = 1; - up(&udev->sem); + /* pwm */ + if (devpriv->pwm_buf_sz) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + devpriv->pwm_urb = urb; - dev_info(&udev->interface->dev, "comedi%d: attached to usbdux.\n", - dev->minor); + /* max bulk ep size in high speed */ + urb->transfer_buffer = kzalloc(devpriv->pwm_buf_sz, + GFP_KERNEL); + if (!urb->transfer_buffer) + return -ENOMEM; + } return 0; } +static void usbdux_free_usb_buffers(struct comedi_device *dev) +{ + struct usbdux_private *devpriv = dev->private; + struct urb *urb; + int i; + + urb = devpriv->pwm_urb; + if (urb) { + kfree(urb->transfer_buffer); + usb_free_urb(urb); + } + if (devpriv->ao_urbs) { + for (i = 0; i < devpriv->n_ao_urbs; i++) { + urb = devpriv->ao_urbs[i]; + if (urb) { + kfree(urb->transfer_buffer); + usb_free_urb(urb); + } + } + kfree(devpriv->ao_urbs); + } + if (devpriv->ai_urbs) { + for (i = 0; i < devpriv->n_ai_urbs; i++) { + urb = devpriv->ai_urbs[i]; + if (urb) { + kfree(urb->transfer_buffer); + usb_free_urb(urb); + } + } + kfree(devpriv->ai_urbs); + } + kfree(devpriv->insn_buf); + kfree(devpriv->in_buf); + kfree(devpriv->dux_commands); +} + static int usbdux_auto_attach(struct comedi_device *dev, unsigned long context_unused) { - struct usb_interface *uinterf = comedi_to_usb_interface(dev); - struct usbduxsub *this_usbduxsub = usb_get_intfdata(uinterf); - struct usb_device *usb = usbduxsub->usbdev; + struct usb_interface *intf = comedi_to_usb_interface(dev); + struct usb_device *usb = comedi_to_usb_dev(dev); + struct usbdux_private *devpriv; + struct comedi_subdevice *s; int ret; - dev->private = this_usbduxsub; /* This is temporary... */ - ret = comedi_load_firmware(dev, &usb->dev, FIRMWARE, - usbdux_firmware_upload, 0); + devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); + if (!devpriv) + return -ENOMEM; + + sema_init(&devpriv->sem, 1); + + usb_set_intfdata(intf, devpriv); + + devpriv->high_speed = (usb->speed == USB_SPEED_HIGH); + if (devpriv->high_speed) { + devpriv->n_ai_urbs = NUMOFINBUFFERSHIGH; + devpriv->n_ao_urbs = NUMOFOUTBUFFERSHIGH; + devpriv->pwm_buf_sz = 512; + } else { + devpriv->n_ai_urbs = NUMOFINBUFFERSFULL; + devpriv->n_ao_urbs = NUMOFOUTBUFFERSFULL; + } + + ret = usbdux_alloc_usb_buffers(dev); + if (ret) + return ret; + + /* setting to alternate setting 3: enabling iso ep and bulk ep. */ + ret = usb_set_interface(usb, intf->altsetting->desc.bInterfaceNumber, + 3); if (ret < 0) { - dev->private = NULL; + dev_err(dev->class_dev, + "could not set alternate setting 3 in high speed\n"); return ret; } - dev->private = NULL; + ret = comedi_load_firmware(dev, &usb->dev, USBDUX_FIRMWARE, + usbdux_firmware_upload, 0); + if (ret < 0) + return ret; + + ret = comedi_alloc_subdevices(dev, (devpriv->high_speed) ? 5 : 4); + if (ret) + return ret; - down(&start_stop_sem); - if (!this_usbduxsub || !this_usbduxsub->probed) { - dev_err(dev->class_dev, - "usbdux: error: auto_attach failed, not connected\n"); - ret = -ENODEV; - } else if (this_usbduxsub->attached) { - dev_err(dev->class_dev, - "error: auto_attach failed, already attached\n"); - ret = -ENODEV; - } else - ret = usbdux_attach_common(dev, this_usbduxsub); - up(&start_stop_sem); - return ret; + /* Analog Input subdevice */ + s = &dev->subdevices[0]; + dev->read_subdev = s; + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ; + s->n_chan = 8; + s->maxdata = 0x0fff; + s->len_chanlist = 8; + s->range_table = &range_usbdux_ai_range; + s->insn_read = usbdux_ai_insn_read; + s->do_cmdtest = usbdux_ai_cmdtest; + s->do_cmd = usbdux_ai_cmd; + s->cancel = usbdux_ai_cancel; + + /* Analog Output subdevice */ + s = &dev->subdevices[1]; + dev->write_subdev = s; + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE; + s->n_chan = USBDUX_NUM_AO_CHAN; + s->maxdata = 0x0fff; + s->len_chanlist = s->n_chan; + s->range_table = &range_usbdux_ao_range; + s->do_cmdtest = usbdux_ao_cmdtest; + s->do_cmd = usbdux_ao_cmd; + s->cancel = usbdux_ao_cancel; + s->insn_read = usbdux_ao_insn_read; + s->insn_write = usbdux_ao_insn_write; + + /* Digital I/O subdevice */ + s = &dev->subdevices[2]; + s->type = COMEDI_SUBD_DIO; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE; + s->n_chan = 8; + s->maxdata = 1; + s->range_table = &range_digital; + s->insn_bits = usbdux_dio_insn_bits; + s->insn_config = usbdux_dio_insn_config; + + /* Counter subdevice */ + s = &dev->subdevices[3]; + s->type = COMEDI_SUBD_COUNTER; + s->subdev_flags = SDF_WRITABLE | SDF_READABLE; + s->n_chan = 4; + s->maxdata = 0xffff; + s->insn_read = usbdux_counter_read; + s->insn_write = usbdux_counter_write; + s->insn_config = usbdux_counter_config; + + if (devpriv->high_speed) { + /* PWM subdevice */ + s = &dev->subdevices[4]; + s->type = COMEDI_SUBD_PWM; + s->subdev_flags = SDF_WRITABLE | SDF_PWM_HBRIDGE; + s->n_chan = 8; + s->maxdata = devpriv->pwm_buf_sz; + s->insn_write = usbdux_pwm_write; + s->insn_config = usbdux_pwm_config; + + usbdux_pwm_period(dev, s, PWM_DEFAULT_PERIOD); + } + + return 0; } static void usbdux_detach(struct comedi_device *dev) { - struct usbduxsub *usb = dev->private; - - if (usb) { - down(&usb->sem); - dev->private = NULL; - usb->attached = 0; - usb->comedidev = NULL; - up(&usb->sem); - } + struct usb_interface *intf = comedi_to_usb_interface(dev); + struct usbdux_private *devpriv = dev->private; + + usb_set_intfdata(intf, NULL); + + if (!devpriv) + return; + + down(&devpriv->sem); + + /* force unlink all urbs */ + usbdux_pwm_stop(dev, 1); + usbdux_ao_stop(dev, 1); + usbdux_ai_stop(dev, 1); + + usbdux_free_usb_buffers(dev); + + up(&devpriv->sem); } static struct comedi_driver usbdux_driver = { @@ -2299,253 +1810,10 @@ static struct comedi_driver usbdux_driver = { .detach = usbdux_detach, }; -static int usbdux_usb_probe(struct usb_interface *uinterf, +static int usbdux_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_device *udev = interface_to_usbdev(uinterf); - struct device *dev = &uinterf->dev; - int i; - int index; - - dev_dbg(dev, "comedi_: usbdux_: " - "finding a free structure for the usb-device\n"); - - down(&start_stop_sem); - /* look for a free place in the usbdux array */ - index = -1; - for (i = 0; i < NUMUSBDUX; i++) { - if (!(usbduxsub[i].probed)) { - index = i; - break; - } - } - - /* no more space */ - if (index == -1) { - dev_err(dev, "Too many usbdux-devices connected.\n"); - up(&start_stop_sem); - return -EMFILE; - } - dev_dbg(dev, "comedi_: usbdux: " - "usbduxsub[%d] is ready to connect to comedi.\n", index); - - sema_init(&(usbduxsub[index].sem), 1); - /* save a pointer to the usb device */ - usbduxsub[index].usbdev = udev; - - /* 2.6: save the interface itself */ - usbduxsub[index].interface = uinterf; - /* get the interface number from the interface */ - usbduxsub[index].ifnum = uinterf->altsetting->desc.bInterfaceNumber; - /* hand the private data over to the usb subsystem */ - /* will be needed for disconnect */ - usb_set_intfdata(uinterf, &(usbduxsub[index])); - - dev_dbg(dev, "comedi_: usbdux: ifnum=%d\n", usbduxsub[index].ifnum); - - /* test if it is high speed (USB 2.0) */ - usbduxsub[index].high_speed = - (usbduxsub[index].usbdev->speed == USB_SPEED_HIGH); - - /* create space for the commands of the DA converter */ - usbduxsub[index].dac_commands = kzalloc(NUMOUTCHANNELS, GFP_KERNEL); - if (!usbduxsub[index].dac_commands) { - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - /* create space for the commands going to the usb device */ - usbduxsub[index].dux_commands = kzalloc(SIZEOFDUXBUFFER, GFP_KERNEL); - if (!usbduxsub[index].dux_commands) { - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - /* create space for the in buffer and set it to zero */ - usbduxsub[index].in_buffer = kzalloc(SIZEINBUF, GFP_KERNEL); - if (!(usbduxsub[index].in_buffer)) { - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - /* create space of the instruction buffer */ - usbduxsub[index].insn_buffer = kzalloc(SIZEINSNBUF, GFP_KERNEL); - if (!(usbduxsub[index].insn_buffer)) { - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - /* create space for the outbuffer */ - usbduxsub[index].out_buffer = kzalloc(SIZEOUTBUF, GFP_KERNEL); - if (!(usbduxsub[index].out_buffer)) { - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - /* setting to alternate setting 3: enabling iso ep and bulk ep. */ - i = usb_set_interface(usbduxsub[index].usbdev, - usbduxsub[index].ifnum, 3); - if (i < 0) { - dev_err(dev, "comedi_: usbdux%d: " - "could not set alternate setting 3 in high speed.\n", - index); - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENODEV; - } - if (usbduxsub[index].high_speed) - usbduxsub[index].num_in_buffers = NUMOFINBUFFERSHIGH; - else - usbduxsub[index].num_in_buffers = NUMOFINBUFFERSFULL; - - usbduxsub[index].urb_in = - kcalloc(usbduxsub[index].num_in_buffers, sizeof(struct urb *), - GFP_KERNEL); - if (!(usbduxsub[index].urb_in)) { - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - for (i = 0; i < usbduxsub[index].num_in_buffers; i++) { - /* one frame: 1ms */ - usbduxsub[index].urb_in[i] = usb_alloc_urb(1, GFP_KERNEL); - if (usbduxsub[index].urb_in[i] == NULL) { - dev_err(dev, "comedi_: usbdux%d: " - "Could not alloc. urb(%d)\n", index, i); - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - usbduxsub[index].urb_in[i]->dev = usbduxsub[index].usbdev; - /* will be filled later with a pointer to the comedi-device */ - /* and ONLY then the urb should be submitted */ - usbduxsub[index].urb_in[i]->context = NULL; - usbduxsub[index].urb_in[i]->pipe = - usb_rcvisocpipe(usbduxsub[index].usbdev, ISOINEP); - usbduxsub[index].urb_in[i]->transfer_flags = URB_ISO_ASAP; - usbduxsub[index].urb_in[i]->transfer_buffer = - kzalloc(SIZEINBUF, GFP_KERNEL); - if (!(usbduxsub[index].urb_in[i]->transfer_buffer)) { - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - usbduxsub[index].urb_in[i]->complete = usbduxsub_ai_isoc_irq; - usbduxsub[index].urb_in[i]->number_of_packets = 1; - usbduxsub[index].urb_in[i]->transfer_buffer_length = SIZEINBUF; - usbduxsub[index].urb_in[i]->iso_frame_desc[0].offset = 0; - usbduxsub[index].urb_in[i]->iso_frame_desc[0].length = SIZEINBUF; - } - - /* out */ - if (usbduxsub[index].high_speed) - usbduxsub[index].num_out_buffers = NUMOFOUTBUFFERSHIGH; - else - usbduxsub[index].num_out_buffers = NUMOFOUTBUFFERSFULL; - - usbduxsub[index].urb_out = - kcalloc(usbduxsub[index].num_out_buffers, sizeof(struct urb *), - GFP_KERNEL); - if (!(usbduxsub[index].urb_out)) { - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - for (i = 0; i < usbduxsub[index].num_out_buffers; i++) { - /* one frame: 1ms */ - usbduxsub[index].urb_out[i] = usb_alloc_urb(1, GFP_KERNEL); - if (usbduxsub[index].urb_out[i] == NULL) { - dev_err(dev, "comedi_: usbdux%d: " - "Could not alloc. urb(%d)\n", index, i); - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - usbduxsub[index].urb_out[i]->dev = usbduxsub[index].usbdev; - /* will be filled later with a pointer to the comedi-device */ - /* and ONLY then the urb should be submitted */ - usbduxsub[index].urb_out[i]->context = NULL; - usbduxsub[index].urb_out[i]->pipe = - usb_sndisocpipe(usbduxsub[index].usbdev, ISOOUTEP); - usbduxsub[index].urb_out[i]->transfer_flags = URB_ISO_ASAP; - usbduxsub[index].urb_out[i]->transfer_buffer = - kzalloc(SIZEOUTBUF, GFP_KERNEL); - if (!(usbduxsub[index].urb_out[i]->transfer_buffer)) { - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - usbduxsub[index].urb_out[i]->complete = usbduxsub_ao_isoc_irq; - usbduxsub[index].urb_out[i]->number_of_packets = 1; - usbduxsub[index].urb_out[i]->transfer_buffer_length = SIZEOUTBUF; - usbduxsub[index].urb_out[i]->iso_frame_desc[0].offset = 0; - usbduxsub[index].urb_out[i]->iso_frame_desc[0].length = - SIZEOUTBUF; - if (usbduxsub[index].high_speed) { - /* uframes */ - usbduxsub[index].urb_out[i]->interval = 8; - } else { - /* frames */ - usbduxsub[index].urb_out[i]->interval = 1; - } - } - - /* pwm */ - if (usbduxsub[index].high_speed) { - /* max bulk ep size in high speed */ - usbduxsub[index].size_pwm_buf = 512; - usbduxsub[index].urb_pwm = usb_alloc_urb(0, GFP_KERNEL); - if (usbduxsub[index].urb_pwm == NULL) { - dev_err(dev, "comedi_: usbdux%d: " - "Could not alloc. pwm urb\n", index); - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - usbduxsub[index].urb_pwm->transfer_buffer = - kzalloc(usbduxsub[index].size_pwm_buf, GFP_KERNEL); - if (!(usbduxsub[index].urb_pwm->transfer_buffer)) { - tidy_up(&(usbduxsub[index])); - up(&start_stop_sem); - return -ENOMEM; - } - } else { - usbduxsub[index].urb_pwm = NULL; - usbduxsub[index].size_pwm_buf = 0; - } - - usbduxsub[index].ai_cmd_running = 0; - usbduxsub[index].ao_cmd_running = 0; - usbduxsub[index].pwm_cmd_running = 0; - - /* we've reached the bottom of the function */ - usbduxsub[index].probed = 1; - up(&start_stop_sem); - - return comedi_usb_auto_config(uinterf, &usbdux_driver, 0); -} - -static void usbdux_usb_disconnect(struct usb_interface *intf) -{ - struct usbduxsub *usbduxsub_tmp = usb_get_intfdata(intf); - struct usb_device *udev = interface_to_usbdev(intf); - - if (!usbduxsub_tmp) { - dev_err(&intf->dev, - "comedi_: disconnect called with null pointer.\n"); - return; - } - if (usbduxsub_tmp->usbdev != udev) { - dev_err(&intf->dev, "comedi_: BUG! called with wrong ptr!!!\n"); - return; - } - comedi_usb_auto_unconfig(intf); - down(&start_stop_sem); - down(&usbduxsub_tmp->sem); - tidy_up(usbduxsub_tmp); - up(&usbduxsub_tmp->sem); - up(&start_stop_sem); - dev_dbg(&intf->dev, "comedi_: disconnected from the usb\n"); + return comedi_usb_auto_config(intf, &usbdux_driver, 0); } static const struct usb_device_id usbdux_usb_table[] = { @@ -2553,13 +1821,12 @@ static const struct usb_device_id usbdux_usb_table[] = { { USB_DEVICE(0x13d8, 0x0002) }, { } }; - MODULE_DEVICE_TABLE(usb, usbdux_usb_table); static struct usb_driver usbdux_usb_driver = { .name = "usbdux", .probe = usbdux_usb_probe, - .disconnect = usbdux_usb_disconnect, + .disconnect = comedi_usb_auto_unconfig, .id_table = usbdux_usb_table, }; module_comedi_usb_driver(usbdux_driver, usbdux_usb_driver); @@ -2567,4 +1834,4 @@ module_comedi_usb_driver(usbdux_driver, usbdux_usb_driver); MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com"); MODULE_DESCRIPTION("Stirling/ITL USB-DUX -- Bernd.Porr@f2s.com"); MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(FIRMWARE); +MODULE_FIRMWARE(USBDUX_FIRMWARE); |