blob: 80fc93bd90286473c6b4fbd2ec01c21109b86d30 [file] [log] [blame]
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.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"
/* A utility program originally written for the Linux OS SCSI subsystem.
* Copyright (C) 2000-2007 D. Gilbert
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
This program outputs information provided by a SCSI LOG SENSE command.
*/
static char * version_str = "0.75 20070821"; /* SPC-4 revision 11 */
#define MX_ALLOC_LEN (0xfffc)
#define SHORT_RESP_LEN 128
#define ALL_PAGE_LPAGE 0x0
#define BUFF_OVER_UNDER_LPAGE 0x1
#define WRITE_ERR_LPAGE 0x2
#define READ_ERR_LPAGE 0x3
#define READ_REV_ERR_LPAGE 0x4
#define VERIFY_ERR_LPAGE 0x5
#define NON_MEDIUM_LPAGE 0x6
#define LAST_N_ERR_LPAGE 0x7
#define LAST_N_DEFERRED_LPAGE 0xb
#define TEMPERATURE_LPAGE 0xd
#define START_STOP_LPAGE 0xe
#define APP_CLIENT_LPAGE 0xf
#define SELF_TEST_LPAGE 0x10
#define PORT_SPECIFIC_LPAGE 0x18
#define GSP_LPAGE 0x19
#define TAPE_ALERT_LPAGE 0x2e
#define IE_LPAGE 0x2f
#define NOT_SUBPG_LOG 0x0
#define ALL_SUBPG_LOG 0xff
#define PCB_STR_LEN 128
#define LOG_SENSE_PROBE_ALLOC_LEN 4
static unsigned char rsp_buff[MX_ALLOC_LEN];
static struct option long_options[] = {
{"all", no_argument, 0, 'a'},
{"brief", no_argument, 0, 'b'},
{"control", required_argument, 0, 'c'},
{"help", no_argument, 0, 'h'},
{"hex", no_argument, 0, 'H'},
{"list", no_argument, 0, 'l'},
{"maxlen", required_argument, 0, 'm'},
{"name", no_argument, 0, 'n'},
{"new", no_argument, 0, 'N'},
{"old", no_argument, 0, 'O'},
{"page", required_argument, 0, 'p'},
{"paramp", required_argument, 0, 'P'},
{"pcb", no_argument, 0, 'q'},
{"ppc", no_argument, 0, 'Q'},
{"raw", no_argument, 0, 'r'},
{"reset", no_argument, 0, 'R'},
{"sp", no_argument, 0, 's'},
{"select", no_argument, 0, 'S'},
{"temperature", no_argument, 0, 't'},
{"transport", no_argument, 0, 'T'},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
{0, 0, 0, 0},
};
struct opts_t {
int do_all;
int do_brief;
int do_help;
int do_hex;
int do_list;
int do_name;
int do_pcb;
int do_ppc;
int do_raw;
int do_pcreset;
int do_select;
int do_sp;
int do_temperature;
int do_transport;
int do_verbose;
int do_version;
int page_control;
int maxlen;
int pg_code;
int subpg_code;
int paramp;
const char * device_name;
int opt_new;
};
static void usage()
{
printf("Usage: sg_logs [--all] [--brief] [--control=PC] [--help] [--hex] "
"[--list]\n"
" [--maxlen=LEN] [--name] [--page=PG[,SPG]] "
"[--paramp=PP] [--pcb]\n"
" [--ppc] [--raw] [--reset] [--select] [--sp] "
"[--temperature]\n"
" [--transport] [--verbose] [--version] DEVICE\n"
" where:\n"
" --all|-a fetch and decode all log pages\n"
" use twice to fetch and decode all log pages "
"and subpages\n"
" --brief|-b shorten the output of some log pages\n"
" --control=PC|-c PC page control(PC) (default: 1)\n"
" 0: current threshhold, 1: current "
"cumulative\n"
" 2: default threshhold, 3: default "
"cumulative\n"
" --help|-h print usage message then exit\n"
" --hex|-H output response in hex (default: decode if "
"known)\n"
" --list|-l list supported log page names (equivalent to "
"'-p 0')\n"
" use twice to list supported log page and "
"subpage names\n"
" --maxlen=LEN|-m LEN max response length (def: 0 "
"-> everything)\n"
" when > 1 will request LEN bytes\n"
" --name|-n decode some pages into multiple name=value "
"lines\n"
" --page=PG|-p PG page code (in decimal)\n"
" --page=PG,SPG|-p PG,SPG\n"
" page code plus subpage code (both default "
"to 0)\n"
" --paramp=PP|-P PP parameter pointer (decimal) (def: 0)\n"
" --pcb|-q show parameter control bytes in decoded "
"output\n");
printf(" --ppc|-Q set the Parameter Pointer Control (PPC) bit "
"(def: 0)\n"
" --raw|-r output response in binary to stdout\n"
" --reset|-R reset log parameters (takes PC and SP into "
"account)\n"
" (uses PCR bit in LOG SELECT)\n"
" --select|-S perform LOG SELECT using SP and PC values\n"
" --sp|-s set the Saving Parameters (SP) bit (def: 0)\n"
" --temperature|-t decode temperature (log page 0xd or "
"0x2f)\n"
" --transport|-T decode transport (protocol specific port "
"0x18) log page\n"
" --verbose|-v increase verbosity\n"
" --version|-V output version string then exit\n\n"
"Performs a SCSI LOG SENSE (or LOG SELECT) command\n");
}
static void usage_old()
{
printf("Usage: sg_logs [-a] [-A] [-b] [-c=PC] [-h] [-H] [-l] [-L] "
"[-m=LEN] [-n]\n"
" [-p=PG[,SPG]] [-paramp=PP] [-pcb] [-ppc] "
"[-r] [-select]\n"
" [-sp] [-t] [-T] [-v] [-V] [-?] DEVICE\n"
" where:\n"
" -a fetch and decode all log pages\n"
" -A fetch and decode all log pages and subpages\n"
" -b shorten the output of some log pages\n"
" -c=PC page control(PC) (default: 1)\n"
" 0: current threshhold, 1: current cumulative\n"
" 2: default threshhold, 3: default cumulative\n"
" -h output in hex (default: decode if known)\n"
" -H output in hex (same as '-h')\n"
" -l list supported log page names (equivalent to "
"'-p=0')\n"
" -L list supported log page and subpages names "
"(equivalent to\n"
" '-p=0,ff')\n"
" -m=LEN max response length (decimal) (def: 0 "
"-> everything)\n"
" -n decode some pages into multiple name=value "
"lines\n"
" -p=PG page code in hex (def: 0)\n"
" -p=PG,SPG both in hex, (defs: 0,0)\n"
" -paramp=PP (in hex) (def: 0)\n"
" -pcb show parameter control bytes in decoded "
"output\n");
printf(" -ppc set the Parameter Pointer Control (PPC) bit "
"(def: 0)\n"
" -r reset log parameters (takes PC and SP into "
"account)\n"
" (uses PCR bit in LOG SELECT)\n"
" -select perform LOG SELECT using SP and PC values\n"
" -sp set the Saving Parameters (SP) bit (def: 0)\n"
" -t outputs temperature log page (0xd)\n"
" -T outputs transport (protocol specific port) log "
"page (0x18)\n"
" -v increase verbosity\n"
" -V output version string\n"
" -? output this usage message\n\n"
"Performs a SCSI LOG SENSE (or LOG SELECT) command\n");
}
static void usage_for(const struct opts_t * optsp)
{
if (optsp->opt_new)
usage();
else
usage_old();
}
/* Trying to decode multipliers as sg_get_num() [as sg_libs does] would
* only confuse things here, so use this local trimmed version */
static int get_num(const char * buf)
{
int res, len, num;
unsigned int unum;
const char * commap;
if ((NULL == buf) || ('\0' == buf[0]))
return -1;
len = strlen(buf);
commap = strchr(buf + 1, ',');
if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) {
res = sscanf(buf + 2, "%x", &unum);
num = unum;
} else if (commap && ('H' == toupper(*(commap - 1)))) {
res = sscanf(buf, "%x", &unum);
num = unum;
} else if ((NULL == commap) && ('H' == toupper(buf[len - 1]))) {
res = sscanf(buf, "%x", &unum);
num = unum;
} else
res = sscanf(buf, "%d", &num);
if (1 == res)
return num;
else
return -1;
}
static int process_cl_new(struct opts_t * optsp, int argc, char * argv[])
{
int c, n, nn;
char * cp;
while (1) {
int option_index = 0;
c = getopt_long(argc, argv, "aAbc:hHlLm:nNOp:P:qQrRsStTvV",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'a':
++optsp->do_all;
break;
case 'A':
optsp->do_all += 2;
break;
case 'b':
++optsp->do_brief;
break;
case 'c':
n = sg_get_num(optarg);
if ((n < 0) || (n > 3)) {
fprintf(stderr, "bad argument to '--control='\n");
usage();
return SG_LIB_SYNTAX_ERROR;
}
optsp->page_control = n;
break;
case 'h':
case '?':
++optsp->do_help;
break;
case 'H':
++optsp->do_hex;
break;
case 'l':
++optsp->do_list;
break;
case 'L':
optsp->do_list += 2;
break;
case 'm':
n = sg_get_num(optarg);
if ((n < 0) || (1 == n)) {
fprintf(stderr, "bad argument to '--maxlen='\n");
usage();
return SG_LIB_SYNTAX_ERROR;
}
optsp->maxlen = n;
break;
case 'n':
++optsp->do_name;
break;
case 'N':
break; /* ignore */
case 'O':
optsp->opt_new = 0;
return 0;
case 'p':
cp = strchr(optarg, ',');
n = get_num(optarg);
if ((n < 0) || (n > 63)) {
fprintf(stderr, "Bad argument to '--page='\n");
usage();
return SG_LIB_SYNTAX_ERROR;
}
if (cp) {
nn = get_num(cp + 1);
if ((nn < 0) || (nn > 255)) {
fprintf(stderr, "Bad second value in argument to "
"'--page='\n");
usage();
return SG_LIB_SYNTAX_ERROR;
}
} else
nn = 0;
optsp->pg_code = n;
optsp->subpg_code = nn;
break;
case 'P':
n = sg_get_num(optarg);
if (n < 0) {
fprintf(stderr, "bad argument to '--paramp='\n");
usage();
return SG_LIB_SYNTAX_ERROR;
}
optsp->paramp = n;
break;
case 'q':
++optsp->do_pcb;
break;
case 'Q':
++optsp->do_ppc;
break;
case 'r':
++optsp->do_raw;
break;
case 'R':
++optsp->do_pcreset;
++optsp->do_select;
break;
case 's':
++optsp->do_sp;
break;
case 'S':
++optsp->do_select;
break;
case 't':
++optsp->do_temperature;
break;
case 'T':
++optsp->do_transport;
break;
case 'v':
++optsp->do_verbose;
break;
case 'V':
++optsp->do_version;
break;
default:
fprintf(stderr, "unrecognised option code %c [0x%x]\n", c, c);
if (optsp->do_help)
break;
usage();
return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
if (NULL == optsp->device_name) {
optsp->device_name = argv[optind];
++optind;
}
if (optind < argc) {
for (; optind < argc; ++optind)
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
return SG_LIB_SYNTAX_ERROR;
}
}
return 0;
}
static int process_cl_old(struct opts_t * optsp, int argc, char * argv[])
{
int k, jmp_out, plen, num, n;
unsigned int u, uu;
const char * cp;
for (k = 1; k < argc; ++k) {
cp = argv[k];
plen = strlen(cp);
if (plen <= 0)
continue;
if ('-' == *cp) {
for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) {
switch (*cp) {
case 'a':
++optsp->do_all;
break;
case 'A':
optsp->do_all += 2;
break;
case 'b':
++optsp->do_brief;
break;
case 'h':
case 'H':
++optsp->do_hex;
break;
case 'l':
++optsp->do_list;
break;
case 'L':
optsp->do_list += 2;
break;
case 'n':
++optsp->do_name;
break;
case 'N':
optsp->opt_new = 1;
return 0;
case 'O':
break;
case 'r':
optsp->do_pcreset = 1;
optsp->do_select = 1;
break;
case 't':
++optsp->do_temperature;
break;
case 'T':
++optsp->do_transport;
break;
case 'v':
++optsp->do_verbose;
break;
case 'V':
++optsp->do_version;
break;
case '?':
++optsp->do_help;
break;
case '-':
++cp;
jmp_out = 1;
break;
default:
jmp_out = 1;
break;
}
if (jmp_out)
break;
}
if (plen <= 0)
continue;
if (0 == strncmp("c=", cp, 2)) {
num = sscanf(cp + 2, "%x", &u);
if ((1 != num) || (u > 3)) {
printf("Bad page control after '-c=' option [0..3]\n");
usage_old();
return SG_LIB_SYNTAX_ERROR;
}
optsp->page_control = u;
} else if (0 == strncmp("m=", cp, 2)) {
num = sscanf(cp + 2, "%d", &n);
if ((1 != num) || (n < 0) || (n > MX_ALLOC_LEN)) {
printf("Bad maximum response length after '-m=' option\n");
usage_old();
return SG_LIB_SYNTAX_ERROR;
}
optsp->maxlen = n;
} else if (0 == strncmp("p=", cp, 2)) {
if (NULL == strchr(cp + 2, ',')) {
num = sscanf(cp + 2, "%x", &u);
if ((1 != num) || (u > 63)) {
fprintf(stderr, "Bad page code value after '-p=' "
"option\n");
usage_old();
return SG_LIB_SYNTAX_ERROR;
}
optsp->pg_code = u;
} else if (2 == sscanf(cp + 2, "%x,%x", &u, &uu)) {
if (uu > 255) {
fprintf(stderr, "Bad sub page code value after '-p=' "
"option\n");
usage_old();
return SG_LIB_SYNTAX_ERROR;
}
optsp->pg_code = u;
optsp->subpg_code = uu;
} else {
fprintf(stderr, "Bad page code, subpage code sequence "
"after '-p=' option\n");
usage_old();
return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == strncmp("paramp=", cp, 7)) {
num = sscanf(cp + 7, "%x", &u);
if ((1 != num) || (u > 0xffff)) {
printf("Bad parameter pointer after '-paramp=' option\n");
usage_old();
return SG_LIB_SYNTAX_ERROR;
}
optsp->paramp = u;
} else if (0 == strncmp("pcb", cp, 3))
optsp->do_pcb = 1;
else if (0 == strncmp("ppc", cp, 3))
optsp->do_ppc = 1;
else if (0 == strncmp("select", cp, 6))
optsp->do_select = 1;
else if (0 == strncmp("sp", cp, 2))
optsp->do_sp = 1;
else if (0 == strncmp("old", cp, 3))
;
else if (jmp_out) {
fprintf(stderr, "Unrecognized option: %s\n", cp);
usage_old();
return SG_LIB_SYNTAX_ERROR;
}
} else if (0 == optsp->device_name)
optsp->device_name = cp;
else {
fprintf(stderr, "too many arguments, got: %s, not expecting: "
"%s\n", optsp->device_name, cp);
usage_old();
return SG_LIB_SYNTAX_ERROR;
}
}
return 0;
}
static int process_cl(struct opts_t * optsp, int argc, char * argv[])
{
int res;
char * cp;
cp = getenv("SG3_UTILS_OLD_OPTS");
if (cp) {
optsp->opt_new = 0;
res = process_cl_old(optsp, argc, argv);
if ((0 == res) && optsp->opt_new)
res = process_cl_new(optsp, argc, argv);
} else {
optsp->opt_new = 1;
res = process_cl_new(optsp, argc, argv);
if ((0 == res) && (0 == optsp->opt_new))
res = process_cl_old(optsp, argc, argv);
}
return res;
}
static void dStrRaw(const char* str, int len)
{
int k;
for (k = 0 ; k < len; ++k)
printf("%c", str[k]);
}
/* Call LOG SENSE twice: the first time ask for 4 byte response to determine
actual length of response; then a second time requesting the
min(actual_len, mx_resp_len) bytes. If the calculated length for the
second fetch is odd then it is incremented (perhaps should be made modulo
4 in the future for SAS). Returns 0 if ok, SG_LIB_CAT_INVALID_OP for
log_sense not supported, SG_LIB_CAT_ILLEGAL_REQ for bad field in log sense
command, SG_LIB_CAT_NOT_READY, SG_LIB_CAT_UNIT_ATTENTION,
SG_LIB_CAT_ABORTED_COMMAND and -1 for other errors. */
static int do_logs(int sg_fd, unsigned char * resp, int mx_resp_len,
int noisy, const struct opts_t * optsp)
{
int actual_len;
int res;
memset(resp, 0, mx_resp_len);
if (optsp->maxlen > 1)
actual_len = mx_resp_len;
else {
if ((res = sg_ll_log_sense(sg_fd, optsp->do_ppc, optsp->do_sp,
optsp->page_control, optsp->pg_code,
optsp->subpg_code, optsp->paramp,
resp, LOG_SENSE_PROBE_ALLOC_LEN,
noisy, optsp->do_verbose))) {
switch (res) {
case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
case SG_LIB_CAT_UNIT_ATTENTION:
case SG_LIB_CAT_ABORTED_COMMAND:
return res;
default:
return -1;
}
}
actual_len = (resp[2] << 8) + resp[3] + 4;
if ((0 == optsp->do_raw) && (optsp->do_verbose > 1)) {
fprintf(stderr, " Log sense (find length) response:\n");
dStrHex((const char *)resp, LOG_SENSE_PROBE_ALLOC_LEN, 1);
fprintf(stderr, " hence calculated response length=%d\n",
actual_len);
}
/* Some HBAs don't like odd transfer lengths */
if (actual_len % 2)
actual_len += 1;
if (actual_len > mx_resp_len)
actual_len = mx_resp_len;
}
if ((res = sg_ll_log_sense(sg_fd, optsp->do_ppc, optsp->do_sp,
optsp->page_control, optsp->pg_code,
optsp->subpg_code, optsp->paramp,
resp, actual_len, noisy, optsp->do_verbose))) {
switch (res) {
case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
case SG_LIB_CAT_UNIT_ATTENTION:
case SG_LIB_CAT_ABORTED_COMMAND:
return res;
default:
return -1;
}
}
if ((0 == optsp->do_raw) && (optsp->do_verbose > 1)) {
fprintf(stderr, " Log sense response:\n");
dStrHex((const char *)resp, actual_len, 1);
}
return 0;
}
static void show_page_name(int pg_code, int subpg_code,
struct sg_simple_inquiry_resp * inq_dat)
{
int done;
char b[64];
memset(b, 0, sizeof(b));
/* first process log pages that do not depend on peripheral type */
if (NOT_SUBPG_LOG == subpg_code)
snprintf(b, sizeof(b) - 1, " 0x%02x ", pg_code);
else
snprintf(b, sizeof(b) - 1, " 0x%02x,0x%02x ", pg_code,
subpg_code);
done = 1;
if ((NOT_SUBPG_LOG == subpg_code) || (ALL_SUBPG_LOG == subpg_code)) {
switch (pg_code) {
case ALL_PAGE_LPAGE: printf("%sSupported log pages", b); break;
case BUFF_OVER_UNDER_LPAGE:
printf("%sBuffer over-run/under-run", b);
break;
case WRITE_ERR_LPAGE: printf("%sError counters (write)", b); break;
case READ_ERR_LPAGE: printf("%sError counters (read)", b); break;
case READ_REV_ERR_LPAGE:
printf("%sError counters (read reverse)", b);
break;
case VERIFY_ERR_LPAGE: printf("%sError counters (verify)", b); break;
case NON_MEDIUM_LPAGE: printf("%sNon-medium errors", b); break;
case LAST_N_ERR_LPAGE: printf("%sLast n error events", b); break;
case LAST_N_DEFERRED_LPAGE: printf("%sLast n deferred errors or "
"asynchronous events", b); break;
case TEMPERATURE_LPAGE: printf("%sTemperature", b); break;
case START_STOP_LPAGE: printf("%sStart-stop cycle counter", b); break;
case APP_CLIENT_LPAGE: printf("%sApplication client", b); break;
case SELF_TEST_LPAGE: printf("%sSelf-test results", b); break;
case PORT_SPECIFIC_LPAGE: printf("%sProtocol specific port", b); break;
case GSP_LPAGE:
printf("%sGeneral statistics and performance", b);
break;
case IE_LPAGE: printf("%sInformational exceptions (SMART)", b); break;
default : done = 0; break;
}
if (done) {
if (ALL_SUBPG_LOG == subpg_code)
printf(" and subpages\n");
else
printf("\n");
return;
}
}
if ((GSP_LPAGE == pg_code) && (subpg_code > 0) && (subpg_code < 32)) {
printf("%sGroup statistics and performance (%d)\n", b, subpg_code);
return;
}
if (subpg_code > 0) {
printf("%s??\n", b);
return;
}
done = 1;
switch (inq_dat->peripheral_type) {
case 0: case 4: case 7: case 0xe:
/* disk (direct access) type devices */
{
switch (pg_code) {
case 0x8:
printf("%sFormat status (sbc-2)\n", b);
break;
case 0x15:
printf("%sBackground scan results (sbc-3)\n", b);
break;
case 0x17:
printf("%sNon-volatile cache (sbc-2)\n", b);
break;
case 0x30:
printf("%sPerformance counters (Hitachi)\n", b);
break;
case 0x37:
printf("%sCache (Seagate), Miscellaneous (Hitachi)\n", b);
break;
case 0x3e:
printf("%sFactory (Seagate/Hitachi)\n", b);
break;
default:
done = 0;
break;
}
}
break;
case 1: case 2:
/* tape (streaming) and printer (obsolete) devices */
{
switch (pg_code) {
case 0xc:
printf("%sSequential access device (ssc-2)\n", b);
break;
case 0x14:
printf("%sDevice statistics (ssc-3)\n", b);
break;
case 0x16:
printf("%sTape diagnostic (ssc-3)\n", b);
break;
case TAPE_ALERT_LPAGE:
printf("%sTapeAlert (ssc-2)\n", b);
break;
default:
done = 0;
break;
}
}
break;
case 8:
/* medium changer type devices */
{
switch (pg_code) {
case 0x14:
printf("%sMedia changer statistics (smc-3)\n", b);
break;
case 0x15:
printf("%sElement statistics (smc-3)\n", b);
break;
case 0x2e:
printf("%sTapeAlert (smc-3)\n", b);
break;
default:
done = 0;
break;
}
}
break;
case 0x12: /* Automation Device interface (ADC) */
{
switch (pg_code) {
case 0x11:
printf("%sDTD status (adc)\n", b);
break;
case 0x12:
printf("%sTape alert response (adc)\n", b);
break;
case 0x13:
printf("%sRequested recovery (adc)\n", b);
break;
case 0x14:
printf("%sDevice statistics (adc)\n", b);
break;
case 0x15:
printf("%sService buffers information (adc)\n", b);
break;
default:
done = 0;
break;
}
}
break;
default:
done = 0;
break;
}
if (done)
return;
printf("%s??\n", b);
}
static void get_pcb_str(int pcb, char * outp, int maxoutlen)
{
char buff[PCB_STR_LEN];
int n;
n = sprintf(buff, "du=%d [ds=%d] tsd=%d etc=%d ", ((pcb & 0x80) ? 1 : 0),
((pcb & 0x40) ? 1 : 0), ((pcb & 0x20) ? 1 : 0),
((pcb & 0x10) ? 1 : 0));
if (pcb & 0x10)
n += sprintf(buff + n, "tmc=%d ", ((pcb & 0xc) >> 2));
#if 1
n += sprintf(buff + n, "format+linking=%d [0x%.2x]", pcb & 3,
pcb);
#else
if (pcb & 0x1)
n += sprintf(buff + n, "lbin=%d ", ((pcb & 0x2) >> 1));
n += sprintf(buff + n, "lp=%d [0x%.2x]", pcb & 0x1, pcb);
#endif
if (outp && (n < maxoutlen)) {
memcpy(outp, buff, n);
outp[n] = '\0';
} else if (outp && (maxoutlen > 0))
outp[0] = '\0';
}
static void show_buffer_under_overrun_page(unsigned char * resp, int len,
int show_pcb)
{
int k, j, num, pl, count_basis, cause, pcb;
unsigned char * ucp;
unsigned char * xp;
unsigned long long ull;
char pcb_str[PCB_STR_LEN];
printf("Buffer over-run/under-run page\n");
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pl = ucp[3] + 4;
count_basis = (ucp[1] >> 5) & 0x7;
cause = (ucp[1] >> 1) & 0xf;
if ((0 == count_basis) && (0 == cause))
printf("Count basis+Cause both undefined(0), unsupported??");
else {
printf(" Count basis: ");
switch (count_basis) {
case 0 : printf("undefined"); break;
case 1 : printf("per command"); break;
case 2 : printf("per failed reconnect"); break;
case 3 : printf("per unit of time"); break;
default: printf("reserved [0x%x]", count_basis); break;
}
printf(", Cause: ");
switch (cause) {
case 0 : printf("undefined"); break;
case 1 : printf("bus busy"); break;
case 2 : printf("transfer rate too slow"); break;
default: printf("reserved [0x%x]", cause); break;
}
printf(", Type: ");
if (ucp[1] & 1)
printf("over-run");
else
printf("under-run");
printf(", count");
k = pl - 4;
xp = ucp + 4;
if (k > (int)sizeof(ull)) {
xp += (k - sizeof(ull));
k = sizeof(ull);
}
ull = 0;
for (j = 0; j < k; ++j) {
if (j > 0)
ull <<= 8;
ull |= xp[j];
}
printf(" = %" PRIu64 "", ull);
}
if (show_pcb) {
pcb = ucp[2];
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
num -= pl;
ucp += pl;
}
}
static void show_error_counter_page(unsigned char * resp, int len,
int show_pcb)
{
int k, j, num, pl, pc, pcb;
unsigned char * ucp;
unsigned char * xp;
unsigned long long ull;
char pcb_str[PCB_STR_LEN];
switch(resp[0] & 0x3f) {
case WRITE_ERR_LPAGE:
printf("Write error counter page\n");
break;
case READ_ERR_LPAGE:
printf("Read error counter page\n");
break;
case READ_REV_ERR_LPAGE:
printf("Read Reverse error counter page\n");
break;
case VERIFY_ERR_LPAGE:
printf("Verify error counter page\n");
break;
default:
printf("expecting error counter page, got page = 0x%x\n", resp[0]);
return;
}
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pc = (ucp[0] << 8) | ucp[1];
pcb = ucp[2];
pl = ucp[3] + 4;
switch (pc) {
case 0: printf(" Errors corrected without substantial delay"); break;
case 1: printf(" Errors corrected with possible delays"); break;
case 2: printf(" Total rewrites or rereads"); break;
case 3: printf(" Total errors corrected"); break;
case 4: printf(" Total times correction algorithm processed"); break;
case 5: printf(" Total bytes processed"); break;
case 6: printf(" Total uncorrected errors"); break;
case 0x8009: printf(" Track following errors [Hitachi]"); break;
case 0x8015: printf(" Positioning errors [Hitachi]"); break;
default: printf(" Reserved or vendor specific [0x%x]", pc); break;
}
k = pl - 4;
xp = ucp + 4;
if (k > (int)sizeof(ull)) {
xp += (k - sizeof(ull));
k = sizeof(ull);
}
ull = 0;
for (j = 0; j < k; ++j) {
if (j > 0)
ull <<= 8;
ull |= xp[j];
}
printf(" = %" PRIu64 "", ull);
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
num -= pl;
ucp += pl;
}
}
static void show_non_medium_error_page(unsigned char * resp, int len,
int show_pcb)
{
int k, j, num, pl, pc, pcb;
unsigned char * ucp;
unsigned char * xp;
unsigned long long ull;
char pcb_str[PCB_STR_LEN];
printf("Non-medium error page\n");
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pc = (ucp[0] << 8) | ucp[1];
pcb = ucp[2];
pl = ucp[3] + 4;
switch (pc) {
case 0:
printf(" Non-medium error count"); break;
default:
if (pc <= 0x7fff)
printf(" Reserved [0x%x]", pc);
else
printf(" Vendor specific [0x%x]", pc);
break;
}
k = pl - 4;
xp = ucp + 4;
if (k > (int)sizeof(ull)) {
xp += (k - sizeof(ull));
k = sizeof(ull);
}
ull = 0;
for (j = 0; j < k; ++j) {
if (j > 0)
ull <<= 8;
ull |= xp[j];
}
printf(" = %" PRIu64 "", ull);
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
num -= pl;
ucp += pl;
}
}
static void show_last_n_error_page(unsigned char * resp, int len,
int show_pcb)
{
int k, num, pl, pc, pcb;
unsigned char * ucp;
char pcb_str[PCB_STR_LEN];
num = len - 4;
ucp = &resp[0] + 4;
if (num < 4) {
printf("No error events logged\n");
return;
}
printf("Last n error events log page\n");
for (k = num; k > 0; k -= pl, ucp += pl) {
if (k < 3) {
printf("short Last n error events log page\n");
return;
}
pl = ucp[3] + 4;
pc = (ucp[0] << 8) + ucp[1];
pcb = ucp[2];
printf(" Error event %d:\n", pc);
if (pl > 4) {
if ((pcb & 0x1) && (pcb & 0x2)) {
printf(" [binary]:\n");
dStrHex((const char *)ucp + 4, pl - 4, 1);
} else if (pcb & 0x1)
printf(" %.*s\n", pl - 4, (const char *)(ucp + 4));
else {
printf(" [data counter?? (LP bit should be set)]:\n");
dStrHex((const char *)ucp + 4, pl - 4, 1);
}
}
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf(" <%s>\n", pcb_str);
}
}
}
static void show_last_n_deferred_error_page(unsigned char * resp,
int len, int show_pcb)
{
int k, num, pl, pc, pcb;
unsigned char * ucp;
char pcb_str[PCB_STR_LEN];
num = len - 4;
ucp = &resp[0] + 4;
if (num < 4) {
printf("No deferred errors logged\n");
return;
}
printf("Last n deferred errors log page\n");
for (k = num; k > 0; k -= pl, ucp += pl) {
if (k < 3) {
printf("short Last n deferred errors log page\n");
return;
}
pl = ucp[3] + 4;
pc = (ucp[0] << 8) + ucp[1];
pcb = ucp[2];
printf(" Deferred error %d:\n", pc);
dStrHex((const char *)ucp + 4, pl - 4, 1);
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf(" <%s>\n", pcb_str);
}
}
}
static const char * self_test_code[] = {
"default", "background short", "background extended", "reserved",
"aborted background", "foreground short", "foreground extended",
"reserved"};
static const char * self_test_result[] = {
"completed without error",
"aborted by SEND DIAGNOSTIC",
"aborted other than by SEND DIAGNOSTIC",
"unknown error, unable to complete",
"self test completed with failure in test segment (which one unkown)",
"first segment in self test failed",
"second segment in self test failed",
"another segment in self test failed",
"reserved", "reserved", "reserved", "reserved", "reserved", "reserved",
"reserved",
"self test in progress"};
static void show_self_test_page(unsigned char * resp, int len, int show_pcb)
{
int k, num, n, res, pcb;
unsigned char * ucp;
unsigned long long ull;
char pcb_str[PCB_STR_LEN];
num = len - 4;
if (num < 0x190) {
printf("short self-test results page [length 0x%x rather than "
"0x190 bytes]\n", num);
return;
}
printf("Self-test results page\n");
for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) {
pcb = ucp[2];
n = (ucp[6] << 8) | ucp[7];
if ((0 == n) && (0 == ucp[4]))
break;
printf(" Parameter code = %d, accumulated power-on hours = %d\n",
(ucp[0] << 8) | ucp[1], n);
printf(" self-test code: %s [%d]\n",
self_test_code[(ucp[4] >> 5) & 0x7], (ucp[4] >> 5) & 0x7);
res = ucp[4] & 0xf;
printf(" self-test result: %s [%d]\n",
self_test_result[res], res);
if (ucp[5])
printf(" self-test number = %d\n", (int)ucp[5]);
ull = ucp[8]; ull <<= 8; ull |= ucp[9]; ull <<= 8; ull |= ucp[10];
ull <<= 8; ull |= ucp[11]; ull <<= 8; ull |= ucp[12];
ull <<= 8; ull |= ucp[13]; ull <<= 8; ull |= ucp[14];
ull <<= 8; ull |= ucp[15];
if ((0xffffffffffffffffULL != ull) && (res > 0) && ( res < 0xf))
printf(" address of first error = 0x%" PRIx64 "\n", ull);
if (ucp[16] & 0xf)
printf(" sense key = 0x%x, asc = 0x%x, asq = 0x%x",
ucp[16] & 0xf, ucp[17], ucp[18]);
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
}
}
static void show_temperature_page(unsigned char * resp, int len,
int show_pcb, int hdr, int show_unknown)
{
int k, num, extra, pc, pcb;
unsigned char * ucp;
char pcb_str[PCB_STR_LEN];
num = len - 4;
ucp = &resp[0] + 4;
if (num < 4) {
printf("badly formed Temperature log page\n");
return;
}
if (hdr)
printf("Temperature log page\n");
for (k = num; k > 0; k -= extra, ucp += extra) {
if (k < 3) {
printf("short Temperature log page\n");
return;
}
extra = ucp[3] + 4;
pc = (ucp[0] << 8) + ucp[1];
pcb = ucp[2];
if (0 == pc) {
if ((extra > 5) && (k > 5)) {
if (ucp[5] < 0xff)
printf(" Current temperature = %d C", ucp[5]);
else
printf(" Current temperature = <not available>");
}
} else if (1 == pc) {
if ((extra > 5) && (k > 5)) {
if (ucp[5] < 0xff)
printf(" Reference temperature = %d C", ucp[5]);
else
printf(" Reference temperature = <not available>");
}
} else if (show_unknown) {
printf(" unknown parameter code = 0x%x, contents in hex:\n", pc);
dStrHex((const char *)ucp, extra, 1);
} else
continue;
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
}
}
static void show_Start_Stop_page(unsigned char * resp, int len, int show_pcb,
int verbose)
{
int k, num, extra, pc, pcb;
unsigned int n;
unsigned char * ucp;
char pcb_str[PCB_STR_LEN];
num = len - 4;
ucp = &resp[0] + 4;
if (num < 4) {
printf("badly formed Start-stop cycle counter log page\n");
return;
}
printf("Start-stop cycle counter log page\n");
for (k = num; k > 0; k -= extra, ucp += extra) {
if (k < 3) {
printf("short Start-stop cycle counter log page\n");
return;
}
extra = ucp[3] + 4;
pc = (ucp[0] << 8) + ucp[1];
pcb = ucp[2];
switch (pc) {
case 1:
if (10 == extra)
printf(" Date of manufacture, year: %.4s, week: %.2s",
&ucp[4], &ucp[8]);
else if (verbose) {
printf(" Date of manufacture parameter length "
"strange: %d\n", extra - 4);
dStrHex((const char *)ucp, extra, 1);
}
break;
case 2:
if (10 == extra)
printf(" Accounting date, year: %.4s, week: %.2s",
&ucp[4], &ucp[8]);
else if (verbose) {
printf(" Accounting date parameter length strange: %d\n",
extra - 4);
dStrHex((const char *)ucp, extra, 1);
}
break;
case 3:
if (extra > 7) {
n = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
if (0xffffffff == n)
printf(" Specified cycle count over device lifetime "
"= -1");
else
printf(" Specified cycle count over device lifetime "
"= %u", n);
}
break;
case 4:
if (extra > 7) {
n = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
if (0xffffffff == n)
printf(" Accumulated start-stop cycles = -1");
else
printf(" Accumulated start-stop cycles = %u", n);
}
break;
default:
printf(" unknown parameter code = 0x%x, contents in hex:\n", pc);
dStrHex((const char *)ucp, extra, 1);
break;
}
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
}
}
static void show_IE_page(unsigned char * resp, int len, int show_pcb, int full)
{
int k, num, extra, pc, pcb;
unsigned char * ucp;
char pcb_str[PCB_STR_LEN];
char b[256];
num = len - 4;
ucp = &resp[0] + 4;
if (num < 4) {
printf("badly formed Informational Exceptions log page\n");
return;
}
if (full)
printf("Informational Exceptions log page\n");
for (k = num; k > 0; k -= extra, ucp += extra) {
if (k < 3) {
printf("short Informational Exceptions log page\n");
return;
}
extra = ucp[3] + 4;
pc = (ucp[0] << 8) + ucp[1];
pcb = ucp[2];
if (0 == pc) {
if (extra > 5) {
if (full) {
printf(" IE asc = 0x%x, ascq = 0x%x", ucp[4], ucp[5]);
if (ucp[4]) {
if(sg_get_asc_ascq_str(ucp[4], ucp[5], sizeof(b), b))
printf("\n [%s]", b);
}
}
if (extra > 6) {
if (ucp[6] < 0xff)
printf("\n Current temperature = %d C", ucp[6]);
else
printf("\n Current temperature = <not available>");
if (extra > 7) {
if (ucp[7] < 0xff)
printf("\n Threshold temperature = %d C [IBM "
"extension]", ucp[7]);
else
printf("\n Treshold temperature = <not "
"available>");
}
}
}
} else if (full) {
printf(" parameter code = 0x%x, contents in hex:\n", pc);
dStrHex((const char *)ucp, extra, 1);
}
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
}
}
static void show_sas_phy_event_info(int peis, unsigned long val,
unsigned long thresh_val)
{
switch (peis) {
case 0:
printf(" No event\n");
break;
case 0x1:
printf(" Invalid word count: %lu\n", val);
break;
case 0x2:
printf(" Running disparity error count: %lu\n", val);
break;
case 0x3:
printf(" Loss of dword synchronization count: %lu\n", val);
break;
case 0x4:
printf(" Phy reset problem count: %lu\n", val);
break;
case 0x5:
printf(" Elasticity buffer overflow count: %lu\n", val);
break;
case 0x6:
printf(" Received ERROR count: %lu\n", val);
break;
case 0x20:
printf(" Received address frame error count: %lu\n", val);
break;
case 0x21:
printf(" Transmitted OPEN_REJECT abandon count: %lu\n", val);
break;
case 0x22:
printf(" Received OPEN_REJECT abandon count: %lu\n", val);
break;
case 0x23:
printf(" Transmitted OPEN_REJECT retry count: %lu\n", val);
break;
case 0x24:
printf(" Received OPEN_REJECT retry count: %lu\n", val);
break;
case 0x25:
printf(" Received AIP (PARTIAL) count: %lu\n", val);
break;
case 0x26:
printf(" Received AIP (CONNECTION) count: %lu\n", val);
break;
case 0x27:
printf(" Transmitted BREAK count: %lu\n", val);
break;
case 0x28:
printf(" Received BREAK count: %lu\n", val);
break;
case 0x29:
printf(" Break timeout count: %lu\n", val);
break;
case 0x2a:
printf(" Connection count: %lu\n", val);
break;
case 0x2b:
printf(" Peak transmitted pathway blocked count: %lu\n",
val & 0xff);
printf(" Peak value detector threshold: %lu\n",
thresh_val & 0xff);
break;
case 0x2c:
printf(" Peak transmitted arbitration wait time (us to 32767): "
"%lu\n", val & 0xffff);
printf(" Peak value detector threshold: %lu\n",
thresh_val & 0xffff);
break;
case 0x2d:
printf(" Peak arbitration time (us): %lu\n", val);
printf(" Peak value detector threshold: %lu\n", thresh_val);
break;
case 0x2e:
printf(" Peak connection time (us): %lu\n", val);
printf(" Peak value detector threshold: %lu\n", thresh_val);
break;
case 0x40:
printf(" Transmitted SSP frame count: %lu\n", val);
break;
case 0x41:
printf(" Received SSP frame count: %lu\n", val);
break;
case 0x42:
printf(" Transmitted SSP frame error count: %lu\n", val);
break;
case 0x43:
printf(" Received SSP frame error count: %lu\n", val);
break;
case 0x44:
printf(" Transmitted CREDIT_BLOCKED count: %lu\n", val);
break;
case 0x45:
printf(" Received CREDIT_BLOCKED count: %lu\n", val);
break;
case 0x50:
printf(" Transmitted SATA frame count: %lu\n", val);
break;
case 0x51:
printf(" Received SATA frame count: %lu\n", val);
break;
case 0x52:
printf(" SATA flow control buffer overflow count: %lu\n", val);
break;
case 0x60:
printf(" Transmitted SMP frame count: %lu\n", val);
break;
case 0x61:
printf(" Received SMP frame count: %lu\n", val);
break;
case 0x63:
printf(" Received SMP frame error count: %lu\n", val);
break;
default:
break;
}
}
static void show_sas_rel_target_port(unsigned char * ucp, int param_len,
const struct opts_t * optsp)
{
int j, m, n, nphys, pcb, t, sz, spld_len;
unsigned char * vcp;
unsigned long long ull;
unsigned long ul;
char pcb_str[PCB_STR_LEN];
char s[64];
sz = sizeof(s);
pcb = ucp[2];
t = (ucp[0] << 8) | ucp[1];
if (optsp->do_name)
printf("rel_target_port=%d\n", t);
else
printf("relative target port id = %d\n", t);
if (optsp->do_name)
printf(" gen_code=%d\n", ucp[6]);
else
printf(" generation code = %d\n", ucp[6]);
nphys = ucp[7];
if (optsp->do_name)
printf(" num_phys=%d\n", nphys);
else {
printf(" number of phys = %d", nphys);
if ((optsp->do_pcb) && (0 == optsp->do_name)) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
}
for (j = 0, vcp = ucp + 8; j < (param_len - 8);
vcp += spld_len, j += spld_len) {
if (optsp->do_name)
printf(" phy_id=%d\n", vcp[1]);
else
printf(" phy identifier = %d\n", vcp[1]);
spld_len = vcp[3];
if (spld_len < 44)
spld_len = 48; /* in SAS-1 and SAS-1.1 vcp[3]==0 */
else
spld_len += 4;
if (optsp->do_name) {
t = ((0x70 & vcp[4]) >> 4);
printf(" att_dev_type=%d\n", t);
printf(" att_iport_mask=0x%x\n", vcp[6]);
printf(" att_phy_id=%d\n", vcp[24]);
printf(" att_reason=0x%x\n", (vcp[4] & 0xf));
for (n = 0, ull = vcp[16]; n < 8; ++n) {
ull <<= 8; ull |= vcp[16 + n];
}
printf(" att_sas_addr=0x%" PRIx64 "\n", ull);
printf(" att_tport_mask=0x%x\n", vcp[7]);
ul = (vcp[32] << 24) | (vcp[33] << 16) | (vcp[34] << 8) | vcp[35];
printf(" inv_dwords=%ld\n", ul);
ul = (vcp[40] << 24) | (vcp[41] << 16) | (vcp[42] << 8) | vcp[43];
printf(" loss_dword_sync=%ld\n", ul);
printf(" neg_log_lrate=%d\n", 0xf & vcp[5]);
ul = (vcp[44] << 24) | (vcp[45] << 16) | (vcp[46] << 8) | vcp[47];
printf(" phy_reset_probs=%ld\n", ul);
ul = (vcp[36] << 24) | (vcp[37] << 16) | (vcp[38] << 8) | vcp[39];
printf(" running_disparity=%ld\n", ul);
printf(" reason=0x%x\n", (vcp[5] & 0xf0) >> 4);
for (n = 0, ull = vcp[8]; n < 8; ++n) {
ull <<= 8; ull |= vcp[8 + n];
}
printf(" sas_addr=0x%" PRIx64 "\n", ull);
} else {
t = ((0x70 & vcp[4]) >> 4);
switch (t) {
case 0: snprintf(s, sz, "no device attached"); break;
case 1: snprintf(s, sz, "end device"); break;
case 2: snprintf(s, sz, "expander device"); break;
case 3: snprintf(s, sz, "expander device (fanout)"); break;
default: snprintf(s, sz, "reserved [%d]", t); break;
}
printf(" attached device type: %s\n", s);
t = 0xf & vcp[4];
switch (t) {
case 0: snprintf(s, sz, "unknown"); break;
case 1: snprintf(s, sz, "power on"); break;
case 2: snprintf(s, sz, "hard reset"); break;
case 3: snprintf(s, sz, "SMP phy control function"); break;
case 4: snprintf(s, sz, "loss of dword synchronization"); break;
case 5: snprintf(s, sz, "mux mix up"); break;
case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
break;
case 7: snprintf(s, sz, "break timeout timer expired"); break;
case 8: snprintf(s, sz, "phy test function stopped"); break;
case 9: snprintf(s, sz, "expander device reduced functionality");
break;
default: snprintf(s, sz, "reserved [0x%x]", t); break;
}
printf(" attached reason: %s\n", s);
t = (vcp[5] & 0xf0) >> 4;
switch (t) {
case 0: snprintf(s, sz, "unknown"); break;
case 1: snprintf(s, sz, "power on"); break;
case 2: snprintf(s, sz, "hard reset"); break;
case 3: snprintf(s, sz, "SMP phy control function"); break;
case 4: snprintf(s, sz, "loss of dword synchronization"); break;
case 5: snprintf(s, sz, "mux mix up"); break;
case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
break;
case 7: snprintf(s, sz, "break timeout timer expired"); break;
case 8: snprintf(s, sz, "phy test function stopped"); break;
case 9: snprintf(s, sz, "expander device reduced functionality");
break;
default: snprintf(s, sz, "reserved [0x%x]", t); break;
}
printf(" reason: %s\n", s);
t = (0xf & vcp[5]);
switch (t) {
case 0: snprintf(s, sz, "phy enabled; unknown");
break;
case 1: snprintf(s, sz, "phy disabled"); break;
case 2: snprintf(s, sz, "phy enabled; speed negotiation failed");
break;
case 3: snprintf(s, sz, "phy enabled; SATA spinup hold state");
break;
case 4: snprintf(s, sz, "phy enabled; port selector");
break;
case 5: snprintf(s, sz, "phy enabled; reset in progress");
break;
case 6: snprintf(s, sz, "phy enabled; unsupported phy attached");
break;
case 8: snprintf(s, sz, "phy enabled; 1.5 Gbps"); break;
case 9: snprintf(s, sz, "phy enabled; 3 Gbps"); break;
case 0xa: snprintf(s, sz, "phy enabled; 6 Gbps"); break;
default: snprintf(s, sz, "reserved [%d]", t); break;
}
printf(" negotiated logical link rate: %s\n", s);
printf(" attached initiator port: ssp=%d stp=%d smp=%d\n",
!! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2));
printf(" attached target port: ssp=%d stp=%d smp=%d\n",
!! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2));
for (n = 0, ull = vcp[8]; n < 8; ++n) {
ull <<= 8; ull |= vcp[8 + n];
}
printf(" SAS address = 0x%" PRIx64 "\n", ull);
for (n = 0, ull = vcp[16]; n < 8; ++n) {
ull <<= 8; ull |= vcp[16 + n];
}
printf(" attached SAS address = 0x%" PRIx64 "\n", ull);
printf(" attached phy identifier = %d\n", vcp[24]);
ul = (vcp[32] << 24) | (vcp[33] << 16) | (vcp[34] << 8) | vcp[35];
printf(" Invalid DWORD count = %ld\n", ul);
ul = (vcp[36] << 24) | (vcp[37] << 16) | (vcp[38] << 8) | vcp[39];
printf(" Running disparity error count = %ld\n", ul);
ul = (vcp[40] << 24) | (vcp[41] << 16) | (vcp[42] << 8) | vcp[43];
printf(" Loss of DWORD synchronization = %ld\n", ul);
ul = (vcp[44] << 24) | (vcp[45] << 16) | (vcp[46] << 8) | vcp[47];
printf(" Phy reset problem = %ld\n", ul);
}
if (spld_len > 51) {
int num_ped, peis;
unsigned char * xcp;
unsigned long pvdt;
num_ped = vcp[51];
if (num_ped > 0) {
if (optsp->do_name) {
printf(" phy_event_desc_num=%d\n", num_ped);
return; /* don't decode at this stage */
} else
printf(" Phy event descriptors:\n");
}
xcp = vcp + 52;
for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) {
peis = xcp[3];
ul = (xcp[4] << 24) | (xcp[5] << 16) | (xcp[6] << 8) |
xcp[7];
pvdt = (xcp[8] << 24) | (xcp[9] << 16) | (xcp[10] << 8) |
xcp[11];
show_sas_phy_event_info(peis, ul, pvdt);
}
}
}
}
static int show_protocol_specific_page(unsigned char * resp, int len,
const struct opts_t * optsp)
{
int k, num, param_len;
unsigned char * ucp;
num = len - 4;
if (optsp->do_name)
printf("log_page=0x%x\n", PORT_SPECIFIC_LPAGE);
for (k = 0, ucp = resp + 4; k < num; ) {
param_len = ucp[3] + 4;
/* each phy has a 48 byte descriptor but since param_len is
an 8 bit quantity then only the first 5 phys (of, for example,
a 8 phy wide link) can be represented */
if (6 != (0xf & ucp[4]))
return 0; /* only decode SAS log page [sas2r05a] */
if ((0 == k) && (0 == optsp->do_name))
printf("SAS Protocol Specific page\n");
show_sas_rel_target_port(ucp, param_len, optsp);
k += param_len;
ucp += param_len;
}
return 1;
}
static int show_stats_perform_page(unsigned char * resp, int len,
const struct opts_t * optsp)
{
int k, num, n, param_len, param_code, spf, subpg_code, extra;
int pcb, nam;
unsigned char * ucp;
const char * ccp;
unsigned long long ull;
char pcb_str[PCB_STR_LEN];
nam = optsp->do_name;
num = len - 4;
ucp = resp + 4;
spf = !!(resp[0] & 0x40);
subpg_code = spf ? resp[1] : 0;
if (nam) {
printf("log_page=0x%x\n", GSP_LPAGE);
if (subpg_code > 0)
printf("log_subpage=0x%x\n", subpg_code);
}
if (subpg_code > 31)
return 0;
if (0 == subpg_code) { /* General statistics and performance log page */
if (num < 0x5c)
return 0;
for (k = num; k > 0; k -= extra, ucp += extra) {
if (k < 3)
return 0;
param_len = ucp[3];
extra = param_len + 4;
param_code = (ucp[0] << 8) + ucp[1];
pcb = ucp[2];
switch (param_code) {
case 1: /* Statistics and performance log parameter */
ccp = nam ? "parameter_code=1" : "Statistics and performance "
"log parameter";
printf("%s\n", ccp);
for (n = 0, ull = ucp[4]; n < 8; ++n) {
ull <<= 8; ull |= ucp[4 + n];
}
ccp = nam ? "read_commands=" : "number of read commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[12]; n < 8; ++n) {
ull <<= 8; ull |= ucp[12 + n];
}
ccp = nam ? "write_commands=" : "number of write commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[20]; n < 8; ++n) {
ull <<= 8; ull |= ucp[20 + n];
}
ccp = nam ? "lb_received="
: "number of logical blocks received = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[28]; n < 8; ++n) {
ull <<= 8; ull |= ucp[28 + n];
}
ccp = nam ? "lb_transmitted="
: "number of logical blocks transmitted = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[36]; n < 8; ++n) {
ull <<= 8; ull |= ucp[36 + n];
}
ccp = nam ? "read_proc_intervals="
: "read command processing intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[44]; n < 8; ++n) {
ull <<= 8; ull |= ucp[44 + n];
}
ccp = nam ? "write_proc_intervals="
: "write command processing intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[52]; n < 8; ++n) {
ull <<= 8; ull |= ucp[52 + n];
}
ccp = nam ? "weight_rw_commands=" : "weighted number of "
"read commands plus write commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[60]; n < 8; ++n) {
ull <<= 8; ull |= ucp[60 + n];
}
ccp = nam ? "weight_rw_processing=" : "weighted read command "
"processing plus write command processing = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
break;
case 2: /* Idle time log parameter */
ccp = nam ? "parameter_code=2" : "Idle time log parameter";
printf("%s\n", ccp);
for (n = 0, ull = ucp[4]; n < 8; ++n) {
ull <<= 8; ull |= ucp[4 + n];
}
ccp = nam ? "idle_time_intervals=" : "idle time "
"intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
break;
case 3: /* Time interval log parameter */
ccp = nam ? "parameter_code=3" : "Time interval log "
"parameter";
printf("%s\n", ccp);
for (n = 0, ull = ucp[4]; n < 4; ++n) {
ull <<= 8; ull |= ucp[4 + n];
}
ccp = nam ? "time_interval_exp=" : "time interval "
"exponent = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[8]; n < 4; ++n) {
ull <<= 8; ull |= ucp[8 + n];
}
ccp = nam ? "time_interval_int=" : "time interval "
"integer = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
break;
case 4: /* FUA statistics and performance log parameter */
ccp = nam ? "parameter_code=4" : "Force unit access "
"statistics and performance log parameter ";
printf("%s\n", ccp);
for (n = 0, ull = ucp[4]; n < 8; ++n) {
ull <<= 8; ull |= ucp[4 + n];
}
ccp = nam ? "read_fua_commands=" : "number of read FUA "
"commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[12]; n < 8; ++n) {
ull <<= 8; ull |= ucp[12 + n];
}
ccp = nam ? "write_fua_commands=" : "number of write FUA "
"commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[20]; n < 8; ++n) {
ull <<= 8; ull |= ucp[20 + n];
}
ccp = nam ? "read_fua_nv_commands="
: "number of read FUA_NV commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[28]; n < 8; ++n) {
ull <<= 8; ull |= ucp[28 + n];
}
ccp = nam ? "write_fua_nv_commands="
: "number of write FUA_NV commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[36]; n < 8; ++n) {
ull <<= 8; ull |= ucp[36 + n];
}
ccp = nam ? "read_fua_proc_intervals="
: "read FUA command processing intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[44]; n < 8; ++n) {
ull <<= 8; ull |= ucp[44 + n];
}
ccp = nam ? "write_fua_proc_intervals="
: "write FUA command processing intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[52]; n < 8; ++n) {
ull <<= 8; ull |= ucp[52 + n];
}
ccp = nam ? "read_fua_nv_proc_intervals="
: "read FUA_NV command processing intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[60]; n < 8; ++n) {
ull <<= 8; ull |= ucp[60 + n];
}
ccp = nam ? "write_fua_nv_proc_intervals="
: "write FUA_NV command processing intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
break;
default:
if (nam) {
printf("parameter_code=%d\n", param_code);
printf(" unknown=1\n");
} else
fprintf(stderr, "show_performance... unknown parameter "
"code %d\n", param_code);
if (optsp->do_verbose)
dStrHex((const char *)ucp, extra, 1);
break;
}
if ((optsp->do_pcb) && (0 == optsp->do_name)) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf(" <%s>\n", pcb_str);
}
}
} else { /* Group statistics and performance (n) log page */
if (num < 0x34)
return 0;
for (k = num; k > 0; k -= extra, ucp += extra) {
if (k < 3)
return 0;
param_len = ucp[3];
extra = param_len + 4;
param_code = (ucp[0] << 8) + ucp[1];
pcb = ucp[2];
switch (param_code) {
case 1: /* Group n Statistics and performance log parameter */
if (nam)
printf("parameter_code=1\n");
else
printf("Group %d Statistics and performance log "
"parameter\n", subpg_code);
for (n = 0, ull = ucp[4]; n < 8; ++n) {
ull <<= 8; ull |= ucp[4 + n];
}
ccp = nam ? "gn_read_commands=" : "group n number of read "
"commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[12]; n < 8; ++n) {
ull <<= 8; ull |= ucp[12 + n];
}
ccp = nam ? "gn_write_commands=" : "group n number of write "
"commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[20]; n < 8; ++n) {
ull <<= 8; ull |= ucp[20 + n];
}
ccp = nam ? "gn_lb_received="
: "group n number of logical blocks received = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[28]; n < 8; ++n) {
ull <<= 8; ull |= ucp[28 + n];
}
ccp = nam ? "gn_lb_transmitted="
: "group n number of logical blocks transmitted = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[36]; n < 8; ++n) {
ull <<= 8; ull |= ucp[36 + n];
}
ccp = nam ? "gn_read_proc_intervals="
: "group n read command processing intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[44]; n < 8; ++n) {
ull <<= 8; ull |= ucp[44 + n];
}
ccp = nam ? "gn_write_proc_intervals="
: "group n write command processing intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
break;
case 4: /* Group n FUA statistics and performance log parameter */
ccp = nam ? "parameter_code=4" : "Group n force unit access "
"statistics and performance log parameter";
printf("%s\n", ccp);
for (n = 0, ull = ucp[4]; n < 8; ++n) {
ull <<= 8; ull |= ucp[4 + n];
}
ccp = nam ? "gn_read_fua_commands="
: "group n number of read FUA commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[12]; n < 8; ++n) {
ull <<= 8; ull |= ucp[12 + n];
}
ccp = nam ? "gn_write_fua_commands="
: "group n number of write FUA commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[20]; n < 8; ++n) {
ull <<= 8; ull |= ucp[20 + n];
}
ccp = nam ? "gn_read_fua_nv_commands="
: "group n number of read FUA_NV commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[28]; n < 8; ++n) {
ull <<= 8; ull |= ucp[28 + n];
}
ccp = nam ? "gn_write_fua_nv_commands="
: "group n number of write FUA_NV commands = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[36]; n < 8; ++n) {
ull <<= 8; ull |= ucp[36 + n];
}
ccp = nam ? "gn_read_fua_proc_intervals="
: "group n read FUA command processing intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[44]; n < 8; ++n) {
ull <<= 8; ull |= ucp[44 + n];
}
ccp = nam ? "gn_write_fua_proc_intervals=" : "group n write "
"FUA command processing intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[52]; n < 8; ++n) {
ull <<= 8; ull |= ucp[52 + n];
}
ccp = nam ? "gn_read_fua_nv_proc_intervals=" : "group n "
"read FUA_NV command processing intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
for (n = 0, ull = ucp[60]; n < 8; ++n) {
ull <<= 8; ull |= ucp[60 + n];
}
ccp = nam ? "gn_write_fua_nv_proc_intervals=" : "group n "
"write FUA_NV command processing intervals = ";
printf(" %s%" PRIu64 "\n", ccp, ull);
break;
default:
if (nam) {
printf("parameter_code=%d\n", param_code);
printf(" unknown=1\n");
} else
fprintf(stderr, "show_performance... unknown parameter "
"code %d\n", param_code);
if (optsp->do_verbose)
dStrHex((const char *)ucp, extra, 1);
break;
}
if ((optsp->do_pcb) && (0 == optsp->do_name)) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf(" <%s>\n", pcb_str);
}
}
}
return 1;
}
static void show_format_status_page(unsigned char * resp, int len,
int show_pcb)
{
int k, j, num, pl, pc, pcb, all_ff, counter;
unsigned char * ucp;
unsigned char * xp;
unsigned long long ull;
char pcb_str[PCB_STR_LEN];
printf("Format status page (sbc-2) [0x8]\n");
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pc = (ucp[0] << 8) | ucp[1];
pcb = ucp[2];
pl = ucp[3] + 4;
counter = 1;
switch (pc) {
case 0: printf(" Format data out:\n");
counter = 0;
dStrHex((const char *)ucp, pl, 0);
break;
case 1: printf(" Grown defects during certification"); break;
case 2: printf(" Total blocks relocated during format"); break;
case 3: printf(" Total new blocks relocated"); break;
case 4: printf(" Power on minutes since format"); break;
default:
printf(" Unknown Format status code = 0x%x\n", pc);
counter = 0;
dStrHex((const char *)ucp, pl, 0);
break;
}
if (counter) {
k = pl - 4;
xp = ucp + 4;
if (k > (int)sizeof(ull)) {
xp += (k - sizeof(ull));
k = sizeof(ull);
}
ull = 0;
for (all_ff = 0, j = 0; j < k; ++j) {
if (j > 0)
ull <<= 8;
else
all_ff = 1;
ull |= xp[j];
if (0xff != xp[j])
all_ff = 0;
}
if (all_ff)
printf(" <not available>");
else
printf(" = %" PRIu64 "", ull);
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
} else {
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
}
}
num -= pl;
ucp += pl;
}
}
static void show_non_volatile_cache_page(unsigned char * resp, int len,
int show_pcb)
{
int j, num, pl, pc, pcb;
unsigned char * ucp;
char pcb_str[PCB_STR_LEN];
printf("Non-volatile cache page (sbc-2) [0x17]\n");
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pc = (ucp[0] << 8) | ucp[1];
pcb = ucp[2];
pl = ucp[3] + 4;
switch (pc) {
case 0:
printf(" Remaining non-volatile time: ");
if (3 == ucp[4]) {
j = (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
switch (j) {
case 0:
printf("0 (i.e. it is now volatile)\n");
break;
case 1:
printf("<unknown>\n");
break;
case 0xffffff:
printf("<indefinite>\n");
break;
default:
printf("%d minutes [%d:%d]\n", j, (j / 60), (j % 60));
break;
}
} else
printf("<unexpected parameter length=%d>\n", ucp[4]);
break;
case 1:
printf(" Maximum non-volatile time: ");
if (3 == ucp[4]) {
j = (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
switch (j) {
case 0:
printf("0 (i.e. it is now volatile)\n");
break;
case 1:
printf("<reserved>\n");
break;
case 0xffffff:
printf("<indefinite>\n");
break;
default:
printf("%d minutes [%d:%d]\n", j, (j / 60), (j % 60));
break;
}
} else
printf("<unexpected parameter length=%d>\n", ucp[4]);
break;
default:
printf(" Unknown Format status code = 0x%x\n", pc);
dStrHex((const char *)ucp, pl, 0);
break;
}
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
}
num -= pl;
ucp += pl;
}
}
static const char * bms_status[] = {
"no background scans active",
"background scan is active",
"background pre-scan is active",
"background scan halted due to fatal error",
"background scan halted due to a vendor specific pattern of error",
"background scan halted due to medium formatted without P-List",
"background scan halted - vendor specific cause",
"background scan halted due to temperature out of range",
"background scan halted until BM interval timer expires", /* 8 */
};
static const char * reassign_status[] = {
"Reassign status: Reserved [0x0]",
"Reassignment pending receipt of Reassign or Write command",
"Logical block successfully reassigned by device server",
"Reassign status: Reserved [0x3]",
"Reassignment by device server failed",
"Logical block recovered by device server via rewrite",
"Logical block reassigned by application client, has valid data",
"Logical block reassigned by application client, contains no valid data",
"Logical block unsuccessfully reassigned by application client", /* 8 */
};
static void show_background_scan_results_page(unsigned char * resp, int len,
int show_pcb, int verbose)
{
int j, m, num, pl, pc, pcb;
unsigned char * ucp;
char str[PCB_STR_LEN];
printf("Background scan results page (sbc-3) [0x15]\n");
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pc = (ucp[0] << 8) | ucp[1];
pcb = ucp[2];
pl = ucp[3] + 4;
switch (pc) {
case 0:
printf(" Status parameters:\n");
if ((pl < 16) || (num < 16)) {
if (num < 16)
fprintf(stderr, " truncated by response length, "
"expected at least 16 bytes\n");
else
fprintf(stderr, " parameter length >= 16 expected, "
"got %d\n", pl);
break;
}
printf(" Accumulated power on minutes: ");
j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
printf("%d [h:m %d:%d]\n", j, (j / 60), (j % 60));
printf(" Status: ");
j = ucp[9];
if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0])))
printf("%s\n", bms_status[j]);
else
printf("unknown [0x%x] background scan status value\n", j);
j = (ucp[10] << 8) + ucp[11];
printf(" Number of background scans performed: %d\n", j);
j = (ucp[12] << 8) + ucp[13];
#ifdef SG3_UTILS_MINGW
printf(" Background medium scan progress: %g%%\n",
(double)(j * 100.0 / 65536.0));
#else
printf(" Background medium scan progress: %.2f%%\n",
(double)(j * 100.0 / 65536.0));
#endif
j = (ucp[14] << 8) + ucp[15];
if (0 == j)
printf(" Number of background medium scans performed: "
"not reported [0]\n");
else
printf(" Number of background medium scans performed: "
"%d\n", j);
break;
default:
printf(" Medium scan parameter # %d\n", pc);
if ((pl < 24) || (num < 24)) {
if (num < 24)
fprintf(stderr, " truncated by response length, "
"expected at least 24 bytes\n");
else
fprintf(stderr, " parameter length >= 24 expected, "
"got %d\n", pl);
break;
}
printf(" Power on minutes when error detected: ");
j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
printf("%d [%d:%d]\n", j, (j / 60), (j % 60));
j = (ucp[8] >> 4) & 0xf;
if (j <
(int)(sizeof(reassign_status) / sizeof(reassign_status[0])))
printf(" %s\n", reassign_status[j]);
else
printf(" Reassign status: reserved [0x%x]\n", j);
printf(" sense key: %s [sk,asc,ascq: 0x%x,0x%x,0x%x]\n",
sg_get_sense_key_str(ucp[8] & 0xf, sizeof(str), str),
ucp[8] & 0xf, ucp[9], ucp[10]);
printf(" %s\n", sg_get_asc_ascq_str(ucp[9], ucp[10],
sizeof(str), str));
if (verbose) {
printf(" vendor bytes [11 -> 15]: ");
for (m = 0; m < 5; ++m)
printf("0x%02x ", ucp[11 + m]);
printf("\n");
}
printf(" LBA (associated with medium error): 0x");
for (m = 0; m < 8; ++m)
printf("%02x", ucp[16 + m]);
printf("\n");
break;
}
if (show_pcb) {
get_pcb_str(pcb, str, sizeof(str));
printf("\n <%s>\n", str);
}
num -= pl;
ucp += pl;
}
}
static void show_sequential_access_page(unsigned char * resp, int len,
int show_pcb, int verbose)
{
int k, j, num, pl, pc, pcb;
unsigned char * ucp;
unsigned char * xp;
unsigned long long ull, gbytes;
char pcb_str[PCB_STR_LEN];
printf("Sequential access device page (ssc-3)\n");
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pc = (ucp[0] << 8) | ucp[1];
pcb = ucp[2];
pl = ucp[3] + 4;
k = pl - 4;
xp = ucp + 4;
if (k > (int)sizeof(ull)) {
xp += (k - sizeof(ull));
k = sizeof(ull);
}
ull = 0;
for (j = 0; j < k; ++j) {
if (j > 0)
ull <<= 8;
ull |= xp[j];
}
gbytes = ull / 1000000000;
switch (pc) {
case 0:
printf(" Data bytes received with WRITE commands: %" PRIu64
" GB", gbytes);
if (verbose)
printf(" [%" PRIu64 " bytes]", ull);
printf("\n");
break;
case 1:
printf(" Data bytes written to media by WRITE commands: %" PRIu64
" GB", gbytes);
if (verbose)
printf(" [%" PRIu64 " bytes]", ull);
printf("\n");
break;
case 2:
printf(" Data bytes read from media by READ commands: %" PRIu64
" GB", gbytes);
if (verbose)
printf(" [%" PRIu64 " bytes]", ull);
printf("\n");
break;
case 3:
printf(" Data bytes transferred by READ commands: %" PRIu64
" GB", gbytes);
if (verbose)
printf(" [%" PRIu64 " bytes]", ull);
printf("\n");
break;
case 4:
printf(" Native capacity from BOP to EOD: %" PRIu64 " MB\n",
ull);
break;
case 5:
printf(" Native capacity from BOP to EW of current partition: "
"%" PRIu64 " MB\n", ull);
break;
case 6:
printf(" Minimum native capacity from EW to EOP of current "
"partition: %" PRIu64 " MB\n", ull);
break;
case 7:
printf(" Native capacity from BOP to current position: %"
PRIu64 " MB\n", ull);
break;
case 8:
printf(" Maximum native capacity in device object buffer: %"
PRIu64 " MB\n", ull);
break;
case 0x100:
if (ull > 0)
printf(" Cleaning action required\n");
else
printf(" Cleaning action not required (or completed)\n");
if (verbose)
printf(" cleaning value: %" PRIu64 "\n", ull);
break;
default:
if (pc >= 0x8000)
printf(" Vendor specific parameter [0x%x] value: %" PRIu64
"\n", pc, ull);
else
printf(" Reserved parameter [0x%x] value: %" PRIu64 "\n",
pc, ull);
break;
}
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
num -= pl;
ucp += pl;
}
}
static void show_device_stats_page(unsigned char * resp, int len,
int show_pcb)
{
int k, j, num, pl, pc, pcb;
unsigned char * ucp;
unsigned char * xp;
unsigned long long ull;
char pcb_str[PCB_STR_LEN];
printf("Device statistics page (ssc-3 and adc)\n");
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pc = (ucp[0] << 8) | ucp[1];
pcb = ucp[2];
pl = ucp[3] + 4;
if (pc < 0x1000) {
k = pl - 4;
xp = ucp + 4;
if (k > (int)sizeof(ull)) {
xp += (k - sizeof(ull));
k = sizeof(ull);
}
ull = 0;
for (j = 0; j < k; ++j) {
if (j > 0)
ull <<= 8;
ull |= xp[j];
}
switch (pc) {
case 0:
printf(" Lifetime media loads: %" PRIu64 "\n", ull);
break;
case 1:
printf(" Lifetime cleaning operations: %" PRIu64 "\n", ull);
break;
case 2:
printf(" Lifetime power on hours: %" PRIu64 "\n", ull);
break;
case 3:
printf(" Lifetime media motion (head) hours: %" PRIu64 "\n",
ull);
break;
case 4:
printf(" Lifetime metres of tape processed: %" PRIu64 "\n",
ull);
break;
case 5:
printf(" Lifetime media motion (head) hours when "
"incompatible media last loaded: %" PRIu64 "\n", ull);
break;
case 6:
printf(" Lifetime power on hours when last temperature "
"condition occurred: %" PRIu64 "\n", ull);
break;
case 7:
printf(" Lifetime power on hours when last power "
"consumption condition occurred: %" PRIu64 "\n", ull);
break;
case 8:
printf(" Media motion (head) hours since last successful "
"cleaning operation: %" PRIu64 "\n", ull);
break;
case 9:
printf(" Media motion (head) hours since 2nd to last "
"successful cleaning: %" PRIu64 "\n", ull);
break;
case 0xa:
printf(" Media motion (head) hours since 3rd to last "
"successful cleaning: %" PRIu64 "\n", ull);
break;
case 0xb:
printf(" Lifetime power on hours when last operator "
"initiated forced reset\n and/or emergency "
"eject occurred: %" PRIu64 "\n", ull);
break;
default:
printf(" Reserved parameter [0x%x] value: %" PRIu64 "\n",
pc, ull);
break;
}
} else {
switch (pc) {
case 0x1000:
printf(" Media motion (head) hours for each medium type:\n");
printf(" <<to be decoded, dump in hex for now>>:\n");
dStrHex((const char *)ucp, pl, 0);
break;
default:
printf(" Reserved parameter [0x%x], dump in hex:\n", pc);
dStrHex((const char *)ucp, pl, 0);
break;
}
}
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
num -= pl;
ucp += pl;
}
}
static void show_media_stats_page(unsigned char * resp, int len,
int show_pcb)
{
int k, j, num, pl, pc, pcb;
unsigned char * ucp;
unsigned char * xp;
unsigned long long ull;
char pcb_str[PCB_STR_LEN];
printf("Media statistics page (smc-3)\n");
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pc = (ucp[0] << 8) | ucp[1];
pcb = ucp[2];
pl = ucp[3] + 4;
k = pl - 4;
xp = ucp + 4;
if (k > (int)sizeof(ull)) {
xp += (k - sizeof(ull));
k = sizeof(ull);
}
ull = 0;
for (j = 0; j < k; ++j) {
if (j > 0)
ull <<= 8;
ull |= xp[j];
}
switch (pc) {
case 0:
printf(" Number of moves: %" PRIu64 "\n", ull);
break;
case 1:
printf(" Number of picks: %" PRIu64 "\n", ull);
break;
case 2:
printf(" Number of pick retries: %" PRIu64 "\n", ull);
break;
case 3:
printf(" Number of places: %" PRIu64 "\n", ull);
break;
case 4:
printf(" Number of place retries: %" PRIu64 "\n", ull);
break;
case 5:
printf(" Number of volume tags read by volume "
"tag reader: %" PRIu64 "\n", ull);
break;
case 6:
printf(" Number of invalid volume tags returned by "
"volume tag reader: %" PRIu64 "\n", ull);
break;
case 7:
printf(" Number of library door opens: %" PRIu64 "\n", ull);
break;
case 8:
printf(" Number of import/export door opens: %" PRIu64 "\n",
ull);
break;
case 9:
printf(" Number of physical inventory scans: %" PRIu64 "\n",
ull);
break;
case 0xa:
printf(" Number of medium transport unrecovered errors: "
"%" PRIu64 "\n", ull);
break;
case 0xb:
printf(" Number of medium transport recovered errors: "
"%" PRIu64 "\n", ull);
break;
case 0xc:
printf(" Number of medium transport X axis translation "
"unrecovered errors: %" PRIu64 "\n", ull);
break;
case 0xd:
printf(" Number of medium transport X axis translation "
"recovered errors: %" PRIu64 "\n", ull);
break;
case 0xe:
printf(" Number of medium transport Y axis translation "
"unrecovered errors: %" PRIu64 "\n", ull);
break;
case 0xf:
printf(" Number of medium transport Y axis translation "
"recovered errors: %" PRIu64 "\n", ull);
break;
case 0x10:
printf(" Number of medium transport Z axis translation "
"unrecovered errors: %" PRIu64 "\n", ull);
break;
case 0x11:
printf(" Number of medium transport Z axis translation "
"recovered errors: %" PRIu64 "\n", ull);
break;
case 0x12:
printf(" Number of medium transport rotational translation "
"unrecovered errors: %" PRIu64 "\n", ull);
break;
case 0x13:
printf(" Number of medium transport rotational translation "
"recovered errors: %" PRIu64 "\n", ull);
break;
case 0x14:
printf(" Number of medium transport inversion translation "
"unrecovered errors: %" PRIu64 "\n", ull);
break;
case 0x15:
printf(" Number of medium transport inversion translation "
"recovered errors: %" PRIu64 "\n", ull);
break;
case 0x16:
printf(" Number of medium transport auxiliary translation "
"unrecovered errors: %" PRIu64 "\n", ull);
break;
case 0x17:
printf(" Number of medium transport auxiliary translation "
"recovered errors: %" PRIu64 "\n", ull);
break;
default:
printf(" Reserved parameter [0x%x] value: %" PRIu64 "\n",
pc, ull);
break;
}
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
num -= pl;
ucp += pl;
}
}
static void show_element_stats_page(unsigned char * resp, int len,
int show_pcb)
{
int num, pl, pc, pcb;
unsigned int v;
unsigned char * ucp;
char str[PCB_STR_LEN];
printf("Element statistics page (smc-3) [0x15]\n");
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pc = (ucp[0] << 8) | ucp[1];
pcb = ucp[2];
pl = ucp[3] + 4;
printf(" Element address: %d\n", pc);
v = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
printf(" Number of places: %u\n", v);
v = (ucp[8] << 24) + (ucp[9] << 16) + (ucp[10] << 8) + ucp[11];
printf(" Number of place retries: %u\n", v);
v = (ucp[12] << 24) + (ucp[13] << 16) + (ucp[14] << 8) + ucp[15];
printf(" Number of picks: %u\n", v);
v = (ucp[16] << 24) + (ucp[17] << 16) + (ucp[18] << 8) + ucp[19];
printf(" Number of pick retries: %u\n", v);
v = (ucp[20] << 24) + (ucp[21] << 16) + (ucp[22] << 8) + ucp[23];
printf(" Number of determined volume identifiers: %u\n", v);
v = (ucp[24] << 24) + (ucp[25] << 16) + (ucp[26] << 8) + ucp[27];
printf(" Number of unreadable volume identifiers: %u\n", v);
if (show_pcb) {
get_pcb_str(pcb, str, sizeof(str));
printf("\n <%s>\n", str);
}
num -= pl;
ucp += pl;
}
}
static char * tape_alert_strs[] = {
"<parameter code 0, unknown>", /* 0x0 */
"Read warning",
"Write warning",
"Hard error",
"Media",
"Read failure",
"Write failure",
"Media life",
"Not data grade", /* 0x8 */
"Write protect",
"No removal",
"Cleaning media",
"Unsupported format",
"Recoverable mechanical cartridge failure",
"Unrecoverable mechanical cartridge failure",
"Memory chip in cartridge failure",
"Forced eject", /* 0x10 */
"Read only format",
"Tape directory corrupted on load",
"Nearing media life",
"Cleaning required",
"Cleaning requested",
"Expired cleaning media",
"Invalid cleaning tape",
"Retension requested", /* 0x18 */
"Dual port interface error",
"Cooling fan failing",
"Power supply failure",
"Power consumption",
"Drive maintenance",
"Hardware A",
"Hardware B",
"Interface", /* 0x20 */
"Eject media",
"Microcode update fail",
"Drive humidity",
"Drive temperature",
"Drive voltage",
"Predictive failure",
"Diagnostics required",
"Obsolete (28h)", /* 0x28 */
"Obsolete (29h)",
"Obsolete (2Ah)",
"Obsolete (2Bh)",
"Obsolete (2Ch)",
"Obsolete (2Dh)",
"Obsolete (2Eh)",
"Reserved (2Fh)",
"Reserved (30h)", /* 0x30 */
"Reserved (31h)",
"Lost statistics",
"Tape directory invalid at unload",
"Tape system area write failure",
"Tape system area read failure",
"No start of data",
"Loading failure",
"Unrecoverable unload failure", /* 0x38 */
"Automation interface failure",
"Firmware failure",
"WORM medium - integrity check failed",
"WORM medium - overwrite attempted",
};
static void show_tape_alert_ssc_page(unsigned char * resp, int len,
int show_pcb,
const struct opts_t * optsp)
{
int num, pl, pc, pcb, flag;
unsigned char * ucp;
char str[PCB_STR_LEN];
/* N.B. the Tape alert log page for smc-3 is different */
printf("Tape alert page (ssc-3) [0x2e]\n");
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pc = (ucp[0] << 8) | ucp[1];
pcb = ucp[2];
pl = ucp[3] + 4;
flag = ucp[4] & 1;
if (optsp->do_verbose && (0 == optsp->do_brief) && flag)
printf(" >>>> ");
if ((0 == optsp->do_brief) || optsp->do_verbose || flag) {
if (pc < (int)(sizeof(tape_alert_strs) /
sizeof(tape_alert_strs[0])))
printf(" %s: %d\n", tape_alert_strs[pc], flag);
else
printf(" Reserved parameter code 0x%x, flag: %d\n", pc,
flag);
}
if (show_pcb) {
get_pcb_str(pcb, str, sizeof(str));
printf("\n <%s>\n", str);
}
num -= pl;
ucp += pl;
}
}
static void show_seagate_cache_page(unsigned char * resp, int len,
int show_pcb)
{
int k, j, num, pl, pc, pcb;
unsigned char * ucp;
unsigned char * xp;
unsigned long long ull;
char pcb_str[PCB_STR_LEN];
printf("Seagate cache page [0x37]\n");
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pc = (ucp[0] << 8) | ucp[1];
pcb = ucp[2];
pl = ucp[3] + 4;
switch (pc) {
case 0: printf(" Blocks sent to initiator"); break;
case 1: printf(" Blocks received from initiator"); break;
case 2: printf(" Blocks read from cache and sent to initiator"); break;
case 3: printf(" Number of read and write commands whose size "
"<= segment size"); break;
case 4: printf(" Number of read and write commands whose size "
"> segment size"); break;
default: printf(" Unknown Seagate parameter code = 0x%x", pc); break;
}
k = pl - 4;
xp = ucp + 4;
if (k > (int)sizeof(ull)) {
xp += (k - sizeof(ull));
k = sizeof(ull);
}
ull = 0;
for (j = 0; j < k; ++j) {
if (j > 0)
ull <<= 8;
ull |= xp[j];
}
printf(" = %" PRIu64 "", ull);
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
num -= pl;
ucp += pl;
}
}
static void show_seagate_factory_page(unsigned char * resp, int len,
int show_pcb)
{
int k, j, num, pl, pc, pcb, valid;
unsigned char * ucp;
unsigned char * xp;
unsigned long long ull;
char pcb_str[PCB_STR_LEN];
printf("Seagate/Hitachi factory page [0x3e]\n");
num = len - 4;
ucp = &resp[0] + 4;
while (num > 3) {
pc = (ucp[0] << 8) | ucp[1];
pcb = ucp[2];
pl = ucp[3] + 4;
valid = 1;
switch (pc) {
case 0: printf(" number of hours powered up"); break;
case 8: printf(" number of minutes until next internal SMART test");
break;
default:
valid = 0;
printf(" Unknown Seagate/Hitachi parameter code = 0x%x", pc);
break;
}
if (valid) {
k = pl - 4;
xp = ucp + 4;
if (k > (int)sizeof(ull)) {
xp += (k - sizeof(ull));
k = sizeof(ull);
}
ull = 0;
for (j = 0; j < k; ++j) {
if (j > 0)
ull <<= 8;
ull |= xp[j];
}
if (0 == pc)
printf(" = %.2f", ((double)ull) / 60.0 );
else
printf(" = %" PRIu64 "", ull);
}
if (show_pcb) {
get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
printf("\n <%s>\n", pcb_str);
} else
printf("\n");
num -= pl;
ucp += pl;
}
}
static void show_ascii_page(unsigned char * resp, int len,
struct sg_simple_inquiry_resp * inq_dat,
const struct opts_t * optsp)
{
int k, num, done, pg_code, subpg_code, spf;
if (len < 0) {
printf("response has bad length\n");
return;
}
num = len - 4;
done = 1;
spf = !!(resp[0] & 0x40);
pg_code = resp[0] & 0x3f;
subpg_code = spf ? resp[1] : 0;
if ((ALL_PAGE_LPAGE != pg_code ) && (ALL_SUBPG_LOG == subpg_code)) {
printf("Supported subpages for log page=0x%x\n", pg_code);
for (k = 0; k < num; k += 2)
show_page_name((int)resp[4 + k], (int)resp[4 + k + 1],
inq_dat);
return;
}
switch (pg_code) {
case ALL_PAGE_LPAGE:
if (spf) {
printf("Supported log pages and subpages:\n");
for (k = 0; k < num; k += 2)
show_page_name((int)resp[4 + k], (int)resp[4 + k + 1],
inq_dat);
} else {
printf("Supported log pages:\n");
for (k = 0; k < num; ++k)
show_page_name((int)resp[4 + k], 0, inq_dat);
}
break;
case BUFF_OVER_UNDER_LPAGE:
show_buffer_under_overrun_page(resp, len, optsp->do_pcb);
break;
case WRITE_ERR_LPAGE:
case READ_ERR_LPAGE:
case READ_REV_ERR_LPAGE:
case VERIFY_ERR_LPAGE:
show_error_counter_page(resp, len, optsp->do_pcb);
break;
case NON_MEDIUM_LPAGE:
show_non_medium_error_page(resp, len, optsp->do_pcb);
break;
case LAST_N_ERR_LPAGE:
show_last_n_error_page(resp, len, optsp->do_pcb);
break;
case 0x8:
{
switch (inq_dat->peripheral_type) {
case 0: case 4: case 7: case 0xe:
/* disk (direct access) type devices */
show_format_status_page(resp, len, optsp->do_pcb);
break;
default:
done = 0;
break;
}
}
break;
case LAST_N_DEFERRED_LPAGE:
show_last_n_deferred_error_page(resp, len, optsp->do_pcb);
break;
case 0xc:
{
switch (inq_dat->peripheral_type) {
case 1: case 2:
/* tape and (printer) type devices */
show_sequential_access_page(resp, len, optsp->do_pcb,
optsp->do_verbose);
break;
default:
done = 0;
break;
}
}
break;
case TEMPERATURE_LPAGE:
show_temperature_page(resp, len, optsp->do_pcb, 1, 1);
break;
case START_STOP_LPAGE:
show_Start_Stop_page(resp, len, optsp->do_pcb, optsp->do_verbose);
break;
case SELF_TEST_LPAGE:
show_self_test_page(resp, len, optsp->do_pcb);
break;
case 0x14:
{
switch (inq_dat->peripheral_type) {
case 1: case 0x12:
/* tape and adc type devices */
show_device_stats_page(resp, len, optsp->do_pcb);
break;
case 8: /* smc-3 */
show_media_stats_page(resp, len, optsp->do_pcb);
break;
default:
done = 0;
break;
}
}
break;
case 0x15:
{
switch (inq_dat->peripheral_type) {
case 0: case 4: case 7: case 0xe:
/* disk (direct access) type devices */
show_background_scan_results_page(resp, len, optsp->do_pcb,
optsp->do_verbose);
break;
case 8: /* smc-3 */
show_element_stats_page(resp, len, optsp->do_pcb);
break;
default:
done = 0;
break;
}
}
break;
case 0x17:
{
switch (inq_dat->peripheral_type) {
case 0: case 4: case 7: case 0xe:
/* disk (direct access) type devices */
show_non_volatile_cache_page(resp, len, optsp->do_pcb);
break;
default:
done = 0;
break;
}
}
break;
case PORT_SPECIFIC_LPAGE:
done = show_protocol_specific_page(resp, len, optsp);
break;
case GSP_LPAGE: /* defined for subpages 0 to 31 inclusive */
done = show_stats_perform_page(resp, len, optsp);
break;
case TAPE_ALERT_LPAGE:
{
switch (inq_dat->peripheral_type) {
case 1: /* ssc only */
show_tape_alert_ssc_page(resp, len, optsp->do_pcb, optsp);
break;
default:
done = 0;
break;
}
}
break;
case IE_LPAGE:
show_IE_page(resp, len, optsp->do_pcb, 1);
break;
case 0x37:
{
switch (inq_dat->peripheral_type) {
case 0: case 4: case 7: case 0xe:
/* disk (direct access) type devices */
show_seagate_cache_page(resp, len, optsp->do_pcb);
break;
default:
done = 0;
break;
}
}
break;
case 0x3e:
{
switch (inq_dat->peripheral_type) {
case 0: case 4: case 7: case 0xe:
/* disk (direct access) type devices */
show_seagate_factory_page(resp, len, optsp->do_pcb);
break;
default:
done = 0;
break;
}
}
break;
default:
done = 0;
break;
}
if (! done) {
printf("No ascii information for page = 0x%x, here is hex:\n",
resp[0] & 0x3f);
if (len > 128) {
dStrHex((const char *)resp, 64, 1);
printf(" ..... [truncated after 64 of %d bytes (use '-h' to "
"see the rest)]\n", len);
}
else
dStrHex((const char *)resp, len, 1);
}
}
static int fetchTemperature(int sg_fd, unsigned char * resp, int max_len,
struct opts_t * optsp)
{
int len;
int res = 0;
optsp->pg_code = TEMPERATURE_LPAGE;
optsp->subpg_code = NOT_SUBPG_LOG;
res = do_logs(sg_fd, resp, max_len, 0, optsp);
if (0 == res) {
len = (resp[2] << 8) + resp[3] + 4;
if (optsp->do_raw)
dStrRaw((const char *)resp, len);
else if (optsp->do_hex)
dStrHex((const char *)resp, len, 1);
else
show_temperature_page(resp, len, optsp->do_pcb, 0, 0);
}else if (SG_LIB_CAT_NOT_READY == res)
fprintf(stderr, "Device not ready\n");
else {
optsp->pg_code = IE_LPAGE;
res = do_logs(sg_fd, resp, max_len, 0, optsp);
if (0 == res) {
len = (resp[2] << 8) + resp[3] + 4;
if (optsp->do_raw)
dStrRaw((const char *)resp, len);
else if (optsp->do_hex)
dStrHex((const char *)resp, len, 1);
else
show_IE_page(resp, len, 0, 0);
} else
fprintf(stderr, "Unable to find temperature in either log page "
"(temperature or IE)\n");
}
sg_cmds_close_device(sg_fd);
return (res >= 0) ? res : SG_LIB_CAT_OTHER;
}
int main(int argc, char * argv[])
{
int sg_fd, k, pg_len, res, resp_len;
int ret = 0;
struct sg_simple_inquiry_resp inq_out;
struct opts_t opts;
memset(&opts, 0, sizeof(opts));
memset(rsp_buff, 0, sizeof(rsp_buff));
/* N.B. some disks only give data for current cumulative */
opts.page_control = 1;
res = process_cl(&opts, argc, argv);
if (res)
return SG_LIB_SYNTAX_ERROR;
if (opts.do_help) {
usage_for(&opts);
return 0;
}
if (opts.do_version) {
fprintf(stderr, "Version string: %s\n", version_str);
return 0;
}
if (NULL == opts.device_name) {
fprintf(stderr, "No DEVICE argument given\n");
usage_for(&opts);
return SG_LIB_SYNTAX_ERROR;
}
if ((sg_fd = sg_cmds_open_device(opts.device_name, 0 /* rw */,
opts.do_verbose)) < 0) {
if ((sg_fd = sg_cmds_open_device(opts.device_name, 1 /* r0 */,
opts.do_verbose)) < 0) {
fprintf(stderr, "error opening file: %s: %s \n",
opts.device_name, safe_strerror(-sg_fd));
return SG_LIB_FILE_ERROR;
}
}
if (opts.do_list || opts.do_all) {
opts.pg_code = ALL_PAGE_LPAGE;
if ((opts.do_list > 1) || (opts.do_all > 1))
opts.subpg_code = ALL_SUBPG_LOG;
}
if (opts.do_transport) {
if ((opts.pg_code > 0) || (opts.subpg_code > 0) ||
opts.do_temperature) {
fprintf(stderr, "'-T' should not be mixed with options "
"implying other pages\n");
return SG_LIB_FILE_ERROR;
}
opts.pg_code = PORT_SPECIFIC_LPAGE;
}
pg_len = 0;
if (0 == opts.do_raw) {
if (sg_simple_inquiry(sg_fd, &inq_out, 1, opts.do_verbose)) {
fprintf(stderr, "%s doesn't respond to a SCSI INQUIRY\n",
opts.device_name);
sg_cmds_close_device(sg_fd);
return SG_LIB_CAT_OTHER;
} else if ((0 == opts.do_hex) && (0 == opts.do_name))
printf(" %.8s %.16s %.4s\n", inq_out.vendor,
inq_out.product, inq_out.revision);
} else
memset(&inq_out, 0, sizeof(inq_out));
if (1 == opts.do_temperature)
return fetchTemperature(sg_fd, rsp_buff, SHORT_RESP_LEN, &opts);
if (opts.do_select) {
k = sg_ll_log_select(sg_fd, !!(opts.do_pcreset), opts.do_sp,
opts.page_control, opts.pg_code, opts.subpg_code,
NULL, 0, 1, opts.do_verbose);
if (k) {
if (SG_LIB_CAT_NOT_READY == k)
fprintf(stderr, "log_select: device not ready\n");
else if (SG_LIB_CAT_INVALID_OP == k)
fprintf(stderr, "log_select: not supported\n");
else if (SG_LIB_CAT_UNIT_ATTENTION == k)
fprintf(stderr, "log_select: unit attention\n");
else if (SG_LIB_CAT_ABORTED_COMMAND == k)
fprintf(stderr, "log_select: aborted command\n");
}
return (k >= 0) ? k : SG_LIB_CAT_OTHER;
}
resp_len = (opts.maxlen > 0) ? opts.maxlen : MX_ALLOC_LEN;
res = do_logs(sg_fd, rsp_buff, resp_len, 1, &opts);
if (0 == res) {
pg_len = (rsp_buff[2] << 8) + rsp_buff[3];
if ((pg_len + 4) > resp_len) {
printf("Only fetched %d bytes of response (available: %d "
"bytes)\n truncate output\n",
resp_len, pg_len + 4);
pg_len = resp_len - 4;
}
} else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "log_sense: not supported\n");
else if (SG_LIB_CAT_NOT_READY == res)
fprintf(stderr, "log_sense: device not ready\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "log_sense: field in cdb illegal\n");
else if (SG_LIB_CAT_UNIT_ATTENTION == res)
fprintf(stderr, "log_sense: unit attention\n");
else if (SG_LIB_CAT_ABORTED_COMMAND == res)
fprintf(stderr, "log_sense: aborted command\n");
if (0 == opts.do_all) {
if (opts.do_raw)
dStrRaw((const char *)rsp_buff, pg_len + 4);
else if (opts.do_hex > 1)
dStrHex((const char *)rsp_buff, pg_len + 4, 1);
else if (pg_len > 1) {
if (opts.do_hex) {
if (rsp_buff[0] & 0x40)
printf("Log page code=0x%x,0x%x, DS=%d, SPF=1, "
"page_len=0x%x\n", rsp_buff[0] & 0x3f, rsp_buff[1],
!!(rsp_buff[0] & 0x80), pg_len);
else
printf("Log page code=0x%x, DS=%d, SPF=0, page_len=0x%x\n",
rsp_buff[0] & 0x3f, !!(rsp_buff[0] & 0x80), pg_len);
dStrHex((const char *)rsp_buff, pg_len + 4, 1);
}
else
show_ascii_page(rsp_buff, pg_len + 4, &inq_out, &opts);
}
}
ret = res;
if (opts.do_all && (pg_len > 1)) {
int my_len = pg_len;
int spf;
unsigned char parr[1024];
spf = !!(rsp_buff[0] & 0x40);
if (my_len > (int)sizeof(parr)) {
fprintf(stderr, "Unexpectedly large page_len=%d, trim to %d\n",
my_len, (int)sizeof(parr));
my_len = sizeof(parr);
}
memcpy(parr, rsp_buff + 4, my_len);
for (k = 0; k < my_len; ++k) {
if (0 == opts.do_raw)
printf("\n");
opts.pg_code = parr[k] & 0x3f;
if (spf)
opts.subpg_code = parr[++k];
else
opts.subpg_code = NOT_SUBPG_LOG;
res = do_logs(sg_fd, rsp_buff, resp_len, 1, &opts);
if (0 == res) {
pg_len = (rsp_buff[2] << 8) + rsp_buff[3];
if ((pg_len + 4) > resp_len) {
fprintf(stderr, "Only fetched %d bytes of response, "
"truncate output\n", resp_len);
pg_len = resp_len - 4;
}
if (opts.do_raw)
dStrRaw((const char *)rsp_buff, pg_len + 4);
else if (opts.do_hex > 1)
dStrHex((const char *)rsp_buff, pg_len + 4, 1);
else if (opts.do_hex) {
if (rsp_buff[0] & 0x40)
printf("Log page code=0x%x,0x%x, DS=%d, SPF=1, page_"
"len=0x%x\n", rsp_buff[0] & 0x3f, rsp_buff[1],
!!(rsp_buff[0] & 0x80), pg_len);
else
printf("Log page code=0x%x, DS=%d, SPF=0, page_len="
"0x%x\n", rsp_buff[0] & 0x3f,
!!(rsp_buff[0] & 0x80), pg_len);
dStrHex((const char *)rsp_buff, pg_len + 4, 1);
}
else
show_ascii_page(rsp_buff, pg_len + 4, &inq_out, &opts);
} else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "log_sense: page=0x%x,0x%x not supported\n",
opts.pg_code, opts.subpg_code);
else if (SG_LIB_CAT_NOT_READY == res)
fprintf(stderr, "log_sense: device not ready\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "log_sense: field in cdb illegal "
"[page=0x%x,0x%x]\n", opts.pg_code, opts.subpg_code);
else if (SG_LIB_CAT_UNIT_ATTENTION == res)
fprintf(stderr, "log_sense: unit attention\n");
else if (SG_LIB_CAT_ABORTED_COMMAND == res)
fprintf(stderr, "log_sense: aborted command\n");
}
}
sg_cmds_close_device(sg_fd);
return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}