4 * Code related to requesting and getting an id from a scsi device
6 * Copyright (C) IBM Corp. 2003
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.
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.
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
24 #include <sys/types.h>
25 #include <sys/ioctl.h>
36 * Assume built under udev with KLIBC
40 #include <sysfs/libsysfs.h>
46 * A priority based list of id, naa, and binary/ascii for the identifier
47 * descriptor in VPD page 0x83.
49 * Brute force search for a match starting with the first value in the
50 * following id_search_list. This is not a performance issue, since there
51 * is normally one or some small number of descriptors.
53 static const struct scsi_id_search_values id_search_list
[] = {
54 { SCSI_ID_NAA
, SCSI_ID_NAA_IEEE_REG_EXTENDED
, SCSI_ID_BINARY
},
55 { SCSI_ID_NAA
, SCSI_ID_NAA_IEEE_REG_EXTENDED
, SCSI_ID_ASCII
},
56 { SCSI_ID_NAA
, SCSI_ID_NAA_IEEE_REG
, SCSI_ID_BINARY
},
57 { SCSI_ID_NAA
, SCSI_ID_NAA_IEEE_REG
, SCSI_ID_ASCII
},
59 * Devices already exist using NAA values that are now marked
60 * reserved. These should not conflict with other values, or it is
61 * a bug in the device. As long as we find the IEEE extended one
62 * first, we really don't care what other ones are used. Using
63 * don't care here means that a device that returns multiple
64 * non-IEEE descriptors in a random order will get different
67 { SCSI_ID_NAA
, SCSI_ID_NAA_DONT_CARE
, SCSI_ID_BINARY
},
68 { SCSI_ID_NAA
, SCSI_ID_NAA_DONT_CARE
, SCSI_ID_ASCII
},
69 { SCSI_ID_EUI_64
, SCSI_ID_NAA_DONT_CARE
, SCSI_ID_BINARY
},
70 { SCSI_ID_EUI_64
, SCSI_ID_NAA_DONT_CARE
, SCSI_ID_ASCII
},
71 { SCSI_ID_T10_VENDOR
, SCSI_ID_NAA_DONT_CARE
, SCSI_ID_BINARY
},
72 { SCSI_ID_T10_VENDOR
, SCSI_ID_NAA_DONT_CARE
, SCSI_ID_ASCII
},
73 { SCSI_ID_VENDOR_SPECIFIC
, SCSI_ID_NAA_DONT_CARE
, SCSI_ID_BINARY
},
74 { SCSI_ID_VENDOR_SPECIFIC
, SCSI_ID_NAA_DONT_CARE
, SCSI_ID_ASCII
},
77 static const char hex_str
[]="0123456789abcdef";
80 * XXX maybe move all these to an sg_io.c file.
86 * Values returned in the result/status, only the ones used by the code
90 #define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */
92 #define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */
93 #define DID_TIME_OUT 0x03 /* Timed out for some other reason */
95 #define DRIVER_TIMEOUT 0x06
96 #define DRIVER_SENSE 0x08 /* Sense_buffer has been set */
98 /* The following "category" function returns one of the following */
99 #define SG_ERR_CAT_CLEAN 0 /* No errors or other information */
100 #define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */
101 #define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */
102 #define SG_ERR_CAT_TIMEOUT 3
103 #define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */
104 #define SG_ERR_CAT_SENSE 98 /* Something else in the sense buffer */
105 #define SG_ERR_CAT_OTHER 99 /* Some other error/warning */
107 static int sg_err_category_new(int scsi_status
, int msg_status
, int
108 host_status
, int driver_status
, const
109 unsigned char *sense_buffer
, int sb_len
)
114 * XXX change to return only two values - failed or OK.
120 if (!scsi_status
&& !msg_status
&& !host_status
&& !driver_status
)
121 return SG_ERR_CAT_CLEAN
;
123 if ((scsi_status
== SCSI_CHECK_CONDITION
) ||
124 (scsi_status
== SCSI_COMMAND_TERMINATED
) ||
125 ((driver_status
& 0xf) == DRIVER_SENSE
)) {
126 if (sense_buffer
&& (sb_len
> 2)) {
130 if (sense_buffer
[0] & 0x2) {
131 sense_key
= sense_buffer
[1] & 0xf;
132 asc
= sense_buffer
[2];
134 sense_key
= sense_buffer
[2] & 0xf;
135 asc
= (sb_len
> 12) ? sense_buffer
[12] : 0;
138 if (sense_key
== RECOVERED_ERROR
)
139 return SG_ERR_CAT_RECOVERED
;
140 else if (sense_key
== UNIT_ATTENTION
) {
142 return SG_ERR_CAT_MEDIA_CHANGED
;
144 return SG_ERR_CAT_RESET
;
147 return SG_ERR_CAT_SENSE
;
150 if ((host_status
== DID_NO_CONNECT
) ||
151 (host_status
== DID_BUS_BUSY
) ||
152 (host_status
== DID_TIME_OUT
))
153 return SG_ERR_CAT_TIMEOUT
;
155 if (!driver_status
) {
156 if (driver_status
== DRIVER_TIMEOUT
)
157 return SG_ERR_CAT_TIMEOUT
;
159 return SG_ERR_CAT_OTHER
;
162 static int sg_err_category3(struct sg_io_hdr
*hp
)
164 return sg_err_category_new(hp
->status
, hp
->msg_status
,
165 hp
->host_status
, hp
->driver_status
,
166 hp
->sbp
, hp
->sb_len_wr
);
169 static int scsi_dump_sense(struct sysfs_class_device
*scsi_dev
,
170 struct sg_io_hdr
*io
)
172 unsigned char *sense_buffer
;
178 int descriptor_format
;
181 char out_buffer
[256];
186 * Figure out and print the sense key, asc and ascq.
188 * If you want to suppress these for a particular drive model, add
189 * a black list entry in the scsi_id config file.
191 * XXX We probably need to: lookup the sense/asc/ascq in a retry
192 * table, and if found return 1 (after dumping the sense, asc, and
193 * ascq). So, if/when we get something like a power on/reset,
194 * we'll retry the command.
197 dprintf("got check condition\n");
199 sb_len
= io
->sb_len_wr
;
201 log_message(LOG_WARNING
, "%s: sense buffer empty\n",
206 sense_buffer
= io
->sbp
;
207 sense_class
= (sense_buffer
[0] >> 4) & 0x07;
208 code
= sense_buffer
[0] & 0xf;
210 if (sense_class
== 7) {
212 * extended sense data.
214 s
= sense_buffer
[7] + 8;
216 log_message(LOG_WARNING
,
217 "%s: sense buffer too small %d bytes,"
218 " %d bytes too short\n", scsi_dev
->name
,
222 if ((code
== 0x0) || (code
== 0x1)) {
223 descriptor_format
= 0;
224 sense_key
= sense_buffer
[2] & 0xf;
229 log_message(LOG_WARNING
, "%s: sense result too"
234 asc
= sense_buffer
[12];
235 ascq
= sense_buffer
[13];
236 } else if ((code
== 0x2) || (code
== 0x3)) {
237 descriptor_format
= 1;
238 sense_key
= sense_buffer
[1] & 0xf;
239 asc
= sense_buffer
[2];
240 ascq
= sense_buffer
[3];
242 log_message(LOG_WARNING
,
243 "%s: invalid sense code 0x%x\n",
244 scsi_dev
->name
, code
);
247 log_message(LOG_WARNING
,
248 "%s: sense key 0x%x ASC 0x%x ASCQ 0x%x\n",
249 scsi_dev
->name
, sense_key
, asc
, ascq
);
252 log_message(LOG_WARNING
,
253 "%s: sense buffer too small %d bytes, %d bytes too short\n",
254 scsi_dev
->name
, sb_len
, 4 - sb_len
);
258 if (sense_buffer
[0] < 15)
259 log_message(LOG_WARNING
, "%s: old sense key: 0x%x\n",
260 scsi_dev
->name
, sense_buffer
[0] & 0x0f);
262 log_message(LOG_WARNING
, "%s: sense = %2x %2x\n",
263 scsi_dev
->name
, sense_buffer
[0],
265 log_message(LOG_WARNING
,
266 "%s: non-extended sense class %d code 0x%0x ",
267 scsi_dev
->name
, sense_class
, code
);
272 for (i
= 0, j
= 0; (i
< s
) && (j
< 254); i
++) {
273 dprintf("i %d, j %d\n", i
, j
);
274 out_buffer
[j
++] = hex_str
[(sense_buffer
[i
] & 0xf0) >> 4];
275 out_buffer
[j
++] = hex_str
[sense_buffer
[i
] & 0x0f];
276 out_buffer
[j
++] = ' ';
278 out_buffer
[j
] = '\0';
279 log_message(LOG_WARNING
, "%s: sense dump:\n", scsi_dev
->name
);
280 log_message(LOG_WARNING
, "%s: %s\n", scsi_dev
->name
, out_buffer
);
286 static int scsi_dump(struct sysfs_class_device
*scsi_dev
, struct sg_io_hdr
*io
)
288 if (!io
->status
&& !io
->host_status
&& !io
->msg_status
&&
289 !io
->driver_status
) {
291 * Impossible, should not be called.
293 log_message(LOG_WARNING
, "%s: called with no error\n",
298 log_message(LOG_WARNING
, "%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x\n",
299 scsi_dev
->name
, io
->driver_status
, io
->host_status
,
300 io
->msg_status
, io
->status
);
301 if (io
->status
== SCSI_CHECK_CONDITION
)
302 return scsi_dump_sense(scsi_dev
, io
);
307 static int scsi_inquiry(struct sysfs_class_device
*scsi_dev
, int fd
,
308 unsigned char evpd
, unsigned char page
, unsigned
309 char *buf
, unsigned int buflen
)
311 unsigned char inq_cmd
[INQUIRY_CMDLEN
] =
312 { INQUIRY_CMD
, evpd
, page
, 0, buflen
, 0 };
313 unsigned char sense
[SENSE_BUFF_LEN
];
314 struct sg_io_hdr io_hdr
;
317 unsigned char *buffer
;
318 int retry
= 3; /* rather random */
321 log_message(LOG_WARNING
, "buflen %d too long\n", buflen
);
324 inq
= malloc(OFFSET
+ sizeof (inq_cmd
) + 512);
325 memset(inq
, 0, OFFSET
+ sizeof (inq_cmd
) + 512);
326 buffer
= inq
+ OFFSET
;
329 memset(&io_hdr
, 0, sizeof(struct sg_io_hdr
));
330 io_hdr
.interface_id
= 'S';
331 io_hdr
.cmd_len
= sizeof(inq_cmd
);
332 io_hdr
.mx_sb_len
= sizeof(sense
);
333 io_hdr
.dxfer_direction
= SG_DXFER_FROM_DEV
;
334 io_hdr
.dxfer_len
= buflen
;
335 io_hdr
.dxferp
= buffer
;
336 io_hdr
.cmdp
= inq_cmd
;
338 io_hdr
.timeout
= DEF_TIMEOUT
;
340 if (ioctl(fd
, SG_IO
, &io_hdr
) < 0) {
341 log_message(LOG_WARNING
, "%s ioctl failed: %s\n",
342 scsi_dev
->name
, strerror(errno
));
346 retval
= sg_err_category3(&io_hdr
);
349 case SG_ERR_CAT_CLEAN
:
350 case SG_ERR_CAT_RECOVERED
:
355 retval
= scsi_dump(scsi_dev
, &io_hdr
);
359 * XXX where is the length checked? That is, was our request
360 * buffer long enough?
365 memcpy(buf
, buffer
, retval
);
366 } else if (retval
> 0) {
368 dprintf("%s: Retrying ...\n", scsi_dev
->name
);
379 * XXX maybe move all these to an sg_io.c file.
384 static int do_scsi_page0_inquiry(struct sysfs_class_device
*scsi_dev
, int fd
,
385 char *buffer
, int len
)
388 char vendor
[MAX_ATTR_LEN
];
390 memset(buffer
, 0, len
);
391 retval
= scsi_inquiry(scsi_dev
, fd
, 1, 0x0, buffer
, len
);
395 if (buffer
[1] != 0) {
396 log_message(LOG_WARNING
, "%s: page 0 not available.\n",
400 if (buffer
[3] > len
) {
401 log_message(LOG_WARNING
, "%s: page 0 buffer too long %d",
402 scsi_dev
->name
, buffer
[3]);
407 * Following check is based on code once included in the 2.5.x
410 * Some ill behaved devices return the standard inquiry here
411 * rather than the evpd data, snoop the data to verify.
413 if (buffer
[3] > MODEL_LENGTH
) {
415 * If the vendor id appears in the page assume the page is
418 if (sysfs_get_attr(scsi_dev
->path
, "vendor", vendor
,
420 log_message(LOG_WARNING
,
421 "%s: cannot get model attribute\n",
425 if (!strncmp(&buffer
[VENDOR_LENGTH
], vendor
, VENDOR_LENGTH
)) {
426 log_message(LOG_WARNING
, "%s invalid page0 data\n",
435 * The caller checks that serial is long enough to include the vendor +
438 static int prepend_vendor_model(struct sysfs_class_device
*scsi_dev
,
441 char attr
[MAX_ATTR_LEN
];
444 if (sysfs_get_attr(scsi_dev
->path
, "vendor", attr
, MAX_ATTR_LEN
)) {
445 log_message(LOG_WARNING
, "%s: cannot get vendor attribute\n",
449 strncpy(serial
, attr
, VENDOR_LENGTH
);
450 ind
= strlen(serial
) - 1;
452 * Remove sysfs added newlines.
454 if (serial
[ind
] == '\n')
457 if (sysfs_get_attr(scsi_dev
->path
, "model", attr
, MAX_ATTR_LEN
)) {
458 log_message(LOG_WARNING
, "%s: cannot get model attribute\n",
462 strncat(serial
, attr
, MODEL_LENGTH
);
463 ind
= strlen(serial
) - 1;
464 if (serial
[ind
] == '\n')
470 * This is not a complete check, since we are using strncat/cpy
471 * above, ind will never be too large.
473 if (ind
!= (VENDOR_LENGTH
+ MODEL_LENGTH
)) {
474 log_message(LOG_WARNING
, "%s: expected length %d, got length %d\n",
475 scsi_dev
->name
, (VENDOR_LENGTH
+ MODEL_LENGTH
),
483 * check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill
486 static int check_fill_0x83_id(struct sysfs_class_device
*scsi_dev
,
488 const struct scsi_id_search_values
*id_search
,
489 char *serial
, int max_len
)
494 * ASSOCIATION must be with the device (value 0)
496 if ((page_83
[1] & 0x30) != 0)
499 if ((page_83
[1] & 0x0f) != id_search
->id_type
)
503 * Possibly check NAA sub-type.
505 if ((id_search
->naa_type
!= SCSI_ID_NAA_DONT_CARE
) &&
506 (id_search
->naa_type
!= (page_83
[4] & 0xf0) >> 4))
510 * Check for matching code set - ASCII or BINARY.
512 if ((page_83
[0] & 0x0f) != id_search
->code_set
)
516 * page_83[3]: identifier length
519 if ((page_83
[0] & 0x0f) != SCSI_ID_ASCII
)
521 * If not ASCII, use two bytes for each binary value.
526 * Add one byte for the NUL termination, and one for the id_type.
529 if (id_search
->id_type
== SCSI_ID_VENDOR_SPECIFIC
)
530 len
+= VENDOR_LENGTH
+ MODEL_LENGTH
;
533 log_message(LOG_WARNING
, "%s: length %d too short - need %d\n",
534 scsi_dev
->name
, max_len
, len
);
538 serial
[0] = hex_str
[id_search
->id_type
];
541 * For SCSI_ID_VENDOR_SPECIFIC prepend the vendor and model before
542 * the id since it is not unique across all vendors and models,
543 * this differs from SCSI_ID_T10_VENDOR, where the vendor is
544 * included in the identifier.
546 if (id_search
->id_type
== SCSI_ID_VENDOR_SPECIFIC
)
547 if (prepend_vendor_model(scsi_dev
, &serial
[1]) < 0) {
548 dprintf("prepend failed\n");
552 i
= 4; /* offset to the start of the identifier */
554 if ((page_83
[0] & 0x0f) == SCSI_ID_ASCII
) {
558 while (i
< (4 + page_83
[3]))
559 serial
[j
++] = page_83
[i
++];
562 * Binary descriptor, convert to ASCII, using two bytes of
563 * ASCII for each byte in the page_83.
565 while (i
< (4 + page_83
[3])) {
566 serial
[j
++] = hex_str
[(page_83
[i
] & 0xf0) >> 4];
567 serial
[j
++] = hex_str
[page_83
[i
] & 0x0f];
574 static int do_scsi_page83_inquiry(struct sysfs_class_device
*scsi_dev
, int fd
,
575 char *serial
, int len
)
579 unsigned char page_83
[256];
581 memset(page_83
, 0, 256);
582 retval
= scsi_inquiry(scsi_dev
, fd
, 1, 0x83, page_83
, 255);
586 if (page_83
[1] != 0x83) {
587 log_message(LOG_WARNING
, "%s: Invalid page 0x83\n",
593 * XXX Some devices (IBM 3542) return all spaces for an identifier if
594 * the LUN is not actually configured. This leads to identifers of
599 * Search for a match in the prioritized id_search_list.
602 id_ind
< sizeof(id_search_list
)/sizeof(id_search_list
[0]);
605 * Examine each descriptor returned. There is normally only
606 * one or a small number of descriptors.
608 for (j
= 4; j
<= page_83
[3] + 3;
609 j
+= page_83
[j
+ 3] + 4) {
610 retval
= check_fill_0x83_id(scsi_dev
, &page_83
[j
],
611 &id_search_list
[id_ind
],
613 dprintf("%s id desc %d/%d/%d\n", scsi_dev
->name
,
614 id_search_list
[id_ind
].id_type
,
615 id_search_list
[id_ind
].naa_type
,
616 id_search_list
[id_ind
].code_set
);
620 } else if (retval
< 0) {
621 dprintf(" failed\n");
624 dprintf(" not used\n");
631 static int do_scsi_page80_inquiry(struct sysfs_class_device
*scsi_dev
, int fd
,
632 char *serial
, int max_len
)
638 unsigned char buf
[256];
641 retval
= scsi_inquiry(scsi_dev
, fd
, 1, 0x80, buf
, 255);
645 if (buf
[1] != 0x80) {
646 log_message(LOG_WARNING
, "%s: Invalid page 0x80\n",
651 len
= 1 + VENDOR_LENGTH
+ MODEL_LENGTH
+ buf
[3];
653 log_message(LOG_WARNING
, "%s: length %d too short - need %d\n",
654 scsi_dev
->name
, max_len
, len
);
658 * Prepend 'S' to avoid unlikely collision with page 0x83 vendor
659 * specific type where we prepend '0' + vendor + model.
662 ser_ind
= prepend_vendor_model(scsi_dev
, &serial
[1]);
666 for (i
= 4; i
< len
+ 4; i
++, ser_ind
++)
667 serial
[ser_ind
] = buf
[i
];
671 int scsi_get_serial (struct sysfs_class_device
*scsi_dev
, const char *devname
,
672 int page_code
, char *serial
, int len
)
674 unsigned char page0
[256];
681 memset(serial
, 0, len
);
682 dprintf("opening %s\n", devname
);
683 fd
= open(devname
, O_RDONLY
);
685 log_message(LOG_WARNING
, "%s cannot open %s: %s\n",
686 scsi_dev
->name
, devname
, strerror(errno
));
690 if (page_code
== 0x80) {
691 if (do_scsi_page80_inquiry(scsi_dev
, fd
, serial
, len
)) {
698 } else if (page_code
== 0x83) {
699 if (do_scsi_page83_inquiry(scsi_dev
, fd
, serial
, len
)) {
706 } else if (page_code
!= 0x00) {
707 log_message(LOG_WARNING
, "%s unsupported page code 0x%d\n",
708 scsi_dev
->name
, page_code
);
713 * Get page 0, the page of the pages. By default, try from best to
714 * worst of supported pages: 0x83 then 0x80.
716 if (do_scsi_page0_inquiry(scsi_dev
, fd
, page0
, 255)) {
718 * Don't try anything else. Black list if a specific page
719 * should be used for this vendor+model, or maybe have an
720 * optional fall-back to page 0x80 or page 0x83.
726 dprintf("%s: Checking page0\n", scsi_dev
->name
);
728 for (ind
= 4; ind
<= page0
[3] + 3; ind
++)
729 if (page0
[ind
] == 0x83)
730 if (!do_scsi_page83_inquiry(scsi_dev
, fd
, serial
,
739 for (ind
= 4; ind
<= page0
[3] + 3; ind
++)
740 if (page0
[ind
] == 0x80)
741 if (!do_scsi_page80_inquiry(scsi_dev
, fd
, serial
,
752 log_message(LOG_WARNING
, "close failed: %s", strerror(errno
));