smartmontools SVN Rev 3317
Utility to control and monitor storage systems with "S.M.A.R.T."
scsiprint.cpp
Go to the documentation of this file.
00001 /*
00002  * scsiprint.cpp
00003  *
00004  * Home page of code is: http://smartmontools.sourceforge.net
00005  *
00006  * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
00007  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
00008  *
00009  * Additional SCSI work:
00010  * Copyright (C) 2003-13 Douglas Gilbert <dgilbert@interlog.com>
00011  *
00012  * This program is free software; you can redistribute it and/or modify
00013  * it under the terms of the GNU General Public License as published by
00014  * the Free Software Foundation; either version 2, or (at your option)
00015  * any later version.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * (for example COPYING); if not, write to the Free Software Foundation,
00019  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00020  *
00021  * This code was originally developed as a Senior Thesis by Michael Cornwell
00022  * at the Concurrent Systems Laboratory (now part of the Storage Systems
00023  * Research Center), Jack Baskin School of Engineering, University of
00024  * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
00025  *
00026  */
00027 
00028 
00029 #include <stdio.h>
00030 #include <string.h>
00031 #include <fcntl.h>
00032 #include <errno.h>
00033 
00034 #include "config.h"
00035 #include "int64.h"
00036 #include "scsicmds.h"
00037 #include "atacmds.h" // smart_command_set
00038 #include "dev_interface.h"
00039 #include "scsiprint.h"
00040 #include "smartctl.h"
00041 #include "utility.h"
00042 
00043 #define GBUF_SIZE 65535
00044 
00045 const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 3851 2013-08-17 20:10:11Z chrfranke $"
00046                                  SCSIPRINT_H_CVSID;
00047 
00048 
00049 UINT8 gBuf[GBUF_SIZE];
00050 #define LOG_RESP_LEN 252
00051 #define LOG_RESP_LONG_LEN ((62 * 256) + 252)
00052 #define LOG_RESP_TAPE_ALERT_LEN 0x144
00053 
00054 /* Log pages supported */
00055 static int gSmartLPage = 0;     /* Informational Exceptions log page */
00056 static int gTempLPage = 0;
00057 static int gSelfTestLPage = 0;
00058 static int gStartStopLPage = 0;
00059 static int gReadECounterLPage = 0;
00060 static int gWriteECounterLPage = 0;
00061 static int gVerifyECounterLPage = 0;
00062 static int gNonMediumELPage = 0;
00063 static int gLastNErrorLPage = 0;
00064 static int gBackgroundResultsLPage = 0;
00065 static int gProtocolSpecificLPage = 0;
00066 static int gTapeAlertsLPage = 0;
00067 static int gSSMediaLPage = 0;
00068 
00069 /* Vendor specific log pages */
00070 static int gSeagateCacheLPage = 0;
00071 static int gSeagateFactoryLPage = 0;
00072 
00073 /* Mode pages supported */
00074 static int gIecMPage = 1;     /* N.B. assume it until we know otherwise */
00075 
00076 /* Remember last successful mode sense/select command */
00077 static int modese_len = 0;
00078 
00079 
00080 static void
00081 scsiGetSupportedLogPages(scsi_device * device)
00082 {
00083     int i, err;
00084 
00085     if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf,
00086                             LOG_RESP_LEN, 0))) {
00087         if (scsi_debugmode > 0)
00088             pout("Log Sense for supported pages failed [%s]\n",
00089                  scsiErrString(err));
00090         return;
00091     }
00092 
00093     for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) {
00094         switch (gBuf[i])
00095         {
00096             case READ_ERROR_COUNTER_LPAGE:
00097                 gReadECounterLPage = 1;
00098                 break;
00099             case WRITE_ERROR_COUNTER_LPAGE:
00100                 gWriteECounterLPage = 1;
00101                 break;
00102             case VERIFY_ERROR_COUNTER_LPAGE:
00103                 gVerifyECounterLPage = 1;
00104                 break;
00105             case LAST_N_ERROR_LPAGE:
00106                 gLastNErrorLPage = 1;
00107                 break;
00108             case NON_MEDIUM_ERROR_LPAGE:
00109                 gNonMediumELPage = 1;
00110                 break;
00111             case TEMPERATURE_LPAGE:
00112                 gTempLPage = 1;
00113                 break;
00114             case STARTSTOP_CYCLE_COUNTER_LPAGE:
00115                 gStartStopLPage = 1;
00116                 break;
00117             case SELFTEST_RESULTS_LPAGE:
00118                 gSelfTestLPage = 1;
00119                 break;
00120             case IE_LPAGE:
00121                 gSmartLPage = 1;
00122                 break;
00123             case BACKGROUND_RESULTS_LPAGE:
00124                 gBackgroundResultsLPage = 1;
00125                 break;
00126             case PROTOCOL_SPECIFIC_LPAGE:
00127                 gProtocolSpecificLPage = 1;
00128                 break;
00129             case TAPE_ALERTS_LPAGE:
00130                 gTapeAlertsLPage = 1;
00131                 break;
00132             case SS_MEDIA_LPAGE:
00133                 gSSMediaLPage = 1;
00134                 break;
00135             case SEAGATE_CACHE_LPAGE:
00136                 gSeagateCacheLPage = 1;
00137                 break;
00138             case SEAGATE_FACTORY_LPAGE:
00139                 gSeagateFactoryLPage = 1;
00140                 break;
00141             default:
00142                 break;
00143         }
00144     }
00145 }
00146 
00147 /* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
00148    (or at least something to report). */
00149 static int
00150 scsiGetSmartData(scsi_device * device, bool attribs)
00151 {
00152     UINT8 asc;
00153     UINT8 ascq;
00154     UINT8 currenttemp = 0;
00155     UINT8 triptemp = 0;
00156     const char * cp;
00157     int err = 0;
00158     print_on();
00159     if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq,
00160                     &currenttemp, &triptemp)) {
00161         /* error message already announced */
00162         print_off();
00163         return -1;
00164     }
00165     print_off();
00166     cp = scsiGetIEString(asc, ascq);
00167     if (cp) {
00168         err = -2;
00169         print_on();
00170         pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq);
00171         print_off();
00172     } else if (gIecMPage)
00173         pout("SMART Health Status: OK\n");
00174 
00175     if (attribs && !gTempLPage) {
00176         if (currenttemp) {
00177             if (255 != currenttemp)
00178                 pout("Current Drive Temperature:     %d C\n", currenttemp);
00179             else
00180                 pout("Current Drive Temperature:     <not available>\n");
00181         }
00182         if (triptemp)
00183             pout("Drive Trip Temperature:        %d C\n", triptemp);
00184     }
00185     pout("\n");
00186     return err;
00187 }
00188 
00189 
00190 // Returns number of logged errors or zero if none or -1 if fetching
00191 // TapeAlerts fails
00192 static const char * const severities = "CWI";
00193 
00194 static int
00195 scsiGetTapeAlertsData(scsi_device * device, int peripheral_type)
00196 {
00197     unsigned short pagelength;
00198     unsigned short parametercode;
00199     int i, err;
00200     const char *s;
00201     const char *ts;
00202     int failures = 0;
00203 
00204     print_on();
00205     if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, 0, gBuf,
00206                         LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) {
00207         pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err));
00208         print_off();
00209         return -1;
00210     }
00211     if (gBuf[0] != 0x2e) {
00212         pout("TapeAlerts Log Sense Failed\n");
00213         print_off();
00214         return -1;
00215     }
00216     pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3];
00217 
00218     for (s=severities; *s; s++) {
00219         for (i = 4; i < pagelength; i += 5) {
00220             parametercode = (unsigned short) gBuf[i] << 8 | gBuf[i+1];
00221 
00222             if (gBuf[i + 4]) {
00223                 ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ?
00224                     scsiTapeAlertsChangerDevice(parametercode) :
00225                     scsiTapeAlertsTapeDevice(parametercode);
00226                 if (*ts == *s) {
00227                     if (!failures)
00228                         pout("TapeAlert Errors (C=Critical, W=Warning, "
00229                              "I=Informational):\n");
00230                     pout("[0x%02x] %s\n", parametercode, ts);
00231                     failures += 1;
00232                 }
00233             }
00234         }
00235     }
00236     print_off();
00237 
00238     if (! failures)
00239         pout("TapeAlert: OK\n");
00240 
00241     return failures;
00242 }
00243 
00244 static void
00245 scsiGetStartStopData(scsi_device * device)
00246 {
00247     UINT32 u;
00248     int err, len, k, extra, pc;
00249     unsigned char * ucp;
00250 
00251     if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf,
00252                             LOG_RESP_LEN, 0))) {
00253         print_on();
00254         pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err));
00255         print_off();
00256         return;
00257     }
00258     if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) {
00259         print_on();
00260         pout("StartStop Log Sense Failed, page mismatch\n");
00261         print_off();
00262         return;
00263     }
00264     len = ((gBuf[2] << 8) | gBuf[3]);
00265     ucp = gBuf + 4;
00266     for (k = len; k > 0; k -= extra, ucp += extra) {
00267         if (k < 3) {
00268             print_on();
00269             pout("StartStop Log Sense Failed: short\n");
00270             print_off();
00271             return;
00272         }
00273         extra = ucp[3] + 4;
00274         pc = (ucp[0] << 8) + ucp[1];
00275         switch (pc) {
00276         case 1:
00277             if (10 == extra)
00278                 pout("Manufactured in week %.2s of year %.4s\n", ucp + 8,
00279                      ucp + 4);
00280             break;
00281         case 2:
00282             /* ignore Accounting date */
00283             break;
00284         case 3:
00285             if (extra > 7) {
00286                 u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
00287                 if (0xffffffff != u)
00288                     pout("Specified cycle count over device lifetime:  %u\n",
00289                          u);
00290             }
00291             break;
00292         case 4:
00293             if (extra > 7) {
00294                 u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
00295                 if (0xffffffff != u)
00296                     pout("Accumulated start-stop cycles:  %u\n", u);
00297             }
00298             break;
00299         case 5:
00300             if (extra > 7) {
00301                 u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
00302                 if (0xffffffff != u)
00303                     pout("Specified load-unload count over device "
00304                          "lifetime:  %u\n", u);
00305             }
00306             break;
00307         case 6:
00308             if (extra > 7) {
00309                 u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
00310                 if (0xffffffff != u)
00311                     pout("Accumulated load-unload cycles:  %u\n", u);
00312             }
00313             break;
00314         default:
00315             /* ignore */
00316             break;
00317         }
00318     }
00319 }
00320 
00321 static void
00322 scsiPrintGrownDefectListLen(scsi_device * device)
00323 {
00324     int err, dl_format, got_rd12, generation;
00325     unsigned int dl_len, div;
00326 
00327     memset(gBuf, 0, 8);
00328     if ((err = scsiReadDefect12(device, 0 /* req_plist */, 1 /* req_glist */,
00329                                 4 /* format: bytes from index */,
00330                                 0 /* addr desc index */, gBuf, 8))) {
00331         if (2 == err) { /* command not supported */
00332             if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */,
00333                                         4 /* format: bytes from index */, gBuf, 4))) {
00334                 if (scsi_debugmode > 0) {
00335                     print_on();
00336                     pout("Read defect list (10) Failed: %s\n", scsiErrString(err));
00337                     print_off();
00338                 }
00339                 return;
00340             } else
00341                 got_rd12 = 0;
00342         } else {
00343             if (scsi_debugmode > 0) {
00344                 print_on();
00345                 pout("Read defect list (12) Failed: %s\n", scsiErrString(err));
00346                 print_off();
00347             }
00348             return;
00349         }
00350     } else
00351         got_rd12 = 1;
00352 
00353     if (got_rd12) {
00354         generation = (gBuf[2] << 8) + gBuf[3];
00355         if ((generation > 1) && (scsi_debugmode > 0)) {
00356             print_on();
00357             pout("Read defect list (12): generation=%d\n", generation);
00358             print_off();
00359         }
00360         dl_len = (gBuf[4] << 24) + (gBuf[5] << 16) + (gBuf[6] << 8) + gBuf[7];
00361     } else {
00362         dl_len = (gBuf[2] << 8) + gBuf[3];
00363     }
00364     if (0x8 != (gBuf[1] & 0x18)) {
00365         print_on();
00366         pout("Read defect list: asked for grown list but didn't get it\n");
00367         print_off();
00368         return;
00369     }
00370     div = 0;
00371     dl_format = (gBuf[1] & 0x7);
00372     switch (dl_format) {
00373         case 0:     /* short block */
00374             div = 4;
00375             break;
00376         case 1:     /* extended bytes from index */
00377         case 2:     /* extended physical sector */
00378             /* extended = 1; # might use in future */
00379             div = 8;
00380             break;
00381         case 3:     /* long block */
00382         case 4:     /* bytes from index */
00383         case 5:     /* physical sector */
00384             div = 8;
00385             break;
00386         default:
00387             print_on();
00388             pout("defect list format %d unknown\n", dl_format);
00389             print_off();
00390             break;
00391     }
00392     if (0 == dl_len)
00393         pout("Elements in grown defect list: 0\n\n");
00394     else {
00395         if (0 == div)
00396             pout("Grown defect list length=%u bytes [unknown "
00397                  "number of elements]\n\n", dl_len);
00398         else
00399             pout("Elements in grown defect list: %u\n\n", dl_len / div);
00400     }
00401 }
00402 
00403 static void
00404 scsiPrintSeagateCacheLPage(scsi_device * device)
00405 {
00406     int k, j, num, pl, pc, err, len;
00407     unsigned char * ucp;
00408     unsigned char * xp;
00409     uint64_t ull;
00410 
00411     if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf,
00412                             LOG_RESP_LEN, 0))) {
00413         print_on();
00414         pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err));
00415         print_off();
00416         return;
00417     }
00418     if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) {
00419         print_on();
00420         pout("Seagate Cache Log Sense Failed, page mismatch\n");
00421         print_off();
00422         return;
00423     }
00424     len = ((gBuf[2] << 8) | gBuf[3]) + 4;
00425     num = len - 4;
00426     ucp = &gBuf[0] + 4;
00427     while (num > 3) {
00428         pc = (ucp[0] << 8) | ucp[1];
00429         pl = ucp[3] + 4;
00430         switch (pc) {
00431         case 0: case 1: case 2: case 3: case 4:
00432             break;
00433         default:
00434             if (scsi_debugmode > 0) {
00435                 print_on();
00436                 pout("Vendor (Seagate) cache lpage has unexpected parameter"
00437                      ", skip\n");
00438                 print_off();
00439             }
00440             return;
00441         }
00442         num -= pl;
00443         ucp += pl;
00444     }
00445     pout("Vendor (Seagate) cache information\n");
00446     num = len - 4;
00447     ucp = &gBuf[0] + 4;
00448     while (num > 3) {
00449         pc = (ucp[0] << 8) | ucp[1];
00450         pl = ucp[3] + 4;
00451         switch (pc) {
00452         case 0: pout("  Blocks sent to initiator"); break;
00453         case 1: pout("  Blocks received from initiator"); break;
00454         case 2: pout("  Blocks read from cache and sent to initiator"); break;
00455         case 3: pout("  Number of read and write commands whose size "
00456                        "<= segment size"); break;
00457         case 4: pout("  Number of read and write commands whose size "
00458                        "> segment size"); break;
00459         default: pout("  Unknown Seagate parameter code [0x%x]", pc); break;
00460         }
00461         k = pl - 4;
00462         xp = ucp + 4;
00463         if (k > (int)sizeof(ull)) {
00464             xp += (k - (int)sizeof(ull));
00465             k = (int)sizeof(ull);
00466         }
00467         ull = 0;
00468         for (j = 0; j < k; ++j) {
00469             if (j > 0)
00470                 ull <<= 8;
00471             ull |= xp[j];
00472         }
00473         pout(" = %" PRIu64 "\n", ull);
00474         num -= pl;
00475         ucp += pl;
00476     }
00477     pout("\n");
00478 }
00479 
00480 static void
00481 scsiPrintSeagateFactoryLPage(scsi_device * device)
00482 {
00483     int k, j, num, pl, pc, len, err, good, bad;
00484     unsigned char * ucp;
00485     unsigned char * xp;
00486     uint64_t ull;
00487 
00488     if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf,
00489                             LOG_RESP_LEN, 0))) {
00490         print_on();
00491         pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err));
00492         print_off();
00493         return;
00494     }
00495     if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) {
00496         print_on();
00497         pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n");
00498         print_off();
00499         return;
00500     }
00501     len = ((gBuf[2] << 8) | gBuf[3]) + 4;
00502     num = len - 4;
00503     ucp = &gBuf[0] + 4;
00504     good = 0;
00505     bad = 0;
00506     while (num > 3) {
00507         pc = (ucp[0] << 8) | ucp[1];
00508         pl = ucp[3] + 4;
00509         switch (pc) {
00510         case 0: case 8:
00511             ++good;
00512             break;
00513         default:
00514             ++bad;
00515             break;
00516         }
00517         num -= pl;
00518         ucp += pl;
00519     }
00520     if ((good < 2) || (bad > 4)) {  /* heuristic */
00521         if (scsi_debugmode > 0) {
00522             print_on();
00523             pout("\nVendor (Seagate/Hitachi) factory lpage has too many "
00524                  "unexpected parameters, skip\n");
00525             print_off();
00526         }
00527         return;
00528     }
00529     pout("Vendor (Seagate/Hitachi) factory information\n");
00530     num = len - 4;
00531     ucp = &gBuf[0] + 4;
00532     while (num > 3) {
00533         pc = (ucp[0] << 8) | ucp[1];
00534         pl = ucp[3] + 4;
00535         good = 0;
00536         switch (pc) {
00537         case 0: pout("  number of hours powered up");
00538             good = 1;
00539             break;
00540         case 8: pout("  number of minutes until next internal SMART test");
00541             good = 1;
00542             break;
00543         default:
00544             if (scsi_debugmode > 0) {
00545                 print_on();
00546                 pout("Vendor (Seagate/Hitachi) factory lpage: "
00547                      "unknown parameter code [0x%x]\n", pc);
00548                 print_off();
00549             }
00550             break;
00551         }
00552         if (good) {
00553             k = pl - 4;
00554             xp = ucp + 4;
00555             if (k > (int)sizeof(ull)) {
00556                 xp += (k - (int)sizeof(ull));
00557                 k = (int)sizeof(ull);
00558             }
00559             ull = 0;
00560             for (j = 0; j < k; ++j) {
00561                 if (j > 0)
00562                     ull <<= 8;
00563                 ull |= xp[j];
00564             }
00565             if (0 == pc)
00566                 pout(" = %.2f\n", ull / 60.0 );
00567             else
00568                 pout(" = %" PRIu64 "\n", ull);
00569         }
00570         num -= pl;
00571         ucp += pl;
00572     }
00573     pout("\n");
00574 }
00575 
00576 static void
00577 scsiPrintErrorCounterLog(scsi_device * device)
00578 {
00579     struct scsiErrorCounter errCounterArr[3];
00580     struct scsiErrorCounter * ecp;
00581     struct scsiNonMediumError nme;
00582     int found[3] = {0, 0, 0};
00583     const char * pageNames[3] = {"read:   ", "write:  ", "verify: "};
00584     double processed_gb;
00585 
00586     if (gReadECounterLPage && (0 == scsiLogSense(device,
00587                 READ_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
00588         scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]);
00589         found[0] = 1;
00590     }
00591     if (gWriteECounterLPage && (0 == scsiLogSense(device,
00592                 WRITE_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
00593         scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]);
00594         found[1] = 1;
00595     }
00596     if (gVerifyECounterLPage && (0 == scsiLogSense(device,
00597                 VERIFY_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
00598         scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]);
00599         ecp = &errCounterArr[2];
00600         for (int k = 0; k < 7; ++k) {
00601             if (ecp->gotPC[k] && ecp->counter[k]) {
00602                 found[2] = 1;
00603                 break;
00604             }
00605         }
00606     }
00607     if (found[0] || found[1] || found[2]) {
00608         pout("Error counter log:\n");
00609         pout("           Errors Corrected by           Total   "
00610              "Correction     Gigabytes    Total\n");
00611         pout("               ECC          rereads/    errors   "
00612              "algorithm      processed    uncorrected\n");
00613         pout("           fast | delayed   rewrites  corrected  "
00614              "invocations   [10^9 bytes]  errors\n");
00615         for (int k = 0; k < 3; ++k) {
00616             if (! found[k])
00617                 continue;
00618             ecp = &errCounterArr[k];
00619             pout("%s%8" PRIu64 " %8" PRIu64 "  %8" PRIu64 "  %8" PRIu64 "   %8" PRIu64,
00620                  pageNames[k], ecp->counter[0], ecp->counter[1],
00621                  ecp->counter[2], ecp->counter[3], ecp->counter[4]);
00622             processed_gb = ecp->counter[5] / 1000000000.0;
00623             pout("   %12.3f    %8" PRIu64 "\n", processed_gb, ecp->counter[6]);
00624         }
00625     }
00626     else
00627         pout("Error Counter logging not supported\n");
00628     if (gNonMediumELPage && (0 == scsiLogSense(device,
00629                 NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
00630         scsiDecodeNonMediumErrPage(gBuf, &nme);
00631         if (nme.gotPC0)
00632             pout("\nNon-medium error count: %8" PRIu64 "\n", nme.counterPC0);
00633         if (nme.gotTFE_H)
00634             pout("Track following error count [Hitachi]: %8" PRIu64 "\n",
00635                  nme.counterTFE_H);
00636         if (nme.gotPE_H)
00637             pout("Positioning error count [Hitachi]: %8" PRIu64 "\n",
00638                  nme.counterPE_H);
00639     }
00640     if (gLastNErrorLPage && (0 == scsiLogSense(device,
00641                 LAST_N_ERROR_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) {
00642         int num = (gBuf[2] << 8) + gBuf[3] + 4;
00643         int truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
00644         if (truncated)
00645             num = LOG_RESP_LONG_LEN;
00646         unsigned char * ucp = gBuf + 4;
00647         num -= 4;
00648         if (num < 4)
00649             pout("\nNo error events logged\n");
00650         else {
00651             pout("\nLast n error events log page\n");
00652             for (int k = num, pl; k > 0; k -= pl, ucp += pl) {
00653                 if (k < 3) {
00654                     pout("  <<short Last n error events log page>>\n");
00655                     break;
00656                 }
00657                 pl = ucp[3] + 4;
00658                 int pc = (ucp[0] << 8) + ucp[1];
00659                 if (pl > 4) {
00660                     if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) {
00661                         pout("  Error event %d:\n", pc);
00662                         pout("    [binary]:\n");
00663                         dStrHex((const char *)ucp + 4, pl - 4, 1);
00664                     } else if (ucp[2] & 0x1) {
00665                         pout("  Error event %d:\n", pc);
00666                         pout("    %.*s\n", pl - 4, (const char *)(ucp + 4));
00667                     } else {
00668                         if (scsi_debugmode > 0) {
00669                             pout("  Error event %d:\n", pc);
00670                             pout("    [data counter??]:\n");
00671                             dStrHex((const char *)ucp + 4, pl - 4, 1);
00672                         }
00673                     }
00674                 }
00675             }
00676             if (truncated)
00677                 pout(" >>>> log truncated, fetched %d of %d available "
00678                      "bytes\n", LOG_RESP_LONG_LEN, truncated);
00679         }
00680     }
00681     pout("\n");
00682 }
00683 
00684 static const char * self_test_code[] = {
00685         "Default         ",
00686         "Background short",
00687         "Background long ",
00688         "Reserved(3)     ",
00689         "Abort background",
00690         "Foreground short",
00691         "Foreground long ",
00692         "Reserved(7)     "
00693 };
00694 
00695 static const char * self_test_result[] = {
00696         "Completed                ",
00697         "Aborted (by user command)",
00698         "Aborted (device reset ?) ",
00699         "Unknown error, incomplete",
00700         "Completed, segment failed",
00701         "Failed in first segment  ",
00702         "Failed in second segment ",
00703         "Failed in segment -->    ",
00704         "Reserved(8)              ",
00705         "Reserved(9)              ",
00706         "Reserved(10)             ",
00707         "Reserved(11)             ",
00708         "Reserved(12)             ",
00709         "Reserved(13)             ",
00710         "Reserved(14)             ",
00711         "Self test in progress ..."
00712 };
00713 
00714 // See SCSI Primary Commands - 3 (SPC-3) rev 23 (draft) section 7.2.10 .
00715 // Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent
00716 // 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
00717 // FAILSMART is returned.
00718 static int
00719 scsiPrintSelfTest(scsi_device * device)
00720 {
00721     int num, k, n, res, err, durationSec;
00722     int noheader = 1;
00723     int retval = 0;
00724     UINT8 * ucp;
00725     uint64_t ull=0;
00726     struct scsi_sense_disect sense_info;
00727 
00728     // check if test is running
00729     if (!scsiRequestSense(device, &sense_info) &&
00730                         (sense_info.asc == 0x04 && sense_info.ascq == 0x09 &&
00731                         sense_info.progress != -1)) {
00732         pout("Self-test execution status:\t\t%d%% of test remaining\n",
00733              100 - ((sense_info.progress * 100) / 65535));
00734     }
00735 
00736     if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf,
00737                             LOG_RESP_SELF_TEST_LEN, 0))) {
00738         print_on();
00739         pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err));
00740         print_off();
00741         return FAILSMART;
00742     }
00743     if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
00744         print_on();
00745         pout("Self-test Log Sense Failed, page mismatch\n");
00746         print_off();
00747         return FAILSMART;
00748     }
00749     // compute page length
00750     num = (gBuf[2] << 8) + gBuf[3];
00751     // Log sense page length 0x190 bytes
00752     if (num != 0x190) {
00753         print_on();
00754         pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num);
00755         print_off();
00756         return FAILSMART;
00757     }
00758     // loop through the twenty possible entries
00759     for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) {
00760         int i;
00761 
00762         // timestamp in power-on hours (or zero if test in progress)
00763         n = (ucp[6] << 8) | ucp[7];
00764 
00765         // The spec says "all 20 bytes will be zero if no test" but
00766         // DG has found otherwise.  So this is a heuristic.
00767         if ((0 == n) && (0 == ucp[4]))
00768             break;
00769 
00770         // only print header if needed
00771         if (noheader) {
00772             pout("SMART Self-test log\n");
00773             pout("Num  Test              Status                 segment  "
00774                    "LifeTime  LBA_first_err [SK ASC ASQ]\n");
00775             pout("     Description                              number   "
00776                    "(hours)\n");
00777             noheader=0;
00778         }
00779 
00780         // print parameter code (test number) & self-test code text
00781         pout("#%2d  %s", (ucp[0] << 8) | ucp[1],
00782             self_test_code[(ucp[4] >> 5) & 0x7]);
00783 
00784         // check the self-test result nibble, using the self-test results
00785         // field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10:
00786         switch ((res = ucp[4] & 0xf)) {
00787         case 0x3:
00788             // an unknown error occurred while the device server
00789             // was processing the self-test and the device server
00790             // was unable to complete the self-test
00791             retval|=FAILSMART;
00792             break;
00793         case 0x4:
00794             // the self-test completed with a failure in a test
00795             // segment, and the test segment that failed is not
00796             // known
00797             retval|=FAILLOG;
00798             break;
00799         case 0x5:
00800             // the first segment of the self-test failed
00801             retval|=FAILLOG;
00802             break;
00803         case 0x6:
00804             // the second segment of the self-test failed
00805             retval|=FAILLOG;
00806             break;
00807         case 0x7:
00808             // another segment of the self-test failed and which
00809             // test is indicated by the contents of the SELF-TEST
00810             // NUMBER field
00811             retval|=FAILLOG;
00812             break;
00813         default:
00814             break;
00815         }
00816         pout("  %s", self_test_result[res]);
00817 
00818         // self-test number identifies test that failed and consists
00819         // of either the number of the segment that failed during
00820         // the test, or the number of the test that failed and the
00821         // number of the segment in which the test was run, using a
00822         // vendor-specific method of putting both numbers into a
00823         // single byte.
00824         if (ucp[5])
00825             pout(" %3d",  (int)ucp[5]);
00826         else
00827             pout("   -");
00828 
00829         // print time that the self-test was completed
00830         if (n==0 && res==0xf)
00831         // self-test in progress
00832             pout("     NOW");
00833         else
00834             pout("   %5d", n);
00835 
00836         // construct 8-byte integer address of first failure
00837         for (i = 0; i < 8; i++) {
00838             ull <<= 8;
00839             ull |= ucp[i+8];
00840         }
00841         // print Address of First Failure, if sensible
00842         if ((~(uint64_t)0 != ull) && (res > 0) && (res < 0xf)) {
00843             char buff[32];
00844 
00845             // was hex but change to decimal to conform with ATA
00846             snprintf(buff, sizeof(buff), "%" PRIu64, ull);
00847             // snprintf(buff, sizeof(buff), "0x%" PRIx64, ull);
00848             pout("%18s", buff);
00849         } else
00850             pout("                 -");
00851 
00852         // if sense key nonzero, then print it, along with
00853         // additional sense code and additional sense code qualifier
00854         if (ucp[16] & 0xf)
00855             pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]);
00856         else
00857             pout(" [-   -    -]\n");
00858     }
00859 
00860     // if header never printed, then there was no output
00861     if (noheader)
00862         pout("No self-tests have been logged\n");
00863     else
00864     if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
00865                         modese_len)) && (durationSec > 0)) {
00866         pout("Long (extended) Self Test duration: %d seconds "
00867              "[%.1f minutes]\n", durationSec, durationSec / 60.0);
00868     }
00869     pout("\n");
00870     return retval;
00871 }
00872 
00873 static const char * bms_status[] = {
00874     "no scans active",
00875     "scan is active",
00876     "pre-scan is active",
00877     "halted due to fatal error",
00878     "halted due to a vendor specific pattern of error",
00879     "halted due to medium formatted without P-List",
00880     "halted - vendor specific cause",
00881     "halted due to temperature out of range",
00882     "waiting until BMS interval timer expires", /* 8 */
00883 };
00884 
00885 static const char * reassign_status[] = {
00886     "Reserved [0x0]",
00887     "Require Write or Reassign Blocks command",
00888     "Successfully reassigned",
00889     "Reserved [0x3]",
00890     "Reassignment by disk failed",
00891     "Recovered via rewrite in-place",
00892     "Reassigned by app, has valid data",
00893     "Reassigned by app, has no valid data",
00894     "Unsuccessfully reassigned by app", /* 8 */
00895 };
00896 
00897 // See SCSI Block Commands - 3 (SBC-3) rev 6 (draft) section 6.2.2 .
00898 // Returns 0 if ok else FAIL* bitmask. Note can have a status entry
00899 // and up to 2048 events (although would hope to have less). May set
00900 // FAILLOG if serious errors detected (in the future).
00901 static int
00902 scsiPrintBackgroundResults(scsi_device * device)
00903 {
00904     int num, j, m, err, pc, pl, truncated;
00905     int noheader = 1;
00906     int firstresult = 1;
00907     int retval = 0;
00908     UINT8 * ucp;
00909 
00910     if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf,
00911                             LOG_RESP_LONG_LEN, 0))) {
00912         print_on();
00913         pout("scsiPrintBackgroundResults Failed [%s]\n", scsiErrString(err));
00914         print_off();
00915         return FAILSMART;
00916     }
00917     if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) {
00918         print_on();
00919         pout("Background scan results Log Sense Failed, page mismatch\n");
00920         print_off();
00921         return FAILSMART;
00922     }
00923     // compute page length
00924     num = (gBuf[2] << 8) + gBuf[3] + 4;
00925     if (num < 20) {
00926         print_on();
00927         pout("Background scan results Log Sense length is %d, no scan "
00928              "status\n", num);
00929         print_off();
00930         return FAILSMART;
00931     }
00932     truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
00933     if (truncated)
00934         num = LOG_RESP_LONG_LEN;
00935     ucp = gBuf + 4;
00936     num -= 4;
00937     while (num > 3) {
00938         pc = (ucp[0] << 8) | ucp[1];
00939         // pcb = ucp[2];
00940         pl = ucp[3] + 4;
00941         switch (pc) {
00942         case 0:
00943             if (noheader) {
00944                 noheader = 0;
00945                 pout("Background scan results log\n");
00946             }
00947             pout("  Status: ");
00948             if ((pl < 16) || (num < 16)) {
00949                 pout("\n");
00950                 break;
00951             }
00952             j = ucp[9];
00953             if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0])))
00954                 pout("%s\n", bms_status[j]);
00955             else
00956                 pout("unknown [0x%x] background scan status value\n", j);
00957             j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
00958             pout("    Accumulated power on time, hours:minutes %d:%02d "
00959                  "[%d minutes]\n", (j / 60), (j % 60), j);
00960             pout("    Number of background scans performed: %d,  ",
00961                  (ucp[10] << 8) + ucp[11]);
00962             pout("scan progress: %.2f%%\n",
00963                  (double)((ucp[12] << 8) + ucp[13]) * 100.0 / 65536.0);
00964             pout("    Number of background medium scans performed: %d\n",
00965                  (ucp[14] << 8) + ucp[15]);
00966             break;
00967         default:
00968             if (noheader) {
00969                 noheader = 0;
00970                 pout("\nBackground scan results log\n");
00971             }
00972             if (firstresult) {
00973                 firstresult = 0;
00974                 pout("\n   #  when        lba(hex)    [sk,asc,ascq]    "
00975                      "reassign_status\n");
00976             }
00977             pout(" %3d ", pc);
00978             if ((pl < 24) || (num < 24)) {
00979                 if (pl < 24)
00980                     pout("parameter length >= 24 expected, got %d\n", pl);
00981                 break;
00982             }
00983             j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
00984             pout("%4d:%02d  ", (j / 60), (j % 60));
00985             for (m = 0; m < 8; ++m)
00986                 pout("%02x", ucp[16 + m]);
00987             pout("  [%x,%x,%x]   ", ucp[8] & 0xf, ucp[9], ucp[10]);
00988             j = (ucp[8] >> 4) & 0xf;
00989             if (j <
00990                 (int)(sizeof(reassign_status) / sizeof(reassign_status[0])))
00991                 pout("%s\n", reassign_status[j]);
00992             else
00993                 pout("Reassign status: reserved [0x%x]\n", j);
00994             break;
00995         }
00996         num -= pl;
00997         ucp += pl;
00998     }
00999     if (truncated)
01000         pout(" >>>> log truncated, fetched %d of %d available "
01001              "bytes\n", LOG_RESP_LONG_LEN, truncated);
01002     pout("\n");
01003     return retval;
01004 }
01005 
01006 // See SCSI Block Commands - 3 (SBC-3) rev 27 (draft) section 6.3.6 .
01007 // Returns 0 if ok else FAIL* bitmask. Note can have a status entry
01008 // and up to 2048 events (although would hope to have less). May set
01009 // FAILLOG if serious errors detected (in the future).
01010 static int
01011 scsiPrintSSMedia(scsi_device * device)
01012 {
01013     int num, err, pc, pl, truncated;
01014     int retval = 0;
01015     UINT8 * ucp;
01016 
01017     if ((err = scsiLogSense(device, SS_MEDIA_LPAGE, 0, gBuf,
01018                             LOG_RESP_LONG_LEN, 0))) {
01019         print_on();
01020         pout("scsiPrintSSMedia Failed [%s]\n", scsiErrString(err));
01021         print_off();
01022         return FAILSMART;
01023     }
01024     if ((gBuf[0] & 0x3f) != SS_MEDIA_LPAGE) {
01025         print_on();
01026         pout("Solid state media Log Sense Failed, page mismatch\n");
01027         print_off();
01028         return FAILSMART;
01029     }
01030     // compute page length
01031     num = (gBuf[2] << 8) + gBuf[3] + 4;
01032     if (num < 12) {
01033         print_on();
01034         pout("Solid state media Log Sense length is %d, too short\n", num);
01035         print_off();
01036         return FAILSMART;
01037     }
01038     truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
01039     if (truncated)
01040         num = LOG_RESP_LONG_LEN;
01041     ucp = gBuf + 4;
01042     num -= 4;
01043     while (num > 3) {
01044         pc = (ucp[0] << 8) | ucp[1];
01045         // pcb = ucp[2];
01046         pl = ucp[3] + 4;
01047         switch (pc) {
01048         case 1:
01049             if (pl < 8) {
01050                 print_on();
01051                 pout("Percentage used endurance indicator too short (pl=%d)\n", pl);
01052                 print_off();
01053                 return FAILSMART;
01054             }
01055             pout("SS Media used endurance indicator: %d%%\n", ucp[7]);
01056         default:        /* ignore other parameter codes */
01057             break;
01058         }
01059         num -= pl;
01060         ucp += pl;
01061     }
01062     return retval;
01063 }
01064 
01065 static void
01066 show_sas_phy_event_info(int peis, unsigned int val, unsigned thresh_val)
01067 {
01068     unsigned int u;
01069 
01070     switch (peis) {
01071     case 0:
01072         pout("     No event\n");
01073         break;
01074     case 0x1:
01075         pout("     Invalid word count: %u\n", val);
01076         break;
01077     case 0x2:
01078         pout("     Running disparity error count: %u\n", val);
01079         break;
01080     case 0x3:
01081         pout("     Loss of dword synchronization count: %u\n", val);
01082         break;
01083     case 0x4:
01084         pout("     Phy reset problem count: %u\n", val);
01085         break;
01086     case 0x5:
01087         pout("     Elasticity buffer overflow count: %u\n", val);
01088         break;
01089     case 0x6:
01090         pout("     Received ERROR  count: %u\n", val);
01091         break;
01092     case 0x20:
01093         pout("     Received address frame error count: %u\n", val);
01094         break;
01095     case 0x21:
01096         pout("     Transmitted abandon-class OPEN_REJECT count: %u\n", val);
01097         break;
01098     case 0x22:
01099         pout("     Received abandon-class OPEN_REJECT count: %u\n", val);
01100         break;
01101     case 0x23:
01102         pout("     Transmitted retry-class OPEN_REJECT count: %u\n", val);
01103         break;
01104     case 0x24:
01105         pout("     Received retry-class OPEN_REJECT count: %u\n", val);
01106         break;
01107     case 0x25:
01108         pout("     Received AIP (WATING ON PARTIAL) count: %u\n", val);
01109         break;
01110     case 0x26:
01111         pout("     Received AIP (WAITING ON CONNECTION) count: %u\n", val);
01112         break;
01113     case 0x27:
01114         pout("     Transmitted BREAK count: %u\n", val);
01115         break;
01116     case 0x28:
01117         pout("     Received BREAK count: %u\n", val);
01118         break;
01119     case 0x29:
01120         pout("     Break timeout count: %u\n", val);
01121         break;
01122     case 0x2a:
01123         pout("     Connection count: %u\n", val);
01124         break;
01125     case 0x2b:
01126         pout("     Peak transmitted pathway blocked count: %u\n",
01127                val & 0xff);
01128         pout("         Peak value detector threshold: %u\n",
01129                thresh_val & 0xff);
01130         break;
01131     case 0x2c:
01132         u = val & 0xffff;
01133         if (u < 0x8000)
01134             pout("     Peak transmitted arbitration wait time (us): "
01135                    "%u\n", u);
01136         else
01137             pout("     Peak transmitted arbitration wait time (ms): "
01138                    "%u\n", 33 + (u - 0x8000));
01139         u = thresh_val & 0xffff;
01140         if (u < 0x8000)
01141             pout("         Peak value detector threshold (us): %u\n",
01142                    u);
01143         else
01144             pout("         Peak value detector threshold (ms): %u\n",
01145                    33 + (u - 0x8000));
01146         break;
01147     case 0x2d:
01148         pout("     Peak arbitration time (us): %u\n", val);
01149         pout("         Peak value detector threshold: %u\n", thresh_val);
01150         break;
01151     case 0x2e:
01152         pout("     Peak connection time (us): %u\n", val);
01153         pout("         Peak value detector threshold: %u\n", thresh_val);
01154         break;
01155     case 0x40:
01156         pout("     Transmitted SSP frame count: %u\n", val);
01157         break;
01158     case 0x41:
01159         pout("     Received SSP frame count: %u\n", val);
01160         break;
01161     case 0x42:
01162         pout("     Transmitted SSP frame error count: %u\n", val);
01163         break;
01164     case 0x43:
01165         pout("     Received SSP frame error count: %u\n", val);
01166         break;
01167     case 0x44:
01168         pout("     Transmitted CREDIT_BLOCKED count: %u\n", val);
01169         break;
01170     case 0x45:
01171         pout("     Received CREDIT_BLOCKED count: %u\n", val);
01172         break;
01173     case 0x50:
01174         pout("     Transmitted SATA frame count: %u\n", val);
01175         break;
01176     case 0x51:
01177         pout("     Received SATA frame count: %u\n", val);
01178         break;
01179     case 0x52:
01180         pout("     SATA flow control buffer overflow count: %u\n", val);
01181         break;
01182     case 0x60:
01183         pout("     Transmitted SMP frame count: %u\n", val);
01184         break;
01185     case 0x61:
01186         pout("     Received SMP frame count: %u\n", val);
01187         break;
01188     case 0x63:
01189         pout("     Received SMP frame error count: %u\n", val);
01190         break;
01191     default:
01192         break;
01193     }
01194 }
01195 
01196 static void
01197 show_sas_port_param(unsigned char * ucp, int param_len)
01198 {
01199     int j, m, n, nphys, t, sz, spld_len;
01200     unsigned char * vcp;
01201     uint64_t ull;
01202     unsigned int ui;
01203     char s[64];
01204 
01205     sz = sizeof(s);
01206     // pcb = ucp[2];
01207     t = (ucp[0] << 8) | ucp[1];
01208     pout("relative target port id = %d\n", t);
01209     pout("  generation code = %d\n", ucp[6]);
01210     nphys = ucp[7];
01211     pout("  number of phys = %d\n", nphys);
01212 
01213     for (j = 0, vcp = ucp + 8; j < (param_len - 8);
01214          vcp += spld_len, j += spld_len) {
01215         pout("  phy identifier = %d\n", vcp[1]);
01216         spld_len = vcp[3];
01217         if (spld_len < 44)
01218             spld_len = 48;      /* in SAS-1 and SAS-1.1 vcp[3]==0 */
01219         else
01220             spld_len += 4;
01221         t = ((0x70 & vcp[4]) >> 4);
01222         switch (t) {
01223         case 0: snprintf(s, sz, "no device attached"); break;
01224         case 1: snprintf(s, sz, "end device"); break;
01225         case 2: snprintf(s, sz, "expander device"); break;
01226         case 3: snprintf(s, sz, "expander device (fanout)"); break;
01227         default: snprintf(s, sz, "reserved [%d]", t); break;
01228         }
01229         pout("    attached device type: %s\n", s);
01230         t = 0xf & vcp[4];
01231         switch (t) {
01232         case 0: snprintf(s, sz, "unknown"); break;
01233         case 1: snprintf(s, sz, "power on"); break;
01234         case 2: snprintf(s, sz, "hard reset"); break;
01235         case 3: snprintf(s, sz, "SMP phy control function"); break;
01236         case 4: snprintf(s, sz, "loss of dword synchronization"); break;
01237         case 5: snprintf(s, sz, "mux mix up"); break;
01238         case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
01239             break;
01240         case 7: snprintf(s, sz, "break timeout timer expired"); break;
01241         case 8: snprintf(s, sz, "phy test function stopped"); break;
01242         case 9: snprintf(s, sz, "expander device reduced functionality");
01243              break;
01244         default: snprintf(s, sz, "reserved [0x%x]", t); break;
01245         }
01246         pout("    attached reason: %s\n", s);
01247         t = (vcp[5] & 0xf0) >> 4;
01248         switch (t) {
01249         case 0: snprintf(s, sz, "unknown"); break;
01250         case 1: snprintf(s, sz, "power on"); break;
01251         case 2: snprintf(s, sz, "hard reset"); break;
01252         case 3: snprintf(s, sz, "SMP phy control function"); break;
01253         case 4: snprintf(s, sz, "loss of dword synchronization"); break;
01254         case 5: snprintf(s, sz, "mux mix up"); break;
01255         case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
01256             break;
01257         case 7: snprintf(s, sz, "break timeout timer expired"); break;
01258         case 8: snprintf(s, sz, "phy test function stopped"); break;
01259         case 9: snprintf(s, sz, "expander device reduced functionality");
01260              break;
01261         default: snprintf(s, sz, "reserved [0x%x]", t); break;
01262         }
01263         pout("    reason: %s\n", s);
01264         t = (0xf & vcp[5]);
01265         switch (t) {
01266         case 0: snprintf(s, sz, "phy enabled; unknown");
01267                      break;
01268         case 1: snprintf(s, sz, "phy disabled"); break;
01269         case 2: snprintf(s, sz, "phy enabled; speed negotiation failed");
01270                      break;
01271         case 3: snprintf(s, sz, "phy enabled; SATA spinup hold state");
01272                      break;
01273         case 4: snprintf(s, sz, "phy enabled; port selector");
01274                      break;
01275         case 5: snprintf(s, sz, "phy enabled; reset in progress");
01276                      break;
01277         case 6: snprintf(s, sz, "phy enabled; unsupported phy attached");
01278                      break;
01279         case 8: snprintf(s, sz, "phy enabled; 1.5 Gbps"); break;
01280         case 9: snprintf(s, sz, "phy enabled; 3 Gbps"); break;
01281         case 0xa: snprintf(s, sz, "phy enabled; 6 Gbps"); break;
01282         default: snprintf(s, sz, "reserved [%d]", t); break;
01283         }
01284         pout("    negotiated logical link rate: %s\n", s);
01285         pout("    attached initiator port: ssp=%d stp=%d smp=%d\n",
01286                !! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2));
01287         pout("    attached target port: ssp=%d stp=%d smp=%d\n",
01288                !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2));
01289         for (n = 0, ull = vcp[8]; n < 8; ++n) {
01290             ull <<= 8; ull |= vcp[8 + n];
01291         }
01292         pout("    SAS address = 0x%" PRIx64 "\n", ull);
01293         for (n = 0, ull = vcp[16]; n < 8; ++n) {
01294             ull <<= 8; ull |= vcp[16 + n];
01295         }
01296         pout("    attached SAS address = 0x%" PRIx64 "\n", ull);
01297         pout("    attached phy identifier = %d\n", vcp[24]);
01298         ui = (vcp[32] << 24) | (vcp[33] << 16) | (vcp[34] << 8) | vcp[35];
01299         pout("    Invalid DWORD count = %u\n", ui);
01300         ui = (vcp[36] << 24) | (vcp[37] << 16) | (vcp[38] << 8) | vcp[39];
01301         pout("    Running disparity error count = %u\n", ui);
01302         ui = (vcp[40] << 24) | (vcp[41] << 16) | (vcp[42] << 8) | vcp[43];
01303         pout("    Loss of DWORD synchronization = %u\n", ui);
01304         ui = (vcp[44] << 24) | (vcp[45] << 16) | (vcp[46] << 8) | vcp[47];
01305         pout("    Phy reset problem = %u\n", ui);
01306         if (spld_len > 51) {
01307             int num_ped, peis;
01308             unsigned char * xcp;
01309             unsigned int pvdt;
01310 
01311             num_ped = vcp[51];
01312             if (num_ped > 0)
01313                pout("    Phy event descriptors:\n");
01314             xcp = vcp + 52;
01315             for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) {
01316                 peis = xcp[3];
01317                 ui = (xcp[4] << 24) | (xcp[5] << 16) | (xcp[6] << 8) |
01318                      xcp[7];
01319                 pvdt = (xcp[8] << 24) | (xcp[9] << 16) | (xcp[10] << 8) |
01320                        xcp[11];
01321                 show_sas_phy_event_info(peis, ui, pvdt);
01322             }
01323         }
01324     }
01325 }
01326 
01327 // Returns 1 if okay, 0 if non SAS descriptors
01328 static int
01329 show_protocol_specific_page(unsigned char * resp, int len)
01330 {
01331     int k, num, param_len;
01332     unsigned char * ucp;
01333 
01334     num = len - 4;
01335     for (k = 0, ucp = resp + 4; k < num; ) {
01336         param_len = ucp[3] + 4;
01337         if (6 != (0xf & ucp[4]))
01338             return 0;   /* only decode SAS log page */
01339         if (0 == k)
01340             pout("Protocol Specific port log page for SAS SSP\n");
01341         show_sas_port_param(ucp, param_len);
01342         k += param_len;
01343         ucp += param_len;
01344     }
01345     pout("\n");
01346     return 1;
01347 }
01348 
01349 
01350 // See Serial Attached SCSI (SAS-2) (e.g. revision 16) the Protocol Specific
01351 // log pageSerial Attached SCSI (SAS-2) (e.g. revision 16) the Protocol
01352 // Specific log page.
01353 // Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent
01354 // 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
01355 // FAILSMART is returned.
01356 static int
01357 scsiPrintSasPhy(scsi_device * device, int reset)
01358 {
01359     int num, err;
01360 
01361     if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf,
01362                             LOG_RESP_LONG_LEN, 0))) {
01363         print_on();
01364         pout("scsiPrintSasPhy Log Sense Failed [%s]\n\n", scsiErrString(err));
01365         print_off();
01366         return FAILSMART;
01367     }
01368     if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) {
01369         print_on();
01370         pout("Protocol specific Log Sense Failed, page mismatch\n\n");
01371         print_off();
01372         return FAILSMART;
01373     }
01374     // compute page length
01375     num = (gBuf[2] << 8) + gBuf[3];
01376     if (1 != show_protocol_specific_page(gBuf, num + 4)) {
01377         print_on();
01378         pout("Only support protocol specific log page on SAS devices\n\n");
01379         print_off();
01380         return FAILSMART;
01381     }
01382     if (reset) {
01383         if ((err = scsiLogSelect(device, 1 /* pcr */, 0 /* sp */, 0 /* pc */,
01384                                  PROTOCOL_SPECIFIC_LPAGE, 0, NULL, 0))) {
01385             print_on();
01386             pout("scsiPrintSasPhy Log Select (reset) Failed [%s]\n\n",
01387                  scsiErrString(err));
01388             print_off();
01389             return FAILSMART;
01390         }
01391     }
01392     return 0;
01393 }
01394 
01395 
01396 static const char * peripheral_dt_arr[] = {
01397         "disk",
01398         "tape",
01399         "printer",
01400         "processor",
01401         "optical disk(4)",
01402         "CD/DVD",
01403         "scanner",
01404         "optical disk(7)",
01405         "medium changer",
01406         "communications",
01407         "graphics(10)",
01408         "graphics(11)",
01409         "storage array",
01410         "enclosure",
01411         "simplified disk",
01412         "optical card reader"
01413 };
01414 
01415 static const char * transport_proto_arr[] = {
01416         "Fibre channel (FCP-2)",
01417         "Parallel SCSI (SPI-4)",
01418         "SSA",
01419         "IEEE 1394 (SBP-2)",
01420         "RDMA (SRP)",
01421         "iSCSI",
01422         "SAS",
01423         "ADT",
01424         "0x8",
01425         "0x9",
01426         "0xa",
01427         "0xb",
01428         "0xc",
01429         "0xd",
01430         "0xe",
01431         "0xf"
01432 };
01433 
01434 /* Returns 0 on success, 1 on general error and 2 for early, clean exit */
01435 static int
01436 scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
01437 {
01438     char timedatetz[DATEANDEPOCHLEN];
01439     struct scsi_iec_mode_page iec;
01440     int err, iec_err, len, req_len, avail_len, n;
01441     int is_tape = 0;
01442     int peri_dt = 0;
01443     int returnval = 0;
01444     int transport = -1;
01445     int form_factor = 0;
01446     int protect = 0;
01447 
01448     memset(gBuf, 0, 96);
01449     req_len = 36;
01450     if ((err = scsiStdInquiry(device, gBuf, req_len))) {
01451         print_on();
01452         pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err));
01453         pout("Retrying with a 64 byte Standard Inquiry\n");
01454         print_off();
01455         /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
01456         req_len = 64;
01457         if ((err = scsiStdInquiry(device, gBuf, req_len))) {
01458             print_on();
01459             pout("Standard Inquiry (64 bytes) failed [%s]\n",
01460                  scsiErrString(err));
01461             print_off();
01462             return 1;
01463         }
01464     }
01465     avail_len = gBuf[4] + 5;
01466     len = (avail_len < req_len) ? avail_len : req_len;
01467     peri_dt = gBuf[0] & 0x1f;
01468     if (peripheral_type)
01469         *peripheral_type = peri_dt;
01470 
01471     if (len < 36) {
01472         print_on();
01473         pout("Short INQUIRY response, skip product id\n");
01474         print_off();
01475         return 1;
01476     }
01477 
01478     if (all && (0 != strncmp((char *)&gBuf[8], "ATA", 3))) {
01479         char vendor[8+1], product[16+1], revision[4+1];
01480         scsi_format_id_string(vendor, (const unsigned char *)&gBuf[8], 8);
01481         scsi_format_id_string(product, (const unsigned char *)&gBuf[16], 16);
01482         scsi_format_id_string(revision, (const unsigned char *)&gBuf[32], 4);
01483 
01484         pout("=== START OF INFORMATION SECTION ===\n");
01485         pout("Vendor:               %.8s\n", vendor);
01486         pout("Product:              %.16s\n", product);
01487         if (gBuf[32] >= ' ')
01488             pout("Revision:             %.4s\n", revision);
01489     }
01490 
01491     if (!*device->get_req_type()/*no type requested*/ &&
01492                (0 == strncmp((char *)&gBuf[8], "ATA", 3))) {
01493         pout("\nProbable ATA device behind a SAT layer\n"
01494              "Try an additional '-d ata' or '-d sat' argument.\n");
01495         return 2;
01496     }
01497     if (! all)
01498         return 0;
01499 
01500     protect = gBuf[5] & 0x1;    /* from and including SPC-3 */
01501 
01502     if (! is_tape) {    /* only do this for disks */
01503         unsigned int lb_size = 0;
01504         unsigned char lb_prov_resp[8];
01505         char cap_str[64];
01506         char si_str[64];
01507         char lb_str[16];
01508         int lb_per_pb_exp = 0;
01509         uint64_t capacity = scsiGetSize(device, &lb_size, &lb_per_pb_exp);
01510 
01511         if (capacity) {
01512             format_with_thousands_sep(cap_str, sizeof(cap_str), capacity);
01513             format_capacity(si_str, sizeof(si_str), capacity);
01514             pout("User Capacity:        %s bytes [%s]\n", cap_str, si_str);
01515             snprintf(lb_str, sizeof(lb_str) - 1, "%u", lb_size);
01516             pout("Logical block size:   %s bytes\n", lb_str);
01517         }
01518         int lbpme = -1;
01519         int lbprz = -1;
01520         if (protect || lb_per_pb_exp) {
01521             unsigned char rc16_12[20] = {0, };
01522 
01523             if (0 == scsiGetProtPBInfo(device, rc16_12)) {
01524                 lb_per_pb_exp = rc16_12[1] & 0xf;       /* just in case */
01525                 if (lb_per_pb_exp > 0) {
01526                     snprintf(lb_str, sizeof(lb_str) - 1, "%u",
01527                              (lb_size * (1 << lb_per_pb_exp)));
01528                     pout("Physical block size:  %s bytes\n", lb_str);
01529                     n = ((rc16_12[2] & 0x3f) << 8) + rc16_12[3];
01530                     pout("Lowest aligned LBA:   %d\n", n);
01531                 }
01532                 if (rc16_12[0] & 0x1) { /* PROT_EN set */
01533                     int p_type = ((rc16_12[0] >> 1) & 0x7);
01534 
01535                     switch (p_type) {
01536                     case 0 :
01537                         pout("Formatted with type 1 protection\n");
01538                         break;
01539                     case 1 :
01540                         pout("Formatted with type 2 protection\n");
01541                         break;
01542                     case 2 :
01543                         pout("Formatted with type 3 protection\n");
01544                         break;
01545                     default:
01546                         pout("Formatted with unknown protection type [%d]\n",
01547                              p_type);
01548                         break;
01549                     }
01550                     int p_i_exp = ((rc16_12[1] >> 4) & 0xf);
01551 
01552                     if (p_i_exp > 0)
01553                         pout("%d protection information intervals per "
01554                              "logical block\n", (1 << p_i_exp));
01555                 }
01556                 /* Pick up some LB provisioning info since its available */
01557                 lbpme = !! (rc16_12[2] & 0x80);
01558                 lbprz = !! (rc16_12[2] & 0x40);
01559             }
01560         }
01561         if (0 == scsiInquiryVpd(device, SCSI_VPD_LOGICAL_BLOCK_PROVISIONING,
01562                                 lb_prov_resp, sizeof(lb_prov_resp))) {
01563             int prov_type = lb_prov_resp[6] & 0x7;
01564 
01565             if (-1 == lbprz)
01566                 lbprz = !! (lb_prov_resp[5] & 0x4);
01567             switch (prov_type) {
01568             case 0:
01569                 pout("Logical block provisioning type unreported, "
01570                      "LBPME=%d, LBPRZ=%d\n", lbpme, lbprz);
01571                 break;
01572             case 1:
01573                 pout("LU is resource provisioned, LBPRZ=%d\n", lbprz);
01574                 break;
01575             case 2:
01576                 pout("LU is thin provisioned, LBPRZ=%d\n", lbprz);
01577                 break;
01578             default:
01579                 pout("LU provisioning type reserved [%d], LBPRZ=%d\n",
01580                      prov_type, lbprz);
01581                 break;
01582             }
01583         } else if (1 == lbpme)
01584             pout("Logical block provisioning enabled, LBPRZ=%d\n", lbprz);
01585 
01586         int rpm = scsiGetRPM(device, modese_len, &form_factor);
01587         if (rpm > 0) {
01588             if (1 == rpm)
01589                 pout("Rotation Rate:        Solid State Device\n");
01590             else
01591                 pout("Rotation Rate:        %d rpm\n", rpm);
01592         }
01593         if (form_factor > 0) {
01594             const char * cp = NULL;
01595 
01596             switch (form_factor) {
01597             case 1:
01598                 cp = "5.25";
01599                 break;
01600             case 2:
01601                 cp = "3.5";
01602                 break;
01603             case 3:
01604                 cp = "2.5";
01605                 break;
01606             case 4:
01607                 cp = "1.8";
01608                 break;
01609             case 5:
01610                 cp = "< 1.8";
01611                 break;
01612             }
01613             if (cp)
01614                 pout("Form Factor:          %s inches\n", cp);
01615         }
01616     }
01617 
01618     /* Do this here to try and detect badly conforming devices (some USB
01619        keys) that will lock up on a InquiryVpd or log sense or ... */
01620     if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) {
01621         if (SIMPLE_ERR_BAD_RESP == iec_err) {
01622             pout(">> Terminate command early due to bad response to IEC "
01623                  "mode page\n");
01624             print_off();
01625             gIecMPage = 0;
01626             return 1;
01627         }
01628     } else
01629         modese_len = iec.modese_len;
01630 
01631     if (! dont_print_serial_number) {
01632         if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_DEVICE_IDENTIFICATION,
01633                                        gBuf, 252))) {
01634             char s[256];
01635 
01636             len = gBuf[3];
01637             scsi_decode_lu_dev_id(gBuf + 4, len, s, sizeof(s), &transport);
01638             if (strlen(s) > 0)
01639                 pout("Logical Unit id:      %s\n", s);
01640         } else if (scsi_debugmode > 0) {
01641             print_on();
01642             if (SIMPLE_ERR_BAD_RESP == err)
01643                 pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
01644             else
01645                 pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
01646             print_off();
01647         }
01648         if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_UNIT_SERIAL_NUMBER,
01649                                        gBuf, 252))) {
01650             char serial[256];
01651             len = gBuf[3];
01652 
01653             gBuf[4 + len] = '\0';
01654             scsi_format_id_string(serial, &gBuf[4], len);
01655             pout("Serial number:        %s\n", serial);
01656         } else if (scsi_debugmode > 0) {
01657             print_on();
01658             if (SIMPLE_ERR_BAD_RESP == err)
01659                 pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
01660             else
01661                 pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
01662             print_off();
01663         }
01664     }
01665 
01666     // print SCSI peripheral device type
01667     if (peri_dt < (int)(sizeof(peripheral_dt_arr) /
01668                         sizeof(peripheral_dt_arr[0])))
01669         pout("Device type:          %s\n", peripheral_dt_arr[peri_dt]);
01670     else
01671         pout("Device type:          <%d>\n", peri_dt);
01672 
01673     // See if transport protocol is known
01674     if (transport < 0)
01675         transport = scsiFetchTransportProtocol(device, modese_len);
01676     if ((transport >= 0) && (transport <= 0xf))
01677         pout("Transport protocol:   %s\n", transport_proto_arr[transport]);
01678 
01679     // print current time and date and timezone
01680     dateandtimezone(timedatetz);
01681     pout("Local Time is:        %s\n", timedatetz);
01682 
01683     if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) ||
01684         (SCSI_PT_MEDIUM_CHANGER == *peripheral_type))
01685         is_tape = 1;
01686     // See if unit accepts SCSI commmands from us
01687     if ((err = scsiTestUnitReady(device))) {
01688         if (SIMPLE_ERR_NOT_READY == err) {
01689             print_on();
01690             if (!is_tape)
01691                 pout("device is NOT READY (e.g. spun down, busy)\n");
01692             else
01693                 pout("device is NOT READY (e.g. no tape)\n");
01694             print_off();
01695          } else if (SIMPLE_ERR_NO_MEDIUM == err) {
01696             print_on();
01697             pout("NO MEDIUM present on device\n");
01698             print_off();
01699          } else if (SIMPLE_ERR_BECOMING_READY == err) {
01700             print_on();
01701             pout("device becoming ready (wait)\n");
01702             print_off();
01703         } else {
01704             print_on();
01705             pout("device Test Unit Ready  [%s]\n", scsiErrString(err));
01706             print_off();
01707         }
01708         failuretest(MANDATORY_CMD, returnval|=FAILID);
01709     }
01710 
01711     if (iec_err) {
01712         if (!is_tape) {
01713             print_on();
01714             pout("SMART support is:     Unavailable - device lacks SMART capability.\n");
01715             if (scsi_debugmode > 0)
01716                 pout(" [%s]\n", scsiErrString(iec_err));
01717             print_off();
01718         }
01719         gIecMPage = 0;
01720         return 0;
01721     }
01722 
01723     if (!is_tape)
01724         pout("SMART support is:     Available - device has SMART capability.\n"
01725              "SMART support is:     %s\n",
01726              (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled");
01727     pout("%s\n", (scsi_IsWarningEnabled(&iec)) ?
01728                   "Temperature Warning:  Enabled" :
01729                   "Temperature Warning:  Disabled or Not Supported");
01730     return 0;
01731 }
01732 
01733 static int
01734 scsiSmartEnable(scsi_device * device)
01735 {
01736     struct scsi_iec_mode_page iec;
01737     int err;
01738 
01739     if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
01740         print_on();
01741         pout("unable to fetch IEC (SMART) mode page [%s]\n",
01742              scsiErrString(err));
01743         print_off();
01744         return 1;
01745     } else
01746         modese_len = iec.modese_len;
01747 
01748     if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) {
01749         print_on();
01750         pout("unable to enable Exception control and warning [%s]\n",
01751              scsiErrString(err));
01752         print_off();
01753         return 1;
01754     }
01755     /* Need to refetch 'iec' since could be modified by previous call */
01756     if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
01757         pout("unable to fetch IEC (SMART) mode page [%s]\n",
01758              scsiErrString(err));
01759         return 1;
01760     } else
01761         modese_len = iec.modese_len;
01762 
01763     pout("Informational Exceptions (SMART) %s\n",
01764          scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
01765     pout("Temperature warning %s\n",
01766          scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
01767     return 0;
01768 }
01769 
01770 static int
01771 scsiSmartDisable(scsi_device * device)
01772 {
01773     struct scsi_iec_mode_page iec;
01774     int err;
01775 
01776     if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
01777         print_on();
01778         pout("unable to fetch IEC (SMART) mode page [%s]\n",
01779              scsiErrString(err));
01780         print_off();
01781         return 1;
01782     } else
01783         modese_len = iec.modese_len;
01784 
01785     if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) {
01786         print_on();
01787         pout("unable to disable Exception control and warning [%s]\n",
01788              scsiErrString(err));
01789         print_off();
01790         return 1;
01791     }
01792     /* Need to refetch 'iec' since could be modified by previous call */
01793     if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
01794         pout("unable to fetch IEC (SMART) mode page [%s]\n",
01795              scsiErrString(err));
01796         return 1;
01797     } else
01798         modese_len = iec.modese_len;
01799 
01800     pout("Informational Exceptions (SMART) %s\n",
01801          scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
01802     pout("Temperature warning %s\n",
01803          scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
01804     return 0;
01805 }
01806 
01807 static void
01808 scsiPrintTemp(scsi_device * device)
01809 {
01810     UINT8 temp = 0;
01811     UINT8 trip = 0;
01812 
01813     if (scsiGetTemp(device, &temp, &trip))
01814         return;
01815 
01816     if (temp) {
01817         if (255 != temp)
01818             pout("Current Drive Temperature:     %d C\n", temp);
01819         else
01820             pout("Current Drive Temperature:     <not available>\n");
01821     }
01822     if (trip)
01823         pout("Drive Trip Temperature:        %d C\n", trip);
01824     if (temp || trip)
01825         pout("\n");
01826 }
01827 
01828 /* Main entry point used by smartctl command. Return 0 for success */
01829 int
01830 scsiPrintMain(scsi_device * device, const scsi_print_options & options)
01831 {
01832     int checkedSupportedLogPages = 0;
01833     UINT8 peripheral_type = 0;
01834     int returnval = 0;
01835     int res, durationSec;
01836     struct scsi_sense_disect sense_info;
01837 
01838     bool any_output = options.drive_info;
01839 
01840     if (supported_vpd_pages_p) {
01841         delete supported_vpd_pages_p;
01842         supported_vpd_pages_p = NULL;
01843     }
01844     supported_vpd_pages_p = new supported_vpd_pages(device);
01845 
01846     res = scsiGetDriveInfo(device, &peripheral_type, options.drive_info);
01847     if (res) {
01848         if (2 == res)
01849             return 0;
01850         else
01851             failuretest(MANDATORY_CMD, returnval |= FAILID);
01852         any_output = true;
01853     }
01854 
01855   // Print read look-ahead status for disks
01856   short int wce = -1, rcd = -1;
01857   if (options.get_rcd || options.get_wce) {
01858     if (SCSI_PT_DIRECT_ACCESS == peripheral_type)
01859        res = scsiGetSetCache(device, modese_len, &wce, &rcd);
01860     else
01861        res = -1; // fetch for disks only
01862     any_output = true;
01863   }
01864 
01865   if (options.get_rcd) {
01866     pout("Read Cache is:        %s\n",
01867       res ? "Unavailable" : // error
01868       rcd ? "Disabled" : "Enabled");
01869    }
01870 
01871   if (options.get_wce) {
01872     pout("Writeback Cache is:   %s\n",
01873       res ? "Unavailable" : // error
01874       !wce ? "Disabled" : "Enabled");
01875    }
01876    if (options.drive_info)
01877      pout("\n");
01878 
01879   // START OF THE ENABLE/DISABLE SECTION OF THE CODE
01880   if (   options.smart_disable           || options.smart_enable
01881       || options.smart_auto_save_disable || options.smart_auto_save_enable)
01882     pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
01883 
01884     if (options.smart_enable) {
01885         if (scsiSmartEnable(device))
01886             failuretest(MANDATORY_CMD, returnval |= FAILSMART);
01887         any_output = true;
01888     }
01889 
01890     if (options.smart_disable) {
01891         if (scsiSmartDisable(device))
01892             failuretest(MANDATORY_CMD,returnval |= FAILSMART);
01893         any_output = true;
01894     }
01895 
01896     if (options.smart_auto_save_enable) {
01897       if (scsiSetControlGLTSD(device, 0, modese_len)) {
01898         pout("Enable autosave (clear GLTSD bit) failed\n");
01899         failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
01900       }
01901       else {
01902          pout("Autosave enabled (GLTSD bit set).\n");
01903       }
01904       any_output = true;
01905     }
01906 
01907     // Enable/Disable write cache
01908     if (options.set_wce && SCSI_PT_DIRECT_ACCESS == peripheral_type) {
01909       short int enable = wce = (options.set_wce > 0);
01910       rcd = -1;
01911       if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
01912           pout("Write cache %sable failed: %s\n", (enable ? "en" : "dis"),
01913                device->get_errmsg());
01914           failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
01915       }
01916       else
01917         pout("Write cache %sabled\n", (enable ? "en" : "dis"));
01918       any_output = true;
01919     }
01920 
01921     // Enable/Disable read cache
01922     if (options.set_rcd && SCSI_PT_DIRECT_ACCESS == peripheral_type) {
01923       short int enable =  (options.set_rcd > 0);
01924       rcd = !enable;
01925       wce = -1;
01926       if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
01927           pout("Read cache %sable failed: %s\n", (enable ? "en" : "dis"),
01928                 device->get_errmsg());
01929           failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
01930       }
01931       else
01932         pout("Read cache %sabled\n", (enable ? "en" : "dis"));
01933       any_output = true;
01934     }
01935 
01936     if (options.smart_auto_save_disable) {
01937       if (scsiSetControlGLTSD(device, 1, modese_len)) {
01938         pout("Disable autosave (set GLTSD bit) failed\n");
01939         failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
01940       }
01941       else {
01942          pout("Autosave disabled (GLTSD bit cleared).\n");
01943       }
01944       any_output = true;
01945     }
01946   if (   options.smart_disable           || options.smart_enable
01947       || options.smart_auto_save_disable || options.smart_auto_save_enable)
01948     pout("\n"); // END OF THE ENABLE/DISABLE SECTION OF THE CODE
01949 
01950     // START OF READ-ONLY OPTIONS APART FROM -V and -i
01951     if (    options.smart_check_status  || options.smart_ss_media_log
01952            || options.smart_vendor_attrib || options.smart_error_log
01953            || options.smart_selftest_log  || options.smart_vendor_attrib
01954            || options.smart_background_log || options.sasphy
01955          )
01956     pout("=== START OF READ SMART DATA SECTION ===\n");
01957 
01958     if (options.smart_check_status) {
01959         scsiGetSupportedLogPages(device);
01960         checkedSupportedLogPages = 1;
01961         if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
01962             (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */
01963             if (gTapeAlertsLPage) {
01964                 if (options.drive_info)
01965                     pout("TapeAlert Supported\n");
01966                 if (-1 == scsiGetTapeAlertsData(device, peripheral_type))
01967                     failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
01968             }
01969             else
01970                 pout("TapeAlert Not Supported\n");
01971         } else { /* disk, cd/dvd, enclosure, etc */
01972             if ((res = scsiGetSmartData(device, options.smart_vendor_attrib))) {
01973                 if (-2 == res)
01974                     returnval |= FAILSTATUS;
01975                 else
01976                     returnval |= FAILSMART;
01977             }
01978         }
01979         any_output = true;
01980     }
01981 
01982     if (options.smart_ss_media_log) {
01983         if (! checkedSupportedLogPages)
01984             scsiGetSupportedLogPages(device);
01985         res = 0;
01986         if (gSSMediaLPage)
01987             res = scsiPrintSSMedia(device);
01988         if (0 != res)
01989             failuretest(OPTIONAL_CMD, returnval|=res);
01990         any_output = true;
01991     }
01992     if (options.smart_vendor_attrib) {
01993         if (! checkedSupportedLogPages)
01994             scsiGetSupportedLogPages(device);
01995         if (gTempLPage) {
01996             scsiPrintTemp(device);
01997         }
01998         if (gStartStopLPage)
01999             scsiGetStartStopData(device);
02000         if (SCSI_PT_DIRECT_ACCESS == peripheral_type) {
02001             scsiPrintGrownDefectListLen(device);
02002             if (gSeagateCacheLPage)
02003                 scsiPrintSeagateCacheLPage(device);
02004             if (gSeagateFactoryLPage)
02005                 scsiPrintSeagateFactoryLPage(device);
02006         }
02007         any_output = true;
02008     }
02009     if (options.smart_error_log) {
02010         if (! checkedSupportedLogPages)
02011             scsiGetSupportedLogPages(device);
02012         scsiPrintErrorCounterLog(device);
02013         if (1 == scsiFetchControlGLTSD(device, modese_len, 1))
02014             pout("\n[GLTSD (Global Logging Target Save Disable) set. "
02015                  "Enable Save with '-S on']\n");
02016         any_output = true;
02017     }
02018     if (options.smart_selftest_log) {
02019         if (! checkedSupportedLogPages)
02020             scsiGetSupportedLogPages(device);
02021         res = 0;
02022         if (gSelfTestLPage)
02023             res = scsiPrintSelfTest(device);
02024         else {
02025             pout("Device does not support Self Test logging\n");
02026             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
02027         }
02028         if (0 != res)
02029             failuretest(OPTIONAL_CMD, returnval|=res);
02030         any_output = true;
02031     }
02032     if (options.smart_background_log) {
02033         if (! checkedSupportedLogPages)
02034             scsiGetSupportedLogPages(device);
02035         res = 0;
02036         if (gBackgroundResultsLPage)
02037             res = scsiPrintBackgroundResults(device);
02038         else {
02039             pout("Device does not support Background scan results logging\n");
02040             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
02041         }
02042         if (0 != res)
02043             failuretest(OPTIONAL_CMD, returnval|=res);
02044         any_output = true;
02045     }
02046     if (options.smart_default_selftest) {
02047         if (scsiSmartDefaultSelfTest(device))
02048             return returnval | FAILSMART;
02049         pout("Default Self Test Successful\n");
02050         any_output = true;
02051     }
02052     if (options.smart_short_cap_selftest) {
02053         if (scsiSmartShortCapSelfTest(device))
02054             return returnval | FAILSMART;
02055         pout("Short Foreground Self Test Successful\n");
02056         any_output = true;
02057     }
02058     // check if another test is running
02059     if (options.smart_short_selftest || options.smart_extend_selftest) {
02060       if (!scsiRequestSense(device, &sense_info) &&
02061             (sense_info.asc == 0x04 && sense_info.ascq == 0x09)) {
02062          if (!options.smart_selftest_force) {
02063            pout("Can't start self-test without aborting current test");
02064            if (sense_info.progress != -1) {
02065              pout(" (%d%% remaining)",
02066                   100 - sense_info.progress * 100 / 65535);
02067            }
02068            pout(",\nadd '-t force' option to override, or run 'smartctl -X' "
02069                 "to abort test.\n");
02070             return -1;
02071          }
02072          else
02073             scsiSmartSelfTestAbort(device);
02074        }
02075     }
02076     if (options.smart_short_selftest) {
02077         if (scsiSmartShortSelfTest(device))
02078             return returnval | FAILSMART;
02079         pout("Short Background Self Test has begun\n");
02080         pout("Use smartctl -X to abort test\n");
02081         any_output = true;
02082     }
02083     if (options.smart_extend_selftest) {
02084         if (scsiSmartExtendSelfTest(device))
02085             return returnval | FAILSMART;
02086         pout("Extended Background Self Test has begun\n");
02087         if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
02088                         modese_len)) && (durationSec > 0)) {
02089             time_t t = time(NULL);
02090 
02091             t += durationSec;
02092             pout("Please wait %d minutes for test to complete.\n",
02093                  durationSec / 60);
02094             pout("Estimated completion time: %s\n", ctime(&t));
02095         }
02096         pout("Use smartctl -X to abort test\n");
02097         any_output = true;
02098     }
02099     if (options.smart_extend_cap_selftest) {
02100         if (scsiSmartExtendCapSelfTest(device))
02101             return returnval | FAILSMART;
02102         pout("Extended Foreground Self Test Successful\n");
02103     }
02104     if (options.smart_selftest_abort) {
02105         if (scsiSmartSelfTestAbort(device))
02106             return returnval | FAILSMART;
02107         pout("Self Test returned without error\n");
02108         any_output = true;
02109     }
02110     if (options.sasphy) {
02111         if (scsiPrintSasPhy(device, options.sasphy_reset))
02112             return returnval | FAILSMART;
02113         any_output = true;
02114     }
02115 
02116     if (!any_output)
02117       pout("SCSI device successfully opened\n\n"
02118            "Use 'smartctl -a' (or '-x') to print SMART (and more) information\n\n");
02119 
02120     return returnval;
02121 }