blob: 943adca54424396c75a3611c88dd1c855853d833 [file] [log] [blame]
/*
* Copyright (c) 2006-2014 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "sg_lib.h"
#include "sg_cmds_basic.h"
/* This utility program was originally written for the Linux OS SCSI subsystem.
This program fetches Vital Product Data (VPD) pages from the given
device and outputs it as directed. VPD pages are obtained via a
SCSI INQUIRY command. Most of the data in this program is obtained
from the SCSI SPC-4 document at http://www.t10.org .
*/
static const char * version_str = "0.79 20140215"; /* spc4r36 + sbc3r35 */
/* And with sbc3r35, vale Mark Evans */
void svpd_enumerate_vendor(void);
int svpd_search_vendor_vpds(int num_vpd);
int svpd_decode_vendor(int sg_fd, int num_vpd, int subvalue, int maxlen,
int do_hex, int do_raw, int do_long, int do_quiet,
int verbose);
const struct svpd_values_name_t * svpd_find_vendor_by_acron(const char * ap);
/* standard VPD pages, in ascending page number order */
#define VPD_SUPPORTED_VPDS 0x0
#define VPD_UNIT_SERIAL_NUM 0x80
#define VPD_IMP_OP_DEF 0x81 /* obsolete in SPC-2 */
#define VPD_ASCII_OP_DEF 0x82 /* obsolete in SPC-2 */
#define VPD_DEVICE_ID 0x83
#define VPD_SOFTW_INF_ID 0x84
#define VPD_MAN_NET_ADDR 0x85
#define VPD_EXT_INQ 0x86
#define VPD_MODE_PG_POLICY 0x87
#define VPD_SCSI_PORTS 0x88
#define VPD_ATA_INFO 0x89
#define VPD_POWER_CONDITION 0x8a
#define VPD_DEVICE_CONSTITUENTS 0x8b
#define VPD_CFA_PROFILE_INFO 0x8c
#define VPD_POWER_CONSUMPTION 0x8d
#define VPD_3PARTY_COPY 0x8f /* 3PC, XCOPY, SPC-4, SBC-3 */
#define VPD_PROTO_LU 0x90
#define VPD_PROTO_PORT 0x91
#define VPD_BLOCK_LIMITS 0xb0 /* SBC-3 */
#define VPD_SA_DEV_CAP 0xb0 /* SSC-3 */
#define VPD_OSD_INFO 0xb0 /* OSD */
#define VPD_BLOCK_DEV_CHARS 0xb1 /* SBC-3 */
#define VPD_MAN_ASS_SN 0xb1 /* SSC-3, ADC-2 */
#define VPD_SECURITY_TOKEN 0xb1 /* OSD */
#define VPD_TA_SUPPORTED 0xb2 /* SSC-3 */
#define VPD_LB_PROVISIONING 0xb2 /* SBC-3 */
#define VPD_REFERRALS 0xb3 /* SBC-3 */
#define VPD_AUTOMATION_DEV_SN 0xb3 /* SSC-3 */
#define VPD_DTDE_ADDRESS 0xb4 /* SSC-4 */
#define VPD_NO_RATHER_STD_INQ -2 /* request for standard inquiry */
/* Device identification VPD page associations */
#define VPD_ASSOC_LU 0
#define VPD_ASSOC_TPORT 1
#define VPD_ASSOC_TDEVICE 2
/* values for selection one or more associations (2**vpd_assoc),
except _AS_IS */
#define VPD_DI_SEL_LU 1
#define VPD_DI_SEL_TPORT 2
#define VPD_DI_SEL_TARGET 4
#define VPD_DI_SEL_AS_IS 32
#define DEF_ALLOC_LEN 252
#define MX_ALLOC_LEN (0xc000 + 0x80)
#define VPD_ATA_INFO_LEN 572
/* This structure is a duplicate of one of the same name in sg_vpd_vendor.c .
Take care that both have the same fields (and types). */
struct svpd_values_name_t {
int value; /* VPD page number */
int subvalue; /* to differentiate if value+pdt are not unique */
int pdt; /* peripheral device type id, -1 is the default */
/* (all or not applicable) value */
const char * acron;
const char * name;
};
static unsigned char rsp_buff[MX_ALLOC_LEN + 2];
static int decode_dev_ids(const char * print_if_found, unsigned char * buff,
int len, int m_assoc, int m_desig_type,
int m_code_set, int long_out, int quiet);
static void decode_transport_id(const char * leadin, unsigned char * ucp,
int len);
static struct option long_options[] = {
{"enumerate", no_argument, 0, 'e'},
{"help", no_argument, 0, 'h'},
{"hex", no_argument, 0, 'H'},
{"ident", no_argument, 0, 'i'},
{"long", no_argument, 0, 'l'},
{"maxlen", required_argument, 0, 'm'},
{"page", required_argument, 0, 'p'},
{"quiet", no_argument, 0, 'q'},
{"raw", no_argument, 0, 'r'},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
{0, 0, 0, 0},
};
/* arranged in alphabetical order by acronym */
static struct svpd_values_name_t standard_vpd_pg[] = {
{VPD_ATA_INFO, 0, -1, "ai", "ATA information (SAT)"},
{VPD_ASCII_OP_DEF, 0, -1, "aod",
"ASCII implemented operating definition (obsolete)"},
{VPD_AUTOMATION_DEV_SN, 0, 1, "adsn", "Automation device serial "
"number (SSC)"},
{VPD_BLOCK_LIMITS, 0, 0, "bl", "Block limits (SBC)"},
{VPD_BLOCK_DEV_CHARS, 0, 0, "bdc", "Block device characteristics "
"(SBC)"},
{VPD_CFA_PROFILE_INFO, 0, 0, "cfa", "CFA profile information"},
{VPD_DEVICE_CONSTITUENTS, 0, -1, "dc", "Device constituents"},
{VPD_DEVICE_ID, 0, -1, "di", "Device identification"},
{VPD_DEVICE_ID, VPD_DI_SEL_AS_IS, -1, "di_asis", "Like 'di' "
"but designators ordered as found"},
{VPD_DEVICE_ID, VPD_DI_SEL_LU, -1, "di_lu", "Device identification, "
"lu only"},
{VPD_DEVICE_ID, VPD_DI_SEL_TPORT, -1, "di_port", "Device "
"identification, target port only"},
{VPD_DEVICE_ID, VPD_DI_SEL_TARGET, -1, "di_target", "Device "
"identification, target device only"},
{VPD_DTDE_ADDRESS, 0, 1, "dtde",
"Data transfer device element address (SSC)"},
{VPD_EXT_INQ, 0, -1, "ei", "Extended inquiry data"},
{VPD_IMP_OP_DEF, 0, -1, "iod",
"Implemented operating definition (obsolete)"},
{VPD_LB_PROVISIONING, 0, 0, "lbpv",
"Logical block provisioning (SBC)"},
{VPD_MAN_ASS_SN, 0, 1, "mas",
"Manufacturer assigned serial number (SSC)"},
{VPD_MAN_ASS_SN, 0, 0x12, "masa",
"Manufacturer assigned serial number (ADC)"},
{VPD_MAN_NET_ADDR, 0, -1, "mna", "Management network addresses"},
{VPD_MODE_PG_POLICY, 0, -1, "mpp", "Mode page policy"},
{VPD_OSD_INFO, 0, 0x11, "oi", "OSD information"},
{VPD_POWER_CONDITION, 0, -1, "pc", "Power condition"},
{VPD_POWER_CONSUMPTION, 0, -1, "psm", "Power consumption"},
{VPD_PROTO_LU, 0, -1, "pslu", "Protocol-specific logical unit "
"information"},
{VPD_PROTO_PORT, 0, -1, "pspo", "Protocol-specific port information"},
{VPD_REFERRALS, 0, 0, "ref", "Referrals (SBC)"},
{VPD_SA_DEV_CAP, 0, 1, "sad",
"Sequential access device capabilities (SSC)"},
{VPD_SOFTW_INF_ID, 0, -1, "sii", "Software interface identification"},
{VPD_NO_RATHER_STD_INQ, 0, -1, "sinq", "Standard inquiry response"},
{VPD_UNIT_SERIAL_NUM, 0, -1, "sn", "Unit serial number"},
{VPD_SCSI_PORTS, 0, -1, "sp", "SCSI ports"},
{VPD_SECURITY_TOKEN, 0, 0x11, "st", "Security token (OSD)"},
{VPD_SUPPORTED_VPDS, 0, -1, "sv", "Supported VPD pages"},
{VPD_TA_SUPPORTED, 0, 1, "tas", "TapeAlert supported flags (SSC)"},
{VPD_3PARTY_COPY, 0, -1, "tpc", "Third party copy"},
{0, 0, 0, NULL, NULL},
};
#ifdef __GNUC__
static int pr2serr(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2)));
#else
static int pr2serr(const char * fmt, ...);
#endif
static int
pr2serr(const char * fmt, ...)
{
va_list args;
int n;
va_start(args, fmt);
n = vfprintf(stderr, fmt, args);
va_end(args);
return n;
}
static void
usage()
{
pr2serr("Usage: sg_vpd [--enumerate] [--help] [--hex] [--ident] "
"[--long]\n"
" [--maxlen=LEN] [--page=PG] [--quiet] [--raw] "
"[--verbose]\n"
" [--version] DEVICE\n");
pr2serr(" where:\n"
" --enumerate|-e enumerate known VPD pages names (ignore "
"DEVICE),\n"
" can be used with --page=num to search\n"
" --help|-h output this usage message then exit\n"
" --hex|-H output page in ASCII hexadecimal\n"
" --ident|-i output device identification VPD page, "
"twice for\n"
" short logical unit designator (equiv: "
"'-qp di_lu')\n"
" --long|-l perform extra decoding\n"
" --maxlen=LEN|-m LEN max response length (allocation "
"length in cdb)\n"
" (def: 0 -> 252 bytes)\n"
" --page=PG|-p PG fetch VPD page where PG is an "
"acronym, or a decimal\n"
" number unless hex indicator "
"is given (e.g. '0x83')\n"
" --quiet|-q suppress some output when decoding\n"
" --raw|-r output page in binary\n"
" --verbose|-v increase verbosity\n"
" --version|-V print version string and exit\n\n"
"Fetch Vital Product Data (VPD) page using SCSI INQUIRY. To "
"list available\npages use '-e'. And '-p -1' yields the "
"standard INQUIRY response.\n");
}
static const struct svpd_values_name_t *
sdp_get_vpd_detail(int page_num, int subvalue, int pdt)
{
const struct svpd_values_name_t * vnp;
int sv, ty;
sv = (subvalue < 0) ? 1 : 0;
ty = (pdt < 0) ? 1 : 0;
for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
if ((page_num == vnp->value) &&
(sv || (subvalue == vnp->subvalue)) &&
(ty || (pdt == vnp->pdt)))
return vnp;
}
if (! ty)
return sdp_get_vpd_detail(page_num, subvalue, -1);
if (! sv)
return sdp_get_vpd_detail(page_num, -1, -1);
return NULL;
}
static const struct svpd_values_name_t *
sdp_find_vpd_by_acron(const char * ap)
{
const struct svpd_values_name_t * vnp;
for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
if (0 == strcmp(vnp->acron, ap))
return vnp;
}
return NULL;
}
static void
enumerate_vpds(int standard, int vendor)
{
const struct svpd_values_name_t * vnp;
if (standard) {
for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
if (vnp->name) {
if (vnp->value < 0)
printf(" %-10s -1 %s\n", vnp->acron, vnp->name);
else
printf(" %-10s 0x%02x %s\n", vnp->acron, vnp->value,
vnp->name);
}
}
}
if (vendor)
svpd_enumerate_vendor();
}
static int
search_standard_vpds(int num_vpd)
{
const struct svpd_values_name_t * vnp;
int matches;
for (vnp = standard_vpd_pg, matches = 0; vnp->acron; ++vnp) {
if ((num_vpd == vnp->value) && vnp->name) {
if (0 == matches)
printf("Matching standard VPD pages:\n");
++matches;
if (vnp->value < 0)
printf(" %-10s -1 %s\n", vnp->acron, vnp->name);
else
printf(" %-10s 0x%02x %s\n", vnp->acron, vnp->value,
vnp->name);
}
}
return matches;
}
static void
dStrRaw(const char * str, int len)
{
int k;
for (k = 0 ; k < len; ++k)
printf("%c", str[k]);
}
/* Assume index is less than 16 */
const char * sg_ansi_version_arr[] =
{
"no conformance claimed",
"SCSI-1", /* obsolete, ANSI X3.131-1986 */
"SCSI-2", /* obsolete, ANSI X3.131-1994 */
"SPC", /* withdrawn */
"SPC-2",
"SPC-3",
"SPC-4",
"reserved [7h]",
"ecma=1, [8h]",
"ecma=1, [9h]",
"ecma=1, [Ah]",
"ecma=1, [Bh]",
"reserved [Ch]",
"reserved [Dh]",
"reserved [Eh]",
"reserved [Fh]",
};
static void
decode_std_inq(unsigned char * b, int len, int verbose)
{
int pqual, n;
if (len < 4)
return;
pqual = (b[0] & 0xe0) >> 5;
if (0 == pqual)
printf("standard INQUIRY:\n");
else if (1 == pqual)
printf("standard INQUIRY: [qualifier indicates no connected "
"LU]\n");
else if (3 == pqual)
printf("standard INQUIRY: [qualifier indicates not capable "
"of supporting LU]\n");
else
printf("standard INQUIRY: [reserved or vendor specific "
"qualifier [%d]]\n", pqual);
printf(" PQual=%d Device_type=%d RMB=%d LU_CONG=%d version=0x%02x ",
pqual, b[0] & 0x1f, !!(b[1] & 0x80), !!(b[1] & 0x40),
(unsigned int)b[2]);
printf(" [%s]\n", sg_ansi_version_arr[b[2] & 0xf]);
printf(" [AERC=%d] [TrmTsk=%d] NormACA=%d HiSUP=%d "
" Resp_data_format=%d\n",
!!(b[3] & 0x80), !!(b[3] & 0x40), !!(b[3] & 0x20),
!!(b[3] & 0x10), b[3] & 0x0f);
if (len < 5)
return;
n = b[4] + 5;
if (verbose)
pr2serr(">> requested %d bytes, %d bytes available\n", len, n);
printf(" SCCS=%d ACC=%d TPGS=%d 3PC=%d Protect=%d ",
!!(b[5] & 0x80), !!(b[5] & 0x40), ((b[5] & 0x30) >> 4),
!!(b[5] & 0x08), !!(b[5] & 0x01));
printf(" [BQue=%d]\n EncServ=%d ", !!(b[6] & 0x80), !!(b[6] & 0x40));
if (b[6] & 0x10)
printf("MultiP=1 (VS=%d) ", !!(b[6] & 0x20));
else
printf("MultiP=0 ");
printf("[MChngr=%d] [ACKREQQ=%d] Addr16=%d\n [RelAdr=%d] ",
!!(b[6] & 0x08), !!(b[6] & 0x04), !!(b[6] & 0x01),
!!(b[7] & 0x80));
printf("WBus16=%d Sync=%d [Linked=%d] [TranDis=%d] ",
!!(b[7] & 0x20), !!(b[7] & 0x10), !!(b[7] & 0x08),
!!(b[7] & 0x04));
printf("CmdQue=%d\n", !!(b[7] & 0x02));
if (len < 36)
return;
printf(" Vendor_identification: %.8s\n", b + 8);
printf(" Product_identification: %.16s\n", b + 16);
printf(" Product_revision_level: %.4s\n", b + 32);
}
static const char * assoc_arr[] =
{
"Addressed logical unit",
"Target port", /* that received request; unless SCSI ports VPD */
"Target device that contains addressed lu",
"Reserved [0x3]",
};
static void
decode_id_vpd(unsigned char * buff, int len, int subvalue, int do_long,
int do_quiet)
{
int m_a, m_d, m_cs;
if (len < 4) {
pr2serr("Device identification VPD page length too short=%d\n", len);
return;
}
m_a = -1;
m_d = -1;
m_cs = -1;
if (0 == subvalue) {
decode_dev_ids(assoc_arr[VPD_ASSOC_LU], buff + 4, len - 4,
VPD_ASSOC_LU, m_d, m_cs, do_long, do_quiet);
decode_dev_ids(assoc_arr[VPD_ASSOC_TPORT], buff + 4, len - 4,
VPD_ASSOC_TPORT, m_d, m_cs, do_long, do_quiet);
decode_dev_ids(assoc_arr[VPD_ASSOC_TDEVICE], buff + 4, len - 4,
VPD_ASSOC_TDEVICE, m_d, m_cs, do_long, do_quiet);
} else if (VPD_DI_SEL_AS_IS == subvalue)
decode_dev_ids(NULL, buff + 4, len - 4, m_a, m_d, m_cs, do_long,
do_quiet);
else {
if (VPD_DI_SEL_LU & subvalue)
decode_dev_ids(assoc_arr[VPD_ASSOC_LU], buff + 4, len - 4,
VPD_ASSOC_LU, m_d, m_cs, do_long, do_quiet);
if (VPD_DI_SEL_TPORT & subvalue)
decode_dev_ids(assoc_arr[VPD_ASSOC_TPORT], buff + 4, len - 4,
VPD_ASSOC_TPORT, m_d, m_cs, do_long, do_quiet);
if (VPD_DI_SEL_TARGET & subvalue)
decode_dev_ids(assoc_arr[VPD_ASSOC_TDEVICE], buff + 4, len - 4,
VPD_ASSOC_TDEVICE, m_d, m_cs, do_long, do_quiet);
}
}
static const char * network_service_type_arr[] =
{
"unspecified",
"storage configuration service",
"diagnostics",
"status",
"logging",
"code download",
"copy service",
"administrative configuration service",
"reserved[0x8]", "reserved[0x9]",
"reserved[0xa]", "reserved[0xb]", "reserved[0xc]", "reserved[0xd]",
"reserved[0xe]", "reserved[0xf]", "reserved[0x10]", "reserved[0x11]",
"reserved[0x12]", "reserved[0x13]", "reserved[0x14]", "reserved[0x15]",
"reserved[0x16]", "reserved[0x17]", "reserved[0x18]", "reserved[0x19]",
"reserved[0x1a]", "reserved[0x1b]", "reserved[0x1c]", "reserved[0x1d]",
"reserved[0x1e]", "reserved[0x1f]",
};
/* VPD_MAN_NET_ADDR */
static void
decode_net_man_vpd(unsigned char * buff, int len, int do_hex)
{
int k, bump, na_len;
unsigned char * ucp;
if ((1 == do_hex) || (do_hex > 2)) {
dStrHex((const char *)buff, len, (1 == do_hex) ? 1 : -1);
return;
}
if (len < 4) {
pr2serr("Management network addresses VPD page length too short=%d\n",
len);
return;
}
len -= 4;
ucp = buff + 4;
for (k = 0; k < len; k += bump, ucp += bump) {
printf(" %s, Service type: %s\n",
assoc_arr[(ucp[0] >> 5) & 0x3],
network_service_type_arr[ucp[0] & 0x1f]);
na_len = (ucp[2] << 8) + ucp[3];
bump = 4 + na_len;
if ((k + bump) > len) {
pr2serr("Management network addresses VPD page, short "
"descriptor length=%d, left=%d\n", bump, (len - k));
return;
}
if (na_len > 0) {
if (do_hex > 1) {
printf(" Network address:\n");
dStrHex((const char *)(ucp + 4), na_len, 0);
} else
printf(" %s\n", ucp + 4);
}
}
}
static const char * mode_page_policy_arr[] =
{
"shared",
"per target port",
"per initiator port",
"per I_T nexus",
};
/* VPD_MODE_PG_POLICY */
static void
decode_mode_policy_vpd(unsigned char * buff, int len, int do_hex)
{
int k, bump;
unsigned char * ucp;
if ((1 == do_hex) || (do_hex > 2)) {
dStrHex((const char *)buff, len, (1 == do_hex) ? 1 : -1);
return;
}
if (len < 4) {
pr2serr("Mode page policy VPD page length too short=%d\n", len);
return;
}
len -= 4;
ucp = buff + 4;
for (k = 0; k < len; k += bump, ucp += bump) {
bump = 4;
if ((k + bump) > len) {
pr2serr("Mode page policy VPD page, short "
"descriptor length=%d, left=%d\n", bump, (len - k));
return;
}
if (do_hex > 1)
dStrHex((const char *)ucp, 4, 1);
else {
printf(" Policy page code: 0x%x", (ucp[0] & 0x3f));
if (ucp[1])
printf(", subpage code: 0x%x\n", ucp[1]);
else
printf("\n");
printf(" MLUS=%d, Policy: %s\n", !!(ucp[2] & 0x80),
mode_page_policy_arr[ucp[2] & 0x3]);
}
}
}
/* VPD_SCSI_PORTS */
static void
decode_scsi_ports_vpd(unsigned char * buff, int len, int do_hex, int do_long,
int do_quiet)
{
int k, bump, rel_port, ip_tid_len, tpd_len;
unsigned char * ucp;
if ((1 == do_hex) || (do_hex > 2)) {
dStrHex((const char *)buff, len, (1 == do_hex) ? 1 : -1);
return;
}
if (len < 4) {
pr2serr("SCSI Ports VPD page length too short=%d\n", len);
return;
}
len -= 4;
ucp = buff + 4;
for (k = 0; k < len; k += bump, ucp += bump) {
rel_port = (ucp[2] << 8) + ucp[3];
printf("Relative port=%d\n", rel_port);
ip_tid_len = (ucp[6] << 8) + ucp[7];
bump = 8 + ip_tid_len;
if ((k + bump) > len) {
pr2serr("SCSI Ports VPD page, short descriptor "
"length=%d, left=%d\n", bump, (len - k));
return;
}
if (ip_tid_len > 0) {
if (do_hex > 1) {
printf(" Initiator port transport id:\n");
dStrHex((const char *)(ucp + 8), ip_tid_len, 1);
} else
decode_transport_id(" ", ucp + 8, ip_tid_len);
}
tpd_len = (ucp[bump + 2] << 8) + ucp[bump + 3];
if ((k + bump + tpd_len + 4) > len) {
pr2serr("SCSI Ports VPD page, short descriptor(tgt) "
"length=%d, left=%d\n", bump, (len - k));
return;
}
if (tpd_len > 0) {
if (do_hex > 1) {
printf(" Target port descriptor(s):\n");
dStrHex((const char *)(ucp + bump + 4), tpd_len, 1);
} else {
if ((0 == do_quiet) || (ip_tid_len > 0))
printf(" Target port descriptor(s):\n");
decode_dev_ids("SCSI Ports", ucp + bump + 4, tpd_len,
VPD_ASSOC_TPORT, -1, -1, do_long, do_quiet);
}
}
bump += tpd_len + 4;
}
}
static const char * code_set_arr[] =
{
"Reserved [0x0]",
"Binary",
"ASCII",
"UTF-8",
"Reserved [0x4]", "Reserved [0x5]", "Reserved [0x6]", "Reserved [0x7]",
"Reserved [0x8]", "Reserved [0x9]", "Reserved [0xa]", "Reserved [0xb]",
"Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]",
};
static const char * desig_type_arr[] =
{
"vendor specific [0x0]",
"T10 vendor identification",
"EUI-64 based",
"NAA",
"Relative target port",
"Target port group", /* spc4r09: _primary_ target port group */
"Logical unit group",
"MD5 logical unit identifier",
"SCSI name string",
"Protocol specific port identifier", /* spc4r36 */
"Reserved [0xa]", "Reserved [0xb]",
"Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]",
};
/* Prints outs an abridged set of device identification designators
selected by association, designator type and/or code set. */
static int
decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc,
int m_desig_type, int m_code_set)
{
int m, p_id, c_set, piv, desig_type, i_len, naa, off, u;
int assoc, is_sas, rtp;
const unsigned char * ucp;
const unsigned char * ip;
unsigned char sas_tport_addr[8];
rtp = 0;
memset(sas_tport_addr, 0, sizeof(sas_tport_addr));
off = -1;
if (buff[2] != 0) {
if (m_assoc != VPD_ASSOC_LU)
return 0;
ip = buff;
p_id = 0;
c_set = 1;
assoc = VPD_ASSOC_LU;
piv = 0;
is_sas = 0;
desig_type = 3;
i_len = 16;
off = 16;
goto decode;
}
while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, m_desig_type,
m_code_set)) == 0) {
ucp = buff + off;
i_len = ucp[3];
if ((off + i_len + 4) > len) {
pr2serr(" VPD page error: designator length longer than\n"
" remaining response length=%d\n", (len - off));
return SG_LIB_CAT_MALFORMED;
}
ip = ucp + 4;
p_id = ((ucp[0] >> 4) & 0xf);
c_set = (ucp[0] & 0xf);
piv = ((ucp[1] & 0x80) ? 1 : 0);
is_sas = (piv && (6 == p_id)) ? 1 : 0;
assoc = ((ucp[1] >> 4) & 0x3);
desig_type = (ucp[1] & 0xf);
decode:
switch (desig_type) {
case 0: /* vendor specific */
break;
case 1: /* T10 vendor identification */
break;
case 2: /* EUI-64 based */
if ((8 != i_len) && (12 != i_len) && (16 != i_len))
pr2serr(" << expect 8, 12 and 16 byte "
"EUI, got %d>>\n", i_len);
printf("0x");
for (m = 0; m < i_len; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("\n");
break;
case 3: /* NAA */
if (1 != c_set) {
pr2serr(" << unexpected code set %d for NAA>>\n", c_set);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
naa = (ip[0] >> 4) & 0xff;
if ((naa < 2) || (naa > 6) || (4 == naa)) {
pr2serr(" << unexpected NAA [0x%x]>>\n", naa);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
if (2 == naa) { /* NAA IEEE extended */
if (8 != i_len) {
pr2serr(" << unexpected NAA 2 identifier "
"length: 0x%x>>\n", i_len);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
printf("0x");
for (m = 0; m < 8; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("\n");
} else if ((3 == naa) || (5 == naa)) {
/* NAA=3 Locally assigned; NAA=5 IEEE Registered */
if (8 != i_len) {
pr2serr(" << unexpected NAA 3 or 5 "
"identifier length: 0x%x>>\n", i_len);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
if ((0 == is_sas) || (1 != assoc)) {
printf("0x");
for (m = 0; m < 8; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("\n");
} else if (rtp) {
printf("0x");
for (m = 0; m < 8; ++m)
printf("%02x", (unsigned int)ip[m]);
printf(",0x%x\n", rtp);
rtp = 0;
} else {
if (sas_tport_addr[0]) {
printf("0x");
for (m = 0; m < 8; ++m)
printf("%02x", (unsigned int)sas_tport_addr[m]);
printf("\n");
}
memcpy(sas_tport_addr, ip, sizeof(sas_tport_addr));
}
} else if (6 == naa) { /* NAA IEEE registered extended */
if (16 != i_len) {
pr2serr(" << unexpected NAA 6 identifier length: "
"0x%x>>\n", i_len);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
printf("0x");
for (m = 0; m < 16; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("\n");
}
break;
case 4: /* Relative target port */
if ((0 == is_sas) || (1 != c_set) || (1 != assoc) || (4 != i_len))
break;
rtp = ((ip[2] << 8) | ip[3]);
if (sas_tport_addr[0]) {
printf("0x");
for (m = 0; m < 8; ++m)
printf("%02x", (unsigned int)sas_tport_addr[m]);
printf(",0x%x\n", rtp);
memset(sas_tport_addr, 0, sizeof(sas_tport_addr));
rtp = 0;
}
break;
case 5: /* (primary) Target port group */
break;
case 6: /* Logical unit group */
break;
case 7: /* MD5 logical unit identifier */
break;
case 8: /* SCSI name string */
if (3 != c_set) {
pr2serr(" << expected UTF-8 code_set>>\n");
dStrHexErr((const char *)ip, i_len, 0);
break;
}
/* does %s print out UTF-8 ok??
* Seems to depend on the locale. Looks ok here with my
* locale setting: en_AU.UTF-8
*/
printf("%s\n", (const char *)ip);
break;
case 9: /* PCIe routing ID */
break;
default: /* reserved */
break;
}
}
if (sas_tport_addr[0]) {
printf("0x");
for (m = 0; m < 8; ++m)
printf("%02x", (unsigned int)sas_tport_addr[m]);
printf("\n");
}
if (-2 == u) {
pr2serr("VPD page error: short designator around offset %d\n", off);
return SG_LIB_CAT_MALFORMED;
}
return 0;
}
static void
decode_designation_descriptor(const unsigned char * ip, int i_len,
int p_id, int c_set, int piv, int assoc,
int desig_type, int long_out, int print_assoc)
{
int m, ci_off, c_id, d_id, naa;
int vsi, k;
uint64_t vsei;
uint64_t id_ext;
char b[64];
if (print_assoc)
printf(" %s:\n", assoc_arr[assoc]);
printf(" designator type: %s, code set: %s\n",
desig_type_arr[desig_type], code_set_arr[c_set]);
if (piv && ((1 == assoc) || (2 == assoc)))
printf(" transport: %s\n",
sg_get_trans_proto_str(p_id, sizeof(b), b));
/* printf(" associated with the %s\n", assoc_arr[assoc]); */
switch (desig_type) {
case 0: /* vendor specific */
k = 0;
if ((1 == c_set) || (2 == c_set)) { /* ASCII or UTF-8 */
for (k = 0; (k < i_len) && isprint(ip[k]); ++k)
;
if (k >= i_len)
k = 1;
}
if (k)
printf(" vendor specific: %.*s\n", i_len, ip);
else {
pr2serr(" vendor specific:\n");
dStrHexErr((const char *)ip, i_len, 0);
}
break;
case 1: /* T10 vendor identification */
printf(" vendor id: %.8s\n", ip);
if (i_len > 8) {
if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */
printf(" vendor specific: %.*s\n", i_len - 8, ip + 8);
} else {
printf(" vendor specific: 0x");
for (m = 8; m < i_len; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("\n");
}
}
break;
case 2: /* EUI-64 based */
if (! long_out) {
if ((8 != i_len) && (12 != i_len) && (16 != i_len)) {
pr2serr(" << expect 8, 12 and 16 byte EUI, got %d>>\n",
i_len);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
printf(" 0x");
for (m = 0; m < i_len; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("\n");
break;
}
printf(" EUI-64 based %d byte identifier\n", i_len);
if (1 != c_set) {
pr2serr(" << expected binary code_set (1)>>\n");
dStrHexErr((const char *)ip, i_len, 0);
break;
}
ci_off = 0;
if (16 == i_len) {
ci_off = 8;
id_ext = 0;
for (m = 0; m < 8; ++m) {
if (m > 0)
id_ext <<= 8;
id_ext |= ip[m];
}
printf(" Identifier extension: 0x%" PRIx64 "\n", id_ext);
} else if ((8 != i_len) && (12 != i_len)) {
pr2serr(" << can only decode 8, 12 and 16 byte ids>>\n");
dStrHexErr((const char *)ip, i_len, 0);
break;
}
c_id = ((ip[ci_off] << 16) | (ip[ci_off + 1] << 8) |
ip[ci_off + 2]);
printf(" IEEE Company_id: 0x%x\n", c_id);
vsei = 0;
for (m = 0; m < 5; ++m) {
if (m > 0)
vsei <<= 8;
vsei |= ip[ci_off + 3 + m];
}
printf(" Vendor Specific Extension Identifier: 0x%" PRIx64
"\n", vsei);
if (12 == i_len) {
d_id = (((unsigned int)ip[8] << 24) | (ip[9] << 16) |
(ip[10] << 8) | ip[11]);
printf(" Directory ID: 0x%x\n", d_id);
}
break;
case 3: /* NAA <n> */
if (1 != c_set) {
pr2serr(" << unexpected code set %d for NAA>>\n", c_set);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
naa = (ip[0] >> 4) & 0xff;
switch (naa) {
case 2: /* NAA 2: IEEE Extended */
if (8 != i_len) {
pr2serr(" << unexpected NAA 2 identifier length: "
"0x%x>>\n", i_len);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
d_id = (((ip[0] & 0xf) << 8) | ip[1]);
c_id = ((ip[2] << 16) | (ip[3] << 8) | ip[4]);
vsi = ((ip[5] << 16) | (ip[6] << 8) | ip[7]);
if (long_out) {
printf(" NAA 2, vendor specific identifier A: "
"0x%x\n", d_id);
printf(" IEEE Company_id: 0x%x\n", c_id);
printf(" vendor specific identifier B: 0x%x\n", vsi);
printf(" [0x");
for (m = 0; m < 8; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("]\n");
}
printf(" 0x");
for (m = 0; m < 8; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("\n");
break;
case 3: /* NAA 3: Locally assigned */
if (8 != i_len) {
pr2serr(" << unexpected NAA 3 identifier length: "
"0x%x>>\n", i_len);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
if (long_out)
printf(" NAA 3, Locally assigned value:\n");
printf(" 0x");
for (m = 0; m < 8; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("\n");
break;
case 5: /* NAA 5: IEEE Registered */
if (8 != i_len) {
pr2serr(" << unexpected NAA 5 identifier length: "
"0x%x>>\n", i_len);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
if (long_out) {
c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
(ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
vsei = ip[3] & 0xf;
for (m = 1; m < 5; ++m) {
vsei <<= 8;
vsei |= ip[3 + m];
}
printf(" NAA 5, IEEE Company_id: 0x%x\n", c_id);
printf(" Vendor Specific Identifier: 0x%" PRIx64
"\n", vsei);
printf(" [0x");
for (m = 0; m < 8; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("]\n");
} else {
printf(" 0x");
for (m = 0; m < 8; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("\n");
}
break;
case 6: /* NAA 6: IEEE Registered extended */
if (16 != i_len) {
pr2serr(" << unexpected NAA 6 identifier length: "
"0x%x>>\n", i_len);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
(ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
vsei = ip[3] & 0xf;
for (m = 1; m < 5; ++m) {
vsei <<= 8;
vsei |= ip[3 + m];
}
if (long_out) {
printf(" NAA 6, IEEE Company_id: 0x%x\n", c_id);
printf(" Vendor Specific Identifier: 0x%" PRIx64
"\n", vsei);
vsei = 0;
for (m = 0; m < 8; ++m) {
if (m > 0)
vsei <<= 8;
vsei |= ip[8 + m];
}
printf(" Vendor Specific Identifier Extension: "
"0x%" PRIx64 "\n", vsei);
printf(" [0x");
for (m = 0; m < 16; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("]\n");
} else {
printf(" 0x");
for (m = 0; m < 16; ++m)
printf("%02x", (unsigned int)ip[m]);
printf("\n");
}
break;
default:
pr2serr(" << unexpected NAA [0x%x]>>\n", naa);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
break;
case 4: /* Relative target port */
if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
pr2serr(" << expected binary code_set, target port "
"association, length 4>>\n");
dStrHexErr((const char *)ip, i_len, 0);
break;
}
d_id = ((ip[2] << 8) | ip[3]);
printf(" Relative target port: 0x%x\n", d_id);
break;
case 5: /* (primary) Target port group */
if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
pr2serr(" << expected binary code_set, target port "
"association, length 4>>\n");
dStrHexErr((const char *)ip, i_len, 0);
break;
}
d_id = ((ip[2] << 8) | ip[3]);
printf(" Target port group: 0x%x\n", d_id);
break;
case 6: /* Logical unit group */
if ((1 != c_set) || (0 != assoc) || (4 != i_len)) {
pr2serr(" << expected binary code_set, logical unit "
"association, length 4>>\n");
dStrHexErr((const char *)ip, i_len, 0);
break;
}
d_id = ((ip[2] << 8) | ip[3]);
printf(" Logical unit group: 0x%x\n", d_id);
break;
case 7: /* MD5 logical unit identifier */
if ((1 != c_set) || (0 != assoc)) {
pr2serr(" << expected binary code_set, logical unit "
"association>>\n");
dStrHexErr((const char *)ip, i_len, 0);
break;
}
printf(" MD5 logical unit identifier:\n");
dStrHex((const char *)ip, i_len, 0);
break;
case 8: /* SCSI name string */
if (3 != c_set) {
pr2serr(" << expected UTF-8 code_set>>\n");
dStrHexErr((const char *)ip, i_len, 0);
break;
}
printf(" SCSI name string:\n");
/* does %s print out UTF-8 ok??
* Seems to depend on the locale. Looks ok here with my
* locale setting: en_AU.UTF-8
*/
printf(" %s\n", (const char *)ip);
break;
case 9: /* Protocol specific port identifier */
/* added in spc4r36, PIV must be set, proto_id indicates */
/* whether UAS (USB) or SOP (PCIe) or ... */
if (! piv)
printf(" >>>> Protocol specific port identifier "
"expects protocol\n"
" identifier to be valid and it is not\n");
if (TPROTO_UAS == p_id) {
printf(" USB device address: 0x%x\n", 0x7f & ip[0]);
printf(" USB interface number: 0x%x\n", ip[2]);
} else if (TPROTO_SOP == p_id) {
printf(" PCIe routing ID, bus number: 0x%x\n", ip[0]);
printf(" function number: 0x%x\n", ip[1]);
printf(" [or device number: 0x%x, function number: "
"0x%x]\n", (0x1f & (ip[1] >> 3)), 0x7 & ip[1]);
} else
pr2serr(" >>>> unexpected protocol indentifier: %s\n"
" with Protocol specific port "
"identifier\n",
sg_get_trans_proto_str(p_id, sizeof(b), b));
break;
default: /* reserved */
pr2serr(" reserved designator=0x%x\n", desig_type);
dStrHexErr((const char *)ip, i_len, 0);
break;
}
}
/* Prints outs device identification designators selected by association,
designator type and/or code set. */
static int
decode_dev_ids(const char * print_if_found, unsigned char * buff, int len,
int m_assoc, int m_desig_type, int m_code_set, int long_out,
int quiet)
{
int assoc, i_len, c_set, piv, p_id, desig_type;
int printed, off, u;
const unsigned char * ucp;
if (quiet)
return decode_dev_ids_quiet(buff, len, m_assoc, m_desig_type,
m_code_set);
if ( buff[2] != 0 ) {
if (m_assoc == VPD_ASSOC_LU)
decode_designation_descriptor( buff, 16, 0, 1, 0, m_assoc, 3,
long_out, 0);
return 0;
}
off = -1;
printed = 0;
while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, m_desig_type,
m_code_set)) == 0) {
ucp = buff + off;
i_len = ucp[3];
if ((off + i_len + 4) > len) {
pr2serr(" VPD page error: designator length longer than\n"
" remaining response length=%d\n", (len - off));
return SG_LIB_CAT_MALFORMED;
}
assoc = ((ucp[1] >> 4) & 0x3);
if (print_if_found && (0 == printed)) {
printed = 1;
printf(" %s:\n", print_if_found);
}
if (NULL == print_if_found)
printf(" %s:\n", assoc_arr[assoc]);
p_id = ((ucp[0] >> 4) & 0xf);
c_set = (ucp[0] & 0xf);
piv = ((ucp[1] & 0x80) ? 1 : 0);
desig_type = (ucp[1] & 0xf);
decode_designation_descriptor(ucp + 4, i_len, p_id, c_set, piv, assoc,
desig_type, long_out, 0);
}
if (-2 == u) {
pr2serr("VPD page error: short designator around offset %d\n", off);
return SG_LIB_CAT_MALFORMED;
}
return 0;
}
/* Transport IDs are initiator port identifiers, typically other than the
initiator port issuing a SCSI command. */
static void
decode_transport_id(const char * leadin, unsigned char * ucp, int len)
{
int format_code, proto_id, num, j, k;
uint64_t ull;
int bump;
for (k = 0, bump= 24; k < len; k += bump, ucp += bump) {
if ((len < 24) || (0 != (len % 4)))
printf("%sTransport Id short or not multiple of 4 "
"[length=%d]:\n", leadin, len);
else
printf("%sTransport Id of initiator:\n", leadin);
format_code = ((ucp[0] >> 6) & 0x3);
proto_id = (ucp[0] & 0xf);
switch (proto_id) {
case TPROTO_FCP: /* Fibre channel */
printf("%s FCP-2 World Wide Name:\n", leadin);
if (0 != format_code)
printf("%s [Unexpected format code: %d]\n", leadin,
format_code);
dStrHex((const char *)&ucp[8], 8, -1);
bump = 24;
break;
case TPROTO_SPI: /* Scsi Parallel Interface */
printf("%s Parallel SCSI initiator SCSI address: 0x%x\n",
leadin, ((ucp[2] << 8) | ucp[3]));
if (0 != format_code)
printf("%s [Unexpected format code: %d]\n", leadin,
format_code);
printf("%s relative port number (of corresponding target): "
"0x%x\n", leadin, ((ucp[6] << 8) | ucp[7]));
bump = 24;
break;
case TPROTO_SSA:
printf("%s SSA (transport id not defined):\n", leadin);
printf("%s format code: %d\n", leadin, format_code);
dStrHex((const char *)ucp, ((len > 24) ? 24 : len), 0);
bump = 24;
break;
case TPROTO_1394: /* IEEE 1394 */
printf("%s IEEE 1394 EUI-64 name:\n", leadin);
if (0 != format_code)
printf("%s [Unexpected format code: %d]\n", leadin,
format_code);
dStrHex((const char *)&ucp[8], 8, -1);
bump = 24;
break;
case TPROTO_SRP:
printf("%s RDMA initiator port identifier:\n", leadin);
if (0 != format_code)
printf("%s [Unexpected format code: %d]\n", leadin,
format_code);
dStrHex((const char *)&ucp[8], 16, -1);
bump = 24;
break;
case TPROTO_ISCSI:
printf("%s iSCSI ", leadin);
num = ((ucp[2] << 8) | ucp[3]);
if (0 == format_code)
printf("name: %.*s\n", num, &ucp[4]);
else if (1 == format_code)
printf("world wide unique port id: %.*s\n", num, &ucp[4]);
else {
pr2serr(" [Unexpected format code: %d]\n", format_code);
dStrHexErr((const char *)ucp, num + 4, 0);
}
bump = (((num + 4) < 24) ? 24 : num + 4);
break;
case TPROTO_SAS:
ull = 0;
for (j = 0; j < 8; ++j) {
if (j > 0)
ull <<= 8;
ull |= ucp[4 + j];
}
printf("%s SAS address: 0x%" PRIx64 "\n", leadin, ull);
if (0 != format_code)
printf("%s [Unexpected format code: %d]\n", leadin,
format_code);
bump = 24;
break;
case TPROTO_ADT:
printf("%s ADT:\n", leadin);
printf("%s format code: %d\n", leadin, format_code);
dStrHex((const char *)ucp, ((len > 24) ? 24 : len), 0);
bump = 24;
break;
case TPROTO_ATA: /* ATA/ATAPI */
printf("%s ATAPI:\n", leadin);
printf("%s format code: %d\n", leadin, format_code);
dStrHex((const char *)ucp, ((len > 24) ? 24 : len), 0);
bump = 24;
break;
case TPROTO_UAS:
printf("%s UAS:\n", leadin);
printf("%s format code: %d\n", leadin, format_code);
dStrHex((const char *)ucp, ((len > 24) ? 24 : len), 0);
bump = 24;
break;
case TPROTO_SOP:
printf("%s SOP ", leadin);
num = ((ucp[2] << 8) | ucp[3]);
if (0 == format_code)
printf("Routing ID: 0x%x\n", num);
else {
pr2serr(" [Unexpected format code: %d]\n", format_code);
dStrHexErr((const char *)ucp, 24, 0);
}
bump = 24;
break;
case TPROTO_NONE:
pr2serr("%s No specified protocol\n", leadin);
/* dStrHexErr((const char *)ucp, ((len > 24) ? 24 : len), 0); */
bump = 24;
break;
default:
pr2serr("%s unknown protocol id=0x%x format_code=%d\n", leadin,
proto_id, format_code);
dStrHexErr((const char *)ucp, ((len > 24) ? 24 : len), 0);
bump = 24;
break;
}
}
}
/* VPD_EXT_INQ Extended Inquiry VPD */
static void
decode_x_inq_vpd(unsigned char * b, int len, int do_hex, int do_long,
int protect)
{
int n;
if (len < 7) {
pr2serr("Extended INQUIRY data VPD page length too short=%d\n", len);
return;
}
if (do_hex) {
dStrHex((const char *)b, len, (1 == do_hex) ? 0 : -1);
return;
}
if (do_long) {
n = (b[4] >> 6) & 0x3;
printf(" ACTIVATE_MICROCODE=%d", n);
if (1 == n)
printf(" [before final WRITE BUFFER]\n");
else if (2 == n)
printf(" [after power on or hard reset]\n");
else
printf("\n");
n = (b[4] >> 3) & 0x7;
printf(" SPT=%d", n);
if (protect) {
switch (n)
{
case 0:
printf(" [protection type 1 supported]\n");
break;
case 1:
printf(" [protection types 1 and 2 supported]\n");
break;
case 2:
printf(" [protection type 2 supported]\n");
break;
case 3:
printf(" [protection types 1 and 3 supported]\n");
break;
case 4:
printf(" [protection type 3 supported]\n");
break;
case 5:
printf(" [protection types 2 and 3 supported]\n");
break;
case 7:
printf(" [protection types 1, 2 and 3 supported]\n");
break;
default:
printf("\n");
break;
}
} else
printf("\n");
printf(" GRD_CHK=%d\n", !!(b[4] & 0x4));
printf(" APP_CHK=%d\n", !!(b[4] & 0x2));
printf(" REF_CHK=%d\n", !!(b[4] & 0x1));
printf(" UASK_SUP=%d\n", !!(b[5] & 0x20));
printf(" GROUP_SUP=%d\n", !!(b[5] & 0x10));
printf(" PRIOR_SUP=%d\n", !!(b[5] & 0x8));
printf(" HEADSUP=%d\n", !!(b[5] & 0x4));
printf(" ORDSUP=%d\n", !!(b[5] & 0x2));
printf(" SIMPSUP=%d\n", !!(b[5] & 0x1));
printf(" WU_SUP=%d\n", !!(b[6] & 0x8));
printf(" CRD_SUP=%d\n", !!(b[6] & 0x4));
printf(" NV_SUP=%d\n", !!(b[6] & 0x2));
printf(" V_SUP=%d\n", !!(b[6] & 0x1));
printf(" P_I_I_SUP=%d\n", !!(b[7] & 0x10));
printf(" LUICLR=%d\n", !!(b[7] & 0x1));
printf(" R_SUP=%d\n", !!(b[8] & 0x10));
printf(" CBCS=%d\n", !!(b[8] & 0x1));
printf(" Multi I_T nexus microcode download=%d\n", b[9] & 0xf);
printf(" Extended self-test completion minutes=%d\n",
(b[10] << 8) + b[11]);
printf(" POA_SUP=%d\n", !!(b[12] & 0x80)); /* spc4r32 */
printf(" HRA_SUP=%d\n", !!(b[12] & 0x40)); /* spc4r32 */
printf(" VSA_SUP=%d\n", !!(b[12] & 0x20)); /* spc4r32 */
printf(" Maximum supported sense data length=%d\n",
b[13]); /* spc4r34 */
return;
}
printf(" ACTIVATE_MICROCODE=%d SPT=%d GRD_CHK=%d APP_CHK=%d "
"REF_CHK=%d\n", ((b[4] >> 6) & 0x3), ((b[4] >> 3) & 0x7),
!!(b[4] & 0x4), !!(b[4] & 0x2), !!(b[4] & 0x1));
printf(" UASK_SUP=%d GROUP_SUP=%d PRIOR_SUP=%d HEADSUP=%d ORDSUP=%d "
"SIMPSUP=%d\n", !!(b[5] & 0x20), !!(b[5] & 0x10), !!(b[5] & 0x8),
!!(b[5] & 0x4), !!(b[5] & 0x2), !!(b[5] & 0x1));
printf(" WU_SUP=%d CRD_SUP=%d NV_SUP=%d V_SUP=%d\n",
!!(b[6] & 0x8), !!(b[6] & 0x4), !!(b[6] & 0x2), !!(b[6] & 0x1));
printf(" P_I_I_SUP=%d LUICLR=%d R_SUP=%d CBCS=%d\n",
!!(b[7] & 0x10), !!(b[7] & 0x1), !!(b[8] & 0x10), !!(b[8] & 0x1));
printf(" Multi I_T nexus microcode download=%d\n", b[9] & 0xf);
printf(" Extended self-test completion minutes=%d\n",
(b[10] << 8) + b[11]); /* spc4r27 */
printf(" POA_SUP=%d HRA_SUP=%d VSA_SUP=%d\n", /* spc4r32 */
!!(b[12] & 0x80), !!(b[12] & 0x40), !!(b[12] & 0x20));
printf(" Maximum supported sense data length=%d\n", b[13]); /* spc4r34 */
}
/* VPD_SOFTW_INF_ID */
static void
decode_softw_inf_id(unsigned char * buff, int len, int do_hex)
{
if (do_hex) {
dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
return;
}
len -= 4;
buff += 4;
for ( ; len > 5; len -= 6, buff += 6) {
printf(" IEEE Company_id: 0x%06x, vendor specific extension "
"id: 0x%06x\n", (buff[0] << 16) | (buff[1] << 8) | buff[2],
(buff[3] << 16) | (buff[4] << 8) | buff[5]);
}
}
/* VPD_ATA_INFO */
static void
decode_ata_info_vpd(unsigned char * buff, int len, int do_long, int do_hex)
{
char b[80];
int num, is_be;
const char * cp;
if (len < 36) {
pr2serr("ATA information VPD page length too short=%d\n", len);
return;
}
if (do_hex && (2 != do_hex)) {
dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
return;
}
memcpy(b, buff + 8, 8);
b[8] = '\0';
printf(" SAT Vendor identification: %s\n", b);
memcpy(b, buff + 16, 16);
b[16] = '\0';
printf(" SAT Product identification: %s\n", b);
memcpy(b, buff + 32, 4);
b[4] = '\0';
printf(" SAT Product revision level: %s\n", b);
if (len < 56)
return;
if (do_long) {
printf(" Signature (Device to host FIS):\n");
dStrHex((const char *)buff + 36, 20, 0);
}
if (len < 60)
return;
is_be = sg_is_big_endian();
if ((0xec == buff[56]) || (0xa1 == buff[56])) {
cp = (0xa1 == buff[56]) ? "PACKET " : "";
printf(" ATA command IDENTIFY %sDEVICE response summary:\n", cp);
num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20,
is_be, b);
b[num] = '\0';
printf(" model: %s\n", b);
num = sg_ata_get_chars((const unsigned short *)(buff + 60), 10, 10,
is_be, b);
b[num] = '\0';
printf(" serial number: %s\n", b);
num = sg_ata_get_chars((const unsigned short *)(buff + 60), 23, 4,
is_be, b);
b[num] = '\0';
printf(" firmware revision: %s\n", b);
if (do_long)
printf(" ATA command IDENTIFY %sDEVICE response in hex:\n", cp);
} else if (do_long)
printf(" ATA command 0x%x got following response:\n",
(unsigned int)buff[56]);
if (len < 572)
return;
if (2 == do_hex)
dStrHex((const char *)(buff + 60), 512, 0);
else if (do_long)
dWordHex((const unsigned short *)(buff + 60), 256, 0, is_be);
}
/* VPD_POWER_CONDITION */
static void
decode_power_condition(unsigned char * buff, int len, int do_hex)
{
if (len < 18) {
pr2serr("Power condition VPD page length too short=%d\n", len);
return;
}
if (do_hex) {
dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
return;
}
printf(" Standby_y=%d Standby_z=%d Idle_c=%d Idle_b=%d Idle_a=%d\n",
!!(buff[4] & 0x2), !!(buff[4] & 0x1),
!!(buff[5] & 0x4), !!(buff[5] & 0x2), !!(buff[5] & 0x1));
printf(" Stopped condition recovery time (ms) %d\n",
(buff[6] << 8) + buff[7]);
printf(" Standby_z condition recovery time (ms) %d\n",
(buff[8] << 8) + buff[9]);
printf(" Standby_y condition recovery time (ms) %d\n",
(buff[10] << 8) + buff[11]);
printf(" Idle_a condition recovery time (ms) %d\n",
(buff[12] << 8) + buff[13]);
printf(" Idle_b condition recovery time (ms) %d\n",
(buff[14] << 8) + buff[15]);
printf(" Idle_c condition recovery time (ms) %d\n",
(buff[16] << 8) + buff[17]);
}
static const char * power_unit_arr[] =
{
"Gigawatts",
"Megawatts",
"Kilowatts",
"Watts",
"Milliwatts",
"Microwatts",
"Unit reserved",
"Unit reserved",
};
/* VPD_POWER_CONSUMPTION */
static void
decode_power_consumption_vpd(unsigned char * buff, int len, int do_hex)
{
int k, bump;
unsigned char * ucp;
unsigned int value;
if ((1 == do_hex) || (do_hex > 2)) {
dStrHex((const char *)buff, len, (1 == do_hex) ? 1 : -1);
return;
}
if (len < 4) {
pr2serr("Power consumption VPD page length too short=%d\n", len);
return;
}
len -= 4;
ucp = buff + 4;
for (k = 0; k < len; k += bump, ucp += bump) {
bump = 4;
if ((k + bump) > len) {
pr2serr("Power consumption VPD page, short descriptor "
"length=%d, left=%d\n", bump, (len - k));
return;
}
if (do_hex > 1)
dStrHex((const char *)ucp, 4, 1);
else {
value = (ucp[2] << 8) + ucp[3];
printf(" Power consumption identifier: 0x%x", ucp[0]);
if (value >= 1000 && (ucp[1] & 0x7) > 0)
printf(" Maximum power consumption: %d.%03d %s\n",
value / 1000, value % 1000,
power_unit_arr[(ucp[1] & 0x7) - 1]);
else
printf(" Maximum power consumption: %d %s\n",
value, power_unit_arr[ucp[1] & 0x7]);
}
}
}
/* This is xcopy(LID4) related: "ROD" == Representation Of Data
* Used by VPD_3PARTY_COPY */
static void
decode_rod_descriptor(const unsigned char * buff, int len)
{
const unsigned char * ucp = buff;
int k, bump, j;
uint64_t ull;
for (k = 0; k < len; k += bump, ucp += bump) {
bump = (ucp[2] << 8) + ucp[3];
switch (ucp[0]) {
case 0:
/* Block ROD device type specific descriptor */
printf(" Optimal block ROD length granularity: %d\n",
(ucp[6] << 8) + ucp[7]);
ull = 0;
for (j = 0; j < 8; j++) {
if (j > 0)
ull <<= 8;
ull |= ucp[8 + j];
}
printf(" Maximum Bytes in block ROD: %" PRIu64 "\n", ull);
ull = 0;
for (j = 0; j < 8; j++) {
if (j > 0)
ull <<= 8;
ull |= ucp[16 + j];
}
printf(" Optimal Bytes in block ROD transfer: %" PRIu64 "\n",
ull);
ull = 0;
for (j = 0; j < 8; j++) {
if (j > 0)
ull <<= 8;
ull |= ucp[24 + j];
}
printf(" Optimal Bytes to token per segment: %" PRIu64 "\n",
ull);
ull = 0;
for (j = 0; j < 8; j++) {
if (j > 0)
ull <<= 8;
ull |= ucp[32 + j];
}
printf(" Optimal Bytes from token per segment:"
" %" PRIu64 "\n", ull);
break;
case 1:
/* Stream ROD device type specific descriptor */
ull = 0;
for (j = 0; j < 8; j++) {
if (j > 0)
ull <<= 8;
ull |= ucp[8 + j];
}
printf(" Maximum Bytes in stream ROD: %" PRIu64 "\n", ull);
ull = 0;
for (j = 0; j < 8; j++) {
if (j > 0)
ull <<= 8;
ull |= ucp[16 + j];
}
printf(" Optimal Bytes in stream ROD transfer:"
" %" PRIu64 "\n", ull);
break;
case 3:
/* Copy manager ROD device type specific descriptor */
ull = 0;
for (j = 0; j < 8; j++) {
if (j > 0)
ull <<= 8;
ull |= ucp[8 + j];
}
printf(" Maximum Bytes in processor ROD:"
" %" PRIu64 "\n", ull);
ull = 0;
for (j = 0; j < 8; j++) {
if (j > 0)
ull <<= 8;
ull |= ucp[16 + j];
}
printf(" Optimal Bytes in processor ROD transfer:"
" %" PRIu64 "\n", ull);
break;
default:
printf(" Unhandled descriptor (format %d, device type %d)\n",
ucp[0] >> 5, ucp[0] & 0x1F);
break;
}
}
}
/* VPD_3PARTY_COPY [3PC, third party copy] */
static void
decode_3party_copy_vpd(unsigned char * buff, int len, int do_hex, int verbose)
{
int j, k, bump, desc_type, desc_len, sa_len;
unsigned int u;
const unsigned char * ucp;
uint64_t ull;
char b[80];
if (len < 4) {
pr2serr("Third-party Copy VPD page length too short=%d\n", len);
return;
}
len -= 4;
ucp = buff + 4;
for (k = 0; k < len; k += bump, ucp += bump) {
desc_type = (ucp[0] << 8) + ucp[1];
desc_len = (ucp[2] << 8) + ucp[3];
if (verbose)
printf("Descriptor type=%d, len %d\n", desc_type, desc_len);
bump = 4 + desc_len;
if ((k + bump) > len) {
pr2serr("Third-party Copy VPD page, short descriptor length=%d, "
"left=%d\n", bump, (len - k));
return;
}
if (0 == desc_len)
continue;
if (2 == do_hex)
dStrHex((const char *)ucp + 4, desc_len, 1);
else if (do_hex > 2)
dStrHex((const char *)ucp, bump, 1);
else {
switch (desc_type) {
case 0x0000: /* Required if POPULATE TOKEN (or friend) used */
printf(" Block Device ROD Token Limits:\n");
printf(" Maximum Range Descriptors: %d\n",
(ucp[10] << 8) + ucp[11]);
u = (ucp[12] << 24) | (ucp[13] << 16) | (ucp[14] << 8) |
ucp[15];
printf(" Maximum Inactivity Timeout: %u seconds\n", u);
u = (ucp[16] << 24) | (ucp[17] << 16) | (ucp[18] << 8) |
ucp[19];
printf(" Default Inactivity Timeout: %u seconds\n", u);
ull = 0;
for (j = 0; j < 8; j++) {
if (j > 0)
ull <<= 8;
ull |= ucp[20 + j];
}
printf(" Maximum Token Transfer Size: %" PRIu64 "\n", ull);
ull = 0;
for (j = 0; j < 8; j++) {
if (j > 0)
ull <<= 8;
ull |= ucp[28 + j];
}
printf(" Optimal Transfer Count: %" PRIu64 "\n", ull);
break;
case 0x0001: /* Mandatory (SPC-4) */
printf(" Supported Commands:\n");
j = 0;
while (j < ucp[4]) {
sa_len = ucp[6 + j];
for (k = 0; k < sa_len; k++) {
sg_get_opcode_sa_name(ucp[5 + j], ucp[7 + j + k],
0, sizeof(b), b);
printf(" %s\n", b);
}
j += sa_len;
}
break;
case 0x0004:
printf(" Parameter Data:\n");
printf(" Maximum CSCD Descriptor Count: %d\n",
(ucp[8] << 8) + ucp[9]);
printf(" Maximum Segment Descriptor Count: %d\n",
(ucp[10] << 8) + ucp[11]);
u = (ucp[12] << 24) | (ucp[13] << 16) | (ucp[14] << 8) |
ucp[15];
printf(" Maximum Descriptor List Length: %u\n", u);
u = (ucp[16] << 24) | (ucp[17] << 16) | (ucp[18] << 8) |
ucp[19];
printf(" Maximum Inline Data Length: %u\n", u);
break;
case 0x0008:
printf(" Supported Descriptors:\n");
for (j = 0; j < ucp[4]; j++) {
printf(" 0x%x\n", ucp[5 + j]);
}
break;
case 0x000C:
printf(" Supported CSCD IDs:\n");
for (j = 0; j < (ucp[4] << 8) + ucp[5]; j += 2) {
u = (ucp[6 + j] << 8) | ucp[7 + j];
printf(" 0x%04x\n", u);
}
break;
case 0x0106:
printf(" ROD Token Features:\n");
printf(" Remote Tokens: %d\n", ucp[4] & 0x0f);
u = (ucp[16] << 24) | (ucp[17] << 16) | (ucp[18] << 8) |
ucp[19];
printf(" Minimum Token Lifetime: %u seconds\n", u);
u = (ucp[20] << 24) | (ucp[21] << 16) | (ucp[22] << 8) |
ucp[23];
printf(" Maximum Token Lifetime: %u seconds\n", u);
u = (ucp[24] << 24) | (ucp[25] << 16) | (ucp[26] << 8) |
ucp[27];
printf(" Maximum Token inactivity timeout: %d\n", u);
decode_rod_descriptor(&ucp[48], (ucp[46] << 8) + ucp[47]);
break;
case 0x0108:
printf(" Supported ROD Token and ROD Types:\n");
for (j = 0; j < (ucp[6] << 8) + ucp[7]; j+= 64) {
u = (ucp[8 + j] << 24) | (ucp[8 + j + 1] << 16) |
(ucp[8 + j + 2] << 8) | ucp[8 + j + 3];
printf(" ROD Type %u:\n", u);
printf(" Internal: %s\n",
ucp[8 + j + 4] & 0x80 ? "yes" : "no");
printf(" Token In: %s\n",
ucp[8 + j + 4] & 0x02 ? "yes" : "no");
printf(" Token Out: %s\n",
ucp[8 + j + 4] & 0x01 ? "yes" : "no");
printf(" Preference: %d\n",
(ucp[8 + j + 6] << 8) + ucp[8 + j + 7]);
}
break;
case 0x8001: /* Mandatory (SPC-4) */
printf(" General Copy Operations:\n");
u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) |
ucp[7];
printf(" Total Concurrent Copies: %u\n", u);
u = (ucp[8] << 24) | (ucp[9] << 16) | (ucp[10] << 8) |
ucp[11];
printf(" Maximum Identified Concurrent Copies: %u\n", u);
u = (ucp[12] << 24) | (ucp[13] << 16) | (ucp[14] << 8) |
ucp[15];
printf(" Maximum Segment Length: %u\n", u);
ull = (1 << ucp[16]);
printf(" Data Segment Granularity: %" PRIu64 "\n", ull);
ull = (1 << ucp[17]);
printf(" Inline Data Granularity: %" PRIu64 "\n", ull);
break;
case 0x9101:
printf(" Stream Copy Operations:\n");
u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) |
ucp[7];
printf(" Maximum Stream Device Transfer Size: %u\n", u);
break;
case 0xC001:
printf(" Held Data:\n");
u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) |
ucp[7];
printf(" Held Data Limit: %u\n", u);
ull = (1 << ucp[8]);
printf(" Held Data Granularity: %" PRIu64 "\n", ull);
break;
default:
pr2serr("Unexpected type=%d\n", desc_type);
dStrHexErr((const char *)ucp, bump, 1);
break;
}
}
}
}
/* VPD_PROTO_LU */
static void
decode_proto_lu_vpd(unsigned char * buff, int len, int do_hex)
{
int k, bump, rel_port, desc_len, proto;
unsigned char * ucp;
if (1 == do_hex) {
dStrHex((const char *)buff, len, 0);
return;
}
if (len < 4) {
pr2serr("Protocol-specific logical unit information VPD page length "
"too short=%d\n", len);
return;
}
len -= 4;
ucp = buff + 4;
for (k = 0; k < len; k += bump, ucp += bump) {
rel_port = (ucp[0] << 8) + ucp[1];
printf("Relative port=%d\n", rel_port);
proto = ucp[2] & 0xf;
desc_len = (ucp[6] << 8) + ucp[7];
bump = 8 + desc_len;
if ((k + bump) > len) {
pr2serr("Protocol-specific logical unit information VPD page, "
"short descriptor length=%d, left=%d\n", bump, (len - k));
return;
}
if (0 == desc_len)
continue;
if (2 == do_hex)
dStrHex((const char *)ucp + 8, desc_len, 1);
else if (do_hex > 2)
dStrHex((const char *)ucp, bump, 1);
else {
switch (proto) {
case TPROTO_SAS:
printf(" Protocol identifier: SAS\n");
printf(" TLR control supported: %d\n", !!(ucp[8] & 0x1));
break;
default:
pr2serr("Unexpected proto=%d\n", proto);
dStrHexErr((const char *)ucp, bump, 1);
break;
}
}
}
}
/* VPD_PROTO_PORT */
static void
decode_proto_port_vpd(unsigned char * buff, int len, int do_hex)
{
int k, j, bump, rel_port, desc_len, proto;
unsigned char * ucp;
unsigned char * pidp;
if (1 == do_hex) {
dStrHex((const char *)buff, len, 0);
return;
}
if (len < 4) {
pr2serr("Protocol-specific port information VPD page length too "
"short=%d\n", len);
return;
}
len -= 4;
ucp = buff + 4;
for (k = 0; k < len; k += bump, ucp += bump) {
rel_port = (ucp[0] << 8) + ucp[1];
printf("Relative port=%d\n", rel_port);
proto = ucp[2] & 0xf;
desc_len = (ucp[6] << 8) + ucp[7];
bump = 8 + desc_len;
if ((k + bump) > len) {
pr2serr("Protocol-specific port VPD page, short descriptor "
"length=%d, left=%d\n", bump, (len - k));
return;
}
if (0 == desc_len)
continue;
if (2 == do_hex)
dStrHex((const char *)ucp + 8, desc_len, 1);
else if (do_hex > 2)
dStrHex((const char *)ucp, bump, 1);
else {
switch (proto) {
case TPROTO_SAS: /* for SSP, added spl3r2 */
printf(" pwr_d_s=%d\n", !!(ucp[3] & 0x1));
pidp = ucp + 8;
for (j = 0; j < desc_len; j += 4, pidp += 4)
printf(" phy id=%d, ssp persistent capable=%d\n",
pidp[1], (0x1 & pidp[2]));
break;
default:
pr2serr("Unexpected proto=%d\n", proto);
dStrHexErr((const char *)ucp, bump, 1);
break;
}
}
}
}
/* VPD_BLOCK_LIMITS sbc */
/* VPD_SA_DEV_CAP ssc */
/* VPD_OSD_INFO osd */
static void
decode_b0_vpd(unsigned char * buff, int len, int do_hex, int pdt)
{
unsigned int u;
if (do_hex) {
dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
return;
}
switch (pdt) {
case PDT_DISK: case PDT_WO: case PDT_OPTICAL: /* Block limits */
if (len < 16) {
pr2serr("Block limits VPD page length too short=%d\n", len);
return;
}
printf(" Write same no zero (WSNZ): %d\n", !!(buff[4] & 0x1));
printf(" Maximum compare and write length: %u blocks\n",
buff[5]);
u = (buff[6] << 8) | buff[7];
printf(" Optimal transfer length granularity: %u blocks\n", u);
u = (buff[8] << 24) | (buff[9] << 16) | (buff[10] << 8) |
buff[11];
printf(" Maximum transfer length: %u blocks\n", u);
u = (buff[12] << 24) | (buff[13] << 16) | (buff[14] << 8) |
buff[15];
printf(" Optimal transfer length: %u blocks\n", u);
if (len > 19) { /* added in sbc3r09 */
u = (buff[16] << 24) | (buff[17] << 16) | (buff[18] << 8) |
buff[19];
printf(" Maximum prefetch length: %u blocks\n", u);
/* was 'Maximum prefetch transfer length' prior to sbc3r33 */
}
if (len > 27) { /* added in sbc3r18 */
u = ((unsigned int)buff[20] << 24) | (buff[21] << 16) |
(buff[22] << 8) | buff[23];
printf(" Maximum unmap LBA count: %u\n", u);
u = ((unsigned int)buff[24] << 24) | (buff[25] << 16) |
(buff[26] << 8) | buff[27];
printf(" Maximum unmap block descriptor count: %u\n", u);
}
if (len > 35) { /* added in sbc3r19 */
int m;
uint64_t mwsl;
u = ((unsigned int)buff[28] << 24) | (buff[29] << 16) |
(buff[30] << 8) | buff[31];
printf(" Optimal unmap granularity: %u\n", u);
printf(" Unmap granularity alignment valid: %u\n",
!!(buff[32] & 0x80));
u = ((unsigned int)(buff[32] & 0x7f) << 24) | (buff[33] << 16) |
(buff[34] << 8) | buff[35];
printf(" Unmap granularity alignment: %u\n", u);
/* added in sbc3r26 */
mwsl = 0;
for (m = 0; m < 8; ++m) {
if (m > 0)
mwsl <<= 8;
mwsl |= buff[36 + m];
}
printf(" Maximum write same length: 0x%" PRIx64 " blocks\n",
mwsl);
}
break;
case PDT_TAPE: case PDT_MCHANGER:
printf(" WORM=%d\n", !!(buff[4] & 0x1));
break;
case PDT_OSD:
default:
pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
dStrHexErr((const char *)buff, len, 0);
break;
}
}
static const char * product_type_arr[] =
{
"Not specified",
"CFast",
"CompactFlash",
"MemoryStick",
"MultiMediaCard",
"Secure Digital Card (SD)",
"XQD",
"Universal Flash Storage Card (UFS)",
};
/* VPD_BLOCK_DEV_CHARS sbc */
/* VPD_MAN_ASS_SN ssc */
/* VPD_SECURITY_TOKEN osd */
static void
decode_b1_vpd(unsigned char * buff, int len, int do_hex, int pdt)
{
unsigned int u, k;
if (do_hex) {
dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
return;
}
switch (pdt) {
case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
if (len < 64) {
pr2serr("Block device characteristics VPD page length too "
"short=%d\n", len);
return;
}
u = (buff[4] << 8) | buff[5];
if (0 == u)
printf(" Medium rotation rate is not reported\n");
else if (1 == u)
printf(" Non-rotating medium (e.g. solid state)\n");
else if ((u < 0x401) || (0xffff == u))
printf(" Reserved [0x%x]\n", u);
else
printf(" Nominal rotation rate: %d rpm\n", u);
u = buff[6];
k = sizeof(product_type_arr) / sizeof(product_type_arr[0]);
if (u < k)
printf(" Product type: %s\n", product_type_arr[u]);
else if (u < 0xf0)
printf(" Product type: Reserved [0x%x]\n", u);
else
printf(" Product type: Vendor specific [0x%x]\n", u);
printf(" WABEREQ=%d\n", (buff[7] >> 6) & 0x3);
printf(" WACEREQ=%d\n", (buff[7] >> 4) & 0x3);
u = buff[7] & 0xf;
printf(" Nominal form factor");
switch (u) {
case 0:
printf(" not reported\n");
break;
case 1:
printf(": 5.25 inch\n");
break;
case 2:
printf(": 3.5 inch\n");
break;
case 3:
printf(": 2.5 inch\n");
break;
case 4:
printf(": 1.8 inch\n");
break;
case 5:
printf(": less then 1.8 inch\n");
break;
default:
printf(": reserved\n");
break;
}
printf(" FUAB=%d\n", buff[8] & 0x2);
printf(" VBULS=%d\n", buff[8] & 0x1);
break;
case PDT_TAPE: case PDT_MCHANGER: case PDT_ADC:
printf(" Manufacturer-assigned serial number: %.*s\n",
len - 4, buff + 4);
break;
default:
pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
dStrHexErr((const char *)buff, len, 0);
break;
}
}
/* VPD_LB_PROVISIONING */
static int
decode_block_lb_prov_vpd(unsigned char * b, int len)
{
int dp;
if (len < 4) {
pr2serr("Logical block provisioning page too short=%d\n", len);
return SG_LIB_CAT_MALFORMED;
}
printf(" Unmap command supported (LBPU): %d\n", !!(0x80 & b[5]));
printf(" Write same (16) with unmap bit supported (LBWS): %d\n",
!!(0x40 & b[5]));
printf(" Write same (10) with unmap bit supported (LBWS10): %d\n",
!!(0x20 & b[5]));
printf(" Logical block provisioning read zeros (LBPRZ): %d\n",
!!(0x4 & b[5]));
printf(" Anchored LBAs supported (ANC_SUP): %d\n", !!(0x2 & b[5]));
dp = !!(b[5] & 0x1);
printf(" Threshold exponent: %d\n", b[4]);
printf(" Descriptor present (DP): %d\n", dp);
printf(" Provisioning type: %d\n", b[6] & 0x7);
if (dp) {
const unsigned char * ucp;
int i_len, p_id, c_set, piv, assoc, desig_type;
ucp = b + 8;
i_len = ucp[3];
if (0 == i_len) {
pr2serr("Logical block provisioning page provisioning group "
"descriptor too short=%d\n", i_len);
return 0;
}
printf(" Provisioning group descriptor\n");
p_id = ((ucp[0] >> 4) & 0xf);
c_set = (ucp[0] & 0xf);
piv = ((ucp[1] & 0x80) ? 1 : 0);
assoc = ((ucp[1] >> 4) & 0x3);
desig_type = (ucp[1] & 0xf);
decode_designation_descriptor(ucp, i_len, p_id, c_set, piv, assoc,
desig_type, 0, 1);
}
return 0;
}
/* VPD_TA_SUPPORTED */
static int
decode_tapealert_supported_vpd(unsigned char * b, int len)
{
if (len < 12) {
pr2serr("TapeAlert supported flags length too short=%d\n", len);
return SG_LIB_CAT_MALFORMED;
}
printf(" Flag01h: %d 02h: %d 03h: %d 04h: %d 05h: %d 06h: %d "
"07h: %d 08h: %d\n", !!(b[4] & 0x80), !!(b[4] & 0x40),
!!(b[4] & 0x20), !!(b[4] & 0x10), !!(b[4] & 0x8), !!(b[4] & 0x4),
!!(b[4] & 0x2), !!(b[4] & 0x1));
printf(" Flag09h: %d 0ah: %d 0bh: %d 0ch: %d 0dh: %d 0eh: %d "
"0fh: %d 10h: %d\n", !!(b[5] & 0x80), !!(b[5] & 0x40),
!!(b[5] & 0x20), !!(b[5] & 0x10), !!(b[5] & 0x8), !!(b[5] & 0x4),
!!(b[5] & 0x2), !!(b[5] & 0x1));
printf(" Flag11h: %d 12h: %d 13h: %d 14h: %d 15h: %d 16h: %d "
"17h: %d 18h: %d\n", !!(b[6] & 0x80), !!(b[6] & 0x40),
!!(b[6] & 0x20), !!(b[6] & 0x10), !!(b[6] & 0x8), !!(b[6] & 0x4),
!!(b[6] & 0x2), !!(b[6] & 0x1));
printf(" Flag19h: %d 1ah: %d 1bh: %d 1ch: %d 1dh: %d 1eh: %d "
"1fh: %d 20h: %d\n", !!(b[7] & 0x80), !!(b[7] & 0x40),
!!(b[7] & 0x20), !!(b[7] & 0x10), !!(b[7] & 0x8), !!(b[7] & 0x4),
!!(b[7] & 0x2), !!(b[7] & 0x1));
printf(" Flag21h: %d 22h: %d 23h: %d 24h: %d 25h: %d 26h: %d "
"27h: %d 28h: %d\n", !!(b[8] & 0x80), !!(b[8] & 0x40),
!!(b[8] & 0x20), !!(b[8] & 0x10), !!(b[8] & 0x8), !!(b[8] & 0x4),
!!(b[8] & 0x2), !!(b[8] & 0x1));
printf(" Flag29h: %d 2ah: %d 2bh: %d 2ch: %d 2dh: %d 2eh: %d "
"2fh: %d 30h: %d\n", !!(b[9] & 0x80), !!(b[9] & 0x40),
!!(b[9] & 0x20), !!(b[9] & 0x10), !!(b[9] & 0x8), !!(b[9] & 0x4),
!!(b[9] & 0x2), !!(b[9] & 0x1));
printf(" Flag31h: %d 32h: %d 33h: %d 34h: %d 35h: %d 36h: %d "
"37h: %d 38h: %d\n", !!(b[10] & 0x80), !!(b[10] & 0x40),
!!(b[10] & 0x20), !!(b[10] & 0x10), !!(b[10] & 0x8),
!!(b[10] & 0x4), !!(b[10] & 0x2), !!(b[10] & 0x1));
printf(" Flag39h: %d 3ah: %d 3bh: %d 3ch: %d 3dh: %d 3eh: %d "
"3fh: %d 40h: %d\n", !!(b[11] & 0x80), !!(b[11] & 0x40),
!!(b[11] & 0x20), !!(b[11] & 0x10), !!(b[11] & 0x8),
!!(b[11] & 0x4), !!(b[11] & 0x2), !!(b[11] & 0x1));
return 0;
}
/* VPD_LB_PROVISIONING sbc */
/* VPD_TA_SUPPORTED ssc */
static void
decode_b2_vpd(unsigned char * buff, int len, int do_hex, int pdt)
{
if (do_hex) {
dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
return;
}
switch (pdt) {
case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
decode_block_lb_prov_vpd(buff, len);
break;
case PDT_TAPE: case PDT_MCHANGER:
decode_tapealert_supported_vpd(buff, len);
break;
default:
pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
dStrHexErr((const char *)buff, len, 0);
break;
}
}
/* VPD_REFERRALS sbc */
/* VPD_AUTOMATION_DEV_SN ssc */
static void
decode_b3_vpd(unsigned char * b, int len, int do_hex, int pdt)
{
char obuff[DEF_ALLOC_LEN];
unsigned int u;
if (do_hex) {
dStrHex((const char *)b, len, (1 == do_hex) ? 0 : -1);
return;
}
switch (pdt) {
case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
if (len < 16) {
pr2serr("Referrals VPD page length too short=%d\n", len);
break;
}
u = ((unsigned int)b[8] << 24) | (b[9] << 16) | (b[10] << 8) | b[11];
printf(" User data segment size: %u\n", u);
u = ((unsigned int)b[12] << 24) | (b[13] << 16) |
(b[14] << 8) | b[15];
printf(" User data segment multiplier: %u\n", u);
break;
case PDT_TAPE: case PDT_MCHANGER:
memset(obuff, 0, sizeof(obuff));
len -= 4;
if (len >= (int)sizeof(obuff))
len = sizeof(obuff) - 1;
memcpy(obuff, b + 4, len);
printf(" Automation device serial number: %s\n", obuff);
break;
default:
pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
dStrHexErr((const char *)b, len, 0);
break;
}
}
/* Returns 0 if successful */
static int
svpd_unable_to_decode(int sg_fd, int num_vpd, int subvalue, int maxlen,
int do_hex, int do_raw, int do_long, int do_quiet,
int verbose)
{
int len, res;
int alloc_len = maxlen;
if (do_quiet) { ; } /* unused, dummy to suppress warning */
if ((! do_hex) && (! do_raw))
printf("Only hex output supported\n");
if (!do_raw) {
if (subvalue)
printf("VPD page code=0x%.2x, subvalue=0x%.2x:\n", num_vpd,
subvalue);
else if (num_vpd >= 0)
printf("VPD page code=0x%.2x:\n", num_vpd);
else
printf("VPD page code=%d:\n", num_vpd);
}
if (0 == alloc_len)
alloc_len = DEF_ALLOC_LEN;
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len,
1, verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len, 1,
verbose);
if (res) {
pr2serr("fetching VPD page (2) code=0x%.2x "
" (alloc_len=%d) failed\n", num_vpd, len);
return res;
}
} else {
pr2serr("warning: response length (%d) longer than "
"requested (%d)\n", len, maxlen);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
if (VPD_ASCII_OP_DEF == num_vpd)
dStrHex((const char *)rsp_buff, len, 0);
else
dStrHex((const char *)rsp_buff, len, (do_long ? 0 : 1));
}
return 0;
} else {
if (num_vpd >= 0)
pr2serr("fetching VPD page code=0x%.2x: failed\n", num_vpd);
else
pr2serr("fetching VPD page code=%d: failed\n", num_vpd);
return res;
}
}
/* Returns 0 if successful, else see sg_ll_inquiry() */
static int
svpd_decode_t10(int sg_fd, int num_vpd, int subvalue, int maxlen, int do_hex,
int do_raw, int do_long, int do_quiet, int verbose)
{
int len, pdt, num, k, pn;
char buff[48];
const struct svpd_values_name_t * vnp;
int res = 0;
int alloc_len = maxlen;
char obuff[DEF_ALLOC_LEN];
if (0 == alloc_len)
alloc_len = (VPD_ATA_INFO == num_vpd) ?
VPD_ATA_INFO_LEN : DEF_ALLOC_LEN;
switch(num_vpd) {
case VPD_NO_RATHER_STD_INQ: /* -2 (want standard inquiry response) */
if (do_long)
alloc_len = DEF_ALLOC_LEN;
else if (0 == maxlen)
alloc_len = 36;
res = sg_ll_inquiry(sg_fd, 0, 0, 0, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
if (do_raw)
dStrRaw((const char *)rsp_buff, alloc_len);
else if (do_hex) {
if (! do_quiet && (do_hex < 3))
printf("Standard Inquiry reponse:\n");
dStrHex((const char *)rsp_buff, alloc_len,
(1 == do_hex) ? 0 : -1);
} else
decode_std_inq(rsp_buff, alloc_len, verbose);
return 0;
}
break;
case VPD_SUPPORTED_VPDS: /* 0x0 */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("Supported VPD pages VPD page:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4; /* spc4r25 */
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching Supported VPD pages "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else if (do_hex)
dStrHex((const char *)rsp_buff, len, (1 == do_hex) ? 0 : -1);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
num = rsp_buff[3];
if (num > (len - 4))
num = (len - 4);
for (k = 0; k < num; ++k) {
pn = rsp_buff[4 + k];
vnp = sdp_get_vpd_detail(pn, -1, pdt);
if (vnp) {
if (do_long)
printf(" 0x%02x %s [%s]\n", pn, vnp->name,
vnp->acron);
else
printf(" %s [%s]\n", vnp->name, vnp->acron);
} else
printf(" 0x%x\n", pn);
}
}
return 0;
}
break;
case VPD_UNIT_SERIAL_NUM: /* 0x80 */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("Unit serial number VPD page:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4; /* spc4r25 */
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching Unit serial number page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else if (do_hex)
dStrHex((const char *)rsp_buff, len, (1 == do_hex) ? 0 : -1);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
memset(obuff, 0, sizeof(obuff));
len -= 4;
if (len >= (int)sizeof(obuff))
len = sizeof(obuff) - 1;
memcpy(obuff, rsp_buff + 4, len);
printf(" Unit serial number: %s\n", obuff);
}
return 0;
}
break;
case VPD_DEVICE_ID: /* 0x83 */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("Device Identification VPD page:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching Device Identification page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else if (do_hex)
dStrHex((const char *)rsp_buff, len, (1 == do_hex) ? 0 : -1);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_id_vpd(rsp_buff, len, subvalue, do_long, do_quiet);
}
return 0;
}
break;
case VPD_SOFTW_INF_ID: /* 0x84 */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("Software interface identification VPD page:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4; /* spc4r25 */
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching Software interface id page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_softw_inf_id(rsp_buff, len, do_hex);
}
return 0;
}
break;
case VPD_MAN_NET_ADDR: /* 0x85 */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("Management network addresses VPD page:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching Management network addresses page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else
decode_net_man_vpd(rsp_buff, len, do_hex);
return 0;
}
break;
case VPD_EXT_INQ: /* 0x86 */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("extended INQUIRY data VPD page:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4; /* spc4r25 */
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching Extended INQUIRY data page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
int protect = 0;
struct sg_simple_inquiry_resp sir;
if (do_long) {
res = sg_simple_inquiry(sg_fd, &sir, 0, verbose);
if (res)
break;
protect = sir.byte_5 & 0x1; /* SPC-3 and later */
}
pdt = rsp_buff[0] & 0x1f;
if (verbose)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_x_inq_vpd(rsp_buff, len, do_hex, do_long, protect);
}
return 0;
}
break;
case VPD_MODE_PG_POLICY: /* 0x87 */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("Mode page policy VPD page:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching Mode page policy page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_mode_policy_vpd(rsp_buff, len, do_hex);
}
return 0;
}
break;
case VPD_SCSI_PORTS: /* 0x88 */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("SCSI Ports VPD page:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching SCSI ports page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) "
"longer than requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_scsi_ports_vpd(rsp_buff, len, do_hex, do_long,
do_quiet);
}
return 0;
}
break;
case VPD_ATA_INFO: /* 0x89 */
if ((! do_raw) && (do_hex < 3) && (! do_quiet))
printf("ATA information VPD page:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching ATA info page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if ((2 == do_raw) || (3 == do_hex)) /* special for hdparm */
dWordHex((const unsigned short *)(rsp_buff + 60),
256, -2, sg_is_big_endian());
else if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_ata_info_vpd(rsp_buff, len, do_long, do_hex);
}
return 0;
}
break;
case VPD_POWER_CONDITION: /* 0x8a */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("Power condition VPD page:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching ATA info page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_power_condition(rsp_buff, len, do_hex);
}
return 0;
}
break;
case VPD_POWER_CONSUMPTION: /* 0x8d */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("Power consumption VPD page:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching Power consumption page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_power_consumption_vpd(rsp_buff, len, do_hex);
}
return 0;
}
break;
case VPD_3PARTY_COPY: /* 0x8f */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("Third party copy VPD page:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4; /* spc4r25 */
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching Third party copy page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else if (1 == do_hex)
dStrHex((const char *)rsp_buff, len, 0);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_3party_copy_vpd(rsp_buff, len, do_hex, verbose);
}
return 0;
}
break;
case VPD_PROTO_LU: /* 0x90 */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("Protocol-specific logical unit information:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching Protocol-specific LU page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_proto_lu_vpd(rsp_buff, len, do_hex);
}
return 0;
}
break;
case VPD_PROTO_PORT: /* 0x91 */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("Protocol-specific port information:\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching Protocol-specific port page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_proto_port_vpd(rsp_buff, len, do_hex);
}
return 0;
}
break;
case 0xb0: /* depends on pdt */
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
pdt = rsp_buff[0] & 0x1f;
if ((! do_raw) && (! do_quiet) && (do_hex < 3)) {
switch (pdt) {
case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
printf("Block limits VPD page (SBC):\n");
break;
case PDT_TAPE: case PDT_MCHANGER:
printf("Sequential access device capabilities VPD page "
"(SSC):\n");
break;
case PDT_OSD:
printf("OSD information VPD page (OSD):\n");
break;
default:
printf("VPD page=0x%x, pdt=0x%x:\n", 0xb0, pdt);
break;
}
}
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching 0xb0 page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_b0_vpd(rsp_buff, len, do_hex, pdt);
}
return 0;
} else if (! do_raw)
printf("VPD page=0xb0\n");
break;
case 0xb1: /* depends on pdt */
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
pdt = rsp_buff[0] & 0x1f;
if ((! do_raw) && (! do_quiet) && (do_hex < 3)) {
switch (pdt) {
case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
printf("Block device characteristics VPD page (SBC):\n");
break;
case PDT_TAPE: case PDT_MCHANGER:
printf("Manufactured assigned serial number VPD page "
"(SSC):\n");
break;
case PDT_OSD:
printf("Security token VPD page (OSD):\n");
break;
case PDT_ADC:
printf("Manufactured assigned serial number VPD page "
"(ADC):\n");
break;
default:
printf("VPD page=0x%x, pdt=0x%x:\n", 0xb1, pdt);
break;
}
}
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching 0xb1 page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_b1_vpd(rsp_buff, len, do_hex, pdt);
}
return 0;
} else if (! do_raw)
printf("VPD page=0xb1\n");
break;
case 0xb2: /* VPD page depends on pdt */
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
pdt = rsp_buff[0] & 0x1f;
if ((! do_raw) && (! do_quiet) && (do_hex < 3)) {
switch (pdt) {
case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
printf("Logical block provisioning VPD page (SBC):\n");
break;
case PDT_TAPE: case PDT_MCHANGER:
printf("TapeAlert supported flags VPD page (SSC):\n");
break;
default:
printf("VPD page=0x%x, pdt=0x%x:\n", 0xb2, pdt);
break;
}
}
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching 0xb2 page "
"(alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_b2_vpd(rsp_buff, len, do_hex, pdt);
}
return 0;
} else if (! do_raw)
printf("VPD page=0xb2\n");
break;
case 0xb3: /* VPD page depends on pdt */
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
pdt = rsp_buff[0] & 0x1f;
if ((! do_raw) && (! do_quiet) && (do_hex < 3)) {
switch (pdt) {
case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
printf("Referrals VPD page (SBC):\n");
break;
case PDT_TAPE: case PDT_MCHANGER:
printf("Automation device serial number VPD page "
"(SSC):\n");
break;
default:
printf("VPD page=0x%x, pdt=0x%x:\n", 0xb3, pdt);
break;
}
}
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching VPD page 0x%x "
"(alloc_len=%d) failed\n", num_vpd, len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
decode_b3_vpd(rsp_buff, len, do_hex, pdt);
}
return 0;
} else if (! do_raw)
printf("VPD page=0xb3\n");
break;
case VPD_DTDE_ADDRESS: /* 0xb4 */
if ((! do_raw) && (! do_quiet) && (do_hex < 3))
printf("Data transfer device element address (SSC):\n");
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, alloc_len, 1,
verbose);
if (0 == res) {
len = ((rsp_buff[2] << 8) + rsp_buff[3]) + 4;
if (num_vpd != rsp_buff[1]) {
pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
"response\n");
if (verbose) {
pr2serr("First 32 bytes of bad response\n");
dStrHexErr((const char *)rsp_buff, 32, 0);
}
return SG_LIB_CAT_MALFORMED;
}
if (len > alloc_len) {
if ((0 == maxlen) && (len < MX_ALLOC_LEN)) {
res = sg_ll_inquiry(sg_fd, 0, 1, num_vpd, rsp_buff, len,
1, verbose);
if (res) {
pr2serr("fetching Data transfer device element "
"address page (alloc_len=%d) failed\n", len);
return res;
}
} else {
pr2serr(">>> warning: response length (%d) longer than "
"requested (%d)\n", len, alloc_len);
len = alloc_len;
}
}
if (do_raw)
dStrRaw((const char *)rsp_buff, len);
else {
pdt = rsp_buff[0] & 0x1f;
if (verbose || do_long)
printf(" [PQual=%d Peripheral device type: %s]\n",
(rsp_buff[0] & 0xe0) >> 5,
sg_get_pdt_str(pdt, sizeof(buff), buff));
printf(" Data transfer device element address: 0x");
for (k = 4; k < len; ++k)
printf("%02x", (unsigned int)rsp_buff[k]);
printf("\n");
}
return 0;
}
break;
default:
return SG_LIB_SYNTAX_ERROR;
}
return res;
}
int
main(int argc, char * argv[])
{
int sg_fd, c, res, matches;
const char * device_name = NULL;
const struct svpd_values_name_t * vnp;
const char * page_str = NULL;
const char * cp;
int num_vpd = 0;
int do_enum = 0;
int do_hex = 0;
int do_ident = 0;
int do_long = 0;
int maxlen = 0;
int do_quiet = 0;
int do_raw = 0;
int do_verbose = 0;
int ret = 0;
int subvalue = 0;
while (1) {
int option_index = 0;
c = getopt_long(argc, argv, "ehHilm:p:qrvV", long_options,
&option_index);
if (c == -1)
break;
switch (c) {
case 'e':
++do_enum;
break;
case 'h':
case '?':
usage();
return 0;
case 'H':
++do_hex;
break;
case 'i':
++do_ident;
break;
case 'l':
++do_long;
break;
case 'm':
maxlen = sg_get_num(optarg);
if ((maxlen < 0) || (maxlen > MX_ALLOC_LEN)) {
pr2serr("argument to '--maxlen' should be %d or less\n",
MX_ALLOC_LEN);
return SG_LIB_SYNTAX_ERROR;
}
break;
case 'p':
if (page_str) {
pr2serr("only one '--page=' option permitted\n");
usage();
return SG_LIB_SYNTAX_ERROR;
} else
page_str = optarg;
break;
case 'q':
++do_quiet;
break;
case 'r':
++do_raw;
break;
case 'v':
++do_verbose;
break;
case 'V':
pr2serr("version: %s\n", version_str);
return 0;
default:
pr2serr("unrecognised option code 0x%x ??\n", c);
usage();
return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
if (NULL == device_name) {
device_name = argv[optind];
++optind;
}
if (optind < argc) {
for (; optind < argc; ++optind)
pr2serr("Unexpected extra argument: %s\n", argv[optind]);
usage();
return SG_LIB_SYNTAX_ERROR;
}
}
if (do_enum) {
if (device_name)
pr2serr("Device name %s ignored when --enumerate given\n",
device_name);
if (page_str) {
if ((0 == strcmp("-1", page_str)) ||
(0 == strcmp("-2", page_str)))
num_vpd = VPD_NO_RATHER_STD_INQ;
else if (isdigit(page_str[0])) {
num_vpd = sg_get_num_nomult(page_str);
if ((num_vpd < 0) || (num_vpd > 255)) {
pr2serr("Bad page code value after '-p' option\n");
return SG_LIB_SYNTAX_ERROR;
}
} else {
pr2serr("with --enumerate only search using VPD page "
"numbers\n");
return SG_LIB_SYNTAX_ERROR;
}
matches = search_standard_vpds(num_vpd);
if (0 == matches)
matches = svpd_search_vendor_vpds(num_vpd);
if (0 == matches)
printf("No matches found for VPD page number 0x%x\n",
num_vpd);
} else { /* enumerate standard then vendor VPD pages */
printf("Standard VPD pages:\n");
enumerate_vpds(1, 1);
}
return 0;
}
if (page_str) {
if ((0 == strcmp("-1", page_str)) || (0 == strcmp("-2", page_str)))
num_vpd = VPD_NO_RATHER_STD_INQ;
else if (isalpha(page_str[0])) {
vnp = sdp_find_vpd_by_acron(page_str);
if (NULL == vnp) {
vnp = svpd_find_vendor_by_acron(page_str);
if (NULL == vnp) {
pr2serr("abbreviation doesn't match a VPD page\n");
printf("available VPD pages:\n");
enumerate_vpds(1, 1);
return SG_LIB_SYNTAX_ERROR;
}
}
num_vpd = vnp->value;
subvalue = vnp->subvalue;
} else {
cp = strchr(page_str, ',');
num_vpd = sg_get_num_nomult(page_str);
if ((num_vpd < 0) || (num_vpd > 255)) {
pr2serr("Bad page code value after '-p' option\n");
printf("available VPD pages:\n");
enumerate_vpds(1, 1);
return SG_LIB_SYNTAX_ERROR;
}
if (cp) {
subvalue = sg_get_num_nomult(cp + 1);
if ((subvalue < 0) || (subvalue > 255)) {
pr2serr("Bad subvalue code value after '-p' option\n");
return SG_LIB_SYNTAX_ERROR;
}
}
}
}
if (do_raw && do_hex) {
pr2serr("Can't do hex and raw at the same time\n");
usage();
return SG_LIB_SYNTAX_ERROR;
}
if (do_ident) {
num_vpd = VPD_DEVICE_ID;
if (do_ident > 1) {
if (0 == do_long)
++do_quiet;
subvalue = VPD_DI_SEL_LU;
}
}
if (NULL == device_name) {
pr2serr("No DEVICE argument given\n");
usage();
return SG_LIB_SYNTAX_ERROR;
}
if (do_raw) {
if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
perror("sg_set_binary_mode");
return SG_LIB_FILE_ERROR;
}
}
if ((sg_fd = sg_cmds_open_device(device_name, 1 /* ro */,
do_verbose)) < 0) {
pr2serr("error opening file: %s: %s\n", device_name,
safe_strerror(-sg_fd));
return SG_LIB_FILE_ERROR;
}
memset(rsp_buff, 0, sizeof(rsp_buff));
res = svpd_decode_t10(sg_fd, num_vpd, subvalue, maxlen, do_hex, do_raw,
do_long, do_quiet, do_verbose);
if (SG_LIB_SYNTAX_ERROR == res) {
res = svpd_decode_vendor(sg_fd, num_vpd, subvalue, maxlen, do_hex,
do_raw, do_long, do_quiet, do_verbose);
if (SG_LIB_SYNTAX_ERROR == res)
res = svpd_unable_to_decode(sg_fd, num_vpd, subvalue, maxlen,
do_hex, do_raw, do_long, do_quiet,
do_verbose);
}
if (SG_LIB_CAT_ABORTED_COMMAND == res)
pr2serr("fetching VPD page failed, aborted command\n");
else if (res)
pr2serr("fetching VPD page failed\n");
ret = res;
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
pr2serr("close error: %s\n", safe_strerror(-res));
if (0 == ret)
return SG_LIB_FILE_ERROR;
}
return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}