scsiprint.cpp   scsiprint.cpp 
/* /*
* scsiprint.cpp * scsiprint.cpp
* *
* Home page of code is: http://smartmontools.sourceforge.net * Home page of code is: http://www.smartmontools.org
* *
* Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.ne t> * Copyright (C) 2002-11 Bruce Allen
* Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
* Copyright (C) 2003-18 Douglas Gilbert <dgilbert@interlog.com>
* *
* Additional SCSI work: * SPDX-License-Identifier: GPL-2.0-or-later
* Copyright (C) 2003-10 Douglas Gilbert <dgilbert@interlog.com>
*
* 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.
*
* You should have received a copy of the GNU General Public License
* (for example COPYING); if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* This code was originally developed as a Senior Thesis by Michael Cornwell
* at the Concurrent Systems Laboratory (now part of the Storage Systems
* Research Center), Jack Baskin School of Engineering, University of
* California, Santa Cruz. http://ssrc.soe.ucsc.edu/
*
*/ */
#include "config.h"
#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include "config.h"
#include "int64.h"
#include "scsicmds.h" #include "scsicmds.h"
#include "atacmds.h" // smart_command_set #include "atacmds.h" // smart_command_set
#include "dev_interface.h" #include "dev_interface.h"
#include "scsiprint.h" #include "scsiprint.h"
#include "smartctl.h" #include "smartctl.h"
#include "utility.h" #include "utility.h"
#include "sg_unaligned.h"
#define GBUF_SIZE 65535 #define GBUF_SIZE 65532
const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 3441 2011-10-12 17:22:15Z c hrfranke $" const char * scsiprint_c_cvsid = "$Id$"
SCSIPRINT_H_CVSID; SCSIPRINT_H_CVSID;
UINT8 gBuf[GBUF_SIZE]; uint8_t gBuf[GBUF_SIZE];
#define LOG_RESP_LEN 252 #define LOG_RESP_LEN 252
#define LOG_RESP_LONG_LEN ((62 * 256) + 252) #define LOG_RESP_LONG_LEN ((62 * 256) + 252)
#define LOG_RESP_TAPE_ALERT_LEN 0x144 #define LOG_RESP_TAPE_ALERT_LEN 0x144
/* Log pages supported */ /* Log pages supported */
static int gSmartLPage = 0; /* Informational Exceptions log page */ static bool gSmartLPage = false; /* Informational Exceptions log page */
static int gTempLPage = 0; static bool gTempLPage = false;
static int gSelfTestLPage = 0; static bool gSelfTestLPage = false;
static int gStartStopLPage = 0; static bool gStartStopLPage = false;
static int gReadECounterLPage = 0; static bool gReadECounterLPage = false;
static int gWriteECounterLPage = 0; static bool gWriteECounterLPage = false;
static int gVerifyECounterLPage = 0; static bool gVerifyECounterLPage = false;
static int gNonMediumELPage = 0; static bool gNonMediumELPage = false;
static int gLastNErrorLPage = 0; static bool gLastNErrorEvLPage = false;
static int gBackgroundResultsLPage = 0; static bool gBackgroundResultsLPage = false;
static int gProtocolSpecificLPage = 0; static bool gProtocolSpecificLPage = false;
static int gTapeAlertsLPage = 0; static bool gTapeAlertsLPage = false;
static int gSSMediaLPage = 0; static bool gSSMediaLPage = false;
static bool gFormatStatusLPage = false;
static bool gEnviroReportingLPage = false;
static bool gEnviroLimitsLPage = false;
static bool gUtilizationLPage = false;
static bool gPendDefectsLPage = false;
static bool gBackgroundOpLPage = false;
static bool gLPSMisalignLPage = false;
/* Vendor specific log pages */ /* Vendor specific log pages */
static int gSeagateCacheLPage = 0; static bool gSeagateCacheLPage = false;
static int gSeagateFactoryLPage = 0; static bool gSeagateFactoryLPage = false;
/* Mode pages supported */ /* Mode pages supported */
static int gIecMPage = 1; /* N.B. assume it until we know otherwise */ static bool gIecMPage = true; /* N.B. assume it until we know otherwise */
/* Remember last successful mode sense/select command */ /* Remember last successful mode sense/select command */
static int modese_len = 0; static int modese_len = 0;
static void scsiGetSupportedLogPages(scsi_device * device) /* Remember this value from the most recent INQUIRY */
static int scsi_version;
#define SCSI_VERSION_SPC_4 0x6
#define SCSI_VERSION_SPC_5 0x7
#define SCSI_VERSION_HIGHEST SCSI_VERSION_SPC_5
/* T10 vendor identification. Should match entry in last Annex of SPC
* drafts and standards (e.g. SPC-4). */
static char scsi_vendor[8+1];
#define T10_VENDOR_SEAGATE "SEAGATE"
#define T10_VENDOR_HITACHI_1 "HITACHI"
#define T10_VENDOR_HITACHI_2 "HL-DT-ST"
#define T10_VENDOR_HITACHI_3 "HGST"
static const char * logSenStr = "Log Sense";
static const char * logSenRspStr = "Log Sense response";
static bool
seagate_or_hitachi(void)
{ {
int i, err; return ((0 == memcmp(scsi_vendor, T10_VENDOR_SEAGATE,
strlen(T10_VENDOR_SEAGATE))) ||
(0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_1,
strlen(T10_VENDOR_HITACHI_1))) ||
(0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_2,
strlen(T10_VENDOR_HITACHI_2))) ||
(0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_3,
strlen(T10_VENDOR_HITACHI_3))));
}
static bool
all_ffs(const uint8_t * bp, int b_len)
{
if ((NULL == bp) || (b_len <= 0))
return false;
for (--b_len; b_len >= 0; --b_len) {
if (0xff != bp[b_len])
return false;
}
return true;
}
static void
scsiGetSupportedLogPages(scsi_device * device)
{
bool got_subpages = false;
int k, bump, err, payload_len, num_unreported, num_unreported_spg;
const uint8_t * up;
uint8_t sup_lpgs[LOG_RESP_LEN];
memset(gBuf, 0, LOG_RESP_LEN);
if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf, if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf,
LOG_RESP_LEN, 0))) { LOG_RESP_LEN, 0))) {
if (scsi_debugmode > 0) if (scsi_debugmode > 0)
pout("Log Sense for supported pages failed [%s]\n", pout("%s for supported pages failed [%s]\n", logSenStr,
scsiErrString(err)); scsiErrString(err));
return; /* try one more time with defined length, workaround for the bug #678
found with ST8000NM0075/E001 */
err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf,
LOG_RESP_LEN, 68); /* 64 max pages + 4b header */
if (scsi_debugmode > 0)
pout("%s for supported pages failed (second attempt) [%s]\n",
logSenStr, scsiErrString(err));
if (err)
return;
memcpy(sup_lpgs, gBuf, LOG_RESP_LEN);
} else if ((scsi_version >= SCSI_VERSION_SPC_4) &&
(scsi_version <= SCSI_VERSION_HIGHEST)) {
/* unclear what code T10 will choose for SPC-6 */
memcpy(sup_lpgs, gBuf, LOG_RESP_LEN);
if ((err = scsiLogSense(device, SUPPORTED_LPAGES, SUPP_SPAGE_L_SPAGE,
gBuf, LOG_RESP_LONG_LEN,
-1 /* just single not double fetch */))) {
if (scsi_debugmode > 0)
pout("%s for supported pages and subpages failed [%s]\n",
logSenStr, scsiErrString(err));
} else {
if (0 == memcmp(gBuf, sup_lpgs, LOG_RESP_LEN)) {
if (scsi_debugmode > 0)
pout("%s: %s ignored subpage field, bad\n",
__func__, logSenRspStr);
} else if (! ((0x40 & gBuf[0]) &&
(SUPP_SPAGE_L_SPAGE == gBuf[1]))) {
if (scsi_debugmode > 0)
pout("%s supported subpages is bad SPF=%u SUBPG=%u\n",
logSenRspStr, !! (0x40 & gBuf[0]), gBuf[2]);
} else
got_subpages = true;
}
} else
memcpy(sup_lpgs, gBuf, LOG_RESP_LEN);
if (got_subpages) {
payload_len = sg_get_unaligned_be16(gBuf + 2);
bump = 2;
up = gBuf + LOGPAGEHDRSIZE;
} else {
payload_len = sup_lpgs[3];
bump = 1;
up = sup_lpgs + LOGPAGEHDRSIZE;
} }
for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) { num_unreported_spg = 0;
switch (gBuf[i]) for (num_unreported = 0, k = 0; k < payload_len; k += bump, up += bump) {
uint8_t pg_num = 0x3f & up[0];
uint8_t sub_pg_num = (0x40 & up[0]) ? up[1] : 0;
switch (pg_num)
{ {
case SUPPORTED_LPAGES:
if (! ((NO_SUBPAGE_L_SPAGE == sub_pg_num) ||
(SUPP_SPAGE_L_SPAGE == sub_pg_num))) {
if (scsi_debugmode > 1)
pout("%s: Strange Log page number: 0x0,0x%x\n",
__func__, sub_pg_num);
}
break;
case READ_ERROR_COUNTER_LPAGE: case READ_ERROR_COUNTER_LPAGE:
gReadECounterLPage = 1; gReadECounterLPage = true;
break; break;
case WRITE_ERROR_COUNTER_LPAGE: case WRITE_ERROR_COUNTER_LPAGE:
gWriteECounterLPage = 1; gWriteECounterLPage = true;
break; break;
case VERIFY_ERROR_COUNTER_LPAGE: case VERIFY_ERROR_COUNTER_LPAGE:
gVerifyECounterLPage = 1; gVerifyECounterLPage = true;
break; break;
case LAST_N_ERROR_LPAGE: case LAST_N_ERROR_EVENTS_LPAGE:
gLastNErrorLPage = 1; gLastNErrorEvLPage = true;
break; break;
case NON_MEDIUM_ERROR_LPAGE: case NON_MEDIUM_ERROR_LPAGE:
gNonMediumELPage = 1; gNonMediumELPage = true;
break; break;
case TEMPERATURE_LPAGE: case TEMPERATURE_LPAGE:
gTempLPage = 1; if (NO_SUBPAGE_L_SPAGE == sub_pg_num)
gTempLPage = true;
else if (ENVIRO_REP_L_SPAGE == sub_pg_num)
gEnviroReportingLPage = true;
else if (ENVIRO_LIMITS_L_SPAGE == sub_pg_num)
gEnviroLimitsLPage = true;
else {
++num_unreported;
++num_unreported_spg;
}
break; break;
case STARTSTOP_CYCLE_COUNTER_LPAGE: case STARTSTOP_CYCLE_COUNTER_LPAGE:
gStartStopLPage = 1; if (NO_SUBPAGE_L_SPAGE == sub_pg_num)
gStartStopLPage = true;
else if (UTILIZATION_L_SPAGE == sub_pg_num)
gUtilizationLPage = true;
else {
++num_unreported;
++num_unreported_spg;
}
break; break;
case SELFTEST_RESULTS_LPAGE: case SELFTEST_RESULTS_LPAGE:
gSelfTestLPage = 1; gSelfTestLPage = true;
break; break;
case IE_LPAGE: case IE_LPAGE:
gSmartLPage = 1; gSmartLPage = true;
break; break;
case BACKGROUND_RESULTS_LPAGE: case BACKGROUND_RESULTS_LPAGE:
gBackgroundResultsLPage = 1; if (NO_SUBPAGE_L_SPAGE == sub_pg_num)
gBackgroundResultsLPage = true;
else if (PEND_DEFECTS_L_SPAGE == sub_pg_num)
gPendDefectsLPage = true;
else if (BACKGROUND_OP_L_SPAGE == sub_pg_num)
gBackgroundOpLPage = true;
else if (LPS_MISALIGN_L_SPAGE == sub_pg_num)
gLPSMisalignLPage = true;
else {
++num_unreported;
++num_unreported_spg;
}
break; break;
case PROTOCOL_SPECIFIC_LPAGE: case PROTOCOL_SPECIFIC_LPAGE:
gProtocolSpecificLPage = 1; gProtocolSpecificLPage = true;
break; break;
case TAPE_ALERTS_LPAGE: case TAPE_ALERTS_LPAGE:
gTapeAlertsLPage = 1; gTapeAlertsLPage = true;
break; break;
case SS_MEDIA_LPAGE: case SS_MEDIA_LPAGE:
gSSMediaLPage = 1; gSSMediaLPage = true;
break;
case FORMAT_STATUS_LPAGE:
gFormatStatusLPage = true;
break; break;
case SEAGATE_CACHE_LPAGE: case SEAGATE_CACHE_LPAGE:
gSeagateCacheLPage = 1; if (failuretest_permissive) {
gSeagateCacheLPage = true;
break;
}
if (seagate_or_hitachi())
gSeagateCacheLPage = true;
break; break;
case SEAGATE_FACTORY_LPAGE: case SEAGATE_FACTORY_LPAGE:
gSeagateFactoryLPage = 1; if (failuretest_permissive) {
gSeagateFactoryLPage = true;
break;
}
if (seagate_or_hitachi())
gSeagateFactoryLPage = true;
break; break;
default: default:
if (pg_num < 0x30) { /* don't count VS pages */
++num_unreported;
if (sub_pg_num > 0)
++num_unreported_spg;
}
break; break;
} }
} }
if (scsi_debugmode > 1)
pout("%s: number of unreported (standard) log pages: %d (sub-pages: "
"%d)\n", __func__, num_unreported, num_unreported_spg);
} }
/* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad /* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
(or at least something to report). */ (or at least something to report). */
static int scsiGetSmartData(scsi_device * device, bool attribs) static int
scsiGetSmartData(scsi_device * device, bool attribs)
{ {
UINT8 asc; uint8_t asc;
UINT8 ascq; uint8_t ascq;
UINT8 currenttemp = 0; uint8_t currenttemp = 255;
UINT8 triptemp = 0; uint8_t triptemp = 255;
const char * cp; const char * cp;
int err = 0; int err = 0;
print_on(); print_on();
if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq, if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq,
&currenttemp, &triptemp)) { &currenttemp, &triptemp)) {
/* error message already announced */ /* error message already announced */
print_off(); print_off();
return -1; return -1;
} }
print_off(); print_off();
cp = scsiGetIEString(asc, ascq); cp = scsiGetIEString(asc, ascq);
if (cp) { if (cp) {
skipping to change at line 166 skipping to change at line 311
&currenttemp, &triptemp)) { &currenttemp, &triptemp)) {
/* error message already announced */ /* error message already announced */
print_off(); print_off();
return -1; return -1;
} }
print_off(); print_off();
cp = scsiGetIEString(asc, ascq); cp = scsiGetIEString(asc, ascq);
if (cp) { if (cp) {
err = -2; err = -2;
print_on(); print_on();
pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq); jout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq);
print_off(); print_off();
} else if (gIecMPage) jglb["smart_status"]["passed"] = false;
pout("SMART Health Status: OK\n"); jglb["smart_status"]["scsi"]["asc"] = asc;
jglb["smart_status"]["scsi"]["ascq"] = ascq;
jglb["smart_status"]["scsi"]["ie_string"] = cp;
}
else if (gIecMPage) {
jout("SMART Health Status: OK\n");
jglb["smart_status"]["passed"] = true;
}
if (attribs && !gTempLPage) { if (attribs && !gTempLPage) {
if (currenttemp || triptemp) if (255 == currenttemp)
pout("\n"); pout("Current Drive Temperature: <not available>\n");
if (currenttemp) { else {
if (255 != currenttemp) jout("Current Drive Temperature: %d C\n", currenttemp);
pout("Current Drive Temperature: %d C\n", currenttemp); jglb["temperature"]["current"] = currenttemp;
else
pout("Current Drive Temperature: <not available>\n");
} }
if (triptemp) if (255 == triptemp)
pout("Drive Trip Temperature: %d C\n", triptemp); pout("Drive Trip Temperature: <not available>\n");
else {
jout("Drive Trip Temperature: %d C\n", triptemp);
jglb["temperature"]["drive_trip"] = triptemp;
}
} }
pout("\n");
return err; return err;
} }
// Returns number of logged errors or zero if none or -1 if fetching // Returns number of logged errors or zero if none or -1 if fetching
// TapeAlerts fails // TapeAlerts fails
static const char * const severities = "CWI"; static const char * const severities = "CWI";
static int scsiGetTapeAlertsData(scsi_device * device, int peripheral_type) static int
scsiGetTapeAlertsData(scsi_device * device, int peripheral_type)
{ {
unsigned short pagelength; unsigned short pagelength;
unsigned short parametercode; unsigned short parametercode;
int i, err; int i, err;
const char *s; const char *s;
const char *ts; const char *ts;
int failures = 0; int failures = 0;
print_on(); print_on();
if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, 0, gBuf, if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, 0, gBuf,
LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) { LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) {
pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err)); pout("%s Failed [%s]\n", __func__, scsiErrString(err));
print_off(); print_off();
return -1; return -1;
} }
if (gBuf[0] != 0x2e) { if (gBuf[0] != 0x2e) {
pout("TapeAlerts Log Sense Failed\n"); pout("TapeAlerts %s Failed\n", logSenStr);
print_off(); print_off();
return -1; return -1;
} }
pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3]; pagelength = sg_get_unaligned_be16(gBuf + 2);
for (s=severities; *s; s++) { for (s=severities; *s; s++) {
for (i = 4; i < pagelength; i += 5) { for (i = 4; i < pagelength; i += 5) {
parametercode = (unsigned short) gBuf[i] << 8 | gBuf[i+1]; parametercode = sg_get_unaligned_be16(gBuf + i);
if (gBuf[i + 4]) { if (gBuf[i + 4]) {
ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ? ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ?
scsiTapeAlertsChangerDevice(parametercode) : scsiTapeAlertsChangerDevice(parametercode) :
scsiTapeAlertsTapeDevice(parametercode); scsiTapeAlertsTapeDevice(parametercode);
if (*ts == *s) { if (*ts == *s) {
if (!failures) if (!failures)
pout("TapeAlert Errors (C=Critical, W=Warning, I=Informa pout("TapeAlert Errors (C=Critical, W=Warning, "
tional):\n"); "I=Informational):\n");
pout("[0x%02x] %s\n", parametercode, ts); pout("[0x%02x] %s\n", parametercode, ts);
failures += 1; failures += 1;
} }
} }
} }
} }
print_off(); print_off();
if (! failures) if (! failures)
pout("TapeAlert: OK\n"); pout("TapeAlert: OK\n");
return failures; return failures;
} }
static void scsiGetStartStopData(scsi_device * device) static void
scsiGetStartStopData(scsi_device * device)
{ {
UINT32 u; int err, len, k, extra;
int err, len, k, extra, pc;
unsigned char * ucp; unsigned char * ucp;
if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf, if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf,
LOG_RESP_LEN, 0))) { LOG_RESP_LEN, 0))) {
print_on(); print_on();
pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err)); pout("%s Failed [%s]\n", __func__, scsiErrString(err));
print_off(); print_off();
return; return;
} }
if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) { if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) {
print_on(); print_on();
pout("StartStop Log Sense Failed, page mismatch\n"); pout("StartStop %s Failed, page mismatch\n", logSenStr);
print_off(); print_off();
return; return;
} }
len = ((gBuf[2] << 8) | gBuf[3]); len = sg_get_unaligned_be16(gBuf + 2);
ucp = gBuf + 4; ucp = gBuf + 4;
for (k = len; k > 0; k -= extra, ucp += extra) { for (k = len; k > 0; k -= extra, ucp += extra) {
if (k < 3) { if (k < 3) {
print_on(); print_on();
pout("StartStop Log Sense Failed: short\n"); pout("StartStop %s: short\n", logSenRspStr);
print_off(); print_off();
return; return;
} }
extra = ucp[3] + 4; extra = ucp[3] + 4;
pc = (ucp[0] << 8) + ucp[1]; int pc = sg_get_unaligned_be16(ucp + 0);
uint32_t u = (extra > 7) ? sg_get_unaligned_be32(ucp + 4) : 0;
bool is_all_ffs = (extra > 7) ? all_ffs(ucp + 4, 4) : false;
switch (pc) { switch (pc) {
case 1: case 1:
if (10 == extra) if (10 == extra)
pout("Manufactured in week %.2s of year %.4s\n", ucp + 8, pout("Manufactured in week %.2s of year %.4s\n", ucp + 8,
ucp + 4); ucp + 4);
break; break;
case 2: case 2:
/* ignore Accounting date */ /* ignore Accounting date */
break; break;
case 3: case 3:
if (extra > 7) { if ((extra > 7) && (! is_all_ffs))
u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7]; pout("Specified cycle count over device lifetime: %u\n", u);
if (0xffffffff != u)
pout("Specified cycle count over device lifetime: %u\n",
u);
}
break; break;
case 4: case 4:
if (extra > 7) { if ((extra > 7) && (! is_all_ffs))
u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7]; pout("Accumulated start-stop cycles: %u\n", u);
if (0xffffffff != u)
pout("Accumulated start-stop cycles: %u\n", u);
}
break; break;
case 5: case 5:
if (extra > 7) { if ((extra > 7) && (! is_all_ffs))
u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7]; pout("Specified load-unload count over device lifetime: "
if (0xffffffff != u) "%u\n", u);
pout("Specified load-unload count over device "
"lifetime: %u\n", u);
}
break; break;
case 6: case 6:
if (extra > 7) { if ((extra > 7) && (! is_all_ffs))
u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7]; pout("Accumulated load-unload cycles: %u\n", u);
if (0xffffffff != u)
pout("Accumulated load-unload cycles: %u\n", u);
}
break; break;
default: default:
/* ignore */ /* ignore */
break; break;
} }
} }
} }
/* PENDING_DEFECTS_SUBPG [0x15,0x1] introduced: SBC-4 */
static void
scsiPrintPendingDefectsLPage(scsi_device * device)
{
int num, pl, pc, err;
uint32_t count;
const uint8_t * bp;
static const char * pDefStr = "Pending Defects";
static const char * jname = "pending_defects";
static void scsiPrintGrownDefectListLen(scsi_device * device) if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE,
PEND_DEFECTS_L_SPAGE, gBuf, LOG_RESP_LONG_LEN,
0))) {
print_on();
pout("%s Failed [%s]\n", __func__, scsiErrString(err));
print_off();
return;
}
if (((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) &&
(gBuf[1] != PEND_DEFECTS_L_SPAGE)) {
print_on();
pout("%s %s, page mismatch\n", pDefStr, logSenRspStr);
print_off();
return;
}
num = sg_get_unaligned_be16(gBuf + 2);
if (num > LOG_RESP_LONG_LEN) {
print_on();
pout("%s %s too long\n", pDefStr, logSenRspStr);
print_off();
return;
}
bp = gBuf + 4;
while (num > 3) {
pc = sg_get_unaligned_be16(bp + 0);
pl = bp[3] + 4;
switch (pc) {
case 0x0:
printf(" Pending defect count:");
if ((pl < 8) || (num < 8)) {
print_on();
pout("%s truncated descriptor\n", pDefStr);
print_off();
return;
}
count = sg_get_unaligned_be32(bp + 4);
jglb[jname]["count"] = count;
if (0 == count)
jout("0 %s\n", pDefStr);
else if (1 == count)
jout("1 Pending Defect, LBA and accumulated_power_on_hours "
"follow\n");
else
jout("%u %s: index, LBA and accumulated_power_on_hours "
"follow\n", count, pDefStr);
break;
default:
if ((pl < 16) || (num < 16)) {
print_on();
pout("%s truncated descriptor\n", pDefStr);
print_off();
return;
}
jout(" %4d: 0x%-16" PRIx64 ", %5u\n", pc,
sg_get_unaligned_be64(bp + 8), sg_get_unaligned_be32(bp + 4));
jglb[jname][pc]["LBA"] = sg_get_unaligned_be64(bp + 8);
jglb[jname][pc]["accum_power_on_hours"] =
sg_get_unaligned_be32(bp + 4);
break;
}
num -= pl;
bp += pl;
}
}
static void
scsiPrintGrownDefectListLen(scsi_device * device)
{ {
int err, dl_format, dl_len, div; bool got_rd12;
int err, dl_format;
unsigned int dl_len, div;
static const char * hname = "Read defect list";
memset(gBuf, 0, 4); memset(gBuf, 0, 8);
if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */, if ((err = scsiReadDefect12(device, 0 /* req_plist */, 1 /* req_glist */,
4 /* bytes from index */, gBuf, 4))) { 4 /* format: bytes from index */,
if (scsi_debugmode > 0) { 0 /* addr desc index */, gBuf, 8))) {
if (2 == err) { /* command not supported */
err = scsiReadDefect10(device, 0 /* req_plist */,
1 /* req_glist */,
4 /* format: bytes from index */, gBuf, 4);
if (err) {
if (scsi_debugmode > 0) {
print_on();
pout("%s (10) Failed: %s\n", hname, scsiErrString(err));
print_off();
}
return;
} else
got_rd12 = 0;
} else if (101 == err) /* Defect list not found, leave quietly */
return;
else {
if (scsi_debugmode > 0) {
print_on();
pout("%s (12) Failed: %s\n", hname, scsiErrString(err));
print_off();
}
return;
}
} else
got_rd12 = true;
if (got_rd12) {
int generation = sg_get_unaligned_be16(gBuf + 2);
if ((generation > 1) && (scsi_debugmode > 0)) {
print_on(); print_on();
pout("Read defect list (10) Failed: %s\n", scsiErrString(err)); pout("%s (12): generation=%d\n", hname, generation);
print_off(); print_off();
} }
return; dl_len = sg_get_unaligned_be32(gBuf + 4);
} } else
dl_len = sg_get_unaligned_be16(gBuf + 2);
if (0x8 != (gBuf[1] & 0x18)) { if (0x8 != (gBuf[1] & 0x18)) {
print_on(); print_on();
pout("Read defect list: asked for grown list but didn't get it\n"); pout("%s: asked for grown list but didn't get it\n", hname);
print_off(); print_off();
return; return;
} }
div = 0; div = 0;
dl_format = (gBuf[1] & 0x7); dl_format = (gBuf[1] & 0x7);
switch (dl_format) { switch (dl_format) {
case 0: /* short block */ case 0: /* short block */
div = 4; div = 4;
break; break;
case 1: /* extended bytes from index */
case 2: /* extended physical sector */
/* extended = 1; # might use in future */
div = 8;
break;
case 3: /* long block */ case 3: /* long block */
case 4: /* bytes from index */ case 4: /* bytes from index */
case 5: /* physical sector */ case 5: /* physical sector */
div = 8; div = 8;
break; break;
default: default:
print_on(); print_on();
pout("defect list format %d unknown\n", dl_format); pout("defect list format %d unknown\n", dl_format);
print_off(); print_off();
break; break;
} }
dl_len = (gBuf[2] << 8) + gBuf[3]; if (0 == dl_len) {
if (0 == dl_len) jout("Elements in grown defect list: 0\n\n");
pout("Elements in grown defect list: 0\n"); jglb["scsi_grown_defect_list"] = 0;
}
else { else {
if (0 == div) if (0 == div)
pout("Grown defect list length=%d bytes [unknown " pout("Grown defect list length=%u bytes [unknown "
"number of elements]\n", dl_len); "number of elements]\n\n", dl_len);
else else {
pout("Elements in grown defect list: %d\n", dl_len / div); jout("Elements in grown defect list: %u\n\n", dl_len / div);
jglb["scsi_grown_defect_list"] = dl_len;
}
} }
} }
static void scsiPrintSeagateCacheLPage(scsi_device * device) static void
scsiPrintSeagateCacheLPage(scsi_device * device)
{ {
int k, j, num, pl, pc, err, len; int num, pl, pc, err, len;
unsigned char * ucp; unsigned char * ucp;
unsigned char * xp;
uint64_t ull; uint64_t ull;
static const char * seaCacStr = "Seagate Cache";
if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf, if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf,
LOG_RESP_LEN, 0))) { LOG_RESP_LEN, 0))) {
print_on(); if (scsi_debugmode > 0) {
pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err)); print_on();
print_off(); pout("%s %s Failed: %s\n", seaCacStr, logSenStr,
scsiErrString(err));
print_off();
}
return; return;
} }
if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) { if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) {
print_on(); if (scsi_debugmode > 0) {
pout("Seagate Cache Log Sense Failed, page mismatch\n"); print_on();
print_off(); pout("%s %s, page mismatch\n", seaCacStr, logSenRspStr);
print_off();
}
return; return;
} }
len = ((gBuf[2] << 8) | gBuf[3]) + 4; len = sg_get_unaligned_be16(gBuf + 2) + 4;
num = len - 4; num = len - 4;
ucp = &gBuf[0] + 4; ucp = &gBuf[0] + 4;
while (num > 3) { while (num > 3) {
pc = (ucp[0] << 8) | ucp[1]; pc = sg_get_unaligned_be16(ucp + 0);
pl = ucp[3] + 4; pl = ucp[3] + 4;
switch (pc) { switch (pc) {
case 0: case 1: case 2: case 3: case 4: case 0: case 1: case 2: case 3: case 4:
break; break;
default: default:
if (scsi_debugmode > 0) { if (scsi_debugmode > 0) {
print_on(); print_on();
pout("Vendor (Seagate) cache lpage has unexpected parameter" pout("Vendor (%s) lpage has unexpected parameter, skip\n",
", skip\n"); seaCacStr);
print_off(); print_off();
} }
return; return;
} }
num -= pl; num -= pl;
ucp += pl; ucp += pl;
} }
pout("Vendor (Seagate) cache information\n"); pout("Vendor (%s) information\n", seaCacStr);
num = len - 4; num = len - 4;
ucp = &gBuf[0] + 4; ucp = &gBuf[0] + 4;
while (num > 3) { while (num > 3) {
pc = (ucp[0] << 8) | ucp[1]; pc = sg_get_unaligned_be16(ucp + 0);
pl = ucp[3] + 4; pl = ucp[3] + 4;
switch (pc) { switch (pc) {
case 0: pout(" Blocks sent to initiator"); break; case 0: pout(" Blocks sent to initiator"); break;
case 1: pout(" Blocks received from initiator"); break; case 1: pout(" Blocks received from initiator"); break;
case 2: pout(" Blocks read from cache and sent to initiator"); break; case 2: pout(" Blocks read from cache and sent to initiator"); break;
case 3: pout(" Number of read and write commands whose size " case 3: pout(" Number of read and write commands whose size "
"<= segment size"); break; "<= segment size"); break;
case 4: pout(" Number of read and write commands whose size " case 4: pout(" Number of read and write commands whose size "
"> segment size"); break; "> segment size"); break;
default: pout(" Unknown Seagate parameter code [0x%x]", pc); break; default: pout(" Unknown Seagate parameter code [0x%x]", pc); break;
} }
k = pl - 4; int k = pl - 4;
xp = ucp + 4; const int sz_ull = (int)sizeof(ull);
if (k > (int)sizeof(ull)) { unsigned char * xp = ucp + 4;
xp += (k - (int)sizeof(ull)); if (k > sz_ull) {
k = (int)sizeof(ull); xp += (k - sz_ull);
} k = sz_ull;
ull = 0;
for (j = 0; j < k; ++j) {
if (j > 0)
ull <<= 8;
ull |= xp[j];
} }
pout(" = %"PRIu64"\n", ull); ull = sg_get_unaligned_be(k, xp + 0);
pout(" = %" PRIu64 "\n", ull);
num -= pl; num -= pl;
ucp += pl; ucp += pl;
} }
pout("\n");
} }
static void scsiPrintSeagateFactoryLPage(scsi_device * device) static void
scsiPrintSeagateFactoryLPage(scsi_device * device)
{ {
int k, j, num, pl, pc, len, err, good, bad; int num, pl, pc, len, err, good, bad;
unsigned char * ucp; unsigned char * ucp;
unsigned char * xp;
uint64_t ull; uint64_t ull;
if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf, if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf,
LOG_RESP_LEN, 0))) { LOG_RESP_LEN, 0))) {
print_on(); if (scsi_debugmode > 0) {
pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err)); print_on();
print_off(); pout("%s Failed [%s]\n", __func__, scsiErrString(err));
print_off();
}
return; return;
} }
if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) { if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) {
print_on(); if (scsi_debugmode > 0) {
pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n"); print_on();
print_off(); pout("Seagate/Hitachi Factory %s, page mismatch\n", logSenRspStr);
print_off();
}
return; return;
} }
len = ((gBuf[2] << 8) | gBuf[3]) + 4; len = sg_get_unaligned_be16(gBuf + 2) + 4;
num = len - 4; num = len - 4;
ucp = &gBuf[0] + 4; ucp = &gBuf[0] + 4;
good = 0; good = 0;
bad = 0; bad = 0;
while (num > 3) { while (num > 3) {
pc = (ucp[0] << 8) | ucp[1]; pc = sg_get_unaligned_be16(ucp + 0);
pl = ucp[3] + 4; pl = ucp[3] + 4;
switch (pc) { switch (pc) {
case 0: case 8: case 0: case 8:
++good; ++good;
break; break;
default: default:
++bad; ++bad;
break; break;
} }
num -= pl; num -= pl;
skipping to change at line 490 skipping to change at line 759
pout("\nVendor (Seagate/Hitachi) factory lpage has too many " pout("\nVendor (Seagate/Hitachi) factory lpage has too many "
"unexpected parameters, skip\n"); "unexpected parameters, skip\n");
print_off(); print_off();
} }
return; return;
} }
pout("Vendor (Seagate/Hitachi) factory information\n"); pout("Vendor (Seagate/Hitachi) factory information\n");
num = len - 4; num = len - 4;
ucp = &gBuf[0] + 4; ucp = &gBuf[0] + 4;
while (num > 3) { while (num > 3) {
pc = (ucp[0] << 8) | ucp[1]; pc = sg_get_unaligned_be16(ucp + 0);
pl = ucp[3] + 4; pl = ucp[3] + 4;
good = 0; good = 0;
switch (pc) { switch (pc) {
case 0: pout(" number of hours powered up"); case 0: pout(" number of hours powered up");
good = 1; good = 1;
break; break;
case 8: pout(" number of minutes until next internal SMART test"); case 8: pout(" number of minutes until next internal SMART test");
good = 1; good = 1;
break; break;
default: default:
if (scsi_debugmode > 0) { if (scsi_debugmode > 0) {
print_on(); print_on();
pout("Vendor (Seagate/Hitachi) factory lpage: " pout("Vendor (Seagate/Hitachi) factory lpage: "
"unknown parameter code [0x%x]\n", pc); "unknown parameter code [0x%x]\n", pc);
print_off(); print_off();
} }
break; break;
} }
if (good) { if (good) {
k = pl - 4; int k = pl - 4;
xp = ucp + 4; unsigned char * xp = ucp + 4;
if (k > (int)sizeof(ull)) { if (k > (int)sizeof(ull)) {
xp += (k - (int)sizeof(ull)); xp += (k - (int)sizeof(ull));
k = (int)sizeof(ull); k = (int)sizeof(ull);
} }
ull = 0; ull = sg_get_unaligned_be(k, xp + 0);
for (j = 0; j < k; ++j) { if (0 == pc) {
if (j > 0)
ull <<= 8;
ull |= xp[j];
}
if (0 == pc)
pout(" = %.2f\n", ull / 60.0 ); pout(" = %.2f\n", ull / 60.0 );
jglb["power_on_time"]["hours"] = ull / 60;
jglb["power_on_time"]["minutes"] = ull % 60;
}
else else
pout(" = %"PRIu64"\n", ull); pout(" = %" PRIu64 "\n", ull);
} }
num -= pl; num -= pl;
ucp += pl; ucp += pl;
} }
pout("\n");
} }
static void scsiPrintErrorCounterLog(scsi_device * device) static void
scsiPrintErrorCounterLog(scsi_device * device)
{ {
struct scsiErrorCounter errCounterArr[3]; struct scsiErrorCounter errCounterArr[3];
struct scsiErrorCounter * ecp; struct scsiErrorCounter * ecp;
struct scsiNonMediumError nme;
int found[3] = {0, 0, 0}; int found[3] = {0, 0, 0};
const char * pageNames[3] = {"read: ", "write: ", "verify: "};
double processed_gb;
if (gReadECounterLPage && (0 == scsiLogSense(device, if (gReadECounterLPage && (0 == scsiLogSense(device,
READ_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { READ_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]); scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]);
found[0] = 1; found[0] = 1;
} }
if (gWriteECounterLPage && (0 == scsiLogSense(device, if (gWriteECounterLPage && (0 == scsiLogSense(device,
WRITE_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { WRITE_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]); scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]);
found[1] = 1; found[1] = 1;
skipping to change at line 563 skipping to change at line 829
scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]); scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]);
ecp = &errCounterArr[2]; ecp = &errCounterArr[2];
for (int k = 0; k < 7; ++k) { for (int k = 0; k < 7; ++k) {
if (ecp->gotPC[k] && ecp->counter[k]) { if (ecp->gotPC[k] && ecp->counter[k]) {
found[2] = 1; found[2] = 1;
break; break;
} }
} }
} }
if (found[0] || found[1] || found[2]) { if (found[0] || found[1] || found[2]) {
pout("\nError counter log:\n"); pout("Error counter log:\n");
pout(" Errors Corrected by Total " pout(" Errors Corrected by Total "
"Correction Gigabytes Total\n"); "Correction Gigabytes Total\n");
pout(" ECC rereads/ errors " pout(" ECC rereads/ errors "
"algorithm processed uncorrected\n"); "algorithm processed uncorrected\n");
pout(" fast | delayed rewrites corrected " pout(" fast | delayed rewrites corrected "
"invocations [10^9 bytes] errors\n"); "invocations [10^9 bytes] errors\n");
json::ref jref = jglb["scsi_error_counter_log"];
for (int k = 0; k < 3; ++k) { for (int k = 0; k < 3; ++k) {
if (! found[k]) if (! found[k])
continue; continue;
ecp = &errCounterArr[k]; ecp = &errCounterArr[k];
pout("%s%8"PRIu64" %8"PRIu64" %8"PRIu64" %8"PRIu64" %8"PRIu64, static const char * const pageNames[3] =
pageNames[k], ecp->counter[0], ecp->counter[1], {"read: ", "write: ", "verify: "};
ecp->counter[2], ecp->counter[3], ecp->counter[4]); static const char * jpageNames[3] =
processed_gb = ecp->counter[5] / 1000000000.0; {"read", "write", "verify"};
pout(" %12.3f %8"PRIu64"\n", processed_gb, ecp->counter[6]); jout("%s%8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64
" %8" PRIu64, pageNames[k], ecp->counter[0],
ecp->counter[1], ecp->counter[2], ecp->counter[3],
ecp->counter[4]);
double processed_gb = ecp->counter[5] / 1000000000.0;
jout(" %12.3f %8" PRIu64 "\n", processed_gb,
ecp->counter[6]);
// Error counter log info
jref[jpageNames[k]]["errors_corrected_by_eccfast"] = ecp->counter[0]
;
jref[jpageNames[k]]["errors_corrected_by_eccdelayed"] = ecp->counter
[1];
jref[jpageNames[k]]["errors_corrected_by_rereads_rewrites"] = ecp->c
ounter[2];
jref[jpageNames[k]]["total_errors_corrected"] = ecp->counter[3];
jref[jpageNames[k]]["correction_algorithm_invocations"] = ecp->count
er[4];
jref[jpageNames[k]]["gigabytes_processed"] = strprintf("%.3f", proce
ssed_gb);
jref[jpageNames[k]]["total_uncorrected_errors"] = ecp->counter[6];
} }
} }
else else
pout("\nError Counter logging not supported\n"); pout("Error Counter logging not supported\n");
if (gNonMediumELPage && (0 == scsiLogSense(device, if (gNonMediumELPage && (0 == scsiLogSense(device,
NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
struct scsiNonMediumError nme;
scsiDecodeNonMediumErrPage(gBuf, &nme); scsiDecodeNonMediumErrPage(gBuf, &nme);
if (nme.gotPC0) if (nme.gotPC0)
pout("\nNon-medium error count: %8"PRIu64"\n", nme.counterPC0); pout("\nNon-medium error count: %8" PRIu64 "\n", nme.counterPC0);
if (nme.gotTFE_H) if (nme.gotTFE_H)
pout("Track following error count [Hitachi]: %8"PRIu64"\n", pout("Track following error count [Hitachi]: %8" PRIu64 "\n",
nme.counterTFE_H); nme.counterTFE_H);
if (nme.gotPE_H) if (nme.gotPE_H)
pout("Positioning error count [Hitachi]: %8"PRIu64"\n", pout("Positioning error count [Hitachi]: %8" PRIu64 "\n",
nme.counterPE_H); nme.counterPE_H);
} }
if (gLastNErrorLPage && (0 == scsiLogSense(device, if (gLastNErrorEvLPage &&
LAST_N_ERROR_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { (0 == scsiLogSense(device, LAST_N_ERROR_EVENTS_LPAGE, 0, gBuf,
int num = (gBuf[2] << 8) + gBuf[3] + 4; LOG_RESP_LONG_LEN, 0))) {
int num = sg_get_unaligned_be16(gBuf + 2) + 4;
int truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; int truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
if (truncated) if (truncated)
num = LOG_RESP_LONG_LEN; num = LOG_RESP_LONG_LEN;
unsigned char * ucp = gBuf + 4; unsigned char * ucp = gBuf + 4;
num -= 4; num -= 4;
if (num < 4) if (num < 4)
pout("\nNo error events logged\n"); pout("\nNo error events logged\n");
else { else {
pout("\nLast n error events log page\n"); pout("\nLast n error events log page\n");
for (int k = num, pl; k > 0; k -= pl, ucp += pl) { for (int k = num, pl; k > 0; k -= pl, ucp += pl) {
if (k < 3) { if (k < 3) {
pout(" <<short Last n error events log page>>\n"); pout(" <<short Last n error events log page>>\n");
break; break;
} }
pl = ucp[3] + 4; pl = ucp[3] + 4;
int pc = (ucp[0] << 8) + ucp[1]; int pc = sg_get_unaligned_be16(ucp + 0);
if (pl > 4) { if (pl > 4) {
if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) { if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) {
pout(" Error event %d:\n", pc); pout(" Error event %d:\n", pc);
pout(" [binary]:\n"); pout(" [binary]:\n");
dStrHex((const char *)ucp + 4, pl - 4, 1); dStrHex((const uint8_t *)ucp + 4, pl - 4, 1);
} else if (ucp[2] & 0x1) { } else if (ucp[2] & 0x1) {
pout(" Error event %d:\n", pc); pout(" Error event %d:\n", pc);
pout(" %.*s\n", pl - 4, (const char *)(ucp + 4)); pout(" %.*s\n", pl - 4, (const char *)(ucp + 4));
} else { } else {
if (scsi_debugmode > 0) { if (scsi_debugmode > 0) {
pout(" Error event %d:\n", pc); pout(" Error event %d:\n", pc);
pout(" [data counter??]:\n"); pout(" [data counter??]:\n");
dStrHex((const char *)ucp + 4, pl - 4, 1); dStrHex((const uint8_t *)ucp + 4, pl - 4, 1);
} }
} }
} }
} }
if (truncated) if (truncated)
pout(" >>>> log truncated, fetched %d of %d available " pout(" >>>> log truncated, fetched %d of %d available "
"bytes\n", LOG_RESP_LONG_LEN, truncated); "bytes\n", LOG_RESP_LONG_LEN, truncated);
} }
} }
pout("\n");
} }
static const char * self_test_code[] = { static const char * self_test_code[] = {
"Default ", "Default ",
"Background short", "Background short",
"Background long ", "Background long ",
"Reserved(3) ", "Reserved(3) ",
"Abort background", "Abort background",
"Foreground short", "Foreground short",
"Foreground long ", "Foreground long ",
skipping to change at line 672 skipping to change at line 957
"Reserved(12) ", "Reserved(12) ",
"Reserved(13) ", "Reserved(13) ",
"Reserved(14) ", "Reserved(14) ",
"Self test in progress ..." "Self test in progress ..."
}; };
// See SCSI Primary Commands - 3 (SPC-3) rev 23 (draft) section 7.2.10 . // See SCSI Primary Commands - 3 (SPC-3) rev 23 (draft) section 7.2.10 .
// Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent // Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent
// 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or // 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
// FAILSMART is returned. // FAILSMART is returned.
static int scsiPrintSelfTest(scsi_device * device) static int
scsiPrintSelfTest(scsi_device * device)
{ {
int num, k, n, res, err, durationSec; int num, k, err, durationSec;
int noheader = 1; int noheader = 1;
int retval = 0; int retval = 0;
UINT8 * ucp; uint8_t * ucp;
uint64_t ull=0; uint64_t ull;
struct scsi_sense_disect sense_info;
static const char * hname = "Self-test";
// check if test is running
if (!scsiRequestSense(device, &sense_info) &&
(sense_info.asc == 0x04 && sense_info.ascq == 0x09 &&
sense_info.progress != -1)) {
pout("%s execution status:\t\t%d%% of test remaining\n", hname,
100 - ((sense_info.progress * 100) / 65535));
}
if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf, if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf,
LOG_RESP_SELF_TEST_LEN, 0))) { LOG_RESP_SELF_TEST_LEN, 0))) {
print_on(); print_on();
pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err)); pout("%s: Failed [%s]\n", __func__, scsiErrString(err));
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) { if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
print_on(); print_on();
pout("Self-test Log Sense Failed, page mismatch\n"); pout("%s %s, page mismatch\n", hname, logSenRspStr);
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
// compute page length // compute page length
num = (gBuf[2] << 8) + gBuf[3]; num = sg_get_unaligned_be16(gBuf + 2);
// Log sense page length 0x190 bytes // Log sense page length 0x190 bytes
if (num != 0x190) { if (num != 0x190) {
print_on(); print_on();
pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num); pout("%s %s length is 0x%x not 0x190 bytes\n", hname, logSenStr, num);
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
// loop through the twenty possible entries // loop through the twenty possible entries
for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) { for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) {
int i;
// timestamp in power-on hours (or zero if test in progress) // timestamp in power-on hours (or zero if test in progress)
n = (ucp[6] << 8) | ucp[7]; int n = sg_get_unaligned_be16(ucp + 6);
// The spec says "all 20 bytes will be zero if no test" but // The spec says "all 20 bytes will be zero if no test" but
// DG has found otherwise. So this is a heuristic. // DG has found otherwise. So this is a heuristic.
if ((0 == n) && (0 == ucp[4])) if ((0 == n) && (0 == ucp[4]))
break; break;
// only print header if needed // only print header if needed
if (noheader) { if (noheader) {
pout("\nSMART Self-test log\n"); pout("SMART %s log\n", hname);
pout("Num Test Status segment " pout("Num Test Status segment "
"LifeTime LBA_first_err [SK ASC ASQ]\n"); "LifeTime LBA_first_err [SK ASC ASQ]\n");
pout(" Description number " pout(" Description number "
"(hours)\n"); "(hours)\n");
noheader=0; noheader=0;
} }
// print parameter code (test number) & self-test code text // print parameter code (test number) & self-test code text
pout("#%2d %s", (ucp[0] << 8) | ucp[1], pout("#%2d %s", sg_get_unaligned_be16(ucp + 0),
self_test_code[(ucp[4] >> 5) & 0x7]); self_test_code[(ucp[4] >> 5) & 0x7]);
// check the self-test result nibble, using the self-test results // check the self-test result nibble, using the self-test results
// field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10: // field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10:
int res;
switch ((res = ucp[4] & 0xf)) { switch ((res = ucp[4] & 0xf)) {
case 0x3: case 0x3:
// an unknown error occurred while the device server // an unknown error occurred while the device server
// was processing the self-test and the device server // was processing the self-test and the device server
// was unable to complete the self-test // was unable to complete the self-test
retval|=FAILSMART; retval|=FAILSMART;
break; break;
case 0x4: case 0x4:
// the self-test completed with a failure in a test // the self-test completed with a failure in a test
// segment, and the test segment that failed is not // segment, and the test segment that failed is not
skipping to change at line 781 skipping to change at line 1076
pout(" -"); pout(" -");
// print time that the self-test was completed // print time that the self-test was completed
if (n==0 && res==0xf) if (n==0 && res==0xf)
// self-test in progress // self-test in progress
pout(" NOW"); pout(" NOW");
else else
pout(" %5d", n); pout(" %5d", n);
// construct 8-byte integer address of first failure // construct 8-byte integer address of first failure
for (i = 0; i < 8; i++) { ull = sg_get_unaligned_be64(ucp + 8);
ull <<= 8; bool is_all_ffs = all_ffs(ucp + 8, 8);
ull |= ucp[i+8];
}
// print Address of First Failure, if sensible // print Address of First Failure, if sensible
if ((~(uint64_t)0 != ull) && (res > 0) && (res < 0xf)) { if ((! is_all_ffs) && (res > 0) && (res < 0xf)) {
char buff[32]; char buff[32];
// was hex but change to decimal to conform with ATA // was hex but change to decimal to conform with ATA
snprintf(buff, sizeof(buff), "%"PRIu64, ull); snprintf(buff, sizeof(buff), "%" PRIu64, ull);
// snprintf(buff, sizeof(buff), "0x%"PRIx64, ull); // snprintf(buff, sizeof(buff), "0x%" PRIx64, ull);
pout("%18s", buff); pout("%18s", buff);
} else } else
pout(" -"); pout(" -");
// if sense key nonzero, then print it, along with // if sense key nonzero, then print it, along with
// additional sense code and additional sense code qualifier // additional sense code and additional sense code qualifier
if (ucp[16] & 0xf) if (ucp[16] & 0xf)
pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]); pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]);
else else
pout(" [- - -]\n"); pout(" [- - -]\n");
} }
// if header never printed, then there was no output // if header never printed, then there was no output
if (noheader) if (noheader)
pout("No self-tests have been logged\n"); pout("No %ss have been logged\n", hname);
else else
pout("\n");
if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
modese_len)) && (durationSec > 0)) { modese_len)) && (durationSec > 0)) {
pout("Long (extended) Self Test duration: %d seconds " pout("\nLong (extended) %s duration: %d seconds "
"[%.1f minutes]\n", durationSec, durationSec / 60.0); "[%.1f minutes]\n", hname, durationSec, durationSec / 60.0);
} }
pout("\n");
return retval; return retval;
} }
static const char * bms_status[] = { static const char * bms_status[] = {
"no scans active", "no scans active",
"scan is active", "scan is active",
"pre-scan is active", "pre-scan is active",
"halted due to fatal error", "halted due to fatal error",
"halted due to a vendor specific pattern of error", "halted due to a vendor specific pattern of error",
"halted due to medium formatted without P-List", "halted due to medium formatted without P-List",
skipping to change at line 845 skipping to change at line 1138
"Recovered via rewrite in-place", "Recovered via rewrite in-place",
"Reassigned by app, has valid data", "Reassigned by app, has valid data",
"Reassigned by app, has no valid data", "Reassigned by app, has no valid data",
"Unsuccessfully reassigned by app", /* 8 */ "Unsuccessfully reassigned by app", /* 8 */
}; };
// See SCSI Block Commands - 3 (SBC-3) rev 6 (draft) section 6.2.2 . // See SCSI Block Commands - 3 (SBC-3) rev 6 (draft) section 6.2.2 .
// Returns 0 if ok else FAIL* bitmask. Note can have a status entry // Returns 0 if ok else FAIL* bitmask. Note can have a status entry
// and up to 2048 events (although would hope to have less). May set // and up to 2048 events (although would hope to have less). May set
// FAILLOG if serious errors detected (in the future). // FAILLOG if serious errors detected (in the future).
static int scsiPrintBackgroundResults(scsi_device * device) static int
scsiPrintBackgroundResults(scsi_device * device)
{ {
int num, j, m, err, pc, pl, truncated; int num, j, m, err, truncated;
int noheader = 1; int noheader = 1;
int firstresult = 1; int firstresult = 1;
int retval = 0; int retval = 0;
UINT8 * ucp; uint8_t * ucp;
static const char * hname = "Background scan results";
if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf, if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf,
LOG_RESP_LONG_LEN, 0))) { LOG_RESP_LONG_LEN, 0))) {
print_on(); print_on();
pout("scsiPrintBackgroundResults Failed [%s]\n", scsiErrString(err)); pout("%s Failed [%s]\n", __func__, scsiErrString(err));
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) { if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) {
print_on(); print_on();
pout("Background scan results Log Sense Failed, page mismatch\n"); pout("%s %s, page mismatch\n", hname, logSenRspStr);
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
// compute page length // compute page length
num = (gBuf[2] << 8) + gBuf[3] + 4; num = sg_get_unaligned_be16(gBuf + 2) + 4;
if (num < 20) { if (num < 20) {
print_on(); print_on();
pout("Background scan results Log Sense length is %d, no scan " pout("%s %s length is %d, no scan status\n", hname, logSenStr, num);
"status\n", num);
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
if (truncated) if (truncated)
num = LOG_RESP_LONG_LEN; num = LOG_RESP_LONG_LEN;
ucp = gBuf + 4; ucp = gBuf + 4;
num -= 4; num -= 4;
while (num > 3) { while (num > 3) {
pc = (ucp[0] << 8) | ucp[1]; int pc = sg_get_unaligned_be16(ucp + 0);
// pcb = ucp[2]; // pcb = ucp[2];
pl = ucp[3] + 4; int pl = ucp[3] + 4;
switch (pc) { switch (pc) {
case 0: case 0:
if (noheader) { if (noheader) {
noheader = 0; noheader = 0;
pout("\nBackground scan results log\n"); pout("%s log\n", hname);
} }
pout(" Status: "); pout(" Status: ");
if ((pl < 16) || (num < 16)) { if ((pl < 16) || (num < 16)) {
pout("\n"); pout("\n");
break; break;
} }
j = ucp[9]; j = ucp[9];
if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0]))) if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0])))
pout("%s\n", bms_status[j]); pout("%s\n", bms_status[j]);
else else
pout("unknown [0x%x] background scan status value\n", j); pout("unknown [0x%x] background scan status value\n", j);
j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7]; j = sg_get_unaligned_be32(ucp + 4);
pout(" Accumulated power on time, hours:minutes %d:%02d " pout(" Accumulated power on time, hours:minutes %d:%02d "
"[%d minutes]\n", (j / 60), (j % 60), j); "[%d minutes]\n", (j / 60), (j % 60), j);
jglb["power_on_time"]["hours"] = j / 60;
jglb["power_on_time"]["minutes"] = j % 60;
pout(" Number of background scans performed: %d, ", pout(" Number of background scans performed: %d, ",
(ucp[10] << 8) + ucp[11]); sg_get_unaligned_be16(ucp + 10));
pout("scan progress: %.2f%%\n", pout("scan progress: %.2f%%\n",
(double)((ucp[12] << 8) + ucp[13]) * 100.0 / 65536.0); (double)sg_get_unaligned_be16(ucp + 12) * 100.0 / 65536.0);
pout(" Number of background medium scans performed: %d\n", pout(" Number of background medium scans performed: %d\n",
(ucp[14] << 8) + ucp[15]); sg_get_unaligned_be16(ucp + 14));
break; break;
default: default:
if (noheader) { if (noheader) {
noheader = 0; noheader = 0;
pout("\nBackground scan results log\n"); pout("\n%s log\n", hname);
} }
if (firstresult) { if (firstresult) {
firstresult = 0; firstresult = 0;
pout("\n # when lba(hex) [sk,asc,ascq] " pout("\n # when lba(hex) [sk,asc,ascq] "
"reassign_status\n"); "reassign_status\n");
} }
pout(" %3d ", pc); pout(" %3d ", pc);
if ((pl < 24) || (num < 24)) { if ((pl < 24) || (num < 24)) {
if (pl < 24) if (pl < 24)
pout("parameter length >= 24 expected, got %d\n", pl); pout("parameter length >= 24 expected, got %d\n", pl);
break; break;
} }
j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7]; j = sg_get_unaligned_be32(ucp + 4);
pout("%4d:%02d ", (j / 60), (j % 60)); pout("%4d:%02d ", (j / 60), (j % 60));
for (m = 0; m < 8; ++m) for (m = 0; m < 8; ++m)
pout("%02x", ucp[16 + m]); pout("%02x", ucp[16 + m]);
pout(" [%x,%x,%x] ", ucp[8] & 0xf, ucp[9], ucp[10]); pout(" [%x,%x,%x] ", ucp[8] & 0xf, ucp[9], ucp[10]);
j = (ucp[8] >> 4) & 0xf; j = (ucp[8] >> 4) & 0xf;
if (j < if (j <
(int)(sizeof(reassign_status) / sizeof(reassign_status[0]))) (int)(sizeof(reassign_status) / sizeof(reassign_status[0])))
pout("%s\n", reassign_status[j]); pout("%s\n", reassign_status[j]);
else else
pout("Reassign status: reserved [0x%x]\n", j); pout("Reassign status: reserved [0x%x]\n", j);
break; break;
} }
num -= pl; num -= pl;
ucp += pl; ucp += pl;
} }
if (truncated) if (truncated)
pout(" >>>> log truncated, fetched %d of %d available " pout(" >>>> log truncated, fetched %d of %d available "
"bytes\n", LOG_RESP_LONG_LEN, truncated); "bytes\n", LOG_RESP_LONG_LEN, truncated);
pout("\n");
return retval; return retval;
} }
// See SCSI Block Commands - 3 (SBC-3) rev 27 (draft) section 6.3.6 . // See SCSI Block Commands - 3 (SBC-3) rev 27 (draft) section 6.3.6 .
// Returns 0 if ok else FAIL* bitmask. Note can have a status entry // Returns 0 if ok else FAIL* bitmask. Note can have a status entry
// and up to 2048 events (although would hope to have less). May set // and up to 2048 events (although would hope to have less). May set
// FAILLOG if serious errors detected (in the future). // FAILLOG if serious errors detected (in the future).
static int scsiPrintSSMedia(scsi_device * device) static int
scsiPrintSSMedia(scsi_device * device)
{ {
int num, err, pc, pl, truncated; int num, err, truncated;
int retval = 0; int retval = 0;
UINT8 * ucp; uint8_t * ucp;
static const char * hname = "Solid state media";
if ((err = scsiLogSense(device, SS_MEDIA_LPAGE, 0, gBuf, if ((err = scsiLogSense(device, SS_MEDIA_LPAGE, 0, gBuf,
LOG_RESP_LONG_LEN, 0))) { LOG_RESP_LONG_LEN, 0))) {
print_on(); print_on();
pout("scsiPrintSSMedia Failed [%s]\n", scsiErrString(err)); pout("%s: Failed [%s]\n", __func__, scsiErrString(err));
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
if ((gBuf[0] & 0x3f) != SS_MEDIA_LPAGE) { if ((gBuf[0] & 0x3f) != SS_MEDIA_LPAGE) {
print_on(); print_on();
pout("Solid state media Log Sense Failed, page mismatch\n"); pout("%s %s, page mismatch\n", hname, logSenStr);
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
// compute page length // compute page length
num = (gBuf[2] << 8) + gBuf[3] + 4; num = sg_get_unaligned_be16(gBuf + 2) + 4;
if (num < 12) { if (num < 12) {
print_on(); print_on();
pout("Solid state media Log Sense length is %d, too short\n", num); pout("%s %s length is %d, too short\n", hname, logSenStr, num);
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
if (truncated) if (truncated)
num = LOG_RESP_LONG_LEN; num = LOG_RESP_LONG_LEN;
ucp = gBuf + 4; ucp = gBuf + 4;
num -= 4; num -= 4;
while (num > 3) { while (num > 3) {
pc = (ucp[0] << 8) | ucp[1]; int pc = sg_get_unaligned_be16(ucp + 0);
// pcb = ucp[2]; // pcb = ucp[2];
pl = ucp[3] + 4; int pl = ucp[3] + 4;
switch (pc) { switch (pc) {
case 1: case 1:
if (pl < 8) { if (pl < 8) {
print_on(); print_on();
pout("Percentage used endurance indicator too short (pl=%d)\n", pout("%s Percentage used endurance indicator parameter "
pl); "too short (pl=%d)\n", hname, pl);
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
pout("SS Media used endurance indicator: %d%%\n", ucp[7]); jout("Percentage used endurance indicator: %d%%\n", ucp[7]);
default: /* ignore other parameter codes */ jglb["scsi_percentage_used_endurance_indicator"] = ucp[7];
default: /* ignore other parameter codes */
break; break;
} }
num -= pl; num -= pl;
ucp += pl; ucp += pl;
} }
return retval; return retval;
} }
static void show_sas_phy_event_info(int peis, unsigned int val, static int
unsigned thresh_val) scsiPrintFormatStatus(scsi_device * device)
{
bool is_count;
int k, num, err, truncated;
int retval = 0;
uint64_t ull;
uint8_t * ucp;
uint8_t * xp;
const char * jout_str;
const char * jglb_str;
static const char * hname = "Format Status";
static const char * jname = "format_status";
if ((err = scsiLogSense(device, FORMAT_STATUS_LPAGE, 0, gBuf,
LOG_RESP_LONG_LEN, 0))) {
print_on();
jout("%s: Failed [%s]\n", __func__, scsiErrString(err));
print_off();
return FAILSMART;
}
if ((gBuf[0] & 0x3f) != FORMAT_STATUS_LPAGE) {
print_on();
jout("%s %s, page mismatch\n", hname, logSenRspStr);
print_off();
return FAILSMART;
}
// compute page length
num = sg_get_unaligned_be16(gBuf + 2) + 4;
if (num < 12) {
print_on();
jout("%s %s length is %d, too short\n", hname, logSenStr, num);
print_off();
return FAILSMART;
}
truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
if (truncated)
num = LOG_RESP_LONG_LEN;
ucp = gBuf + 4;
num -= 4;
while (num > 3) {
int pc = sg_get_unaligned_be16(ucp + 0);
// pcb = ucp[2];
int pl = ucp[3] + 4;
is_count = true;
jout_str = "";
jglb_str = "x";
switch (pc) {
case 0:
if (scsi_debugmode > 1) {
if (pl < 5)
jout("Format data out: <empty>\n");
else {
if (all_ffs(ucp + 4, pl - 4))
jout("Format data out: <not available>\n");
else {
jout("Format data out:\n");
dStrHex((const uint8_t *)ucp + 4, pl - 4, 0);
}
}
}
is_count = false;
break;
case 1:
jout_str = "Grown defects during certification";
jglb_str = "grown_defects_during_cert";
break;
case 2:
jout_str = "Total blocks reassigned during format";
jglb_str = "blocks_reassigned_during_format";
break;
case 3:
jout_str = "Total new blocks reassigned";
jglb_str = "total_new_block_since_format";
break;
case 4:
jout_str = "Power on minutes since format";
jglb_str = "power_on_minutes_since_format";
break;
default:
if (scsi_debugmode > 3) {
pout(" Unknown Format parameter code = 0x%x\n", pc);
dStrHex((const uint8_t *)ucp, pl, 0);
}
is_count = false;
break;
}
if (is_count) {
k = pl - 4;
xp = ucp + 4;
if (all_ffs(xp, k)) {
pout("%s <not available>\n", jout_str);
} else {
if (k > (int)sizeof(ull)) {
xp += (k - sizeof(ull));
k = sizeof(ull);
}
ull = sg_get_unaligned_be(k, xp);
jout("%s = %" PRIu64 "\n", jout_str, ull);
jglb[jname][jglb_str] = ull;
}
} else
num -= pl;
ucp += pl;
}
return retval;
}
static void
show_sas_phy_event_info(int peis, unsigned int val, unsigned thresh_val)
{ {
unsigned int u; unsigned int u;
switch (peis) { switch (peis) {
case 0: case 0:
pout(" No event\n"); pout(" No event\n");
break; break;
case 0x1: case 0x1:
pout(" Invalid word count: %u\n", val); pout(" Invalid word count: %u\n", val);
break; break;
skipping to change at line 1049 skipping to change at line 1460
case 0x22: case 0x22:
pout(" Received abandon-class OPEN_REJECT count: %u\n", val); pout(" Received abandon-class OPEN_REJECT count: %u\n", val);
break; break;
case 0x23: case 0x23:
pout(" Transmitted retry-class OPEN_REJECT count: %u\n", val); pout(" Transmitted retry-class OPEN_REJECT count: %u\n", val);
break; break;
case 0x24: case 0x24:
pout(" Received retry-class OPEN_REJECT count: %u\n", val); pout(" Received retry-class OPEN_REJECT count: %u\n", val);
break; break;
case 0x25: case 0x25:
pout(" Received AIP (WATING ON PARTIAL) count: %u\n", val); pout(" Received AIP (WAITING ON PARTIAL) count: %u\n", val);
break; break;
case 0x26: case 0x26:
pout(" Received AIP (WAITING ON CONNECTION) count: %u\n", val); pout(" Received AIP (WAITING ON CONNECTION) count: %u\n", val);
break; break;
case 0x27: case 0x27:
pout(" Transmitted BREAK count: %u\n", val); pout(" Transmitted BREAK count: %u\n", val);
break; break;
case 0x28: case 0x28:
pout(" Received BREAK count: %u\n", val); pout(" Received BREAK count: %u\n", val);
break; break;
skipping to change at line 1137 skipping to change at line 1548
pout(" Received SMP frame count: %u\n", val); pout(" Received SMP frame count: %u\n", val);
break; break;
case 0x63: case 0x63:
pout(" Received SMP frame error count: %u\n", val); pout(" Received SMP frame error count: %u\n", val);
break; break;
default: default:
break; break;
} }
} }
static void show_sas_port_param(unsigned char * ucp, int param_len) static void
show_sas_port_param(unsigned char * ucp, int param_len)
{ {
int j, m, n, nphys, t, sz, spld_len; int j, m, nphys, t, sz, spld_len;
unsigned char * vcp; unsigned char * vcp;
uint64_t ull;
unsigned int ui;
char s[64]; char s[64];
sz = sizeof(s); sz = sizeof(s);
// pcb = ucp[2]; // pcb = ucp[2];
t = (ucp[0] << 8) | ucp[1]; t = sg_get_unaligned_be16(ucp + 0);
pout("relative target port id = %d\n", t); pout("relative target port id = %d\n", t);
pout(" generation code = %d\n", ucp[6]); pout(" generation code = %d\n", ucp[6]);
nphys = ucp[7]; nphys = ucp[7];
pout(" number of phys = %d\n", nphys); pout(" number of phys = %d\n", nphys);
for (j = 0, vcp = ucp + 8; j < (param_len - 8); for (j = 0, vcp = ucp + 8; j < (param_len - 8);
vcp += spld_len, j += spld_len) { vcp += spld_len, j += spld_len) {
pout(" phy identifier = %d\n", vcp[1]); pout(" phy identifier = %d\n", vcp[1]);
spld_len = vcp[3]; spld_len = vcp[3];
if (spld_len < 44) if (spld_len < 44)
spld_len = 48; /* in SAS-1 and SAS-1.1 vcp[3]==0 */ spld_len = 48; /* in SAS-1 and SAS-1.1 vcp[3]==0 */
else else
spld_len += 4; spld_len += 4;
t = ((0x70 & vcp[4]) >> 4); t = ((0x70 & vcp[4]) >> 4);
switch (t) { switch (t) {
case 0: snprintf(s, sz, "no device attached"); break; case 0: snprintf(s, sz, "no device attached"); break;
case 1: snprintf(s, sz, "end device"); break; case 1: snprintf(s, sz, "SAS or SATA device"); break;
case 2: snprintf(s, sz, "expander device"); break; case 2: snprintf(s, sz, "expander device"); break;
case 3: snprintf(s, sz, "expander device (fanout)"); break; case 3: snprintf(s, sz, "expander device (fanout)"); break;
default: snprintf(s, sz, "reserved [%d]", t); break; default: snprintf(s, sz, "reserved [%d]", t); break;
} }
pout(" attached device type: %s\n", s); pout(" attached device type: %s\n", s);
t = 0xf & vcp[4]; t = 0xf & vcp[4];
switch (t) { switch (t) {
case 0: snprintf(s, sz, "unknown"); break; case 0: snprintf(s, sz, "unknown"); break;
case 1: snprintf(s, sz, "power on"); break; case 1: snprintf(s, sz, "power on"); break;
case 2: snprintf(s, sz, "hard reset"); break; case 2: snprintf(s, sz, "hard reset"); break;
skipping to change at line 1222 skipping to change at line 1632
break; break;
case 4: snprintf(s, sz, "phy enabled; port selector"); case 4: snprintf(s, sz, "phy enabled; port selector");
break; break;
case 5: snprintf(s, sz, "phy enabled; reset in progress"); case 5: snprintf(s, sz, "phy enabled; reset in progress");
break; break;
case 6: snprintf(s, sz, "phy enabled; unsupported phy attached"); case 6: snprintf(s, sz, "phy enabled; unsupported phy attached");
break; break;
case 8: snprintf(s, sz, "phy enabled; 1.5 Gbps"); break; case 8: snprintf(s, sz, "phy enabled; 1.5 Gbps"); break;
case 9: snprintf(s, sz, "phy enabled; 3 Gbps"); break; case 9: snprintf(s, sz, "phy enabled; 3 Gbps"); break;
case 0xa: snprintf(s, sz, "phy enabled; 6 Gbps"); break; case 0xa: snprintf(s, sz, "phy enabled; 6 Gbps"); break;
case 0xb: snprintf(s, sz, "phy enabled; 12 Gbps"); break;
default: snprintf(s, sz, "reserved [%d]", t); break; default: snprintf(s, sz, "reserved [%d]", t); break;
} }
pout(" negotiated logical link rate: %s\n", s); pout(" negotiated logical link rate: %s\n", s);
pout(" attached initiator port: ssp=%d stp=%d smp=%d\n", pout(" attached initiator port: ssp=%d stp=%d smp=%d\n",
!! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2)); !! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2));
pout(" attached target port: ssp=%d stp=%d smp=%d\n", pout(" attached target port: ssp=%d stp=%d smp=%d\n",
!! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2)); !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2));
for (n = 0, ull = vcp[8]; n < 8; ++n) { if (!dont_print_serial_number) {
ull <<= 8; ull |= vcp[8 + n]; uint64_t ull = sg_get_unaligned_be64(vcp + 8);
}
pout(" SAS address = 0x%" PRIx64 "\n", ull); pout(" SAS address = 0x%" PRIx64 "\n", ull);
for (n = 0, ull = vcp[16]; n < 8; ++n) { ull = sg_get_unaligned_be64(vcp + 16);
ull <<= 8; ull |= vcp[16 + n]; pout(" attached SAS address = 0x%" PRIx64 "\n", ull);
} }
pout(" attached SAS address = 0x%" PRIx64 "\n", ull);
pout(" attached phy identifier = %d\n", vcp[24]); pout(" attached phy identifier = %d\n", vcp[24]);
ui = (vcp[32] << 24) | (vcp[33] << 16) | (vcp[34] << 8) | vcp[35]; unsigned int ui = sg_get_unaligned_be32(vcp + 32);
pout(" Invalid DWORD count = %u\n", ui); pout(" Invalid DWORD count = %u\n", ui);
ui = (vcp[36] << 24) | (vcp[37] << 16) | (vcp[38] << 8) | vcp[39]; ui = sg_get_unaligned_be32(vcp + 36);
pout(" Running disparity error count = %u\n", ui); pout(" Running disparity error count = %u\n", ui);
ui = (vcp[40] << 24) | (vcp[41] << 16) | (vcp[42] << 8) | vcp[43]; ui = sg_get_unaligned_be32(vcp + 40);
pout(" Loss of DWORD synchronization = %u\n", ui); pout(" Loss of DWORD synchronization = %u\n", ui);
ui = (vcp[44] << 24) | (vcp[45] << 16) | (vcp[46] << 8) | vcp[47]; ui = sg_get_unaligned_be32(vcp + 44);
pout(" Phy reset problem = %u\n", ui); pout(" Phy reset problem = %u\n", ui);
if (spld_len > 51) { if (spld_len > 51) {
int num_ped, peis; int num_ped;
unsigned char * xcp; unsigned char * xcp;
unsigned int pvdt;
num_ped = vcp[51]; num_ped = vcp[51];
if (num_ped > 0) if (num_ped > 0)
pout(" Phy event descriptors:\n"); pout(" Phy event descriptors:\n");
xcp = vcp + 52; xcp = vcp + 52;
for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) { for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) {
int peis;
unsigned int pvdt;
peis = xcp[3]; peis = xcp[3];
ui = (xcp[4] << 24) | (xcp[5] << 16) | (xcp[6] << 8) | ui = sg_get_unaligned_be32(xcp + 4);
xcp[7]; pvdt = sg_get_unaligned_be32(xcp + 8);
pvdt = (xcp[8] << 24) | (xcp[9] << 16) | (xcp[10] << 8) |
xcp[11];
show_sas_phy_event_info(peis, ui, pvdt); show_sas_phy_event_info(peis, ui, pvdt);
} }
} }
} }
} }
// Returns 1 if okay, 0 if non SAS descriptors // Returns 1 if okay, 0 if non SAS descriptors
static int show_protocol_specific_page(unsigned char * resp, int len) static int
show_protocol_specific_page(unsigned char * resp, int len)
{ {
int k, num, param_len; int k, num;
unsigned char * ucp; unsigned char * ucp;
num = len - 4; num = len - 4;
for (k = 0, ucp = resp + 4; k < num; ) { for (k = 0, ucp = resp + 4; k < num; ) {
param_len = ucp[3] + 4; int param_len = ucp[3] + 4;
if (6 != (0xf & ucp[4])) if (SCSI_TPROTO_SAS != (0xf & ucp[4]))
return 0; /* only decode SAS log page */ return 0; /* only decode SAS log page */
if (0 == k) if (0 == k)
pout("Protocol Specific port log page for SAS SSP\n"); pout("Protocol Specific port log page for SAS SSP\n");
show_sas_port_param(ucp, param_len); show_sas_port_param(ucp, param_len);
k += param_len; k += param_len;
ucp += param_len; ucp += param_len;
} }
pout("\n");
return 1; return 1;
} }
// See Serial Attached SCSI (SAS-2) (e.g. revision 16) the Protocol Specific // See Serial Attached SCSI (SPL-3) (e.g. revision 6g) the Protocol Specific
// log pageSerial Attached SCSI (SAS-2) (e.g. revision 16) the Protocol // log page [0x18]. Returns 0 if ok else FAIL* bitmask.
// Specific log page. static int
// Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent scsiPrintSasPhy(scsi_device * device, int reset)
// 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
// FAILSMART is returned.
static int scsiPrintSasPhy(scsi_device * device, int reset)
{ {
int num, err; int num, err;
static const char * hname = "Protocol specific port";
if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf, if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf,
LOG_RESP_LONG_LEN, 0))) { LOG_RESP_LONG_LEN, 0))) {
print_on(); print_on();
pout("scsiPrintSasPhy Log Sense Failed [%s]\n", scsiErrString(err)); pout("%s %s Failed [%s]\n\n", __func__, logSenStr,
scsiErrString(err));
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) { if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) {
print_on(); print_on();
pout("Protocol specific Log Sense Failed, page mismatch\n"); pout("%s %s, page mismatch\n\n", hname, logSenRspStr);
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
// compute page length // compute page length
num = (gBuf[2] << 8) + gBuf[3]; num = sg_get_unaligned_be16(gBuf + 2);
if (1 != show_protocol_specific_page(gBuf, num + 4)) { if (1 != show_protocol_specific_page(gBuf, num + 4)) {
print_on(); print_on();
pout("Only support protocol specific log page on SAS devices\n"); pout("Only support %s log page on SAS devices\n\n", hname);
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
if (reset) { if (reset) {
if ((err = scsiLogSelect(device, 1 /* pcr */, 0 /* sp */, 0 /* pc */, if ((err = scsiLogSelect(device, 1 /* pcr */, 0 /* sp */, 0 /* pc */,
PROTOCOL_SPECIFIC_LPAGE, 0, NULL, 0))) { PROTOCOL_SPECIFIC_LPAGE, 0, NULL, 0))) {
print_on(); print_on();
pout("scsiPrintSasPhy Log Select (reset) Failed [%s]\n", pout("%s Log Select (reset) Failed [%s]\n\n", __func__,
scsiErrString(err)); scsiErrString(err));
print_off(); print_off();
return FAILSMART; return FAILSMART;
} }
} }
return 0; return 0;
} }
static const char * peripheral_dt_arr[] = { static const char * peripheral_dt_arr[32] = {
"disk", "disk",
"tape", "tape",
"printer", "printer",
"processor", "processor",
"optical disk(4)", "optical disk(4)",
"CD/DVD", "CD/DVD",
"scanner", "scanner",
"optical disk(7)", "optical disk(7)",
"medium changer", "medium changer",
"communications", "communications",
"graphics(10)", "graphics(10)",
"graphics(11)", "graphics(11)",
"storage array", "storage array",
"enclosure", "enclosure",
"simplified disk", "simplified disk",
"optical card reader" "optical card reader",
"reserved [0x10]",
"object based storage",
"automation/driver interface",
"security manager device",
"host managed zoned block device",
"reserved [0x15]",
"reserved [0x16]",
"reserved [0x17]",
"reserved [0x18]",
"reserved [0x19]",
"reserved [0x1a]",
"reserved [0x1b]",
"reserved [0x1c]",
"reserved [0x1d]",
"well known logical unit",
"unknown or no device type",
}; };
/* Symbolic indexes to this array SCSI_TPROTO_* in scscmds.h */
static const char * transport_proto_arr[] = { static const char * transport_proto_arr[] = {
"Fibre channel (FCP-2)", "Fibre channel (FCP-2)",
"Parallel SCSI (SPI-4)", "Parallel SCSI (SPI-4)",
"SSA", "SSA",
"IEEE 1394 (SBP-2)", "IEEE 1394 (SBP-2)",
"RDMA (SRP)", "RDMA (SRP)",
"iSCSI", "iSCSI",
"SAS", "SAS (SPL-3)",
"ADT", "ADT",
"0x8", "ATA (ACS-2)",
"0x9", "UAS",
"0xa", "SOP",
"0xb", "PCIe",
"0xc", "0xc",
"0xd", "0xd",
"0xe", "0xe",
"0xf" "None given [0xf]"
}; };
/* Returns 0 on success, 1 on general error and 2 for early, clean exit */ /* Returns 0 on success, 1 on general error and 2 for early, clean exit */
static int scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool static int
all) scsiGetDriveInfo(scsi_device * device, uint8_t * peripheral_type, bool all)
{ {
char timedatetz[DATEANDEPOCHLEN];
struct scsi_iec_mode_page iec; struct scsi_iec_mode_page iec;
int err, iec_err, len, req_len, avail_len; int err, iec_err, len, req_len, avail_len;
int is_tape = 0; bool is_tape = false;
int peri_dt = 0; int peri_dt = 0;
int returnval = 0;
int transport = -1; int transport = -1;
int form_factor = 0;
int haw_zbc = 0;
int protect = 0;
memset(gBuf, 0, 96); memset(gBuf, 0, 96);
req_len = 36; req_len = 36;
if ((err = scsiStdInquiry(device, gBuf, req_len))) { if ((err = scsiStdInquiry(device, gBuf, req_len))) {
print_on(); print_on();
pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err)); pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err));
pout("Retrying with a 64 byte Standard Inquiry\n"); pout("Retrying with a 64 byte Standard Inquiry\n");
print_off(); print_off();
/* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */ /* Marvell controllers fail with 36 byte StdInquiry, but 64 is ok */
req_len = 64; req_len = 64;
if ((err = scsiStdInquiry(device, gBuf, req_len))) { if ((err = scsiStdInquiry(device, gBuf, req_len))) {
print_on(); print_on();
pout("Standard Inquiry (64 bytes) failed [%s]\n", pout("Standard Inquiry (64 bytes) failed [%s]\n",
scsiErrString(err)); scsiErrString(err));
print_off(); print_off();
return 1; return 1;
} }
} }
avail_len = gBuf[4] + 5; avail_len = gBuf[4] + 5;
len = (avail_len < req_len) ? avail_len : req_len; len = (avail_len < req_len) ? avail_len : req_len;
peri_dt = gBuf[0] & 0x1f; peri_dt = gBuf[0] & 0x1f;
if (peripheral_type) *peripheral_type = peri_dt;
*peripheral_type = peri_dt; if ((SCSI_PT_SEQUENTIAL_ACCESS == peri_dt) ||
(SCSI_PT_MEDIUM_CHANGER == peri_dt))
is_tape = true;
if (len < 36) { if (len < 36) {
print_on(); print_on();
pout("Short INQUIRY response, skip product id\n"); pout("Short INQUIRY response, skip product id\n");
print_off(); print_off();
return 1; return 1;
} }
// Upper bits of version bytes were used in older standards
// Only interested in SPC-4 (0x6) and SPC-5 (assumed to be 0x7)
scsi_version = gBuf[2] & 0x7;
if (all && (0 != strncmp((char *)&gBuf[8], "ATA", 3))) { if (all && (0 != strncmp((char *)&gBuf[8], "ATA", 3))) {
pout("Vendor: %.8s\n", (char *)&gBuf[8]); char product[16+1], revision[4+1];
pout("Product: %.16s\n", (char *)&gBuf[16]); scsi_format_id_string(scsi_vendor, &gBuf[8], 8);
if (gBuf[32] >= ' ') scsi_format_id_string(product, &gBuf[16], 16);
pout("Revision: %.4s\n", (char *)&gBuf[32]); scsi_format_id_string(revision, &gBuf[32], 4);
pout("=== START OF INFORMATION SECTION ===\n");
jout("Vendor: %.8s\n", scsi_vendor);
jglb["vendor"] = scsi_vendor;
jout("Product: %.16s\n", product);
jglb["product"] = product;
jglb["model_name"] = strprintf("%s%s%s",
scsi_vendor, (*scsi_vendor && *product ? " " : ""), product);
if (gBuf[32] >= ' ') {
jout("Revision: %.4s\n", revision);
// jglb["firmware_version"] = revision;
jglb["revision"] = revision; /* could be a hardware rev */
}
if ((scsi_version > 0x3) && (scsi_version < 0x8)) {
char sv_arr[8];
snprintf(sv_arr, sizeof(sv_arr), "SPC-%d", scsi_version - 2);
jout("Compliance: %s\n", sv_arr);
jglb["scsi_version"] = sv_arr;
}
} }
if (!*device->get_req_type()/*no type requested*/ && if (!*device->get_req_type()/*no type requested*/ &&
(0 == strncmp((char *)&gBuf[8], "ATA", 3))) { (0 == strncmp((char *)&gBuf[8], "ATA", 3))) {
pout("\nProbable ATA device behind a SAT layer\n" pout("\nProbable ATA device behind a SAT layer\n"
"Try an additional '-d ata' or '-d sat' argument.\n"); "Try an additional '-d ata' or '-d sat' argument.\n");
return 2; return 2;
} }
if (! all) if (! all)
return 0; return 0;
unsigned int lb_size; protect = gBuf[5] & 0x1; /* from and including SPC-3 */
char cap_str[64];
char si_str[64];
char lb_str[16];
uint64_t capacity = scsiGetSize(device, &lb_size);
if (capacity) { if (! is_tape) { /* assume disk if not tape drive (or tape changer) */
format_with_thousands_sep(cap_str, sizeof(cap_str), capacity); struct scsi_readcap_resp srr;
format_capacity(si_str, sizeof(si_str), capacity); int lbpme = -1;
pout("User Capacity: %s bytes [%s]\n", cap_str, si_str); int lbprz = -1;
snprintf(lb_str, sizeof(lb_str) - 1, "%u", lb_size); unsigned char lb_prov_resp[8];
pout("Logical block size: %s bytes\n", lb_str); uint64_t capacity = scsiGetSize(device, false /*avoid_rcap16 */,
&srr);
if (capacity) {
char cap_str[64], si_str[64];
format_with_thousands_sep(cap_str, sizeof(cap_str), capacity);
format_capacity(si_str, sizeof(si_str), capacity);
jout("User Capacity: %s bytes [%s]\n", cap_str, si_str);
if (srr.lb_size)
jglb["user_capacity"]["blocks"].set_unsafe_uint64(capacity /
srr.lb_size);
jglb["user_capacity"]["bytes"].set_unsafe_uint64(capacity);
jout("Logical block size: %u bytes\n", srr.lb_size);
jglb["logical_block_size"] = srr.lb_size;
if (protect || srr.lb_p_pb_exp) {
if (srr.lb_p_pb_exp > 0) {
unsigned pb_size = srr.lb_size * (1 << srr.lb_p_pb_exp);
jout("Physical block size: %u bytes\n", pb_size);
jglb["physical_block_size"] = pb_size;
if (srr.l_a_lba > 0) // not common so cut the clutter
pout("Lowest aligned LBA: %u\n", srr.l_a_lba);
}
if (srr.prot_type > 0) {
switch (srr.prot_type) {
case 1 :
pout("Formatted with type 1 protection\n");
break;
case 2 :
pout("Formatted with type 2 protection\n");
break;
case 3 :
pout("Formatted with type 3 protection\n");
break;
default:
pout("Formatted with unknown protection type [%d]\n",
srr.prot_type);
break;
}
unsigned p_i_per_lb = (1 << srr.p_i_exp);
const unsigned pi_sz = 8; /* ref-tag(4 bytes),
app-tag(2), tag-mask(2) */
if (p_i_per_lb > 1)
pout("%d protection information intervals per "
"logical block\n", p_i_per_lb);
pout("%d bytes of protection information per logical "
"block\n", pi_sz * p_i_per_lb);
}
/* Pick up some LB provisioning info since its available */
lbpme = (int)srr.lbpme;
lbprz = (int)srr.lbprz;
}
}
/* Thin Provisioning VPD page renamed Logical Block Provisioning VPD
* page in sbc3r25; some fields changed their meaning so that the
* new page covered both thin and resource provisioned LUs. */
if (0 == scsiInquiryVpd(device, SCSI_VPD_LOGICAL_BLOCK_PROVISIONING,
lb_prov_resp, sizeof(lb_prov_resp))) {
int prov_type = lb_prov_resp[6] & 0x7; /* added sbc3r27 */
int vpd_lbprz = ((lb_prov_resp[5] >> 2) & 0x7); /* sbc4r07 */
if (-1 == lbprz)
lbprz = vpd_lbprz;
else if ((0 == vpd_lbprz) && (1 == lbprz))
; /* vpd_lbprz introduced in sbc3r27, expanded in sbc4r07 */
else
lbprz = vpd_lbprz;
switch (prov_type) {
case 0:
if (lbpme <= 0) {
pout("LU is fully provisioned");
if (lbprz)
pout(" [LBPRZ=%d]\n", lbprz);
else
pout("\n");
} else
pout("LB provisioning type: not reported [LBPME=1, "
"LBPRZ=%d]\n", lbprz);
break;
case 1:
pout("LU is resource provisioned, LBPRZ=%d\n", lbprz);
break;
case 2:
pout("LU is thin provisioned, LBPRZ=%d\n", lbprz);
break;
default:
pout("LU provisioning type reserved [%d], LBPRZ=%d\n",
prov_type, lbprz);
break;
}
} else if (1 == lbpme) {
if (scsi_debugmode > 0)
pout("rcap_16 sets LBPME but no LB provisioning VPD page\n");
pout("Logical block provisioning enabled, LBPRZ=%d\n", lbprz);
}
int rpm = scsiGetRPM(device, modese_len, &form_factor, &haw_zbc);
if (rpm >= 0) {
if (0 == rpm)
; // Not reported
else if (1 == rpm)
jout("Rotation Rate: Solid State Device\n");
else if ((rpm <= 0x400) || (0xffff == rpm))
; // Reserved
else
jout("Rotation Rate: %d rpm\n", rpm);
jglb["rotation_rate"] = (rpm == 1 ? 0 : rpm);
}
if (form_factor > 0) {
const char * cp = NULL;
switch (form_factor) {
case 1:
cp = "5.25";
break;
case 2:
cp = "3.5";
break;
case 3:
cp = "2.5";
break;
case 4:
cp = "1.8";
break;
case 5:
cp = "< 1.8";
break;
}
jglb["form_factor"]["scsi_value"] = form_factor;
if (cp) {
jout("Form Factor: %s inches\n", cp);
jglb["form_factor"]["name"] = strprintf("%s inches", cp);
}
}
if (haw_zbc > 0)
pout("Host aware zoned block capable\n");
} }
/* Do this here to try and detect badly conforming devices (some USB /* Do this here to try and detect badly conforming devices (some USB
keys) that will lock up on a InquiryVpd or log sense or ... */ keys) that will lock up on a InquiryVpd or log sense or ... */
if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) { if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) {
if (SIMPLE_ERR_BAD_RESP == iec_err) { if (SIMPLE_ERR_BAD_RESP == iec_err) {
pout(">> Terminate command early due to bad response to IEC " pout(">> Terminate command early due to bad response to IEC "
"mode page\n"); "mode page\n");
print_off(); print_off();
gIecMPage = 0; gIecMPage = 0;
return 1; return 1;
} }
} else } else
modese_len = iec.modese_len; modese_len = iec.modese_len;
if (! dont_print_serial_number) { if (! dont_print_serial_number) {
if (0 == (err = scsiInquiryVpd(device, 0x83, gBuf, 200))) { if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_DEVICE_IDENTIFICATION,
char s[256]; gBuf, 252))) {
char s[256];
len = gBuf[3]; len = gBuf[3];
scsi_decode_lu_dev_id(gBuf + 4, len, s, sizeof(s), &transport); scsi_decode_lu_dev_id(gBuf + 4, len, s, sizeof(s), &transport);
if (strlen(s) > 0) if (strlen(s) > 0)
pout("Logical Unit id: %s\n", s); pout("Logical Unit id: %s\n", s);
} else if (scsi_debugmode > 0) { } else if (scsi_debugmode > 0) {
print_on(); print_on();
if (SIMPLE_ERR_BAD_RESP == err) if (SIMPLE_ERR_BAD_RESP == err)
pout("Vital Product Data (VPD) bit ignored in INQUIRY\n"); pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
else else
pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err); pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
print_off(); print_off();
} }
if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) { if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_UNIT_SERIAL_NUMBER,
gBuf, 252))) {
char serial[256];
len = gBuf[3]; len = gBuf[3];
gBuf[4 + len] = '\0'; gBuf[4 + len] = '\0';
pout("Serial number: %s\n", &gBuf[4]); scsi_format_id_string(serial, &gBuf[4], len);
jout("Serial number: %s\n", serial);
jglb["serial_number"] = serial;
} else if (scsi_debugmode > 0) { } else if (scsi_debugmode > 0) {
print_on(); print_on();
if (SIMPLE_ERR_BAD_RESP == err) if (SIMPLE_ERR_BAD_RESP == err)
pout("Vital Product Data (VPD) bit ignored in INQUIRY\n"); pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
else else
pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err); pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
print_off(); print_off();
} }
} }
// print SCSI peripheral device type // print SCSI peripheral device type
jglb["device_type"]["scsi_value"] = peri_dt;
if (peri_dt < (int)(sizeof(peripheral_dt_arr) / if (peri_dt < (int)(sizeof(peripheral_dt_arr) /
sizeof(peripheral_dt_arr[0]))) sizeof(peripheral_dt_arr[0]))) {
pout("Device type: %s\n", peripheral_dt_arr[peri_dt]); jout("Device type: %s\n", peripheral_dt_arr[peri_dt]);
jglb["device_type"]["name"] = peripheral_dt_arr[peri_dt];
}
else else
pout("Device type: <%d>\n", peri_dt); jout("Device type: <%d>\n", peri_dt);
// See if transport protocol is known // See if transport protocol is known
if (transport < 0) if (transport < 0)
transport = scsiFetchTransportProtocol(device, modese_len); transport = scsiFetchTransportProtocol(device, modese_len);
if ((transport >= 0) && (transport <= 0xf)) if ((transport >= 0) && (transport <= 0xf))
pout("Transport protocol: %s\n", transport_proto_arr[transport]); pout("Transport protocol: %s\n", transport_proto_arr[transport]);
// print current time and date and timezone // print current time and date and timezone
dateandtimezone(timedatetz); time_t now = time(0);
pout("Local Time is: %s\n", timedatetz); char timedatetz[DATEANDEPOCHLEN]; dateandtimezoneepoch(timedatetz, now);
jout("Local Time is: %s\n", timedatetz);
jglb["local_time"]["time_t"] = now;
jglb["local_time"]["asctime"] = timedatetz;
if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) || // See if unit accepts SCSI commands from us
(SCSI_PT_MEDIUM_CHANGER == *peripheral_type))
is_tape = 1;
// See if unit accepts SCSI commmands from us
if ((err = scsiTestUnitReady(device))) { if ((err = scsiTestUnitReady(device))) {
if (SIMPLE_ERR_NOT_READY == err) { if (SIMPLE_ERR_NOT_READY == err) {
print_on(); print_on();
if (!is_tape) if (!is_tape)
pout("device is NOT READY (e.g. spun down, busy)\n"); pout("device is NOT READY (e.g. spun down, busy)\n");
else else
pout("device is NOT READY (e.g. no tape)\n"); pout("device is NOT READY (e.g. no tape)\n");
print_off(); print_off();
} else if (SIMPLE_ERR_NO_MEDIUM == err) { } else if (SIMPLE_ERR_NO_MEDIUM == err) {
print_on(); print_on();
pout("NO MEDIUM present on device\n"); if (is_tape)
pout("NO tape present in drive\n");
else
pout("NO MEDIUM present in device\n");
print_off(); print_off();
} else if (SIMPLE_ERR_BECOMING_READY == err) { } else if (SIMPLE_ERR_BECOMING_READY == err) {
print_on(); print_on();
pout("device becoming ready (wait)\n"); pout("device becoming ready (wait)\n");
print_off(); print_off();
} else { } else {
print_on(); print_on();
pout("device Test Unit Ready [%s]\n", scsiErrString(err)); pout("device Test Unit Ready [%s]\n", scsiErrString(err));
print_off(); print_off();
} }
failuretest(MANDATORY_CMD, returnval|=FAILID); if (! is_tape) {
int returnval = 0; // TODO: exit with FAILID if failuretest returns
failuretest(MANDATORY_CMD, returnval|=FAILID);
}
} }
if (iec_err) { if (iec_err) {
if (!is_tape) { if (!is_tape) {
print_on(); print_on();
pout("Device does not support SMART"); pout("SMART support is: Unavailable - device lacks SMART "
"capability.\n");
if (scsi_debugmode > 0) if (scsi_debugmode > 0)
pout(" [%s]\n", scsiErrString(iec_err)); pout(" [%s]\n", scsiErrString(iec_err));
else
pout("\n");
print_off(); print_off();
} }
gIecMPage = 0; gIecMPage = 0;
return 0; return 0;
} }
if (!is_tape) if (!is_tape)
pout("Device supports SMART and is %s\n", pout("SMART support is: Available - device has SMART capability.\n"
"SMART support is: %s\n",
(scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled"); (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled");
pout("%s\n", (scsi_IsWarningEnabled(&iec)) ? pout("%s\n", (scsi_IsWarningEnabled(&iec)) ?
"Temperature Warning Enabled" : "Temperature Warning: Enabled" :
"Temperature Warning Disabled or Not Supported"); "Temperature Warning: Disabled or Not Supported");
return 0; return 0;
} }
static int scsiSmartEnable(scsi_device * device) static int
scsiSmartEnable(scsi_device * device)
{ {
struct scsi_iec_mode_page iec; struct scsi_iec_mode_page iec;
int err; int err;
if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
print_on(); print_on();
pout("unable to fetch IEC (SMART) mode page [%s]\n", pout("unable to fetch IEC (SMART) mode page [%s]\n",
scsiErrString(err)); scsiErrString(err));
print_off(); print_off();
return 1; return 1;
skipping to change at line 1586 skipping to change at line 2190
} else } else
modese_len = iec.modese_len; modese_len = iec.modese_len;
pout("Informational Exceptions (SMART) %s\n", pout("Informational Exceptions (SMART) %s\n",
scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled"); scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
pout("Temperature warning %s\n", pout("Temperature warning %s\n",
scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled"); scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
return 0; return 0;
} }
static int scsiSmartDisable(scsi_device * device) static int
scsiSmartDisable(scsi_device * device)
{ {
struct scsi_iec_mode_page iec; struct scsi_iec_mode_page iec;
int err; int err;
if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
print_on(); print_on();
pout("unable to fetch IEC (SMART) mode page [%s]\n", pout("unable to fetch IEC (SMART) mode page [%s]\n",
scsiErrString(err)); scsiErrString(err));
print_off(); print_off();
return 1; return 1;
skipping to change at line 1622 skipping to change at line 2227
} else } else
modese_len = iec.modese_len; modese_len = iec.modese_len;
pout("Informational Exceptions (SMART) %s\n", pout("Informational Exceptions (SMART) %s\n",
scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled"); scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
pout("Temperature warning %s\n", pout("Temperature warning %s\n",
scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled"); scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
return 0; return 0;
} }
static void scsiPrintTemp(scsi_device * device) static void
scsiPrintTemp(scsi_device * device)
{ {
UINT8 temp = 0; uint8_t temp = 255;
UINT8 trip = 0; uint8_t trip = 255;
if (scsiGetTemp(device, &temp, &trip)) if (scsiGetTemp(device, &temp, &trip))
return; return;
if (temp) { if (255 == temp)
if (255 != temp) pout("Current Drive Temperature: <not available>\n");
pout("Current Drive Temperature: %d C\n", temp); else {
else jout("Current Drive Temperature: %d C\n", temp);
pout("Current Drive Temperature: <not available>\n"); jglb["temperature"]["current"] = temp;
} }
if (trip) if (255 == trip)
pout("Drive Trip Temperature: %d C\n", trip); pout("Drive Trip Temperature: <not available>\n");
else {
jout("Drive Trip Temperature: %d C\n", trip);
jglb["temperature"]["drive_trip"] = trip;
}
pout("\n");
} }
/* Main entry point used by smartctl command. Return 0 for success */ /* Main entry point used by smartctl command. Return 0 for success */
int scsiPrintMain(scsi_device * device, const scsi_print_options & options) int
scsiPrintMain(scsi_device * device, const scsi_print_options & options)
{ {
int checkedSupportedLogPages = 0; int checkedSupportedLogPages = 0;
UINT8 peripheral_type = 0; uint8_t peripheral_type = 0;
int returnval = 0; int returnval = 0;
int res, durationSec; int res, durationSec;
struct scsi_sense_disect sense_info;
bool is_disk;
bool is_tape;
bool any_output = options.drive_info; bool any_output = options.drive_info;
if (supported_vpd_pages_p) {
delete supported_vpd_pages_p;
supported_vpd_pages_p = NULL;
}
supported_vpd_pages_p = new supported_vpd_pages(device);
res = scsiGetDriveInfo(device, &peripheral_type, options.drive_info); res = scsiGetDriveInfo(device, &peripheral_type, options.drive_info);
if (res) { if (res) {
if (2 == res) if (2 == res)
return 0; return 0;
else else
failuretest(MANDATORY_CMD, returnval |= FAILID); failuretest(MANDATORY_CMD, returnval |= FAILID);
any_output = true; any_output = true;
} }
is_disk = ((SCSI_PT_DIRECT_ACCESS == peripheral_type) ||
(SCSI_PT_HOST_MANAGED == peripheral_type));
is_tape = ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
(SCSI_PT_MEDIUM_CHANGER == peripheral_type));
short int wce = -1, rcd = -1;
// Print read look-ahead status for disks
if (options.get_rcd || options.get_wce) {
if (is_disk) {
res = scsiGetSetCache(device, modese_len, &wce, &rcd);
if (options.get_rcd)
pout("Read Cache is: %s\n",
res ? "Unavailable" : // error
rcd ? "Disabled" : "Enabled");
if (options.get_wce)
pout("Writeback Cache is: %s\n",
res ? "Unavailable" : // error
!wce ? "Disabled" : "Enabled");
}
} else
any_output = true;
if (options.drive_info)
pout("\n");
// START OF THE ENABLE/DISABLE SECTION OF THE CODE
if (options.smart_disable || options.smart_enable ||
options.smart_auto_save_disable || options.smart_auto_save_enable)
pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
if (options.smart_enable) { if (options.smart_enable) {
if (scsiSmartEnable(device)) if (scsiSmartEnable(device))
failuretest(MANDATORY_CMD, returnval |= FAILSMART); failuretest(MANDATORY_CMD, returnval |= FAILSMART);
any_output = true; any_output = true;
} }
if (options.smart_disable) { if (options.smart_disable) {
if (scsiSmartDisable(device)) if (scsiSmartDisable(device))
failuretest(MANDATORY_CMD,returnval |= FAILSMART); failuretest(MANDATORY_CMD,returnval |= FAILSMART);
any_output = true; any_output = true;
} }
if (options.smart_auto_save_enable) { if (options.smart_auto_save_enable) {
if (scsiSetControlGLTSD(device, 0, modese_len)) { if (scsiSetControlGLTSD(device, 0, modese_len)) {
pout("Enable autosave (clear GLTSD bit) failed\n"); pout("Enable autosave (clear GLTSD bit) failed\n");
failuretest(OPTIONAL_CMD,returnval |= FAILSMART); failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
} } else
any_output = true; pout("Autosave enabled (GLTSD bit cleared).\n");
any_output = true;
}
// Enable/Disable write cache
if (options.set_wce && is_disk) {
short int enable = wce = (options.set_wce > 0);
rcd = -1;
if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
pout("Write cache %sable failed: %s\n", (enable ? "en" : "dis"),
device->get_errmsg());
failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
} else
pout("Write cache %sabled\n", (enable ? "en" : "dis"));
any_output = true;
}
// Enable/Disable read cache
if (options.set_rcd && is_disk) {
short int enable = (options.set_rcd > 0);
rcd = !enable;
wce = -1;
if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
pout("Read cache %sable failed: %s\n", (enable ? "en" : "dis"),
device->get_errmsg());
failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
} else
pout("Read cache %sabled\n", (enable ? "en" : "dis"));
any_output = true;
} }
if (options.smart_auto_save_disable) { if (options.smart_auto_save_disable) {
if (scsiSetControlGLTSD(device, 1, modese_len)) { if (scsiSetControlGLTSD(device, 1, modese_len)) {
pout("Disable autosave (set GLTSD bit) failed\n"); pout("Disable autosave (set GLTSD bit) failed\n");
failuretest(OPTIONAL_CMD,returnval |= FAILSMART); failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
} } else
any_output = true; pout("Autosave disabled (GLTSD bit set).\n");
any_output = true;
} }
if (options.smart_disable || options.smart_enable ||
options.smart_auto_save_disable || options.smart_auto_save_enable)
pout("\n"); // END OF THE ENABLE/DISABLE SECTION OF THE CODE
// START OF READ-ONLY OPTIONS APART FROM -V and -i
if (options.smart_check_status || options.smart_ss_media_log ||
options.smart_vendor_attrib || options.smart_error_log ||
options.smart_selftest_log || options.smart_background_log ||
options.sasphy)
pout("=== START OF READ SMART DATA SECTION ===\n");
if (options.smart_check_status) { if (options.smart_check_status) {
scsiGetSupportedLogPages(device); scsiGetSupportedLogPages(device);
checkedSupportedLogPages = 1; checkedSupportedLogPages = 1;
if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) || if (is_tape) {
(SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */
if (gTapeAlertsLPage) { if (gTapeAlertsLPage) {
if (options.drive_info) if (options.drive_info)
pout("TapeAlert Supported\n"); pout("TapeAlert Supported\n");
if (-1 == scsiGetTapeAlertsData(device, peripheral_type)) if (-1 == scsiGetTapeAlertsData(device, peripheral_type))
failuretest(OPTIONAL_CMD, returnval |= FAILSMART); failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
} }
else else
pout("TapeAlert Not Supported\n"); pout("TapeAlert Not Supported\n");
} else { /* disk, cd/dvd, enclosure, etc */ } else { /* disk, cd/dvd, enclosure, etc */
if ((res = scsiGetSmartData(device, options.smart_vendor_attrib))) { if ((res = scsiGetSmartData(device,
options.smart_vendor_attrib))) {
if (-2 == res) if (-2 == res)
returnval |= FAILSTATUS; returnval |= FAILSTATUS;
else else
returnval |= FAILSMART; returnval |= FAILSMART;
} }
} }
any_output = true; any_output = true;
} }
if (options.smart_ss_media_log) {
if (is_disk && options.smart_ss_media_log) {
if (! checkedSupportedLogPages) if (! checkedSupportedLogPages)
scsiGetSupportedLogPages(device); scsiGetSupportedLogPages(device);
res = 0; res = 0;
if (gSSMediaLPage) if (gSSMediaLPage)
res = scsiPrintSSMedia(device); res = scsiPrintSSMedia(device);
if (0 != res) if (0 != res)
failuretest(OPTIONAL_CMD, returnval|=res); failuretest(OPTIONAL_CMD, returnval|=res);
if (gFormatStatusLPage)
res = scsiPrintFormatStatus(device);
if (0 != res)
failuretest(OPTIONAL_CMD, returnval|=res);
any_output = true; any_output = true;
} }
if (options.smart_vendor_attrib) { if (options.smart_vendor_attrib) {
if (! checkedSupportedLogPages) if (! checkedSupportedLogPages)
scsiGetSupportedLogPages(device); scsiGetSupportedLogPages(device);
if (gTempLPage) { if (gTempLPage)
if (options.smart_check_status)
pout("\n");
scsiPrintTemp(device); scsiPrintTemp(device);
}
if (gStartStopLPage) if (gStartStopLPage)
scsiGetStartStopData(device); scsiGetStartStopData(device);
if (SCSI_PT_DIRECT_ACCESS == peripheral_type) { if (is_disk) {
scsiPrintGrownDefectListLen(device); scsiPrintGrownDefectListLen(device);
if (gSeagateCacheLPage) if (gSeagateCacheLPage)
scsiPrintSeagateCacheLPage(device); scsiPrintSeagateCacheLPage(device);
if (gSeagateFactoryLPage) if (gSeagateFactoryLPage)
scsiPrintSeagateFactoryLPage(device); scsiPrintSeagateFactoryLPage(device);
} }
any_output = true; any_output = true;
} }
if (options.smart_error_log) { if (options.smart_error_log) {
if (! checkedSupportedLogPages) if (! checkedSupportedLogPages)
scsiGetSupportedLogPages(device); scsiGetSupportedLogPages(device);
scsiPrintErrorCounterLog(device); scsiPrintErrorCounterLog(device);
if (gPendDefectsLPage)
scsiPrintPendingDefectsLPage(device);
if (1 == scsiFetchControlGLTSD(device, modese_len, 1)) if (1 == scsiFetchControlGLTSD(device, modese_len, 1))
pout("\n[GLTSD (Global Logging Target Save Disable) set. " pout("\n[GLTSD (Global Logging Target Save Disable) set. "
"Enable Save with '-S on']\n"); "Enable Save with '-S on']\n");
any_output = true; any_output = true;
} }
if (options.smart_selftest_log) { if (options.smart_selftest_log) {
if (! checkedSupportedLogPages) if (! checkedSupportedLogPages)
scsiGetSupportedLogPages(device); scsiGetSupportedLogPages(device);
res = 0; res = 0;
if (gSelfTestLPage) if (gSelfTestLPage)
res = scsiPrintSelfTest(device); res = scsiPrintSelfTest(device);
else { else {
pout("Device does not support Self Test logging\n"); pout("Device does not support Self Test logging\n");
failuretest(OPTIONAL_CMD, returnval|=FAILSMART); failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
} }
if (0 != res) if (0 != res)
failuretest(OPTIONAL_CMD, returnval|=res); failuretest(OPTIONAL_CMD, returnval|=res);
any_output = true; any_output = true;
} }
if (options.smart_background_log) { if (options.smart_background_log && is_disk) {
if (! checkedSupportedLogPages) if (! checkedSupportedLogPages)
scsiGetSupportedLogPages(device); scsiGetSupportedLogPages(device);
res = 0; res = 0;
if (gBackgroundResultsLPage) if (gBackgroundResultsLPage)
res = scsiPrintBackgroundResults(device); res = scsiPrintBackgroundResults(device);
else { else {
pout("Device does not support Background scan results logging\n"); pout("Device does not support Background scan results logging\n");
failuretest(OPTIONAL_CMD, returnval|=FAILSMART); failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
} }
if (0 != res) if (0 != res)
skipping to change at line 1788 skipping to change at line 2483
return returnval | FAILSMART; return returnval | FAILSMART;
pout("Default Self Test Successful\n"); pout("Default Self Test Successful\n");
any_output = true; any_output = true;
} }
if (options.smart_short_cap_selftest) { if (options.smart_short_cap_selftest) {
if (scsiSmartShortCapSelfTest(device)) if (scsiSmartShortCapSelfTest(device))
return returnval | FAILSMART; return returnval | FAILSMART;
pout("Short Foreground Self Test Successful\n"); pout("Short Foreground Self Test Successful\n");
any_output = true; any_output = true;
} }
// check if another test is running
if (options.smart_short_selftest || options.smart_extend_selftest) {
if (!scsiRequestSense(device, &sense_info) &&
(sense_info.asc == 0x04 && sense_info.ascq == 0x09)) {
if (!options.smart_selftest_force) {
pout("Can't start self-test without aborting current test");
if (sense_info.progress != -1)
pout(" (%d%% remaining)",
100 - sense_info.progress * 100 / 65535);
pout(",\nadd '-t force' option to override, or run "
"'smartctl -X' to abort test.\n");
return -1;
} else
scsiSmartSelfTestAbort(device);
}
}
if (options.smart_short_selftest) { if (options.smart_short_selftest) {
if (scsiSmartShortSelfTest(device)) if (scsiSmartShortSelfTest(device))
return returnval | FAILSMART; return returnval | FAILSMART;
pout("Short Background Self Test has begun\n"); pout("Short Background Self Test has begun\n");
pout("Use smartctl -X to abort test\n"); pout("Use smartctl -X to abort test\n");
any_output = true; any_output = true;
} }
if (options.smart_extend_selftest) { if (options.smart_extend_selftest) {
if (scsiSmartExtendSelfTest(device)) if (scsiSmartExtendSelfTest(device))
return returnval | FAILSMART; return returnval | FAILSMART;
skipping to change at line 1822 skipping to change at line 2533
if (scsiSmartExtendCapSelfTest(device)) if (scsiSmartExtendCapSelfTest(device))
return returnval | FAILSMART; return returnval | FAILSMART;
pout("Extended Foreground Self Test Successful\n"); pout("Extended Foreground Self Test Successful\n");
} }
if (options.smart_selftest_abort) { if (options.smart_selftest_abort) {
if (scsiSmartSelfTestAbort(device)) if (scsiSmartSelfTestAbort(device))
return returnval | FAILSMART; return returnval | FAILSMART;
pout("Self Test returned without error\n"); pout("Self Test returned without error\n");
any_output = true; any_output = true;
} }
if (options.sasphy) { if (options.sasphy && gProtocolSpecificLPage) {
if (scsiPrintSasPhy(device, options.sasphy_reset)) if (scsiPrintSasPhy(device, options.sasphy_reset))
return returnval | FAILSMART; return returnval | FAILSMART;
any_output = true; any_output = true;
} }
if (!any_output) if (!any_output)
pout("SCSI device successfully opened\n\n" pout("SCSI device successfully opened\n\nUse 'smartctl -a' (or '-x') "
"Use 'smartctl -a' (or '-x') to print SMART (and more) information\n\ "to print SMART (and more) information\n\n");
n");
return returnval; return returnval;
} }
 End of changes. 257 change blocks. 
398 lines changed or deleted 1110 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/