]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/staging/comedi/drivers/usbdux.c
Merge branch 'omap-pool'
[linux-2.6-omap-h63xx.git] / drivers / staging / comedi / drivers / usbdux.c
index 35138257be7fbc9f316d0013c6a204ea045c838f..d0b59e98314b7bb44d4be4d220cae435efd8f612 100644 (file)
@@ -1,4 +1,4 @@
-#define DRIVER_VERSION "v2.1"
+#define DRIVER_VERSION "v2.2"
 #define DRIVER_AUTHOR "Bernd Porr, BerndPorr@f2s.com"
 #define DRIVER_DESC "Stirling/ITL USB-DUX -- Bernd.Porr@f2s.com"
 /*
@@ -25,8 +25,8 @@ Driver: usbdux
 Description: University of Stirling USB DAQ & INCITE Technology Limited
 Devices: [ITL] USB-DUX (usbdux.o)
 Author: Bernd Porr <BerndPorr@f2s.com>
-Updated: 25 Nov 2007
-Status: Testing
+Updated: 8 Dec 2008
+Status: Stable
 Configuration options:
   You have to upload firmware with the -i option. The
   firmware is usually installed under /usr/share/usb or
@@ -79,6 +79,7 @@ sampling rate. If you sample two channels you get 4kHz and so on.
  * 1.2:  added PWM suport via EP4
  * 2.0:  PWM seems to be stable and is not interfering with the other functions
  * 2.1:  changed PWM API
+ * 2.2:  added firmware kernel request to fix an udev problem
  *
  */
 
@@ -94,6 +95,7 @@ sampling rate. If you sample two channels you get 4kHz and so on.
 #include <linux/smp_lock.h>
 #include <linux/fcntl.h>
 #include <linux/compiler.h>
+#include <linux/firmware.h>
 
 #include "../comedidev.h"
 
@@ -212,7 +214,7 @@ sampling rate. If you sample two channels you get 4kHz and so on.
 
 /**************************************************/
 /* comedi constants */
