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 */
75 _SK_DISK_TYPE_TEST_MAX = SK_DISK_TYPE_SUNPLUS /* only auto test until here */
78 #if __BYTE_ORDER == __LITTLE_ENDIAN
79 #define MAKE_TAG(a,b,c,d) \
80 (((uint32_t) d << 24) | \
81 ((uint32_t) c << 16) | \
82 ((uint32_t) b << 8) | \
85 #define MAKE_TAG(a,b,c,d) \
86 (((uint32_t) a << 24) | \
87 ((uint32_t) b << 16) | \
88 ((uint32_t) c << 8) | \
92 typedef enum SkBlobTag {
93 SK_BLOB_TAG_IDENTIFY = MAKE_TAG('I', 'D', 'F', 'Y'),
94 SK_BLOB_TAG_SMART_STATUS = MAKE_TAG('S', 'M', 'S', 'T'),
95 SK_BLOB_TAG_SMART_DATA = MAKE_TAG('S', 'M', 'D', 'T'),
96 SK_BLOB_TAG_SMART_THRESHOLDS = MAKE_TAG('S', 'M', 'T', 'H')
106 uint8_t identify[512];
107 uint8_t smart_data[512];
108 uint8_t smart_thresholds[512];
110 SkBool identify_valid:1;
111 SkBool smart_data_valid:1;
112 SkBool smart_thresholds_valid:1;
114 SkBool blob_smart_status:1;
115 SkBool blob_smart_status_valid:1;
117 SkIdentifyParsedData identify_parsed_data;
118 SkSmartParsedData smart_parsed_data;
124 typedef enum SkAtaCommand {
125 SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC,
126 SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1,
127 SK_ATA_COMMAND_SMART = 0xB0,
128 SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5
131 /* ATA SMART subcommands (ATA8 7.52.1) */
132 typedef enum SkSmartCommand {
133 SK_SMART_COMMAND_READ_DATA = 0xD0,
134 SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1,
135 SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4,
136 SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8,
137 SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9,
138 SK_SMART_COMMAND_RETURN_STATUS = 0xDA
141 static const char *disk_type_to_string(SkDiskType type) {
143 /* %STRINGPOOLSTART% */
144 static const char* const map[_SK_DISK_TYPE_MAX] = {
145 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "16 Byte SCSI ATA SAT Passthru",
146 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "12 Byte SCSI ATA SAT Passthru",
147 [SK_DISK_TYPE_LINUX_IDE] = "Native Linux IDE",
148 [SK_DISK_TYPE_SUNPLUS] = "Sunplus SCSI ATA Passthru",
149 [SK_DISK_TYPE_JMICRON] = "JMicron SCSI ATA Passthru",
150 [SK_DISK_TYPE_BLOB] = "Blob",
151 [SK_DISK_TYPE_UNKNOWN] = "Unknown"
153 /* %STRINGPOOLSTOP% */
155 if (type >= _SK_DISK_TYPE_MAX)
158 return _P(map[type]);
161 static SkBool disk_smart_is_available(SkDisk *d) {
162 return d->identify_valid && !!(d->identify[164] & 1);
165 static SkBool disk_smart_is_enabled(SkDisk *d) {
166 return d->identify_valid && !!(d->identify[170] & 1);
169 static SkBool disk_smart_is_conveyance_test_available(SkDisk *d) {
170 assert(d->smart_data_valid);
172 return !!(d->smart_data[367] & 32);
174 static SkBool disk_smart_is_short_and_extended_test_available(SkDisk *d) {
175 assert(d->smart_data_valid);
177 return !!(d->smart_data[367] & 16);
180 static SkBool disk_smart_is_start_test_available(SkDisk *d) {
181 assert(d->smart_data_valid);
183 return !!(d->smart_data[367] & 1);
186 static SkBool disk_smart_is_abort_test_available(SkDisk *d) {
187 assert(d->smart_data_valid);
189 return !!(d->smart_data[367] & 41);
192 static int disk_linux_ide_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
193 uint8_t *bytes = cmd_data;
196 assert(d->type == SK_DISK_TYPE_LINUX_IDE);
200 case SK_DIRECTION_OUT:
202 /* We could use HDIO_DRIVE_TASKFILE here, but
203 * that's a deprecated ioctl(), hence we don't
204 * do it. And we don't need writing anyway. */
209 case SK_DIRECTION_IN: {
212 /* We have HDIO_DRIVE_CMD which can only read, but not write,
213 * and cannot do LBA. We use it for all read commands. */
215 ioctl_data = alloca(4 + *len);
216 memset(ioctl_data, 0, 4 + *len);
218 ioctl_data[0] = (uint8_t) command; /* COMMAND */
219 ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */
220 ioctl_data[2] = bytes[1]; /* FEATURE */
221 ioctl_data[3] = bytes[3]; /* NSECTOR */
223 if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0)
226 memset(bytes, 0, 12);
227 bytes[11] = ioctl_data[0];
228 bytes[1] = ioctl_data[1];
229 bytes[3] = ioctl_data[2];
231 memcpy(data, ioctl_data+4, *len);
236 case SK_DIRECTION_NONE: {
237 uint8_t ioctl_data[7];
239 /* We have HDIO_DRIVE_TASK which can neither read nor
240 * write, but can do LBA. We use it for all commands that
241 * do neither read nor write */
243 memset(ioctl_data, 0, sizeof(ioctl_data));
245 ioctl_data[0] = (uint8_t) command; /* COMMAND */
246 ioctl_data[1] = bytes[1]; /* FEATURE */
247 ioctl_data[2] = bytes[3]; /* NSECTOR */
249 ioctl_data[3] = bytes[9]; /* LBA LOW */
250 ioctl_data[4] = bytes[8]; /* LBA MID */
251 ioctl_data[5] = bytes[7]; /* LBA HIGH */
252 ioctl_data[6] = bytes[10]; /* SELECT */
254 if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data)))
257 memset(bytes, 0, 12);
258 bytes[11] = ioctl_data[0];
259 bytes[1] = ioctl_data[1];
260 bytes[3] = ioctl_data[2];
262 bytes[9] = ioctl_data[3];
263 bytes[8] = ioctl_data[4];
264 bytes[7] = ioctl_data[5];
266 bytes[10] = ioctl_data[6];
277 /* Sends a SCSI command block */
278 static int sg_io(int fd, int direction,
279 const void *cdb, size_t cdb_len,
280 void *data, size_t data_len,
281 void *sense, size_t sense_len) {
283 struct sg_io_hdr io_hdr;
285 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
287 io_hdr.interface_id = 'S';
288 io_hdr.cmdp = (unsigned char*) cdb;
289 io_hdr.cmd_len = cdb_len;
290 io_hdr.dxferp = data;
291 io_hdr.dxfer_len = data_len;
293 io_hdr.mx_sb_len = sense_len;
294 io_hdr.dxfer_direction = direction;
295 io_hdr.timeout = SK_TIMEOUT;
297 return ioctl(fd, SG_IO, &io_hdr);
300 static int disk_passthrough_16_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
301 uint8_t *bytes = cmd_data;
304 uint8_t *desc = sense+8;
307 static const int direction_map[] = {
308 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
309 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
310 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
313 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_16);
315 /* ATA Pass-Through 16 byte command, as described in "T10 04-262r8
316 * ATA Command Pass-Through":
317 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
319 memset(cdb, 0, sizeof(cdb));
321 cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */
323 if (direction == SK_DIRECTION_NONE) {
324 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
325 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
327 } else if (direction == SK_DIRECTION_IN) {
328 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
329 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
331 } else if (direction == SK_DIRECTION_OUT) {
332 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
333 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
336 cdb[3] = bytes[0]; /* FEATURES */
339 cdb[5] = bytes[2]; /* SECTORS */
342 cdb[8] = bytes[9]; /* LBA LOW */
343 cdb[10] = bytes[8]; /* LBA MID */
344 cdb[12] = bytes[7]; /* LBA HIGH */
346 cdb[13] = bytes[10] & 0x4F; /* SELECT */
347 cdb[14] = (uint8_t) command;
349 memset(sense, 0, sizeof(sense));
351 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
354 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
359 memset(bytes, 0, 12);
367 bytes[10] = desc[12];
368 bytes[11] = desc[13];
373 static int disk_passthrough_12_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
374 uint8_t *bytes = cmd_data;
377 uint8_t *desc = sense+8;
380 static const int direction_map[] = {
381 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
382 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
383 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
386 assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12);
388 /* ATA Pass-Through 12 byte command, as described in "T10 04-262r8
389 * ATA Command Pass-Through":
390 * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
392 memset(cdb, 0, sizeof(cdb));
394 cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */
396 if (direction == SK_DIRECTION_NONE) {
397 cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
398 cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
400 } else if (direction == SK_DIRECTION_IN) {
401 cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
402 cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
404 } else if (direction == SK_DIRECTION_OUT) {
405 cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
406 cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
409 cdb[3] = bytes[1]; /* FEATURES */
410 cdb[4] = bytes[3]; /* SECTORS */
412 cdb[5] = bytes[9]; /* LBA LOW */
413 cdb[6] = bytes[8]; /* LBA MID */
414 cdb[7] = bytes[7]; /* LBA HIGH */
416 cdb[8] = bytes[10] & 0x4F; /* SELECT */
417 cdb[9] = (uint8_t) command;
419 memset(sense, 0, sizeof(sense));
421 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
424 if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
429 memset(bytes, 0, 12);
431 bytes[1] = desc[3]; /* FEATURES */
432 bytes[2] = desc[4]; /* STATUS */
433 bytes[3] = desc[5]; /* SECTORS */
434 bytes[9] = desc[7]; /* LBA LOW */
435 bytes[8] = desc[9]; /* LBA MID */
436 bytes[7] = desc[11]; /* LBA HIGH */
437 bytes[10] = desc[12]; /* SELECT */
438 bytes[11] = desc[13]; /* ERROR */
443 static int disk_sunplus_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
444 uint8_t *bytes = cmd_data;
446 uint8_t sense[32], buf[8];
448 static const int direction_map[] = {
449 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
450 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
451 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
454 assert(d->type == SK_DISK_TYPE_SUNPLUS);
456 /* SunplusIT specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
458 memset(cdb, 0, sizeof(cdb));
460 cdb[0] = 0xF8; /* OPERATION CODE: Sunplus specific */
461 cdb[1] = 0x00; /* Subcommand: Pass-thru */
464 if (direction == SK_DIRECTION_NONE)
465 cdb[3] = 0x00; /* protocol */
466 else if (direction == SK_DIRECTION_IN)
467 cdb[3] = 0x10; /* protocol */
468 else if (direction == SK_DIRECTION_OUT)
469 cdb[3] = 0x11; /* protocol */
471 cdb[4] = bytes[3]; /* size? */
472 cdb[5] = bytes[1]; /* FEATURES */
473 cdb[6] = bytes[3]; /* SECTORS */
474 cdb[7] = bytes[9]; /* LBA LOW */
475 cdb[8] = bytes[8]; /* LBA MID */
476 cdb[9] = bytes[7]; /* LBA HIGH */
477 cdb[10] = bytes[10] | 0xA0; /* SELECT */
478 cdb[11] = (uint8_t) command;
480 memset(sense, 0, sizeof(sense));
483 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0)
486 memset(cdb, 0, sizeof(cdb));
492 memset(buf, 0, sizeof(buf));
494 /* Ask for response */
495 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), buf, sizeof(buf), sense, sizeof(sense))) < 0)
498 memset(bytes, 0, 12);
500 bytes[2] = buf[1]; /* ERROR */
501 bytes[3] = buf[2]; /* SECTORS */
502 bytes[9] = buf[3]; /* LBA LOW */
503 bytes[8] = buf[4]; /* LBA MID */
504 bytes[7] = buf[5]; /* LBA HIGH */
505 bytes[10] = buf[6]; /* SELECT */
506 bytes[11] = buf[7]; /* STATUS */
511 static int disk_jmicron_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* _data, size_t *_len) {
512 uint8_t *bytes = cmd_data;
517 SkBool is_smart_status = FALSE;
519 size_t len = _len ? *_len : 0;
520 uint8_t smart_status = 0;
522 static const int direction_map[] = {
523 [SK_DIRECTION_NONE] = SG_DXFER_NONE,
524 [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
525 [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
528 assert(d->type == SK_DISK_TYPE_JMICRON);
530 /* JMicron specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */
532 memset(cdb, 0, sizeof(cdb));
534 cdb[0] = 0xdf; /* operation code */
537 cdb[3] = 0x00; /* size HI */
538 cdb[4] = sizeof(port); /* size LO */
540 cdb[6] = 0x72; /* register address HI */
541 cdb[7] = 0x0f; /* register address LO */
547 memset(sense, 0, sizeof(sense));
549 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), &port, sizeof(port), sense, sizeof(sense))) < 0)
552 /* Port & 0x04 is port #0, Port & 0x40 is port #1 */
556 cdb[0] = 0xdf; /* OPERATION CODE: 12 byte pass through */
558 if (command == SK_ATA_COMMAND_SMART && bytes[1] == SK_SMART_COMMAND_RETURN_STATUS) {
559 /* We need to rewrite the SMART status request */
560 is_smart_status = TRUE;
561 direction = SK_DIRECTION_IN;
562 data = &smart_status;
563 len = sizeof(smart_status);
565 } else if (direction == SK_DIRECTION_NONE)
567 else if (direction == SK_DIRECTION_IN)
569 else if (direction == SK_DIRECTION_OUT)
574 cdb[3] = (uint8_t) (len >> 8);
575 cdb[4] = (uint8_t) (len & 0xFF);
577 cdb[5] = bytes[1]; /* FEATURES */
578 cdb[6] = bytes[3]; /* SECTORS */
580 cdb[7] = bytes[9]; /* LBA LOW */
581 cdb[8] = bytes[8]; /* LBA MID */
582 cdb[9] = bytes[7]; /* LBA HIGH */
584 cdb[10] = bytes[10] | ((port & 0x04) ? 0xA0 : 0xB0); /* SELECT */
585 cdb[11] = (uint8_t) command;
587 memset(sense, 0, sizeof(sense));
589 if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len, sense, sizeof(sense))) < 0)
592 memset(bytes, 0, 12);
594 if (is_smart_status) {
595 if (smart_status == 0x01 || smart_status == 0xc2) {
596 bytes[7] = 0xc2; /* LBA HIGH */
597 bytes[8] = 0x4f; /* LBA MID */
598 } else if (smart_status == 0x00 || smart_status == 0x2c) {
599 bytes[7] = 0x2c; /* LBA HIGH */
600 bytes[8] = 0xf4; /* LBA MID */
606 cdb[0] = 0xdf; /* operation code */
609 cdb[3] = 0x00; /* size HI */
610 cdb[4] = sizeof(regbuf); /* size LO */
612 cdb[6] = (port & 0x04) ? 0x80 : 0x90; /* register address HI */
613 cdb[7] = 0x00; /* register address LO */
619 if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), regbuf, sizeof(regbuf), sense, sizeof(sense))) < 0)
622 bytes[2] = regbuf[14]; /* STATUS */
623 bytes[3] = regbuf[0]; /* SECTORS */
624 bytes[9] = regbuf[6]; /* LBA LOW */
625 bytes[8] = regbuf[4]; /* LBA MID */
626 bytes[7] = regbuf[10]; /* LBA HIGH */
627 bytes[10] = regbuf[9]; /* SELECT */
628 bytes[11] = regbuf[13]; /* ERROR */
634 static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) {
636 static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) = {
637 [SK_DISK_TYPE_LINUX_IDE] = disk_linux_ide_command,
638 [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = disk_passthrough_12_command,
639 [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = disk_passthrough_16_command,
640 [SK_DISK_TYPE_SUNPLUS] = disk_sunplus_command,
641 [SK_DISK_TYPE_JMICRON] = disk_jmicron_command,
642 [SK_DISK_TYPE_BLOB] = NULL,
643 [SK_DISK_TYPE_UNKNOWN] = NULL
647 assert(d->type <= _SK_DISK_TYPE_MAX);
648 assert(direction <= _SK_DIRECTION_MAX);
650 assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0));
651 assert(direction != SK_DIRECTION_NONE || (!data && !len));
653 if (!disk_command_table[d->type]) {
658 return disk_command_table[d->type](d, command, direction, cmd_data, data, len);
661 static int disk_identify_device(SkDisk *d) {
667 if (d->type == SK_DISK_TYPE_BLOB)
670 memset(d->identify, 0, len);
671 memset(cmd, 0, sizeof(cmd));
675 if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0)
683 /* Check if IDENTIFY data is all NULs */
684 for (p = d->identify; p < (const uint8_t*) d->identify+len; p++)
695 d->identify_valid = TRUE;
700 int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake) {
705 if (!d->identify_valid) {
710 if (d->type == SK_DISK_TYPE_BLOB) {
715 memset(cmd, 0, sizeof(cmd));
717 if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
720 if (cmd[0] != 0 || (ntohs(cmd[5]) & 1) != 0) {
725 status = ntohs(cmd[1]) & 0xFF;
726 *awake = status == 0xFF || status == 0x80; /* idle and active/idle is considered awake */
731 static int disk_smart_enable(SkDisk *d, SkBool b) {
734 if (!disk_smart_is_available(d)) {
739 if (d->type == SK_DISK_TYPE_BLOB) {
744 memset(cmd, 0, sizeof(cmd));
746 cmd[0] = htons(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS);
747 cmd[2] = htons(0x0000U);
748 cmd[3] = htons(0x00C2U);
749 cmd[4] = htons(0x4F00U);
751 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0);
754 int sk_disk_smart_read_data(SkDisk *d) {
759 if (!disk_smart_is_available(d)) {
764 if (d->type == SK_DISK_TYPE_BLOB)
767 memset(cmd, 0, sizeof(cmd));
769 cmd[0] = htons(SK_SMART_COMMAND_READ_DATA);
771 cmd[2] = htons(0x0000U);
772 cmd[3] = htons(0x00C2U);
773 cmd[4] = htons(0x4F00U);
775 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0)
778 d->smart_data_valid = TRUE;
783 static int disk_smart_read_thresholds(SkDisk *d) {
788 if (!disk_smart_is_available(d)) {
793 if (d->type == SK_DISK_TYPE_BLOB)
796 memset(cmd, 0, sizeof(cmd));
798 cmd[0] = htons(SK_SMART_COMMAND_READ_THRESHOLDS);
800 cmd[2] = htons(0x0000U);
801 cmd[3] = htons(0x00C2U);
802 cmd[4] = htons(0x4F00U);
804 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_thresholds, &len)) < 0)
807 d->smart_thresholds_valid = TRUE;
812 int sk_disk_smart_status(SkDisk *d, SkBool *good) {
816 if (!disk_smart_is_available(d)) {
821 if (d->type == SK_DISK_TYPE_BLOB) {
823 if (d->blob_smart_status_valid) {
824 *good = d->blob_smart_status;
832 memset(cmd, 0, sizeof(cmd));
834 cmd[0] = htons(SK_SMART_COMMAND_RETURN_STATUS);
835 cmd[1] = htons(0x0000U);
836 cmd[3] = htons(0x00C2U);
837 cmd[4] = htons(0x4F00U);
839 if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
842 /* SAT/USB bridges truncate packets, so we only check for 4F,
843 * not for 2C on those */
844 if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) &&
845 cmd[4] == htons(0x4F00U))
847 else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) &&
848 cmd[4] == htons(0xF400U))
858 int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) {
862 if (!disk_smart_is_available(d)) {
867 if (d->type == SK_DISK_TYPE_BLOB) {
872 if (!d->smart_data_valid)
873 if ((ret = sk_disk_smart_read_data(d)) < 0)
876 assert(d->smart_data_valid);
878 if (test != SK_SMART_SELF_TEST_SHORT &&
879 test != SK_SMART_SELF_TEST_EXTENDED &&
880 test != SK_SMART_SELF_TEST_CONVEYANCE &&
881 test != SK_SMART_SELF_TEST_ABORT) {
886 if (!disk_smart_is_start_test_available(d)
887 || (test == SK_SMART_SELF_TEST_ABORT && !disk_smart_is_abort_test_available(d))
888 || ((test == SK_SMART_SELF_TEST_SHORT || test == SK_SMART_SELF_TEST_EXTENDED) && !disk_smart_is_short_and_extended_test_available(d))
889 || (test == SK_SMART_SELF_TEST_CONVEYANCE && !disk_smart_is_conveyance_test_available(d))) {
894 if (test == SK_SMART_SELF_TEST_ABORT &&
895 !disk_smart_is_abort_test_available(d)) {
900 memset(cmd, 0, sizeof(cmd));
902 cmd[0] = htons(SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE);
903 cmd[2] = htons(0x0000U);
904 cmd[3] = htons(0x00C2U);
905 cmd[4] = htons(0x4F00U | (uint16_t) test);
907 return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, NULL);
910 static void swap_strings(char *s, size_t len) {
911 assert((len & 1) == 0);
913 for (; len > 0; s += 2, len -= 2) {
921 static void clean_strings(char *s) {
925 if (*e < ' ' || *e >= 127)
929 static void drop_spaces(char *s) {
931 SkBool prev_space = FALSE;
954 static void read_string(char *d, uint8_t *s, size_t len) {
957 swap_strings(d, len);
962 int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) {
966 if (!d->identify_valid) {
971 read_string(d->identify_parsed_data.serial, d->identify+20, 20);
972 read_string(d->identify_parsed_data.firmware, d->identify+46, 8);
973 read_string(d->identify_parsed_data.model, d->identify+54, 40);
975 *ipd = &d->identify_parsed_data;
980 int sk_disk_smart_is_available(SkDisk *d, SkBool *b) {
984 if (!d->identify_valid) {
989 *b = disk_smart_is_available(d);
993 int sk_disk_identify_is_available(SkDisk *d, SkBool *b) {
997 *b = d->identify_valid;
1001 const char *sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status) {
1003 /* %STRINGPOOLSTART% */
1004 static const char* const map[] = {
1005 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.",
1006 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.",
1007 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.",
1008 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.",
1009 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.",
1010 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.",
1011 [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status"
1013 /* %STRINGPOOLSTOP% */
1015 if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX)
1018 return _P(map[status]);
1021 const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status) {
1023 /* %STRINGPOOLSTART% */
1024 static const char* const map[] = {
1025 [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.",
1026 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED] = "The self-test routine was aborted by the host.",
1027 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED] = "The self-test routine was interrupted by the host with a hardware or software reset.",
1028 [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.",
1029 [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.",
1030 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL] = "The previous self-test completed having the electrical element of the test failed.",
1031 [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.",
1032 [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ] = "The previous self-test completed having the read element of the test failed.",
1033 [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.",
1034 [SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS] = "Self-test routine in progress"
1036 /* %STRINGPOOLSTOP% */
1038 if (status >= _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX)
1041 return _P(map[status]);
1044 const char* sk_smart_self_test_to_string(SkSmartSelfTest test) {
1047 case SK_SMART_SELF_TEST_SHORT:
1049 case SK_SMART_SELF_TEST_EXTENDED:
1051 case SK_SMART_SELF_TEST_CONVEYANCE:
1052 return "conveyance";
1053 case SK_SMART_SELF_TEST_ABORT:
1060 SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) {
1063 if (!d->start_test_available)
1067 case SK_SMART_SELF_TEST_SHORT:
1068 case SK_SMART_SELF_TEST_EXTENDED:
1069 return d->short_and_extended_test_available;
1070 case SK_SMART_SELF_TEST_CONVEYANCE:
1071 return d->conveyance_test_available;
1072 case SK_SMART_SELF_TEST_ABORT:
1073 return d->abort_test_available;
1079 unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) {
1082 if (!sk_smart_self_test_available(d, test))
1086 case SK_SMART_SELF_TEST_SHORT:
1087 return d->short_test_polling_minutes;
1088 case SK_SMART_SELF_TEST_EXTENDED:
1089 return d->extended_test_polling_minutes;
1090 case SK_SMART_SELF_TEST_CONVEYANCE:
1091 return d->conveyance_test_polling_minutes;
1097 static void make_pretty(SkSmartAttributeParsedData *a) {
1098 uint64_t fourtyeight;
1103 if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN)
1107 ((uint64_t) a->raw[0]) |
1108 (((uint64_t) a->raw[1]) << 8) |
1109 (((uint64_t) a->raw[2]) << 16) |
1110 (((uint64_t) a->raw[3]) << 24) |
1111 (((uint64_t) a->raw[4]) << 32) |
1112 (((uint64_t) a->raw[5]) << 40);
1114 if (!strcmp(a->name, "spin-up-time"))
1115 a->pretty_value = fourtyeight & 0xFFFF;
1116 else if (!strcmp(a->name, "airflow-temperature-celsius") ||
1117 !strcmp(a->name, "temperature-celsius") ||
1118 !strcmp(a->name, "temperature-celsius-2"))
1119 a->pretty_value = (fourtyeight & 0xFFFF)*1000 + 273150;
1120 else if (!strcmp(a->name, "temperature-centi-celsius"))
1121 a->pretty_value = (fourtyeight & 0xFFFF)*100 + 273150;
1122 else if (!strcmp(a->name, "power-on-minutes"))
1123 a->pretty_value = (((uint64_t) a->raw[0]) | (uint64_t) a->raw[1]) * 60 * 1000;
1124 else if (!strcmp(a->name, "power-on-seconds"))
1125 a->pretty_value = fourtyeight * 1000;
1126 else if (!strcmp(a->name, "power-on-half-minutes"))
1127 a->pretty_value = fourtyeight * 30 * 1000;
1128 else if (!strcmp(a->name, "power-on-hours") ||
1129 !strcmp(a->name, "loaded-hours") ||
1130 !strcmp(a->name, "head-flying-hours"))
1131 a->pretty_value = (fourtyeight & 0xFFFFFFFFU) * 60 * 60 * 1000;
1132 else if (!strcmp(a->name, "reallocated-sector-count") ||
1133 !strcmp(a->name, "current-pending-sector"))
1134 a->pretty_value = fourtyeight & 0xFFFFFFFFU;
1136 a->pretty_value = fourtyeight;
1139 typedef struct SkSmartAttributeInfo {
1141 SkSmartAttributeUnit unit;
1142 } SkSmartAttributeInfo;
1144 /* This data is stolen from smartmontools */
1146 /* %STRINGPOOLSTART% */
1147 static const SkSmartAttributeInfo const attribute_info[256] = {
1148 [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1149 [2] = { "throughput-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1150 [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1151 [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1152 [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1153 [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1154 [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1155 [8] = { "seek-time-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1156 [9] = { "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1157 [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1158 [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1159 [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1160 [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1161 [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1162 [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE },
1163 [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1164 [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1165 [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1166 [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1167 [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1168 [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE },
1169 [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1170 [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1171 [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1172 [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1173 [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1174 [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1175 [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1176 [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1177 [204] = { "shock-count-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE },
1178 [205] = { "shock-rate-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE },
1179 [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1180 [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1181 [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN},
1182 [209] = { "offline-seek-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1183 [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1184 [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
1185 [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1186 [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1187 [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1188 [225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
1189 [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1190 [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1191 [228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
1192 [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1193 [231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1194 [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1195 [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE }
1197 /* %STRINGPOOLSTOP% */
1199 typedef enum SkSmartQuirk {
1200 SK_SMART_QUIRK_9_POWERONMINUTES = 1,
1201 SK_SMART_QUIRK_9_POWERONSECONDS = 2,
1202 SK_SMART_QUIRK_9_POWERONHALFMINUTES = 4,
1203 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 8,
1204 SK_SMART_QUIRK_193_LOADUNLOAD = 16,
1205 SK_SMART_QUIRK_194_10XCELSIUS = 32,
1206 SK_SMART_QUIRK_194_UNKNOWN = 64,
1207 SK_SMART_QUIRK_200_WRITEERRORCOUNT = 128,
1208 SK_SMART_QUIRK_201_DETECTEDTACOUNT = 256,
1209 SK_SMART_QUIRK_9_UNKNOWN = 512,
1210 SK_SMART_QUIRK_197_UNKNOWN = 1024,
1211 SK_SMART_QUIRK_198_UNKNOWN = 2048,
1214 /* %STRINGPOOLSTART% */
1215 static const char *quirk_name[] = {
1218 "9_POWERONHALFMINUTES",
1219 "192_EMERGENCYRETRACTCYCLECT",
1223 "200_WRITEERRORCOUNT",
1224 "201_DETECTEDTACOUNT",
1230 /* %STRINGPOOLSTOP% */
1232 typedef struct SkSmartQuirkDatabase {
1234 const char *firmware;
1236 } SkSmartQuirkDatabase;
1238 static const SkSmartQuirkDatabase quirk_database[] = { {
1241 "^FUJITSU MHY2120BH$",
1242 "^0085000B$", /* seems to be specific to this firmware */
1243 SK_SMART_QUIRK_9_UNKNOWN|
1244 SK_SMART_QUIRK_197_UNKNOWN|
1245 SK_SMART_QUIRK_198_UNKNOWN
1247 "^FUJITSU MHR2040AT$",
1249 SK_SMART_QUIRK_9_POWERONSECONDS|
1250 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1251 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1253 "^FUJITSU MHS20[6432]0AT( .)?$",
1255 SK_SMART_QUIRK_9_POWERONSECONDS|
1256 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1257 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1258 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1262 "FUJITSU MHG2...ATU?.*|"
1263 "FUJITSU MHH2...ATU?.*|"
1264 "FUJITSU MHJ2...ATU?.*|"
1265 "FUJITSU MHK2...ATU?.*|"
1266 "FUJITSU MHL2300AT|"
1267 "FUJITSU MHM2(20|15|10|06)0AT|"
1268 "FUJITSU MHN2...AT|"
1269 "FUJITSU MHR2020AT|"
1270 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1271 "FUJITSU MHU2...ATU?.*|"
1272 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1273 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1276 SK_SMART_QUIRK_9_POWERONSECONDS
1282 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1285 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1292 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1293 SK_SMART_QUIRK_194_10XCELSIUS
1295 "^SAMSUNG SP40A2H$",
1297 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1299 "^SAMSUNG SP80A4H$",
1301 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1303 "^SAMSUNG SP8004H$",
1305 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1310 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1311 "Maxtor 4G(120J6|160J[68])|"
1312 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1315 SK_SMART_QUIRK_9_POWERONMINUTES|
1316 SK_SMART_QUIRK_194_UNKNOWN
1319 "Maxtor 2F0[234]0[JL]0|"
1320 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1321 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1322 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1323 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1324 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1325 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1326 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1327 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1328 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1329 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1330 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1331 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1332 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1333 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1334 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1335 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1336 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1337 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1338 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1339 "Maxtor (98196H8|96147H6)|"
1340 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1341 "Maxtor 6(E0[234]|K04)0L0|"
1342 "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|"
1343 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1344 "Maxtor 7Y250[PM]0|"
1345 "Maxtor [45]A(25|30|32)0[JN]0|"
1346 "Maxtor 7L(25|30)0[SR]0"
1349 SK_SMART_QUIRK_9_POWERONMINUTES
1355 "HITACHI_DK14FA-20B|"
1356 "HITACHI_DK23..-..B?|"
1357 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1358 "HE[JN]4230[23]0F9AT00|"
1359 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1362 SK_SMART_QUIRK_9_POWERONMINUTES|
1363 SK_SMART_QUIRK_193_LOADUNLOAD
1372 static int match(const char*regex, const char *s, SkBool *result) {
1378 if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1383 if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1385 if (k != REG_NOMATCH) {
1399 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1401 const SkSmartQuirkDatabase *db;
1405 for (db = quirk_database; db->model || db->firmware; db++) {
1408 SkBool matching = FALSE;
1410 if ((k = match(db->model, model, &matching)) < 0)
1418 SkBool matching = FALSE;
1420 if ((k = match(db->firmware, firmware, &matching)) < 0)
1434 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1435 const SkIdentifyParsedData *ipd;
1436 SkSmartQuirk quirk = 0;
1438 /* These are the complex ones */
1439 if (sk_disk_identify_parse(d, &ipd) < 0)
1442 if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1449 /* %STRINGPOOLSTART% */
1450 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1451 static const SkSmartAttributeInfo a = {
1452 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1456 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1457 static const SkSmartAttributeInfo a = {
1458 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1462 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1463 static const SkSmartAttributeInfo a = {
1464 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1467 } else if (quirk & SK_SMART_QUIRK_9_UNKNOWN)
1469 /* %STRINGPOOLSTOP% */
1474 /* %STRINGPOOLSTART% */
1475 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1476 static const SkSmartAttributeInfo a = {
1477 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1481 /* %STRINGPOOLSTOP% */
1486 /* %STRINGPOOLSTART% */
1487 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1488 static const SkSmartAttributeInfo a = {
1489 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN
1492 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1494 /* %STRINGPOOLSTOP% */
1499 if (quirk & SK_SMART_QUIRK_197_UNKNOWN)
1505 if (quirk & SK_SMART_QUIRK_198_UNKNOWN)
1511 /* %STRINGPOOLSTART% */
1512 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1513 static const SkSmartAttributeInfo a = {
1514 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1518 /* %STRINGPOOLSTOP% */
1523 /* %STRINGPOOLSTART% */
1524 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1525 static const SkSmartAttributeInfo a = {
1526 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1530 /* %STRINGPOOLSTOP% */
1536 /* These are the simple cases */
1537 if (attribute_info[id].name)
1538 return &attribute_info[id];
1543 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1545 if (!d->smart_data_valid) {
1550 switch (d->smart_data[362]) {
1553 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1558 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1562 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1567 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1572 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1577 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1581 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1585 d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1586 d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1588 d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1590 d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1591 d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1592 d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1593 d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1595 d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1596 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]);
1597 d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1599 *spd = &d->smart_parsed_data;
1604 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1608 if (!d->smart_thresholds_valid) {
1609 a->threshold_valid = FALSE;
1613 for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1618 a->threshold_valid = FALSE;
1619 a->good_valid = FALSE;
1623 a->threshold = p[1];
1624 a->threshold_valid = p[1] != 0xFE;
1626 a->good_valid = FALSE;
1629 /* Always-Fail and Always-Passing thresholds are not relevant
1630 * for our assessment. */
1631 if (p[1] >= 1 && p[1] <= 0xFD) {
1633 if (a->worst_value_valid) {
1634 a->good = a->good && (a->worst_value > a->threshold);
1635 a->good_valid = TRUE;
1638 if (a->current_value_valid) {
1639 a->good = a->good && (a->current_value > a->threshold);
1640 a->good_valid = TRUE;
1645 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1649 if (!d->smart_data_valid) {
1654 for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1655 SkSmartAttributeParsedData a;
1656 const SkSmartAttributeInfo *i;
1662 memset(&a, 0, sizeof(a));
1664 a.current_value = p[3];
1665 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1666 a.worst_value = p[4];
1667 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
1669 a.flags = ((uint16_t) p[2] << 8) | p[1];
1670 a.prefailure = !!(p[1] & 1);
1671 a.online = !!(p[1] & 2);
1673 memcpy(a.raw, p+5, 6);
1675 if ((i = lookup_attribute(d, p[0]))) {
1676 a.name = _P(i->name);
1677 a.pretty_unit = i->unit;
1679 if (asprintf(&an, "attribute-%u", a.id) < 0) {
1685 a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1690 find_threshold(d, &a);
1692 /* Handle a few fields specially */
1693 if ((!strcmp(a.name, "reallocated-sector-count") ||
1694 !strcmp(a.name, "current-pending-sector")) &&
1695 a.pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS &&
1696 a.pretty_value > 0) {
1698 a.good_valid = TRUE;
1701 cb(d, &a, userdata);
1708 static const char *yes_no(SkBool b) {
1709 return b ? "yes" : "no";
1712 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
1714 /* %STRINGPOOLSTART% */
1715 const char * const map[] = {
1716 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
1717 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
1718 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
1719 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
1720 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK"
1722 /* %STRINGPOOLSTOP% */
1724 if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
1727 return _P(map[unit]);
1730 struct attr_helper {
1735 static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1737 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
1740 if (!strcmp(a->name, "temperature-centi-celsius") ||
1741 !strcmp(a->name, "temperature-celsius") ||
1742 !strcmp(a->name, "temperature-celsius-2") ||
1743 !strcmp(a->name, "airflow-temperature-celsius")) {
1745 if (!ah->found || a->pretty_value > *ah->value)
1746 *ah->value = a->pretty_value;
1752 int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
1753 struct attr_helper ah;
1761 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
1772 static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1774 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
1777 if (!strcmp(a->name, "power-on-minutes") ||
1778 !strcmp(a->name, "power-on-seconds") ||
1779 !strcmp(a->name, "power-on-half-minutes") ||
1780 !strcmp(a->name, "power-on-hours")) {
1782 if (!ah->found || a->pretty_value > *ah->value)
1783 *ah->value = a->pretty_value;
1789 int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
1790 struct attr_helper ah;
1796 ah.value = mseconds;
1798 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
1809 static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1811 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
1814 if (!strcmp(a->name, "power-cycle-count")) {
1816 if (!ah->found || a->pretty_value > *ah->value)
1817 *ah->value = a->pretty_value;
1823 int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
1824 struct attr_helper ah;
1832 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
1843 static void reallocated_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1845 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1848 if (!strcmp(a->name, "reallocated-sector-count")) {
1850 if (!ah->found || a->pretty_value > *ah->value)
1851 *ah->value = a->pretty_value;
1857 static void pending_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1859 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1862 if (!strcmp(a->name, "current-pending-sector")) {
1864 if (!ah->found || a->pretty_value > *ah->value)
1865 *ah->value = a->pretty_value;
1871 int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
1872 struct attr_helper ah1, ah2;
1873 uint64_t sectors1, sectors2;
1879 ah1.value = §ors1;
1881 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) reallocated_cb, &ah1) < 0)
1885 ah2.value = §ors2;
1887 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) pending_cb, &ah2) < 0)
1890 if (!ah1.found && !ah2.found) {
1895 if (ah1.found && ah2.found)
1896 *sectors = sectors1 + sectors2;
1898 *sectors = sectors1;
1900 *sectors = sectors2;
1905 const char* sk_smart_overall_to_string(SkSmartOverall overall) {
1907 /* %STRINGPOOLSTART% */
1908 const char * const map[] = {
1909 [SK_SMART_OVERALL_GOOD] = "GOOD",
1910 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
1911 [SK_SMART_OVERALL_BAD_ATTRIBUTE] = "BAD_ATTRIBUTE",
1912 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR"
1914 /* %STRINGPOOLSTOP% */
1916 if (overall >= _SK_SMART_OVERALL_MAX)
1919 return _P(map[overall]);
1922 static void bad_attribute_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
1923 if (a->prefailure && a->good_valid && !a->good)
1927 int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
1934 if (sk_disk_smart_status(d, &good) < 0)
1938 *overall = SK_SMART_OVERALL_BAD_STATUS;
1942 if (sk_disk_smart_get_bad(d, §ors) < 0) {
1943 if (errno != ENOENT)
1945 } else if (sectors > 0) {
1946 *overall = SK_SMART_OVERALL_BAD_SECTOR;
1951 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_cb, &good) < 0)
1955 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE;
1959 *overall = SK_SMART_OVERALL_GOOD;
1963 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
1968 snprintf(s, len, "%u", id);
1975 static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
1977 switch (pretty_unit) {
1978 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
1980 if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
1981 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
1982 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
1983 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
1984 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
1985 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
1986 else if (pretty_value >= 1000LLU*60LLU*60LLU)
1987 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
1988 else if (pretty_value >= 1000LLU*60LLU)
1989 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
1990 else if (pretty_value >= 1000LLU)
1991 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
1993 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
1997 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
1998 snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
2001 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
2002 snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
2005 case SK_SMART_ATTRIBUTE_UNIT_NONE:
2006 snprintf(s, len, "%llu", (unsigned long long) pretty_value);
2009 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
2010 snprintf(s, len, "n/a");
2013 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
2022 #define HIGHLIGHT "\x1B[1m"
2023 #define ENDHIGHLIGHT "\x1B[0m"
2025 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2028 char tt[32], tw[32], tc[32];
2031 snprintf(tt, sizeof(tt), "%3u", a->threshold);
2032 tt[sizeof(tt)-1] = 0;
2033 snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2034 tw[sizeof(tw)-1] = 0;
2035 snprintf(tc, sizeof(tc), "%3u", a->current_value);
2036 tc[sizeof(tc)-1] = 0;
2038 highlight = a->good_valid && !a->good && isatty(1);
2041 fprintf(stderr, HIGHLIGHT);
2043 printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-3s\n",
2045 print_name(name, sizeof(name), a->id, a->name),
2046 a->current_value_valid ? tc : "n/a",
2047 a->worst_value_valid ? tw : "n/a",
2048 a->threshold_valid ? tt : "n/a",
2049 print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2050 a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2051 a->prefailure ? "prefail" : "old-age",
2052 a->online ? "online" : "offline",
2053 a->good_valid ? yes_no(a->good) : "n/a");
2056 fprintf(stderr, ENDHIGHLIGHT);
2059 int sk_disk_dump(SkDisk *d) {
2061 SkBool awake = FALSE;
2066 printf("Device: %s\n"
2068 d->name ? d->name : "n/a",
2069 disk_type_to_string(d->type));
2071 ret = sk_disk_get_size(d, &size);
2073 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2075 printf("Size: %s\n", strerror(errno));
2077 if (d->identify_valid) {
2078 const SkIdentifyParsedData *ipd;
2079 SkSmartQuirk quirk = 0;
2082 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2085 printf("Model: [%s]\n"
2088 "SMART Available: %s\n",
2092 yes_no(disk_smart_is_available(d)));
2094 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2099 for (i = 0; quirk_name[i]; i++)
2101 printf(" %s", _P(quirk_name[i]));
2107 ret = sk_disk_check_sleep_mode(d, &awake);
2108 printf("Awake: %s\n",
2109 ret >= 0 ? yes_no(awake) : strerror(errno));
2111 if (disk_smart_is_available(d)) {
2112 SkSmartOverall overall;
2113 const SkSmartParsedData *spd;
2116 uint64_t value, power_on;
2118 ret = sk_disk_smart_status(d, &good);
2119 printf("SMART Disk Health Good: %s\n",
2120 ret >= 0 ? yes_no(good) : strerror(errno));
2122 if ((ret = sk_disk_smart_read_data(d)) < 0)
2125 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2128 printf("Off-line Data Collection Status: [%s]\n"
2129 "Total Time To Complete Off-Line Data Collection: %u s\n"
2130 "Self-Test Execution Status: [%s]\n"
2131 "Percent Self-Test Remaining: %u%%\n"
2132 "Conveyance Self-Test Available: %s\n"
2133 "Short/Extended Self-Test Available: %s\n"
2134 "Start Self-Test Available: %s\n"
2135 "Abort Self-Test Available: %s\n"
2136 "Short Self-Test Polling Time: %u min\n"
2137 "Extended Self-Test Polling Time: %u min\n"
2138 "Conveyance Self-Test Polling Time: %u min\n",
2139 sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2140 spd->total_offline_data_collection_seconds,
2141 sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2142 spd->self_test_execution_percent_remaining,
2143 yes_no(spd->conveyance_test_available),
2144 yes_no(spd->short_and_extended_test_available),
2145 yes_no(spd->start_test_available),
2146 yes_no(spd->abort_test_available),
2147 spd->short_test_polling_minutes,
2148 spd->extended_test_polling_minutes,
2149 spd->conveyance_test_polling_minutes);
2151 if (sk_disk_smart_get_bad(d, &value) < 0)
2152 printf("Bad Sectors: %s\n", strerror(errno));
2154 printf("%sBad Sectors: %s%s\n",
2155 value > 0 ? HIGHLIGHT : "",
2156 print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2157 value > 0 ? ENDHIGHLIGHT : "");
2159 if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2160 printf("Powered On: %s\n", strerror(errno));
2163 printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2165 if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2166 printf("Power Cycles: %s\n", strerror(errno));
2168 printf("Power Cycles: %llu\n", (unsigned long long) value);
2170 if (value > 0 && power_on > 0)
2171 printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2174 if (sk_disk_smart_get_temperature(d, &value) < 0)
2175 printf("Temperature: %s\n", strerror(errno));
2177 printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2179 if (sk_disk_smart_get_overall(d, &overall) < 0)
2180 printf("Overall Status: %s\n", strerror(errno));
2182 printf("%sOverall Status: %s%s\n",
2183 overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2184 sk_smart_overall_to_string(overall),
2185 overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2187 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-3s\n",
2199 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2202 printf("ATA SMART not supported.\n");
2207 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2211 if (d->size == (uint64_t) -1) {
2220 static int disk_find_type(SkDisk *d, dev_t devnum) {
2222 struct udev_device *dev = NULL, *usb;
2227 if (!(udev = udev_new())) {
2232 if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2237 if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2238 const char *product, *vendor;
2241 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2242 sscanf(product, "%04x", &pid) != 1) {
2247 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2248 sscanf(vendor, "%04x", &vid) != 1) {
2253 if ((vid == 0x0c0b && pid == 0xb159) ||
2254 (vid == 0x04fc && pid == 0x0c25) ||
2255 (vid == 0x04fc && pid == 0x0c15))
2256 d->type = SK_DISK_TYPE_SUNPLUS;
2257 else if ((vid == 0x152d && pid == 0x2329) ||
2258 (vid == 0x152d && pid == 0x2336) ||
2259 (vid == 0x152d && pid == 0x2338) ||
2260 (vid == 0x152d && pid == 0x2339))
2261 d->type = SK_DISK_TYPE_JMICRON;
2263 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2265 } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2266 d->type = SK_DISK_TYPE_LINUX_IDE;
2267 else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2268 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2270 d->type = SK_DISK_TYPE_UNKNOWN;
2276 udev_device_unref(dev);
2284 int sk_disk_open(const char *name, SkDisk **_d) {
2291 if (!(d = calloc(1, sizeof(SkDisk)))) {
2298 d->type = SK_DISK_TYPE_BLOB;
2299 d->size = (uint64_t) -1;
2302 if (!(d->name = strdup(name))) {
2307 if ((d->fd = open(name,
2308 O_RDONLY|O_NOCTTY|O_NONBLOCK
2318 if ((ret = fstat(d->fd, &st)) < 0)
2321 if (!S_ISBLK(st.st_mode)) {
2327 /* So, it's a block device. Let's make sure the ioctls work */
2328 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2331 if (d->size <= 0 || d->size == (uint64_t) -1) {
2337 /* OK, it's a real block device with a size. Now let's find the suitable API */
2338 if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2341 if (d->type == SK_DISK_TYPE_UNKNOWN) {
2342 /* We have no clue, so let's autotest for a working API */
2343 for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2344 if (disk_identify_device(d) >= 0)
2346 if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2347 d->type = SK_DISK_TYPE_UNKNOWN;
2349 disk_identify_device(d);
2351 /* Check if driver can do SMART, and enable if necessary */
2352 if (disk_smart_is_available(d)) {
2354 if (!disk_smart_is_enabled(d)) {
2355 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2358 if ((ret = disk_identify_device(d)) < 0)
2361 if (!disk_smart_is_enabled(d)) {
2368 disk_smart_read_thresholds(d);
2384 void sk_disk_free(SkDisk *d) {
2395 int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2397 SkBool good, have_good = FALSE;
2405 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2406 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2407 (d->smart_thresholds ? 8 + sizeof(d->smart_thresholds) : 0);
2409 if (sk_disk_smart_status(d, &good) >= 0) {
2420 if (!(d->blob = malloc(size))) {
2427 /* These memory accesses are only OK as long as all our
2428 * objects are sensibly aligned, which they are... */
2430 if (d->identify_valid) {
2431 p[0] = SK_BLOB_TAG_IDENTIFY;
2432 p[1] = htonl(sizeof(d->identify));
2435 memcpy(p, d->identify, sizeof(d->identify));
2436 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2440 p[0] = SK_BLOB_TAG_SMART_STATUS;
2442 p[2] = htonl(!!good);
2446 if (d->smart_data_valid) {
2447 p[0] = SK_BLOB_TAG_SMART_DATA;
2448 p[1] = htonl(sizeof(d->smart_data));
2451 memcpy(p, d->smart_data, sizeof(d->smart_data));
2452 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2455 if (d->smart_thresholds_valid) {
2456 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2457 p[1] = htonl(sizeof(d->smart_thresholds));
2460 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2461 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2464 assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2472 int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2475 SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2480 if (d->type != SK_DISK_TYPE_BLOB) {
2490 /* First run, verify if everything makes sense */
2494 uint32_t tag, tsize;
2502 memcpy(&tsize, p+1, 4);
2506 if (left < ntohl(tsize)) {
2513 case SK_BLOB_TAG_IDENTIFY:
2514 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2521 case SK_BLOB_TAG_SMART_STATUS:
2522 if (ntohl(tsize) != 4 || bssv) {
2529 case SK_BLOB_TAG_SMART_DATA:
2530 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2537 case SK_BLOB_TAG_SMART_THRESHOLDS:
2538 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
2546 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2547 left -= ntohl(tsize);
2555 d->identify_valid = idv;
2556 d->smart_data_valid = sdv;
2557 d->smart_thresholds_valid = stv;
2558 d->blob_smart_status_valid = bssv;
2560 /* Second run, actually copy things in */
2564 uint32_t tag, tsize;
2568 memcpy(&tsize, p+1, 4);
2572 assert(left >= ntohl(tsize));
2576 case SK_BLOB_TAG_IDENTIFY:
2577 assert(ntohl(tsize) == sizeof(d->identify));
2578 memcpy(d->identify, p, sizeof(d->identify));
2581 case SK_BLOB_TAG_SMART_STATUS: {
2583 assert(ntohl(tsize) == 4);
2585 d->blob_smart_status = !!ok;
2589 case SK_BLOB_TAG_SMART_DATA:
2590 assert(ntohl(tsize) == sizeof(d->smart_data));
2591 memcpy(d->smart_data, p, sizeof(d->smart_data));
2594 case SK_BLOB_TAG_SMART_THRESHOLDS:
2595 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
2596 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
2600 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2601 left -= ntohl(tsize);