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 SkIdentifyParsedData identify_parsed_data;
119 SkSmartParsedData smart_parsed_data;
125 typedef enum SkAtaCommand {
126 SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC,
127 SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1,
128 SK_ATA_COMMAND_SMART = 0xB0,
129 SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5
132 /* ATA SMART subcommands (ATA8 7.52.1) */
133 typedef enum SkSmartCommand {
134 SK_SMART_COMMAND_READ_DATA = 0xD0,
135 SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1,
136 SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4,
137 SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8,
138 SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9,
139 SK_SMART_COMMAND_RETURN_STATUS = 0xDA
142 static const char *disk_type_to_human_string(SkDiskType type) {
144 /* %STRINGPOOLSTART% */
145 static const char* const map[_SK_DISK_TYPE_MAX] = {
146 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "16 Byte SCSI ATA SAT Passthru",
147 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "12 Byte SCSI ATA SAT Passthru",
148 [SK_DISK_TYPE_LINUX_IDE] = "Native Linux IDE",
149 [SK_DISK_TYPE_SUNPLUS] = "Sunplus SCSI ATA Passthru",
150 [SK_DISK_TYPE_JMICRON] = "JMicron SCSI ATA Passthru",
151 [SK_DISK_TYPE_BLOB] = "Blob",
152 [SK_DISK_TYPE_AUTO] = "Automatic",
153 [SK_DISK_TYPE_NONE] = "None"
155 /* %STRINGPOOLSTOP% */
157 if (type >= _SK_DISK_TYPE_MAX)
160 return _P(map[type]);
163 static const char *disk_type_to_prefix_string(SkDiskType type) {
165 /* %STRINGPOOLSTART% */
166 static const char* const map[_SK_DISK_TYPE_MAX] = {
167 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "sat16",
168 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "sat12",
169 [SK_DISK_TYPE_LINUX_IDE] = "linux-ide",
170 [SK_DISK_TYPE_SUNPLUS] = "sunplus",
171 [SK_DISK_TYPE_JMICRON] = "jmicron",
172 [SK_DISK_TYPE_NONE] = "none",
173 [SK_DISK_TYPE_AUTO] = "auto",
175 /* %STRINGPOOLSTOP% */
177 if (type >= _SK_DISK_TYPE_MAX)
180 return _P(map[type]);
183 static const char *disk_type_from_string(const char *s, SkDiskType *type) {
189 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
193 if (!(t = disk_type_to_prefix_string(u)))
198 if (strncmp(s, t, l))
212 static SkBool disk_smart_is_available(SkDisk *d) {
213 return d->identify_valid && !!(d->identify[164] & 1);
216 static SkBool disk_smart_is_enabled(SkDisk *d) {
217 return d->identify_valid && !!(d->identify[170] & 1);
220 static SkBool disk_smart_is_conveyance_test_available(SkDisk *d) {
221 assert(d->smart_data_valid);
223 return !!(d->smart_data[367] & 32);
225 static SkBool disk_smart_is_short_and_extended_test_available(SkDisk *d) {
226 assert(d->smart_data_valid);
228 return !!(d->smart_data[367] & 16);
231 static SkBool disk_smart_is_start_test_available(SkDisk *d) {
232 assert(d->smart_data_valid);
234 return !!(d->smart_data[367] & 1);
237 static SkBool disk_smart_is_abort_test_available(SkDisk *d) {
238 assert(d->smart_data_valid);
240 return !!(d->smart_data[367] & 41);
243 static int disk_linux_ide_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
244 uint8_t *bytes = cmd_data;
247 assert(d->type == SK_DISK_TYPE_LINUX_IDE);
251 case SK_DIRECTION_OUT:
253 /* We could use HDIO_DRIVE_TASKFILE here, but
254 * that's a deprecated ioctl(), hence we don't
255 * do it. And we don't need writing anyway. */
260 case SK_DIRECTION_IN: {
263 /* We have HDIO_DRIVE_CMD which can only read, but not write,
264 * and cannot do LBA. We use it for all read commands. */
266 ioctl_data = alloca(4 + *len);
267 memset(ioctl_data, 0, 4 + *len);
269 ioctl_data[0] = (uint8_t) command; /* COMMAND */
270 ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */
271 ioctl_data[2] = bytes[1]; /* FEATURE */
272 ioctl_data[3] = bytes[3]; /* NSECTOR */
274 if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0)
277 memset(bytes, 0, 12);
278 bytes[11] = ioctl_data[0];
279 bytes[1] = ioctl_data[1];
280 bytes[3] = ioctl_data[2];
282 memcpy(data, ioctl_data+4, *len);
287 case SK_DIRECTION_NONE: {
288 uint8_t ioctl_data[7];
290 /* We have HDIO_DRIVE_TASK which can neither read nor
291 * write, but can do LBA. We use it for all commands that
292 * do neither read nor write */
294 memset(ioctl_data, 0, sizeof(ioctl_data));
296 ioctl_data[0] = (uint8_t) command; /* COMMAND */
297 ioctl_data[1] = bytes[1]; /* FEATURE */
298 ioctl_data[2] = bytes[3]; /* NSECTOR */
300 ioctl_data[3] = bytes[9]; /* LBA LOW */
301 ioctl_data[4] = bytes[8]; /* LBA MID */
302 ioctl_data[5] = bytes[7]; /* LBA HIGH */
303 ioctl_data[6] = bytes[10]; /* SELECT */
305 if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data)))
308 memset(bytes, 0, 12);
309 bytes[11] = ioctl_data[0];
310 bytes[1] = ioctl_data[1];
311 bytes[3] = ioctl_data[2];
313 bytes[9] = ioctl_data[3];
314 bytes[8] = ioctl_data[4];
315 bytes[7] = ioctl_data[5];
317 bytes[10] = ioctl_data[6];
328 /* Sends a SCSI command block */
329 static int sg_io(int fd, int direction,
330 const void *cdb, size_t cdb_len,
331 void *data, size_t data_len,
332 void *sense, size_t sense_len) {
334 struct sg_io_hdr io_hdr;
336 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
338 io_hdr.interface_id = 'S';
339 io_hdr.cmdp = (unsigned char*) cdb;
340 io_hdr.cmd_len = cdb_len;
341 io_hdr.dxferp = data;
342 io_hdr.dxfer_len = data_len;
344 io_hdr.mx_sb_len = sense_len;
345 io_hdr.dxfer_direction = direction;
346 io_hdr.timeout = SK_TIMEOUT;
348 return ioctl(fd, SG_IO, &io_hdr);
351 static int disk_passthrough_16_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
352 uint8_t *bytes = cmd_data;
355 uint8_t *desc = sense+8;
358 static const int direction_map[] = {
359 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
360 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
361 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
364 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_16);
366 /* ATA Pass-Through 16 byte command, as described in "T10 04-262r8
367 * ATA Command Pass-Through":
368 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
370 memset(cdb, 0, sizeof(cdb));
372 cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */
374 if (direction == SK_DIRECTION_NONE) {
375 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
376 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
378 } else if (direction == SK_DIRECTION_IN) {
379 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
380 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
382 } else if (direction == SK_DIRECTION_OUT) {
383 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
384 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
387 cdb[3] = bytes[0]; /* FEATURES */
390 cdb[5] = bytes[2]; /* SECTORS */
393 cdb[8] = bytes[9]; /* LBA LOW */
394 cdb[10] = bytes[8]; /* LBA MID */
395 cdb[12] = bytes[7]; /* LBA HIGH */
397 cdb[13] = bytes[10] & 0x4F; /* SELECT */
398 cdb[14] = (uint8_t) command;
400 memset(sense, 0, sizeof(sense));
402 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
405 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
410 memset(bytes, 0, 12);
418 bytes[10] = desc[12];
419 bytes[11] = desc[13];
424 static int disk_passthrough_12_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
425 uint8_t *bytes = cmd_data;
428 uint8_t *desc = sense+8;
431 static const int direction_map[] = {
432 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
433 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
434 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
437 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12);
439 /* ATA Pass-Through 12 byte command, as described in "T10 04-262r8
440 * ATA Command Pass-Through":
441 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
443 memset(cdb, 0, sizeof(cdb));
445 cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */
447 if (direction == SK_DIRECTION_NONE) {
448 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
449 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
451 } else if (direction == SK_DIRECTION_IN) {
452 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
453 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
455 } else if (direction == SK_DIRECTION_OUT) {
456 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
457 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
460 cdb[3] = bytes[1]; /* FEATURES */
461 cdb[4] = bytes[3]; /* SECTORS */
463 cdb[5] = bytes[9]; /* LBA LOW */
464 cdb[6] = bytes[8]; /* LBA MID */
465 cdb[7] = bytes[7]; /* LBA HIGH */
467 cdb[8] = bytes[10] & 0x4F; /* SELECT */
468 cdb[9] = (uint8_t) command;
470 memset(sense, 0, sizeof(sense));
472 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
475 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
480 memset(bytes, 0, 12);
482 bytes[1] = desc[3]; /* FEATURES */
483 bytes[2] = desc[4]; /* STATUS */
484 bytes[3] = desc[5]; /* SECTORS */
485 bytes[9] = desc[7]; /* LBA LOW */
486 bytes[8] = desc[9]; /* LBA MID */
487 bytes[7] = desc[11]; /* LBA HIGH */
488 bytes[10] = desc[12]; /* SELECT */
489 bytes[11] = desc[13]; /* ERROR */
494 static int disk_sunplus_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
495 uint8_t *bytes = cmd_data;
497 uint8_t sense[32], buf[8];
499 static const int direction_map[] = {
500 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
501 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
502 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
505 assert(d->type == SK_DISK_TYPE_SUNPLUS);
507 /* SunplusIT specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
509 memset(cdb, 0, sizeof(cdb));
511 cdb[0] = 0xF8; /* OPERATION CODE: Sunplus specific */
512 cdb[1] = 0x00; /* Subcommand: Pass-thru */
515 if (direction == SK_DIRECTION_NONE)
516 cdb[3] = 0x00; /* protocol */
517 else if (direction == SK_DIRECTION_IN)
518 cdb[3] = 0x10; /* protocol */
519 else if (direction == SK_DIRECTION_OUT)
520 cdb[3] = 0x11; /* protocol */
522 cdb[4] = bytes[3]; /* size? */
523 cdb[5] = bytes[1]; /* FEATURES */
524 cdb[6] = bytes[3]; /* SECTORS */
525 cdb[7] = bytes[9]; /* LBA LOW */
526 cdb[8] = bytes[8]; /* LBA MID */
527 cdb[9] = bytes[7]; /* LBA HIGH */
528 cdb[10] = bytes[10] | 0xA0; /* SELECT */
529 cdb[11] = (uint8_t) command;
531 memset(sense, 0, sizeof(sense));
534 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
537 memset(cdb, 0, sizeof(cdb));
543 memset(buf, 0, sizeof(buf));
545 /* Ask for response */
546 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), buf, sizeof(buf), sense, sizeof(sense))) < 0)
549 memset(bytes, 0, 12);
551 bytes[2] = buf[1]; /* ERROR */
552 bytes[3] = buf[2]; /* SECTORS */
553 bytes[9] = buf[3]; /* LBA LOW */
554 bytes[8] = buf[4]; /* LBA MID */
555 bytes[7] = buf[5]; /* LBA HIGH */
556 bytes[10] = buf[6]; /* SELECT */
557 bytes[11] = buf[7]; /* STATUS */
562 static int disk_jmicron_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* _data, size_t *_len) {
563 uint8_t *bytes = cmd_data;
568 SkBool is_smart_status = FALSE;
570 size_t len = _len ? *_len : 0;
571 uint8_t smart_status = 0;
573 static const int direction_map[] = {
574 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
575 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
576 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
579 assert(d->type == SK_DISK_TYPE_JMICRON);
581 /* JMicron specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
583 memset(cdb, 0, sizeof(cdb));
585 cdb[0] = 0xdf; /* operation code */
588 cdb[3] = 0x00; /* size HI */
589 cdb[4] = sizeof(port); /* size LO */
591 cdb[6] = 0x72; /* register address HI */
592 cdb[7] = 0x0f; /* register address LO */
598 memset(sense, 0, sizeof(sense));
600 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), &port, sizeof(port), sense, sizeof(sense))) < 0)
603 /* Port & 0x04 is port #0, Port & 0x40 is port #1 */
607 cdb[0] = 0xdf; /* OPERATION CODE: 12 byte pass through */
609 if (command == SK_ATA_COMMAND_SMART && bytes[1] == SK_SMART_COMMAND_RETURN_STATUS) {
610 /* We need to rewrite the SMART status request */
611 is_smart_status = TRUE;
612 direction = SK_DIRECTION_IN;
613 data = &smart_status;
614 len = sizeof(smart_status);
616 } else if (direction == SK_DIRECTION_NONE)
618 else if (direction == SK_DIRECTION_IN)
620 else if (direction == SK_DIRECTION_OUT)
625 cdb[3] = (uint8_t) (len >> 8);
626 cdb[4] = (uint8_t) (len & 0xFF);
628 cdb[5] = bytes[1]; /* FEATURES */
629 cdb[6] = bytes[3]; /* SECTORS */
631 cdb[7] = bytes[9]; /* LBA LOW */
632 cdb[8] = bytes[8]; /* LBA MID */
633 cdb[9] = bytes[7]; /* LBA HIGH */
635 cdb[10] = bytes[10] | ((port & 0x04) ? 0xA0 : 0xB0); /* SELECT */
636 cdb[11] = (uint8_t) command;
638 memset(sense, 0, sizeof(sense));
640 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len, sense, sizeof(sense))) < 0)
643 memset(bytes, 0, 12);
645 if (is_smart_status) {
646 if (smart_status == 0x01 || smart_status == 0xc2) {
647 bytes[7] = 0xc2; /* LBA HIGH */
648 bytes[8] = 0x4f; /* LBA MID */
649 } else if (smart_status == 0x00 || smart_status == 0x2c) {
650 bytes[7] = 0x2c; /* LBA HIGH */
651 bytes[8] = 0xf4; /* LBA MID */
657 cdb[0] = 0xdf; /* operation code */
660 cdb[3] = 0x00; /* size HI */
661 cdb[4] = sizeof(regbuf); /* size LO */
663 cdb[6] = (port & 0x04) ? 0x80 : 0x90; /* register address HI */
664 cdb[7] = 0x00; /* register address LO */
670 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), regbuf, sizeof(regbuf), sense, sizeof(sense))) < 0)
673 bytes[2] = regbuf[14]; /* STATUS */
674 bytes[3] = regbuf[0]; /* SECTORS */
675 bytes[9] = regbuf[6]; /* LBA LOW */
676 bytes[8] = regbuf[4]; /* LBA MID */
677 bytes[7] = regbuf[10]; /* LBA HIGH */
678 bytes[10] = regbuf[9]; /* SELECT */
679 bytes[11] = regbuf[13]; /* ERROR */
685 static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
687 static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) = {
688 [SK_DISK_TYPE_LINUX_IDE] = disk_linux_ide_command,
689 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = disk_passthrough_12_command,
690 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = disk_passthrough_16_command,
691 [SK_DISK_TYPE_SUNPLUS] = disk_sunplus_command,
692 [SK_DISK_TYPE_JMICRON] = disk_jmicron_command,
693 [SK_DISK_TYPE_BLOB] = NULL,
694 [SK_DISK_TYPE_AUTO] = NULL,
695 [SK_DISK_TYPE_NONE] = NULL
699 assert(d->type <= _SK_DISK_TYPE_MAX);
700 assert(direction <= _SK_DIRECTION_MAX);
702 assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0));
703 assert(direction != SK_DIRECTION_NONE || (!data && !len));
705 if (!disk_command_table[d->type]) {
710 return disk_command_table[d->type](d, command, direction, cmd_data, data, len);
713 static int disk_identify_device(SkDisk *d) {
719 if (d->type == SK_DISK_TYPE_BLOB)
722 memset(d->identify, 0, len);
723 memset(cmd, 0, sizeof(cmd));
727 if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0)
735 /* Check if IDENTIFY data is all NULs */
736 for (p = d->identify; p < (const uint8_t*) d->identify+len; p++)
747 d->identify_valid = TRUE;
752 int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake) {
757 if (!d->identify_valid) {
762 if (d->type == SK_DISK_TYPE_BLOB) {
767 memset(cmd, 0, sizeof(cmd));
769 if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
772 if (cmd[0] != 0 || (ntohs(cmd[5]) & 1) != 0) {
777 status = ntohs(cmd[1]) & 0xFF;
778 *awake = status == 0xFF || status == 0x80; /* idle and active/idle is considered awake */
783 static int disk_smart_enable(SkDisk *d, SkBool b) {
786 if (!disk_smart_is_available(d)) {
791 if (d->type == SK_DISK_TYPE_BLOB) {
796 memset(cmd, 0, sizeof(cmd));
798 cmd[0] = htons(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS);
799 cmd[2] = htons(0x0000U);
800 cmd[3] = htons(0x00C2U);
801 cmd[4] = htons(0x4F00U);
803 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0);
806 int sk_disk_smart_read_data(SkDisk *d) {
811 if (!disk_smart_is_available(d)) {
816 if (d->type == SK_DISK_TYPE_BLOB)
819 memset(cmd, 0, sizeof(cmd));
821 cmd[0] = htons(SK_SMART_COMMAND_READ_DATA);
823 cmd[2] = htons(0x0000U);
824 cmd[3] = htons(0x00C2U);
825 cmd[4] = htons(0x4F00U);
827 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0)
830 d->smart_data_valid = TRUE;
835 static int disk_smart_read_thresholds(SkDisk *d) {
840 if (!disk_smart_is_available(d)) {
845 if (d->type == SK_DISK_TYPE_BLOB)
848 memset(cmd, 0, sizeof(cmd));
850 cmd[0] = htons(SK_SMART_COMMAND_READ_THRESHOLDS);
852 cmd[2] = htons(0x0000U);
853 cmd[3] = htons(0x00C2U);
854 cmd[4] = htons(0x4F00U);
856 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_thresholds, &len)) < 0)
859 d->smart_thresholds_valid = TRUE;
864 int sk_disk_smart_status(SkDisk *d, SkBool *good) {
868 if (!disk_smart_is_available(d)) {
873 if (d->type == SK_DISK_TYPE_BLOB) {
875 if (d->blob_smart_status_valid) {
876 *good = d->blob_smart_status;
884 memset(cmd, 0, sizeof(cmd));
886 cmd[0] = htons(SK_SMART_COMMAND_RETURN_STATUS);
887 cmd[1] = htons(0x0000U);
888 cmd[3] = htons(0x00C2U);
889 cmd[4] = htons(0x4F00U);
891 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
894 /* SAT/USB bridges truncate packets, so we only check for 4F,
895 * not for 2C on those */
896 if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) &&
897 cmd[4] == htons(0x4F00U))
899 else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) &&
900 cmd[4] == htons(0xF400U))
910 int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) {
914 if (!disk_smart_is_available(d)) {
919 if (d->type == SK_DISK_TYPE_BLOB) {
924 if (!d->smart_data_valid)
925 if ((ret = sk_disk_smart_read_data(d)) < 0)
928 assert(d->smart_data_valid);
930 if (test != SK_SMART_SELF_TEST_SHORT &&
931 test != SK_SMART_SELF_TEST_EXTENDED &&
932 test != SK_SMART_SELF_TEST_CONVEYANCE &&
933 test != SK_SMART_SELF_TEST_ABORT) {
938 if (!disk_smart_is_start_test_available(d)
939 || (test == SK_SMART_SELF_TEST_ABORT && !disk_smart_is_abort_test_available(d))
940 || ((test == SK_SMART_SELF_TEST_SHORT || test == SK_SMART_SELF_TEST_EXTENDED) && !disk_smart_is_short_and_extended_test_available(d))
941 || (test == SK_SMART_SELF_TEST_CONVEYANCE && !disk_smart_is_conveyance_test_available(d))) {
946 if (test == SK_SMART_SELF_TEST_ABORT &&
947 !disk_smart_is_abort_test_available(d)) {
952 memset(cmd, 0, sizeof(cmd));
954 cmd[0] = htons(SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE);
955 cmd[2] = htons(0x0000U);
956 cmd[3] = htons(0x00C2U);
957 cmd[4] = htons(0x4F00U | (uint16_t) test);
959 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, NULL);
962 static void swap_strings(char *s, size_t len) {
963 assert((len & 1) == 0);
965 for (; len > 0; s += 2, len -= 2) {
973 static void clean_strings(char *s) {
977 if (*e < ' ' || *e >= 127)
981 static void drop_spaces(char *s) {
983 SkBool prev_space = FALSE;
1006 static void read_string(char *d, uint8_t *s, size_t len) {
1009 swap_strings(d, len);
1014 int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) {
1018 if (!d->identify_valid) {
1023 read_string(d->identify_parsed_data.serial, d->identify+20, 20);
1024 read_string(d->identify_parsed_data.firmware, d->identify+46, 8);
1025 read_string(d->identify_parsed_data.model, d->identify+54, 40);
1027 *ipd = &d->identify_parsed_data;
1032 int sk_disk_smart_is_available(SkDisk *d, SkBool *b) {
1036 if (!d->identify_valid) {
1041 *b = disk_smart_is_available(d);
1045 int sk_disk_identify_is_available(SkDisk *d, SkBool *b) {
1049 *b = d->identify_valid;
1053 const char *sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status) {
1055 /* %STRINGPOOLSTART% */
1056 static const char* const map[] = {
1057 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.",
1058 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.",
1059 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.",
1060 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.",
1061 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.",
1062 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.",
1063 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status"
1065 /* %STRINGPOOLSTOP% */
1067 if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX)
1070 return _P(map[status]);
1073 const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status) {
1075 /* %STRINGPOOLSTART% */
1076 static const char* const map[] = {
1077 [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.",
1078 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED] = "The self-test routine was aborted by the host.",
1079 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED] = "The self-test routine was interrupted by the host with a hardware or software reset.",
1080 [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.",
1081 [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.",
1082 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL] = "The previous self-test completed having the electrical element of the test failed.",
1083 [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.",
1084 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ] = "The previous self-test completed having the read element of the test failed.",
1085 [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.",
1086 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS] = "Self-test routine in progress"
1088 /* %STRINGPOOLSTOP% */
1090 if (status >= _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX)
1093 return _P(map[status]);
1096 const char* sk_smart_self_test_to_string(SkSmartSelfTest test) {
1099 case SK_SMART_SELF_TEST_SHORT:
1101 case SK_SMART_SELF_TEST_EXTENDED:
1103 case SK_SMART_SELF_TEST_CONVEYANCE:
1104 return "conveyance";
1105 case SK_SMART_SELF_TEST_ABORT:
1112 SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) {
1115 if (!d->start_test_available)
1119 case SK_SMART_SELF_TEST_SHORT:
1120 case SK_SMART_SELF_TEST_EXTENDED:
1121 return d->short_and_extended_test_available;
1122 case SK_SMART_SELF_TEST_CONVEYANCE:
1123 return d->conveyance_test_available;
1124 case SK_SMART_SELF_TEST_ABORT:
1125 return d->abort_test_available;
1131 unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) {
1134 if (!sk_smart_self_test_available(d, test))
1138 case SK_SMART_SELF_TEST_SHORT:
1139 return d->short_test_polling_minutes;
1140 case SK_SMART_SELF_TEST_EXTENDED:
1141 return d->extended_test_polling_minutes;
1142 case SK_SMART_SELF_TEST_CONVEYANCE:
1143 return d->conveyance_test_polling_minutes;
1149 static void make_pretty(SkSmartAttributeParsedData *a) {
1150 uint64_t fourtyeight;
1155 if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN)
1159 ((uint64_t) a->raw[0]) |
1160 (((uint64_t) a->raw[1]) << 8) |
1161 (((uint64_t) a->raw[2]) << 16) |
1162 (((uint64_t) a->raw[3]) << 24) |
1163 (((uint64_t) a->raw[4]) << 32) |
1164 (((uint64_t) a->raw[5]) << 40);
1166 if (!strcmp(a->name, "spin-up-time"))
1167 a->pretty_value = fourtyeight & 0xFFFF;
1168 else if (!strcmp(a->name, "airflow-temperature-celsius") ||
1169 !strcmp(a->name, "temperature-celsius") ||
1170 !strcmp(a->name, "temperature-celsius-2"))
1171 a->pretty_value = (fourtyeight & 0xFFFF)*1000 + 273150;
1172 else if (!strcmp(a->name, "temperature-centi-celsius"))
1173 a->pretty_value = (fourtyeight & 0xFFFF)*100 + 273150;
1174 else if (!strcmp(a->name, "power-on-minutes"))
1175 a->pretty_value = (((uint64_t) a->raw[0]) | (uint64_t) a->raw[1]) * 60 * 1000;
1176 else if (!strcmp(a->name, "power-on-seconds"))
1177 a->pretty_value = fourtyeight * 1000;
1178 else if (!strcmp(a->name, "power-on-half-minutes"))
1179 a->pretty_value = fourtyeight * 30 * 1000;
1180 else if (!strcmp(a->name, "power-on-hours") ||
1181 !strcmp(a->name, "loaded-hours") ||
1182 !strcmp(a->name, "head-flying-hours"))
1183 a->pretty_value = (fourtyeight & 0xFFFFFFFFU) * 60 * 60 * 1000;
1184 else if (!strcmp(a->name, "reallocated-sector-count") ||
1185 !strcmp(a->name, "current-pending-sector"))
1186 a->pretty_value = fourtyeight & 0xFFFFFFFFU;
1188 a->pretty_value = fourtyeight;
1191 typedef struct SkSmartAttributeInfo {
1193 SkSmartAttributeUnit unit;
1194 } SkSmartAttributeInfo;
1196 /* This data is stolen from smartmontools */
1198 /* %STRINGPOOLSTART% */
1199 static const SkSmartAttributeInfo const attribute_info[256] = {
1200 [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1201 [2] = { "throughput-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1202 [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1203 [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1204 [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1205 [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1206 [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1207 [8] = { "seek-time-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1208 [9] = { "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1209 [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1210 [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1211 [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1212 [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1213 [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1214 [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE },
1215 [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1216 [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1217 [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1218 [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1219 [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1220 [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE },
1221 [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1222 [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1223 [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1224 [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1225 [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1226 [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1227 [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1228 [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1229 [204] = { "shock-count-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE },
1230 [205] = { "shock-rate-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE },
1231 [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1232 [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1233 [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN},
1234 [209] = { "offline-seek-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1235 [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1236 [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
1237 [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1238 [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1239 [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1240 [225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
1241 [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1242 [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1243 [228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
1244 [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1245 [231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1246 [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1247 [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE }
1249 /* %STRINGPOOLSTOP% */
1251 typedef enum SkSmartQuirk {
1252 SK_SMART_QUIRK_9_POWERONMINUTES = 0x0001,
1253 SK_SMART_QUIRK_9_POWERONSECONDS = 0x0002,
1254 SK_SMART_QUIRK_9_POWERONHALFMINUTES = 0x0004,
1255 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x0008,
1256 SK_SMART_QUIRK_193_LOADUNLOAD = 0x0010,
1257 SK_SMART_QUIRK_194_10XCELSIUS = 0x0020,
1258 SK_SMART_QUIRK_194_UNKNOWN = 0x0040,
1259 SK_SMART_QUIRK_200_WRITEERRORCOUNT = 0x0080,
1260 SK_SMART_QUIRK_201_DETECTEDTACOUNT = 0x0100,
1261 SK_SMART_QUIRK_5_UNKNOWN = 0x0200,
1262 SK_SMART_QUIRK_9_UNKNOWN = 0x0400,
1263 SK_SMART_QUIRK_197_UNKNOWN = 0x0800,
1264 SK_SMART_QUIRK_198_UNKNOWN = 0x1000,
1267 /* %STRINGPOOLSTART% */
1268 static const char *quirk_name[] = {
1271 "9_POWERONHALFMINUTES",
1272 "192_EMERGENCYRETRACTCYCLECT",
1276 "200_WRITEERRORCOUNT",
1277 "201_DETECTEDTACOUNT",
1284 /* %STRINGPOOLSTOP% */
1286 typedef struct SkSmartQuirkDatabase {
1288 const char *firmware;
1290 } SkSmartQuirkDatabase;
1292 static const SkSmartQuirkDatabase quirk_database[] = { {
1295 "^FUJITSU MHY2120BH$",
1296 "^0085000B$", /* seems to be specific to this firmware */
1297 SK_SMART_QUIRK_9_UNKNOWN|
1298 SK_SMART_QUIRK_197_UNKNOWN|
1299 SK_SMART_QUIRK_198_UNKNOWN
1301 "^FUJITSU MHR2040AT$",
1303 SK_SMART_QUIRK_9_POWERONSECONDS|
1304 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1305 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1307 "^FUJITSU MHS20[6432]0AT( .)?$",
1309 SK_SMART_QUIRK_9_POWERONSECONDS|
1310 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1311 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1312 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1316 "FUJITSU MHG2...ATU?.*|"
1317 "FUJITSU MHH2...ATU?.*|"
1318 "FUJITSU MHJ2...ATU?.*|"
1319 "FUJITSU MHK2...ATU?.*|"
1320 "FUJITSU MHL2300AT|"
1321 "FUJITSU MHM2(20|15|10|06)0AT|"
1322 "FUJITSU MHN2...AT|"
1323 "FUJITSU MHR2020AT|"
1324 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1325 "FUJITSU MHU2...ATU?.*|"
1326 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1327 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1330 SK_SMART_QUIRK_9_POWERONSECONDS
1336 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1339 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1346 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1347 SK_SMART_QUIRK_194_10XCELSIUS
1349 "^SAMSUNG SP40A2H$",
1351 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1353 "^SAMSUNG SP80A4H$",
1355 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1357 "^SAMSUNG SP8004H$",
1359 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1364 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1365 "Maxtor 4G(120J6|160J[68])|"
1366 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1369 SK_SMART_QUIRK_9_POWERONMINUTES|
1370 SK_SMART_QUIRK_194_UNKNOWN
1373 "Maxtor 2F0[234]0[JL]0|"
1374 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1375 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1376 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1377 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1378 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1379 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1380 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1381 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1382 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1383 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1384 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1385 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1386 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1387 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1388 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1389 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1390 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1391 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1392 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1393 "Maxtor (98196H8|96147H6)|"
1394 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1395 "Maxtor 6(E0[234]|K04)0L0|"
1396 "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|"
1397 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1398 "Maxtor 7Y250[PM]0|"
1399 "Maxtor [45]A(25|30|32)0[JN]0|"
1400 "Maxtor 7L(25|30)0[SR]0"
1403 SK_SMART_QUIRK_9_POWERONMINUTES
1409 "HITACHI_DK14FA-20B|"
1410 "HITACHI_DK23..-..B?|"
1411 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1412 "HE[JN]4230[23]0F9AT00|"
1413 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1416 SK_SMART_QUIRK_9_POWERONMINUTES|
1417 SK_SMART_QUIRK_193_LOADUNLOAD
1419 "^HTS541010G9SA00$",
1421 SK_SMART_QUIRK_5_UNKNOWN
1429 static int match(const char*regex, const char *s, SkBool *result) {
1435 if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1440 if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1442 if (k != REG_NOMATCH) {
1456 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1458 const SkSmartQuirkDatabase *db;
1462 for (db = quirk_database; db->model || db->firmware; db++) {
1465 SkBool matching = FALSE;
1467 if ((k = match(db->model, model, &matching)) < 0)
1475 SkBool matching = FALSE;
1477 if ((k = match(db->firmware, firmware, &matching)) < 0)
1491 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1492 const SkIdentifyParsedData *ipd;
1493 SkSmartQuirk quirk = 0;
1495 /* These are the complex ones */
1496 if (sk_disk_identify_parse(d, &ipd) < 0)
1499 if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1506 if (quirk & SK_SMART_QUIRK_5_UNKNOWN)
1512 /* %STRINGPOOLSTART% */
1513 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1514 static const SkSmartAttributeInfo a = {
1515 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1519 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1520 static const SkSmartAttributeInfo a = {
1521 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1525 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1526 static const SkSmartAttributeInfo a = {
1527 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1530 } else if (quirk & SK_SMART_QUIRK_9_UNKNOWN)
1532 /* %STRINGPOOLSTOP% */
1537 /* %STRINGPOOLSTART% */
1538 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1539 static const SkSmartAttributeInfo a = {
1540 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1544 /* %STRINGPOOLSTOP% */
1549 /* %STRINGPOOLSTART% */
1550 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1551 static const SkSmartAttributeInfo a = {
1552 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN
1555 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1557 /* %STRINGPOOLSTOP% */
1562 if (quirk & SK_SMART_QUIRK_197_UNKNOWN)
1568 if (quirk & SK_SMART_QUIRK_198_UNKNOWN)
1574 /* %STRINGPOOLSTART% */
1575 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1576 static const SkSmartAttributeInfo a = {
1577 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1581 /* %STRINGPOOLSTOP% */
1586 /* %STRINGPOOLSTART% */
1587 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1588 static const SkSmartAttributeInfo a = {
1589 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1593 /* %STRINGPOOLSTOP% */
1599 /* These are the simple cases */
1600 if (attribute_info[id].name)
1601 return &attribute_info[id];
1606 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1608 if (!d->smart_data_valid) {
1613 switch (d->smart_data[362]) {
1616 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1621 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1625 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1630 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1635 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1640 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1644 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1648 d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1649 d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1651 d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1653 d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1654 d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1655 d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1656 d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1658 d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1659 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]);
1660 d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1662 *spd = &d->smart_parsed_data;
1667 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1671 if (!d->smart_thresholds_valid) {
1672 a->threshold_valid = FALSE;
1676 for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1681 a->threshold_valid = FALSE;
1682 a->good_valid = FALSE;
1686 a->threshold = p[1];
1687 a->threshold_valid = p[1] != 0xFE;
1689 a->good_valid = FALSE;
1692 /* Always-Fail and Always-Passing thresholds are not relevant
1693 * for our assessment. */
1694 if (p[1] >= 1 && p[1] <= 0xFD) {
1696 if (a->worst_value_valid) {
1697 a->good = a->good && (a->worst_value > a->threshold);
1698 a->good_valid = TRUE;
1701 if (a->current_value_valid) {
1702 a->good = a->good && (a->current_value > a->threshold);
1703 a->good_valid = TRUE;
1708 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1712 if (!d->smart_data_valid) {
1717 for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1718 SkSmartAttributeParsedData a;
1719 const SkSmartAttributeInfo *i;
1725 memset(&a, 0, sizeof(a));
1727 a.current_value = p[3];
1728 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1729 a.worst_value = p[4];
1730 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
1732 a.flags = ((uint16_t) p[2] << 8) | p[1];
1733 a.prefailure = !!(p[1] & 1);
1734 a.online = !!(p[1] & 2);
1736 memcpy(a.raw, p+5, 6);
1738 if ((i = lookup_attribute(d, p[0]))) {
1739 a.name = _P(i->name);
1740 a.pretty_unit = i->unit;
1742 if (asprintf(&an, "attribute-%u", a.id) < 0) {
1748 a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1753 find_threshold(d, &a);
1755 /* Handle a few fields specially */
1756 if ((!strcmp(a.name, "reallocated-sector-count") ||
1757 !strcmp(a.name, "current-pending-sector")) &&
1758 a.pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS &&
1759 a.pretty_value > 0) {
1761 a.good_valid = TRUE;
1764 cb(d, &a, userdata);
1771 static const char *yes_no(SkBool b) {
1772 return b ? "yes" : "no";
1775 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
1777 /* %STRINGPOOLSTART% */
1778 const char * const map[] = {
1779 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
1780 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
1781 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
1782 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
1783 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK"
1785 /* %STRINGPOOLSTOP% */
1787 if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
1790 return _P(map[unit]);
1793 struct attr_helper {
1798 static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1800 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
1803 if (!strcmp(a->name, "temperature-centi-celsius") ||
1804 !strcmp(a->name, "temperature-celsius") ||
1805 !strcmp(a->name, "temperature-celsius-2") ||
1806 !strcmp(a->name, "airflow-temperature-celsius")) {
1808 if (!ah->found || a->pretty_value > *ah->value)
1809 *ah->value = a->pretty_value;
1815 int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
1816 struct attr_helper ah;
1824 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
1835 static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1837 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
1840 if (!strcmp(a->name, "power-on-minutes") ||
1841 !strcmp(a->name, "power-on-seconds") ||
1842 !strcmp(a->name, "power-on-half-minutes") ||
1843 !strcmp(a->name, "power-on-hours")) {
1845 if (!ah->found || a->pretty_value > *ah->value)
1846 *ah->value = a->pretty_value;
1852 int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
1853 struct attr_helper ah;
1859 ah.value = mseconds;
1861 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
1872 static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1874 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
1877 if (!strcmp(a->name, "power-cycle-count")) {
1879 if (!ah->found || a->pretty_value > *ah->value)
1880 *ah->value = a->pretty_value;
1886 int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
1887 struct attr_helper ah;
1895 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
1906 static void reallocated_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1908 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1911 if (!strcmp(a->name, "reallocated-sector-count")) {
1913 if (!ah->found || a->pretty_value > *ah->value)
1914 *ah->value = a->pretty_value;
1920 static void pending_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1922 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1925 if (!strcmp(a->name, "current-pending-sector")) {
1927 if (!ah->found || a->pretty_value > *ah->value)
1928 *ah->value = a->pretty_value;
1934 int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
1935 struct attr_helper ah1, ah2;
1936 uint64_t sectors1, sectors2;
1942 ah1.value = §ors1;
1944 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) reallocated_cb, &ah1) < 0)
1948 ah2.value = §ors2;
1950 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) pending_cb, &ah2) < 0)
1953 if (!ah1.found && !ah2.found) {
1958 if (ah1.found && ah2.found)
1959 *sectors = sectors1 + sectors2;
1961 *sectors = sectors1;
1963 *sectors = sectors2;
1968 const char* sk_smart_overall_to_string(SkSmartOverall overall) {
1970 /* %STRINGPOOLSTART% */
1971 const char * const map[] = {
1972 [SK_SMART_OVERALL_GOOD] = "GOOD",
1973 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
1974 [SK_SMART_OVERALL_BAD_ATTRIBUTE] = "BAD_ATTRIBUTE",
1975 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR"
1977 /* %STRINGPOOLSTOP% */
1979 if (overall >= _SK_SMART_OVERALL_MAX)
1982 return _P(map[overall]);
1985 static void bad_attribute_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
1986 if (a->prefailure && a->good_valid && !a->good)
1990 int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
1997 if (sk_disk_smart_status(d, &good) < 0)
2001 *overall = SK_SMART_OVERALL_BAD_STATUS;
2005 if (sk_disk_smart_get_bad(d, §ors) < 0) {
2006 if (errno != ENOENT)
2008 } else if (sectors > 0) {
2009 *overall = SK_SMART_OVERALL_BAD_SECTOR;
2014 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_cb, &good) < 0)
2018 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE;
2022 *overall = SK_SMART_OVERALL_GOOD;
2026 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
2031 snprintf(s, len, "%u", id);
2038 static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
2040 switch (pretty_unit) {
2041 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
2043 if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
2044 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
2045 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
2046 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
2047 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
2048 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
2049 else if (pretty_value >= 1000LLU*60LLU*60LLU)
2050 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
2051 else if (pretty_value >= 1000LLU*60LLU)
2052 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
2053 else if (pretty_value >= 1000LLU)
2054 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
2056 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
2060 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
2061 snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
2064 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
2065 snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
2068 case SK_SMART_ATTRIBUTE_UNIT_NONE:
2069 snprintf(s, len, "%llu", (unsigned long long) pretty_value);
2072 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
2073 snprintf(s, len, "n/a");
2076 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
2085 #define HIGHLIGHT "\x1B[1m"
2086 #define ENDHIGHLIGHT "\x1B[0m"
2088 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2091 char tt[32], tw[32], tc[32];
2094 snprintf(tt, sizeof(tt), "%3u", a->threshold);
2095 tt[sizeof(tt)-1] = 0;
2096 snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2097 tw[sizeof(tw)-1] = 0;
2098 snprintf(tc, sizeof(tc), "%3u", a->current_value);
2099 tc[sizeof(tc)-1] = 0;
2101 highlight = a->good_valid && !a->good && isatty(1);
2104 fprintf(stderr, HIGHLIGHT);
2106 printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-3s\n",
2108 print_name(name, sizeof(name), a->id, a->name),
2109 a->current_value_valid ? tc : "n/a",
2110 a->worst_value_valid ? tw : "n/a",
2111 a->threshold_valid ? tt : "n/a",
2112 print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2113 a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2114 a->prefailure ? "prefail" : "old-age",
2115 a->online ? "online" : "offline",
2116 a->good_valid ? yes_no(a->good) : "n/a");
2119 fprintf(stderr, ENDHIGHLIGHT);
2122 int sk_disk_dump(SkDisk *d) {
2124 SkBool awake = FALSE;
2129 printf("Device: %s%s%s\n"
2131 d->name ? disk_type_to_prefix_string(d->type) : "",
2133 d->name ? d->name : "n/a",
2134 disk_type_to_human_string(d->type));
2136 ret = sk_disk_get_size(d, &size);
2138 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2140 printf("Size: %s\n", strerror(errno));
2142 if (d->identify_valid) {
2143 const SkIdentifyParsedData *ipd;
2144 SkSmartQuirk quirk = 0;
2147 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2150 printf("Model: [%s]\n"
2153 "SMART Available: %s\n",
2157 yes_no(disk_smart_is_available(d)));
2159 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2164 for (i = 0; quirk_name[i]; i++)
2166 printf(" %s", _P(quirk_name[i]));
2172 ret = sk_disk_check_sleep_mode(d, &awake);
2173 printf("Awake: %s\n",
2174 ret >= 0 ? yes_no(awake) : strerror(errno));
2176 if (disk_smart_is_available(d)) {
2177 SkSmartOverall overall;
2178 const SkSmartParsedData *spd;
2181 uint64_t value, power_on;
2183 ret = sk_disk_smart_status(d, &good);
2184 printf("SMART Disk Health Good: %s\n",
2185 ret >= 0 ? yes_no(good) : strerror(errno));
2187 if ((ret = sk_disk_smart_read_data(d)) < 0)
2190 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2193 printf("Off-line Data Collection Status: [%s]\n"
2194 "Total Time To Complete Off-Line Data Collection: %u s\n"
2195 "Self-Test Execution Status: [%s]\n"
2196 "Percent Self-Test Remaining: %u%%\n"
2197 "Conveyance Self-Test Available: %s\n"
2198 "Short/Extended Self-Test Available: %s\n"
2199 "Start Self-Test Available: %s\n"
2200 "Abort Self-Test Available: %s\n"
2201 "Short Self-Test Polling Time: %u min\n"
2202 "Extended Self-Test Polling Time: %u min\n"
2203 "Conveyance Self-Test Polling Time: %u min\n",
2204 sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2205 spd->total_offline_data_collection_seconds,
2206 sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2207 spd->self_test_execution_percent_remaining,
2208 yes_no(spd->conveyance_test_available),
2209 yes_no(spd->short_and_extended_test_available),
2210 yes_no(spd->start_test_available),
2211 yes_no(spd->abort_test_available),
2212 spd->short_test_polling_minutes,
2213 spd->extended_test_polling_minutes,
2214 spd->conveyance_test_polling_minutes);
2216 if (sk_disk_smart_get_bad(d, &value) < 0)
2217 printf("Bad Sectors: %s\n", strerror(errno));
2219 printf("%sBad Sectors: %s%s\n",
2220 value > 0 ? HIGHLIGHT : "",
2221 print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2222 value > 0 ? ENDHIGHLIGHT : "");
2224 if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2225 printf("Powered On: %s\n", strerror(errno));
2228 printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2230 if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2231 printf("Power Cycles: %s\n", strerror(errno));
2233 printf("Power Cycles: %llu\n", (unsigned long long) value);
2235 if (value > 0 && power_on > 0)
2236 printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2239 if (sk_disk_smart_get_temperature(d, &value) < 0)
2240 printf("Temperature: %s\n", strerror(errno));
2242 printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2244 if (sk_disk_smart_get_overall(d, &overall) < 0)
2245 printf("Overall Status: %s\n", strerror(errno));
2247 printf("%sOverall Status: %s%s\n",
2248 overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2249 sk_smart_overall_to_string(overall),
2250 overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2252 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-3s\n",
2264 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2267 printf("ATA SMART not supported.\n");
2272 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2276 if (d->size == (uint64_t) -1) {
2285 static int disk_find_type(SkDisk *d, dev_t devnum) {
2287 struct udev_device *dev = NULL, *usb;
2293 if (!(udev = udev_new())) {
2298 if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2303 if ((a = udev_device_get_property_value(dev, "ID_ATA_SMART_ACCESS"))) {
2306 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
2309 if (!(t = disk_type_to_prefix_string(u)))
2312 if (!strcmp(a, t)) {
2319 d->type = SK_DISK_TYPE_NONE;
2324 if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2325 const char *product, *vendor;
2328 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2329 sscanf(product, "%04x", &pid) != 1) {
2334 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2335 sscanf(vendor, "%04x", &vid) != 1) {
2340 if ((vid == 0x0c0b && pid == 0xb159) ||
2341 (vid == 0x04fc && pid == 0x0c25) ||
2342 (vid == 0x04fc && pid == 0x0c15))
2343 d->type = SK_DISK_TYPE_SUNPLUS;
2344 else if ((vid == 0x152d && pid == 0x2329) ||
2345 (vid == 0x152d && pid == 0x2336) ||
2346 (vid == 0x152d && pid == 0x2338) ||
2347 (vid == 0x152d && pid == 0x2339))
2348 d->type = SK_DISK_TYPE_JMICRON;
2350 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2352 } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2353 d->type = SK_DISK_TYPE_LINUX_IDE;
2354 else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2355 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2357 d->type = SK_DISK_TYPE_AUTO;
2363 udev_device_unref(dev);
2371 int sk_disk_open(const char *name, SkDisk **_d) {
2378 if (!(d = calloc(1, sizeof(SkDisk)))) {
2384 d->size = (uint64_t) -1;
2387 d->type = SK_DISK_TYPE_BLOB;
2391 d->type = SK_DISK_TYPE_AUTO;
2393 if (!(dn = disk_type_from_string(name, &d->type)))
2396 if (!(d->name = strdup(dn))) {
2401 if ((d->fd = open(d->name,
2402 O_RDONLY|O_NOCTTY|O_NONBLOCK
2412 if ((ret = fstat(d->fd, &st)) < 0)
2415 if (!S_ISBLK(st.st_mode)) {
2421 /* So, it's a block device. Let's make sure the ioctls work */
2422 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2425 if (d->size <= 0 || d->size == (uint64_t) -1) {
2431 /* OK, it's a real block device with a size. Now let's find the suitable API */
2432 if (d->type == SK_DISK_TYPE_AUTO)
2433 if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2436 if (d->type == SK_DISK_TYPE_AUTO) {
2437 /* We have no clue, so let's autotest for a working API */
2438 for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2439 if (disk_identify_device(d) >= 0)
2441 if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2442 d->type = SK_DISK_TYPE_NONE;
2444 disk_identify_device(d);
2446 /* Check if driver can do SMART, and enable if necessary */
2447 if (disk_smart_is_available(d)) {
2449 if (!disk_smart_is_enabled(d)) {
2450 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2453 if ((ret = disk_identify_device(d)) < 0)
2456 if (!disk_smart_is_enabled(d)) {
2463 disk_smart_read_thresholds(d);
2479 void sk_disk_free(SkDisk *d) {
2490 int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2492 SkBool good, have_good = FALSE;
2500 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2501 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2502 (d->smart_thresholds ? 8 + sizeof(d->smart_thresholds) : 0);
2504 if (sk_disk_smart_status(d, &good) >= 0) {
2515 if (!(d->blob = malloc(size))) {
2522 /* These memory accesses are only OK as long as all our
2523 * objects are sensibly aligned, which they are... */
2525 if (d->identify_valid) {
2526 p[0] = SK_BLOB_TAG_IDENTIFY;
2527 p[1] = htonl(sizeof(d->identify));
2530 memcpy(p, d->identify, sizeof(d->identify));
2531 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2535 p[0] = SK_BLOB_TAG_SMART_STATUS;
2537 p[2] = htonl(!!good);
2541 if (d->smart_data_valid) {
2542 p[0] = SK_BLOB_TAG_SMART_DATA;
2543 p[1] = htonl(sizeof(d->smart_data));
2546 memcpy(p, d->smart_data, sizeof(d->smart_data));
2547 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2550 if (d->smart_thresholds_valid) {
2551 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2552 p[1] = htonl(sizeof(d->smart_thresholds));
2555 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2556 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2559 assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2567 int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2570 SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2575 if (d->type != SK_DISK_TYPE_BLOB) {
2585 /* First run, verify if everything makes sense */
2589 uint32_t tag, tsize;
2597 memcpy(&tsize, p+1, 4);
2601 if (left < ntohl(tsize)) {
2608 case SK_BLOB_TAG_IDENTIFY:
2609 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2616 case SK_BLOB_TAG_SMART_STATUS:
2617 if (ntohl(tsize) != 4 || bssv) {
2624 case SK_BLOB_TAG_SMART_DATA:
2625 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2632 case SK_BLOB_TAG_SMART_THRESHOLDS:
2633 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
2641 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2642 left -= ntohl(tsize);
2650 d->identify_valid = idv;
2651 d->smart_data_valid = sdv;
2652 d->smart_thresholds_valid = stv;
2653 d->blob_smart_status_valid = bssv;
2655 /* Second run, actually copy things in */
2659 uint32_t tag, tsize;
2663 memcpy(&tsize, p+1, 4);
2667 assert(left >= ntohl(tsize));
2671 case SK_BLOB_TAG_IDENTIFY:
2672 assert(ntohl(tsize) == sizeof(d->identify));
2673 memcpy(d->identify, p, sizeof(d->identify));
2676 case SK_BLOB_TAG_SMART_STATUS: {
2678 assert(ntohl(tsize) == 4);
2680 d->blob_smart_status = !!ok;
2684 case SK_BLOB_TAG_SMART_DATA:
2685 assert(ntohl(tsize) == sizeof(d->smart_data));
2686 memcpy(d->smart_data, p, sizeof(d->smart_data));
2689 case SK_BLOB_TAG_SMART_THRESHOLDS:
2690 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
2691 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
2695 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2696 left -= ntohl(tsize);