1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
4 This file is part of libatasmart.
6 Copyright 2008 Lennart Poettering
8 libatasmart is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as
10 published by the Free Software Foundation, either version 2.1 of the
11 License, or (at your option) any later version.
13 libatasmart is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public
19 License along with libatasmart. If not, If not, see
20 <http://www.gnu.org/licenses/>.
27 #include <arpa/inet.h>
37 #include <sys/ioctl.h>
38 #include <scsi/scsi.h>
40 #include <scsi/scsi_ioctl.h>
41 #include <linux/hdreg.h>
43 #include <sys/types.h>
52 #define SK_TIMEOUT 2000
54 typedef enum SkDirection {
61 typedef enum SkDiskType {
62 SK_DISK_TYPE_ATA_PASSTHROUGH, /* ATA passthrough over SCSI transport */
75 uint8_t identify[512];
76 uint8_t smart_data[512];
77 uint8_t smart_threshold_data[512];
79 SkBool identify_data_valid:1;
80 SkBool smart_data_valid:1;
81 SkBool smart_threshold_data_valid:1;
83 SkIdentifyParsedData identify_parsed_data;
84 SkSmartParsedData smart_parsed_data;
88 typedef enum SkAtaCommand {
89 SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC,
90 SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1,
91 SK_ATA_COMMAND_SMART = 0xB0,
92 SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5
95 /* ATA SMART subcommands (ATA8 7.52.1) */
96 typedef enum SkSmartCommand {
97 SK_SMART_COMMAND_READ_DATA = 0xD0,
98 SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1,
99 SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4,
100 SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8,
101 SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9,
102 SK_SMART_COMMAND_RETURN_STATUS = 0xDA
105 static SkBool disk_smart_is_available(SkDisk *d) {
106 return d->identify_data_valid && !!(d->identify[164] & 1);
109 static SkBool disk_smart_is_enabled(SkDisk *d) {
110 return d->identify_data_valid && !!(d->identify[170] & 1);
113 static SkBool disk_smart_is_conveyance_test_available(SkDisk *d) {
114 assert(d->smart_data_valid);
116 return !!(d->smart_data[367] & 32);
118 static SkBool disk_smart_is_short_and_extended_test_available(SkDisk *d) {
119 assert(d->smart_data_valid);
121 return !!(d->smart_data[367] & 16);
124 static SkBool disk_smart_is_start_test_available(SkDisk *d) {
125 assert(d->smart_data_valid);
127 return !!(d->smart_data[367] & 1);
130 static SkBool disk_smart_is_abort_test_available(SkDisk *d) {
131 assert(d->smart_data_valid);
133 return !!(d->smart_data[367] & 41);
136 static int disk_ata_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
137 uint8_t *bytes = cmd_data;
140 assert(d->type == SK_DISK_TYPE_ATA);
144 case SK_DIRECTION_OUT:
146 /* We could use HDIO_DRIVE_TASKFILE here, but
147 * that's a deprecated ioctl(), hence we don't
148 * do it. And we don't need writing anyway. */
153 case SK_DIRECTION_IN: {
156 /* We have HDIO_DRIVE_CMD which can only read, but not write,
157 * and cannot do LBA. We use it for all read commands. */
159 ioctl_data = alloca(4 + *len);
160 memset(ioctl_data, 0, 4 + *len);
162 ioctl_data[0] = (uint8_t) command; /* COMMAND */
163 ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */
164 ioctl_data[2] = bytes[1]; /* FEATURE */
165 ioctl_data[3] = bytes[3]; /* NSECTOR */
167 if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0)
170 memset(bytes, 0, 12);
171 bytes[11] = ioctl_data[0];
172 bytes[1] = ioctl_data[1];
173 bytes[3] = ioctl_data[2];
175 memcpy(data, ioctl_data+4, *len);
180 case SK_DIRECTION_NONE: {
181 uint8_t ioctl_data[7];
183 /* We have HDIO_DRIVE_TASK which can neither read nor
184 * write, but can do LBA. We use it for all commands that
185 * do neither read nor write */
187 memset(ioctl_data, 0, sizeof(ioctl_data));
189 ioctl_data[0] = (uint8_t) command; /* COMMAND */
190 ioctl_data[1] = bytes[1]; /* FEATURE */
191 ioctl_data[2] = bytes[3]; /* NSECTOR */
193 ioctl_data[3] = bytes[9]; /* LBA LOW */
194 ioctl_data[4] = bytes[8]; /* LBA MID */
195 ioctl_data[5] = bytes[7]; /* LBA HIGH */
196 ioctl_data[6] = bytes[10]; /* SELECT */
198 if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data)))
201 memset(bytes, 0, 12);
202 bytes[11] = ioctl_data[0];
203 bytes[1] = ioctl_data[1];
204 bytes[3] = ioctl_data[2];
206 bytes[9] = ioctl_data[3];
207 bytes[8] = ioctl_data[4];
208 bytes[7] = ioctl_data[5];
210 bytes[10] = ioctl_data[6];
221 /* Sends a SCSI command block */
222 static int sg_io(int fd, int direction,
223 const void *cdb, size_t cdb_len,
224 void *data, size_t data_len,
225 void *sense, size_t sense_len) {
227 struct sg_io_hdr io_hdr;
229 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
231 io_hdr.interface_id = 'S';
232 io_hdr.cmdp = (unsigned char*) cdb;
233 io_hdr.cmd_len = cdb_len;
234 io_hdr.dxferp = data;
235 io_hdr.dxfer_len = data_len;
237 io_hdr.mx_sb_len = sense_len;
238 io_hdr.dxfer_direction = direction;
239 io_hdr.timeout = SK_TIMEOUT;
241 return ioctl(fd, SG_IO, &io_hdr);
244 static int disk_passthrough_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
245 uint8_t *bytes = cmd_data;
248 uint8_t *desc = sense+8;
251 static const int direction_map[] = {
252 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
253 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
254 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
257 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH);
259 /* ATA Pass-Through 16 byte command, as described in "T10 04-262r8
260 * ATA Command Pass-Through":
261 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
263 memset(cdb, 0, sizeof(cdb));
265 cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */
267 if (direction == SK_DIRECTION_NONE) {
268 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
269 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
271 } else if (direction == SK_DIRECTION_IN) {
272 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
273 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
275 } else if (direction == SK_DIRECTION_OUT) {
276 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
277 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
280 cdb[3] = bytes[0]; /* FEATURES */
283 cdb[5] = bytes[2]; /* SECTORS */
286 cdb[8] = bytes[9]; /* LBA LOW */
287 cdb[10] = bytes[8]; /* LBA MID */
288 cdb[12] = bytes[7]; /* LBA HIGH */
290 cdb[13] = bytes[10] & 0x4F; /* SELECT */
291 cdb[14] = (uint8_t) command;
293 memset(sense, 0, sizeof(sense));
295 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, (size_t) cdb[6] * 512, sense, sizeof(sense))) < 0)
298 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
303 memset(bytes, 0, 12);
311 bytes[10] = desc[12];
312 bytes[11] = desc[13];
317 static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
319 static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) = {
320 [SK_DISK_TYPE_ATA] = disk_ata_command,
321 [SK_DISK_TYPE_ATA_PASSTHROUGH] = disk_passthrough_command,
325 assert(d->type <= _SK_DISK_TYPE_MAX);
326 assert(direction <= _SK_DIRECTION_MAX);
328 assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0));
329 assert(direction != SK_DIRECTION_NONE || (!data && !len));
331 return disk_command_table[d->type](d, command, direction, cmd_data, data, len);
334 static int disk_identify_device(SkDisk *d) {
339 memset(cmd, 0, sizeof(cmd));
343 if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0)
351 d->identify_data_valid = TRUE;
356 int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake) {
360 if (!d->identify_data_valid) {
365 memset(cmd, 0, sizeof(cmd));
367 if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
370 if (cmd[0] != 0 || (ntohs(cmd[5]) & 1) != 0) {
375 *awake = ntohs(cmd[1]) == 0xFF;
380 static int disk_smart_enable(SkDisk *d, SkBool b) {
383 if (!disk_smart_is_available(d)) {
388 memset(cmd, 0, sizeof(cmd));
390 cmd[0] = htons(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS);
391 cmd[2] = htons(0x0000U);
392 cmd[3] = htons(0x00C2U);
393 cmd[4] = htons(0x4F00U);
395 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0);
398 int sk_disk_smart_read_data(SkDisk *d) {
403 if (!disk_smart_is_available(d)) {
408 memset(cmd, 0, sizeof(cmd));
410 cmd[0] = htons(SK_SMART_COMMAND_READ_DATA);
412 cmd[2] = htons(0x0000U);
413 cmd[3] = htons(0x00C2U);
414 cmd[4] = htons(0x4F00U);
416 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0)
419 d->smart_data_valid = TRUE;
424 static int disk_smart_read_thresholds(SkDisk *d) {
429 if (!disk_smart_is_available(d)) {
434 memset(cmd, 0, sizeof(cmd));
436 cmd[0] = htons(SK_SMART_COMMAND_READ_THRESHOLDS);
438 cmd[2] = htons(0x0000U);
439 cmd[3] = htons(0x00C2U);
440 cmd[4] = htons(0x4F00U);
442 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_threshold_data, &len)) < 0)
445 d->smart_threshold_data_valid = TRUE;
450 int sk_disk_smart_status(SkDisk *d, SkBool *good) {
454 if (!disk_smart_is_available(d)) {
459 memset(cmd, 0, sizeof(cmd));
461 cmd[0] = htons(SK_SMART_COMMAND_RETURN_STATUS);
462 cmd[1] = htons(0x0000U);
463 cmd[3] = htons(0x00C2U);
464 cmd[4] = htons(0x4F00U);
466 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
469 if (cmd[3] == htons(0x00C2U) &&
470 cmd[4] == htons(0x4F00U))
472 else if (cmd[3] == htons(0x002CU) &&
473 cmd[4] == htons(0xF400U))
483 int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) {
487 if (!disk_smart_is_available(d)) {
492 if (!d->smart_data_valid)
493 if ((ret = sk_disk_smart_read_data(d)) < 0)
496 assert(d->smart_data_valid);
498 if (test != SK_SMART_SELF_TEST_SHORT &&
499 test != SK_SMART_SELF_TEST_EXTENDED &&
500 test != SK_SMART_SELF_TEST_CONVEYANCE &&
501 test != SK_SMART_SELF_TEST_ABORT) {
506 if (!disk_smart_is_start_test_available(d)
507 || (test == SK_SMART_SELF_TEST_ABORT && !disk_smart_is_abort_test_available(d))
508 || ((test == SK_SMART_SELF_TEST_SHORT || test == SK_SMART_SELF_TEST_EXTENDED) && !disk_smart_is_short_and_extended_test_available(d))
509 || (test == SK_SMART_SELF_TEST_CONVEYANCE && !disk_smart_is_conveyance_test_available(d))) {
514 if (test == SK_SMART_SELF_TEST_ABORT &&
515 !disk_smart_is_abort_test_available(d)) {
520 memset(cmd, 0, sizeof(cmd));
522 cmd[0] = htons(SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE);
523 cmd[2] = htons(0x0000U);
524 cmd[3] = htons(0x00C2U);
525 cmd[4] = htons(0x4F00U | (uint16_t) test);
527 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, NULL);
530 static void swap_strings(char *s, size_t len) {
531 assert((len & 1) == 0);
533 for (; len > 0; s += 2, len -= 2) {
541 static void clean_strings(char *s) {
545 if (*e < ' ' || *e >= 127)
549 static void drop_spaces(char *s) {
551 SkBool prev_space = FALSE;
574 static void read_string(char *d, uint8_t *s, size_t len) {
577 swap_strings(d, len);
582 int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) {
584 if (!d->identify_data_valid) {
589 read_string(d->identify_parsed_data.serial, d->identify+20, 20);
590 read_string(d->identify_parsed_data.firmware, d->identify+46, 8);
591 read_string(d->identify_parsed_data.model, d->identify+54, 40);
593 *ipd = &d->identify_parsed_data;
598 int sk_disk_smart_is_available(SkDisk *d, SkBool *b) {
600 if (!d->identify_data_valid) {
605 *b = disk_smart_is_available(d);
609 int sk_disk_identify_is_available(SkDisk *d, SkBool *b) {
611 *b = d->identify_data_valid;
615 const char *sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status) {
617 /* %STRINGPOOLSTART% */
618 static const char* const map[] = {
619 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.",
620 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.",
621 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.",
622 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.",
623 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.",
624 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.",
625 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status"
627 /* %STRINGPOOLSTOP% */
629 if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX)
632 return _P(map[status]);
635 const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status) {
637 /* %STRINGPOOLSTART% */
638 static const char* const map[] = {
639 [SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER] = "The previous self-test routine completed without error or no self-test has ever been run.",
640 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED] = "The self-test routine was aborted by the host.",
641 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED] = "The self-test routine was interrupted by the host with a hardware or software reset.",
642 [SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL] = "A fatal error or unknown test error occurred while the device was executing its self-test routine and the device was unable to complete the self-test routine.",
643 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN] = "The previous self-test completed having a test element that failed and the test element that failed.",
644 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL] = "The previous self-test completed having the electrical element of the test failed.",
645 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO] = "The previous self-test completed having the servo (and/or seek) test element of the test failed.",
646 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ] = "The previous self-test completed having the read element of the test failed.",
647 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING] = "The previous self-test completed having a test element that failed and the device is suspected of having handling damage.",
648 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS] = "Self-test routine in progress"
650 /* %STRINGPOOLSTOP% */
652 if (status >= _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX)
655 return _P(map[status]);
658 const char* sk_smart_self_test_to_string(SkSmartSelfTest test) {
661 case SK_SMART_SELF_TEST_SHORT:
663 case SK_SMART_SELF_TEST_EXTENDED:
665 case SK_SMART_SELF_TEST_CONVEYANCE:
667 case SK_SMART_SELF_TEST_ABORT:
674 SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) {
676 if (!d->start_test_available)
680 case SK_SMART_SELF_TEST_SHORT:
681 case SK_SMART_SELF_TEST_EXTENDED:
682 return d->short_and_extended_test_available;
683 case SK_SMART_SELF_TEST_CONVEYANCE:
684 return d->conveyance_test_available;
685 case SK_SMART_SELF_TEST_ABORT:
686 return d->abort_test_available;
692 unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) {
694 if (!sk_smart_self_test_available(d, test))
698 case SK_SMART_SELF_TEST_SHORT:
699 return d->short_test_polling_minutes;
700 case SK_SMART_SELF_TEST_EXTENDED:
701 return d->extended_test_polling_minutes;
702 case SK_SMART_SELF_TEST_CONVEYANCE:
703 return d->conveyance_test_polling_minutes;
709 static void make_pretty(SkSmartAttributeParsedData *a) {
710 uint64_t fourtyeight;
715 if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN)
719 ((uint64_t) a->raw[0]) |
720 (((uint64_t) a->raw[1]) << 8) |
721 (((uint64_t) a->raw[2]) << 16) |
722 (((uint64_t) a->raw[3]) << 24) |
723 (((uint64_t) a->raw[4]) << 32) |
724 (((uint64_t) a->raw[5]) << 40);
726 if (!strcmp(a->name, "spin-up-time"))
727 a->pretty_value = fourtyeight & 0xFFFF;
728 else if (!strcmp(a->name, "airflow-temperature-celsius") ||
729 !strcmp(a->name, "temperature-celsius-1") ||
730 !strcmp(a->name, "temperature-celsius-2"))
731 a->pretty_value = (fourtyeight & 0xFFFF)*1000 + 273150;
732 else if (!strcmp(a->name, "temperature-centi-celsius"))
733 a->pretty_value = (fourtyeight & 0xFFFF)*100 + 273150;
734 else if (!strcmp(a->name, "power-on-minutes"))
735 a->pretty_value = fourtyeight * 60 * 1000;
736 else if (!strcmp(a->name, "power-on-seconds"))
737 a->pretty_value = fourtyeight * 1000;
738 else if (!strcmp(a->name, "power-on-half-minutes"))
739 a->pretty_value = fourtyeight * 30 * 1000;
740 else if (!strcmp(a->name, "power-on-hours") ||
741 !strcmp(a->name, "loaded-hours") ||
742 !strcmp(a->name, "head-flying-hours"))
743 a->pretty_value = fourtyeight * 60 * 60 * 1000;
745 a->pretty_value = fourtyeight;
748 typedef struct SkSmartAttributeInfo {
750 SkSmartAttributeUnit unit;
751 } SkSmartAttributeInfo;
753 /* This data is stolen from smartmontools */
755 /* %STRINGPOOLSTART% */
756 static const SkSmartAttributeInfo const attribute_info[255] = {
757 [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
758 [2] = { "throughput-perfomance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
759 [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
760 [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
761 [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
762 [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
763 [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
764 [8] = { "seek-time-perfomance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
765 [9] = { "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
766 [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
767 [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
768 [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
769 [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
770 [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
771 [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE },
772 [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
773 [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
774 [192] = { "power-off-retract-count-1", SK_SMART_ATTRIBUTE_UNIT_NONE },
775 [193] = { "load-cycle-count-1", SK_SMART_ATTRIBUTE_UNIT_NONE },
776 [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
777 [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE },
778 [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
779 [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
780 [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
781 [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
782 [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
783 [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
784 [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
785 [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_NONE },
786 [204] = { "shock-count-write-opern", SK_SMART_ATTRIBUTE_UNIT_NONE },
787 [205] = { "shock-rate-write-opern", SK_SMART_ATTRIBUTE_UNIT_NONE },
788 [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
789 [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
790 [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN},
791 [209] = { "offline-seek-perfomance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
792 [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
793 [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
794 [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
795 [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
796 [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
797 [225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
798 [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
799 [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
800 [228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
801 [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
802 [231] = { "temperature-celsius-1", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
803 [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
804 [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE }
806 /* %STRINGPOOLSTOP% */
808 typedef enum SkSmartQuirk {
809 SK_SMART_QUIRK_9_POWERONMINUTES = 1,
810 SK_SMART_QUIRK_9_POWERONSECONDS = 2,
811 SK_SMART_QUIRK_9_POWERONHALFMINUTES = 4,
812 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 8,
813 SK_SMART_QUIRK_193_LOADUNLOAD = 16,
814 SK_SMART_QUIRK_194_10XCELSIUS = 32,
815 SK_SMART_QUIRK_194_UNKNOWN = 64,
816 SK_SMART_QUIRK_200_WRITEERRORCOUNT = 128,
817 SK_SMART_QUIRK_201_DETECTEDTACOUNT = 256,
820 /* %STRINGPOOLSTART% */
821 static const char *quirk_name[] = {
824 "9_POWERONHALFMINUTES",
825 "192_EMERGENCYRETRACTCYCLECT",
829 "200_WRITEERRORCOUNT",
830 "201_DETECTEDTACOUNT",
833 /* %STRINGPOOLSTOP% */
835 typedef struct SkSmartQuirkDatabase {
837 const char *firmware;
839 } SkSmartQuirkDatabase;
841 /* %STRINGPOOLSTART% */
842 static const SkSmartQuirkDatabase quirk_database[] = { {
843 "^FUJITSU MHR2040AT$",
845 SK_SMART_QUIRK_9_POWERONSECONDS|
846 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
847 SK_SMART_QUIRK_200_WRITEERRORCOUNT
849 "^FUJITSU MHS20[6432]0AT( .)?$",
851 SK_SMART_QUIRK_9_POWERONSECONDS|
852 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
853 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
854 SK_SMART_QUIRK_201_DETECTEDTACOUNT
859 SK_SMART_QUIRK_9_POWERONHALFMINUTES
863 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
864 SK_SMART_QUIRK_194_10XCELSIUS
868 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
869 SK_SMART_QUIRK_194_10XCELSIUS
873 SK_SMART_QUIRK_9_POWERONHALFMINUTES
877 SK_SMART_QUIRK_9_POWERONHALFMINUTES
880 ".*-(2[3-9]|3[0-9])$",
881 SK_SMART_QUIRK_9_POWERONHALFMINUTES
884 "^Maxtor 2B0(0[468]|1[05]|20)H1$",
886 SK_SMART_QUIRK_9_POWERONMINUTES|
887 SK_SMART_QUIRK_194_UNKNOWN
889 "^Maxtor 4G(120J6|160J[68])$",
891 SK_SMART_QUIRK_9_POWERONMINUTES|
892 SK_SMART_QUIRK_194_UNKNOWN
894 "^Maxtor 4D0(20H1|40H2|60H3|80H4)$",
896 SK_SMART_QUIRK_9_POWERONMINUTES|
897 SK_SMART_QUIRK_194_UNKNOWN
900 "^HITACHI_DK14FA-20B$",
902 SK_SMART_QUIRK_9_POWERONMINUTES|
903 SK_SMART_QUIRK_193_LOADUNLOAD
905 "^HITACHI_DK23..-..B?$",
907 SK_SMART_QUIRK_9_POWERONMINUTES|
908 SK_SMART_QUIRK_193_LOADUNLOAD
910 "^(HITACHI_DK23FA-20J|HTA422020F9AT[JN]0)$",
912 SK_SMART_QUIRK_9_POWERONMINUTES|
913 SK_SMART_QUIRK_193_LOADUNLOAD
918 SK_SMART_QUIRK_9_POWERONMINUTES
922 SK_SMART_QUIRK_9_POWERONMINUTES
926 SK_SMART_QUIRK_9_POWERONSECONDS
930 SK_SMART_QUIRK_9_POWERONSECONDS
937 /* %STRINGPOOLSTOP% */
939 static int match(const char*regex, const char *s, SkBool *result) {
945 if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
950 if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
952 if (k != REG_NOMATCH) {
966 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
968 const SkSmartQuirkDatabase *db;
972 for (db = quirk_database; db->model || db->firmware; db++) {
975 SkBool matching = FALSE;
977 if ((k = match(_P(db->model), model, &matching)) < 0)
985 SkBool matching = FALSE;
987 if ((k = match(_P(db->firmware), firmware, &matching)) < 0)
1001 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1002 const SkIdentifyParsedData *ipd;
1003 SkSmartQuirk quirk = 0;
1005 /* These are the complex ones */
1006 if (sk_disk_identify_parse(d, &ipd) < 0)
1009 if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1016 /* %STRINGPOOLSTART% */
1017 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1018 static const SkSmartAttributeInfo a = {
1019 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1023 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1024 static const SkSmartAttributeInfo a = {
1025 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1029 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1030 static const SkSmartAttributeInfo a = {
1031 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1035 /* %STRINGPOOLSTOP% */
1040 /* %STRINGPOOLSTART% */
1041 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1042 static const SkSmartAttributeInfo a = {
1043 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1047 /* %STRINGPOOLSTOP% */
1052 /* %STRINGPOOLSTART% */
1053 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1054 static const SkSmartAttributeInfo a = {
1055 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN
1058 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1060 /* %STRINGPOOLSTOP% */
1065 /* %STRINGPOOLSTART% */
1066 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1067 static const SkSmartAttributeInfo a = {
1068 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1072 /* %STRINGPOOLSTOP% */
1077 /* %STRINGPOOLSTART% */
1078 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1079 static const SkSmartAttributeInfo a = {
1080 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1084 /* %STRINGPOOLSTOP% */
1090 /* These are the simple cases */
1091 if (attribute_info[id].name)
1092 return &attribute_info[id];
1097 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1099 if (!d->smart_data_valid) {
1104 switch (d->smart_data[362]) {
1107 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1112 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1116 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1121 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1126 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1131 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1135 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1139 d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1140 d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1142 d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1144 d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1145 d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1146 d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1147 d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1149 d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1150 d->smart_parsed_data.extended_test_polling_minutes = d->smart_data[373] != 0xFF ? d->smart_data[373] : ((uint16_t) d->smart_data[376] << 8 | (uint16_t) d->smart_data[375]);
1151 d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1153 *spd = &d->smart_parsed_data;
1158 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1162 if (!d->smart_threshold_data_valid) {
1163 a->threshold_valid = FALSE;
1167 for (n = 0, p = d->smart_threshold_data+2; n < 30; n++, p+=12)
1172 a->threshold_valid = FALSE;
1173 a->good_valid = FALSE;
1177 a->threshold = p[1];
1178 a->threshold_valid = p[1] != 0xFE;
1180 a->good_valid = FALSE;
1183 /* Always-Fail and Always-Pssing thresholds are not relevant
1184 * for our assessment. */
1185 if (p[1] >= 1 && p[1] <= 0xFD) {
1187 if (a->worst_value_valid) {
1188 a->good = a->good && (a->worst_value > a->threshold);
1189 a->good_valid = TRUE;
1192 if (a->current_value_valid) {
1193 a->good = a->good && (a->current_value > a->threshold);
1194 a->good_valid = TRUE;
1199 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1203 if (!d->smart_data_valid) {
1208 for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1209 SkSmartAttributeParsedData a;
1210 const SkSmartAttributeInfo *i;
1216 memset(&a, 0, sizeof(a));
1218 a.current_value = p[3];
1219 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1220 a.worst_value = p[4];
1221 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
1223 a.flags = ((uint16_t) p[2] << 8) | p[1];
1224 a.prefailure = !!(p[1] & 1);
1225 a.online = !!(p[1] & 2);
1227 memcpy(a.raw, p+5, 6);
1229 if ((i = lookup_attribute(d, p[0]))) {
1230 a.name = _P(i->name);
1231 a.pretty_unit = i->unit;
1233 if (asprintf(&an, "attribute-%u", a.id) < 0) {
1239 a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1244 find_threshold(d, &a);
1246 cb(d, &a, userdata);
1254 static const char *yes_no(SkBool b) {
1255 return b ? "yes" : "no";
1258 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
1260 /* %STRINGPOOLSTART% */
1261 const char * const map[] = {
1262 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
1263 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
1264 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
1265 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
1266 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK"
1268 /* %STRINGPOOLSTOP% */
1270 if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
1273 return _P(map[unit]);
1276 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
1281 snprintf(s, len, "%u", id);
1288 static char *print_value(char *s, size_t len, const SkSmartAttributeParsedData *a) {
1290 switch (a->pretty_unit) {
1291 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
1293 if (a->pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
1294 snprintf(s, len, "%0.1f years", ((double) a->pretty_value)/(1000.0*60*60*24*365));
1295 else if (a->pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
1296 snprintf(s, len, "%0.1f months", ((double) a->pretty_value)/(1000.0*60*60*24*30));
1297 else if (a->pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
1298 snprintf(s, len, "%0.1f days", ((double) a->pretty_value)/(1000.0*60*60*24));
1299 else if (a->pretty_value >= 1000LLU*60LLU*60LLU)
1300 snprintf(s, len, "%0.1f h", ((double) a->pretty_value)/(1000.0*60*60));
1301 else if (a->pretty_value >= 1000LLU*60LLU)
1302 snprintf(s, len, "%0.1f min", ((double) a->pretty_value)/(1000.0*60));
1303 else if (a->pretty_value >= 1000LLU)
1304 snprintf(s, len, "%0.1f s", ((double) a->pretty_value)/(1000.0));
1306 snprintf(s, len, "%llu ms", (unsigned long long) a->pretty_value);
1310 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
1311 snprintf(s, len, "%0.1f C", ((double) a->pretty_value - 273150) / 1000);
1314 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
1315 snprintf(s, len, "%llu sectors", (unsigned long long) a->pretty_value);
1318 case SK_SMART_ATTRIBUTE_UNIT_NONE:
1319 snprintf(s, len, "%llu", (unsigned long long) a->pretty_value);
1322 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
1323 snprintf(s, len, "n/a");
1326 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
1335 #define HIGHLIGHT "\x1B[1m"
1336 #define ENDHIGHLIGHT "\x1B[0m"
1338 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
1341 char tt[32], tw[32], tc[32];
1344 snprintf(tt, sizeof(tt), "%3u", a->threshold);
1345 tt[sizeof(tt)-1] = 0;
1346 snprintf(tw, sizeof(tw), "%3u", a->worst_value);
1347 tw[sizeof(tw)-1] = 0;
1348 snprintf(tc, sizeof(tc), "%3u", a->current_value);
1349 tc[sizeof(tc)-1] = 0;
1351 highlight = a->good_valid && !a->good && isatty(1);
1354 fprintf(stderr, HIGHLIGHT);
1356 printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-3s\n",
1358 print_name(name, sizeof(name), a->id, a->name),
1359 a->current_value_valid ? tc : "n/a",
1360 a->worst_value_valid ? tw : "n/a",
1361 a->threshold_valid ? tt : "n/a",
1362 print_value(pretty, sizeof(pretty), a),
1363 a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
1364 a->prefailure ? "prefail" : "old-age",
1365 a->online ? "online" : "offline",
1366 a->good_valid ? yes_no(a->good) : "n/a");
1369 fprintf(stderr, ENDHIGHLIGHT);
1372 int sk_disk_dump(SkDisk *d) {
1374 SkBool awake = FALSE;
1376 printf("Device: %s\n"
1379 (unsigned long) (d->size/1024/1024));
1381 if (d->identify_data_valid) {
1382 const SkIdentifyParsedData *ipd;
1383 SkSmartQuirk quirk = 0;
1386 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
1389 printf("Model: [%s]\n"
1392 "SMART Available: %s\n",
1396 yes_no(disk_smart_is_available(d)));
1398 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
1403 for (i = 0; quirk_name[i]; i++)
1405 printf(" %s", _P(quirk_name[i]));
1411 ret = sk_disk_check_sleep_mode(d, &awake);
1412 printf("Awake: %s\n",
1413 ret >= 0 ? yes_no(awake) : "unknown");
1415 if (disk_smart_is_available(d)) {
1416 const SkSmartParsedData *spd;
1419 if ((ret = sk_disk_smart_status(d, &good)) < 0)
1422 printf("Disk Health Good: %s\n",
1425 if ((ret = sk_disk_smart_read_data(d)) < 0)
1428 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
1431 printf("Off-line Data Collection Status: [%s]\n"
1432 "Total Time To Complete Off-Line Data Collection: %u s\n"
1433 "Self-Test Execution Status: [%s]\n"
1434 "Percent Self-Test Remaining: %u%%\n"
1435 "Conveyance Self-Test Available: %s\n"
1436 "Short/Extended Self-Test Available: %s\n"
1437 "Start Self-Test Available: %s\n"
1438 "Abort Self-Test Available: %s\n"
1439 "Short Self-Test Polling Time: %u min\n"
1440 "Extended Self-Test Polling Time: %u min\n"
1441 "Conveyance Self-Test Polling Time: %u min\n",
1442 sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
1443 spd->total_offline_data_collection_seconds,
1444 sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
1445 spd->self_test_execution_percent_remaining,
1446 yes_no(spd->conveyance_test_available),
1447 yes_no(spd->short_and_extended_test_available),
1448 yes_no(spd->start_test_available),
1449 yes_no(spd->abort_test_available),
1450 spd->short_test_polling_minutes,
1451 spd->extended_test_polling_minutes,
1452 spd->conveyance_test_polling_minutes);
1454 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-3s\n",
1466 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
1473 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
1479 int sk_disk_open(const char *name, SkDisk **_d) {
1487 if (!(d = calloc(1, sizeof(SkDisk)))) {
1492 if (!(d->name = strdup(name))) {
1497 if ((d->fd = open(name, O_RDWR|O_NOCTTY)) < 0) {
1502 if ((ret = fstat(d->fd, &st)) < 0)
1505 if (!S_ISBLK(st.st_mode)) {
1511 /* So, it's a block device. Let's make sure the ioctls work */
1513 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
1516 if (d->size <= 0 || d->size == (uint64_t) -1) {
1522 /* OK, it's a real block device with a size. Find a way to
1523 * identify the device. */
1524 for (d->type = 0; d->type != SK_DISK_TYPE_UNKNOWN; d->type++)
1525 if (disk_identify_device(d) >= 0)
1528 /* Check if driver can do SMART, and enable if necessary */
1529 if (disk_smart_is_available(d)) {
1531 if (!disk_smart_is_enabled(d)) {
1532 if ((ret = disk_smart_enable(d, TRUE)) < 0)
1535 if ((ret = disk_identify_device(d)) < 0)
1538 if (!disk_smart_is_enabled(d)) {
1545 disk_smart_read_thresholds(d);
1560 void sk_disk_free(SkDisk *d) {