chiark / gitweb /
support pre-SPC3 page 83 format
[elogind.git] / extras / scsi_id / scsi_serial.c
1 /*
2  * scsi_serial.c
3  *
4  * Code related to requesting and getting an id from a scsi device
5  *
6  * Copyright (C) IBM Corp. 2003
7  *
8  *  This library is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU Lesser General Public License as
10  *  published by the Free Software Foundation; either version 2.1 of the
11  *  License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful, but
14  *  WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21  *  USA
22  */
23
24 #include <sys/types.h>
25 #include <sys/ioctl.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <syslog.h>
33 #include <linux/compiler.h> /* need __user when built via klibc */
34 #include <scsi/sg.h>
35 #include <sysfs/libsysfs.h>
36 #include "scsi_id.h"
37 #include "scsi.h"
38
39 /*
40  * A priority based list of id, naa, and binary/ascii for the identifier
41  * descriptor in VPD page 0x83.
42  *
43  * Brute force search for a match starting with the first value in the
44  * following id_search_list. This is not a performance issue, since there
45  * is normally one or some small number of descriptors.
46  */
47 static const struct scsi_id_search_values id_search_list[] = {
48         { SCSI_ID_NAA,  SCSI_ID_NAA_IEEE_REG_EXTENDED,  SCSI_ID_BINARY },
49         { SCSI_ID_NAA,  SCSI_ID_NAA_IEEE_REG_EXTENDED,  SCSI_ID_ASCII },
50         { SCSI_ID_NAA,  SCSI_ID_NAA_IEEE_REG,   SCSI_ID_BINARY },
51         { SCSI_ID_NAA,  SCSI_ID_NAA_IEEE_REG,   SCSI_ID_ASCII },
52         /*
53          * Devices already exist using NAA values that are now marked
54          * reserved. These should not conflict with other values, or it is
55          * a bug in the device. As long as we find the IEEE extended one
56          * first, we really don't care what other ones are used. Using
57          * don't care here means that a device that returns multiple
58          * non-IEEE descriptors in a random order will get different
59          * names.
60          */
61         { SCSI_ID_NAA,  SCSI_ID_NAA_DONT_CARE,  SCSI_ID_BINARY },
62         { SCSI_ID_NAA,  SCSI_ID_NAA_DONT_CARE,  SCSI_ID_ASCII },
63         { SCSI_ID_EUI_64,       SCSI_ID_NAA_DONT_CARE,  SCSI_ID_BINARY },
64         { SCSI_ID_EUI_64,       SCSI_ID_NAA_DONT_CARE,  SCSI_ID_ASCII },
65         { SCSI_ID_T10_VENDOR,   SCSI_ID_NAA_DONT_CARE,  SCSI_ID_BINARY },
66         { SCSI_ID_T10_VENDOR,   SCSI_ID_NAA_DONT_CARE,  SCSI_ID_ASCII },
67         { SCSI_ID_VENDOR_SPECIFIC,      SCSI_ID_NAA_DONT_CARE,  SCSI_ID_BINARY },
68         { SCSI_ID_VENDOR_SPECIFIC,      SCSI_ID_NAA_DONT_CARE,  SCSI_ID_ASCII },
69 };
70
71 static const char hex_str[]="0123456789abcdef";
72
73 /*
74  * Values returned in the result/status, only the ones used by the code
75  * are used here.
76  */
77
78 #define DID_NO_CONNECT 0x01     /* Unable to connect before timeout */
79
80 #define DID_BUS_BUSY 0x02       /* Bus remain busy until timeout */
81 #define DID_TIME_OUT 0x03       /* Timed out for some other reason */
82
83 #define DRIVER_TIMEOUT 0x06
84 #define DRIVER_SENSE 0x08       /* Sense_buffer has been set */
85
86 /* The following "category" function returns one of the following */
87 #define SG_ERR_CAT_CLEAN        0      /* No errors or other information */
88 #define SG_ERR_CAT_MEDIA_CHANGED        1 /* interpreted from sense buffer */
89 #define SG_ERR_CAT_RESET        2      /* interpreted from sense buffer */
90 #define SG_ERR_CAT_TIMEOUT      3
91 #define SG_ERR_CAT_RECOVERED    4  /* Successful command after recovered err */
92 #define SG_ERR_CAT_NOTSUPPORTED 5 /* Illegal / unsupported command */
93 #define SG_ERR_CAT_SENSE        98     /* Something else in the sense buffer */
94 #define SG_ERR_CAT_OTHER        99     /* Some other error/warning */
95
96 static int sg_err_category_new(int scsi_status, int msg_status, int
97                                host_status, int driver_status, const
98                                unsigned char *sense_buffer, int sb_len)
99 {
100         scsi_status &= 0x7e;
101
102         /*
103          * XXX change to return only two values - failed or OK.
104          */
105
106         /*
107          * checks msg_status
108          */
109         if (!scsi_status && !msg_status && !host_status && !driver_status)
110                 return SG_ERR_CAT_CLEAN;
111
112         if ((scsi_status == SCSI_CHECK_CONDITION) ||
113             (scsi_status == SCSI_COMMAND_TERMINATED) ||
114             ((driver_status & 0xf) == DRIVER_SENSE)) {
115                 if (sense_buffer && (sb_len > 2)) {
116                         int sense_key;
117                         unsigned char asc;
118
119                         if (sense_buffer[0] & 0x2) {
120                                 sense_key = sense_buffer[1] & 0xf;
121                                 asc = sense_buffer[2];
122                         } else {
123                                 sense_key = sense_buffer[2] & 0xf;
124                                 asc = (sb_len > 12) ? sense_buffer[12] : 0;
125                         }
126
127                         if (sense_key == RECOVERED_ERROR)
128                                 return SG_ERR_CAT_RECOVERED;
129                         else if (sense_key == UNIT_ATTENTION) {
130                                 if (0x28 == asc)
131                                         return SG_ERR_CAT_MEDIA_CHANGED;
132                                 if (0x29 == asc)
133                                         return SG_ERR_CAT_RESET;
134                         } else if (sense_key == ILLEGAL_REQUEST) {
135                                 return SG_ERR_CAT_NOTSUPPORTED;
136                         }
137                 }
138                 return SG_ERR_CAT_SENSE;
139         }
140         if (!host_status) {
141                 if ((host_status == DID_NO_CONNECT) ||
142                     (host_status == DID_BUS_BUSY) ||
143                     (host_status == DID_TIME_OUT))
144                         return SG_ERR_CAT_TIMEOUT;
145         }
146         if (!driver_status) {
147                 if (driver_status == DRIVER_TIMEOUT)
148                         return SG_ERR_CAT_TIMEOUT;
149         }
150         return SG_ERR_CAT_OTHER;
151 }
152
153 static int sg_err_category3(struct sg_io_hdr *hp)
154 {
155         return sg_err_category_new(hp->status, hp->msg_status,
156                                    hp->host_status, hp->driver_status,
157                                    hp->sbp, hp->sb_len_wr);
158 }
159
160 static int scsi_dump_sense(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
161 {
162         unsigned char *sense_buffer;
163         int s;
164         int sb_len;
165         int code;
166         int sense_class;
167         int sense_key;
168         int descriptor_format;
169         int asc, ascq;
170 #ifdef DUMP_SENSE
171         char out_buffer[256];
172         int i, j;
173 #endif
174
175         /*
176          * Figure out and print the sense key, asc and ascq.
177          *
178          * If you want to suppress these for a particular drive model, add
179          * a black list entry in the scsi_id config file.
180          *
181          * XXX We probably need to: lookup the sense/asc/ascq in a retry
182          * table, and if found return 1 (after dumping the sense, asc, and
183          * ascq). So, if/when we get something like a power on/reset,
184          * we'll retry the command.
185          */
186
187         dprintf("got check condition\n");
188
189         sb_len = io->sb_len_wr;
190         if (sb_len < 1) {
191                 log_message(LOG_WARNING, "%s: sense buffer empty\n",
192                             scsi_dev->name);
193                 return -1;
194         }
195
196         sense_buffer = io->sbp;
197         sense_class = (sense_buffer[0] >> 4) & 0x07;
198         code = sense_buffer[0] & 0xf;
199
200         if (sense_class == 7) {
201                 /*
202                  * extended sense data.
203                  */
204                 s = sense_buffer[7] + 8;
205                 if (sb_len < s) {
206                         log_message(LOG_WARNING,
207                                     "%s: sense buffer too small %d bytes,"
208                                     " %d bytes too short\n", scsi_dev->name,
209                                     sb_len, s - sb_len);
210                         return -1;
211                 }
212                 if ((code == 0x0) || (code == 0x1)) {
213                         descriptor_format = 0;
214                         sense_key = sense_buffer[2] & 0xf;
215                         if (s < 14) {
216                                 /*
217                                  * Possible?
218                                  */
219                                 log_message(LOG_WARNING, "%s: sense result too"
220                                             " small %d bytes\n",
221                                             scsi_dev->name, s);
222                                 return -1;
223                         }
224                         asc = sense_buffer[12];
225                         ascq = sense_buffer[13];
226                 } else if ((code == 0x2) || (code == 0x3)) {
227                         descriptor_format = 1;
228                         sense_key = sense_buffer[1] & 0xf;
229                         asc = sense_buffer[2];
230                         ascq = sense_buffer[3];
231                 } else {
232                         log_message(LOG_WARNING,
233                                     "%s: invalid sense code 0x%x\n",
234                                     scsi_dev->name, code);
235                         return -1;
236                 }
237                 log_message(LOG_WARNING,
238                             "%s: sense key 0x%x ASC 0x%x ASCQ 0x%x\n",
239                             scsi_dev->name, sense_key, asc, ascq);
240         } else {
241                 if (sb_len < 4) {
242                         log_message(LOG_WARNING,
243                                     "%s: sense buffer too small %d bytes, %d bytes too short\n",
244                                     scsi_dev->name, sb_len, 4 - sb_len);
245                         return -1;
246                 }
247
248                 if (sense_buffer[0] < 15)
249                         log_message(LOG_WARNING, "%s: old sense key: 0x%x\n",
250                                     scsi_dev->name, sense_buffer[0] & 0x0f);
251                 else
252                         log_message(LOG_WARNING, "%s: sense = %2x %2x\n",
253                                     scsi_dev->name,  sense_buffer[0],
254                                     sense_buffer[2]);
255                 log_message(LOG_WARNING,
256                             "%s: non-extended sense class %d code 0x%0x\n",
257                             scsi_dev->name, sense_class, code);
258
259         }
260
261 #ifdef DUMP_SENSE
262         for (i = 0, j = 0; (i < s) && (j < 254); i++) {
263                 dprintf("i %d, j %d\n", i, j);
264                 out_buffer[j++] = hex_str[(sense_buffer[i] & 0xf0) >> 4];
265                 out_buffer[j++] = hex_str[sense_buffer[i] & 0x0f];
266                 out_buffer[j++] = ' ';
267         }
268         out_buffer[j] = '\0';
269         log_message(LOG_WARNING, "%s: sense dump:\n", scsi_dev->name);
270         log_message(LOG_WARNING, "%s: %s\n", scsi_dev->name, out_buffer);
271
272 #endif
273         return -1;
274 }
275
276 static int scsi_dump(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
277 {
278         if (!io->status && !io->host_status && !io->msg_status &&
279             !io->driver_status) {
280                 /*
281                  * Impossible, should not be called.
282                  */
283                 log_message(LOG_WARNING, "%s: called with no error\n",
284                             __FUNCTION__);
285                 return -1;
286         }
287
288         log_message(LOG_WARNING, "%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x\n",
289                     scsi_dev->name, io->driver_status, io->host_status,
290                     io->msg_status, io->status);
291         if (io->status == SCSI_CHECK_CONDITION)
292                 return scsi_dump_sense(scsi_dev, io);
293         else
294                 return -1;
295 }
296
297 static int scsi_inquiry(struct sysfs_device *scsi_dev, int fd,
298                         unsigned char evpd, unsigned char page,
299                         unsigned char *buf, unsigned int buflen)
300 {
301         unsigned char inq_cmd[INQUIRY_CMDLEN] =
302                 { INQUIRY_CMD, evpd, page, 0, buflen, 0 };
303         unsigned char sense[SENSE_BUFF_LEN];
304         struct sg_io_hdr io_hdr;
305         int retval;
306         int retry = 3; /* rather random */
307
308         if (buflen > SCSI_INQ_BUFF_LEN) {
309                 log_message(LOG_WARNING, "buflen %d too long\n", buflen);
310                 return -1;
311         }
312
313 resend:
314         dprintf("%s evpd %d, page 0x%x\n", scsi_dev->name, evpd, page);
315
316         memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
317         io_hdr.interface_id = 'S';
318         io_hdr.cmd_len = sizeof(inq_cmd);
319         io_hdr.mx_sb_len = sizeof(sense);
320         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
321         io_hdr.dxfer_len = buflen;
322         io_hdr.dxferp = buf;
323         io_hdr.cmdp = inq_cmd;
324         io_hdr.sbp = sense;
325         io_hdr.timeout = DEF_TIMEOUT;
326
327         if (ioctl(fd, SG_IO, &io_hdr) < 0) {
328                 log_message(LOG_WARNING, "%s: ioctl failed: %s\n",
329                             scsi_dev->name, strerror(errno));
330                 retval = -1;
331                 goto error;
332         }
333
334         retval = sg_err_category3(&io_hdr);
335
336         switch (retval) {
337                 case SG_ERR_CAT_NOTSUPPORTED:
338                         buf[1] = 0;
339                         /* Fallthrough */
340                 case SG_ERR_CAT_CLEAN:
341                 case SG_ERR_CAT_RECOVERED:
342                         retval = 0;
343                         break;
344
345                 default:
346                         retval = scsi_dump(scsi_dev, &io_hdr);
347         }
348
349         if (!retval) {
350                 retval = buflen;
351         } else if (retval > 0) {
352                 if (--retry > 0) {
353                         dprintf("%s: Retrying ...\n", scsi_dev->name);
354                         goto resend;
355                 }
356                 retval = -1;
357         }
358
359 error:
360         if (retval < 0)
361                 log_message(LOG_WARNING,
362                             "%s: Unable to get INQUIRY vpd %d page 0x%x.\n",
363                             scsi_dev->name, evpd, page);
364
365         return retval;
366 }
367
368 /* Get list of supported EVPD pages */
369 static int do_scsi_page0_inquiry(struct sysfs_device *scsi_dev, int fd,
370                                  unsigned char *buffer, unsigned int len)
371 {
372         int retval;
373         struct sysfs_attribute *vendor;
374
375         memset(buffer, 0, len);
376         retval = scsi_inquiry(scsi_dev, fd, 1, 0x0, buffer, len);
377         if (retval < 0)
378                 return 1;
379
380         if (buffer[1] != 0) {
381                 log_message(LOG_WARNING, "%s: page 0 not available.\n",
382                             scsi_dev->name);
383                 return 1;
384         }
385         if (buffer[3] > len) {
386                 log_message(LOG_WARNING, "%s: page 0 buffer too long %d\n",
387                            scsi_dev->name,  buffer[3]);
388                 return 1;
389         }
390
391         /*
392          * Following check is based on code once included in the 2.5.x
393          * kernel.
394          *
395          * Some ill behaved devices return the standard inquiry here
396          * rather than the evpd data, snoop the data to verify.
397          */
398         if (buffer[3] > MODEL_LENGTH) {
399                 /*
400                  * If the vendor id appears in the page assume the page is
401                  * invalid.
402                  */
403                 vendor = sysfs_get_device_attr(scsi_dev, "vendor");
404                 if (!vendor) {
405                         log_message(LOG_WARNING,
406                                     "%s: cannot get model attribute\n",
407                                     scsi_dev->name);
408                         return 1;
409                 }
410                 if (!strncmp((char *)&buffer[VENDOR_LENGTH], vendor->value,
411                              VENDOR_LENGTH)) {
412                         log_message(LOG_WARNING, "%s: invalid page0 data\n",
413                                     scsi_dev->name);
414                         return 1;
415                 }
416         }
417         return 0;
418 }
419
420 /*
421  * The caller checks that serial is long enough to include the vendor +
422  * model.
423  */
424 static int prepend_vendor_model(struct sysfs_device *scsi_dev, char *serial)
425 {
426         struct sysfs_attribute *attr;
427         int ind;
428
429         attr = sysfs_get_device_attr(scsi_dev, "vendor");
430         if (!attr) {
431                 log_message(LOG_WARNING, "%s: cannot get vendor attribute\n",
432                             scsi_dev->name);
433                 return 1;
434         }
435         strncpy(serial, attr->value, VENDOR_LENGTH);
436         ind = strlen(serial) - 1;
437         /*
438          * Remove sysfs added newlines.
439          */
440         if (serial[ind] == '\n')
441                 serial[ind] = '\0';
442
443         attr = sysfs_get_device_attr(scsi_dev, "model");
444         if (!attr) {
445                 log_message(LOG_WARNING, "%s: cannot get model attribute\n",
446                             scsi_dev->name);
447                 return 1;
448         }
449         strncat(serial, attr->value, MODEL_LENGTH);
450         ind = strlen(serial) - 1;
451         if (serial[ind] == '\n')
452                 serial[ind] = '\0';
453         else
454                 ind++;
455
456         /*
457          * This is not a complete check, since we are using strncat/cpy
458          * above, ind will never be too large.
459          */
460         if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) {
461                 log_message(LOG_WARNING, "%s: expected length %d, got length %d\n",
462                             scsi_dev->name, (VENDOR_LENGTH + MODEL_LENGTH),
463                             ind);
464                 return 1;
465         }
466         return ind;
467 }
468
469 /**
470  * check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill
471  * serial number.
472  **/
473 static int check_fill_0x83_id(struct sysfs_device *scsi_dev,
474                               unsigned char *page_83,
475                               const struct scsi_id_search_values
476                               *id_search, char *serial, int max_len)
477 {
478         int i, j, len;
479
480         /*
481          * ASSOCIATION must be with the device (value 0)
482          */
483         if ((page_83[1] & 0x30) != 0)
484                 return 1;
485
486         if ((page_83[1] & 0x0f) != id_search->id_type)
487                 return 1;
488
489         /*
490          * Possibly check NAA sub-type.
491          */
492         if ((id_search->naa_type != SCSI_ID_NAA_DONT_CARE) &&
493             (id_search->naa_type != (page_83[4] & 0xf0) >> 4))
494                 return 1;
495
496         /*
497          * Check for matching code set - ASCII or BINARY.
498          */
499         if ((page_83[0] & 0x0f) != id_search->code_set)
500                 return 1;
501
502         /*
503          * page_83[3]: identifier length
504          */
505         len = page_83[3];
506         if ((page_83[0] & 0x0f) != SCSI_ID_ASCII)
507                 /*
508                  * If not ASCII, use two bytes for each binary value.
509                  */
510                 len *= 2;
511
512         /*
513          * Add one byte for the NUL termination, and one for the id_type.
514          */
515         len += 2;
516         if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC)
517                 len += VENDOR_LENGTH + MODEL_LENGTH;
518
519         if (max_len < len) {
520                 log_message(LOG_WARNING, "%s: length %d too short - need %d\n",
521                             scsi_dev->name, max_len, len);
522                 return 1;
523         }
524
525         serial[0] = hex_str[id_search->id_type];
526
527         /*
528          * For SCSI_ID_VENDOR_SPECIFIC prepend the vendor and model before
529          * the id since it is not unique across all vendors and models,
530          * this differs from SCSI_ID_T10_VENDOR, where the vendor is
531          * included in the identifier.
532          */
533         if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC)
534                 if (prepend_vendor_model(scsi_dev, &serial[1]) < 0) {
535                         dprintf("prepend failed\n");
536                         return 1;
537                 }
538
539         i = 4; /* offset to the start of the identifier */
540         j = strlen(serial);
541         if ((page_83[0] & 0x0f) == SCSI_ID_ASCII) {
542                 /*
543                  * ASCII descriptor.
544                  */
545                 while (i < (4 + page_83[3]))
546                         serial[j++] = page_83[i++];
547         } else {
548                 /*
549                  * Binary descriptor, convert to ASCII, using two bytes of
550                  * ASCII for each byte in the page_83.
551                  */
552                 while (i < (4 + page_83[3])) {
553                         serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4];
554                         serial[j++] = hex_str[page_83[i] & 0x0f];
555                         i++;
556                 }
557         }
558         return 0;
559 }
560
561 /* Extract the raw binary from VPD 0x83 pre-SPC devices */
562 static int check_fill_0x83_prespc3(struct sysfs_device *scsi_dev,
563                                    unsigned char *page_83,
564                                    const struct scsi_id_search_values
565                                    *id_search, char *serial, int max_len)
566 {
567         int i, j;
568         
569         serial[0] = hex_str[id_search->id_type];
570         /* serial has been memset to zero before */
571         j = strlen(serial);     /* j = 1; */
572
573         for (i = 0; i < page_83[3]; ++i) {
574                 serial[j++] = hex_str[(page_83[4+i] & 0xf0) >> 4];
575                 serial[j++] = hex_str[ page_83[4+i] & 0x0f];
576         }
577         dprintf("using pre-spc3-83 for %s.\n", scsi_dev->name);
578         return 0;
579 }
580
581
582 /* Get device identification VPD page */
583 static int do_scsi_page83_inquiry(struct sysfs_device *scsi_dev, int fd,
584                                   char *serial, int len)
585 {
586         int retval;
587         unsigned int id_ind, j;
588         unsigned char page_83[SCSI_INQ_BUFF_LEN];
589
590         memset(page_83, 0, SCSI_INQ_BUFF_LEN);
591         retval = scsi_inquiry(scsi_dev, fd, 1, PAGE_83, page_83,
592                               SCSI_INQ_BUFF_LEN);
593         if (retval < 0)
594                 return 1;
595
596         if (page_83[1] != PAGE_83) {
597                 log_message(LOG_WARNING, "%s: Invalid page 0x83\n",
598                             scsi_dev->name);
599                 return 1;
600         }
601         
602         /*
603          * XXX Some devices (IBM 3542) return all spaces for an identifier if
604          * the LUN is not actually configured. This leads to identifers of
605          * the form: "1            ".
606          */
607
608         /*
609          * Model 4, 5, and (some) model 6 EMC Symmetrix devices return
610          * a page 83 reply according to SCSI-2 format instead of SPC-2/3.
611          *
612          * The SCSI-2 page 83 format returns an IEEE WWN in binary
613          * encoded hexi-decimal in the 16 bytes following the initial
614          * 4-byte page 83 reply header.
615          *
616          * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part
617          * of an Identification descriptor.  The 3rd byte of the first
618          * Identification descriptor is a reserved (BSZ) byte field.
619          *
620          * Reference the 7th byte of the page 83 reply to determine
621          * whether the reply is compliant with SCSI-2 or SPC-2/3
622          * specifications.  A zero value in the 7th byte indicates
623          * an SPC-2/3 conformant reply, (i.e., the reserved field of the
624          * first Identification descriptor).  This byte will be non-zero
625          * for a SCSI-2 conformant page 83 reply from these EMC
626          * Symmetrix models since the 7th byte of the reply corresponds
627          * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is,
628          * 0x006048.
629          */
630         
631         if (page_83[6] != 0) 
632                 return check_fill_0x83_prespc3(scsi_dev, page_83, 
633                                                id_search_list, serial, len);
634
635         /*
636          * Search for a match in the prioritized id_search_list.
637          */
638         for (id_ind = 0;
639              id_ind < sizeof(id_search_list)/sizeof(id_search_list[0]);
640              id_ind++) {
641                 /*
642                  * Examine each descriptor returned. There is normally only
643                  * one or a small number of descriptors.
644                  */
645                 for (j = 4; j <= (unsigned int)page_83[3] + 3; j += page_83[j + 3] + 4) {
646                         retval = check_fill_0x83_id(scsi_dev, &page_83[j],
647                                                     &id_search_list[id_ind],
648                                                     serial, len);
649                         dprintf("%s id desc %d/%d/%d\n", scsi_dev->name,
650                                 id_search_list[id_ind].id_type,
651                                 id_search_list[id_ind].naa_type,
652                                 id_search_list[id_ind].code_set);
653                         if (!retval) {
654                                 dprintf("       used\n");
655                                 return retval;
656                         } else if (retval < 0) {
657                                 dprintf("       failed\n");
658                                 return retval;
659                         } else {
660                                 dprintf("       not used\n");
661                         }
662                 }
663         }
664         return 1;
665 }
666
667 /*
668  * Get device identification VPD page for older SCSI-2 device which is not
669  * compliant with either SPC-2 or SPC-3 format.
670  *
671  * Return the hard coded error code value 2 if the page 83 reply is not
672  * conformant to the SCSI-2 format.
673  */
674 static int do_scsi_page83_prespc3_inquiry(struct sysfs_device *scsi_dev, int fd,
675                                           char *serial, int len)
676 {
677         int retval;
678         int i, j;
679         unsigned char page_83[SCSI_INQ_BUFF_LEN];
680
681         memset(page_83, 0, SCSI_INQ_BUFF_LEN);
682         retval = scsi_inquiry(scsi_dev, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN);
683         if (retval < 0)
684                 return 1;
685
686         if (page_83[1] != PAGE_83) {
687                 log_message(LOG_WARNING, "%s: Invalid page 0x83\n", scsi_dev->name);
688                 return 1;
689         }
690         /*
691          * Model 4, 5, and (some) model 6 EMC Symmetrix devices return
692          * a page 83 reply according to SCSI-2 format instead of SPC-2/3.
693          *
694          * The SCSI-2 page 83 format returns an IEEE WWN in binary
695          * encoded hexi-decimal in the 16 bytes following the initial
696          * 4-byte page 83 reply header.
697          *
698          * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part
699          * of an Identification descriptor.  The 3rd byte of the first
700          * Identification descriptor is a reserved (BSZ) byte field.
701          *
702          * Reference the 7th byte of the page 83 reply to determine
703          * whether the reply is compliant with SCSI-2 or SPC-2/3
704          * specifications.  A zero value in the 7th byte indicates
705          * an SPC-2/3 conformant reply, (i.e., the reserved field of the
706          * first Identification descriptor).  This byte will be non-zero
707          * for a SCSI-2 conformant page 83 reply from these EMC
708          * Symmetrix models since the 7th byte of the reply corresponds
709          * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is,
710          * 0x006048.
711          */
712         if (page_83[6] == 0)
713                 return 2;
714
715         serial[0] = hex_str[id_search_list[0].id_type];
716         /*
717          * The first four bytes contain data, not a descriptor.
718          */
719         i = 4;
720         j = strlen(serial);
721         /*
722          * Binary descriptor, convert to ASCII,
723          * using two bytes of ASCII for each byte
724          * in the page_83.
725          */
726         while (i < (page_83[3]+4)) {
727                 serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4];
728                 serial[j++] = hex_str[page_83[i] & 0x0f];
729                 i++;
730         }
731         dprintf("using pre-spc3-83 for %s.\n", scsi_dev->name);
732         return 0;
733 }
734
735 /* Get unit serial number VPD page */
736 static int do_scsi_page80_inquiry(struct sysfs_device *scsi_dev, int fd,
737                                   char *serial, int max_len)
738 {
739         int retval;
740         int ser_ind;
741         int i;
742         int len;
743         unsigned char buf[SCSI_INQ_BUFF_LEN];
744
745         memset(buf, 0, SCSI_INQ_BUFF_LEN);
746         retval = scsi_inquiry(scsi_dev, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN);
747         if (retval < 0)
748                 return retval;
749
750         if (buf[1] != PAGE_80) {
751                 log_message(LOG_WARNING, "%s: Invalid page 0x80\n",
752                             scsi_dev->name);
753                 return 1;
754         }
755
756         len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3];
757         if (max_len < len) {
758                 log_message(LOG_WARNING, "%s: length %d too short - need %d\n",
759                             scsi_dev->name, max_len, len);
760                 return 1;
761         }
762         /*
763          * Prepend 'S' to avoid unlikely collision with page 0x83 vendor
764          * specific type where we prepend '0' + vendor + model.
765          */
766         serial[0] = 'S';
767         ser_ind = prepend_vendor_model(scsi_dev, &serial[1]);
768         if (ser_ind < 0)
769                 return 1;
770         len = buf[3];
771         for (i = 4; i < len + 4; i++, ser_ind++)
772                 serial[ser_ind] = buf[i];
773         return 0;
774 }
775
776 int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
777                      int page_code, char *serial, int len)
778 {
779         unsigned char page0[SCSI_INQ_BUFF_LEN];
780         int fd;
781         int ind;
782         int retval;
783
784         memset(serial, 0, len);
785         dprintf("opening %s\n", devname);
786         fd = open(devname, O_RDONLY | O_NONBLOCK);
787         if (fd < 0) {
788                 log_message(LOG_WARNING, "%s: cannot open %s: %s\n",
789                             scsi_dev->name, devname, strerror(errno));
790                 return 1;
791         }
792
793         if (page_code == PAGE_80) {
794                 if (do_scsi_page80_inquiry(scsi_dev, fd, serial, len)) {
795                         retval = 1;
796                         goto completed;
797                 } else  {
798                         retval = 0;
799                         goto completed;
800                 }
801         } else if (page_code == PAGE_83) {
802                 if (do_scsi_page83_inquiry(scsi_dev, fd, serial, len)) {
803                         retval = 1;
804                         goto completed;
805                 } else  {
806                         retval = 0;
807                         goto completed;
808                 }
809         } else if (page_code == PAGE_83_PRE_SPC3) {
810                 retval = do_scsi_page83_prespc3_inquiry(scsi_dev, fd, serial, len);
811                 if (retval) {
812                         /*
813                          * Fallback to servicing a SPC-2/3 compliant page 83
814                          * inquiry if the page 83 reply format does not
815                          * conform to pre-SPC3 expectations.
816                          */
817                         if (retval == 2) {
818                                 if (do_scsi_page83_inquiry(scsi_dev, fd, serial, len)) {
819                                         retval = 1;
820                                         goto completed;
821                                 } else  {
822                                         retval = 0;
823                                         goto completed;
824                                 }
825                         }
826                         else {
827                                 retval = 1;
828                                 goto completed;
829                         }
830                 } else  {
831                         retval = 0;
832                         goto completed;
833                 }
834         } else if (page_code != 0x00) {
835                 log_message(LOG_WARNING, "%s: unsupported page code 0x%d\n",
836                             scsi_dev->name, page_code);
837                 return 1;
838         }
839
840         /*
841          * Get page 0, the page of the pages. By default, try from best to
842          * worst of supported pages: 0x83 then 0x80.
843          */
844         if (do_scsi_page0_inquiry(scsi_dev, fd, page0, SCSI_INQ_BUFF_LEN)) {
845                 /*
846                  * Don't try anything else. Black list if a specific page
847                  * should be used for this vendor+model, or maybe have an
848                  * optional fall-back to page 0x80 or page 0x83.
849                  */
850                 retval = 1;
851                 goto completed;
852         }
853
854         dprintf("%s: Checking page0\n", scsi_dev->name);
855
856         for (ind = 4; ind <= page0[3] + 3; ind++)
857                 if (page0[ind] == PAGE_83)
858                         if (!do_scsi_page83_inquiry(scsi_dev, fd, serial,
859                                                     len)) {
860                                 /*
861                                  * Success
862                                  */
863                                 retval = 0;
864                                 goto completed;
865                         }
866
867         for (ind = 4; ind <= page0[3] + 3; ind++)
868                 if (page0[ind] == PAGE_80)
869                         if (!do_scsi_page80_inquiry(scsi_dev, fd, serial,
870                                                     len)) {
871                                 /*
872                                  * Success
873                                  */
874                                 retval = 0;
875                                 goto completed;
876                         }
877         retval = 1;
878 completed:
879         if (close(fd) < 0)
880                 log_message(LOG_WARNING, "%s: close failed: %s\n", 
881                             scsi_dev->name, strerror(errno));
882         return retval;
883 }