1. Summary
  2. Files
  3. Support
  4. Report Spam
  5. Create account
  6. Log in

root/trunk/smartmontools/scsiprint.cpp @ 3690

Revision 3690, 61.2 KB (checked in by samm2, 6 months ago)

Implemented self-test progress indicator and -t force for SCSI drives.
By default new test will not try to start if another test is active.
Also progress will be displayed, if available

Some internal changes:

- scsi_sense_disect extended to support progress indicator
- scsiRequestSense() extended to fill progress indicator, if available, code is based on sdparm -C sense
- sg_scsi_sense_desc_find moved to scsicmds.{cpp,h} without "static" declaration because it is now used also by scsiRequestSense()
- added progress indicator to -l selftest SCSI output
- manual updated

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