commit 5dca5368cf8e9c2634099d3f186f5f11edff9844
Author: David Zeuthen <davidz@redhat.com>
Date:   Fri Nov 20 20:21:58 2009 -0500

    scsi_id: Export WWN and Unit Serial Number
    
    Since the Unit Serial Number and the WWN are useful bits of
    information export them as properties in the udev database. These
    bits of information are often printed on the physical disk, see
    
    http://people.freedesktop.org/~david/FUJITSU-MAY2036RC-sas-disk-picture.jpg
    
    and displaying them separately in the UI (or in /dev/disk) might help
    users identify the physical disk. This patch exports these bits of
    information as ID_WWN and ID_SCSI_SERIAL.
    
    Note that ata_id also use the ID_WWN property since commit
    66094a4a7fc1d303e80785d586800eae9841502b (2009-11-04).
    
    Also print out ID_SCSI=1 so users of the udev database knows the disk
    speaks SCSI.
    
    Here's the scsi_id output from one of my SAS disks with these changes:
    
     ID_SCSI=1
     ID_VENDOR=FUJITSU
     ID_VENDOR_ENC=FUJITSU\x20
     ID_MODEL=MAY2036RC
     ID_MODEL_ENC=MAY2036RC\x20\x20\x20\x20\x20\x20\x20
     ID_REVISION=0103
     ID_TYPE=disk
     ID_SERIAL=3500000e01b83f520
     ID_SERIAL_SHORT=500000e01b83f520
     ID_WWN=0x500000e01b83f520
     ID_SCSI_SERIAL=B3G1P8500RWT
    
    Unfortunately we can't overload ID_SERIAL for two reasons
    
     1. ID_SERIAL (and ID_SERIAL_SHORT) exported by scsi_id isn't really
        the unit serial number (as defined by SCSI) - it is sometimes the
        WWN (since it is more unique than the Unit Serial Number) and
        complex rules (to make the serial unique) govern what value to
        use.
    
     2. It would break existing setups if the value of ID_SERIAL changed
    
    Signed-off-by: David Zeuthen <davidz@redhat.com>

---
 extras/scsi_id/scsi_id.c     |    7 +++++
 extras/scsi_id/scsi_id.h     |    6 ++++
 extras/scsi_id/scsi_serial.c |   57 ++++++++++++++++++++++++++++---------------
 3 files changed, 51 insertions(+), 19 deletions(-)

