[SCSI] sd,sr: add early detection of medium not present
[linux-2.6-block.git] / drivers / scsi / sr.c
index a0c4e13d4dabbbe70a962e103e78577ce71c6803..896be4ab285d53bab06a97f6d885ab6429591894 100644 (file)
@@ -78,6 +78,7 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_WORM);
 
 static int sr_probe(struct device *);
 static int sr_remove(struct device *);
+static int sr_done(struct scsi_cmnd *);
 
 static struct scsi_driver sr_template = {
        .owner                  = THIS_MODULE,
@@ -86,6 +87,7 @@ static struct scsi_driver sr_template = {
                .probe          = sr_probe,
                .remove         = sr_remove,
        },
+       .done                   = sr_done,
 };
 
 static unsigned long sr_index_bits[SR_DISKS / BITS_PER_LONG];
@@ -177,21 +179,28 @@ 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;
@@ -201,19 +210,27 @@ static 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;
@@ -286,12 +303,7 @@ 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_prep_fn(struct request_queue *q, struct request *rq)
@@ -427,12 +439,6 @@ static int sr_prep_fn(struct request_queue *q, struct request *rq)
        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.