printk(KERN_ERR "req nr_sec %lu, cur_nr_sec %u\n", req->nr_sectors,
                        req->current_nr_sectors);
 
-       /* release the command and kill it */
-       scsi_release_buffers(cmd);
-       scsi_put_command(cmd);
        return BLKPREP_KILL;
 }
 
        scsi_io_completion(cmd, cmd->request_bufflen);
 }
 
-static int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req)
+int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req)
 {
        struct scsi_cmnd *cmd;
+       int ret = scsi_prep_state_check(sdev, req);
+
+       if (ret != BLKPREP_OK)
+               return ret;
 
        cmd = scsi_get_cmd_from_req(sdev, req);
        if (unlikely(!cmd))
        cmd->done = scsi_blk_pc_done;
        return BLKPREP_OK;
 }
+EXPORT_SYMBOL(scsi_setup_blk_pc_cmnd);
 
 /*
  * Setup a REQ_TYPE_FS command.  These are simple read/write request
  * from filesystems that still need to be translated to SCSI CDBs from
  * the ULD.
  */
-static int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req)
+int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req)
 {
        struct scsi_cmnd *cmd;
-       struct scsi_driver *drv;
-       int ret;
+       int ret = scsi_prep_state_check(sdev, req);
 
+       if (ret != BLKPREP_OK)
+               return ret;
        /*
         * Filesystem requests must transfer data.
         */
        if (unlikely(!cmd))
                return BLKPREP_DEFER;
 
-       ret = scsi_init_io(cmd);
-       if (unlikely(ret))
-               return ret;
-
-       /*
-        * Initialize the actual SCSI command for this request.
-        */
-       drv = *(struct scsi_driver **)req->rq_disk->private_data;
-       if (unlikely(!drv->init_command(cmd))) {
-               scsi_release_buffers(cmd);
-               scsi_put_command(cmd);
-               return BLKPREP_KILL;
-       }
-
-       return BLKPREP_OK;
+       return scsi_init_io(cmd);
 }
+EXPORT_SYMBOL(scsi_setup_fs_cmnd);
 
-static int scsi_prep_fn(struct request_queue *q, struct request *req)
+int scsi_prep_state_check(struct scsi_device *sdev, struct request *req)
 {
-       struct scsi_device *sdev = q->queuedata;
        int ret = BLKPREP_OK;
 
        /*
                                ret = BLKPREP_KILL;
                        break;
                }
-
-               if (ret != BLKPREP_OK)
-                       goto out;
        }
+       return ret;
+}
+EXPORT_SYMBOL(scsi_prep_state_check);
 
-       switch (req->cmd_type) {
-       case REQ_TYPE_BLOCK_PC:
-               ret = scsi_setup_blk_pc_cmnd(sdev, req);
-               break;
-       case REQ_TYPE_FS:
-               ret = scsi_setup_fs_cmnd(sdev, req);
-               break;
-       default:
-               /*
-                * All other command types are not supported.
-                *
-                * Note that these days the SCSI subsystem does not use
-                * REQ_TYPE_SPECIAL requests anymore.  These are only used
-                * (directly or via blk_insert_request) by non-SCSI drivers.
-                */
-               blk_dump_rq_flags(req, "SCSI bad req");
-               ret = BLKPREP_KILL;
-               break;
-       }
+int scsi_prep_return(struct request_queue *q, struct request *req, int ret)
+{
+       struct scsi_device *sdev = q->queuedata;
 
- out:
        switch (ret) {
        case BLKPREP_KILL:
                req->errors = DID_NO_CONNECT << 16;
+               /* release the command and kill it */
+               if (req->special) {
+                       struct scsi_cmnd *cmd = req->special;
+                       scsi_release_buffers(cmd);
+                       scsi_put_command(cmd);
+                       req->special = NULL;
+               }
                break;
        case BLKPREP_DEFER:
                /*
 
        return ret;
 }
+EXPORT_SYMBOL(scsi_prep_return);
+
+static int scsi_prep_fn(struct request_queue *q, struct request *req)
+{
+       struct scsi_device *sdev = q->queuedata;
+       int ret = BLKPREP_KILL;
+
+       if (req->cmd_type == REQ_TYPE_BLOCK_PC)
+               ret = scsi_setup_blk_pc_cmnd(sdev, req);
+       return scsi_prep_return(q, req, ret);
+}
 
 /*
  * scsi_dev_queue_ready: if we can send requests to sdev, return 1 else
 
                .shutdown       = sd_shutdown,
        },
        .rescan                 = sd_rescan,
-       .init_command           = sd_init_command,
 };
 
 /*
  *
  *     Returns 1 if successful and 0 if error (or cannot be done now).
  **/
-static int sd_init_command(struct scsi_cmnd * SCpnt)
+static int sd_prep_fn(struct request_queue *q, struct request *rq)
 {
-       struct scsi_device *sdp = SCpnt->device;
-       struct request *rq = SCpnt->request;
+       struct scsi_cmnd *SCpnt;
+       struct scsi_device *sdp = q->queuedata;
        struct gendisk *disk = rq->rq_disk;
        sector_t block = rq->sector;
-       unsigned int this_count = SCpnt->request_bufflen >> 9;
+       unsigned int this_count = rq->nr_sectors;
        unsigned int timeout = sdp->timeout;
+       int ret;
+
+       if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
+               ret = scsi_setup_blk_pc_cmnd(sdp, rq);
+               goto out;
+       } else if (rq->cmd_type != REQ_TYPE_FS) {
+               ret = BLKPREP_KILL;
+               goto out;
+       }
+       ret = scsi_setup_fs_cmnd(sdp, rq);
+       if (ret != BLKPREP_OK)
+               goto out;
+       SCpnt = rq->special;
+
+       /* from here on until we're complete, any goto out
+        * is used for a killable error condition */
+       ret = BLKPREP_KILL;
 
        SCSI_LOG_HLQUEUE(1, scmd_printk(KERN_INFO, SCpnt,
                                        "sd_init_command: block=%llu, "
                                                rq->nr_sectors));
                SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
                                                "Retry with 0x%p\n", SCpnt));