-static const comedi_lrange range_usbdux_ai_range = { 4, {
+static const struct comedi_lrange range_usbdux_ai_range = { 4, {
                        BIP_RANGE(4.096),
                        BIP_RANGE(4.096 / 2),
                        UNI_RANGE(4.096),
@@ -220,7 +222,7 @@ static const comedi_lrange range_usbdux_ai_range = { 4, {
        }
 };
 
-static const comedi_lrange range_usbdux_ao_range = { 2, {
+static const struct comedi_lrange range_usbdux_ao_range = { 2, {
                        BIP_RANGE(4.096),
                        UNI_RANGE(4.096),
        }
@@ -251,7 +253,7 @@ struct usbduxsub {
        /* pwm-transfer handling */
        struct urb *urbPwm;
        /* PWM period */
-       lsampl_t pwmPeriod;
+       unsigned int pwmPeriod;
        /* PWM internal delay for the GPIF in the FX2 */
        int8_t pwmDelay;
        /* size of the PWM buffer which holds the bit pattern */
@@ -267,7 +269,7 @@ struct usbduxsub {
        /* interface structure in 2.6 */
        struct usb_interface *interface;
        /* comedi device for the interrupt context */
-       comedi_device *comedidev;
+       struct comedi_device *comedidev;
        /* is it USB_SPEED_HIGH or not? */
        short int high_speed;
        /* asynchronous command is running */
@@ -361,7 +363,7 @@ static int usbdux_ai_stop(struct usbduxsub *this_usbduxsub, int do_unlink)
  * This will cancel a running acquisition operation.
  * This is called by comedi but never from inside the driver.
  */
-static int usbdux_ai_cancel(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 {
        struct usbduxsub *this_usbduxsub;
        int res = 0;
@@ -390,8 +392,8 @@ static void usbduxsub_ai_IsocIrq(struct urb *urb)
 {
        int i, err, n;
        struct usbduxsub *this_usbduxsub;
-       comedi_device *this_comedidev;
-       comedi_subdevice *s;
+       struct comedi_device *this_comedidev;
+       struct comedi_subdevice *s;
 
        /* the context variable points to the subdevice */
        this_comedidev = urb->context;
@@ -559,7 +561,7 @@ static int usbdux_ao_stop(struct usbduxsub *this_usbduxsub, int do_unlink)
 }
 
 /* force unlink, is called by comedi */
-static int usbdux_ao_cancel(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_ao_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 {
        struct usbduxsub *this_usbduxsub = dev->private;
        int res = 0;
@@ -584,8 +586,8 @@ static void usbduxsub_ao_IsocIrq(struct urb *urb)
        int i, ret;
        int8_t *datap;
        struct usbduxsub *this_usbduxsub;
-       comedi_device *this_comedidev;
-       comedi_subdevice *s;
+       struct comedi_device *this_comedidev;
+       struct comedi_subdevice *s;
 
        /* the context variable points to the subdevice */
        this_comedidev = urb->context;
@@ -654,7 +656,7 @@ static void usbduxsub_ao_IsocIrq(struct urb *urb)
                ((uint8_t *) (urb->transfer_buffer))[0] =
                        s->async->cmd.chanlist_len;
                for (i = 0; i < s->async->cmd.chanlist_len; i++) {
-                       sampl_t temp;
+                       short temp;
                        if (i >= NUMOUTCHANNELS)
                                break;
 
@@ -718,31 +720,29 @@ static int usbduxsub_start(struct usbduxsub *usbduxsub)
        int errcode = 0;
        uint8_t local_transfer_buffer[16];
 
-       if (usbduxsub->probed) {
-               /* 7f92 to zero */
-               local_transfer_buffer[0] = 0;
-               errcode = usb_control_msg(usbduxsub->usbdev,
-                       /* create a pipe for a control transfer */
-                       usb_sndctrlpipe(usbduxsub->usbdev, 0),
-                       /* bRequest, "Firmware" */
-                       USBDUXSUB_FIRMWARE,
-                       /* bmRequestType */
-                       VENDOR_DIR_OUT,
-                       /* Value */
-                       USBDUXSUB_CPUCS,
-                       /* Index */
-                       0x0000,
-                       /* address of the transfer buffer */
-                       local_transfer_buffer,
-                       /* Length */
-                       1,
-                       /* Timeout */
-                       EZTIMEOUT);
-               if (errcode < 0) {
-                       dev_err(&usbduxsub->interface->dev,
-                               "comedi_: control msg failed (start)\n");
-                       return errcode;
-               }
+       /* 7f92 to zero */
+       local_transfer_buffer[0] = 0;
+       errcode = usb_control_msg(usbduxsub->usbdev,
+                                 /* create a pipe for a control transfer */
+                                 usb_sndctrlpipe(usbduxsub->usbdev, 0),
+                                 /* bRequest, "Firmware" */
+                                 USBDUXSUB_FIRMWARE,
+                                 /* bmRequestType */
+                                 VENDOR_DIR_OUT,
+                                 /* Value */
+                                 USBDUXSUB_CPUCS,
+                                 /* Index */
+                                 0x0000,
+                                 /* address of the transfer buffer */
+                                 local_transfer_buffer,
+                                 /* Length */
+                                 1,
+                                 /* Timeout */
+                                 EZTIMEOUT);
+       if (errcode < 0) {
+               dev_err(&usbduxsub->interface->dev,
+                       "comedi_: control msg failed (start)\n");
+               return errcode;
        }
        return 0;
 }
@@ -752,28 +752,27 @@ static int usbduxsub_stop(struct usbduxsub *usbduxsub)
        int errcode = 0;
 
        uint8_t local_transfer_buffer[16];
-       if (usbduxsub->probed) {
-               /* 7f92 to one */
-               local_transfer_buffer[0] = 1;
-               errcode = usb_control_msg(usbduxsub->usbdev,
-                       usb_sndctrlpipe(usbduxsub->usbdev, 0),
-                       /* bRequest, "Firmware" */
-                       USBDUXSUB_FIRMWARE,
-                       /* bmRequestType */
-                       VENDOR_DIR_OUT,
-                       /* Value */
-                       USBDUXSUB_CPUCS,
-                       /* Index */
-                       0x0000, local_transfer_buffer,
-                       /* Length */
-                       1,
-                       /* Timeout */
-                       EZTIMEOUT);
-               if (errcode < 0) {
-                       dev_err(&usbduxsub->interface->dev,
-                               "comedi_: control msg failed (stop)\n");
-                       return errcode;
-               }
+
+       /* 7f92 to one */
+       local_transfer_buffer[0] = 1;
+       errcode = usb_control_msg(usbduxsub->usbdev,
+                                 usb_sndctrlpipe(usbduxsub->usbdev, 0),
+                                 /* bRequest, "Firmware" */
+                                 USBDUXSUB_FIRMWARE,
+                                 /* bmRequestType */
+                                 VENDOR_DIR_OUT,
+                                 /* Value */
+                                 USBDUXSUB_CPUCS,
+                                 /* Index */
+                                 0x0000, local_transfer_buffer,
+                                 /* Length */
+                                 1,
+                                 /* Timeout */
+                                 EZTIMEOUT);
+       if (errcode < 0) {
+               dev_err(&usbduxsub->interface->dev,
+                       "comedi_: control msg failed (stop)\n");
+               return errcode;
        }
        return 0;
 }
@@ -784,13 +783,7 @@ static int usbduxsub_upload(struct usbduxsub *usbduxsub,
 {
        int errcode;
 
-       if (usbduxsub->probed) {
-               dev_dbg(&usbduxsub->interface->dev,
-                       "comedi%d: usbdux: uploading %d bytes"
-                       " to addr %d, first byte=%d.\n",
-                       usbduxsub->comedidev->minor, len,
-                       startAddr, local_transfer_buffer[0]);
-               errcode = usb_control_msg(usbduxsub->usbdev,
+       errcode = usb_control_msg(usbduxsub->usbdev,
                        usb_sndctrlpipe(usbduxsub->usbdev, 0),
                        /* brequest, firmware */
                        USBDUXSUB_FIRMWARE,
@@ -806,16 +799,12 @@ static int usbduxsub_upload(struct usbduxsub *usbduxsub,
                        len,
                        /* timeout */
                        EZTIMEOUT);
-               dev_dbg(&usbduxsub->interface->dev,
-                       "comedi_: result=%d\n", errcode);
-               if (errcode < 0) {
-                       dev_err(&usbduxsub->interface->dev,
-                               "comedi_: upload failed\n");
-                       return errcode;
-               }
-       } else {
-               /* no device on the bus for this index */
-               return -EFAULT;
+       dev_dbg(&usbduxsub->interface->dev,
+               "comedi_: result=%d\n", errcode);
+       if (errcode < 0) {
+               dev_err(&usbduxsub->interface->dev,
+               "comedi_: upload failed\n");
+               return errcode;
        }
        return 0;
 }
@@ -907,8 +896,8 @@ static int usbduxsub_submit_OutURBs(struct usbduxsub *usbduxsub)
        return 0;
 }
 
-static int usbdux_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
-                            comedi_cmd *cmd)
+static int usbdux_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+                            struct comedi_cmd *cmd)
 {
        int err = 0, tmp, i;
        unsigned int tmpTimer;
@@ -1127,7 +1116,7 @@ static int receive_dux_commands(struct usbduxsub *this_usbduxsub, int command)
        return -EFAULT;
 }
 
-static int usbdux_ai_inttrig(comedi_device *dev, comedi_subdevice *s,
+static int usbdux_ai_inttrig(struct comedi_device *dev, struct comedi_subdevice *s,
                             unsigned int trignum)
 {
        int ret;
@@ -1171,9 +1160,9 @@ static int usbdux_ai_inttrig(comedi_device *dev, comedi_subdevice *s,
        return 1;
 }
 
-static int usbdux_ai_cmd(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 {
-       comedi_cmd *cmd = &s->async->cmd;
+       struct comedi_cmd *cmd = &s->async->cmd;
        unsigned int chan, range;
        int i, ret;
        struct usbduxsub *this_usbduxsub = dev->private;
@@ -1287,11 +1276,11 @@ static int usbdux_ai_cmd(comedi_device *dev, comedi_subdevice *s)
 }
 
 /* Mode 0 is used to get a single conversion on demand */
-static int usbdux_ai_insn_read(comedi_device *dev, comedi_subdevice *s,
-                              comedi_insn *insn, lsampl_t *data)
+static int usbdux_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+                              struct comedi_insn *insn, unsigned int *data)
 {
        int i;
-       lsampl_t one = 0;
+       unsigned int one = 0;
        int chan, range;
        int err;
        struct usbduxsub *this_usbduxsub = dev->private;
@@ -1348,8 +1337,8 @@ static int usbdux_ai_insn_read(comedi_device *dev, comedi_subdevice *s,
 /************************************/
 /* analog out */
 
-static int usbdux_ao_insn_read(comedi_device *dev, comedi_subdevice *s,
-                              comedi_insn *insn, lsampl_t *data)
+static int usbdux_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+                              struct comedi_insn *insn, unsigned int *data)
 {
        int i;
        int chan = CR_CHAN(insn->chanspec);
@@ -1370,8 +1359,8 @@ static int usbdux_ao_insn_read(comedi_device *dev, comedi_subdevice *s,
        return i;
 }
 
-static int usbdux_ao_insn_write(comedi_device *dev, comedi_subdevice *s,
-                               comedi_insn *insn, lsampl_t *data)
+static int usbdux_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s,
+                               struct comedi_insn *insn, unsigned int *data)
 {
        int i, err;
        int chan = CR_CHAN(insn->chanspec);
@@ -1420,7 +1409,7 @@ static int usbdux_ao_insn_write(comedi_device *dev, comedi_subdevice *s,
        return i;
 }
 
-static int usbdux_ao_inttrig(comedi_device *dev, comedi_subdevice *s,
+static int usbdux_ao_inttrig(struct comedi_device *dev, struct comedi_subdevice *s,
                             unsigned int trignum)
 {
        int ret;
@@ -1461,8 +1450,8 @@ static int usbdux_ao_inttrig(comedi_device *dev, comedi_subdevice *s,
        return 1;
 }
 
-static int usbdux_ao_cmdtest(comedi_device *dev, comedi_subdevice *s,
-                            comedi_cmd *cmd)
+static int usbdux_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+                            struct comedi_cmd *cmd)
 {
        int err = 0, tmp;
        struct usbduxsub *this_usbduxsub = dev->private;
@@ -1600,9 +1589,9 @@ static int usbdux_ao_cmdtest(comedi_device *dev, comedi_subdevice *s,
        return 0;
 }
 
-static int usbdux_ao_cmd(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 {
-       comedi_cmd *cmd = &s->async->cmd;
+       struct comedi_cmd *cmd = &s->async->cmd;
        unsigned int chan, gain;
        int i, ret;
        struct usbduxsub *this_usbduxsub = dev->private;
@@ -1708,8 +1697,8 @@ static int usbdux_ao_cmd(comedi_device *dev, comedi_subdevice *s)
        return 0;
 }
 
-static int usbdux_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
-                                 comedi_insn *insn, lsampl_t *data)
+static int usbdux_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+                                 struct comedi_insn *insn, unsigned int *data)
 {
        int chan = CR_CHAN(insn->chanspec);
 
@@ -1739,8 +1728,8 @@ static int usbdux_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
        return insn->n;
 }
 
-static int usbdux_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
-                               comedi_insn *insn, lsampl_t *data)
+static int usbdux_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
+                               struct comedi_insn *insn, unsigned int *data)
 {
 
        struct usbduxsub *this_usbduxsub = dev->private;
@@ -1786,8 +1775,8 @@ static int usbdux_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
 }
 
 /* reads the 4 counters, only two are used just now */
-static int usbdux_counter_read(comedi_device *dev, comedi_subdevice *s,
-                              comedi_insn *insn, lsampl_t *data)
+static int usbdux_counter_read(struct comedi_device *dev, struct comedi_subdevice *s,
+                              struct comedi_insn *insn, unsigned int *data)
 {
        struct usbduxsub *this_usbduxsub = dev->private;
        int chan = insn->chanspec;
@@ -1820,8 +1809,8 @@ static int usbdux_counter_read(comedi_device *dev, comedi_subdevice *s,
        return 1;
 }
 
-static int usbdux_counter_write(comedi_device *dev, comedi_subdevice *s,
-                               comedi_insn *insn, lsampl_t *data)
+static int usbdux_counter_write(struct comedi_device *dev, struct comedi_subdevice *s,
+                               struct comedi_insn *insn, unsigned int *data)
 {
        struct usbduxsub *this_usbduxsub = dev->private;
        int err;
@@ -1850,8 +1839,8 @@ static int usbdux_counter_write(comedi_device *dev, comedi_subdevice *s,
        return 1;
 }
 
-static int usbdux_counter_config(comedi_device *dev, comedi_subdevice *s,
-                                comedi_insn *insn, lsampl_t *data)
+static int usbdux_counter_config(struct comedi_device *dev, struct comedi_subdevice *s,
+                                struct comedi_insn *insn, unsigned int *data)
 {
        /* nothing to do so far */
        return 2;
@@ -1894,7 +1883,7 @@ static int usbdux_pwm_stop(struct usbduxsub *this_usbduxsub, int do_unlink)
 }
 
 /* force unlink - is called by comedi */
-static int usbdux_pwm_cancel(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_pwm_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 {
        struct usbduxsub *this_usbduxsub = dev->private;
        int res = 0;
@@ -1916,8 +1905,8 @@ static void usbduxsub_pwm_irq(struct urb *urb)
 {
        int ret;
        struct usbduxsub *this_usbduxsub;
-       comedi_device *this_comedidev;
-       comedi_subdevice *s;
+       struct comedi_device *this_comedidev;
+       struct comedi_subdevice *s;
 
        /* printk(KERN_DEBUG "PWM: IRQ\n"); */
 
@@ -2007,8 +1996,8 @@ static int usbduxsub_submit_PwmURBs(struct usbduxsub *usbduxsub)
        return 0;
 }
 
-static int usbdux_pwm_period(comedi_device *dev, comedi_subdevice *s,
-                            lsampl_t period)
+static int usbdux_pwm_period(struct comedi_device *dev, struct comedi_subdevice *s,
+                            unsigned int period)
 {
        struct usbduxsub *this_usbduxsub = dev->private;
        int fx2delay = 255;
@@ -2035,7 +2024,7 @@ static int usbdux_pwm_period(comedi_device *dev, comedi_subdevice *s,
 }
 
 /* is called from insn so there's no need to do all the sanity checks */
-static int usbdux_pwm_start(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_pwm_start(struct comedi_device *dev, struct comedi_subdevice *s)
 {
        int ret, i;
        struct usbduxsub *this_usbduxsub = dev->private;
@@ -2067,8 +2056,8 @@ static int usbdux_pwm_start(comedi_device *dev, comedi_subdevice *s)
 }
 
 /* generates the bit pattern for PWM with the optional sign bit */
-static int usbdux_pwm_pattern(comedi_device *dev, comedi_subdevice *s,
-                             int channel, lsampl_t value, lsampl_t sign)
+static int usbdux_pwm_pattern(struct comedi_device *dev, struct comedi_subdevice *s,
+                             int channel, unsigned int value, unsigned int sign)
 {
        struct usbduxsub *this_usbduxsub = dev->private;
        int i, szbuf;
@@ -2108,8 +2097,8 @@ static int usbdux_pwm_pattern(comedi_device *dev, comedi_subdevice *s,
        return 1;
 }
 
-static int usbdux_pwm_write(comedi_device *dev, comedi_subdevice *s,
-                           comedi_insn *insn, lsampl_t *data)
+static int usbdux_pwm_write(struct comedi_device *dev, struct comedi_subdevice *s,
+                           struct comedi_insn *insn, unsigned int *data)
 {
        struct usbduxsub *this_usbduxsub = dev->private;
 
@@ -2133,16 +2122,16 @@ static int usbdux_pwm_write(comedi_device *dev, comedi_subdevice *s,
                                  data[0], 0);
 }
 
-static int usbdux_pwm_read(comedi_device *x1, comedi_subdevice *x2,
-                          comedi_insn *x3, lsampl_t *x4)
+static int usbdux_pwm_read(struct comedi_device *x1, struct comedi_subdevice *x2,
+                          struct comedi_insn *x3, unsigned int *x4)
 {
        /* not needed */
        return -EINVAL;
 };
 
 /* switches on/off PWM */
-static int usbdux_pwm_config(comedi_device *dev, comedi_subdevice *s,
-                            comedi_insn *insn, lsampl_t *data)
+static int usbdux_pwm_config(struct comedi_device *dev, struct comedi_subdevice *s,
+                            struct comedi_insn *insn, unsigned int *data)
 {
        struct usbduxsub *this_usbduxsub = dev->private;
        switch (data[0]) {
@@ -2292,13 +2281,13 @@ static unsigned hex2unsigned(char *h)
 #define FIRMWARE_MAX_LEN 0x2000
 
 /* taken from David Brownell's fxload and adjusted for this driver */
-static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
+static int read_firmware(struct usbduxsub *usbduxsub, const void *firmwarePtr,
                         long size)
 {
        struct device *dev = &usbduxsub->interface->dev;
        int i = 0;
        unsigned char *fp = (char *)firmwarePtr;
-       unsigned char *firmwareBinary = NULL;
+       unsigned char *firmwareBinary;
        int res = 0;
        int maxAddr = 0;
 
@@ -2322,6 +2311,7 @@ static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
                        j++;
                        if (j >= sizeof(buf)) {
                                dev_err(dev, "comedi_: bogus firmware file!\n");
+                               kfree(firmwareBinary);
                                return -1;
                        }
                }
@@ -2344,6 +2334,7 @@ static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
                if (buf[0] != ':') {
                        dev_err(dev, "comedi_: upload: not an ihex record: %s",
                                buf);
+                       kfree(firmwareBinary);
                        return -EFAULT;
                }
 
@@ -2360,6 +2351,7 @@ static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
                if (maxAddr >= FIRMWARE_MAX_LEN) {
                        dev_err(dev, "comedi_: firmware upload goes "
                                "beyond FX2 RAM boundaries.\n");
+                       kfree(firmwareBinary);
                        return -EFAULT;
                }
                /* dev_dbg(dev, "comedi_: off=%x, len=%x:\n", off, len); */
@@ -2375,6 +2367,7 @@ static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
                if (type != 0) {
                        dev_err(dev, "comedi_: unsupported record type: %u\n",
                                type);
+                       kfree(firmwareBinary);
                        return -EFAULT;
                }
 
@@ -2395,6 +2388,34 @@ static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
        return res;
 }
 
+static void usbdux_firmware_request_complete_handler(const struct firmware *fw,
+                                                    void *context)
+{
+       struct usbduxsub *usbduxsub_tmp = context;
+       struct usb_device *usbdev = usbduxsub_tmp->usbdev;
+       int ret;
+
+       if (fw == NULL) {
+               dev_err(&usbdev->dev,
+                       "Firmware complete handler without firmware!\n");
+               return;
+       }
+
+       /*
+        * we need to upload the firmware here because fw will be
+        * freed once we've left this function
+        */
+       ret = read_firmware(usbduxsub_tmp, fw->data, fw->size);
+
+       if (ret) {
+               dev_err(&usbdev->dev,
+                       "Could not upload firmware (err=%d)\n",
+                       ret);
+               return;
+       }
+       comedi_usb_auto_config(usbdev, BOARDNAME);
+}
+
 /* allocate memory for the urbs and initialise them */
 static int usbduxsub_probe(struct usb_interface *uinterf,
                           const struct usb_device_id *id)
@@ -2403,6 +2424,7 @@ static int usbduxsub_probe(struct usb_interface *uinterf,
        struct device *dev = &uinterf->dev;
        int i;
        int index;
+       int ret;
 
        dev_dbg(dev, "comedi_: usbdux_: "
                "finding a free structure for the usb-device\n");
@@ -2637,6 +2659,19 @@ static int usbduxsub_probe(struct usb_interface *uinterf,
        /* we've reached the bottom of the function */
        usbduxsub[index].probed = 1;
        up(&start_stop_sem);
+
+       ret = request_firmware_nowait(THIS_MODULE,
+                                     FW_ACTION_HOTPLUG,
+                                     "usbdux_firmware.hex",
+                                     &udev->dev,
+                                     usbduxsub + index,
+                                     usbdux_firmware_request_complete_handler);
+
+       if (ret) {
+               dev_err(dev, "Could not load firmware (err=%d)\n", ret);
+               return ret;
+       }
+
        dev_info(dev, "comedi_: usbdux%d "
                 "has been successfully initialised.\n", index);
        /* success */
@@ -2658,6 +2693,7 @@ static void usbduxsub_disconnect(struct usb_interface *intf)
                        "comedi_: BUG! called with wrong ptr!!!\n");
                return;
        }
+       comedi_usb_auto_unconfig(udev);
        down(&start_stop_sem);
        down(&usbduxsub_tmp->sem);
        tidy_up(usbduxsub_tmp);
@@ -2667,14 +2703,14 @@ static void usbduxsub_disconnect(struct usb_interface *intf)
 }
 
 /* is called when comedi-config is called */
-static int usbdux_attach(comedi_device *dev, comedi_devconfig *it)
+static int usbdux_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 {
        int ret;
        int index;
        int i;
        struct usbduxsub *udev;
 
-       comedi_subdevice *s = NULL;
+       struct comedi_subdevice *s = NULL;
        dev->private = NULL;
 
        down(&start_stop_sem);
@@ -2834,7 +2870,7 @@ static int usbdux_attach(comedi_device *dev, comedi_devconfig *it)
        return 0;
 }
 
-static int usbdux_detach(comedi_device *dev)
+static int usbdux_detach(struct comedi_device *dev)
 {
        struct usbduxsub *usbduxsub_tmp;
 
@@ -2867,26 +2903,13 @@ static int usbdux_detach(comedi_device *dev)
 }
 
 /* main driver struct */
-static comedi_driver driver_usbdux = {
+static struct comedi_driver driver_usbdux = {
       .driver_name =   "usbdux",
       .module =                THIS_MODULE,
       .attach =                usbdux_attach,
       .detach =                usbdux_detach,
 };
 
-static void init_usb_devices(void)
-{
-       int index;
-
-       /* all devices entries are invalid to begin with */
-       /* they will become valid by the probe function */
-       /* and then finally by the attach-function */
-       for (index = 0; index < NUMUSBDUX; index++) {
-               memset(&(usbduxsub[index]), 0x00, sizeof(usbduxsub[index]));
-               init_MUTEX(&(usbduxsub[index].sem));
-       }
-}
-
 /* Table with the USB-devices: just now only testing IDs */
 static struct usb_device_id usbduxsub_table[] = {
        {USB_DEVICE(0x13d8, 0x0001) },
@@ -2907,18 +2930,17 @@ static struct usb_driver usbduxsub_driver = {
 /* Can't use the nice macro as I have also to initialise the USB */
 /* subsystem: */
 /* registering the usb-system _and_ the comedi-driver */
-static int init_usbdux(void)
+static int __init init_usbdux(void)
 {
        printk(KERN_INFO KBUILD_MODNAME ": "
               DRIVER_VERSION ":" DRIVER_DESC "\n");
-       init_usb_devices();
        usb_register(&usbduxsub_driver);
        comedi_driver_register(&driver_usbdux);
        return 0;
 }
 
 /* deregistering the comedi driver and the usb-subsystem */
-static void exit_usbdux(void)
+static void __exit exit_usbdux(void)
 {
        comedi_driver_unregister(&driver_usbdux);
        usb_deregister(&usbduxsub_driver);