/*
* scsiprint.cpp
*
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
*
- * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-11 Bruce Allen
* Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
- *
- * Additional SCSI work:
- * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
+ * Copyright (C) 2003-15 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
* 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.
+ * (for example COPYING); if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This code was originally developed as a Senior Thesis by Michael Cornwell
* at the Concurrent Systems Laboratory (now part of the Storage Systems
#include "config.h"
#include "int64.h"
-#include "extern.h"
#include "scsicmds.h"
+#include "atacmds.h" // smart_command_set
+#include "dev_interface.h"
#include "scsiprint.h"
#include "smartctl.h"
#include "utility.h"
-#include "scsiata.h"
#define GBUF_SIZE 65535
-const char* scsiprint_c_cvsid="$Id: scsiprint.cpp,v 1.118 2006/09/27 21:42:03 chrfranke Exp $"
-CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
-
-// control block which points to external global control variables
-extern smartmonctrl *con;
+const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 4292 2016-04-12 23:06:59Z dpgilbert $"
+ SCSIPRINT_H_CVSID;
-// to hold onto exit code for atexit routine
-extern int exitstatus;
UINT8 gBuf[GBUF_SIZE];
#define LOG_RESP_LEN 252
-#define LOG_RESP_LONG_LEN 16384
+#define LOG_RESP_LONG_LEN ((62 * 256) + 252)
#define LOG_RESP_TAPE_ALERT_LEN 0x144
/* Log pages supported */
static int gNonMediumELPage = 0;
static int gLastNErrorLPage = 0;
static int gBackgroundResultsLPage = 0;
+static int gProtocolSpecificLPage = 0;
static int gTapeAlertsLPage = 0;
+static int gSSMediaLPage = 0;
+
+/* Vendor specific log pages */
static int gSeagateCacheLPage = 0;
static int gSeagateFactoryLPage = 0;
/* Remember last successful mode sense/select command */
static int modese_len = 0;
-// Compares failure type to policy in effect, and either exits or
-// simply returns to the calling routine.
-extern void failuretest(int type, int returnvalue);
-static void scsiGetSupportedLogPages(int device)
+static void
+scsiGetSupportedLogPages(scsi_device * device)
{
int i, err;
if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf,
LOG_RESP_LEN, 0))) {
- if (con->reportscsiioctl > 0)
- pout("Log Sense for supported pages failed [%s]\n",
- scsiErrString(err));
- return;
- }
+ if (scsi_debugmode > 0)
+ pout("Log Sense for supported pages failed [%s]\n",
+ scsiErrString(err));
+ /* 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("Log Sense for supported pages failed (second attempt) [%s]\n",
+ scsiErrString(err));
+ if (err)
+ return;
+ }
for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) {
switch (gBuf[i])
case BACKGROUND_RESULTS_LPAGE:
gBackgroundResultsLPage = 1;
break;
+ case PROTOCOL_SPECIFIC_LPAGE:
+ gProtocolSpecificLPage = 1;
+ break;
case TAPE_ALERTS_LPAGE:
gTapeAlertsLPage = 1;
break;
+ case SS_MEDIA_LPAGE:
+ gSSMediaLPage = 1;
+ break;
case SEAGATE_CACHE_LPAGE:
gSeagateCacheLPage = 1;
break;
/* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
(or at least something to report). */
-static int scsiGetSmartData(int device, int attribs)
+static int
+scsiGetSmartData(scsi_device * device, bool attribs)
{
UINT8 asc;
UINT8 ascq;
- UINT8 currenttemp = 0;
- UINT8 triptemp = 0;
+ UINT8 currenttemp = 255;
+ UINT8 triptemp = 255;
const char * cp;
int err = 0;
-
- PRINT_ON(con);
+ print_on();
if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq,
¤ttemp, &triptemp)) {
/* error message already announced */
- PRINT_OFF(con);
+ print_off();
return -1;
}
- PRINT_OFF(con);
+ print_off();
cp = scsiGetIEString(asc, ascq);
if (cp) {
err = -2;
- PRINT_ON(con);
- pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq);
- PRINT_OFF(con);
+ print_on();
+ pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq);
+ print_off();
} else if (gIecMPage)
pout("SMART Health Status: OK\n");
if (attribs && !gTempLPage) {
- if (currenttemp || triptemp)
- pout("\n");
- if (currenttemp) {
- if (255 != currenttemp)
- pout("Current Drive Temperature: %d C\n", currenttemp);
- else
- pout("Current Drive Temperature: <not available>\n");
- }
- if (triptemp)
+ if (255 == currenttemp)
+ pout("Current Drive Temperature: <not available>\n");
+ else
+ pout("Current Drive Temperature: %d C\n", currenttemp);
+ if (255 == triptemp)
+ pout("Drive Trip Temperature: <not available>\n");
+ else
pout("Drive Trip Temperature: %d C\n", triptemp);
}
+ pout("\n");
return err;
}
// Returns number of logged errors or zero if none or -1 if fetching
// TapeAlerts fails
-static char *severities = "CWI";
+static const char * const severities = "CWI";
-static int scsiGetTapeAlertsData(int device, int peripheral_type)
+static int
+scsiGetTapeAlertsData(scsi_device * device, int peripheral_type)
{
unsigned short pagelength;
unsigned short parametercode;
int i, err;
- char *s;
+ const char *s;
const char *ts;
int failures = 0;
- PRINT_ON(con);
+ print_on();
if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, 0, gBuf,
LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) {
- pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err));
- PRINT_OFF(con);
+ pout("%s Failed [%s]\n", __func__, scsiErrString(err));
+ print_off();
return -1;
}
if (gBuf[0] != 0x2e) {
pout("TapeAlerts Log Sense Failed\n");
- PRINT_OFF(con);
+ print_off();
return -1;
}
pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3];
scsiTapeAlertsTapeDevice(parametercode);
if (*ts == *s) {
if (!failures)
- pout("TapeAlert Errors (C=Critical, W=Warning, I=Informational):\n");
+ pout("TapeAlert Errors (C=Critical, W=Warning, "
+ "I=Informational):\n");
pout("[0x%02x] %s\n", parametercode, ts);
- failures += 1;
+ failures += 1;
}
}
}
}
- PRINT_OFF(con);
+ print_off();
if (! failures)
pout("TapeAlert: OK\n");
return failures;
}
-static void scsiGetStartStopData(int device)
+static void
+scsiGetStartStopData(scsi_device * device)
{
- UINT32 u;
- int err, len, k, extra, pc;
+ int err, len, k, extra;
unsigned char * ucp;
if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf,
LOG_RESP_LEN, 0))) {
- PRINT_ON(con);
- pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err));
- PRINT_OFF(con);
+ print_on();
+ pout("%s Failed [%s]\n", __func__, scsiErrString(err));
+ print_off();
return;
}
if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) {
- PRINT_ON(con);
+ print_on();
pout("StartStop Log Sense Failed, page mismatch\n");
- PRINT_OFF(con);
+ print_off();
return;
}
len = ((gBuf[2] << 8) | gBuf[3]);
ucp = gBuf + 4;
for (k = len; k > 0; k -= extra, ucp += extra) {
if (k < 3) {
- PRINT_ON(con);
+ print_on();
pout("StartStop Log Sense Failed: short\n");
- PRINT_OFF(con);
+ print_off();
return;
}
extra = ucp[3] + 4;
- pc = (ucp[0] << 8) + ucp[1];
+ int pc = (ucp[0] << 8) + ucp[1];
+ UINT32 u;
switch (pc) {
case 1:
if (10 == extra)
if (extra > 7) {
u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
if (0xffffffff != u)
- pout("Recommended maximum start stop count: %u times\n",
+ pout("Specified cycle count over device lifetime: %u\n",
u);
}
break;
if (extra > 7) {
u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
if (0xffffffff != u)
- pout("Current start stop count: %u times\n", u);
+ pout("Accumulated start-stop cycles: %u\n", u);
+ }
+ break;
+ case 5:
+ if (extra > 7) {
+ u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
+ if (0xffffffff != u)
+ pout("Specified load-unload count over device "
+ "lifetime: %u\n", u);
+ }
+ break;
+ case 6:
+ if (extra > 7) {
+ u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
+ if (0xffffffff != u)
+ pout("Accumulated load-unload cycles: %u\n", u);
}
break;
default:
break;
}
}
-}
+}
-static void scsiPrintGrownDefectListLen(int device)
+static void
+scsiPrintGrownDefectListLen(scsi_device * device)
{
- int err, dl_format, dl_len, div;
-
- memset(gBuf, 0, 4);
- if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */,
- 4 /* bytes from index */, gBuf, 4))) {
- if (con->reportscsiioctl > 0) {
- PRINT_ON(con);
- pout("Read defect list (10) Failed: %s\n", scsiErrString(err));
- PRINT_OFF(con);
+ int err, dl_format, got_rd12;
+ unsigned int dl_len, div;
+
+ memset(gBuf, 0, 8);
+ if ((err = scsiReadDefect12(device, 0 /* req_plist */, 1 /* req_glist */,
+ 4 /* format: bytes from index */,
+ 0 /* addr desc index */, gBuf, 8))) {
+ if (2 == err) { /* command not supported */
+ if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */,
+ 4 /* format: bytes from index */, gBuf, 4))) {
+ if (scsi_debugmode > 0) {
+ print_on();
+ pout("Read defect list (10) Failed: %s\n", 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("Read defect list (12) Failed: %s\n", scsiErrString(err));
+ print_off();
+ }
+ return;
}
- return;
+ } else
+ got_rd12 = 1;
+
+ if (got_rd12) {
+ int generation = (gBuf[2] << 8) + gBuf[3];
+ if ((generation > 1) && (scsi_debugmode > 0)) {
+ print_on();
+ pout("Read defect list (12): generation=%d\n", generation);
+ print_off();
+ }
+ dl_len = (gBuf[4] << 24) + (gBuf[5] << 16) + (gBuf[6] << 8) + gBuf[7];
+ } else {
+ dl_len = (gBuf[2] << 8) + gBuf[3];
}
if (0x8 != (gBuf[1] & 0x18)) {
- PRINT_ON(con);
+ print_on();
pout("Read defect list: asked for grown list but didn't get it\n");
- PRINT_OFF(con);
+ print_off();
return;
}
div = 0;
case 0: /* short block */
div = 4;
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 4: /* bytes from index */
case 5: /* physical sector */
div = 8;
break;
default:
- PRINT_ON(con);
+ print_on();
pout("defect list format %d unknown\n", dl_format);
- PRINT_OFF(con);
+ print_off();
break;
}
- dl_len = (gBuf[2] << 8) + gBuf[3];
if (0 == dl_len)
- pout("Elements in grown defect list: 0\n");
+ pout("Elements in grown defect list: 0\n\n");
else {
if (0 == div)
- pout("Grown defect list length=%d bytes [unknown "
- "number of elements]\n", dl_len);
+ pout("Grown defect list length=%u bytes [unknown "
+ "number of elements]\n\n", dl_len);
else
- pout("Elements in grown defect list: %d\n", dl_len / div);
+ pout("Elements in grown defect list: %u\n\n", dl_len / div);
}
}
-static void scsiPrintSeagateCacheLPage(int 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 * xp;
uint64_t ull;
if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf,
LOG_RESP_LEN, 0))) {
- PRINT_ON(con);
+ print_on();
pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err));
- PRINT_OFF(con);
+ print_off();
return;
}
if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) {
- PRINT_ON(con);
+ print_on();
pout("Seagate Cache Log Sense Failed, page mismatch\n");
- PRINT_OFF(con);
+ print_off();
return;
}
len = ((gBuf[2] << 8) | gBuf[3]) + 4;
switch (pc) {
case 0: case 1: case 2: case 3: case 4:
break;
- default:
- if (con->reportscsiioctl > 0) {
- PRINT_ON(con);
+ default:
+ if (scsi_debugmode > 0) {
+ print_on();
pout("Vendor (Seagate) cache lpage has unexpected parameter"
", skip\n");
- PRINT_OFF(con);
+ print_off();
}
return;
}
"> segment size"); break;
default: pout(" Unknown Seagate parameter code [0x%x]", pc); break;
}
- k = pl - 4;
- xp = ucp + 4;
+ int k = pl - 4;
+ unsigned char * xp = ucp + 4;
if (k > (int)sizeof(ull)) {
xp += (k - (int)sizeof(ull));
k = (int)sizeof(ull);
}
ull = 0;
- for (j = 0; j < k; ++j) {
+ for (int j = 0; j < k; ++j) {
if (j > 0)
ull <<= 8;
ull |= xp[j];
}
- pout(" = %"PRIu64"\n", ull);
+ pout(" = %" PRIu64 "\n", ull);
num -= pl;
ucp += pl;
}
+ pout("\n");
}
-static void scsiPrintSeagateFactoryLPage(int 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 * xp;
uint64_t ull;
if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf,
LOG_RESP_LEN, 0))) {
- PRINT_ON(con);
- pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err));
- PRINT_OFF(con);
+ print_on();
+ pout("%s Failed [%s]\n", __func__, scsiErrString(err));
+ print_off();
return;
}
if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) {
- PRINT_ON(con);
+ print_on();
pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n");
- PRINT_OFF(con);
+ print_off();
return;
}
len = ((gBuf[2] << 8) | gBuf[3]) + 4;
case 0: case 8:
++good;
break;
- default:
+ default:
++bad;
break;
}
ucp += pl;
}
if ((good < 2) || (bad > 4)) { /* heuristic */
- if (con->reportscsiioctl > 0) {
- PRINT_ON(con);
+ if (scsi_debugmode > 0) {
+ print_on();
pout("\nVendor (Seagate/Hitachi) factory lpage has too many "
"unexpected parameters, skip\n");
- PRINT_OFF(con);
+ print_off();
}
return;
}
good = 1;
break;
default:
- if (con->reportscsiioctl > 0) {
- PRINT_ON(con);
+ if (scsi_debugmode > 0) {
+ print_on();
pout("Vendor (Seagate/Hitachi) factory lpage: "
"unknown parameter code [0x%x]\n", pc);
- PRINT_OFF(con);
+ print_off();
}
break;
}
if (good) {
- k = pl - 4;
- xp = ucp + 4;
+ int k = pl - 4;
+ unsigned char * xp = ucp + 4;
if (k > (int)sizeof(ull)) {
xp += (k - (int)sizeof(ull));
k = (int)sizeof(ull);
}
ull = 0;
- for (j = 0; j < k; ++j) {
+ for (int j = 0; j < k; ++j) {
if (j > 0)
ull <<= 8;
ull |= xp[j];
}
if (0 == pc)
- pout(" = %.2f\n", uint64_to_double(ull) / 60.0 );
+ pout(" = %.2f\n", ull / 60.0 );
else
- pout(" = %"PRIu64"\n", ull);
+ pout(" = %" PRIu64 "\n", ull);
}
num -= pl;
ucp += pl;
}
+ pout("\n");
}
-static void scsiPrintErrorCounterLog(int device)
+static void
+scsiPrintErrorCounterLog(scsi_device * device)
{
struct scsiErrorCounter errCounterArr[3];
struct scsiErrorCounter * ecp;
- struct scsiNonMediumError nme;
int found[3] = {0, 0, 0};
- const char * pageNames[3] = {"read: ", "write: ", "verify: "};
- int k;
- double processed_gb;
if (gReadECounterLPage && (0 == scsiLogSense(device,
READ_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
VERIFY_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]);
ecp = &errCounterArr[2];
- for (k = 0; k < 7; ++k) {
+ for (int k = 0; k < 7; ++k) {
if (ecp->gotPC[k] && ecp->counter[k]) {
found[2] = 1;
break;
}
}
if (found[0] || found[1] || found[2]) {
- pout("\nError counter log:\n");
+ pout("Error counter log:\n");
pout(" Errors Corrected by Total "
"Correction Gigabytes Total\n");
pout(" ECC rereads/ errors "
"algorithm processed uncorrected\n");
pout(" fast | delayed rewrites corrected "
"invocations [10^9 bytes] errors\n");
- for (k = 0; k < 3; ++k) {
+ for (int k = 0; k < 3; ++k) {
if (! found[k])
continue;
ecp = &errCounterArr[k];
- pout("%s%8"PRIu64" %8"PRIu64" %8"PRIu64" %8"PRIu64" %8"PRIu64,
- pageNames[k], ecp->counter[0], ecp->counter[1],
+ static const char * const pageNames[3] = {"read: ", "write: ", "verify: "};
+ pout("%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]);
- processed_gb = uint64_to_double(ecp->counter[5]) / 1000000000.0;
- pout(" %12.3f %8"PRIu64"\n", processed_gb, ecp->counter[6]);
+ double processed_gb = ecp->counter[5] / 1000000000.0;
+ pout(" %12.3f %8" PRIu64 "\n", processed_gb, ecp->counter[6]);
}
}
- else
- pout("\nError Counter logging not supported\n");
+ else
+ pout("Error Counter logging not supported\n");
if (gNonMediumELPage && (0 == scsiLogSense(device,
NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
+ struct scsiNonMediumError nme;
scsiDecodeNonMediumErrPage(gBuf, &nme);
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)
- pout("Track following error count [Hitachi]: %8"PRIu64"\n",
+ pout("Track following error count [Hitachi]: %8" PRIu64 "\n",
nme.counterTFE_H);
if (nme.gotPE_H)
- pout("Positioning error count [Hitachi]: %8"PRIu64"\n",
+ pout("Positioning error count [Hitachi]: %8" PRIu64 "\n",
nme.counterPE_H);
}
if (gLastNErrorLPage && (0 == scsiLogSense(device,
LAST_N_ERROR_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) {
- unsigned char * ucp;
- int num, k, pc, pl, truncated;
-
- num = (gBuf[2] << 8) + gBuf[3] + 4;
- truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
+ int num = (gBuf[2] << 8) + gBuf[3] + 4;
+ int truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
if (truncated)
num = LOG_RESP_LONG_LEN;
- ucp = gBuf + 4;
+ unsigned char * ucp = gBuf + 4;
num -= 4;
if (num < 4)
pout("\nNo error events logged\n");
else {
pout("\nLast n error events log page\n");
- for (k = num; k > 0; k -= pl, ucp += pl) {
+ for (int k = num, pl; k > 0; k -= pl, ucp += pl) {
if (k < 3) {
pout(" <<short Last n error events log page>>\n");
break;
}
pl = ucp[3] + 4;
- pc = (ucp[0] << 8) + ucp[1];
+ int pc = (ucp[0] << 8) + ucp[1];
if (pl > 4) {
if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) {
pout(" Error event %d:\n", pc);
pout(" Error event %d:\n", pc);
pout(" %.*s\n", pl - 4, (const char *)(ucp + 4));
} else {
- if (con->reportscsiioctl > 0) {
+ if (scsi_debugmode > 0) {
pout(" Error event %d:\n", pc);
pout(" [data counter??]:\n");
dStrHex((const char *)ucp + 4, pl - 4, 1);
"bytes\n", LOG_RESP_LONG_LEN, truncated);
}
}
+ pout("\n");
}
static const char * self_test_code[] = {
- "Default ",
- "Background short",
- "Background long ",
+ "Default ",
+ "Background short",
+ "Background long ",
"Reserved(3) ",
- "Abort background",
- "Foreground short",
+ "Abort background",
+ "Foreground short",
"Foreground long ",
"Reserved(7) "
};
static const char * self_test_result[] = {
"Completed ",
- "Interrupted ('-X' switch)",
- "Interrupted (bus reset ?)",
+ "Aborted (by user command)",
+ "Aborted (device reset ?) ",
"Unknown error, incomplete",
"Completed, segment failed",
"Failed in first segment ",
"Failed in second segment ",
"Failed in segment --> ",
- "Reserved(8) ",
- "Reserved(9) ",
- "Reserved(10) ",
- "Reserved(11) ",
- "Reserved(12) ",
- "Reserved(13) ",
+ "Reserved(8) ",
+ "Reserved(9) ",
+ "Reserved(10) ",
+ "Reserved(11) ",
+ "Reserved(12) ",
+ "Reserved(13) ",
"Reserved(14) ",
"Self test in progress ..."
};
// 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
// FAILSMART is returned.
-static int scsiPrintSelfTest(int device)
+static int
+scsiPrintSelfTest(scsi_device * device)
{
- int num, k, n, res, err, durationSec;
+ int num, k, err, durationSec;
int noheader = 1;
int retval = 0;
UINT8 * ucp;
uint64_t ull=0;
+ struct scsi_sense_disect sense_info;
+
+ // check if test is running
+ if (!scsiRequestSense(device, &sense_info) &&
+ (sense_info.asc == 0x04 && sense_info.ascq == 0x09 &&
+ sense_info.progress != -1)) {
+ pout("Self-test execution status:\t\t%d%% of test remaining\n",
+ 100 - ((sense_info.progress * 100) / 65535));
+ }
if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf,
LOG_RESP_SELF_TEST_LEN, 0))) {
- PRINT_ON(con);
- pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err));
- PRINT_OFF(con);
+ print_on();
+ pout("%s: Failed [%s]\n", __func__, scsiErrString(err));
+ print_off();
return FAILSMART;
}
if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
- PRINT_ON(con);
+ print_on();
pout("Self-test Log Sense Failed, page mismatch\n");
- PRINT_OFF(con);
+ print_off();
return FAILSMART;
}
// compute page length
num = (gBuf[2] << 8) + gBuf[3];
// Log sense page length 0x190 bytes
if (num != 0x190) {
- PRINT_ON(con);
+ print_on();
pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num);
- PRINT_OFF(con);
+ print_off();
return FAILSMART;
}
// loop through the twenty possible entries
int i;
// timestamp in power-on hours (or zero if test in progress)
- n = (ucp[6] << 8) | ucp[7];
+ int n = (ucp[6] << 8) | ucp[7];
// The spec says "all 20 bytes will be zero if no test" but
// DG has found otherwise. So this is a heuristic.
// only print header if needed
if (noheader) {
- pout("\nSMART Self-test log\n");
+ pout("SMART Self-test log\n");
pout("Num Test Status segment "
"LifeTime LBA_first_err [SK ASC ASQ]\n");
pout(" Description number "
}
// print parameter code (test number) & self-test code text
- pout("#%2d %s", (ucp[0] << 8) | ucp[1],
+ pout("#%2d %s", (ucp[0] << 8) | ucp[1],
self_test_code[(ucp[4] >> 5) & 0x7]);
// 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:
+ int res;
switch ((res = ucp[4] & 0xf)) {
case 0x3:
// an unknown error occurred while the device server
if (n==0 && res==0xf)
// self-test in progress
pout(" NOW");
- else
+ else
pout(" %5d", n);
-
+
// construct 8-byte integer address of first failure
for (i = 0; i < 8; i++) {
ull <<= 8;
char buff[32];
// was hex but change to decimal to conform with ATA
- snprintf(buff, sizeof(buff), "%"PRIu64, ull);
- // snprintf(buff, sizeof(buff), "0x%"PRIx64, ull);
+ snprintf(buff, sizeof(buff), "%" PRIu64, ull);
+ // snprintf(buff, sizeof(buff), "0x%" PRIx64, ull);
pout("%18s", buff);
} else
pout(" -");
if (noheader)
pout("No self-tests have been logged\n");
else
- pout("\n");
- if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
+ if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
modese_len)) && (durationSec > 0)) {
- pout("Long (extended) Self Test duration: %d seconds "
+ pout("\nLong (extended) Self Test duration: %d seconds "
"[%.1f minutes]\n", durationSec, durationSec / 60.0);
}
+ pout("\n");
return retval;
}
"halted due to medium formatted without P-List",
"halted - vendor specific cause",
"halted due to temperature out of range",
- "halted until BM interval timer expires", /* 8 */
+ "waiting until BMS interval timer expires", /* 8 */
};
static const char * reassign_status[] = {
- "No reassignment needed",
- "Require Reassign or Write command",
+ "Reserved [0x0]",
+ "Require Write or Reassign Blocks command",
"Successfully reassigned",
"Reserved [0x3]",
- "Failed",
+ "Reassignment by disk failed",
"Recovered via rewrite in-place",
"Reassigned by app, has valid data",
"Reassigned by app, has no valid data",
// 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
// FAILLOG if serious errors detected (in the future).
-static int scsiPrintBackgroundResults(int 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 firstresult = 1;
int retval = 0;
if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf,
LOG_RESP_LONG_LEN, 0))) {
- PRINT_ON(con);
- pout("scsiPrintBackgroundResults Failed [%s]\n", scsiErrString(err));
- PRINT_OFF(con);
+ print_on();
+ pout("%s Failed [%s]\n", __func__, scsiErrString(err));
+ print_off();
return FAILSMART;
}
if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) {
- PRINT_ON(con);
+ print_on();
pout("Background scan results Log Sense Failed, page mismatch\n");
- PRINT_OFF(con);
+ print_off();
return FAILSMART;
}
// compute page length
num = (gBuf[2] << 8) + gBuf[3] + 4;
if (num < 20) {
- PRINT_ON(con);
+ print_on();
pout("Background scan results Log Sense length is %d, no scan "
"status\n", num);
- PRINT_OFF(con);
+ print_off();
return FAILSMART;
}
truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
ucp = gBuf + 4;
num -= 4;
while (num > 3) {
- pc = (ucp[0] << 8) | ucp[1];
+ int pc = (ucp[0] << 8) | ucp[1];
// pcb = ucp[2];
- pl = ucp[3] + 4;
+ int pl = ucp[3] + 4;
switch (pc) {
case 0:
if (noheader) {
noheader = 0;
- pout("\nBackground scan results log\n");
+ pout("Background scan results log\n");
}
pout(" Status: ");
if ((pl < 16) || (num < 16)) {
(ucp[10] << 8) + ucp[11]);
pout("scan progress: %.2f%%\n",
(double)((ucp[12] << 8) + ucp[13]) * 100.0 / 65536.0);
+ pout(" Number of background medium scans performed: %d\n",
+ (ucp[14] << 8) + ucp[15]);
break;
default:
if (noheader) {
if (truncated)
pout(" >>>> log truncated, fetched %d of %d available "
"bytes\n", LOG_RESP_LONG_LEN, truncated);
+ pout("\n");
return retval;
}
-static const char * peripheral_dt_arr[] = {
+// 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
+// and up to 2048 events (although would hope to have less). May set
+// FAILLOG if serious errors detected (in the future).
+static int
+scsiPrintSSMedia(scsi_device * device)
+{
+ int num, err, truncated;
+ int retval = 0;
+ UINT8 * ucp;
+
+ if ((err = scsiLogSense(device, SS_MEDIA_LPAGE, 0, gBuf,
+ LOG_RESP_LONG_LEN, 0))) {
+ print_on();
+ pout("%s: Failed [%s]\n", __func__, scsiErrString(err));
+ print_off();
+ return FAILSMART;
+ }
+ if ((gBuf[0] & 0x3f) != SS_MEDIA_LPAGE) {
+ print_on();
+ pout("Solid state media Log Sense Failed, page mismatch\n");
+ print_off();
+ return FAILSMART;
+ }
+ // compute page length
+ num = (gBuf[2] << 8) + gBuf[3] + 4;
+ if (num < 12) {
+ print_on();
+ pout("Solid state media Log Sense length is %d, too short\n", 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 = (ucp[0] << 8) | ucp[1];
+ // pcb = ucp[2];
+ int pl = ucp[3] + 4;
+ switch (pc) {
+ case 1:
+ if (pl < 8) {
+ print_on();
+ pout("SS Media Percentage used endurance indicator parameter "
+ "too short (pl=%d)\n", pl);
+ print_off();
+ return FAILSMART;
+ }
+ pout("Percentage used endurance indicator: %d%%\n", ucp[7]);
+ default: /* ignore other parameter codes */
+ break;
+ }
+ num -= pl;
+ ucp += pl;
+ }
+ return retval;
+}
+
+static void
+show_sas_phy_event_info(int peis, unsigned int val, unsigned thresh_val)
+{
+ unsigned int u;
+
+ switch (peis) {
+ case 0:
+ pout(" No event\n");
+ break;
+ case 0x1:
+ pout(" Invalid word count: %u\n", val);
+ break;
+ case 0x2:
+ pout(" Running disparity error count: %u\n", val);
+ break;
+ case 0x3:
+ pout(" Loss of dword synchronization count: %u\n", val);
+ break;
+ case 0x4:
+ pout(" Phy reset problem count: %u\n", val);
+ break;
+ case 0x5:
+ pout(" Elasticity buffer overflow count: %u\n", val);
+ break;
+ case 0x6:
+ pout(" Received ERROR count: %u\n", val);
+ break;
+ case 0x20:
+ pout(" Received address frame error count: %u\n", val);
+ break;
+ case 0x21:
+ pout(" Transmitted abandon-class OPEN_REJECT count: %u\n", val);
+ break;
+ case 0x22:
+ pout(" Received abandon-class OPEN_REJECT count: %u\n", val);
+ break;
+ case 0x23:
+ pout(" Transmitted retry-class OPEN_REJECT count: %u\n", val);
+ break;
+ case 0x24:
+ pout(" Received retry-class OPEN_REJECT count: %u\n", val);
+ break;
+ case 0x25:
+ pout(" Received AIP (WATING ON PARTIAL) count: %u\n", val);
+ break;
+ case 0x26:
+ pout(" Received AIP (WAITING ON CONNECTION) count: %u\n", val);
+ break;
+ case 0x27:
+ pout(" Transmitted BREAK count: %u\n", val);
+ break;
+ case 0x28:
+ pout(" Received BREAK count: %u\n", val);
+ break;
+ case 0x29:
+ pout(" Break timeout count: %u\n", val);
+ break;
+ case 0x2a:
+ pout(" Connection count: %u\n", val);
+ break;
+ case 0x2b:
+ pout(" Peak transmitted pathway blocked count: %u\n",
+ val & 0xff);
+ pout(" Peak value detector threshold: %u\n",
+ thresh_val & 0xff);
+ break;
+ case 0x2c:
+ u = val & 0xffff;
+ if (u < 0x8000)
+ pout(" Peak transmitted arbitration wait time (us): "
+ "%u\n", u);
+ else
+ pout(" Peak transmitted arbitration wait time (ms): "
+ "%u\n", 33 + (u - 0x8000));
+ u = thresh_val & 0xffff;
+ if (u < 0x8000)
+ pout(" Peak value detector threshold (us): %u\n",
+ u);
+ else
+ pout(" Peak value detector threshold (ms): %u\n",
+ 33 + (u - 0x8000));
+ break;
+ case 0x2d:
+ pout(" Peak arbitration time (us): %u\n", val);
+ pout(" Peak value detector threshold: %u\n", thresh_val);
+ break;
+ case 0x2e:
+ pout(" Peak connection time (us): %u\n", val);
+ pout(" Peak value detector threshold: %u\n", thresh_val);
+ break;
+ case 0x40:
+ pout(" Transmitted SSP frame count: %u\n", val);
+ break;
+ case 0x41:
+ pout(" Received SSP frame count: %u\n", val);
+ break;
+ case 0x42:
+ pout(" Transmitted SSP frame error count: %u\n", val);
+ break;
+ case 0x43:
+ pout(" Received SSP frame error count: %u\n", val);
+ break;
+ case 0x44:
+ pout(" Transmitted CREDIT_BLOCKED count: %u\n", val);
+ break;
+ case 0x45:
+ pout(" Received CREDIT_BLOCKED count: %u\n", val);
+ break;
+ case 0x50:
+ pout(" Transmitted SATA frame count: %u\n", val);
+ break;
+ case 0x51:
+ pout(" Received SATA frame count: %u\n", val);
+ break;
+ case 0x52:
+ pout(" SATA flow control buffer overflow count: %u\n", val);
+ break;
+ case 0x60:
+ pout(" Transmitted SMP frame count: %u\n", val);
+ break;
+ case 0x61:
+ pout(" Received SMP frame count: %u\n", val);
+ break;
+ case 0x63:
+ pout(" Received SMP frame error count: %u\n", val);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+show_sas_port_param(unsigned char * ucp, int param_len)
+{
+ int j, m, n, nphys, t, sz, spld_len;
+ unsigned char * vcp;
+ uint64_t ull;
+ char s[64];
+
+ sz = sizeof(s);
+ // pcb = ucp[2];
+ t = (ucp[0] << 8) | ucp[1];
+ pout("relative target port id = %d\n", t);
+ pout(" generation code = %d\n", ucp[6]);
+ nphys = ucp[7];
+ pout(" number of phys = %d\n", nphys);
+
+ for (j = 0, vcp = ucp + 8; j < (param_len - 8);
+ vcp += spld_len, j += spld_len) {
+ pout(" phy identifier = %d\n", vcp[1]);
+ spld_len = vcp[3];
+ if (spld_len < 44)
+ spld_len = 48; /* in SAS-1 and SAS-1.1 vcp[3]==0 */
+ else
+ spld_len += 4;
+ t = ((0x70 & vcp[4]) >> 4);
+ switch (t) {
+ case 0: snprintf(s, sz, "no device attached"); break;
+ case 1: snprintf(s, sz, "SAS or SATA device"); break;
+ case 2: snprintf(s, sz, "expander device"); break;
+ case 3: snprintf(s, sz, "expander device (fanout)"); break;
+ default: snprintf(s, sz, "reserved [%d]", t); break;
+ }
+ pout(" attached device type: %s\n", s);
+ t = 0xf & vcp[4];
+ switch (t) {
+ case 0: snprintf(s, sz, "unknown"); break;
+ case 1: snprintf(s, sz, "power on"); break;
+ case 2: snprintf(s, sz, "hard reset"); break;
+ case 3: snprintf(s, sz, "SMP phy control function"); break;
+ case 4: snprintf(s, sz, "loss of dword synchronization"); break;
+ case 5: snprintf(s, sz, "mux mix up"); break;
+ case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
+ break;
+ case 7: snprintf(s, sz, "break timeout timer expired"); break;
+ case 8: snprintf(s, sz, "phy test function stopped"); break;
+ case 9: snprintf(s, sz, "expander device reduced functionality");
+ break;
+ default: snprintf(s, sz, "reserved [0x%x]", t); break;
+ }
+ pout(" attached reason: %s\n", s);
+ t = (vcp[5] & 0xf0) >> 4;
+ switch (t) {
+ case 0: snprintf(s, sz, "unknown"); break;
+ case 1: snprintf(s, sz, "power on"); break;
+ case 2: snprintf(s, sz, "hard reset"); break;
+ case 3: snprintf(s, sz, "SMP phy control function"); break;
+ case 4: snprintf(s, sz, "loss of dword synchronization"); break;
+ case 5: snprintf(s, sz, "mux mix up"); break;
+ case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
+ break;
+ case 7: snprintf(s, sz, "break timeout timer expired"); break;
+ case 8: snprintf(s, sz, "phy test function stopped"); break;
+ case 9: snprintf(s, sz, "expander device reduced functionality");
+ break;
+ default: snprintf(s, sz, "reserved [0x%x]", t); break;
+ }
+ pout(" reason: %s\n", s);
+ t = (0xf & vcp[5]);
+ switch (t) {
+ case 0: snprintf(s, sz, "phy enabled; unknown");
+ break;
+ case 1: snprintf(s, sz, "phy disabled"); break;
+ case 2: snprintf(s, sz, "phy enabled; speed negotiation failed");
+ break;
+ case 3: snprintf(s, sz, "phy enabled; SATA spinup hold state");
+ break;
+ case 4: snprintf(s, sz, "phy enabled; port selector");
+ break;
+ case 5: snprintf(s, sz, "phy enabled; reset in progress");
+ break;
+ case 6: snprintf(s, sz, "phy enabled; unsupported phy attached");
+ break;
+ case 8: snprintf(s, sz, "phy enabled; 1.5 Gbps"); break;
+ case 9: snprintf(s, sz, "phy enabled; 3 Gbps"); break;
+ case 0xa: snprintf(s, sz, "phy enabled; 6 Gbps"); break;
+ case 0xb: snprintf(s, sz, "phy enabled; 12 Gbps"); break;
+ default: snprintf(s, sz, "reserved [%d]", t); break;
+ }
+ pout(" negotiated logical link rate: %s\n", s);
+ pout(" attached initiator port: ssp=%d stp=%d smp=%d\n",
+ !! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2));
+ pout(" attached target port: ssp=%d stp=%d smp=%d\n",
+ !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2));
+ for (n = 0, ull = vcp[8]; n < 8; ++n) {
+ ull <<= 8; ull |= vcp[8 + n];
+ }
+ pout(" SAS address = 0x%" PRIx64 "\n", ull);
+ for (n = 0, ull = vcp[16]; n < 8; ++n) {
+ ull <<= 8; ull |= vcp[16 + n];
+ }
+ pout(" attached SAS address = 0x%" PRIx64 "\n", ull);
+ pout(" attached phy identifier = %d\n", vcp[24]);
+ unsigned int ui;
+ ui = (vcp[32] << 24) | (vcp[33] << 16) | (vcp[34] << 8) | vcp[35];
+ pout(" Invalid DWORD count = %u\n", ui);
+ ui = (vcp[36] << 24) | (vcp[37] << 16) | (vcp[38] << 8) | vcp[39];
+ pout(" Running disparity error count = %u\n", ui);
+ ui = (vcp[40] << 24) | (vcp[41] << 16) | (vcp[42] << 8) | vcp[43];
+ pout(" Loss of DWORD synchronization = %u\n", ui);
+ ui = (vcp[44] << 24) | (vcp[45] << 16) | (vcp[46] << 8) | vcp[47];
+ pout(" Phy reset problem = %u\n", ui);
+ if (spld_len > 51) {
+ int num_ped;
+ unsigned char * xcp;
+
+ num_ped = vcp[51];
+ if (num_ped > 0)
+ pout(" Phy event descriptors:\n");
+ xcp = vcp + 52;
+ for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) {
+ int peis;
+ unsigned int pvdt;
+ peis = xcp[3];
+ ui = (xcp[4] << 24) | (xcp[5] << 16) | (xcp[6] << 8) |
+ xcp[7];
+ pvdt = (xcp[8] << 24) | (xcp[9] << 16) | (xcp[10] << 8) |
+ xcp[11];
+ show_sas_phy_event_info(peis, ui, pvdt);
+ }
+ }
+ }
+}
+
+// Returns 1 if okay, 0 if non SAS descriptors
+static int
+show_protocol_specific_page(unsigned char * resp, int len)
+{
+ int k, num;
+ unsigned char * ucp;
+
+ num = len - 4;
+ for (k = 0, ucp = resp + 4; k < num; ) {
+ int param_len = ucp[3] + 4;
+ if (6 != (0xf & ucp[4]))
+ return 0; /* only decode SAS log page */
+ if (0 == k)
+ pout("Protocol Specific port log page for SAS SSP\n");
+ show_sas_port_param(ucp, param_len);
+ k += param_len;
+ ucp += param_len;
+ }
+ pout("\n");
+ return 1;
+}
+
+
+// See Serial Attached SCSI (SPL-3) (e.g. revision 6g) the Protocol Specific
+// log page [0x18]. Returns 0 if ok else FAIL* bitmask.
+static int
+scsiPrintSasPhy(scsi_device * device, int reset)
+{
+ int num, err;
+
+ if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf,
+ LOG_RESP_LONG_LEN, 0))) {
+ print_on();
+ pout("%s Log Sense Failed [%s]\n\n", __func__, scsiErrString(err));
+ print_off();
+ return FAILSMART;
+ }
+ if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) {
+ print_on();
+ pout("Protocol specific Log Sense Failed, page mismatch\n\n");
+ print_off();
+ return FAILSMART;
+ }
+ // compute page length
+ num = (gBuf[2] << 8) + gBuf[3];
+ if (1 != show_protocol_specific_page(gBuf, num + 4)) {
+ print_on();
+ pout("Only support protocol specific log page on SAS devices\n\n");
+ print_off();
+ return FAILSMART;
+ }
+ if (reset) {
+ if ((err = scsiLogSelect(device, 1 /* pcr */, 0 /* sp */, 0 /* pc */,
+ PROTOCOL_SPECIFIC_LPAGE, 0, NULL, 0))) {
+ print_on();
+ pout("%s Log Select (reset) Failed [%s]\n\n", __func__,
+ scsiErrString(err));
+ print_off();
+ return FAILSMART;
+ }
+ }
+ return 0;
+}
+
+
+static const char * peripheral_dt_arr[32] = {
"disk",
"tape",
"printer",
"storage array",
"enclosure",
"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",
};
static const char * transport_proto_arr[] = {
"IEEE 1394 (SBP-2)",
"RDMA (SRP)",
"iSCSI",
- "SAS",
+ "SAS (SPL-3)",
"ADT",
- "0x8",
- "0x9",
- "0xa",
+ "ATA (ACS-2)",
+ "UAS",
+ "SOP",
"0xb",
"0xc",
"0xd",
};
/* Returns 0 on success, 1 on general error and 2 for early, clean exit */
-static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
+static int
+scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
{
- char manufacturer[9];
- char product[17];
- char revision[5];
char timedatetz[DATEANDEPOCHLEN];
struct scsi_iec_mode_page iec;
- int err, iec_err, len, req_len, avail_len, val;
- int is_tape = 0;
+ int err, iec_err, len, req_len, avail_len, scsi_version;
+ bool is_tape = false;
int peri_dt = 0;
- int returnval=0;
-
+ int transport = -1;
+ int form_factor = 0;
+ int haw_zbc = 0;
+ int protect = 0;
+
memset(gBuf, 0, 96);
req_len = 36;
if ((err = scsiStdInquiry(device, gBuf, req_len))) {
- PRINT_ON(con);
+ print_on();
pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err));
pout("Retrying with a 64 byte Standard Inquiry\n");
- PRINT_OFF(con);
+ print_off();
/* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
req_len = 64;
if ((err = scsiStdInquiry(device, gBuf, req_len))) {
- PRINT_ON(con);
+ print_on();
pout("Standard Inquiry (64 bytes) failed [%s]\n",
scsiErrString(err));
- PRINT_OFF(con);
+ print_off();
return 1;
}
}
avail_len = gBuf[4] + 5;
len = (avail_len < req_len) ? avail_len : req_len;
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) {
- PRINT_ON(con);
+ print_on();
pout("Short INQUIRY response, skip product id\n");
- PRINT_OFF(con);
+ print_off();
return 1;
}
- memset(manufacturer, 0, sizeof(manufacturer));
- strncpy(manufacturer, (char *)&gBuf[8], 8);
-
- memset(product, 0, sizeof(product));
- strncpy(product, (char *)&gBuf[16], 16);
-
- memset(revision, 0, sizeof(revision));
- strncpy(revision, (char *)&gBuf[32], 4);
- if (all && (0 != strncmp(manufacturer, "ATA", 3)))
- pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
-
- if (0 == strncmp(manufacturer, "3ware", 5) || 0 == strncmp(manufacturer, "AMCC", 4) ) {
-#if defined(_WIN32) || defined(__CYGWIN__)
- pout("please try changing device to /dev/hdX,N\n");
-#else
- pout("please try adding '-d 3ware,N'\n");
- pout("you may also need to change device to /dev/twaN or /dev/tweN\n");
-#endif
- return 2;
- } else if ((len >= 42) &&
- (0 == strncmp((const char *)(gBuf + 36), "MVSATA", 6))) {
- pout("please try '-d marvell'\n");
+ // 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))) {
+ char vendor[8+1], product[16+1], revision[4+1];
+ scsi_format_id_string(vendor, (const unsigned char *)&gBuf[8], 8);
+ scsi_format_id_string(product, (const unsigned char *)&gBuf[16], 16);
+ scsi_format_id_string(revision, (const unsigned char *)&gBuf[32], 4);
+
+ pout("=== START OF INFORMATION SECTION ===\n");
+ pout("Vendor: %.8s\n", vendor);
+ pout("Product: %.16s\n", product);
+ if (gBuf[32] >= ' ')
+ pout("Revision: %.4s\n", revision);
+ if (scsi_version == 0x6)
+ pout("Compliance: SPC-4\n");
+ else if (scsi_version == 0x7)
+ pout("Compliance: SPC-5\n");
+ }
+
+ if (!*device->get_req_type()/*no type requested*/ &&
+ (0 == strncmp((char *)&gBuf[8], "ATA", 3))) {
+ pout("\nProbable ATA device behind a SAT layer\n"
+ "Try an additional '-d ata' or '-d sat' argument.\n");
return 2;
- } else if ((0 == con->controller_explicit) &&
- (0 == strncmp(manufacturer, "ATA ", 8)) &&
- has_sat_pass_through(device, 0)) {
- con->controller_type = CONTROLLER_SAT;
- if (con->reportscsiioctl > 0) {
- PRINT_ON(con);
- pout("Detected SAT interface, switch to device type 'sat'\n");
- PRINT_OFF(con);
+ }
+ if (! all)
+ return 0;
+
+ protect = gBuf[5] & 0x1; /* from and including SPC-3 */
+
+ if (! is_tape) { /* assume disk if not tape drive (or tape changer) */
+ unsigned int lb_size = 0;
+ unsigned char lb_prov_resp[8];
+ char lb_str[16];
+ int lb_per_pb_exp = 0;
+ uint64_t capacity = scsiGetSize(device, &lb_size, &lb_per_pb_exp);
+
+ 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);
+ pout("User Capacity: %s bytes [%s]\n", cap_str, si_str);
+ snprintf(lb_str, sizeof(lb_str) - 1, "%u", lb_size);
+ pout("Logical block size: %s bytes\n", lb_str);
}
- return 2;
- } else if ((avail_len >= 96) && (0 == strncmp(manufacturer, "ATA", 3))) {
- /* <<<< This is Linux specific code to detect SATA disks using a
- SCSI-ATA command translation layer. This may be generalized
- later when the t10.org SAT project matures. >>>> */
- pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
- req_len = 252;
- memset(gBuf, 0, req_len);
- if ((err = scsiInquiryVpd(device, 0x83, gBuf, req_len))) {
- PRINT_ON(con);
- pout("Inquiry for VPD page 0x83 [device id] failed [%s]\n",
- scsiErrString(err));
- PRINT_OFF(con);
- return 1;
+ int lbpme = -1;
+ int lbprz = -1;
+ if (protect || lb_per_pb_exp) {
+ unsigned char rc16_12[20] = {0, };
+
+ if (0 == scsiGetProtPBInfo(device, rc16_12)) {
+ lb_per_pb_exp = rc16_12[1] & 0xf; /* just in case */
+ if (lb_per_pb_exp > 0) {
+ snprintf(lb_str, sizeof(lb_str) - 1, "%u",
+ (lb_size * (1 << lb_per_pb_exp)));
+ pout("Physical block size: %s bytes\n", lb_str);
+ int n = ((rc16_12[2] & 0x3f) << 8) + rc16_12[3];
+ if (n > 0) // not common so cut the clutter
+ pout("Lowest aligned LBA: %d\n", n);
+ }
+ if (rc16_12[0] & 0x1) { /* PROT_EN set */
+ int p_type = ((rc16_12[0] >> 1) & 0x7);
+
+ switch (p_type) {
+ case 0 :
+ pout("Formatted with type 1 protection\n");
+ break;
+ case 1 :
+ pout("Formatted with type 2 protection\n");
+ break;
+ case 2 :
+ pout("Formatted with type 3 protection\n");
+ break;
+ default:
+ pout("Formatted with unknown protection type [%d]\n",
+ p_type);
+ break;
+ }
+ int p_i_exp = ((rc16_12[1] >> 4) & 0xf);
+
+ if (p_i_exp > 0)
+ pout("%d protection information intervals per "
+ "logical block\n", (1 << p_i_exp));
+ }
+ /* Pick up some LB provisioning info since its available */
+ lbpme = !! (rc16_12[2] & 0x80);
+ lbprz = !! (rc16_12[2] & 0x40);
+ }
}
- avail_len = ((gBuf[2] << 8) + gBuf[3]) + 4;
- len = (avail_len < req_len) ? avail_len : req_len;
- if (isLinuxLibAta(gBuf, len)) {
- pout("\nIn Linux, SATA disks accessed via libata are "
- "only supported by smartmontools\n"
- "for kernel versions 2.6.15 and above.\n Try "
- "an additional '-d ata' or '-d sat' argument.\n");
- return 2;
+ /* 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)
+ pout("Rotation Rate: Solid State Device\n");
+ else if ((rpm <= 0x400) || (0xffff == rpm))
+ ; // Reserved
+ else
+ pout("Rotation Rate: %d rpm\n", 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;
+ }
+ if (cp)
+ pout("Form Factor: %s inches\n", cp);
+ }
+ if (haw_zbc > 0)
+ pout("Host aware zoned block capable\n");
}
- if (! all)
- return 0;
/* Do this here to try and detect badly conforming devices (some USB
keys) that will lock up on a InquiryVpd or log sense or ... */
if (SIMPLE_ERR_BAD_RESP == iec_err) {
pout(">> Terminate command early due to bad response to IEC "
"mode page\n");
- PRINT_OFF(con);
+ print_off();
gIecMPage = 0;
return 1;
}
} else
modese_len = iec.modese_len;
- if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) {
- /* should use VPD page 0x83 and fall back to this page (0x80)
- * if 0x83 not supported. NAA requires a lot of decoding code */
- len = gBuf[3];
- gBuf[4 + len] = '\0';
- pout("Serial number: %s\n", &gBuf[4]);
- }
- else if (con->reportscsiioctl > 0) {
- PRINT_ON(con);
- if (SIMPLE_ERR_BAD_RESP == err)
- pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
- else
- pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
- PRINT_OFF(con);
+ if (! dont_print_serial_number) {
+ if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_DEVICE_IDENTIFICATION,
+ gBuf, 252))) {
+ char s[256];
+
+ len = gBuf[3];
+ scsi_decode_lu_dev_id(gBuf + 4, len, s, sizeof(s), &transport);
+ if (strlen(s) > 0)
+ pout("Logical Unit id: %s\n", s);
+ } else if (scsi_debugmode > 0) {
+ print_on();
+ if (SIMPLE_ERR_BAD_RESP == err)
+ pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
+ else
+ pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
+ print_off();
+ }
+ if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_UNIT_SERIAL_NUMBER,
+ gBuf, 252))) {
+ char serial[256];
+ len = gBuf[3];
+
+ gBuf[4 + len] = '\0';
+ scsi_format_id_string(serial, &gBuf[4], len);
+ pout("Serial number: %s\n", serial);
+ } else if (scsi_debugmode > 0) {
+ print_on();
+ if (SIMPLE_ERR_BAD_RESP == err)
+ pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
+ else
+ pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
+ print_off();
+ }
}
// print SCSI peripheral device type
- if (peri_dt < (int)(sizeof(peripheral_dt_arr) /
+ if (peri_dt < (int)(sizeof(peripheral_dt_arr) /
sizeof(peripheral_dt_arr[0])))
- pout("Device type: %s\n", peripheral_dt_arr[peri_dt]);
+ pout("Device type: %s\n", peripheral_dt_arr[peri_dt]);
else
- pout("Device type: <%d>\n", peri_dt);
+ pout("Device type: <%d>\n", peri_dt);
// See if transport protocol is known
- val = scsiFetchTransportProtocol(device, modese_len);
- if ((val >= 0) && (val <= 0xf))
- pout("Transport protocol: %s\n", transport_proto_arr[val]);
+ if (transport < 0)
+ transport = scsiFetchTransportProtocol(device, modese_len);
+ if ((transport >= 0) && (transport <= 0xf))
+ pout("Transport protocol: %s\n", transport_proto_arr[transport]);
// print current time and date and timezone
dateandtimezone(timedatetz);
- pout("Local Time is: %s\n", timedatetz);
+ pout("Local Time is: %s\n", timedatetz);
- if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) ||
- (SCSI_PT_MEDIUM_CHANGER == *peripheral_type))
- is_tape = 1;
// See if unit accepts SCSI commmands from us
if ((err = scsiTestUnitReady(device))) {
if (SIMPLE_ERR_NOT_READY == err) {
- PRINT_ON(con);
+ print_on();
if (!is_tape)
pout("device is NOT READY (e.g. spun down, busy)\n");
else
pout("device is NOT READY (e.g. no tape)\n");
- PRINT_OFF(con);
- } else if (SIMPLE_ERR_NO_MEDIUM == err) {
- PRINT_ON(con);
- pout("NO MEDIUM present on device\n");
- PRINT_OFF(con);
- } else if (SIMPLE_ERR_BECOMING_READY == err) {
- PRINT_ON(con);
+ print_off();
+ } else if (SIMPLE_ERR_NO_MEDIUM == err) {
+ print_on();
+ if (is_tape)
+ pout("NO tape present in drive\n");
+ else
+ pout("NO MEDIUM present in device\n");
+ print_off();
+ } else if (SIMPLE_ERR_BECOMING_READY == err) {
+ print_on();
pout("device becoming ready (wait)\n");
- PRINT_OFF(con);
+ print_off();
} else {
- PRINT_ON(con);
+ print_on();
pout("device Test Unit Ready [%s]\n", scsiErrString(err));
- PRINT_OFF(con);
+ print_off();
+ }
+ if (! is_tape) {
+ int returnval = 0; // TODO: exit with FAILID if failuretest returns
+
+ failuretest(MANDATORY_CMD, returnval|=FAILID);
}
- failuretest(MANDATORY_CMD, returnval|=FAILID);
}
-
+
if (iec_err) {
if (!is_tape) {
- PRINT_ON(con);
- pout("Device does not support SMART");
- if (con->reportscsiioctl > 0)
+ print_on();
+ pout("SMART support is: Unavailable - device lacks SMART capability.\n");
+ if (scsi_debugmode > 0)
pout(" [%s]\n", scsiErrString(iec_err));
- else
- pout("\n");
- PRINT_OFF(con);
+ print_off();
}
gIecMPage = 0;
return 0;
}
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");
- pout("%s\n", (scsi_IsWarningEnabled(&iec)) ?
- "Temperature Warning Enabled" :
- "Temperature Warning Disabled or Not Supported");
+ pout("%s\n", (scsi_IsWarningEnabled(&iec)) ?
+ "Temperature Warning: Enabled" :
+ "Temperature Warning: Disabled or Not Supported");
return 0;
}
-static int scsiSmartEnable(int device)
+static int
+scsiSmartEnable(scsi_device * device)
{
struct scsi_iec_mode_page iec;
int err;
if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
- PRINT_ON(con);
- pout("unable to fetch IEC (SMART) mode page [%s]\n",
+ print_on();
+ pout("unable to fetch IEC (SMART) mode page [%s]\n",
scsiErrString(err));
- PRINT_OFF(con);
+ print_off();
return 1;
} else
modese_len = iec.modese_len;
if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) {
- PRINT_ON(con);
+ print_on();
pout("unable to enable Exception control and warning [%s]\n",
scsiErrString(err));
- PRINT_OFF(con);
+ print_off();
return 1;
}
/* Need to refetch 'iec' since could be modified by previous call */
if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
- pout("unable to fetch IEC (SMART) mode page [%s]\n",
+ pout("unable to fetch IEC (SMART) mode page [%s]\n",
scsiErrString(err));
return 1;
} else
scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
return 0;
}
-
-static int scsiSmartDisable(int device)
+
+static int
+scsiSmartDisable(scsi_device * device)
{
struct scsi_iec_mode_page iec;
int err;
if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
- PRINT_ON(con);
- pout("unable to fetch IEC (SMART) mode page [%s]\n",
+ print_on();
+ pout("unable to fetch IEC (SMART) mode page [%s]\n",
scsiErrString(err));
- PRINT_OFF(con);
+ print_off();
return 1;
} else
modese_len = iec.modese_len;
if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) {
- PRINT_ON(con);
+ print_on();
pout("unable to disable Exception control and warning [%s]\n",
scsiErrString(err));
- PRINT_OFF(con);
+ print_off();
return 1;
}
/* Need to refetch 'iec' since could be modified by previous call */
if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
- pout("unable to fetch IEC (SMART) mode page [%s]\n",
+ pout("unable to fetch IEC (SMART) mode page [%s]\n",
scsiErrString(err));
return 1;
} else
return 0;
}
-static void scsiPrintTemp(int device)
+static void
+scsiPrintTemp(scsi_device * device)
{
- UINT8 temp = 0;
- UINT8 trip = 0;
+ UINT8 temp = 255;
+ UINT8 trip = 255;
if (scsiGetTemp(device, &temp, &trip))
return;
-
- if (temp) {
- if (255 != temp)
- pout("Current Drive Temperature: %d C\n", temp);
- else
- pout("Current Drive Temperature: <not available>\n");
- }
- if (trip)
+
+ if (255 == temp)
+ pout("Current Drive Temperature: <not available>\n");
+ else
+ pout("Current Drive Temperature: %d C\n", temp);
+ if (255 == trip)
+ pout("Drive Trip Temperature: <not available>\n");
+ else
pout("Drive Trip Temperature: %d C\n", trip);
+ pout("\n");
}
/* Main entry point used by smartctl command. Return 0 for success */
-int scsiPrintMain(int fd)
+int
+scsiPrintMain(scsi_device * device, const scsi_print_options & options)
{
int checkedSupportedLogPages = 0;
UINT8 peripheral_type = 0;
int returnval = 0;
int res, durationSec;
+ struct scsi_sense_disect sense_info;
+ bool is_disk;
+ bool is_tape;
- res = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo);
+ 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);
if (res) {
if (2 == res)
return 0;
else
failuretest(MANDATORY_CMD, returnval |= FAILID);
+ any_output = true;
}
+ is_disk = (SCSI_PT_DIRECT_ACCESS == 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 (con->smartenable) {
- if (scsiSmartEnable(fd))
+ 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 (scsiSmartEnable(device))
failuretest(MANDATORY_CMD, returnval |= FAILSMART);
+ any_output = true;
}
- if (con->smartdisable) {
- if (scsiSmartDisable(fd))
+ if (options.smart_disable) {
+ if (scsiSmartDisable(device))
failuretest(MANDATORY_CMD,returnval |= FAILSMART);
+ any_output = true;
}
-
- if (con->smartautosaveenable) {
- if (scsiSetControlGLTSD(fd, 0, modese_len)) {
- pout("Enable autosave (clear GLTSD bit) failed\n");
- failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
- }
- }
-
- if (con->smartautosavedisable) {
- if (scsiSetControlGLTSD(fd, 1, modese_len)) {
- pout("Disable autosave (set GLTSD bit) failed\n");
- failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
- }
- }
-
- if (con->checksmart) {
- scsiGetSupportedLogPages(fd);
+
+ if (options.smart_auto_save_enable) {
+ if (scsiSetControlGLTSD(device, 0, modese_len)) {
+ pout("Enable autosave (clear GLTSD bit) failed\n");
+ failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
+ } else
+ 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 (scsiSetControlGLTSD(device, 1, modese_len)) {
+ pout("Disable autosave (set GLTSD bit) failed\n");
+ failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
+ } else
+ 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) {
+ scsiGetSupportedLogPages(device);
checkedSupportedLogPages = 1;
- if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
- (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */
+ if (is_tape) {
if (gTapeAlertsLPage) {
- if (con->driveinfo)
+ if (options.drive_info)
pout("TapeAlert Supported\n");
- if (-1 == scsiGetTapeAlertsData(fd, peripheral_type))
+ if (-1 == scsiGetTapeAlertsData(device, peripheral_type))
failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
}
else
pout("TapeAlert Not Supported\n");
} else { /* disk, cd/dvd, enclosure, etc */
- if ((res = scsiGetSmartData(fd, con->smartvendorattrib))) {
+ if ((res = scsiGetSmartData(device, options.smart_vendor_attrib))) {
if (-2 == res)
returnval |= FAILSTATUS;
else
returnval |= FAILSMART;
}
}
- }
- if (con->smartvendorattrib) {
+ any_output = true;
+ }
+
+ if (is_disk && options.smart_ss_media_log) {
if (! checkedSupportedLogPages)
- scsiGetSupportedLogPages(fd);
- if (gTempLPage) {
- if (con->checksmart)
- pout("\n");
- scsiPrintTemp(fd);
- }
+ scsiGetSupportedLogPages(device);
+ res = 0;
+ if (gSSMediaLPage)
+ res = scsiPrintSSMedia(device);
+ if (0 != res)
+ failuretest(OPTIONAL_CMD, returnval|=res);
+ any_output = true;
+ }
+ if (options.smart_vendor_attrib) {
+ if (! checkedSupportedLogPages)
+ scsiGetSupportedLogPages(device);
+ if (gTempLPage)
+ scsiPrintTemp(device);
if (gStartStopLPage)
- scsiGetStartStopData(fd);
- if (SCSI_PT_DIRECT_ACCESS == peripheral_type) {
- scsiPrintGrownDefectListLen(fd);
+ scsiGetStartStopData(device);
+ if (is_disk) {
+ scsiPrintGrownDefectListLen(device);
if (gSeagateCacheLPage)
- scsiPrintSeagateCacheLPage(fd);
+ scsiPrintSeagateCacheLPage(device);
if (gSeagateFactoryLPage)
- scsiPrintSeagateFactoryLPage(fd);
+ scsiPrintSeagateFactoryLPage(device);
}
+ any_output = true;
}
- if (con->smarterrorlog) {
+ if (options.smart_error_log) {
if (! checkedSupportedLogPages)
- scsiGetSupportedLogPages(fd);
- scsiPrintErrorCounterLog(fd);
- if (1 == scsiFetchControlGLTSD(fd, modese_len, 1))
+ scsiGetSupportedLogPages(device);
+ scsiPrintErrorCounterLog(device);
+ if (1 == scsiFetchControlGLTSD(device, modese_len, 1))
pout("\n[GLTSD (Global Logging Target Save Disable) set. "
"Enable Save with '-S on']\n");
+ any_output = true;
}
- if (con->smartselftestlog) {
+ if (options.smart_selftest_log) {
if (! checkedSupportedLogPages)
- scsiGetSupportedLogPages(fd);
+ scsiGetSupportedLogPages(device);
res = 0;
if (gSelfTestLPage)
- res = scsiPrintSelfTest(fd);
+ res = scsiPrintSelfTest(device);
else {
pout("Device does not support Self Test logging\n");
failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
}
if (0 != res)
failuretest(OPTIONAL_CMD, returnval|=res);
+ any_output = true;
}
- if (con->smartbackgroundlog) {
+ if (options.smart_background_log && is_disk) {
if (! checkedSupportedLogPages)
- scsiGetSupportedLogPages(fd);
+ scsiGetSupportedLogPages(device);
res = 0;
if (gBackgroundResultsLPage)
- res = scsiPrintBackgroundResults(fd);
+ res = scsiPrintBackgroundResults(device);
else {
pout("Device does not support Background scan results logging\n");
failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
}
if (0 != res)
failuretest(OPTIONAL_CMD, returnval|=res);
+ any_output = true;
}
- if (con->smartexeoffimmediate) {
- if (scsiSmartDefaultSelfTest(fd))
+ if (options.smart_default_selftest) {
+ if (scsiSmartDefaultSelfTest(device))
return returnval | FAILSMART;
pout("Default Self Test Successful\n");
+ any_output = true;
}
- if (con->smartshortcapselftest) {
- if (scsiSmartShortCapSelfTest(fd))
+ if (options.smart_short_cap_selftest) {
+ if (scsiSmartShortCapSelfTest(device))
return returnval | FAILSMART;
pout("Short Foreground Self Test Successful\n");
+ 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 (con->smartshortselftest ) {
- if (scsiSmartShortSelfTest(fd))
+ if (options.smart_short_selftest) {
+ if (scsiSmartShortSelfTest(device))
return returnval | FAILSMART;
pout("Short Background Self Test has begun\n");
pout("Use smartctl -X to abort test\n");
+ any_output = true;
}
- if (con->smartextendselftest) {
- if (scsiSmartExtendSelfTest(fd))
+ if (options.smart_extend_selftest) {
+ if (scsiSmartExtendSelfTest(device))
return returnval | FAILSMART;
pout("Extended Background Self Test has begun\n");
- if ((0 == scsiFetchExtendedSelfTestTime(fd, &durationSec,
+ if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
modese_len)) && (durationSec > 0)) {
time_t t = time(NULL);
t += durationSec;
- pout("Please wait %d minutes for test to complete.\n",
+ pout("Please wait %d minutes for test to complete.\n",
durationSec / 60);
pout("Estimated completion time: %s\n", ctime(&t));
}
- pout("Use smartctl -X to abort test\n");
+ pout("Use smartctl -X to abort test\n");
+ any_output = true;
}
- if (con->smartextendcapselftest) {
- if (scsiSmartExtendCapSelfTest(fd))
+ if (options.smart_extend_cap_selftest) {
+ if (scsiSmartExtendCapSelfTest(device))
return returnval | FAILSMART;
pout("Extended Foreground Self Test Successful\n");
}
- if (con->smartselftestabort) {
- if (scsiSmartSelfTestAbort(fd))
+ if (options.smart_selftest_abort) {
+ if (scsiSmartSelfTestAbort(device))
return returnval | FAILSMART;
pout("Self Test returned without error\n");
- }
+ any_output = true;
+ }
+ if (options.sasphy && gProtocolSpecificLPage) {
+ if (scsiPrintSasPhy(device, options.sasphy_reset))
+ return returnval | FAILSMART;
+ any_output = true;
+ }
+
+ if (!any_output)
+ pout("SCSI device successfully opened\n\nUse 'smartctl -a' (or '-x') "
+ "to print SMART (and more) information\n\n");
+
return returnval;
}