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_ATA, /* 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_ATA] = "Native Linux ATA",
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_ata_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_ATA);
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_ATA] = disk_ata_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 a->pretty_value = fourtyeight & 0xFFFFFFFFU;
1135 a->pretty_value = fourtyeight;
1138 typedef struct SkSmartAttributeInfo {
1140 SkSmartAttributeUnit unit;
1141 } SkSmartAttributeInfo;
1143 /* This data is stolen from smartmontools */
1145 /* %STRINGPOOLSTART% */
1146 static const SkSmartAttributeInfo const attribute_info[256] = {
1147 [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1148 [2] = { "throughput-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1149 [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1150 [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1151 [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1152 [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1153 [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1154 [8] = { "seek-time-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1155 [9] = { "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1156 [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1157 [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1158 [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1159 [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1160 [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1161 [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE },
1162 [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1163 [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1164 [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1165 [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1166 [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1167 [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE },
1168 [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1169 [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1170 [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
1171 [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1172 [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1173 [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
1174 [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1175 [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1176 [204] = { "shock-count-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE },
1177 [205] = { "shock-rate-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE },
1178 [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1179 [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1180 [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN},
1181 [209] = { "offline-seek-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1182 [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1183 [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
1184 [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1185 [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1186 [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1187 [225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
1188 [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1189 [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
1190 [228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
1191 [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
1192 [231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN },
1193 [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
1194 [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE }
1196 /* %STRINGPOOLSTOP% */
1198 typedef enum SkSmartQuirk {
1199 SK_SMART_QUIRK_9_POWERONMINUTES = 1,
1200 SK_SMART_QUIRK_9_POWERONSECONDS = 2,
1201 SK_SMART_QUIRK_9_POWERONHALFMINUTES = 4,
1202 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 8,
1203 SK_SMART_QUIRK_193_LOADUNLOAD = 16,
1204 SK_SMART_QUIRK_194_10XCELSIUS = 32,
1205 SK_SMART_QUIRK_194_UNKNOWN = 64,
1206 SK_SMART_QUIRK_200_WRITEERRORCOUNT = 128,
1207 SK_SMART_QUIRK_201_DETECTEDTACOUNT = 256
1210 /* %STRINGPOOLSTART% */
1211 static const char *quirk_name[] = {
1214 "9_POWERONHALFMINUTES",
1215 "192_EMERGENCYRETRACTCYCLECT",
1219 "200_WRITEERRORCOUNT",
1220 "201_DETECTEDTACOUNT",
1223 /* %STRINGPOOLSTOP% */
1225 typedef struct SkSmartQuirkDatabase {
1227 const char *firmware;
1229 } SkSmartQuirkDatabase;
1231 static const SkSmartQuirkDatabase quirk_database[] = { {
1234 "^FUJITSU MHR2040AT$",
1236 SK_SMART_QUIRK_9_POWERONSECONDS|
1237 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1238 SK_SMART_QUIRK_200_WRITEERRORCOUNT
1240 "^FUJITSU MHS20[6432]0AT( .)?$",
1242 SK_SMART_QUIRK_9_POWERONSECONDS|
1243 SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT|
1244 SK_SMART_QUIRK_200_WRITEERRORCOUNT|
1245 SK_SMART_QUIRK_201_DETECTEDTACOUNT
1249 "FUJITSU MHG2...ATU?.*|"
1250 "FUJITSU MHH2...ATU?.*|"
1251 "FUJITSU MHJ2...ATU?.*|"
1252 "FUJITSU MHK2...ATU?.*|"
1253 "FUJITSU MHL2300AT|"
1254 "FUJITSU MHM2(20|15|10|06)0AT|"
1255 "FUJITSU MHN2...AT|"
1256 "FUJITSU MHR2020AT|"
1257 "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|"
1258 "FUJITSU MHU2...ATU?.*|"
1259 "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|"
1260 "FUJITSU MP[A-G]3...A[HTEV]U?.*"
1263 SK_SMART_QUIRK_9_POWERONSECONDS
1269 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]"
1272 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1279 SK_SMART_QUIRK_9_POWERONHALFMINUTES|
1280 SK_SMART_QUIRK_194_10XCELSIUS
1282 "^SAMSUNG SP40A2H$",
1284 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1286 "^SAMSUNG SP80A4H$",
1288 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1290 "^SAMSUNG SP8004H$",
1292 SK_SMART_QUIRK_9_POWERONHALFMINUTES
1297 "Maxtor 2B0(0[468]|1[05]|20)H1|"
1298 "Maxtor 4G(120J6|160J[68])|"
1299 "Maxtor 4D0(20H1|40H2|60H3|80H4)"
1302 SK_SMART_QUIRK_9_POWERONMINUTES|
1303 SK_SMART_QUIRK_194_UNKNOWN
1306 "Maxtor 2F0[234]0[JL]0|"
1307 "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|"
1308 "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|"
1309 "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|"
1310 "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|"
1311 "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|"
1312 "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|"
1313 "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|"
1314 "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|"
1315 "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|"
1316 "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|"
1317 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|"
1318 "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|"
1319 "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|"
1320 "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|"
1321 "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|"
1322 "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|"
1323 "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|"
1324 "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|"
1325 "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|"
1326 "Maxtor (98196H8|96147H6)|"
1327 "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|"
1328 "Maxtor 6(E0[234]|K04)0L0|"
1329 "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|"
1330 "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|"
1331 "Maxtor 7Y250[PM]0|"
1332 "Maxtor [45]A(25|30|32)0[JN]0|"
1333 "Maxtor 7L(25|30)0[SR]0"
1336 SK_SMART_QUIRK_9_POWERONMINUTES
1342 "HITACHI_DK14FA-20B|"
1343 "HITACHI_DK23..-..B?|"
1344 "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|"
1345 "HE[JN]4230[23]0F9AT00|"
1346 "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00"
1349 SK_SMART_QUIRK_9_POWERONMINUTES|
1350 SK_SMART_QUIRK_193_LOADUNLOAD
1359 static int match(const char*regex, const char *s, SkBool *result) {
1365 if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) {
1370 if ((k = regexec(&re, s, 0, NULL, 0)) != 0) {
1372 if (k != REG_NOMATCH) {
1386 static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) {
1388 const SkSmartQuirkDatabase *db;
1392 for (db = quirk_database; db->model || db->firmware; db++) {
1395 SkBool matching = FALSE;
1397 if ((k = match(db->model, model, &matching)) < 0)
1405 SkBool matching = FALSE;
1407 if ((k = match(db->firmware, firmware, &matching)) < 0)
1421 static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) {
1422 const SkIdentifyParsedData *ipd;
1423 SkSmartQuirk quirk = 0;
1425 /* These are the complex ones */
1426 if (sk_disk_identify_parse(d, &ipd) < 0)
1429 if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0)
1436 /* %STRINGPOOLSTART% */
1437 if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) {
1438 static const SkSmartAttributeInfo a = {
1439 "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1443 } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) {
1444 static const SkSmartAttributeInfo a = {
1445 "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1449 } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) {
1450 static const SkSmartAttributeInfo a = {
1451 "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
1455 /* %STRINGPOOLSTOP% */
1460 /* %STRINGPOOLSTART% */
1461 if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
1462 static const SkSmartAttributeInfo a = {
1463 "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1467 /* %STRINGPOOLSTOP% */
1472 /* %STRINGPOOLSTART% */
1473 if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) {
1474 static const SkSmartAttributeInfo a = {
1475 "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN
1478 } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN)
1480 /* %STRINGPOOLSTOP% */
1485 /* %STRINGPOOLSTART% */
1486 if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) {
1487 static const SkSmartAttributeInfo a = {
1488 "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1492 /* %STRINGPOOLSTOP% */
1497 /* %STRINGPOOLSTART% */
1498 if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) {
1499 static const SkSmartAttributeInfo a = {
1500 "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE
1504 /* %STRINGPOOLSTOP% */
1510 /* These are the simple cases */
1511 if (attribute_info[id].name)
1512 return &attribute_info[id];
1517 int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
1519 if (!d->smart_data_valid) {
1524 switch (d->smart_data[362]) {
1527 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
1532 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
1536 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
1541 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
1546 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
1551 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
1555 d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
1559 d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
1560 d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
1562 d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8);
1564 d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
1565 d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
1566 d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
1567 d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
1569 d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
1570 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]);
1571 d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
1573 *spd = &d->smart_parsed_data;
1578 static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
1582 if (!d->smart_thresholds_valid) {
1583 a->threshold_valid = FALSE;
1587 for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12)
1592 a->threshold_valid = FALSE;
1593 a->good_valid = FALSE;
1597 a->threshold = p[1];
1598 a->threshold_valid = p[1] != 0xFE;
1600 a->good_valid = FALSE;
1603 /* Always-Fail and Always-Passing thresholds are not relevant
1604 * for our assessment. */
1605 if (p[1] >= 1 && p[1] <= 0xFD) {
1607 if (a->worst_value_valid) {
1608 a->good = a->good && (a->worst_value > a->threshold);
1609 a->good_valid = TRUE;
1612 if (a->current_value_valid) {
1613 a->good = a->good && (a->current_value > a->threshold);
1614 a->good_valid = TRUE;
1619 int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) {
1623 if (!d->smart_data_valid) {
1628 for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
1629 SkSmartAttributeParsedData a;
1630 const SkSmartAttributeInfo *i;
1636 memset(&a, 0, sizeof(a));
1638 a.current_value = p[3];
1639 a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD;
1640 a.worst_value = p[4];
1641 a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD;
1643 a.flags = ((uint16_t) p[2] << 8) | p[1];
1644 a.prefailure = !!(p[1] & 1);
1645 a.online = !!(p[1] & 2);
1647 memcpy(a.raw, p+5, 6);
1649 if ((i = lookup_attribute(d, p[0]))) {
1650 a.name = _P(i->name);
1651 a.pretty_unit = i->unit;
1653 if (asprintf(&an, "attribute-%u", a.id) < 0) {
1659 a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
1664 find_threshold(d, &a);
1666 /* Handle a few fields specially */
1667 if ((!strcmp(a.name, "reallocated-sector-count") ||
1668 !strcmp(a.name, "current-pending-sector")) &&
1669 a.pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS &&
1670 a.pretty_value > 0) {
1672 a.good_valid = TRUE;
1675 cb(d, &a, userdata);
1682 static const char *yes_no(SkBool b) {
1683 return b ? "yes" : "no";
1686 const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
1688 /* %STRINGPOOLSTART% */
1689 const char * const map[] = {
1690 [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
1691 [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
1692 [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
1693 [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
1694 [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK"
1696 /* %STRINGPOOLSTOP% */
1698 if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
1701 return _P(map[unit]);
1704 struct attr_helper {
1709 static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1711 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
1714 if (!strcmp(a->name, "temperature-centi-celsius") ||
1715 !strcmp(a->name, "temperature-celsius") ||
1716 !strcmp(a->name, "temperature-celsius-2") ||
1717 !strcmp(a->name, "airflow-temperature-celsius")) {
1719 if (!ah->found || a->pretty_value > *ah->value)
1720 *ah->value = a->pretty_value;
1726 int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) {
1727 struct attr_helper ah;
1735 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0)
1746 static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1748 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS)
1751 if (!strcmp(a->name, "power-on-minutes") ||
1752 !strcmp(a->name, "power-on-seconds") ||
1753 !strcmp(a->name, "power-on-half-minutes") ||
1754 !strcmp(a->name, "power-on-hours")) {
1756 if (!ah->found || a->pretty_value > *ah->value)
1757 *ah->value = a->pretty_value;
1763 int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) {
1764 struct attr_helper ah;
1770 ah.value = mseconds;
1772 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0)
1783 static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1785 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE)
1788 if (!strcmp(a->name, "power-cycle-count")) {
1790 if (!ah->found || a->pretty_value > *ah->value)
1791 *ah->value = a->pretty_value;
1797 int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) {
1798 struct attr_helper ah;
1806 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0)
1817 static void reallocated_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1819 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1822 if (!strcmp(a->name, "reallocated-sector-count")) {
1824 if (!ah->found || a->pretty_value > *ah->value)
1825 *ah->value = a->pretty_value;
1831 static void pending_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) {
1833 if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS)
1836 if (!strcmp(a->name, "current-pending-sector")) {
1838 if (!ah->found || a->pretty_value > *ah->value)
1839 *ah->value = a->pretty_value;
1845 int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) {
1846 struct attr_helper ah1, ah2;
1847 uint64_t sectors1, sectors2;
1853 ah1.value = §ors1;
1855 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) reallocated_cb, &ah1) < 0)
1859 ah2.value = §ors2;
1861 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) pending_cb, &ah2) < 0)
1864 if (!ah1.found && !ah2.found) {
1869 if (ah1.found && ah2.found)
1870 *sectors = sectors1 + sectors2;
1872 *sectors = sectors1;
1874 *sectors = sectors2;
1879 const char* sk_smart_overall_to_string(SkSmartOverall overall) {
1881 /* %STRINGPOOLSTART% */
1882 const char * const map[] = {
1883 [SK_SMART_OVERALL_GOOD] = "GOOD",
1884 [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS",
1885 [SK_SMART_OVERALL_BAD_ATTRIBUTE] = "BAD_ATTRIBUTE",
1886 [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR"
1888 /* %STRINGPOOLSTOP% */
1890 if (overall >= _SK_SMART_OVERALL_MAX)
1893 return _P(map[overall]);
1896 static void bad_attribute_cb(SkDisk *d, const SkSmartAttributeParsedData *a, SkBool *good) {
1897 if (a->prefailure && a->good_valid && !a->good)
1901 int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) {
1908 if (sk_disk_smart_status(d, &good) < 0)
1912 *overall = SK_SMART_OVERALL_BAD_STATUS;
1916 if (sk_disk_smart_get_bad(d, §ors) < 0) {
1917 if (errno != ENOENT)
1919 } else if (sectors > 0) {
1920 *overall = SK_SMART_OVERALL_BAD_SECTOR;
1925 if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) bad_attribute_cb, &good) < 0)
1929 *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE;
1933 *overall = SK_SMART_OVERALL_GOOD;
1937 static char* print_name(char *s, size_t len, uint8_t id, const char *k) {
1942 snprintf(s, len, "%u", id);
1949 static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
1951 switch (pretty_unit) {
1952 case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
1954 if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
1955 snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
1956 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
1957 snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
1958 else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
1959 snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
1960 else if (pretty_value >= 1000LLU*60LLU*60LLU)
1961 snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60));
1962 else if (pretty_value >= 1000LLU*60LLU)
1963 snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60));
1964 else if (pretty_value >= 1000LLU)
1965 snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0));
1967 snprintf(s, len, "%llu ms", (unsigned long long) pretty_value);
1971 case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
1972 snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000);
1975 case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
1976 snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
1979 case SK_SMART_ATTRIBUTE_UNIT_NONE:
1980 snprintf(s, len, "%llu", (unsigned long long) pretty_value);
1983 case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
1984 snprintf(s, len, "n/a");
1987 case _SK_SMART_ATTRIBUTE_UNIT_MAX:
1996 #define HIGHLIGHT "\x1B[1m"
1997 #define ENDHIGHLIGHT "\x1B[0m"
1999 static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) {
2002 char tt[32], tw[32], tc[32];
2005 snprintf(tt, sizeof(tt), "%3u", a->threshold);
2006 tt[sizeof(tt)-1] = 0;
2007 snprintf(tw, sizeof(tw), "%3u", a->worst_value);
2008 tw[sizeof(tw)-1] = 0;
2009 snprintf(tc, sizeof(tc), "%3u", a->current_value);
2010 tc[sizeof(tc)-1] = 0;
2012 highlight = a->good_valid && !a->good && isatty(1);
2015 fprintf(stderr, HIGHLIGHT);
2017 printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-3s\n",
2019 print_name(name, sizeof(name), a->id, a->name),
2020 a->current_value_valid ? tc : "n/a",
2021 a->worst_value_valid ? tw : "n/a",
2022 a->threshold_valid ? tt : "n/a",
2023 print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit),
2024 a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5],
2025 a->prefailure ? "prefail" : "old-age",
2026 a->online ? "online" : "offline",
2027 a->good_valid ? yes_no(a->good) : "n/a");
2030 fprintf(stderr, ENDHIGHLIGHT);
2033 int sk_disk_dump(SkDisk *d) {
2035 SkBool awake = FALSE;
2040 printf("Device: %s\n"
2042 d->name ? d->name : "n/a",
2043 disk_type_to_string(d->type));
2045 ret = sk_disk_get_size(d, &size);
2047 printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024));
2049 printf("Size: %s\n", strerror(errno));
2051 if (d->identify_valid) {
2052 const SkIdentifyParsedData *ipd;
2053 SkSmartQuirk quirk = 0;
2056 if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
2059 printf("Model: [%s]\n"
2062 "SMART Available: %s\n",
2066 yes_no(disk_smart_is_available(d)));
2068 if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk)))
2073 for (i = 0; quirk_name[i]; i++)
2075 printf(" %s", _P(quirk_name[i]));
2081 ret = sk_disk_check_sleep_mode(d, &awake);
2082 printf("Awake: %s\n",
2083 ret >= 0 ? yes_no(awake) : strerror(errno));
2085 if (disk_smart_is_available(d)) {
2086 SkSmartOverall overall;
2087 const SkSmartParsedData *spd;
2090 uint64_t value, power_on;
2092 ret = sk_disk_smart_status(d, &good);
2093 printf("SMART Disk Health Good: %s\n",
2094 ret >= 0 ? yes_no(good) : strerror(errno));
2096 if ((ret = sk_disk_smart_read_data(d)) < 0)
2099 if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
2102 printf("Off-line Data Collection Status: [%s]\n"
2103 "Total Time To Complete Off-Line Data Collection: %u s\n"
2104 "Self-Test Execution Status: [%s]\n"
2105 "Percent Self-Test Remaining: %u%%\n"
2106 "Conveyance Self-Test Available: %s\n"
2107 "Short/Extended Self-Test Available: %s\n"
2108 "Start Self-Test Available: %s\n"
2109 "Abort Self-Test Available: %s\n"
2110 "Short Self-Test Polling Time: %u min\n"
2111 "Extended Self-Test Polling Time: %u min\n"
2112 "Conveyance Self-Test Polling Time: %u min\n",
2113 sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
2114 spd->total_offline_data_collection_seconds,
2115 sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
2116 spd->self_test_execution_percent_remaining,
2117 yes_no(spd->conveyance_test_available),
2118 yes_no(spd->short_and_extended_test_available),
2119 yes_no(spd->start_test_available),
2120 yes_no(spd->abort_test_available),
2121 spd->short_test_polling_minutes,
2122 spd->extended_test_polling_minutes,
2123 spd->conveyance_test_polling_minutes);
2125 if (sk_disk_smart_get_bad(d, &value) < 0)
2126 printf("Bad Sectors: %s\n", strerror(errno));
2128 printf("%sBad Sectors: %s%s\n",
2129 value > 0 ? HIGHLIGHT : "",
2130 print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS),
2131 value > 0 ? ENDHIGHLIGHT : "");
2133 if (sk_disk_smart_get_power_on(d, &power_on) < 0) {
2134 printf("Powered On: %s\n", strerror(errno));
2137 printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2139 if (sk_disk_smart_get_power_cycle(d, &value) < 0)
2140 printf("Power Cycles: %s\n", strerror(errno));
2142 printf("Power Cycles: %llu\n", (unsigned long long) value);
2144 if (value > 0 && power_on > 0)
2145 printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS));
2148 if (sk_disk_smart_get_temperature(d, &value) < 0)
2149 printf("Temperature: %s\n", strerror(errno));
2151 printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN));
2153 if (sk_disk_smart_get_overall(d, &overall) < 0)
2154 printf("Overall Status: %s\n", strerror(errno));
2156 printf("%sOverall Status: %s%s\n",
2157 overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "",
2158 sk_smart_overall_to_string(overall),
2159 overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : "");
2161 printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-3s\n",
2173 if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
2176 printf("ATA SMART not supported.\n");
2181 int sk_disk_get_size(SkDisk *d, uint64_t *bytes) {
2185 if (d->size == (uint64_t) -1) {
2194 static int disk_find_type(SkDisk *d, dev_t devnum) {
2196 struct udev_device *dev = NULL, *usb;
2201 if (!(udev = udev_new())) {
2206 if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) {
2211 if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) {
2212 const char *product, *vendor;
2215 if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) ||
2216 sscanf(product, "%04x", &pid) != 1) {
2221 if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) ||
2222 sscanf(vendor, "%04x", &vid) != 1) {
2227 if ((vid == 0x0c0b && pid == 0xb159) ||
2228 (vid == 0x04fc && pid == 0x0c25))
2229 d->type = SK_DISK_TYPE_SUNPLUS;
2230 else if ((vid == 0x152d && pid == 0x2329) ||
2231 (vid == 0x152d && pid == 0x2336) ||
2232 (vid == 0x152d && pid == 0x2338) ||
2233 (vid == 0x152d && pid == 0x2339))
2234 d->type = SK_DISK_TYPE_JMICRON;
2236 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
2238 } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL))
2239 d->type = SK_DISK_TYPE_ATA;
2240 else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL))
2241 d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16;
2243 d->type = SK_DISK_TYPE_UNKNOWN;
2249 udev_device_unref(dev);
2257 int sk_disk_open(const char *name, SkDisk **_d) {
2264 if (!(d = calloc(1, sizeof(SkDisk)))) {
2271 d->type = SK_DISK_TYPE_BLOB;
2272 d->size = (uint64_t) -1;
2275 if (!(d->name = strdup(name))) {
2280 if ((d->fd = open(name,
2281 O_RDONLY|O_NOCTTY|O_NONBLOCK
2291 if ((ret = fstat(d->fd, &st)) < 0)
2294 if (!S_ISBLK(st.st_mode)) {
2300 /* So, it's a block device. Let's make sure the ioctls work */
2301 if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
2304 if (d->size <= 0 || d->size == (uint64_t) -1) {
2310 /* OK, it's a real block device with a size. Now let's find the suitable API */
2311 if ((ret = disk_find_type(d, st.st_rdev)) < 0)
2314 if (d->type == SK_DISK_TYPE_UNKNOWN) {
2315 /* We have no clue, so let's autotest for a working API */
2316 for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++)
2317 if (disk_identify_device(d) >= 0)
2319 if (d->type >= _SK_DISK_TYPE_TEST_MAX)
2320 d->type = SK_DISK_TYPE_UNKNOWN;
2322 disk_identify_device(d);
2324 /* Check if driver can do SMART, and enable if necessary */
2325 if (disk_smart_is_available(d)) {
2327 if (!disk_smart_is_enabled(d)) {
2328 if ((ret = disk_smart_enable(d, TRUE)) < 0)
2331 if ((ret = disk_identify_device(d)) < 0)
2334 if (!disk_smart_is_enabled(d)) {
2341 disk_smart_read_thresholds(d);
2357 void sk_disk_free(SkDisk *d) {
2368 int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) {
2370 SkBool good, have_good = FALSE;
2378 (d->identify_valid ? 8 + sizeof(d->identify) : 0) +
2379 (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
2380 (d->smart_thresholds ? 8 + sizeof(d->smart_thresholds) : 0);
2382 if (sk_disk_smart_status(d, &good) >= 0) {
2393 if (!(d->blob = malloc(size))) {
2400 /* These memory accesses are only OK as long as all our
2401 * objects are sensibly aligned, which they are... */
2403 if (d->identify_valid) {
2404 p[0] = SK_BLOB_TAG_IDENTIFY;
2405 p[1] = htonl(sizeof(d->identify));
2408 memcpy(p, d->identify, sizeof(d->identify));
2409 p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify));
2413 p[0] = SK_BLOB_TAG_SMART_STATUS;
2415 p[2] = htonl(!!good);
2419 if (d->smart_data_valid) {
2420 p[0] = SK_BLOB_TAG_SMART_DATA;
2421 p[1] = htonl(sizeof(d->smart_data));
2424 memcpy(p, d->smart_data, sizeof(d->smart_data));
2425 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data));
2428 if (d->smart_thresholds_valid) {
2429 p[0] = SK_BLOB_TAG_SMART_THRESHOLDS;
2430 p[1] = htonl(sizeof(d->smart_thresholds));
2433 memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds));
2434 p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds));
2437 assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size);
2445 int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) {
2448 SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE;
2453 if (d->type != SK_DISK_TYPE_BLOB) {
2463 /* First run, verify if everything makes sense */
2467 uint32_t tag, tsize;
2475 memcpy(&tsize, p+1, 4);
2479 if (left < ntohl(tsize)) {
2486 case SK_BLOB_TAG_IDENTIFY:
2487 if (ntohl(tsize) != sizeof(d->identify) || idv) {
2494 case SK_BLOB_TAG_SMART_STATUS:
2495 if (ntohl(tsize) != 4 || bssv) {
2502 case SK_BLOB_TAG_SMART_DATA:
2503 if (ntohl(tsize) != sizeof(d->smart_data) || sdv) {
2510 case SK_BLOB_TAG_SMART_THRESHOLDS:
2511 if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) {
2519 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2520 left -= ntohl(tsize);
2528 d->identify_valid = idv;
2529 d->smart_data_valid = sdv;
2530 d->smart_thresholds_valid = stv;
2531 d->blob_smart_status_valid = bssv;
2533 /* Second run, actually copy things in */
2537 uint32_t tag, tsize;
2541 memcpy(&tsize, p+1, 4);
2545 assert(left >= ntohl(tsize));
2549 case SK_BLOB_TAG_IDENTIFY:
2550 assert(ntohl(tsize) == sizeof(d->identify));
2551 memcpy(d->identify, p, sizeof(d->identify));
2554 case SK_BLOB_TAG_SMART_STATUS: {
2556 assert(ntohl(tsize) == 4);
2558 d->blob_smart_status = !!ok;
2562 case SK_BLOB_TAG_SMART_DATA:
2563 assert(ntohl(tsize) == sizeof(d->smart_data));
2564 memcpy(d->smart_data, p, sizeof(d->smart_data));
2567 case SK_BLOB_TAG_SMART_THRESHOLDS:
2568 assert(ntohl(tsize) == sizeof(d->smart_thresholds));
2569 memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds));
2573 p = (uint32_t*) ((uint8_t*) p + ntohl(tsize));
2574 left -= ntohl(tsize);