[SCSI] sd,sr: add early detection of medium not present
[linux-2.6-block.git] / drivers / scsi / sr.c
index f9a52af7f5b468a3fce49719b43606668f75d264..896be4ab285d53bab06a97f6d885ab6429591894 100644 (file)
@@ -78,7 +78,7 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_WORM);
 
 static int sr_probe(struct device *);
 static int sr_remove(struct device *);
-static int sr_init_command(struct scsi_cmnd *);
+static int sr_done(struct scsi_cmnd *);
 
 static struct scsi_driver sr_template = {
        .owner                  = THIS_MODULE,
@@ -87,7 +87,7 @@ static struct scsi_driver sr_template = {
                .probe          = sr_probe,
                .remove         = sr_remove,
        },
-       .init_command           = sr_init_command,
+       .done                   = sr_done,
 };
 
 static unsigned long sr_index_bits[SR_DISKS / BITS_PER_LONG];
@@ -175,25 +175,32 @@ static void scsi_cd_put(struct scsi_cd *cd)
  * an inode for that to work, and we do not always have one.
  */
 
-int sr_media_change(struct cdrom_device_info *cdi, int slot)
+static int sr_media_change(struct cdrom_device_info *cdi, int slot)
 {
        struct scsi_cd *cd = cdi->handle;
        int retval;
+       struct scsi_sense_hdr *sshdr;
 
        if (CDSL_CURRENT != slot) {
                /* no changer support */
                return -EINVAL;
        }
 
-       retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES);
-       if (retval) {
-               /* Unable to test, unit probably not ready.  This usually
-                * means there is no disc in the drive.  Mark as changed,
-                * and we will figure it out later once the drive is
-                * available again.  */
+       sshdr =  kzalloc(sizeof(*sshdr), GFP_KERNEL);
+       retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES,
+                                     sshdr);
+       if (retval || (scsi_sense_valid(sshdr) &&
+                      /* 0x3a is medium not present */
+                      sshdr->asc == 0x3a)) {
+               /* Media not present or unable to test, unit probably not
+                * ready. This usually means there is no disc in the drive.
+                * Mark as changed, and we will figure it out later once
+                * the drive is available again.
+                */
                cd->device->changed = 1;
-               return 1;       /* This will force a flush, if called from
-                                * check_disk_change */
+               /* This will force a flush, if called from check_disk_change */
+               retval = 1;
+               goto out;
        };
 
        retval = cd->device->changed;
@@ -203,19 +210,27 @@ int sr_media_change(struct cdrom_device_info *cdi, int slot)
        if (retval) {
                /* check multisession offset etc */
                sr_cd_check(cdi);
-
                get_sectorsize(cd);
        }
+
+out:
+       /* Notify userspace, that media has changed. */
+       if (retval != cd->previous_state)
+               sdev_evt_send_simple(cd->device, SDEV_EVT_MEDIA_CHANGE,
+                                    GFP_KERNEL);
+       cd->previous_state = retval;
+       kfree(sshdr);
+
        return retval;
 }
  
 /*
- * rw_intr is the interrupt routine for the device driver.
+ * sr_done is the interrupt routine for the device driver.
  *
- * It will be notified on the end of a SCSI read / write, and will take on
+ * It will be notified on the end of a SCSI read / write, and will take one
  * of several actions based on success or failure.
  */
-static void rw_intr(struct scsi_cmnd * SCpnt)
+static int sr_done(struct scsi_cmnd *SCpnt)
 {
        int result = SCpnt->result;
        int this_count = SCpnt->request_bufflen;
@@ -288,27 +303,42 @@ static void rw_intr(struct scsi_cmnd * SCpnt)
                }
        }
 
-       /*
-        * This calls the generic completion function, now that we know
-        * how many actual sectors finished, and how many sectors we need
-        * to say have failed.
-        */
-       scsi_io_completion(SCpnt, good_bytes);
+       return 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) {
@@ -316,7 +346,7 @@ static int sr_init_command(struct scsi_cmnd * SCpnt)
                 * quietly refuse to do anything to a changed disc until the
                 * changed bit has been reset
                 */
-               return 0;
+               goto out;
        }
 
        /*
@@ -333,21 +363,21 @@ static int sr_init_command(struct scsi_cmnd * SCpnt)
 
        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;
        }
 
        {
@@ -368,10 +398,10 @@ static int sr_init_command(struct scsi_cmnd * SCpnt)
        /*
         * 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);
@@ -379,12 +409,12 @@ static int sr_init_command(struct scsi_cmnd * SCpnt)
 
        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;
@@ -409,17 +439,13 @@ static int sr_init_command(struct scsi_cmnd * SCpnt)
        SCpnt->allowed = MAX_RETRIES;
        SCpnt->timeout_per_command = timeout;
 
-       /*
-        * This is the completion routine we use.  This is matched in terms
-        * of capability to this function.
-        */
-       SCpnt->done = rw_intr;
-
        /*
         * 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)
@@ -590,6 +616,7 @@ static int sr_probe(struct device *dev)
 
        /* 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;
@@ -624,7 +651,7 @@ static void get_sectorsize(struct scsi_cd *cd)
        unsigned char *buffer;
        int the_result, retries = 3;
        int sector_size;
-       request_queue_t *queue;
+       struct request_queue *queue;
 
        buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
        if (!buffer)
@@ -885,7 +912,11 @@ static int __init init_sr(void)
        rc = register_blkdev(SCSI_CDROM_MAJOR, "sr");
        if (rc)
                return rc;
-       return scsi_register_driver(&sr_template.gendrv);
+       rc = scsi_register_driver(&sr_template.gendrv);
+       if (rc)
+               unregister_blkdev(SCSI_CDROM_MAJOR, "sr");
+
+       return rc;
 }
 
 static void __exit exit_sr(void)