-               return 0;
+               goto out;
        }
 
        if (sdp->changed) {
                 * the changed bit has been reset
                 */
                /* printk("SCSI disk has been changed. Prohibiting further I/O.\n"); */
-               return 0;
+               goto out;
        }
+
        SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n",
                                        (unsigned long long)block));
 
                if ((block & 1) || (rq->nr_sectors & 1)) {
                        scmd_printk(KERN_ERR, SCpnt,
                                    "Bad block number requested\n");
-                       return 0;
+                       goto out;
                } else {
                        block = block >> 1;
                        this_count = this_count >> 1;
                if ((block & 3) || (rq->nr_sectors & 3)) {
                        scmd_printk(KERN_ERR, SCpnt,
                                    "Bad block number requested\n");
-                       return 0;
+                       goto out;
                } else {
                        block = block >> 2;
                        this_count = this_count >> 2;
                if ((block & 7) || (rq->nr_sectors & 7)) {
                        scmd_printk(KERN_ERR, SCpnt,
                                    "Bad block number requested\n");
-                       return 0;
+                       goto out;
                } else {
                        block = block >> 3;
                        this_count = this_count >> 3;
        }
        if (rq_data_dir(rq) == WRITE) {
                if (!sdp->writeable) {
-                       return 0;
+                       goto out;
                }
                SCpnt->cmnd[0] = WRITE_6;
                SCpnt->sc_data_direction = DMA_TO_DEVICE;
                SCpnt->sc_data_direction = DMA_FROM_DEVICE;
        } else {
                scmd_printk(KERN_ERR, SCpnt, "Unknown command %x\n", rq->cmd_flags);
-               return 0;
+               goto out;
        }
 
        SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
                         */
                        scmd_printk(KERN_ERR, SCpnt,
                                    "FUA write on READ/WRITE(6) drive\n");
-                       return 0;
+                       goto out;
                }
 
                SCpnt->cmnd[1] |= (unsigned char) ((block >> 16) & 0x1f);
         * This indicates that the command is ready from our end to be
         * queued.
         */
-       return 1;
+       ret = BLKPREP_OK;
+ out:
+       return scsi_prep_return(q, rq, ret);
 }
 
 /**
 
        sd_revalidate_disk(gd);
 
+       blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);
        blk_queue_issue_flush_fn(sdp->request_queue, sd_issue_flush);
 
        gd->driverfs_dev = &sdp->sdev_gendev;
 
 
 static int sr_probe(struct device *);
 static int sr_remove(struct device *);
-static int sr_init_command(struct scsi_cmnd *);
 
 static struct scsi_driver sr_template = {
        .owner                  = THIS_MODULE,
                .probe          = sr_probe,
                .remove         = sr_remove,
        },
-       .init_command           = sr_init_command,
 };
 
 static unsigned long sr_index_bits[SR_DISKS / BITS_PER_LONG];
        scsi_io_completion(SCpnt, good_bytes);
 }
 
-static int sr_init_command(struct scsi_cmnd * SCpnt)
+static int sr_prep_fn(struct request_queue *q, struct request *rq)
 {
        int block=0, this_count, s_size, timeout = SR_TIMEOUT;
-       struct scsi_cd *cd = scsi_cd(SCpnt->request->rq_disk);
+       struct scsi_cd *cd;
+       struct scsi_cmnd *SCpnt;
+       struct scsi_device *sdp = q->queuedata;
+       int ret;
+
+       if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
+               ret = scsi_setup_blk_pc_cmnd(sdp, rq);
+               goto out;
+       } else if (rq->cmd_type != REQ_TYPE_FS) {
+               ret = BLKPREP_KILL;
+               goto out;
+       }
+       ret = scsi_setup_fs_cmnd(sdp, rq);
+       if (ret != BLKPREP_OK)
+               goto out;
+       SCpnt = rq->special;
+       cd = scsi_cd(rq->rq_disk);
+
+       /* from here on until we're complete, any goto out
+        * is used for a killable error condition */
+       ret = BLKPREP_KILL;
 
        SCSI_LOG_HLQUEUE(1, printk("Doing sr request, dev = %s, block = %d\n",
                                cd->disk->disk_name, block));
 
        if (!cd->device || !scsi_device_online(cd->device)) {
                SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n",
-                                       SCpnt->request->nr_sectors));
+                                       rq->nr_sectors));
                SCSI_LOG_HLQUEUE(2, printk("Retry with 0x%p\n", SCpnt));
