As suggested the legacy device count is set to zero. A new module parameter
authorBernd Porr <Bernd.Porr@f2s.com>
Mon, 8 Dec 2008 23:30:13 +0000 (23:30 +0000)
committerBernd Porr <Bernd.Porr@f2s.com>
Mon, 8 Dec 2008 23:30:13 +0000 (23:30 +0000)
for comedi_fops allows setting the number of legacy devices: comedi_num_legacy_minors. The default is zero.
usbdux and usbduxfast upload now their firmware via the firmware kernel meachanism. No udev rules are needed for that except the default ones. The firmware will usually be loaded from /lib/firmware. Upload via comedi_config is still possible for static comedi devices (comedi_num_legacy_minors>0). Frank, thanks for the example code which sped up rewriting of the code substantially.

comedi/comedi_fops.c
comedi/comedi_ksyms.c
comedi/drivers.c
comedi/drivers/usbdux.c
comedi/drivers/usbduxfast.c

index 9ac73dde9deefabb8bba6669ba676a6f72fadfa9..9d27a615cafec786d3ae060251f96b4b59348ba8 100644 (file)
@@ -62,6 +62,9 @@ module_param(comedi_debug, int, 0644);
 int comedi_autoconfig = 1;
 module_param(comedi_autoconfig, bool, 0444);
 
+int comedi_num_legacy_minors = 0;
+module_param(comedi_num_legacy_minors, int, 0444);
+
 static DEFINE_SPINLOCK(comedi_file_info_table_lock);
 static struct comedi_device_file_info* comedi_file_info_table[COMEDI_NUM_MINORS];
 
