File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / smartmontools / scsiprint.cpp
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 14 07:54:04 2013 UTC (10 years, 8 months ago) by misho
Branches: smartmontools, elwix, MAIN
CVS tags: v6_2, HEAD
v 6.2

    1: /*
    2:  * scsiprint.cpp
    3:  *
    4:  * Home page of code is: http://smartmontools.sourceforge.net
    5:  *
    6:  * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
    7:  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
    8:  *
    9:  * Additional SCSI work:
   10:  * Copyright (C) 2003-13 Douglas Gilbert <dgilbert@interlog.com>
   11:  *
   12:  * This program is free software; you can redistribute it and/or modify
   13:  * it under the terms of the GNU General Public License as published by
   14:  * the Free Software Foundation; either version 2, or (at your option)
   15:  * any later version.
   16:  *
   17:  * You should have received a copy of the GNU General Public License
   18:  * (for example COPYING); if not, write to the Free Software Foundation,
   19:  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   20:  *
   21:  * This code was originally developed as a Senior Thesis by Michael Cornwell
   22:  * at the Concurrent Systems Laboratory (now part of the Storage Systems
   23:  * Research Center), Jack Baskin School of Engineering, University of
   24:  * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
   25:  *
   26:  */
   27: 
   28: 
   29: #include <stdio.h>
   30: #include <string.h>
   31: #include <fcntl.h>
   32: #include <errno.h>
   33: 
   34: #include "config.h"
   35: #include "int64.h"
   36: #include "scsicmds.h"
   37: #include "atacmds.h" // smart_command_set
   38: #include "dev_interface.h"
   39: #include "scsiprint.h"
   40: #include "smartctl.h"
   41: #include "utility.h"
   42: 
   43: #define GBUF_SIZE 65535
   44: 
   45: const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp,v 1.1.1.3 2013/10/14 07:54:04 misho Exp $"
   46:                                  SCSIPRINT_H_CVSID;
   47: 
   48: 
   49: UINT8 gBuf[GBUF_SIZE];
   50: #define LOG_RESP_LEN 252
   51: #define LOG_RESP_LONG_LEN ((62 * 256) + 252)
   52: #define LOG_RESP_TAPE_ALERT_LEN 0x144
   53: 
   54: /* Log pages supported */
   55: static int gSmartLPage = 0;     /* Informational Exceptions log page */
   56: static int gTempLPage = 0;
   57: static int gSelfTestLPage = 0;
   58: static int gStartStopLPage = 0;
   59: static int gReadECounterLPage = 0;
   60: static int gWriteECounterLPage = 0;
   61: static int gVerifyECounterLPage = 0;
   62: static int gNonMediumELPage = 0;
   63: static int gLastNErrorLPage = 0;
   64: static int gBackgroundResultsLPage = 0;
   65: static int gProtocolSpecificLPage = 0;
   66: static int gTapeAlertsLPage = 0;
   67: static int gSSMediaLPage = 0;
   68: 
   69: /* Vendor specific log pages */
   70: static int gSeagateCacheLPage = 0;
   71: static int gSeagateFactoryLPage = 0;
   72: 
   73: /* Mode pages supported */
   74: static int gIecMPage = 1;     /* N.B. assume it until we know otherwise */
   75: 
   76: /* Remember last successful mode sense/select command */
   77: static int modese_len = 0;
   78: 
   79: 
   80: static void
   81: scsiGetSupportedLogPages(scsi_device * device)
   82: {
   83:     int i, err;
   84: 
   85:     if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf,
   86:                             LOG_RESP_LEN, 0))) {
   87:         if (scsi_debugmode > 0)
   88:             pout("Log Sense for supported pages failed [%s]\n",
   89:                  scsiErrString(err));
   90:         return;
   91:     }
   92: 
   93:     for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) {
   94:         switch (gBuf[i])
   95:         {
   96:             case READ_ERROR_COUNTER_LPAGE:
   97:                 gReadECounterLPage = 1;
   98:                 break;
   99:             case WRITE_ERROR_COUNTER_LPAGE:
  100:                 gWriteECounterLPage = 1;
  101:                 break;
  102:             case VERIFY_ERROR_COUNTER_LPAGE:
  103:                 gVerifyECounterLPage = 1;
  104:                 break;
  105:             case LAST_N_ERROR_LPAGE:
  106:                 gLastNErrorLPage = 1;
  107:                 break;
  108:             case NON_MEDIUM_ERROR_LPAGE:
  109:                 gNonMediumELPage = 1;
  110:                 break;
  111:             case TEMPERATURE_LPAGE:
  112:                 gTempLPage = 1;
  113:                 break;
  114:             case STARTSTOP_CYCLE_COUNTER_LPAGE:
  115:                 gStartStopLPage = 1;
  116:                 break;
  117:             case SELFTEST_RESULTS_LPAGE:
  118:                 gSelfTestLPage = 1;
  119:                 break;
  120:             case IE_LPAGE:
  121:                 gSmartLPage = 1;
  122:                 break;
  123:             case BACKGROUND_RESULTS_LPAGE:
  124:                 gBackgroundResultsLPage = 1;
  125:                 break;
  126:             case PROTOCOL_SPECIFIC_LPAGE:
  127:                 gProtocolSpecificLPage = 1;
  128:                 break;
  129:             case TAPE_ALERTS_LPAGE:
  130:                 gTapeAlertsLPage = 1;
  131:                 break;
  132:             case SS_MEDIA_LPAGE:
  133:                 gSSMediaLPage = 1;
  134:                 break;
  135:             case SEAGATE_CACHE_LPAGE:
  136:                 gSeagateCacheLPage = 1;
  137:                 break;
  138:             case SEAGATE_FACTORY_LPAGE:
  139:                 gSeagateFactoryLPage = 1;
  140:                 break;
  141:             default:
  142:                 break;
  143:         }
  144:     }
  145: }
  146: 
  147: /* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
  148:    (or at least something to report). */
  149: static int
  150: scsiGetSmartData(scsi_device * device, bool attribs)
  151: {
  152:     UINT8 asc;
  153:     UINT8 ascq;
  154:     UINT8 currenttemp = 0;
  155:     UINT8 triptemp = 0;
  156:     const char * cp;
  157:     int err = 0;
  158:     print_on();
  159:     if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq,
  160:                     &currenttemp, &triptemp)) {
  161:         /* error message already announced */
  162:         print_off();
  163:         return -1;
  164:     }
  165:     print_off();
  166:     cp = scsiGetIEString(asc, ascq);
  167:     if (cp) {
  168:         err = -2;
  169:         print_on();
  170:         pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq);
  171:         print_off();
  172:     } else if (gIecMPage)
  173:         pout("SMART Health Status: OK\n");
  174: 
  175:     if (attribs && !gTempLPage) {
  176:         if (currenttemp) {
  177:             if (255 != currenttemp)
  178:                 pout("Current Drive Temperature:     %d C\n", currenttemp);
  179:             else
  180:                 pout("Current Drive Temperature:     <not available>\n");
  181:         }
  182:         if (triptemp)
  183:             pout("Drive Trip Temperature:        %d C\n", triptemp);
  184:     }
  185:     pout("\n");
  186:     return err;
  187: }
  188: 
  189: 
  190: // Returns number of logged errors or zero if none or -1 if fetching
  191: // TapeAlerts fails
  192: static const char * const severities = "CWI";
  193: 
  194: static int
  195: scsiGetTapeAlertsData(scsi_device * device, int peripheral_type)
  196: {
  197:     unsigned short pagelength;
  198:     unsigned short parametercode;
  199:     int i, err;
  200:     const char *s;
  201:     const char *ts;
  202:     int failures = 0;
  203: 
  204:     print_on();
  205:     if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, 0, gBuf,
  206:                         LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) {
  207:         pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err));
  208:         print_off();
  209:         return -1;
  210:     }
  211:     if (gBuf[0] != 0x2e) {
  212:         pout("TapeAlerts Log Sense Failed\n");
  213:         print_off();
  214:         return -1;
  215:     }
  216:     pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3];
  217: 
  218:     for (s=severities; *s; s++) {
  219:         for (i = 4; i < pagelength; i += 5) {
  220:             parametercode = (unsigned short) gBuf[i] << 8 | gBuf[i+1];
  221: 
  222:             if (gBuf[i + 4]) {
  223:                 ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ?
  224:                     scsiTapeAlertsChangerDevice(parametercode) :
  225:                     scsiTapeAlertsTapeDevice(parametercode);
  226:                 if (*ts == *s) {
  227:                     if (!failures)
  228:                         pout("TapeAlert Errors (C=Critical, W=Warning, "
  229:                              "I=Informational):\n");
  230:                     pout("[0x%02x] %s\n", parametercode, ts);
  231:                     failures += 1;
  232:                 }
  233:             }
  234:         }
  235:     }
  236:     print_off();
  237: 
  238:     if (! failures)
  239:         pout("TapeAlert: OK\n");
  240: 
  241:     return failures;
  242: }
  243: 
  244: static void
  245: scsiGetStartStopData(scsi_device * device)
  246: {
  247:     UINT32 u;
  248:     int err, len, k, extra, pc;
  249:     unsigned char * ucp;
  250: 
  251:     if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf,
  252:                             LOG_RESP_LEN, 0))) {
  253:         print_on();
  254:         pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err));
  255:         print_off();
  256:         return;
  257:     }
  258:     if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) {
  259:         print_on();
  260:         pout("StartStop Log Sense Failed, page mismatch\n");
  261:         print_off();
  262:         return;
  263:     }
  264:     len = ((gBuf[2] << 8) | gBuf[3]);
  265:     ucp = gBuf + 4;
  266:     for (k = len; k > 0; k -= extra, ucp += extra) {
  267:         if (k < 3) {
  268:             print_on();
  269:             pout("StartStop Log Sense Failed: short\n");
  270:             print_off();
  271:             return;
  272:         }
  273:         extra = ucp[3] + 4;
  274:         pc = (ucp[0] << 8) + ucp[1];
  275:         switch (pc) {
  276:         case 1:
  277:             if (10 == extra)
  278:                 pout("Manufactured in week %.2s of year %.4s\n", ucp + 8,
  279:                      ucp + 4);
  280:             break;
  281:         case 2:
  282:             /* ignore Accounting date */
  283:             break;
  284:         case 3:
  285:             if (extra > 7) {
  286:                 u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
  287:                 if (0xffffffff != u)
  288:                     pout("Specified cycle count over device lifetime:  %u\n",
  289:                          u);
  290:             }
  291:             break;
  292:         case 4:
  293:             if (extra > 7) {
  294:                 u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
  295:                 if (0xffffffff != u)
  296:                     pout("Accumulated start-stop cycles:  %u\n", u);
  297:             }
  298:             break;
  299:         case 5:
  300:             if (extra > 7) {
  301:                 u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
  302:                 if (0xffffffff != u)
  303:                     pout("Specified load-unload count over device "
  304:                          "lifetime:  %u\n", u);
  305:             }
  306:             break;
  307:         case 6:
  308:             if (extra > 7) {
  309:                 u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
  310:                 if (0xffffffff != u)
  311:                     pout("Accumulated load-unload cycles:  %u\n", u);
  312:             }
  313:             break;
  314:         default:
  315:             /* ignore */
  316:             break;
  317:         }
  318:     }
  319: }
  320: 
  321: static void
  322: scsiPrintGrownDefectListLen(scsi_device * device)
  323: {
  324:     int err, dl_format, got_rd12, generation;
  325:     unsigned int dl_len, div;
  326: 
  327:     memset(gBuf, 0, 8);
  328:     if ((err = scsiReadDefect12(device, 0 /* req_plist */, 1 /* req_glist */,
  329:                                 4 /* format: bytes from index */,
  330:                                 0 /* addr desc index */, gBuf, 8))) {
  331:         if (2 == err) { /* command not supported */
  332:             if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */,
  333:                                         4 /* format: bytes from index */, gBuf, 4))) {
  334:                 if (scsi_debugmode > 0) {
  335:                     print_on();
  336:                     pout("Read defect list (10) Failed: %s\n", scsiErrString(err));
  337:                     print_off();
  338:                 }
  339:                 return;
  340:             } else
  341:                 got_rd12 = 0;
  342:         } else {
  343:             if (scsi_debugmode > 0) {
  344:                 print_on();
  345:                 pout("Read defect list (12) Failed: %s\n", scsiErrString(err));
  346:                 print_off();
  347:             }
  348:             return;
  349:         }
  350:     } else
  351:         got_rd12 = 1;
  352: 
  353:     if (got_rd12) {
  354:         generation = (gBuf[2] << 8) + gBuf[3];
  355:         if ((generation > 1) && (scsi_debugmode > 0)) {
  356:             print_on();
  357:             pout("Read defect list (12): generation=%d\n", generation);
  358:             print_off();
  359:         }
  360:         dl_len = (gBuf[4] << 24) + (gBuf[5] << 16) + (gBuf[6] << 8) + gBuf[7];
  361:     } else {
  362:         dl_len = (gBuf[2] << 8) + gBuf[3];
  363:     }
  364:     if (0x8 != (gBuf[1] & 0x18)) {
  365:         print_on();
  366:         pout("Read defect list: asked for grown list but didn't get it\n");
  367:         print_off();
  368:         return;
  369:     }
  370:     div = 0;
  371:     dl_format = (gBuf[1] & 0x7);
  372:     switch (dl_format) {
  373:         case 0:     /* short block */
  374:             div = 4;
  375:             break;
  376:         case 1:     /* extended bytes from index */
  377:         case 2:     /* extended physical sector */
  378:             /* extended = 1; # might use in future */
  379:             div = 8;
  380:             break;
  381:         case 3:     /* long block */
  382:         case 4:     /* bytes from index */
  383:         case 5:     /* physical sector */
  384:             div = 8;
  385:             break;
  386:         default:
  387:             print_on();
  388:             pout("defect list format %d unknown\n", dl_format);
  389:             print_off();
  390:             break;
  391:     }
  392:     if (0 == dl_len)
  393:         pout("Elements in grown defect list: 0\n\n");
  394:     else {
  395:         if (0 == div)
  396:             pout("Grown defect list length=%u bytes [unknown "
  397:                  "number of elements]\n\n", dl_len);
  398:         else
  399:             pout("Elements in grown defect list: %u\n\n", dl_len / div);
  400:     }
  401: }
  402: 
  403: static void
  404: scsiPrintSeagateCacheLPage(scsi_device * device)
  405: {
  406:     int k, j, num, pl, pc, err, len;
  407:     unsigned char * ucp;
  408:     unsigned char * xp;
  409:     uint64_t ull;
  410: 
  411:     if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf,
  412:                             LOG_RESP_LEN, 0))) {
  413:         print_on();
  414:         pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err));
  415:         print_off();
  416:         return;
  417:     }
  418:     if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) {
  419:         print_on();
  420:         pout("Seagate Cache Log Sense Failed, page mismatch\n");
  421:         print_off();
  422:         return;
  423:     }
  424:     len = ((gBuf[2] << 8) | gBuf[3]) + 4;
  425:     num = len - 4;
  426:     ucp = &gBuf[0] + 4;
  427:     while (num > 3) {
  428:         pc = (ucp[0] << 8) | ucp[1];
  429:         pl = ucp[3] + 4;
  430:         switch (pc) {
  431:         case 0: case 1: case 2: case 3: case 4:
  432:             break;
  433:         default:
  434:             if (scsi_debugmode > 0) {
  435:                 print_on();
  436:                 pout("Vendor (Seagate) cache lpage has unexpected parameter"
  437:                      ", skip\n");
  438:                 print_off();
  439:             }
  440:             return;
  441:         }
  442:         num -= pl;
  443:         ucp += pl;
  444:     }
  445:     pout("Vendor (Seagate) cache information\n");
  446:     num = len - 4;
  447:     ucp = &gBuf[0] + 4;
  448:     while (num > 3) {
  449:         pc = (ucp[0] << 8) | ucp[1];
  450:         pl = ucp[3] + 4;
  451:         switch (pc) {
  452:         case 0: pout("  Blocks sent to initiator"); break;
  453:         case 1: pout("  Blocks received from initiator"); break;
  454:         case 2: pout("  Blocks read from cache and sent to initiator"); break;
  455:         case 3: pout("  Number of read and write commands whose size "
  456:                        "<= segment size"); break;
  457:         case 4: pout("  Number of read and write commands whose size "
  458:                        "> segment size"); break;
  459:         default: pout("  Unknown Seagate parameter code [0x%x]", pc); break;
  460:         }
  461:         k = pl - 4;
  462:         xp = ucp + 4;
  463:         if (k > (int)sizeof(ull)) {
  464:             xp += (k - (int)sizeof(ull));
  465:             k = (int)sizeof(ull);
  466:         }
  467:         ull = 0;
  468:         for (j = 0; j < k; ++j) {
  469:             if (j > 0)
  470:                 ull <<= 8;
  471:             ull |= xp[j];
  472:         }
  473:         pout(" = %"PRIu64"\n", ull);
  474:         num -= pl;
  475:         ucp += pl;
  476:     }
  477:     pout("\n");
  478: }
  479: 
  480: static void
  481: scsiPrintSeagateFactoryLPage(scsi_device * device)
  482: {
  483:     int k, j, num, pl, pc, len, err, good, bad;
  484:     unsigned char * ucp;
  485:     unsigned char * xp;
  486:     uint64_t ull;
  487: 
  488:     if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf,
  489:                             LOG_RESP_LEN, 0))) {
  490:         print_on();
  491:         pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err));
  492:         print_off();
  493:         return;
  494:     }
  495:     if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) {
  496:         print_on();
  497:         pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n");
  498:         print_off();
  499:         return;
  500:     }
  501:     len = ((gBuf[2] << 8) | gBuf[3]) + 4;
  502:     num = len - 4;
  503:     ucp = &gBuf[0] + 4;
  504:     good = 0;
  505:     bad = 0;
  506:     while (num > 3) {
  507:         pc = (ucp[0] << 8) | ucp[1];
  508:         pl = ucp[3] + 4;
  509:         switch (pc) {
  510:         case 0: case 8:
  511:             ++good;
  512:             break;
  513:         default:
  514:             ++bad;
  515:             break;
  516:         }
  517:         num -= pl;
  518:         ucp += pl;
  519:     }
  520:     if ((good < 2) || (bad > 4)) {  /* heuristic */
  521:         if (scsi_debugmode > 0) {
  522:             print_on();
  523:             pout("\nVendor (Seagate/Hitachi) factory lpage has too many "
  524:                  "unexpected parameters, skip\n");
  525:             print_off();
  526:         }
  527:         return;
  528:     }
  529:     pout("Vendor (Seagate/Hitachi) factory information\n");
  530:     num = len - 4;
  531:     ucp = &gBuf[0] + 4;
  532:     while (num > 3) {
  533:         pc = (ucp[0] << 8) | ucp[1];
  534:         pl = ucp[3] + 4;
  535:         good = 0;
  536:         switch (pc) {
  537:         case 0: pout("  number of hours powered up");
  538:             good = 1;
  539:             break;
  540:         case 8: pout("  number of minutes until next internal SMART test");
  541:             good = 1;
  542:             break;
  543:         default:
  544:             if (scsi_debugmode > 0) {
  545:                 print_on();
  546:                 pout("Vendor (Seagate/Hitachi) factory lpage: "
  547:                      "unknown parameter code [0x%x]\n", pc);
  548:                 print_off();
  549:             }
  550:             break;
  551:         }
  552:         if (good) {
  553:             k = pl - 4;
  554:             xp = ucp + 4;
  555:             if (k > (int)sizeof(ull)) {
  556:                 xp += (k - (int)sizeof(ull));
  557:                 k = (int)sizeof(ull);
  558:             }
  559:             ull = 0;
  560:             for (j = 0; j < k; ++j) {
  561:                 if (j > 0)
  562:                     ull <<= 8;
  563:                 ull |= xp[j];
  564:             }
  565:             if (0 == pc)
  566:                 pout(" = %.2f\n", ull / 60.0 );
  567:             else
  568:                 pout(" = %"PRIu64"\n", ull);
  569:         }
  570:         num -= pl;
  571:         ucp += pl;
  572:     }
  573:     pout("\n");
  574: }
  575: 
  576: static void
  577: scsiPrintErrorCounterLog(scsi_device * device)
  578: {
  579:     struct scsiErrorCounter errCounterArr[3];
  580:     struct scsiErrorCounter * ecp;
  581:     struct scsiNonMediumError nme;
  582:     int found[3] = {0, 0, 0};
  583:     const char * pageNames[3] = {"read:   ", "write:  ", "verify: "};
  584:     double processed_gb;
  585: 
  586:     if (gReadECounterLPage && (0 == scsiLogSense(device,
  587:                 READ_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
  588:         scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]);
  589:         found[0] = 1;
  590:     }
  591:     if (gWriteECounterLPage && (0 == scsiLogSense(device,
  592:                 WRITE_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
  593:         scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]);
  594:         found[1] = 1;
  595:     }
  596:     if (gVerifyECounterLPage && (0 == scsiLogSense(device,
  597:                 VERIFY_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
  598:         scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]);
  599:         ecp = &errCounterArr[2];
  600:         for (int k = 0; k < 7; ++k) {
  601:             if (ecp->gotPC[k] && ecp->counter[k]) {
  602:                 found[2] = 1;
  603:                 break;
  604:             }
  605:         }
  606:     }
  607:     if (found[0] || found[1] || found[2]) {
  608:         pout("Error counter log:\n");
  609:         pout("           Errors Corrected by           Total   "
  610:              "Correction     Gigabytes    Total\n");
  611:         pout("               ECC          rereads/    errors   "
  612:              "algorithm      processed    uncorrected\n");
  613:         pout("           fast | delayed   rewrites  corrected  "
  614:              "invocations   [10^9 bytes]  errors\n");
  615:         for (int k = 0; k < 3; ++k) {
  616:             if (! found[k])
  617:                 continue;
  618:             ecp = &errCounterArr[k];
  619:             pout("%s%8"PRIu64" %8"PRIu64"  %8"PRIu64"  %8"PRIu64"   %8"PRIu64,
  620:                  pageNames[k], ecp->counter[0], ecp->counter[1],
  621:                  ecp->counter[2], ecp->counter[3], ecp->counter[4]);
  622:             processed_gb = ecp->counter[5] / 1000000000.0;
  623:             pout("   %12.3f    %8"PRIu64"\n", processed_gb, ecp->counter[6]);
  624:         }
  625:     }
  626:     else
  627:         pout("Error Counter logging not supported\n");
  628:     if (gNonMediumELPage && (0 == scsiLogSense(device,
  629:                 NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
  630:         scsiDecodeNonMediumErrPage(gBuf, &nme);
  631:         if (nme.gotPC0)
  632:             pout("\nNon-medium error count: %8"PRIu64"\n", nme.counterPC0);
  633:         if (nme.gotTFE_H)
  634:             pout("Track following error count [Hitachi]: %8"PRIu64"\n",
  635:                  nme.counterTFE_H);
  636:         if (nme.gotPE_H)
  637:             pout("Positioning error count [Hitachi]: %8"PRIu64"\n",
  638:                  nme.counterPE_H);
  639:     }
  640:     if (gLastNErrorLPage && (0 == scsiLogSense(device,
  641:                 LAST_N_ERROR_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) {
  642:         int num = (gBuf[2] << 8) + gBuf[3] + 4;
  643:         int truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
  644:         if (truncated)
  645:             num = LOG_RESP_LONG_LEN;
  646:         unsigned char * ucp = gBuf + 4;
  647:         num -= 4;
  648:         if (num < 4)
  649:             pout("\nNo error events logged\n");
  650:         else {
  651:             pout("\nLast n error events log page\n");
  652:             for (int k = num, pl; k > 0; k -= pl, ucp += pl) {
  653:                 if (k < 3) {
  654:                     pout("  <<short Last n error events log page>>\n");
  655:                     break;
  656:                 }
  657:                 pl = ucp[3] + 4;
  658:                 int pc = (ucp[0] << 8) + ucp[1];
  659:                 if (pl > 4) {
  660:                     if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) {
  661:                         pout("  Error event %d:\n", pc);
  662:                         pout("    [binary]:\n");
  663:                         dStrHex((const char *)ucp + 4, pl - 4, 1);
  664:                     } else if (ucp[2] & 0x1) {
  665:                         pout("  Error event %d:\n", pc);
  666:                         pout("    %.*s\n", pl - 4, (const char *)(ucp + 4));
  667:                     } else {
  668:                         if (scsi_debugmode > 0) {
  669:                             pout("  Error event %d:\n", pc);
  670:                             pout("    [data counter??]:\n");
  671:                             dStrHex((const char *)ucp + 4, pl - 4, 1);
  672:                         }
  673:                     }
  674:                 }
  675:             }
  676:             if (truncated)
  677:                 pout(" >>>> log truncated, fetched %d of %d available "
  678:                      "bytes\n", LOG_RESP_LONG_LEN, truncated);
  679:         }
  680:     }
  681:     pout("\n");
  682: }
  683: 
  684: static const char * self_test_code[] = {
  685:         "Default         ",
  686:         "Background short",
  687:         "Background long ",
  688:         "Reserved(3)     ",
  689:         "Abort background",
  690:         "Foreground short",
  691:         "Foreground long ",
  692:         "Reserved(7)     "
  693: };
  694: 
  695: static const char * self_test_result[] = {
  696:         "Completed                ",
  697:         "Aborted (by user command)",
  698:         "Aborted (device reset ?) ",
  699:         "Unknown error, incomplete",
  700:         "Completed, segment failed",
  701:         "Failed in first segment  ",
  702:         "Failed in second segment ",
  703:         "Failed in segment -->    ",
  704:         "Reserved(8)              ",
  705:         "Reserved(9)              ",
  706:         "Reserved(10)             ",
  707:         "Reserved(11)             ",
  708:         "Reserved(12)             ",
  709:         "Reserved(13)             ",
  710:         "Reserved(14)             ",
  711:         "Self test in progress ..."
  712: };
  713: 
  714: // See SCSI Primary Commands - 3 (SPC-3) rev 23 (draft) section 7.2.10 .
  715: // Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent
  716: // 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
  717: // FAILSMART is returned.
  718: static int
  719: scsiPrintSelfTest(scsi_device * device)
  720: {
  721:     int num, k, n, res, err, durationSec;
  722:     int noheader = 1;
  723:     int retval = 0;
  724:     UINT8 * ucp;
  725:     uint64_t ull=0;
  726:     struct scsi_sense_disect sense_info;
  727: 
  728:     // check if test is running
  729:     if (!scsiRequestSense(device, &sense_info) &&
  730:                         (sense_info.asc == 0x04 && sense_info.ascq == 0x09 &&
  731:                         sense_info.progress != -1)) {
  732:         pout("Self-test execution status:\t\t%d%% of test remaining\n",
  733:              100 - ((sense_info.progress * 100) / 65535));
  734:     }
  735: 
  736:     if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf,
  737:                             LOG_RESP_SELF_TEST_LEN, 0))) {
  738:         print_on();
  739:         pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err));
  740:         print_off();
  741:         return FAILSMART;
  742:     }
  743:     if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
  744:         print_on();
  745:         pout("Self-test Log Sense Failed, page mismatch\n");
  746:         print_off();
  747:         return FAILSMART;
  748:     }
  749:     // compute page length
  750:     num = (gBuf[2] << 8) + gBuf[3];
  751:     // Log sense page length 0x190 bytes
  752:     if (num != 0x190) {
  753:         print_on();
  754:         pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num);
  755:         print_off();
  756:         return FAILSMART;
  757:     }
  758:     // loop through the twenty possible entries
  759:     for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) {
  760:         int i;
  761: 
  762:         // timestamp in power-on hours (or zero if test in progress)
  763:         n = (ucp[6] << 8) | ucp[7];
  764: 
  765:         // The spec says "all 20 bytes will be zero if no test" but
  766:         // DG has found otherwise.  So this is a heuristic.
  767:         if ((0 == n) && (0 == ucp[4]))
  768:             break;
  769: 
  770:         // only print header if needed
  771:         if (noheader) {
  772:             pout("SMART Self-test log\n");
  773:             pout("Num  Test              Status                 segment  "
  774:                    "LifeTime  LBA_first_err [SK ASC ASQ]\n");
  775:             pout("     Description                              number   "
  776:                    "(hours)\n");
  777:             noheader=0;
  778:         }
  779: 
  780:         // print parameter code (test number) & self-test code text
  781:         pout("#%2d  %s", (ucp[0] << 8) | ucp[1],
  782:             self_test_code[(ucp[4] >> 5) & 0x7]);
  783: 
  784:         // check the self-test result nibble, using the self-test results
  785:         // field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10:
  786:         switch ((res = ucp[4] & 0xf)) {
  787:         case 0x3:
  788:             // an unknown error occurred while the device server
  789:             // was processing the self-test and the device server
  790:             // was unable to complete the self-test
  791:             retval|=FAILSMART;
  792:             break;
  793:         case 0x4:
  794:             // the self-test completed with a failure in a test
  795:             // segment, and the test segment that failed is not
  796:             // known
  797:             retval|=FAILLOG;
  798:             break;
  799:         case 0x5:
  800:             // the first segment of the self-test failed
  801:             retval|=FAILLOG;
  802:             break;
  803:         case 0x6:
  804:             // the second segment of the self-test failed
  805:             retval|=FAILLOG;
  806:             break;
  807:         case 0x7:
  808:             // another segment of the self-test failed and which
  809:             // test is indicated by the contents of the SELF-TEST
  810:             // NUMBER field
  811:             retval|=FAILLOG;
  812:             break;
  813:         default:
  814:             break;
  815:         }
  816:         pout("  %s", self_test_result[res]);
  817: 
  818:         // self-test number identifies test that failed and consists
  819:         // of either the number of the segment that failed during
  820:         // the test, or the number of the test that failed and the
  821:         // number of the segment in which the test was run, using a
  822:         // vendor-specific method of putting both numbers into a
  823:         // single byte.
  824:         if (ucp[5])
  825:             pout(" %3d",  (int)ucp[5]);
  826:         else
  827:             pout("   -");
  828: 
  829:         // print time that the self-test was completed
  830:         if (n==0 && res==0xf)
  831:         // self-test in progress
  832:             pout("     NOW");
  833:         else
  834:             pout("   %5d", n);
  835: 
  836:         // construct 8-byte integer address of first failure
  837:         for (i = 0; i < 8; i++) {
  838:             ull <<= 8;
  839:             ull |= ucp[i+8];
  840:         }
  841:         // print Address of First Failure, if sensible
  842:         if ((~(uint64_t)0 != ull) && (res > 0) && (res < 0xf)) {
  843:             char buff[32];
  844: 
  845:             // was hex but change to decimal to conform with ATA
  846:             snprintf(buff, sizeof(buff), "%"PRIu64, ull);
  847:             // snprintf(buff, sizeof(buff), "0x%"PRIx64, ull);
  848:             pout("%18s", buff);
  849:         } else
  850:             pout("                 -");
  851: 
  852:         // if sense key nonzero, then print it, along with
  853:         // additional sense code and additional sense code qualifier
  854:         if (ucp[16] & 0xf)
  855:             pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]);
  856:         else
  857:             pout(" [-   -    -]\n");
  858:     }
  859: 
  860:     // if header never printed, then there was no output
  861:     if (noheader)
  862:         pout("No self-tests have been logged\n");
  863:     else
  864:     if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
  865:                         modese_len)) && (durationSec > 0)) {
  866:         pout("Long (extended) Self Test duration: %d seconds "
  867:              "[%.1f minutes]\n", durationSec, durationSec / 60.0);
  868:     }
  869:     pout("\n");
  870:     return retval;
  871: }
  872: 
  873: static const char * bms_status[] = {
  874:     "no scans active",
  875:     "scan is active",
  876:     "pre-scan is active",
  877:     "halted due to fatal error",
  878:     "halted due to a vendor specific pattern of error",
  879:     "halted due to medium formatted without P-List",
  880:     "halted - vendor specific cause",
  881:     "halted due to temperature out of range",
  882:     "waiting until BMS interval timer expires", /* 8 */
  883: };
  884: 
  885: static const char * reassign_status[] = {
  886:     "Reserved [0x0]",
  887:     "Require Write or Reassign Blocks command",
  888:     "Successfully reassigned",
  889:     "Reserved [0x3]",
  890:     "Reassignment by disk failed",
  891:     "Recovered via rewrite in-place",
  892:     "Reassigned by app, has valid data",
  893:     "Reassigned by app, has no valid data",
  894:     "Unsuccessfully reassigned by app", /* 8 */
  895: };
  896: 
  897: // See SCSI Block Commands - 3 (SBC-3) rev 6 (draft) section 6.2.2 .
  898: // Returns 0 if ok else FAIL* bitmask. Note can have a status entry
  899: // and up to 2048 events (although would hope to have less). May set
  900: // FAILLOG if serious errors detected (in the future).
  901: static int
  902: scsiPrintBackgroundResults(scsi_device * device)
  903: {
  904:     int num, j, m, err, pc, pl, truncated;
  905:     int noheader = 1;
  906:     int firstresult = 1;
  907:     int retval = 0;
  908:     UINT8 * ucp;
  909: 
  910:     if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf,
  911:                             LOG_RESP_LONG_LEN, 0))) {
  912:         print_on();
  913:         pout("scsiPrintBackgroundResults Failed [%s]\n", scsiErrString(err));
  914:         print_off();
  915:         return FAILSMART;
  916:     }
  917:     if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) {
  918:         print_on();
  919:         pout("Background scan results Log Sense Failed, page mismatch\n");
  920:         print_off();
  921:         return FAILSMART;
  922:     }
  923:     // compute page length
  924:     num = (gBuf[2] << 8) + gBuf[3] + 4;
  925:     if (num < 20) {
  926:         print_on();
  927:         pout("Background scan results Log Sense length is %d, no scan "
  928:              "status\n", num);
  929:         print_off();
  930:         return FAILSMART;
  931:     }
  932:     truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
  933:     if (truncated)
  934:         num = LOG_RESP_LONG_LEN;
  935:     ucp = gBuf + 4;
  936:     num -= 4;
  937:     while (num > 3) {
  938:         pc = (ucp[0] << 8) | ucp[1];
  939:         // pcb = ucp[2];
  940:         pl = ucp[3] + 4;
  941:         switch (pc) {
  942:         case 0:
  943:             if (noheader) {
  944:                 noheader = 0;
  945:                 pout("Background scan results log\n");
  946:             }
  947:             pout("  Status: ");
  948:             if ((pl < 16) || (num < 16)) {
  949:                 pout("\n");
  950:                 break;
  951:             }
  952:             j = ucp[9];
  953:             if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0])))
  954:                 pout("%s\n", bms_status[j]);
  955:             else
  956:                 pout("unknown [0x%x] background scan status value\n", j);
  957:             j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
  958:             pout("    Accumulated power on time, hours:minutes %d:%02d "
  959:                  "[%d minutes]\n", (j / 60), (j % 60), j);
  960:             pout("    Number of background scans performed: %d,  ",
  961:                  (ucp[10] << 8) + ucp[11]);
  962:             pout("scan progress: %.2f%%\n",
  963:                  (double)((ucp[12] << 8) + ucp[13]) * 100.0 / 65536.0);
  964:             pout("    Number of background medium scans performed: %d\n",
  965:                  (ucp[14] << 8) + ucp[15]);
  966:             break;
  967:         default:
  968:             if (noheader) {
  969:                 noheader = 0;
  970:                 pout("\nBackground scan results log\n");
  971:             }
  972:             if (firstresult) {
  973:                 firstresult = 0;
  974:                 pout("\n   #  when        lba(hex)    [sk,asc,ascq]    "
  975:                      "reassign_status\n");
  976:             }
  977:             pout(" %3d ", pc);
  978:             if ((pl < 24) || (num < 24)) {
  979:                 if (pl < 24)
  980:                     pout("parameter length >= 24 expected, got %d\n", pl);
  981:                 break;
  982:             }
  983:             j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
  984:             pout("%4d:%02d  ", (j / 60), (j % 60));
  985:             for (m = 0; m < 8; ++m)
  986:                 pout("%02x", ucp[16 + m]);
  987:             pout("  [%x,%x,%x]   ", ucp[8] & 0xf, ucp[9], ucp[10]);
  988:             j = (ucp[8] >> 4) & 0xf;
  989:             if (j <
  990:                 (int)(sizeof(reassign_status) / sizeof(reassign_status[0])))
  991:                 pout("%s\n", reassign_status[j]);
  992:             else
  993:                 pout("Reassign status: reserved [0x%x]\n", j);
  994:             break;
  995:         }
  996:         num -= pl;
  997:         ucp += pl;
  998:     }
  999:     if (truncated)
 1000:         pout(" >>>> log truncated, fetched %d of %d available "
 1001:              "bytes\n", LOG_RESP_LONG_LEN, truncated);
 1002:     pout("\n");
 1003:     return retval;
 1004: }
 1005: 
 1006: // See SCSI Block Commands - 3 (SBC-3) rev 27 (draft) section 6.3.6 .
 1007: // Returns 0 if ok else FAIL* bitmask. Note can have a status entry
 1008: // and up to 2048 events (although would hope to have less). May set
 1009: // FAILLOG if serious errors detected (in the future).
 1010: static int
 1011: scsiPrintSSMedia(scsi_device * device)
 1012: {
 1013:     int num, err, pc, pl, truncated;
 1014:     int retval = 0;
 1015:     UINT8 * ucp;
 1016: 
 1017:     if ((err = scsiLogSense(device, SS_MEDIA_LPAGE, 0, gBuf,
 1018:                             LOG_RESP_LONG_LEN, 0))) {
 1019:         print_on();
 1020:         pout("scsiPrintSSMedia Failed [%s]\n", scsiErrString(err));
 1021:         print_off();
 1022:         return FAILSMART;
 1023:     }
 1024:     if ((gBuf[0] & 0x3f) != SS_MEDIA_LPAGE) {
 1025:         print_on();
 1026:         pout("Solid state media Log Sense Failed, page mismatch\n");
 1027:         print_off();
 1028:         return FAILSMART;
 1029:     }
 1030:     // compute page length
 1031:     num = (gBuf[2] << 8) + gBuf[3] + 4;
 1032:     if (num < 12) {
 1033:         print_on();
 1034:         pout("Solid state media Log Sense length is %d, too short\n", num);
 1035:         print_off();
 1036:         return FAILSMART;
 1037:     }
 1038:     truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
 1039:     if (truncated)
 1040:         num = LOG_RESP_LONG_LEN;
 1041:     ucp = gBuf + 4;
 1042:     num -= 4;
 1043:     while (num > 3) {
 1044:         pc = (ucp[0] << 8) | ucp[1];
 1045:         // pcb = ucp[2];
 1046:         pl = ucp[3] + 4;
 1047:         switch (pc) {
 1048:         case 1:
 1049:             if (pl < 8) {
 1050:                 print_on();
 1051:                 pout("Percentage used endurance indicator too short (pl=%d)\n", pl);
 1052:                 print_off();
 1053:                 return FAILSMART;
 1054:             }
 1055:             pout("SS Media used endurance indicator: %d%%\n", ucp[7]);
 1056:         default:        /* ignore other parameter codes */
 1057:             break;
 1058:         }
 1059:         num -= pl;
 1060:         ucp += pl;
 1061:     }
 1062:     return retval;
 1063: }
 1064: 
 1065: static void
 1066: show_sas_phy_event_info(int peis, unsigned int val, unsigned thresh_val)
 1067: {
 1068:     unsigned int u;
 1069: 
 1070:     switch (peis) {
 1071:     case 0:
 1072:         pout("     No event\n");
 1073:         break;
 1074:     case 0x1:
 1075:         pout("     Invalid word count: %u\n", val);
 1076:         break;
 1077:     case 0x2:
 1078:         pout("     Running disparity error count: %u\n", val);
 1079:         break;
 1080:     case 0x3:
 1081:         pout("     Loss of dword synchronization count: %u\n", val);
 1082:         break;
 1083:     case 0x4:
 1084:         pout("     Phy reset problem count: %u\n", val);
 1085:         break;
 1086:     case 0x5:
 1087:         pout("     Elasticity buffer overflow count: %u\n", val);
 1088:         break;
 1089:     case 0x6:
 1090:         pout("     Received ERROR  count: %u\n", val);
 1091:         break;
 1092:     case 0x20:
 1093:         pout("     Received address frame error count: %u\n", val);
 1094:         break;
 1095:     case 0x21:
 1096:         pout("     Transmitted abandon-class OPEN_REJECT count: %u\n", val);
 1097:         break;
 1098:     case 0x22:
 1099:         pout("     Received abandon-class OPEN_REJECT count: %u\n", val);
 1100:         break;
 1101:     case 0x23:
 1102:         pout("     Transmitted retry-class OPEN_REJECT count: %u\n", val);
 1103:         break;
 1104:     case 0x24:
 1105:         pout("     Received retry-class OPEN_REJECT count: %u\n", val);
 1106:         break;
 1107:     case 0x25:
 1108:         pout("     Received AIP (WATING ON PARTIAL) count: %u\n", val);
 1109:         break;
 1110:     case 0x26:
 1111:         pout("     Received AIP (WAITING ON CONNECTION) count: %u\n", val);
 1112:         break;
 1113:     case 0x27:
 1114:         pout("     Transmitted BREAK count: %u\n", val);
 1115:         break;
 1116:     case 0x28:
 1117:         pout("     Received BREAK count: %u\n", val);
 1118:         break;
 1119:     case 0x29:
 1120:         pout("     Break timeout count: %u\n", val);
 1121:         break;
 1122:     case 0x2a:
 1123:         pout("     Connection count: %u\n", val);
 1124:         break;
 1125:     case 0x2b:
 1126:         pout("     Peak transmitted pathway blocked count: %u\n",
 1127:                val & 0xff);
 1128:         pout("         Peak value detector threshold: %u\n",
 1129:                thresh_val & 0xff);
 1130:         break;
 1131:     case 0x2c:
 1132:         u = val & 0xffff;
 1133:         if (u < 0x8000)
 1134:             pout("     Peak transmitted arbitration wait time (us): "
 1135:                    "%u\n", u);
 1136:         else
 1137:             pout("     Peak transmitted arbitration wait time (ms): "
 1138:                    "%u\n", 33 + (u - 0x8000));
 1139:         u = thresh_val & 0xffff;
 1140:         if (u < 0x8000)
 1141:             pout("         Peak value detector threshold (us): %u\n",
 1142:                    u);
 1143:         else
 1144:             pout("         Peak value detector threshold (ms): %u\n",
 1145:                    33 + (u - 0x8000));
 1146:         break;
 1147:     case 0x2d:
 1148:         pout("     Peak arbitration time (us): %u\n", val);
 1149:         pout("         Peak value detector threshold: %u\n", thresh_val);
 1150:         break;
 1151:     case 0x2e:
 1152:         pout("     Peak connection time (us): %u\n", val);
 1153:         pout("         Peak value detector threshold: %u\n", thresh_val);
 1154:         break;
 1155:     case 0x40:
 1156:         pout("     Transmitted SSP frame count: %u\n", val);
 1157:         break;
 1158:     case 0x41:
 1159:         pout("     Received SSP frame count: %u\n", val);
 1160:         break;
 1161:     case 0x42:
 1162:         pout("     Transmitted SSP frame error count: %u\n", val);
 1163:         break;
 1164:     case 0x43:
 1165:         pout("     Received SSP frame error count: %u\n", val);
 1166:         break;
 1167:     case 0x44:
 1168:         pout("     Transmitted CREDIT_BLOCKED count: %u\n", val);
 1169:         break;
 1170:     case 0x45:
 1171:         pout("     Received CREDIT_BLOCKED count: %u\n", val);
 1172:         break;
 1173:     case 0x50:
 1174:         pout("     Transmitted SATA frame count: %u\n", val);
 1175:         break;
 1176:     case 0x51:
 1177:         pout("     Received SATA frame count: %u\n", val);
 1178:         break;
 1179:     case 0x52:
 1180:         pout("     SATA flow control buffer overflow count: %u\n", val);
 1181:         break;
 1182:     case 0x60:
 1183:         pout("     Transmitted SMP frame count: %u\n", val);
 1184:         break;
 1185:     case 0x61:
 1186:         pout("     Received SMP frame count: %u\n", val);
 1187:         break;
 1188:     case 0x63:
 1189:         pout("     Received SMP frame error count: %u\n", val);
 1190:         break;
 1191:     default:
 1192:         break;
 1193:     }
 1194: }
 1195: 
 1196: static void
 1197: show_sas_port_param(unsigned char * ucp, int param_len)
 1198: {
 1199:     int j, m, n, nphys, t, sz, spld_len;
 1200:     unsigned char * vcp;
 1201:     uint64_t ull;
 1202:     unsigned int ui;
 1203:     char s[64];
 1204: 
 1205:     sz = sizeof(s);
 1206:     // pcb = ucp[2];
 1207:     t = (ucp[0] << 8) | ucp[1];
 1208:     pout("relative target port id = %d\n", t);
 1209:     pout("  generation code = %d\n", ucp[6]);
 1210:     nphys = ucp[7];
 1211:     pout("  number of phys = %d\n", nphys);
 1212: 
 1213:     for (j = 0, vcp = ucp + 8; j < (param_len - 8);
 1214:          vcp += spld_len, j += spld_len) {
 1215:         pout("  phy identifier = %d\n", vcp[1]);
 1216:         spld_len = vcp[3];
 1217:         if (spld_len < 44)
 1218:             spld_len = 48;      /* in SAS-1 and SAS-1.1 vcp[3]==0 */
 1219:         else
 1220:             spld_len += 4;
 1221:         t = ((0x70 & vcp[4]) >> 4);
 1222:         switch (t) {
 1223:         case 0: snprintf(s, sz, "no device attached"); break;
 1224:         case 1: snprintf(s, sz, "end device"); break;
 1225:         case 2: snprintf(s, sz, "expander device"); break;
 1226:         case 3: snprintf(s, sz, "expander device (fanout)"); break;
 1227:         default: snprintf(s, sz, "reserved [%d]", t); break;
 1228:         }
 1229:         pout("    attached device type: %s\n", s);
 1230:         t = 0xf & vcp[4];
 1231:         switch (t) {
 1232:         case 0: snprintf(s, sz, "unknown"); break;
 1233:         case 1: snprintf(s, sz, "power on"); break;
 1234:         case 2: snprintf(s, sz, "hard reset"); break;
 1235:         case 3: snprintf(s, sz, "SMP phy control function"); break;
 1236:         case 4: snprintf(s, sz, "loss of dword synchronization"); break;
 1237:         case 5: snprintf(s, sz, "mux mix up"); break;
 1238:         case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
 1239:             break;
 1240:         case 7: snprintf(s, sz, "break timeout timer expired"); break;
 1241:         case 8: snprintf(s, sz, "phy test function stopped"); break;
 1242:         case 9: snprintf(s, sz, "expander device reduced functionality");
 1243:              break;
 1244:         default: snprintf(s, sz, "reserved [0x%x]", t); break;
 1245:         }
 1246:         pout("    attached reason: %s\n", s);
 1247:         t = (vcp[5] & 0xf0) >> 4;
 1248:         switch (t) {
 1249:         case 0: snprintf(s, sz, "unknown"); break;
 1250:         case 1: snprintf(s, sz, "power on"); break;
 1251:         case 2: snprintf(s, sz, "hard reset"); break;
 1252:         case 3: snprintf(s, sz, "SMP phy control function"); break;
 1253:         case 4: snprintf(s, sz, "loss of dword synchronization"); break;
 1254:         case 5: snprintf(s, sz, "mux mix up"); break;
 1255:         case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
 1256:             break;
 1257:         case 7: snprintf(s, sz, "break timeout timer expired"); break;
 1258:         case 8: snprintf(s, sz, "phy test function stopped"); break;
 1259:         case 9: snprintf(s, sz, "expander device reduced functionality");
 1260:              break;
 1261:         default: snprintf(s, sz, "reserved [0x%x]", t); break;
 1262:         }
 1263:         pout("    reason: %s\n", s);
 1264:         t = (0xf & vcp[5]);
 1265:         switch (t) {
 1266:         case 0: snprintf(s, sz, "phy enabled; unknown");
 1267:                      break;
 1268:         case 1: snprintf(s, sz, "phy disabled"); break;
 1269:         case 2: snprintf(s, sz, "phy enabled; speed negotiation failed");
 1270:                      break;
 1271:         case 3: snprintf(s, sz, "phy enabled; SATA spinup hold state");
 1272:                      break;
 1273:         case 4: snprintf(s, sz, "phy enabled; port selector");
 1274:                      break;
 1275:         case 5: snprintf(s, sz, "phy enabled; reset in progress");
 1276:                      break;
 1277:         case 6: snprintf(s, sz, "phy enabled; unsupported phy attached");
 1278:                      break;
 1279:         case 8: snprintf(s, sz, "phy enabled; 1.5 Gbps"); break;
 1280:         case 9: snprintf(s, sz, "phy enabled; 3 Gbps"); break;
 1281:         case 0xa: snprintf(s, sz, "phy enabled; 6 Gbps"); break;
 1282:         default: snprintf(s, sz, "reserved [%d]", t); break;
 1283:         }
 1284:         pout("    negotiated logical link rate: %s\n", s);
 1285:         pout("    attached initiator port: ssp=%d stp=%d smp=%d\n",
 1286:                !! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2));
 1287:         pout("    attached target port: ssp=%d stp=%d smp=%d\n",
 1288:                !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2));
 1289:         for (n = 0, ull = vcp[8]; n < 8; ++n) {
 1290:             ull <<= 8; ull |= vcp[8 + n];
 1291:         }
 1292:         pout("    SAS address = 0x%" PRIx64 "\n", ull);
 1293:         for (n = 0, ull = vcp[16]; n < 8; ++n) {
 1294:             ull <<= 8; ull |= vcp[16 + n];
 1295:         }
 1296:         pout("    attached SAS address = 0x%" PRIx64 "\n", ull);
 1297:         pout("    attached phy identifier = %d\n", vcp[24]);
 1298:         ui = (vcp[32] << 24) | (vcp[33] << 16) | (vcp[34] << 8) | vcp[35];
 1299:         pout("    Invalid DWORD count = %u\n", ui);
 1300:         ui = (vcp[36] << 24) | (vcp[37] << 16) | (vcp[38] << 8) | vcp[39];
 1301:         pout("    Running disparity error count = %u\n", ui);
 1302:         ui = (vcp[40] << 24) | (vcp[41] << 16) | (vcp[42] << 8) | vcp[43];
 1303:         pout("    Loss of DWORD synchronization = %u\n", ui);
 1304:         ui = (vcp[44] << 24) | (vcp[45] << 16) | (vcp[46] << 8) | vcp[47];
 1305:         pout("    Phy reset problem = %u\n", ui);
 1306:         if (spld_len > 51) {
 1307:             int num_ped, peis;
 1308:             unsigned char * xcp;
 1309:             unsigned int pvdt;
 1310: 
 1311:             num_ped = vcp[51];
 1312:             if (num_ped > 0)
 1313:                pout("    Phy event descriptors:\n");
 1314:             xcp = vcp + 52;
 1315:             for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) {
 1316:                 peis = xcp[3];
 1317:                 ui = (xcp[4] << 24) | (xcp[5] << 16) | (xcp[6] << 8) |
 1318:                      xcp[7];
 1319:                 pvdt = (xcp[8] << 24) | (xcp[9] << 16) | (xcp[10] << 8) |
 1320:                        xcp[11];
 1321:                 show_sas_phy_event_info(peis, ui, pvdt);
 1322:             }
 1323:         }
 1324:     }
 1325: }
 1326: 
 1327: // Returns 1 if okay, 0 if non SAS descriptors
 1328: static int
 1329: show_protocol_specific_page(unsigned char * resp, int len)
 1330: {
 1331:     int k, num, param_len;
 1332:     unsigned char * ucp;
 1333: 
 1334:     num = len - 4;
 1335:     for (k = 0, ucp = resp + 4; k < num; ) {
 1336:         param_len = ucp[3] + 4;
 1337:         if (6 != (0xf & ucp[4]))
 1338:             return 0;   /* only decode SAS log page */
 1339:         if (0 == k)
 1340:             pout("Protocol Specific port log page for SAS SSP\n");
 1341:         show_sas_port_param(ucp, param_len);
 1342:         k += param_len;
 1343:         ucp += param_len;
 1344:     }
 1345:     pout("\n");
 1346:     return 1;
 1347: }
 1348: 
 1349: 
 1350: // See Serial Attached SCSI (SAS-2) (e.g. revision 16) the Protocol Specific
 1351: // log pageSerial Attached SCSI (SAS-2) (e.g. revision 16) the Protocol
 1352: // Specific log page.
 1353: // Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent
 1354: // 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
 1355: // FAILSMART is returned.
 1356: static int
 1357: scsiPrintSasPhy(scsi_device * device, int reset)
 1358: {
 1359:     int num, err;
 1360: 
 1361:     if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf,
 1362:                             LOG_RESP_LONG_LEN, 0))) {
 1363:         print_on();
 1364:         pout("scsiPrintSasPhy Log Sense Failed [%s]\n\n", scsiErrString(err));
 1365:         print_off();
 1366:         return FAILSMART;
 1367:     }
 1368:     if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) {
 1369:         print_on();
 1370:         pout("Protocol specific Log Sense Failed, page mismatch\n\n");
 1371:         print_off();
 1372:         return FAILSMART;
 1373:     }
 1374:     // compute page length
 1375:     num = (gBuf[2] << 8) + gBuf[3];
 1376:     if (1 != show_protocol_specific_page(gBuf, num + 4)) {
 1377:         print_on();
 1378:         pout("Only support protocol specific log page on SAS devices\n\n");
 1379:         print_off();
 1380:         return FAILSMART;
 1381:     }
 1382:     if (reset) {
 1383:         if ((err = scsiLogSelect(device, 1 /* pcr */, 0 /* sp */, 0 /* pc */,
 1384:                                  PROTOCOL_SPECIFIC_LPAGE, 0, NULL, 0))) {
 1385:             print_on();
 1386:             pout("scsiPrintSasPhy Log Select (reset) Failed [%s]\n\n",
 1387:                  scsiErrString(err));
 1388:             print_off();
 1389:             return FAILSMART;
 1390:         }
 1391:     }
 1392:     return 0;
 1393: }
 1394: 
 1395: 
 1396: static const char * peripheral_dt_arr[] = {
 1397:         "disk",
 1398:         "tape",
 1399:         "printer",
 1400:         "processor",
 1401:         "optical disk(4)",
 1402:         "CD/DVD",
 1403:         "scanner",
 1404:         "optical disk(7)",
 1405:         "medium changer",
 1406:         "communications",
 1407:         "graphics(10)",
 1408:         "graphics(11)",
 1409:         "storage array",
 1410:         "enclosure",
 1411:         "simplified disk",
 1412:         "optical card reader"
 1413: };
 1414: 
 1415: static const char * transport_proto_arr[] = {
 1416:         "Fibre channel (FCP-2)",
 1417:         "Parallel SCSI (SPI-4)",
 1418:         "SSA",
 1419:         "IEEE 1394 (SBP-2)",
 1420:         "RDMA (SRP)",
 1421:         "iSCSI",
 1422:         "SAS",
 1423:         "ADT",
 1424:         "0x8",
 1425:         "0x9",
 1426:         "0xa",
 1427:         "0xb",
 1428:         "0xc",
 1429:         "0xd",
 1430:         "0xe",
 1431:         "0xf"
 1432: };
 1433: 
 1434: /* Returns 0 on success, 1 on general error and 2 for early, clean exit */
 1435: static int
 1436: scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
 1437: {
 1438:     char timedatetz[DATEANDEPOCHLEN];
 1439:     struct scsi_iec_mode_page iec;
 1440:     int err, iec_err, len, req_len, avail_len, n;
 1441:     int is_tape = 0;
 1442:     int peri_dt = 0;
 1443:     int returnval = 0;
 1444:     int transport = -1;
 1445:     int form_factor = 0;
 1446:     int protect = 0;
 1447: 
 1448:     memset(gBuf, 0, 96);
 1449:     req_len = 36;
 1450:     if ((err = scsiStdInquiry(device, gBuf, req_len))) {
 1451:         print_on();
 1452:         pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err));
 1453:         pout("Retrying with a 64 byte Standard Inquiry\n");
 1454:         print_off();
 1455:         /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
 1456:         req_len = 64;
 1457:         if ((err = scsiStdInquiry(device, gBuf, req_len))) {
 1458:             print_on();
 1459:             pout("Standard Inquiry (64 bytes) failed [%s]\n",
 1460:                  scsiErrString(err));
 1461:             print_off();
 1462:             return 1;
 1463:         }
 1464:     }
 1465:     avail_len = gBuf[4] + 5;
 1466:     len = (avail_len < req_len) ? avail_len : req_len;
 1467:     peri_dt = gBuf[0] & 0x1f;
 1468:     if (peripheral_type)
 1469:         *peripheral_type = peri_dt;
 1470: 
 1471:     if (len < 36) {
 1472:         print_on();
 1473:         pout("Short INQUIRY response, skip product id\n");
 1474:         print_off();
 1475:         return 1;
 1476:     }
 1477: 
 1478:     if (all && (0 != strncmp((char *)&gBuf[8], "ATA", 3))) {
 1479:         char vendor[8+1], product[16+1], revision[4+1];
 1480:         scsi_format_id_string(vendor, (const unsigned char *)&gBuf[8], 8);
 1481:         scsi_format_id_string(product, (const unsigned char *)&gBuf[16], 16);
 1482:         scsi_format_id_string(revision, (const unsigned char *)&gBuf[32], 4);
 1483: 
 1484:         pout("=== START OF INFORMATION SECTION ===\n");
 1485:         pout("Vendor:               %.8s\n", vendor);
 1486:         pout("Product:              %.16s\n", product);
 1487:         if (gBuf[32] >= ' ')
 1488:             pout("Revision:             %.4s\n", revision);
 1489:     }
 1490: 
 1491:     if (!*device->get_req_type()/*no type requested*/ &&
 1492:                (0 == strncmp((char *)&gBuf[8], "ATA", 3))) {
 1493:         pout("\nProbable ATA device behind a SAT layer\n"
 1494:              "Try an additional '-d ata' or '-d sat' argument.\n");
 1495:         return 2;
 1496:     }
 1497:     if (! all)
 1498:         return 0;
 1499: 
 1500:     protect = gBuf[5] & 0x1;    /* from and including SPC-3 */
 1501: 
 1502:     if (! is_tape) {    /* only do this for disks */
 1503:         unsigned int lb_size = 0;
 1504:         unsigned char lb_prov_resp[8];
 1505:         char cap_str[64];
 1506:         char si_str[64];
 1507:         char lb_str[16];
 1508:         int lb_per_pb_exp = 0;
 1509:         uint64_t capacity = scsiGetSize(device, &lb_size, &lb_per_pb_exp);
 1510: 
 1511:         if (capacity) {
 1512:             format_with_thousands_sep(cap_str, sizeof(cap_str), capacity);
 1513:             format_capacity(si_str, sizeof(si_str), capacity);
 1514:             pout("User Capacity:        %s bytes [%s]\n", cap_str, si_str);
 1515:             snprintf(lb_str, sizeof(lb_str) - 1, "%u", lb_size);
 1516:             pout("Logical block size:   %s bytes\n", lb_str);
 1517:         }
 1518:         int lbpme = -1;
 1519:         int lbprz = -1;
 1520:         if (protect || lb_per_pb_exp) {
 1521:             unsigned char rc16_12[20] = {0, };
 1522: 
 1523:             if (0 == scsiGetProtPBInfo(device, rc16_12)) {
 1524:                 lb_per_pb_exp = rc16_12[1] & 0xf;       /* just in case */
 1525:                 if (lb_per_pb_exp > 0) {
 1526:                     snprintf(lb_str, sizeof(lb_str) - 1, "%u",
 1527:                              (lb_size * (1 << lb_per_pb_exp)));
 1528:                     pout("Physical block size:  %s bytes\n", lb_str);
 1529:                     n = ((rc16_12[2] & 0x3f) << 8) + rc16_12[3];
 1530:                     pout("Lowest aligned LBA:   %d\n", n);
 1531:                 }
 1532:                 if (rc16_12[0] & 0x1) { /* PROT_EN set */
 1533:                     int p_type = ((rc16_12[0] >> 1) & 0x7);
 1534: 
 1535:                     switch (p_type) {
 1536:                     case 0 :
 1537:                         pout("Formatted with type 1 protection\n");
 1538:                         break;
 1539:                     case 1 :
 1540:                         pout("Formatted with type 2 protection\n");
 1541:                         break;
 1542:                     case 2 :
 1543:                         pout("Formatted with type 3 protection\n");
 1544:                         break;
 1545:                     default:
 1546:                         pout("Formatted with unknown protection type [%d]\n",
 1547:                              p_type);
 1548:                         break;
 1549:                     }
 1550:                     int p_i_exp = ((rc16_12[1] >> 4) & 0xf);
 1551: 
 1552:                     if (p_i_exp > 0)
 1553:                         pout("%d protection information intervals per "
 1554:                              "logical block\n", (1 << p_i_exp));
 1555:                 }
 1556:                 /* Pick up some LB provisioning info since its available */
 1557:                 lbpme = !! (rc16_12[2] & 0x80);
 1558:                 lbprz = !! (rc16_12[2] & 0x40);
 1559:             }
 1560:         }
 1561:         if (0 == scsiInquiryVpd(device, SCSI_VPD_LOGICAL_BLOCK_PROVISIONING,
 1562:                                 lb_prov_resp, sizeof(lb_prov_resp))) {
 1563:             int prov_type = lb_prov_resp[6] & 0x7;
 1564: 
 1565:             if (-1 == lbprz)
 1566:                 lbprz = !! (lb_prov_resp[5] & 0x4);
 1567:             switch (prov_type) {
 1568:             case 0:
 1569:                 pout("Logical block provisioning type unreported, "
 1570:                      "LBPME=%d, LBPRZ=%d\n", lbpme, lbprz);
 1571:                 break;
 1572:             case 1:
 1573:                 pout("LU is resource provisioned, LBPRZ=%d\n", lbprz);
 1574:                 break;
 1575:             case 2:
 1576:                 pout("LU is thin provisioned, LBPRZ=%d\n", lbprz);
 1577:                 break;
 1578:             default:
 1579:                 pout("LU provisioning type reserved [%d], LBPRZ=%d\n",
 1580:                      prov_type, lbprz);
 1581:                 break;
 1582:             }
 1583:         } else if (1 == lbpme)
 1584:             pout("Logical block provisioning enabled, LBPRZ=%d\n", lbprz);
 1585: 
 1586:         int rpm = scsiGetRPM(device, modese_len, &form_factor);
 1587:         if (rpm > 0) {
 1588:             if (1 == rpm)
 1589:                 pout("Rotation Rate:        Solid State Device\n");
 1590:             else
 1591:                 pout("Rotation Rate:        %d rpm\n", rpm);
 1592:         }
 1593:         if (form_factor > 0) {
 1594:             const char * cp = NULL;
 1595: 
 1596:             switch (form_factor) {
 1597:             case 1:
 1598:                 cp = "5.25";
 1599:                 break;
 1600:             case 2:
 1601:                 cp = "3.5";
 1602:                 break;
 1603:             case 3:
 1604:                 cp = "2.5";
 1605:                 break;
 1606:             case 4:
 1607:                 cp = "1.8";
 1608:                 break;
 1609:             case 5:
 1610:                 cp = "< 1.8";
 1611:                 break;
 1612:             }
 1613:             if (cp)
 1614:                 pout("Form Factor:          %s inches\n", cp);
 1615:         }
 1616:     }
 1617: 
 1618:     /* Do this here to try and detect badly conforming devices (some USB
 1619:        keys) that will lock up on a InquiryVpd or log sense or ... */
 1620:     if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) {
 1621:         if (SIMPLE_ERR_BAD_RESP == iec_err) {
 1622:             pout(">> Terminate command early due to bad response to IEC "
 1623:                  "mode page\n");
 1624:             print_off();
 1625:             gIecMPage = 0;
 1626:             return 1;
 1627:         }
 1628:     } else
 1629:         modese_len = iec.modese_len;
 1630: 
 1631:     if (! dont_print_serial_number) {
 1632:         if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_DEVICE_IDENTIFICATION,
 1633:                                        gBuf, 252))) {
 1634:             char s[256];
 1635: 
 1636:             len = gBuf[3];
 1637:             scsi_decode_lu_dev_id(gBuf + 4, len, s, sizeof(s), &transport);
 1638:             if (strlen(s) > 0)
 1639:                 pout("Logical Unit id:      %s\n", s);
 1640:         } else if (scsi_debugmode > 0) {
 1641:             print_on();
 1642:             if (SIMPLE_ERR_BAD_RESP == err)
 1643:                 pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
 1644:             else
 1645:                 pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
 1646:             print_off();
 1647:         }
 1648:         if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_UNIT_SERIAL_NUMBER,
 1649:                                        gBuf, 252))) {
 1650:             char serial[256];
 1651:             len = gBuf[3];
 1652: 
 1653:             gBuf[4 + len] = '\0';
 1654:             scsi_format_id_string(serial, &gBuf[4], len);
 1655:             pout("Serial number:        %s\n", serial);
 1656:         } else if (scsi_debugmode > 0) {
 1657:             print_on();
 1658:             if (SIMPLE_ERR_BAD_RESP == err)
 1659:                 pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
 1660:             else
 1661:                 pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
 1662:             print_off();
 1663:         }
 1664:     }
 1665: 
 1666:     // print SCSI peripheral device type
 1667:     if (peri_dt < (int)(sizeof(peripheral_dt_arr) /
 1668:                         sizeof(peripheral_dt_arr[0])))
 1669:         pout("Device type:          %s\n", peripheral_dt_arr[peri_dt]);
 1670:     else
 1671:         pout("Device type:          <%d>\n", peri_dt);
 1672: 
 1673:     // See if transport protocol is known
 1674:     if (transport < 0)
 1675:         transport = scsiFetchTransportProtocol(device, modese_len);
 1676:     if ((transport >= 0) && (transport <= 0xf))
 1677:         pout("Transport protocol:   %s\n", transport_proto_arr[transport]);
 1678: 
 1679:     // print current time and date and timezone
 1680:     dateandtimezone(timedatetz);
 1681:     pout("Local Time is:        %s\n", timedatetz);
 1682: 
 1683:     if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) ||
 1684:         (SCSI_PT_MEDIUM_CHANGER == *peripheral_type))
 1685:         is_tape = 1;
 1686:     // See if unit accepts SCSI commmands from us
 1687:     if ((err = scsiTestUnitReady(device))) {
 1688:         if (SIMPLE_ERR_NOT_READY == err) {
 1689:             print_on();
 1690:             if (!is_tape)
 1691:                 pout("device is NOT READY (e.g. spun down, busy)\n");
 1692:             else
 1693:                 pout("device is NOT READY (e.g. no tape)\n");
 1694:             print_off();
 1695:          } else if (SIMPLE_ERR_NO_MEDIUM == err) {
 1696:             print_on();
 1697:             pout("NO MEDIUM present on device\n");
 1698:             print_off();
 1699:          } else if (SIMPLE_ERR_BECOMING_READY == err) {
 1700:             print_on();
 1701:             pout("device becoming ready (wait)\n");
 1702:             print_off();
 1703:         } else {
 1704:             print_on();
 1705:             pout("device Test Unit Ready  [%s]\n", scsiErrString(err));
 1706:             print_off();
 1707:         }
 1708:         failuretest(MANDATORY_CMD, returnval|=FAILID);
 1709:     }
 1710: 
 1711:     if (iec_err) {
 1712:         if (!is_tape) {
 1713:             print_on();
 1714:             pout("SMART support is:     Unavailable - device lacks SMART capability.\n");
 1715:             if (scsi_debugmode > 0)
 1716:                 pout(" [%s]\n", scsiErrString(iec_err));
 1717:             print_off();
 1718:         }
 1719:         gIecMPage = 0;
 1720:         return 0;
 1721:     }
 1722: 
 1723:     if (!is_tape)
 1724:         pout("SMART support is:     Available - device has SMART capability.\n"
 1725:              "SMART support is:     %s\n",
 1726:              (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled");
 1727:     pout("%s\n", (scsi_IsWarningEnabled(&iec)) ?
 1728:                   "Temperature Warning:  Enabled" :
 1729:                   "Temperature Warning:  Disabled or Not Supported");
 1730:     return 0;
 1731: }
 1732: 
 1733: static int
 1734: scsiSmartEnable(scsi_device * device)
 1735: {
 1736:     struct scsi_iec_mode_page iec;
 1737:     int err;
 1738: 
 1739:     if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
 1740:         print_on();
 1741:         pout("unable to fetch IEC (SMART) mode page [%s]\n",
 1742:              scsiErrString(err));
 1743:         print_off();
 1744:         return 1;
 1745:     } else
 1746:         modese_len = iec.modese_len;
 1747: 
 1748:     if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) {
 1749:         print_on();
 1750:         pout("unable to enable Exception control and warning [%s]\n",
 1751:              scsiErrString(err));
 1752:         print_off();
 1753:         return 1;
 1754:     }
 1755:     /* Need to refetch 'iec' since could be modified by previous call */
 1756:     if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
 1757:         pout("unable to fetch IEC (SMART) mode page [%s]\n",
 1758:              scsiErrString(err));
 1759:         return 1;
 1760:     } else
 1761:         modese_len = iec.modese_len;
 1762: 
 1763:     pout("Informational Exceptions (SMART) %s\n",
 1764:          scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
 1765:     pout("Temperature warning %s\n",
 1766:          scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
 1767:     return 0;
 1768: }
 1769: 
 1770: static int
 1771: scsiSmartDisable(scsi_device * device)
 1772: {
 1773:     struct scsi_iec_mode_page iec;
 1774:     int err;
 1775: 
 1776:     if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
 1777:         print_on();
 1778:         pout("unable to fetch IEC (SMART) mode page [%s]\n",
 1779:              scsiErrString(err));
 1780:         print_off();
 1781:         return 1;
 1782:     } else
 1783:         modese_len = iec.modese_len;
 1784: 
 1785:     if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) {
 1786:         print_on();
 1787:         pout("unable to disable Exception control and warning [%s]\n",
 1788:              scsiErrString(err));
 1789:         print_off();
 1790:         return 1;
 1791:     }
 1792:     /* Need to refetch 'iec' since could be modified by previous call */
 1793:     if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
 1794:         pout("unable to fetch IEC (SMART) mode page [%s]\n",
 1795:              scsiErrString(err));
 1796:         return 1;
 1797:     } else
 1798:         modese_len = iec.modese_len;
 1799: 
 1800:     pout("Informational Exceptions (SMART) %s\n",
 1801:          scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
 1802:     pout("Temperature warning %s\n",
 1803:          scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
 1804:     return 0;
 1805: }
 1806: 
 1807: static void
 1808: scsiPrintTemp(scsi_device * device)
 1809: {
 1810:     UINT8 temp = 0;
 1811:     UINT8 trip = 0;
 1812: 
 1813:     if (scsiGetTemp(device, &temp, &trip))
 1814:         return;
 1815: 
 1816:     if (temp) {
 1817:         if (255 != temp)
 1818:             pout("Current Drive Temperature:     %d C\n", temp);
 1819:         else
 1820:             pout("Current Drive Temperature:     <not available>\n");
 1821:     }
 1822:     if (trip)
 1823:         pout("Drive Trip Temperature:        %d C\n", trip);
 1824:     if (temp || trip)
 1825:         pout("\n");
 1826: }
 1827: 
 1828: /* Main entry point used by smartctl command. Return 0 for success */
 1829: int
 1830: scsiPrintMain(scsi_device * device, const scsi_print_options & options)
 1831: {
 1832:     int checkedSupportedLogPages = 0;
 1833:     UINT8 peripheral_type = 0;
 1834:     int returnval = 0;
 1835:     int res, durationSec;
 1836:     struct scsi_sense_disect sense_info;
 1837: 
 1838:     bool any_output = options.drive_info;
 1839: 
 1840:     if (supported_vpd_pages_p) {
 1841:         delete supported_vpd_pages_p;
 1842:         supported_vpd_pages_p = NULL;
 1843:     }
 1844:     supported_vpd_pages_p = new supported_vpd_pages(device);
 1845: 
 1846:     res = scsiGetDriveInfo(device, &peripheral_type, options.drive_info);
 1847:     if (res) {
 1848:         if (2 == res)
 1849:             return 0;
 1850:         else
 1851:             failuretest(MANDATORY_CMD, returnval |= FAILID);
 1852:         any_output = true;
 1853:     }
 1854: 
 1855:   // Print read look-ahead status for disks
 1856:   short int wce = -1, rcd = -1;
 1857:   if (options.get_rcd || options.get_wce) {
 1858:     if (SCSI_PT_DIRECT_ACCESS == peripheral_type)
 1859:        res = scsiGetSetCache(device, modese_len, &wce, &rcd);
 1860:     else
 1861:        res = -1; // fetch for disks only
 1862:     any_output = true;
 1863:   }
 1864: 
 1865:   if (options.get_rcd) {
 1866:     pout("Read Cache is:        %s\n",
 1867:       res ? "Unavailable" : // error
 1868:       rcd ? "Disabled" : "Enabled");
 1869:    }
 1870: 
 1871:   if (options.get_wce) {
 1872:     pout("Writeback Cache is:   %s\n",
 1873:       res ? "Unavailable" : // error
 1874:       !wce ? "Disabled" : "Enabled");
 1875:    }
 1876:    if (options.drive_info)
 1877:      pout("\n");
 1878: 
 1879:   // START OF THE ENABLE/DISABLE SECTION OF THE CODE
 1880:   if (   options.smart_disable           || options.smart_enable
 1881:       || options.smart_auto_save_disable || options.smart_auto_save_enable)
 1882:     pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
 1883: 
 1884:     if (options.smart_enable) {
 1885:         if (scsiSmartEnable(device))
 1886:             failuretest(MANDATORY_CMD, returnval |= FAILSMART);
 1887:         any_output = true;
 1888:     }
 1889: 
 1890:     if (options.smart_disable) {
 1891:         if (scsiSmartDisable(device))
 1892:             failuretest(MANDATORY_CMD,returnval |= FAILSMART);
 1893:         any_output = true;
 1894:     }
 1895: 
 1896:     if (options.smart_auto_save_enable) {
 1897:       if (scsiSetControlGLTSD(device, 0, modese_len)) {
 1898:         pout("Enable autosave (clear GLTSD bit) failed\n");
 1899:         failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
 1900:       }
 1901:       else {
 1902:          pout("Autosave enabled (GLTSD bit set).\n");
 1903:       }
 1904:       any_output = true;
 1905:     }
 1906: 
 1907:     // Enable/Disable write cache
 1908:     if (options.set_wce && SCSI_PT_DIRECT_ACCESS == peripheral_type) {
 1909:       short int enable = wce = (options.set_wce > 0);
 1910:       rcd = -1;
 1911:       if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
 1912:           pout("Write cache %sable failed: %s\n", (enable ? "en" : "dis"),
 1913:                device->get_errmsg());
 1914:           failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
 1915:       }
 1916:       else
 1917:         pout("Write cache %sabled\n", (enable ? "en" : "dis"));
 1918:       any_output = true;
 1919:     }
 1920: 
 1921:     // Enable/Disable read cache
 1922:     if (options.set_rcd && SCSI_PT_DIRECT_ACCESS == peripheral_type) {
 1923:       short int enable =  (options.set_rcd > 0);
 1924:       rcd = !enable;
 1925:       wce = -1;
 1926:       if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
 1927:           pout("Read cache %sable failed: %s\n", (enable ? "en" : "dis"),
 1928:                 device->get_errmsg());
 1929:           failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
 1930:       }
 1931:       else
 1932:         pout("Read cache %sabled\n", (enable ? "en" : "dis"));
 1933:       any_output = true;
 1934:     }
 1935: 
 1936:     if (options.smart_auto_save_disable) {
 1937:       if (scsiSetControlGLTSD(device, 1, modese_len)) {
 1938:         pout("Disable autosave (set GLTSD bit) failed\n");
 1939:         failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
 1940:       }
 1941:       else {
 1942:          pout("Autosave disabled (GLTSD bit cleared).\n");
 1943:       }
 1944:       any_output = true;
 1945:     }
 1946:   if (   options.smart_disable           || options.smart_enable
 1947:       || options.smart_auto_save_disable || options.smart_auto_save_enable)
 1948:     pout("\n"); // END OF THE ENABLE/DISABLE SECTION OF THE CODE
 1949: 
 1950:     // START OF READ-ONLY OPTIONS APART FROM -V and -i
 1951:     if (    options.smart_check_status  || options.smart_ss_media_log
 1952:            || options.smart_vendor_attrib || options.smart_error_log
 1953:            || options.smart_selftest_log  || options.smart_vendor_attrib
 1954:            || options.smart_background_log || options.sasphy
 1955:          )
 1956:     pout("=== START OF READ SMART DATA SECTION ===\n");
 1957: 
 1958:     if (options.smart_check_status) {
 1959:         scsiGetSupportedLogPages(device);
 1960:         checkedSupportedLogPages = 1;
 1961:         if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
 1962:             (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */
 1963:             if (gTapeAlertsLPage) {
 1964:                 if (options.drive_info)
 1965:                     pout("TapeAlert Supported\n");
 1966:                 if (-1 == scsiGetTapeAlertsData(device, peripheral_type))
 1967:                     failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
 1968:             }
 1969:             else
 1970:                 pout("TapeAlert Not Supported\n");
 1971:         } else { /* disk, cd/dvd, enclosure, etc */
 1972:             if ((res = scsiGetSmartData(device, options.smart_vendor_attrib))) {
 1973:                 if (-2 == res)
 1974:                     returnval |= FAILSTATUS;
 1975:                 else
 1976:                     returnval |= FAILSMART;
 1977:             }
 1978:         }
 1979:         any_output = true;
 1980:     }
 1981: 
 1982:     if (options.smart_ss_media_log) {
 1983:         if (! checkedSupportedLogPages)
 1984:             scsiGetSupportedLogPages(device);
 1985:         res = 0;
 1986:         if (gSSMediaLPage)
 1987:             res = scsiPrintSSMedia(device);
 1988:         if (0 != res)
 1989:             failuretest(OPTIONAL_CMD, returnval|=res);
 1990:         any_output = true;
 1991:     }
 1992:     if (options.smart_vendor_attrib) {
 1993:         if (! checkedSupportedLogPages)
 1994:             scsiGetSupportedLogPages(device);
 1995:         if (gTempLPage) {
 1996:             scsiPrintTemp(device);
 1997:         }
 1998:         if (gStartStopLPage)
 1999:             scsiGetStartStopData(device);
 2000:         if (SCSI_PT_DIRECT_ACCESS == peripheral_type) {
 2001:             scsiPrintGrownDefectListLen(device);
 2002:             if (gSeagateCacheLPage)
 2003:                 scsiPrintSeagateCacheLPage(device);
 2004:             if (gSeagateFactoryLPage)
 2005:                 scsiPrintSeagateFactoryLPage(device);
 2006:         }
 2007:         any_output = true;
 2008:     }
 2009:     if (options.smart_error_log) {
 2010:         if (! checkedSupportedLogPages)
 2011:             scsiGetSupportedLogPages(device);
 2012:         scsiPrintErrorCounterLog(device);
 2013:         if (1 == scsiFetchControlGLTSD(device, modese_len, 1))
 2014:             pout("\n[GLTSD (Global Logging Target Save Disable) set. "
 2015:                  "Enable Save with '-S on']\n");
 2016:         any_output = true;
 2017:     }
 2018:     if (options.smart_selftest_log) {
 2019:         if (! checkedSupportedLogPages)
 2020:             scsiGetSupportedLogPages(device);
 2021:         res = 0;
 2022:         if (gSelfTestLPage)
 2023:             res = scsiPrintSelfTest(device);
 2024:         else {
 2025:             pout("Device does not support Self Test logging\n");
 2026:             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
 2027:         }
 2028:         if (0 != res)
 2029:             failuretest(OPTIONAL_CMD, returnval|=res);
 2030:         any_output = true;
 2031:     }
 2032:     if (options.smart_background_log) {
 2033:         if (! checkedSupportedLogPages)
 2034:             scsiGetSupportedLogPages(device);
 2035:         res = 0;
 2036:         if (gBackgroundResultsLPage)
 2037:             res = scsiPrintBackgroundResults(device);
 2038:         else {
 2039:             pout("Device does not support Background scan results logging\n");
 2040:             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
 2041:         }
 2042:         if (0 != res)
 2043:             failuretest(OPTIONAL_CMD, returnval|=res);
 2044:         any_output = true;
 2045:     }
 2046:     if (options.smart_default_selftest) {
 2047:         if (scsiSmartDefaultSelfTest(device))
 2048:             return returnval | FAILSMART;
 2049:         pout("Default Self Test Successful\n");
 2050:         any_output = true;
 2051:     }
 2052:     if (options.smart_short_cap_selftest) {
 2053:         if (scsiSmartShortCapSelfTest(device))
 2054:             return returnval | FAILSMART;
 2055:         pout("Short Foreground Self Test Successful\n");
 2056:         any_output = true;
 2057:     }
 2058:     // check if another test is running
 2059:     if (options.smart_short_selftest || options.smart_extend_selftest) {
 2060:       if (!scsiRequestSense(device, &sense_info) &&
 2061:             (sense_info.asc == 0x04 && sense_info.ascq == 0x09)) {
 2062:          if (!options.smart_selftest_force) {
 2063:            pout("Can't start self-test without aborting current test");
 2064:            if (sense_info.progress != -1) {
 2065:              pout(" (%d%% remaining)",
 2066:                   100 - sense_info.progress * 100 / 65535);
 2067:            }
 2068:            pout(",\nadd '-t force' option to override, or run 'smartctl -X' "
 2069:                 "to abort test.\n");
 2070:             return -1;
 2071:          }
 2072:          else
 2073:             scsiSmartSelfTestAbort(device);
 2074:        }
 2075:     }
 2076:     if (options.smart_short_selftest) {
 2077:         if (scsiSmartShortSelfTest(device))
 2078:             return returnval | FAILSMART;
 2079:         pout("Short Background Self Test has begun\n");
 2080:         pout("Use smartctl -X to abort test\n");
 2081:         any_output = true;
 2082:     }
 2083:     if (options.smart_extend_selftest) {
 2084:         if (scsiSmartExtendSelfTest(device))
 2085:             return returnval | FAILSMART;
 2086:         pout("Extended Background Self Test has begun\n");
 2087:         if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
 2088:                         modese_len)) && (durationSec > 0)) {
 2089:             time_t t = time(NULL);
 2090: 
 2091:             t += durationSec;
 2092:             pout("Please wait %d minutes for test to complete.\n",
 2093:                  durationSec / 60);
 2094:             pout("Estimated completion time: %s\n", ctime(&t));
 2095:         }
 2096:         pout("Use smartctl -X to abort test\n");
 2097:         any_output = true;
 2098:     }
 2099:     if (options.smart_extend_cap_selftest) {
 2100:         if (scsiSmartExtendCapSelfTest(device))
 2101:             return returnval | FAILSMART;
 2102:         pout("Extended Foreground Self Test Successful\n");
 2103:     }
 2104:     if (options.smart_selftest_abort) {
 2105:         if (scsiSmartSelfTestAbort(device))
 2106:             return returnval | FAILSMART;
 2107:         pout("Self Test returned without error\n");
 2108:         any_output = true;
 2109:     }
 2110:     if (options.sasphy) {
 2111:         if (scsiPrintSasPhy(device, options.sasphy_reset))
 2112:             return returnval | FAILSMART;
 2113:         any_output = true;
 2114:     }
 2115: 
 2116:     if (!any_output)
 2117:       pout("SCSI device successfully opened\n\n"
 2118:            "Use 'smartctl -a' (or '-x') to print SMART (and more) information\n\n");
 2119: 
 2120:     return returnval;
 2121: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>