-               return 0;
+               goto out;
        }
 
        if (cd->device->changed) {
                 * quietly refuse to do anything to a changed disc until the
                 * changed bit has been reset
                 */
-               return 0;
+               goto out;
        }
 
        /*
 
        if (s_size != 512 && s_size != 1024 && s_size != 2048) {
                scmd_printk(KERN_ERR, SCpnt, "bad sector size %d\n", s_size);
-               return 0;
+               goto out;
        }
 
-       if (rq_data_dir(SCpnt->request) == WRITE) {
+       if (rq_data_dir(rq) == WRITE) {
                if (!cd->device->writeable)
-                       return 0;
+                       goto out;
                SCpnt->cmnd[0] = WRITE_10;
                SCpnt->sc_data_direction = DMA_TO_DEVICE;
                cd->cdi.media_written = 1;
-       } else if (rq_data_dir(SCpnt->request) == READ) {
+       } else if (rq_data_dir(rq) == READ) {
                SCpnt->cmnd[0] = READ_10;
                SCpnt->sc_data_direction = DMA_FROM_DEVICE;
        } else {
-               blk_dump_rq_flags(SCpnt->request, "Unknown sr command");
-               return 0;
+               blk_dump_rq_flags(rq, "Unknown sr command");
+               goto out;
        }
 
        {
        /*
         * request doesn't start on hw block boundary, add scatter pads
         */
-       if (((unsigned int)SCpnt->request->sector % (s_size >> 9)) ||
+       if (((unsigned int)rq->sector % (s_size >> 9)) ||
            (SCpnt->request_bufflen % s_size)) {
                scmd_printk(KERN_NOTICE, SCpnt, "unaligned transfer\n");
-               return 0;
+               goto out;
        }
 
        this_count = (SCpnt->request_bufflen >> 9) / (s_size >> 9);
 
        SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%ld 512 byte blocks.\n",
                                cd->cdi.name,
-                               (rq_data_dir(SCpnt->request) == WRITE) ?
+                               (rq_data_dir(rq) == WRITE) ?
                                        "writing" : "reading",
-                               this_count, SCpnt->request->nr_sectors));
+                               this_count, rq->nr_sectors));
 
        SCpnt->cmnd[1] = 0;
-       block = (unsigned int)SCpnt->request->sector / (s_size >> 9);
+       block = (unsigned int)rq->sector / (s_size >> 9);
 
        if (this_count > 0xffff) {
                this_count = 0xffff;
         * This indicates that the command is ready from our end to be
         * queued.
         */
-       return 1;
+       ret = BLKPREP_OK;
+ out:
+       return scsi_prep_return(q, rq, ret);
 }
 
 static int sr_block_open(struct inode *inode, struct file *file)
 
        /* FIXME: need to handle a get_capabilities failure properly ?? */
        get_capabilities(cd);
+       blk_queue_prep_rq(sdev->request_queue, sr_prep_fn);
        sr_vendor_init(cd);
 
        disk->driverfs_dev = &sdev->sdev_gendev;
 
 
 struct module;
 struct scsi_cmnd;
+struct scsi_device;
+struct request;
+struct request_queue;
 
 
 struct scsi_driver {
        struct module           *owner;
        struct device_driver    gendrv;
 
-       int (*init_command)(struct scsi_cmnd *);
        void (*rescan)(struct device *);
 };
 #define to_scsi_driver(drv) \
 #define scsi_unregister_interface(intf) \
        class_interface_unregister(intf)
 
+int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req);
+int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req);
+int scsi_prep_state_check(struct scsi_device *sdev, struct request *req);
+int scsi_prep_return(struct request_queue *q, struct request *req, int ret);
+
 #endif /* _SCSI_SCSI_DRIVER_H */
 
 static int sd_suspend(struct device *dev, pm_message_t state);
 static int sd_resume(struct device *dev);
 static void sd_rescan(struct device *);
-static int  sd_init_command(struct scsi_cmnd *);
 static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer);
 static void scsi_disk_release(struct class_device *cdev);
 static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *);