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 = 1,
1253 SK_SMART_QUIRK_9_POWERONSECONDS = 2,
1254 SK_SMART_QUIRK_9_POWERONHALFMINUTES = 4,
1255 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 8,
1256 SK_SMART_QUIRK_193_LOADUNLOAD = 16,
1257 SK_SMART_QUIRK_194_10XCELSIUS = 32,
1258 SK_SMART_QUIRK_194_UNKNOWN = 64,
1259 SK_SMART_QUIRK_200_WRITEERRORCOUNT = 128,
1260 SK_SMART_QUIRK_201_DETECTEDTACOUNT = 256,
1261 SK_SMART_QUIRK_9_UNKNOWN = 512,
1262 SK_SMART_QUIRK_197_UNKNOWN = 1024,
1263 SK_SMART_QUIRK_198_UNKNOWN = 2048,
1266 /* %STRINGPOOLSTART% */
1267 static const char *quirk_name[] = {
1270 "9_POWERONHALFMINUTES",
1271 "192_EMERGENCYRETRACTCYCLECT",
1275 "200_WRITEERRORCOUNT",
1276 "201_DETECTEDTACOUNT",
1282 /* %STRINGPOOLSTOP% */
1284 typedef struct SkSmartQuirkDatabase {
1286 const char *firmware;
1288 } SkSmartQuirkDatabase;
1290 static const SkSmartQuirkDatabase quirk_database[] = { {
1293 "^FUJITSU MHY2120BH$",
1294 "^0085000B$", /* seems to be specific to this firmware */
1295 SK_SMART_QUIRK_9_UNKNOWN|
1296 SK_SMART_QUIRK_197_UNKNOWN|
1297 SK_SMART_QUIRK_198_UNKNOWN
1299 "^FUJITSU MHR2040AT$",
1301 SK_SMART_QUIRK_9_POWERONSECONDS|
1302 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1303 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1305 "^FUJITSU MHS20[6432]0AT( .)?$",
1307 SK_SMART_QUIRK_9_POWERONSECONDS|
1308 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1309 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1310 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1314 "FUJITSU MHG2...ATU?.*|"
1315 "FUJITSU MHH2...ATU?.*|"
1316 "FUJITSU MHJ2...ATU?.*|"
1317 "FUJITSU MHK2...ATU?.*|"
1318 "FUJITSU MHL2300AT|"
1319 "FUJITSU MHM2(20|15|10|06)0AT|"
1320 "FUJITSU MHN2...AT|"
1321 "FUJITSU MHR2020AT|"
1322 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1323 "FUJITSU MHU2...ATU?.*|"
1324 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1325 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1328 SK_SMART_QUIRK_9_POWERONSECONDS
1334 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1337 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1344 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1345 SK_SMART_QUIRK_194_10XCELSIUS
1347 "^SAMSUNG SP40A2H$",
1349 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1351 "^SAMSUNG SP80A4H$",
1353 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1355 "^SAMSUNG SP8004H$",
1357 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1362 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1363 "Maxtor 4G(120J6|160J[68])|"
1364 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1367 SK_SMART_QUIRK_9_POWERONMINUTES|
1368 SK_SMART_QUIRK_194_UNKNOWN
1371 "Maxtor 2F0[234]0[JL]0|"
1372 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1373 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1374 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1375 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1376 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1377 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1378 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1379 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1380 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1381 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1382 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1383 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1384 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1385 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1386 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1387 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1388 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1389 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1390 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1391 "Maxtor (98196H8|96147H6)|"
1392 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1393 "Maxtor 6(E0[234]|K04)0L0|"
1394 "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|"
1395 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1396 "Maxtor 7Y250[PM]0|"
1397 "Maxtor [45]A(25|30|32)0[JN]0|"
1398 "Maxtor 7L(25|30)0[SR]0"
1401 SK_SMART_QUIRK_9_POWERONMINUTES
1407 "HITACHI_DK14FA-20B|"
1408 "HITACHI_DK23..-..B?|"
1409 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1410 "HE[JN]4230[23]0F9AT00|"
1411 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1414 SK_SMART_QUIRK_9_POWERONMINUTES|
1415 SK_SMART_QUIRK_193_LOADUNLOAD
1424 static int match(const char*regex, const char *s, SkBool *result) {
1430 if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1435 if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1437 if (k != REG_NOMATCH) {
1451 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1453 const SkSmartQuirkDatabase *db;
1457 for (db = quirk_database; db->model || db->firmware; db++) {
1460 SkBool matching = FALSE;
1462 if ((k = match(db->model, model, &matching)) < 0)
1470 SkBool matching = FALSE;
1472 if ((k = match(db->firmware, firmware, &matching)) < 0)
1486 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1487 const SkIdentifyParsedData *ipd;
1488 SkSmartQuirk quirk = 0;
1490 /* These are the complex ones */
1491 if (sk_disk_identify_parse(d, &ipd) < 0)
1494 if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1501 /* %STRINGPOOLSTART% */
1502 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1503 static const SkSmartAttributeInfo a = {
1504 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1508 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1509 static const SkSmartAttributeInfo a = {
1510 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1514 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1515 static const SkSmartAttributeInfo a = {
1516 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1519 } else if (quirk & SK_SMART_QUIRK_9_UNKNOWN)
1521 /* %STRINGPOOLSTOP% */
1526 /* %STRINGPOOLSTART% */
1527 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1528 static const SkSmartAttributeInfo a = {
1529 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1533 /* %STRINGPOOLSTOP% */
1538 /* %STRINGPOOLSTART% */
1539 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1540 static const SkSmartAttributeInfo a = {
1541 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN
1544 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1546 /* %STRINGPOOLSTOP% */
1551 if (quirk & SK_SMART_QUIRK_197_UNKNOWN)
1557 if (quirk & SK_SMART_QUIRK_198_UNKNOWN)
1563 /* %STRINGPOOLSTART% */
1564 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1565 static const SkSmartAttributeInfo a = {
1566 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1570 /* %STRINGPOOLSTOP% */
1575 /* %STRINGPOOLSTART% */
1576 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1577 static const SkSmartAttributeInfo a = {
1578 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1582 /* %STRINGPOOLSTOP% */
1588 /* These are the simple cases */
1589 if (attribute_info[id].name)
1590 return &attribute_info[id];
1595 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1597 if (!d->smart_data_valid) {
1602 switch (d->smart_data[362]) {
1605 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1610 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1614 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1619 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1624 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1629 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1633 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1637 d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1638 d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1640 d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1642 d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1643 d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1644 d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1645 d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1647 d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1648 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]);
1649 d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1651 *spd = &d->smart_parsed_data;
1656 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1660 if (!d->smart_thresholds_valid) {
1661 a->threshold_valid = FALSE;
1665 for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1670 a->threshold_valid = FALSE;
1671 a->good_valid = FALSE;
1675 a->threshold = p[1];
1676 a->threshold_valid = p[1] != 0xFE;
1678 a->good_valid = FALSE;
1681 /* Always-Fail and Always-Passing thresholds are not relevant
1682 * for our assessment. */
1683 if (p[1] >= 1 && p[1] <= 0xFD) {
1685 if (a->worst_value_valid) {
1686 a->good = a->good && (a->worst_value > a->threshold);
1687 a->good_valid = TRUE;
1690 if (a->current_value_valid) {
1691 a->good = a->good && (a->current_value > a->threshold);
1692 a->good_valid = TRUE;
1697 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1701 if (!d->smart_data_valid) {
1706 for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1707 SkSmartAttributeParsedData a;
1708 const SkSmartAttributeInfo *i;
1714 memset(&a, 0, sizeof(a));
1716 a.current_value = p[3];
1717 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1718 a.worst_value = p[4];
1719 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
1721 a.flags = ((uint16_t) p[2] << 8) | p[1];
1722 a.prefailure = !!(p[1] & 1);
1723 a.online = !!(p[1] & 2);
1725 memcpy(a.raw, p+5, 6);
1727 if ((i = lookup_attribute(d, p[0]))) {
1728 a.name = _P(i->name);
1729 a.pretty_unit = i->unit;
1731 if (asprintf(&an, "attribute-%u", a.id) < 0) {
1737 a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1742 find_threshold(d, &a);
1744 /* Handle a few fields specially */
1745 if ((!strcmp(a.name, "reallocated-sector-count") ||
1746 !strcmp(a.name, "current-pending-sector")) &&
1747 a.pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS &&
1748 a.pretty_value > 0) {
1750 a.good_valid = TRUE;
1753 cb(d, &a, userdata);
1760 static const char *yes_no(SkBool b) {
1761 return b ? "yes" : "no";
1764 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
1766 /* %STRINGPOOLSTART% */
1767 const char * const map[] = {
1768 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
1769 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
1770 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
1771 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
1772 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK"
1774 /* %STRINGPOOLSTOP% */
1776 if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
1779 return _P(map[unit]);
1782 struct attr_helper {
1787 static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1789 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
1792 if (!strcmp(a->name, "temperature-centi-celsius") ||
1793 !strcmp(a->name, "temperature-celsius") ||
1794 !strcmp(a->name, "temperature-celsius-2") ||
1795 !strcmp(a->name, "airflow-temperature-celsius")) {
1797 if (!ah->found || a->pretty_value > *ah->value)
1798 *ah->value = a->pretty_value;
1804 int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
1805 struct attr_helper ah;
1813 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
1824 static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1826 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
1829 if (!strcmp(a->name, "power-on-minutes") ||
1830 !strcmp(a->name, "power-on-seconds") ||
1831 !strcmp(a->name, "power-on-half-minutes") ||
1832 !strcmp(a->name, "power-on-hours")) {
1834 if (!ah->found || a->pretty_value > *ah->value)
1835 *ah->value = a->pretty_value;
1841 int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
1842 struct attr_helper ah;
1848 ah.value = mseconds;
1850 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
1861 static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1863 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
1866 if (!strcmp(a->name, "power-cycle-count")) {
1868 if (!ah->found || a->pretty_value > *ah->value)
1869 *ah->value = a->pretty_value;
1875 int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
1876 struct attr_helper ah;
1884 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
1895 static void reallocated_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1897 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1900 if (!strcmp(a->name, "reallocated-sector-count")) {
1902 if (!ah->found || a->pretty_value > *ah->value)
1903 *ah->value = a->pretty_value;
1909 static void pending_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1911 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1914 if (!strcmp(a->name, "current-pending-sector")) {
1916 if (!ah->found || a->pretty_value > *ah->value)
1917 *ah->value = a->pretty_value;
1923 int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
1924 struct attr_helper ah1, ah2;
1925 uint64_t sectors1, sectors2;
1931 ah1.value = §ors1;
1933 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) reallocated_cb, &ah1) < 0)
1937 ah2.value = §ors2;
1939 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) pending_cb, &ah2) < 0)
1942 if (!ah1.found && !ah2.found) {
1947 if (ah1.found && ah2.found)
1948 *sectors = sectors1 + sectors2;
1950 *sectors = sectors1;
1952 *sectors = sectors2;
1957 const char* sk_smart_overall_to_string(SkSmartOverall overall) {
1959 /* %STRINGPOOLSTART% */
1960 const char * const map[] = {
1961 [SK_SMART_OVERALL_GOOD] = "GOOD",
1962 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
1963 [SK_SMART_OVERALL_BAD_ATTRIBUTE] = "BAD_ATTRIBUTE",
1964 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR"
1966 /* %STRINGPOOLSTOP% */
1968 if (overall >= _SK_SMART_OVERALL_MAX)
1971 return _P(map[overall]);
1974 static void bad_attribute_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
1975 if (a->prefailure && a->good_valid && !a->good)
1979 int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
1986 if (sk_disk_smart_status(d, &good) < 0)
1990 *overall = SK_SMART_OVERALL_BAD_STATUS;
1994 if (sk_disk_smart_get_bad(d, §ors) < 0) {
1995 if (errno != ENOENT)
1997 } else if (sectors > 0) {
1998 *overall = SK_SMART_OVERALL_BAD_SECTOR;
2003 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_cb, &good) < 0)
2007 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE;
2011 *overall = SK_SMART_OVERALL_GOOD;
2015 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
2020 snprintf(s, len, "%u", id);
2027 static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
2029 switch (pretty_unit) {
2030 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
2032 if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
2033 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
2034 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
2035 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
2036 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
2037 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
2038 else if (pretty_value >= 1000LLU*60LLU*60LLU)
2039 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
2040 else if (pretty_value >= 1000LLU*60LLU)
2041 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
2042 else if (pretty_value >= 1000LLU)
2043 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
2045 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
2049 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
2050 snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
2053 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
2054 snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
2057 case SK_SMART_ATTRIBUTE_UNIT_NONE:
2058 snprintf(s, len, "%llu", (unsigned long long) pretty_value);
2061 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
2062 snprintf(s, len, "n/a");
2065 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
2074 #define HIGHLIGHT "\x1B[1m"
2075 #define ENDHIGHLIGHT "\x1B[0m"
2077 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2080 char tt[32], tw[32], tc[32];
2083 snprintf(tt, sizeof(tt), "%3u", a->threshold);
2084 tt[sizeof(tt)-1] = 0;
2085 snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2086 tw[sizeof(tw)-1] = 0;
2087 snprintf(tc, sizeof(tc), "%3u", a->current_value);
2088 tc[sizeof(tc)-1] = 0;
2090 highlight = a->good_valid && !a->good && isatty(1);
2093 fprintf(stderr, HIGHLIGHT);
2095 printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-3s\n",
2097 print_name(name, sizeof(name), a->id, a->name),
2098 a->current_value_valid ? tc : "n/a",
2099 a->worst_value_valid ? tw : "n/a",
2100 a->threshold_valid ? tt : "n/a",
2101 print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2102 a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2103 a->prefailure ? "prefail" : "old-age",
2104 a->online ? "online" : "offline",
2105 a->good_valid ? yes_no(a->good) : "n/a");
2108 fprintf(stderr, ENDHIGHLIGHT);
2111 int sk_disk_dump(SkDisk *d) {
2113 SkBool awake = FALSE;
2118 printf("Device: %s%s%s\n"
2120 d->name ? disk_type_to_prefix_string(d->type) : "",
2122 d->name ? d->name : "n/a",
2123 disk_type_to_human_string(d->type));
2125 ret = sk_disk_get_size(d, &size);
2127 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2129 printf("Size: %s\n", strerror(errno));
2131 if (d->identify_valid) {
2132 const SkIdentifyParsedData *ipd;
2133 SkSmartQuirk quirk = 0;
2136 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2139 printf("Model: [%s]\n"
2142 "SMART Available: %s\n",
2146 yes_no(disk_smart_is_available(d)));
2148 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2153 for (i = 0; quirk_name[i]; i++)
2155 printf(" %s", _P(quirk_name[i]));
2161 ret = sk_disk_check_sleep_mode(d, &awake);
2162 printf("Awake: %s\n",
2163 ret >= 0 ? yes_no(awake) : strerror(errno));
2165 if (disk_smart_is_available(d)) {
2166 SkSmartOverall overall;
2167 const SkSmartParsedData *spd;
2170 uint64_t value, power_on;
2172 ret = sk_disk_smart_status(d, &good);
2173 printf("SMART Disk Health Good: %s\n",
2174 ret >= 0 ? yes_no(good) : strerror(errno));
2176 if ((ret = sk_disk_smart_read_data(d)) < 0)
2179 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2182 printf("Off-line Data Collection Status: [%s]\n"
2183 "Total Time To Complete Off-Line Data Collection: %u s\n"
2184 "Self-Test Execution Status: [%s]\n"
2185 "Percent Self-Test Remaining: %u%%\n"
2186 "Conveyance Self-Test Available: %s\n"
2187 "Short/Extended Self-Test Available: %s\n"
2188 "Start Self-Test Available: %s\n"
2189 "Abort Self-Test Available: %s\n"
2190 "Short Self-Test Polling Time: %u min\n"
2191 "Extended Self-Test Polling Time: %u min\n"
2192 "Conveyance Self-Test Polling Time: %u min\n",
2193 sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2194 spd->total_offline_data_collection_seconds,
2195 sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2196 spd->self_test_execution_percent_remaining,
2197 yes_no(spd->conveyance_test_available),
2198 yes_no(spd->short_and_extended_test_available),
2199 yes_no(spd->start_test_available),
2200 yes_no(spd->abort_test_available),
2201 spd->short_test_polling_minutes,
2202 spd->extended_test_polling_minutes,
2203 spd->conveyance_test_polling_minutes);
2205 if (sk_disk_smart_get_bad(d, &value) < 0)
2206 printf("Bad Sectors: %s\n", strerror(errno));
2208 printf("%sBad Sectors: %s%s\n",
2209 value > 0 ? HIGHLIGHT : "",
2210 print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2211 value > 0 ? ENDHIGHLIGHT : "");
2213 if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2214 printf("Powered On: %s\n", strerror(errno));
2217 printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2219 if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2220 printf("Power Cycles: %s\n", strerror(errno));
2222 printf("Power Cycles: %llu\n", (unsigned long long) value);
2224 if (value > 0 && power_on > 0)
2225 printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2228 if (sk_disk_smart_get_temperature(d, &value) < 0)
2229 printf("Temperature: %s\n", strerror(errno));
2231 printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2233 if (sk_disk_smart_get_overall(d, &overall) < 0)
2234 printf("Overall Status: %s\n", strerror(errno));
2236 printf("%sOverall Status: %s%s\n",
2237 overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2238 sk_smart_overall_to_string(overall),
2239 overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2241 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-3s\n",
2253 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2256 printf("ATA SMART not supported.\n");
2261 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2265 if (d->size == (uint64_t) -1) {
2274 static int disk_find_type(SkDisk *d, dev_t devnum) {
2276 struct udev_device *dev = NULL, *usb;
2282 if (!(udev = udev_new())) {
2287 if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2292 if ((a = udev_device_get_property_value(dev, "ID_ATA_SMART_ACCESS"))) {
2295 for (u = 0; u < _SK_DISK_TYPE_MAX; u++) {
2298 if (!(t = disk_type_to_prefix_string(u)))
2301 if (!strcmp(a, t)) {
2308 d->type = SK_DISK_TYPE_NONE;
2313 if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2314 const char *product, *vendor;
2317 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2318 sscanf(product, "%04x", &pid) != 1) {
2323 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2324 sscanf(vendor, "%04x", &vid) != 1) {
2329 if ((vid == 0x0c0b && pid == 0xb159) ||
2330 (vid == 0x04fc && pid == 0x0c25) ||
2331 (vid == 0x04fc && pid == 0x0c15))
2332 d->type = SK_DISK_TYPE_SUNPLUS;
2333 else if ((vid == 0x152d && pid == 0x2329) ||
2334 (vid == 0x152d && pid == 0x2336) ||
2335 (vid == 0x152d && pid == 0x2338) ||
2336 (vid == 0x152d && pid == 0x2339))
2337 d->type = SK_DISK_TYPE_JMICRON;
2339 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2341 } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2342 d->type = SK_DISK_TYPE_LINUX_IDE;
2343 else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2344 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2346 d->type = SK_DISK_TYPE_AUTO;
2352 udev_device_unref(dev);
2360 int sk_disk_open(const char *name, SkDisk **_d) {
2367 if (!(d = calloc(1, sizeof(SkDisk)))) {
2373 d->size = (uint64_t) -1;
2376 d->type = SK_DISK_TYPE_BLOB;
2380 d->type = SK_DISK_TYPE_AUTO;
2382 if (!(dn = disk_type_from_string(name, &d->type)))
2385 if (!(d->name = strdup(dn))) {
2390 if ((d->fd = open(d->name,
2391 O_RDONLY|O_NOCTTY|O_NONBLOCK
2401 if ((ret = fstat(d->fd, &st)) < 0)
2404 if (!S_ISBLK(st.st_mode)) {
2410 /* So, it's a block device. Let's make sure the ioctls work */
2411 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2414 if (d->size <= 0 || d->size == (uint64_t) -1) {
2420 /* OK, it's a real block device with a size. Now let's find the suitable API */
2421 if (d->type == SK_DISK_TYPE_AUTO)
2422 if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2425 if (d->type == SK_DISK_TYPE_AUTO) {
2426 /* We have no clue, so let's autotest for a working API */
2427 for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2428 if (disk_identify_device(d) >= 0)
2430 if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2431 d->type = SK_DISK_TYPE_NONE;
2433 disk_identify_device(d);
2435 /* Check if driver can do SMART, and enable if necessary */
2436 if (disk_smart_is_available(d)) {
2438 if (!disk_smart_is_enabled(d)) {
2439 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2442 if ((ret = disk_identify_device(d)) < 0)
2445 if (!disk_smart_is_enabled(d)) {
2452 disk_smart_read_thresholds(d);
2468 void sk_disk_free(SkDisk *d) {
2479 int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2481 SkBool good, have_good = FALSE;
2489 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2490 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2491 (d->smart_thresholds ? 8 + sizeof(d->smart_thresholds) : 0);
2493 if (sk_disk_smart_status(d, &good) >= 0) {
2504 if (!(d->blob = malloc(size))) {
2511 /* These memory accesses are only OK as long as all our
2512 * objects are sensibly aligned, which they are... */
2514 if (d->identify_valid) {
2515 p[0] = SK_BLOB_TAG_IDENTIFY;
2516 p[1] = htonl(sizeof(d->identify));
2519 memcpy(p, d->identify, sizeof(d->identify));
2520 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2524 p[0] = SK_BLOB_TAG_SMART_STATUS;
2526 p[2] = htonl(!!good);
2530 if (d->smart_data_valid) {
2531 p[0] = SK_BLOB_TAG_SMART_DATA;
2532 p[1] = htonl(sizeof(d->smart_data));
2535 memcpy(p, d->smart_data, sizeof(d->smart_data));
2536 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2539 if (d->smart_thresholds_valid) {
2540 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2541 p[1] = htonl(sizeof(d->smart_thresholds));
2544 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2545 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2548 assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2556 int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2559 SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2564 if (d->type != SK_DISK_TYPE_BLOB) {
2574 /* First run, verify if everything makes sense */
2578 uint32_t tag, tsize;
2586 memcpy(&tsize, p+1, 4);
2590 if (left < ntohl(tsize)) {
2597 case SK_BLOB_TAG_IDENTIFY:
2598 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2605 case SK_BLOB_TAG_SMART_STATUS:
2606 if (ntohl(tsize) != 4 || bssv) {
2613 case SK_BLOB_TAG_SMART_DATA:
2614 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2621 case SK_BLOB_TAG_SMART_THRESHOLDS:
2622 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
2630 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2631 left -= ntohl(tsize);
2639 d->identify_valid = idv;
2640 d->smart_data_valid = sdv;
2641 d->smart_thresholds_valid = stv;
2642 d->blob_smart_status_valid = bssv;
2644 /* Second run, actually copy things in */
2648 uint32_t tag, tsize;
2652 memcpy(&tsize, p+1, 4);
2656 assert(left >= ntohl(tsize));
2660 case SK_BLOB_TAG_IDENTIFY:
2661 assert(ntohl(tsize) == sizeof(d->identify));
2662 memcpy(d->identify, p, sizeof(d->identify));
2665 case SK_BLOB_TAG_SMART_STATUS: {
2667 assert(ntohl(tsize) == 4);
2669 d->blob_smart_status = !!ok;
2673 case SK_BLOB_TAG_SMART_DATA:
2674 assert(ntohl(tsize) == sizeof(d->smart_data));
2675 memcpy(d->smart_data, p, sizeof(d->smart_data));
2678 case SK_BLOB_TAG_SMART_THRESHOLDS:
2679 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
2680 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
2684 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2685 left -= ntohl(tsize);