staging: comedi: dt9812: convert to use comedi (*auto_attach)
authorH Hartley Sweeten <hsweeten@visionengravers.com>
Tue, 14 May 2013 21:30:20 +0000 (14:30 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 16 May 2013 23:26:00 +0000 (16:26 -0700)
Converting this driver to use the comedi (*auto_attach) mechanism
allows pushing the usb (*probe) into the comedi (*auto_attach) and
the usb (disconnect) into the comedi (*detach). This removes the
disconnect between the usb driver and the comedi driver. Now when
the comedi driver is attached it will always have a usb device
associated with it.

This removes the 16 usb device limitation and allows bringing all
the private data into a single struct that can be kzalloc'ed when
the comedi driver is (*auto_attached). It also allows removing the
the sanity checks that make sure a usb device is connected to the
comedi device in the helper functions.

For aesthetic reasons, add some whitespace to the subdevice init.

Also, fix the analog out subdevice. There are 2 analog output
channels available on the usb device.

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/drivers/dt9812.c

index 57bdb06921e3fa40d268a82ce8efa5f2ad749475..0efff288a86a4c7966eec61dbb6acda24763d455 100644 (file)
@@ -45,7 +45,6 @@ for my needs.
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/module.h>
-#include <linux/kref.h>
 #include <linux/uaccess.h>
 #include <linux/usb.h>
 
@@ -258,55 +257,26 @@ struct dt9812_usb_cmd {
 #endif
 };
 
-#define DT9812_NUM_SLOTS       16
-
-static DEFINE_SEMAPHORE(dt9812_mutex);
-
-struct usb_dt9812 {
-       struct slot_dt9812 *slot;
-       struct usb_device *udev;
-       u16 vendor;
-       u16 product;
-       u16 device;
-       u32 serial;
+struct dt9812_private {
+       struct semaphore sem;
        struct {
                __u8 addr;
                size_t size;
        } cmd_wr, cmd_rd;
-       struct kref kref;
-};
-
-struct dt9812_private {
-       struct semaphore sem;
-       struct slot_dt9812 *slot;
+       u32 serial;
+       u16 vendor;
+       u16 product;
+       u16 device;
        u16 ao_shadow[2];
        u8 do_shadow;
 };
 
-struct slot_dt9812 {
-       struct usb_dt9812 *usb;
-       struct dt9812_private *devpriv;
-};
-
-static struct slot_dt9812 dt9812[DT9812_NUM_SLOTS];
-
-static inline struct usb_dt9812 *to_dt9812_dev(struct kref *d)
-{
-       return container_of(d, struct usb_dt9812, kref);
-}
-
-static void dt9812_delete(struct kref *kref)
-{
-       struct usb_dt9812 *dev = to_dt9812_dev(kref);
-
-       usb_put_dev(dev->udev);
-       kfree(dev);
-}
-
-static int dt9812_read_info(struct usb_dt9812 *dev, int offset, void *buf,
-                           size_t buf_size)
+static int dt9812_read_info(struct comedi_device *dev,
+                           int offset, void *buf, size_t buf_size)
 {
-       struct usb_device *usb = dev->udev;
+       struct usb_interface *intf = comedi_to_usb_interface(dev);
+       struct usb_device *usb = interface_to_usbdev(intf);
+       struct dt9812_private *devpriv = dev->private;
        struct dt9812_usb_cmd cmd;
        int count, ret;
 
@@ -316,19 +286,22 @@ static int dt9812_read_info(struct usb_dt9812 *dev, int offset, void *buf,
        cmd.u.flash_data_info.numbytes = cpu_to_le16(buf_size);
 
        /* DT9812 only responds to 32 byte writes!! */
-       ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, dev->cmd_wr.addr),
+       ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
                           &cmd, 32, &count, HZ * 1);
        if (ret)
                return ret;
 
-       return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, dev->cmd_rd.addr),
+       return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
                            buf, buf_size, &count, HZ * 1);
 }
 
