]> git.proxmox.com Git - mirror_smartmontools-debian.git/blobdiff - scsiprint.cpp
Enhance dh_clean to clean up better
[mirror_smartmontools-debian.git] / scsiprint.cpp
index f426b27bf6546afd2fd3a740a2b452628ca7184e..5e4cb4d164d6d0a08dc27337e369db88c1cb35d3 100644 (file)
@@ -3,11 +3,11 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
  *
  * Additional SCSI work:
- * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
+ * Copyright (C) 2003-13 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
@@ -15,8 +15,8 @@
  * 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;
+const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 4040 2015-03-10 22:30:44Z dpgilbert $"
+                                 SCSIPRINT_H_CVSID;
 
-// control block which points to external global control variables
-extern smartmonctrl *con;
-
-// 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 */
@@ -67,7 +62,11 @@ static int gVerifyECounterLPage = 0;
 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;
 
@@ -77,21 +76,19 @@ static int gIecMPage = 1;     /* N.B. assume it until we know otherwise */
 /* 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)); 
+        if (scsi_debugmode > 0)
+            pout("Log Sense for supported pages failed [%s]\n",
+                 scsiErrString(err));
         return;
-    } 
+    }
 
     for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) {
         switch (gBuf[i])
@@ -126,9 +123,15 @@ static void scsiGetSupportedLogPages(int device)
             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;
@@ -143,7 +146,8 @@ static void scsiGetSupportedLogPages(int device)
 
 /* 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;
@@ -151,27 +155,24 @@ static int scsiGetSmartData(int device, int attribs)
     UINT8 triptemp = 0;
     const char * cp;
     int err = 0;
-
-    PRINT_ON(con);
+    print_on();
     if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq,
                     &currenttemp, &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);
@@ -181,33 +182,35 @@ static int scsiGetSmartData(int device, int attribs)
         if (triptemp)
             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);
+        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];
@@ -222,14 +225,15 @@ static int scsiGetTapeAlertsData(int device, int peripheral_type)
                     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");
@@ -237,7 +241,8 @@ static int scsiGetTapeAlertsData(int device, int peripheral_type)
     return failures;
 }
 
-static void scsiGetStartStopData(int device)
+static void
+scsiGetStartStopData(scsi_device * device)
 {
     UINT32 u;
     int err, len, k, extra, pc;
@@ -245,24 +250,24 @@ static void scsiGetStartStopData(int device)
 
     if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf,
                             LOG_RESP_LEN, 0))) {
-        PRINT_ON(con);
+        print_on();
         pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err));
-        PRINT_OFF(con);
+        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;
@@ -280,7 +285,7 @@ static void scsiGetStartStopData(int device)
             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;
@@ -288,7 +293,22 @@ static void scsiGetStartStopData(int device)
             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:
@@ -296,26 +316,57 @@ static void scsiGetStartStopData(int device)
             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, generation;
+    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) {
+        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;
@@ -324,30 +375,35 @@ static void scsiPrintGrownDefectListLen(int device)
         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;
     unsigned char * ucp;
@@ -356,15 +412,15 @@ static void scsiPrintSeagateCacheLPage(int device)
 
     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;
@@ -376,12 +432,12 @@ static void scsiPrintSeagateCacheLPage(int device)
         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;
         }
@@ -416,13 +472,15 @@ static void scsiPrintSeagateCacheLPage(int device)
                 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;
     unsigned char * ucp;
@@ -431,15 +489,15 @@ static void scsiPrintSeagateFactoryLPage(int device)
 
     if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf,
                             LOG_RESP_LEN, 0))) {
-        PRINT_ON(con);
+        print_on();
         pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err));
-        PRINT_OFF(con);
+        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;
@@ -454,7 +512,7 @@ static void scsiPrintSeagateFactoryLPage(int device)
         case 0: case 8:
             ++good;
             break;
-        default: 
+        default:
             ++bad;
             break;
         }
@@ -462,11 +520,11 @@ static void scsiPrintSeagateFactoryLPage(int device)
         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;
     }
@@ -485,11 +543,11 @@ static void scsiPrintSeagateFactoryLPage(int device)
             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;
         }
@@ -507,23 +565,24 @@ static void scsiPrintSeagateFactoryLPage(int device)
                 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,
@@ -540,7 +599,7 @@ static void scsiPrintErrorCounterLog(int device)
                 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;
@@ -548,60 +607,57 @@ static void scsiPrintErrorCounterLog(int device)
         }
     }
     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], 
+            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]);
+            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))) {
         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);
@@ -611,7 +667,7 @@ static void scsiPrintErrorCounterLog(int device)
                         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);
@@ -624,34 +680,35 @@ static void scsiPrintErrorCounterLog(int device)
                      "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 ..."
 };
@@ -660,34 +717,44 @@ static const char * self_test_result[] = {
 // 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 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);
+        print_on();
         pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err));
-        PRINT_OFF(con);
+        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
@@ -704,7 +771,7 @@ static int scsiPrintSelfTest(int device)
 
         // 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   "
@@ -713,7 +780,7 @@ static int scsiPrintSelfTest(int device)
         }
 
         // 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
@@ -765,9 +832,9 @@ static int scsiPrintSelfTest(int device)
         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;
@@ -778,8 +845,8 @@ static int scsiPrintSelfTest(int device)
             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("                 -");
@@ -796,12 +863,12 @@ static int scsiPrintSelfTest(int device)
     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;
 }
 
@@ -814,15 +881,15 @@ static const char * bms_status[] = {
     "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",
@@ -833,7 +900,8 @@ static const char * reassign_status[] = {
 // 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 noheader = 1;
@@ -843,24 +911,24 @@ static int scsiPrintBackgroundResults(int device)
 
     if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf,
                             LOG_RESP_LONG_LEN, 0))) {
-        PRINT_ON(con);
+        print_on();
         pout("scsiPrintBackgroundResults Failed [%s]\n", scsiErrString(err));
-        PRINT_OFF(con);
+        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;
@@ -876,7 +944,7 @@ static int scsiPrintBackgroundResults(int device)
         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)) {
@@ -895,6 +963,8 @@ static int scsiPrintBackgroundResults(int device)
                  (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) {
@@ -931,9 +1001,398 @@ static int scsiPrintBackgroundResults(int device)
     if (truncated)
         pout(" >>>> log truncated, fetched %d of %d available "
              "bytes\n", LOG_RESP_LONG_LEN, truncated);
+    pout("\n");
     return retval;
 }
 
+// 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, pc, pl, truncated;
+    int retval = 0;
+    UINT8 * ucp;
+
+    if ((err = scsiLogSense(device, SS_MEDIA_LPAGE, 0, gBuf,
+                            LOG_RESP_LONG_LEN, 0))) {
+        print_on();
+        pout("scsiPrintSSMedia Failed [%s]\n", 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) {
+        pc = (ucp[0] << 8) | ucp[1];
+        // pcb = ucp[2];
+        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;
+    unsigned int ui;
+    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]);
+        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, peis;
+            unsigned char * xcp;
+            unsigned int pvdt;
+
+            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) {
+                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, param_len;
+    unsigned char * ucp;
+
+    num = len - 4;
+    for (k = 0, ucp = resp + 4; k < num; ) {
+        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("scsiPrintSasPhy Log Sense Failed [%s]\n\n", 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("scsiPrintSasPhy Log Select (reset) Failed [%s]\n\n",
+                 scsiErrString(err));
+            print_off();
+            return FAILSMART;
+        }
+    }
+    return 0;
+}
+
+
 static const char * peripheral_dt_arr[] = {
         "disk",
         "tape",
@@ -951,6 +1410,22 @@ static const char * peripheral_dt_arr[] = {
         "enclosure",
         "simplified disk",
         "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[] = {
@@ -960,11 +1435,11 @@ 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",
@@ -973,32 +1448,34 @@ static const char * transport_proto_arr[] = {
 };
 
 /* 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 err, iec_err, len, req_len, avail_len, n, scsi_version;
     int is_tape = 0;
     int peri_dt = 0;
-    int returnval=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;
         }
     }
@@ -1009,70 +1486,165 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
         *peripheral_type = peri_dt;
 
     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) {    /* only do this for disks */
+        unsigned int lb_size = 0;
+        unsigned char lb_prov_resp[8];
+        char cap_str[64];
+        char si_str[64];
+        char lb_str[16];
+        int lb_per_pb_exp = 0;
+        uint64_t capacity = scsiGetSize(device, &lb_size, &lb_per_pb_exp);
+
+        if (capacity) {
+            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);
+                    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);
+            }
+        }
+        if (0 == scsiInquiryVpd(device, SCSI_VPD_LOGICAL_BLOCK_PROVISIONING,
+                                lb_prov_resp, sizeof(lb_prov_resp))) {
+            int prov_type = lb_prov_resp[6] & 0x7;
+
+            if (-1 == lbprz)
+                lbprz = !! (lb_prov_resp[5] & 0x4);
+            switch (prov_type) {
+            case 0:
+                pout("LB provisioning type: unreported, LBPME=%d, LBPRZ=%d\n",
+                     lbpme, 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)
+            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);
         }
-        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;
+        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 ... */
@@ -1080,44 +1652,64 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
         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))
@@ -1125,75 +1717,75 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     // 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);
+            print_off();
          } else if (SIMPLE_ERR_NO_MEDIUM == err) {
-            PRINT_ON(con);
+            print_on();
             pout("NO MEDIUM present on device\n");
-            PRINT_OFF(con);
+            print_off();
          } else if (SIMPLE_ERR_BECOMING_READY == err) {
-            PRINT_ON(con);
+            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();
         }
         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
