A colon in the description confused one of the scripts down the line in comedilib.
[comedi.git] / comedi / drivers / usbduxfast.c
index 57e30de3465c46297354ec06ee8614cc69242a77..80c6b0bdd65b5a5a03e8941b30a2d87b2930c387 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"
 /*
 
 /*
 Driver: usbduxfast
-Description: ITL USB-DUXfast
-Devices: [ITL] USB-DUX (usbduxfast.o)
-Author: Bernd Porr <BerndPorr@f2s.com>
-Updated: 04 Dec 2006
-Status: testing
+Description: Driver for USB-DUX-FAST of INCITE Technology Limited
+Devices: [ITL] USB-DUX (usbduxfast)
+Author: Bernd Porr <tech@linux-usb-daq.co.uk>
+Updated: 13 May 2012
+Status: stable
+
+The device has one subdevice for analogue input.
+  - subdevice: 0
+    number of channels: 16
+    max data value: 4096
+    ranges:
+      all channelss: 
+        range = 0 : [-0.75 V,0.75 V] 
+        range = 1 : [-0.5 V,0.5 V]
+    command:
+      The channel-list allows 1,2,3 and 16 channels.
+      start: now|ext|int (external trigger via pin at HD-D connector)
+      scan_begin: follow|timer|ext
+      convert: timer|ext (contains the sampling interval. Min interval
+                          for single channel acquisition is 33us 
+                          and for multiplexed acquisition 300us)
+      scan_end: count
+      stop: none|count
+
+Configuration options:
+  The device requires firmware which is usually
+  uploaded automatically by udev/hotplug at the moment
+  the driver module is being loaded.
+  In case udev/hotplug is not enabled you need to upload 
+  the firmware with comedi_config -i usbduxfast_firmware.bin
+  The firmware is usually installed under /lib/firmware
+  or can be downloaded form http://www.linux-usb-daq.co.uk.
 */
 
 /*
@@ -45,15 +72,17 @@ 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>
 #include <linux/input.h>
 #include <linux/usb.h>
-#include <linux/smp_lock.h>
 #include <linux/fcntl.h>
 #include <linux/compiler.h>
 #include "comedi_fc.h"
@@ -67,7 +96,7 @@ Status: testing
 #define BOARDNAME "usbduxfast"
 
 // timeout for the USB-transfer
-#define EZTIMEOUT 30
+#define BULK_TIMEOUT 1000
 
 // constants for "firmware" upload and download
 #define USBDUXFASTSUB_FIRMWARE 0xA0
@@ -171,7 +200,7 @@ typedef struct {
        uint8_t *dux_commands;
        // counter which ignores the first buffers
        int ignore;
-       struct semaphore sem;
+       struct mutex mutex;
 } usbduxfastsub_t;
 
 // The pointer to the private usb-data of the driver
@@ -183,7 +212,7 @@ typedef struct {
 // initialised before comedi can access it.
 static usbduxfastsub_t usbduxfastsub[NUMUSBDUXFAST];
 
-static DECLARE_MUTEX(start_stop_sem);
+static DEFINE_MUTEX(start_stop_mutex);
 
 // bulk transfers to usbduxfast
 
@@ -207,7 +236,7 @@ static int send_dux_commands(usbduxfastsub_t * this_usbduxfastsub, int cmd_type)
                usb_sndbulkpipe(this_usbduxfastsub->usbdev,
                        CHANNELLISTEP),
                this_usbduxfastsub->dux_commands,
-               SIZEOFDUXBUFFER, &nsent, 10000);
+               SIZEOFDUXBUFFER, &nsent, BULK_TIMEOUT);
        if (result < 0) {
                printk("comedi%d: could not transmit dux_commands to the usb-device, err=%d\n", this_usbduxfastsub->comedidev->minor, result);
        }
@@ -283,14 +312,14 @@ static int usbduxfast_ai_cancel(comedi_device * dev, comedi_subdevice * s)
                printk("comedi: usbduxfast_ai_cancel: this_usbduxfastsub=NULL\n");
                return -EFAULT;
        }
-       down(&this_usbduxfastsub->sem);
+       mutex_lock(&this_usbduxfastsub->mutex);
        if (!(this_usbduxfastsub->probed)) {
-               up(&this_usbduxfastsub->sem);
+               mutex_unlock(&this_usbduxfastsub->mutex);
                return -ENODEV;
        }
        // unlink
        res = usbduxfast_ai_stop(this_usbduxfastsub, 1);
-       up(&this_usbduxfastsub->sem);
+       mutex_unlock(&this_usbduxfastsub->mutex);
 
        return res;
 }
@@ -389,8 +418,14 @@ static void usbduxfastsub_ai_Irq(struct urb *urb PT_REGS_ARG)
                        this_usbduxfastsub->ai_sample_count -= n;
                }
                // write the full buffer to comedi
-               cfc_write_array_to_buffer(s,
-                       urb->transfer_buffer, urb->actual_length);
+               err = cfc_write_array_to_buffer(s,
+                                               urb->transfer_buffer, urb->actual_length);
+
+               if (unlikely(err == 0)) {
+                       /* buffer overflow */
+                       usbduxfast_ai_stop(this_usbduxfastsub, 0);
+                       return;
+               }
 
                // tell comedi that data is there
                comedi_event(this_usbduxfastsub->comedidev, s);
