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 3898 2014-04-30 17:44:13Z dpgilbert $"
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("\nLong (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("SS Media Percentage used endurance indicator parameter "
01052                      "too short (pl=%d)\n", pl);
01053                 print_off();
01054                 return FAILSMART;
01055             }
01056             pout("Percentage used endurance indicator: %d%%\n", ucp[7]);
01057         default:        /* ignore other parameter codes */
01058             break;
01059         }
01060         num -= pl;
01061         ucp += pl;
01062     }
01063     return retval;
01064 }
01065 
01066 static void
01067 show_sas_phy_event_info(int peis, unsigned int val, unsigned thresh_val)
01068 {
01069     unsigned int u;
01070 
01071     switch (peis) {
01072     case 0:
01073         pout("     No event\n");
01074         break;
01075     case 0x1:
01076         pout("     Invalid word count: %u\n", val);
01077         break;
01078     case 0x2:
01079         pout("     Running disparity error count: %u\n", val);
01080         break;
01081     case 0x3:
01082         pout("     Loss of dword synchronization count: %u\n", val);
01083         break;
01084     case 0x4:
01085         pout("     Phy reset problem count: %u\n", val);
01086         break;
01087     case 0x5:
01088         pout("     Elasticity buffer overflow count: %u\n", val);
01089         break;
01090     case 0x6:
01091         pout("     Received ERROR  count: %u\n", val);
01092         break;
01093     case 0x20:
01094         pout("     Received address frame error count: %u\n", val);
01095         break;
01096     case 0x21:
01097         pout("     Transmitted abandon-class OPEN_REJECT count: %u\n", val);
01098         break;
01099     case 0x22:
01100         pout("     Received abandon-class OPEN_REJECT count: %u\n", val);
01101         break;
01102     case 0x23:
01103         pout("     Transmitted retry-class OPEN_REJECT count: %u\n", val);
01104         break;
01105     case 0x24:
01106         pout("     Received retry-class OPEN_REJECT count: %u\n", val);
01107         break;
01108     case 0x25:
01109         pout("     Received AIP (WATING ON PARTIAL) count: %u\n", val);
01110         break;
01111     case 0x26:
01112         pout("     Received AIP (WAITING ON CONNECTION) count: %u\n", val);
01113         break;
01114     case 0x27:
01115         pout("     Transmitted BREAK count: %u\n", val);
01116         break;
01117     case 0x28:
01118         pout("     Received BREAK count: %u\n", val);
01119         break;
01120     case 0x29:
01121         pout("     Break timeout count: %u\n", val);
01122         break;
01123     case 0x2a:
01124         pout("     Connection count: %u\n", val);
01125         break;
01126     case 0x2b:
01127         pout("     Peak transmitted pathway blocked count: %u\n",
01128                val & 0xff);
01129         pout("         Peak value detector threshold: %u\n",
01130                thresh_val & 0xff);
01131         break;
01132     case 0x2c:
01133         u = val & 0xffff;
01134         if (u < 0x8000)
01135             pout("     Peak transmitted arbitration wait time (us): "
01136                    "%u\n", u);
01137         else
01138             pout("     Peak transmitted arbitration wait time (ms): "
01139                    "%u\n", 33 + (u - 0x8000));
01140         u = thresh_val & 0xffff;
01141         if (u < 0x8000)
01142             pout("         Peak value detector threshold (us): %u\n",
01143                    u);
01144         else
01145             pout("         Peak value detector threshold (ms): %u\n",
01146                    33 + (u - 0x8000));
01147         break;
01148     case 0x2d:
01149         pout("     Peak arbitration time (us): %u\n", val);
01150         pout("         Peak value detector threshold: %u\n", thresh_val);
01151         break;
01152     case 0x2e:
01153         pout("     Peak connection time (us): %u\n", val);
01154         pout("         Peak value detector threshold: %u\n", thresh_val);
01155         break;
01156     case 0x40:
01157         pout("     Transmitted SSP frame count: %u\n", val);
01158         break;
01159     case 0x41:
01160         pout("     Received SSP frame count: %u\n", val);
01161         break;
01162     case 0x42:
01163         pout("     Transmitted SSP frame error count: %u\n", val);
01164         break;
01165     case 0x43:
01166         pout("     Received SSP frame error count: %u\n", val);
01167         break;
01168     case 0x44:
01169         pout("     Transmitted CREDIT_BLOCKED count: %u\n", val);
01170         break;
01171     case 0x45:
01172         pout("     Received CREDIT_BLOCKED count: %u\n", val);
01173         break;
01174     case 0x50:
01175         pout("     Transmitted SATA frame count: %u\n", val);
01176         break;
01177     case 0x51:
01178         pout("     Received SATA frame count: %u\n", val);
01179         break;
01180     case 0x52:
01181         pout("     SATA flow control buffer overflow count: %u\n", val);
01182         break;
01183     case 0x60:
01184         pout("     Transmitted SMP frame count: %u\n", val);
01185         break;
01186     case 0x61:
01187         pout("     Received SMP frame count: %u\n", val);
01188         break;
01189     case 0x63:
01190         pout("     Received SMP frame error count: %u\n", val);
01191         break;
01192     default:
01193         break;
01194     }
01195 }
01196 
01197 static void
01198 show_sas_port_param(unsigned char * ucp, int param_len)
01199 {
01200     int j, m, n, nphys, t, sz, spld_len;
01201     unsigned char * vcp;
01202     uint64_t ull;
01203     unsigned int ui;
01204     char s[64];
01205 
01206     sz = sizeof(s);
01207     // pcb = ucp[2];
01208     t = (ucp[0] << 8) | ucp[1];
01209     pout("relative target port id = %d\n", t);
01210     pout("  generation code = %d\n", ucp[6]);
01211     nphys = ucp[7];
01212     pout("  number of phys = %d\n", nphys);
01213 
01214     for (j = 0, vcp = ucp + 8; j < (param_len - 8);
01215          vcp += spld_len, j += spld_len) {
01216         pout("  phy identifier = %d\n", vcp[1]);
01217         spld_len = vcp[3];
01218         if (spld_len < 44)
01219             spld_len = 48;      /* in SAS-1 and SAS-1.1 vcp[3]==0 */
01220         else
01221             spld_len += 4;
01222         t = ((0x70 & vcp[4]) >> 4);
01223         switch (t) {
01224         case 0: snprintf(s, sz, "no device attached"); break;
01225         case 1: snprintf(s, sz, "SAS or SATA device"); break;
01226         case 2: snprintf(s, sz, "expander device"); break;
01227         case 3: snprintf(s, sz, "expander device (fanout)"); break;
01228         default: snprintf(s, sz, "reserved [%d]", t); break;
01229         }
01230         pout("    attached device type: %s\n", s);
01231         t = 0xf & vcp[4];
01232         switch (t) {
01233         case 0: snprintf(s, sz, "unknown"); break;
01234         case 1: snprintf(s, sz, "power on"); break;
01235         case 2: snprintf(s, sz, "hard reset"); break;
01236         case 3: snprintf(s, sz, "SMP phy control function"); break;
01237         case 4: snprintf(s, sz, "loss of dword synchronization"); break;
01238         case 5: snprintf(s, sz, "mux mix up"); break;
01239         case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
01240             break;
01241         case 7: snprintf(s, sz, "break timeout timer expired"); break;
01242         case 8: snprintf(s, sz, "phy test function stopped"); break;
01243         case 9: snprintf(s, sz, "expander device reduced functionality");
01244              break;
01245         default: snprintf(s, sz, "reserved [0x%x]", t); break;
01246         }
01247         pout("    attached reason: %s\n", s);
01248         t = (vcp[5] & 0xf0) >> 4;
01249         switch (t) {
01250         case 0: snprintf(s, sz, "unknown"); break;
01251         case 1: snprintf(s, sz, "power on"); break;
01252         case 2: snprintf(s, sz, "hard reset"); break;
01253         case 3: snprintf(s, sz, "SMP phy control function"); break;
01254         case 4: snprintf(s, sz, "loss of dword synchronization"); break;
01255         case 5: snprintf(s, sz, "mux mix up"); break;
01256         case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
01257             break;
01258         case 7: snprintf(s, sz, "break timeout timer expired"); break;
01259         case 8: snprintf(s, sz, "phy test function stopped"); break;
01260         case 9: snprintf(s, sz, "expander device reduced functionality");
01261              break;
01262         default: snprintf(s, sz, "reserved [0x%x]", t); break;
01263         }
01264         pout("    reason: %s\n", s);
01265         t = (0xf & vcp[5]);
01266         switch (t) {
01267         case 0: snprintf(s, sz, "phy enabled; unknown");
01268                      break;
01269         case 1: snprintf(s, sz, "phy disabled"); break;
01270         case 2: snprintf(s, sz, "phy enabled; speed negotiation failed");
01271                      break;
01272         case 3: snprintf(s, sz, "phy enabled; SATA spinup hold state");
01273                      break;
01274         case 4: snprintf(s, sz, "phy enabled; port selector");
01275                      break;
01276         case 5: snprintf(s, sz, "phy enabled; reset in progress");
01277                      break;
01278         case 6: snprintf(s, sz, "phy enabled; unsupported phy attached");
01279                      break;
01280         case 8: snprintf(s, sz, "phy enabled; 1.5 Gbps"); break;
01281         case 9: snprintf(s, sz, "phy enabled; 3 Gbps"); break;
01282         case 0xa: snprintf(s, sz, "phy enabled; 6 Gbps"); break;
01283         case 0xb: snprintf(s, sz, "phy enabled; 12 Gbps"); break;
01284         default: snprintf(s, sz, "reserved [%d]", t); break;
01285         }
01286         pout("    negotiated logical link rate: %s\n", s);
01287         pout("    attached initiator port: ssp=%d stp=%d smp=%d\n",
01288                !! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2));
01289         pout("    attached target port: ssp=%d stp=%d smp=%d\n",
01290                !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2));
01291         for (n = 0, ull = vcp[8]; n < 8; ++n) {
01292             ull <<= 8; ull |= vcp[8 + n];
01293         }
01294         pout("    SAS address = 0x%" PRIx64 "\n", ull);
01295         for (n = 0, ull = vcp[16]; n < 8; ++n) {
01296             ull <<= 8; ull |= vcp[16 + n];
01297         }
01298         pout("    attached SAS address = 0x%" PRIx64 "\n", ull);
01299         pout("    attached phy identifier = %d\n", vcp[24]);
01300         ui = (vcp[32] << 24) | (vcp[33] << 16) | (vcp[34] << 8) | vcp[35];
01301         pout("    Invalid DWORD count = %u\n", ui);
01302         ui = (vcp[36] << 24) | (vcp[37] << 16) | (vcp[38] << 8) | vcp[39];
01303         pout("    Running disparity error count = %u\n", ui);
01304         ui = (vcp[40] << 24) | (vcp[41] << 16) | (vcp[42] << 8) | vcp[43];
01305         pout("    Loss of DWORD synchronization = %u\n", ui);
01306         ui = (vcp[44] << 24) | (vcp[45] << 16) | (vcp[46] << 8) | vcp[47];
01307         pout("    Phy reset problem = %u\n", ui);
01308         if (spld_len > 51) {
01309             int num_ped, peis;
01310             unsigned char * xcp;
01311             unsigned int pvdt;
01312 
01313             num_ped = vcp[51];
01314             if (num_ped > 0)
01315                pout("    Phy event descriptors:\n");
01316             xcp = vcp + 52;
01317             for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) {
01318                 peis = xcp[3];
01319                 ui = (xcp[4] << 24) | (xcp[5] << 16) | (xcp[6] << 8) |
01320                      xcp[7];
01321                 pvdt = (xcp[8] << 24) | (xcp[9] << 16) | (xcp[10] << 8) |
01322                        xcp[11];
01323                 show_sas_phy_event_info(peis, ui, pvdt);
01324             }
01325         }
01326     }
01327 }
01328 
01329 // Returns 1 if okay, 0 if non SAS descriptors
01330 static int
01331 show_protocol_specific_page(unsigned char * resp, int len)
01332 {
01333     int k, num, param_len;
01334     unsigned char * ucp;
01335 
01336     num = len - 4;
01337     for (k = 0, ucp = resp + 4; k < num; ) {
01338         param_len = ucp[3] + 4;
01339         if (6 != (0xf & ucp[4]))
01340             return 0;   /* only decode SAS log page */
01341         if (0 == k)
01342             pout("Protocol Specific port log page for SAS SSP\n");
01343         show_sas_port_param(ucp, param_len);
01344         k += param_len;
01345         ucp += param_len;
01346     }
01347     pout("\n");
01348     return 1;
01349 }
01350 
01351 
01352 // See Serial Attached SCSI (SPL-3) (e.g. revision 6g) the Protocol Specific
01353 // log page [0x18]. Returns 0 if ok else FAIL* bitmask.
01354 static int
01355 scsiPrintSasPhy(scsi_device * device, int reset)
01356 {
01357     int num, err;
01358 
01359     if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf,
01360                             LOG_RESP_LONG_LEN, 0))) {
01361         print_on();
01362         pout("scsiPrintSasPhy Log Sense Failed [%s]\n\n", scsiErrString(err));
01363         print_off();
01364         return FAILSMART;
01365     }
01366     if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) {
01367         print_on();
01368         pout("Protocol specific Log Sense Failed, page mismatch\n\n");
01369         print_off();
01370         return FAILSMART;
01371     }
01372     // compute page length
01373     num = (gBuf[2] << 8) + gBuf[3];
01374     if (1 != show_protocol_specific_page(gBuf, num + 4)) {
01375         print_on();
01376         pout("Only support protocol specific log page on SAS devices\n\n");
01377         print_off();
01378         return FAILSMART;
01379     }
01380     if (reset) {
01381         if ((err = scsiLogSelect(device, 1 /* pcr */, 0 /* sp */, 0 /* pc */,
01382                                  PROTOCOL_SPECIFIC_LPAGE, 0, NULL, 0))) {
01383             print_on();
01384             pout("scsiPrintSasPhy Log Select (reset) Failed [%s]\n\n",
01385                  scsiErrString(err));
01386             print_off();
01387             return FAILSMART;
01388         }
01389     }
01390     return 0;
01391 }
01392 
01393 
01394 static const char * peripheral_dt_arr[] = {
01395         "disk",
01396         "tape",
01397         "printer",
01398         "processor",
01399         "optical disk(4)",
01400         "CD/DVD",
01401         "scanner",
01402         "optical disk(7)",
01403         "medium changer",
01404         "communications",
01405         "graphics(10)",
01406         "graphics(11)",
01407         "storage array",
01408         "enclosure",
01409         "simplified disk",
01410         "optical card reader"
01411         "reserved [0x10]"
01412         "object based storage"
01413         "automation/driver interface"
01414         "security manager device"
01415         "host managed zoned block device"
01416         "reserved [0x15]"
01417         "reserved [0x16]"
01418         "reserved [0x17]"
01419         "reserved [0x18]"
01420         "reserved [0x19]"
01421         "reserved [0x1a]"
01422         "reserved [0x1b]"
01423         "reserved [0x1c]"
01424         "reserved [0x1d]"
01425         "well known logical unit"
01426         "unknown or no device type"
01427 };
01428 
01429 static const char * transport_proto_arr[] = {
01430         "Fibre channel (FCP-2)",
01431         "Parallel SCSI (SPI-4)",
01432         "SSA",
01433         "IEEE 1394 (SBP-2)",
01434         "RDMA (SRP)",
01435         "iSCSI",
01436         "SAS (SPL-3)",
01437         "ADT",
01438         "ATA (ACS-2)",
01439         "UAS",
01440         "SOP",
01441         "0xb",
01442         "0xc",
01443         "0xd",
01444         "0xe",
01445         "0xf"
01446 };
01447 
01448 /* Returns 0 on success, 1 on general error and 2 for early, clean exit */
01449 static int
01450 scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
01451 {
01452     char timedatetz[DATEANDEPOCHLEN];
01453     struct scsi_iec_mode_page iec;
01454     int err, iec_err, len, req_len, avail_len, n, scsi_version;
01455     int is_tape = 0;
01456     int peri_dt = 0;
01457     int returnval = 0;
01458     int transport = -1;
01459     int form_factor = 0;
01460     int haw_zbc = 0;
01461     int protect = 0;
01462 
01463     memset(gBuf, 0, 96);
01464     req_len = 36;
01465     if ((err = scsiStdInquiry(device, gBuf, req_len))) {
01466         print_on();
01467         pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err));
01468         pout("Retrying with a 64 byte Standard Inquiry\n");
01469         print_off();
01470         /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
01471         req_len = 64;
01472         if ((err = scsiStdInquiry(device, gBuf, req_len))) {
01473             print_on();
01474             pout("Standard Inquiry (64 bytes) failed [%s]\n",
01475                  scsiErrString(err));
01476             print_off();
01477             return 1;
01478         }
01479     }
01480     avail_len = gBuf[4] + 5;
01481     len = (avail_len < req_len) ? avail_len : req_len;
01482     peri_dt = gBuf[0] & 0x1f;
01483     if (peripheral_type)
01484         *peripheral_type = peri_dt;
01485 
01486     if (len < 36) {
01487         print_on();
01488         pout("Short INQUIRY response, skip product id\n");
01489         print_off();
01490         return 1;
01491     }
01492     // Upper bits of version bytes were used in older standards
01493     // Only interested in SPC-4 (0x6) and SPC-5 (assumed to be 0x7)
01494     scsi_version = gBuf[2] & 0x7;
01495 
01496     if (all && (0 != strncmp((char *)&gBuf[8], "ATA", 3))) {
01497         char vendor[8+1], product[16+1], revision[4+1];
01498         scsi_format_id_string(vendor, (const unsigned char *)&gBuf[8], 8);
01499         scsi_format_id_string(product, (const unsigned char *)&gBuf[16], 16);
01500         scsi_format_id_string(revision, (const unsigned char *)&gBuf[32], 4);
01501 
01502         pout("=== START OF INFORMATION SECTION ===\n");
01503         pout("Vendor:               %.8s\n", vendor);
01504         pout("Product:              %.16s\n", product);
01505         if (gBuf[32] >= ' ')
01506             pout("Revision:             %.4s\n", revision);
01507         if (scsi_version == 0x6)
01508             pout("Compliance:           SPC-4\n");
01509         else if (scsi_version == 0x7)
01510             pout("Compliance:           SPC-5\n");
01511     }
01512 
01513     if (!*device->get_req_type()/*no type requested*/ &&
01514                (0 == strncmp((char *)&gBuf[8], "ATA", 3))) {
01515         pout("\nProbable ATA device behind a SAT layer\n"
01516              "Try an additional '-d ata' or '-d sat' argument.\n");
01517         return 2;
01518     }
01519     if (! all)
01520         return 0;
01521 
01522     protect = gBuf[5] & 0x1;    /* from and including SPC-3 */
01523 
01524     if (! is_tape) {    /* only do this for disks */
01525         unsigned int lb_size = 0;
01526         unsigned char lb_prov_resp[8];
01527         char cap_str[64];
01528         char si_str[64];
01529         char lb_str[16];
01530         int lb_per_pb_exp = 0;
01531         uint64_t capacity = scsiGetSize(device, &lb_size, &lb_per_pb_exp);
01532 
01533         if (capacity) {
01534             format_with_thousands_sep(cap_str, sizeof(cap_str), capacity);
01535             format_capacity(si_str, sizeof(si_str), capacity);
01536             pout("User Capacity:        %s bytes [%s]\n", cap_str, si_str);
01537             snprintf(lb_str, sizeof(lb_str) - 1, "%u", lb_size);
01538             pout("Logical block size:   %s bytes\n", lb_str);
01539         }
01540         int lbpme = -1;
01541         int lbprz = -1;
01542         if (protect || lb_per_pb_exp) {
01543             unsigned char rc16_12[20] = {0, };
01544 
01545             if (0 == scsiGetProtPBInfo(device, rc16_12)) {
01546                 lb_per_pb_exp = rc16_12[1] & 0xf;       /* just in case */
01547                 if (lb_per_pb_exp > 0) {
01548                     snprintf(lb_str, sizeof(lb_str) - 1, "%u",
01549                              (lb_size * (1 << lb_per_pb_exp)));
01550                     pout("Physical block size:  %s bytes\n", lb_str);
01551                     n = ((rc16_12[2] & 0x3f) << 8) + rc16_12[3];
01552                     if (n > 0)  // not common so cut the clutter
01553                         pout("Lowest aligned LBA:   %d\n", n);
01554                 }
01555                 if (rc16_12[0] & 0x1) { /* PROT_EN set */
01556                     int p_type = ((rc16_12[0] >> 1) & 0x7);
01557 
01558                     switch (p_type) {
01559                     case 0 :
01560                         pout("Formatted with type 1 protection\n");
01561                         break;
01562                     case 1 :
01563                         pout("Formatted with type 2 protection\n");
01564                         break;
01565                     case 2 :
01566                         pout("Formatted with type 3 protection\n");
01567                         break;
01568                     default:
01569                         pout("Formatted with unknown protection type [%d]\n",
01570                              p_type);
01571                         break;
01572                     }
01573                     int p_i_exp = ((rc16_12[1] >> 4) & 0xf);
01574 
01575                     if (p_i_exp > 0)
01576                         pout("%d protection information intervals per "
01577                              "logical block\n", (1 << p_i_exp));
01578                 }
01579                 /* Pick up some LB provisioning info since its available */
01580                 lbpme = !! (rc16_12[2] & 0x80);
01581                 lbprz = !! (rc16_12[2] & 0x40);
01582             }
01583         }
01584         if (0 == scsiInquiryVpd(device, SCSI_VPD_LOGICAL_BLOCK_PROVISIONING,
01585                                 lb_prov_resp, sizeof(lb_prov_resp))) {
01586             int prov_type = lb_prov_resp[6] & 0x7;
01587 
01588             if (-1 == lbprz)
01589                 lbprz = !! (lb_prov_resp[5] & 0x4);
01590             switch (prov_type) {
01591             case 0:
01592                 pout("LB provisioning type: unreported, LBPME=%d, LBPRZ=%d\n",
01593                      lbpme, lbprz);
01594                 break;
01595             case 1:
01596                 pout("LU is resource provisioned, LBPRZ=%d\n", lbprz);
01597                 break;
01598             case 2:
01599                 pout("LU is thin provisioned, LBPRZ=%d\n", lbprz);
01600                 break;
01601             default:
01602                 pout("LU provisioning type reserved [%d], LBPRZ=%d\n",
01603                      prov_type, lbprz);
01604                 break;
01605             }
01606         } else if (1 == lbpme)
01607             pout("Logical block provisioning enabled, LBPRZ=%d\n", lbprz);
01608 
01609         int rpm = scsiGetRPM(device, modese_len, &form_factor, &haw_zbc);
01610         if (rpm >= 0) {
01611             if (0 == rpm)
01612                 ;       // Not reported
01613             else if (1 == rpm)
01614                 pout("Rotation Rate:        Solid State Device\n");
01615             else if ((rpm <= 0x400) || (0xffff == rpm))
01616                 ;       // Reserved
01617             else
01618                 pout("Rotation Rate:        %d rpm\n", rpm);
01619         }
01620         if (form_factor > 0) {
01621             const char * cp = NULL;
01622 
01623             switch (form_factor) {
01624             case 1:
01625                 cp = "5.25";
01626                 break;
01627             case 2:
01628                 cp = "3.5";
01629                 break;
01630             case 3:
01631                 cp = "2.5";
01632                 break;
01633             case 4:
01634                 cp = "1.8";
01635                 break;
01636             case 5:
01637                 cp = "< 1.8";
01638                 break;
01639             }
01640             if (cp)
01641                 pout("Form Factor:          %s inches\n", cp);
01642         }
01643         if (haw_zbc > 0)
01644             pout("Host aware zoned block capable\n");
01645     }
01646 
01647     /* Do this here to try and detect badly conforming devices (some USB
01648        keys) that will lock up on a InquiryVpd or log sense or ... */
01649     if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) {
01650         if (SIMPLE_ERR_BAD_RESP == iec_err) {
01651             pout(">> Terminate command early due to bad response to IEC "
01652                  "mode page\n");
01653             print_off();
01654             gIecMPage = 0;
01655             return 1;
01656         }
01657     } else
01658         modese_len = iec.modese_len;
01659 
01660     if (! dont_print_serial_number) {
01661         if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_DEVICE_IDENTIFICATION,
01662                                        gBuf, 252))) {
01663             char s[256];
01664 
01665             len = gBuf[3];
01666             scsi_decode_lu_dev_id(gBuf + 4, len, s, sizeof(s), &transport);
01667             if (strlen(s) > 0)
01668                 pout("Logical Unit id:      %s\n", s);
01669         } else if (scsi_debugmode > 0) {
01670             print_on();
01671             if (SIMPLE_ERR_BAD_RESP == err)
01672                 pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
01673             else
01674                 pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
01675             print_off();
01676         }
01677         if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_UNIT_SERIAL_NUMBER,
01678                                        gBuf, 252))) {
01679             char serial[256];
01680             len = gBuf[3];
01681 
01682             gBuf[4 + len] = '\0';
01683             scsi_format_id_string(serial, &gBuf[4], len);
01684             pout("Serial number:        %s\n", serial);
01685         } else if (scsi_debugmode > 0) {
01686             print_on();
01687             if (SIMPLE_ERR_BAD_RESP == err)
01688                 pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
01689             else
01690                 pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
01691             print_off();
01692         }
01693     }
01694 
01695     // print SCSI peripheral device type
01696     if (peri_dt < (int)(sizeof(peripheral_dt_arr) /
01697                         sizeof(peripheral_dt_arr[0])))
01698         pout("Device type:          %s\n", peripheral_dt_arr[peri_dt]);
01699     else
01700         pout("Device type:          <%d>\n", peri_dt);
01701 
01702     // See if transport protocol is known
01703     if (transport < 0)
01704         transport = scsiFetchTransportProtocol(device, modese_len);
01705     if ((transport >= 0) && (transport <= 0xf))
01706         pout("Transport protocol:   %s\n", transport_proto_arr[transport]);
01707 
01708     // print current time and date and timezone
01709     dateandtimezone(timedatetz);
01710     pout("Local Time is:        %s\n", timedatetz);
01711 
01712     if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) ||
01713         (SCSI_PT_MEDIUM_CHANGER == *peripheral_type))
01714         is_tape = 1;
01715     // See if unit accepts SCSI commmands from us
01716     if ((err = scsiTestUnitReady(device))) {
01717         if (SIMPLE_ERR_NOT_READY == err) {
01718             print_on();
01719             if (!is_tape)
01720                 pout("device is NOT READY (e.g. spun down, busy)\n");
01721             else
01722                 pout("device is NOT READY (e.g. no tape)\n");
01723             print_off();
01724          } else if (SIMPLE_ERR_NO_MEDIUM == err) {
01725             print_on();
01726             pout("NO MEDIUM present on device\n");
01727             print_off();
01728          } else if (SIMPLE_ERR_BECOMING_READY == err) {
01729             print_on();
01730             pout("device becoming ready (wait)\n");
01731             print_off();
01732         } else {
01733             print_on();
01734             pout("device Test Unit Ready  [%s]\n", scsiErrString(err));
01735             print_off();
01736         }
01737         failuretest(MANDATORY_CMD, returnval|=FAILID);
01738     }
01739 
01740     if (iec_err) {
01741         if (!is_tape) {
01742             print_on();
01743             pout("SMART support is:     Unavailable - device lacks SMART capability.\n");
01744             if (scsi_debugmode > 0)
01745                 pout(" [%s]\n", scsiErrString(iec_err));
01746             print_off();
01747         }
01748         gIecMPage = 0;
01749         return 0;
01750     }
01751 
01752     if (!is_tape)
01753         pout("SMART support is:     Available - device has SMART capability.\n"
01754              "SMART support is:     %s\n",
01755              (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled");
01756     pout("%s\n", (scsi_IsWarningEnabled(&iec)) ?
01757                   "Temperature Warning:  Enabled" :
01758                   "Temperature Warning:  Disabled or Not Supported");
01759     return 0;
01760 }
01761 
01762 static int
01763 scsiSmartEnable(scsi_device * device)
01764 {
01765     struct scsi_iec_mode_page iec;
01766     int err;
01767 
01768     if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
01769         print_on();
01770         pout("unable to fetch IEC (SMART) mode page [%s]\n",
01771              scsiErrString(err));
01772         print_off();
01773         return 1;
01774     } else
01775         modese_len = iec.modese_len;
01776 
01777     if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) {
01778         print_on();
01779         pout("unable to enable Exception control and warning [%s]\n",
01780              scsiErrString(err));
01781         print_off();
01782         return 1;
01783     }
01784     /* Need to refetch 'iec' since could be modified by previous call */
01785     if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
01786         pout("unable to fetch IEC (SMART) mode page [%s]\n",
01787              scsiErrString(err));
01788         return 1;
01789     } else
01790         modese_len = iec.modese_len;
01791 
01792     pout("Informational Exceptions (SMART) %s\n",
01793          scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
01794     pout("Temperature warning %s\n",
01795          scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
01796     return 0;
01797 }
01798 
01799 static int
01800 scsiSmartDisable(scsi_device * device)
01801 {
01802     struct scsi_iec_mode_page iec;
01803     int err;
01804 
01805     if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
01806         print_on();
01807         pout("unable to fetch IEC (SMART) mode page [%s]\n",
01808              scsiErrString(err));
01809         print_off();
01810         return 1;
01811     } else
01812         modese_len = iec.modese_len;
01813 
01814     if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) {
01815         print_on();
01816         pout("unable to disable Exception control and warning [%s]\n",
01817              scsiErrString(err));
01818         print_off();
01819         return 1;
01820     }
01821     /* Need to refetch 'iec' since could be modified by previous call */
01822     if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
01823         pout("unable to fetch IEC (SMART) mode page [%s]\n",
01824              scsiErrString(err));
01825         return 1;
01826     } else
01827         modese_len = iec.modese_len;
01828 
01829     pout("Informational Exceptions (SMART) %s\n",
01830          scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
01831     pout("Temperature warning %s\n",
01832          scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
01833     return 0;
01834 }
01835 
01836 static void
01837 scsiPrintTemp(scsi_device * device)
01838 {
01839     UINT8 temp = 0;
01840     UINT8 trip = 0;
01841 
01842     if (scsiGetTemp(device, &temp, &trip))
01843         return;
01844 
01845     if (temp) {
01846         if (255 != temp)
01847             pout("Current Drive Temperature:     %d C\n", temp);
01848         else
01849             pout("Current Drive Temperature:     <not available>\n");
01850     }
01851     if (trip)
01852         pout("Drive Trip Temperature:        %d C\n", trip);
01853     if (temp || trip)
01854         pout("\n");
01855 }
01856 
01857 /* Main entry point used by smartctl command. Return 0 for success */
01858 int
01859 scsiPrintMain(scsi_device * device, const scsi_print_options & options)
01860 {
01861     int checkedSupportedLogPages = 0;
01862     UINT8 peripheral_type = 0;
01863     int returnval = 0;
01864     int res, durationSec;
01865     struct scsi_sense_disect sense_info;
01866 
01867     bool any_output = options.drive_info;
01868 
01869     if (supported_vpd_pages_p) {
01870         delete supported_vpd_pages_p;
01871         supported_vpd_pages_p = NULL;
01872     }
01873     supported_vpd_pages_p = new supported_vpd_pages(device);
01874 
01875     res = scsiGetDriveInfo(device, &peripheral_type, options.drive_info);
01876     if (res) {
01877         if (2 == res)
01878             return 0;
01879         else
01880             failuretest(MANDATORY_CMD, returnval |= FAILID);
01881         any_output = true;
01882     }
01883 
01884   // Print read look-ahead status for disks
01885   short int wce = -1, rcd = -1;
01886   if (options.get_rcd || options.get_wce) {
01887     if (SCSI_PT_DIRECT_ACCESS == peripheral_type)
01888        res = scsiGetSetCache(device, modese_len, &wce, &rcd);
01889     else
01890        res = -1; // fetch for disks only
01891     any_output = true;
01892   }
01893 
01894   if (options.get_rcd) {
01895     pout("Read Cache is:        %s\n",
01896       res ? "Unavailable" : // error
01897       rcd ? "Disabled" : "Enabled");
01898    }
01899 
01900   if (options.get_wce) {
01901     pout("Writeback Cache is:   %s\n",
01902       res ? "Unavailable" : // error
01903       !wce ? "Disabled" : "Enabled");
01904    }
01905    if (options.drive_info)
01906      pout("\n");
01907 
01908   // START OF THE ENABLE/DISABLE SECTION OF THE CODE
01909   if (   options.smart_disable           || options.smart_enable
01910       || options.smart_auto_save_disable || options.smart_auto_save_enable)
01911     pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
01912 
01913     if (options.smart_enable) {
01914         if (scsiSmartEnable(device))
01915             failuretest(MANDATORY_CMD, returnval |= FAILSMART);
01916         any_output = true;
01917     }
01918 
01919     if (options.smart_disable) {
01920         if (scsiSmartDisable(device))
01921             failuretest(MANDATORY_CMD,returnval |= FAILSMART);
01922         any_output = true;
01923     }
01924 
01925     if (options.smart_auto_save_enable) {
01926       if (scsiSetControlGLTSD(device, 0, modese_len)) {
01927         pout("Enable autosave (clear GLTSD bit) failed\n");
01928         failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
01929       }
01930       else {
01931          pout("Autosave enabled (GLTSD bit set).\n");
01932       }
01933       any_output = true;
01934     }
01935 
01936     // Enable/Disable write cache
01937     if (options.set_wce && SCSI_PT_DIRECT_ACCESS == peripheral_type) {
01938       short int enable = wce = (options.set_wce > 0);
01939       rcd = -1;
01940       if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
01941           pout("Write cache %sable failed: %s\n", (enable ? "en" : "dis"),
01942                device->get_errmsg());
01943           failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
01944       }
01945       else
01946         pout("Write cache %sabled\n", (enable ? "en" : "dis"));
01947       any_output = true;
01948     }
01949 
01950     // Enable/Disable read cache
01951     if (options.set_rcd && SCSI_PT_DIRECT_ACCESS == peripheral_type) {
01952       short int enable =  (options.set_rcd > 0);
01953       rcd = !enable;
01954       wce = -1;
01955       if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
01956           pout("Read cache %sable failed: %s\n", (enable ? "en" : "dis"),
01957                 device->get_errmsg());
01958           failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
01959       }
01960       else
01961         pout("Read cache %sabled\n", (enable ? "en" : "dis"));
01962       any_output = true;
01963     }
01964 
01965     if (options.smart_auto_save_disable) {
01966       if (scsiSetControlGLTSD(device, 1, modese_len)) {
01967         pout("Disable autosave (set GLTSD bit) failed\n");
01968         failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
01969       }
01970       else {
01971          pout("Autosave disabled (GLTSD bit cleared).\n");
01972       }
01973       any_output = true;
01974     }
01975   if (   options.smart_disable           || options.smart_enable
01976       || options.smart_auto_save_disable || options.smart_auto_save_enable)
01977     pout("\n"); // END OF THE ENABLE/DISABLE SECTION OF THE CODE
01978 
01979     // START OF READ-ONLY OPTIONS APART FROM -V and -i
01980     if (    options.smart_check_status  || options.smart_ss_media_log
01981            || options.smart_vendor_attrib || options.smart_error_log
01982            || options.smart_selftest_log  || options.smart_vendor_attrib
01983            || options.smart_background_log || options.sasphy
01984          )
01985     pout("=== START OF READ SMART DATA SECTION ===\n");
01986 
01987     if (options.smart_check_status) {
01988         scsiGetSupportedLogPages(device);
01989         checkedSupportedLogPages = 1;
01990         if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
01991             (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */
01992             if (gTapeAlertsLPage) {
01993                 if (options.drive_info)
01994                     pout("TapeAlert Supported\n");
01995                 if (-1 == scsiGetTapeAlertsData(device, peripheral_type))
01996                     failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
01997             }
01998             else
01999                 pout("TapeAlert Not Supported\n");
02000         } else { /* disk, cd/dvd, enclosure, etc */
02001             if ((res = scsiGetSmartData(device, options.smart_vendor_attrib))) {
02002                 if (-2 == res)
02003                     returnval |= FAILSTATUS;
02004                 else
02005                     returnval |= FAILSMART;
02006             }
02007         }
02008         any_output = true;
02009     }
02010 
02011     if (options.smart_ss_media_log) {
02012         if (! checkedSupportedLogPages)
02013             scsiGetSupportedLogPages(device);
02014         res = 0;
02015         if (gSSMediaLPage)
02016             res = scsiPrintSSMedia(device);
02017         if (0 != res)
02018             failuretest(OPTIONAL_CMD, returnval|=res);
02019         any_output = true;
02020     }
02021     if (options.smart_vendor_attrib) {
02022         if (! checkedSupportedLogPages)
02023             scsiGetSupportedLogPages(device);
02024         if (gTempLPage) {
02025             scsiPrintTemp(device);
02026         }
02027         if (gStartStopLPage)
02028             scsiGetStartStopData(device);
02029         if (SCSI_PT_DIRECT_ACCESS == peripheral_type) {
02030             scsiPrintGrownDefectListLen(device);
02031             if (gSeagateCacheLPage)
02032                 scsiPrintSeagateCacheLPage(device);
02033             if (gSeagateFactoryLPage)
02034                 scsiPrintSeagateFactoryLPage(device);
02035         }
02036         any_output = true;
02037     }
02038     if (options.smart_error_log) {
02039         if (! checkedSupportedLogPages)
02040             scsiGetSupportedLogPages(device);
02041         scsiPrintErrorCounterLog(device);
02042         if (1 == scsiFetchControlGLTSD(device, modese_len, 1))
02043             pout("\n[GLTSD (Global Logging Target Save Disable) set. "
02044                  "Enable Save with '-S on']\n");
02045         any_output = true;
02046     }
02047     if (options.smart_selftest_log) {
02048         if (! checkedSupportedLogPages)
02049             scsiGetSupportedLogPages(device);
02050         res = 0;
02051         if (gSelfTestLPage)
02052             res = scsiPrintSelfTest(device);
02053         else {
02054             pout("Device does not support Self Test logging\n");
02055             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
02056         }
02057         if (0 != res)
02058             failuretest(OPTIONAL_CMD, returnval|=res);
02059         any_output = true;
02060     }
02061     if (options.smart_background_log) {
02062         if (! checkedSupportedLogPages)
02063             scsiGetSupportedLogPages(device);
02064         res = 0;
02065         if (gBackgroundResultsLPage)
02066             res = scsiPrintBackgroundResults(device);
02067         else {
02068             pout("Device does not support Background scan results logging\n");
02069             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
02070         }
02071         if (0 != res)
02072             failuretest(OPTIONAL_CMD, returnval|=res);
02073         any_output = true;
02074     }
02075     if (options.smart_default_selftest) {
02076         if (scsiSmartDefaultSelfTest(device))
02077             return returnval | FAILSMART;
02078         pout("Default Self Test Successful\n");
02079         any_output = true;
02080     }
02081     if (options.smart_short_cap_selftest) {
02082         if (scsiSmartShortCapSelfTest(device))
02083             return returnval | FAILSMART;
02084         pout("Short Foreground Self Test Successful\n");
02085         any_output = true;
02086     }
02087     // check if another test is running
02088     if (options.smart_short_selftest || options.smart_extend_selftest) {
02089       if (!scsiRequestSense(device, &sense_info) &&
02090             (sense_info.asc == 0x04 && sense_info.ascq == 0x09)) {
02091          if (!options.smart_selftest_force) {
02092            pout("Can't start self-test without aborting current test");
02093            if (sense_info.progress != -1) {
02094              pout(" (%d%% remaining)",
02095                   100 - sense_info.progress * 100 / 65535);
02096            }
02097            pout(",\nadd '-t force' option to override, or run 'smartctl -X' "
02098                 "to abort test.\n");
02099             return -1;
02100          }
02101          else
02102             scsiSmartSelfTestAbort(device);
02103        }
02104     }
02105     if (options.smart_short_selftest) {
02106         if (scsiSmartShortSelfTest(device))
02107             return returnval | FAILSMART;
02108         pout("Short Background Self Test has begun\n");
02109         pout("Use smartctl -X to abort test\n");
02110         any_output = true;
02111     }
02112     if (options.smart_extend_selftest) {
02113         if (scsiSmartExtendSelfTest(device))
02114             return returnval | FAILSMART;
02115         pout("Extended Background Self Test has begun\n");
02116         if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
02117                         modese_len)) && (durationSec > 0)) {
02118             time_t t = time(NULL);
02119 
02120             t += durationSec;
02121             pout("Please wait %d minutes for test to complete.\n",
02122                  durationSec / 60);
02123             pout("Estimated completion time: %s\n", ctime(&t));
02124         }
02125         pout("Use smartctl -X to abort test\n");
02126         any_output = true;
02127     }
02128     if (options.smart_extend_cap_selftest) {
02129         if (scsiSmartExtendCapSelfTest(device))
02130             return returnval | FAILSMART;
02131         pout("Extended Foreground Self Test Successful\n");
02132     }
02133     if (options.smart_selftest_abort) {
02134         if (scsiSmartSelfTestAbort(device))
02135             return returnval | FAILSMART;
02136         pout("Self Test returned without error\n");
02137         any_output = true;
02138     }
02139     if (options.sasphy && gProtocolSpecificLPage) {
02140         if (scsiPrintSasPhy(device, options.sasphy_reset))
02141             return returnval | FAILSMART;
02142         any_output = true;
02143     }
02144 
02145     if (!any_output)
02146       pout("SCSI device successfully opened\n\n"
02147            "Use 'smartctl -a' (or '-x') to print SMART (and more) information\n\n");
02148 
02149     return returnval;
02150 }