#include <linux/compat.h>
 #include <linux/chio.h>                        /* here are all the ioctls */
 #include <linux/mutex.h>
+#include <linux/idr.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 
 #define CH_DT_MAX       16
 #define CH_TYPES        8
+#define CH_MAX_DEVS     128
 
 MODULE_DESCRIPTION("device driver for scsi media changer devices");
 MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org>");
        struct mutex        lock;
 } scsi_changer;
 
-static LIST_HEAD(ch_devlist);
-static DEFINE_SPINLOCK(ch_devlist_lock);
-static int ch_devcount;
+static DEFINE_IDR(ch_index_idr);
+static DEFINE_SPINLOCK(ch_index_lock);
 
 static struct scsi_driver ch_template =
 {
 static int
 ch_open(struct inode *inode, struct file *file)
 {
-       scsi_changer *tmp, *ch;
+       scsi_changer *ch;
        int minor = iminor(inode);
 
-       spin_lock(&ch_devlist_lock);
-       ch = NULL;
-       list_for_each_entry(tmp,&ch_devlist,list) {
-               if (tmp->minor == minor)
-                       ch = tmp;
-       }
+       spin_lock(&ch_index_lock);
+       ch = idr_find(&ch_index_idr, minor);
+
        if (NULL == ch || scsi_device_get(ch->device)) {
-               spin_unlock(&ch_devlist_lock);
+               spin_unlock(&ch_index_lock);
                return -ENXIO;
        }
-       spin_unlock(&ch_devlist_lock);
+       spin_unlock(&ch_index_lock);
 
        file->private_data = ch;
        return 0;
 {
        struct scsi_device *sd = to_scsi_device(dev);
        struct class_device *class_dev;
+       int minor, ret = -ENOMEM;
        scsi_changer *ch;
 
        if (sd->type != TYPE_MEDIUM_CHANGER)
        if (NULL == ch)
                return -ENOMEM;
 
-       ch->minor = ch_devcount;
+       if (!idr_pre_get(&ch_index_idr, GFP_KERNEL))
+               goto free_ch;
+
+       spin_lock(&ch_index_lock);
+       ret = idr_get_new(&ch_index_idr, ch, &minor);
+       spin_unlock(&ch_index_lock);
+
+       if (ret)
+               goto free_ch;
+
+       if (minor > CH_MAX_DEVS) {
+               ret = -ENODEV;
+               goto remove_idr;
+       }
+
+       ch->minor = minor;
        sprintf(ch->name,"ch%d",ch->minor);
 
        class_dev = class_device_create(ch_sysfs_class, NULL,
        if (IS_ERR(class_dev)) {
                printk(KERN_WARNING "ch%d: class_device_create failed\n",
                       ch->minor);
-               kfree(ch);
-               return PTR_ERR(class_dev);
+               ret = PTR_ERR(class_dev);
+               goto remove_idr;
        }
 
        mutex_init(&ch->lock);
        if (init)
                ch_init_elem(ch);
 
+       dev_set_drvdata(dev, ch);
        sdev_printk(KERN_INFO, sd, "Attached scsi changer %s\n", ch->name);
 
-       spin_lock(&ch_devlist_lock);
-       list_add_tail(&ch->list,&ch_devlist);
-       ch_devcount++;
-       spin_unlock(&ch_devlist_lock);
        return 0;
+remove_idr:
+       idr_remove(&ch_index_idr, minor);
+free_ch:
+       kfree(ch);
+       return ret;
 }
 
 static int ch_remove(struct device *dev)
 {
-       struct scsi_device *sd = to_scsi_device(dev);
-       scsi_changer *tmp, *ch;
+       scsi_changer *ch = dev_get_drvdata(dev);
 
-       spin_lock(&ch_devlist_lock);
-       ch = NULL;
-       list_for_each_entry(tmp,&ch_devlist,list) {
-               if (tmp->device == sd)
-                       ch = tmp;
-       }
-       BUG_ON(NULL == ch);
-       list_del(&ch->list);
-       spin_unlock(&ch_devlist_lock);
+       spin_lock(&ch_index_lock);
+       idr_remove(&ch_index_idr, ch->minor);
+       spin_unlock(&ch_index_lock);
 
        class_device_destroy(ch_sysfs_class,
                             MKDEV(SCSI_CHANGER_MAJOR,ch->minor));
        kfree(ch->dt);
        kfree(ch);
-       ch_devcount--;
        return 0;
 }
 
        scsi_unregister_driver(&ch_template.gendrv);
        unregister_chrdev(SCSI_CHANGER_MAJOR, "ch");
        class_destroy(ch_sysfs_class);
+       idr_destroy(&ch_index_idr);
 }
 
 module_init(init_ch_module);