--- a/extras/scsi_id/scsi_id.c
+++ b/extras/scsi_id/scsi_id.c
@@ -549,6 +549,7 @@ static int scsi_id(struct udev *udev, ch
 	if (export) {
 		char serial_str[MAX_SERIAL_LEN];
 
+		printf("ID_SCSI=1\n");
 		printf("ID_VENDOR=%s\n", vendor_str);
 		printf("ID_VENDOR_ENC=%s\n", vendor_enc_str);
 		printf("ID_MODEL=%s\n", model_str);
@@ -563,6 +564,12 @@ static int scsi_id(struct udev *udev, ch
 			udev_util_replace_chars(serial_str, NULL);
 			printf("ID_SERIAL_SHORT=%s\n", serial_str);
 		}
+		if (dev_scsi.wwn[0] != '\0') {
+			printf("ID_WWN=0x%s\n", dev_scsi.wwn);
+		}
+		if (dev_scsi.unit_serial_number[0] != '\0') {
+			printf("ID_SCSI_SERIAL=%s\n", dev_scsi.unit_serial_number);
+		}
 		goto out;
 	}
 
--- a/extras/scsi_id/scsi_id.h
+++ b/extras/scsi_id/scsi_id.h
@@ -39,6 +39,12 @@ struct scsi_id_device {
 	char serial[MAX_SERIAL_LEN];
 	char serial_short[MAX_SERIAL_LEN];
 	int use_sg;
+
+        /* Always from page 0x80 e.g. 'B3G1P8500RWT' - may not be unique */
+        char unit_serial_number[MAX_SERIAL_LEN];
+
+        /* NULs if not set - otherwise hex encoding using lower-case e.g. '50014ee0016eb572' */
+        char wwn[17];
 };
 
 extern int scsi_std_inquiry(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname);
--- a/extras/scsi_id/scsi_serial.c
+++ b/extras/scsi_id/scsi_serial.c
@@ -85,11 +85,15 @@ static const char hex_str[]="0123456789a
 #define SG_ERR_CAT_MEDIA_CHANGED	1	/* interpreted from sense buffer */
 #define SG_ERR_CAT_RESET		2	/* interpreted from sense buffer */
 #define SG_ERR_CAT_TIMEOUT		3
-#define SG_ERR_CAT_RECOVERED		4 	/* Successful command after recovered err */
-#define SG_ERR_CAT_NOTSUPPORTED 	5	/* Illegal / unsupported command */
+#define SG_ERR_CAT_RECOVERED		4	/* Successful command after recovered err */
+#define SG_ERR_CAT_NOTSUPPORTED		5	/* Illegal / unsupported command */
 #define SG_ERR_CAT_SENSE		98	/* Something else in the sense buffer */
 #define SG_ERR_CAT_OTHER		99	/* Some other error/warning */
 
+static int do_scsi_page80_inquiry(struct udev *udev,
+				  struct scsi_id_device *dev_scsi, int fd,
+				  char *serial, char *serial_short, int max_len);
+
 static int sg_err_category_new(struct udev *udev,
 			       int scsi_status, int msg_status, int
 			       host_status, int driver_status, const
@@ -420,7 +424,7 @@ static int do_scsi_page0_inquiry(struct 
 		return 1;
 	}
 	if (buffer[3] > len) {
-		info(udev, "%s: page 0 buffer too long %d\n", dev_scsi->kernel,  buffer[3]);
+		info(udev, "%s: page 0 buffer too long %d\n", dev_scsi->kernel,	 buffer[3]);
 		return 1;
 	}
 
@@ -477,7 +481,8 @@ static int check_fill_0x83_id(struct ude
 			      struct scsi_id_device *dev_scsi,
 			      unsigned char *page_83,
 			      const struct scsi_id_search_values
-			      *id_search, char *serial, char *serial_short, int max_len)
+			      *id_search, char *serial, char *serial_short, int max_len,
+                              char *wwn)
 {
 	int i, j, s, len;
 
@@ -561,6 +566,10 @@ static int check_fill_0x83_id(struct ude
 	}
 
 	strcpy(serial_short, &serial[s]);
+
+        if (id_search->id_type == SCSI_ID_NAA && wwn != NULL) {
+                strncpy(wwn, &serial[s], 16);
+        }
 	return 0;
 }
 
@@ -591,12 +600,16 @@ static int check_fill_0x83_prespc3(struc
 /* Get device identification VPD page */
 static int do_scsi_page83_inquiry(struct udev *udev,
 				  struct scsi_id_device *dev_scsi, int fd,
-				  char *serial, char *serial_short, int len)
+				  char *serial, char *serial_short, int len,
+                                  char *unit_serial_number, char *wwn)
 {
 	int retval;
 	unsigned int id_ind, j;
 	unsigned char page_83[SCSI_INQ_BUFF_LEN];
 
+        /* also pick up the page 80 serial number */
+        do_scsi_page80_inquiry(udev, dev_scsi, fd, NULL, unit_serial_number, MAX_SERIAL_LEN);
+
 	memset(page_83, 0, SCSI_INQ_BUFF_LEN);
 	retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83,
 			      SCSI_INQ_BUFF_LEN);
@@ -643,7 +656,8 @@ static int do_scsi_page83_inquiry(struct
 					       serial, serial_short, len);
 
 	/*
-	 * Search for a match in the prioritized id_search_list.
+	 * Search for a match in the prioritized id_search_list - since WWN ids
+         * come first we can pick up the WWN in check_fill_0x83_id().
 	 */
 	for (id_ind = 0;
 	     id_ind < sizeof(id_search_list)/sizeof(id_search_list[0]);
@@ -656,7 +670,8 @@ static int do_scsi_page83_inquiry(struct
 			retval = check_fill_0x83_id(udev,
 						    dev_scsi, &page_83[j],
 						    &id_search_list[id_ind],
-						    serial, serial_short, len);
+						    serial, serial_short, len,
+                                                    wwn);
 			dbg(udev, "%s id desc %d/%d/%d\n", dev_scsi->kernel,
 				id_search_list[id_ind].id_type,
 				id_search_list[id_ind].naa_type,
@@ -775,15 +790,19 @@ static int do_scsi_page80_inquiry(struct
 	 * Prepend 'S' to avoid unlikely collision with page 0x83 vendor
 	 * specific type where we prepend '0' + vendor + model.
 	 */
-	serial[0] = 'S';
-	ser_ind = prepend_vendor_model(udev, dev_scsi, &serial[1]);
-	if (ser_ind < 0)
-		return 1;
-	len = buf[3];
-	for (i = 4; i < len + 4; i++, ser_ind++)
-		serial[ser_ind] = buf[i];
-	memcpy(serial_short, &buf[4], len);
-	serial_short[len] = '\0';
+        len = buf[3];
+        if (serial != NULL) {
+                serial[0] = 'S';
+                ser_ind = prepend_vendor_model(udev, dev_scsi, &serial[1]);
+                if (ser_ind < 0)
+                        return 1;
+                for (i = 4; i < len + 4; i++, ser_ind++)
+                        serial[ser_ind] = buf[i];
+        }
+        if (serial_short != NULL) {
+                memcpy(serial_short, &buf[4], len);
+                serial_short[len] = '\0';
+        }
 	return 0;
 }
 
@@ -866,7 +885,7 @@ int scsi_get_serial(struct udev *udev,
 			goto completed;
 		}
 	} else if (page_code == PAGE_83) {
-		if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len)) {
+		if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn)) {
 			retval = 1;
 			goto completed;
 		} else  {
@@ -882,7 +901,7 @@ int scsi_get_serial(struct udev *udev,
 			 * conform to pre-SPC3 expectations.
 			 */
 			if (retval == 2) {
-				if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len)) {
+				if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn)) {
 					retval = 1;
 					goto completed;
 				} else  {
@@ -922,7 +941,7 @@ int scsi_get_serial(struct udev *udev,
 	for (ind = 4; ind <= page0[3] + 3; ind++)
 		if (page0[ind] == PAGE_83)
 			if (!do_scsi_page83_inquiry(udev, dev_scsi, fd,
-						    dev_scsi->serial, dev_scsi->serial_short, len)) {
+						    dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn)) {
 				/*
 				 * Success
 				 */
