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 a->pretty_value = fourtyeight * 1000;
1188 else if (!strcmp(a->name, "power-on-half-minutes"))
1189 a->pretty_value = fourtyeight * 30 * 1000;
1190 else if (!strcmp(a->name, "power-on-hours") ||
1191 !strcmp(a->name, "loaded-hours") ||
1192 !strcmp(a->name, "head-flying-hours"))
1193 a->pretty_value = (fourtyeight & 0xFFFFFFFFU) * 60 * 60 * 1000;
1194 else if (!strcmp(a->name, "reallocated-sector-count") ||
1195 !strcmp(a->name, "current-pending-sector"))
1196 a->pretty_value = fourtyeight & 0xFFFFFFFFU;
1198 a->pretty_value = fourtyeight;
1201 typedef void (*SkSmartAttributeVerify)(SkDisk *d, SkSmartAttributeParsedData *a);
1203 typedef struct SkSmartAttributeInfo {
1205 SkSmartAttributeUnit unit;
1206 SkSmartAttributeVerify verify;
1207 } SkSmartAttributeInfo;
1209 static void verify_temperature(SkDisk *d, SkSmartAttributeParsedData *a) {
1211 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MKELVIN);
1213 if (a->pretty_value < SK_MKELVIN_VALID_MIN ||
1214 a->pretty_value > SK_MKELVIN_VALID_MAX) {
1215 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1216 d->attribute_verification_bad = TRUE;
1220 static void verify_short_time(SkDisk *d, SkSmartAttributeParsedData *a) {
1222 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS);
1224 if (a->pretty_value < SK_MSECOND_VALID_MIN ||
1225 a->pretty_value > SK_MSECOND_VALID_SHORT_MAX) {
1226 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1227 d->attribute_verification_bad = TRUE;
1231 static void verify_long_time(SkDisk *d, SkSmartAttributeParsedData *a) {
1233 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS);
1235 if (a->pretty_value < SK_MSECOND_VALID_MIN ||
1236 a->pretty_value > SK_MSECOND_VALID_LONG_MAX) {
1237 a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1238 d->attribute_verification_bad = TRUE;
1242 static void verify_sectors(SkDisk *d, SkSmartAttributeParsedData *a) {
1243 uint64_t max_sectors;
1247 assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS);
1249 max_sectors = d->size / 512ULL;
1251 if (max_sectors > 0 && a->pretty_value > max_sectors) {
1252 a->pretty_value = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1253 d->attribute_verification_bad = TRUE;
1255 if ((!strcmp(a->name, "reallocated-sector-count") ||
1256 !strcmp(a->name, "current-pending-sector")) &&
1257 a->pretty_value > 0)
1262 /* This data is stolen from smartmontools */
1264 /* %STRINGPOOLSTART% */
1265 static const SkSmartAttributeInfo const attribute_info[256] = {
1266 [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1267 [2] = { "throughput-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1268 [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1269 [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1270 [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1271 [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1272 [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1273 [8] = { "seek-time-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1274 [9] = { "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1275 [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1276 [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1277 [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1278 [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1279 [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1280 [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1281 [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1282 [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1283 [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1284 [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1285 [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1286 [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1287 [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1288 [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1289 [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
1290 [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1291 [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1292 [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1293 [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1294 [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1295 [204] = { "shock-count-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1296 [205] = { "shock-rate-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1297 [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1298 [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1299 [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1300 [209] = { "offline-seek-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1301 [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1302 [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1303 [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1304 [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1305 [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1306 [225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1307 [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time },
1308 [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1309 [228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
1310 [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
1311 [231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
1312 [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
1313 [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }
1315 /* %STRINGPOOLSTOP% */
1317 typedef enum SkSmartQuirk {
1318 SK_SMART_QUIRK_9_POWERONMINUTES = 0x0001,
1319 SK_SMART_QUIRK_9_POWERONSECONDS = 0x0002,
1320 SK_SMART_QUIRK_9_POWERONHALFMINUTES = 0x0004,
1321 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x0008,
1322 SK_SMART_QUIRK_193_LOADUNLOAD = 0x0010,
1323 SK_SMART_QUIRK_194_10XCELSIUS = 0x0020,
1324 SK_SMART_QUIRK_194_UNKNOWN = 0x0040,
1325 SK_SMART_QUIRK_200_WRITEERRORCOUNT = 0x0080,
1326 SK_SMART_QUIRK_201_DETECTEDTACOUNT = 0x0100,
1327 SK_SMART_QUIRK_5_UNKNOWN = 0x0200,
1328 SK_SMART_QUIRK_9_UNKNOWN = 0x0400,
1329 SK_SMART_QUIRK_197_UNKNOWN = 0x0800,
1330 SK_SMART_QUIRK_198_UNKNOWN = 0x1000,
1333 /* %STRINGPOOLSTART% */
1334 static const char *quirk_name[] = {
1337 "9_POWERONHALFMINUTES",
1338 "192_EMERGENCYRETRACTCYCLECT",
1342 "200_WRITEERRORCOUNT",
1343 "201_DETECTEDTACOUNT",
1350 /* %STRINGPOOLSTOP% */
1352 typedef struct SkSmartQuirkDatabase {
1354 const char *firmware;
1356 } SkSmartQuirkDatabase;
1358 static const SkSmartQuirkDatabase quirk_database[] = { {
1362 "FUJITSU MHY2120BH|"
1365 "^0085000B$", /* seems to be specific to this firmware */
1366 SK_SMART_QUIRK_9_POWERONMINUTES|
1367 SK_SMART_QUIRK_197_UNKNOWN|
1368 SK_SMART_QUIRK_198_UNKNOWN
1370 "^FUJITSU MHR2040AT$",
1372 SK_SMART_QUIRK_9_POWERONSECONDS|
1373 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1374 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1376 "^FUJITSU MHS20[6432]0AT( .)?$",
1378 SK_SMART_QUIRK_9_POWERONSECONDS|
1379 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1380 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1381 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1385 "FUJITSU MHG2...ATU?.*|"
1386 "FUJITSU MHH2...ATU?.*|"
1387 "FUJITSU MHJ2...ATU?.*|"
1388 "FUJITSU MHK2...ATU?.*|"
1389 "FUJITSU MHL2300AT|"
1390 "FUJITSU MHM2(20|15|10|06)0AT|"
1391 "FUJITSU MHN2...AT|"
1392 "FUJITSU MHR2020AT|"
1393 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1394 "FUJITSU MHU2...ATU?.*|"
1395 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1396 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1399 SK_SMART_QUIRK_9_POWERONSECONDS
1405 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1408 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1415 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1416 SK_SMART_QUIRK_194_10XCELSIUS
1418 "^SAMSUNG SP40A2H$",
1420 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1422 "^SAMSUNG SP80A4H$",
1424 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1426 "^SAMSUNG SP8004H$",
1428 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1433 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1434 "Maxtor 4G(120J6|160J[68])|"
1435 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1438 SK_SMART_QUIRK_9_POWERONMINUTES|
1439 SK_SMART_QUIRK_194_UNKNOWN
1442 "Maxtor 2F0[234]0[JL]0|"
1443 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1444 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1445 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1446 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1447 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1448 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1449 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1450 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1451 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1452 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1453 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1454 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1455 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1456 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1457 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1458 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1459 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1460 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1461 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1462 "Maxtor (98196H8|96147H6)|"
1463 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1464 "Maxtor 6(E0[234]|K04)0L0|"
1465 "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|"
1466 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1467 "Maxtor 7Y250[PM]0|"
1468 "Maxtor [45]A(25|30|32)0[JN]0|"
1469 "Maxtor 7L(25|30)0[SR]0"
1472 SK_SMART_QUIRK_9_POWERONMINUTES
1478 "HITACHI_DK14FA-20B|"
1479 "HITACHI_DK23..-..B?|"
1480 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1481 "HE[JN]4230[23]0F9AT00|"
1482 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1485 SK_SMART_QUIRK_9_POWERONMINUTES|
1486 SK_SMART_QUIRK_193_LOADUNLOAD
1488 "^HTS541010G9SA00$",
1490 SK_SMART_QUIRK_5_UNKNOWN
1498 static int match(const char*regex, const char *s, SkBool *result) {
1504 if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1509 if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1511 if (k != REG_NOMATCH) {
1525 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1527 const SkSmartQuirkDatabase *db;
1531 for (db = quirk_database; db->model || db->firmware; db++) {
1534 SkBool matching = FALSE;
1536 if ((k = match(db->model, model, &matching)) < 0)
1544 SkBool matching = FALSE;
1546 if ((k = match(db->firmware, firmware, &matching)) < 0)
1560 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1561 const SkIdentifyParsedData *ipd;
1562 SkSmartQuirk quirk = 0;
1564 /* These are the complex ones */
1565 if (sk_disk_identify_parse(d, &ipd) < 0)
1568 if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1575 if (quirk & SK_SMART_QUIRK_5_UNKNOWN)
1581 /* %STRINGPOOLSTART% */
1582 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1583 static const SkSmartAttributeInfo a = {
1584 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1588 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1589 static const SkSmartAttributeInfo a = {
1590 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1594 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1595 static const SkSmartAttributeInfo a = {
1596 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time
1599 } else if (quirk & SK_SMART_QUIRK_9_UNKNOWN)
1601 /* %STRINGPOOLSTOP% */
1606 /* %STRINGPOOLSTART% */
1607 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1608 static const SkSmartAttributeInfo a = {
1609 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1613 /* %STRINGPOOLSTOP% */
1618 /* %STRINGPOOLSTART% */
1619 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1620 static const SkSmartAttributeInfo a = {
1621 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature
1624 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1626 /* %STRINGPOOLSTOP% */
1631 if (quirk & SK_SMART_QUIRK_197_UNKNOWN)
1637 if (quirk & SK_SMART_QUIRK_198_UNKNOWN)
1643 /* %STRINGPOOLSTART% */
1644 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1645 static const SkSmartAttributeInfo a = {
1646 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1650 /* %STRINGPOOLSTOP% */
1655 /* %STRINGPOOLSTART% */
1656 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1657 static const SkSmartAttributeInfo a = {
1658 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL
1662 /* %STRINGPOOLSTOP% */
1668 /* These are the simple cases */
1669 if (attribute_info[id].name)
1670 return &attribute_info[id];
1675 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1677 if (!d->smart_data_valid) {
1682 switch (d->smart_data[362]) {
1685 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1690 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1694 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1699 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1704 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1709 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1713 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1717 d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1718 d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1720 d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1722 d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1723 d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1724 d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1725 d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1727 d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1728 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]);
1729 d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1731 *spd = &d->smart_parsed_data;
1736 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1740 if (!d->smart_thresholds_valid)
1743 for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1750 a->threshold = p[1];
1751 a->threshold_valid = p[1] != 0xFE;
1753 a->good_now_valid = FALSE;
1755 a->good_in_the_past_valid = FALSE;
1756 a->good_in_the_past = TRUE;
1758 /* Always-Fail and Always-Passing thresholds are not relevant
1759 * for our assessment. */
1760 if (p[1] >= 1 && p[1] <= 0xFD) {
1762 if (a->worst_value_valid) {
1763 a->good_in_the_past = a->good_in_the_past && (a->worst_value > a->threshold);
1764 a->good_in_the_past_valid = TRUE;
1767 if (a->current_value_valid) {
1768 a->good_now = a->good_now && (a->current_value > a->threshold);
1769 a->good_now_valid = TRUE;
1774 (a->good_now_valid && !a->good_now) ||
1775 (a->good_in_the_past_valid && !a->good_in_the_past);
1780 a->threshold_valid = FALSE;
1781 a->good_now_valid = FALSE;
1782 a->good_in_the_past_valid = FALSE;
1786 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1790 if (!d->smart_data_valid) {
1795 for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1796 SkSmartAttributeParsedData a;
1797 const SkSmartAttributeInfo *i;
1803 memset(&a, 0, sizeof(a));
1805 a.current_value = p[3];
1806 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1807 a.worst_value = p[4];
1808 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
1810 a.flags = ((uint16_t) p[2] << 8) | p[1];
1811 a.prefailure = !!(p[1] & 1);
1812 a.online = !!(p[1] & 2);
1814 memcpy(a.raw, p+5, 6);
1816 if ((i = lookup_attribute(d, p[0]))) {
1817 a.name = _P(i->name);
1818 a.pretty_unit = i->unit;
1820 if (asprintf(&an, "attribute-%u", a.id) < 0) {
1826 a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1831 find_threshold(d, &a);
1836 cb(d, &a, userdata);
1843 static const char *yes_no(SkBool b) {
1844 return b ? "yes" : "no";
1847 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
1849 /* %STRINGPOOLSTART% */
1850 const char * const map[] = {
1851 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
1852 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
1853 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
1854 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
1855 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK"
1857 /* %STRINGPOOLSTOP% */
1859 if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
1862 return _P(map[unit]);
1865 struct attr_helper {
1870 static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1872 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
1875 if (!strcmp(a->name, "temperature-centi-celsius") ||
1876 !strcmp(a->name, "temperature-celsius") ||
1877 !strcmp(a->name, "temperature-celsius-2") ||
1878 !strcmp(a->name, "airflow-temperature-celsius")) {
1880 if (!ah->found || a->pretty_value > *ah->value)
1881 *ah->value = a->pretty_value;
1887 int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
1888 struct attr_helper ah;
1896 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
1907 static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1909 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
1912 if (!strcmp(a->name, "power-on-minutes") ||
1913 !strcmp(a->name, "power-on-seconds") ||
1914 !strcmp(a->name, "power-on-half-minutes") ||
1915 !strcmp(a->name, "power-on-hours")) {
1917 if (!ah->found || a->pretty_value > *ah->value)
1918 *ah->value = a->pretty_value;
1924 int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
1925 struct attr_helper ah;
1931 ah.value = mseconds;
1933 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
1944 static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1946 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
1949 if (!strcmp(a->name, "power-cycle-count")) {
1951 if (!ah->found || a->pretty_value > *ah->value)
1952 *ah->value = a->pretty_value;
1958 int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
1959 struct attr_helper ah;
1967 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
1978 static void reallocated_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1980 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1983 if (!strcmp(a->name, "reallocated-sector-count")) {
1985 if (!ah->found || a->pretty_value > *ah->value)
1986 *ah->value = a->pretty_value;
1992 static void pending_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1994 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1997 if (!strcmp(a->name, "current-pending-sector")) {
1999 if (!ah->found || a->pretty_value > *ah->value)
2000 *ah->value = a->pretty_value;
2006 int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
2007 struct attr_helper ah1, ah2;
2008 uint64_t sectors1, sectors2;
2014 ah1.value = §ors1;
2016 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) reallocated_cb, &ah1) < 0)
2020 ah2.value = §ors2;
2022 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) pending_cb, &ah2) < 0)
2025 if (!ah1.found && !ah2.found) {
2030 if (ah1.found && ah2.found)
2031 *sectors = sectors1 + sectors2;
2033 *sectors = sectors1;
2035 *sectors = sectors2;
2040 const char* sk_smart_overall_to_string(SkSmartOverall overall) {
2042 /* %STRINGPOOLSTART% */
2043 const char * const map[] = {
2044 [SK_SMART_OVERALL_GOOD] = "GOOD",
2045 [SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST] = "BAD_ATTRIBUTE_IN_THE_PAST",
2046 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR",
2047 [SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW] = "BAD_ATTRIBUTE_NOW",
2048 [SK_SMART_OVERALL_BAD_SECTOR_MANY] = "BAD_SECTOR_MANY",
2049 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
2051 /* %STRINGPOOLSTOP% */
2053 if (overall >= _SK_SMART_OVERALL_MAX)
2056 return _P(map[overall]);
2059 static void bad_attribute_now_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
2060 if (a->prefailure && a->good_now_valid && !a->good_now)
2064 static void bad_attribute_in_the_past_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
2065 if (a->prefailure && a->good_in_the_past_valid && !a->good_in_the_past)
2069 static uint64_t u64log2(uint64_t n) {
2084 int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
2086 uint64_t sectors, sector_threshold;
2091 /* First, check SMART self-assesment */
2092 if (sk_disk_smart_status(d, &good) < 0)
2096 *overall = SK_SMART_OVERALL_BAD_STATUS;
2100 /* Second, check if the number of bad sectors is greater than
2101 * a certain threshold */
2102 if (sk_disk_smart_get_bad(d, §ors) < 0) {
2103 if (errno != ENOENT)
2108 /* We use log2(n_sectors) as a threshold here. We had to pick
2109 * something, and this makes a bit of sense, or doesn't it? */
2110 sector_threshold = u64log2(d->size/512);
2112 if (sectors >= sector_threshold) {
2113 *overall = SK_SMART_OVERALL_BAD_SECTOR_MANY;
2118 /* Third, check if any of the SMART attributes is bad */
2120 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_now_cb, &good) < 0)
2124 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW;
2128 /* Fourth, check if there are any bad sectors at all */
2130 *overall = SK_SMART_OVERALL_BAD_SECTOR;
2134 /* Fifth, check if any of the SMART attributes ever was bad */
2136 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_in_the_past_cb, &good) < 0)
2140 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST;
2144 /* Sixth, there's really nothing to complain about, so give it a pass */
2145 *overall = SK_SMART_OVERALL_GOOD;
2149 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
2154 snprintf(s, len, "%u", id);
2161 static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
2163 switch (pretty_unit) {
2164 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
2166 if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
2167 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
2168 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
2169 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
2170 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
2171 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
2172 else if (pretty_value >= 1000LLU*60LLU*60LLU)
2173 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
2174 else if (pretty_value >= 1000LLU*60LLU)
2175 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
2176 else if (pretty_value >= 1000LLU)
2177 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
2179 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
2183 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
2184 snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
2187 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
2188 snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
2191 case SK_SMART_ATTRIBUTE_UNIT_NONE:
2192 snprintf(s, len, "%llu", (unsigned long long) pretty_value);
2195 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
2196 snprintf(s, len, "n/a");
2199 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
2208 #define HIGHLIGHT "\x1B[1m"
2209 #define ENDHIGHLIGHT "\x1B[0m"
2211 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2214 char tt[32], tw[32], tc[32];
2217 snprintf(tt, sizeof(tt), "%3u", a->threshold);
2218 tt[sizeof(tt)-1] = 0;
2219 snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2220 tw[sizeof(tw)-1] = 0;
2221 snprintf(tc, sizeof(tc), "%3u", a->current_value);
2222 tc[sizeof(tc)-1] = 0;
2224 highlight = a->warn && isatty(1);
2227 fprintf(stderr, HIGHLIGHT);
2229 printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-4s %-4s\n",
2231 print_name(name, sizeof(name), a->id, a->name),
2232 a->current_value_valid ? tc : "n/a",
2233 a->worst_value_valid ? tw : "n/a",
2234 a->threshold_valid ? tt : "n/a",
2235 print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2236 a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2237 a->prefailure ? "prefail" : "old-age",
2238 a->online ? "online" : "offline",
2239 a->good_now_valid ? yes_no(a->good_now) : "n/a",
2240 a->good_in_the_past_valid ? yes_no(a->good_in_the_past) : "n/a");
2243 fprintf(stderr, ENDHIGHLIGHT);
2246 int sk_disk_dump(SkDisk *d) {
2248 SkBool awake = FALSE;
2253 printf("Device: %s%s%s\n"
2255 d->name ? disk_type_to_prefix_string(d->type) : "",
2257 d->name ? d->name : "n/a",
2258 disk_type_to_human_string(d->type));
2260 ret = sk_disk_get_size(d, &size);
2262 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2264 printf("Size: %s\n", strerror(errno));
2266 if (d->identify_valid) {
2267 const SkIdentifyParsedData *ipd;
2268 SkSmartQuirk quirk = 0;
2271 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2274 printf("Model: [%s]\n"
2277 "SMART Available: %s\n",
2281 yes_no(disk_smart_is_available(d)));
2283 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2288 for (i = 0; quirk_name[i]; i++)
2290 printf(" %s", _P(quirk_name[i]));
2295 ret = sk_disk_check_sleep_mode(d, &awake);
2296 printf("Awake: %s\n",
2297 ret >= 0 ? yes_no(awake) : strerror(errno));
2299 if (disk_smart_is_available(d)) {
2300 SkSmartOverall overall;
2301 const SkSmartParsedData *spd;
2304 uint64_t value, power_on;
2306 ret = sk_disk_smart_status(d, &good);
2307 printf("%sSMART Disk Health Good: %s%s\n",
2308 ret >= 0 && !good ? HIGHLIGHT : "",
2309 ret >= 0 ? yes_no(good) : strerror(errno),
2310 ret >= 0 && !good ? ENDHIGHLIGHT : "");
2311 if ((ret = sk_disk_smart_read_data(d)) < 0)
2314 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2317 printf("Off-line Data Collection Status: [%s]\n"
2318 "Total Time To Complete Off-Line Data Collection: %u s\n"
2319 "Self-Test Execution Status: [%s]\n"
2320 "Percent Self-Test Remaining: %u%%\n"
2321 "Conveyance Self-Test Available: %s\n"
2322 "Short/Extended Self-Test Available: %s\n"
2323 "Start Self-Test Available: %s\n"
2324 "Abort Self-Test Available: %s\n"
2325 "Short Self-Test Polling Time: %u min\n"
2326 "Extended Self-Test Polling Time: %u min\n"
2327 "Conveyance Self-Test Polling Time: %u min\n",
2328 sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2329 spd->total_offline_data_collection_seconds,
2330 sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2331 spd->self_test_execution_percent_remaining,
2332 yes_no(spd->conveyance_test_available),
2333 yes_no(spd->short_and_extended_test_available),
2334 yes_no(spd->start_test_available),
2335 yes_no(spd->abort_test_available),
2336 spd->short_test_polling_minutes,
2337 spd->extended_test_polling_minutes,
2338 spd->conveyance_test_polling_minutes);
2340 if (sk_disk_smart_get_bad(d, &value) < 0)
2341 printf("Bad Sectors: %s\n", strerror(errno));
2343 printf("%sBad Sectors: %s%s\n",
2344 value > 0 ? HIGHLIGHT : "",
2345 print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2346 value > 0 ? ENDHIGHLIGHT : "");
2348 if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2349 printf("Powered On: %s\n", strerror(errno));
2352 printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2354 if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2355 printf("Power Cycles: %s\n", strerror(errno));
2357 printf("Power Cycles: %llu\n", (unsigned long long) value);
2359 if (value > 0 && power_on > 0)
2360 printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2363 if (sk_disk_smart_get_temperature(d, &value) < 0)
2364 printf("Temperature: %s\n", strerror(errno));
2366 printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2368 printf("Attribute Parsing Verification: %s\n",
2369 d->attribute_verification_bad ? "Bad" : "Good");
2371 if (sk_disk_smart_get_overall(d, &overall) < 0)
2372 printf("Overall Status: %s\n", strerror(errno));
2374 printf("%sOverall Status: %s%s\n",
2375 overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2376 sk_smart_overall_to_string(overall),
2377 overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2379 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-4s %-4s\n",
2392 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2395 printf("ATA SMART not supported.\n");
2400 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2404 if (d->size == (uint64_t) -1) {
2413 static int disk_find_type(SkDisk *d, dev_t devnum) {
2415 struct udev_device *dev = NULL, *usb;
2421 if (!(udev = udev_new())) {
2426 if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2431 if ((a = udev_device_get_property_value(dev, "ID_ATA_SMART_ACCESS"))) {
2434 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
2437 if (!(t = disk_type_to_prefix_string(u)))
2440 if (!strcmp(a, t)) {
2447 d->type = SK_DISK_TYPE_NONE;
2452 if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2453 const char *product, *vendor;
2456 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2457 sscanf(product, "%04x", &pid) != 1) {
2462 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2463 sscanf(vendor, "%04x", &vid) != 1) {
2468 if ((vid == 0x0c0b && pid == 0xb159) ||
2469 (vid == 0x04fc && pid == 0x0c25) ||
2470 (vid == 0x04fc && pid == 0x0c15))
2471 d->type = SK_DISK_TYPE_SUNPLUS;
2472 else if ((vid == 0x152d && pid == 0x2329) ||
2473 (vid == 0x152d && pid == 0x2336) ||
2474 (vid == 0x152d && pid == 0x2338) ||
2475 (vid == 0x152d && pid == 0x2339))
2476 d->type = SK_DISK_TYPE_JMICRON;
2478 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2480 } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2481 d->type = SK_DISK_TYPE_LINUX_IDE;
2482 else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2483 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2485 d->type = SK_DISK_TYPE_AUTO;
2491 udev_device_unref(dev);
2499 int sk_disk_open(const char *name, SkDisk **_d) {
2506 if (!(d = calloc(1, sizeof(SkDisk)))) {
2512 d->size = (uint64_t) -1;
2515 d->type = SK_DISK_TYPE_BLOB;
2519 d->type = SK_DISK_TYPE_AUTO;
2521 if (!(dn = disk_type_from_string(name, &d->type)))
2524 if (!(d->name = strdup(dn))) {
2529 if ((d->fd = open(d->name,
2530 O_RDONLY|O_NOCTTY|O_NONBLOCK
2540 if ((ret = fstat(d->fd, &st)) < 0)
2543 if (!S_ISBLK(st.st_mode)) {
2549 /* So, it's a block device. Let's make sure the ioctls work */
2550 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2553 if (d->size <= 0 || d->size == (uint64_t) -1) {
2559 /* OK, it's a real block device with a size. Now let's find the suitable API */
2560 if (d->type == SK_DISK_TYPE_AUTO)
2561 if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2564 if (d->type == SK_DISK_TYPE_AUTO) {
2565 /* We have no clue, so let's autotest for a working API */
2566 for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2567 if (disk_identify_device(d) >= 0)
2569 if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2570 d->type = SK_DISK_TYPE_NONE;
2572 disk_identify_device(d);
2574 /* Check if driver can do SMART, and enable if necessary */
2575 if (disk_smart_is_available(d)) {
2577 if (!disk_smart_is_enabled(d)) {
2578 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2581 if ((ret = disk_identify_device(d)) < 0)
2584 if (!disk_smart_is_enabled(d)) {
2591 disk_smart_read_thresholds(d);
2607 void sk_disk_free(SkDisk *d) {
2618 int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2620 SkBool good, have_good = FALSE;
2628 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2629 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2630 (d->smart_thresholds ? 8 + sizeof(d->smart_thresholds) : 0);
2632 if (sk_disk_smart_status(d, &good) >= 0) {
2643 if (!(d->blob = malloc(size))) {
2650 /* These memory accesses are only OK as long as all our
2651 * objects are sensibly aligned, which they are... */
2653 if (d->identify_valid) {
2654 p[0] = SK_BLOB_TAG_IDENTIFY;
2655 p[1] = htonl(sizeof(d->identify));
2658 memcpy(p, d->identify, sizeof(d->identify));
2659 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2663 p[0] = SK_BLOB_TAG_SMART_STATUS;
2665 p[2] = htonl(!!good);
2669 if (d->smart_data_valid) {
2670 p[0] = SK_BLOB_TAG_SMART_DATA;
2671 p[1] = htonl(sizeof(d->smart_data));
2674 memcpy(p, d->smart_data, sizeof(d->smart_data));
2675 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2678 if (d->smart_thresholds_valid) {
2679 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2680 p[1] = htonl(sizeof(d->smart_thresholds));
2683 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2684 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2687 assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2695 int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2698 SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2703 if (d->type != SK_DISK_TYPE_BLOB) {
2713 /* First run, verify if everything makes sense */
2717 uint32_t tag, tsize;
2725 memcpy(&tsize, p+1, 4);
2729 if (left < ntohl(tsize)) {
2736 case SK_BLOB_TAG_IDENTIFY:
2737 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2744 case SK_BLOB_TAG_SMART_STATUS:
2745 if (ntohl(tsize) != 4 || bssv) {
2752 case SK_BLOB_TAG_SMART_DATA:
2753 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2760 case SK_BLOB_TAG_SMART_THRESHOLDS:
2761 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
2769 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2770 left -= ntohl(tsize);
2778 d->identify_valid = idv;
2779 d->smart_data_valid = sdv;
2780 d->smart_thresholds_valid = stv;
2781 d->blob_smart_status_valid = bssv;
2783 /* Second run, actually copy things in */
2787 uint32_t tag, tsize;
2791 memcpy(&tsize, p+1, 4);
2795 assert(left >= ntohl(tsize));
2799 case SK_BLOB_TAG_IDENTIFY:
2800 assert(ntohl(tsize) == sizeof(d->identify));
2801 memcpy(d->identify, p, sizeof(d->identify));
2804 case SK_BLOB_TAG_SMART_STATUS: {
2806 assert(ntohl(tsize) == 4);
2808 d->blob_smart_status = !!ok;
2812 case SK_BLOB_TAG_SMART_DATA:
2813 assert(ntohl(tsize) == sizeof(d->smart_data));
2814 memcpy(d->smart_data, p, sizeof(d->smart_data));
2817 case SK_BLOB_TAG_SMART_THRESHOLDS:
2818 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
2819 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
2823 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2824 left -= ntohl(tsize);