/* * Copyright (c) 2008, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include "utils.h" #include "api_lib.h" #include "adapt_impl.h" #include "fc_scsi.h" /* * Perform INQUIRY of SCSI-generic device. */ HBA_STATUS sg_issue_inquiry(const char *file, HBA_UINT8 cdb_byte1, HBA_UINT8 cdb_byte2, void *buf, HBA_UINT32 *lenp, HBA_UINT8 *statp, void *sense, HBA_UINT32 *sense_lenp) { struct sg_io_hdr hdr; struct scsi_inquiry cmd; size_t len; HBA_UINT32 slen; int fd; int rc; len = *lenp; slen = *sense_lenp; if (slen > 255) slen = 255; /* must fit in an 8-bit field */ if (len > 255) len = 255; /* sometimes must fit in 8-byte field */ *lenp = 0; *statp = 0; fd = open(file, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: open of %s failed, errno=0x%x\n", __func__, file, errno); return HBA_STATUS_ERROR; } memset(&hdr, 0, sizeof(hdr)); memset(&cmd, 0, sizeof(cmd)); memset(buf, 0, len); cmd.in_op = SCSI_OP_INQUIRY; cmd.in_flags = cdb_byte1; cmd.in_page_code = cdb_byte2; ua_net16_put(&cmd.in_alloc_len, len); /* field may actually be 8 bits */ hdr.interface_id = 'S'; hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.cmd_len = sizeof(cmd); hdr.mx_sb_len = slen; hdr.dxfer_len = len; hdr.dxferp = (unsigned char *) buf; hdr.cmdp = (unsigned char *) &cmd; hdr.sbp = (unsigned char *) sense; hdr.timeout = 3000; /* mS to wait for result */ rc = ioctl(fd, SG_IO, &hdr); if (rc < 0) { rc = errno; fprintf(stderr, "%s: SG_IO error. file %s, errno=0x%x\n", __func__, file, errno); close(fd); return HBA_STATUS_ERROR; } close(fd); *lenp = len - hdr.resid; *sense_lenp = hdr.sb_len_wr; *statp = hdr.status; return HBA_STATUS_OK; } static inline unsigned int sg_get_id_type(struct scsi_inquiry_desc *dp) { return dp->id_type_flags & SCSI_INQT_TYPE_MASK; } /* * Get device ID information for HBA-API. * See the spec. We get the "best" information and leave the rest. * The buffer is left empty if nothing is gotten. */ void sg_get_dev_id(const char *name, char *buf, size_t result_len) { struct scsi_inquiry_dev_id *idp; struct scsi_inquiry_desc *dp; struct scsi_inquiry_desc *best = NULL; char sense[252]; HBA_UINT32 len; HBA_UINT32 slen; u_char scsi_stat; size_t rlen; size_t dlen; unsigned int type; memset(buf, 0, result_len); len = result_len; slen = sizeof(sense); idp = (struct scsi_inquiry_dev_id *) buf; sg_issue_inquiry(name, SCSI_INQF_EVPD, SCSI_INQP_DEV_ID, buf, &len, &scsi_stat, sense, &slen); if (len < sizeof(*idp)) return; if (idp->is_page_code != SCSI_INQP_DEV_ID) return; len -= sizeof(*idp); rlen = net16_get(&idp->is_page_len); if (rlen > len) rlen = len; dp = (struct scsi_inquiry_desc *) (idp + 1); for (; rlen >= sizeof(*dp); rlen -= dlen, dp = (struct scsi_inquiry_desc *) ((char *) dp + dlen)) { dlen = dp->id_designator_len + sizeof(*dp) - sizeof(dp->id_designator[0]); if (dlen > rlen) break; type = sg_get_id_type(dp); if (type > SCSI_DTYPE_NAA) continue; if (best == NULL) best = dp; else if (type == sg_get_id_type(best) && (dp->id_designator_len < best->id_designator_len || (dp->id_designator_len == best->id_designator_len && memcmp(dp->id_designator, best->id_designator, best->id_designator_len) < 0))) { best = dp; } else if (type > sg_get_id_type(best)) best = dp; } if (best) { dp = best; dlen = dp->id_designator_len + sizeof(*dp) - sizeof(dp->id_designator[0]); if (dlen > result_len) dlen = 0; /* can't happen */ else memmove(buf, dp, dlen); /* areas may overlap */ memset(buf + dlen, 0, result_len - dlen); } } /* * Read Capacity for HBA-API. */ HBA_STATUS sg_issue_read_capacity(const char *file, void *resp, HBA_UINT32 *resp_lenp, HBA_UINT8 *statp, void *sense, HBA_UINT32 *sense_lenp) { struct sg_io_hdr hdr; struct scsi_rcap10 cmd; struct scsi_rcap16 cmd_16; size_t len; int fd; int rc; len = *resp_lenp; *resp_lenp = 0; fd = open(file, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: open of %s failed, errno=0x%x\n", __func__, file, errno); return errno; } memset(&hdr, 0, sizeof(hdr)); /* If the response buffer size is enough to * accomodate READ CAPACITY(16) response issue * SCSI READ CAPACITY(16) else issue * SCSI READ CAPACITY(10) */ if (len >= sizeof(struct scsi_rcap16_resp)) { memset(&cmd_16, 0, sizeof(cmd_16)); cmd_16.rc_op = SCSI_OP_SA_IN_16; cmd_16.rc_sa = SCSI_SA_READ_CAP16; ua_net32_put(&cmd_16.rc_alloc_len, len); hdr.cmd_len = sizeof(cmd_16); hdr.cmdp = (unsigned char *) &cmd_16; } else { memset(&cmd, 0, sizeof(cmd)); cmd.rc_op = SCSI_OP_READ_CAP10; hdr.cmd_len = sizeof(cmd); hdr.cmdp = (unsigned char *) &cmd; } hdr.interface_id = 'S'; hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.mx_sb_len = *sense_lenp; hdr.dxfer_len = len; hdr.dxferp = (unsigned char *) resp; hdr.sbp = (unsigned char *) sense; hdr.timeout = UINT_MAX; hdr.timeout = 3000; /* mS to wait for result */ rc = ioctl(fd, SG_IO, &hdr); if (rc < 0) { rc = errno; fprintf(stderr, "%s: SG_IO error. file %s, errno=0x%x\n", __func__, file, errno); close(fd); return HBA_STATUS_ERROR; } close(fd); *resp_lenp = len - hdr.resid; *sense_lenp = hdr.sb_len_wr; *statp = hdr.status; return HBA_STATUS_OK; } /* * Report LUNs for HBA-API. */ HBA_STATUS sg_issue_report_luns(const char *file, void *resp, HBA_UINT32 *resp_lenp, HBA_UINT8 *statp, void *sense, HBA_UINT32 *sense_lenp) { struct sg_io_hdr hdr; struct scsi_report_luns cmd; size_t len; int fd; int rc; len = *resp_lenp; *resp_lenp = 0; fd = open(file, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: open of %s failed, errno=0x%x\n", __func__, file, errno); return errno; } memset(&hdr, 0, sizeof(hdr)); memset(&cmd, 0, sizeof(cmd)); cmd.rl_op = SCSI_OP_REPORT_LUNS; ua_net32_put(&cmd.rl_alloc_len, len); hdr.interface_id = 'S'; hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.cmd_len = sizeof(cmd); hdr.mx_sb_len = *sense_lenp; hdr.dxfer_len = len; hdr.dxferp = (unsigned char *) resp; hdr.cmdp = (unsigned char *) &cmd; hdr.sbp = (unsigned char *) sense; hdr.timeout = UINT_MAX; hdr.timeout = 3000; /* mS to wait for result */ rc = ioctl(fd, SG_IO, &hdr); if (rc < 0) { rc = errno; fprintf(stderr, "%s: SG_IO error. file %s, errno=0x%x\n", __func__, file, errno); close(fd); return HBA_STATUS_ERROR; } close(fd); *resp_lenp = len - hdr.resid; *sense_lenp = hdr.sb_len_wr; *statp = hdr.status; return HBA_STATUS_OK; }