@@ -419,30 +454,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
+                                 BULK_TIMEOUT);
+       if (errcode < 0) {
+               printk("comedi_: usbduxfast_: control msg failed (start)\n");
+               return errcode;
        }
        return 0;
 }
@@ -452,28 +485,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
+                BULK_TIMEOUT);
+       if (errcode < 0) {
+               printk("comedi_: usbduxfast: control msg failed (stop)\n");
+               return errcode;
        }
        return 0;
 }
@@ -484,40 +515,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
+                BULK_TIMEOUT);
+       if (errcode < 0) {
+               printk("comedi_: usbduxfast: uppload failed\n");
+               return errcode;
        }
        return 0;
 }
@@ -722,9 +739,9 @@ static int usbduxfast_ai_inttrig(comedi_device * dev,
        if (!this_usbduxfastsub) {
                return -EFAULT;
        }
-       down(&this_usbduxfastsub->sem);
+       mutex_lock(&this_usbduxfastsub->mutex);
        if (!(this_usbduxfastsub->probed)) {
-               up(&this_usbduxfastsub->sem);
+               mutex_unlock(&this_usbduxfastsub->mutex);
                return -ENODEV;
        }
 #ifdef CONFIG_COMEDI_DEBUG
@@ -734,7 +751,7 @@ static int usbduxfast_ai_inttrig(comedi_device * dev,
        if (trignum != 0) {
                printk("comedi%d: usbduxfast_ai_inttrig: invalid trignum\n",
                        dev->minor);
-               up(&this_usbduxfastsub->sem);
+               mutex_unlock(&this_usbduxfastsub->mutex);
                return -EINVAL;
        }
        if (!(this_usbduxfastsub->ai_cmd_running)) {
@@ -743,7 +760,7 @@ static int usbduxfast_ai_inttrig(comedi_device * dev,
                if (ret < 0) {
                        printk("comedi%d: usbduxfast_ai_inttrig: urbSubmit: err=%d\n", dev->minor, ret);
                        this_usbduxfastsub->ai_cmd_running = 0;
-                       up(&this_usbduxfastsub->sem);
+                       mutex_unlock(&this_usbduxfastsub->mutex);
                        return ret;
                }
                s->async->inttrig = NULL;
@@ -751,7 +768,7 @@ static int usbduxfast_ai_inttrig(comedi_device * dev,
                printk("comedi%d: ai_inttrig but acqu is already running\n",
                        dev->minor);
        }
-       up(&this_usbduxfastsub->sem);
+       mutex_unlock(&this_usbduxfastsub->mutex);
        return 1;
 }
 
@@ -777,14 +794,14 @@ static int usbduxfast_ai_cmd(comedi_device * dev, comedi_subdevice * s)
        if (!this_usbduxfastsub) {
                return -EFAULT;
        }
-       down(&this_usbduxfastsub->sem);
+       mutex_lock(&this_usbduxfastsub->mutex);
        if (!(this_usbduxfastsub->probed)) {
-               up(&this_usbduxfastsub->sem);
+               mutex_unlock(&this_usbduxfastsub->mutex);
                return -ENODEV;
        }
        if (this_usbduxfastsub->ai_cmd_running) {
                printk("comedi%d: ai_cmd not possible. Another ai_cmd is running.\n", dev->minor);
-               up(&this_usbduxfastsub->sem);
+               mutex_unlock(&this_usbduxfastsub->mutex);
                return -EBUSY;
        }
        // set current channel of the running aquisition to zero
@@ -799,13 +816,13 @@ static int usbduxfast_ai_cmd(comedi_device * dev, comedi_subdevice * s)
                        chan = CR_CHAN(cmd->chanlist[i]);
                        if (chan != i) {
                                printk("comedi%d: cmd is accepting only consecutive channels.\n", dev->minor);
-                               up(&this_usbduxfastsub->sem);
+                               mutex_unlock(&this_usbduxfastsub->mutex);
                                return -EINVAL;
                        }
                        if ((gain != CR_RANGE(cmd->chanlist[i]))
                                && (cmd->chanlist_len > 3)) {
                                printk("comedi%d: the gain must be the same for all channels.\n", dev->minor);
-                               up(&this_usbduxfastsub->sem);
+                               mutex_unlock(&this_usbduxfastsub->mutex);
                                return -EINVAL;
                        }
                        if (i >= NUMCHANNELS) {
@@ -818,7 +835,7 @@ static int usbduxfast_ai_cmd(comedi_device * dev, comedi_subdevice * s)
        steps = 0;
        if (cmd->scan_begin_src == TRIG_TIMER) {
                printk("comedi%d: usbduxfast: scan_begin_src==TRIG_TIMER not valid.\n", dev->minor);
-               up(&this_usbduxfastsub->sem);
+               mutex_unlock(&this_usbduxfastsub->mutex);
                return -EINVAL;
        }
        if (cmd->convert_src == TRIG_TIMER) {
@@ -826,19 +843,19 @@ static int usbduxfast_ai_cmd(comedi_device * dev, comedi_subdevice * s)
        }
        if ((steps < MIN_SAMPLING_PERIOD) && (cmd->chanlist_len != 1)) {
                printk("comedi%d: usbduxfast: ai_cmd: steps=%ld, scan_begin_arg=%d. Not properly tested by cmdtest?\n", dev->minor, steps, cmd->scan_begin_arg);
-               up(&this_usbduxfastsub->sem);
+               mutex_unlock(&this_usbduxfastsub->mutex);
                return -EINVAL;
        }
        if (steps > MAX_SAMPLING_PERIOD) {
                printk("comedi%d: usbduxfast: ai_cmd: sampling rate too low.\n",
                        dev->minor);
-               up(&this_usbduxfastsub->sem);
+               mutex_unlock(&this_usbduxfastsub->mutex);
                return -EINVAL;
        }
        if ((cmd->start_src == TRIG_EXT) && (cmd->chanlist_len != 1)
                && (cmd->chanlist_len != 16)) {
                printk("comedi%d: usbduxfast: ai_cmd: TRIG_EXT only with 1 or 16 channels possible.\n", dev->minor);
-               up(&this_usbduxfastsub->sem);
+               mutex_unlock(&this_usbduxfastsub->mutex);
                return -EINVAL;
        }
 #ifdef CONFIG_COMEDI_DEBUG
@@ -1091,7 +1108,7 @@ static int usbduxfast_ai_cmd(comedi_device * dev, comedi_subdevice * s)
        default:
                printk("comedi %d: unsupported combination of channels\n",
                        dev->minor);
-               up(&this_usbduxfastsub->sem);
+               mutex_unlock(&this_usbduxfastsub->mutex);
                return -EFAULT;
        }
 
@@ -1102,7 +1119,7 @@ static int usbduxfast_ai_cmd(comedi_device * dev, comedi_subdevice * s)
        result = send_dux_commands(this_usbduxfastsub, SENDADCOMMANDS);
        if (result < 0) {
                printk("comedi%d: adc command could not be submitted. Aborting...\n", dev->minor);
-               up(&this_usbduxfastsub->sem);
+               mutex_unlock(&this_usbduxfastsub->mutex);
                return result;
        }
        if (cmd->stop_src == TRIG_COUNT) {
@@ -1110,7 +1127,7 @@ static int usbduxfast_ai_cmd(comedi_device * dev, comedi_subdevice * s)
                        (cmd->stop_arg) * (cmd->scan_end_arg);
                if (usbduxfastsub->ai_sample_count < 1) {
                        printk("comedi%d: (cmd->stop_arg)*(cmd->scan_end_arg)<1, aborting.\n", dev->minor);
-                       up(&this_usbduxfastsub->sem);
+                       mutex_unlock(&this_usbduxfastsub->mutex);
                        return -EFAULT;
                }
                this_usbduxfastsub->ai_continous = 0;
@@ -1127,7 +1144,7 @@ static int usbduxfast_ai_cmd(comedi_device * dev, comedi_subdevice * s)
                if (ret < 0) {
                        this_usbduxfastsub->ai_cmd_running = 0;
                        // fixme: unlink here??
-                       up(&this_usbduxfastsub->sem);
+                       mutex_unlock(&this_usbduxfastsub->mutex);
                        return ret;
                }
                s->async->inttrig = NULL;
@@ -1137,7 +1154,7 @@ static int usbduxfast_ai_cmd(comedi_device * dev, comedi_subdevice * s)
                // wait for an internal signal
                s->async->inttrig = usbduxfast_ai_inttrig;
        }
-       up(&this_usbduxfastsub->sem);
+       mutex_unlock(&this_usbduxfastsub->mutex);
 
        return 0;
 }
@@ -1159,14 +1176,14 @@ static int usbduxfast_ai_insn_read(comedi_device * dev,
        printk("comedi%d: ai_insn_read, insn->n=%d, insn->subdev=%d\n",
                dev->minor, insn->n, insn->subdev);
 #endif
-       down(&usbduxfastsub->sem);
+       mutex_lock(&usbduxfastsub->mutex);
        if (!(usbduxfastsub->probed)) {
-               up(&usbduxfastsub->sem);
+               mutex_unlock(&usbduxfastsub->mutex);
                return -ENODEV;
        }
        if (usbduxfastsub->ai_cmd_running) {
                printk("comedi%d: ai_insn_read not possible. Async Command is running.\n", dev->minor);
-               up(&usbduxfastsub->sem);
+               mutex_unlock(&usbduxfastsub->mutex);
                return -EBUSY;
        }
        // sample one channel
@@ -1223,7 +1240,7 @@ static int usbduxfast_ai_insn_read(comedi_device * dev,
        err = send_dux_commands(usbduxfastsub, SENDADCOMMANDS);
        if (err < 0) {
                printk("comedi%d: adc command could not be submitted. Aborting...\n", dev->minor);
-               up(&usbduxfastsub->sem);
+               mutex_unlock(&usbduxfastsub->mutex);
                return err;
        }
 #ifdef CONFIG_COMEDI_DEBUG
@@ -1236,11 +1253,11 @@ static int usbduxfast_ai_insn_read(comedi_device * dev,
                err = USB_BULK_MSG(usbduxfastsub->usbdev,
                        usb_rcvbulkpipe(usbduxfastsub->usbdev, BULKINEP),
                        usbduxfastsub->transfer_buffer,
-                       SIZEINBUF, &actual_length, 10000);
+                       SIZEINBUF, &actual_length, BULK_TIMEOUT);
                if (err < 0) {
                        printk("comedi%d: insn timeout. No data.\n",
                                dev->minor);
-                       up(&usbduxfastsub->sem);
+                       mutex_unlock(&usbduxfastsub->mutex);
                        return err;
                }
        }
@@ -1249,18 +1266,18 @@ static int usbduxfast_ai_insn_read(comedi_device * dev,
                err = USB_BULK_MSG(usbduxfastsub->usbdev,
                        usb_rcvbulkpipe(usbduxfastsub->usbdev, BULKINEP),
                        usbduxfastsub->transfer_buffer,
-                       SIZEINBUF, &actual_length, 10000);
+                       SIZEINBUF, &actual_length, BULK_TIMEOUT);
                if (err < 0) {
                        printk("comedi%d: insn data error: %d\n",
                                dev->minor, err);
-                       up(&usbduxfastsub->sem);
+                       mutex_unlock(&usbduxfastsub->mutex);
                        return err;
                }
                n = actual_length / sizeof(uint16_t);
                if ((n % 16) != 0) {
                        printk("comedi%d: insn data packet corrupted.\n",
                                dev->minor);
-                       up(&usbduxfastsub->sem);
+                       mutex_unlock(&usbduxfastsub->mutex);
                        return -EINVAL;
                }
                for (j = chan; (j < n) && (i < insn->n); j = j + 16) {
@@ -1270,7 +1287,7 @@ static int usbduxfast_ai_insn_read(comedi_device * dev,
                        i++;
                }
        }
-       up(&usbduxfastsub->sem);
+       mutex_unlock(&usbduxfastsub->mutex);
        return i;
 }
 
@@ -1294,8 +1311,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,
+                        const void *firmwarePtr,
+                        long size)
 {
        int i = 0;
        unsigned char *fp = (char *)firmwarePtr;
@@ -1432,10 +1450,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 +1492,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");
@@ -1453,7 +1501,7 @@ static int usbduxfastsub_probe(struct usb_interface *uinterf,
 #ifdef CONFIG_COMEDI_DEBUG
        printk("comedi_: usbduxfast_: finding a free structure for the usb-device\n");
 #endif
-       down(&start_stop_sem);
+       mutex_lock(&start_stop_mutex);
        // look for a free place in the usbduxfast array
        index = -1;
        for (i = 0; i < NUMUSBDUXFAST; i++) {
@@ -1466,14 +1514,14 @@ static int usbduxfastsub_probe(struct usb_interface *uinterf,
        // no more space
        if (index == -1) {
                printk("Too many usbduxfast-devices connected.\n");
-               up(&start_stop_sem);
+               mutex_unlock(&start_stop_mutex);
                return PROBE_ERR_RETURN(-EMFILE);
        }
 #ifdef CONFIG_COMEDI_DEBUG
        printk("comedi_: usbduxfast: usbduxfastsub[%d] is ready to connect to comedi.\n", index);
 #endif
 
-       init_MUTEX(&(usbduxfastsub[index].sem));
+       mutex_init(&(usbduxfastsub[index].mutex));
        // save a pointer to the usb device
        usbduxfastsub[index].usbdev = udev;
 
@@ -1499,7 +1547,7 @@ static int usbduxfastsub_probe(struct usb_interface *uinterf,
        if (!usbduxfastsub[index].dux_commands) {
                printk("comedi_: usbduxfast: error alloc space for dac commands\n");
                tidy_up(&(usbduxfastsub[index]));
-               up(&start_stop_sem);
+               mutex_unlock(&start_stop_mutex);
                return PROBE_ERR_RETURN(-ENOMEM);
        }
        // create space of the instruction buffer
@@ -1507,7 +1555,7 @@ static int usbduxfastsub_probe(struct usb_interface *uinterf,
        if (!(usbduxfastsub[index].insnBuffer)) {
                printk("comedi_: usbduxfast: could not alloc space for insnBuffer\n");
                tidy_up(&(usbduxfastsub[index]));
-               up(&start_stop_sem);
+               mutex_unlock(&start_stop_mutex);
                return PROBE_ERR_RETURN(-ENOMEM);
        }
        // setting to alternate setting 1: enabling bulk ep
@@ -1516,14 +1564,14 @@ static int usbduxfastsub_probe(struct usb_interface *uinterf,
        if (i < 0) {
                printk("comedi_: usbduxfast%d: could not switch to alternate setting 1.\n", index);
                tidy_up(&(usbduxfastsub[index]));
-               up(&start_stop_sem);
+               mutex_unlock(&start_stop_mutex);
                return PROBE_ERR_RETURN(-ENODEV);
        }
        usbduxfastsub[index].urbIn = USB_ALLOC_URB(0);
        if (usbduxfastsub[index].urbIn == NULL) {
                printk("comedi_: usbduxfast%d: Could not alloc. urb\n", index);
                tidy_up(&(usbduxfastsub[index]));
-               up(&start_stop_sem);
+               mutex_unlock(&start_stop_mutex);
                return PROBE_ERR_RETURN(-ENOMEM);
        }
        usbduxfastsub[index].transfer_buffer = kmalloc(SIZEINBUF, GFP_KERNEL);
@@ -1531,14 +1579,31 @@ static int usbduxfastsub_probe(struct usb_interface *uinterf,
                printk("comedi_: usbduxfast%d: could not alloc. transb.\n",
                        index);
                tidy_up(&(usbduxfastsub[index]));
-               up(&start_stop_sem);
+               mutex_unlock(&start_stop_mutex);
                return PROBE_ERR_RETURN(-ENOMEM);
        }
        // we've reached the bottom of the function
        usbduxfastsub[index].probed = 1;
-       up(&start_stop_sem);
+       mutex_unlock(&start_stop_mutex);
+
+       ret = request_firmware_nowait(THIS_MODULE,
+                                     FW_ACTION_HOTPLUG,
+                                     "usbduxfast_firmware.hex",
+                                     &udev->dev,
+                                     GFP_KERNEL,
+                                     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,11 +1630,14 @@ static void usbduxfastsub_disconnect(struct usb_interface *intf)
                printk("comedi_: usbduxfast: BUG! called with wrong ptr!!!\n");
                return;
        }
-       down(&start_stop_sem);
-       down(&usbduxfastsub_tmp->sem);
+
+       comedi_usb_auto_unconfig(udev);
+
+       mutex_lock(&start_stop_mutex);
+       mutex_lock(&usbduxfastsub_tmp->mutex);
        tidy_up(usbduxfastsub_tmp);
-       up(&usbduxfastsub_tmp->sem);
-       up(&start_stop_sem);
+       mutex_unlock(&usbduxfastsub_tmp->mutex);
+       mutex_unlock(&start_stop_mutex);
 #ifdef CONFIG_COMEDI_DEBUG
        printk("comedi_: usbduxfast: disconnected from the usb\n");
 #endif
@@ -1584,7 +1652,7 @@ static int usbduxfast_attach(comedi_device * dev, comedi_devconfig * it)
        comedi_subdevice *s = NULL;
        dev->private = NULL;
 
-       down(&start_stop_sem);
+       mutex_lock(&start_stop_mutex);
        // find a valid device which has been detected by the probe function of the usb
        index = -1;
        for (i = 0; i < NUMUSBDUXFAST; i++) {
@@ -1596,20 +1664,20 @@ static int usbduxfast_attach(comedi_device * dev, comedi_devconfig * it)
 
        if (index < 0) {
                printk("comedi%d: usbduxfast: error: attach failed, no usbduxfast devs connected to the usb bus.\n", dev->minor);
-               up(&start_stop_sem);
+               mutex_unlock(&start_stop_mutex);
                return -ENODEV;
        }
 
-       down(&(usbduxfastsub[index].sem));
+       mutex_lock(&(usbduxfastsub[index].mutex));
        // pointer back to the corresponding comedi device
        usbduxfastsub[index].comedidev = dev;
 
        // trying to upload the firmware into the chip
        if (comedi_aux_data(it->options, 0) &&
-               it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
-               read_firmware(usbduxfastsub,
-                       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]);
        }
 
        dev->board_name = BOARDNAME;
@@ -1621,7 +1689,7 @@ static int usbduxfast_attach(comedi_device * dev, comedi_devconfig * it)
        if ((ret = alloc_subdevices(dev, N_SUBDEVICES)) < 0) {
                printk("comedi%d: usbduxfast: error alloc space for subdev\n",
                        dev->minor);
-               up(&start_stop_sem);
+               mutex_unlock(&start_stop_mutex);
                return ret;
        }
 
@@ -1659,9 +1727,9 @@ static int usbduxfast_attach(comedi_device * dev, comedi_devconfig * it)
        // finally decide that it's attached
        usbduxfastsub[index].attached = 1;
 
-       up(&(usbduxfastsub[index].sem));
+       mutex_unlock(&(usbduxfastsub[index].mutex));
 
-       up(&start_stop_sem);
+       mutex_unlock(&start_stop_mutex);
 
        printk("comedi%d: successfully attached to usbduxfast.\n", dev->minor);
 
@@ -1687,8 +1755,8 @@ static int usbduxfast_detach(comedi_device * dev)
                return -EFAULT;
        }
 
-       down(&usbduxfastsub_tmp->sem);
-       down(&start_stop_sem);
+       mutex_lock(&usbduxfastsub_tmp->mutex);
+       mutex_lock(&start_stop_mutex);
        // Don't allow detach to free the private structure
        // It's one entry of of usbduxfastsub[]
        dev->private = NULL;
@@ -1698,8 +1766,8 @@ static int usbduxfast_detach(comedi_device * dev)
        printk("comedi%d: usbduxfast: detach: successfully removed\n",
                dev->minor);
 #endif
-       up(&start_stop_sem);
-       up(&usbduxfastsub_tmp->sem);
+       mutex_unlock(&start_stop_mutex);
+       mutex_unlock(&usbduxfastsub_tmp->mutex);
        return 0;
 }
 
@@ -1723,7 +1791,16 @@ static void init_usb_devices(void)
        for (index = 0; index < NUMUSBDUXFAST; index++) {
                memset(&(usbduxfastsub[index]), 0x00,
                        sizeof(usbduxfastsub[index]));
-               init_MUTEX(&(usbduxfastsub[index].sem));
+               mutex_init(&(usbduxfastsub[index].mutex));
+       }
+}
+
+static void uninit_usb_devices(void)
+{
+       int index;
+
+       for (index = 0; index < NUMUSBDUXFAST; index++) {
+               mutex_destroy(&(usbduxfastsub[index].mutex));
        }
 }
 
@@ -1756,7 +1833,8 @@ static struct usb_driver usbduxfastsub_driver = {
 // registering the usb-system _and_ the comedi-driver
 static int init_usbduxfast(void)
 {
-       info(DRIVER_VERSION ":" DRIVER_DESC);
+       printk(KERN_INFO KBUILD_MODNAME ": "
+              DRIVER_VERSION ":" DRIVER_DESC "\n");
        init_usb_devices();
        usb_register(&usbduxfastsub_driver);
        comedi_driver_register(&driver_usbduxfast);
@@ -1768,6 +1846,7 @@ static void exit_usbduxfast(void)
 {
        comedi_driver_unregister(&driver_usbduxfast);
        usb_deregister(&usbduxfastsub_driver);
+       uninit_usb_devices();
 }
 
 module_init(init_usbduxfast);