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