From: Vitaly Wool <vw...@dev.rtsoft.ru>

Add media change reason string to SDEV_MEDIA_CHANGE uevent
in the format of "SDEV_MEDIA_CHANGE_REASON=<reason>, where
reason is one of MEDIA_DETACH, MEDIA_ATTACH and MEDIA_BAD.

Signed-off-by: Vitaly Wool <vw...@dev.rtsoft.ru>
Signed-off-by: Nikita Yushchenko <nyushche...@dev.rtsoft.ru>
Signed-off-by: Lim Key Seong <key.seong....@intel.com>
---
 drivers/scsi/scsi_lib.c    | 66 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/sr.c          |  1 +
 include/scsi/scsi_device.h | 18 +++++++++++++
 3 files changed, 85 insertions(+)

diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 448ebda..de991c4 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -2676,6 +2676,64 @@ scsi_device_set_state(struct scsi_device *sdev, enum 
scsi_device_state state)
 }
 EXPORT_SYMBOL(scsi_device_set_state);
 
+#define MAX_RETRIES     3
+#define SR_TIMEOUT      (3 * HZ)
+
+/**
+ *     get_change_reason - Obtain media change reason by querying the device
+ *     @sdev:  scsi device to get media change reason from.
+ *
+ *     Returns reason as specified in @scsi_media_change_reason
+ */
+static enum scsi_media_change_reason get_change_reason(struct scsi_device 
*sdev)
+{
+       int ret, is_good;
+       enum scsi_media_change_reason reason = SDEV_MEDIA_BAD;
+       struct scsi_sense_hdr sshdr;
+
+       ret = scsi_test_unit_ready(sdev, SR_TIMEOUT, MAX_RETRIES, &sshdr);
+       is_good = scsi_status_is_good(ret);
+       pr_debug("%s: changed %d, is_good %d, asc 0x%x, ascq 0x%x\n",
+               __func__, sdev->changed, is_good, sshdr.asc, sshdr.ascq);
+
+       if (is_good)
+               reason = SDEV_MEDIA_ATTACH;
+       else {
+               switch (sshdr.asc) {
+               case 0x28:
+               case 0x29:
+                       reason = SDEV_MEDIA_UNDEF;
+                       break;
+
+               case 0x04:
+                       if (sshdr.ascq == 0x01) {
+                               reason = SDEV_MEDIA_UNDEF;
+                               break;
+                       }
+               /* otherwise fall through */
+               case 0x3A:
+                       reason = SDEV_MEDIA_DETACH;
+                       break;
+
+               default:
+                       reason = SDEV_MEDIA_BAD;
+                       break;
+               }
+       }
+       if (!sdev->changed && reason == sdev->last_change_reason)
+               reason = SDEV_MEDIA_UNDEF;
+       else
+               sdev->last_change_reason = reason;
+       return reason;
+}
+
+static char *media_change_reasons[SDEV_MEDIA_REASON_MAX + 1] = {
+       [SDEV_MEDIA_ATTACH] = "SDEV_MEDIA_CHANGE_REASON=MEDIA_ATTACH",
+       [SDEV_MEDIA_DETACH] = "SDEV_MEDIA_CHANGE_REASON=MEDIA_DETACH",
+       [SDEV_MEDIA_BAD] = "SDEV_MEDIA_CHANGE_REASON=MEDIA_BAD",
+};
+
+
 /**
  *     sdev_evt_emit - emit a single SCSI device uevent
  *     @sdev: associated SCSI device
@@ -2687,10 +2745,18 @@ static void scsi_evt_emit(struct scsi_device *sdev, 
struct scsi_event *evt)
 {
        int idx = 0;
        char *envp[3];
+       enum scsi_media_change_reason r;
 
        switch (evt->evt_type) {
        case SDEV_EVT_MEDIA_CHANGE:
                envp[idx++] = "SDEV_MEDIA_CHANGE=1";
+               if (sdev->add_change_reason) {
+                       r = get_change_reason(sdev);
+                       if (media_change_reasons[r])
+                               envp[idx++] = media_change_reasons[r];
+                       pr_debug("%s: reason %s\n", __func__,
+                               media_change_reasons[r] ?: "n/a");
+               }
                break;
        case SDEV_EVT_INQUIRY_CHANGE_REPORTED:
                envp[idx++] = "SDEV_UA=INQUIRY_DATA_HAS_CHANGED";
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 64c8674..ca8883c 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -732,6 +732,7 @@ static int sr_probe(struct device *dev)
        disk->flags |= GENHD_FL_REMOVABLE;
        add_disk(disk);
 
+       sdev->add_change_reason = 1; /* Let SCSI add change reason to uevent */
        sdev_printk(KERN_DEBUG, sdev,
                    "Attached scsi CD-ROM %s\n", cd->cdi.name);
        scsi_autopm_put_device(cd->device);
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index a4c9336..012626f 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -64,6 +64,21 @@ enum scsi_device_event {
        SDEV_EVT_MAXBITS        = SDEV_EVT_LAST + 1
 };
 
+/**
+ * SCSI media change event reasons
+ * @SDEV_MEDIA_ATTACH: a valid medium has been inserted
+ * @SDEV_MEDIA_BAD: an unreadable medium has been inserted into the drive
+ * @SDEV_MEDIA_DETACH: a medium has been removed
+ * @SDEV_MEDIA_UNDEF: no valid reason has been detected
+ */
+enum scsi_media_change_reason {
+       SDEV_MEDIA_ATTACH = 1,
+       SDEV_MEDIA_BAD,
+       SDEV_MEDIA_DETACH,
+       SDEV_MEDIA_UNDEF,
+       SDEV_MEDIA_REASON_MAX = SDEV_MEDIA_UNDEF
+};
+
 struct scsi_event {
        enum scsi_device_event  evt_type;
        struct list_head        node;
@@ -172,6 +187,7 @@ struct scsi_device {
        unsigned is_visible:1;  /* is the device visible in sysfs */
        unsigned wce_default_on:1;      /* Cache is ON by default */
        unsigned no_dif:1;      /* T10 PI (DIF) should be disabled */
+       unsigned add_change_reason:1; /* Add media change reason? */
        unsigned broken_fua:1;          /* Don't set FUA bit */
        unsigned lun_in_cdb:1;          /* Store LUN bits in CDB[1] */
 
@@ -197,6 +213,8 @@ struct scsi_device {
 
        struct scsi_dh_data     *scsi_dh_data;
        enum scsi_device_state sdev_state;
+
+       enum scsi_media_change_reason   last_change_reason;
        unsigned long           sdev_data[0];
 } __attribute__((aligned(sizeof(unsigned long))));
 
-- 
2.7.3

-- 
_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to