chiark / gitweb /
Merge branch 'master' from gregkh@master.kernel.org:/pub/scm/linux/hotplug/udev
[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 /* Get device identification VPD page */
562 static int do_scsi_page83_inquiry(struct sysfs_device *scsi_dev, int fd,
563                                   char *serial, int len)
564 {
565         int retval;
566         unsigned int id_ind, j;
567         unsigned char page_83[SCSI_INQ_BUFF_LEN];
568
569         memset(page_83, 0, SCSI_INQ_BUFF_LEN);
570         retval = scsi_inquiry(scsi_dev, fd, 1, PAGE_83, page_83,
571                               SCSI_INQ_BUFF_LEN);
572         if (retval < 0)
573                 return 1;
574
575         if (page_83[1] != PAGE_83) {
576                 log_message(LOG_WARNING, "%s: Invalid page 0x83\n",
577                             scsi_dev->name);
578                 return 1;
579         }
580         
581         /*
582          * XXX Some devices (IBM 3542) return all spaces for an identifier if
583          * the LUN is not actually configured. This leads to identifers of
584          * the form: "1            ".
585          */
586
587         /*
588          * Search for a match in the prioritized id_search_list.
589          */
590         for (id_ind = 0;
591              id_ind < sizeof(id_search_list)/sizeof(id_search_list[0]);
592              id_ind++) {
593                 /*
594                  * Examine each descriptor returned. There is normally only
595                  * one or a small number of descriptors.
596                  */
597                 for (j = 4; j <= (unsigned int)page_83[3] + 3; j += page_83[j + 3] + 4) {
598                         retval = check_fill_0x83_id(scsi_dev, &page_83[j],
599                                                     &id_search_list[id_ind],
600                                                     serial, len);
601                         dprintf("%s id desc %d/%d/%d\n", scsi_dev->name,
602                                 id_search_list[id_ind].id_type,
603                                 id_search_list[id_ind].naa_type,
604                                 id_search_list[id_ind].code_set);
605                         if (!retval) {
606                                 dprintf("       used\n");
607                                 return retval;
608                         } else if (retval < 0) {
609                                 dprintf("       failed\n");
610                                 return retval;
611                         } else {
612                                 dprintf("       not used\n");
613                         }
614                 }
615         }
616         return 1;
617 }
618
619 /*
620  * Get device identification VPD page for older SCSI-2 device which is not
621  * compliant with either SPC-2 or SPC-3 format.
622  *
623  * Return the hard coded error code value 2 if the page 83 reply is not
624  * conformant to the SCSI-2 format.
625  */
626 static int do_scsi_page83_prespc3_inquiry(struct sysfs_device *scsi_dev, int fd,
627                                           char *serial, int len)
628 {
629         int retval;
630         int i, j;
631         unsigned char page_83[SCSI_INQ_BUFF_LEN];
632
633         memset(page_83, 0, SCSI_INQ_BUFF_LEN);
634         retval = scsi_inquiry(scsi_dev, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN);
635         if (retval < 0)
636                 return 1;
637
638         if (page_83[1] != PAGE_83) {
639                 log_message(LOG_WARNING, "%s: Invalid page 0x83\n", scsi_dev->name);
640                 return 1;
641         }
642         /*
643          * Model 4, 5, and (some) model 6 EMC Symmetrix devices return
644          * a page 83 reply according to SCSI-2 format instead of SPC-2/3.
645          *
646          * The SCSI-2 page 83 format returns an IEEE WWN in binary
647          * encoded hexi-decimal in the 16 bytes following the initial
648          * 4-byte page 83 reply header.
649          *
650          * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part
651          * of an Identification descriptor.  The 3rd byte of the first
652          * Identification descriptor is a reserved (BSZ) byte field.
653          *
654          * Reference the 7th byte of the page 83 reply to determine
655          * whether the reply is compliant with SCSI-2 or SPC-2/3
656          * specifications.  A zero value in the 7th byte indicates
657          * an SPC-2/3 conformant reply, (i.e., the reserved field of the
658          * first Identification descriptor).  This byte will be non-zero
659          * for a SCSI-2 conformant page 83 reply from these EMC
660          * Symmetrix models since the 7th byte of the reply corresponds
661          * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is,
662          * 0x006048.
663          */
664         if (page_83[6] == 0)
665                 return 2;
666
667         serial[0] = hex_str[id_search_list[0].id_type];
668         /*
669          * The first four bytes contain data, not a descriptor.
670          */
671         i = 4;
672         j = strlen(serial);
673         /*
674          * Binary descriptor, convert to ASCII,
675          * using two bytes of ASCII for each byte
676          * in the page_83.
677          */
678         while (i < (page_83[3]+4)) {
679                 serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4];
680                 serial[j++] = hex_str[page_83[i] & 0x0f];
681                 i++;
682         }
683         dprintf("using pre-spc3-83 for %s.\n", scsi_dev->name);
684         return 0;
685 }
686
687 /* Get unit serial number VPD page */
688 static int do_scsi_page80_inquiry(struct sysfs_device *scsi_dev, int fd,
689                                   char *serial, int max_len)
690 {
691         int retval;
692         int ser_ind;
693         int i;
694         int len;
695         unsigned char buf[SCSI_INQ_BUFF_LEN];
696
697         memset(buf, 0, SCSI_INQ_BUFF_LEN);
698         retval = scsi_inquiry(scsi_dev, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN);
699         if (retval < 0)
700                 return retval;
701
702         if (buf[1] != PAGE_80) {
703                 log_message(LOG_WARNING, "%s: Invalid page 0x80\n",
704                             scsi_dev->name);
705                 return 1;
706         }
707
708         len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3];
709         if (max_len < len) {
710                 log_message(LOG_WARNING, "%s: length %d too short - need %d\n",
711                             scsi_dev->name, max_len, len);
712                 return 1;
713         }
714         /*
715          * Prepend 'S' to avoid unlikely collision with page 0x83 vendor
716          * specific type where we prepend '0' + vendor + model.
717          */
718         serial[0] = 'S';
719         ser_ind = prepend_vendor_model(scsi_dev, &serial[1]);
720         if (ser_ind < 0)
721                 return 1;
722         len = buf[3];
723         for (i = 4; i < len + 4; i++, ser_ind++)
724                 serial[ser_ind] = buf[i];
725         return 0;
726 }
727
728 int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
729                      int page_code, char *serial, int len)
730 {
731         unsigned char page0[SCSI_INQ_BUFF_LEN];
732         int fd;
733         int ind;
734         int retval;
735
736         memset(serial, 0, len);
737         dprintf("opening %s\n", devname);
738         fd = open(devname, O_RDONLY | O_NONBLOCK);
739         if (fd < 0) {
740                 log_message(LOG_WARNING, "%s: cannot open %s: %s\n",
741                             scsi_dev->name, devname, strerror(errno));
742                 return 1;
743         }
744
745         if (page_code == PAGE_80) {
746                 if (do_scsi_page80_inquiry(scsi_dev, fd, serial, len)) {
747                         retval = 1;
748                         goto completed;
749                 } else  {
750                         retval = 0;
751                         goto completed;
752                 }
753         } else if (page_code == PAGE_83) {
754                 if (do_scsi_page83_inquiry(scsi_dev, fd, serial, len)) {
755                         retval = 1;
756                         goto completed;
757                 } else  {
758                         retval = 0;
759                         goto completed;
760                 }
761         } else if (page_code == PAGE_83_PRE_SPC3) {
762                 retval = do_scsi_page83_prespc3_inquiry(scsi_dev, fd, serial, len);
763                 if (retval) {
764                         /*
765                          * Fallback to servicing a SPC-2/3 compliant page 83
766                          * inquiry if the page 83 reply format does not
767                          * conform to pre-SPC3 expectations.
768                          */
769                         if (retval == 2) {
770                                 if (do_scsi_page83_inquiry(scsi_dev, fd, serial, len)) {
771                                         retval = 1;
772                                         goto completed;
773                                 } else  {
774                                         retval = 0;
775                                         goto completed;
776                                 }
777                         }
778                         else {
779                                 retval = 1;
780                                 goto completed;
781                         }
782                 } else  {
783                         retval = 0;
784                         goto completed;
785                 }
786         } else if (page_code != 0x00) {
787                 log_message(LOG_WARNING, "%s: unsupported page code 0x%d\n",
788                             scsi_dev->name, page_code);
789                 return 1;
790         }
791
792         /*
793          * Get page 0, the page of the pages. By default, try from best to
794          * worst of supported pages: 0x83 then 0x80.
795          */
796         if (do_scsi_page0_inquiry(scsi_dev, fd, page0, SCSI_INQ_BUFF_LEN)) {
797                 /*
798                  * Don't try anything else. Black list if a specific page
799                  * should be used for this vendor+model, or maybe have an
800                  * optional fall-back to page 0x80 or page 0x83.
801                  */
802                 retval = 1;
803                 goto completed;
804         }
805
806         dprintf("%s: Checking page0\n", scsi_dev->name);
807
808         for (ind = 4; ind <= page0[3] + 3; ind++)
809                 if (page0[ind] == PAGE_83)
810                         if (!do_scsi_page83_inquiry(scsi_dev, fd, serial,
811                                                     len)) {
812                                 /*
813                                  * Success
814                                  */
815                                 retval = 0;
816                                 goto completed;
817                         }
818
819         for (ind = 4; ind <= page0[3] + 3; ind++)
820                 if (page0[ind] == PAGE_80)
821                         if (!do_scsi_page80_inquiry(scsi_dev, fd, serial,
822                                                     len)) {
823                                 /*
824                                  * Success
825                                  */
826                                 retval = 0;
827                                 goto completed;
828                         }
829         retval = 1;
830 completed:
831         if (close(fd) < 0)
832                 log_message(LOG_WARNING, "%s: close failed: %s\n", 
833                             scsi_dev->name, strerror(errno));
834         return retval;
835 }