chiark / gitweb /
[PATCH] update udev scsi_id to scsi_id 0.4
[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 <scsi/sg.h>
34 #include <sysfs/libsysfs.h>
35 #include "scsi_id.h"
36 #include "scsi.h"
37
38 /*
39  * A priority based list of id, naa, and binary/ascii for the identifier
40  * descriptor in VPD page 0x83.
41  *
42  * Brute force search for a match starting with the first value in the
43  * following id_search_list. This is not a performance issue, since there
44  * is normally one or some small number of descriptors.
45  */
46 static const struct scsi_id_search_values id_search_list[] = {
47         { SCSI_ID_NAA,  SCSI_ID_NAA_IEEE_REG_EXTENDED,  SCSI_ID_BINARY },
48         { SCSI_ID_NAA,  SCSI_ID_NAA_IEEE_REG_EXTENDED,  SCSI_ID_ASCII },
49         { SCSI_ID_NAA,  SCSI_ID_NAA_IEEE_REG,   SCSI_ID_BINARY },
50         { SCSI_ID_NAA,  SCSI_ID_NAA_IEEE_REG,   SCSI_ID_ASCII },
51         /*
52          * Devices already exist using NAA values that are now marked
53          * reserved. These should not conflict with other values, or it is
54          * a bug in the device. As long as we find the IEEE extended one
55          * first, we really don't care what other ones are used. Using
56          * don't care here means that a device that returns multiple
57          * non-IEEE descriptors in a random order will get different
58          * names.
59          */
60         { SCSI_ID_NAA,  SCSI_ID_NAA_DONT_CARE,  SCSI_ID_BINARY },
61         { SCSI_ID_NAA,  SCSI_ID_NAA_DONT_CARE,  SCSI_ID_ASCII },
62         { SCSI_ID_EUI_64,       SCSI_ID_NAA_DONT_CARE,  SCSI_ID_BINARY },
63         { SCSI_ID_EUI_64,       SCSI_ID_NAA_DONT_CARE,  SCSI_ID_ASCII },
64         { SCSI_ID_T10_VENDOR,   SCSI_ID_NAA_DONT_CARE,  SCSI_ID_BINARY },
65         { SCSI_ID_T10_VENDOR,   SCSI_ID_NAA_DONT_CARE,  SCSI_ID_ASCII },
66         { SCSI_ID_VENDOR_SPECIFIC,      SCSI_ID_NAA_DONT_CARE,  SCSI_ID_BINARY },
67         { SCSI_ID_VENDOR_SPECIFIC,      SCSI_ID_NAA_DONT_CARE,  SCSI_ID_ASCII },
68 };
69
70 static const char hex_str[]="0123456789abcdef";
71
72 /*
73  * Values returned in the result/status, only the ones used by the code
74  * are used here.
75  */
76
77 #define DID_NO_CONNECT 0x01     /* Unable to connect before timeout */
78
79 #define DID_BUS_BUSY 0x02       /* Bus remain busy until timeout */
80 #define DID_TIME_OUT 0x03       /* Timed out for some other reason */
81
82 #define DRIVER_TIMEOUT 0x06
83 #define DRIVER_SENSE 0x08       /* Sense_buffer has been set */
84
85 /* The following "category" function returns one of the following */
86 #define SG_ERR_CAT_CLEAN        0      /* No errors or other information */
87 #define SG_ERR_CAT_MEDIA_CHANGED        1 /* interpreted from sense buffer */
88 #define SG_ERR_CAT_RESET        2      /* interpreted from sense buffer */
89 #define SG_ERR_CAT_TIMEOUT      3
90 #define SG_ERR_CAT_RECOVERED    4  /* Successful command after recovered err */
91 #define SG_ERR_CAT_SENSE        98     /* Something else in the sense buffer */
92 #define SG_ERR_CAT_OTHER        99     /* Some other error/warning */
93
94 static int sg_err_category_new(int scsi_status, int msg_status, int
95                                host_status, int driver_status, const
96                                unsigned char *sense_buffer, int sb_len)
97 {
98         scsi_status &= 0x7e;
99
100         /*
101          * XXX change to return only two values - failed or OK.
102          */
103
104         /*
105          * checks msg_status
106          */
107         if (!scsi_status && !msg_status && !host_status && !driver_status)
108                 return SG_ERR_CAT_CLEAN;
109
110         if ((scsi_status == SCSI_CHECK_CONDITION) ||
111             (scsi_status == SCSI_COMMAND_TERMINATED) ||
112             ((driver_status & 0xf) == DRIVER_SENSE)) {
113                 if (sense_buffer && (sb_len > 2)) {
114                         int sense_key;
115                         unsigned char asc;
116
117                         if (sense_buffer[0] & 0x2) {
118                                 sense_key = sense_buffer[1] & 0xf;
119                                 asc = sense_buffer[2];
120                         } else {
121                                 sense_key = sense_buffer[2] & 0xf;
122                                 asc = (sb_len > 12) ? sense_buffer[12] : 0;
123                         }
124
125                         if (sense_key == RECOVERED_ERROR)
126                                 return SG_ERR_CAT_RECOVERED;
127                         else if (sense_key == UNIT_ATTENTION) {
128                                 if (0x28 == asc)
129                                         return SG_ERR_CAT_MEDIA_CHANGED;
130                                 if (0x29 == asc)
131                                         return SG_ERR_CAT_RESET;
132                         }
133                 }
134                 return SG_ERR_CAT_SENSE;
135         }
136         if (!host_status) {
137                 if ((host_status == DID_NO_CONNECT) ||
138                     (host_status == DID_BUS_BUSY) ||
139                     (host_status == DID_TIME_OUT))
140                         return SG_ERR_CAT_TIMEOUT;
141         }
142         if (!driver_status) {
143                 if (driver_status == DRIVER_TIMEOUT)
144                         return SG_ERR_CAT_TIMEOUT;
145         }
146         return SG_ERR_CAT_OTHER;
147 }
148
149 static int sg_err_category3(struct sg_io_hdr *hp)
150 {
151         return sg_err_category_new(hp->status, hp->msg_status,
152                                    hp->host_status, hp->driver_status,
153                                    hp->sbp, hp->sb_len_wr);
154 }
155
156 static int scsi_dump_sense(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
157 {
158         unsigned char *sense_buffer;
159         int s;
160         int sb_len;
161         int code;
162         int sense_class;
163         int sense_key;
164         int descriptor_format;
165         int asc, ascq;
166 #ifdef DUMP_SENSE
167         char out_buffer[256];
168         int i, j;
169 #endif
170
171         /*
172          * Figure out and print the sense key, asc and ascq.
173          *
174          * If you want to suppress these for a particular drive model, add
175          * a black list entry in the scsi_id config file.
176          *
177          * XXX We probably need to: lookup the sense/asc/ascq in a retry
178          * table, and if found return 1 (after dumping the sense, asc, and
179          * ascq). So, if/when we get something like a power on/reset,
180          * we'll retry the command.
181          */
182
183         dprintf("got check condition\n");
184
185         sb_len = io->sb_len_wr;
186         if (sb_len < 1) {
187                 log_message(LOG_WARNING, "%s: sense buffer empty\n",
188                             scsi_dev->name);
189                 return -1;
190         }
191
192         sense_buffer = io->sbp;
193         sense_class = (sense_buffer[0] >> 4) & 0x07;
194         code = sense_buffer[0] & 0xf;
195
196         if (sense_class == 7) {
197                 /*
198                  * extended sense data.
199                  */
200                 s = sense_buffer[7] + 8;
201                 if (sb_len < s) {
202                         log_message(LOG_WARNING,
203                                     "%s: sense buffer too small %d bytes,"
204                                     " %d bytes too short\n", scsi_dev->name,
205                                     sb_len, s - sb_len);
206                         return -1;
207                 }
208                 if ((code == 0x0) || (code == 0x1)) {
209                         descriptor_format = 0;
210                         sense_key = sense_buffer[2] & 0xf;
211                         if (s < 14) {
212                                 /*
213                                  * Possible?
214                                  */
215                                 log_message(LOG_WARNING, "%s: sense result too"
216                                             " small %d bytes\n",
217                                             scsi_dev->name, s);
218                                 return -1;
219                         }
220                         asc = sense_buffer[12];
221                         ascq = sense_buffer[13];
222                 } else if ((code == 0x2) || (code == 0x3)) {
223                         descriptor_format = 1;
224                         sense_key = sense_buffer[1] & 0xf;
225                         asc = sense_buffer[2];
226                         ascq = sense_buffer[3];
227                 } else {
228                         log_message(LOG_WARNING,
229                                     "%s: invalid sense code 0x%x\n",
230                                     scsi_dev->name, code);
231                         return -1;
232                 }
233                 log_message(LOG_WARNING,
234                             "%s: sense key 0x%x ASC 0x%x ASCQ 0x%x\n",
235                             scsi_dev->name, sense_key, asc, ascq);
236         } else {
237                 if (sb_len < 4) {
238                         log_message(LOG_WARNING,
239                                     "%s: sense buffer too small %d bytes, %d bytes too short\n",
240                                     scsi_dev->name, sb_len, 4 - sb_len);
241                         return -1;
242                 }
243
244                 if (sense_buffer[0] < 15)
245                         log_message(LOG_WARNING, "%s: old sense key: 0x%x\n",
246                                     scsi_dev->name, sense_buffer[0] & 0x0f);
247                 else
248                         log_message(LOG_WARNING, "%s: sense = %2x %2x\n",
249                                     scsi_dev->name,  sense_buffer[0],
250                                     sense_buffer[2]);
251                 log_message(LOG_WARNING,
252                             "%s: non-extended sense class %d code 0x%0x\n",
253                             scsi_dev->name, sense_class, code);
254
255         }
256
257 #ifdef DUMP_SENSE
258         for (i = 0, j = 0; (i < s) && (j < 254); i++) {
259                 dprintf("i %d, j %d\n", i, j);
260                 out_buffer[j++] = hex_str[(sense_buffer[i] & 0xf0) >> 4];
261                 out_buffer[j++] = hex_str[sense_buffer[i] & 0x0f];
262                 out_buffer[j++] = ' ';
263         }
264         out_buffer[j] = '\0';
265         log_message(LOG_WARNING, "%s: sense dump:\n", scsi_dev->name);
266         log_message(LOG_WARNING, "%s: %s\n", scsi_dev->name, out_buffer);
267
268 #endif
269         return -1;
270 }
271
272 static int scsi_dump(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
273 {
274         if (!io->status && !io->host_status && !io->msg_status &&
275             !io->driver_status) {
276                 /*
277                  * Impossible, should not be called.
278                  */
279                 log_message(LOG_WARNING, "%s: called with no error\n",
280                             __FUNCTION__);
281                 return -1;
282         }
283
284         log_message(LOG_WARNING, "%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x\n",
285                     scsi_dev->name, io->driver_status, io->host_status,
286                     io->msg_status, io->status);
287         if (io->status == SCSI_CHECK_CONDITION)
288                 return scsi_dump_sense(scsi_dev, io);
289         else
290                 return -1;
291 }
292
293 static int scsi_inquiry(struct sysfs_device *scsi_dev, int fd, unsigned
294                         char evpd, unsigned char page, unsigned char *buf,
295                         unsigned int buflen)
296 {
297         unsigned char inq_cmd[INQUIRY_CMDLEN] =
298                 { INQUIRY_CMD, evpd, page, 0, buflen, 0 };
299         unsigned char sense[SENSE_BUFF_LEN];
300         struct sg_io_hdr io_hdr;
301         int retval;
302         unsigned char *inq;
303         unsigned char *buffer;
304         int retry = 3; /* rather random */
305
306         if (buflen > 255) {
307                 log_message(LOG_WARNING, "buflen %d too long\n", buflen);
308                 return -1;
309         }
310         inq = malloc(OFFSET + sizeof (inq_cmd) + 512);
311         memset(inq, 0, OFFSET + sizeof (inq_cmd) + 512);
312         buffer = inq + OFFSET;
313
314 resend:
315         memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
316         io_hdr.interface_id = 'S';
317         io_hdr.cmd_len = sizeof(inq_cmd);
318         io_hdr.mx_sb_len = sizeof(sense);
319         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
320         io_hdr.dxfer_len = buflen;
321         io_hdr.dxferp = buffer;
322         io_hdr.cmdp = inq_cmd;
323         io_hdr.sbp = sense;
324         io_hdr.timeout = DEF_TIMEOUT;
325
326         if (ioctl(fd, SG_IO, &io_hdr) < 0) {
327                 log_message(LOG_WARNING, "%s: ioctl failed: %s\n",
328                             scsi_dev->name, strerror(errno));
329                 return -1;
330         }
331
332         retval = sg_err_category3(&io_hdr);
333
334         switch (retval) {
335                 case SG_ERR_CAT_CLEAN:
336                 case SG_ERR_CAT_RECOVERED:
337                         retval = 0;
338                         break;
339
340                 default:
341                         retval = scsi_dump(scsi_dev, &io_hdr);
342         }
343
344         if (!retval) {
345                 retval = buflen;
346                 memcpy(buf, buffer, retval);
347         } else if (retval > 0) {
348                 if (--retry > 0) {
349                         dprintf("%s: Retrying ...\n", scsi_dev->name);
350                         goto resend;
351                 }
352                 retval = -1;
353         }
354
355         free(inq);
356         return retval;
357 }
358
359 static int do_scsi_page0_inquiry(struct sysfs_device *scsi_dev, int fd,
360                                  char *buffer, int len)
361 {
362         int retval;
363         char vendor[MAX_ATTR_LEN];
364
365         memset(buffer, 0, len);
366         retval = scsi_inquiry(scsi_dev, fd, 1, 0x0, buffer, len);
367         if (retval < 0)
368                 return 1;
369
370         if (buffer[1] != 0) {
371                 log_message(LOG_WARNING, "%s: page 0 not available.\n",
372                             scsi_dev->name);
373                 return 1;
374         }
375         if (buffer[3] > len) {
376                 log_message(LOG_WARNING, "%s: page 0 buffer too long %d\n",
377                            scsi_dev->name,  buffer[3]);
378                 return 1;
379         }
380
381         /*
382          * Following check is based on code once included in the 2.5.x
383          * kernel.
384          *
385          * Some ill behaved devices return the standard inquiry here
386          * rather than the evpd data, snoop the data to verify.
387          */
388         if (buffer[3] > MODEL_LENGTH) {
389                 /*
390                  * If the vendor id appears in the page assume the page is
391                  * invalid.
392                  */
393                 if (sysfs_get_attr(scsi_dev->path, "vendor", vendor,
394                                    MAX_ATTR_LEN)) {
395                         log_message(LOG_WARNING,
396                                     "%s: cannot get model attribute\n",
397                                     scsi_dev->name);
398                         return 1;
399                 }
400                 if (!strncmp(&buffer[VENDOR_LENGTH], vendor, VENDOR_LENGTH)) {
401                         log_message(LOG_WARNING, "%s: invalid page0 data\n",
402                                     scsi_dev->name);
403                         return 1;
404                 }
405         }
406         return 0;
407 }
408
409 /*
410  * The caller checks that serial is long enough to include the vendor +
411  * model.
412  */
413 static int prepend_vendor_model(struct sysfs_device *scsi_dev, char *serial)
414 {
415         char attr[MAX_ATTR_LEN];
416         int ind;
417
418         if (sysfs_get_attr(scsi_dev->path, "vendor", attr, MAX_ATTR_LEN)) {
419                 log_message(LOG_WARNING, "%s: cannot get vendor attribute\n",
420                             scsi_dev->name);
421                 return 1;
422         }
423         strncpy(serial, attr, VENDOR_LENGTH);
424         ind = strlen(serial) - 1;
425         /*
426          * Remove sysfs added newlines.
427          */
428         if (serial[ind] == '\n')
429                 serial[ind] = '\0';
430
431         if (sysfs_get_attr(scsi_dev->path, "model", attr, MAX_ATTR_LEN)) {
432                 log_message(LOG_WARNING, "%s: cannot get model attribute\n",
433                             scsi_dev->name);
434                 return 1;
435         }
436         strncat(serial, attr, MODEL_LENGTH);
437         ind = strlen(serial) - 1;
438         if (serial[ind] == '\n')
439                 serial[ind] = '\0';
440         else
441                 ind++;
442
443         /*
444          * This is not a complete check, since we are using strncat/cpy
445          * above, ind will never be too large.
446          */
447         if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) {
448                 log_message(LOG_WARNING, "%s: expected length %d, got length %d\n",
449                             scsi_dev->name, (VENDOR_LENGTH + MODEL_LENGTH),
450                             ind);
451                 return 1;
452         }
453         return ind;
454 }
455
456 /**
457  * check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill
458  * serial number.
459  **/
460 static int check_fill_0x83_id(struct sysfs_device *scsi_dev, char
461                               *page_83, const struct scsi_id_search_values
462                               *id_search, char *serial, int max_len)
463 {
464         int i, j, len;
465
466         /*
467          * ASSOCIATION must be with the device (value 0)
468          */
469         if ((page_83[1] & 0x30) != 0)
470                 return 1;
471
472         if ((page_83[1] & 0x0f) != id_search->id_type)
473                 return 1;
474
475         /*
476          * Possibly check NAA sub-type.
477          */
478         if ((id_search->naa_type != SCSI_ID_NAA_DONT_CARE) &&
479             (id_search->naa_type != (page_83[4] & 0xf0) >> 4))
480                 return 1;
481
482         /*
483          * Check for matching code set - ASCII or BINARY.
484          */
485         if ((page_83[0] & 0x0f) != id_search->code_set)
486                 return 1;
487
488         /*
489          * page_83[3]: identifier length
490          */
491         len = page_83[3];
492         if ((page_83[0] & 0x0f) != SCSI_ID_ASCII)
493                 /*
494                  * If not ASCII, use two bytes for each binary value.
495                  */
496                 len *= 2;
497
498         /*
499          * Add one byte for the NUL termination, and one for the id_type.
500          */
501         len += 2;
502         if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC)
503                 len += VENDOR_LENGTH + MODEL_LENGTH;
504
505         if (max_len < len) {
506                 log_message(LOG_WARNING, "%s: length %d too short - need %d\n",
507                             scsi_dev->name, max_len, len);
508                 return 1;
509         }
510
511         serial[0] = hex_str[id_search->id_type];
512
513         /*
514          * For SCSI_ID_VENDOR_SPECIFIC prepend the vendor and model before
515          * the id since it is not unique across all vendors and models,
516          * this differs from SCSI_ID_T10_VENDOR, where the vendor is
517          * included in the identifier.
518          */
519         if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC)
520                 if (prepend_vendor_model(scsi_dev, &serial[1]) < 0) {
521                         dprintf("prepend failed\n");
522                         return 1;
523                 }
524
525         i = 4; /* offset to the start of the identifier */
526         j = strlen(serial);
527         if ((page_83[0] & 0x0f) == SCSI_ID_ASCII) {
528                 /*
529                  * ASCII descriptor.
530                  */
531                 while (i < (4 + page_83[3]))
532                         serial[j++] = page_83[i++];
533         } else {
534                 /*
535                  * Binary descriptor, convert to ASCII, using two bytes of
536                  * ASCII for each byte in the page_83.
537                  */
538                 while (i < (4 + page_83[3])) {
539                         serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4];
540                         serial[j++] = hex_str[page_83[i] & 0x0f];
541                         i++;
542                 }
543         }
544         return 0;
545 }
546
547 static int do_scsi_page83_inquiry(struct sysfs_device *scsi_dev, int fd,
548                                   char *serial, int len)
549 {
550         int retval;
551         int id_ind, j;
552         unsigned char page_83[256];
553
554         memset(page_83, 0, 256);
555         retval = scsi_inquiry(scsi_dev, fd, 1, 0x83, page_83, 255);
556         if (retval < 0)
557                 return 1;
558
559         if (page_83[1] != 0x83) {
560                 log_message(LOG_WARNING, "%s: Invalid page 0x83\n",
561                             scsi_dev->name);
562                 return 1;
563         }
564         
565         /*
566          * XXX Some devices (IBM 3542) return all spaces for an identifier if
567          * the LUN is not actually configured. This leads to identifers of
568          * the form: "1            ".
569          */
570
571         /*
572          * Search for a match in the prioritized id_search_list.
573          */
574         for (id_ind = 0;
575              id_ind < sizeof(id_search_list)/sizeof(id_search_list[0]);
576              id_ind++) {
577                 /*
578                  * Examine each descriptor returned. There is normally only
579                  * one or a small number of descriptors.
580                  */
581                 for (j = 4; j <= page_83[3] + 3;
582                         j += page_83[j + 3] + 4) {
583                         retval = check_fill_0x83_id(scsi_dev, &page_83[j],
584                                                     &id_search_list[id_ind],
585                                                     serial, len);
586                         dprintf("%s id desc %d/%d/%d\n", scsi_dev->name,
587                                 id_search_list[id_ind].id_type,
588                                 id_search_list[id_ind].naa_type,
589                                 id_search_list[id_ind].code_set);
590                         if (!retval) {
591                                 dprintf("       used\n");
592                                 return retval;
593                         } else if (retval < 0) {
594                                 dprintf("       failed\n");
595                                 return retval;
596                         } else {
597                                 dprintf("       not used\n");
598                         }
599                 }
600         }
601         return 1;
602 }
603
604 static int do_scsi_page80_inquiry(struct sysfs_device *scsi_dev, int fd,
605                                   char *serial, int max_len)
606 {
607         int retval;
608         int ser_ind;
609         int i;
610         int len;
611         unsigned char buf[256];
612
613         memset(buf, 0, 256);
614         retval = scsi_inquiry(scsi_dev, fd, 1, 0x80, buf, 255);
615         if (retval < 0)
616                 return retval;
617
618         if (buf[1] != 0x80) {
619                 log_message(LOG_WARNING, "%s: Invalid page 0x80\n",
620                             scsi_dev->name);
621                 return 1;
622         }
623
624         len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3];
625         if (max_len < len) {
626                 log_message(LOG_WARNING, "%s: length %d too short - need %d\n",
627                             scsi_dev->name, max_len, len);
628                 return 1;
629         }
630         /*
631          * Prepend 'S' to avoid unlikely collision with page 0x83 vendor
632          * specific type where we prepend '0' + vendor + model.
633          */
634         serial[0] = 'S';
635         ser_ind = prepend_vendor_model(scsi_dev, &serial[1]);
636         if (ser_ind < 0)
637                 return 1;
638         len = buf[3];
639         for (i = 4; i < len + 4; i++, ser_ind++)
640                 serial[ser_ind] = buf[i];
641         return 0;
642 }
643
644 int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
645                      int page_code, char *serial, int len)
646 {
647         unsigned char page0[256];
648         int fd;
649         int ind;
650         int retval;
651
652         if (len > 255) {
653         }
654         memset(serial, 0, len);
655         dprintf("opening %s\n", devname);
656         fd = open(devname, O_RDONLY | O_NONBLOCK);
657         if (fd < 0) {
658                 log_message(LOG_WARNING, "%s: cannot open %s: %s\n",
659                             scsi_dev->name, devname, strerror(errno));
660                 return 1;
661         }
662
663         if (page_code == 0x80) {
664                 if (do_scsi_page80_inquiry(scsi_dev, fd, serial, len)) {
665                         retval = 1;
666                         goto completed;
667                 } else  {
668                         retval = 0;
669                         goto completed;
670                 }
671         } else if (page_code == 0x83) {
672                 if (do_scsi_page83_inquiry(scsi_dev, fd, serial, len)) {
673                         retval = 1;
674                         goto completed;
675                 } else  {
676                         retval = 0;
677                         goto completed;
678                 }
679         } else if (page_code != 0x00) {
680                 log_message(LOG_WARNING, "%s: unsupported page code 0x%d\n",
681                             scsi_dev->name, page_code);
682                 return 1;
683         }
684
685         /*
686          * Get page 0, the page of the pages. By default, try from best to
687          * worst of supported pages: 0x83 then 0x80.
688          */
689         if (do_scsi_page0_inquiry(scsi_dev, fd, page0, 255)) {
690                 /*
691                  * Don't try anything else. Black list if a specific page
692                  * should be used for this vendor+model, or maybe have an
693                  * optional fall-back to page 0x80 or page 0x83.
694                  */
695                 retval = 1;
696                 goto completed;
697         }
698
699         dprintf("%s: Checking page0\n", scsi_dev->name);
700
701         for (ind = 4; ind <= page0[3] + 3; ind++)
702                 if (page0[ind] == 0x83)
703                         if (!do_scsi_page83_inquiry(scsi_dev, fd, serial,
704                                                     len)) {
705                                 /*
706                                  * Success
707                                  */
708                                 retval = 0;
709                                 goto completed;
710                         }
711
712         for (ind = 4; ind <= page0[3] + 3; ind++)
713                 if (page0[ind] == 0x80)
714                         if (!do_scsi_page80_inquiry(scsi_dev, fd, serial,
715                                                     len)) {
716                                 /*
717                                  * Success
718                                  */
719                                 retval = 0;
720                                 goto completed;
721                         }
722         retval = 1;
723 completed:
724         if (close(fd) < 0)
725                 log_message(LOG_WARNING, "%s: close failed: %s\n", 
726                             scsi_dev->name, strerror(errno));
727         return retval;
728 }