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>
45 #include <sys/param.h>
54 #define SK_TIMEOUT 2000
56 typedef enum SkDirection {
63 typedef enum SkDiskType {
64 /* These three will be autotested for: */
65 SK_DISK_TYPE_ATA_PASSTHROUGH_12, /* ATA passthrough over SCSI transport, 12-byte version */
66 SK_DISK_TYPE_ATA_PASSTHROUGH_16, /* ATA passthrough over SCSI transport, 16-byte version */
67 SK_DISK_TYPE_LINUX_IDE, /* Classic Linux /dev/hda ioctls */
69 /* These three will not be autotested for */
70 SK_DISK_TYPE_SUNPLUS, /* SunPlus USB/ATA bridges */
71 SK_DISK_TYPE_JMICRON, /* JMicron USB/ATA bridges */
72 SK_DISK_TYPE_BLOB, /* From a file */
73 SK_DISK_TYPE_NONE, /* No access method */
74 SK_DISK_TYPE_AUTO, /* We don't know yet */
76 _SK_DISK_TYPE_TEST_MAX = SK_DISK_TYPE_SUNPLUS /* only auto test until here */
79 #if __BYTE_ORDER == __LITTLE_ENDIAN
80 #define MAKE_TAG(a,b,c,d) \
81 (((uint32_t) d << 24) | \
82 ((uint32_t) c << 16) | \
83 ((uint32_t) b << 8) | \
86 #define MAKE_TAG(a,b,c,d) \
87 (((uint32_t) a << 24) | \
88 ((uint32_t) b << 16) | \
89 ((uint32_t) c << 8) | \
93 typedef enum SkBlobTag {
94 SK_BLOB_TAG_IDENTIFY = MAKE_TAG('I', 'D', 'F', 'Y'),
95 SK_BLOB_TAG_SMART_STATUS = MAKE_TAG('S', 'M', 'S', 'T'),
96 SK_BLOB_TAG_SMART_DATA = MAKE_TAG('S', 'M', 'D', 'T'),
97 SK_BLOB_TAG_SMART_THRESHOLDS = MAKE_TAG('S', 'M', 'T', 'H')
107 uint8_t identify[512];
108 uint8_t smart_data[512];
109 uint8_t smart_thresholds[512];
111 SkBool identify_valid:1;
112 SkBool smart_data_valid:1;
113 SkBool smart_thresholds_valid:1;
115 SkBool blob_smart_status:1;
116 SkBool blob_smart_status_valid:1;
118 SkBool attribute_verification_bad:1;
120 SkIdentifyParsedData identify_parsed_data;
121 SkSmartParsedData smart_parsed_data;
127 typedef enum SkAtaCommand {
128 SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC,
129 SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1,
130 SK_ATA_COMMAND_SMART = 0xB0,
131 SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5
134 /* ATA SMART subcommands (ATA8 7.52.1) */
135 typedef enum SkSmartCommand {
136 SK_SMART_COMMAND_READ_DATA = 0xD0,
137 SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1,
138 SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4,
139 SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8,
140 SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9,
141 SK_SMART_COMMAND_RETURN_STATUS = 0xDA
144 /* Hmm, if the data we parse is out of a certain range just consider it misparsed */
145 #define SK_MKELVIN_VALID_MIN ((uint64_t) ((-15LL*1000LL) + 273150LL))
146 #define SK_MKELVIN_VALID_MAX ((uint64_t) ((100LL*1000LL) + 273150LL))
148 #define SK_MSECOND_VALID_MIN 1ULL
149 #define SK_MSECOND_VALID_SHORT_MAX (60ULL * 60ULL * 1000ULL)
150 #define SK_MSECOND_VALID_LONG_MAX (30ULL * 365ULL * 24ULL * 60ULL * 60ULL * 1000ULL)
152 static const char *disk_type_to_human_string(SkDiskType type) {
154 /* %STRINGPOOLSTART% */
155 static const char* const map[_SK_DISK_TYPE_MAX] = {
156 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "16 Byte SCSI ATA SAT Passthru",
157 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "12 Byte SCSI ATA SAT Passthru",
158 [SK_DISK_TYPE_LINUX_IDE] = "Native Linux IDE",
159 [SK_DISK_TYPE_SUNPLUS] = "Sunplus SCSI ATA Passthru",
160 [SK_DISK_TYPE_JMICRON] = "JMicron SCSI ATA Passthru",
161 [SK_DISK_TYPE_BLOB] = "Blob",
162 [SK_DISK_TYPE_AUTO] = "Automatic",
163 [SK_DISK_TYPE_NONE] = "None"
165 /* %STRINGPOOLSTOP% */
167 if (type >= _SK_DISK_TYPE_MAX)
170 return _P(map[type]);
173 static const char *disk_type_to_prefix_string(SkDiskType type) {
175 /* %STRINGPOOLSTART% */
176 static const char* const map[_SK_DISK_TYPE_MAX] = {
177 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "sat16",
178 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "sat12",
179 [SK_DISK_TYPE_LINUX_IDE] = "linux-ide",
180 [SK_DISK_TYPE_SUNPLUS] = "sunplus",
181 [SK_DISK_TYPE_JMICRON] = "jmicron",
182 [SK_DISK_TYPE_NONE] = "none",
183 [SK_DISK_TYPE_AUTO] = "auto",
185 /* %STRINGPOOLSTOP% */
187 if (type >= _SK_DISK_TYPE_MAX)
190 return _P(map[type]);
193 static const char *disk_type_from_string(const char *s, SkDiskType *type) {
199 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
203 if (!(t = disk_type_to_prefix_string(u)))
208 if (strncmp(s, t, l))
222 static SkBool disk_smart_is_available(SkDisk *d) {
223 return d->identify_valid && !!(d->identify[164] & 1);
226 static SkBool disk_smart_is_enabled(SkDisk *d) {
227 return d->identify_valid && !!(d->identify[170] & 1);
230 static SkBool disk_smart_is_conveyance_test_available(SkDisk *d) {
231 assert(d->smart_data_valid);
233 return !!(d->smart_data[367] & 32);
235 static SkBool disk_smart_is_short_and_extended_test_available(SkDisk *d) {
236 assert(d->smart_data_valid);
238 return !!(d->smart_data[367] & 16);
241 static SkBool disk_smart_is_start_test_available(SkDisk *d) {
242 assert(d->smart_data_valid);
244 return !!(d->smart_data[367] & 1);
247 static SkBool disk_smart_is_abort_test_available(SkDisk *d) {
248 assert(d->smart_data_valid);
250 return !!(d->smart_data[367] & 41);
253 static int disk_linux_ide_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
254 uint8_t *bytes = cmd_data;
257 assert(d->type == SK_DISK_TYPE_LINUX_IDE);
261 case SK_DIRECTION_OUT:
263 /* We could use HDIO_DRIVE_TASKFILE here, but
264 * that's a deprecated ioctl(), hence we don't
265 * do it. And we don't need writing anyway. */
270 case SK_DIRECTION_IN: {
273 /* We have HDIO_DRIVE_CMD which can only read, but not write,
274 * and cannot do LBA. We use it for all read commands. */
276 ioctl_data = alloca(4 + *len);
277 memset(ioctl_data, 0, 4 + *len);
279 ioctl_data[0] = (uint8_t) command; /* COMMAND */
280 ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */
281 ioctl_data[2] = bytes[1]; /* FEATURE */
282 ioctl_data[3] = bytes[3]; /* NSECTOR */
284 if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0)
287 memset(bytes, 0, 12);
288 bytes[11] = ioctl_data[0];
289 bytes[1] = ioctl_data[1];
290 bytes[3] = ioctl_data[2];
292 memcpy(data, ioctl_data+4, *len);
297 case SK_DIRECTION_NONE: {
298 uint8_t ioctl_data[7];
300 /* We have HDIO_DRIVE_TASK which can neither read nor
301 * write, but can do LBA. We use it for all commands that
302 * do neither read nor write */
304 memset(ioctl_data, 0, sizeof(ioctl_data));
306 ioctl_data[0] = (uint8_t) command; /* COMMAND */
307 ioctl_data[1] = bytes[1]; /* FEATURE */
308 ioctl_data[2] = bytes[3]; /* NSECTOR */
310 ioctl_data[3] = bytes[9]; /* LBA LOW */
311 ioctl_data[4] = bytes[8]; /* LBA MID */
312 ioctl_data[5] = bytes[7]; /* LBA HIGH */
313 ioctl_data[6] = bytes[10]; /* SELECT */
315 if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data)))
318 memset(bytes, 0, 12);
319 bytes[11] = ioctl_data[0];
320 bytes[1] = ioctl_data[1];
321 bytes[3] = ioctl_data[2];
323 bytes[9] = ioctl_data[3];
324 bytes[8] = ioctl_data[4];
325 bytes[7] = ioctl_data[5];
327 bytes[10] = ioctl_data[6];
338 /* Sends a SCSI command block */
339 static int sg_io(int fd, int direction,
340 const void *cdb, size_t cdb_len,
341 void *data, size_t data_len,
342 void *sense, size_t sense_len) {
344 struct sg_io_hdr io_hdr;
346 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
348 io_hdr.interface_id = 'S';
349 io_hdr.cmdp = (unsigned char*) cdb;
350 io_hdr.cmd_len = cdb_len;
351 io_hdr.dxferp = data;
352 io_hdr.dxfer_len = data_len;
354 io_hdr.mx_sb_len = sense_len;
355 io_hdr.dxfer_direction = direction;
356 io_hdr.timeout = SK_TIMEOUT;
358 return ioctl(fd, SG_IO, &io_hdr);
361 static int disk_passthrough_16_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
362 uint8_t *bytes = cmd_data;
365 uint8_t *desc = sense+8;
368 static const int direction_map[] = {
369 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
370 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
371 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
374 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_16);
376 /* ATA Pass-Through 16 byte command, as described in "T10 04-262r8
377 * ATA Command Pass-Through":
378 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
380 memset(cdb, 0, sizeof(cdb));
382 cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */
384 if (direction == SK_DIRECTION_NONE) {
385 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
386 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
388 } else if (direction == SK_DIRECTION_IN) {
389 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
390 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
392 } else if (direction == SK_DIRECTION_OUT) {
393 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
394 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
397 cdb[3] = bytes[0]; /* FEATURES */
400 cdb[5] = bytes[2]; /* SECTORS */
403 cdb[8] = bytes[9]; /* LBA LOW */
404 cdb[10] = bytes[8]; /* LBA MID */
405 cdb[12] = bytes[7]; /* LBA HIGH */
407 cdb[13] = bytes[10] & 0x4F; /* SELECT */
408 cdb[14] = (uint8_t) command;
410 memset(sense, 0, sizeof(sense));
412 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
415 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
420 memset(bytes, 0, 12);
428 bytes[10] = desc[12];
429 bytes[11] = desc[13];
434 static int disk_passthrough_12_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
435 uint8_t *bytes = cmd_data;
438 uint8_t *desc = sense+8;
441 static const int direction_map[] = {
442 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
443 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
444 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
447 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12);
449 /* ATA Pass-Through 12 byte command, as described in "T10 04-262r8
450 * ATA Command Pass-Through":
451 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
453 memset(cdb, 0, sizeof(cdb));
455 cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */
457 if (direction == SK_DIRECTION_NONE) {
458 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
459 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
461 } else if (direction == SK_DIRECTION_IN) {
462 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
463 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
465 } else if (direction == SK_DIRECTION_OUT) {
466 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
467 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
470 cdb[3] = bytes[1]; /* FEATURES */
471 cdb[4] = bytes[3]; /* SECTORS */
473 cdb[5] = bytes[9]; /* LBA LOW */
474 cdb[6] = bytes[8]; /* LBA MID */
475 cdb[7] = bytes[7]; /* LBA HIGH */
477 cdb[8] = bytes[10] & 0x4F; /* SELECT */
478 cdb[9] = (uint8_t) command;
480 memset(sense, 0, sizeof(sense));
482 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
485 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
490 memset(bytes, 0, 12);
492 bytes[1] = desc[3]; /* FEATURES */
493 bytes[2] = desc[4]; /* STATUS */
494 bytes[3] = desc[5]; /* SECTORS */
495 bytes[9] = desc[7]; /* LBA LOW */
496 bytes[8] = desc[9]; /* LBA MID */
497 bytes[7] = desc[11]; /* LBA HIGH */
498 bytes[10] = desc[12]; /* SELECT */
499 bytes[11] = desc[13]; /* ERROR */
504 static int disk_sunplus_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
505 uint8_t *bytes = cmd_data;
507 uint8_t sense[32], buf[8];
509 static const int direction_map[] = {
510 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
511 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
512 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
515 assert(d->type == SK_DISK_TYPE_SUNPLUS);
517 /* SunplusIT specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
519 memset(cdb, 0, sizeof(cdb));
521 cdb[0] = 0xF8; /* OPERATION CODE: Sunplus specific */
522 cdb[1] = 0x00; /* Subcommand: Pass-thru */
525 if (direction == SK_DIRECTION_NONE)
526 cdb[3] = 0x00; /* protocol */
527 else if (direction == SK_DIRECTION_IN)
528 cdb[3] = 0x10; /* protocol */
529 else if (direction == SK_DIRECTION_OUT)
530 cdb[3] = 0x11; /* protocol */
532 cdb[4] = bytes[3]; /* size? */
533 cdb[5] = bytes[1]; /* FEATURES */
534 cdb[6] = bytes[3]; /* SECTORS */
535 cdb[7] = bytes[9]; /* LBA LOW */
536 cdb[8] = bytes[8]; /* LBA MID */
537 cdb[9] = bytes[7]; /* LBA HIGH */
538 cdb[10] = bytes[10] | 0xA0; /* SELECT */
539 cdb[11] = (uint8_t) command;
541 memset(sense, 0, sizeof(sense));
544 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
547 memset(cdb, 0, sizeof(cdb));
553 memset(buf, 0, sizeof(buf));
555 /* Ask for response */
556 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), buf, sizeof(buf), sense, sizeof(sense))) < 0)
559 memset(bytes, 0, 12);
561 bytes[2] = buf[1]; /* ERROR */
562 bytes[3] = buf[2]; /* SECTORS */
563 bytes[9] = buf[3]; /* LBA LOW */
564 bytes[8] = buf[4]; /* LBA MID */
565 bytes[7] = buf[5]; /* LBA HIGH */
566 bytes[10] = buf[6]; /* SELECT */
567 bytes[11] = buf[7]; /* STATUS */
572 static int disk_jmicron_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* _data, size_t *_len) {
573 uint8_t *bytes = cmd_data;
578 SkBool is_smart_status = FALSE;
580 size_t len = _len ? *_len : 0;
581 uint8_t smart_status = 0;
583 static const int direction_map[] = {
584 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
585 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
586 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
589 assert(d->type == SK_DISK_TYPE_JMICRON);
591 /* JMicron specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
593 memset(cdb, 0, sizeof(cdb));
595 cdb[0] = 0xdf; /* operation code */
598 cdb[3] = 0x00; /* size HI */
599 cdb[4] = sizeof(port); /* size LO */
601 cdb[6] = 0x72; /* register address HI */
602 cdb[7] = 0x0f; /* register address LO */
608 memset(sense, 0, sizeof(sense));
610 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), &port, sizeof(port), sense, sizeof(sense))) < 0)
613 /* Port & 0x04 is port #0, Port & 0x40 is port #1 */
617 cdb[0] = 0xdf; /* OPERATION CODE: 12 byte pass through */
619 if (command == SK_ATA_COMMAND_SMART && bytes[1] == SK_SMART_COMMAND_RETURN_STATUS) {
620 /* We need to rewrite the SMART status request */
621 is_smart_status = TRUE;
622 direction = SK_DIRECTION_IN;
623 data = &smart_status;
624 len = sizeof(smart_status);
626 } else if (direction == SK_DIRECTION_NONE)
628 else if (direction == SK_DIRECTION_IN)
630 else if (direction == SK_DIRECTION_OUT)
635 cdb[3] = (uint8_t) (len >> 8);
636 cdb[4] = (uint8_t) (len & 0xFF);
638 cdb[5] = bytes[1]; /* FEATURES */
639 cdb[6] = bytes[3]; /* SECTORS */
641 cdb[7] = bytes[9]; /* LBA LOW */
642 cdb[8] = bytes[8]; /* LBA MID */
643 cdb[9] = bytes[7]; /* LBA HIGH */
645 cdb[10] = bytes[10] | ((port & 0x04) ? 0xA0 : 0xB0); /* SELECT */
646 cdb[11] = (uint8_t) command;
648 memset(sense, 0, sizeof(sense));
650 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len, sense, sizeof(sense))) < 0)
653 memset(bytes, 0, 12);
655 if (is_smart_status) {
656 if (smart_status == 0x01 || smart_status == 0xc2) {
657 bytes[7] = 0xc2; /* LBA HIGH */
658 bytes[8] = 0x4f; /* LBA MID */
659 } else if (smart_status == 0x00 || smart_status == 0x2c) {
660 bytes[7] = 0x2c; /* LBA HIGH */
661 bytes[8] = 0xf4; /* LBA MID */
667 cdb[0] = 0xdf; /* operation code */
670 cdb[3] = 0x00; /* size HI */
671 cdb[4] = sizeof(regbuf); /* size LO */
673 cdb[6] = (port & 0x04) ? 0x80 : 0x90; /* register address HI */
674 cdb[7] = 0x00; /* register address LO */
680 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), regbuf, sizeof(regbuf), sense, sizeof(sense))) < 0)
683 bytes[2] = regbuf[14]; /* STATUS */
684 bytes[3] = regbuf[0]; /* SECTORS */
685 bytes[9] = regbuf[6]; /* LBA LOW */
686 bytes[8] = regbuf[4]; /* LBA MID */
687 bytes[7] = regbuf[10]; /* LBA HIGH */
688 bytes[10] = regbuf[9]; /* SELECT */
689 bytes[11] = regbuf[13]; /* ERROR */
695 static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
697 static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) = {
698 [SK_DISK_TYPE_LINUX_IDE] = disk_linux_ide_command,
699 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = disk_passthrough_12_command,
700 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = disk_passthrough_16_command,
701 [SK_DISK_TYPE_SUNPLUS] = disk_sunplus_command,
702 [SK_DISK_TYPE_JMICRON] = disk_jmicron_command,
703 [SK_DISK_TYPE_BLOB] = NULL,
704 [SK_DISK_TYPE_AUTO] = NULL,
705 [SK_DISK_TYPE_NONE] = NULL
709 assert(d->type <= _SK_DISK_TYPE_MAX);
710 assert(direction <= _SK_DIRECTION_MAX);
712 assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0));
713 assert(direction != SK_DIRECTION_NONE || (!data && !len));
715 if (!disk_command_table[d->type]) {
720 return disk_command_table[d->type](d, command, direction, cmd_data, data, len);
723 static int disk_identify_device(SkDisk *d) {
729 if (d->type == SK_DISK_TYPE_BLOB)
732 memset(d->identify, 0, len);
733 memset(cmd, 0, sizeof(cmd));
737 if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0)
745 /* Check if IDENTIFY data is all NULs */
746 for (p = d->identify; p < (const uint8_t*) d->identify+len; p++)
757 d->identify_valid = TRUE;
762 int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake) {
767 if (!d->identify_valid) {
772 if (d->type == SK_DISK_TYPE_BLOB) {
777 memset(cmd, 0, sizeof(cmd));
779 if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
782 if (cmd[0] != 0 || (ntohs(cmd[5]) & 1) != 0) {
787 status = ntohs(cmd[1]) & 0xFF;
788 *awake = status == 0xFF || status == 0x80; /* idle and active/idle is considered awake */
793 static int disk_smart_enable(SkDisk *d, SkBool b) {
796 if (!disk_smart_is_available(d)) {
801 if (d->type == SK_DISK_TYPE_BLOB) {
806 memset(cmd, 0, sizeof(cmd));
808 cmd[0] = htons(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS);
809 cmd[2] = htons(0x0000U);
810 cmd[3] = htons(0x00C2U);
811 cmd[4] = htons(0x4F00U);
813 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0);
816 int sk_disk_smart_read_data(SkDisk *d) {
821 if (!disk_smart_is_available(d)) {
826 if (d->type == SK_DISK_TYPE_BLOB)
829 memset(cmd, 0, sizeof(cmd));
831 cmd[0] = htons(SK_SMART_COMMAND_READ_DATA);
833 cmd[2] = htons(0x0000U);
834 cmd[3] = htons(0x00C2U);
835 cmd[4] = htons(0x4F00U);
837 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0)
840 d->smart_data_valid = TRUE;
845 static int disk_smart_read_thresholds(SkDisk *d) {
850 if (!disk_smart_is_available(d)) {
855 if (d->type == SK_DISK_TYPE_BLOB)
858 memset(cmd, 0, sizeof(cmd));
860 cmd[0] = htons(SK_SMART_COMMAND_READ_THRESHOLDS);
862 cmd[2] = htons(0x0000U);
863 cmd[3] = htons(0x00C2U);
864 cmd[4] = htons(0x4F00U);
866 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_thresholds, &len)) < 0)
869 d->smart_thresholds_valid = TRUE;
874 int sk_disk_smart_status(SkDisk *d, SkBool *good) {
878 if (!disk_smart_is_available(d)) {
883 if (d->type == SK_DISK_TYPE_BLOB) {
885 if (d->blob_smart_status_valid) {
886 *good = d->blob_smart_status;
894 memset(cmd, 0, sizeof(cmd));
896 cmd[0] = htons(SK_SMART_COMMAND_RETURN_STATUS);
897 cmd[1] = htons(0x0000U);
898 cmd[3] = htons(0x00C2U);
899 cmd[4] = htons(0x4F00U);
901 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
904 /* SAT/USB bridges truncate packets, so we only check for 4F,
905 * not for 2C on those */
906 if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) &&
907 cmd[4] == htons(0x4F00U))
909 else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) &&
910 cmd[4] == htons(0xF400U))
920 int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) {
924 if (!disk_smart_is_available(d)) {
929 if (d->type == SK_DISK_TYPE_BLOB) {
934 if (!d->smart_data_valid)
935 if ((ret = sk_disk_smart_read_data(d)) < 0)
938 assert(d->smart_data_valid);
940 if (test != SK_SMART_SELF_TEST_SHORT &&
941 test != SK_SMART_SELF_TEST_EXTENDED &&
942 test != SK_SMART_SELF_TEST_CONVEYANCE &&
943 test != SK_SMART_SELF_TEST_ABORT) {
948 if (!disk_smart_is_start_test_available(d)
949 || (test == SK_SMART_SELF_TEST_ABORT && !disk_smart_is_abort_test_available(d))
950 || ((test == SK_SMART_SELF_TEST_SHORT || test == SK_SMART_SELF_TEST_EXTENDED) && !disk_smart_is_short_and_extended_test_available(d))
951 || (test == SK_SMART_SELF_TEST_CONVEYANCE && !disk_smart_is_conveyance_test_available(d))) {
956 if (test == SK_SMART_SELF_TEST_ABORT &&
957 !disk_smart_is_abort_test_available(d)) {
962 memset(cmd, 0, sizeof(cmd));
964 cmd[0] = htons(SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE);
965 cmd[2] = htons(0x0000U);
966 cmd[3] = htons(0x00C2U);
967 cmd[4] = htons(0x4F00U | (uint16_t) test);
969 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, NULL);
972 static void swap_strings(char *s, size_t len) {
973 assert((len & 1) == 0);
975 for (; len > 0; s += 2, len -= 2) {
983 static void clean_strings(char *s) {
987 if (*e < ' ' || *e >= 127)
991 static void drop_spaces(char *s) {
993 SkBool prev_space = FALSE;
1016 static void read_string(char *d, uint8_t *s, size_t len) {
1019 swap_strings(d, len);
1024 int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) {
1028 if (!d->identify_valid) {
1033 read_string(d->identify_parsed_data.serial, d->identify+20, 20);
1034 read_string(d->identify_parsed_data.firmware, d->identify+46, 8);
1035 read_string(d->identify_parsed_data.model, d->identify+54, 40);
1037 *ipd = &d->identify_parsed_data;
1042 int sk_disk_smart_is_available(SkDisk *d, SkBool *b) {
1046 if (!d->identify_valid) {
1051 *b = disk_smart_is_available(d);
1055 int sk_disk_identify_is_available(SkDisk *d, SkBool *b) {
1059 *b = d->identify_valid;
1063 const char *sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status) {
1065 /* %STRINGPOOLSTART% */
1066 static const char* const map[] = {
1067 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.",
1068 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.",
1069 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.",
1070 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.",
1071 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.",
1072 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.",
1073 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status"
1075 /* %STRINGPOOLSTOP% */
1077 if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX)
1080 return _P(map[status]);
1083 const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status) {
1085 /* %STRINGPOOLSTART% */
1086 static const char* const map[] = {
1087 [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.",
1088 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED] = "The self-test routine was aborted by the host.",
1089 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED] = "The self-test routine was interrupted by the host with a hardware or software reset.",
1090 [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.",
1091 [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.",
1092 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL] = "The previous self-test completed having the electrical element of the test failed.",
1093 [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.",
1094 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ] = "The previous self-test completed having the read element of the test failed.",
1095 [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.",
1096 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS] = "Self-test routine in progress"
1098 /* %STRINGPOOLSTOP% */
1100 if (status >= _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX)
1103 return _P(map[status]);
1106 const char* sk_smart_self_test_to_string(SkSmartSelfTest test) {
1109 case SK_SMART_SELF_TEST_SHORT:
1111 case SK_SMART_SELF_TEST_EXTENDED:
1113 case SK_SMART_SELF_TEST_CONVEYANCE:
1114 return "conveyance";
1115 case SK_SMART_SELF_TEST_ABORT:
1122 SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) {
1125 if (!d->start_test_available)
1129 case SK_SMART_SELF_TEST_SHORT:
1130 case SK_SMART_SELF_TEST_EXTENDED:
1131 return d->short_and_extended_test_available;
1132 case SK_SMART_SELF_TEST_CONVEYANCE:
1133 return d->conveyance_test_available;
1134 case SK_SMART_SELF_TEST_ABORT:
1135 return d->abort_test_available;
1141 unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) {
1144 if (!sk_smart_self_test_available(d, test))
1148 case SK_SMART_SELF_TEST_SHORT:
1149 return d->short_test_polling_minutes;
1150 case SK_SMART_SELF_TEST_EXTENDED:
1151 return d->extended_test_polling_minutes;
1152 case SK_SMART_SELF_TEST_CONVEYANCE:
1153 return d->conveyance_test_polling_minutes;
1159 static void make_pretty(SkSmartAttributeParsedData *a) {
1160 uint64_t fourtyeight;
1165 if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN)
1169 ((uint64_t) a->raw[0]) |
1170 (((uint64_t) a->raw[1]) << 8) |
1171 (((uint64_t) a->raw[2]) << 16) |
1172 (((uint64_t) a->raw[3]) << 24) |
1173 (((uint64_t) a->raw[4]) << 32) |
1174 (((uint64_t) a->raw[5]) << 40);
1176 if (!strcmp(a->name, "spin-up-time"))
1177 a->pretty_value = fourtyeight & 0xFFFF;
1178 else if (!strcmp(a->name, "airflow-temperature-celsius") ||
1179 !strcmp(a->name, "temperature-celsius") ||
1180 !strcmp(a->name, "temperature-celsius-2"))
1181 a->pretty_value = (fourtyeight & 0xFFFF)*1000 + 273150;
1182 else if (!strcmp(a->name, "temperature-centi-celsius"))
1183 a->pretty_value = (fourtyeight & 0xFFFF)*100 + 273150;
1184 else if (!strcmp(a->name, "power-on-minutes"))
1185 a->pretty_value = fourtyeight * 60 * 1000;
1186 else if (!strcmp(a->name, "power-on-seconds") ||
1187 !strcmp(a->name, "power-on-seconds-2"))
1188 a->pretty_value = fourtyeight * 1000;
1189 else if (!strcmp(a->name, "power-on-half-minutes"))
1190 a->pretty_value = fourtyeight * 30 * 1000;
1191 else if (!strcmp(a->name, "power-on-hours") ||
1192 !strcmp(a->name, "loaded-hours") ||
1193 !strcmp(a->name, "head-flying-hours"))
1194 a->pretty_value = (fourtyeight & 0xFFFFFFFFU) * 60 * 60 * 1000;
1195 else if (!strcmp(a->name, "reallocated-sector-count") ||
1196 !strcmp(a->name, "current-pending-sector"))
1197 a->pretty_value = fourtyeight & 0xFFFFFFFFU;
1199 a->pretty_value = fourtyeight;
1202 typedef void (*SkSmartAttributeVerify)(SkDisk *d, SkSmartAttributeParsedData *a);
1204 typedef struct SkSmartAttributeInfo {
1206 SkSmartAttributeUnit unit;
1207 SkSmartAttributeVerify verify;
1208 } SkSmartAttributeInfo;
1210 static void verify_temperature(SkDisk *d, SkSmartAttributeParsedData *a) {
1212 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MKELVIN);
1214 if (a->pretty_value < SK_MKELVIN_VALID_MIN ||
1215 a->pretty_value > SK_MKELVIN_VALID_MAX) {
1216 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1217 d->attribute_verification_bad = TRUE;
1221 static void verify_short_time(SkDisk *d, SkSmartAttributeParsedData *a) {
1223 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS);
1225 if (a->pretty_value < SK_MSECOND_VALID_MIN ||
1226 a->pretty_value > SK_MSECOND_VALID_SHORT_MAX) {
1227 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1228 d->attribute_verification_bad = TRUE;
1232 static void verify_long_time(SkDisk *d, SkSmartAttributeParsedData *a) {
1234 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS);
1236 if (a->pretty_value < SK_MSECOND_VALID_MIN ||
1237 a->pretty_value > SK_MSECOND_VALID_LONG_MAX) {
1238 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1239 d->attribute_verification_bad = TRUE;
1243 static void verify_sectors(SkDisk *d, SkSmartAttributeParsedData *a) {
1244 uint64_t max_sectors;
1248 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS);
1250 max_sectors = d->size / 512ULL;
1252 if (max_sectors > 0 && a->pretty_value > max_sectors) {
1253 a->pretty_value = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1254 d->attribute_verification_bad = TRUE;
1256 if ((!strcmp(a->name, "reallocated-sector-count") ||
1257 !strcmp(a->name, "current-pending-sector")) &&
1258 a->pretty_value > 0)
1263 /* This data is stolen from smartmontools */
1265 /* %STRINGPOOLSTART% */
1266 static const SkSmartAttributeInfo const attribute_info[256] = {
1267 [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1268 [2] = { "throughput-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1269 [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1270 [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1271 [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1272 [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1273 [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1274 [8] = { "seek-time-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1275 [9] = { "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1276 [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1277 [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1278 [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1279 [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1280 [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1281 [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1282 [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1283 [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1284 [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1285 [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1286 [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1287 [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1288 [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1289 [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1290 [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1291 [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1292 [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1293 [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1294 [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1295 [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1296 [204] = { "shock-count-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1297 [205] = { "shock-rate-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1298 [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1299 [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1300 [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1301 [209] = { "offline-seek-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1302 [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1303 [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1304 [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1305 [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1306 [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1307 [225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1308 [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1309 [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1310 [228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1311 [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1312 [231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1314 /* http://www.adtron.com/pdf/SMART_for_XceedLite_SATA_RevA.pdf */
1315 [232] = { "endurance-remaining", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1316 [233] = { "power-on-seconds-2", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1317 [234] = { "uncorrectable-ecc-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, NULL },
1318 [235] = { "good-block-rate", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1320 [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1321 [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }
1323 /* %STRINGPOOLSTOP% */
1325 typedef enum SkSmartQuirk {
1326 SK_SMART_QUIRK_9_POWERONMINUTES = 0x0001,
1327 SK_SMART_QUIRK_9_POWERONSECONDS = 0x0002,
1328 SK_SMART_QUIRK_9_POWERONHALFMINUTES = 0x0004,
1329 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x0008,
1330 SK_SMART_QUIRK_193_LOADUNLOAD = 0x0010,
1331 SK_SMART_QUIRK_194_10XCELSIUS = 0x0020,
1332 SK_SMART_QUIRK_194_UNKNOWN = 0x0040,
1333 SK_SMART_QUIRK_200_WRITEERRORCOUNT = 0x0080,
1334 SK_SMART_QUIRK_201_DETECTEDTACOUNT = 0x0100,
1335 SK_SMART_QUIRK_5_UNKNOWN = 0x0200,
1336 SK_SMART_QUIRK_9_UNKNOWN = 0x0400,
1337 SK_SMART_QUIRK_197_UNKNOWN = 0x0800,
1338 SK_SMART_QUIRK_198_UNKNOWN = 0x1000,
1341 /* %STRINGPOOLSTART% */
1342 static const char *quirk_name[] = {
1345 "9_POWERONHALFMINUTES",
1346 "192_EMERGENCYRETRACTCYCLECT",
1350 "200_WRITEERRORCOUNT",
1351 "201_DETECTEDTACOUNT",
1358 /* %STRINGPOOLSTOP% */
1360 typedef struct SkSmartQuirkDatabase {
1362 const char *firmware;
1364 } SkSmartQuirkDatabase;
1366 static const SkSmartQuirkDatabase quirk_database[] = { {
1370 "FUJITSU MHY2120BH|"
1373 "^0085000B$", /* seems to be specific to this firmware */
1374 SK_SMART_QUIRK_9_POWERONMINUTES|
1375 SK_SMART_QUIRK_197_UNKNOWN|
1376 SK_SMART_QUIRK_198_UNKNOWN
1378 "^FUJITSU MHR2040AT$",
1380 SK_SMART_QUIRK_9_POWERONSECONDS|
1381 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1382 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1384 "^FUJITSU MHS20[6432]0AT( .)?$",
1386 SK_SMART_QUIRK_9_POWERONSECONDS|
1387 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1388 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1389 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1393 "FUJITSU MHG2...ATU?.*|"
1394 "FUJITSU MHH2...ATU?.*|"
1395 "FUJITSU MHJ2...ATU?.*|"
1396 "FUJITSU MHK2...ATU?.*|"
1397 "FUJITSU MHL2300AT|"
1398 "FUJITSU MHM2(20|15|10|06)0AT|"
1399 "FUJITSU MHN2...AT|"
1400 "FUJITSU MHR2020AT|"
1401 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1402 "FUJITSU MHU2...ATU?.*|"
1403 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1404 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1407 SK_SMART_QUIRK_9_POWERONSECONDS
1413 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1416 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1423 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1424 SK_SMART_QUIRK_194_10XCELSIUS
1426 "^SAMSUNG SP40A2H$",
1428 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1430 "^SAMSUNG SP80A4H$",
1432 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1434 "^SAMSUNG SP8004H$",
1436 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1441 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1442 "Maxtor 4G(120J6|160J[68])|"
1443 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1446 SK_SMART_QUIRK_9_POWERONMINUTES|
1447 SK_SMART_QUIRK_194_UNKNOWN
1450 "Maxtor 2F0[234]0[JL]0|"
1451 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1452 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1453 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1454 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1455 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1456 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1457 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1458 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1459 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1460 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1461 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1462 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1463 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1464 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1465 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1466 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1467 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1468 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1469 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1470 "Maxtor (98196H8|96147H6)|"
1471 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1472 "Maxtor 6(E0[234]|K04)0L0|"
1473 "Maxtor 6(B(30|25|20|16|12|10|08)0[MPRS]|L(080[MLP]|(100|120)[MP]|160[MP]|200[MPRS]|250[RS]|300[RS]))0|"
1474 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1475 "Maxtor 7Y250[PM]0|"
1476 "Maxtor [45]A(25|30|32)0[JN]0|"
1477 "Maxtor 7L(25|30)0[SR]0"
1480 SK_SMART_QUIRK_9_POWERONMINUTES
1486 "HITACHI_DK14FA-20B|"
1487 "HITACHI_DK23..-..B?|"
1488 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1489 "HE[JN]4230[23]0F9AT00|"
1490 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1493 SK_SMART_QUIRK_9_POWERONMINUTES|
1494 SK_SMART_QUIRK_193_LOADUNLOAD
1496 "^HTS541010G9SA00$",
1498 SK_SMART_QUIRK_5_UNKNOWN
1506 static int match(const char*regex, const char *s, SkBool *result) {
1512 if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1517 if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1519 if (k != REG_NOMATCH) {
1533 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1535 const SkSmartQuirkDatabase *db;
1539 for (db = quirk_database; db->model || db->firmware; db++) {
1542 SkBool matching = FALSE;
1544 if ((k = match(db->model, model, &matching)) < 0)
1552 SkBool matching = FALSE;
1554 if ((k = match(db->firmware, firmware, &matching)) < 0)
1568 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1569 const SkIdentifyParsedData *ipd;
1570 SkSmartQuirk quirk = 0;
1572 /* These are the complex ones */
1573 if (sk_disk_identify_parse(d, &ipd) < 0)
1576 if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1583 if (quirk & SK_SMART_QUIRK_5_UNKNOWN)
1589 /* %STRINGPOOLSTART% */
1590 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1591 static const SkSmartAttributeInfo a = {
1592 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1596 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1597 static const SkSmartAttributeInfo a = {
1598 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1602 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1603 static const SkSmartAttributeInfo a = {
1604 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1607 } else if (quirk & SK_SMART_QUIRK_9_UNKNOWN)
1609 /* %STRINGPOOLSTOP% */
1614 /* %STRINGPOOLSTART% */
1615 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1616 static const SkSmartAttributeInfo a = {
1617 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1621 /* %STRINGPOOLSTOP% */
1626 /* %STRINGPOOLSTART% */
1627 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1628 static const SkSmartAttributeInfo a = {
1629 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature
1632 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1634 /* %STRINGPOOLSTOP% */
1639 if (quirk & SK_SMART_QUIRK_197_UNKNOWN)
1645 if (quirk & SK_SMART_QUIRK_198_UNKNOWN)
1651 /* %STRINGPOOLSTART% */
1652 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1653 static const SkSmartAttributeInfo a = {
1654 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1658 /* %STRINGPOOLSTOP% */
1663 /* %STRINGPOOLSTART% */
1664 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1665 static const SkSmartAttributeInfo a = {
1666 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1670 /* %STRINGPOOLSTOP% */
1676 /* These are the simple cases */
1677 if (attribute_info[id].name)
1678 return &attribute_info[id];
1683 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1685 if (!d->smart_data_valid) {
1690 switch (d->smart_data[362]) {
1693 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1698 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1702 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1707 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1712 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1717 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1721 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1725 d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1726 d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1728 d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1730 d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1731 d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1732 d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1733 d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1735 d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1736 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]);
1737 d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1739 *spd = &d->smart_parsed_data;
1744 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1748 if (!d->smart_thresholds_valid)
1751 for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1758 a->threshold = p[1];
1759 a->threshold_valid = p[1] != 0xFE;
1761 a->good_now_valid = FALSE;
1763 a->good_in_the_past_valid = FALSE;
1764 a->good_in_the_past = TRUE;
1766 /* Always-Fail and Always-Passing thresholds are not relevant
1767 * for our assessment. */
1768 if (p[1] >= 1 && p[1] <= 0xFD) {
1770 if (a->worst_value_valid) {
1771 a->good_in_the_past = a->good_in_the_past && (a->worst_value > a->threshold);
1772 a->good_in_the_past_valid = TRUE;
1775 if (a->current_value_valid) {
1776 a->good_now = a->good_now && (a->current_value > a->threshold);
1777 a->good_now_valid = TRUE;
1782 (a->good_now_valid && !a->good_now) ||
1783 (a->good_in_the_past_valid && !a->good_in_the_past);
1788 a->threshold_valid = FALSE;
1789 a->good_now_valid = FALSE;
1790 a->good_in_the_past_valid = FALSE;
1794 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1798 if (!d->smart_data_valid) {
1803 for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1804 SkSmartAttributeParsedData a;
1805 const SkSmartAttributeInfo *i;
1811 memset(&a, 0, sizeof(a));
1813 a.current_value = p[3];
1814 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1815 a.worst_value = p[4];
1816 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
1818 a.flags = ((uint16_t) p[2] << 8) | p[1];
1819 a.prefailure = !!(p[1] & 1);
1820 a.online = !!(p[1] & 2);
1822 memcpy(a.raw, p+5, 6);
1824 if ((i = lookup_attribute(d, p[0]))) {
1825 a.name = _P(i->name);
1826 a.pretty_unit = i->unit;
1828 if (asprintf(&an, "attribute-%u", a.id) < 0) {
1834 a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1839 find_threshold(d, &a);
1844 cb(d, &a, userdata);
1851 static const char *yes_no(SkBool b) {
1852 return b ? "yes" : "no";
1855 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
1857 /* %STRINGPOOLSTART% */
1858 const char * const map[] = {
1859 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
1860 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
1861 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
1862 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
1863 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK"
1865 /* %STRINGPOOLSTOP% */
1867 if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
1870 return _P(map[unit]);
1873 struct attr_helper {
1878 static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1880 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
1883 if (!strcmp(a->name, "temperature-centi-celsius") ||
1884 !strcmp(a->name, "temperature-celsius") ||
1885 !strcmp(a->name, "temperature-celsius-2") ||
1886 !strcmp(a->name, "airflow-temperature-celsius")) {
1888 if (!ah->found || a->pretty_value > *ah->value)
1889 *ah->value = a->pretty_value;
1895 int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
1896 struct attr_helper ah;
1904 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
1915 static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1917 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
1920 if (!strcmp(a->name, "power-on-minutes") ||
1921 !strcmp(a->name, "power-on-seconds") ||
1922 !strcmp(a->name, "power-on-seconds-2") ||
1923 !strcmp(a->name, "power-on-half-minutes") ||
1924 !strcmp(a->name, "power-on-hours")) {
1926 if (!ah->found || a->pretty_value > *ah->value)
1927 *ah->value = a->pretty_value;
1933 int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
1934 struct attr_helper ah;
1940 ah.value = mseconds;
1942 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
1953 static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1955 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
1958 if (!strcmp(a->name, "power-cycle-count")) {
1960 if (!ah->found || a->pretty_value > *ah->value)
1961 *ah->value = a->pretty_value;
1967 int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
1968 struct attr_helper ah;
1976 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
1987 static void reallocated_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1989 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1992 if (!strcmp(a->name, "reallocated-sector-count")) {
1994 if (!ah->found || a->pretty_value > *ah->value)
1995 *ah->value = a->pretty_value;
2001 static void pending_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
2003 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
2006 if (!strcmp(a->name, "current-pending-sector")) {
2008 if (!ah->found || a->pretty_value > *ah->value)
2009 *ah->value = a->pretty_value;
2015 int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
2016 struct attr_helper ah1, ah2;
2017 uint64_t sectors1, sectors2;
2023 ah1.value = §ors1;
2025 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) reallocated_cb, &ah1) < 0)
2029 ah2.value = §ors2;
2031 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) pending_cb, &ah2) < 0)
2034 if (!ah1.found && !ah2.found) {
2039 if (ah1.found && ah2.found)
2040 *sectors = sectors1 + sectors2;
2042 *sectors = sectors1;
2044 *sectors = sectors2;
2049 const char* sk_smart_overall_to_string(SkSmartOverall overall) {
2051 /* %STRINGPOOLSTART% */
2052 const char * const map[] = {
2053 [SK_SMART_OVERALL_GOOD] = "GOOD",
2054 [SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST] = "BAD_ATTRIBUTE_IN_THE_PAST",
2055 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR",
2056 [SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW] = "BAD_ATTRIBUTE_NOW",
2057 [SK_SMART_OVERALL_BAD_SECTOR_MANY] = "BAD_SECTOR_MANY",
2058 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
2060 /* %STRINGPOOLSTOP% */
2062 if (overall >= _SK_SMART_OVERALL_MAX)
2065 return _P(map[overall]);
2068 static void bad_attribute_now_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
2069 if (a->prefailure && a->good_now_valid && !a->good_now)
2073 static void bad_attribute_in_the_past_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
2074 if (a->prefailure && a->good_in_the_past_valid && !a->good_in_the_past)
2078 static uint64_t u64log2(uint64_t n) {
2093 int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
2095 uint64_t sectors, sector_threshold;
2100 /* First, check SMART self-assesment */
2101 if (sk_disk_smart_status(d, &good) < 0)
2105 *overall = SK_SMART_OVERALL_BAD_STATUS;
2109 /* Second, check if the number of bad sectors is greater than
2110 * a certain threshold */
2111 if (sk_disk_smart_get_bad(d, §ors) < 0) {
2112 if (errno != ENOENT)
2117 /* We use log2(n_sectors) as a threshold here. We had to pick
2118 * something, and this makes a bit of sense, or doesn't it? */
2119 sector_threshold = u64log2(d->size/512);
2121 if (sectors >= sector_threshold) {
2122 *overall = SK_SMART_OVERALL_BAD_SECTOR_MANY;
2127 /* Third, check if any of the SMART attributes is bad */
2129 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_now_cb, &good) < 0)
2133 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW;
2137 /* Fourth, check if there are any bad sectors at all */
2139 *overall = SK_SMART_OVERALL_BAD_SECTOR;
2143 /* Fifth, check if any of the SMART attributes ever was bad */
2145 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_in_the_past_cb, &good) < 0)
2149 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST;
2153 /* Sixth, there's really nothing to complain about, so give it a pass */
2154 *overall = SK_SMART_OVERALL_GOOD;
2158 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
2163 snprintf(s, len, "%u", id);
2170 static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
2172 switch (pretty_unit) {
2173 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
2175 if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
2176 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
2177 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
2178 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
2179 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
2180 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
2181 else if (pretty_value >= 1000LLU*60LLU*60LLU)
2182 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
2183 else if (pretty_value >= 1000LLU*60LLU)
2184 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
2185 else if (pretty_value >= 1000LLU)
2186 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
2188 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
2192 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
2193 snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
2196 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
2197 snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
2200 case SK_SMART_ATTRIBUTE_UNIT_NONE:
2201 snprintf(s, len, "%llu", (unsigned long long) pretty_value);
2204 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
2205 snprintf(s, len, "n/a");
2208 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
2217 #define HIGHLIGHT "\x1B[1m"
2218 #define ENDHIGHLIGHT "\x1B[0m"
2220 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2223 char tt[32], tw[32], tc[32];
2226 snprintf(tt, sizeof(tt), "%3u", a->threshold);
2227 tt[sizeof(tt)-1] = 0;
2228 snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2229 tw[sizeof(tw)-1] = 0;
2230 snprintf(tc, sizeof(tc), "%3u", a->current_value);
2231 tc[sizeof(tc)-1] = 0;
2233 highlight = a->warn && isatty(1);
2236 fprintf(stderr, HIGHLIGHT);
2238 printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-4s %-4s\n",
2240 print_name(name, sizeof(name), a->id, a->name),
2241 a->current_value_valid ? tc : "n/a",
2242 a->worst_value_valid ? tw : "n/a",
2243 a->threshold_valid ? tt : "n/a",
2244 print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2245 a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2246 a->prefailure ? "prefail" : "old-age",
2247 a->online ? "online" : "offline",
2248 a->good_now_valid ? yes_no(a->good_now) : "n/a",
2249 a->good_in_the_past_valid ? yes_no(a->good_in_the_past) : "n/a");
2252 fprintf(stderr, ENDHIGHLIGHT);
2255 int sk_disk_dump(SkDisk *d) {
2257 SkBool awake = FALSE;
2262 printf("Device: %s%s%s\n"
2264 d->name ? disk_type_to_prefix_string(d->type) : "",
2266 d->name ? d->name : "n/a",
2267 disk_type_to_human_string(d->type));
2269 ret = sk_disk_get_size(d, &size);
2271 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2273 printf("Size: %s\n", strerror(errno));
2275 if (d->identify_valid) {
2276 const SkIdentifyParsedData *ipd;
2277 SkSmartQuirk quirk = 0;
2280 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2283 printf("Model: [%s]\n"
2286 "SMART Available: %s\n",
2290 yes_no(disk_smart_is_available(d)));
2292 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2297 for (i = 0; quirk_name[i]; i++)
2299 printf(" %s", _P(quirk_name[i]));
2304 ret = sk_disk_check_sleep_mode(d, &awake);
2305 printf("Awake: %s\n",
2306 ret >= 0 ? yes_no(awake) : strerror(errno));
2308 if (disk_smart_is_available(d)) {
2309 SkSmartOverall overall;
2310 const SkSmartParsedData *spd;
2313 uint64_t value, power_on;
2315 ret = sk_disk_smart_status(d, &good);
2316 printf("%sSMART Disk Health Good: %s%s\n",
2317 ret >= 0 && !good ? HIGHLIGHT : "",
2318 ret >= 0 ? yes_no(good) : strerror(errno),
2319 ret >= 0 && !good ? ENDHIGHLIGHT : "");
2320 if ((ret = sk_disk_smart_read_data(d)) < 0)
2323 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2326 printf("Off-line Data Collection Status: [%s]\n"
2327 "Total Time To Complete Off-Line Data Collection: %u s\n"
2328 "Self-Test Execution Status: [%s]\n"
2329 "Percent Self-Test Remaining: %u%%\n"
2330 "Conveyance Self-Test Available: %s\n"
2331 "Short/Extended Self-Test Available: %s\n"
2332 "Start Self-Test Available: %s\n"
2333 "Abort Self-Test Available: %s\n"
2334 "Short Self-Test Polling Time: %u min\n"
2335 "Extended Self-Test Polling Time: %u min\n"
2336 "Conveyance Self-Test Polling Time: %u min\n",
2337 sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2338 spd->total_offline_data_collection_seconds,
2339 sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2340 spd->self_test_execution_percent_remaining,
2341 yes_no(spd->conveyance_test_available),
2342 yes_no(spd->short_and_extended_test_available),
2343 yes_no(spd->start_test_available),
2344 yes_no(spd->abort_test_available),
2345 spd->short_test_polling_minutes,
2346 spd->extended_test_polling_minutes,
2347 spd->conveyance_test_polling_minutes);
2349 if (sk_disk_smart_get_bad(d, &value) < 0)
2350 printf("Bad Sectors: %s\n", strerror(errno));
2352 printf("%sBad Sectors: %s%s\n",
2353 value > 0 ? HIGHLIGHT : "",
2354 print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2355 value > 0 ? ENDHIGHLIGHT : "");
2357 if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2358 printf("Powered On: %s\n", strerror(errno));
2361 printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2363 if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2364 printf("Power Cycles: %s\n", strerror(errno));
2366 printf("Power Cycles: %llu\n", (unsigned long long) value);
2368 if (value > 0 && power_on > 0)
2369 printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2372 if (sk_disk_smart_get_temperature(d, &value) < 0)
2373 printf("Temperature: %s\n", strerror(errno));
2375 printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2377 printf("Attribute Parsing Verification: %s\n",
2378 d->attribute_verification_bad ? "Bad" : "Good");
2380 if (sk_disk_smart_get_overall(d, &overall) < 0)
2381 printf("Overall Status: %s\n", strerror(errno));
2383 printf("%sOverall Status: %s%s\n",
2384 overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2385 sk_smart_overall_to_string(overall),
2386 overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2388 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-4s %-4s\n",
2401 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2404 printf("ATA SMART not supported.\n");
2409 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2413 if (d->size == (uint64_t) -1) {
2422 static int disk_find_type(SkDisk *d, dev_t devnum) {
2424 struct udev_device *dev = NULL, *usb;
2430 if (!(udev = udev_new())) {
2435 if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2440 if ((a = udev_device_get_property_value(dev, "ID_ATA_SMART_ACCESS"))) {
2443 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
2446 if (!(t = disk_type_to_prefix_string(u)))
2449 if (!strcmp(a, t)) {
2456 d->type = SK_DISK_TYPE_NONE;
2461 if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2462 const char *product, *vendor;
2465 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2466 sscanf(product, "%04x", &pid) != 1) {
2471 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2472 sscanf(vendor, "%04x", &vid) != 1) {
2477 if ((vid == 0x152d && pid == 0x2329)) {
2478 /* Some JMicron bridges seem to choke on SMART
2479 * commands, so let's explicitly black list
2482 * https://bugzilla.redhat.com/show_bug.cgi?id=515881 */
2483 d->type = SK_DISK_TYPE_NONE;
2484 } else if ((vid == 0x0c0b && pid == 0xb159) ||
2485 (vid == 0x04fc && pid == 0x0c25) ||
2486 (vid == 0x04fc && pid == 0x0c15))
2487 d->type = SK_DISK_TYPE_SUNPLUS;
2488 else if ((vid == 0x152d && pid == 0x2336) ||
2489 (vid == 0x152d && pid == 0x2338) ||
2490 (vid == 0x152d && pid == 0x2339))
2491 d->type = SK_DISK_TYPE_JMICRON;
2493 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2495 } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2496 d->type = SK_DISK_TYPE_LINUX_IDE;
2497 else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2498 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2500 d->type = SK_DISK_TYPE_AUTO;
2506 udev_device_unref(dev);
2514 int sk_disk_open(const char *name, SkDisk **_d) {
2521 if (!(d = calloc(1, sizeof(SkDisk)))) {
2527 d->size = (uint64_t) -1;
2530 d->type = SK_DISK_TYPE_BLOB;
2534 d->type = SK_DISK_TYPE_AUTO;
2536 if (!(dn = disk_type_from_string(name, &d->type)))
2539 if (!(d->name = strdup(dn))) {
2544 if ((d->fd = open(d->name,
2545 O_RDONLY|O_NOCTTY|O_NONBLOCK
2555 if ((ret = fstat(d->fd, &st)) < 0)
2558 if (!S_ISBLK(st.st_mode)) {
2564 /* So, it's a block device. Let's make sure the ioctls work */
2565 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2568 if (d->size <= 0 || d->size == (uint64_t) -1) {
2574 /* OK, it's a real block device with a size. Now let's find the suitable API */
2575 if (d->type == SK_DISK_TYPE_AUTO)
2576 if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2579 if (d->type == SK_DISK_TYPE_AUTO) {
2580 /* We have no clue, so let's autotest for a working API */
2581 for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2582 if (disk_identify_device(d) >= 0)
2584 if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2585 d->type = SK_DISK_TYPE_NONE;
2587 disk_identify_device(d);
2589 /* Check if driver can do SMART, and enable if necessary */
2590 if (disk_smart_is_available(d)) {
2592 if (!disk_smart_is_enabled(d)) {
2593 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2596 if ((ret = disk_identify_device(d)) < 0)
2599 if (!disk_smart_is_enabled(d)) {
2606 disk_smart_read_thresholds(d);
2622 void sk_disk_free(SkDisk *d) {
2633 int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2635 SkBool good, have_good = FALSE;
2643 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2644 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2645 (d->smart_thresholds ? 8 + sizeof(d->smart_thresholds) : 0);
2647 if (sk_disk_smart_status(d, &good) >= 0) {
2658 if (!(d->blob = malloc(size))) {
2665 /* These memory accesses are only OK as long as all our
2666 * objects are sensibly aligned, which they are... */
2668 if (d->identify_valid) {
2669 p[0] = SK_BLOB_TAG_IDENTIFY;
2670 p[1] = htonl(sizeof(d->identify));
2673 memcpy(p, d->identify, sizeof(d->identify));
2674 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2678 p[0] = SK_BLOB_TAG_SMART_STATUS;
2680 p[2] = htonl(!!good);
2684 if (d->smart_data_valid) {
2685 p[0] = SK_BLOB_TAG_SMART_DATA;
2686 p[1] = htonl(sizeof(d->smart_data));
2689 memcpy(p, d->smart_data, sizeof(d->smart_data));
2690 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2693 if (d->smart_thresholds_valid) {
2694 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2695 p[1] = htonl(sizeof(d->smart_thresholds));
2698 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2699 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2702 assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2710 int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2713 SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2718 if (d->type != SK_DISK_TYPE_BLOB) {
2728 /* First run, verify if everything makes sense */
2732 uint32_t tag, tsize;
2740 memcpy(&tsize, p+1, 4);
2744 if (left < ntohl(tsize)) {
2751 case SK_BLOB_TAG_IDENTIFY:
2752 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2759 case SK_BLOB_TAG_SMART_STATUS:
2760 if (ntohl(tsize) != 4 || bssv) {
2767 case SK_BLOB_TAG_SMART_DATA:
2768 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2775 case SK_BLOB_TAG_SMART_THRESHOLDS:
2776 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
2784 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2785 left -= ntohl(tsize);
2793 d->identify_valid = idv;
2794 d->smart_data_valid = sdv;
2795 d->smart_thresholds_valid = stv;
2796 d->blob_smart_status_valid = bssv;
2798 /* Second run, actually copy things in */
2802 uint32_t tag, tsize;
2806 memcpy(&tsize, p+1, 4);
2810 assert(left >= ntohl(tsize));
2814 case SK_BLOB_TAG_IDENTIFY:
2815 assert(ntohl(tsize) == sizeof(d->identify));
2816 memcpy(d->identify, p, sizeof(d->identify));
2819 case SK_BLOB_TAG_SMART_STATUS: {
2821 assert(ntohl(tsize) == 4);
2823 d->blob_smart_status = !!ok;
2827 case SK_BLOB_TAG_SMART_DATA:
2828 assert(ntohl(tsize) == sizeof(d->smart_data));
2829 memcpy(d->smart_data, p, sizeof(d->smart_data));
2832 case SK_BLOB_TAG_SMART_THRESHOLDS:
2833 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
2834 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
2838 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2839 left -= ntohl(tsize);