2 * device-inq.c: inquire SCSI device information.
4 * Copyright (c) 2010 EMC Corporation, Haiying Tang <Tang_Haiying@emc.com>
7 * This program refers to "SCSI Primary Commands - 3 (SPC-3)
8 * at http://www.t10.org and sg_inq.c in sg3_utils-1.26 for
9 * Linux OS SCSI subsystem, by D. Gilbert.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/types.h>
33 #include <sys/ioctl.h>
34 #include <sys/mount.h>
35 #include <sys/select.h>
36 #include <scsi/scsi.h>
37 #include <scsi/scsi_ioctl.h>
51 #include "device-discovery.h"
53 #define DEF_ALLOC_LEN 255
54 #define MX_ALLOC_LEN (0xc000 + 0x80)
56 static struct bl_serial *bl_create_scsi_string(int len, const char *bytes)
60 s = malloc(sizeof(*s) + len);
62 s->data = (char *)&s[1];
64 memcpy(s->data, bytes, len);
69 static void bl_free_scsi_string(struct bl_serial *str)
75 #define sg_io_ok(io_hdr) \
76 ((((io_hdr).status & 0x7e) == 0) && \
77 ((io_hdr).host_status == 0) && \
78 (((io_hdr).driver_status & 0x0f) == 0))
80 static int sg_timeout = 1 * 1000;
82 static int bldev_inquire_page(int fd, int page, char *buffer, int len)
84 unsigned char cmd[] = { INQUIRY, 0, 0, 0, 0, 0 };
85 unsigned char sense_b[28];
86 struct sg_io_hdr io_hdr;
91 cmd[3] = (unsigned char)((len >> 8) & 0xff);
92 cmd[4] = (unsigned char)(len & 0xff);
94 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
95 io_hdr.interface_id = 'S';
96 io_hdr.cmd_len = sizeof(cmd);
97 io_hdr.mx_sb_len = sizeof(sense_b);
98 io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
99 io_hdr.dxfer_len = len;
100 io_hdr.dxferp = buffer;
102 io_hdr.sbp = sense_b;
103 io_hdr.timeout = sg_timeout;
104 if (ioctl(fd, SG_IO, &io_hdr) < 0)
107 if (sg_io_ok(io_hdr))
112 static int bldev_inquire_pages(int fd, int page, char **buffer)
118 *buffer = calloc(DEF_ALLOC_LEN, sizeof(char));
120 BL_LOG_ERR("%s: Out of memory!\n", __func__);
124 status = bldev_inquire_page(fd, page, *buffer, DEF_ALLOC_LEN);
129 if ((*(*buffer + 1) & 0xff) != page)
132 len = (*(*buffer + 2) << 8) + *(*buffer + 3) + 4;
133 if (len > MX_ALLOC_LEN) {
134 BL_LOG_ERR("SCSI response length too long: %d\n", len);
137 if (len > DEF_ALLOC_LEN) {
138 tmp = realloc(*buffer, len);
140 BL_LOG_ERR("%s: Out of memory!\n", __func__);
145 status = bldev_inquire_page(fd, page, *buffer, len);
154 /* For EMC multipath devices, use VPD page (0xc0) to get status.
155 * For other devices, return ACTIVE for now
157 extern enum bl_path_state_e bldev_read_ap_state(int fd)
161 enum bl_path_state_e ap_state = BL_PATH_STATE_ACTIVE;
163 status = bldev_inquire_pages(fd, 0xc0, &buffer);
167 if (buffer[4] < 0x02)
168 ap_state = BL_PATH_STATE_PASSIVE;
175 struct bl_serial *bldev_read_serial(int fd, const char *filename)
177 struct bl_serial *serial_out = NULL;
180 struct bl_dev_id *dev_root, *dev_id;
181 unsigned int pos, len, current_id = 0;
183 status = bldev_inquire_pages(fd, 0x83, &buffer);
187 dev_root = (struct bl_dev_id *)buffer;
192 while (pos < (len - sizeof(struct bl_dev_id) + sizeof(unsigned char))) {
193 dev_id = (struct bl_dev_id *)&(dev_root->data[pos]);
194 if ((dev_id->ids & 0xf) < current_id)
196 switch (dev_id->ids & 0xf) {
197 /* We process SCSI ID with four ID cases: 0, 1, 2 and 3.
198 * When more than one ID is available, priority is
201 case 2: /* EUI-64 based */
202 if ((dev_id->len != 8) && (dev_id->len != 12) &&
206 /* TODO: NAA validity judgement too complicated,
207 * so just ingore it here.
209 if ((dev_id->type & 0xf) != 1) {
210 BL_LOG_ERR("Binary code_set expected\n");
213 case 0: /* vendor specific */
214 case 1: /* T10 vendor identification */
215 current_id = dev_id->ids & 0xf;
217 bl_free_scsi_string(serial_out);
218 serial_out = bl_create_scsi_string(dev_id->len,
224 pos += (dev_id->len + sizeof(struct bl_dev_id) -
225 sizeof(unsigned char));
229 serial_out = bl_create_scsi_string(strlen(filename), filename);