@@ -1895,7 +1898,7 @@ static struct cdev comedi_cdev;
 static void comedi_cleanup_legacy_minors(void)
 {
        unsigned i;
-       for (i = 0; i < COMEDI_NUM_LEGACY_MINORS; i++) {
+       for (i = 0; i < comedi_num_legacy_minors; i++) {
                comedi_free_board_minor(i);
        }
 }
@@ -1935,7 +1938,7 @@ static int __init comedi_init(void)
        comedi_proc_init();
 
        // create devices files for legacy/manual use
-       for (i = 0; i < COMEDI_NUM_LEGACY_MINORS; i++) {
+       for (i = 0; i < comedi_num_legacy_minors; i++) {
                int minor;
                minor = comedi_alloc_board_minor(NULL);
                if(minor < 0)
index 7c3658596990d0149dcf6d158452a1ad0ff2d88c..178c2bd03ae73b76ff2a3492cebc3bcd890ea03b 100644 (file)
@@ -60,6 +60,8 @@ EXPORT_SYMBOL_GPL(comedi_alloc_board_minor);
 EXPORT_SYMBOL_GPL(comedi_free_board_minor);
 EXPORT_SYMBOL_GPL(comedi_pci_auto_config);
 EXPORT_SYMBOL_GPL(comedi_pci_auto_unconfig);
+EXPORT_SYMBOL_GPL(comedi_usb_auto_config);
+EXPORT_SYMBOL_GPL(comedi_usb_auto_unconfig);
 
 /* for kcomedilib */
 EXPORT_SYMBOL(check_chanlist);
index b5059a823059c377f4a43660305ffe44333744f6..bb9b4828cc8d573b91df579f4d1e55bbc631d683 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/device.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/usb.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -796,13 +797,21 @@ int comedi_auto_config(struct device *hardware_device, const char *board_name, c
        int minor;
        struct comedi_device_file_info *dev_file_info;
        int retval;
+       unsigned *private_data = NULL;
 
        if(!comedi_autoconfig)
                return -ENODEV;
 
        minor = comedi_alloc_board_minor(hardware_device);
        if(minor < 0) return minor;
-       dev_set_drvdata(hardware_device, (void*)(unsigned long)minor);
+       
+       private_data = kmalloc(sizeof(unsigned), GFP_KERNEL);
+       if(private_data == NULL) {
+               retval = -ENOMEM;
+               goto cleanup;
+       }
+       *private_data = minor;
+       dev_set_drvdata(hardware_device, private_data);
 
        dev_file_info = comedi_get_device_file_info(minor);
 
@@ -815,8 +824,11 @@ int comedi_auto_config(struct device *hardware_device, const char *board_name, c
        mutex_lock(&dev_file_info->device->mutex);
        retval = comedi_device_attach(dev_file_info->device, &it);
        mutex_unlock(&dev_file_info->device->mutex);
+
+cleanup:       
        if(retval < 0)
        {
+               kfree(private_data);
                comedi_free_board_minor(minor);
        }
        return retval;
@@ -824,11 +836,14 @@ int comedi_auto_config(struct device *hardware_device, const char *board_name, c
 
 void comedi_auto_unconfig(struct device *hardware_device)
 {
-       unsigned long minor = (unsigned long)dev_get_drvdata(hardware_device);
+       unsigned *minor = (unsigned *)dev_get_drvdata(hardware_device);
+       if(minor == NULL) return;
 
-       BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
+       BUG_ON(*minor >= COMEDI_NUM_BOARD_MINORS);
 
-       comedi_free_board_minor(minor);
+       comedi_free_board_minor(*minor);
+       dev_set_drvdata(hardware_device, NULL);
+       kfree(minor);
 }
 
 int comedi_pci_auto_config(struct pci_dev *pcidev, const char *board_name)
@@ -847,3 +862,16 @@ void comedi_pci_auto_unconfig(struct pci_dev *pcidev)
 {
        comedi_auto_unconfig(&pcidev->dev);
 }
+
+int comedi_usb_auto_config(struct usb_device *usbdev,
+       const char *board_name)
+{
+       BUG_ON(usbdev == NULL);
+       return comedi_auto_config(&usbdev->dev, board_name, NULL, 0);
+}
+
+void comedi_usb_auto_unconfig(struct usb_device *usbdev)
+{
+       BUG_ON(usbdev == NULL);
+       comedi_auto_unconfig(&usbdev->dev);
+}
index 3c352e4153b7e1dda9ed35e9fa215c9fefb555a0..14202a15bdc3721123b438a4bd7778d8cff9fdf7 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
@@ -75,6 +75,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
  *
  */
 
@@ -90,6 +91,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 <linux/comedidev.h>
 #include <linux/usb.h>
@@ -762,30 +764,28 @@ static int usbduxsub_start(usbduxsub_t * 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) {
-                       printk("comedi_: usbdux_: 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) {
+               printk("comedi_: usbdux_: control msg failed (start)\n");
+               return errcode;
        }
        return 0;
 }
@@ -795,78 +795,64 @@ static int usbduxsub_stop(usbduxsub_t * 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) {
-                       printk("comedi_: usbdux: 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) {
+               printk("comedi_: usbdux: control msg failed (stop)\n");
+               return errcode;
        }
        return 0;
 }
 
 static int usbduxsub_upload(usbduxsub_t * usbduxsub,
-       uint8_t * local_transfer_buffer,
-       unsigned int startAddr, unsigned int len)
+                           uint8_t * local_transfer_buffer,
+                           unsigned int startAddr, unsigned int len)
 {
        int errcode;
 
-       if (usbduxsub->probed) {
-#ifdef CONFIG_COMEDI_DEBUG
-               printk("comedi%d: usbdux: uploading %d bytes",
-                       usbduxsub->comedidev->minor, len);
-               printk(" to addr %d, first byte=%d.\n",
-                       startAddr, local_transfer_buffer[0]);
-#endif
-               errcode = USB_CONTROL_MSG
-                       (usbduxsub->usbdev,
-                       usb_sndctrlpipe(usbduxsub->usbdev, 0),
-                       // brequest, firmware
-                       USBDUXSUB_FIRMWARE,
-                       // bmRequestType
-                       VENDOR_DIR_OUT,
-                       // value
-                       startAddr,
-                       // index
-                       0x0000,
-                       // our local safe buffer
-                       local_transfer_buffer,
-                       // length
-                       len,
-                       // timeout
-                       EZTIMEOUT);
-#ifdef NOISY_DUX_DEBUGBUG
-               printk("comedi_: usbdux: result=%d\n", errcode);
-#endif
-               if (errcode < 0) {
-                       printk("comedi_: usbdux: uppload failed\n");
-                       return errcode;
-               }
-       } else {
-               // no device on the bus for this index
-               return -EFAULT;
+       errcode = USB_CONTROL_MSG
+               (usbduxsub->usbdev,
+                usb_sndctrlpipe(usbduxsub->usbdev, 0),
+                // brequest, firmware
+                USBDUXSUB_FIRMWARE,
+                // bmRequestType
+                VENDOR_DIR_OUT,
+                // value
+                startAddr,
+                // index
+                0x0000,
+                // our local safe buffer
+                local_transfer_buffer,
+                // length
+                len,
+                // timeout
+                EZTIMEOUT);
+       if (errcode < 0) {
+               printk("comedi_: usbdux: uppload failed\n");
+               return errcode;
        }
        return 0;
 }
 
 int firmwareUpload(usbduxsub_t * usbduxsub,
-       uint8_t * firmwareBinary, int sizeFirmware)
+                  uint8_t * firmwareBinary, 
+                  int sizeFirmware)
 {
        int ret;
 
@@ -2433,10 +2419,41 @@ static int read_firmware(usbduxsub_t * usbduxsub, void *firmwarePtr, long size)
        return res;
 }
 
+
+static void usbdux_firmware_request_complete_handler(
+       const struct firmware *fw,
+       void *context)
+{
+       usbduxsub_t * usbduxsub_tmp = (usbduxsub_t *)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 one 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
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
 static void *usbduxsub_probe(struct usb_device *udev,
-       unsigned int interfnum, const struct usb_device_id *id)
+                            unsigned int interfnum, const struct usb_device_id *id)
 {
 #else
 static int usbduxsub_probe(struct usb_interface *uinterf,
@@ -2446,6 +2463,7 @@ static int usbduxsub_probe(struct usb_interface *uinterf,
 #endif
        int i;
        int index;
+       int ret;
 
 #ifdef CONFIG_COMEDI_DEBUG
        printk("comedi_: usbdux_: finding a free structure for the usb-device\n");
@@ -2677,6 +2695,21 @@ 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(&udev->dev,
+                       "Could not load firmware (err=%d)\n",
+                       ret);
+               return ret;
+       }
        printk("comedi_: usbdux%d has been successfully initialised.\n", index);
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
        return (void *)(&usbduxsub[index]);
@@ -2704,6 +2737,7 @@ static void usbduxsub_disconnect(struct usb_interface *intf)
                printk("comedi_: usbdux: BUG! called with wrong ptr!!!\n");
                return;
        }
+       comedi_usb_auto_unconfig(udev);
        down(&start_stop_sem);
        down(&usbduxsub_tmp->sem);
        tidy_up(usbduxsub_tmp);
@@ -2747,10 +2781,10 @@ static int usbdux_attach(comedi_device * dev, comedi_devconfig * it)
        if (comedi_aux_data(it->options, 0) &&
                it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
                read_firmware(usbduxsub + index,
-                       comedi_aux_data(it->options, 0),
-                       it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]);
+                             comedi_aux_data(it->options, 0),
+                             it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]);
        }
-
+       
        dev->board_name = BOARDNAME;
 
        /* set number of subdevices */
index f1526ebdfc60940a5382230db46f9f873ab400ca..a42efb8dd4b11afe303551ab2f3f2ae23d6db238 100644 (file)
@@ -1,4 +1,4 @@
-#define DRIVER_VERSION "v0.99a"
+#define DRIVER_VERSION "v1.0"
 #define DRIVER_AUTHOR "Bernd Porr, BerndPorr@f2s.com"
 #define DRIVER_DESC "USB-DUXfast, BerndPorr@f2s.com"
 /*
@@ -25,8 +25,8 @@ Driver: usbduxfast
 Description: ITL USB-DUXfast
 Devices: [ITL] USB-DUX (usbduxfast.o)
 Author: Bernd Porr <BerndPorr@f2s.com>
-Updated: 04 Dec 2006
-Status: testing
+Updated: 08 Dec 2008
+Status: stable
 */
 
 /*
@@ -45,9 +45,12 @@ Status: testing
  *       Added insn command basically for testing. Sample rate is 1MHz/16ch=62.5kHz
  * 0.99: Ian Abbott pointed out a bug which has been corrected. Thanks!
  * 0.99a: added external trigger.
+ * 1.00: added firmware kernel request to the driver which fixed
+ *       udev coldplug problem
  */
 
 #include <linux/kernel.h>
+#include <linux/firmware.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -419,30 +422,28 @@ static int usbduxfastsub_start(usbduxfastsub_t * usbduxfastsub)
        int errcode = 0;
        unsigned char local_transfer_buffer[16];
 
-       if (usbduxfastsub->probed) {
-               // 7f92 to zero
-               local_transfer_buffer[0] = 0;
-               errcode = USB_CONTROL_MSG(usbduxfastsub->usbdev,
-                       // create a pipe for a control transfer
-                       usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
-                       // bRequest, "Firmware"
-                       USBDUXFASTSUB_FIRMWARE,
-                       // bmRequestType
-                       VENDOR_DIR_OUT,
-                       // Value
-                       USBDUXFASTSUB_CPUCS,
-                       // Index
-                       0x0000,
-                       // address of the transfer buffer
-                       local_transfer_buffer,
-                       // Length
-                       1,
-                       // Timeout
-                       EZTIMEOUT);
-               if (errcode < 0) {
-                       printk("comedi_: usbduxfast_: control msg failed (start)\n");
-                       return errcode;
-               }
+       // 7f92 to zero
+       local_transfer_buffer[0] = 0;
+       errcode = USB_CONTROL_MSG(usbduxfastsub->usbdev,
+                                 // create a pipe for a control transfer
+                                 usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
+                                 // bRequest, "Firmware"
+                                 USBDUXFASTSUB_FIRMWARE,
+                                 // bmRequestType
+                                 VENDOR_DIR_OUT,
+                                 // Value
+                                 USBDUXFASTSUB_CPUCS,
+                                 // Index
+                                 0x0000,
+                                 // address of the transfer buffer
+                                 local_transfer_buffer,
+                                 // Length
+                                 1,
+                                 // Timeout
+                                 EZTIMEOUT);
+       if (errcode < 0) {
+               printk("comedi_: usbduxfast_: control msg failed (start)\n");
+               return errcode;
        }
        return 0;
 }
@@ -452,28 +453,26 @@ static int usbduxfastsub_stop(usbduxfastsub_t * usbduxfastsub)
        int errcode = 0;
 
        unsigned char local_transfer_buffer[16];
-       if (usbduxfastsub->probed) {
-               // 7f92 to one
-               local_transfer_buffer[0] = 1;
-               errcode = USB_CONTROL_MSG
-                       (usbduxfastsub->usbdev,
-                       usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
-                       // bRequest, "Firmware"
-                       USBDUXFASTSUB_FIRMWARE,
-                       // bmRequestType
-                       VENDOR_DIR_OUT,
-                       // Value
-                       USBDUXFASTSUB_CPUCS,
-                       // Index
-                       0x0000, local_transfer_buffer,
-                       // Length
-                       1,
-                       // Timeout
-                       EZTIMEOUT);
-               if (errcode < 0) {
-                       printk("comedi_: usbduxfast: control msg failed (stop)\n");
-                       return errcode;
-               }
+       // 7f92 to one
+       local_transfer_buffer[0] = 1;
+       errcode = USB_CONTROL_MSG
+               (usbduxfastsub->usbdev,
+                usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
+                // bRequest, "Firmware"
+                USBDUXFASTSUB_FIRMWARE,
+                // bmRequestType
+                VENDOR_DIR_OUT,
+                // Value
+                USBDUXFASTSUB_CPUCS,
+                // Index
+                0x0000, local_transfer_buffer,
+                // Length
+                1,
+                // Timeout
+                EZTIMEOUT);
+       if (errcode < 0) {
+               printk("comedi_: usbduxfast: control msg failed (stop)\n");
+               return errcode;
        }
        return 0;
 }
@@ -484,40 +483,26 @@ static int usbduxfastsub_upload(usbduxfastsub_t * usbduxfastsub,
 {
        int errcode;
 
-       if (usbduxfastsub->probed) {
-#ifdef CONFIG_COMEDI_DEBUG
-               printk("comedi%d: usbduxfast: uploading %d bytes",
-                       usbduxfastsub->comedidev->minor, len);
-               printk(" to addr %d, first byte=%d.\n",
-                       startAddr, local_transfer_buffer[0]);
-#endif
-               errcode = USB_CONTROL_MSG
-                       (usbduxfastsub->usbdev,
-                       usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
-                       // brequest, firmware
-                       USBDUXFASTSUB_FIRMWARE,
-                       // bmRequestType
-                       VENDOR_DIR_OUT,
-                       // value
-                       startAddr,
-                       // index
-                       0x0000,
-                       // our local safe buffer
-                       local_transfer_buffer,
-                       // length
-                       len,
-                       // timeout
-                       EZTIMEOUT);
-#ifdef CONFIG_COMEDI_DEBUG
-               printk("comedi_: usbduxfast: result=%d\n", errcode);
-#endif
-               if (errcode < 0) {
-                       printk("comedi_: usbduxfast: uppload failed\n");
-                       return errcode;
-               }
-       } else {
-               // no device on the bus for this index
-               return -EFAULT;
+       errcode = USB_CONTROL_MSG
+               (usbduxfastsub->usbdev,
+                usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
+                // brequest, firmware
+                USBDUXFASTSUB_FIRMWARE,
+                // bmRequestType
+                VENDOR_DIR_OUT,
+                // value
+                startAddr,
+                // index
+                0x0000,
+                // our local safe buffer
+                local_transfer_buffer,
+                // length
+                len,
+                // timeout
+                EZTIMEOUT);
+       if (errcode < 0) {
+               printk("comedi_: usbduxfast: uppload failed\n");
+               return errcode;
        }
        return 0;
 }
@@ -1294,8 +1279,9 @@ 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(usbduxfastsub_t * usbduxfastsub, void *firmwarePtr,
-       long size)
+static int read_firmware(usbduxfastsub_t * usbduxfastsub, 
+                        void *firmwarePtr,
+                        long size)
 {
        int i = 0;
        unsigned char *fp = (char *)firmwarePtr;
@@ -1432,10 +1418,39 @@ static void tidy_up(usbduxfastsub_t * usbduxfastsub_tmp)
        usbduxfastsub_tmp->ai_cmd_running = 0;
 }
 
+static void usbduxfast_firmware_request_complete_handler(
+       const struct firmware *fw, 
+       void *context)
+{
+       usbduxfastsub_t * usbduxfastsub_tmp = (usbduxfastsub_t *)context;
+       struct usb_device *usbdev = usbduxfastsub_tmp->usbdev;
+       int ret;
+
+       if(fw == NULL) {
+               return;
+       }
+
+       // we need to upload the firmware here because fw will be
+       // freed one we've left this function
+       ret=read_firmware(usbduxfastsub_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
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
 static void *usbduxfastsub_probe(struct usb_device *udev,
-       unsigned int interfnum, const struct usb_device_id *id)
+                                unsigned int interfnum, 
+                                const struct usb_device_id *id)
 {
 #else
 static int usbduxfastsub_probe(struct usb_interface *uinterf,
@@ -1445,6 +1460,7 @@ static int usbduxfastsub_probe(struct usb_interface *uinterf,
 #endif
        int i;
        int index;
+       int ret;
 
        if (udev->speed != USB_SPEED_HIGH) {
                printk("comedi_: usbduxfast_: This driver needs USB 2.0 to operate. Aborting...\n");
@@ -1537,8 +1553,24 @@ static int usbduxfastsub_probe(struct usb_interface *uinterf,
        // we've reached the bottom of the function
        usbduxfastsub[index].probed = 1;
        up(&start_stop_sem);
+
+       ret = request_firmware_nowait(THIS_MODULE,
+                                     FW_ACTION_HOTPLUG, 
+                                     "usbduxfast_firmware.hex",
+                                     &udev->dev,
+                                     usbduxfastsub + index, 
+                                     usbduxfast_firmware_request_complete_handler);
+
+       if (ret) {
+               dev_err(&udev->dev,
+                       "could not load firmware (err=%d)\n",
+                       ret);
+               return ret;
+       }
        printk("comedi_: usbduxfast%d has been successfully initialized.\n",
-               index);
+              index);
+       
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
        return (void *)(&usbduxfastsub[index]);
 #else
@@ -1565,6 +1597,9 @@ static void usbduxfastsub_disconnect(struct usb_interface *intf)
                printk("comedi_: usbduxfast: BUG! called with wrong ptr!!!\n");
                return;
        }
+       
+       comedi_usb_auto_unconfig(udev);
+
        down(&start_stop_sem);
        down(&usbduxfastsub_tmp->sem);
        tidy_up(usbduxfastsub_tmp);
@@ -1606,10 +1641,10 @@ static int usbduxfast_attach(comedi_device * dev, comedi_devconfig * it)
 
        // trying to upload the firmware into the chip
        if (comedi_aux_data(it->options, 0) &&
-               it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
+           it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
                read_firmware(&usbduxfastsub[index],
-                       comedi_aux_data(it->options, 0),
-                       it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]);
+                             comedi_aux_data(it->options, 0),
+                             it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]);
        }
 
        dev->board_name = BOARDNAME;