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