@@ -1205,31 +1797,32 @@ static int scsiSmartEnable(int device)
          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
@@ -1242,14 +1835,15 @@ static int scsiSmartDisable(int device)
     return 0;
 }
 
-static void scsiPrintTemp(int device)
+static void
+scsiPrintTemp(scsi_device * device)
 {
     UINT8 temp = 0;
     UINT8 trip = 0;
 
     if (scsiGetTemp(device, &temp, &trip))
         return;
-  
+
     if (temp) {
         if (255 != temp)
             pout("Current Drive Temperature:     %d C\n", temp);
@@ -1258,162 +1852,301 @@ static void scsiPrintTemp(int device)
     }
     if (trip)
         pout("Drive Trip Temperature:        %d C\n", trip);
+    if (temp || 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 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(fd, &peripheral_type, con->driveinfo);
+    res = scsiGetDriveInfo(device, &peripheral_type, options.drive_info);
     if (res) {
         if (2 == res)
             return 0;
         else
             failuretest(MANDATORY_CMD, returnval |= FAILID);
+        any_output = true;
     }
 
-    if (con->smartenable) {
-        if (scsiSmartEnable(fd))
+  // Print read look-ahead status for disks
+  short int wce = -1, rcd = -1;
+  if (options.get_rcd || options.get_wce) {
+    if (SCSI_PT_DIRECT_ACCESS == peripheral_type)
+       res = scsiGetSetCache(device, modese_len, &wce, &rcd);
+    else
+       res = -1; // fetch for disks only
+    any_output = true;
+  }
+
+  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");
+   }
+   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)) {
+
+    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 set).\n");
+      }
+      any_output = true;
+    }
+
+    // Enable/Disable write cache
+    if (options.set_wce && SCSI_PT_DIRECT_ACCESS == peripheral_type) {
+      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 && SCSI_PT_DIRECT_ACCESS == peripheral_type) {
+      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 (con->smartautosavedisable) {
-      if (scsiSetControlGLTSD(fd, 1, modese_len)) {
+
+    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 cleared).\n");
+      }
+      any_output = true;
     }
-    
-    if (con->checksmart) {
-        scsiGetSupportedLogPages(fd);
+  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_vendor_attrib
+           || 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 (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 (options.smart_ss_media_log) {
+        if (! checkedSupportedLogPages)
+            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(fd);
+            scsiGetSupportedLogPages(device);
         if (gTempLPage) {
-            if (con->checksmart)
-                pout("\n");
-            scsiPrintTemp(fd);         
+            scsiPrintTemp(device);
         }
         if (gStartStopLPage)
-            scsiGetStartStopData(fd);
+            scsiGetStartStopData(device);
         if (SCSI_PT_DIRECT_ACCESS == peripheral_type) {
-            scsiPrintGrownDefectListLen(fd);
+            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) {
         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\n"
+           "Use 'smartctl -a' (or '-x') to print SMART (and more) information\n\n");
+
     return returnval;
 }