-static int dt9812_read_multiple_registers(struct usb_dt9812 *dev, int reg_count,
-                                         u8 *address, u8 *value)
+static int dt9812_read_multiple_registers(struct comedi_device *dev,
+                                         int reg_count, u8 *address,
+                                         u8 *value)
 {
-       struct usb_device *usb = dev->udev;
+       struct usb_interface *intf = comedi_to_usb_interface(dev);
+       struct usb_device *usb = interface_to_usbdev(intf);
+       struct dt9812_private *devpriv = dev->private;
        struct dt9812_usb_cmd cmd;
        int i, count, ret;
 
@@ -338,20 +311,22 @@ static int dt9812_read_multiple_registers(struct usb_dt9812 *dev, int reg_count,
                cmd.u.read_multi_info.address[i] = address[i];
 
        /* DT9812 only responds to 32 byte writes!! */
-       ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, dev->cmd_wr.addr),
+       ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
                           &cmd, 32, &count, HZ * 1);
        if (ret)
                return ret;
 
-       return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, dev->cmd_rd.addr),
+       return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
                            value, reg_count, &count, HZ * 1);
 }
 
-static int dt9812_write_multiple_registers(struct usb_dt9812 *dev,
+static int dt9812_write_multiple_registers(struct comedi_device *dev,
                                           int reg_count, u8 *address,
                                           u8 *value)
 {
-       struct usb_device *usb = dev->udev;
+       struct usb_interface *intf = comedi_to_usb_interface(dev);
+       struct usb_device *usb = interface_to_usbdev(intf);
+       struct dt9812_private *devpriv = dev->private;
        struct dt9812_usb_cmd cmd;
        int i, count;
 
@@ -363,14 +338,17 @@ static int dt9812_write_multiple_registers(struct usb_dt9812 *dev,
        }
 
        /* DT9812 only responds to 32 byte writes!! */
-       return usb_bulk_msg(usb, usb_sndbulkpipe(usb, dev->cmd_wr.addr),
+       return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
                            &cmd, 32, &count, HZ * 1);
 }
 
-static int dt9812_rmw_multiple_registers(struct usb_dt9812 *dev, int reg_count,
+static int dt9812_rmw_multiple_registers(struct comedi_device *dev,
+                                        int reg_count,
                                         struct dt9812_rmw_byte *rmw)
 {
-       struct usb_device *usb = dev->udev;
+       struct usb_interface *intf = comedi_to_usb_interface(dev);
+       struct usb_device *usb = interface_to_usbdev(intf);
+       struct dt9812_private *devpriv = dev->private;
        struct dt9812_usb_cmd cmd;
        int i, count;
 
@@ -380,30 +358,26 @@ static int dt9812_rmw_multiple_registers(struct usb_dt9812 *dev, int reg_count,
                cmd.u.rmw_multi_info.rmw[i] = rmw[i];
 
        /* DT9812 only responds to 32 byte writes!! */
-       return usb_bulk_msg(usb, usb_sndbulkpipe(usb, dev->cmd_wr.addr),
+       return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
                            &cmd, 32, &count, HZ * 1);
 }
 
 static int dt9812_digital_in(struct comedi_device *dev, u8 *bits)
 {
        struct dt9812_private *devpriv = dev->private;
-       struct slot_dt9812 *slot = devpriv->slot;
-       int ret = -ENODEV;
+       u8 reg[2] = { F020_SFR_P3, F020_SFR_P1 };
+       u8 value[2];
+       int ret;
 
        down(&devpriv->sem);
-       if (slot->usb) {
-               u8 reg[2] = { F020_SFR_P3, F020_SFR_P1 };
-               u8 value[2];
-
-               ret = dt9812_read_multiple_registers(slot->usb, 2, reg, value);
-               if (ret == 0) {
-                       /*
-                        * bits 0-6 in F020_SFR_P3 are bits 0-6 in the digital
-                        * input port bit 3 in F020_SFR_P1 is bit 7 in the
-                        * digital input port
-                        */
-                       *bits = (value[0] & 0x7f) | ((value[1] & 0x08) << 4);
-               }
+       ret = dt9812_read_multiple_registers(dev, 2, reg, value);
+       if (ret == 0) {
+               /*
+                * bits 0-6 in F020_SFR_P3 are bits 0-6 in the digital
+                * input port bit 3 in F020_SFR_P1 is bit 7 in the
+                * digital input port
+                */
+               *bits = (value[0] & 0x7f) | ((value[1] & 0x08) << 4);
        }
        up(&devpriv->sem);
 
@@ -413,17 +387,13 @@ static int dt9812_digital_in(struct comedi_device *dev, u8 *bits)
 static int dt9812_digital_out(struct comedi_device *dev, u8 bits)
 {
        struct dt9812_private *devpriv = dev->private;
-       struct slot_dt9812 *slot = devpriv->slot;
-       int ret = -ENODEV;
+       u8 reg[1] = { F020_SFR_P2 };
+       u8 value[1] = { bits };
+       int ret;
 
        down(&devpriv->sem);
-       if (slot->usb) {
-               u8 reg[1] = { F020_SFR_P2 };
-               u8 value[1] = { bits };
-
-               ret = dt9812_write_multiple_registers(slot->usb, 1, reg, value);
-               devpriv->do_shadow = bits;
-       }
+       ret = dt9812_write_multiple_registers(dev, 1, reg, value);
+       devpriv->do_shadow = bits;
        up(&devpriv->sem);
 
        return ret;
@@ -444,10 +414,8 @@ static void dt9812_configure_mux(struct comedi_device *dev,
                                 struct dt9812_rmw_byte *rmw, int channel)
 {
        struct dt9812_private *devpriv = dev->private;
-       struct slot_dt9812 *slot = devpriv->slot;
-       struct usb_dt9812 *usb = slot->usb;
 
-       if (usb->device == DT9812_DEVID_DT9812_10) {
+       if (devpriv->device == DT9812_DEVID_DT9812_10) {
                /* In the DT9812/10V MUX is selected by P1.5-7 */
                rmw->address = F020_SFR_P1;
                rmw->and_mask = 0xe0;
@@ -465,11 +433,9 @@ static void dt9812_configure_gain(struct comedi_device *dev,
                                  enum dt9812_gain gain)
 {
        struct dt9812_private *devpriv = dev->private;
-       struct slot_dt9812 *slot = devpriv->slot;
-       struct usb_dt9812 *usb = slot->usb;
 
        /* In the DT9812/10V, there is an external gain of 0.5 */
-       if (usb->device == DT9812_DEVID_DT9812_10)
+       if (devpriv->device == DT9812_DEVID_DT9812_10)
                gain <<= 1;
 
        rmw->address = F020_SFR_ADC0CF;
@@ -516,7 +482,6 @@ static int dt9812_analog_in(struct comedi_device *dev,
                            int channel, u16 *value, enum dt9812_gain gain)
 {
        struct dt9812_private *devpriv = dev->private;
-       struct slot_dt9812 *slot = devpriv->slot;
        struct dt9812_rmw_byte rmw[3];
        u8 reg[3] = {
                F020_SFR_ADC0CN,
@@ -524,11 +489,9 @@ static int dt9812_analog_in(struct comedi_device *dev,
                F020_SFR_ADC0L
        };
        u8 val[3];
-       int ret = -ENODEV;
+       int ret;
 
        down(&devpriv->sem);
-       if (!slot->usb)
-               goto exit;
 
        /* 1 select the gain */
        dt9812_configure_gain(dev, &rmw[0], gain);
@@ -541,12 +504,12 @@ static int dt9812_analog_in(struct comedi_device *dev,
        rmw[2].and_mask = 0xff;
        rmw[2].or_value = F020_MASK_ADC0CN_AD0EN | F020_MASK_ADC0CN_AD0BUSY;
 
-       ret = dt9812_rmw_multiple_registers(slot->usb, 3, rmw);
+       ret = dt9812_rmw_multiple_registers(dev, 3, rmw);
        if (ret)
                goto exit;
 
        /* read the status and ADC */
-       ret = dt9812_read_multiple_registers(slot->usb, 3, reg, val);
+       ret = dt9812_read_multiple_registers(dev, 3, reg, val);
        if (ret)
                goto exit;
 
@@ -561,7 +524,7 @@ static int dt9812_analog_in(struct comedi_device *dev,
         */
        if ((val[0] & (F020_MASK_ADC0CN_AD0INT | F020_MASK_ADC0CN_AD0BUSY)) ==
            F020_MASK_ADC0CN_AD0INT) {
-               switch (slot->usb->device) {
+               switch (devpriv->device) {
                case DT9812_DEVID_DT9812_10:
                        /*
                         * For DT9812-10V the personality module set the
@@ -597,53 +560,51 @@ static int dt9812_analog_out_shadow(struct comedi_device *dev,
 static int dt9812_analog_out(struct comedi_device *dev, int channel, u16 value)
 {
        struct dt9812_private *devpriv = dev->private;
-       struct slot_dt9812 *slot = devpriv->slot;
-       int ret = -ENODEV;
+       struct dt9812_rmw_byte rmw[3];
+       int ret;
 
        down(&devpriv->sem);
-       if (slot->usb) {
-               struct dt9812_rmw_byte rmw[3];
 
-               switch (channel) {
-               case 0:
-                       /* 1. Set DAC mode */
-                       rmw[0].address = F020_SFR_DAC0CN;
-                       rmw[0].and_mask = 0xff;
-                       rmw[0].or_value = F020_MASK_DACxCN_DACxEN;
-
-                       /* 2 load low byte of DAC value first */
-                       rmw[1].address = F020_SFR_DAC0L;
-                       rmw[1].and_mask = 0xff;
-                       rmw[1].or_value = value & 0xff;
-
-                       /* 3 load high byte of DAC value next to latch the
-                          12-bit value */
-                       rmw[2].address = F020_SFR_DAC0H;
-                       rmw[2].and_mask = 0xff;
-                       rmw[2].or_value = (value >> 8) & 0xf;
-                       break;
+       switch (channel) {
+       case 0:
+               /* 1. Set DAC mode */
+               rmw[0].address = F020_SFR_DAC0CN;
+               rmw[0].and_mask = 0xff;
+               rmw[0].or_value = F020_MASK_DACxCN_DACxEN;
+
+               /* 2 load low byte of DAC value first */
+               rmw[1].address = F020_SFR_DAC0L;
+               rmw[1].and_mask = 0xff;
+               rmw[1].or_value = value & 0xff;
+
+               /* 3 load high byte of DAC value next to latch the
+                       12-bit value */
+               rmw[2].address = F020_SFR_DAC0H;
+               rmw[2].and_mask = 0xff;
+               rmw[2].or_value = (value >> 8) & 0xf;
+               break;
 
-               case 1:
-                       /* 1. Set DAC mode */
-                       rmw[0].address = F020_SFR_DAC1CN;
-                       rmw[0].and_mask = 0xff;
-                       rmw[0].or_value = F020_MASK_DACxCN_DACxEN;
-
-                       /* 2 load low byte of DAC value first */
-                       rmw[1].address = F020_SFR_DAC1L;
-                       rmw[1].and_mask = 0xff;
-                       rmw[1].or_value = value & 0xff;
-
-                       /* 3 load high byte of DAC value next to latch the
-                          12-bit value */
-                       rmw[2].address = F020_SFR_DAC1H;
-                       rmw[2].and_mask = 0xff;
-                       rmw[2].or_value = (value >> 8) & 0xf;
-                       break;
-               }
-               ret = dt9812_rmw_multiple_registers(slot->usb, 3, rmw);
-               devpriv->ao_shadow[channel] = value;
+       case 1:
+               /* 1. Set DAC mode */
+               rmw[0].address = F020_SFR_DAC1CN;
+               rmw[0].and_mask = 0xff;
+               rmw[0].or_value = F020_MASK_DACxCN_DACxEN;
+
+               /* 2 load low byte of DAC value first */
+               rmw[1].address = F020_SFR_DAC1L;
+               rmw[1].and_mask = 0xff;
+               rmw[1].or_value = value & 0xff;
+
+               /* 3 load high byte of DAC value next to latch the
+                       12-bit value */
+               rmw[2].address = F020_SFR_DAC1H;
+               rmw[2].and_mask = 0xff;
+               rmw[2].or_value = (value >> 8) & 0xf;
+               break;
        }
+       ret = dt9812_rmw_multiple_registers(dev, 3, rmw);
+       devpriv->ao_shadow[channel] = value;
+
        up(&devpriv->sem);
 
        return ret;
@@ -727,10 +688,11 @@ static int dt9812_ao_winsn(struct comedi_device *dev,
        return n;
 }
 
-static int dt9812_find_endpoints(struct usb_interface *intf,
-                                struct usb_dt9812 *devpriv)
+static int dt9812_find_endpoints(struct comedi_device *dev)
 {
+       struct usb_interface *intf = comedi_to_usb_interface(dev);
        struct usb_host_interface *host = intf->cur_altsetting;
+       struct dt9812_private *devpriv = dev->private;
        struct usb_endpoint_descriptor *ep;
        int i;
 
@@ -774,24 +736,26 @@ static int dt9812_find_endpoints(struct usb_interface *intf,
        return 0;
 }
 
-static int dt9812_reset_device(struct usb_interface *intf,
-                              struct usb_dt9812 *devpriv)
+static int dt9812_reset_device(struct comedi_device *dev)
 {
+       struct usb_interface *intf = comedi_to_usb_interface(dev);
+       struct usb_device *usb = interface_to_usbdev(intf);
+       struct dt9812_private *devpriv = dev->private;
        int ret;
        int i;
        u32 tmp32;
        u16 tmp16;
        u8 tmp8;
 
-       ret = dt9812_read_info(devpriv, 0, &tmp8, sizeof(tmp8));
+       ret = dt9812_read_info(dev, 0, &tmp8, sizeof(tmp8));
        if (ret) {
                /*
                 * Seems like a configuration reset is necessary if driver is
                 * reloaded while device is attached
                 */
-               usb_reset_configuration(devpriv->udev);
+               usb_reset_configuration(usb);
                for (i = 0; i < 10; i++) {
-                       ret = dt9812_read_info(devpriv, 1, &tmp8, sizeof(tmp8));
+                       ret = dt9812_read_info(dev, 1, &tmp8, sizeof(tmp8));
                        if (ret == 0)
                                break;
                }
@@ -801,28 +765,28 @@ static int dt9812_reset_device(struct usb_interface *intf,
                }
        }
 
-       ret = dt9812_read_info(devpriv, 1, &tmp16, sizeof(tmp16));
+       ret = dt9812_read_info(dev, 1, &tmp16, sizeof(tmp16));
        if (ret) {
                dev_err(&intf->dev, "failed to read vendor id\n");
                return ret;
        }
        devpriv->vendor = le16_to_cpu(tmp16);
 
-       ret = dt9812_read_info(devpriv, 3, &tmp16, sizeof(tmp16));
+       ret = dt9812_read_info(dev, 3, &tmp16, sizeof(tmp16));
        if (ret) {
                dev_err(&intf->dev, "failed to read product id\n");
                return ret;
        }
        devpriv->product = le16_to_cpu(tmp16);
 
-       ret = dt9812_read_info(devpriv, 5, &tmp16, sizeof(tmp16));
+       ret = dt9812_read_info(dev, 5, &tmp16, sizeof(tmp16));
        if (ret) {
                dev_err(&intf->dev, "failed to read device id\n");
                return ret;
        }
        devpriv->device = le16_to_cpu(tmp16);
 
-       ret = dt9812_read_info(devpriv, 7, &tmp32, sizeof(tmp32));
+       ret = dt9812_read_info(dev, 7, &tmp32, sizeof(tmp32));
        if (ret) {
                dev_err(&intf->dev, "failed to read serial number\n");
                return ret;
@@ -837,11 +801,11 @@ static int dt9812_reset_device(struct usb_interface *intf,
        return 0;
 }
 
-static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static int dt9812_auto_attach(struct comedi_device *dev,
+                             unsigned long context)
 {
-       struct slot_dt9812 *slot = NULL;
+       struct usb_interface *intf = comedi_to_usb_interface(dev);
        struct dt9812_private *devpriv;
-       int i;
        struct comedi_subdevice *s;
        bool is_unipolar;
        int ret;
@@ -850,186 +814,110 @@ static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
        if (!devpriv)
                return -ENOMEM;
        dev->private = devpriv;
-       sema_init(&devpriv->sem, 1);
 
-       down(&dt9812_mutex);
+       sema_init(&devpriv->sem, 1);
+       usb_set_intfdata(intf, devpriv);
 
-       /*
-        * Find the first unused slot for the comedi device
-        * that has a usb device connected.
-        */
-       for (i = 0; i < DT9812_NUM_SLOTS; i++) {
-               if (dt9812[i].usb && !dt9812[i].devpriv) {
-                       slot = &dt9812[i];
-                       break;
-               }
-       }
-       if (!slot) {
-               up(&dt9812_mutex);
-               return -ENODEV;
-       }
+       ret = dt9812_find_endpoints(dev);
+       if (ret)
+               return ret;
 
-       slot->devpriv = devpriv;
-       devpriv->slot = slot;
-       is_unipolar = (slot->usb->device == DT9812_DEVID_DT9812_2PT5);
+       ret = dt9812_reset_device(dev);
+       if (ret)
+               return ret;
 
-       up(&dt9812_mutex);
+       is_unipolar = (devpriv->device == DT9812_DEVID_DT9812_2PT5);
 
        ret = comedi_alloc_subdevices(dev, 4);
        if (ret)
                return ret;
 
-       /* digital input subdevice */
+       /* Digital Input subdevice */
        s = &dev->subdevices[0];
-       s->type = COMEDI_SUBD_DI;
-       s->subdev_flags = SDF_READABLE;
-       s->n_chan = 8;
-       s->maxdata = 1;
-       s->range_table = &range_digital;
-       s->insn_read = &dt9812_di_rinsn;
-
-       /* digital output subdevice */
+       s->type         = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan       = 8;
+       s->maxdata      = 1;
+       s->range_table  = &range_digital;
+       s->insn_read    = dt9812_di_rinsn;
+
+       /* Digital Output subdevice */
        s = &dev->subdevices[1];
-       s->type = COMEDI_SUBD_DO;
-       s->subdev_flags = SDF_WRITEABLE;
-       s->n_chan = 8;
-       s->maxdata = 1;
-       s->range_table = &range_digital;
-       s->insn_write = &dt9812_do_winsn;
+       s->type         = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITEABLE;
+       s->n_chan       = 8;
+       s->maxdata      = 1;
+       s->range_table  = &range_digital;
+       s->insn_write   = dt9812_do_winsn;
 
        devpriv->do_shadow = 0;
 
-       /* analog input subdevice */
+       /* Analog Input subdevice */
        s = &dev->subdevices[2];
-       s->type = COMEDI_SUBD_AI;
-       s->subdev_flags = SDF_READABLE | SDF_GROUND;
-       s->n_chan = 8;
-       s->maxdata = 4095;
-       s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
-       s->insn_read = &dt9812_ai_rinsn;
-
-       /* analog output subdevice */
+       s->type         = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE | SDF_GROUND;
+       s->n_chan       = 8;
+       s->maxdata      = 0x0fff;
+       s->range_table  = is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
+       s->insn_read    = dt9812_ai_rinsn;
+
+       /* Analog Output subdevice */
        s = &dev->subdevices[3];
-       s->type = COMEDI_SUBD_AO;
-       s->subdev_flags = SDF_WRITEABLE;
-       s->n_chan = 0;
-       s->maxdata = 4095;
-       s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
-       s->insn_write = &dt9812_ao_winsn;
-       s->insn_read = &dt9812_ao_rinsn;
+       s->type         = COMEDI_SUBD_AO;
+       s->subdev_flags = SDF_WRITEABLE;
+       s->n_chan       = 2;
+       s->maxdata      = 0x0fff;
+       s->range_table  = is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
+       s->insn_write   = dt9812_ao_winsn;
+       s->insn_read    = dt9812_ao_rinsn;
 
        devpriv->ao_shadow[0] = is_unipolar ? 0x0000 : 0x0800;
        devpriv->ao_shadow[1] = is_unipolar ? 0x0000 : 0x0800;
 
-       dev_info(dev->class_dev, "successfully attached to dt9812.\n");
-
        return 0;
 }
 
 static void dt9812_detach(struct comedi_device *dev)
 {
+       struct usb_interface *intf = comedi_to_usb_interface(dev);
        struct dt9812_private *devpriv = dev->private;
 
-       if (devpriv && devpriv->slot)
-               devpriv->slot = NULL;
-}
-
-static struct comedi_driver dt9812_comedi_driver = {
-       .module = THIS_MODULE,
-       .driver_name = "dt9812",
-       .attach = dt9812_attach,
-       .detach = dt9812_detach,
-};
-
-static int dt9812_probe(struct usb_interface *intf,
-                       const struct usb_device_id *id)
-{
-       struct slot_dt9812 *slot = NULL;
-       struct usb_dt9812 *dev = NULL;
-       int retval = -ENOMEM;
-       int i;
-
-       /* allocate memory for our device state and initialize it */
-       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-       if (dev == NULL)
-               goto error;
-
-       kref_init(&dev->kref);
-
-       down(&dt9812_mutex);
-
-       /* Find an empty slot for the usb device */
-       for (i = 0; i < DT9812_NUM_SLOTS; i++) {
-               if (!dt9812[i].usb) {
-                       slot = &dt9812[i];
-                       break;
-               }
-       }
-       if (!slot) {
-               up(&dt9812_mutex);
-               retval = -ENODEV;
-               goto error;
-       }
-
-       slot->usb = dev;
-       dev->slot = slot;
-
-       up(&dt9812_mutex);
-
-       dev->udev = usb_get_dev(interface_to_usbdev(intf));
-
-       retval = dt9812_find_endpoints(intf, dev);
-       if (retval)
-               goto error;
-
-       retval = dt9812_reset_device(intf, dev);
-       if (retval)
-               goto error;
+       if (!devpriv)
+               return;
 
-       /* save our data pointer in this interface device */
-       usb_set_intfdata(intf, dev);
+       down(&devpriv->sem);
 
-       return 0;
+       usb_set_intfdata(intf, NULL);
 
-error:
-       if (dev)
-               kref_put(&dev->kref, dt9812_delete);
-       return retval;
+       up(&devpriv->sem);
 }
 
-static void dt9812_disconnect(struct usb_interface *intf)
-{
-       struct usb_dt9812 *dev;
-       int minor = intf->minor;
-
-       down(&dt9812_mutex);
-       dev = usb_get_intfdata(intf);
-       if (dev->slot) {
-               dev->slot->usb = NULL;
-               dev->slot = NULL;
-       }
-       usb_set_intfdata(intf, NULL);
-       up(&dt9812_mutex);
-
-       /* queue final destruction */
-       kref_put(&dev->kref, dt9812_delete);
+static struct comedi_driver dt9812_driver = {
+       .driver_name    = "dt9812",
+       .module         = THIS_MODULE,
+       .auto_attach    = dt9812_auto_attach,
+       .detach         = dt9812_detach,
+};
 
-       dev_info(&intf->dev, "USB Dt9812 #%d now disconnected\n", minor);
+static int dt9812_usb_probe(struct usb_interface *intf,
+                           const struct usb_device_id *id)
+{
+       return comedi_usb_auto_config(intf, &dt9812_driver, id->driver_info);
 }
 
-static const struct usb_device_id dt9812_table[] = {
+static const struct usb_device_id dt9812_usb_table[] = {
        { USB_DEVICE(0x0867, 0x9812) },
        { }
 };
-MODULE_DEVICE_TABLE(usb, dt9812_table);
+MODULE_DEVICE_TABLE(usb, dt9812_usb_table);
 
 static struct usb_driver dt9812_usb_driver = {
        .name           = "dt9812",
-       .id_table       = dt9812_table,
-       .probe          = dt9812_probe,
-       .disconnect     = dt9812_disconnect,
+       .id_table       = dt9812_usb_table,
+       .probe          = dt9812_usb_probe,
+       .disconnect     = comedi_usb_auto_unconfig,
 };
-module_comedi_usb_driver(dt9812_comedi_driver, dt9812_usb_driver);
+module_comedi_usb_driver(dt9812_driver, dt9812_usb_driver);
 
 MODULE_AUTHOR("Anders Blomdell <anders.blomdell@control.lth.se>");
 MODULE_DESCRIPTION("Comedi DT9812 driver");