[SCSI] hptiop: Add RR44xx adapter support
[safe/jmp/linux-2.6] / drivers / scsi / ch.c
index 53b3955..fe11c1d 100644 (file)
@@ -7,12 +7,10 @@
 
 #define VERSION "0.25"
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/major.h>
 #include <linux/string.h>
 #include <linux/interrupt.h>
 #include <linux/blkdev.h>
 #include <linux/completion.h>
-#include <linux/ioctl32.h>
 #include <linux/compat.h>
 #include <linux/chio.h>                        /* here are all the ioctls */
+#include <linux/mutex.h>
+#include <linux/idr.h>
+#include <linux/smp_lock.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_ioctl.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_device.h>
-#include <scsi/scsi_request.h>
+#include <scsi/scsi_eh.h>
 #include <scsi/scsi_dbg.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>");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(SCSI_CHANGER_MAJOR);
+MODULE_ALIAS_SCSI_DEVICE(TYPE_MEDIUM_CHANGER);
 
 static int init = 1;
 module_param(init, int, 0444);
@@ -75,7 +78,7 @@ static int vendor_counts[CH_TYPES-4];
 module_param_array(vendor_firsts, int, NULL, 0444);
 module_param_array(vendor_counts, int, NULL, 0444);
 
-static char *vendor_labels[CH_TYPES-4] = {
+static const char * vendor_labels[CH_TYPES-4] = {
        "v0", "v1", "v2", "v3"
 };
 // module_param_string_array(vendor_labels, NULL, 0444);
@@ -89,17 +92,6 @@ static char *vendor_labels[CH_TYPES-4] = {
 
 #define MAX_RETRIES   1
 
-static int  ch_probe(struct device *);
-static int  ch_remove(struct device *);
-static int  ch_open(struct inode * inode, struct file * filp);
-static int  ch_release(struct inode * inode, struct file * filp);
-static int  ch_ioctl(struct inode * inode, struct file * filp,
-                    unsigned int cmd, unsigned long arg);
-#ifdef CONFIG_COMPAT
-static long ch_ioctl_compat(struct file * filp,
-                           unsigned int cmd, unsigned long arg);
-#endif
-
 static struct class * ch_sysfs_class;
 
 typedef struct {
@@ -112,40 +104,18 @@ typedef struct {
        u_int               counts[CH_TYPES];
        u_int               unit_attention;
        u_int               voltags;
-       struct semaphore    lock;
+       struct mutex        lock;
 } scsi_changer;
 
-static LIST_HEAD(ch_devlist);
-static spinlock_t ch_devlist_lock = SPIN_LOCK_UNLOCKED;
-static int ch_devcount;
+static DEFINE_IDR(ch_index_idr);
+static DEFINE_SPINLOCK(ch_index_lock);
 
-static struct scsi_driver ch_template =
-{
-       .owner          = THIS_MODULE,
-       .gendrv         = {
-               .name   = "ch",
-               .probe  = ch_probe,
-               .remove = ch_remove,
-       },
-};
-
-static struct file_operations changer_fops =
-{
-       .owner        = THIS_MODULE,
-       .open         = ch_open,
-       .release      = ch_release,
-       .ioctl        = ch_ioctl,
-#ifdef CONFIG_COMPAT
-       .compat_ioctl = ch_ioctl_compat,
-#endif
-};
-
-static struct {
+static const struct {
        unsigned char  sense;
        unsigned char  asc;
        unsigned char  ascq;
        int            errno;
-} err[] = {
+} ch_err[] = {
 /* Just filled in what looks right. Hav'nt checked any standard paper for
    these errno assignments, so they may be wrong... */
        {
@@ -180,18 +150,18 @@ static struct {
 
 /* ------------------------------------------------------------------- */
 
-static int ch_find_errno(unsigned char *sense_buffer)
+static int ch_find_errno(struct scsi_sense_hdr *sshdr)
 {
        int i,errno = 0;
 
        /* Check to see if additional sense information is available */
-       if (sense_buffer[7]  > 5 &&
-           sense_buffer[12] != 0) {
-               for (i = 0; err[i].errno != 0; i++) {
-                       if (err[i].sense == sense_buffer[ 2] &&
-                           err[i].asc   == sense_buffer[12] &&
-                           err[i].ascq  == sense_buffer[13]) {
-                               errno = -err[i].errno;
+       if (scsi_sense_valid(sshdr) &&
+           sshdr->asc != 0) {
+               for (i = 0; ch_err[i].errno != 0; i++) {
+                       if (ch_err[i].sense == sshdr->sense_key &&
+                           ch_err[i].asc   == sshdr->asc &&
+                           ch_err[i].ascq  == sshdr->ascq) {
+                               errno = -ch_err[i].errno;
                                break;
                        }
                }
@@ -206,12 +176,8 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
           void *buffer, unsigned buflength,
           enum dma_data_direction direction)
 {
-       int errno, retries = 0, timeout;
-       struct scsi_request *sr;
-       
-       sr = scsi_allocate_request(ch->device, GFP_KERNEL);
-       if (NULL == sr)
-               return -ENOMEM;
+       int errno, retries = 0, timeout, result;
+       struct scsi_sense_hdr sshdr;
 
        timeout = (cmd[0] == INITIALIZE_ELEMENT_STATUS)
                ? timeout_init : timeout_move;
@@ -223,16 +189,17 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
                __scsi_print_command(cmd);
        }
 
-        scsi_wait_req(sr, cmd, buffer, buflength,
-                     timeout * HZ, MAX_RETRIES);
+        result = scsi_execute_req(ch->device, cmd, direction, buffer,
+                                 buflength, &sshdr, timeout * HZ,
+                                 MAX_RETRIES, NULL);
 
-       dprintk("result: 0x%x\n",sr->sr_result);
-       if (driver_byte(sr->sr_result) & DRIVER_SENSE) {
+       dprintk("result: 0x%x\n",result);
+       if (driver_byte(result) & DRIVER_SENSE) {
                if (debug)
-                       scsi_print_req_sense(ch->name, sr);
-               errno = ch_find_errno(sr->sr_sense_buffer);
+                       scsi_print_sense_hdr(ch->name, &sshdr);
+               errno = ch_find_errno(&sshdr);
 
-               switch(sr->sr_sense_buffer[2] & 0xf) {
+               switch(sshdr.sense_key) {
                case UNIT_ATTENTION:
                        ch->unit_attention = 1;
                        if (retries++ < 3)
@@ -240,7 +207,6 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
                        break;
                }
        }
-       scsi_release_request(sr);
        return errno;
 }
 
@@ -250,7 +216,7 @@ static int
 ch_elem_to_typecode(scsi_changer *ch, u_int elem)
 {
        int i;
-       
+
        for (i = 0; i < CH_TYPES; i++) {
                if (elem >= ch->firsts[i]  &&
                    elem <  ch->firsts[i] +
@@ -266,15 +232,15 @@ ch_read_element_status(scsi_changer *ch, u_int elem, char *data)
        u_char  cmd[12];
        u_char  *buffer;
        int     result;
-       
+
        buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
        if(!buffer)
                return -ENOMEM;
-       
+
  retry:
        memset(cmd,0,sizeof(cmd));
        cmd[0] = READ_ELEMENT_STATUS;
-       cmd[1] = (ch->device->lun << 5) | 
+       cmd[1] = (ch->device->lun << 5) |
                (ch->voltags ? 0x10 : 0) |
                ch_elem_to_typecode(ch,elem);
        cmd[2] = (elem >> 8) & 0xff;
@@ -301,7 +267,7 @@ ch_read_element_status(scsi_changer *ch, u_int elem, char *data)
        return result;
 }
 
-static int 
+static int
 ch_init_elem(scsi_changer *ch)
 {
        int err;
@@ -324,11 +290,10 @@ ch_readconfig(scsi_changer *ch)
        int     result,id,lun,i;
        u_int   elem;
 
-       buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
+       buffer = kzalloc(512, GFP_KERNEL | GFP_DMA);
        if (!buffer)
                return -ENOMEM;
-       memset(buffer,0,512);
-       
+
        memset(cmd,0,sizeof(cmd));
        cmd[0] = MODE_SENSE;
        cmd[1] = ch->device->lun << 5;
@@ -371,7 +336,7 @@ ch_readconfig(scsi_changer *ch)
        } else {
                vprintk("reading element address assigment page failed!\n");
        }
-       
+
        /* vendor specific element types */
        for (i = 0; i < 4; i++) {
                if (0 == vendor_counts[i])
@@ -388,6 +353,12 @@ ch_readconfig(scsi_changer *ch)
        /* look up the devices of the data transfer elements */
        ch->dt = kmalloc(ch->counts[CHET_DT]*sizeof(struct scsi_device),
                         GFP_KERNEL);
+
+       if (!ch->dt) {
+               kfree(buffer);
+               return -ENOMEM;
+       }
+
        for (elem = 0; elem < ch->counts[CHET_DT]; elem++) {
                id  = -1;
                lun = 0;
@@ -449,7 +420,7 @@ static int
 ch_position(scsi_changer *ch, u_int trans, u_int elem, int rotate)
 {
        u_char  cmd[10];
-       
+
        dprintk("position: 0x%x\n",elem);
        if (0 == trans)
                trans = ch->firsts[CHET_MT];
@@ -468,7 +439,7 @@ static int
 ch_move(scsi_changer *ch, u_int trans, u_int src, u_int dest, int rotate)
 {
        u_char  cmd[12];
-       
+
        dprintk("move: 0x%x => 0x%x\n",src,dest);
        if (0 == trans)
                trans = ch->firsts[CHET_MT];
@@ -490,7 +461,7 @@ ch_exchange(scsi_changer *ch, u_int trans, u_int src,
            u_int dest1, u_int dest2, int rotate1, int rotate2)
 {
        u_char  cmd[12];
-       
+
        dprintk("exchange: 0x%x => 0x%x => 0x%x\n",
                src,dest1,dest2);
        if (0 == trans)
@@ -507,7 +478,7 @@ ch_exchange(scsi_changer *ch, u_int trans, u_int src,
        cmd[8]  = (dest2 >> 8) & 0xff;
        cmd[9]  =  dest2       & 0xff;
        cmd[10] = (rotate1 ? 1 : 0) | (rotate2 ? 2 : 0);
-       
+
        return ch_do_scsi(ch, cmd, NULL,0, DMA_NONE);
 }
 
@@ -535,10 +506,9 @@ ch_set_voltag(scsi_changer *ch, u_int elem,
        u_char  *buffer;
        int result;
 
-       buffer = kmalloc(512, GFP_KERNEL);
+       buffer = kzalloc(512, GFP_KERNEL);
        if (!buffer)
                return -ENOMEM;
-       memset(buffer,0,512);
 
        dprintk("%s %s voltag: 0x%x => \"%s\"\n",
                clear     ? "clear"     : "set",
@@ -546,14 +516,14 @@ ch_set_voltag(scsi_changer *ch, u_int elem,
                elem, tag);
        memset(cmd,0,sizeof(cmd));
        cmd[0]  = SEND_VOLUME_TAG;
-       cmd[1] = (ch->device->lun << 5) | 
+       cmd[1] = (ch->device->lun << 5) |
                ch_elem_to_typecode(ch,elem);
        cmd[2] = (elem >> 8) & 0xff;
        cmd[3] = elem        & 0xff;
        cmd[5] = clear
                ? (alternate ? 0x0d : 0x0c)
                : (alternate ? 0x0b : 0x0a);
-       
+
        cmd[9] = 255;
 
        memcpy(buffer,tag,32);
@@ -564,13 +534,13 @@ ch_set_voltag(scsi_changer *ch, u_int elem,
        return result;
 }
 
-static int ch_gstatus(scsi_changer *ch, int type, unsigned char *dest)
+static int ch_gstatus(scsi_changer *ch, int type, unsigned char __user *dest)
 {
        int retval = 0;
        u_char data[16];
        unsigned int i;
-       
-       down(&ch->lock);
+
+       mutex_lock(&ch->lock);
        for (i = 0; i < ch->counts[type]; i++) {
                if (0 != ch_read_element_status
                    (ch, ch->firsts[type]+i,data)) {
@@ -587,7 +557,7 @@ static int ch_gstatus(scsi_changer *ch, int type, unsigned char *dest)
                if (0 != retval)
                        break;
        }
-       up(&ch->lock);
+       mutex_unlock(&ch->lock);
        return retval;
 }
 
@@ -606,22 +576,22 @@ ch_release(struct inode *inode, struct file *file)
 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;
-       }
+       lock_kernel();
+       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);
+               unlock_kernel();
                return -ENXIO;
        }
-       spin_unlock(&ch_devlist_lock);
+       spin_unlock(&ch_index_lock);
 
        file->private_data = ch;
+       unlock_kernel();
        return 0;
 }
 
@@ -633,24 +603,25 @@ ch_checkrange(scsi_changer *ch, unsigned int type, unsigned int unit)
        return 0;
 }
 
-static int ch_ioctl(struct inode * inode, struct file * file,
+static long ch_ioctl(struct file *file,
                    unsigned int cmd, unsigned long arg)
 {
        scsi_changer *ch = file->private_data;
        int retval;
-       
+       void __user *argp = (void __user *)arg;
+
        switch (cmd) {
        case CHIOGPARAMS:
        {
                struct changer_params params;
-               
+
                params.cp_curpicker = 0;
                params.cp_npickers  = ch->counts[CHET_MT];
                params.cp_nslots    = ch->counts[CHET_ST];
                params.cp_nportals  = ch->counts[CHET_IE];
                params.cp_ndrives   = ch->counts[CHET_DT];
-               
-               if (copy_to_user((void *) arg, &params, sizeof(params)))
+
+               if (copy_to_user(argp, &params, sizeof(params)))
                        return -EFAULT;
                return 0;
        }
@@ -675,35 +646,35 @@ static int ch_ioctl(struct inode * inode, struct file * file,
                        vparams.cvp_n4  = ch->counts[CHET_V4];
                        strncpy(vparams.cvp_label4,vendor_labels[3],16);
                }
-               if (copy_to_user((void *) arg, &vparams, sizeof(vparams)))
+               if (copy_to_user(argp, &vparams, sizeof(vparams)))
                        return -EFAULT;
                return 0;
        }
-       
+
        case CHIOPOSITION:
        {
                struct changer_position pos;
-               
-               if (copy_from_user(&pos, (void*)arg, sizeof (pos)))
+
+               if (copy_from_user(&pos, argp, sizeof (pos)))
                        return -EFAULT;
 
                if (0 != ch_checkrange(ch, pos.cp_type, pos.cp_unit)) {
                        dprintk("CHIOPOSITION: invalid parameter\n");
                        return -EBADSLT;
                }
-               down(&ch->lock);
+               mutex_lock(&ch->lock);
                retval = ch_position(ch,0,
                                     ch->firsts[pos.cp_type] + pos.cp_unit,
                                     pos.cp_flags & CP_INVERT);
-               up(&ch->lock);
+               mutex_unlock(&ch->lock);
                return retval;
        }
-       
+
        case CHIOMOVE:
        {
                struct changer_move mv;
 
-               if (copy_from_user(&mv, (void*)arg, sizeof (mv)))
+               if (copy_from_user(&mv, argp, sizeof (mv)))
                        return -EFAULT;
 
                if (0 != ch_checkrange(ch, mv.cm_fromtype, mv.cm_fromunit) ||
@@ -711,21 +682,21 @@ static int ch_ioctl(struct inode * inode, struct file * file,
                        dprintk("CHIOMOVE: invalid parameter\n");
                        return -EBADSLT;
                }
-               
-               down(&ch->lock);
+
+               mutex_lock(&ch->lock);
                retval = ch_move(ch,0,
                                 ch->firsts[mv.cm_fromtype] + mv.cm_fromunit,
                                 ch->firsts[mv.cm_totype]   + mv.cm_tounit,
                                 mv.cm_flags & CM_INVERT);
-               up(&ch->lock);
+               mutex_unlock(&ch->lock);
                return retval;
        }
 
        case CHIOEXCHANGE:
        {
                struct changer_exchange mv;
-               
-               if (copy_from_user(&mv, (void*)arg, sizeof (mv)))
+
+               if (copy_from_user(&mv, argp, sizeof (mv)))
                        return -EFAULT;
 
                if (0 != ch_checkrange(ch, mv.ce_srctype,  mv.ce_srcunit ) ||
@@ -734,23 +705,23 @@ static int ch_ioctl(struct inode * inode, struct file * file,
                        dprintk("CHIOEXCHANGE: invalid parameter\n");
                        return -EBADSLT;
                }
-               
-               down(&ch->lock);
+
+               mutex_lock(&ch->lock);
                retval = ch_exchange
                        (ch,0,
                         ch->firsts[mv.ce_srctype]  + mv.ce_srcunit,
                         ch->firsts[mv.ce_fdsttype] + mv.ce_fdstunit,
                         ch->firsts[mv.ce_sdsttype] + mv.ce_sdstunit,
                         mv.ce_flags & CE_INVERT1, mv.ce_flags & CE_INVERT2);
-               up(&ch->lock);
+               mutex_unlock(&ch->lock);
                return retval;
        }
 
        case CHIOGSTATUS:
        {
                struct changer_element_status ces;
-               
-               if (copy_from_user(&ces, (void*)arg, sizeof (ces)))
+
+               if (copy_from_user(&ces, argp, sizeof (ces)))
                        return -EFAULT;
                if (ces.ces_type < 0 || ces.ces_type >= CH_TYPES)
                        return -EINVAL;
@@ -761,35 +732,36 @@ static int ch_ioctl(struct inode * inode, struct file * file,
        case CHIOGELEM:
        {
                struct changer_get_element cge;
-               u_char  cmd[12];
-               u_char  *buffer;
+               u_char ch_cmd[12];
+               u_char *buffer;
                unsigned int elem;
                int     result,i;
-               
-               if (copy_from_user(&cge, (void*)arg, sizeof (cge)))
+
+               if (copy_from_user(&cge, argp, sizeof (cge)))
                        return -EFAULT;
 
                if (0 != ch_checkrange(ch, cge.cge_type, cge.cge_unit))
                        return -EINVAL;
                elem = ch->firsts[cge.cge_type] + cge.cge_unit;
-               
+
                buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
                if (!buffer)
                        return -ENOMEM;
-               down(&ch->lock);
-               
+               mutex_lock(&ch->lock);
+
        voltag_retry:
-               memset(cmd,0,sizeof(cmd));
-               cmd[0] = READ_ELEMENT_STATUS;
-               cmd[1] = (ch->device->lun << 5) |
+               memset(ch_cmd, 0, sizeof(ch_cmd));
+               ch_cmd[0] = READ_ELEMENT_STATUS;
+               ch_cmd[1] = (ch->device->lun << 5) |
                        (ch->voltags ? 0x10 : 0) |
                        ch_elem_to_typecode(ch,elem);
-               cmd[2] = (elem >> 8) & 0xff;
-               cmd[3] = elem        & 0xff;
-               cmd[5] = 1;
-               cmd[9] = 255;
-               
-               if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256, DMA_FROM_DEVICE))) {
+               ch_cmd[2] = (elem >> 8) & 0xff;
+               ch_cmd[3] = elem        & 0xff;
+               ch_cmd[5] = 1;
+               ch_cmd[9] = 255;
+
+               result = ch_do_scsi(ch, ch_cmd, buffer, 256, DMA_FROM_DEVICE);
+               if (!result) {
                        cge.cge_status = buffer[18];
                        cge.cge_flags = 0;
                        if (buffer[18] & CESTATUS_EXCEPT) {
@@ -827,27 +799,27 @@ static int ch_ioctl(struct inode * inode, struct file * file,
                        goto voltag_retry;
                }
                kfree(buffer);
-               up(&ch->lock);
-               
-               if (copy_to_user((void*)arg, &cge, sizeof (cge)))
+               mutex_unlock(&ch->lock);
+
+               if (copy_to_user(argp, &cge, sizeof (cge)))
                        return -EFAULT;
                return result;
        }
 
        case CHIOINITELEM:
        {
-               down(&ch->lock);
+               mutex_lock(&ch->lock);
                retval = ch_init_elem(ch);
-               up(&ch->lock);
+               mutex_unlock(&ch->lock);
                return retval;
        }
-               
+
        case CHIOSVOLTAG:
        {
                struct changer_set_voltag csv;
                int elem;
 
-               if (copy_from_user(&csv, (void*)arg, sizeof(csv)))
+               if (copy_from_user(&csv, argp, sizeof(csv)))
                        return -EFAULT;
 
                if (0 != ch_checkrange(ch, csv.csv_type, csv.csv_unit)) {
@@ -855,17 +827,17 @@ static int ch_ioctl(struct inode * inode, struct file * file,
                        return -EBADSLT;
                }
                elem = ch->firsts[csv.csv_type] + csv.csv_unit;
-               down(&ch->lock);
+               mutex_lock(&ch->lock);
                retval = ch_set_voltag(ch, elem,
                                       csv.csv_flags & CSV_AVOLTAG,
                                       csv.csv_flags & CSV_CLEARTAG,
                                       csv.csv_voltag);
-               up(&ch->lock);
+               mutex_unlock(&ch->lock);
                return retval;
        }
 
        default:
-               return scsi_ioctl(ch->device, cmd, (void*)arg);
+               return scsi_ioctl(ch->device, cmd, argp);
 
        }
 }
@@ -882,7 +854,7 @@ static long ch_ioctl_compat(struct file * file,
                            unsigned int cmd, unsigned long arg)
 {
        scsi_changer *ch = file->private_data;
-       
+
        switch (cmd) {
        case CHIOGPARAMS:
        case CHIOGVPARAMS:
@@ -893,14 +865,13 @@ static long ch_ioctl_compat(struct file * file,
        case CHIOINITELEM:
        case CHIOSVOLTAG:
                /* compatible */
-               return ch_ioctl(NULL /* inode, unused */,
-                               file, cmd, arg);
+               return ch_ioctl(file, cmd, arg);
        case CHIOGSTATUS32:
        {
                struct changer_element_status32 ces32;
-               unsigned char *data;
-               
-               if (copy_from_user(&ces32, (void*)arg, sizeof (ces32)))
+               unsigned char __user *data;
+
+               if (copy_from_user(&ces32, (void __user *)arg, sizeof (ces32)))
                        return -EFAULT;
                if (ces32.ces_type < 0 || ces32.ces_type >= CH_TYPES)
                        return -EINVAL;
@@ -921,66 +892,99 @@ static long ch_ioctl_compat(struct file * file,
 static int ch_probe(struct device *dev)
 {
        struct scsi_device *sd = to_scsi_device(dev);
+       struct device *class_dev;
+       int minor, ret = -ENOMEM;
        scsi_changer *ch;
-       
+
        if (sd->type != TYPE_MEDIUM_CHANGER)
                return -ENODEV;
-    
-       ch = kmalloc(sizeof(*ch), GFP_KERNEL);
+
+       ch = kzalloc(sizeof(*ch), GFP_KERNEL);
        if (NULL == ch)
                return -ENOMEM;
 
-       memset(ch,0,sizeof(*ch));
-       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);
-       init_MUTEX(&ch->lock);
+
+       class_dev = device_create(ch_sysfs_class, dev,
+                                 MKDEV(SCSI_CHANGER_MAJOR, ch->minor), ch,
+                                 "s%s", ch->name);
+       if (IS_ERR(class_dev)) {
+               printk(KERN_WARNING "ch%d: device_create failed\n",
+                      ch->minor);
+               ret = PTR_ERR(class_dev);
+               goto remove_idr;
+       }
+
+       mutex_init(&ch->lock);
        ch->device = sd;
        ch_readconfig(ch);
        if (init)
                ch_init_elem(ch);
 
-       class_device_create(ch_sysfs_class,
-                           MKDEV(SCSI_CHANGER_MAJOR,ch->minor),
-                           dev, "s%s", ch->name);
-
-       printk(KERN_INFO "Attached scsi changer %s "
-              "at scsi%d, channel %d, id %d, lun %d\n", 
-              ch->name, sd->host->host_no, sd->channel, sd->id, sd->lun);
-       
-       spin_lock(&ch_devlist_lock);
-       list_add_tail(&ch->list,&ch_devlist);
-       ch_devcount++;
-       spin_unlock(&ch_devlist_lock);
+       dev_set_drvdata(dev, ch);
+       sdev_printk(KERN_INFO, sd, "Attached scsi changer %s\n", ch->name);
+
        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));
+       device_destroy(ch_sysfs_class, MKDEV(SCSI_CHANGER_MAJOR,ch->minor));
        kfree(ch->dt);
        kfree(ch);
-       ch_devcount--;
        return 0;
 }
 
+static struct scsi_driver ch_template = {
+       .owner          = THIS_MODULE,
+       .gendrv         = {
+               .name   = "ch",
+               .probe  = ch_probe,
+               .remove = ch_remove,
+       },
+};
+
+static const struct file_operations changer_fops = {
+       .owner          = THIS_MODULE,
+       .open           = ch_open,
+       .release        = ch_release,
+       .unlocked_ioctl = ch_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = ch_ioctl_compat,
+#endif
+};
+
 static int __init init_ch_module(void)
 {
        int rc;
-       
+
        printk(KERN_INFO "SCSI Media Changer driver v" VERSION " \n");
         ch_sysfs_class = class_create(THIS_MODULE, "scsi_changer");
         if (IS_ERR(ch_sysfs_class)) {
@@ -1005,11 +1009,12 @@ static int __init init_ch_module(void)
        return rc;
 }
 
-static void __exit exit_ch_module(void) 
+static void __exit exit_ch_module(void)
 {
        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);